diff --git a/.detect-secrets.cfg b/.detect-secrets.cfg index 66ed5236eeb..38912567c9b 100644 --- a/.detect-secrets.cfg +++ b/.detect-secrets.cfg @@ -7,6 +7,10 @@ [exclude-files] # pnpm lockfiles contain lots of high-entropy package integrity blobs. pattern = (^|/)pnpm-lock\.yaml$ +# Generated output and vendored assets. +pattern = (^|/)(dist|vendor)/ +# Local config file with allowlist patterns. +pattern = (^|/)\.detect-secrets\.cfg$ [exclude-lines] # Fastlane checks for private key marker; not a real key. diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 00000000000..bcfdefcddb1 --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,17 @@ +# actionlint configuration +# https://github.com/rhysd/actionlint/blob/main/docs/config.md + +self-hosted-runner: + labels: + # Blacksmith CI runners + - blacksmith-4vcpu-ubuntu-2404 + - blacksmith-4vcpu-windows-2025 + +# Ignore patterns for known issues +paths: + .github/workflows/**/*.yml: + ignore: + # Ignore shellcheck warnings (we run shellcheck separately) + - 'shellcheck reported issue.+' + # Ignore intentional if: false for disabled jobs + - 'constant expression "false" in condition' diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..c0e1d465b60 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,113 @@ +# Dependabot configuration +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 + +registries: + npm-npmjs: + type: npm-registry + url: https://registry.npmjs.org + replaces-base: true + +updates: + # npm dependencies (root) + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + production: + dependency-type: production + update-types: + - minor + - patch + development: + dependency-type: development + update-types: + - minor + - patch + open-pull-requests-limit: 10 + registries: + - npm-npmjs + + # GitHub Actions + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + actions: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 + + # Swift Package Manager - macOS app + - package-ecosystem: swift + directory: /apps/macos + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + swift-deps: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 + + # Swift Package Manager - shared ClawdbotKit + - package-ecosystem: swift + directory: /apps/shared/ClawdbotKit + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + swift-deps: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 + + # Swift Package Manager - Swabble + - package-ecosystem: swift + directory: /Swabble + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + swift-deps: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 + + # Gradle - Android app + - package-ecosystem: gradle + directory: /apps/android + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + android-deps: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000000..5d2837a6ceb --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,174 @@ +"channel: bluebubbles": + - changed-files: + - any-glob-to-any-file: + - "extensions/bluebubbles/**" + - "docs/channels/bluebubbles.md" +"channel: discord": + - changed-files: + - any-glob-to-any-file: + - "src/discord/**" + - "extensions/discord/**" + - "docs/channels/discord.md" +"channel: googlechat": + - changed-files: + - any-glob-to-any-file: + - "extensions/googlechat/**" + - "docs/channels/googlechat.md" +"channel: imessage": + - changed-files: + - any-glob-to-any-file: + - "src/imessage/**" + - "extensions/imessage/**" + - "docs/channels/imessage.md" +"channel: line": + - changed-files: + - any-glob-to-any-file: + - "extensions/line/**" +"channel: matrix": + - changed-files: + - any-glob-to-any-file: + - "extensions/matrix/**" + - "docs/channels/matrix.md" +"channel: mattermost": + - changed-files: + - any-glob-to-any-file: + - "extensions/mattermost/**" + - "docs/channels/mattermost.md" +"channel: msteams": + - changed-files: + - any-glob-to-any-file: + - "extensions/msteams/**" + - "docs/channels/msteams.md" +"channel: nextcloud-talk": + - changed-files: + - any-glob-to-any-file: + - "extensions/nextcloud-talk/**" + - "docs/channels/nextcloud-talk.md" +"channel: nostr": + - changed-files: + - any-glob-to-any-file: + - "extensions/nostr/**" + - "docs/channels/nostr.md" +"channel: signal": + - changed-files: + - any-glob-to-any-file: + - "src/signal/**" + - "extensions/signal/**" + - "docs/channels/signal.md" +"channel: slack": + - changed-files: + - any-glob-to-any-file: + - "src/slack/**" + - "extensions/slack/**" + - "docs/channels/slack.md" +"channel: telegram": + - changed-files: + - any-glob-to-any-file: + - "src/telegram/**" + - "extensions/telegram/**" + - "docs/channels/telegram.md" +"channel: tlon": + - changed-files: + - any-glob-to-any-file: + - "extensions/tlon/**" + - "docs/channels/tlon.md" +"channel: voice-call": + - changed-files: + - any-glob-to-any-file: + - "extensions/voice-call/**" +"channel: whatsapp-web": + - changed-files: + - any-glob-to-any-file: + - "src/web/**" + - "extensions/whatsapp/**" + - "docs/channels/whatsapp.md" +"channel: zalo": + - changed-files: + - any-glob-to-any-file: + - "extensions/zalo/**" + - "docs/channels/zalo.md" +"channel: zalouser": + - changed-files: + - any-glob-to-any-file: + - "extensions/zalouser/**" + - "docs/channels/zalouser.md" + +"app: android": + - changed-files: + - any-glob-to-any-file: + - "apps/android/**" + - "docs/platforms/android.md" +"app: ios": + - changed-files: + - any-glob-to-any-file: + - "apps/ios/**" + - "docs/platforms/ios.md" +"app: macos": + - changed-files: + - any-glob-to-any-file: + - "apps/macos/**" + - "docs/platforms/macos.md" + - "docs/platforms/mac/**" +"app: web-ui": + - changed-files: + - any-glob-to-any-file: + - "ui/**" + - "src/gateway/control-ui.ts" + - "src/gateway/control-ui-shared.ts" + - "src/gateway/protocol/**" + - "src/gateway/server-methods/chat.ts" + - "src/infra/control-ui-assets.ts" + +"gateway": + - changed-files: + - any-glob-to-any-file: + - "src/gateway/**" + - "src/daemon/**" + - "docs/gateway/**" + +"docs": + - changed-files: + - any-glob-to-any-file: + - "docs/**" + - "docs.acp.md" + +"extensions: copilot-proxy": + - changed-files: + - any-glob-to-any-file: + - "extensions/copilot-proxy/**" +"extensions: diagnostics-otel": + - changed-files: + - any-glob-to-any-file: + - "extensions/diagnostics-otel/**" +"extensions: google-antigravity-auth": + - changed-files: + - any-glob-to-any-file: + - "extensions/google-antigravity-auth/**" +"extensions: google-gemini-cli-auth": + - changed-files: + - any-glob-to-any-file: + - "extensions/google-gemini-cli-auth/**" +"extensions: llm-task": + - changed-files: + - any-glob-to-any-file: + - "extensions/llm-task/**" +"extensions: lobster": + - changed-files: + - any-glob-to-any-file: + - "extensions/lobster/**" +"extensions: memory-core": + - changed-files: + - any-glob-to-any-file: + - "extensions/memory-core/**" +"extensions: memory-lancedb": + - changed-files: + - any-glob-to-any-file: + - "extensions/memory-lancedb/**" +"extensions: open-prose": + - changed-files: + - any-glob-to-any-file: + - "extensions/open-prose/**" +"extensions: qwen-portal-auth": + - changed-files: + - any-glob-to-any-file: + - "extensions/qwen-portal-auth/**" diff --git a/.github/workflows/auto-response.yml b/.github/workflows/auto-response.yml new file mode 100644 index 00000000000..7f242a09463 --- /dev/null +++ b/.github/workflows/auto-response.yml @@ -0,0 +1,59 @@ +name: Auto response + +on: + issues: + types: [labeled] + pull_request: + types: [labeled] + +permissions: + issues: write + pull-requests: write + +jobs: + auto-response: + runs-on: ubuntu-latest + steps: + - name: Handle labeled items + uses: actions/github-script@v7 + with: + script: | + const rules = [ + { + label: "skill-clawdhub", + close: true, + message: + "Thanks for the contribution! New skills should be published to Clawdhub for everyone to use. We’re keeping the core lean on skills, so I’m closing this out.", + }, + ]; + + const labelName = context.payload.label?.name; + if (!labelName) { + return; + } + + const rule = rules.find((item) => item.label === labelName); + if (!rule) { + return; + } + + const issueNumber = context.payload.issue?.number ?? context.payload.pull_request?.number; + if (!issueNumber) { + return; + } + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: rule.message, + }); + + if (rule.close) { + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + state: "closed", + }); + } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be0d8926f13..8cc86bd6309 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,20 +32,29 @@ jobs: node-version: 22.x check-latest: true + - name: Setup pnpm (corepack retry) + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare pnpm@10.23.0 --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 + - name: Runtime versions run: | node -v npm -v + pnpm -v - name: Capture node path run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV" - - name: Enable corepack and pin pnpm - run: | - corepack enable - corepack prepare pnpm@10.23.0 --activate - pnpm -v - - name: Install dependencies (frozen) env: CI: true @@ -108,6 +117,20 @@ jobs: node-version: 22.x check-latest: true + - name: Setup pnpm (corepack retry) + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare pnpm@10.23.0 --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 + - name: Setup Bun uses: oven-sh/setup-bun@v2 with: @@ -118,16 +141,11 @@ jobs: node -v npm -v bun -v + pnpm -v - name: Capture node path run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV" - - name: Enable corepack and pin pnpm - run: | - corepack enable - corepack prepare pnpm@10.23.0 --activate - pnpm -v - - name: Install dependencies env: CI: true @@ -168,6 +186,8 @@ jobs: checks-windows: runs-on: blacksmith-4vcpu-windows-2025 + env: + NODE_OPTIONS: --max-old-space-size=4096 defaults: run: shell: bash @@ -212,6 +232,20 @@ jobs: node-version: 22.x check-latest: true + - name: Setup pnpm (corepack retry) + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare pnpm@10.23.0 --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 + - name: Setup Bun uses: oven-sh/setup-bun@v2 with: @@ -222,16 +256,11 @@ jobs: node -v npm -v bun -v + pnpm -v - name: Capture node path run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV" - - name: Enable corepack and pin pnpm - run: | - corepack enable - corepack prepare pnpm@10.23.0 --activate - pnpm -v - - name: Install dependencies env: CI: true @@ -279,20 +308,29 @@ jobs: node-version: 22.x check-latest: true + - name: Setup pnpm (corepack retry) + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare pnpm@10.23.0 --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 + - name: Runtime versions run: | node -v npm -v + pnpm -v - name: Capture node path run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV" - - name: Enable corepack and pin pnpm - run: | - corepack enable - corepack prepare pnpm@10.23.0 --activate - pnpm -v - - name: Install dependencies env: CI: true @@ -304,6 +342,8 @@ jobs: pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true - name: Run ${{ matrix.task }} + env: + NODE_OPTIONS: --max-old-space-size=4096 run: ${{ matrix.command }} macos-app: @@ -590,6 +630,8 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: 8.11.1 - name: Install Android SDK packages run: | diff --git a/.github/workflows/install-smoke.yml b/.github/workflows/install-smoke.yml index 84d1b7f3254..16eba4eed7b 100644 --- a/.github/workflows/install-smoke.yml +++ b/.github/workflows/install-smoke.yml @@ -13,12 +13,19 @@ jobs: - name: Checkout CLI uses: actions/checkout@v4 - - name: Setup pnpm - uses: pnpm/action-setup@v3 - with: - version: 10 - - name: Enable Corepack - run: corepack enable + - name: Setup pnpm (corepack retry) + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare pnpm@10.23.0 --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 - name: Install pnpm deps (minimal) run: pnpm install --ignore-scripts --frozen-lockfile diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000000..8d078774b7b --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,23 @@ +name: Labeler + +on: + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + label: + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: "2729701" + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + - uses: actions/labeler@v5 + with: + configuration-path: .github/labeler.yml + repo-token: ${{ steps.app-token.outputs.token }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000000..80813a0d320 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,105 @@ +# Pre-commit hooks for clawdbot +# Install: prek install +# Run manually: prek run --all-files +# +# See https://pre-commit.com for more information + +repos: + # Basic file hygiene + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + exclude: '^(docs/|dist/|vendor/|.*\.snap$)' + - id: end-of-file-fixer + exclude: '^(docs/|dist/|vendor/|.*\.snap$)' + - id: check-yaml + args: [--allow-multiple-documents] + - id: check-added-large-files + args: [--maxkb=500] + - id: check-merge-conflict + + # Secret detection (same as CI) + - repo: https://github.com/Yelp/detect-secrets + rev: v1.5.0 + hooks: + - id: detect-secrets + args: + - --baseline + - .secrets.baseline + - --exclude-files + - '(^|/)(dist/|vendor/|pnpm-lock\.yaml$|\.detect-secrets\.cfg$)' + - --exclude-lines + - 'key_content\.include\?\("BEGIN PRIVATE KEY"\)' + - --exclude-lines + - 'case \.apiKeyEnv: "API key \(env var\)"' + - --exclude-lines + - 'case apikey = "apiKey"' + - --exclude-lines + - '"gateway\.remote\.password"' + - --exclude-lines + - '"gateway\.auth\.password"' + - --exclude-lines + - '"talk\.apiKey"' + - --exclude-lines + - '=== "string"' + - --exclude-lines + - 'typeof remote\?\.password === "string"' + + # Shell script linting + - repo: https://github.com/koalaman/shellcheck-precommit + rev: v0.11.0 + hooks: + - id: shellcheck + args: [--severity=error] # Only fail on errors, not warnings/info + # Exclude vendor and scripts with embedded code or known issues + exclude: '^(vendor/|scripts/e2e/)' + + # GitHub Actions linting + - repo: https://github.com/rhysd/actionlint + rev: v1.7.10 + hooks: + - id: actionlint + + # GitHub Actions security audit + - repo: https://github.com/zizmorcore/zizmor-pre-commit + rev: v1.22.0 + hooks: + - id: zizmor + args: [--persona=regular, --min-severity=medium, --min-confidence=medium] + exclude: '^(vendor/|Swabble/)' + + # Project checks (same commands as CI) + - repo: local + hooks: + # oxlint --type-aware src test + - id: oxlint + name: oxlint + entry: scripts/pre-commit/run-node-tool.sh oxlint --type-aware src test + language: system + pass_filenames: false + types_or: [javascript, jsx, ts, tsx] + + # oxfmt --check src test + - id: oxfmt + name: oxfmt + entry: scripts/pre-commit/run-node-tool.sh oxfmt --check src test + language: system + pass_filenames: false + types_or: [javascript, jsx, ts, tsx] + + # swiftlint (same as CI) + - id: swiftlint + name: swiftlint + entry: swiftlint --config .swiftlint.yml + language: system + pass_filenames: false + types: [swift] + + # swiftformat --lint (same as CI) + - id: swiftformat + name: swiftformat + entry: swiftformat --lint apps/macos/Sources --config .swiftformat + language: system + pass_filenames: false + types: [swift] diff --git a/.secrets.baseline b/.secrets.baseline index 4c0ca50a495..826d5b4def1 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -159,23 +159,23 @@ { "type": "Base64 High Entropy String", "filename": "appcast.xml", - "hashed_secret": "1b1c2b73eca84e441a823c37a06c71c9fadcfe24", + "hashed_secret": "4e5f0a148d9ef42afeb73b1c77643e2ef2dee0b9", "is_verified": false, - "line_number": 19 + "line_number": 90 }, { "type": "Base64 High Entropy String", "filename": "appcast.xml", - "hashed_secret": "5c47736fee5151b26b3bb61bb38955da0e8937c6", + "hashed_secret": "f1ccdaf78c308ec2cf608818da13f5f1e4809ed1", "is_verified": false, - "line_number": 35 + "line_number": 138 }, { "type": "Base64 High Entropy String", "filename": "appcast.xml", - "hashed_secret": "bbbca47179268f154c63affa0ca441c6e49e650f", + "hashed_secret": "2691dc9c9ded92ba62a2d8ee589e2d78e2aa0479", "is_verified": false, - "line_number": 52 + "line_number": 212 } ], "apps/macos/Tests/ClawdbotIPCTests/AnthropicAuthResolverTests.swift": [ @@ -194,13 +194,22 @@ "line_number": 42 } ], - "apps/macos/Tests/ClawdbotIPCTests/ConnectionsSettingsSmokeTests.swift": [ + "apps/macos/Tests/ClawdbotIPCTests/GatewayEndpointStoreTests.swift": [ { "type": "Secret Keyword", - "filename": "apps/macos/Tests/ClawdbotIPCTests/ConnectionsSettingsSmokeTests.swift", - "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "filename": "apps/macos/Tests/ClawdbotIPCTests/GatewayEndpointStoreTests.swift", + "hashed_secret": "19dad5cecb110281417d1db56b60e1b006d55bb4", "is_verified": false, - "line_number": 83 + "line_number": 61 + } + ], + "apps/macos/Tests/ClawdbotIPCTests/GatewayLaunchAgentManagerTests.swift": [ + { + "type": "Secret Keyword", + "filename": "apps/macos/Tests/ClawdbotIPCTests/GatewayLaunchAgentManagerTests.swift", + "hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073", + "is_verified": false, + "line_number": 13 } ], "apps/macos/Tests/ClawdbotIPCTests/TailscaleIntegrationSectionTests.swift": [ @@ -212,109 +221,919 @@ "line_number": 27 } ], - "docs/configuration.md": [ + "apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift": [ { "type": "Secret Keyword", - "filename": "docs/configuration.md", - "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "filename": "apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift", + "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 268 - }, + "line_number": 100 + } + ], + "docs/brave-search.md": [ { "type": "Secret Keyword", - "filename": "docs/configuration.md", - "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e", + "filename": "docs/brave-search.md", + "hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac", "is_verified": false, - "line_number": 465 - }, + "line_number": 26 + } + ], + "docs/channels/bluebubbles.md": [ { "type": "Secret Keyword", - "filename": "docs/configuration.md", - "hashed_secret": "22af290a1a3d5e941193a41a3d3a9e4ca8da5e27", + "filename": "docs/channels/bluebubbles.md", + "hashed_secret": "555da20df20d4172e00f1b73d7c3943802055270", "is_verified": false, - "line_number": 718 - }, + "line_number": 32 + } + ], + "docs/channels/matrix.md": [ { "type": "Secret Keyword", - "filename": "docs/configuration.md", - "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", - "is_verified": false, - "line_number": 760 - }, - { - "type": "Secret Keyword", - "filename": "docs/configuration.md", - "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd", - "is_verified": false, - "line_number": 859 - }, - { - "type": "Secret Keyword", - "filename": "docs/configuration.md", + "filename": "docs/channels/matrix.md", "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6", "is_verified": false, - "line_number": 982 + "line_number": 58 } ], - "docs/faq.md": [ + "docs/channels/nextcloud-talk.md": [ { "type": "Secret Keyword", - "filename": "docs/faq.md", - "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "filename": "docs/channels/nextcloud-talk.md", + "hashed_secret": "76ed0a056aa77060de25754586440cff390791d0", "is_verified": false, - "line_number": 593 + "line_number": 47 + } + ], + "docs/channels/nostr.md": [ + { + "type": "Secret Keyword", + "filename": "docs/channels/nostr.md", + "hashed_secret": "edeb23e25a619c434d22bb7f1c3ca4841166b4e8", + "is_verified": false, + "line_number": 65 + } + ], + "docs/channels/slack.md": [ + { + "type": "Secret Keyword", + "filename": "docs/channels/slack.md", + "hashed_secret": "3f4800fb7c1fb79a9a48bfd562d90bc6b2e2b718", + "is_verified": false, + "line_number": 141 + } + ], + "docs/concepts/memory.md": [ + { + "type": "Secret Keyword", + "filename": "docs/concepts/memory.md", + "hashed_secret": "39d711243bfcee9fec8299b204e1aa9c3430fa12", + "is_verified": false, + "line_number": 108 }, { "type": "Secret Keyword", - "filename": "docs/faq.md", + "filename": "docs/concepts/memory.md", + "hashed_secret": "1a8abbf465c52363ab4c9c6ad945b8e857cbea55", + "is_verified": false, + "line_number": 131 + }, + { + "type": "Secret Keyword", + "filename": "docs/concepts/memory.md", + "hashed_secret": "b9f640d6095b9f6b5a65983f7b76dbbb254e0044", + "is_verified": false, + "line_number": 373 + } + ], + "docs/concepts/model-providers.md": [ + { + "type": "Secret Keyword", + "filename": "docs/concepts/model-providers.md", "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", "is_verified": false, - "line_number": 650 - } - ], - "docs/skills-config.md": [ + "line_number": 168 + }, { "type": "Secret Keyword", - "filename": "docs/skills-config.md", + "filename": "docs/concepts/model-providers.md", + "hashed_secret": "ef83ad68b9b66e008727b7c417c6a8f618b5177e", + "is_verified": false, + "line_number": 255 + } + ], + "docs/environment.md": [ + { + "type": "Secret Keyword", + "filename": "docs/environment.md", + "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "is_verified": false, + "line_number": 29 + }, + { + "type": "Secret Keyword", + "filename": "docs/environment.md", + "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25", + "is_verified": false, + "line_number": 31 + } + ], + "docs/gateway/configuration-examples.md": [ + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration-examples.md", + "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "is_verified": false, + "line_number": 53 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration-examples.md", + "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25", + "is_verified": false, + "line_number": 55 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration-examples.md", + "hashed_secret": "22af290a1a3d5e941193a41a3d3a9e4ca8da5e27", + "is_verified": false, + "line_number": 319 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration-examples.md", "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd", "is_verified": false, - "line_number": 28 - } - ], - "docs/skills.md": [ + "line_number": 414 + }, { "type": "Secret Keyword", - "filename": "docs/skills.md", + "filename": "docs/gateway/configuration-examples.md", + "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", + "is_verified": false, + "line_number": 548 + } + ], + "docs/gateway/configuration.md": [ + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "is_verified": false, + "line_number": 272 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25", + "is_verified": false, + "line_number": 274 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 1029 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e", + "is_verified": false, + "line_number": 1470 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "bde4db9b4c3be4049adc3b9a69851d7c35119770", + "is_verified": false, + "line_number": 1486 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "22af290a1a3d5e941193a41a3d3a9e4ca8da5e27", + "is_verified": false, + "line_number": 2268 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 2344 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd", "is_verified": false, - "line_number": 97 - } - ], - "docs/tailscale.md": [ + "line_number": 2658 + }, { "type": "Secret Keyword", - "filename": "docs/tailscale.md", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6", + "is_verified": false, + "line_number": 2844 + } + ], + "docs/gateway/local-models.md": [ + { + "type": "Secret Keyword", + "filename": "docs/gateway/local-models.md", + "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", + "is_verified": false, + "line_number": 32 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/local-models.md", + "hashed_secret": "49fd535e63175a827aab3eff9ac58a9e82460ac9", + "is_verified": false, + "line_number": 121 + } + ], + "docs/gateway/tailscale.md": [ + { + "type": "Secret Keyword", + "filename": "docs/gateway/tailscale.md", "hashed_secret": "9cb0dc5383312aa15b9dc6745645bde18ff5ade9", "is_verified": false, - "line_number": 52 + "line_number": 75 } ], - "docs/talk.md": [ + "docs/help/faq.md": [ { "type": "Secret Keyword", - "filename": "docs/talk.md", + "filename": "docs/help/faq.md", + "hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac", + "is_verified": false, + "line_number": 925 + }, + { + "type": "Secret Keyword", + "filename": "docs/help/faq.md", + "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "is_verified": false, + "line_number": 1113 + }, + { + "type": "Secret Keyword", + "filename": "docs/help/faq.md", + "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25", + "is_verified": false, + "line_number": 1114 + }, + { + "type": "Secret Keyword", + "filename": "docs/help/faq.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 1439 + }, + { + "type": "Secret Keyword", + "filename": "docs/help/faq.md", + "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6", + "is_verified": false, + "line_number": 1715 + } + ], + "docs/nodes/talk.md": [ + { + "type": "Secret Keyword", + "filename": "docs/nodes/talk.md", "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e", "is_verified": false, "line_number": 50 } ], - "docs/telegram.md": [ + "docs/perplexity.md": [ { "type": "Secret Keyword", - "filename": "docs/telegram.md", - "hashed_secret": "e9fe51f94eadabf54dbf2fbbd57188b9abee436e", + "filename": "docs/perplexity.md", + "hashed_secret": "6b26c117c66a0c030e239eef595c1e18865132a8", "is_verified": false, - "line_number": 57 + "line_number": 35 + } + ], + "docs/providers/anthropic.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/anthropic.md", + "hashed_secret": "c7a8c334eef5d1749fface7d42c66f9ae5e8cf36", + "is_verified": false, + "line_number": 32 + } + ], + "docs/providers/glm.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/glm.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 22 + } + ], + "docs/providers/minimax.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/minimax.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 49 + }, + { + "type": "Secret Keyword", + "filename": "docs/providers/minimax.md", + "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", + "is_verified": false, + "line_number": 118 + } + ], + "docs/providers/moonshot.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/moonshot.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 39 + } + ], + "docs/providers/openai.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/openai.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 31 + } + ], + "docs/providers/opencode.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/opencode.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 25 + } + ], + "docs/providers/openrouter.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/openrouter.md", + "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "is_verified": false, + "line_number": 22 + } + ], + "docs/providers/synthetic.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/synthetic.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 31 + } + ], + "docs/providers/zai.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/zai.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 25 + } + ], + "docs/tools/browser.md": [ + { + "type": "Basic Auth Credentials", + "filename": "docs/tools/browser.md", + "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", + "is_verified": false, + "line_number": 163 + } + ], + "docs/tools/firecrawl.md": [ + { + "type": "Secret Keyword", + "filename": "docs/tools/firecrawl.md", + "hashed_secret": "674397e2c0c2faaa85961c708d2a96a7cc7af217", + "is_verified": false, + "line_number": 28 + } + ], + "docs/tools/skills-config.md": [ + { + "type": "Secret Keyword", + "filename": "docs/tools/skills-config.md", + "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd", + "is_verified": false, + "line_number": 30 + } + ], + "docs/tools/skills.md": [ + { + "type": "Secret Keyword", + "filename": "docs/tools/skills.md", + "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd", + "is_verified": false, + "line_number": 160 + } + ], + "docs/tools/web.md": [ + { + "type": "Secret Keyword", + "filename": "docs/tools/web.md", + "hashed_secret": "6b26c117c66a0c030e239eef595c1e18865132a8", + "is_verified": false, + "line_number": 61 + }, + { + "type": "Secret Keyword", + "filename": "docs/tools/web.md", + "hashed_secret": "96c682c88ed551f22fe76d206c2dfb7df9221ad9", + "is_verified": false, + "line_number": 112 + }, + { + "type": "Secret Keyword", + "filename": "docs/tools/web.md", + "hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac", + "is_verified": false, + "line_number": 160 + }, + { + "type": "Secret Keyword", + "filename": "docs/tools/web.md", + "hashed_secret": "674397e2c0c2faaa85961c708d2a96a7cc7af217", + "is_verified": false, + "line_number": 223 + } + ], + "docs/tts.md": [ + { + "type": "Secret Keyword", + "filename": "docs/tts.md", + "hashed_secret": "bde4db9b4c3be4049adc3b9a69851d7c35119770", + "is_verified": false, + "line_number": 72 + }, + { + "type": "Secret Keyword", + "filename": "docs/tts.md", + "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e", + "is_verified": false, + "line_number": 77 + } + ], + "extensions/bluebubbles/src/actions.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/actions.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 73 + } + ], + "extensions/bluebubbles/src/attachments.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/attachments.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 35 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/attachments.test.ts", + "hashed_secret": "db1530e1ea43af094d3d75b8dbaf19a4a182a318", + "is_verified": false, + "line_number": 99 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/attachments.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 117 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/attachments.test.ts", + "hashed_secret": "052f076c732648ab32d2fcde9fe255319bfa0c7b", + "is_verified": false, + "line_number": 229 + } + ], + "extensions/bluebubbles/src/chat.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 33 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 68 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "5c5a15a8b0b3e154d77746945e563ba40100681b", + "is_verified": false, + "line_number": 85 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "faacad0ce4ea1c19b46e128fd79679d37d3d331d", + "is_verified": false, + "line_number": 134 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "4dcc26a1d99532846fedf1265df4f40f4e0005b8", + "is_verified": false, + "line_number": 219 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "fd2a721f7be1ee3d691a011affcdb11d0ca365a8", + "is_verified": false, + "line_number": 282 + } + ], + "extensions/bluebubbles/src/monitor.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/monitor.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 187 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/monitor.test.ts", + "hashed_secret": "1ae0af3fe72b3ba394f9fa95a6cffc090d726c23", + "is_verified": false, + "line_number": 394 + } + ], + "extensions/bluebubbles/src/reactions.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/reactions.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 38 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/reactions.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 179 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/reactions.test.ts", + "hashed_secret": "a4a05c9a6449eb9d6cdac81dd7edc49230e327e6", + "is_verified": false, + "line_number": 210 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/reactions.test.ts", + "hashed_secret": "a2833da9f0a16f09994754d0a31749cecf8c8c77", + "is_verified": false, + "line_number": 316 + } + ], + "extensions/bluebubbles/src/send.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/send.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 38 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/send.test.ts", + "hashed_secret": "faacad0ce4ea1c19b46e128fd79679d37d3d331d", + "is_verified": false, + "line_number": 675 + } + ], + "extensions/bluebubbles/src/targets.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/bluebubbles/src/targets.test.ts", + "hashed_secret": "a3af2fb0c1e2a30bb038049e1e4b401593af6225", + "is_verified": false, + "line_number": 62 + } + ], + "extensions/bluebubbles/src/targets.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/bluebubbles/src/targets.ts", + "hashed_secret": "a3af2fb0c1e2a30bb038049e1e4b401593af6225", + "is_verified": false, + "line_number": 214 + } + ], + "extensions/copilot-proxy/index.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/copilot-proxy/index.ts", + "hashed_secret": "50f013532a9770a2c2cfdc38b7581dd01df69b70", + "is_verified": false, + "line_number": 4 + } + ], + "extensions/google-antigravity-auth/index.ts": [ + { + "type": "Base64 High Entropy String", + "filename": "extensions/google-antigravity-auth/index.ts", + "hashed_secret": "709d0f232b6ac4f8d24dec3e4fabfdb14257174f", + "is_verified": false, + "line_number": 9 + } + ], + "extensions/matrix/src/matrix/accounts.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/matrix/src/matrix/accounts.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 75 + } + ], + "extensions/matrix/src/matrix/client.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/matrix/src/matrix/client.test.ts", + "hashed_secret": "fe7fcdaea49ece14677acd32374d2f1225819d5c", + "is_verified": false, + "line_number": 14 + }, + { + "type": "Secret Keyword", + "filename": "extensions/matrix/src/matrix/client.test.ts", + "hashed_secret": "3dc927d80543dc0f643940b70d066bd4b4c4b78e", + "is_verified": false, + "line_number": 24 + } + ], + "extensions/matrix/src/matrix/client/storage.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/matrix/src/matrix/client/storage.ts", + "hashed_secret": "7505d64a54e061b7acd54ccd58b49dc43500b635", + "is_verified": false, + "line_number": 9 + } + ], + "extensions/memory-lancedb/config.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/memory-lancedb/config.ts", + "hashed_secret": "ecb252044b5ea0f679ee78ec1a12904739e2904d", + "is_verified": false, + "line_number": 70 + } + ], + "extensions/memory-lancedb/index.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/memory-lancedb/index.test.ts", + "hashed_secret": "ed65c049bb2f78ee4f703b2158ba9cc6ea31fb7e", + "is_verified": false, + "line_number": 70 + } + ], + "extensions/msteams/src/probe.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/msteams/src/probe.test.ts", + "hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073", + "is_verified": false, + "line_number": 34 + } + ], + "extensions/nextcloud-talk/src/accounts.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/nextcloud-talk/src/accounts.ts", + "hashed_secret": "920f8f5815b381ea692e9e7c2f7119f2b1aa620a", + "is_verified": false, + "line_number": 26 + }, + { + "type": "Secret Keyword", + "filename": "extensions/nextcloud-talk/src/accounts.ts", + "hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb", + "is_verified": false, + "line_number": 139 + } + ], + "extensions/nextcloud-talk/src/channel.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/nextcloud-talk/src/channel.ts", + "hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb", + "is_verified": false, + "line_number": 390 + } + ], + "extensions/nostr/README.md": [ + { + "type": "Secret Keyword", + "filename": "extensions/nostr/README.md", + "hashed_secret": "edeb23e25a619c434d22bb7f1c3ca4841166b4e8", + "is_verified": false, + "line_number": 43 + } + ], + "extensions/nostr/src/channel.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/channel.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 48 + }, + { + "type": "Secret Keyword", + "filename": "extensions/nostr/src/channel.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 48 + } + ], + "extensions/nostr/src/nostr-bus.fuzz.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.fuzz.test.ts", + "hashed_secret": "2b4489606a23fb31fcdc849fa7e577ba90f6d39a", + "is_verified": false, + "line_number": 202 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.fuzz.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 203 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.fuzz.test.ts", + "hashed_secret": "b84cb0c3925d34496e6c8b0e55b8c1664a438035", + "is_verified": false, + "line_number": 208 + } + ], + "extensions/nostr/src/nostr-bus.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 11 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.test.ts", + "hashed_secret": "7258e28563f03fb4c5994e8402e6f610d1f0f110", + "is_verified": false, + "line_number": 33 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.test.ts", + "hashed_secret": "2b4489606a23fb31fcdc849fa7e577ba90f6d39a", + "is_verified": false, + "line_number": 101 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.test.ts", + "hashed_secret": "ef717286343f6da3f4e6f68c6de02a5148a801c4", + "is_verified": false, + "line_number": 106 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.test.ts", + "hashed_secret": "98b35fe4c45011220f509ebb5546d3889b55a891", + "is_verified": false, + "line_number": 111 + } + ], + "extensions/nostr/src/nostr-profile.fuzz.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-profile.fuzz.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 12 + } + ], + "extensions/nostr/src/nostr-profile.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-profile.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 14 + } + ], + "extensions/nostr/src/types.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/types.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 8 + }, + { + "type": "Secret Keyword", + "filename": "extensions/nostr/src/types.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 8 + }, + { + "type": "Secret Keyword", + "filename": "extensions/nostr/src/types.test.ts", + "hashed_secret": "3bee216ebc256d692260fc3adc765050508fef5e", + "is_verified": false, + "line_number": 127 + } + ], + "extensions/open-prose/skills/prose/SKILL.md": [ + { + "type": "Basic Auth Credentials", + "filename": "extensions/open-prose/skills/prose/SKILL.md", + "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", + "is_verified": false, + "line_number": 200 + } + ], + "extensions/open-prose/skills/prose/state/postgres.md": [ + { + "type": "Secret Keyword", + "filename": "extensions/open-prose/skills/prose/state/postgres.md", + "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", + "is_verified": false, + "line_number": 75 + }, + { + "type": "Basic Auth Credentials", + "filename": "extensions/open-prose/skills/prose/state/postgres.md", + "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", + "is_verified": false, + "line_number": 198 + } + ], + "extensions/zalo/README.md": [ + { + "type": "Secret Keyword", + "filename": "extensions/zalo/README.md", + "hashed_secret": "f51aaee16a4a756d287f126b99c081b73cba7f15", + "is_verified": false, + "line_number": 41 + } + ], + "extensions/zalo/src/monitor.webhook.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/zalo/src/monitor.webhook.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 43 + } + ], + "skills/1password/references/cli-examples.md": [ + { + "type": "Secret Keyword", + "filename": "skills/1password/references/cli-examples.md", + "hashed_secret": "9dda0987cc3054773a2df97e352d4f64d233ef10", + "is_verified": false, + "line_number": 17 } ], "skills/local-places/SERVER_README.md": [ @@ -344,50 +1163,395 @@ "line_number": 18 } ], - "src/agents/models-config.test.ts": [ + "src/agents/memory-search.test.ts": [ { "type": "Secret Keyword", - "filename": "src/agents/models-config.test.ts", - "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "filename": "src/agents/memory-search.test.ts", + "hashed_secret": "a1b49d68a91fdf9c9217773f3fac988d77fa0f50", "is_verified": false, - "line_number": 25 - }, - { - "type": "Secret Keyword", - "filename": "src/agents/models-config.test.ts", - "hashed_secret": "3a81eb091f80c845232225be5663d270e90dacb7", - "is_verified": false, - "line_number": 90 + "line_number": 164 } ], - "src/agents/skills.test.ts": [ + "src/agents/model-auth.test.ts": [ { "type": "Secret Keyword", - "filename": "src/agents/skills.test.ts", - "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "filename": "src/agents/model-auth.test.ts", + "hashed_secret": "07a6b9cec637c806195e8aa7e5c0851ab03dc35e", "is_verified": false, - "line_number": 158 + "line_number": 211 }, { "type": "Secret Keyword", - "filename": "src/agents/skills.test.ts", - "hashed_secret": "7a85f4764bbd6daf1c3545efbbf0f279a6dc0beb", + "filename": "src/agents/model-auth.test.ts", + "hashed_secret": "21f296583ccd80c5ab9b3330a8b0d47e4a409fb9", "is_verified": false, - "line_number": 265 + "line_number": 240 }, { "type": "Secret Keyword", - "filename": "src/agents/skills.test.ts", + "filename": "src/agents/model-auth.test.ts", + "hashed_secret": "77e991e9f56e6fa4ed1a908208048421f1214c07", + "is_verified": false, + "line_number": 264 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/model-auth.test.ts", + "hashed_secret": "dff6d4ff5dc357cf451d1855ab9cbda562645c9f", + "is_verified": false, + "line_number": 295 + } + ], + "src/agents/model-auth.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/model-auth.ts", + "hashed_secret": "8956265d216d474a080edaa97880d37fc1386f33", + "is_verified": false, + "line_number": 22 + } + ], + "src/agents/models-config.auto-injects-github-copilot-provider-token-is.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.auto-injects-github-copilot-provider-token-is.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + } + ], + "src/agents/models-config.falls-back-default-baseurl-token-exchange-fails.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.falls-back-default-baseurl-token-exchange-fails.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + } + ], + "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts", + "hashed_secret": "fcdd655b11f33ba4327695084a347b2ba192976c", + "is_verified": false, + "line_number": 50 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts", + "hashed_secret": "3a81eb091f80c845232225be5663d270e90dacb7", + "is_verified": false, + "line_number": 108 + } + ], + "src/agents/models-config.normalizes-gemini-3-ids-preview-google-providers.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.normalizes-gemini-3-ids-preview-google-providers.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.normalizes-gemini-3-ids-preview-google-providers.test.ts", + "hashed_secret": "980d02eb9335ae7c9e9984f6c8ad432352a0d2ac", + "is_verified": false, + "line_number": 57 + } + ], + "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts", + "hashed_secret": "fcdd655b11f33ba4327695084a347b2ba192976c", + "is_verified": false, + "line_number": 112 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts", + "hashed_secret": "94c4be5a1976115e8152960c21e04400a4fccdf6", + "is_verified": false, + "line_number": 146 + } + ], + "src/agents/models-config.uses-first-github-copilot-profile-env-tokens.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.uses-first-github-copilot-profile-env-tokens.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + } + ], + "src/agents/openai-responses.reasoning-replay.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/openai-responses.reasoning-replay.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 124 + } + ], + "src/agents/pi-embedded-runner.applygoogleturnorderingfix.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.applygoogleturnorderingfix.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 58 + } + ], + "src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 57 + } + ], + "src/agents/pi-embedded-runner.createsystempromptoverride.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.createsystempromptoverride.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 56 + } + ], + "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.falls-back-provider-default-per-dm-not.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.falls-back-provider-default-per-dm-not.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 56 + } + ], + "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.returns-undefined-sessionkey-is-undefined.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.returns-undefined-sessionkey-is-undefined.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 56 + } + ], + "src/agents/pi-embedded-runner.limithistoryturns.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.limithistoryturns.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 57 + } + ], + "src/agents/pi-embedded-runner.resolvesessionagentids.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.resolvesessionagentids.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 56 + } + ], + "src/agents/pi-embedded-runner.splitsdktools.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.splitsdktools.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 57 + } + ], + "src/agents/pi-embedded-runner.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 117 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.test.ts", + "hashed_secret": "fcdd655b11f33ba4327695084a347b2ba192976c", + "is_verified": false, + "line_number": 178 + } + ], + "src/agents/skills.applyskillenvoverrides.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/skills.applyskillenvoverrides.test.ts", "hashed_secret": "5df3a673d724e8a1eb673a8baf623e183940804d", "is_verified": false, - "line_number": 462 + "line_number": 54 }, { "type": "Secret Keyword", - "filename": "src/agents/skills.test.ts", + "filename": "src/agents/skills.applyskillenvoverrides.test.ts", "hashed_secret": "8921daaa546693e52bc1f9c40bdcf15e816e0448", "is_verified": false, - "line_number": 490 + "line_number": 80 + } + ], + "src/agents/skills.build-workspace-skills-prompt.prefers-workspace-skills-managed-skills.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/skills.build-workspace-skills-prompt.prefers-workspace-skills-managed-skills.test.ts", + "hashed_secret": "7a85f4764bbd6daf1c3545efbbf0f279a6dc0beb", + "is_verified": false, + "line_number": 124 + } + ], + "src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 102 + } + ], + "src/agents/tools/web-fetch.ssrf.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-fetch.ssrf.test.ts", + "hashed_secret": "5ce8e9d54c77266fff990194d2219a708c59b76c", + "is_verified": false, + "line_number": 55 + } + ], + "src/agents/tools/web-search.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-search.ts", + "hashed_secret": "dfba7aade0868074c2861c98e2a9a92f3178a51b", + "is_verified": false, + "line_number": 85 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-search.ts", + "hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb", + "is_verified": false, + "line_number": 190 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-search.ts", + "hashed_secret": "c4865ff9250aca23b0d98eb079dad70ebec1cced", + "is_verified": false, + "line_number": 198 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-search.ts", + "hashed_secret": "527ee41f36386e85fa932ef09471ca017f3c95c8", + "is_verified": false, + "line_number": 199 + } + ], + "src/agents/tools/web-tools.enabled-defaults.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-tools.enabled-defaults.test.ts", + "hashed_secret": "47b249a75ca78fdb578d0f28c33685e27ea82684", + "is_verified": false, + "line_number": 213 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-tools.enabled-defaults.test.ts", + "hashed_secret": "d0ffd81d6d7ad1bc3c365660fe8882480c9a986e", + "is_verified": false, + "line_number": 242 + } + ], + "src/agents/tools/web-tools.fetch.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-tools.fetch.test.ts", + "hashed_secret": "5ce8e9d54c77266fff990194d2219a708c59b76c", + "is_verified": false, + "line_number": 101 + } + ], + "src/auto-reply/reply.directive.directive-behavior.prefers-alias-matches-fuzzy-selection-is-ambiguous.e2e.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/auto-reply/reply.directive.directive-behavior.prefers-alias-matches-fuzzy-selection-is-ambiguous.e2e.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 90 + }, + { + "type": "Secret Keyword", + "filename": "src/auto-reply/reply.directive.directive-behavior.prefers-alias-matches-fuzzy-selection-is-ambiguous.e2e.test.ts", + "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", + "is_verified": false, + "line_number": 96 + } + ], + "src/auto-reply/reply.directive.directive-behavior.supports-fuzzy-model-matches-model-directive.e2e.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/auto-reply/reply.directive.directive-behavior.supports-fuzzy-model-matches-model-directive.e2e.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 87 + }, + { + "type": "Secret Keyword", + "filename": "src/auto-reply/reply.directive.directive-behavior.supports-fuzzy-model-matches-model-directive.e2e.test.ts", + "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", + "is_verified": false, + "line_number": 228 + } + ], + "src/auto-reply/status.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/auto-reply/status.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 20 + } + ], + "src/browser/cdp.helpers.test.ts": [ + { + "type": "Basic Auth Credentials", + "filename": "src/browser/cdp.helpers.test.ts", + "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", + "is_verified": false, + "line_number": 22 + } + ], + "src/browser/cdp.test.ts": [ + { + "type": "Basic Auth Credentials", + "filename": "src/browser/cdp.test.ts", + "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", + "is_verified": false, + "line_number": 172 } ], "src/browser/target-id.test.ts": [ @@ -396,57 +1560,386 @@ "filename": "src/browser/target-id.test.ts", "hashed_secret": "4e126c049580d66ca1549fa534d95a7263f27f46", "is_verified": false, - "line_number": 16 + "line_number": 13 } ], - "src/commands/antigravity-oauth.ts": [ + "src/cli/update-cli.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/cli/update-cli.test.ts", + "hashed_secret": "e4f91dd323bac5bfc4f60a6e433787671dc2421d", + "is_verified": false, + "line_number": 112 + } + ], + "src/commands/auth-choice.preferred-provider.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/auth-choice.preferred-provider.ts", + "hashed_secret": "c03a8d10174dd7eb2b3288b570a5a74fdd9ae05d", + "is_verified": false, + "line_number": 8 + } + ], + "src/commands/auth-choice.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/auth-choice.test.ts", + "hashed_secret": "2480500ff391183070fe22ba8665a8be19350833", + "is_verified": false, + "line_number": 289 + }, + { + "type": "Secret Keyword", + "filename": "src/commands/auth-choice.test.ts", + "hashed_secret": "77e991e9f56e6fa4ed1a908208048421f1214c07", + "is_verified": false, + "line_number": 350 + }, + { + "type": "Secret Keyword", + "filename": "src/commands/auth-choice.test.ts", + "hashed_secret": "1b4d8423b11d32dd0c466428ac81de84a4a9442b", + "is_verified": false, + "line_number": 528 + } + ], + "src/commands/configure.gateway-auth.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/configure.gateway-auth.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 8 + } + ], + "src/commands/models/list.status.test.ts": [ { "type": "Base64 High Entropy String", - "filename": "src/commands/antigravity-oauth.ts", - "hashed_secret": "709d0f232b6ac4f8d24dec3e4fabfdb14257174f", + "filename": "src/commands/models/list.status.test.ts", + "hashed_secret": "d6ae2508a78a232d5378ef24b85ce40cbb4d7ff0", "is_verified": false, - "line_number": 17 + "line_number": 11 }, { "type": "Base64 High Entropy String", - "filename": "src/commands/antigravity-oauth.ts", - "hashed_secret": "3848603b8e866f62d07c206ff622279b9dcb0238", + "filename": "src/commands/models/list.status.test.ts", + "hashed_secret": "2d8012102440ea97852b3152239218f00579bafa", "is_verified": false, - "line_number": 20 - } - ], - "src/commands/onboard-auth.ts": [ + "line_number": 18 + }, + { + "type": "Base64 High Entropy String", + "filename": "src/commands/models/list.status.test.ts", + "hashed_secret": "51848e2be4b461a549218d3167f19c01be6b98b8", + "is_verified": false, + "line_number": 46 + }, { "type": "Secret Keyword", - "filename": "src/commands/onboard-auth.ts", + "filename": "src/commands/models/list.status.test.ts", + "hashed_secret": "51848e2be4b461a549218d3167f19c01be6b98b8", + "is_verified": false, + "line_number": 46 + }, + { + "type": "Secret Keyword", + "filename": "src/commands/models/list.status.test.ts", + "hashed_secret": "1c1e381bfb72d3b7bfca9437053d9875356680f0", + "is_verified": false, + "line_number": 52 + } + ], + "src/commands/onboard-auth.config-minimax.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/onboard-auth.config-minimax.ts", "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", "is_verified": false, + "line_number": 30 + }, + { + "type": "Secret Keyword", + "filename": "src/commands/onboard-auth.config-minimax.ts", + "hashed_secret": "ddcb713196b974770575a9bea5a4e7d46361f8e9", + "is_verified": false, + "line_number": 85 + } + ], + "src/commands/onboard-auth.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/onboard-auth.test.ts", + "hashed_secret": "666c100dab549a6f56da7da546bd848ed5086541", + "is_verified": false, + "line_number": 230 + }, + { + "type": "Secret Keyword", + "filename": "src/commands/onboard-auth.test.ts", + "hashed_secret": "e184b402822abc549b37689c84e8e0e33c39a1f1", + "is_verified": false, + "line_number": 262 + } + ], + "src/commands/onboard-non-interactive.ai-gateway.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/onboard-non-interactive.ai-gateway.test.ts", + "hashed_secret": "77e991e9f56e6fa4ed1a908208048421f1214c07", + "is_verified": false, "line_number": 50 } ], - "src/config/config.test.ts": [ + "src/commands/onboard-non-interactive/api-keys.ts": [ { "type": "Secret Keyword", - "filename": "src/config/config.test.ts", - "hashed_secret": "bea2f7b64fab8d1d414d0449530b1e088d36d5b1", + "filename": "src/commands/onboard-non-interactive/api-keys.ts", + "hashed_secret": "112f3a99b283a4e1788dedd8e0e5d35375c33747", "is_verified": false, - "line_number": 520 + "line_number": 10 } ], - "src/gateway/server.auth.test.ts": [ + "src/config/config.env-vars.test.ts": [ { "type": "Secret Keyword", - "filename": "src/gateway/server.auth.test.ts", - "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "filename": "src/config/config.env-vars.test.ts", + "hashed_secret": "a24ef9c1a27cac44823571ceef2e8262718eee36", "is_verified": false, - "line_number": 89 + "line_number": 15 }, { "type": "Secret Keyword", - "filename": "src/gateway/server.auth.test.ts", + "filename": "src/config/config.env-vars.test.ts", + "hashed_secret": "29d5f92e9ee44d4854d6dfaeefc3dc27d779fdf3", + "is_verified": false, + "line_number": 47 + }, + { + "type": "Secret Keyword", + "filename": "src/config/config.env-vars.test.ts", + "hashed_secret": "1672b6a1e7956c6a70f45d699aa42a351b1f8b80", + "is_verified": false, + "line_number": 63 + } + ], + "src/config/config.talk-api-key-fallback.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/config/config.talk-api-key-fallback.test.ts", + "hashed_secret": "bea2f7b64fab8d1d414d0449530b1e088d36d5b1", + "is_verified": false, + "line_number": 42 + } + ], + "src/config/config.web-search-provider.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/config/config.web-search-provider.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 14 + } + ], + "src/config/env-substitution.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/config/env-substitution.test.ts", + "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", + "is_verified": false, + "line_number": 38 + }, + { + "type": "Secret Keyword", + "filename": "src/config/env-substitution.test.ts", + "hashed_secret": "ec417f567082612f8fd6afafe1abcab831fca840", + "is_verified": false, + "line_number": 69 + }, + { + "type": "Secret Keyword", + "filename": "src/config/env-substitution.test.ts", + "hashed_secret": "520bd69c3eb1646d9a78181ecb4c90c51fdf428d", + "is_verified": false, + "line_number": 70 + }, + { + "type": "Secret Keyword", + "filename": "src/config/env-substitution.test.ts", + "hashed_secret": "f136444bf9b3d01a9f9b772b80ac6bf7b6a43ef0", + "is_verified": false, + "line_number": 228 + } + ], + "src/config/schema.ts": [ + { + "type": "Secret Keyword", + "filename": "src/config/schema.ts", + "hashed_secret": "e73c9fcad85cd4eecc74181ec4bdb31064d68439", + "is_verified": false, + "line_number": 184 + }, + { + "type": "Secret Keyword", + "filename": "src/config/schema.ts", + "hashed_secret": "2eda7cd978f39eebec3bf03e4410a40e14167fff", + "is_verified": false, + "line_number": 220 + }, + { + "type": "Secret Keyword", + "filename": "src/config/schema.ts", + "hashed_secret": "9f4cda226d3868676ac7f86f59e4190eb94bd208", + "is_verified": false, + "line_number": 418 + }, + { + "type": "Secret Keyword", + "filename": "src/config/schema.ts", + "hashed_secret": "01822c8bbf6a8b136944b14182cb885100ec2eae", + "is_verified": false, + "line_number": 437 + }, + { + "type": "Secret Keyword", + "filename": "src/config/schema.ts", + "hashed_secret": "bb7dfd9746e660e4a4374951ec5938ef0e343255", + "is_verified": false, + "line_number": 487 + } + ], + "src/config/slack-http-config.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/config/slack-http-config.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 11 + } + ], + "src/gateway/auth.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/gateway/auth.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 43 + }, + { + "type": "Secret Keyword", + "filename": "src/gateway/auth.test.ts", "hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c", "is_verified": false, - "line_number": 109 + "line_number": 51 + } + ], + "src/gateway/call.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/gateway/call.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 285 + }, + { + "type": "Secret Keyword", + "filename": "src/gateway/call.test.ts", + "hashed_secret": "e493f561d90c6638c1f51c5a8a069c3b129b79ed", + "is_verified": false, + "line_number": 295 + }, + { + "type": "Secret Keyword", + "filename": "src/gateway/call.test.ts", + "hashed_secret": "2e07956ffc9bc4fd624064c40b7495c85d5f1467", + "is_verified": false, + "line_number": 300 + }, + { + "type": "Secret Keyword", + "filename": "src/gateway/call.test.ts", + "hashed_secret": "bddc29032de580fb53b3a9a0357dd409086db800", + "is_verified": false, + "line_number": 313 + } + ], + "src/gateway/client.test.ts": [ + { + "type": "Private Key", + "filename": "src/gateway/client.test.ts", + "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", + "is_verified": false, + "line_number": 83 + } + ], + "src/gateway/gateway-cli-backend.live.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/gateway/gateway-cli-backend.live.test.ts", + "hashed_secret": "3e2fd4a90d5afbd27974730c4d6a9592fe300825", + "is_verified": false, + "line_number": 38 + } + ], + "src/gateway/gateway-models.profiles.live.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/gateway/gateway-models.profiles.live.test.ts", + "hashed_secret": "3e2fd4a90d5afbd27974730c4d6a9592fe300825", + "is_verified": false, + "line_number": 219 + } + ], + "src/gateway/gateway.e2e.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/gateway/gateway.e2e.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 73 + } + ], + "src/gateway/server.auth.e2e.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/gateway/server.auth.e2e.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 179 + }, + { + "type": "Secret Keyword", + "filename": "src/gateway/server.auth.e2e.test.ts", + "hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c", + "is_verified": false, + "line_number": 197 + } + ], + "src/gateway/session-utils.test.ts": [ + { + "type": "Base64 High Entropy String", + "filename": "src/gateway/session-utils.test.ts", + "hashed_secret": "bb9a5d9483409d2c60b28268a0efcb93324d4cda", + "is_verified": false, + "line_number": 156 + } + ], + "src/gateway/tools-invoke-http.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/gateway/tools-invoke-http.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 56 + } + ], + "src/gateway/ws-log.test.ts": [ + { + "type": "Base64 High Entropy String", + "filename": "src/gateway/ws-log.test.ts", + "hashed_secret": "edd2e7ac4f61d0c606e80a0919d727540842a307", + "is_verified": false, + "line_number": 22 } ], "src/infra/env.test.ts": [ @@ -465,34 +1958,214 @@ "line_number": 25 } ], + "src/infra/outbound/message-action-runner.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/infra/outbound/message-action-runner.test.ts", + "hashed_secret": "804ec071803318791b835cffd6e509c8d32239db", + "is_verified": false, + "line_number": 88 + }, + { + "type": "Secret Keyword", + "filename": "src/infra/outbound/message-action-runner.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 385 + } + ], + "src/infra/outbound/outbound-policy.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/infra/outbound/outbound-policy.test.ts", + "hashed_secret": "804ec071803318791b835cffd6e509c8d32239db", + "is_verified": false, + "line_number": 33 + } + ], "src/infra/shell-env.test.ts": [ { "type": "Secret Keyword", "filename": "src/infra/shell-env.test.ts", "hashed_secret": "65c10dc3549fe07424148a8a4790a3341ecbc253", "is_verified": false, - "line_number": 35 - }, - { - "type": "Base64 High Entropy String", - "filename": "src/infra/shell-env.test.ts", - "hashed_secret": "64db6bf7f0e5a0491df4419f0eb1bbcc402989e8", - "is_verified": false, - "line_number": 56 + "line_number": 27 }, { "type": "Secret Keyword", "filename": "src/infra/shell-env.test.ts", "hashed_secret": "e013ffda590d2178607c16d11b1ea42f75ceb0e7", "is_verified": false, - "line_number": 73 + "line_number": 59 }, { "type": "Base64 High Entropy String", "filename": "src/infra/shell-env.test.ts", "hashed_secret": "be6ee9a6bf9f2dad84a5a67d6c0576a5bacc391e", "is_verified": false, - "line_number": 75 + "line_number": 61 + } + ], + "src/logging/redact.test.ts": [ + { + "type": "Base64 High Entropy String", + "filename": "src/logging/redact.test.ts", + "hashed_secret": "dd7754662b89333191ff45e8257a3e6d3fcd3990", + "is_verified": false, + "line_number": 9 + }, + { + "type": "Private Key", + "filename": "src/logging/redact.test.ts", + "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", + "is_verified": false, + "line_number": 64 + }, + { + "type": "Hex High Entropy String", + "filename": "src/logging/redact.test.ts", + "hashed_secret": "7992945213f7d76889fa83ff0f2be352409c837e", + "is_verified": false, + "line_number": 65 + }, + { + "type": "Base64 High Entropy String", + "filename": "src/logging/redact.test.ts", + "hashed_secret": "063995ecb4fa5afe2460397d322925cd867b7d74", + "is_verified": false, + "line_number": 79 + } + ], + "src/media-understanding/apply.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/apply.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 14 + } + ], + "src/media-understanding/providers/deepgram/audio.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/providers/deepgram/audio.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 31 + } + ], + "src/media-understanding/providers/google/video.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/providers/google/video.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 28 + } + ], + "src/media-understanding/providers/openai/audio.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/providers/openai/audio.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 26 + } + ], + "src/media-understanding/runner.auto-audio.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/runner.auto-audio.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 42 + } + ], + "src/media-understanding/runner.deepgram.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/runner.deepgram.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 46 + } + ], + "src/memory/embeddings.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/memory/embeddings.test.ts", + "hashed_secret": "a47110e348a3063541fb1f1f640d635d457181a0", + "is_verified": false, + "line_number": 32 + }, + { + "type": "Secret Keyword", + "filename": "src/memory/embeddings.test.ts", + "hashed_secret": "c734e47630dda71619c696d88381f06f7511bd78", + "is_verified": false, + "line_number": 149 + }, + { + "type": "Secret Keyword", + "filename": "src/memory/embeddings.test.ts", + "hashed_secret": "56e1d57b8db262b08bc73c60ed08d8c92e59503f", + "is_verified": false, + "line_number": 179 + } + ], + "src/pairing/pairing-store.ts": [ + { + "type": "Base64 High Entropy String", + "filename": "src/pairing/pairing-store.ts", + "hashed_secret": "f8c6f1ff98c5ee78c27d34a3ca68f35ad79847af", + "is_verified": false, + "line_number": 12 + } + ], + "src/security/audit.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/security/audit.test.ts", + "hashed_secret": "b1775a785f09a6ebaf2dc33d6eaeb98974d9cdb8", + "is_verified": false, + "line_number": 180 + }, + { + "type": "Hex High Entropy String", + "filename": "src/security/audit.test.ts", + "hashed_secret": "fa8da98a5bdb77b4902cbb4338e6e94ea825300e", + "is_verified": false, + "line_number": 209 + }, + { + "type": "Secret Keyword", + "filename": "src/security/audit.test.ts", + "hashed_secret": "21f688ab56f76a99e5c6ed342291422f4e57e47f", + "is_verified": false, + "line_number": 1046 + }, + { + "type": "Secret Keyword", + "filename": "src/security/audit.test.ts", + "hashed_secret": "3dc927d80543dc0f643940b70d066bd4b4c4b78e", + "is_verified": false, + "line_number": 1077 + } + ], + "src/tts/tts.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/tts/tts.test.ts", + "hashed_secret": "2e7a7ee14caebf378fc32d6cf6f557f347c96773", + "is_verified": false, + "line_number": 33 + }, + { + "type": "Hex High Entropy String", + "filename": "src/tts/tts.test.ts", + "hashed_secret": "b214f706bb602c1cc2adc5c6165e73622305f4bb", + "is_verified": false, + "line_number": 68 } ], "src/web/qr-image.test.ts": [ @@ -504,15 +2177,15 @@ "line_number": 12 } ], - "vendor/a2ui/README.md": [ + "test/provider-timeout.e2e.test.ts": [ { "type": "Secret Keyword", - "filename": "vendor/a2ui/README.md", - "hashed_secret": "2619a5397a5d054dab3fe24e6a8da1fbd76ec3a6", + "filename": "test/provider-timeout.e2e.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "is_verified": false, - "line_number": 123 + "line_number": 182 } ] }, - "generated_at": "2026-01-05T13:01:00Z" + "generated_at": "2026-01-25T10:55:04Z" } diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 00000000000..515f25a5f1e --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,25 @@ +# ShellCheck configuration +# https://www.shellcheck.net/wiki/ + +# Disable common false positives and style suggestions + +# SC2034: Variable appears unused (often exported or used indirectly) +disable=SC2034 + +# SC2155: Declare and assign separately (common idiom, rarely causes issues) +disable=SC2155 + +# SC2295: Expansions inside ${..} need quoting (info-level, rarely causes issues) +disable=SC2295 + +# SC1012: \r is literal (tr -d '\r' works as intended on most systems) +disable=SC1012 + +# SC2026: Word outside quotes (info-level, often intentional) +disable=SC2026 + +# SC2016: Expressions don't expand in single quotes (often intentional in sed/awk) +disable=SC2016 + +# SC2129: Consider using { cmd1; cmd2; } >> file (style preference) +disable=SC2129 diff --git a/.swiftformat b/.swiftformat index e0d3bc8b658..6622d0b01cc 100644 --- a/.swiftformat +++ b/.swiftformat @@ -23,7 +23,7 @@ # Whitespace --trimwhitespace always --emptybraces no-space ---nospaceoperators ...,..< +--nospaceoperators ...,..< --ranges no-space --someAny true --voidtype void diff --git a/AGENTS.md b/AGENTS.md index 3751208c8cc..ac85a00d861 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,6 +13,7 @@ - Core channel docs: `docs/channels/` - Core channel code: `src/telegram`, `src/discord`, `src/slack`, `src/signal`, `src/imessage`, `src/web` (WhatsApp web), `src/channels`, `src/routing` - Extensions (channel plugins): `extensions/*` (e.g. `extensions/msteams`, `extensions/matrix`, `extensions/zalo`, `extensions/zalouser`, `extensions/voice-call`) +- When adding channels/extensions/apps/docs, review `.github/labeler.yml` for label coverage. ## Docs Linking (Mintlify) - Docs are hosted on Mintlify (docs.clawd.bot). @@ -37,6 +38,7 @@ ## Build, Test, and Development Commands - Runtime baseline: Node **22+** (keep Node + Bun paths working). - Install deps: `pnpm install` +- Pre-commit hooks: `prek install` (runs same checks as CI) - Also supported: `bun install` (keep `pnpm-lock.yaml` + Bun patching in sync when touching deps/patches). - Prefer Bun for TypeScript execution (scripts, dev, tests): `bun ` / `bunx `. - Run CLI in dev: `pnpm clawdbot ...` (bun) or `pnpm dev`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 59ce99755df..668a918232b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,50 +2,124 @@ Docs: https://docs.clawd.bot +## 2026.1.25 +Status: unreleased. + +### Changes +- Doctor: warn on gateway exposure without auth. (#2016) Thanks @Alex-Alaniz. +- Docs: add Vercel AI Gateway to providers sidebar. (#1901) Thanks @jerilynzheng. +- Agents: expand cron tool description with full schema docs. (#1988) Thanks @tomascupr. +- Skills: add missing dependency metadata for GitHub, Notion, Slack, Discord. (#1995) Thanks @jackheuberger. +- Docs: add Render deployment guide. (#1975) Thanks @anurag. +- Docs: add Claude Max API Proxy guide. (#1875) Thanks @atalovesyou. +- Docs: add DigitalOcean deployment guide. (#1870) Thanks @0xJonHoldsCrypto. +- Docs: add Raspberry Pi install guide. (#1871) Thanks @0xJonHoldsCrypto. +- Docs: add GCP Compute Engine deployment guide. (#1848) Thanks @hougangdev. +- Docs: credit both contributors for Control UI refresh. (#1852) Thanks @EnzeD. +- Onboarding: add Venice API key to non-interactive flow. (#1893) Thanks @jonisjongithub. +- Tlon: format thread reply IDs as @ud. (#1837) Thanks @wca4a. +- Gateway: prefer newest session metadata when combining stores. (#1823) Thanks @emanuelst. +- Web UI: keep sub-agent announce replies visible in WebChat. (#1977) Thanks @andrescardonas7. +- CI: increase Node heap size for macOS checks. (#1890) Thanks @realZachi. +- macOS: avoid crash when rendering code blocks by bumping Textual to 0.3.1. (#2033) Thanks @garricn. +- Browser: fall back to URL matching for extension relay target resolution. (#1999) Thanks @jonit-dev. +- Update: ignore dist/control-ui for dirty checks and restore after ui builds. (#1976) Thanks @Glucksberg. +- Telegram: allow caption param for media sends. (#1888) Thanks @mguellsegarra. +- Telegram: avoid block replies when streaming is disabled. (#1885) Thanks @ivancasco. +- Auth: show copyable Google auth URL after ASCII prompt. (#1787) Thanks @robbyczgw-cla. +- Routing: precompile session key regexes. (#1697) Thanks @Ray0907. +- TUI: avoid width overflow when rendering selection lists. (#1686) Thanks @mossein. +- Telegram: keep topic IDs in restart sentinel notifications. (#1807) Thanks @hsrvc. +- Config: apply config.env before ${VAR} substitution. (#1813) Thanks @spanishflu-est1918. +- Slack: clear ack reaction after streamed replies. (#2044) Thanks @fancyboi999. +- macOS: keep custom SSH usernames in remote target. (#2046) Thanks @algal. + +### Fixes +- Security: harden Tailscale Serve auth by validating identity via local tailscaled before trusting headers. +- Web UI: improve WebChat image paste previews and allow image-only sends. (#1925) Thanks @smartprogrammer93. +- Gateway: default auth now fail-closed (token/password required; Tailscale Serve identity remains allowed). + +## 2026.1.24-3 + +### Fixes +- Gateway: harden reverse proxy handling for local-client detection and unauthenticated proxied connects. (#1795) Thanks @orlyjamie. +- Security audit: flag loopback Control UI with auth disabled as critical. (#1795) Thanks @orlyjamie. +- CLI: resume claude-cli sessions and stream CLI replies to TUI clients. (#1921) Thanks @rmorse. + +## 2026.1.24-2 + +### Fixes +- Packaging: include dist/link-understanding output in npm tarball (fixes missing apply.js import on install). + +## 2026.1.24-1 + +### Fixes +- Packaging: include dist/shared output in npm tarball (fixes missing reasoning-tags import on install). + ## 2026.1.24 ### Highlights -- Ollama: provider discovery + docs. (#1606) Thanks @abhaymundhara. https://docs.clawd.bot/providers/ollama -- Venius (Venice AI): highlight provider guide + cross-links + expanded guidance. https://docs.clawd.bot/providers/venice +- Providers: Ollama discovery + docs; Venice guide upgrades + cross-links. (#1606) Thanks @abhaymundhara. https://docs.clawd.bot/providers/ollama https://docs.clawd.bot/providers/venice +- Channels: LINE plugin (Messaging API) with rich replies + quick replies. (#1630) Thanks @plum-dawg. +- TTS: Edge fallback (keyless) + `/tts` auto modes. (#1668, #1667) Thanks @steipete, @sebslight. https://docs.clawd.bot/tts +- Exec approvals: approve in-chat via `/approve` across all channels (including plugins). (#1621) Thanks @czekaj. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/slash-commands +- Telegram: DM topics as separate sessions + outbound link preview toggle. (#1597, #1700) Thanks @rohannagpal, @zerone0x. https://docs.clawd.bot/channels/telegram ### Changes +- Channels: add LINE plugin (Messaging API) with rich replies, quick replies, and plugin HTTP registry. (#1630) Thanks @plum-dawg. - TTS: add Edge TTS provider fallback, defaulting to keyless Edge with MP3 retry on format failures. (#1668) Thanks @steipete. https://docs.clawd.bot/tts -- Web search: add Brave freshness filter parameter for time-scoped results. (#1688) Thanks @JonUleis. https://docs.clawd.bot/tools/web - TTS: add auto mode enum (off/always/inbound/tagged) with per-session `/tts` override. (#1667) Thanks @sebslight. https://docs.clawd.bot/tts +- Telegram: treat DM topics as separate sessions and keep DM history limits stable with thread suffixes. (#1597) Thanks @rohannagpal. +- Telegram: add `channels.telegram.linkPreview` to toggle outbound link previews. (#1700) Thanks @zerone0x. https://docs.clawd.bot/channels/telegram +- Web search: add Brave freshness filter parameter for time-scoped results. (#1688) Thanks @JonUleis. https://docs.clawd.bot/tools/web +- UI: refresh Control UI dashboard design system (colors, icons, typography). (#1745, #1786) Thanks @EnzeD, @mousberg. +- Exec approvals: forward approval prompts to chat with `/approve` for all channels (including plugins). (#1621) Thanks @czekaj. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/slash-commands +- Gateway: expose config.patch in the gateway tool with safe partial updates + restart sentinel. (#1653) Thanks @Glucksberg. +- Diagnostics: add diagnostic flags for targeted debug logs (config + env override). https://docs.clawd.bot/diagnostics/flags - Docs: expand FAQ (migration, scheduling, concurrency, model recommendations, OpenAI subscription auth, Pi sizing, hackable install, docs SSL workaround). - Docs: add verbose installer troubleshooting guidance. - Docs: add macOS VM guide with local/hosted options + VPS/nodes guidance. (#1693) Thanks @f-trycua. -- Docs: update Fly.io guide notes. - Docs: add Bedrock EC2 instance role setup + IAM steps. (#1625) Thanks @sergical. https://docs.clawd.bot/bedrock -- Exec approvals: forward approval prompts to chat with `/approve` for all channels (including plugins). (#1621) Thanks @czekaj. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/slash-commands -- Gateway: expose config.patch in the gateway tool with safe partial updates + restart sentinel. (#1653) Thanks @Glucksberg. -- Telegram: treat DM topics as separate sessions and keep DM history limits stable with thread suffixes. (#1597) Thanks @rohannagpal. -- Telegram: add verbose raw-update logging for inbound Telegram updates. (#1597) Thanks @rohannagpal. +- Docs: update Fly.io guide notes. +- Dev: add prek pre-commit hooks + dependabot config for weekly updates. (#1720) Thanks @dguido. ### Fixes -- BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing. -- Web UI: hide internal `message_id` hints in chat bubbles. +- Web UI: fix config/debug layout overflow, scrolling, and code block sizing. (#1715) Thanks @saipreetham589. - Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent. - Web UI: clear stale disconnect banners on reconnect; allow form saves with unsupported schema paths but block missing schema. (#1707) Thanks @Glucksberg. -- Heartbeat: normalize target identifiers for consistent routing. -- TUI: reload history after gateway reconnect to restore session state. (#1663) -- Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639) -- Telegram: set fetch duplex="half" for uploads on Node 22 to avoid sendPhoto failures. (#1684) Thanks @commdata2338. +- Web UI: hide internal `message_id` hints in chat bubbles. +- Gateway: allow Control UI token-only auth to skip device pairing even when device identity is present (`gateway.controlUi.allowInsecureAuth`). (#1679) Thanks @steipete. +- Matrix: decrypt E2EE media attachments with preflight size guard. (#1744) Thanks @araa47. +- BlueBubbles: route phone-number targets to DMs, avoid leaking routing IDs, and auto-create missing DMs (Private API required). (#1751) Thanks @tyler6204. https://docs.clawd.bot/channels/bluebubbles +- BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing. +- iMessage: normalize chat_id/chat_guid/chat_identifier prefixes case-insensitively and keep service-prefixed handles stable. (#1708) Thanks @aaronn. - Signal: repair reaction sends (group/UUID targets + CLI author flags). (#1651) Thanks @vilkasdev. - Signal: add configurable signal-cli startup timeout + external daemon mode docs. (#1677) https://docs.clawd.bot/channels/signal -- Exec: keep approvals for elevated ask unless full mode. (#1616) Thanks @ivancasco. +- Telegram: set fetch duplex="half" for uploads on Node 22 to avoid sendPhoto failures. (#1684) Thanks @commdata2338. +- Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639) +- Telegram: honor per-account proxy for outbound API calls. (#1774) Thanks @radek-paclt. +- Telegram: fall back to text when voice notes are blocked by privacy settings. (#1725) Thanks @foeken. +- Voice Call: return stream TwiML for outbound conversation calls on initial Twilio webhook. (#1634) +- Voice Call: serialize Twilio TTS playback and cancel on barge-in to prevent overlap. (#1713) Thanks @dguido. +- Google Chat: tighten email allowlist matching, typing cleanup, media caps, and onboarding/docs/tests. (#1635) Thanks @iHildy. +- Google Chat: normalize space targets without double `spaces/` prefix. - Agents: auto-compact on context overflow prompt errors before failing. (#1627) Thanks @rodrigouroz. - Agents: use the active auth profile for auto-compaction recovery. +- Media understanding: skip image understanding when the primary model already supports vision. (#1747) Thanks @tyler6204. - Models: default missing custom provider fields so minimal configs are accepted. +- Messaging: keep newline chunking safe for fenced markdown blocks across channels. +- Messaging: treat newline chunking as paragraph-aware (blank-line splits) to keep lists and headings together. (#1726) Thanks @tyler6204. +- TUI: reload history after gateway reconnect to restore session state. (#1663) +- Heartbeat: normalize target identifiers for consistent routing. +- Exec: keep approvals for elevated ask unless full mode. (#1616) Thanks @ivancasco. +- Exec: treat Windows platform labels as Windows for node shell selection. (#1760) Thanks @ymat19. +- Gateway: include inline config env vars in service install environments. (#1735) Thanks @Seredeep. - Gateway: skip Tailscale DNS probing when tailscale.mode is off. (#1671) - Gateway: reduce log noise for late invokes + remote node probes; debounce skills refresh. (#1607) Thanks @petter-b. - Gateway: clarify Control UI/WebChat auth error hints for missing tokens. (#1690) - Gateway: listen on IPv6 loopback when bound to 127.0.0.1 so localhost webhooks work. +- Gateway: store lock files in the temp directory to avoid stale locks on persistent volumes. (#1676) - macOS: default direct-transport `ws://` URLs to port 18789; document `gateway.remote.transport`. (#1603) Thanks @ngutman. -- Voice Call: return stream TwiML for outbound conversation calls on initial Twilio webhook. (#1634) -- Google Chat: tighten email allowlist matching, typing cleanup, media caps, and onboarding/docs/tests. (#1635) Thanks @iHildy. -- Google Chat: normalize space targets without double `spaces/` prefix. -- Messaging: keep newline chunking safe for fenced markdown blocks across channels. - Tests: cap Vitest workers on CI macOS to reduce timeouts. (#1597) Thanks @rohannagpal. - Tests: avoid fake-timer dependency in embedded runner stream mock to reduce CI flakes. (#1597) Thanks @rohannagpal. - Tests: increase embedded runner ordering test timeout to reduce CI flakes. (#1597) Thanks @rohannagpal. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7b37ea38998..d6eb0532fca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,3 +40,13 @@ Please include in your PR: - [ ] Confirm you understand what the code does AI PRs are first-class citizens here. We just want transparency so reviewers know what to look for. + +## Current Focus & Roadmap 🗺 + +We are currently prioritizing: +- **Stability**: Fixing edge cases in channel connections (WhatsApp/Telegram). +- **UX**: Improving the onboarding wizard and error messages. +- **Skills**: Expanding the library of bundled skills and improving the Skill Creation developer experience. +- **Performance**: Optimizing token usage and compaction logic. + +Check the [GitHub Issues](https://github.com/clawdbot/clawdbot/issues) for "good first issue" labels! diff --git a/README.md b/README.md index 2c2093b951a..217a4b61c46 100644 --- a/README.md +++ b/README.md @@ -459,7 +459,7 @@ Use these when you’re past the onboarding flow and want the deeper reference. ## Clawd -Clawdbot was built for **Clawd**, a space lobster AI assistant. 🦞 +Clawdbot was built for **Clawd**, a space lobster AI assistant. 🦞 by Peter Steinberger and the community. - [clawd.me](https://clawd.me) @@ -468,7 +468,7 @@ by Peter Steinberger and the community. ## Community -See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines, maintainers, and how to submit PRs. +See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines, maintainers, and how to submit PRs. AI/vibe-coded PRs welcome! 🤖 Special thanks to [Mario Zechner](https://mariozechner.at/) for his support and for @@ -477,32 +477,34 @@ Special thanks to [Mario Zechner](https://mariozechner.at/) for his support and Thanks to all clawtributors:

- steipete bohdanpodvirnyi iHildy joaohlisboa mneves75 MatthieuBizien MaudeBot Glucksberg rahthakor vrknetha - radek-paclt Tobias Bischoff joshp123 czekaj mukhtharcm maxsumrall xadenryan rodrigouroz juanpablodlc hsrvc - magimetal meaningfool patelhiren NicholasSpisak sebslight jonisjongithub abhisekbasu1 zerone0x jamesgroat claude - JustYannicc tyler6204 SocialNerd42069 Hyaxia dantelex daveonkels google-labs-jules[bot] lc0rp vignesh07 mteam88 - Eng. Juan Combetto Mariano Belinky dbhurley TSavo julianengel bradleypriest benithors timolins nachx639 pvoo - sreekaransrinath gupsammy cristip73 stefangalescu nachoiacovino Vasanth Rao Naik Sabavat petter-b cpojer scald gumadeiras - andranik-sahakyan davidguttman sleontenko denysvitali sircrumpet peschee rafaelreis-r thewilloftheshadow ratulsarna lutr0 - danielz1z emanuelst KristijanJovanovski CashWilliams rdev osolmaz joshrad-dev kiranjd adityashaw2 sheeek - artuskg Takhoffman onutc pauloportella neooriginal manuelhettich minghinmatthewlam myfunc travisirby buddyh - connorshea kyleok mcinteerj dependabot[bot] John-Rood timkrase gerardward2007 obviyus roshanasingh4 tosh-hamburg - azade-c bjesuiter cheeeee Josh Phillips dlauer pookNast Whoaa512 YuriNachos chriseidhof robbyczgw-cla - ysqander aj47 superman32432432 Yurii Chukhlib grp06 ngutman antons austinm911 blacksmith-sh[bot] damoahdominic - dan-dr HeimdallStrategy imfing jalehman jarvis-medmatic kkarimi mahmoudashraf93 pkrmf RandyVentures Ryan Lisse - dougvk erikpr1994 Ghost jonasjancarik Keith the Silly Goose L36 Server Marc mitschabaude-bot mkbehr neist - sibbl chrisrodz Friederike Seiler gabriel-trigo iamadig Jonathan D. Rhyne (DJ-D) Kit koala73 manmal ogulcancelik - pasogott petradonka rubyrunsstuff siddhantjain suminhthanh svkozak VACInc wes-davis zats 24601 - adam91holt ameno- Chris Taylor Django Navarro evalexpr henrino3 humanwritten larlyssa odysseus0 oswalpalash - pcty-nextgen-service-account Syhids Aaron Konyer aaronveklabs andreabadesso Andrii cash-echo-bot Clawd ClawdFx erik-agens - Evizero fcatuhe itsjaydesu ivancasco ivanrvpereira jayhickey jeffersonwarrior jeffersonwarrior jverdi longmaba - mickahouan mjrussell odnxe p6l-richard philipp-spiess robaxelsen Sash Catanzarite T5-AndyML travisp VAC - william arzt zknicker abhaymundhara alejandro maza andrewting19 anpoirier arthyn Asleep123 bolismauro conhecendoia - dasilva333 Dimitrios Ploutarchos Drake Thomsen EnzeD fal3 Felix Krause ganghyun kim grrowl gtsifrikas HazAT - hrdwdmrbl hugobarauna Jamie Openshaw Jarvis Jefferson Nunn Kevin Lin kitze levifig Lloyd loukotal - martinpucik Matt mini Miles mrdbstn MSch Mustafa Tag Eldeen ndraiman nexty5870 prathamdby ptn1411 - reeltimeapps RLTCmpe Rolf Fredheim Rony Kelner Samrat Jha sergical shiv19 siraht snopoke testingabc321 - The Admiral thesash Ubuntu voidserf Vultr-Clawd Admin Wimmie wstock yazinsai Zach Knickerbocker Alphonse-arianee - Azade carlulsoe ddyo Erik latitudeki5223 Manuel Maly Mourad Boustani odrobnik pcty-nextgen-ios-builder Quentin - Randy Torres rhjoh ronak-guliani William Stock + steipete plum-dawg bohdanpodvirnyi iHildy joaohlisboa mneves75 MatthieuBizien MaudeBot Glucksberg rahthakor + vrknetha radek-paclt Tobias Bischoff joshp123 czekaj mukhtharcm sebslight maxsumrall xadenryan rodrigouroz + juanpablodlc hsrvc magimetal meaningfool tyler6204 patelhiren NicholasSpisak jonisjongithub zerone0x abhisekbasu1 + jamesgroat claude JustYannicc Hyaxia dantelex SocialNerd42069 daveonkels google-labs-jules[bot] lc0rp mousberg + vignesh07 mteam88 dbhurley Mariano Belinky Eng. Juan Combetto TSavo julianengel bradleypriest benithors rohannagpal + timolins f-trycua benostein nachx639 pvoo sreekaransrinath gupsammy cristip73 stefangalescu nachoiacovino + Vasanth Rao Naik Sabavat petter-b cpojer scald gumadeiras andranik-sahakyan davidguttman sleontenko denysvitali orlyjamie + thewilloftheshadow sircrumpet peschee rafaelreis-r ratulsarna lutr0 danielz1z emanuelst KristijanJovanovski rdev + joshrad-dev kiranjd osolmaz adityashaw2 CashWilliams sheeek artuskg Takhoffman onutc pauloportella + neooriginal manuelhettich minghinmatthewlam myfunc travisirby buddyh connorshea kyleok mcinteerj dependabot[bot] + John-Rood timkrase uos-status gerardward2007 obviyus roshanasingh4 tosh-hamburg azade-c JonUleis bjesuiter + cheeeee Josh Phillips robbyczgw-cla dlauer pookNast Whoaa512 YuriNachos chriseidhof ngutman ysqander + aj47 superman32432432 Yurii Chukhlib grp06 antons austinm911 blacksmith-sh[bot] damoahdominic dan-dr HeimdallStrategy + imfing jalehman jarvis-medmatic kkarimi mahmoudashraf93 pkrmf RandyVentures Ryan Lisse dougvk erikpr1994 + Ghost jonasjancarik Keith the Silly Goose L36 Server Marc mitschabaude-bot mkbehr neist sibbl chrisrodz + Friederike Seiler gabriel-trigo iamadig Jonathan D. Rhyne (DJ-D) Kit koala73 manmal ogulcancelik pasogott petradonka + rubyrunsstuff siddhantjain suminhthanh svkozak VACInc wes-davis zats 24601 adam91holt ameno- + Chris Taylor Django Navarro evalexpr henrino3 humanwritten larlyssa odysseus0 oswalpalash pcty-nextgen-service-account rmorse + Syhids Aaron Konyer aaronveklabs andreabadesso Andrii cash-echo-bot Clawd ClawdFx dguido EnzeD + erik-agens Evizero fcatuhe itsjaydesu ivancasco ivanrvpereira jayhickey jeffersonwarrior jeffersonwarrior jverdi + longmaba mickahouan mjrussell odnxe p6l-richard philipp-spiess robaxelsen Sash Catanzarite T5-AndyML travisp + VAC william arzt zknicker abhaymundhara alejandro maza andrewting19 anpoirier arthyn Asleep123 bolismauro + conhecendoia dasilva333 Developer Dimitrios Ploutarchos Drake Thomsen fal3 Felix Krause foeken ganghyun kim grrowl + gtsifrikas HazAT hrdwdmrbl hugobarauna Jamie Openshaw Jarvis Jefferson Nunn Kevin Lin kitze levifig + Lloyd loukotal louzhixian martinpucik Matt mini Miles mrdbstn MSch Mustafa Tag Eldeen ndraiman + nexty5870 Noctivoro prathamdby ptn1411 reeltimeapps RLTCmpe Rolf Fredheim Rony Kelner Samrat Jha senoldogann + Seredeep sergical shiv19 shiyuanhai siraht snopoke testingabc321 The Admiral thesash Ubuntu + voidserf Vultr-Clawd Admin Wimmie wstock yazinsai ymat19 Zach Knickerbocker 0xJonHoldsCrypto aaronn Alphonse-arianee + atalovesyou Azade carlulsoe ddyo Erik hougangdev latitudeki5223 Manuel Maly Mourad Boustani odrobnik + pcty-nextgen-ios-builder Quentin Randy Torres rhjoh ronak-guliani William Stock

diff --git a/SECURITY.md b/SECURITY.md index d2af462baeb..43d49399632 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -12,4 +12,3 @@ If you believe you’ve found a security issue in Clawdbot, please report it pri For threat model + hardening guidance (including `clawdbot security audit --deep` and `--fix`), see: - `https://docs.clawd.bot/gateway/security` - diff --git a/appcast.xml b/appcast.xml index bed929dfbad..8158ac24418 100644 --- a/appcast.xml +++ b/appcast.xml @@ -2,6 +2,101 @@ Clawdbot + + 2026.1.24-1 + Sun, 25 Jan 2026 14:05:25 +0000 + https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml + 7952 + 2026.1.24-1 + 15.0 + Clawdbot 2026.1.24-1 +

Fixes

+
    +
  • Packaging: include dist/shared output in npm tarball (fixes missing reasoning-tags import on install).
  • +
+

View full changelog

+]]>
+ +
+ + 2026.1.24 + Sun, 25 Jan 2026 13:31:05 +0000 + https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml + 7944 + 2026.1.24 + 15.0 + Clawdbot 2026.1.24 +

Highlights

+
    +
  • Providers: Ollama discovery + docs; Venice guide upgrades + cross-links. (#1606) Thanks @abhaymundhara. https://docs.clawd.bot/providers/ollama https://docs.clawd.bot/providers/venice
  • +
  • Channels: LINE plugin (Messaging API) with rich replies + quick replies. (#1630) Thanks @plum-dawg.
  • +
  • TTS: Edge fallback (keyless) + /tts auto modes. (#1668, #1667) Thanks @steipete, @sebslight. https://docs.clawd.bot/tts
  • +
  • Exec approvals: approve in-chat via /approve across all channels (including plugins). (#1621) Thanks @czekaj. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/slash-commands
  • +
  • Telegram: DM topics as separate sessions + outbound link preview toggle. (#1597, #1700) Thanks @rohannagpal, @zerone0x. https://docs.clawd.bot/channels/telegram
  • +
+

Changes

+
    +
  • Channels: add LINE plugin (Messaging API) with rich replies, quick replies, and plugin HTTP registry. (#1630) Thanks @plum-dawg.
  • +
  • TTS: add Edge TTS provider fallback, defaulting to keyless Edge with MP3 retry on format failures. (#1668) Thanks @steipete. https://docs.clawd.bot/tts
  • +
  • TTS: add auto mode enum (off/always/inbound/tagged) with per-session /tts override. (#1667) Thanks @sebslight. https://docs.clawd.bot/tts
  • +
  • Telegram: treat DM topics as separate sessions and keep DM history limits stable with thread suffixes. (#1597) Thanks @rohannagpal.
  • +
  • Telegram: add channels.telegram.linkPreview to toggle outbound link previews. (#1700) Thanks @zerone0x. https://docs.clawd.bot/channels/telegram
  • +
  • Web search: add Brave freshness filter parameter for time-scoped results. (#1688) Thanks @JonUleis. https://docs.clawd.bot/tools/web
  • +
  • UI: refresh Control UI dashboard design system (typography, colors, spacing). (#1786) Thanks @mousberg.
  • +
  • Exec approvals: forward approval prompts to chat with /approve for all channels (including plugins). (#1621) Thanks @czekaj. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/slash-commands
  • +
  • Gateway: expose config.patch in the gateway tool with safe partial updates + restart sentinel. (#1653) Thanks @Glucksberg.
  • +
  • Diagnostics: add diagnostic flags for targeted debug logs (config + env override). https://docs.clawd.bot/diagnostics/flags
  • +
  • Docs: expand FAQ (migration, scheduling, concurrency, model recommendations, OpenAI subscription auth, Pi sizing, hackable install, docs SSL workaround).
  • +
  • Docs: add verbose installer troubleshooting guidance.
  • +
  • Docs: add macOS VM guide with local/hosted options + VPS/nodes guidance. (#1693) Thanks @f-trycua.
  • +
  • Docs: add Bedrock EC2 instance role setup + IAM steps. (#1625) Thanks @sergical. https://docs.clawd.bot/bedrock
  • +
  • Docs: update Fly.io guide notes.
  • +
  • Dev: add prek pre-commit hooks + dependabot config for weekly updates. (#1720) Thanks @dguido.
  • +
+

Fixes

+
    +
  • Web UI: fix config/debug layout overflow, scrolling, and code block sizing. (#1715) Thanks @saipreetham589.
  • +
  • Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent.
  • +
  • Web UI: clear stale disconnect banners on reconnect; allow form saves with unsupported schema paths but block missing schema. (#1707) Thanks @Glucksberg.
  • +
  • Web UI: hide internal message_id hints in chat bubbles.
  • +
  • Gateway: allow Control UI token-only auth to skip device pairing even when device identity is present (gateway.controlUi.allowInsecureAuth). (#1679) Thanks @steipete.
  • +
  • Matrix: decrypt E2EE media attachments with preflight size guard. (#1744) Thanks @araa47.
  • +
  • BlueBubbles: route phone-number targets to DMs, avoid leaking routing IDs, and auto-create missing DMs (Private API required). (#1751) Thanks @tyler6204. https://docs.clawd.bot/channels/bluebubbles
  • +
  • BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing.
  • +
  • Signal: repair reaction sends (group/UUID targets + CLI author flags). (#1651) Thanks @vilkasdev.
  • +
  • Signal: add configurable signal-cli startup timeout + external daemon mode docs. (#1677) https://docs.clawd.bot/channels/signal
  • +
  • Telegram: set fetch duplex="half" for uploads on Node 22 to avoid sendPhoto failures. (#1684) Thanks @commdata2338.
  • +
  • Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639)
  • +
  • Telegram: honor per-account proxy for outbound API calls. (#1774) Thanks @radek-paclt.
  • +
  • Telegram: fall back to text when voice notes are blocked by privacy settings. (#1725) Thanks @foeken.
  • +
  • Voice Call: return stream TwiML for outbound conversation calls on initial Twilio webhook. (#1634)
  • +
  • Voice Call: serialize Twilio TTS playback and cancel on barge-in to prevent overlap. (#1713) Thanks @dguido.
  • +
  • Google Chat: tighten email allowlist matching, typing cleanup, media caps, and onboarding/docs/tests. (#1635) Thanks @iHildy.
  • +
  • Google Chat: normalize space targets without double spaces/ prefix.
  • +
  • Agents: auto-compact on context overflow prompt errors before failing. (#1627) Thanks @rodrigouroz.
  • +
  • Agents: use the active auth profile for auto-compaction recovery.
  • +
  • Media understanding: skip image understanding when the primary model already supports vision. (#1747) Thanks @tyler6204.
  • +
  • Models: default missing custom provider fields so minimal configs are accepted.
  • +
  • Messaging: keep newline chunking safe for fenced markdown blocks across channels.
  • +
  • TUI: reload history after gateway reconnect to restore session state. (#1663)
  • +
  • Heartbeat: normalize target identifiers for consistent routing.
  • +
  • Exec: keep approvals for elevated ask unless full mode. (#1616) Thanks @ivancasco.
  • +
  • Exec: treat Windows platform labels as Windows for node shell selection. (#1760) Thanks @ymat19.
  • +
  • Gateway: include inline config env vars in service install environments. (#1735) Thanks @Seredeep.
  • +
  • Gateway: skip Tailscale DNS probing when tailscale.mode is off. (#1671)
  • +
  • Gateway: reduce log noise for late invokes + remote node probes; debounce skills refresh. (#1607) Thanks @petter-b.
  • +
  • Gateway: clarify Control UI/WebChat auth error hints for missing tokens. (#1690)
  • +
  • Gateway: listen on IPv6 loopback when bound to 127.0.0.1 so localhost webhooks work.
  • +
  • Gateway: store lock files in the temp directory to avoid stale locks on persistent volumes. (#1676)
  • +
  • macOS: default direct-transport ws:// URLs to port 18789; document gateway.remote.transport. (#1603) Thanks @ngutman.
  • +
  • Tests: cap Vitest workers on CI macOS to reduce timeouts. (#1597) Thanks @rohannagpal.
  • +
  • Tests: avoid fake-timer dependency in embedded runner stream mock to reduce CI flakes. (#1597) Thanks @rohannagpal.
  • +
  • Tests: increase embedded runner ordering test timeout to reduce CI flakes. (#1597) Thanks @rohannagpal.
  • +
+

View full changelog

+]]>
+ +
2026.1.23 Sat, 24 Jan 2026 13:02:18 +0000 @@ -89,127 +184,5 @@ ]]> - - 2026.1.22 - Fri, 23 Jan 2026 08:58:14 +0000 - https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml - 7530 - 2026.1.22 - 15.0 - Clawdbot 2026.1.22 -

Changes

-
    -
  • Highlight: Compaction safeguard now uses adaptive chunking, progressive fallback, and UI status + retries. (#1466) Thanks @dlauer.
  • -
  • Providers: add Antigravity usage tracking to status output. (#1490) Thanks @patelhiren.
  • -
  • Slack: add chat-type reply threading overrides via replyToModeByChatType. (#1442) Thanks @stefangalescu.
  • -
  • BlueBubbles: add asVoice support for MP3/CAF voice memos in sendAttachment. (#1477, #1482) Thanks @Nicell.
  • -
  • Onboarding: add hatch choice (TUI/Web/Later), token explainer, background dashboard seed on macOS, and showcase link.
  • -
-

Fixes

-
    -
  • BlueBubbles: stop typing indicator on idle/no-reply. (#1439) Thanks @Nicell.
  • -
  • Message tool: keep path/filePath as-is for send; hydrate buffers only for sendAttachment. (#1444) Thanks @hopyky.
  • -
  • Auto-reply: only report a model switch when session state is available. (#1465) Thanks @robbyczgw-cla.
  • -
  • Control UI: resolve local avatar URLs with basePath across injection + identity RPC. (#1457) Thanks @dlauer.
  • -
  • Agents: sanitize assistant history text to strip tool-call markers. (#1456) Thanks @zerone0x.
  • -
  • Discord: clarify Message Content Intent onboarding hint. (#1487) Thanks @kyleok.
  • -
  • Gateway: stop the service before uninstalling and fail if it remains loaded.
  • -
  • Agents: surface concrete API error details instead of generic AI service errors.
  • -
  • Exec: fall back to non-PTY when PTY spawn fails (EBADF). (#1484)
  • -
  • Exec approvals: allow per-segment allowlists for chained shell commands on gateway + node hosts. (#1458) Thanks @czekaj.
  • -
  • Agents: make OpenAI sessions image-sanitize-only; gate tool-id/repair sanitization by provider.
  • -
  • Doctor: honor CLAWDBOT_GATEWAY_TOKEN for auth checks and security audit token reuse. (#1448) Thanks @azade-c.
  • -
  • Agents: make tool summaries more readable and only show optional params when set.
  • -
  • Agents: honor SOUL.md guidance even when the file is nested or path-qualified. (#1434) Thanks @neooriginal.
  • -
  • Matrix (plugin): persist m.direct for resolved DMs and harden room fallback. (#1436, #1486) Thanks @sibbl.
  • -
  • CLI: prefer ~ for home paths in output.
  • -
  • Mattermost (plugin): enforce pairing/allowlist gating, keep @username targets, and clarify plugin-only docs. (#1428) Thanks @damoahdominic.
  • -
  • Agents: centralize transcript sanitization in the runner; keep tags and error turns intact.
  • -
  • Auth: skip auth profiles in cooldown during initial selection and rotation. (#1316) Thanks @odrobnik.
  • -
  • Agents/TUI: honor user-pinned auth profiles during cooldown and preserve search picker ranking. (#1432) Thanks @tobiasbischoff.
  • -
  • Docs: fix gog auth services example to include docs scope. (#1454) Thanks @zerone0x.
  • -
  • Slack: reduce WebClient retries to avoid duplicate sends. (#1481)
  • -
  • Slack: read thread replies for message reads when threadId is provided (replies-only). (#1450) Thanks @rodrigouroz.
  • -
  • macOS: prefer linked channels in gateway summary to avoid false “not linked” status.
  • -
  • macOS/tests: fix gateway summary lookup after guard unwrap; prevent browser opens during tests. (ECID-1483)
  • -
-

View full changelog

-]]>
- -
- - 2026.1.21 - Thu, 22 Jan 2026 12:22:35 +0000 - https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml - 7374 - 2026.1.21 - 15.0 - Clawdbot 2026.1.21 -

Highlights

-
    -
  • Lobster optional plugin tool for typed workflows + approval gates. https://docs.clawd.bot/tools/lobster
  • -
  • Custom assistant identity + avatars in the Control UI. https://docs.clawd.bot/cli/agents https://docs.clawd.bot/web/control-ui
  • -
  • Cache optimizations: cache-ttl pruning + defaults reduce token spend on cold requests. https://docs.clawd.bot/concepts/session-pruning
  • -
  • Exec approvals + elevated ask/full modes. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/elevated
  • -
  • Signal typing/read receipts + MSTeams attachments. https://docs.clawd.bot/channels/signal https://docs.clawd.bot/channels/msteams
  • -
  • /models UX refresh + clawdbot update wizard. https://docs.clawd.bot/cli/models https://docs.clawd.bot/cli/update
  • -
-

Changes

-
    -
  • Highlight: Lobster optional plugin tool for typed workflows + approval gates. https://docs.clawd.bot/tools/lobster (#1152) Thanks @vignesh07.
  • -
  • Agents/UI: add identity avatar config support and Control UI avatar rendering. (#1329, #1424) Thanks @dlauer. https://docs.clawd.bot/gateway/configuration https://docs.clawd.bot/cli/agents
  • -
  • Control UI: add custom assistant identity support and per-session identity display. (#1420) Thanks @robbyczgw-cla. https://docs.clawd.bot/web/control-ui
  • -
  • CLI: add clawdbot update wizard with interactive channel selection + restart prompts, plus preflight checks before rebasing. https://docs.clawd.bot/cli/update
  • -
  • Models/Commands: add /models, improve /model listing UX, and expand clawdbot models paging. (#1398) Thanks @vignesh07. https://docs.clawd.bot/cli/models
  • -
  • CLI: move gateway service commands under clawdbot gateway, flatten node service commands under clawdbot node, and add gateway probe for reachability. https://docs.clawd.bot/cli/gateway https://docs.clawd.bot/cli/node
  • -
  • Exec: add elevated ask/full modes, tighten allowlist gating, and render approvals tables on write. https://docs.clawd.bot/tools/elevated https://docs.clawd.bot/tools/exec-approvals
  • -
  • Exec approvals: default to local host, add gateway/node targeting + target details, support wildcard agent allowlists, and tighten allowlist parsing/safe bins. https://docs.clawd.bot/cli/approvals https://docs.clawd.bot/tools/exec-approvals
  • -
  • Heartbeat: allow explicit session keys and active hours. (#1256) Thanks @zknicker. https://docs.clawd.bot/gateway/heartbeat
  • -
  • Sessions: add per-channel idle durations via sessions.channelIdleMinutes. (#1353) Thanks @cash-echo-bot.
  • -
  • Nodes: run exec-style, expose PATH in status/describe, and bootstrap PATH for node-host execution. https://docs.clawd.bot/cli/node
  • -
  • Cache: add cache.ttlPrune mode and auth-aware defaults for cache TTL behavior.
  • -
  • Queue: add per-channel debounce overrides for auto-reply. https://docs.clawd.bot/concepts/queue
  • -
  • Discord: add wildcard channel config support. (#1334) Thanks @pvoo. https://docs.clawd.bot/channels/discord
  • -
  • Signal: add typing indicators and DM read receipts via signal-cli. https://docs.clawd.bot/channels/signal
  • -
  • MSTeams: add file uploads, adaptive cards, and attachment handling improvements. (#1410) Thanks @Evizero. https://docs.clawd.bot/channels/msteams
  • -
  • Onboarding: remove the run setup-token auth option (paste setup-token or reuse CLI creds instead).
  • -
  • macOS: refresh Settings (location access in Permissions, connection mode in menu, remove CLI install UI).
  • -
  • Diagnostics: add cache trace config for debugging. (#1370) Thanks @parubets.
  • -
  • Docs: Lobster guides + org URL updates, /model allowlist troubleshooting, Gmail message search examples, gateway.mode troubleshooting, prompt injection guidance, npm prefix/node CLI notes, control UI dev gatewayUrl note, tool_use FAQ, showcase video, and sharp/node-gyp workaround. (#1427, #1220, #1405) Thanks @vignesh07, @mbelinky.
  • -
-

Breaking

-
    -
  • BREAKING: Control UI now rejects insecure HTTP without device identity by default. Use HTTPS (Tailscale Serve) or set gateway.controlUi.allowInsecureAuth: true to allow token-only auth. https://docs.clawd.bot/web/control-ui#insecure-http
  • -
  • BREAKING: Envelope and system event timestamps now default to host-local time (was UTC) so agents don’t have to constantly convert.
  • -
-

Fixes

-
    -
  • Streaming/Typing/Media: keep reply tags across streamed chunks, start typing indicators at run start, and accept MEDIA paths with spaces/tilde while preferring the message tool hint for image replies.
  • -
  • Agents/Providers: drop unsigned thinking blocks for Claude models (Google Antigravity) and enforce alphanumeric tool call ids for strict providers (Mistral/OpenRouter). (#1372) Thanks @zerone0x.
  • -
  • Exec approvals: treat main as the default agent, align node/gateway allowlist prechecks, validate resolved paths, avoid allowlist resolve races, and avoid null optional params. (#1417, #1414, #1425) Thanks @czekaj.
  • -
  • Exec/Windows: resolve Windows exec paths with extensions and handle safe-bin exe names.
  • -
  • Nodes/macOS: prompt on allowlist miss for node exec approvals, persist allowlist decisions, and flatten node invoke errors. (#1394) Thanks @ngutman.
  • -
  • Gateway: prevent multiple gateways from sharing the same config/state (singleton lock), keep auto bind loopback-first with explicit tailnet binding, and improve SSH auth handling. (#1380)
  • -
  • Control UI: remove the chat stop button, keep the composer aligned to the bottom edge, stabilize session previews, and refresh the debug panel on route-driven tab changes. (#1373) Thanks @yazinsai.
  • -
  • UI/config: export SECTION_META for config form modules. (#1418) Thanks @MaudeBot.
  • -
  • macOS: keep chat pinned during streaming replies, include Textual resources, respect wildcard exec approvals, allow SSH agent auth, and default distribution builds to universal binaries. (#1279, #1362, #1384, #1396) Thanks @ameno-, @JustYannicc.
  • -
  • BlueBubbles: resolve short message IDs safely, expose full IDs in templates, and harden short-id fetch wrappers. (#1369, #1387) Thanks @tyler6204.
  • -
  • Models/Configure: inherit session model overrides in threads/topics, map OpenCode Zen models to the correct APIs, narrow Anthropic OAuth allowlist handling, seed allowlist fallbacks, list the full catalog when no allowlist is set, and limit /model list output. (#1376, #1416)
  • -
  • Memory: prevent CLI hangs by deferring vector probes, add sqlite-vec/embedding timeouts, and make session memory indexing async.
  • -
  • Cron: cap reminder context history to 10 messages and honor contextMessages. (#1103) Thanks @mkbehr.
  • -
  • Cache: restore the 1h cache TTL option and reset the pruning window.
  • -
  • Zalo Personal: tolerate ANSI/log-prefixed JSON output from zca. (#1379) Thanks @ptn1411.
  • -
  • Browser: suppress Chrome restore prompts for managed profiles. (#1419) Thanks @jamesgroat.
  • -
  • Infra: preserve fetch helper methods/preconnect when wrapping abort signals and normalize Telegram fetch aborts.
  • -
  • Config/Doctor: avoid stack traces for invalid configs, log the config path, avoid WhatsApp config resurrection, and warn when gateway.mode is unset. (#900)
  • -
  • CLI: read Codex CLI account_id for workspace billing. (#1422) Thanks @aj47.
  • -
  • Logs/Status: align rolling log filenames with local time and report sandboxed runtime in clawdbot status. (#1343)
  • -
  • Embedded runner: persist injected history images so attachments aren’t reloaded each turn. (#1374) Thanks @Nicell.
  • -
  • Nodes/Subagents: include agent/node/gateway context in tool failure logs and ensure subagent list uses the command session.
  • -
-

View full changelog

-]]>
- -
\ No newline at end of file diff --git a/apps/android/app/build.gradle.kts b/apps/android/app/build.gradle.kts index d8d77ebe18a..a015c0e3642 100644 --- a/apps/android/app/build.gradle.kts +++ b/apps/android/app/build.gradle.kts @@ -21,8 +21,8 @@ android { applicationId = "com.clawdbot.android" minSdk = 31 targetSdk = 36 - versionCode = 202601240 - versionName = "2026.1.24" + versionCode = 202601250 + versionName = "2026.1.25" } buildTypes { diff --git a/apps/android/app/src/main/java/com/clawdbot/android/CameraHudState.kt b/apps/android/app/src/main/java/com/clawdbot/android/CameraHudState.kt index 1d876903a3e..1c9b3986f64 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/CameraHudState.kt +++ b/apps/android/app/src/main/java/com/clawdbot/android/CameraHudState.kt @@ -12,4 +12,3 @@ data class CameraHudState( val kind: CameraHudKind, val message: String, ) - diff --git a/apps/android/app/src/main/java/com/clawdbot/android/VoiceWakeMode.kt b/apps/android/app/src/main/java/com/clawdbot/android/VoiceWakeMode.kt index e8a6eff1586..6c3e2c20115 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/VoiceWakeMode.kt +++ b/apps/android/app/src/main/java/com/clawdbot/android/VoiceWakeMode.kt @@ -12,4 +12,3 @@ enum class VoiceWakeMode(val rawValue: String) { } } } - diff --git a/apps/android/app/src/main/java/com/clawdbot/android/node/SmsManager.kt b/apps/android/app/src/main/java/com/clawdbot/android/node/SmsManager.kt index e449993d281..3e12a56dfbd 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/node/SmsManager.kt +++ b/apps/android/app/src/main/java/com/clawdbot/android/node/SmsManager.kt @@ -135,7 +135,7 @@ class SmsManager(private val context: Context) { /** * Send an SMS message. - * + * * @param paramsJson JSON with "to" (phone number) and "message" (text) fields * @return SendResult indicating success or failure */ diff --git a/apps/android/app/src/main/res/values/colors.xml b/apps/android/app/src/main/res/values/colors.xml index 6e79939c6c9..dfadc94cf03 100644 --- a/apps/android/app/src/main/res/values/colors.xml +++ b/apps/android/app/src/main/res/values/colors.xml @@ -1,4 +1,3 @@ #0A0A0A - diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml index 5e4d8a77d15..3665960c20d 100644 --- a/apps/android/app/src/main/res/values/strings.xml +++ b/apps/android/app/src/main/res/values/strings.xml @@ -1,4 +1,3 @@ Clawdbot Node - diff --git a/apps/android/app/src/test/java/com/clawdbot/android/voice/VoiceWakeCommandExtractorTest.kt b/apps/android/app/src/test/java/com/clawdbot/android/voice/VoiceWakeCommandExtractorTest.kt index bdcee32848a..f6e512fa324 100644 --- a/apps/android/app/src/test/java/com/clawdbot/android/voice/VoiceWakeCommandExtractorTest.kt +++ b/apps/android/app/src/test/java/com/clawdbot/android/voice/VoiceWakeCommandExtractorTest.kt @@ -23,4 +23,3 @@ class VoiceWakeCommandExtractorTest { assertNull(VoiceWakeCommandExtractor.extractCommand("hey claude!", listOf("claude"))) } } - diff --git a/apps/android/settings.gradle.kts b/apps/android/settings.gradle.kts index 05466f48f0f..d9d0158c9c2 100644 --- a/apps/android/settings.gradle.kts +++ b/apps/android/settings.gradle.kts @@ -16,4 +16,3 @@ dependencyResolutionManagement { rootProject.name = "ClawdbotNodeAndroid" include(":app") - diff --git a/apps/ios/.swiftlint.yml b/apps/ios/.swiftlint.yml index 7b64147b5e0..fc8509c8385 100644 --- a/apps/ios/.swiftlint.yml +++ b/apps/ios/.swiftlint.yml @@ -3,4 +3,3 @@ parent_config: ../../.swiftlint.yml included: - Sources - ../shared/ClawdisNodeKit/Sources - diff --git a/apps/ios/Sources/Info.plist b/apps/ios/Sources/Info.plist index 9dd7a03156c..e1cf2b71d33 100644 --- a/apps/ios/Sources/Info.plist +++ b/apps/ios/Sources/Info.plist @@ -19,9 +19,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2026.1.24 + 2026.1.25 CFBundleVersion - 20260124 + 20260125 NSAppTransportSecurity NSAllowsArbitraryLoadsInWebContent diff --git a/apps/ios/Tests/Info.plist b/apps/ios/Tests/Info.plist index 798a7742198..6ff977b0579 100644 --- a/apps/ios/Tests/Info.plist +++ b/apps/ios/Tests/Info.plist @@ -17,8 +17,8 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2026.1.24 + 2026.1.25 CFBundleVersion - 20260124 + 20260125 diff --git a/apps/ios/project.yml b/apps/ios/project.yml index 52faeb9d0b5..0073b4ef903 100644 --- a/apps/ios/project.yml +++ b/apps/ios/project.yml @@ -81,8 +81,8 @@ targets: properties: CFBundleDisplayName: Clawdbot CFBundleIconName: AppIcon - CFBundleShortVersionString: "2026.1.24" - CFBundleVersion: "20260124" + CFBundleShortVersionString: "2026.1.25" + CFBundleVersion: "20260125" UILaunchScreen: {} UIApplicationSceneManifest: UIApplicationSupportsMultipleScenes: false @@ -130,5 +130,5 @@ targets: path: Tests/Info.plist properties: CFBundleDisplayName: ClawdbotTests - CFBundleShortVersionString: "2026.1.24" - CFBundleVersion: "20260124" + CFBundleShortVersionString: "2026.1.25" + CFBundleVersion: "20260125" diff --git a/apps/macos/Icon.icon/icon.json b/apps/macos/Icon.icon/icon.json index 32754bd1882..33ba22b1c92 100644 --- a/apps/macos/Icon.icon/icon.json +++ b/apps/macos/Icon.icon/icon.json @@ -33,4 +33,4 @@ ], "squares" : "shared" } -} \ No newline at end of file +} diff --git a/apps/macos/Package.resolved b/apps/macos/Package.resolved index ffc524d1c6a..ef960964915 100644 --- a/apps/macos/Package.resolved +++ b/apps/macos/Package.resolved @@ -123,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/gonzalezreal/textual", "state" : { - "revision" : "a03c1e103d88de4ea0dd8320ea1611ec0d4b29b3", - "version" : "0.2.0" + "revision" : "5b06b811c0f5313b6b84bbef98c635a630638c38", + "version" : "0.3.1" } } ], diff --git a/apps/macos/Sources/Clawdbot/AppState.swift b/apps/macos/Sources/Clawdbot/AppState.swift index eeaf034d0e8..6ccb8336989 100644 --- a/apps/macos/Sources/Clawdbot/AppState.swift +++ b/apps/macos/Sources/Clawdbot/AppState.swift @@ -413,10 +413,17 @@ final class AppState { } private func updateRemoteTarget(host: String) { - let parsed = CommandResolver.parseSSHTarget(self.remoteTarget) - let user = parsed?.user ?? NSUserName() - let port = parsed?.port ?? 22 - let assembled = port == 22 ? "\(user)@\(host)" : "\(user)@\(host):\(port)" + let trimmed = self.remoteTarget.trimmingCharacters(in: .whitespacesAndNewlines) + guard let parsed = CommandResolver.parseSSHTarget(trimmed) else { return } + let trimmedUser = parsed.user?.trimmingCharacters(in: .whitespacesAndNewlines) + let user = (trimmedUser?.isEmpty ?? true) ? nil : trimmedUser + let port = parsed.port + let assembled: String + if let user { + assembled = port == 22 ? "\(user)@\(host)" : "\(user)@\(host):\(port)" + } else { + assembled = port == 22 ? host : "\(host):\(port)" + } if assembled != self.remoteTarget { self.remoteTarget = assembled } diff --git a/apps/macos/Sources/Clawdbot/Resources/DeviceModels/LICENSE.apple-device-identifiers.txt b/apps/macos/Sources/Clawdbot/Resources/DeviceModels/LICENSE.apple-device-identifiers.txt index 4592f65f552..d1b9e4b3ce5 100644 --- a/apps/macos/Sources/Clawdbot/Resources/DeviceModels/LICENSE.apple-device-identifiers.txt +++ b/apps/macos/Sources/Clawdbot/Resources/DeviceModels/LICENSE.apple-device-identifiers.txt @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/apps/macos/Sources/Clawdbot/Resources/DeviceModels/ios-device-identifiers.json b/apps/macos/Sources/Clawdbot/Resources/DeviceModels/ios-device-identifiers.json index 236ac2f0ed2..76caa5452ea 100644 --- a/apps/macos/Sources/Clawdbot/Resources/DeviceModels/ios-device-identifiers.json +++ b/apps/macos/Sources/Clawdbot/Resources/DeviceModels/ios-device-identifiers.json @@ -173,4 +173,4 @@ "iPod5,1": "iPod touch (5th generation)", "iPod7,1": "iPod touch (6th generation)", "iPod9,1": "iPod touch (7th generation)" -} \ No newline at end of file +} diff --git a/apps/macos/Sources/Clawdbot/Resources/DeviceModels/mac-device-identifiers.json b/apps/macos/Sources/Clawdbot/Resources/DeviceModels/mac-device-identifiers.json index 2b7483581c1..03d5a5eccb1 100644 --- a/apps/macos/Sources/Clawdbot/Resources/DeviceModels/mac-device-identifiers.json +++ b/apps/macos/Sources/Clawdbot/Resources/DeviceModels/mac-device-identifiers.json @@ -211,4 +211,4 @@ "Mac Pro (2019)", "Mac Pro (Rack, 2019)" ] -} \ No newline at end of file +} diff --git a/apps/macos/Sources/Clawdbot/Resources/Info.plist b/apps/macos/Sources/Clawdbot/Resources/Info.plist index 1c7d9619fe3..ee9e3113dea 100644 --- a/apps/macos/Sources/Clawdbot/Resources/Info.plist +++ b/apps/macos/Sources/Clawdbot/Resources/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2026.1.24 + 2026.1.25 CFBundleVersion - 202601240 + 202601250 CFBundleIconFile Clawdbot CFBundleURLTypes diff --git a/apps/shared/ClawdbotKit/Package.swift b/apps/shared/ClawdbotKit/Package.swift index 076842fcef3..88dc28b5ce5 100644 --- a/apps/shared/ClawdbotKit/Package.swift +++ b/apps/shared/ClawdbotKit/Package.swift @@ -15,7 +15,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/steipete/ElevenLabsKit", exact: "0.1.0"), - .package(url: "https://github.com/gonzalezreal/textual", exact: "0.2.0"), + .package(url: "https://github.com/gonzalezreal/textual", exact: "0.3.1"), ], targets: [ .target( diff --git a/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift b/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift index db2ffa36dbd..819014cdaca 100644 --- a/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift +++ b/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift @@ -574,46 +574,22 @@ public actor GatewayChannelActor { params: [String: AnyCodable]?, timeoutMs: Double? = nil) async throws -> Data { - do { - try await self.connect() - } catch { - throw self.wrap(error, context: "gateway connect") - } - let id = UUID().uuidString + try await self.connectOrThrow(context: "gateway connect") let effectiveTimeout = timeoutMs ?? self.defaultRequestTimeoutMs - // Encode request using the generated models to avoid JSONSerialization/ObjC bridging pitfalls. - let paramsObject: ProtoAnyCodable? = params.map { entries in - let dict = entries.reduce(into: [String: ProtoAnyCodable]()) { dict, entry in - dict[entry.key] = ProtoAnyCodable(entry.value.value) - } - return ProtoAnyCodable(dict) - } - let frame = RequestFrame( - type: "req", - id: id, - method: method, - params: paramsObject) - let data: Data - do { - data = try self.encoder.encode(frame) - } catch { - self.logger.error( - "gateway request encode failed \(method, privacy: .public) error=\(error.localizedDescription, privacy: .public)") - throw error - } + let payload = try self.encodeRequest(method: method, params: params, kind: "request") let response = try await withCheckedThrowingContinuation { (cont: CheckedContinuation) in - self.pending[id] = cont + self.pending[payload.id] = cont Task { [weak self] in guard let self else { return } try? await Task.sleep(nanoseconds: UInt64(effectiveTimeout * 1_000_000)) - await self.timeoutRequest(id: id, timeoutMs: effectiveTimeout) + await self.timeoutRequest(id: payload.id, timeoutMs: effectiveTimeout) } Task { do { - try await self.task?.send(.data(data)) + try await self.task?.send(.data(payload.data)) } catch { let wrapped = self.wrap(error, context: "gateway send \(method)") - let waiter = self.pending.removeValue(forKey: id) + let waiter = self.pending.removeValue(forKey: payload.id) // Treat send failures as a broken socket: mark disconnected and trigger reconnect. self.connected = false self.task?.cancel(with: .goingAway, reason: nil) @@ -643,6 +619,29 @@ public actor GatewayChannelActor { return Data() // Should not happen, but tolerate empty payloads. } + public func send(method: String, params: [String: AnyCodable]?) async throws { + try await self.connectOrThrow(context: "gateway connect") + let payload = try self.encodeRequest(method: method, params: params, kind: "send") + guard let task = self.task else { + throw NSError( + domain: "Gateway", + code: 5, + userInfo: [NSLocalizedDescriptionKey: "gateway socket unavailable"]) + } + do { + try await task.send(.data(payload.data)) + } catch { + let wrapped = self.wrap(error, context: "gateway send \(method)") + self.connected = false + self.task?.cancel(with: .goingAway, reason: nil) + Task { [weak self] in + guard let self else { return } + await self.scheduleReconnect() + } + throw wrapped + } + } + // Wrap low-level URLSession/WebSocket errors with context so UI can surface them. private func wrap(_ error: Error, context: String) -> Error { if let urlError = error as? URLError { @@ -657,6 +656,42 @@ public actor GatewayChannelActor { return NSError(domain: ns.domain, code: ns.code, userInfo: [NSLocalizedDescriptionKey: "\(context): \(desc)"]) } + private func connectOrThrow(context: String) async throws { + do { + try await self.connect() + } catch { + throw self.wrap(error, context: context) + } + } + + private func encodeRequest( + method: String, + params: [String: AnyCodable]?, + kind: String) throws -> (id: String, data: Data) + { + let id = UUID().uuidString + // Encode request using the generated models to avoid JSONSerialization/ObjC bridging pitfalls. + let paramsObject: ProtoAnyCodable? = params.map { entries in + let dict = entries.reduce(into: [String: ProtoAnyCodable]()) { dict, entry in + dict[entry.key] = ProtoAnyCodable(entry.value.value) + } + return ProtoAnyCodable(dict) + } + let frame = RequestFrame( + type: "req", + id: id, + method: method, + params: paramsObject) + do { + let data = try self.encoder.encode(frame) + return (id: id, data: data) + } catch { + self.logger.error( + "gateway \(kind) encode failed \(method, privacy: .public) error=\(error.localizedDescription, privacy: .public)") + throw error + } + } + private func failPending(_ error: Error) async { let waiters = self.pending self.pending.removeAll() diff --git a/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayNodeSession.swift b/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayNodeSession.swift index a2ac2ad6d57..122231f0240 100644 --- a/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayNodeSession.swift +++ b/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayNodeSession.swift @@ -143,7 +143,7 @@ public actor GatewayNodeSession { "payloadJSON": AnyCodable(payloadJSON ?? NSNull()), ] do { - _ = try await channel.request(method: "node.event", params: params, timeoutMs: 8000) + try await channel.send(method: "node.event", params: params) } catch { self.logger.error("node event failed: \(error.localizedDescription, privacy: .public)") } @@ -224,7 +224,7 @@ public actor GatewayNodeSession { ]) } do { - _ = try await channel.request(method: "node.invoke.result", params: params, timeoutMs: 15000) + try await channel.send(method: "node.invoke.result", params: params) } catch { self.logger.error("node invoke result failed: \(error.localizedDescription, privacy: .public)") } diff --git a/dist/control-ui/assets/index-08nzABV3.css b/dist/control-ui/assets/index-08nzABV3.css new file mode 100644 index 00000000000..58e39560cbd --- /dev/null +++ b/dist/control-ui/assets/index-08nzABV3.css @@ -0,0 +1 @@ +@import"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap";:root{--bg: #0c0d12;--bg-accent: #0d0e14;--bg-elevated: #181a21;--bg-hover: #252830;--bg-muted: #252830;--card: #13151c;--card-foreground: #f8fafc;--card-highlight: rgba(255, 255, 255, .04);--popover: #13151c;--popover-foreground: #f8fafc;--panel: #0c0d12;--panel-strong: #181a21;--panel-hover: #252830;--chrome: rgba(12, 13, 18, .95);--chrome-strong: rgba(12, 13, 18, .98);--text: #f8fafc;--text-strong: #ffffff;--chat-text: #f8fafc;--muted: #94a3b8;--muted-strong: #64748b;--muted-foreground: #94a3b8;--border: #333842;--border-strong: #454d5c;--border-hover: #5a6373;--input: #333842;--ring: #ff4d4d;--accent: #ff4d4d;--accent-hover: #ff6666;--accent-muted: #ff4d4d;--accent-subtle: rgba(255, 77, 77, .12);--accent-foreground: #f8fafc;--primary: #ff4d4d;--primary-foreground: #ffffff;--secondary: #252830;--secondary-foreground: #f8fafc;--accent-2: #3b82f6;--accent-2-muted: rgba(59, 130, 246, .7);--ok: #22c55e;--ok-muted: rgba(34, 197, 94, .7);--ok-subtle: rgba(34, 197, 94, .1);--destructive: #ef4444;--destructive-foreground: #fafafa;--warn: #eab308;--warn-muted: rgba(234, 179, 8, .7);--warn-subtle: rgba(234, 179, 8, .1);--danger: #ef4444;--danger-muted: rgba(239, 68, 68, .7);--danger-subtle: rgba(239, 68, 68, .1);--info: #3b82f6;--focus: rgba(255, 77, 77, .2);--focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--ring);--grid-line: rgba(255, 255, 255, .03);--theme-switch-x: 50%;--theme-switch-y: 50%;--mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace;--font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-display: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -2px rgba(0, 0, 0, .1);--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -4px rgba(0, 0, 0, .1);--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, .1), 0 8px 10px -6px rgba(0, 0, 0, .1);--radius-sm: 4px;--radius-md: 6px;--radius-lg: 8px;--radius-xl: 12px;--radius-full: 9999px;--radius: 6px;--ease-out: cubic-bezier(.16, 1, .3, 1);--ease-in-out: cubic-bezier(.4, 0, .2, 1);--duration-fast: .15s;--duration-normal: .2s;--duration-slow: .3s;color-scheme:dark}:root[data-theme=light]{--bg: #f8f8f7;--bg-accent: #f3f2f0;--bg-elevated: #ffffff;--bg-hover: #eae8e6;--bg-muted: #eae8e6;--bg-content: #f0efed;--card: #ffffff;--card-foreground: #1c1917;--card-highlight: rgba(0, 0, 0, .04);--popover: #ffffff;--popover-foreground: #1c1917;--panel: #f8f8f7;--panel-strong: #f0efed;--panel-hover: #e5e3e1;--chrome: rgba(248, 248, 247, .95);--chrome-strong: rgba(248, 248, 247, .98);--text: #44403c;--text-strong: #292524;--chat-text: #44403c;--muted: #5c5856;--muted-strong: #44403c;--muted-foreground: #5c5856;--border: #e0dedc;--border-strong: #d6d3d1;--border-hover: #a8a5a0;--input: #e0dedc;--accent: #b91c1c;--accent-hover: #dc2626;--accent-muted: #b91c1c;--accent-subtle: rgba(185, 28, 28, .18);--accent-foreground: #ffffff;--primary: #b91c1c;--primary-foreground: #ffffff;--secondary: #eae8e6;--secondary-foreground: #44403c;--ok: #15803d;--ok-muted: rgba(21, 128, 61, .75);--ok-subtle: rgba(21, 128, 61, .12);--destructive: #b91c1c;--destructive-foreground: #fafafa;--warn: #a16207;--warn-muted: rgba(161, 98, 7, .75);--warn-subtle: rgba(161, 98, 7, .12);--danger: #b91c1c;--danger-muted: rgba(185, 28, 28, .75);--danger-subtle: rgba(185, 28, 28, .12);--info: #1d4ed8;--focus: rgba(185, 28, 28, .25);--grid-line: rgba(0, 0, 0, .06);color-scheme:light}*{box-sizing:border-box}html,body{height:100%}body{margin:0;font:400 14px/1.5 var(--font-body);letter-spacing:-.011em;background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@keyframes theme-circle-transition{0%{clip-path:circle(0% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%))}to{clip-path:circle(150% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%))}}html.theme-transition{view-transition-name:theme}html.theme-transition::view-transition-old(theme){mix-blend-mode:normal;animation:none;z-index:1}html.theme-transition::view-transition-new(theme){mix-blend-mode:normal;z-index:2;animation:theme-circle-transition .4s var(--ease-out) forwards}@media(prefers-reduced-motion:reduce){html.theme-transition::view-transition-old(theme),html.theme-transition::view-transition-new(theme){animation:none!important}}clawdbot-app{display:block;position:relative;z-index:1;min-height:100vh}a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}button,input,textarea,select{font:inherit;color:inherit}::selection{background:var(--accent-subtle);color:var(--text-strong)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border);border-radius:var(--radius-full)}::-webkit-scrollbar-thumb:hover{background:var(--border-strong)}@keyframes rise{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes scale-in{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}@keyframes dashboard-enter{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}:focus-visible{outline:none;box-shadow:var(--focus-ring)}.shell{--shell-pad: 16px;--shell-gap: 16px;--shell-nav-width: 220px;--shell-topbar-height: 56px;--shell-focus-duration: .2s;--shell-focus-ease: var(--ease-out);height:100vh;display:grid;grid-template-columns:var(--shell-nav-width) minmax(0,1fr);grid-template-rows:var(--shell-topbar-height) 1fr;grid-template-areas:"topbar topbar" "nav content";gap:0;animation:dashboard-enter .4s var(--ease-out);transition:grid-template-columns var(--shell-focus-duration) var(--shell-focus-ease);overflow:hidden}@supports (height: 100dvh){.shell{height:100dvh}}.shell--chat{min-height:100vh;height:100vh;overflow:hidden}@supports (height: 100dvh){.shell--chat{height:100dvh}}.shell--nav-collapsed,.shell--chat-focus{grid-template-columns:0px minmax(0,1fr)}.shell--onboarding{grid-template-rows:0 1fr}.shell--onboarding .topbar{display:none}.shell--onboarding .content{padding-top:0}.shell--chat-focus .content{padding-top:0;gap:0}.topbar{grid-area:topbar;position:sticky;top:0;z-index:40;display:flex;justify-content:space-between;align-items:center;gap:16px;padding:0 20px;height:var(--shell-topbar-height);border-bottom:1px solid var(--border);background:var(--bg)}.topbar-left{display:flex;align-items:center;gap:12px}.topbar .nav-collapse-toggle{width:36px;height:36px;margin-bottom:0}.topbar .nav-collapse-toggle__icon{width:20px;height:20px}.topbar .nav-collapse-toggle__icon svg{width:20px;height:20px}.brand{display:flex;align-items:center;gap:10px}.brand-logo{width:28px;height:28px;flex-shrink:0}.brand-logo img{width:100%;height:100%;object-fit:contain}.brand-text{display:flex;flex-direction:column;gap:1px}.brand-title{font-size:15px;font-weight:600;letter-spacing:-.02em;line-height:1.1;color:var(--text-strong)}.brand-sub{font-size:11px;font-weight:500;color:var(--muted);letter-spacing:.02em;line-height:1}.topbar-status{display:flex;align-items:center;gap:8px}.topbar-status .pill{padding:6px 10px;gap:6px;font-size:12px;font-weight:500;height:32px;box-sizing:border-box}.topbar-status .pill .mono{display:flex;align-items:center;line-height:1;margin-top:0}.topbar-status .statusDot{width:6px;height:6px}.topbar-status .theme-toggle{--theme-item: 24px;--theme-gap: 2px;--theme-pad: 3px}.topbar-status .theme-icon{width:12px;height:12px}.nav{grid-area:nav;overflow-y:auto;overflow-x:hidden;padding:16px 12px;border-right:1px solid var(--border);background:var(--bg);transition:width var(--shell-focus-duration) var(--shell-focus-ease),padding var(--shell-focus-duration) var(--shell-focus-ease),opacity var(--shell-focus-duration) var(--shell-focus-ease);min-height:0}.shell--chat-focus .nav{width:0;padding:0;border-width:0;overflow:hidden;pointer-events:none;opacity:0}.nav--collapsed{width:0;min-width:0;padding:0;overflow:hidden;border:none;opacity:0;pointer-events:none}.nav-collapse-toggle{width:32px;height:32px;display:flex;align-items:center;justify-content:center;background:transparent;border:1px solid transparent;border-radius:var(--radius-md);cursor:pointer;transition:background var(--duration-fast) ease,border-color var(--duration-fast) ease;margin-bottom:16px}.nav-collapse-toggle:hover{background:var(--bg-hover);border-color:var(--border)}.nav-collapse-toggle__icon{display:flex;align-items:center;justify-content:center;width:18px;height:18px;color:var(--muted);transition:color var(--duration-fast) ease}.nav-collapse-toggle__icon svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round}.nav-collapse-toggle:hover .nav-collapse-toggle__icon{color:var(--text)}.nav-group{margin-bottom:20px;display:grid;gap:2px}.nav-group:last-child{margin-bottom:0}.nav-group__items{display:grid;gap:1px}.nav-group--collapsed .nav-group__items{display:none}.nav-label{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;padding:6px 10px;font-size:11px;font-weight:500;color:var(--muted);margin-bottom:4px;background:transparent;border:none;cursor:pointer;text-align:left;border-radius:var(--radius-sm);transition:color var(--duration-fast) ease,background var(--duration-fast) ease}.nav-label:hover{color:var(--text);background:var(--bg-hover)}.nav-label--static{cursor:default}.nav-label--static:hover{color:var(--muted);background:transparent}.nav-label__text{flex:1}.nav-label__chevron{font-size:10px;opacity:.5;transition:transform var(--duration-fast) ease}.nav-group--collapsed .nav-label__chevron{transform:rotate(-90deg)}.nav-item{position:relative;display:flex;align-items:center;justify-content:flex-start;gap:10px;padding:8px 10px;border-radius:var(--radius-md);border:1px solid transparent;background:transparent;color:var(--muted);cursor:pointer;text-decoration:none;transition:border-color var(--duration-fast) ease,background var(--duration-fast) ease,color var(--duration-fast) ease}.nav-item__icon{width:16px;height:16px;display:flex;align-items:center;justify-content:center;flex-shrink:0;opacity:.7;transition:opacity var(--duration-fast) ease}.nav-item__icon svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round}.nav-item__text{font-size:13px;font-weight:500;white-space:nowrap}.nav-item:hover{color:var(--text);background:var(--bg-hover);text-decoration:none}.nav-item:hover .nav-item__icon{opacity:1}.nav-item.active{color:var(--text-strong);background:var(--accent-subtle)}.nav-item.active .nav-item__icon{opacity:1;color:var(--accent)}.content{grid-area:content;padding:8px 8px 24px;display:flex;flex-direction:column;gap:20px;min-height:0;overflow-y:auto;overflow-x:hidden}:root[data-theme=light] .content{background:var(--bg-content)}.content--chat{overflow:hidden;padding-bottom:0}.content-header{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;padding:4px 8px;overflow:hidden;transform-origin:top center;transition:opacity var(--shell-focus-duration) var(--shell-focus-ease),transform var(--shell-focus-duration) var(--shell-focus-ease),max-height var(--shell-focus-duration) var(--shell-focus-ease),padding var(--shell-focus-duration) var(--shell-focus-ease);max-height:80px}.shell--chat-focus .content-header{opacity:0;transform:translateY(-8px);max-height:0px;padding:0;pointer-events:none}.page-title{font-size:24px;font-weight:600;letter-spacing:-.02em;line-height:1.2;color:var(--text-strong)}.page-sub{color:var(--muted);font-size:13px;font-weight:400;margin-top:4px}.page-meta{display:flex;gap:8px}.content--chat .content-header{flex-direction:row;align-items:center;justify-content:space-between;gap:16px}.content--chat .content-header>div:first-child{text-align:left}.content--chat .page-meta{justify-content:flex-start}.content--chat .chat-controls{flex-shrink:0}.grid{display:grid;gap:16px}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.stat-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(140px,1fr))}.note-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}.row{display:flex;gap:10px;align-items:center}.stack{display:grid;gap:12px}.filters{display:flex;flex-wrap:wrap;gap:8px;align-items:center}@media(max-width:1100px){.shell{--shell-pad: 12px;--shell-gap: 12px;grid-template-columns:1fr;grid-template-rows:auto auto 1fr;grid-template-areas:"topbar" "nav" "content"}.nav{position:static;max-height:none;display:flex;gap:6px;overflow-x:auto;border-right:none;border-bottom:1px solid var(--border);padding:10px 14px;background:var(--bg)}.nav-group{grid-auto-flow:column;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));margin-bottom:0}.grid-cols-2,.grid-cols-3{grid-template-columns:1fr}.topbar{position:static;padding:12px 14px;gap:10px}.topbar-status{flex-wrap:wrap}.table-head,.table-row,.list-item{grid-template-columns:1fr}}@media(max-width:1100px){.nav{display:flex;flex-direction:row;flex-wrap:nowrap;gap:4px;padding:10px 14px;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}.nav::-webkit-scrollbar{display:none}.nav-group,.nav-group__items{display:contents}.nav-label{display:none}.nav-group--collapsed .nav-group__items{display:contents}.nav-item{padding:8px 14px;font-size:13px;border-radius:var(--radius-md);white-space:nowrap;flex-shrink:0}}@media(max-width:600px){.shell{--shell-pad: 8px;--shell-gap: 8px}.topbar{padding:10px 12px;gap:8px;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center}.brand{flex:1;min-width:0}.brand-title{font-size:14px}.brand-sub{display:none}.topbar-status{gap:6px;width:auto;flex-wrap:nowrap}.topbar-status .pill{padding:4px 8px;font-size:11px;gap:4px}.topbar-status .pill .mono{display:none}.topbar-status .pill span:nth-child(2){display:none}.nav{padding:8px 10px;gap:4px;-webkit-overflow-scrolling:touch;scrollbar-width:none}.nav::-webkit-scrollbar{display:none}.nav-group{display:contents}.nav-label{display:none}.nav-item{padding:6px 10px;font-size:12px;border-radius:var(--radius-md);white-space:nowrap;flex-shrink:0}.content-header{display:none}.content{padding:4px 4px 16px;gap:12px}.card{padding:12px;border-radius:var(--radius-md)}.card-title{font-size:13px}.stat-grid{gap:8px;grid-template-columns:repeat(2,1fr)}.stat{padding:10px;border-radius:var(--radius-md)}.stat-label{font-size:11px}.stat-value{font-size:18px}.note-grid{grid-template-columns:1fr;gap:8px}.form-grid{grid-template-columns:1fr;gap:10px}.field input,.field textarea,.field select{padding:8px 10px;border-radius:var(--radius-md);font-size:14px}.btn{padding:8px 12px;font-size:12px}.pill{padding:4px 10px;font-size:12px}.chat-header{flex-direction:column;align-items:stretch;gap:8px}.chat-header__left{flex-direction:column;align-items:stretch}.chat-header__right{justify-content:space-between}.chat-session{min-width:unset;width:100%}.chat-thread{margin-top:8px;padding:12px 8px}.chat-msg{max-width:90%}.chat-bubble{padding:8px 12px;border-radius:var(--radius-md)}.chat-compose{gap:8px}.chat-compose__field textarea{min-height:60px;padding:8px 10px;border-radius:var(--radius-md);font-size:14px}.log-stream{border-radius:var(--radius-md);max-height:380px}.log-row{grid-template-columns:1fr;gap:4px;padding:8px}.log-time{font-size:10px}.log-level{font-size:9px}.log-subsystem{font-size:11px}.log-message{font-size:12px}.list-item{padding:10px;border-radius:var(--radius-md)}.list-title{font-size:13px}.list-sub{font-size:11px}.code-block{padding:8px;border-radius:var(--radius-md);font-size:11px}.theme-toggle{--theme-item: 24px;--theme-gap: 2px;--theme-pad: 3px}.theme-icon{width:12px;height:12px}}@media(max-width:400px){.shell{--shell-pad: 4px}.topbar{padding:8px 10px}.brand-title{font-size:13px}.nav{padding:6px 8px}.nav-item{padding:6px 8px;font-size:11px}.content{padding:4px 4px 12px;gap:10px}.card{padding:10px}.stat{padding:8px}.stat-value{font-size:16px}.chat-bubble{padding:8px 10px}.chat-compose__field textarea{min-height:52px;padding:8px 10px;font-size:13px}.btn{padding:6px 10px;font-size:11px}.topbar-status .pill{padding:3px 6px;font-size:10px}.theme-toggle{--theme-item: 22px;--theme-gap: 2px;--theme-pad: 2px}.theme-icon{width:11px;height:11px}}.chat{position:relative;display:flex;flex-direction:column;flex:1 1 0;height:100%;min-height:0;overflow:hidden;background:transparent!important;border:none!important;box-shadow:none!important}.chat-header{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:nowrap;flex-shrink:0;padding-bottom:12px;margin-bottom:12px;background:transparent}.chat-header__left{display:flex;align-items:center;gap:12px;flex-wrap:wrap;min-width:0}.chat-session{min-width:180px}.chat-thread{flex:1 1 0;overflow-y:auto;overflow-x:hidden;padding:12px 4px;margin:0 -4px;min-height:0;border-radius:12px;background:transparent}.chat-focus-exit{position:absolute;top:12px;right:12px;z-index:100;width:32px;height:32px;border-radius:50%;border:1px solid var(--border);background:var(--panel);color:var(--muted);font-size:20px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s ease-out,color .15s ease-out,border-color .15s ease-out;box-shadow:0 4px 12px #0003}.chat-focus-exit:hover{background:var(--panel-strong);color:var(--text);border-color:var(--accent)}.chat-focus-exit svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round}.chat-compose{position:sticky;bottom:0;flex-shrink:0;display:flex;align-items:stretch;gap:12px;margin-top:auto;padding:12px 4px 4px;background:linear-gradient(to bottom,transparent,var(--bg) 20%);z-index:10}:root[data-theme=light] .chat-compose{background:linear-gradient(to bottom,transparent,var(--bg-content) 20%)}.chat-compose__field{flex:1 1 auto;min-width:0;display:flex;align-items:stretch}.chat-compose__field>span{display:none}.chat-compose .chat-compose__field textarea{width:100%;height:40px;min-height:40px;max-height:150px;padding:9px 12px;border-radius:8px;resize:vertical;white-space:pre-wrap;font-family:var(--font-body);font-size:14px;line-height:1.45}.chat-compose__field textarea:disabled{opacity:.7;cursor:not-allowed}.chat-compose__actions{flex-shrink:0;display:flex;align-items:stretch;gap:8px}.chat-compose .chat-compose__actions .btn{padding:0 16px;font-size:13px;height:40px;min-height:40px;max-height:40px;line-height:1;white-space:nowrap;box-sizing:border-box}.chat-controls{display:flex;align-items:center;justify-content:flex-start;gap:12px;flex-wrap:wrap}.chat-controls__session{min-width:140px}.chat-controls__thinking{display:flex;align-items:center;gap:6px;font-size:13px}.btn--icon{padding:8px!important;min-width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--border);background:#ffffff0f}.chat-controls__separator{color:#fff6;font-size:18px;margin:0 8px;font-weight:300}:root[data-theme=light] .chat-controls__separator{color:#1018284d}.btn--icon:hover{background:#ffffff1f;border-color:#fff3}:root[data-theme=light] .btn--icon{background:#fff;border-color:var(--border);box-shadow:0 1px 2px #1018280d;color:var(--muted)}:root[data-theme=light] .btn--icon:hover{background:#fff;border-color:var(--border-strong);color:var(--text)}.btn--icon svg{display:block;width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round}.chat-controls__session select{padding:6px 10px;font-size:13px}.chat-controls__thinking{display:flex;align-items:center;gap:4px;font-size:12px;padding:4px 10px;background:#ffffff0a;border-radius:6px;border:1px solid var(--border)}:root[data-theme=light] .chat-controls__thinking{background:#ffffffe6;border-color:#10182826}@media(max-width:640px){.chat-session{min-width:140px}.chat-compose{grid-template-columns:1fr}.chat-controls{flex-wrap:wrap;gap:8px}.chat-controls__session{min-width:120px}}.chat-thinking{margin-bottom:10px;padding:10px 12px;border-radius:10px;border:1px dashed rgba(255,255,255,.18);background:#ffffff0a;color:var(--muted);font-size:12px;line-height:1.4}:root[data-theme=light] .chat-thinking{border-color:#10182840;background:#1018280a}.chat-text{font-size:14px;line-height:1.5;word-wrap:break-word;overflow-wrap:break-word}.chat-text :where(p+p,p+ul,p+ol,p+pre,p+blockquote){margin-top:.75em}.chat-text :where(ul,ol){padding-left:1.5em}.chat-text :where(a){color:var(--accent);text-decoration:underline;text-underline-offset:2px}.chat-text :where(a:hover){opacity:.8}.chat-text :where(:not(pre)>code){background:#00000026;padding:.15em .4em;border-radius:4px}.chat-text :where(pre){background:#00000026;border-radius:6px;padding:10px 12px;overflow-x:auto}.chat-text :where(pre code){background:none;padding:0}.chat-text :where(blockquote){border-left:3px solid var(--border-strong);margin-left:0;color:var(--muted);background:#ffffff05;padding:8px 12px;border-radius:0 var(--radius-sm) var(--radius-sm) 0}.chat-text :where(blockquote blockquote){margin-top:8px;border-left-color:var(--border-hover);background:#ffffff08}.chat-text :where(blockquote blockquote blockquote){border-left-color:var(--muted-strong);background:#ffffff0a}:root[data-theme=light] .chat-text :where(blockquote){background:#00000008}:root[data-theme=light] .chat-text :where(blockquote blockquote){background:#0000000d}:root[data-theme=light] .chat-text :where(blockquote blockquote blockquote){background:#0000000a}:root[data-theme=light] .chat-text :where(:not(pre)>code){background:#00000014;border:1px solid rgba(0,0,0,.1)}:root[data-theme=light] .chat-text :where(pre){background:#0000000d;border:1px solid rgba(0,0,0,.1)}.chat-text :where(hr){border:none;border-top:1px solid var(--border);margin:1em 0}.chat-group{display:flex;gap:12px;align-items:flex-start;margin-bottom:16px;margin-left:4px;margin-right:16px}.chat-group.user{flex-direction:row-reverse;justify-content:flex-start}.chat-group-messages{display:flex;flex-direction:column;gap:2px;max-width:min(900px,calc(100% - 60px))}.chat-group.user .chat-group-messages{align-items:flex-end}.chat-group.user .chat-group-footer{justify-content:flex-end}.chat-group-footer{display:flex;gap:8px;align-items:baseline;margin-top:6px}.chat-sender-name{font-weight:500;font-size:12px;color:var(--muted)}.chat-group-timestamp{font-size:11px;color:var(--muted);opacity:.7}.chat-avatar{width:40px;height:40px;border-radius:8px;background:var(--panel-strong);display:grid;place-items:center;font-weight:600;font-size:14px;flex-shrink:0;align-self:flex-end;margin-bottom:4px}.chat-avatar.user{background:var(--accent-subtle);color:var(--accent)}.chat-avatar.assistant,.chat-avatar.other,.chat-avatar.tool{background:var(--secondary);color:var(--muted)}img.chat-avatar{display:block;object-fit:cover;object-position:center}.chat-bubble{position:relative;display:inline-block;border:1px solid transparent;background:var(--card);border-radius:var(--radius-lg);padding:10px 14px;box-shadow:none;transition:background .15s ease-out,border-color .15s ease-out;max-width:100%;word-wrap:break-word}.chat-bubble.has-copy{padding-right:36px}.chat-copy-btn{position:absolute;top:6px;right:8px;border:1px solid var(--border);background:var(--bg);color:var(--muted);border-radius:var(--radius-md);padding:4px 6px;font-size:14px;line-height:1;cursor:pointer;opacity:0;pointer-events:none;transition:opacity .12s ease-out,background .12s ease-out}.chat-copy-btn__icon{display:inline-flex;width:14px;height:14px;position:relative}.chat-copy-btn__icon svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round}.chat-copy-btn__icon-copy,.chat-copy-btn__icon-check{position:absolute;top:0;left:0;transition:opacity .15s ease}.chat-copy-btn__icon-check,.chat-copy-btn[data-copied="1"] .chat-copy-btn__icon-copy{opacity:0}.chat-copy-btn[data-copied="1"] .chat-copy-btn__icon-check{opacity:1}.chat-bubble:hover .chat-copy-btn{opacity:1;pointer-events:auto}.chat-copy-btn:hover{background:var(--bg-hover)}.chat-copy-btn[data-copying="1"]{opacity:0;pointer-events:none}.chat-copy-btn[data-error="1"]{opacity:1;pointer-events:auto;border-color:var(--danger-subtle);background:var(--danger-subtle);color:var(--danger)}.chat-copy-btn[data-copied="1"]{opacity:1;pointer-events:auto;border-color:var(--ok-subtle);background:var(--ok-subtle);color:var(--ok)}.chat-copy-btn:focus-visible{opacity:1;pointer-events:auto;outline:2px solid var(--accent);outline-offset:2px}@media(hover:none){.chat-copy-btn{opacity:1;pointer-events:auto}}:root[data-theme=light] .chat-bubble{border-color:var(--border);box-shadow:inset 0 1px 0 var(--card-highlight)}.chat-bubble:hover{background:var(--bg-hover)}.chat-group.user .chat-bubble{background:var(--accent-subtle);border-color:transparent}:root[data-theme=light] .chat-group.user .chat-bubble{border-color:#ea580c33;background:#fb923c1f}.chat-group.user .chat-bubble:hover{background:#ff4d4d26}.chat-bubble.streaming{animation:pulsing-border 1.5s ease-out infinite}@keyframes pulsing-border{0%,to{border-color:var(--border)}50%{border-color:var(--accent)}}.chat-bubble.fade-in{animation:fade-in .2s ease-out}@keyframes fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.chat-tool-card{border:1px solid var(--border);border-radius:8px;padding:12px;margin-top:8px;background:var(--card);box-shadow:inset 0 1px 0 var(--card-highlight);transition:border-color .15s ease-out,background .15s ease-out;max-height:120px;overflow:hidden}.chat-tool-card:hover{border-color:var(--border-strong);background:var(--bg-hover)}.chat-tool-card:first-child{margin-top:0}.chat-tool-card--clickable{cursor:pointer}.chat-tool-card--clickable:focus{outline:2px solid var(--accent);outline-offset:2px}.chat-tool-card__header{display:flex;justify-content:space-between;align-items:center;gap:8px}.chat-tool-card__title{display:inline-flex;align-items:center;gap:6px;font-weight:600;font-size:13px;line-height:1.2}.chat-tool-card__icon{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex-shrink:0}.chat-tool-card__icon svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round}.chat-tool-card__action{display:inline-flex;align-items:center;gap:4px;font-size:12px;color:var(--accent);opacity:.8;transition:opacity .15s ease-out}.chat-tool-card__action svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round}.chat-tool-card--clickable:hover .chat-tool-card__action{opacity:1}.chat-tool-card__status{display:inline-flex;align-items:center;color:var(--ok)}.chat-tool-card__status svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round}.chat-tool-card__status-text{font-size:11px;margin-top:4px}.chat-tool-card__detail{font-size:12px;color:var(--muted);margin-top:4px}.chat-tool-card__preview{font-size:11px;color:var(--muted);margin-top:8px;padding:8px 10px;background:var(--secondary);border-radius:var(--radius-md);white-space:pre-wrap;overflow:hidden;max-height:44px;line-height:1.4;border:1px solid var(--border)}.chat-tool-card--clickable:hover .chat-tool-card__preview{background:var(--bg-hover);border-color:var(--border-strong)}.chat-tool-card__inline{font-size:11px;color:var(--text);margin-top:6px;padding:6px 8px;background:var(--secondary);border-radius:var(--radius-sm);white-space:pre-wrap;word-break:break-word}.chat-reading-indicator{background:transparent;border:1px solid var(--border);padding:12px;display:inline-flex}.chat-reading-indicator__dots{display:flex;gap:6px;align-items:center}.chat-reading-indicator__dots span{width:6px;height:6px;border-radius:50%;background:var(--muted);animation:reading-pulse 1.4s ease-in-out infinite}.chat-reading-indicator__dots span:nth-child(1){animation-delay:0s}.chat-reading-indicator__dots span:nth-child(2){animation-delay:.2s}.chat-reading-indicator__dots span:nth-child(3){animation-delay:.4s}@keyframes reading-pulse{0%,60%,to{opacity:.3;transform:scale(.8)}30%{opacity:1;transform:scale(1)}}.chat-split-container{display:flex;gap:0;flex:1;min-height:0;height:100%}.chat-main{min-width:400px;display:flex;flex-direction:column;overflow:hidden;transition:flex .25s ease-out}.chat-sidebar{flex:1;min-width:300px;border-left:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden;animation:slide-in .2s ease-out}@keyframes slide-in{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}.sidebar-panel{display:flex;flex-direction:column;height:100%;background:var(--panel)}.sidebar-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--border);flex-shrink:0;position:sticky;top:0;z-index:10;background:var(--panel)}.sidebar-header .btn{padding:4px 8px;font-size:14px;min-width:auto;line-height:1}.sidebar-title{font-weight:600;font-size:14px}.sidebar-content{flex:1;overflow:auto;padding:16px}.sidebar-markdown{font-size:14px;line-height:1.5}.sidebar-markdown pre{background:#0000001f;border-radius:4px;padding:12px;overflow-x:auto}.sidebar-markdown code{font-family:var(--mono);font-size:13px}@media(max-width:768px){.chat-split-container--open{position:fixed;inset:0;z-index:1000}.chat-split-container--open .chat-main{display:none}.chat-split-container--open .chat-sidebar{width:100%;min-width:0;border-left:none}}.card{border:1px solid var(--border);background:var(--card);border-radius:var(--radius-lg);padding:16px;animation:rise .3s var(--ease-out);transition:border-color var(--duration-fast) ease;box-shadow:inset 0 1px 0 var(--card-highlight)}.card:hover{border-color:var(--border-strong)}.card-title{font-size:14px;font-weight:600;color:var(--text-strong)}.card-sub{color:var(--muted);font-size:13px;margin-top:4px}.stat{background:var(--card);border-radius:var(--radius-md);padding:12px 14px;border:1px solid var(--border);transition:border-color var(--duration-fast) ease;box-shadow:inset 0 1px 0 var(--card-highlight)}.stat:hover{border-color:var(--border-strong)}.stat-label{color:var(--muted);font-size:12px;font-weight:500}.stat-value{font-size:20px;font-weight:600;margin-top:4px;letter-spacing:-.02em}.stat-value.ok{color:var(--ok)}.stat-value.warn{color:var(--warn)}.stat-card{display:grid;gap:4px}.note-title{font-weight:600}.status-list{display:grid;gap:8px}.status-list div{display:flex;justify-content:space-between;gap:12px;padding:8px 0;border-bottom:1px solid var(--border)}.status-list div:last-child{border-bottom:none}.account-count{margin-top:10px;font-size:12px;font-weight:500;color:var(--muted)}.account-card-list{margin-top:16px;display:grid;gap:12px}.account-card{border:1px solid var(--border);border-radius:var(--radius-md);padding:12px;background:var(--bg-elevated);transition:border-color var(--duration-fast) ease}.account-card:hover{border-color:var(--border-strong)}.account-card-header{display:flex;justify-content:space-between;align-items:baseline;gap:12px}.account-card-title{font-weight:500}.account-card-id{font-family:var(--mono);font-size:12px;color:var(--muted)}.account-card-status{margin-top:10px;font-size:13px}.account-card-status div{padding:4px 0}.account-card-error{margin-top:8px;color:var(--danger);font-size:12px}.label{color:var(--muted);font-size:12px;font-weight:500}.pill{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--border);padding:6px 12px;border-radius:var(--radius-full);background:var(--secondary);font-size:13px;font-weight:500;transition:border-color var(--duration-fast) ease}.pill:hover{border-color:var(--border-strong)}.pill.danger{border-color:var(--danger-subtle);background:var(--danger-subtle);color:var(--danger)}.theme-toggle{--theme-item: 28px;--theme-gap: 2px;--theme-pad: 4px;position:relative}.theme-toggle__track{position:relative;display:grid;grid-template-columns:repeat(3,var(--theme-item));gap:var(--theme-gap);padding:var(--theme-pad);border-radius:var(--radius-full);border:1px solid var(--border);background:var(--secondary)}.theme-toggle__indicator{position:absolute;top:50%;left:var(--theme-pad);width:var(--theme-item);height:var(--theme-item);border-radius:var(--radius-full);transform:translateY(-50%) translate(calc(var(--theme-index, 0) * (var(--theme-item) + var(--theme-gap))));background:var(--accent);transition:transform var(--duration-normal) var(--ease-out);z-index:0}.theme-toggle__button{height:var(--theme-item);width:var(--theme-item);display:grid;place-items:center;border:0;border-radius:var(--radius-full);background:transparent;color:var(--muted);cursor:pointer;position:relative;z-index:1;transition:color var(--duration-fast) ease}.theme-toggle__button:hover{color:var(--text)}.theme-toggle__button.active{color:var(--accent-foreground)}.theme-toggle__button.active .theme-icon{stroke:var(--accent-foreground)}.theme-icon{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round}.statusDot{width:8px;height:8px;border-radius:var(--radius-full);background:var(--danger)}.statusDot.ok{background:var(--ok)}.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;border:1px solid var(--border);background:var(--bg-elevated);padding:8px 14px;border-radius:var(--radius-md);font-size:13px;font-weight:500;cursor:pointer;transition:border-color var(--duration-fast) ease,background var(--duration-fast) ease}.btn:hover{background:var(--bg-hover);border-color:var(--border-strong)}.btn:active{background:var(--secondary)}.btn svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}.btn.primary{border-color:var(--accent);background:var(--accent);color:var(--primary-foreground)}.btn.primary:hover{background:var(--accent-hover);border-color:var(--accent-hover)}.btn-kbd{display:inline-flex;align-items:center;justify-content:center;margin-left:6px;padding:2px 5px;font-family:var(--mono);font-size:11px;font-weight:500;line-height:1;border-radius:4px;background:#ffffff26;color:inherit;opacity:.8}.btn.primary .btn-kbd{background:#fff3}:root[data-theme=light] .btn-kbd{background:#00000014}:root[data-theme=light] .btn.primary .btn-kbd{background:#ffffff40}.btn.active{border-color:var(--accent);background:var(--accent-subtle);color:var(--accent)}.btn.danger{border-color:transparent;background:var(--danger-subtle);color:var(--danger)}.btn.danger:hover{background:#ef444426}.btn--sm{padding:6px 10px;font-size:12px}.btn:disabled{opacity:.5;cursor:not-allowed}.field{display:grid;gap:6px}.field.full{grid-column:1 / -1}.field span{color:var(--muted);font-size:13px;font-weight:500}.field input,.field textarea,.field select{border:1px solid var(--input);background:var(--card);border-radius:var(--radius-md);padding:8px 12px;outline:none;box-shadow:inset 0 1px 0 var(--card-highlight);transition:border-color var(--duration-fast) ease,box-shadow var(--duration-fast) ease}.field input:focus,.field textarea:focus,.field select:focus{border-color:var(--ring);box-shadow:var(--focus-ring)}.field select{appearance:none;padding-right:36px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23a1a1aa' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;cursor:pointer}.field textarea{font-family:var(--mono);min-height:160px;resize:vertical;white-space:pre;line-height:1.5}.field.checkbox{grid-template-columns:auto 1fr;align-items:center}.config-form .field.checkbox{grid-template-columns:18px minmax(0,1fr);column-gap:10px}.config-form .field.checkbox input[type=checkbox]{margin:0;width:16px;height:16px;accent-color:var(--accent)}.form-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}:root[data-theme=light] .field input,:root[data-theme=light] .field textarea,:root[data-theme=light] .field select{background:var(--card);border-color:var(--input)}:root[data-theme=light] .btn{background:var(--bg);border-color:var(--input)}:root[data-theme=light] .btn:hover{background:var(--bg-hover)}:root[data-theme=light] .btn.primary{background:var(--accent);border-color:var(--accent)}.muted{color:var(--muted)}.mono{font-family:var(--mono)}.callout{padding:12px 14px;border-radius:var(--radius-md);background:var(--secondary);border:1px solid var(--border);font-size:13px}.callout.danger{border-color:var(--danger-subtle);background:var(--danger-subtle);color:var(--danger)}.callout.info{border-color:#3b82f633;background:#3b82f61a;color:var(--info)}.callout.success{border-color:var(--ok-subtle);background:var(--ok-subtle);color:var(--ok)}.compaction-indicator{font-size:13px;padding:10px 12px;margin-bottom:8px;animation:fade-in .2s var(--ease-out)}.compaction-indicator--active{animation:compaction-pulse 1.5s ease-in-out infinite}.compaction-indicator--complete{animation:fade-in .2s var(--ease-out)}@keyframes compaction-pulse{0%,to{opacity:.7}50%{opacity:1}}.code-block{font-family:var(--mono);font-size:13px;line-height:1.5;background:var(--secondary);padding:12px;border-radius:var(--radius-md);border:1px solid var(--border);max-height:360px;overflow:auto}:root[data-theme=light] .code-block,:root[data-theme=light] .list-item,:root[data-theme=light] .table-row,:root[data-theme=light] .chip{background:var(--bg)}.list{display:grid;gap:8px;container-type:inline-size}.list-item{display:grid;grid-template-columns:minmax(0,1fr) minmax(200px,260px);gap:16px;align-items:start;border:1px solid var(--border);border-radius:var(--radius-md);padding:12px;background:var(--card);transition:border-color var(--duration-fast) ease}.list-item-clickable{cursor:pointer}.list-item-clickable:hover{border-color:var(--border-strong)}.list-item-selected{border-color:var(--accent);box-shadow:var(--focus-ring)}.list-main{display:grid;gap:4px;min-width:0}.list-title{font-weight:500}.list-sub{color:var(--muted);font-size:12px}.list-meta{text-align:right;color:var(--muted);font-size:12px;display:grid;gap:4px;min-width:200px}.list-meta .btn{padding:6px 10px}.list-meta .field input,.list-meta .field textarea,.list-meta .field select{width:100%}@container (max-width: 560px){.list-item{grid-template-columns:1fr}.list-meta{min-width:0;text-align:left}}.chip-row{display:flex;flex-wrap:wrap;gap:6px}.chip{font-size:12px;font-weight:500;border:1px solid var(--border);border-radius:var(--radius-full);padding:4px 10px;color:var(--muted);background:var(--secondary);transition:border-color var(--duration-fast) ease}.chip input{margin-right:6px}.chip-ok{color:var(--ok);border-color:var(--ok-subtle)}.chip-warn{color:var(--warn);border-color:var(--warn-subtle)}.table{display:grid;gap:6px}.table-head,.table-row{display:grid;grid-template-columns:1.4fr 1fr .8fr .7fr .8fr .8fr .8fr .8fr .6fr;gap:12px;align-items:center}.table-head{font-size:12px;font-weight:500;color:var(--muted);padding:0 12px}.table-row{border:1px solid var(--border);padding:10px 12px;border-radius:var(--radius-md);background:var(--card);transition:border-color var(--duration-fast) ease}.table-row:hover{border-color:var(--border-strong)}.session-link{text-decoration:none;color:var(--accent);font-weight:500}.session-link:hover{text-decoration:underline}.log-stream{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--card);max-height:500px;overflow:auto;container-type:inline-size}.log-row{display:grid;grid-template-columns:90px 70px minmax(140px,200px) minmax(0,1fr);gap:12px;align-items:start;padding:8px 12px;border-bottom:1px solid var(--border);font-size:12px;transition:background var(--duration-fast) ease}.log-row:hover{background:var(--bg-hover)}.log-row:last-child{border-bottom:none}.log-time{color:var(--muted);font-family:var(--mono)}.log-level{font-size:11px;font-weight:500;border:1px solid var(--border);border-radius:var(--radius-sm);padding:2px 6px;width:fit-content}.log-level.trace,.log-level.debug{color:var(--muted)}.log-level.info{color:var(--info);border-color:#3b82f64d}.log-level.warn{color:var(--warn);border-color:var(--warn-subtle)}.log-level.error,.log-level.fatal{color:var(--danger);border-color:var(--danger-subtle)}.log-chip.trace,.log-chip.debug{color:var(--muted)}.log-chip.info{color:var(--info);border-color:#3b82f64d}.log-chip.warn{color:var(--warn);border-color:var(--warn-subtle)}.log-chip.error,.log-chip.fatal{color:var(--danger);border-color:var(--danger-subtle)}.log-subsystem{color:var(--muted);font-family:var(--mono)}.log-message{white-space:pre-wrap;word-break:break-word;font-family:var(--mono)}@container (max-width: 620px){.log-row{grid-template-columns:70px 60px minmax(0,1fr)}.log-subsystem{display:none}}.chat{display:flex;flex-direction:column;min-height:0}.shell--chat .chat{flex:1}.chat-header{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;flex-wrap:wrap}.chat-header__left{display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap;min-width:0}.chat-header__right{display:flex;align-items:center;gap:8px}.chat-session{min-width:240px}.chat-thread{margin-top:16px;display:flex;flex-direction:column;gap:12px;flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding:16px 12px;min-width:0;border-radius:0;border:none;background:transparent}.chat-queue{margin-top:12px;padding:12px;border-radius:var(--radius-lg);border:1px solid var(--border);background:var(--card);display:grid;gap:8px}.chat-queue__title{font-family:var(--mono);font-size:12px;font-weight:500;color:var(--muted)}.chat-queue__list{display:grid;gap:8px}.chat-queue__item{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:start;gap:12px;padding:10px 12px;border-radius:var(--radius-md);border:1px dashed var(--border-strong);background:var(--secondary)}.chat-queue__text{color:var(--chat-text);font-size:13px;line-height:1.45;white-space:pre-wrap;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}.chat-queue__remove{align-self:start;padding:4px 10px;font-size:12px;line-height:1}.chat-line{display:flex}.chat-line.user{justify-content:flex-end}.chat-line.assistant,.chat-line.other{justify-content:flex-start}.chat-msg{display:grid;gap:6px;max-width:min(700px,82%)}.chat-line.user .chat-msg{justify-items:end}.chat-bubble{border:1px solid transparent;background:var(--card);border-radius:var(--radius-lg);padding:10px 14px;min-width:0}:root[data-theme=light] .chat-bubble{border-color:var(--border);background:var(--bg)}.chat-line.user .chat-bubble{border-color:transparent;background:var(--accent-subtle)}:root[data-theme=light] .chat-line.user .chat-bubble{border-color:#ea580c33;background:#fb923c1f}.chat-line.assistant .chat-bubble{border-color:transparent;background:var(--secondary)}:root[data-theme=light] .chat-line.assistant .chat-bubble{border-color:var(--border);background:var(--bg-muted)}@keyframes chatStreamPulse{0%,to{border-color:var(--border)}50%{border-color:var(--accent)}}.chat-bubble.streaming{animation:chatStreamPulse 1.5s ease-in-out infinite}@media(prefers-reduced-motion:reduce){.chat-bubble.streaming{animation:none;border-color:var(--accent)}}.chat-bubble.chat-reading-indicator{width:fit-content;padding:10px 16px}.chat-reading-indicator__dots{display:inline-flex;align-items:center;gap:4px;height:12px}.chat-reading-indicator__dots>span{display:inline-block;width:6px;height:6px;border-radius:var(--radius-full);background:var(--muted);opacity:.6;transform:translateY(0);animation:chatReadingDot 1.2s ease-in-out infinite;will-change:transform,opacity}.chat-reading-indicator__dots>span:nth-child(2){animation-delay:.15s}.chat-reading-indicator__dots>span:nth-child(3){animation-delay:.3s}@keyframes chatReadingDot{0%,80%,to{opacity:.4;transform:translateY(0)}40%{opacity:1;transform:translateY(-3px)}}@media(prefers-reduced-motion:reduce){.chat-reading-indicator__dots>span{animation:none;opacity:.6}}.chat-text{overflow-wrap:anywhere;word-break:break-word;color:var(--chat-text);line-height:1.5}.chat-text :where(p,ul,ol,pre,blockquote,table){margin:0}.chat-text :where(p+p,p+ul,p+ol,p+pre,p+blockquote,p+table){margin-top:.75em}.chat-text :where(ul,ol){padding-left:1.2em}.chat-text :where(li+li){margin-top:.25em}.chat-text :where(a){color:var(--accent)}.chat-text :where(a:hover){text-decoration:underline}.chat-text :where(blockquote){border-left:2px solid var(--border-strong);padding-left:12px;color:var(--muted)}.chat-text :where(hr){border:0;border-top:1px solid var(--border);margin:1em 0}.chat-text :where(code){font-family:var(--mono);font-size:.9em}.chat-text :where(:not(pre)>code){padding:.15em .35em;border-radius:var(--radius-sm);border:1px solid var(--border);background:var(--secondary)}:root[data-theme=light] .chat-text :where(:not(pre)>code){background:var(--bg-muted)}.chat-text :where(pre){margin-top:.75em;padding:10px 12px;border-radius:var(--radius-md);border:1px solid var(--border);background:var(--secondary);overflow:auto}:root[data-theme=light] .chat-text :where(pre){background:var(--bg-muted)}.chat-text :where(pre code){font-size:12px;white-space:pre}.chat-text :where(table){margin-top:.75em;border-collapse:collapse;width:100%;font-size:13px}.chat-text :where(th,td){border:1px solid var(--border);padding:6px 10px;vertical-align:top}.chat-text :where(th){font-family:var(--mono);font-weight:500;color:var(--muted);background:var(--secondary)}.chat-tool-card{margin-top:8px;padding:10px 12px;border-radius:var(--radius-md);border:1px solid var(--border);background:var(--secondary);display:grid;gap:4px}:root[data-theme=light] .chat-tool-card{background:var(--bg-muted)}.chat-tool-card__title{font-family:var(--mono);font-size:12px;font-weight:500;color:var(--text)}.chat-tool-card__detail{font-family:var(--mono);font-size:11px;color:var(--muted)}.chat-tool-card__details{margin-top:6px}.chat-tool-card__summary{font-family:var(--mono);font-size:11px;color:var(--muted);cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:6px}.chat-tool-card__summary::-webkit-details-marker{display:none}.chat-tool-card__summary-meta{color:var(--muted);opacity:.7}.chat-tool-card__details[open] .chat-tool-card__summary{color:var(--text)}.chat-tool-card__output{margin-top:8px;font-family:var(--mono);font-size:11px;line-height:1.5;white-space:pre-wrap;color:var(--chat-text);padding:8px 10px;border-radius:var(--radius-md);border:1px solid var(--border);background:var(--card)}:root[data-theme=light] .chat-tool-card__output{background:var(--bg)}.chat-stamp{font-size:11px;color:var(--muted)}.chat-line.user .chat-stamp{text-align:right}.chat-compose{margin-top:12px;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:10px}.shell--chat .chat-compose{position:sticky;bottom:0;z-index:5;margin-top:0;padding-top:12px;background:linear-gradient(180deg,transparent 0%,var(--bg) 40%)}.shell--chat-focus .chat-compose{bottom:calc(var(--shell-pad) + 8px);padding-bottom:calc(12px + env(safe-area-inset-bottom,0px));border-bottom-left-radius:var(--radius-lg);border-bottom-right-radius:var(--radius-lg)}.chat-compose__field{gap:4px}.chat-compose__field textarea{min-height:72px;padding:10px 14px;border-radius:var(--radius-lg);resize:vertical;white-space:pre-wrap;font-family:var(--font-body);line-height:1.5;border:1px solid var(--input);background:var(--card);box-shadow:inset 0 1px 0 var(--card-highlight);transition:border-color var(--duration-fast) ease,box-shadow var(--duration-fast) ease}.chat-compose__field textarea:focus{border-color:var(--ring);box-shadow:var(--focus-ring)}.chat-compose__field textarea:disabled{opacity:.5;cursor:not-allowed}.chat-compose__actions{justify-content:flex-end;align-self:end}@media(max-width:900px){.chat-session{min-width:180px}.chat-compose{grid-template-columns:1fr}}.qr-wrap{margin-top:16px;border-radius:var(--radius-md);background:var(--card);border:1px dashed var(--border-strong);padding:16px;display:inline-flex}.qr-wrap img{width:160px;height:160px;border-radius:var(--radius-sm);image-rendering:pixelated}.exec-approval-overlay{position:fixed;inset:0;background:#000c;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;padding:24px;z-index:200}.exec-approval-card{width:min(540px,100%);background:var(--card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:20px;animation:scale-in .2s var(--ease-out)}.exec-approval-header{display:flex;align-items:center;justify-content:space-between;gap:16px}.exec-approval-title{font-size:14px;font-weight:600}.exec-approval-sub{color:var(--muted);font-size:13px;margin-top:4px}.exec-approval-queue{font-size:11px;font-weight:500;color:var(--muted);border:1px solid var(--border);border-radius:var(--radius-full);padding:4px 10px}.exec-approval-command{margin-top:12px;padding:10px 12px;background:var(--secondary);border:1px solid var(--border);border-radius:var(--radius-md);word-break:break-word;white-space:pre-wrap;font-family:var(--mono);font-size:13px}.exec-approval-meta{margin-top:12px;display:grid;gap:6px;font-size:13px;color:var(--muted)}.exec-approval-meta-row{display:flex;justify-content:space-between;gap:12px}.exec-approval-meta-row span:last-child{color:var(--text);font-family:var(--mono)}.exec-approval-error{margin-top:10px;font-size:13px;color:var(--danger)}.exec-approval-actions{margin-top:16px;display:flex;flex-wrap:wrap;gap:8px}.config-layout{display:grid;grid-template-columns:260px minmax(0,1fr);gap:0;min-height:calc(100vh - 160px);margin:-16px;border-radius:var(--radius-xl);overflow:hidden;border:1px solid var(--border);background:var(--panel)}.config-sidebar{display:flex;flex-direction:column;background:var(--bg-accent);border-right:1px solid var(--border)}:root[data-theme=light] .config-sidebar{background:var(--bg-hover)}.config-sidebar__header{display:flex;align-items:center;justify-content:space-between;padding:18px;border-bottom:1px solid var(--border)}.config-sidebar__title{font-weight:600;font-size:14px;letter-spacing:-.01em}.config-sidebar__footer{margin-top:auto;padding:14px;border-top:1px solid var(--border)}.config-search{position:relative;padding:14px;border-bottom:1px solid var(--border)}.config-search__icon{position:absolute;left:28px;top:50%;transform:translateY(-50%);width:16px;height:16px;color:var(--muted);pointer-events:none}.config-search__input{width:100%;padding:11px 36px 11px 42px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--bg-elevated);font-size:13px;outline:none;transition:border-color var(--duration-fast) ease,box-shadow var(--duration-fast) ease,background var(--duration-fast) ease}.config-search__input::placeholder{color:var(--muted)}.config-search__input:focus{border-color:var(--accent);box-shadow:var(--focus-ring);background:var(--bg-hover)}:root[data-theme=light] .config-search__input{background:#fff}:root[data-theme=light] .config-search__input:focus{background:#fff}.config-search__clear{position:absolute;right:22px;top:50%;transform:translateY(-50%);width:22px;height:22px;border:none;border-radius:var(--radius-full);background:var(--bg-hover);color:var(--muted);font-size:14px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background var(--duration-fast) ease,color var(--duration-fast) ease}.config-search__clear:hover{background:var(--border-strong);color:var(--text)}.config-nav{flex:1;overflow-y:auto;padding:10px}.config-nav__item{display:flex;align-items:center;gap:12px;width:100%;padding:11px 14px;border:none;border-radius:var(--radius-md);background:transparent;color:var(--muted);font-size:13px;font-weight:500;text-align:left;cursor:pointer;transition:background var(--duration-fast) ease,color var(--duration-fast) ease}.config-nav__item:hover{background:var(--bg-hover);color:var(--text)}:root[data-theme=light] .config-nav__item:hover{background:#0000000a}.config-nav__item.active{background:var(--accent-subtle);color:var(--accent)}.config-nav__icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:15px;opacity:.7}.config-nav__item:hover .config-nav__icon,.config-nav__item.active .config-nav__icon{opacity:1}.config-nav__icon svg{width:18px;height:18px;stroke:currentColor;fill:none}.config-nav__label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.config-mode-toggle{display:flex;padding:4px;background:var(--bg-elevated);border-radius:var(--radius-md);border:1px solid var(--border)}:root[data-theme=light] .config-mode-toggle{background:#fff}.config-mode-toggle__btn{flex:1;padding:9px 14px;border:none;border-radius:var(--radius-sm);background:transparent;color:var(--muted);font-size:12px;font-weight:600;cursor:pointer;transition:background var(--duration-fast) ease,color var(--duration-fast) ease,box-shadow var(--duration-fast) ease}.config-mode-toggle__btn:hover{color:var(--text)}.config-mode-toggle__btn.active{background:var(--accent);color:#fff;box-shadow:var(--shadow-sm)}.config-main{display:flex;flex-direction:column;min-width:0;background:var(--panel)}.config-actions{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 22px;background:var(--bg-accent);border-bottom:1px solid var(--border)}:root[data-theme=light] .config-actions{background:var(--bg-hover)}.config-actions__left,.config-actions__right{display:flex;align-items:center;gap:10px}.config-changes-badge{padding:6px 14px;border-radius:var(--radius-full);background:var(--accent-subtle);border:1px solid rgba(255,77,77,.3);color:var(--accent);font-size:12px;font-weight:600}.config-status{font-size:13px;color:var(--muted)}.config-diff{margin:18px 22px 0;border:1px solid rgba(255,77,77,.25);border-radius:var(--radius-lg);background:var(--accent-subtle);overflow:hidden}.config-diff__summary{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;cursor:pointer;font-size:13px;font-weight:600;color:var(--accent);list-style:none}.config-diff__summary::-webkit-details-marker{display:none}.config-diff__chevron{width:16px;height:16px;transition:transform var(--duration-normal) var(--ease-out)}.config-diff__chevron svg{width:100%;height:100%}.config-diff[open] .config-diff__chevron{transform:rotate(180deg)}.config-diff__content{padding:0 18px 18px;display:grid;gap:10px}.config-diff__item{display:flex;align-items:baseline;gap:14px;padding:10px 14px;border-radius:var(--radius-md);background:var(--bg-elevated);font-size:12px;font-family:var(--mono)}:root[data-theme=light] .config-diff__item{background:#fff}.config-diff__path{font-weight:600;color:var(--text);flex-shrink:0}.config-diff__values{display:flex;align-items:baseline;gap:10px;min-width:0;flex-wrap:wrap}.config-diff__from{color:var(--danger);opacity:.85}.config-diff__arrow{color:var(--muted)}.config-diff__to{color:var(--ok)}.config-section-hero{display:flex;align-items:center;gap:16px;padding:16px 22px;border-bottom:1px solid var(--border);background:var(--bg-accent)}:root[data-theme=light] .config-section-hero{background:var(--bg-hover)}.config-section-hero__icon{width:30px;height:30px;color:var(--accent);display:flex;align-items:center;justify-content:center}.config-section-hero__icon svg{width:100%;height:100%;stroke:currentColor;fill:none}.config-section-hero__text{display:grid;gap:3px;min-width:0}.config-section-hero__title{font-size:16px;font-weight:600;letter-spacing:-.01em}.config-section-hero__desc{font-size:13px;color:var(--muted)}.config-subnav{display:flex;gap:8px;padding:12px 22px 14px;border-bottom:1px solid var(--border);background:var(--bg-accent);overflow-x:auto}:root[data-theme=light] .config-subnav{background:var(--bg-hover)}.config-subnav__item{border:1px solid transparent;border-radius:var(--radius-full);padding:7px 14px;font-size:12px;font-weight:600;color:var(--muted);background:var(--bg-elevated);cursor:pointer;transition:background var(--duration-fast) ease,color var(--duration-fast) ease,border-color var(--duration-fast) ease;white-space:nowrap}:root[data-theme=light] .config-subnav__item{background:#fff}.config-subnav__item:hover{color:var(--text);border-color:var(--border)}.config-subnav__item.active{color:var(--accent);border-color:#ff4d4d66;background:var(--accent-subtle)}.config-content{flex:1;overflow-y:auto;padding:22px}.config-raw-field textarea{min-height:500px;font-family:var(--mono);font-size:13px;line-height:1.55}.config-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;padding:80px 24px;color:var(--muted)}.config-loading__spinner{width:40px;height:40px;border:3px solid var(--border);border-top-color:var(--accent);border-radius:var(--radius-full);animation:spin .75s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.config-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;padding:80px 24px;text-align:center}.config-empty__icon{font-size:56px;opacity:.35}.config-empty__text{color:var(--muted);font-size:15px}.config-form--modern{display:grid;gap:26px}.config-section-card{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--bg-elevated);overflow:hidden;transition:border-color var(--duration-fast) ease}.config-section-card:hover{border-color:var(--border-strong)}:root[data-theme=light] .config-section-card{background:#fff}.config-section-card__header{display:flex;align-items:flex-start;gap:16px;padding:20px 22px;background:var(--bg-accent);border-bottom:1px solid var(--border)}:root[data-theme=light] .config-section-card__header{background:var(--bg-hover)}.config-section-card__icon{width:34px;height:34px;color:var(--accent);flex-shrink:0}.config-section-card__icon svg{width:100%;height:100%}.config-section-card__titles{flex:1;min-width:0}.config-section-card__title{margin:0;font-size:17px;font-weight:600;letter-spacing:-.01em}.config-section-card__desc{margin:5px 0 0;font-size:13px;color:var(--muted);line-height:1.45}.config-section-card__content{padding:22px}.cfg-fields{display:grid;gap:22px}.cfg-field{display:grid;gap:8px}.cfg-field--error{padding:14px;border-radius:var(--radius-md);background:var(--danger-subtle);border:1px solid rgba(239,68,68,.3)}.cfg-field__label{font-size:13px;font-weight:600;color:var(--text)}.cfg-field__help{font-size:12px;color:var(--muted);line-height:1.45}.cfg-field__error{font-size:12px;color:var(--danger)}.cfg-input-wrap{display:flex;gap:10px}.cfg-input{flex:1;padding:11px 14px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--bg-accent);font-size:14px;outline:none;transition:border-color var(--duration-fast) ease,box-shadow var(--duration-fast) ease,background var(--duration-fast) ease}.cfg-input::placeholder{color:var(--muted);opacity:.7}.cfg-input:focus{border-color:var(--accent);box-shadow:var(--focus-ring);background:var(--bg-hover)}:root[data-theme=light] .cfg-input{background:#fff}:root[data-theme=light] .cfg-input:focus{background:#fff}.cfg-input--sm{padding:9px 12px;font-size:13px}.cfg-input__reset{padding:10px 14px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--bg-elevated);color:var(--muted);font-size:14px;cursor:pointer;transition:background var(--duration-fast) ease,color var(--duration-fast) ease}.cfg-input__reset:hover:not(:disabled){background:var(--bg-hover);color:var(--text)}.cfg-input__reset:disabled{opacity:.5;cursor:not-allowed}.cfg-textarea{width:100%;padding:12px 14px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--bg-accent);font-family:var(--mono);font-size:13px;line-height:1.55;resize:vertical;outline:none;transition:border-color var(--duration-fast) ease,box-shadow var(--duration-fast) ease}.cfg-textarea:focus{border-color:var(--accent);box-shadow:var(--focus-ring)}:root[data-theme=light] .cfg-textarea{background:#fff}.cfg-textarea--sm{padding:10px 12px;font-size:12px}.cfg-number{display:inline-flex;border:1px solid var(--border-strong);border-radius:var(--radius-md);overflow:hidden;background:var(--bg-accent)}:root[data-theme=light] .cfg-number{background:#fff}.cfg-number__btn{width:44px;border:none;background:var(--bg-elevated);color:var(--text);font-size:18px;font-weight:300;cursor:pointer;transition:background var(--duration-fast) ease}.cfg-number__btn:hover:not(:disabled){background:var(--bg-hover)}.cfg-number__btn:disabled{opacity:.4;cursor:not-allowed}:root[data-theme=light] .cfg-number__btn{background:var(--bg-hover)}:root[data-theme=light] .cfg-number__btn:hover:not(:disabled){background:var(--border)}.cfg-number__input{width:85px;padding:11px;border:none;border-left:1px solid var(--border);border-right:1px solid var(--border);background:transparent;font-size:14px;text-align:center;outline:none;-moz-appearance:textfield}.cfg-number__input::-webkit-outer-spin-button,.cfg-number__input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.cfg-select{padding:11px 40px 11px 14px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background-color:var(--bg-accent);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 12px center;font-size:14px;cursor:pointer;outline:none;appearance:none;transition:border-color var(--duration-fast) ease,box-shadow var(--duration-fast) ease}.cfg-select:focus{border-color:var(--accent);box-shadow:var(--focus-ring)}:root[data-theme=light] .cfg-select{background-color:#fff}.cfg-segmented{display:inline-flex;padding:4px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--bg-accent)}:root[data-theme=light] .cfg-segmented{background:var(--bg-hover)}.cfg-segmented__btn{padding:9px 18px;border:none;border-radius:var(--radius-sm);background:transparent;color:var(--muted);font-size:13px;font-weight:500;cursor:pointer;transition:background var(--duration-fast) ease,color var(--duration-fast) ease,box-shadow var(--duration-fast) ease}.cfg-segmented__btn:hover:not(:disabled):not(.active){color:var(--text)}.cfg-segmented__btn.active{background:var(--accent);color:#fff;box-shadow:var(--shadow-sm)}.cfg-segmented__btn:disabled{opacity:.5;cursor:not-allowed}.cfg-toggle-row{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:16px 18px;border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--bg-accent);cursor:pointer;transition:background var(--duration-fast) ease,border-color var(--duration-fast) ease}.cfg-toggle-row:hover:not(.disabled){background:var(--bg-hover);border-color:var(--border-strong)}.cfg-toggle-row.disabled{opacity:.55;cursor:not-allowed}:root[data-theme=light] .cfg-toggle-row{background:#fff}:root[data-theme=light] .cfg-toggle-row:hover:not(.disabled){background:var(--bg-hover)}.cfg-toggle-row__content{flex:1;min-width:0}.cfg-toggle-row__label{display:block;font-size:14px;font-weight:500;color:var(--text)}.cfg-toggle-row__help{display:block;margin-top:3px;font-size:12px;color:var(--muted);line-height:1.45}.cfg-toggle{position:relative;flex-shrink:0}.cfg-toggle input{position:absolute;opacity:0;width:0;height:0}.cfg-toggle__track{display:block;width:50px;height:28px;background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:var(--radius-full);position:relative;transition:background var(--duration-normal) ease,border-color var(--duration-normal) ease}:root[data-theme=light] .cfg-toggle__track{background:var(--border)}.cfg-toggle__track:after{content:"";position:absolute;top:3px;left:3px;width:20px;height:20px;background:var(--text);border-radius:var(--radius-full);box-shadow:var(--shadow-sm);transition:transform var(--duration-normal) var(--ease-out),background var(--duration-normal) ease}.cfg-toggle input:checked+.cfg-toggle__track{background:var(--ok-subtle);border-color:#22c55e66}.cfg-toggle input:checked+.cfg-toggle__track:after{transform:translate(22px);background:var(--ok)}.cfg-toggle input:focus+.cfg-toggle__track{box-shadow:var(--focus-ring)}.cfg-object{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--bg-accent);overflow:hidden}:root[data-theme=light] .cfg-object{background:#fff}.cfg-object__header{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;cursor:pointer;list-style:none;transition:background var(--duration-fast) ease}.cfg-object__header:hover{background:var(--bg-hover)}.cfg-object__header::-webkit-details-marker{display:none}.cfg-object__title{font-size:14px;font-weight:600;color:var(--text)}.cfg-object__chevron{width:18px;height:18px;color:var(--muted);transition:transform var(--duration-normal) var(--ease-out)}.cfg-object__chevron svg{width:100%;height:100%}.cfg-object[open] .cfg-object__chevron{transform:rotate(180deg)}.cfg-object__help{padding:0 18px 14px;font-size:12px;color:var(--muted);border-bottom:1px solid var(--border)}.cfg-object__content{padding:18px;display:grid;gap:18px}.cfg-array{border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden}.cfg-array__header{display:flex;align-items:center;gap:14px;padding:14px 18px;background:var(--bg-accent);border-bottom:1px solid var(--border)}:root[data-theme=light] .cfg-array__header{background:var(--bg-hover)}.cfg-array__label{flex:1;font-size:14px;font-weight:600;color:var(--text)}.cfg-array__count{font-size:12px;color:var(--muted);padding:4px 10px;background:var(--bg-elevated);border-radius:var(--radius-full)}:root[data-theme=light] .cfg-array__count{background:#fff}.cfg-array__add{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--bg-elevated);color:var(--text);font-size:12px;font-weight:500;cursor:pointer;transition:background var(--duration-fast) ease}.cfg-array__add:hover:not(:disabled){background:var(--bg-hover)}.cfg-array__add:disabled{opacity:.5;cursor:not-allowed}.cfg-array__add-icon{width:14px;height:14px}.cfg-array__add-icon svg{width:100%;height:100%}.cfg-array__help{padding:12px 18px;font-size:12px;color:var(--muted);border-bottom:1px solid var(--border)}.cfg-array__empty{padding:36px 18px;text-align:center;color:var(--muted);font-size:13px}.cfg-array__items{display:grid;gap:1px;background:var(--border)}.cfg-array__item{background:var(--panel)}.cfg-array__item-header{display:flex;align-items:center;justify-content:space-between;padding:12px 18px;background:var(--bg-accent);border-bottom:1px solid var(--border)}:root[data-theme=light] .cfg-array__item-header{background:var(--bg-hover)}.cfg-array__item-index{font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}.cfg-array__item-remove{width:30px;height:30px;display:flex;align-items:center;justify-content:center;border:none;border-radius:var(--radius-md);background:transparent;color:var(--muted);cursor:pointer;transition:background var(--duration-fast) ease,color var(--duration-fast) ease}.cfg-array__item-remove svg{width:16px;height:16px}.cfg-array__item-remove:hover:not(:disabled){background:var(--danger-subtle);color:var(--danger)}.cfg-array__item-remove:disabled{opacity:.4;cursor:not-allowed}.cfg-array__item-content{padding:18px}.cfg-map{border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden}.cfg-map__header{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 18px;background:var(--bg-accent);border-bottom:1px solid var(--border)}:root[data-theme=light] .cfg-map__header{background:var(--bg-hover)}.cfg-map__label{font-size:13px;font-weight:600;color:var(--muted)}.cfg-map__add{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--bg-elevated);color:var(--text);font-size:12px;font-weight:500;cursor:pointer;transition:background var(--duration-fast) ease}.cfg-map__add:hover:not(:disabled){background:var(--bg-hover)}.cfg-map__add-icon{width:14px;height:14px}.cfg-map__add-icon svg{width:100%;height:100%}.cfg-map__empty{padding:28px 18px;text-align:center;color:var(--muted);font-size:13px}.cfg-map__items{display:grid;gap:10px;padding:14px}.cfg-map__item{display:grid;grid-template-columns:150px 1fr auto;gap:10px;align-items:start}.cfg-map__item-key,.cfg-map__item-value{min-width:0}.cfg-map__item-remove{width:34px;height:34px;display:flex;align-items:center;justify-content:center;border:none;border-radius:var(--radius-md);background:transparent;color:var(--muted);cursor:pointer;transition:background var(--duration-fast) ease,color var(--duration-fast) ease}.cfg-map__item-remove svg{width:16px;height:16px}.cfg-map__item-remove:hover:not(:disabled){background:var(--danger-subtle);color:var(--danger)}.pill--sm{padding:5px 12px;font-size:11px}.pill--ok{border-color:#22c55e59;color:var(--ok)}.pill--danger{border-color:#ef444459;color:var(--danger)}@media(max-width:768px){.config-layout{grid-template-columns:1fr}.config-sidebar{border-right:none;border-bottom:1px solid var(--border)}.config-sidebar__header{padding:14px 16px}.config-nav{display:flex;flex-wrap:nowrap;gap:6px;padding:10px 14px;overflow-x:auto;-webkit-overflow-scrolling:touch}.config-nav__item{flex:0 0 auto;padding:9px 14px;white-space:nowrap}.config-nav__label{display:inline}.config-sidebar__footer{display:none}.config-actions{flex-wrap:wrap;padding:14px 16px}.config-actions__left,.config-actions__right{width:100%;justify-content:center}.config-section-hero{padding:14px 16px}.config-subnav{padding:10px 16px 12px}.config-content{padding:18px}.config-section-card__header{padding:16px 18px}.config-section-card__content{padding:18px}.cfg-toggle-row{padding:14px 16px}.cfg-map__item{grid-template-columns:1fr;gap:10px}.cfg-map__item-remove{justify-self:end}}@media(max-width:480px){.config-nav__icon{width:26px;height:26px;font-size:17px}.config-nav__label{display:none}.config-section-card__icon{width:30px;height:30px}.config-section-card__title{font-size:16px}.cfg-segmented{flex-wrap:wrap}.cfg-segmented__btn{flex:1 0 auto;min-width:70px}} diff --git a/dist/control-ui/assets/index-BvhR9FCb.css b/dist/control-ui/assets/index-BvhR9FCb.css deleted file mode 100644 index d9800ad6ba0..00000000000 --- a/dist/control-ui/assets/index-BvhR9FCb.css +++ /dev/null @@ -1 +0,0 @@ -@import"https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=Unbounded:wght@400;500;600&family=Work+Sans:wght@400;500;600;700&display=swap";:root{--bg: #0a0f14;--bg-accent: #111826;--bg-grad-1: #162031;--bg-grad-2: #1f2a22;--bg-overlay: rgba(255, 255, 255, .05);--bg-glow: rgba(245, 159, 74, .12);--panel: rgba(14, 20, 30, .88);--panel-strong: rgba(18, 26, 38, .96);--chrome: rgba(9, 14, 20, .72);--chrome-strong: rgba(9, 14, 20, .86);--text: rgba(244, 246, 251, .96);--chat-text: rgba(231, 237, 244, .92);--muted: rgba(156, 169, 189, .72);--border: rgba(255, 255, 255, .09);--border-strong: rgba(255, 255, 255, .16);--accent: #f59f4a;--accent-2: #34c7b7;--ok: #2bd97f;--warn: #f2c94c;--danger: #ff6b6b;--focus: rgba(245, 159, 74, .35);--grid-line: rgba(255, 255, 255, .04);--theme-switch-x: 50%;--theme-switch-y: 50%;--mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--font-body: "Work Sans", system-ui, sans-serif;--font-display: "Unbounded", "Times New Roman", serif;color-scheme:dark}:root[data-theme=light]{--bg: #f5f1ea;--bg-accent: #ffffff;--bg-grad-1: #f1e6d6;--bg-grad-2: #e5eef4;--bg-overlay: rgba(28, 32, 46, .05);--bg-glow: rgba(52, 199, 183, .14);--panel: rgba(255, 255, 255, .9);--panel-strong: rgba(255, 255, 255, .97);--chrome: rgba(255, 255, 255, .75);--chrome-strong: rgba(255, 255, 255, .88);--text: rgba(27, 36, 50, .98);--chat-text: rgba(36, 48, 66, .9);--muted: rgba(80, 94, 114, .7);--border: rgba(18, 24, 40, .12);--border-strong: rgba(18, 24, 40, .2);--accent: #e28a3f;--accent-2: #1ba99d;--ok: #1aa86c;--warn: #b3771c;--danger: #d44848;--focus: rgba(226, 138, 63, .35);--grid-line: rgba(18, 24, 40, .06);color-scheme:light}*{box-sizing:border-box}html,body{height:100%}body{margin:0;font:15px/1.5 var(--font-body);background:radial-gradient(1200px 900px at 15% -10%,var(--bg-grad-1) 0%,transparent 55%) fixed,radial-gradient(900px 700px at 80% 10%,var(--bg-grad-2) 0%,transparent 60%) fixed,linear-gradient(160deg,var(--bg) 0%,var(--bg-accent) 100%) fixed;color:var(--text)}body:before{content:"";position:fixed;inset:0;background:linear-gradient(140deg,var(--bg-overlay) 0%,rgba(255,255,255,0) 40%),radial-gradient(620px 420px at 75% 75%,var(--bg-glow),transparent 60%);pointer-events:none;z-index:0}@keyframes theme-circle-transition{0%{clip-path:circle(0% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%))}to{clip-path:circle(150% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%))}}html.theme-transition{view-transition-name:theme}html.theme-transition::view-transition-old(theme){mix-blend-mode:normal;animation:none;z-index:1}html.theme-transition::view-transition-new(theme){mix-blend-mode:normal;z-index:2;animation:theme-circle-transition .45s ease-out forwards}@media(prefers-reduced-motion:reduce){html.theme-transition::view-transition-old(theme),html.theme-transition::view-transition-new(theme){animation:none!important}}clawdbot-app{display:block;position:relative;z-index:1;min-height:100vh}a{color:inherit}button,input,textarea,select{font:inherit;color:inherit}@keyframes rise{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes dashboard-enter{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.shell{--shell-pad: 16px;--shell-gap: 16px;--shell-nav-width: 220px;--shell-topbar-height: 56px;--shell-focus-duration: .22s;--shell-focus-ease: cubic-bezier(.2, .85, .25, 1);height:100vh;display:grid;grid-template-columns:var(--shell-nav-width) minmax(0,1fr);grid-template-rows:var(--shell-topbar-height) 1fr;grid-template-areas:"topbar topbar" "nav content";gap:0;animation:dashboard-enter .6s ease-out;transition:grid-template-columns var(--shell-focus-duration) var(--shell-focus-ease);overflow:hidden}@supports (height: 100dvh){.shell{height:100dvh}}.shell--chat{min-height:100vh;height:100vh;overflow:hidden}@supports (height: 100dvh){.shell--chat{height:100dvh}}.shell--nav-collapsed,.shell--chat-focus{grid-template-columns:0px minmax(0,1fr)}.shell--onboarding{grid-template-rows:0 1fr}.shell--onboarding .topbar{display:none}.shell--onboarding .content{padding-top:0}.shell--chat-focus .content{padding-top:0;gap:0}.topbar{grid-area:topbar;position:sticky;top:0;z-index:40;display:flex;justify-content:space-between;align-items:center;gap:16px;padding:0 20px;height:var(--shell-topbar-height);border-bottom:1px solid var(--border);background:var(--panel);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.topbar-left{display:flex;align-items:center;gap:12px}.topbar .nav-collapse-toggle{width:44px;height:44px;margin-bottom:0}.topbar .nav-collapse-toggle__icon{font-size:22px}.brand{display:flex;flex-direction:column;gap:2px}.brand-title{font-family:var(--font-display);font-size:16px;letter-spacing:1px;text-transform:uppercase;font-weight:600;line-height:1.1}.brand-sub{font-size:10px;color:var(--muted);letter-spacing:.8px;text-transform:uppercase;line-height:1}.topbar-status{display:flex;align-items:center;gap:8px}.topbar-status .pill{padding:4px 10px;gap:6px;font-size:11px}.topbar-status .statusDot{width:6px;height:6px}.topbar-status .theme-toggle{--theme-item: 22px;--theme-gap: 4px;--theme-pad: 4px}.topbar-status .theme-icon{width:12px;height:12px}.nav{grid-area:nav;overflow-y:auto;overflow-x:hidden;padding:16px;border-right:1px solid var(--border);background:var(--panel);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);transition:width var(--shell-focus-duration) var(--shell-focus-ease),padding var(--shell-focus-duration) var(--shell-focus-ease);min-height:0}.shell--chat-focus .nav{width:0;padding:0;border-width:0;overflow:hidden;pointer-events:none}.nav--collapsed{width:0;min-width:0;padding:0;overflow:hidden;border:none;opacity:0;pointer-events:none}.nav-collapse-toggle{width:32px;height:32px;display:flex;align-items:center;justify-content:center;background:transparent;border:1px solid transparent;border-radius:6px;cursor:pointer;transition:background .15s ease,border-color .15s ease;margin-bottom:16px}.nav-collapse-toggle:hover{background:#ffffff14;border-color:var(--border)}:root[data-theme=light] .nav-collapse-toggle:hover{background:#0000000f}.nav-collapse-toggle__icon{font-size:16px;color:var(--muted)}.nav-group{margin-bottom:18px;display:grid;gap:6px;padding-bottom:12px;border-bottom:1px dashed rgba(255,255,255,.08)}.nav-group:last-child{margin-bottom:0;padding-bottom:0;border-bottom:none}.nav-group__items{display:grid;gap:4px}.nav-group--collapsed .nav-group__items{display:none}.nav-label{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;padding:4px 0;font-size:11px;font-weight:500;text-transform:uppercase;letter-spacing:1.4px;color:var(--text);opacity:.7;margin-bottom:4px;background:transparent;border:none;cursor:pointer;text-align:left}.nav-label:hover{opacity:1}.nav-label--static{cursor:default}.nav-label--static:hover{opacity:.7}.nav-label__text{flex:1}.nav-label__chevron{font-size:12px;opacity:.6}.nav-item{position:relative;display:flex;align-items:center;justify-content:flex-start;gap:8px;padding:10px 12px 10px 14px;border-radius:12px;border:1px solid transparent;background:transparent;color:var(--muted);cursor:pointer;text-decoration:none;transition:border-color .16s ease,background .16s ease,color .16s ease}.nav-item__icon{font-size:16px;width:18px;height:18px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.nav-item__text{font-size:13px;white-space:nowrap}.nav-item:hover{color:var(--text);border-color:#ffffff1f;background:#ffffff0f}.nav-item:before{content:"";position:absolute;left:0;top:50%;width:4px;height:60%;border-radius:0 999px 999px 0;transform:translateY(-50%);background:transparent}.nav-item.active{color:var(--text);border-color:#f59f4a73;background:#f59f4a1f}.nav-item.active:before{background:var(--accent);box-shadow:0 0 12px #f59f4a66}.content{grid-area:content;padding:8px 6px 20px;display:flex;flex-direction:column;gap:20px;min-height:0;overflow-y:auto;overflow-x:hidden}.content--chat{overflow:hidden}.content-header{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;padding:0 6px;overflow:hidden;transform-origin:top center;transition:opacity var(--shell-focus-duration) var(--shell-focus-ease),transform var(--shell-focus-duration) var(--shell-focus-ease),max-height var(--shell-focus-duration) var(--shell-focus-ease),padding var(--shell-focus-duration) var(--shell-focus-ease);max-height:90px}.shell--chat-focus .content-header{opacity:0;transform:translateY(-10px);max-height:0px;padding:0;pointer-events:none}.page-title{font-family:var(--font-display);font-size:26px;letter-spacing:.6px}.page-sub{color:var(--muted);font-size:12px;letter-spacing:.4px}.page-meta{display:flex;gap:10px}.content--chat .content-header{flex-direction:row;align-items:center;justify-content:space-between;gap:16px}.content--chat .content-header>div:first-child{text-align:left}.content--chat .page-meta{justify-content:flex-start}.content--chat .chat-controls{flex-shrink:0}.grid{display:grid;gap:18px}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.stat-grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(140px,1fr))}.note-grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}.row{display:flex;gap:12px;align-items:center}.stack{display:grid;gap:14px}.filters{display:flex;flex-wrap:wrap;gap:10px;align-items:center}@media(max-width:1100px){.shell{--shell-pad: 12px;--shell-gap: 12px;--shell-nav-col: 1fr;grid-template-columns:1fr;grid-template-rows:auto auto 1fr;grid-template-areas:"topbar" "nav" "content"}.nav{position:static;max-height:none;display:flex;gap:16px;overflow-x:auto;border-right:none;padding:12px}.nav-group{grid-auto-flow:column;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));border-bottom:none;padding-bottom:0}.grid-cols-2,.grid-cols-3{grid-template-columns:1fr}.topbar{position:static;flex-direction:column;align-items:flex-start;gap:12px}.topbar-status{width:100%;flex-wrap:wrap}.table-head,.table-row,.list-item{grid-template-columns:1fr}}@media(max-width:1100px){.nav{display:flex;flex-direction:row;flex-wrap:nowrap;gap:6px;padding:10px 12px;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}.nav::-webkit-scrollbar{display:none}.nav-group,.nav-group__items{display:contents}.nav-label{display:none}.nav-group--collapsed .nav-group__items{display:contents}.nav-item{padding:8px 14px;font-size:13px;border-radius:10px;white-space:nowrap;flex-shrink:0}.nav-item:before{display:none}}@media(max-width:600px){.shell{--shell-pad: 8px;--shell-gap: 8px}.topbar{padding:10px 12px;border-radius:12px;gap:8px;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center}.brand{flex:1;min-width:0}.brand-title{font-size:15px;letter-spacing:.3px}.brand-sub{display:none}.topbar-status{gap:6px;width:auto;flex-wrap:nowrap}.topbar-status .pill{padding:4px 8px;font-size:11px;gap:4px}.topbar-status .pill .mono{display:none}.topbar-status .pill span:nth-child(2){display:none}.nav{padding:8px;border-radius:12px;gap:8px;-webkit-overflow-scrolling:touch;scrollbar-width:none}.nav::-webkit-scrollbar{display:none}.nav-group{display:contents}.nav-label{display:none}.nav-item{padding:7px 10px;font-size:12px;border-radius:8px;white-space:nowrap;flex-shrink:0}.nav-item:before{display:none}.content-header{display:none}.content{padding:4px 4px 16px;gap:12px}.card{padding:12px;border-radius:12px}.card-title{font-size:14px}.stat-grid{gap:8px;grid-template-columns:repeat(2,1fr)}.stat{padding:10px;border-radius:10px}.stat-label{font-size:10px}.stat-value{font-size:16px}.note-grid,.form-grid{grid-template-columns:1fr;gap:10px}.field input,.field textarea,.field select{padding:8px 10px;border-radius:10px;font-size:14px}.btn{padding:8px 12px;font-size:13px}.pill{padding:4px 10px;font-size:12px}.chat-header{flex-direction:column;align-items:stretch;gap:8px}.chat-header__left{flex-direction:column;align-items:stretch}.chat-header__right{justify-content:space-between}.chat-session{min-width:unset;width:100%}.chat-thread{margin-top:8px;padding:10px 8px;border-radius:12px}.chat-msg{max-width:92%}.chat-bubble{padding:8px 10px;border-radius:12px}.chat-compose{gap:8px}.chat-compose__field textarea{min-height:60px;padding:8px 10px;border-radius:12px;font-size:14px}.log-stream{border-radius:10px;max-height:400px}.log-row{grid-template-columns:1fr;gap:4px;padding:8px}.log-time{font-size:10px}.log-level{font-size:9px}.log-subsystem{font-size:11px}.log-message{font-size:12px}.list-item{padding:10px;border-radius:10px}.list-title{font-size:14px}.list-sub{font-size:11px}.code-block{padding:8px;border-radius:10px;font-size:11px}.theme-toggle{--theme-item: 24px;--theme-gap: 4px;--theme-pad: 4px}.theme-icon{width:14px;height:14px}}.chat{position:relative;display:flex;flex-direction:column;flex:1 1 0;height:100%;min-height:0;overflow:hidden;background:transparent!important;border:none!important;box-shadow:none!important}.chat-header{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:nowrap;flex-shrink:0;padding-bottom:12px;margin-bottom:12px;background:transparent}.chat-header__left{display:flex;align-items:center;gap:12px;flex-wrap:wrap;min-width:0}.chat-header__right{display:flex;align-items:center;gap:8px}.chat-session{min-width:180px}.chat-thread{flex:1 1 0;overflow-y:auto;overflow-x:hidden;padding:12px;margin:0 -12px;min-height:0;border-radius:12px;background:transparent}.chat-focus-exit{position:absolute;top:12px;right:12px;z-index:100;width:32px;height:32px;border-radius:50%;border:1px solid var(--border);background:var(--panel);color:var(--muted);font-size:20px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s ease-out,color .15s ease-out,border-color .15s ease-out;box-shadow:0 4px 12px #0003}.chat-focus-exit:hover{background:var(--panel-strong);color:var(--text);border-color:var(--accent)}.chat-compose{position:sticky;bottom:0;flex-shrink:0;display:flex;align-items:flex-end;gap:12px;margin-top:auto;padding:16px 0 4px;background:linear-gradient(to bottom,transparent,var(--bg) 20%);z-index:10}.chat-compose__field{flex:1 1 auto;min-width:0}.chat-compose__field>span{display:none}.chat-compose .chat-compose__field textarea{width:100%;min-height:36px;max-height:150px;padding:8px 12px;border-radius:10px;resize:vertical;white-space:pre-wrap;font-family:var(--font-body);font-size:14px;line-height:1.45}.chat-compose__actions{flex-shrink:0;display:flex;align-items:stretch}.chat-compose .chat-compose__actions .btn{padding:8px 16px;font-size:13px;min-height:36px;white-space:nowrap}.chat-controls{display:flex;align-items:center;justify-content:flex-start;gap:12px;flex-wrap:wrap}.chat-controls__session{min-width:140px}.chat-controls__thinking{display:flex;align-items:center;gap:6px;font-size:13px}.btn--icon{padding:8px!important;min-width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--border);background:#ffffff0f}.chat-controls__separator{color:#fff6;font-size:18px;margin:0 8px;font-weight:300}:root[data-theme=light] .chat-controls__separator{color:#1018284d}.btn--icon:hover{background:#ffffff1f;border-color:#fff3}:root[data-theme=light] .btn--icon{background:#ffffffe6;border-color:#10182833;box-shadow:0 1px 2px #1018280d;color:#101828b3}:root[data-theme=light] .btn--icon:hover{background:#fff;border-color:#1018284d;color:#101828e6}.btn--icon svg{display:block}.chat-controls__session select{padding:6px 10px;font-size:13px}.chat-controls__thinking{display:flex;align-items:center;gap:4px;font-size:12px;padding:4px 10px;background:#ffffff0a;border-radius:6px;border:1px solid var(--border)}:root[data-theme=light] .chat-controls__thinking{background:#ffffffe6;border-color:#10182826}@media(max-width:640px){.chat-session{min-width:140px}.chat-compose{grid-template-columns:1fr}.chat-controls{flex-wrap:wrap;gap:8px}.chat-controls__session{min-width:120px}}.chat-thinking{margin-bottom:10px;padding:10px 12px;border-radius:10px;border:1px dashed rgba(255,255,255,.18);background:#ffffff0a;color:var(--muted);font-size:12px;line-height:1.4}:root[data-theme=light] .chat-thinking{border-color:#1018282e;background:#10182808}.chat-text{font-size:14px;line-height:1.5;word-wrap:break-word;overflow-wrap:break-word}.chat-text :where(p+p,p+ul,p+ol,p+pre,p+blockquote){margin-top:.75em}.chat-text :where(ul,ol){padding-left:1.5em}.chat-text :where(a){color:var(--accent);text-decoration:underline;text-underline-offset:2px}.chat-text :where(a:hover){opacity:.8}.chat-text :where(code){font-family:var(--mono);font-size:.9em}.chat-text :where(:not(pre)>code){background:#00000026;padding:.15em .4em;border-radius:4px}.chat-text :where(pre){background:#00000026;border-radius:6px;padding:10px 12px;overflow-x:auto}.chat-text :where(pre code){background:none;padding:0}.chat-text :where(blockquote){border-left:3px solid var(--border);padding-left:12px;color:var(--muted)}.chat-text :where(hr){border:none;border-top:1px solid var(--border);margin:1em 0}.chat-group{display:flex;gap:12px;align-items:flex-start;margin-bottom:16px;margin-left:16px;margin-right:16px}.chat-group.user{flex-direction:row-reverse;justify-content:flex-start}.chat-group-messages{display:flex;flex-direction:column;gap:2px;max-width:min(900px,calc(100% - 60px))}.chat-group.user .chat-group-messages{align-items:flex-end}.chat-group.user .chat-group-footer{justify-content:flex-end}.chat-group-footer{display:flex;gap:8px;align-items:baseline;margin-top:6px}.chat-sender-name{font-weight:500;font-size:12px;color:var(--muted)}.chat-group-timestamp{font-size:11px;color:var(--muted);opacity:.7}.chat-avatar{width:40px;height:40px;border-radius:8px;background:var(--panel-strong);display:grid;place-items:center;font-weight:600;font-size:14px;flex-shrink:0;align-self:flex-end;margin-bottom:4px}.chat-avatar.user{background:#f59f4a33;color:#f59f4a}.chat-avatar.assistant{background:#34c7b733;color:#34c7b7}.chat-avatar.other{background:#96969633;color:#969696}.chat-avatar.tool{background:#868e9633;color:#868e96}img.chat-avatar{display:block;object-fit:cover;object-position:center}.chat-bubble{position:relative;display:inline-block;border:1px solid var(--border);background:#0000001f;border-radius:12px;padding:10px 14px;box-shadow:none;transition:background .15s ease-out,border-color .15s ease-out;max-width:100%;word-wrap:break-word}.chat-bubble.has-copy{padding-right:36px}.chat-copy-btn{position:absolute;top:6px;right:8px;border:1px solid var(--border);background:#00000038;color:var(--muted);border-radius:8px;padding:4px 6px;font-size:14px;line-height:1;cursor:pointer;opacity:0;pointer-events:none;transition:opacity .12s ease-out,background .12s ease-out}.chat-copy-btn__icon{display:inline-block;width:1em;text-align:center}.chat-bubble:hover .chat-copy-btn{opacity:1;pointer-events:auto}.chat-copy-btn:hover{background:#0000004d}.chat-copy-btn[data-copying="1"]{opacity:0;pointer-events:none}.chat-copy-btn[data-error="1"]{opacity:1;pointer-events:auto;border-color:#ff453acc;background:#ff453a2e;color:#ff453a}.chat-copy-btn[data-copied="1"]{opacity:1;pointer-events:auto;border-color:#34c7b7cc;background:#34c7b72e;color:#34c7b7}.chat-copy-btn:focus-visible{opacity:1;pointer-events:auto;outline:2px solid var(--accent);outline-offset:2px}@media(hover:none){.chat-copy-btn{opacity:1;pointer-events:auto}}.chat-bubble:hover{background:#0000002e}.chat-group.user .chat-bubble{background:#f59f4a26;border-color:#f59f4a4d}.chat-group.user .chat-bubble:hover{background:#f59f4a38}.chat-bubble.streaming{animation:pulsing-border 1.5s ease-out infinite}@keyframes pulsing-border{0%,to{border-color:var(--border)}50%{border-color:var(--accent)}}.chat-bubble.fade-in{animation:fade-in .2s ease-out}@keyframes fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.chat-tool-card{border:1px solid var(--border);border-radius:8px;padding:12px;margin-top:8px;transition:border-color .15s ease-out,background .15s ease-out;max-height:120px;overflow:hidden}.chat-tool-card:hover{border-color:var(--accent);background:#0000000f}.chat-tool-card:first-child{margin-top:0}.chat-tool-card--clickable{cursor:pointer}.chat-tool-card--clickable:focus{outline:2px solid var(--accent);outline-offset:2px}.chat-tool-card__header{display:flex;justify-content:space-between;align-items:center;gap:8px}.chat-tool-card__title{display:inline-flex;align-items:center;gap:6px;font-weight:600;font-size:13px;line-height:1.2}.chat-tool-card__icon{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;font-size:14px;line-height:1;font-family:"Apple Color Emoji","Segoe UI Emoji","Noto Color Emoji",sans-serif;vertical-align:middle;flex-shrink:0}.chat-tool-card__action{font-size:12px;color:var(--accent);opacity:.8;transition:opacity .15s ease-out}.chat-tool-card--clickable:hover .chat-tool-card__action{opacity:1}.chat-tool-card__status{font-size:14px;color:var(--ok)}.chat-tool-card__status-text{font-size:11px;margin-top:4px}.chat-tool-card__detail{font-size:12px;color:var(--muted);margin-top:4px}.chat-tool-card__preview{font-size:11px;color:var(--muted);margin-top:8px;padding:8px 10px;background:#00000014;border-radius:6px;white-space:pre-wrap;overflow:hidden;max-height:44px;line-height:1.4;border:1px solid rgba(255,255,255,.04)}.chat-tool-card--clickable:hover .chat-tool-card__preview{background:#0000001f;border-color:#ffffff14}.chat-tool-card__inline{font-size:11px;color:var(--text);margin-top:6px;padding:6px 8px;background:#0000000f;border-radius:4px;white-space:pre-wrap;word-break:break-word}.chat-reading-indicator{background:transparent;border:1px solid var(--border);padding:12px;display:inline-flex}.chat-reading-indicator__dots{display:flex;gap:6px;align-items:center}.chat-reading-indicator__dots span{width:6px;height:6px;border-radius:50%;background:var(--muted);animation:reading-pulse 1.4s ease-in-out infinite}.chat-reading-indicator__dots span:nth-child(1){animation-delay:0s}.chat-reading-indicator__dots span:nth-child(2){animation-delay:.2s}.chat-reading-indicator__dots span:nth-child(3){animation-delay:.4s}@keyframes reading-pulse{0%,60%,to{opacity:.3;transform:scale(.8)}30%{opacity:1;transform:scale(1)}}.chat-split-container{display:flex;gap:0;flex:1;min-height:0;height:100%}.chat-main{min-width:400px;display:flex;flex-direction:column;overflow:hidden;transition:flex .25s ease-out}.chat-sidebar{flex:1;min-width:300px;border-left:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden;animation:slide-in .2s ease-out}@keyframes slide-in{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}.sidebar-panel{display:flex;flex-direction:column;height:100%;background:var(--panel)}.sidebar-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--border);flex-shrink:0;position:sticky;top:0;z-index:10;background:var(--panel)}.sidebar-header .btn{padding:4px 8px;font-size:14px;min-width:auto;line-height:1}.sidebar-title{font-weight:600;font-size:14px}.sidebar-content{flex:1;overflow:auto;padding:16px}.sidebar-markdown{font-size:14px;line-height:1.5}.sidebar-markdown pre{background:#0000001f;border-radius:4px;padding:12px;overflow-x:auto}.sidebar-markdown code{font-family:var(--mono);font-size:13px}@media(max-width:768px){.chat-split-container--open{position:fixed;inset:0;z-index:1000}.chat-split-container--open .chat-main{display:none}.chat-split-container--open .chat-sidebar{width:100%;min-width:0;border-left:none}}.card{border:1px solid var(--border);background:linear-gradient(160deg,rgba(255,255,255,.04),transparent 65%),var(--panel);border-radius:16px;padding:16px;box-shadow:0 18px 36px #00000047;animation:rise .4s ease}.card-title{font-family:var(--font-display);font-size:16px;letter-spacing:.6px;text-transform:uppercase}.card-sub{color:var(--muted);font-size:12px}.stat{background:linear-gradient(140deg,rgba(255,255,255,.04),transparent 70%),var(--panel-strong);border-radius:14px;padding:12px;border:1px solid var(--border-strong)}.stat-label{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:1px}.stat-value{font-size:18px;margin-top:6px}.stat-value.ok{color:var(--ok)}.stat-value.warn{color:var(--warn)}.stat-card{display:grid;gap:6px}.note-title{font-weight:600;letter-spacing:.2px}.status-list{display:grid;gap:8px}.status-list div{display:flex;justify-content:space-between;gap:12px;padding:6px 0;border-bottom:1px dashed rgba(255,255,255,.06)}.status-list div:last-child{border-bottom:none}.account-count{margin-top:8px;font-size:12px;font-weight:600;letter-spacing:.4px;color:var(--muted)}.account-card-list{margin-top:16px;display:grid;gap:10px}.account-card{border:1px solid var(--border);border-radius:10px;padding:12px;background:linear-gradient(160deg,rgba(255,255,255,.06),transparent),#ffffff08}.account-card-header{display:flex;justify-content:space-between;align-items:baseline;gap:12px}.account-card-title{font-weight:600}.account-card-id{font-family:var(--mono);font-size:12px;color:var(--muted)}.account-card-status{margin-top:8px;font-size:13px}.account-card-status div{padding:4px 0}.account-card-error{margin-top:6px;color:var(--danger);font-size:12px}.label{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.9px}.pill{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--border-strong);padding:6px 12px;border-radius:999px;background:linear-gradient(160deg,rgba(255,255,255,.06),transparent),var(--panel)}.theme-toggle{--theme-item: 28px;--theme-gap: 6px;--theme-pad: 6px;position:relative}.theme-toggle__track{position:relative;display:grid;grid-template-columns:repeat(3,var(--theme-item));gap:var(--theme-gap);padding:var(--theme-pad);border-radius:999px;border:1px solid var(--border-strong);background:#ffffff0a}.theme-toggle__indicator{position:absolute;top:50%;left:var(--theme-pad);width:var(--theme-item);height:var(--theme-item);border-radius:999px;transform:translateY(-50%) translate(calc(var(--theme-index, 0) * (var(--theme-item) + var(--theme-gap))));background:linear-gradient(160deg,rgba(255,255,255,.12),transparent),var(--panel-strong);border:1px solid var(--border-strong);box-shadow:0 8px 16px #00000040;transition:transform .18s ease-out,background .18s ease-out,box-shadow .18s ease-out;z-index:0}.theme-toggle__button{height:var(--theme-item);width:var(--theme-item);display:grid;place-items:center;border:0;border-radius:999px;background:transparent;color:var(--muted);cursor:pointer;position:relative;z-index:1;transition:color .15s ease-out,background .15s ease-out}.theme-toggle__button:hover{color:var(--text);background:#ffffff14}.theme-toggle__button.active{color:var(--text)}.theme-icon{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:1.75px;stroke-linecap:round;stroke-linejoin:round}.pill.danger{border-color:#ff5c5c80;color:var(--danger)}.statusDot{width:8px;height:8px;border-radius:999px;background:var(--danger);box-shadow:0 0 0 2px #00000040}.statusDot.ok{background:var(--ok);box-shadow:0 0 0 2px #00000040,0 0 10px #2bd97f66}.btn{border:1px solid var(--border-strong);background:#ffffff0a;padding:8px 14px;border-radius:999px;cursor:pointer;transition:transform .15s ease,border-color .15s ease,background .15s ease}.btn:hover{background:#ffffff1a;transform:translateY(-1px)}.btn.primary{border-color:#f59f4a73;background:#f59f4a33}.btn.active{border-color:#f59f4a8c;background:#f59f4a29}.btn.danger{border-color:#ff6b6b73;background:#ff6b6b2e}.btn--sm{padding:5px 10px;font-size:12px}.btn:disabled{opacity:.5;cursor:not-allowed;transform:none}.field{display:grid;gap:6px}.field.full{grid-column:1 / -1}.field span{color:var(--muted);font-size:11px;letter-spacing:.4px}.field input,.field textarea,.field select{border:1px solid var(--border-strong);background:#00000038;border-radius:12px;padding:9px 11px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.field input:focus,.field textarea:focus,.field select:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--focus);background:#00000047}.field select{appearance:none;padding-right:38px;background-color:var(--panel-strong);background-image:linear-gradient(45deg,transparent 50%,var(--muted) 50%),linear-gradient(135deg,var(--muted) 50%,transparent 50%),linear-gradient(to right,transparent,transparent);background-position:calc(100% - 18px) 50%,calc(100% - 12px) 50%,calc(100% - 38px) 50%;background-size:6px 6px,6px 6px,1px 60%;background-repeat:no-repeat;box-shadow:inset 0 1px #ffffff0a}.field textarea{font-family:var(--mono);min-height:180px;resize:vertical;white-space:pre}.field textarea:focus{background:#00000052}.field.checkbox{grid-template-columns:auto 1fr;align-items:center}.config-form .field.checkbox{grid-template-columns:18px minmax(0,1fr);column-gap:10px}.config-form .field.checkbox input[type=checkbox]{margin:0}.form-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}:root[data-theme=light] .field input,:root[data-theme=light] .field textarea,:root[data-theme=light] .field select{background:#fff;border-color:#10182840;box-shadow:0 1px 2px #1018280f}:root[data-theme=light] .field input:focus,:root[data-theme=light] .field textarea:focus,:root[data-theme=light] .field select:focus{background:#fff}:root[data-theme=light] .btn{background:#ffffffe6;border-color:#10182833;box-shadow:0 1px 2px #1018280d}:root[data-theme=light] .btn:hover{background:#fff;border-color:#1018284d}:root[data-theme=light] .btn.primary{background:#f59f4a26}:root[data-theme=light] .btn.active{background:#f59f4a1f}.muted{color:var(--muted)}.mono{font-family:var(--mono)}.callout{padding:10px 12px;border-radius:14px;background:linear-gradient(160deg,rgba(255,255,255,.06),transparent),#ffffff08;border:1px solid var(--border)}.callout.danger{border-color:#ff5c5c66;color:var(--danger)}.callout.info{border-color:#5c9cff66;color:var(--accent)}.callout.success{border-color:#5cff8066;color:var(--positive, #5cff80)}.compaction-indicator{font-size:13px;padding:8px 12px;margin-bottom:8px;animation:compaction-fade-in .2s ease-out}.compaction-indicator--active{animation:compaction-pulse 1.5s ease-in-out infinite}.compaction-indicator--complete{animation:compaction-fade-in .2s ease-out}@keyframes compaction-fade-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}@keyframes compaction-pulse{0%,to{opacity:.7}50%{opacity:1}}.code-block{font-family:var(--mono);font-size:12px;background:#00000059;padding:10px;border-radius:12px;border:1px solid var(--border);max-height:360px;overflow:auto}:root[data-theme=light] .code-block,:root[data-theme=light] .list-item,:root[data-theme=light] .table-row,:root[data-theme=light] .chip{background:#ffffffd9}.list{display:grid;gap:12px;container-type:inline-size}.list-item{display:grid;grid-template-columns:minmax(0,1fr) minmax(220px,260px);gap:14px;align-items:start;border:1px solid var(--border);border-radius:14px;padding:12px;background:#0003}.list-item-clickable{cursor:pointer;transition:border-color .15s ease,box-shadow .15s ease}.list-item-clickable:hover{border-color:var(--border-strong)}.list-item-selected{border-color:var(--accent);box-shadow:0 0 0 1px var(--focus)}.list-main{display:grid;gap:6px;min-width:0}.list-title{font-weight:600}.list-sub{color:var(--muted);font-size:12px}.list-meta{text-align:right;color:var(--muted);font-size:11px;display:grid;gap:4px;min-width:220px}.list-meta .btn{padding:6px 10px}.list-meta .field input,.list-meta .field textarea,.list-meta .field select{width:100%}@container (max-width: 560px){.list-item{grid-template-columns:1fr}.list-meta{min-width:0;text-align:left}}.chip-row{display:flex;flex-wrap:wrap;gap:6px}.chip{font-size:11px;border:1px solid var(--border);border-radius:999px;padding:4px 8px;color:var(--muted);background:#0003}.chip input{margin-right:6px}.chip-ok{color:var(--ok);border-color:#1bd98a66}.chip-warn{color:var(--warn);border-color:#f2c94c66}.table{display:grid;gap:8px}.table-head,.table-row{display:grid;grid-template-columns:1.4fr 1fr .8fr .7fr .8fr .8fr .8fr .8fr .6fr;gap:12px;align-items:center}.table-head{font-size:11px;text-transform:uppercase;letter-spacing:.8px;color:var(--muted)}.table-row{border:1px solid var(--border);padding:10px;border-radius:12px;background:#0003}.session-link{text-decoration:none;color:var(--accent)}.session-link:hover{text-decoration:underline}.log-stream{border:1px solid var(--border);border-radius:14px;background:#0003;max-height:520px;overflow:auto;container-type:inline-size}.log-row{display:grid;grid-template-columns:90px 70px minmax(140px,200px) minmax(0,1fr);gap:12px;align-items:start;padding:6px 10px;border-bottom:1px solid var(--border);font-size:12px}.log-row:last-child{border-bottom:none}.log-time{color:var(--muted)}.log-level{text-transform:uppercase;font-size:10px;font-weight:600;border:1px solid var(--border);border-radius:999px;padding:2px 6px;width:fit-content}.log-level.trace,.log-level.debug{color:var(--muted)}.log-level.info{color:var(--info);border-color:#4c96f266}.log-level.warn{color:var(--warn);border-color:#f2c94c66}.log-level.error,.log-level.fatal{color:var(--danger);border-color:#ff5c5c66}.log-chip.trace,.log-chip.debug{color:var(--muted)}.log-chip.info{color:var(--info);border-color:#4c96f266}.log-chip.warn{color:var(--warn);border-color:#f2c94c66}.log-chip.error,.log-chip.fatal{color:var(--danger);border-color:#ff5c5c66}.log-subsystem{color:var(--muted)}.log-message{white-space:pre-wrap;word-break:break-word}@container (max-width: 620px){.log-row{grid-template-columns:70px 60px minmax(0,1fr)}.log-subsystem{display:none}}.chat{display:flex;flex-direction:column;min-height:0}.shell--chat .chat{flex:1}.chat-header{display:flex;justify-content:space-between;align-items:flex-end;gap:12px;flex-wrap:wrap}.chat-header__left{display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap;min-width:0}.chat-header__right{display:flex;align-items:center;gap:10px}.chat-session{min-width:240px}.chat-thread{margin-top:12px;display:flex;flex-direction:column;gap:12px;flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding:14px 12px;min-width:0;border-radius:0;border:none;background:transparent}:root[data-theme=light] .chat-thread{background:transparent}.chat-queue{margin-top:12px;padding:10px 12px;border-radius:16px;border:1px solid var(--border);background:#0000002e;display:grid;gap:8px}:root[data-theme=light] .chat-queue{background:#1018280a}.chat-queue__title{font-family:var(--font-mono);font-size:12px;color:var(--muted)}.chat-queue__list{display:grid;gap:8px}.chat-queue__item{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:start;gap:10px;padding:8px 10px;border-radius:12px;border:1px dashed var(--border);background:#0003}:root[data-theme=light] .chat-queue__item{background:#1018280d}.chat-queue__text{color:var(--chat-text);font-size:13px;line-height:1.4;white-space:pre-wrap;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}.chat-queue__remove{align-self:start;padding:4px 10px;font-size:12px;line-height:1}.chat-line{display:flex}.chat-line.user{justify-content:flex-end}.chat-line.assistant,.chat-line.other{justify-content:flex-start}.chat-msg{display:grid;gap:6px;max-width:min(720px,82%)}.chat-line.user .chat-msg{justify-items:end}.chat-bubble{border:1px solid var(--border);background:#0000003d;border-radius:16px;padding:10px 12px;min-width:0;box-shadow:0 12px 22px #0000003d}:root[data-theme=light] .chat-bubble{background:#ffffffd9;box-shadow:0 12px 26px #10182814}.chat-line.user .chat-bubble{border-color:#f59f4a73;background:linear-gradient(135deg,#f59f4a42,#f59f4a1f)}.chat-line.assistant .chat-bubble{border-color:#34c7b733;background:linear-gradient(135deg,#34c7b71f,#0000003d)}:root[data-theme=light] .chat-line.assistant .chat-bubble{background:linear-gradient(135deg,#1bb9b11f,#ffffffd9)}@keyframes chatStreamPulse{0%{box-shadow:0 12px 22px #0000003d,0 0 #34c7b700}60%{box-shadow:0 12px 22px #0000003d,0 0 0 6px #34c7b714}to{box-shadow:0 12px 22px #0000003d,0 0 #34c7b700}}.chat-bubble.streaming{border-color:#34c7b766;animation:chatStreamPulse 1.6s ease-in-out infinite}@media(prefers-reduced-motion:reduce){.chat-bubble.streaming{animation:none}}.chat-bubble.chat-reading-indicator{width:fit-content;padding:10px 14px}.chat-reading-indicator__dots{display:inline-flex;align-items:center;gap:6px;height:10px}.chat-reading-indicator__dots>span{display:inline-block;width:6px;height:6px;border-radius:999px;background:var(--chat-text);opacity:.55;transform:translateY(0);animation:chatReadingDot 1.1s ease-in-out infinite;will-change:transform,opacity}.chat-reading-indicator__dots>span:nth-child(2){animation-delay:.12s}.chat-reading-indicator__dots>span:nth-child(3){animation-delay:.24s}@keyframes chatReadingDot{0%,80%,to{opacity:.38;transform:translateY(0) scale(.92)}40%{opacity:1;transform:translateY(-3px) scale(1.18)}}@media(prefers-reduced-motion:reduce){.chat-reading-indicator__dots>span{animation:none;opacity:.75}}.chat-text{overflow-wrap:anywhere;word-break:break-word;color:var(--chat-text);line-height:1.5}.chat-text :where(p,ul,ol,pre,blockquote,table){margin:0}.chat-text :where(p+p,p+ul,p+ol,p+pre,p+blockquote,p+table){margin-top:.75em}.chat-text :where(ul,ol){padding-left:1.1em}.chat-text :where(li+li){margin-top:.25em}.chat-text :where(a){color:var(--accent);text-decoration-thickness:2px;text-underline-offset:2px}.chat-text :where(a:hover){text-decoration-thickness:3px}.chat-text :where(blockquote){border-left:2px solid rgba(255,255,255,.14);padding-left:12px;color:var(--muted)}:root[data-theme=light] .chat-text :where(blockquote){border-left-color:#10182829}.chat-text :where(hr){border:0;border-top:1px solid var(--border);opacity:.6;margin:.9em 0}.chat-text :where(code){font-family:var(--font-mono);font-size:.92em}.chat-text :where(:not(pre)>code){padding:.15em .35em;border-radius:8px;border:1px solid var(--border);background:#0003}:root[data-theme=light] .chat-text :where(:not(pre)>code){background:#1018280d}.chat-text :where(pre){margin-top:.75em;padding:10px 12px;border-radius:14px;border:1px solid var(--border);background:#00000038;overflow:auto}:root[data-theme=light] .chat-text :where(pre){background:#1018280a}.chat-text :where(pre code){font-size:12px;white-space:pre}.chat-text :where(table){margin-top:.75em;border-collapse:collapse;width:100%;font-size:12px}.chat-text :where(th,td){border:1px solid var(--border);padding:6px 8px;vertical-align:top}.chat-text :where(th){font-family:var(--font-mono);font-weight:600;color:var(--muted)}.chat-tool-card{margin-top:8px;padding:8px 10px;border-radius:12px;border:1px solid var(--border);background:#00000038;display:grid;gap:4px}:root[data-theme=light] .chat-tool-card{background:#ffffffb3}.chat-tool-card__title{font-family:var(--font-mono);font-size:12px;color:var(--chat-text)}.chat-tool-card__detail{font-family:var(--font-mono);font-size:11px;color:var(--muted)}.chat-tool-card__details{margin-top:6px}.chat-tool-card__summary{font-family:var(--font-mono);font-size:11px;color:var(--muted);cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:6px}.chat-tool-card__summary::-webkit-details-marker{display:none}.chat-tool-card__summary-meta{color:var(--muted);opacity:.8}.chat-tool-card__details[open] .chat-tool-card__summary{color:var(--chat-text)}.chat-tool-card__output{margin-top:6px;font-family:var(--font-mono);font-size:11px;line-height:1.45;white-space:pre-wrap;color:var(--chat-text);padding:8px;border-radius:10px;border:1px solid var(--border);background:#0003}:root[data-theme=light] .chat-tool-card__output{background:#1018280d}.chat-stamp{font-size:11px;color:var(--muted)}.chat-line.user .chat-stamp{text-align:right}.chat-compose{margin-top:12px;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:10px}.shell--chat .chat-compose{position:sticky;bottom:0;z-index:5;margin-top:0;padding-top:12px;background:linear-gradient(180deg,rgba(0,0,0,0) 0%,var(--panel) 35%)}.shell--chat-focus .chat-compose{bottom:calc(var(--shell-pad) + 8px);padding-bottom:calc(14px + env(safe-area-inset-bottom,0px));border-bottom-left-radius:18px;border-bottom-right-radius:18px}.chat-compose__field{gap:4px}.chat-compose__field textarea{min-height:72px;padding:10px 12px;border-radius:16px;resize:vertical;white-space:pre-wrap;font-family:var(--font-body);line-height:1.45}.chat-compose__field textarea:disabled{opacity:.7;cursor:not-allowed}.chat-compose__actions{justify-content:flex-end;align-self:end}@media(max-width:900px){.chat-session{min-width:200px}.chat-compose{grid-template-columns:1fr}}.qr-wrap{margin-top:12px;border-radius:14px;background:#0003;border:1px dashed rgba(255,255,255,.18);padding:12px;display:inline-flex}.qr-wrap img{width:180px;height:180px;border-radius:10px;image-rendering:pixelated}.exec-approval-overlay{position:fixed;inset:0;background:#080c12b3;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;padding:24px;z-index:200}.exec-approval-card{width:min(560px,100%);background:var(--panel-strong);border:1px solid var(--border-strong);border-radius:18px;padding:20px;box-shadow:0 28px 60px #00000059;animation:rise .25s ease}.exec-approval-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.exec-approval-title{font-family:var(--font-display);font-size:14px;letter-spacing:.8px;text-transform:uppercase}.exec-approval-sub{color:var(--muted);font-size:12px}.exec-approval-queue{font-size:11px;text-transform:uppercase;letter-spacing:1px;color:var(--muted);border:1px solid var(--border);border-radius:999px;padding:4px 10px}.exec-approval-command{margin-top:12px;padding:10px 12px;background:#00000040;border:1px solid var(--border);border-radius:12px;word-break:break-word;white-space:pre-wrap}.exec-approval-meta{margin-top:12px;display:grid;gap:6px;font-size:12px;color:var(--muted)}.exec-approval-meta-row{display:flex;justify-content:space-between;gap:12px}.exec-approval-meta-row span:last-child{color:var(--text);font-family:var(--mono)}.exec-approval-error{margin-top:10px;font-size:12px;color:var(--danger)}.exec-approval-actions{margin-top:16px;display:flex;flex-wrap:wrap;gap:10px}.config-layout{display:grid;grid-template-columns:240px minmax(0,1fr);gap:0;min-height:calc(100vh - 140px);margin:-16px;border-radius:16px;overflow:hidden;border:1px solid var(--border);background:var(--panel)}.config-sidebar{display:flex;flex-direction:column;background:#0003;border-right:1px solid var(--border)}:root[data-theme=light] .config-sidebar{background:#00000008}.config-sidebar__header{display:flex;align-items:center;justify-content:space-between;padding:16px;border-bottom:1px solid var(--border)}.config-sidebar__title{font-weight:600;font-size:14px;letter-spacing:.3px}.config-sidebar__footer{margin-top:auto;padding:12px;border-top:1px solid var(--border)}.config-search{position:relative;padding:12px;border-bottom:1px solid var(--border)}.config-search__icon{position:absolute;left:24px;top:50%;transform:translateY(-50%);width:16px;height:16px;color:var(--muted);pointer-events:none}.config-search__input{width:100%;padding:10px 32px 10px 40px;border:1px solid var(--border);border-radius:8px;background:#00000026;font-size:13px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.config-search__input::placeholder{color:var(--muted)}.config-search__input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--focus);background:#0003}:root[data-theme=light] .config-search__input{background:#fffc}:root[data-theme=light] .config-search__input:focus{background:#fff}.config-search__clear{position:absolute;right:20px;top:50%;transform:translateY(-50%);width:20px;height:20px;border:none;border-radius:50%;background:#ffffff1a;color:var(--muted);font-size:16px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s ease,color .15s ease}.config-search__clear:hover{background:#fff3;color:var(--text)}.config-nav{flex:1;overflow-y:auto;padding:8px}.config-nav__item{display:flex;align-items:center;gap:12px;width:100%;padding:10px 12px;border:none;border-radius:8px;background:transparent;color:var(--muted);font-size:13px;font-weight:500;text-align:left;cursor:pointer;transition:background .15s ease,color .15s ease}.config-nav__item:hover{background:#ffffff0d;color:var(--text)}:root[data-theme=light] .config-nav__item:hover{background:#0000000d}.config-nav__item.active{background:#f59f4a1f;color:var(--accent)}.config-nav__icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:14px}.config-nav__icon svg{width:18px;height:18px;stroke:currentColor;fill:none}.config-nav__label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.config-mode-toggle{display:flex;padding:3px;background:#0003;border-radius:8px;border:1px solid var(--border)}:root[data-theme=light] .config-mode-toggle{background:#0000000f}.config-mode-toggle__btn{flex:1;padding:8px 12px;border:none;border-radius:6px;background:transparent;color:var(--muted);font-size:12px;font-weight:600;cursor:pointer;transition:background .15s ease,color .15s ease,box-shadow .15s ease}.config-mode-toggle__btn:hover{color:var(--text)}.config-mode-toggle__btn.active{background:#ffffff1a;color:var(--text);box-shadow:0 1px 3px #0003}:root[data-theme=light] .config-mode-toggle__btn.active{background:#fff;box-shadow:0 1px 3px #0000001a}.config-main{display:flex;flex-direction:column;min-width:0;background:var(--panel)}.config-actions{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 20px;background:#00000014;border-bottom:1px solid var(--border)}:root[data-theme=light] .config-actions{background:#00000005}.config-actions__left,.config-actions__right{display:flex;align-items:center;gap:8px}.config-changes-badge{padding:5px 12px;border-radius:999px;background:#f59f4a26;border:1px solid rgba(245,159,74,.3);color:var(--accent);font-size:12px;font-weight:600}.config-status{font-size:13px;color:var(--muted)}.config-diff{margin:16px 20px 0;border:1px solid rgba(245,159,74,.3);border-radius:10px;background:#f59f4a0d;overflow:hidden}.config-diff__summary{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;cursor:pointer;font-size:13px;font-weight:600;color:var(--accent);list-style:none}.config-diff__summary::-webkit-details-marker{display:none}.config-diff__chevron{width:16px;height:16px;transition:transform .2s ease}.config-diff__chevron svg{width:100%;height:100%}.config-diff[open] .config-diff__chevron{transform:rotate(180deg)}.config-diff__content{padding:0 16px 16px;display:grid;gap:8px}.config-diff__item{display:flex;align-items:baseline;gap:12px;padding:8px 12px;border-radius:6px;background:#0000001a;font-size:12px;font-family:var(--mono)}:root[data-theme=light] .config-diff__item{background:#fff9}.config-diff__path{font-weight:600;color:var(--text);flex-shrink:0}.config-diff__values{display:flex;align-items:baseline;gap:8px;min-width:0;flex-wrap:wrap}.config-diff__from{color:var(--danger);opacity:.8}.config-diff__arrow{color:var(--muted)}.config-diff__to{color:var(--ok)}.config-section-hero{display:flex;align-items:center;gap:14px;padding:14px 20px;border-bottom:1px solid var(--border);background:#0000000a}:root[data-theme=light] .config-section-hero{background:#00000004}.config-section-hero__icon{width:28px;height:28px;color:var(--accent);display:flex;align-items:center;justify-content:center}.config-section-hero__icon svg{width:100%;height:100%;stroke:currentColor;fill:none}.config-section-hero__text{display:grid;gap:2px;min-width:0}.config-section-hero__title{font-size:15px;font-weight:600}.config-section-hero__desc{font-size:12px;color:var(--muted)}.config-subnav{display:flex;gap:8px;padding:10px 20px 12px;border-bottom:1px solid var(--border);background:#00000008;overflow-x:auto}:root[data-theme=light] .config-subnav{background:#00000005}.config-subnav__item{border:1px solid transparent;border-radius:999px;padding:6px 12px;font-size:12px;font-weight:600;color:var(--muted);background:#0000001f;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease;white-space:nowrap}:root[data-theme=light] .config-subnav__item{background:#0000000f}.config-subnav__item:hover{color:var(--text);background:#ffffff14}:root[data-theme=light] .config-subnav__item:hover{background:#00000014}.config-subnav__item.active{color:var(--accent);border-color:#f59f4a66;background:#f59f4a1f}.config-content{flex:1;overflow-y:auto;padding:20px}.config-raw-field textarea{min-height:500px;font-family:var(--mono);font-size:13px;line-height:1.5}.config-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:80px 20px;color:var(--muted)}.config-loading__spinner{width:36px;height:36px;border:3px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.config-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:80px 20px;text-align:center}.config-empty__icon{font-size:56px;opacity:.4}.config-empty__text{color:var(--muted);font-size:15px}.config-form--modern{display:grid;gap:24px}.config-section-card{border:1px solid var(--border);border-radius:12px;background:#ffffff05;overflow:hidden}:root[data-theme=light] .config-section-card{background:#ffffff80}.config-section-card__header{display:flex;align-items:flex-start;gap:14px;padding:18px 20px;background:#0000000f;border-bottom:1px solid var(--border)}:root[data-theme=light] .config-section-card__header{background:#00000005}.config-section-card__icon{width:32px;height:32px;color:var(--accent);flex-shrink:0}.config-section-card__icon svg{width:100%;height:100%}.config-section-card__titles{flex:1;min-width:0}.config-section-card__title{margin:0;font-size:17px;font-weight:600}.config-section-card__desc{margin:4px 0 0;font-size:13px;color:var(--muted);line-height:1.4}.config-section-card__content{padding:20px}.cfg-fields{display:grid;gap:20px}.cfg-field{display:grid;gap:6px}.cfg-field--error{padding:12px;border-radius:8px;background:#ff5c5c1a;border:1px solid rgba(255,92,92,.3)}.cfg-field__label{font-size:13px;font-weight:600;color:var(--text)}.cfg-field__help{font-size:12px;color:var(--muted);line-height:1.4}.cfg-field__error{font-size:12px;color:var(--danger)}.cfg-input-wrap{display:flex;gap:8px}.cfg-input{flex:1;padding:10px 12px;border:1px solid var(--border);border-radius:8px;background:#0000001f;font-size:14px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.cfg-input::placeholder{color:var(--muted);opacity:.7}.cfg-input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--focus);background:#0000002e}:root[data-theme=light] .cfg-input{background:#fff}:root[data-theme=light] .cfg-input:focus{background:#fff}.cfg-input--sm{padding:8px 10px;font-size:13px}.cfg-input__reset{padding:8px 12px;border:1px solid var(--border);border-radius:8px;background:#ffffff0d;color:var(--muted);font-size:14px;cursor:pointer;transition:background .15s ease,color .15s ease}.cfg-input__reset:hover:not(:disabled){background:#ffffff1a;color:var(--text)}.cfg-input__reset:disabled{opacity:.5;cursor:not-allowed}.cfg-textarea{width:100%;padding:10px 12px;border:1px solid var(--border);border-radius:8px;background:#0000001f;font-family:var(--mono);font-size:13px;line-height:1.5;resize:vertical;outline:none;transition:border-color .15s ease,box-shadow .15s ease}.cfg-textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--focus)}:root[data-theme=light] .cfg-textarea{background:#fff}.cfg-textarea--sm{padding:8px 10px;font-size:12px}.cfg-number{display:inline-flex;border:1px solid var(--border);border-radius:8px;overflow:hidden;background:#0000001f}:root[data-theme=light] .cfg-number{background:#fff}.cfg-number__btn{width:40px;border:none;background:#ffffff0d;color:var(--text);font-size:18px;font-weight:300;cursor:pointer;transition:background .15s ease}.cfg-number__btn:hover:not(:disabled){background:#ffffff1a}.cfg-number__btn:disabled{opacity:.4;cursor:not-allowed}:root[data-theme=light] .cfg-number__btn{background:#00000008}:root[data-theme=light] .cfg-number__btn:hover:not(:disabled){background:#0000000f}.cfg-number__input{width:80px;padding:10px;border:none;border-left:1px solid var(--border);border-right:1px solid var(--border);background:transparent;font-size:14px;text-align:center;outline:none;-moz-appearance:textfield}.cfg-number__input::-webkit-outer-spin-button,.cfg-number__input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.cfg-select{padding:10px 36px 10px 12px;border:1px solid var(--border);border-radius:8px;background-color:#0000001f;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;font-size:14px;cursor:pointer;outline:none;appearance:none;transition:border-color .15s ease,box-shadow .15s ease}.cfg-select:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--focus)}:root[data-theme=light] .cfg-select{background-color:#fff}.cfg-segmented{display:inline-flex;padding:3px;border:1px solid var(--border);border-radius:8px;background:#0000001f}:root[data-theme=light] .cfg-segmented{background:#0000000a}.cfg-segmented__btn{padding:8px 16px;border:none;border-radius:6px;background:transparent;color:var(--muted);font-size:13px;font-weight:500;cursor:pointer;transition:background .15s ease,color .15s ease,box-shadow .15s ease}.cfg-segmented__btn:hover:not(:disabled):not(.active){color:var(--text)}.cfg-segmented__btn.active{background:#ffffff1f;color:var(--text);box-shadow:0 1px 3px #0003}:root[data-theme=light] .cfg-segmented__btn.active{background:#fff;box-shadow:0 1px 3px #0000001a}.cfg-segmented__btn:disabled{opacity:.5;cursor:not-allowed}.cfg-toggle-row{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:14px 16px;border:1px solid var(--border);border-radius:10px;background:#0000000f;cursor:pointer;transition:background .15s ease,border-color .15s ease}.cfg-toggle-row:hover:not(.disabled){background:#0000001a;border-color:var(--border-strong)}.cfg-toggle-row.disabled{opacity:.6;cursor:not-allowed}:root[data-theme=light] .cfg-toggle-row{background:#ffffff80}:root[data-theme=light] .cfg-toggle-row:hover:not(.disabled){background:#fffc}.cfg-toggle-row__content{flex:1;min-width:0}.cfg-toggle-row__label{display:block;font-size:14px;font-weight:500;color:var(--text)}.cfg-toggle-row__help{display:block;margin-top:2px;font-size:12px;color:var(--muted);line-height:1.4}.cfg-toggle{position:relative;flex-shrink:0}.cfg-toggle input{position:absolute;opacity:0;width:0;height:0}.cfg-toggle__track{display:block;width:48px;height:28px;background:#ffffff1f;border:1px solid var(--border);border-radius:999px;position:relative;transition:background .2s ease,border-color .2s ease}:root[data-theme=light] .cfg-toggle__track{background:#0000001a}.cfg-toggle__track:after{content:"";position:absolute;top:3px;left:3px;width:20px;height:20px;background:var(--text);border-radius:50%;box-shadow:0 2px 4px #0000004d;transition:transform .2s ease,background .2s ease}.cfg-toggle input:checked+.cfg-toggle__track{background:#2bd97f40;border-color:#2bd97f80}.cfg-toggle input:checked+.cfg-toggle__track:after{transform:translate(20px);background:var(--ok)}.cfg-toggle input:focus+.cfg-toggle__track{box-shadow:0 0 0 3px var(--focus)}.cfg-object{border:1px solid var(--border);border-radius:10px;background:#0000000a;overflow:hidden}:root[data-theme=light] .cfg-object{background:#fff6}.cfg-object__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;cursor:pointer;list-style:none;transition:background .15s ease}.cfg-object__header:hover{background:#ffffff08}:root[data-theme=light] .cfg-object__header:hover{background:#00000005}.cfg-object__header::-webkit-details-marker{display:none}.cfg-object__title{font-size:14px;font-weight:600;color:var(--text)}.cfg-object__chevron{width:18px;height:18px;color:var(--muted);transition:transform .2s ease}.cfg-object__chevron svg{width:100%;height:100%}.cfg-object[open] .cfg-object__chevron{transform:rotate(180deg)}.cfg-object__help{padding:0 16px 12px;font-size:12px;color:var(--muted);border-bottom:1px solid var(--border)}.cfg-object__content{padding:16px;display:grid;gap:16px}.cfg-array{border:1px solid var(--border);border-radius:10px;overflow:hidden}.cfg-array__header{display:flex;align-items:center;gap:12px;padding:12px 16px;background:#0000000f;border-bottom:1px solid var(--border)}:root[data-theme=light] .cfg-array__header{background:#00000005}.cfg-array__label{flex:1;font-size:14px;font-weight:600;color:var(--text)}.cfg-array__count{font-size:12px;color:var(--muted);padding:3px 8px;background:#ffffff0f;border-radius:999px}:root[data-theme=light] .cfg-array__count{background:#0000000f}.cfg-array__add{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border:1px solid var(--border);border-radius:6px;background:#ffffff0d;color:var(--text);font-size:12px;font-weight:500;cursor:pointer;transition:background .15s ease}.cfg-array__add:hover:not(:disabled){background:#ffffff1a}.cfg-array__add:disabled{opacity:.5;cursor:not-allowed}.cfg-array__add-icon{width:14px;height:14px}.cfg-array__add-icon svg{width:100%;height:100%}.cfg-array__help{padding:10px 16px;font-size:12px;color:var(--muted);border-bottom:1px solid var(--border)}.cfg-array__empty{padding:32px 16px;text-align:center;color:var(--muted);font-size:13px}.cfg-array__items{display:grid;gap:1px;background:var(--border)}.cfg-array__item{background:var(--panel)}.cfg-array__item-header{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background:#0000000a;border-bottom:1px solid var(--border)}:root[data-theme=light] .cfg-array__item-header{background:#00000005}.cfg-array__item-index{font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.5px}.cfg-array__item-remove{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border:none;border-radius:6px;background:transparent;color:var(--muted);cursor:pointer;transition:background .15s ease,color .15s ease}.cfg-array__item-remove svg{width:16px;height:16px}.cfg-array__item-remove:hover:not(:disabled){background:#ff5c5c26;color:var(--danger)}.cfg-array__item-remove:disabled{opacity:.4;cursor:not-allowed}.cfg-array__item-content{padding:16px}.cfg-map{border:1px solid var(--border);border-radius:10px;overflow:hidden}.cfg-map__header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;background:#0000000f;border-bottom:1px solid var(--border)}:root[data-theme=light] .cfg-map__header{background:#00000005}.cfg-map__label{font-size:13px;font-weight:600;color:var(--muted)}.cfg-map__add{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border:1px solid var(--border);border-radius:6px;background:#ffffff0d;color:var(--text);font-size:12px;font-weight:500;cursor:pointer;transition:background .15s ease}.cfg-map__add:hover:not(:disabled){background:#ffffff1a}.cfg-map__add-icon{width:14px;height:14px}.cfg-map__add-icon svg{width:100%;height:100%}.cfg-map__empty{padding:24px 16px;text-align:center;color:var(--muted);font-size:13px}.cfg-map__items{display:grid;gap:8px;padding:12px}.cfg-map__item{display:grid;grid-template-columns:140px 1fr auto;gap:8px;align-items:start}.cfg-map__item-key,.cfg-map__item-value{min-width:0}.cfg-map__item-remove{width:32px;height:32px;display:flex;align-items:center;justify-content:center;border:none;border-radius:6px;background:transparent;color:var(--muted);cursor:pointer;transition:background .15s ease,color .15s ease}.cfg-map__item-remove svg{width:16px;height:16px}.cfg-map__item-remove:hover:not(:disabled){background:#ff5c5c26;color:var(--danger)}.pill--sm{padding:4px 10px;font-size:11px}.pill--ok{border-color:#2bd97f66;color:var(--ok)}.pill--danger{border-color:#ff5c5c66;color:var(--danger)}@media(max-width:768px){.config-layout{grid-template-columns:1fr}.config-sidebar{border-right:none;border-bottom:1px solid var(--border)}.config-sidebar__header{padding:12px 16px}.config-nav{display:flex;flex-wrap:nowrap;gap:4px;padding:8px 12px;overflow-x:auto;-webkit-overflow-scrolling:touch}.config-nav__item{flex:0 0 auto;padding:8px 12px;white-space:nowrap}.config-nav__label{display:inline}.config-sidebar__footer{display:none}.config-actions{flex-wrap:wrap;padding:12px 16px}.config-actions__left,.config-actions__right{width:100%;justify-content:center}.config-section-hero{padding:12px 16px}.config-subnav{padding:8px 16px 10px}.config-content{padding:16px}.config-section-card__header{padding:14px 16px}.config-section-card__content{padding:16px}.cfg-toggle-row{padding:12px 14px}.cfg-map__item{grid-template-columns:1fr;gap:8px}.cfg-map__item-remove{justify-self:end}}@media(max-width:480px){.config-nav__icon{width:24px;height:24px;font-size:16px}.config-nav__label{display:none}.config-section-card__icon{width:28px;height:28px}.config-section-card__title{font-size:15px}.cfg-segmented{flex-wrap:wrap}.cfg-segmented__btn{flex:1 0 auto;min-width:60px}} diff --git a/dist/control-ui/assets/index-DQcOTEYz.js b/dist/control-ui/assets/index-DQcOTEYz.js new file mode 100644 index 00000000000..e897319f1e7 --- /dev/null +++ b/dist/control-ui/assets/index-DQcOTEYz.js @@ -0,0 +1,3119 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))s(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const o of a.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&s(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function s(i){if(i.ep)return;i.ep=!0;const a=n(i);fetch(i.href,a)}})();const qt=globalThis,Ts=qt.ShadowRoot&&(qt.ShadyCSS===void 0||qt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Cs=Symbol(),Pi=new WeakMap;let qa=class{constructor(t,n,s){if(this._$cssResult$=!0,s!==Cs)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=n}get styleSheet(){let t=this.o;const n=this.t;if(Ts&&t===void 0){const s=n!==void 0&&n.length===1;s&&(t=Pi.get(n)),t===void 0&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),s&&Pi.set(n,t))}return t}toString(){return this.cssText}};const Or=e=>new qa(typeof e=="string"?e:e+"",void 0,Cs),Dr=(e,...t)=>{const n=e.length===1?e[0]:t.reduce((s,i,a)=>s+(o=>{if(o._$cssResult$===!0)return o.cssText;if(typeof o=="number")return o;throw Error("Value passed to 'css' function must be a 'css' function result: "+o+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[a+1],e[0]);return new qa(n,e,Cs)},Br=(e,t)=>{if(Ts)e.adoptedStyleSheets=t.map(n=>n instanceof CSSStyleSheet?n:n.styleSheet);else for(const n of t){const s=document.createElement("style"),i=qt.litNonce;i!==void 0&&s.setAttribute("nonce",i),s.textContent=n.cssText,e.appendChild(s)}},Ni=Ts?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let n="";for(const s of t.cssRules)n+=s.cssText;return Or(n)})(e):e;const{is:Fr,defineProperty:Ur,getOwnPropertyDescriptor:Kr,getOwnPropertyNames:Hr,getOwnPropertySymbols:zr,getPrototypeOf:jr}=Object,nn=globalThis,Oi=nn.trustedTypes,qr=Oi?Oi.emptyScript:"",Vr=nn.reactiveElementPolyfillSupport,bt=(e,t)=>e,Gt={toAttribute(e,t){switch(t){case Boolean:e=e?qr:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let n=e;switch(t){case Boolean:n=e!==null;break;case Number:n=e===null?null:Number(e);break;case Object:case Array:try{n=JSON.parse(e)}catch{n=null}}return n}},Es=(e,t)=>!Fr(e,t),Di={attribute:!0,type:String,converter:Gt,reflect:!1,useDefault:!1,hasChanged:Es};Symbol.metadata??=Symbol("metadata"),nn.litPropertyMetadata??=new WeakMap;let Ye=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,n=Di){if(n.state&&(n.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((n=Object.create(n)).wrapped=!0),this.elementProperties.set(t,n),!n.noAccessor){const s=Symbol(),i=this.getPropertyDescriptor(t,s,n);i!==void 0&&Ur(this.prototype,t,i)}}static getPropertyDescriptor(t,n,s){const{get:i,set:a}=Kr(this.prototype,t)??{get(){return this[n]},set(o){this[n]=o}};return{get:i,set(o){const c=i?.call(this);a?.call(this,o),this.requestUpdate(t,c,s)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??Di}static _$Ei(){if(this.hasOwnProperty(bt("elementProperties")))return;const t=jr(this);t.finalize(),t.l!==void 0&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(bt("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(bt("properties"))){const n=this.properties,s=[...Hr(n),...zr(n)];for(const i of s)this.createProperty(i,n[i])}const t=this[Symbol.metadata];if(t!==null){const n=litPropertyMetadata.get(t);if(n!==void 0)for(const[s,i]of n)this.elementProperties.set(s,i)}this._$Eh=new Map;for(const[n,s]of this.elementProperties){const i=this._$Eu(n,s);i!==void 0&&this._$Eh.set(i,n)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const n=[];if(Array.isArray(t)){const s=new Set(t.flat(1/0).reverse());for(const i of s)n.unshift(Ni(i))}else t!==void 0&&n.push(Ni(t));return n}static _$Eu(t,n){const s=n.attribute;return s===!1?void 0:typeof s=="string"?s:typeof t=="string"?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),this.renderRoot!==void 0&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,n=this.constructor.elementProperties;for(const s of n.keys())this.hasOwnProperty(s)&&(t.set(s,this[s]),delete this[s]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return Br(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,n,s){this._$AK(t,s)}_$ET(t,n){const s=this.constructor.elementProperties.get(t),i=this.constructor._$Eu(t,s);if(i!==void 0&&s.reflect===!0){const a=(s.converter?.toAttribute!==void 0?s.converter:Gt).toAttribute(n,s.type);this._$Em=t,a==null?this.removeAttribute(i):this.setAttribute(i,a),this._$Em=null}}_$AK(t,n){const s=this.constructor,i=s._$Eh.get(t);if(i!==void 0&&this._$Em!==i){const a=s.getPropertyOptions(i),o=typeof a.converter=="function"?{fromAttribute:a.converter}:a.converter?.fromAttribute!==void 0?a.converter:Gt;this._$Em=i;const c=o.fromAttribute(n,a.type);this[i]=c??this._$Ej?.get(i)??c,this._$Em=null}}requestUpdate(t,n,s,i=!1,a){if(t!==void 0){const o=this.constructor;if(i===!1&&(a=this[t]),s??=o.getPropertyOptions(t),!((s.hasChanged??Es)(a,n)||s.useDefault&&s.reflect&&a===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,s))))return;this.C(t,n,s)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(t,n,{useDefault:s,reflect:i,wrapped:a},o){s&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??n??this[t]),a!==!0||o!==void 0)||(this._$AL.has(t)||(this.hasUpdated||s||(n=void 0),this._$AL.set(t,n)),i===!0&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(n){Promise.reject(n)}const t=this.scheduleUpdate();return t!=null&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[i,a]of this._$Ep)this[i]=a;this._$Ep=void 0}const s=this.constructor.elementProperties;if(s.size>0)for(const[i,a]of s){const{wrapped:o}=a,c=this[i];o!==!0||this._$AL.has(i)||c===void 0||this.C(i,void 0,a,c)}}let t=!1;const n=this._$AL;try{t=this.shouldUpdate(n),t?(this.willUpdate(n),this._$EO?.forEach(s=>s.hostUpdate?.()),this.update(n)):this._$EM()}catch(s){throw t=!1,this._$EM(),s}t&&this._$AE(n)}willUpdate(t){}_$AE(t){this._$EO?.forEach(n=>n.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(n=>this._$ET(n,this[n])),this._$EM()}updated(t){}firstUpdated(t){}};Ye.elementStyles=[],Ye.shadowRootOptions={mode:"open"},Ye[bt("elementProperties")]=new Map,Ye[bt("finalized")]=new Map,Vr?.({ReactiveElement:Ye}),(nn.reactiveElementVersions??=[]).push("2.1.2");const Ls=globalThis,Bi=e=>e,Yt=Ls.trustedTypes,Fi=Yt?Yt.createPolicy("lit-html",{createHTML:e=>e}):void 0,Va="$lit$",xe=`lit$${Math.random().toFixed(9).slice(2)}$`,Wa="?"+xe,Wr=`<${Wa}>`,Oe=document,$t=()=>Oe.createComment(""),xt=e=>e===null||typeof e!="object"&&typeof e!="function",Ms=Array.isArray,Gr=e=>Ms(e)||typeof e?.[Symbol.iterator]=="function",On=`[ +\f\r]`,rt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ui=/-->/g,Ki=/>/g,Le=RegExp(`>|${On}(?:([^\\s"'>=/]+)(${On}*=${On}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),Hi=/'/g,zi=/"/g,Ga=/^(?:script|style|textarea|title)$/i,Yr=e=>(t,...n)=>({_$litType$:e,strings:t,values:n}),r=Yr(1),Se=Symbol.for("lit-noChange"),g=Symbol.for("lit-nothing"),ji=new WeakMap,Pe=Oe.createTreeWalker(Oe,129);function Ya(e,t){if(!Ms(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return Fi!==void 0?Fi.createHTML(t):t}const Qr=(e,t)=>{const n=e.length-1,s=[];let i,a=t===2?"":t===3?"":"",o=rt;for(let c=0;c"?(o=i??rt,u=-1):d[1]===void 0?u=-2:(u=o.lastIndex-d[2].length,p=d[1],o=d[3]===void 0?Le:d[3]==='"'?zi:Hi):o===zi||o===Hi?o=Le:o===Ui||o===Ki?o=rt:(o=Le,i=void 0);const v=o===Le&&e[c+1].startsWith("/>")?" ":"";a+=o===rt?l+Wr:u>=0?(s.push(p),l.slice(0,u)+Va+l.slice(u)+xe+v):l+xe+(u===-2?c:v)}return[Ya(e,a+(e[n]||"")+(t===2?"":t===3?"":"")),s]};let ts=class Qa{constructor({strings:t,_$litType$:n},s){let i;this.parts=[];let a=0,o=0;const c=t.length-1,l=this.parts,[p,d]=Qr(t,n);if(this.el=Qa.createElement(p,s),Pe.currentNode=this.el.content,n===2||n===3){const u=this.el.content.firstChild;u.replaceWith(...u.childNodes)}for(;(i=Pe.nextNode())!==null&&l.length0){i.textContent=Yt?Yt.emptyScript:"";for(let v=0;v2||s[0]!==""||s[1]!==""?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=g}_$AI(t,n=this,s,i){const a=this.strings;let o=!1;if(a===void 0)t=Je(this,t,n,0),o=!xt(t)||t!==this._$AH&&t!==Se,o&&(this._$AH=t);else{const c=t;let l,p;for(t=a[0],l=0;l{const s=n?.renderBefore??t;let i=s._$litPart$;if(i===void 0){const a=n?.renderBefore??null;s._$litPart$=i=new sn(t.insertBefore($t(),a),a,void 0,n??{})}return i._$AI(e),i};const Is=globalThis;let Ze=class extends Ye{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const n=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=il(n,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return Se}};Ze._$litElement$=!0,Ze.finalized=!0,Is.litElementHydrateSupport?.({LitElement:Ze});const al=Is.litElementPolyfillSupport;al?.({LitElement:Ze});(Is.litElementVersions??=[]).push("4.2.2");const Ja=e=>(t,n)=>{n!==void 0?n.addInitializer(()=>{customElements.define(e,t)}):customElements.define(e,t)};const ol={attribute:!0,type:String,converter:Gt,reflect:!1,hasChanged:Es},rl=(e=ol,t,n)=>{const{kind:s,metadata:i}=n;let a=globalThis.litPropertyMetadata.get(i);if(a===void 0&&globalThis.litPropertyMetadata.set(i,a=new Map),s==="setter"&&((e=Object.create(e)).wrapped=!0),a.set(n.name,e),s==="accessor"){const{name:o}=n;return{set(c){const l=t.get.call(this);t.set.call(this,c),this.requestUpdate(o,l,e,!0,c)},init(c){return c!==void 0&&this.C(o,void 0,e,c),c}}}if(s==="setter"){const{name:o}=n;return function(c){const l=this[o];t.call(this,c),this.requestUpdate(o,l,e,!0,c)}}throw Error("Unsupported decorator location: "+s)};function on(e){return(t,n)=>typeof n=="object"?rl(e,t,n):((s,i,a)=>{const o=i.hasOwnProperty(a);return i.constructor.createProperty(a,s),o?Object.getOwnPropertyDescriptor(i,a):void 0})(e,t,n)}function y(e){return on({...e,state:!0,attribute:!1})}const ll=50,cl=200,dl="Assistant";function qi(e,t){if(typeof e!="string")return;const n=e.trim();if(n)return n.length<=t?n:n.slice(0,t)}function ns(e){const t=qi(e?.name,ll)??dl,n=qi(e?.avatar??void 0,cl)??null;return{agentId:typeof e?.agentId=="string"&&e.agentId.trim()?e.agentId.trim():null,name:t,avatar:n}}function ul(){return ns(typeof window>"u"?{}:{name:window.__CLAWDBOT_ASSISTANT_NAME__,avatar:window.__CLAWDBOT_ASSISTANT_AVATAR__})}const Xa="clawdbot.control.settings.v1";function pl(){const t={gatewayUrl:`${location.protocol==="https:"?"wss":"ws"}://${location.host}`,token:"",sessionKey:"main",lastActiveSessionKey:"main",theme:"system",chatFocusMode:!1,chatShowThinking:!0,splitRatio:.6,navCollapsed:!1,navGroupsCollapsed:{}};try{const n=localStorage.getItem(Xa);if(!n)return t;const s=JSON.parse(n);return{gatewayUrl:typeof s.gatewayUrl=="string"&&s.gatewayUrl.trim()?s.gatewayUrl.trim():t.gatewayUrl,token:typeof s.token=="string"?s.token:t.token,sessionKey:typeof s.sessionKey=="string"&&s.sessionKey.trim()?s.sessionKey.trim():t.sessionKey,lastActiveSessionKey:typeof s.lastActiveSessionKey=="string"&&s.lastActiveSessionKey.trim()?s.lastActiveSessionKey.trim():typeof s.sessionKey=="string"&&s.sessionKey.trim()||t.lastActiveSessionKey,theme:s.theme==="light"||s.theme==="dark"||s.theme==="system"?s.theme:t.theme,chatFocusMode:typeof s.chatFocusMode=="boolean"?s.chatFocusMode:t.chatFocusMode,chatShowThinking:typeof s.chatShowThinking=="boolean"?s.chatShowThinking:t.chatShowThinking,splitRatio:typeof s.splitRatio=="number"&&s.splitRatio>=.4&&s.splitRatio<=.7?s.splitRatio:t.splitRatio,navCollapsed:typeof s.navCollapsed=="boolean"?s.navCollapsed:t.navCollapsed,navGroupsCollapsed:typeof s.navGroupsCollapsed=="object"&&s.navGroupsCollapsed!==null?s.navGroupsCollapsed:t.navGroupsCollapsed}}catch{return t}}function fl(e){localStorage.setItem(Xa,JSON.stringify(e))}function eo(e){const t=(e??"").trim();if(!t)return null;const n=t.split(":").filter(Boolean);if(n.length<3||n[0]!=="agent")return null;const s=n[1]?.trim(),i=n.slice(2).join(":");return!s||!i?null:{agentId:s,rest:i}}const hl=[{label:"Chat",tabs:["chat"]},{label:"Control",tabs:["overview","channels","instances","sessions","cron"]},{label:"Agent",tabs:["skills","nodes"]},{label:"Settings",tabs:["config","debug","logs"]}],to={overview:"/overview",channels:"/channels",instances:"/instances",sessions:"/sessions",cron:"/cron",skills:"/skills",nodes:"/nodes",chat:"/chat",config:"/config",debug:"/debug",logs:"/logs"},no=new Map(Object.entries(to).map(([e,t])=>[t,e]));function rn(e){if(!e)return"";let t=e.trim();return t.startsWith("/")||(t=`/${t}`),t==="/"?"":(t.endsWith("/")&&(t=t.slice(0,-1)),t)}function kt(e){if(!e)return"/";let t=e.trim();return t.startsWith("/")||(t=`/${t}`),t.length>1&&t.endsWith("/")&&(t=t.slice(0,-1)),t}function Rs(e,t=""){const n=rn(t),s=to[e];return n?`${n}${s}`:s}function so(e,t=""){const n=rn(t);let s=e||"/";n&&(s===n?s="/":s.startsWith(`${n}/`)&&(s=s.slice(n.length)));let i=kt(s).toLowerCase();return i.endsWith("/index.html")&&(i="/"),i==="/"?"chat":no.get(i)??null}function gl(e){let t=kt(e);if(t.endsWith("/index.html")&&(t=kt(t.slice(0,-11))),t==="/")return"";const n=t.split("/").filter(Boolean);if(n.length===0)return"";for(let s=0;s`,barChart:r``,link:r``,radio:r``,fileText:r``,zap:r``,monitor:r``,settings:r``,bug:r``,scrollText:r``,folder:r``,menu:r``,x:r``,check:r``,copy:r``,search:r``,brain:r``,book:r``,loader:r``,wrench:r``,fileCode:r``,edit:r``,penLine:r``,paperclip:r``,globe:r``,image:r``,smartphone:r``,plug:r``,circle:r``,puzzle:r``},bl=/<\s*\/?\s*(?:think(?:ing)?|thought|antthinking|final)\b/i,Dt=/<\s*\/?\s*final\b[^>]*>/gi,Vi=/<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\b[^>]*>/gi;function yl(e,t){return e.trimStart()}function wl(e,t){if(!e||!bl.test(e))return e;let n=e;Dt.test(n)?(Dt.lastIndex=0,n=n.replace(Dt,"")):Dt.lastIndex=0,Vi.lastIndex=0;let s="",i=0,a=!1;for(const o of n.matchAll(Vi)){const c=o.index??0,l=o[1]==="/";a?l&&(a=!1):(s+=n.slice(i,c),l||(a=!0)),i=c+o[0].length}return s+=n.slice(i),yl(s)}function At(e){return!e&&e!==0?"n/a":new Date(e).toLocaleString()}function O(e){if(!e&&e!==0)return"n/a";const t=Date.now()-e;if(t<0)return"just now";const n=Math.round(t/1e3);if(n<60)return`${n}s ago`;const s=Math.round(n/60);if(s<60)return`${s}m ago`;const i=Math.round(s/60);return i<48?`${i}h ago`:`${Math.round(i/24)}d ago`}function io(e){if(!e&&e!==0)return"n/a";if(e<1e3)return`${e}ms`;const t=Math.round(e/1e3);if(t<60)return`${t}s`;const n=Math.round(t/60);if(n<60)return`${n}m`;const s=Math.round(n/60);return s<48?`${s}h`:`${Math.round(s/24)}d`}function is(e){return!e||e.length===0?"none":e.filter(t=>!!(t&&t.trim())).join(", ")}function as(e,t=120){return e.length<=t?e:`${e.slice(0,Math.max(0,t-1))}…`}function ao(e,t){return e.length<=t?{text:e,truncated:!1,total:e.length}:{text:e.slice(0,Math.max(0,t)),truncated:!0,total:e.length}}function Qt(e,t){const n=Number(e);return Number.isFinite(n)?n:t}function Dn(e){return wl(e)}const $l=/^\[([^\]]+)\]\s*/,xl=["WebChat","WhatsApp","Telegram","Signal","Slack","Discord","iMessage","Teams","Matrix","Zalo","Zalo Personal","BlueBubbles"],Bn=new WeakMap,Fn=new WeakMap;function kl(e){return/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}Z\b/.test(e)||/\d{4}-\d{2}-\d{2} \d{2}:\d{2}\b/.test(e)?!0:xl.some(t=>e.startsWith(`${t} `))}function Un(e){const t=e.match($l);if(!t)return e;const n=t[1]??"";return kl(n)?e.slice(t[0].length):e}function os(e){const t=e,n=typeof t.role=="string"?t.role:"",s=t.content;if(typeof s=="string")return n==="assistant"?Dn(s):Un(s);if(Array.isArray(s)){const i=s.map(a=>{const o=a;return o.type==="text"&&typeof o.text=="string"?o.text:null}).filter(a=>typeof a=="string");if(i.length>0){const a=i.join(` +`);return n==="assistant"?Dn(a):Un(a)}}return typeof t.text=="string"?n==="assistant"?Dn(t.text):Un(t.text):null}function oo(e){if(!e||typeof e!="object")return os(e);const t=e;if(Bn.has(t))return Bn.get(t)??null;const n=os(e);return Bn.set(t,n),n}function Wi(e){const n=e.content,s=[];if(Array.isArray(n))for(const c of n){const l=c;if(l.type==="thinking"&&typeof l.thinking=="string"){const p=l.thinking.trim();p&&s.push(p)}}if(s.length>0)return s.join(` +`);const i=Sl(e);if(!i)return null;const o=[...i.matchAll(/<\s*think(?:ing)?\s*>([\s\S]*?)<\s*\/\s*think(?:ing)?\s*>/gi)].map(c=>(c[1]??"").trim()).filter(Boolean);return o.length>0?o.join(` +`):null}function Al(e){if(!e||typeof e!="object")return Wi(e);const t=e;if(Fn.has(t))return Fn.get(t)??null;const n=Wi(e);return Fn.set(t,n),n}function Sl(e){const t=e,n=t.content;if(typeof n=="string")return n;if(Array.isArray(n)){const s=n.map(i=>{const a=i;return a.type==="text"&&typeof a.text=="string"?a.text:null}).filter(i=>typeof i=="string");if(s.length>0)return s.join(` +`)}return typeof t.text=="string"?t.text:null}function _l(e){const t=e.trim();if(!t)return"";const n=t.split(/\r?\n/).map(s=>s.trim()).filter(Boolean).map(s=>`_${s}_`);return n.length?["_Reasoning:_",...n].join(` +`):""}function Gi(e){e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t="";for(let n=0;n>>8&255,e[2]^=t>>>16&255,e[3]^=t>>>24&255,e}function Ps(e=globalThis.crypto){if(e&&typeof e.randomUUID=="function")return e.randomUUID();if(e&&typeof e.getRandomValues=="function"){const t=new Uint8Array(16);return e.getRandomValues(t),Gi(t)}return Gi(Tl())}async function Xe(e){if(!(!e.client||!e.connected)){e.chatLoading=!0,e.lastError=null;try{const t=await e.client.request("chat.history",{sessionKey:e.sessionKey,limit:200});e.chatMessages=Array.isArray(t.messages)?t.messages:[],e.chatThinkingLevel=t.thinkingLevel??null}catch(t){e.lastError=String(t)}finally{e.chatLoading=!1}}}async function Cl(e,t){if(!e.client||!e.connected)return!1;const n=t.trim();if(!n)return!1;const s=Date.now();e.chatMessages=[...e.chatMessages,{role:"user",content:[{type:"text",text:n}],timestamp:s}],e.chatSending=!0,e.lastError=null;const i=Ps();e.chatRunId=i,e.chatStream="",e.chatStreamStartedAt=s;try{return await e.client.request("chat.send",{sessionKey:e.sessionKey,message:n,deliver:!1,idempotencyKey:i}),!0}catch(a){const o=String(a);return e.chatRunId=null,e.chatStream=null,e.chatStreamStartedAt=null,e.lastError=o,e.chatMessages=[...e.chatMessages,{role:"assistant",content:[{type:"text",text:"Error: "+o}],timestamp:Date.now()}],!1}finally{e.chatSending=!1}}async function El(e){if(!e.client||!e.connected)return!1;const t=e.chatRunId;try{return await e.client.request("chat.abort",t?{sessionKey:e.sessionKey,runId:t}:{sessionKey:e.sessionKey}),!0}catch(n){return e.lastError=String(n),!1}}function Ll(e,t){if(!t||t.sessionKey!==e.sessionKey||t.runId&&e.chatRunId&&t.runId!==e.chatRunId)return null;if(t.state==="delta"){const n=os(t.message);if(typeof n=="string"){const s=e.chatStream??"";(!s||n.length>=s.length)&&(e.chatStream=n)}}else t.state==="final"||t.state==="aborted"?(e.chatStream=null,e.chatRunId=null,e.chatStreamStartedAt=null):t.state==="error"&&(e.chatStream=null,e.chatRunId=null,e.chatStreamStartedAt=null,e.lastError=t.errorMessage??"chat error");return t.state}async function st(e){if(!(!e.client||!e.connected)&&!e.sessionsLoading){e.sessionsLoading=!0,e.sessionsError=null;try{const t={includeGlobal:e.sessionsIncludeGlobal,includeUnknown:e.sessionsIncludeUnknown},n=Qt(e.sessionsFilterActive,0),s=Qt(e.sessionsFilterLimit,0);n>0&&(t.activeMinutes=n),s>0&&(t.limit=s);const i=await e.client.request("sessions.list",t);i&&(e.sessionsResult=i)}catch(t){e.sessionsError=String(t)}finally{e.sessionsLoading=!1}}}async function Ml(e,t,n){if(!e.client||!e.connected)return;const s={key:t};"label"in n&&(s.label=n.label),"thinkingLevel"in n&&(s.thinkingLevel=n.thinkingLevel),"verboseLevel"in n&&(s.verboseLevel=n.verboseLevel),"reasoningLevel"in n&&(s.reasoningLevel=n.reasoningLevel);try{await e.client.request("sessions.patch",s),await st(e)}catch(i){e.sessionsError=String(i)}}async function Il(e,t){if(!(!e.client||!e.connected||e.sessionsLoading||!window.confirm(`Delete session "${t}"? + +Deletes the session entry and archives its transcript.`))){e.sessionsLoading=!0,e.sessionsError=null;try{await e.client.request("sessions.delete",{key:t,deleteTranscript:!0}),await st(e)}catch(s){e.sessionsError=String(s)}finally{e.sessionsLoading=!1}}}const Yi=50,Rl=80,Pl=12e4;function Nl(e){if(!e||typeof e!="object")return null;const t=e;if(typeof t.text=="string")return t.text;const n=t.content;if(!Array.isArray(n))return null;const s=n.map(i=>{if(!i||typeof i!="object")return null;const a=i;return a.type==="text"&&typeof a.text=="string"?a.text:null}).filter(i=>!!i);return s.length===0?null:s.join(` +`)}function Qi(e){if(e==null)return null;if(typeof e=="number"||typeof e=="boolean")return String(e);const t=Nl(e);let n;if(typeof e=="string")n=e;else if(t)n=t;else try{n=JSON.stringify(e,null,2)}catch{n=String(e)}const s=ao(n,Pl);return s.truncated?`${s.text} + +… truncated (${s.total} chars, showing first ${s.text.length}).`:s.text}function Ol(e){const t=[];return t.push({type:"toolcall",name:e.name,arguments:e.args??{}}),e.output&&t.push({type:"toolresult",name:e.name,text:e.output}),{role:"assistant",toolCallId:e.toolCallId,runId:e.runId,content:t,timestamp:e.startedAt}}function Dl(e){if(e.toolStreamOrder.length<=Yi)return;const t=e.toolStreamOrder.length-Yi,n=e.toolStreamOrder.splice(0,t);for(const s of n)e.toolStreamById.delete(s)}function Bl(e){e.chatToolMessages=e.toolStreamOrder.map(t=>e.toolStreamById.get(t)?.message).filter(t=>!!t)}function rs(e){e.toolStreamSyncTimer!=null&&(clearTimeout(e.toolStreamSyncTimer),e.toolStreamSyncTimer=null),Bl(e)}function Fl(e,t=!1){if(t){rs(e);return}e.toolStreamSyncTimer==null&&(e.toolStreamSyncTimer=window.setTimeout(()=>rs(e),Rl))}function Ns(e){e.toolStreamById.clear(),e.toolStreamOrder=[],e.chatToolMessages=[],rs(e)}const Ul=5e3;function Kl(e,t){const n=t.data??{},s=typeof n.phase=="string"?n.phase:"";e.compactionClearTimer!=null&&(window.clearTimeout(e.compactionClearTimer),e.compactionClearTimer=null),s==="start"?e.compactionStatus={active:!0,startedAt:Date.now(),completedAt:null}:s==="end"&&(e.compactionStatus={active:!1,startedAt:e.compactionStatus?.startedAt??null,completedAt:Date.now()},e.compactionClearTimer=window.setTimeout(()=>{e.compactionStatus=null,e.compactionClearTimer=null},Ul))}function Hl(e,t){if(!t)return;if(t.stream==="compaction"){Kl(e,t);return}if(t.stream!=="tool")return;const n=typeof t.sessionKey=="string"?t.sessionKey:void 0;if(n&&n!==e.sessionKey||!n&&e.chatRunId&&t.runId!==e.chatRunId||e.chatRunId&&t.runId!==e.chatRunId||!e.chatRunId)return;const s=t.data??{},i=typeof s.toolCallId=="string"?s.toolCallId:"";if(!i)return;const a=typeof s.name=="string"?s.name:"tool",o=typeof s.phase=="string"?s.phase:"",c=o==="start"?s.args:void 0,l=o==="update"?Qi(s.partialResult):o==="result"?Qi(s.result):void 0,p=Date.now();let d=e.toolStreamById.get(i);d?(d.name=a,c!==void 0&&(d.args=c),l!==void 0&&(d.output=l),d.updatedAt=p):(d={toolCallId:i,runId:t.runId,sessionKey:n,name:a,args:c,output:l,startedAt:typeof t.ts=="number"?t.ts:p,updatedAt:p,message:{}},e.toolStreamById.set(i,d),e.toolStreamOrder.push(i)),d.message=Ol(d),Dl(e),Fl(e,o==="result")}function ln(e,t=!1){e.chatScrollFrame&&cancelAnimationFrame(e.chatScrollFrame),e.chatScrollTimeout!=null&&(clearTimeout(e.chatScrollTimeout),e.chatScrollTimeout=null);const n=()=>{const s=e.querySelector(".chat-thread");if(s){const i=getComputedStyle(s).overflowY;if(i==="auto"||i==="scroll"||s.scrollHeight-s.clientHeight>1)return s}return document.scrollingElement??document.documentElement};e.updateComplete.then(()=>{e.chatScrollFrame=requestAnimationFrame(()=>{e.chatScrollFrame=null;const s=n();if(!s)return;const i=s.scrollHeight-s.scrollTop-s.clientHeight;if(!(t||e.chatUserNearBottom||i<200))return;t&&(e.chatHasAutoScrolled=!0),s.scrollTop=s.scrollHeight,e.chatUserNearBottom=!0;const o=t?150:120;e.chatScrollTimeout=window.setTimeout(()=>{e.chatScrollTimeout=null;const c=n();if(!c)return;const l=c.scrollHeight-c.scrollTop-c.clientHeight;(t||e.chatUserNearBottom||l<200)&&(c.scrollTop=c.scrollHeight,e.chatUserNearBottom=!0)},o)})})}function ro(e,t=!1){e.logsScrollFrame&&cancelAnimationFrame(e.logsScrollFrame),e.updateComplete.then(()=>{e.logsScrollFrame=requestAnimationFrame(()=>{e.logsScrollFrame=null;const n=e.querySelector(".log-stream");if(!n)return;const s=n.scrollHeight-n.scrollTop-n.clientHeight;(t||s<80)&&(n.scrollTop=n.scrollHeight)})})}function zl(e,t){const n=t.currentTarget;if(!n)return;const s=n.scrollHeight-n.scrollTop-n.clientHeight;e.chatUserNearBottom=s<200}function jl(e,t){const n=t.currentTarget;if(!n)return;const s=n.scrollHeight-n.scrollTop-n.clientHeight;e.logsAtBottom=s<80}function ql(e){e.chatHasAutoScrolled=!1,e.chatUserNearBottom=!0}function Vl(e,t){if(e.length===0)return;const n=new Blob([`${e.join(` +`)} +`],{type:"text/plain"}),s=URL.createObjectURL(n),i=document.createElement("a"),a=new Date().toISOString().slice(0,19).replace(/[:T]/g,"-");i.href=s,i.download=`clawdbot-logs-${t}-${a}.log`,i.click(),URL.revokeObjectURL(s)}function Wl(e){if(typeof ResizeObserver>"u")return;const t=e.querySelector(".topbar");if(!t)return;const n=()=>{const{height:s}=t.getBoundingClientRect();e.style.setProperty("--topbar-height",`${s}px`)};n(),e.topbarObserver=new ResizeObserver(()=>n()),e.topbarObserver.observe(t)}function De(e){return typeof structuredClone=="function"?structuredClone(e):JSON.parse(JSON.stringify(e))}function et(e){return`${JSON.stringify(e,null,2).trimEnd()} +`}function lo(e,t,n){if(t.length===0)return;let s=e;for(let a=0;a0&&(n.timeoutSeconds=s),n}async function ec(e){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{const t=Jl(e.cronForm),n=Xl(e.cronForm),s=e.cronForm.agentId.trim(),i={name:e.cronForm.name.trim(),description:e.cronForm.description.trim()||void 0,agentId:s||void 0,enabled:e.cronForm.enabled,schedule:t,sessionTarget:e.cronForm.sessionTarget,wakeMode:e.cronForm.wakeMode,payload:n,isolation:e.cronForm.postToMainPrefix.trim()&&e.cronForm.sessionTarget==="isolated"?{postToMainPrefix:e.cronForm.postToMainPrefix.trim()}:void 0};if(!i.name)throw new Error("Name required.");await e.client.request("cron.add",i),e.cronForm={...e.cronForm,name:"",description:"",payloadText:""},await cn(e),await Tt(e)}catch(t){e.cronError=String(t)}finally{e.cronBusy=!1}}}async function tc(e,t,n){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.update",{id:t.id,patch:{enabled:n}}),await cn(e),await Tt(e)}catch(s){e.cronError=String(s)}finally{e.cronBusy=!1}}}async function nc(e,t){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.run",{id:t.id,mode:"force"}),await po(e,t.id)}catch(n){e.cronError=String(n)}finally{e.cronBusy=!1}}}async function sc(e,t){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.remove",{id:t.id}),e.cronRunsJobId===t.id&&(e.cronRunsJobId=null,e.cronRuns=[]),await cn(e),await Tt(e)}catch(n){e.cronError=String(n)}finally{e.cronBusy=!1}}}async function po(e,t){if(!(!e.client||!e.connected))try{const n=await e.client.request("cron.runs",{id:t,limit:50});e.cronRunsJobId=t,e.cronRuns=Array.isArray(n.entries)?n.entries:[]}catch(n){e.cronError=String(n)}}async function oe(e,t){if(!(!e.client||!e.connected)&&!e.channelsLoading){e.channelsLoading=!0,e.channelsError=null;try{const n=await e.client.request("channels.status",{probe:t,timeoutMs:8e3});e.channelsSnapshot=n,e.channelsLastSuccess=Date.now()}catch(n){e.channelsError=String(n)}finally{e.channelsLoading=!1}}}async function ic(e,t){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{const n=await e.client.request("web.login.start",{force:t,timeoutMs:3e4});e.whatsappLoginMessage=n.message??null,e.whatsappLoginQrDataUrl=n.qrDataUrl??null,e.whatsappLoginConnected=null}catch(n){e.whatsappLoginMessage=String(n),e.whatsappLoginQrDataUrl=null,e.whatsappLoginConnected=null}finally{e.whatsappBusy=!1}}}async function ac(e){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{const t=await e.client.request("web.login.wait",{timeoutMs:12e4});e.whatsappLoginMessage=t.message??null,e.whatsappLoginConnected=t.connected??null,t.connected&&(e.whatsappLoginQrDataUrl=null)}catch(t){e.whatsappLoginMessage=String(t),e.whatsappLoginConnected=null}finally{e.whatsappBusy=!1}}}async function oc(e){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{await e.client.request("channels.logout",{channel:"whatsapp"}),e.whatsappLoginMessage="Logged out.",e.whatsappLoginQrDataUrl=null,e.whatsappLoginConnected=null}catch(t){e.whatsappLoginMessage=String(t)}finally{e.whatsappBusy=!1}}}async function dn(e){if(!(!e.client||!e.connected)&&!e.debugLoading){e.debugLoading=!0;try{const[t,n,s,i]=await Promise.all([e.client.request("status",{}),e.client.request("health",{}),e.client.request("models.list",{}),e.client.request("last-heartbeat",{})]);e.debugStatus=t,e.debugHealth=n;const a=s;e.debugModels=Array.isArray(a?.models)?a?.models:[],e.debugHeartbeat=i}catch(t){e.debugCallError=String(t)}finally{e.debugLoading=!1}}}async function rc(e){if(!(!e.client||!e.connected)){e.debugCallError=null,e.debugCallResult=null;try{const t=e.debugCallParams.trim()?JSON.parse(e.debugCallParams):{},n=await e.client.request(e.debugCallMethod.trim(),t);e.debugCallResult=JSON.stringify(n,null,2)}catch(t){e.debugCallError=String(t)}}}const lc=2e3,cc=new Set(["trace","debug","info","warn","error","fatal"]);function dc(e){if(typeof e!="string")return null;const t=e.trim();if(!t.startsWith("{")||!t.endsWith("}"))return null;try{const n=JSON.parse(t);return!n||typeof n!="object"?null:n}catch{return null}}function uc(e){if(typeof e!="string")return null;const t=e.toLowerCase();return cc.has(t)?t:null}function pc(e){if(!e.trim())return{raw:e,message:e};try{const t=JSON.parse(e),n=t&&typeof t._meta=="object"&&t._meta!==null?t._meta:null,s=typeof t.time=="string"?t.time:typeof n?.date=="string"?n?.date:null,i=uc(n?.logLevelName??n?.level),a=typeof t[0]=="string"?t[0]:typeof n?.name=="string"?n?.name:null,o=dc(a);let c=null;o&&(typeof o.subsystem=="string"?c=o.subsystem:typeof o.module=="string"&&(c=o.module)),!c&&a&&a.length<120&&(c=a);let l=null;return typeof t[1]=="string"?l=t[1]:!o&&typeof t[0]=="string"?l=t[0]:typeof t.message=="string"&&(l=t.message),{raw:e,time:s,level:i,subsystem:c,message:l??e,meta:n??void 0}}catch{return{raw:e,message:e}}}async function Os(e,t){if(!(!e.client||!e.connected)&&!(e.logsLoading&&!t?.quiet)){t?.quiet||(e.logsLoading=!0),e.logsError=null;try{const s=await e.client.request("logs.tail",{cursor:t?.reset?void 0:e.logsCursor??void 0,limit:e.logsLimit,maxBytes:e.logsMaxBytes}),a=(Array.isArray(s.lines)?s.lines.filter(c=>typeof c=="string"):[]).map(pc),o=!!(t?.reset||s.reset||e.logsCursor==null);e.logsEntries=o?a:[...e.logsEntries,...a].slice(-lc),typeof s.cursor=="number"&&(e.logsCursor=s.cursor),typeof s.file=="string"&&(e.logsFile=s.file),e.logsTruncated=!!s.truncated,e.logsLastFetchAt=Date.now()}catch(n){e.logsError=String(n)}finally{t?.quiet||(e.logsLoading=!1)}}}const fo={p:0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,n:0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,h:8n,a:0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,d:0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,Gx:0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,Gy:0x6666666666666666666666666666666666666666666666666666666666666658n},{p:V,n:Vt,Gx:Ji,Gy:Xi,a:Kn,d:Hn,h:fc}=fo,Be=32,Ds=64,hc=(...e)=>{"captureStackTrace"in Error&&typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(...e)},H=(e="")=>{const t=new Error(e);throw hc(t,H),t},gc=e=>typeof e=="bigint",vc=e=>typeof e=="string",mc=e=>e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array",_e=(e,t,n="")=>{const s=mc(e),i=e?.length,a=t!==void 0;if(!s||a&&i!==t){const o=n&&`"${n}" `,c=a?` of length ${t}`:"",l=s?`length=${i}`:`type=${typeof e}`;H(o+"expected Uint8Array"+c+", got "+l)}return e},un=e=>new Uint8Array(e),ho=e=>Uint8Array.from(e),go=(e,t)=>e.toString(16).padStart(t,"0"),vo=e=>Array.from(_e(e)).map(t=>go(t,2)).join(""),ve={_0:48,_9:57,A:65,F:70,a:97,f:102},ea=e=>{if(e>=ve._0&&e<=ve._9)return e-ve._0;if(e>=ve.A&&e<=ve.F)return e-(ve.A-10);if(e>=ve.a&&e<=ve.f)return e-(ve.a-10)},mo=e=>{const t="hex invalid";if(!vc(e))return H(t);const n=e.length,s=n/2;if(n%2)return H(t);const i=un(s);for(let a=0,o=0;aglobalThis?.crypto,bc=()=>bo()?.subtle??H("crypto.subtle must be defined, consider polyfill"),St=(...e)=>{const t=un(e.reduce((s,i)=>s+_e(i).length,0));let n=0;return e.forEach(s=>{t.set(s,n),n+=s.length}),t},yc=(e=Be)=>bo().getRandomValues(un(e)),Zt=BigInt,Re=(e,t,n,s="bad number: out of range")=>gc(e)&&t<=e&&e{const n=e%t;return n>=0n?n:t+n},yo=e=>A(e,Vt),wc=(e,t)=>{(e===0n||t<=0n)&&H("no inverse n="+e+" mod="+t);let n=A(e,t),s=t,i=0n,a=1n;for(;n!==0n;){const o=s/n,c=s%n,l=i-a*o;s=n,n=c,i=a,a=l}return s===1n?A(i,t):H("no inverse")},$c=e=>{const t=ko[e];return typeof t!="function"&&H("hashes."+e+" not set"),t},zn=e=>e instanceof ee?e:H("Point expected"),cs=2n**256n;class ee{static BASE;static ZERO;X;Y;Z;T;constructor(t,n,s,i){const a=cs;this.X=Re(t,0n,a),this.Y=Re(n,0n,a),this.Z=Re(s,1n,a),this.T=Re(i,0n,a),Object.freeze(this)}static CURVE(){return fo}static fromAffine(t){return new ee(t.x,t.y,1n,A(t.x*t.y))}static fromBytes(t,n=!1){const s=Hn,i=ho(_e(t,Be)),a=t[31];i[31]=a&-129;const o=$o(i);Re(o,0n,n?cs:V);const l=A(o*o),p=A(l-1n),d=A(s*l+1n);let{isValid:u,value:h}=kc(p,d);u||H("bad point: y not sqrt");const v=(h&1n)===1n,w=(a&128)!==0;return!n&&h===0n&&w&&H("bad point: x==0, isLastByteOdd"),w!==v&&(h=A(-h)),new ee(h,o,1n,A(h*o))}static fromHex(t,n){return ee.fromBytes(mo(t),n)}get x(){return this.toAffine().x}get y(){return this.toAffine().y}assertValidity(){const t=Kn,n=Hn,s=this;if(s.is0())return H("bad point: ZERO");const{X:i,Y:a,Z:o,T:c}=s,l=A(i*i),p=A(a*a),d=A(o*o),u=A(d*d),h=A(l*t),v=A(d*A(h+p)),w=A(u+A(n*A(l*p)));if(v!==w)return H("bad point: equation left != right (1)");const $=A(i*a),k=A(o*c);return $!==k?H("bad point: equation left != right (2)"):this}equals(t){const{X:n,Y:s,Z:i}=this,{X:a,Y:o,Z:c}=zn(t),l=A(n*c),p=A(a*i),d=A(s*c),u=A(o*i);return l===p&&d===u}is0(){return this.equals(Qe)}negate(){return new ee(A(-this.X),this.Y,this.Z,A(-this.T))}double(){const{X:t,Y:n,Z:s}=this,i=Kn,a=A(t*t),o=A(n*n),c=A(2n*A(s*s)),l=A(i*a),p=t+n,d=A(A(p*p)-a-o),u=l+o,h=u-c,v=l-o,w=A(d*h),$=A(u*v),k=A(d*v),T=A(h*u);return new ee(w,$,T,k)}add(t){const{X:n,Y:s,Z:i,T:a}=this,{X:o,Y:c,Z:l,T:p}=zn(t),d=Kn,u=Hn,h=A(n*o),v=A(s*c),w=A(a*u*p),$=A(i*l),k=A((n+s)*(o+c)-h-v),T=A($-w),M=A($+w),P=A(v-d*h),L=A(k*T),C=A(M*P),E=A(k*P),pe=A(T*M);return new ee(L,C,pe,E)}subtract(t){return this.add(zn(t).negate())}multiply(t,n=!0){if(!n&&(t===0n||this.is0()))return Qe;if(Re(t,1n,Vt),t===1n)return this;if(this.equals(Fe))return Pc(t).p;let s=Qe,i=Fe;for(let a=this;t>0n;a=a.double(),t>>=1n)t&1n?s=s.add(a):n&&(i=i.add(a));return s}multiplyUnsafe(t){return this.multiply(t,!1)}toAffine(){const{X:t,Y:n,Z:s}=this;if(this.equals(Qe))return{x:0n,y:1n};const i=wc(s,V);A(s*i)!==1n&&H("invalid inverse");const a=A(t*i),o=A(n*i);return{x:a,y:o}}toBytes(){const{x:t,y:n}=this.assertValidity().toAffine(),s=wo(n);return s[31]|=t&1n?128:0,s}toHex(){return vo(this.toBytes())}clearCofactor(){return this.multiply(Zt(fc),!1)}isSmallOrder(){return this.clearCofactor().is0()}isTorsionFree(){let t=this.multiply(Vt/2n,!1).double();return Vt%2n&&(t=t.add(this)),t.is0()}}const Fe=new ee(Ji,Xi,1n,A(Ji*Xi)),Qe=new ee(0n,1n,1n,0n);ee.BASE=Fe;ee.ZERO=Qe;const wo=e=>mo(go(Re(e,0n,cs),Ds)).reverse(),$o=e=>Zt("0x"+vo(ho(_e(e)).reverse())),ce=(e,t)=>{let n=e;for(;t-- >0n;)n*=n,n%=V;return n},xc=e=>{const n=e*e%V*e%V,s=ce(n,2n)*n%V,i=ce(s,1n)*e%V,a=ce(i,5n)*i%V,o=ce(a,10n)*a%V,c=ce(o,20n)*o%V,l=ce(c,40n)*c%V,p=ce(l,80n)*l%V,d=ce(p,80n)*l%V,u=ce(d,10n)*a%V;return{pow_p_5_8:ce(u,2n)*e%V,b2:n}},ta=0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n,kc=(e,t)=>{const n=A(t*t*t),s=A(n*n*t),i=xc(e*s).pow_p_5_8;let a=A(e*n*i);const o=A(t*a*a),c=a,l=A(a*ta),p=o===e,d=o===A(-e),u=o===A(-e*ta);return p&&(a=c),(d||u)&&(a=l),(A(a)&1n)===1n&&(a=A(-a)),{isValid:p||d,value:a}},ds=e=>yo($o(e)),Bs=(...e)=>ko.sha512Async(St(...e)),Ac=(...e)=>$c("sha512")(St(...e)),xo=e=>{const t=e.slice(0,Be);t[0]&=248,t[31]&=127,t[31]|=64;const n=e.slice(Be,Ds),s=ds(t),i=Fe.multiply(s),a=i.toBytes();return{head:t,prefix:n,scalar:s,point:i,pointBytes:a}},Fs=e=>Bs(_e(e,Be)).then(xo),Sc=e=>xo(Ac(_e(e,Be))),_c=e=>Fs(e).then(t=>t.pointBytes),Tc=e=>Bs(e.hashable).then(e.finish),Cc=(e,t,n)=>{const{pointBytes:s,scalar:i}=e,a=ds(t),o=Fe.multiply(a).toBytes();return{hashable:St(o,s,n),finish:p=>{const d=yo(a+ds(p)*i);return _e(St(o,wo(d)),Ds)}}},Ec=async(e,t)=>{const n=_e(e),s=await Fs(t),i=await Bs(s.prefix,n);return Tc(Cc(s,i,n))},ko={sha512Async:async e=>{const t=bc(),n=St(e);return un(await t.digest("SHA-512",n.buffer))},sha512:void 0},Lc=(e=yc(Be))=>e,Mc={getExtendedPublicKeyAsync:Fs,getExtendedPublicKey:Sc,randomSecretKey:Lc},Jt=8,Ic=256,Ao=Math.ceil(Ic/Jt)+1,us=2**(Jt-1),Rc=()=>{const e=[];let t=Fe,n=t;for(let s=0;s{const n=t.negate();return e?n:t},Pc=e=>{const t=na||(na=Rc());let n=Qe,s=Fe;const i=2**Jt,a=i,o=Zt(i-1),c=Zt(Jt);for(let l=0;l>=c,p>us&&(p-=a,e+=1n);const d=l*us,u=d,h=d+Math.abs(p)-1,v=l%2!==0,w=p<0;p===0?s=s.add(sa(v,t[u])):n=n.add(sa(w,t[h]))}return e!==0n&&H("invalid wnaf"),{p:n,f:s}},jn="clawdbot-device-identity-v1";function ps(e){let t="";for(const n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll("+","-").replaceAll("/","_").replace(/=+$/g,"")}function So(e){const t=e.replaceAll("-","+").replaceAll("_","/"),n=t+"=".repeat((4-t.length%4)%4),s=atob(n),i=new Uint8Array(s.length);for(let a=0;at.toString(16).padStart(2,"0")).join("")}async function _o(e){const t=await crypto.subtle.digest("SHA-256",e);return Nc(new Uint8Array(t))}async function Oc(){const e=Mc.randomSecretKey(),t=await _c(e);return{deviceId:await _o(t),publicKey:ps(t),privateKey:ps(e)}}async function Us(){try{const n=localStorage.getItem(jn);if(n){const s=JSON.parse(n);if(s?.version===1&&typeof s.deviceId=="string"&&typeof s.publicKey=="string"&&typeof s.privateKey=="string"){const i=await _o(So(s.publicKey));if(i!==s.deviceId){const a={...s,deviceId:i};return localStorage.setItem(jn,JSON.stringify(a)),{deviceId:i,publicKey:s.publicKey,privateKey:s.privateKey}}return{deviceId:s.deviceId,publicKey:s.publicKey,privateKey:s.privateKey}}}}catch{}const e=await Oc(),t={version:1,deviceId:e.deviceId,publicKey:e.publicKey,privateKey:e.privateKey,createdAtMs:Date.now()};return localStorage.setItem(jn,JSON.stringify(t)),e}async function Dc(e,t){const n=So(e),s=new TextEncoder().encode(t),i=await Ec(s,n);return ps(i)}const To="clawdbot.device.auth.v1";function Ks(e){return e.trim()}function Bc(e){if(!Array.isArray(e))return[];const t=new Set;for(const n of e){const s=n.trim();s&&t.add(s)}return[...t].sort()}function Hs(){try{const e=window.localStorage.getItem(To);if(!e)return null;const t=JSON.parse(e);return!t||t.version!==1||!t.deviceId||typeof t.deviceId!="string"||!t.tokens||typeof t.tokens!="object"?null:t}catch{return null}}function Co(e){try{window.localStorage.setItem(To,JSON.stringify(e))}catch{}}function Fc(e){const t=Hs();if(!t||t.deviceId!==e.deviceId)return null;const n=Ks(e.role),s=t.tokens[n];return!s||typeof s.token!="string"?null:s}function Eo(e){const t=Ks(e.role),n={version:1,deviceId:e.deviceId,tokens:{}},s=Hs();s&&s.deviceId===e.deviceId&&(n.tokens={...s.tokens});const i={token:e.token,role:t,scopes:Bc(e.scopes),updatedAtMs:Date.now()};return n.tokens[t]=i,Co(n),i}function Lo(e){const t=Hs();if(!t||t.deviceId!==e.deviceId)return;const n=Ks(e.role);if(!t.tokens[n])return;const s={...t,tokens:{...t.tokens}};delete s.tokens[n],Co(s)}async function Te(e,t){if(!(!e.client||!e.connected)&&!e.devicesLoading){e.devicesLoading=!0,t?.quiet||(e.devicesError=null);try{const n=await e.client.request("device.pair.list",{});e.devicesList={pending:Array.isArray(n?.pending)?n.pending:[],paired:Array.isArray(n?.paired)?n.paired:[]}}catch(n){t?.quiet||(e.devicesError=String(n))}finally{e.devicesLoading=!1}}}async function Uc(e,t){if(!(!e.client||!e.connected))try{await e.client.request("device.pair.approve",{requestId:t}),await Te(e)}catch(n){e.devicesError=String(n)}}async function Kc(e,t){if(!(!e.client||!e.connected||!window.confirm("Reject this device pairing request?")))try{await e.client.request("device.pair.reject",{requestId:t}),await Te(e)}catch(s){e.devicesError=String(s)}}async function Hc(e,t){if(!(!e.client||!e.connected))try{const n=await e.client.request("device.token.rotate",t);if(n?.token){const s=await Us(),i=n.role??t.role;(n.deviceId===s.deviceId||t.deviceId===s.deviceId)&&Eo({deviceId:s.deviceId,role:i,token:n.token,scopes:n.scopes??t.scopes??[]}),window.prompt("New device token (copy and store securely):",n.token)}await Te(e)}catch(n){e.devicesError=String(n)}}async function zc(e,t){if(!(!e.client||!e.connected||!window.confirm(`Revoke token for ${t.deviceId} (${t.role})?`)))try{await e.client.request("device.token.revoke",t);const s=await Us();t.deviceId===s.deviceId&&Lo({deviceId:s.deviceId,role:t.role}),await Te(e)}catch(s){e.devicesError=String(s)}}async function pn(e,t){if(!(!e.client||!e.connected)&&!e.nodesLoading){e.nodesLoading=!0,t?.quiet||(e.lastError=null);try{const n=await e.client.request("node.list",{});e.nodes=Array.isArray(n.nodes)?n.nodes:[]}catch(n){t?.quiet||(e.lastError=String(n))}finally{e.nodesLoading=!1}}}function jc(e){if(!e||e.kind==="gateway")return{method:"exec.approvals.get",params:{}};const t=e.nodeId.trim();return t?{method:"exec.approvals.node.get",params:{nodeId:t}}:null}function qc(e,t){if(!e||e.kind==="gateway")return{method:"exec.approvals.set",params:t};const n=e.nodeId.trim();return n?{method:"exec.approvals.node.set",params:{...t,nodeId:n}}:null}async function zs(e,t){if(!(!e.client||!e.connected)&&!e.execApprovalsLoading){e.execApprovalsLoading=!0,e.lastError=null;try{const n=jc(t);if(!n){e.lastError="Select a node before loading exec approvals.";return}const s=await e.client.request(n.method,n.params);Vc(e,s)}catch(n){e.lastError=String(n)}finally{e.execApprovalsLoading=!1}}}function Vc(e,t){e.execApprovalsSnapshot=t,e.execApprovalsDirty||(e.execApprovalsForm=De(t.file??{}))}async function Wc(e,t){if(!(!e.client||!e.connected)){e.execApprovalsSaving=!0,e.lastError=null;try{const n=e.execApprovalsSnapshot?.hash;if(!n){e.lastError="Exec approvals hash missing; reload and retry.";return}const s=e.execApprovalsForm??e.execApprovalsSnapshot?.file??{},i=qc(t,{file:s,baseHash:n});if(!i){e.lastError="Select a node before saving exec approvals.";return}await e.client.request(i.method,i.params),e.execApprovalsDirty=!1,await zs(e,t)}catch(n){e.lastError=String(n)}finally{e.execApprovalsSaving=!1}}}function Gc(e,t,n){const s=De(e.execApprovalsForm??e.execApprovalsSnapshot?.file??{});lo(s,t,n),e.execApprovalsForm=s,e.execApprovalsDirty=!0}function Yc(e,t){const n=De(e.execApprovalsForm??e.execApprovalsSnapshot?.file??{});co(n,t),e.execApprovalsForm=n,e.execApprovalsDirty=!0}async function js(e){if(!(!e.client||!e.connected)&&!e.presenceLoading){e.presenceLoading=!0,e.presenceError=null,e.presenceStatus=null;try{const t=await e.client.request("system-presence",{});Array.isArray(t)?(e.presenceEntries=t,e.presenceStatus=t.length===0?"No instances yet.":null):(e.presenceEntries=[],e.presenceStatus="No presence payload.")}catch(t){e.presenceError=String(t)}finally{e.presenceLoading=!1}}}function tt(e,t,n){if(!t.trim())return;const s={...e.skillMessages};n?s[t]=n:delete s[t],e.skillMessages=s}function fn(e){return e instanceof Error?e.message:String(e)}async function Ct(e,t){if(t?.clearMessages&&Object.keys(e.skillMessages).length>0&&(e.skillMessages={}),!(!e.client||!e.connected)&&!e.skillsLoading){e.skillsLoading=!0,e.skillsError=null;try{const n=await e.client.request("skills.status",{});n&&(e.skillsReport=n)}catch(n){e.skillsError=fn(n)}finally{e.skillsLoading=!1}}}function Qc(e,t,n){e.skillEdits={...e.skillEdits,[t]:n}}async function Zc(e,t,n){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{await e.client.request("skills.update",{skillKey:t,enabled:n}),await Ct(e),tt(e,t,{kind:"success",message:n?"Skill enabled":"Skill disabled"})}catch(s){const i=fn(s);e.skillsError=i,tt(e,t,{kind:"error",message:i})}finally{e.skillsBusyKey=null}}}async function Jc(e,t){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{const n=e.skillEdits[t]??"";await e.client.request("skills.update",{skillKey:t,apiKey:n}),await Ct(e),tt(e,t,{kind:"success",message:"API key saved"})}catch(n){const s=fn(n);e.skillsError=s,tt(e,t,{kind:"error",message:s})}finally{e.skillsBusyKey=null}}}async function Xc(e,t,n,s){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{const i=await e.client.request("skills.install",{name:n,installId:s,timeoutMs:12e4});await Ct(e),tt(e,t,{kind:"success",message:i?.message??"Installed"})}catch(i){const a=fn(i);e.skillsError=a,tt(e,t,{kind:"error",message:a})}finally{e.skillsBusyKey=null}}}function ed(){return typeof window>"u"||typeof window.matchMedia!="function"||window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function qs(e){return e==="system"?ed():e}const Ft=e=>Number.isNaN(e)?.5:e<=0?0:e>=1?1:e,td=()=>typeof window>"u"||typeof window.matchMedia!="function"?!1:window.matchMedia("(prefers-reduced-motion: reduce)").matches??!1,Ut=e=>{e.classList.remove("theme-transition"),e.style.removeProperty("--theme-switch-x"),e.style.removeProperty("--theme-switch-y")},nd=({nextTheme:e,applyTheme:t,context:n,currentTheme:s})=>{if(s===e)return;const i=globalThis.document??null;if(!i){t();return}const a=i.documentElement,o=i,c=td();if(!!o.startViewTransition&&!c){let p=.5,d=.5;if(n?.pointerClientX!==void 0&&n?.pointerClientY!==void 0&&typeof window<"u")p=Ft(n.pointerClientX/window.innerWidth),d=Ft(n.pointerClientY/window.innerHeight);else if(n?.element){const u=n.element.getBoundingClientRect();u.width>0&&u.height>0&&typeof window<"u"&&(p=Ft((u.left+u.width/2)/window.innerWidth),d=Ft((u.top+u.height/2)/window.innerHeight))}a.style.setProperty("--theme-switch-x",`${p*100}%`),a.style.setProperty("--theme-switch-y",`${d*100}%`),a.classList.add("theme-transition");try{const u=o.startViewTransition?.(()=>{t()});u?.finished?u.finished.finally(()=>Ut(a)):Ut(a)}catch{Ut(a),t()}return}t(),Ut(a)};function sd(e){e.nodesPollInterval==null&&(e.nodesPollInterval=window.setInterval(()=>{pn(e,{quiet:!0})},5e3))}function id(e){e.nodesPollInterval!=null&&(clearInterval(e.nodesPollInterval),e.nodesPollInterval=null)}function Vs(e){e.logsPollInterval==null&&(e.logsPollInterval=window.setInterval(()=>{e.tab==="logs"&&Os(e,{quiet:!0})},2e3))}function Ws(e){e.logsPollInterval!=null&&(clearInterval(e.logsPollInterval),e.logsPollInterval=null)}function Gs(e){e.debugPollInterval==null&&(e.debugPollInterval=window.setInterval(()=>{e.tab==="debug"&&dn(e)},3e3))}function Ys(e){e.debugPollInterval!=null&&(clearInterval(e.debugPollInterval),e.debugPollInterval=null)}function ke(e,t){const n={...t,lastActiveSessionKey:t.lastActiveSessionKey?.trim()||t.sessionKey.trim()||"main"};e.settings=n,fl(n),t.theme!==e.theme&&(e.theme=t.theme,hn(e,qs(t.theme))),e.applySessionKey=e.settings.lastActiveSessionKey}function Mo(e,t){const n=t.trim();n&&e.settings.lastActiveSessionKey!==n&&ke(e,{...e.settings,lastActiveSessionKey:n})}function ad(e){if(!window.location.search)return;const t=new URLSearchParams(window.location.search),n=t.get("token"),s=t.get("password"),i=t.get("session"),a=t.get("gatewayUrl");let o=!1;if(n!=null){const l=n.trim();l&&l!==e.settings.token&&ke(e,{...e.settings,token:l}),t.delete("token"),o=!0}if(s!=null){const l=s.trim();l&&(e.password=l),t.delete("password"),o=!0}if(i!=null){const l=i.trim();l&&(e.sessionKey=l,ke(e,{...e.settings,sessionKey:l,lastActiveSessionKey:l}))}if(a!=null){const l=a.trim();l&&l!==e.settings.gatewayUrl&&ke(e,{...e.settings,gatewayUrl:l}),t.delete("gatewayUrl"),o=!0}if(!o)return;const c=new URL(window.location.href);c.search=t.toString(),window.history.replaceState({},"",c.toString())}function od(e,t){e.tab!==t&&(e.tab=t),t==="chat"&&(e.chatHasAutoScrolled=!1),t==="logs"?Vs(e):Ws(e),t==="debug"?Gs(e):Ys(e),Qs(e),Ro(e,t,!1)}function rd(e,t,n){nd({nextTheme:t,applyTheme:()=>{e.theme=t,ke(e,{...e.settings,theme:t}),hn(e,qs(t))},context:n,currentTheme:e.theme})}async function Qs(e){e.tab==="overview"&&await Po(e),e.tab==="channels"&&await gd(e),e.tab==="instances"&&await js(e),e.tab==="sessions"&&await st(e),e.tab==="cron"&&await Zs(e),e.tab==="skills"&&await Ct(e),e.tab==="nodes"&&(await pn(e),await Te(e),await be(e),await zs(e)),e.tab==="chat"&&(await wd(e),ln(e,!e.chatHasAutoScrolled)),e.tab==="config"&&(await uo(e),await be(e)),e.tab==="debug"&&(await dn(e),e.eventLog=e.eventLogBuffer),e.tab==="logs"&&(e.logsAtBottom=!0,await Os(e,{reset:!0}),ro(e,!0))}function ld(){if(typeof window>"u")return"";const e=window.__CLAWDBOT_CONTROL_UI_BASE_PATH__;return typeof e=="string"&&e.trim()?rn(e):gl(window.location.pathname)}function cd(e){e.theme=e.settings.theme??"system",hn(e,qs(e.theme))}function hn(e,t){if(e.themeResolved=t,typeof document>"u")return;const n=document.documentElement;n.dataset.theme=t,n.style.colorScheme=t}function dd(e){if(typeof window>"u"||typeof window.matchMedia!="function")return;if(e.themeMedia=window.matchMedia("(prefers-color-scheme: dark)"),e.themeMediaHandler=n=>{e.theme==="system"&&hn(e,n.matches?"dark":"light")},typeof e.themeMedia.addEventListener=="function"){e.themeMedia.addEventListener("change",e.themeMediaHandler);return}e.themeMedia.addListener(e.themeMediaHandler)}function ud(e){if(!e.themeMedia||!e.themeMediaHandler)return;if(typeof e.themeMedia.removeEventListener=="function"){e.themeMedia.removeEventListener("change",e.themeMediaHandler);return}e.themeMedia.removeListener(e.themeMediaHandler),e.themeMedia=null,e.themeMediaHandler=null}function pd(e,t){if(typeof window>"u")return;const n=so(window.location.pathname,e.basePath)??"chat";Io(e,n),Ro(e,n,t)}function fd(e){if(typeof window>"u")return;const t=so(window.location.pathname,e.basePath);if(!t)return;const s=new URL(window.location.href).searchParams.get("session")?.trim();s&&(e.sessionKey=s,ke(e,{...e.settings,sessionKey:s,lastActiveSessionKey:s})),Io(e,t)}function Io(e,t){e.tab!==t&&(e.tab=t),t==="chat"&&(e.chatHasAutoScrolled=!1),t==="logs"?Vs(e):Ws(e),t==="debug"?Gs(e):Ys(e),e.connected&&Qs(e)}function Ro(e,t,n){if(typeof window>"u")return;const s=kt(Rs(t,e.basePath)),i=kt(window.location.pathname),a=new URL(window.location.href);t==="chat"&&e.sessionKey?a.searchParams.set("session",e.sessionKey):a.searchParams.delete("session"),i!==s&&(a.pathname=s),n?window.history.replaceState({},"",a.toString()):window.history.pushState({},"",a.toString())}function hd(e,t,n){if(typeof window>"u")return;const s=new URL(window.location.href);s.searchParams.set("session",t),window.history.replaceState({},"",s.toString())}async function Po(e){await Promise.all([oe(e,!1),js(e),st(e),Tt(e),dn(e)])}async function gd(e){await Promise.all([oe(e,!0),uo(e),be(e)])}async function Zs(e){await Promise.all([oe(e,!1),Tt(e),cn(e)])}function No(e){return e.chatSending||!!e.chatRunId}function vd(e){const t=e.trim();if(!t)return!1;const n=t.toLowerCase();return n==="/stop"?!0:n==="stop"||n==="esc"||n==="abort"||n==="wait"||n==="exit"}async function Oo(e){e.connected&&(e.chatMessage="",await El(e))}function md(e,t){const n=t.trim();n&&(e.chatQueue=[...e.chatQueue,{id:Ps(),text:n,createdAt:Date.now()}])}async function Do(e,t,n){Ns(e);const s=await Cl(e,t);return!s&&n?.previousDraft!=null&&(e.chatMessage=n.previousDraft),s&&Mo(e,e.sessionKey),s&&n?.restoreDraft&&n.previousDraft?.trim()&&(e.chatMessage=n.previousDraft),ln(e),s&&!e.chatRunId&&Bo(e),s}async function Bo(e){if(!e.connected||No(e))return;const[t,...n]=e.chatQueue;if(!t)return;e.chatQueue=n,await Do(e,t.text)||(e.chatQueue=[t,...e.chatQueue])}function bd(e,t){e.chatQueue=e.chatQueue.filter(n=>n.id!==t)}async function yd(e,t,n){if(!e.connected)return;const s=e.chatMessage,i=(t??e.chatMessage).trim();if(i){if(vd(i)){await Oo(e);return}if(t==null&&(e.chatMessage=""),No(e)){md(e,i);return}await Do(e,i,{previousDraft:t==null?s:void 0,restoreDraft:!!(t&&n?.restoreDraft)})}}async function wd(e){await Promise.all([Xe(e),st(e),fs(e)]),ln(e,!0)}const $d=Bo;function xd(e){const t=eo(e.sessionKey);return t?.agentId?t.agentId:e.hello?.snapshot?.sessionDefaults?.defaultAgentId?.trim()||"main"}function kd(e,t){const n=rn(e),s=encodeURIComponent(t);return n?`${n}/avatar/${s}?meta=1`:`/avatar/${s}?meta=1`}async function fs(e){if(!e.connected){e.chatAvatarUrl=null;return}const t=xd(e);if(!t){e.chatAvatarUrl=null;return}e.chatAvatarUrl=null;const n=kd(e.basePath,t);try{const s=await fetch(n,{method:"GET"});if(!s.ok){e.chatAvatarUrl=null;return}const i=await s.json(),a=typeof i.avatarUrl=="string"?i.avatarUrl.trim():"";e.chatAvatarUrl=a||null}catch{e.chatAvatarUrl=null}}const Fo={CHILD:2},Uo=e=>(...t)=>({_$litDirective$:e,values:t});let Ko=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,n,s){this._$Ct=t,this._$AM=n,this._$Ci=s}_$AS(t,n){return this.update(t,n)}update(t,n){return this.render(...n)}};const{I:Ad}=nl,ia=e=>e,aa=()=>document.createComment(""),lt=(e,t,n)=>{const s=e._$AA.parentNode,i=t===void 0?e._$AB:t._$AA;if(n===void 0){const a=s.insertBefore(aa(),i),o=s.insertBefore(aa(),i);n=new Ad(a,o,e,e.options)}else{const a=n._$AB.nextSibling,o=n._$AM,c=o!==e;if(c){let l;n._$AQ?.(e),n._$AM=e,n._$AP!==void 0&&(l=e._$AU)!==o._$AU&&n._$AP(l)}if(a!==i||c){let l=n._$AA;for(;l!==a;){const p=ia(l).nextSibling;ia(s).insertBefore(l,i),l=p}}}return n},Me=(e,t,n=e)=>(e._$AI(t,n),e),Sd={},_d=(e,t=Sd)=>e._$AH=t,Td=e=>e._$AH,qn=e=>{e._$AR(),e._$AA.remove()};const oa=(e,t,n)=>{const s=new Map;for(let i=t;i<=n;i++)s.set(e[i],i);return s},Ho=Uo(class extends Ko{constructor(e){if(super(e),e.type!==Fo.CHILD)throw Error("repeat() can only be used in text expressions")}dt(e,t,n){let s;n===void 0?n=t:t!==void 0&&(s=t);const i=[],a=[];let o=0;for(const c of e)i[o]=s?s(c,o):o,a[o]=n(c,o),o++;return{values:a,keys:i}}render(e,t,n){return this.dt(e,t,n).values}update(e,[t,n,s]){const i=Td(e),{values:a,keys:o}=this.dt(t,n,s);if(!Array.isArray(i))return this.ut=o,a;const c=this.ut??=[],l=[];let p,d,u=0,h=i.length-1,v=0,w=a.length-1;for(;u<=h&&v<=w;)if(i[u]===null)u++;else if(i[h]===null)h--;else if(c[u]===o[v])l[v]=Me(i[u],a[v]),u++,v++;else if(c[h]===o[w])l[w]=Me(i[h],a[w]),h--,w--;else if(c[u]===o[w])l[w]=Me(i[u],a[w]),lt(e,l[w+1],i[u]),u++,w--;else if(c[h]===o[v])l[v]=Me(i[h],a[v]),lt(e,i[u],i[h]),h--,v++;else if(p===void 0&&(p=oa(o,v,w),d=oa(c,u,h)),p.has(c[u]))if(p.has(c[h])){const $=d.get(o[v]),k=$!==void 0?i[$]:null;if(k===null){const T=lt(e,i[u]);Me(T,a[v]),l[v]=T}else l[v]=Me(k,a[v]),lt(e,i[u],k),i[$]=null;v++}else qn(i[h]),h--;else qn(i[u]),u++;for(;v<=w;){const $=lt(e,l[w+1]);Me($,a[v]),l[v++]=$}for(;u<=h;){const $=i[u++];$!==null&&qn($)}return this.ut=o,_d(e,l),Se}});function zo(e){const t=e;let n=typeof t.role=="string"?t.role:"unknown";const s=typeof t.toolCallId=="string"||typeof t.tool_call_id=="string",i=t.content,a=Array.isArray(i)?i:null,o=Array.isArray(a)&&a.some(u=>{const v=String(u.type??"").toLowerCase();return v==="toolresult"||v==="tool_result"}),c=typeof t.toolName=="string"||typeof t.tool_name=="string";(s||o||c)&&(n="toolResult");let l=[];typeof t.content=="string"?l=[{type:"text",text:t.content}]:Array.isArray(t.content)?l=t.content.map(u=>({type:u.type||"text",text:u.text,name:u.name,args:u.args||u.arguments})):typeof t.text=="string"&&(l=[{type:"text",text:t.text}]);const p=typeof t.timestamp=="number"?t.timestamp:Date.now(),d=typeof t.id=="string"?t.id:void 0;return{role:n,content:l,timestamp:p,id:d}}function Js(e){const t=e.toLowerCase();return e==="user"||e==="User"?e:e==="assistant"?"assistant":e==="system"?"system":t==="toolresult"||t==="tool_result"||t==="tool"||t==="function"?"tool":e}function jo(e){const t=e,n=typeof t.role=="string"?t.role.toLowerCase():"";return n==="toolresult"||n==="tool_result"}class hs extends Ko{constructor(t){if(super(t),this.it=g,t.type!==Fo.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(t){if(t===g||t==null)return this._t=void 0,this.it=t;if(t===Se)return t;if(typeof t!="string")throw Error(this.constructor.directiveName+"() called with a non-string value");if(t===this.it)return this._t;this.it=t;const n=[t];return n.raw=n,this._t={_$litType$:this.constructor.resultType,strings:n,values:[]}}}hs.directiveName="unsafeHTML",hs.resultType=1;const gs=Uo(hs);const{entries:qo,setPrototypeOf:ra,isFrozen:Cd,getPrototypeOf:Ed,getOwnPropertyDescriptor:Ld}=Object;let{freeze:Z,seal:ne,create:vs}=Object,{apply:ms,construct:bs}=typeof Reflect<"u"&&Reflect;Z||(Z=function(t){return t});ne||(ne=function(t){return t});ms||(ms=function(t,n){for(var s=arguments.length,i=new Array(s>2?s-2:0),a=2;a1?n-1:0),i=1;i1?n-1:0),i=1;i2&&arguments[2]!==void 0?arguments[2]:Wt;ra&&ra(e,null);let s=t.length;for(;s--;){let i=t[s];if(typeof i=="string"){const a=n(i);a!==i&&(Cd(t)||(t[s]=a),i=a)}e[i]=!0}return e}function Od(e){for(let t=0;t/gm),Kd=ne(/\$\{[\w\W]*/gm),Hd=ne(/^data-[\-\w.\u00B7-\uFFFF]+$/),zd=ne(/^aria-[\-\w]+$/),Vo=ne(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),jd=ne(/^(?:\w+script|data):/i),qd=ne(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),Wo=ne(/^html$/i),Vd=ne(/^[a-z][.\w]*(-[.\w]+)+$/i);var fa=Object.freeze({__proto__:null,ARIA_ATTR:zd,ATTR_WHITESPACE:qd,CUSTOM_ELEMENT:Vd,DATA_ATTR:Hd,DOCTYPE_NAME:Wo,ERB_EXPR:Ud,IS_ALLOWED_URI:Vo,IS_SCRIPT_OR_DATA:jd,MUSTACHE_EXPR:Fd,TMPLIT_EXPR:Kd});const ft={element:1,text:3,progressingInstruction:7,comment:8,document:9},Wd=function(){return typeof window>"u"?null:window},Gd=function(t,n){if(typeof t!="object"||typeof t.createPolicy!="function")return null;let s=null;const i="data-tt-policy-suffix";n&&n.hasAttribute(i)&&(s=n.getAttribute(i));const a="dompurify"+(s?"#"+s:"");try{return t.createPolicy(a,{createHTML(o){return o},createScriptURL(o){return o}})}catch{return console.warn("TrustedTypes policy "+a+" could not be created."),null}},ha=function(){return{afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}};function Go(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:Wd();const t=_=>Go(_);if(t.version="3.3.1",t.removed=[],!e||!e.document||e.document.nodeType!==ft.document||!e.Element)return t.isSupported=!1,t;let{document:n}=e;const s=n,i=s.currentScript,{DocumentFragment:a,HTMLTemplateElement:o,Node:c,Element:l,NodeFilter:p,NamedNodeMap:d=e.NamedNodeMap||e.MozNamedAttrMap,HTMLFormElement:u,DOMParser:h,trustedTypes:v}=e,w=l.prototype,$=pt(w,"cloneNode"),k=pt(w,"remove"),T=pt(w,"nextSibling"),M=pt(w,"childNodes"),P=pt(w,"parentNode");if(typeof o=="function"){const _=n.createElement("template");_.content&&_.content.ownerDocument&&(n=_.content.ownerDocument)}let L,C="";const{implementation:E,createNodeIterator:pe,createDocumentFragment:yn,getElementsByTagName:wn}=n,{importNode:kr}=s;let W=ha();t.isSupported=typeof qo=="function"&&typeof P=="function"&&E&&E.createHTMLDocument!==void 0;const{MUSTACHE_EXPR:$n,ERB_EXPR:xn,TMPLIT_EXPR:kn,DATA_ATTR:Ar,ARIA_ATTR:Sr,IS_SCRIPT_OR_DATA:_r,ATTR_WHITESPACE:di,CUSTOM_ELEMENT:Tr}=fa;let{IS_ALLOWED_URI:ui}=fa,K=null;const pi=I({},[...ca,...Gn,...Yn,...Qn,...da]);let z=null;const fi=I({},[...ua,...Zn,...pa,...Ht]);let B=Object.seal(vs(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),it=null,An=null;const He=Object.seal(vs(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let hi=!0,Sn=!0,gi=!1,vi=!0,ze=!1,Lt=!0,Ce=!1,_n=!1,Tn=!1,je=!1,Mt=!1,It=!1,mi=!0,bi=!1;const Cr="user-content-";let Cn=!0,at=!1,qe={},re=null;const En=I({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let yi=null;const wi=I({},["audio","video","img","source","image","track"]);let Ln=null;const $i=I({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Rt="http://www.w3.org/1998/Math/MathML",Pt="http://www.w3.org/2000/svg",fe="http://www.w3.org/1999/xhtml";let Ve=fe,Mn=!1,In=null;const Er=I({},[Rt,Pt,fe],Vn);let Nt=I({},["mi","mo","mn","ms","mtext"]),Ot=I({},["annotation-xml"]);const Lr=I({},["title","style","font","a","script"]);let ot=null;const Mr=["application/xhtml+xml","text/html"],Ir="text/html";let U=null,We=null;const Rr=n.createElement("form"),xi=function(f){return f instanceof RegExp||f instanceof Function},Rn=function(){let f=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(!(We&&We===f)){if((!f||typeof f!="object")&&(f={}),f=de(f),ot=Mr.indexOf(f.PARSER_MEDIA_TYPE)===-1?Ir:f.PARSER_MEDIA_TYPE,U=ot==="application/xhtml+xml"?Vn:Wt,K=se(f,"ALLOWED_TAGS")?I({},f.ALLOWED_TAGS,U):pi,z=se(f,"ALLOWED_ATTR")?I({},f.ALLOWED_ATTR,U):fi,In=se(f,"ALLOWED_NAMESPACES")?I({},f.ALLOWED_NAMESPACES,Vn):Er,Ln=se(f,"ADD_URI_SAFE_ATTR")?I(de($i),f.ADD_URI_SAFE_ATTR,U):$i,yi=se(f,"ADD_DATA_URI_TAGS")?I(de(wi),f.ADD_DATA_URI_TAGS,U):wi,re=se(f,"FORBID_CONTENTS")?I({},f.FORBID_CONTENTS,U):En,it=se(f,"FORBID_TAGS")?I({},f.FORBID_TAGS,U):de({}),An=se(f,"FORBID_ATTR")?I({},f.FORBID_ATTR,U):de({}),qe=se(f,"USE_PROFILES")?f.USE_PROFILES:!1,hi=f.ALLOW_ARIA_ATTR!==!1,Sn=f.ALLOW_DATA_ATTR!==!1,gi=f.ALLOW_UNKNOWN_PROTOCOLS||!1,vi=f.ALLOW_SELF_CLOSE_IN_ATTR!==!1,ze=f.SAFE_FOR_TEMPLATES||!1,Lt=f.SAFE_FOR_XML!==!1,Ce=f.WHOLE_DOCUMENT||!1,je=f.RETURN_DOM||!1,Mt=f.RETURN_DOM_FRAGMENT||!1,It=f.RETURN_TRUSTED_TYPE||!1,Tn=f.FORCE_BODY||!1,mi=f.SANITIZE_DOM!==!1,bi=f.SANITIZE_NAMED_PROPS||!1,Cn=f.KEEP_CONTENT!==!1,at=f.IN_PLACE||!1,ui=f.ALLOWED_URI_REGEXP||Vo,Ve=f.NAMESPACE||fe,Nt=f.MATHML_TEXT_INTEGRATION_POINTS||Nt,Ot=f.HTML_INTEGRATION_POINTS||Ot,B=f.CUSTOM_ELEMENT_HANDLING||{},f.CUSTOM_ELEMENT_HANDLING&&xi(f.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(B.tagNameCheck=f.CUSTOM_ELEMENT_HANDLING.tagNameCheck),f.CUSTOM_ELEMENT_HANDLING&&xi(f.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(B.attributeNameCheck=f.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),f.CUSTOM_ELEMENT_HANDLING&&typeof f.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(B.allowCustomizedBuiltInElements=f.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),ze&&(Sn=!1),Mt&&(je=!0),qe&&(K=I({},da),z=[],qe.html===!0&&(I(K,ca),I(z,ua)),qe.svg===!0&&(I(K,Gn),I(z,Zn),I(z,Ht)),qe.svgFilters===!0&&(I(K,Yn),I(z,Zn),I(z,Ht)),qe.mathMl===!0&&(I(K,Qn),I(z,pa),I(z,Ht))),f.ADD_TAGS&&(typeof f.ADD_TAGS=="function"?He.tagCheck=f.ADD_TAGS:(K===pi&&(K=de(K)),I(K,f.ADD_TAGS,U))),f.ADD_ATTR&&(typeof f.ADD_ATTR=="function"?He.attributeCheck=f.ADD_ATTR:(z===fi&&(z=de(z)),I(z,f.ADD_ATTR,U))),f.ADD_URI_SAFE_ATTR&&I(Ln,f.ADD_URI_SAFE_ATTR,U),f.FORBID_CONTENTS&&(re===En&&(re=de(re)),I(re,f.FORBID_CONTENTS,U)),f.ADD_FORBID_CONTENTS&&(re===En&&(re=de(re)),I(re,f.ADD_FORBID_CONTENTS,U)),Cn&&(K["#text"]=!0),Ce&&I(K,["html","head","body"]),K.table&&(I(K,["tbody"]),delete it.tbody),f.TRUSTED_TYPES_POLICY){if(typeof f.TRUSTED_TYPES_POLICY.createHTML!="function")throw ut('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if(typeof f.TRUSTED_TYPES_POLICY.createScriptURL!="function")throw ut('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');L=f.TRUSTED_TYPES_POLICY,C=L.createHTML("")}else L===void 0&&(L=Gd(v,i)),L!==null&&typeof C=="string"&&(C=L.createHTML(""));Z&&Z(f),We=f}},ki=I({},[...Gn,...Yn,...Dd]),Ai=I({},[...Qn,...Bd]),Pr=function(f){let x=P(f);(!x||!x.tagName)&&(x={namespaceURI:Ve,tagName:"template"});const S=Wt(f.tagName),D=Wt(x.tagName);return In[f.namespaceURI]?f.namespaceURI===Pt?x.namespaceURI===fe?S==="svg":x.namespaceURI===Rt?S==="svg"&&(D==="annotation-xml"||Nt[D]):!!ki[S]:f.namespaceURI===Rt?x.namespaceURI===fe?S==="math":x.namespaceURI===Pt?S==="math"&&Ot[D]:!!Ai[S]:f.namespaceURI===fe?x.namespaceURI===Pt&&!Ot[D]||x.namespaceURI===Rt&&!Nt[D]?!1:!Ai[S]&&(Lr[S]||!ki[S]):!!(ot==="application/xhtml+xml"&&In[f.namespaceURI]):!1},le=function(f){ct(t.removed,{element:f});try{P(f).removeChild(f)}catch{k(f)}},Ee=function(f,x){try{ct(t.removed,{attribute:x.getAttributeNode(f),from:x})}catch{ct(t.removed,{attribute:null,from:x})}if(x.removeAttribute(f),f==="is")if(je||Mt)try{le(x)}catch{}else try{x.setAttribute(f,"")}catch{}},Si=function(f){let x=null,S=null;if(Tn)f=""+f;else{const F=Wn(f,/^[\r\n\t ]+/);S=F&&F[0]}ot==="application/xhtml+xml"&&Ve===fe&&(f=''+f+"");const D=L?L.createHTML(f):f;if(Ve===fe)try{x=new h().parseFromString(D,ot)}catch{}if(!x||!x.documentElement){x=E.createDocument(Ve,"template",null);try{x.documentElement.innerHTML=Mn?C:D}catch{}}const q=x.body||x.documentElement;return f&&S&&q.insertBefore(n.createTextNode(S),q.childNodes[0]||null),Ve===fe?wn.call(x,Ce?"html":"body")[0]:Ce?x.documentElement:q},_i=function(f){return pe.call(f.ownerDocument||f,f,p.SHOW_ELEMENT|p.SHOW_COMMENT|p.SHOW_TEXT|p.SHOW_PROCESSING_INSTRUCTION|p.SHOW_CDATA_SECTION,null)},Pn=function(f){return f instanceof u&&(typeof f.nodeName!="string"||typeof f.textContent!="string"||typeof f.removeChild!="function"||!(f.attributes instanceof d)||typeof f.removeAttribute!="function"||typeof f.setAttribute!="function"||typeof f.namespaceURI!="string"||typeof f.insertBefore!="function"||typeof f.hasChildNodes!="function")},Ti=function(f){return typeof c=="function"&&f instanceof c};function he(_,f,x){Kt(_,S=>{S.call(t,f,x,We)})}const Ci=function(f){let x=null;if(he(W.beforeSanitizeElements,f,null),Pn(f))return le(f),!0;const S=U(f.nodeName);if(he(W.uponSanitizeElement,f,{tagName:S,allowedTags:K}),Lt&&f.hasChildNodes()&&!Ti(f.firstElementChild)&&G(/<[/\w!]/g,f.innerHTML)&&G(/<[/\w!]/g,f.textContent)||f.nodeType===ft.progressingInstruction||Lt&&f.nodeType===ft.comment&&G(/<[/\w]/g,f.data))return le(f),!0;if(!(He.tagCheck instanceof Function&&He.tagCheck(S))&&(!K[S]||it[S])){if(!it[S]&&Li(S)&&(B.tagNameCheck instanceof RegExp&&G(B.tagNameCheck,S)||B.tagNameCheck instanceof Function&&B.tagNameCheck(S)))return!1;if(Cn&&!re[S]){const D=P(f)||f.parentNode,q=M(f)||f.childNodes;if(q&&D){const F=q.length;for(let X=F-1;X>=0;--X){const ge=$(q[X],!0);ge.__removalCount=(f.__removalCount||0)+1,D.insertBefore(ge,T(f))}}}return le(f),!0}return f instanceof l&&!Pr(f)||(S==="noscript"||S==="noembed"||S==="noframes")&&G(/<\/no(script|embed|frames)/i,f.innerHTML)?(le(f),!0):(ze&&f.nodeType===ft.text&&(x=f.textContent,Kt([$n,xn,kn],D=>{x=dt(x,D," ")}),f.textContent!==x&&(ct(t.removed,{element:f.cloneNode()}),f.textContent=x)),he(W.afterSanitizeElements,f,null),!1)},Ei=function(f,x,S){if(mi&&(x==="id"||x==="name")&&(S in n||S in Rr))return!1;if(!(Sn&&!An[x]&&G(Ar,x))){if(!(hi&&G(Sr,x))){if(!(He.attributeCheck instanceof Function&&He.attributeCheck(x,f))){if(!z[x]||An[x]){if(!(Li(f)&&(B.tagNameCheck instanceof RegExp&&G(B.tagNameCheck,f)||B.tagNameCheck instanceof Function&&B.tagNameCheck(f))&&(B.attributeNameCheck instanceof RegExp&&G(B.attributeNameCheck,x)||B.attributeNameCheck instanceof Function&&B.attributeNameCheck(x,f))||x==="is"&&B.allowCustomizedBuiltInElements&&(B.tagNameCheck instanceof RegExp&&G(B.tagNameCheck,S)||B.tagNameCheck instanceof Function&&B.tagNameCheck(S))))return!1}else if(!Ln[x]){if(!G(ui,dt(S,di,""))){if(!((x==="src"||x==="xlink:href"||x==="href")&&f!=="script"&&Rd(S,"data:")===0&&yi[f])){if(!(gi&&!G(_r,dt(S,di,"")))){if(S)return!1}}}}}}}return!0},Li=function(f){return f!=="annotation-xml"&&Wn(f,Tr)},Mi=function(f){he(W.beforeSanitizeAttributes,f,null);const{attributes:x}=f;if(!x||Pn(f))return;const S={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:z,forceKeepAttr:void 0};let D=x.length;for(;D--;){const q=x[D],{name:F,namespaceURI:X,value:ge}=q,Ge=U(F),Nn=ge;let j=F==="value"?Nn:Pd(Nn);if(S.attrName=Ge,S.attrValue=j,S.keepAttr=!0,S.forceKeepAttr=void 0,he(W.uponSanitizeAttribute,f,S),j=S.attrValue,bi&&(Ge==="id"||Ge==="name")&&(Ee(F,f),j=Cr+j),Lt&&G(/((--!?|])>)|<\/(style|title|textarea)/i,j)){Ee(F,f);continue}if(Ge==="attributename"&&Wn(j,"href")){Ee(F,f);continue}if(S.forceKeepAttr)continue;if(!S.keepAttr){Ee(F,f);continue}if(!vi&&G(/\/>/i,j)){Ee(F,f);continue}ze&&Kt([$n,xn,kn],Ri=>{j=dt(j,Ri," ")});const Ii=U(f.nodeName);if(!Ei(Ii,Ge,j)){Ee(F,f);continue}if(L&&typeof v=="object"&&typeof v.getAttributeType=="function"&&!X)switch(v.getAttributeType(Ii,Ge)){case"TrustedHTML":{j=L.createHTML(j);break}case"TrustedScriptURL":{j=L.createScriptURL(j);break}}if(j!==Nn)try{X?f.setAttributeNS(X,F,j):f.setAttribute(F,j),Pn(f)?le(f):la(t.removed)}catch{Ee(F,f)}}he(W.afterSanitizeAttributes,f,null)},Nr=function _(f){let x=null;const S=_i(f);for(he(W.beforeSanitizeShadowDOM,f,null);x=S.nextNode();)he(W.uponSanitizeShadowNode,x,null),Ci(x),Mi(x),x.content instanceof a&&_(x.content);he(W.afterSanitizeShadowDOM,f,null)};return t.sanitize=function(_){let f=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},x=null,S=null,D=null,q=null;if(Mn=!_,Mn&&(_=""),typeof _!="string"&&!Ti(_))if(typeof _.toString=="function"){if(_=_.toString(),typeof _!="string")throw ut("dirty is not a string, aborting")}else throw ut("toString is not a function");if(!t.isSupported)return _;if(_n||Rn(f),t.removed=[],typeof _=="string"&&(at=!1),at){if(_.nodeName){const ge=U(_.nodeName);if(!K[ge]||it[ge])throw ut("root node is forbidden and cannot be sanitized in-place")}}else if(_ instanceof c)x=Si(""),S=x.ownerDocument.importNode(_,!0),S.nodeType===ft.element&&S.nodeName==="BODY"||S.nodeName==="HTML"?x=S:x.appendChild(S);else{if(!je&&!ze&&!Ce&&_.indexOf("<")===-1)return L&&It?L.createHTML(_):_;if(x=Si(_),!x)return je?null:It?C:""}x&&Tn&&le(x.firstChild);const F=_i(at?_:x);for(;D=F.nextNode();)Ci(D),Mi(D),D.content instanceof a&&Nr(D.content);if(at)return _;if(je){if(Mt)for(q=yn.call(x.ownerDocument);x.firstChild;)q.appendChild(x.firstChild);else q=x;return(z.shadowroot||z.shadowrootmode)&&(q=kr.call(s,q,!0)),q}let X=Ce?x.outerHTML:x.innerHTML;return Ce&&K["!doctype"]&&x.ownerDocument&&x.ownerDocument.doctype&&x.ownerDocument.doctype.name&&G(Wo,x.ownerDocument.doctype.name)&&(X=" +`+X),ze&&Kt([$n,xn,kn],ge=>{X=dt(X,ge," ")}),L&&It?L.createHTML(X):X},t.setConfig=function(){let _=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};Rn(_),_n=!0},t.clearConfig=function(){We=null,_n=!1},t.isValidAttribute=function(_,f,x){We||Rn({});const S=U(_),D=U(f);return Ei(S,D,x)},t.addHook=function(_,f){typeof f=="function"&&ct(W[_],f)},t.removeHook=function(_,f){if(f!==void 0){const x=Md(W[_],f);return x===-1?void 0:Id(W[_],x,1)[0]}return la(W[_])},t.removeHooks=function(_){W[_]=[]},t.removeAllHooks=function(){W=ha()},t}var ys=Go();function Xs(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var Ke=Xs();function Yo(e){Ke=e}var yt={exec:()=>null};function R(e,t=""){let n=typeof e=="string"?e:e.source,s={replace:(i,a)=>{let o=typeof a=="string"?a:a.source;return o=o.replace(Y.caret,"$1"),n=n.replace(i,o),s},getRegex:()=>new RegExp(n,t)};return s}var Yd=(()=>{try{return!!new RegExp("(?<=1)(?/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] +\S/,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>new RegExp(`^( {0,3}${e})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}#`),htmlBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}<(?:[a-z].*>|!--)`,"i")},Qd=/^(?:[ \t]*(?:\n|$))+/,Zd=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,Jd=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,Et=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,Xd=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,ei=/(?:[*+-]|\d{1,9}[.)])/,Qo=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,Zo=R(Qo).replace(/bull/g,ei).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),eu=R(Qo).replace(/bull/g,ei).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),ti=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,tu=/^[^\n]+/,ni=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,nu=R(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",ni).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),su=R(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,ei).getRegex(),gn="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",si=/|$))/,iu=R("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",si).replace("tag",gn).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Jo=R(ti).replace("hr",Et).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",gn).getRegex(),au=R(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",Jo).getRegex(),ii={blockquote:au,code:Zd,def:nu,fences:Jd,heading:Xd,hr:Et,html:iu,lheading:Zo,list:su,newline:Qd,paragraph:Jo,table:yt,text:tu},ga=R("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",Et).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",gn).getRegex(),ou={...ii,lheading:eu,table:ga,paragraph:R(ti).replace("hr",Et).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",ga).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",gn).getRegex()},ru={...ii,html:R(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",si).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:yt,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:R(ti).replace("hr",Et).replace("heading",` *#{1,6} *[^ +]`).replace("lheading",Zo).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},lu=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,cu=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,Xo=/^( {2,}|\\)\n(?!\s*$)/,du=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",Yd?"(?`+)[^`]+\k(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),nr=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,gu=R(nr,"u").replace(/punct/g,vn).getRegex(),vu=R(nr,"u").replace(/punct/g,tr).getRegex(),sr="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",mu=R(sr,"gu").replace(/notPunctSpace/g,er).replace(/punctSpace/g,ai).replace(/punct/g,vn).getRegex(),bu=R(sr,"gu").replace(/notPunctSpace/g,fu).replace(/punctSpace/g,pu).replace(/punct/g,tr).getRegex(),yu=R("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,er).replace(/punctSpace/g,ai).replace(/punct/g,vn).getRegex(),wu=R(/\\(punct)/,"gu").replace(/punct/g,vn).getRegex(),$u=R(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),xu=R(si).replace("(?:-->|$)","-->").getRegex(),ku=R("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",xu).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),Xt=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+[^`]*?`+(?!`)|[^\[\]\\`])*?/,Au=R(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]*(?:\n[ \t]*)?)(title))?\s*\)/).replace("label",Xt).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),ir=R(/^!?\[(label)\]\[(ref)\]/).replace("label",Xt).replace("ref",ni).getRegex(),ar=R(/^!?\[(ref)\](?:\[\])?/).replace("ref",ni).getRegex(),Su=R("reflink|nolink(?!\\()","g").replace("reflink",ir).replace("nolink",ar).getRegex(),va=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,oi={_backpedal:yt,anyPunctuation:wu,autolink:$u,blockSkip:hu,br:Xo,code:cu,del:yt,emStrongLDelim:gu,emStrongRDelimAst:mu,emStrongRDelimUnd:yu,escape:lu,link:Au,nolink:ar,punctuation:uu,reflink:ir,reflinkSearch:Su,tag:ku,text:du,url:yt},_u={...oi,link:R(/^!?\[(label)\]\((.*?)\)/).replace("label",Xt).getRegex(),reflink:R(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Xt).getRegex()},ws={...oi,emStrongRDelimAst:bu,emStrongLDelim:vu,url:R(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",va).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:R(/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},ma=e=>Cu[e];function me(e,t){if(t){if(Y.escapeTest.test(e))return e.replace(Y.escapeReplace,ma)}else if(Y.escapeTestNoEncode.test(e))return e.replace(Y.escapeReplaceNoEncode,ma);return e}function ba(e){try{e=encodeURI(e).replace(Y.percentDecode,"%")}catch{return null}return e}function ya(e,t){let n=e.replace(Y.findPipe,(a,o,c)=>{let l=!1,p=o;for(;--p>=0&&c[p]==="\\";)l=!l;return l?"|":" |"}),s=n.split(Y.splitPipe),i=0;if(s[0].trim()||s.shift(),s.length>0&&!s.at(-1)?.trim()&&s.pop(),t)if(s.length>t)s.splice(t);else for(;s.length0?-2:-1}function wa(e,t,n,s,i){let a=t.href,o=t.title||null,c=e[1].replace(i.other.outputLinkReplace,"$1");s.state.inLink=!0;let l={type:e[0].charAt(0)==="!"?"image":"link",raw:n,href:a,title:o,text:c,tokens:s.inlineTokens(c)};return s.state.inLink=!1,l}function Lu(e,t,n){let s=e.match(n.other.indentCodeCompensation);if(s===null)return t;let i=s[1];return t.split(` +`).map(a=>{let o=a.match(n.other.beginningSpace);if(o===null)return a;let[c]=o;return c.length>=i.length?a.slice(i.length):a}).join(` +`)}var en=class{options;rules;lexer;constructor(e){this.options=e||Ke}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?n:gt(n,` +`)}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],s=Lu(n,t[3]||"",this.rules);return{type:"code",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:s}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let s=gt(n,"#");(this.options.pedantic||!s||this.rules.other.endingSpaceChar.test(s))&&(n=s.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:gt(t[0],` +`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=gt(t[0],` +`).split(` +`),s="",i="",a=[];for(;n.length>0;){let o=!1,c=[],l;for(l=0;l1,i={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");let a=this.rules.other.listItemRegex(n),o=!1;for(;e;){let l=!1,p="",d="";if(!(t=a.exec(e))||this.rules.block.hr.test(e))break;p=t[0],e=e.substring(p.length);let u=t[2].split(` +`,1)[0].replace(this.rules.other.listReplaceTabs,$=>" ".repeat(3*$.length)),h=e.split(` +`,1)[0],v=!u.trim(),w=0;if(this.options.pedantic?(w=2,d=u.trimStart()):v?w=t[1].length+1:(w=t[2].search(this.rules.other.nonSpaceChar),w=w>4?1:w,d=u.slice(w),w+=t[1].length),v&&this.rules.other.blankLine.test(h)&&(p+=h+` +`,e=e.substring(h.length+1),l=!0),!l){let $=this.rules.other.nextBulletRegex(w),k=this.rules.other.hrRegex(w),T=this.rules.other.fencesBeginRegex(w),M=this.rules.other.headingBeginRegex(w),P=this.rules.other.htmlBeginRegex(w);for(;e;){let L=e.split(` +`,1)[0],C;if(h=L,this.options.pedantic?(h=h.replace(this.rules.other.listReplaceNesting," "),C=h):C=h.replace(this.rules.other.tabCharGlobal," "),T.test(h)||M.test(h)||P.test(h)||$.test(h)||k.test(h))break;if(C.search(this.rules.other.nonSpaceChar)>=w||!h.trim())d+=` +`+C.slice(w);else{if(v||u.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||T.test(u)||M.test(u)||k.test(u))break;d+=` +`+h}!v&&!h.trim()&&(v=!0),p+=L+` +`,e=e.substring(L.length+1),u=C.slice(w)}}i.loose||(o?i.loose=!0:this.rules.other.doubleBlankLine.test(p)&&(o=!0)),i.items.push({type:"list_item",raw:p,task:!!this.options.gfm&&this.rules.other.listIsTask.test(d),loose:!1,text:d,tokens:[]}),i.raw+=p}let c=i.items.at(-1);if(c)c.raw=c.raw.trimEnd(),c.text=c.text.trimEnd();else return;i.raw=i.raw.trimEnd();for(let l of i.items){if(this.lexer.state.top=!1,l.tokens=this.lexer.blockTokens(l.text,[]),l.task){if(l.text=l.text.replace(this.rules.other.listReplaceTask,""),l.tokens[0]?.type==="text"||l.tokens[0]?.type==="paragraph"){l.tokens[0].raw=l.tokens[0].raw.replace(this.rules.other.listReplaceTask,""),l.tokens[0].text=l.tokens[0].text.replace(this.rules.other.listReplaceTask,"");for(let d=this.lexer.inlineQueue.length-1;d>=0;d--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[d].src)){this.lexer.inlineQueue[d].src=this.lexer.inlineQueue[d].src.replace(this.rules.other.listReplaceTask,"");break}}let p=this.rules.other.listTaskCheckbox.exec(l.raw);if(p){let d={type:"checkbox",raw:p[0]+" ",checked:p[0]!=="[ ]"};l.checked=d.checked,i.loose?l.tokens[0]&&["paragraph","text"].includes(l.tokens[0].type)&&"tokens"in l.tokens[0]&&l.tokens[0].tokens?(l.tokens[0].raw=d.raw+l.tokens[0].raw,l.tokens[0].text=d.raw+l.tokens[0].text,l.tokens[0].tokens.unshift(d)):l.tokens.unshift({type:"paragraph",raw:d.raw,text:d.raw,tokens:[d]}):l.tokens.unshift(d)}}if(!i.loose){let p=l.tokens.filter(u=>u.type==="space"),d=p.length>0&&p.some(u=>this.rules.other.anyLine.test(u.raw));i.loose=d}}if(i.loose)for(let l of i.items){l.loose=!0;for(let p of l.tokens)p.type==="text"&&(p.type="paragraph")}return i}}html(e){let t=this.rules.block.html.exec(e);if(t)return{type:"html",block:!0,raw:t[0],pre:t[1]==="pre"||t[1]==="script"||t[1]==="style",text:t[0]}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),s=t[2]?t[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",i=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:n,raw:t[0],href:s,title:i}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=ya(t[1]),s=t[2].replace(this.rules.other.tableAlignChars,"").split("|"),i=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,"").split(` +`):[],a={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(let o of s)this.rules.other.tableAlignRight.test(o)?a.align.push("right"):this.rules.other.tableAlignCenter.test(o)?a.align.push("center"):this.rules.other.tableAlignLeft.test(o)?a.align.push("left"):a.align.push(null);for(let o=0;o({text:c,tokens:this.lexer.inline(c),header:!1,align:a.align[l]})));return a}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:t[2].charAt(0)==="="?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===` +`?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let a=gt(n.slice(0,-1),"\\");if((n.length-a.length)%2===0)return}else{let a=Eu(t[2],"()");if(a===-2)return;if(a>-1){let o=(t[0].indexOf("!")===0?5:4)+t[1].length+a;t[2]=t[2].substring(0,a),t[0]=t[0].substring(0,o).trim(),t[3]=""}}let s=t[2],i="";if(this.options.pedantic){let a=this.rules.other.pedanticHrefTitle.exec(s);a&&(s=a[1],i=a[3])}else i=t[3]?t[3].slice(1,-1):"";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?s=s.slice(1):s=s.slice(1,-1)),wa(t,{href:s&&s.replace(this.rules.inline.anyPunctuation,"$1"),title:i&&i.replace(this.rules.inline.anyPunctuation,"$1")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let s=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),i=t[s.toLowerCase()];if(!i){let a=n[0].charAt(0);return{type:"text",raw:a,text:a}}return wa(n,i,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!(!s||s[3]&&n.match(this.rules.other.unicodeAlphaNumeric))&&(!(s[1]||s[2])||!n||this.rules.inline.punctuation.exec(n))){let i=[...s[0]].length-1,a,o,c=i,l=0,p=s[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(p.lastIndex=0,t=t.slice(-1*e.length+i);(s=p.exec(t))!=null;){if(a=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!a)continue;if(o=[...a].length,s[3]||s[4]){c+=o;continue}else if((s[5]||s[6])&&i%3&&!((i+o)%3)){l+=o;continue}if(c-=o,c>0)continue;o=Math.min(o,o+c+l);let d=[...s[0]][0].length,u=e.slice(0,i+s.index+d+o);if(Math.min(i,o)%2){let v=u.slice(1,-1);return{type:"em",raw:u,text:v,tokens:this.lexer.inlineTokens(v)}}let h=u.slice(2,-2);return{type:"strong",raw:u,text:h,tokens:this.lexer.inlineTokens(h)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal," "),s=this.rules.other.nonSpaceChar.test(n),i=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return s&&i&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){let t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,s;return t[2]==="@"?(n=t[1],s="mailto:"+n):(n=t[1],s=n),{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,s;if(t[2]==="@")n=t[0],s="mailto:"+n;else{let i;do i=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??"";while(i!==t[0]);n=t[0],t[1]==="www."?s="http://"+t[0]:s=t[0]}return{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:n}}}},ie=class $s{tokens;options;state;inlineQueue;tokenizer;constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||Ke,this.options.tokenizer=this.options.tokenizer||new en,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let n={other:Y,block:zt.normal,inline:ht.normal};this.options.pedantic?(n.block=zt.pedantic,n.inline=ht.pedantic):this.options.gfm&&(n.block=zt.gfm,this.options.breaks?n.inline=ht.breaks:n.inline=ht.gfm),this.tokenizer.rules=n}static get rules(){return{block:zt,inline:ht}}static lex(t,n){return new $s(n).lex(t)}static lexInline(t,n){return new $s(n).inlineTokens(t)}lex(t){t=t.replace(Y.carriageReturn,` +`),this.blockTokens(t,this.tokens);for(let n=0;n(i=o.call({lexer:this},t,n))?(t=t.substring(i.raw.length),n.push(i),!0):!1))continue;if(i=this.tokenizer.space(t)){t=t.substring(i.raw.length);let o=n.at(-1);i.raw.length===1&&o!==void 0?o.raw+=` +`:n.push(i);continue}if(i=this.tokenizer.code(t)){t=t.substring(i.raw.length);let o=n.at(-1);o?.type==="paragraph"||o?.type==="text"?(o.raw+=(o.raw.endsWith(` +`)?"":` +`)+i.raw,o.text+=` +`+i.text,this.inlineQueue.at(-1).src=o.text):n.push(i);continue}if(i=this.tokenizer.fences(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.heading(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.hr(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.blockquote(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.list(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.html(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.def(t)){t=t.substring(i.raw.length);let o=n.at(-1);o?.type==="paragraph"||o?.type==="text"?(o.raw+=(o.raw.endsWith(` +`)?"":` +`)+i.raw,o.text+=` +`+i.raw,this.inlineQueue.at(-1).src=o.text):this.tokens.links[i.tag]||(this.tokens.links[i.tag]={href:i.href,title:i.title},n.push(i));continue}if(i=this.tokenizer.table(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.lheading(t)){t=t.substring(i.raw.length),n.push(i);continue}let a=t;if(this.options.extensions?.startBlock){let o=1/0,c=t.slice(1),l;this.options.extensions.startBlock.forEach(p=>{l=p.call({lexer:this},c),typeof l=="number"&&l>=0&&(o=Math.min(o,l))}),o<1/0&&o>=0&&(a=t.substring(0,o+1))}if(this.state.top&&(i=this.tokenizer.paragraph(a))){let o=n.at(-1);s&&o?.type==="paragraph"?(o.raw+=(o.raw.endsWith(` +`)?"":` +`)+i.raw,o.text+=` +`+i.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=o.text):n.push(i),s=a.length!==t.length,t=t.substring(i.raw.length);continue}if(i=this.tokenizer.text(t)){t=t.substring(i.raw.length);let o=n.at(-1);o?.type==="text"?(o.raw+=(o.raw.endsWith(` +`)?"":` +`)+i.raw,o.text+=` +`+i.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=o.text):n.push(i);continue}if(t){let o="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(o);break}else throw new Error(o)}}return this.state.top=!0,n}inline(t,n=[]){return this.inlineQueue.push({src:t,tokens:n}),n}inlineTokens(t,n=[]){let s=t,i=null;if(this.tokens.links){let l=Object.keys(this.tokens.links);if(l.length>0)for(;(i=this.tokenizer.rules.inline.reflinkSearch.exec(s))!=null;)l.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(s=s.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(i=this.tokenizer.rules.inline.anyPunctuation.exec(s))!=null;)s=s.slice(0,i.index)+"++"+s.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let a;for(;(i=this.tokenizer.rules.inline.blockSkip.exec(s))!=null;)a=i[2]?i[2].length:0,s=s.slice(0,i.index+a)+"["+"a".repeat(i[0].length-a-2)+"]"+s.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);s=this.options.hooks?.emStrongMask?.call({lexer:this},s)??s;let o=!1,c="";for(;t;){o||(c=""),o=!1;let l;if(this.options.extensions?.inline?.some(d=>(l=d.call({lexer:this},t,n))?(t=t.substring(l.raw.length),n.push(l),!0):!1))continue;if(l=this.tokenizer.escape(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.tag(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.link(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.reflink(t,this.tokens.links)){t=t.substring(l.raw.length);let d=n.at(-1);l.type==="text"&&d?.type==="text"?(d.raw+=l.raw,d.text+=l.text):n.push(l);continue}if(l=this.tokenizer.emStrong(t,s,c)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.codespan(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.br(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.del(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.autolink(t)){t=t.substring(l.raw.length),n.push(l);continue}if(!this.state.inLink&&(l=this.tokenizer.url(t))){t=t.substring(l.raw.length),n.push(l);continue}let p=t;if(this.options.extensions?.startInline){let d=1/0,u=t.slice(1),h;this.options.extensions.startInline.forEach(v=>{h=v.call({lexer:this},u),typeof h=="number"&&h>=0&&(d=Math.min(d,h))}),d<1/0&&d>=0&&(p=t.substring(0,d+1))}if(l=this.tokenizer.inlineText(p)){t=t.substring(l.raw.length),l.raw.slice(-1)!=="_"&&(c=l.raw.slice(-1)),o=!0;let d=n.at(-1);d?.type==="text"?(d.raw+=l.raw,d.text+=l.text):n.push(l);continue}if(t){let d="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(d);break}else throw new Error(d)}}return n}},tn=class{options;parser;constructor(e){this.options=e||Ke}space(e){return""}code({text:e,lang:t,escaped:n}){let s=(t||"").match(Y.notSpaceStart)?.[0],i=e.replace(Y.endingNewline,"")+` +`;return s?'
'+(n?i:me(i,!0))+`
+`:"
"+(n?i:me(i,!0))+`
+`}blockquote({tokens:e}){return`
+${this.parser.parse(e)}
+`}html({text:e}){return e}def(e){return""}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)} +`}hr(e){return`
+`}list(e){let t=e.ordered,n=e.start,s="";for(let o=0;o +`+s+" +`}listitem(e){return`
  • ${this.parser.parse(e.tokens)}
  • +`}checkbox({checked:e}){return" '}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    +`}table(e){let t="",n="";for(let i=0;i${s}`),` + +`+t+` +`+s+`
    +`}tablerow({text:e}){return` +${e} +`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+` +`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${me(e,!0)}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let s=this.parser.parseInline(n),i=ba(e);if(i===null)return s;e=i;let a='
    ",a}image({href:e,title:t,text:n,tokens:s}){s&&(n=this.parser.parseInline(s,this.parser.textRenderer));let i=ba(e);if(i===null)return me(n);e=i;let a=`${n}{let o=i[a].flat(1/0);n=n.concat(this.walkTokens(o,t))}):i.tokens&&(n=n.concat(this.walkTokens(i.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let s={...n};if(s.async=this.defaults.async||s.async||!1,n.extensions&&(n.extensions.forEach(i=>{if(!i.name)throw new Error("extension name required");if("renderer"in i){let a=t.renderers[i.name];a?t.renderers[i.name]=function(...o){let c=i.renderer.apply(this,o);return c===!1&&(c=a.apply(this,o)),c}:t.renderers[i.name]=i.renderer}if("tokenizer"in i){if(!i.level||i.level!=="block"&&i.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let a=t[i.level];a?a.unshift(i.tokenizer):t[i.level]=[i.tokenizer],i.start&&(i.level==="block"?t.startBlock?t.startBlock.push(i.start):t.startBlock=[i.start]:i.level==="inline"&&(t.startInline?t.startInline.push(i.start):t.startInline=[i.start]))}"childTokens"in i&&i.childTokens&&(t.childTokens[i.name]=i.childTokens)}),s.extensions=t),n.renderer){let i=this.defaults.renderer||new tn(this.defaults);for(let a in n.renderer){if(!(a in i))throw new Error(`renderer '${a}' does not exist`);if(["options","parser"].includes(a))continue;let o=a,c=n.renderer[o],l=i[o];i[o]=(...p)=>{let d=c.apply(i,p);return d===!1&&(d=l.apply(i,p)),d||""}}s.renderer=i}if(n.tokenizer){let i=this.defaults.tokenizer||new en(this.defaults);for(let a in n.tokenizer){if(!(a in i))throw new Error(`tokenizer '${a}' does not exist`);if(["options","rules","lexer"].includes(a))continue;let o=a,c=n.tokenizer[o],l=i[o];i[o]=(...p)=>{let d=c.apply(i,p);return d===!1&&(d=l.apply(i,p)),d}}s.tokenizer=i}if(n.hooks){let i=this.defaults.hooks||new vt;for(let a in n.hooks){if(!(a in i))throw new Error(`hook '${a}' does not exist`);if(["options","block"].includes(a))continue;let o=a,c=n.hooks[o],l=i[o];vt.passThroughHooks.has(a)?i[o]=p=>{if(this.defaults.async&&vt.passThroughHooksRespectAsync.has(a))return(async()=>{let u=await c.call(i,p);return l.call(i,u)})();let d=c.call(i,p);return l.call(i,d)}:i[o]=(...p)=>{if(this.defaults.async)return(async()=>{let u=await c.apply(i,p);return u===!1&&(u=await l.apply(i,p)),u})();let d=c.apply(i,p);return d===!1&&(d=l.apply(i,p)),d}}s.hooks=i}if(n.walkTokens){let i=this.defaults.walkTokens,a=n.walkTokens;s.walkTokens=function(o){let c=[];return c.push(a.call(this,o)),i&&(c=c.concat(i.call(this,o))),c}}this.defaults={...this.defaults,...s}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return ie.lex(e,t??this.defaults)}parser(e,t){return ae.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{let s={...n},i={...this.defaults,...s},a=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&s.async===!1)return a(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof t>"u"||t===null)return a(new Error("marked(): input parameter is undefined or null"));if(typeof t!="string")return a(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));if(i.hooks&&(i.hooks.options=i,i.hooks.block=e),i.async)return(async()=>{let o=i.hooks?await i.hooks.preprocess(t):t,c=await(i.hooks?await i.hooks.provideLexer():e?ie.lex:ie.lexInline)(o,i),l=i.hooks?await i.hooks.processAllTokens(c):c;i.walkTokens&&await Promise.all(this.walkTokens(l,i.walkTokens));let p=await(i.hooks?await i.hooks.provideParser():e?ae.parse:ae.parseInline)(l,i);return i.hooks?await i.hooks.postprocess(p):p})().catch(a);try{i.hooks&&(t=i.hooks.preprocess(t));let o=(i.hooks?i.hooks.provideLexer():e?ie.lex:ie.lexInline)(t,i);i.hooks&&(o=i.hooks.processAllTokens(o)),i.walkTokens&&this.walkTokens(o,i.walkTokens);let c=(i.hooks?i.hooks.provideParser():e?ae.parse:ae.parseInline)(o,i);return i.hooks&&(c=i.hooks.postprocess(c)),c}catch(o){return a(o)}}}onError(e,t){return n=>{if(n.message+=` +Please report this to https://github.com/markedjs/marked.`,e){let s="

    An error occurred:

    "+me(n.message+"",!0)+"
    ";return t?Promise.resolve(s):s}if(t)return Promise.reject(n);throw n}}},Ue=new Mu;function N(e,t){return Ue.parse(e,t)}N.options=N.setOptions=function(e){return Ue.setOptions(e),N.defaults=Ue.defaults,Yo(N.defaults),N};N.getDefaults=Xs;N.defaults=Ke;N.use=function(...e){return Ue.use(...e),N.defaults=Ue.defaults,Yo(N.defaults),N};N.walkTokens=function(e,t){return Ue.walkTokens(e,t)};N.parseInline=Ue.parseInline;N.Parser=ae;N.parser=ae.parse;N.Renderer=tn;N.TextRenderer=ri;N.Lexer=ie;N.lexer=ie.lex;N.Tokenizer=en;N.Hooks=vt;N.parse=N;N.options;N.setOptions;N.use;N.walkTokens;N.parseInline;ae.parse;ie.lex;N.setOptions({gfm:!0,breaks:!0,mangle:!1});const $a=["a","b","blockquote","br","code","del","em","h1","h2","h3","h4","hr","i","li","ol","p","pre","strong","table","tbody","td","th","thead","tr","ul"],xa=["class","href","rel","target","title","start"];let ka=!1;const Iu=14e4,Ru=4e4,Pu=200,Jn=5e4,Ne=new Map;function Nu(e){const t=Ne.get(e);return t===void 0?null:(Ne.delete(e),Ne.set(e,t),t)}function Aa(e,t){if(Ne.set(e,t),Ne.size<=Pu)return;const n=Ne.keys().next().value;n&&Ne.delete(n)}function Ou(){ka||(ka=!0,ys.addHook("afterSanitizeAttributes",e=>{!(e instanceof HTMLAnchorElement)||!e.getAttribute("href")||(e.setAttribute("rel","noreferrer noopener"),e.setAttribute("target","_blank"))}))}function ks(e){const t=e.trim();if(!t)return"";if(Ou(),t.length<=Jn){const o=Nu(t);if(o!==null)return o}const n=ao(t,Iu),s=n.truncated?` + +… truncated (${n.total} chars, showing first ${n.text.length}).`:"";if(n.text.length>Ru){const c=`
    ${Du(`${n.text}${s}`)}
    `,l=ys.sanitize(c,{ALLOWED_TAGS:$a,ALLOWED_ATTR:xa});return t.length<=Jn&&Aa(t,l),l}const i=N.parse(`${n.text}${s}`),a=ys.sanitize(i,{ALLOWED_TAGS:$a,ALLOWED_ATTR:xa});return t.length<=Jn&&Aa(t,a),a}function Du(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}const Bu=1500,Fu=2e3,or="Copy as markdown",Uu="Copied",Ku="Copy failed";async function Hu(e){if(!e)return!1;try{return await navigator.clipboard.writeText(e),!0}catch{return!1}}function jt(e,t){e.title=t,e.setAttribute("aria-label",t)}function zu(e){const t=e.label??or;return r` + + `}function ju(e){return zu({text:()=>e,label:or})}const qu={icon:"puzzle",detailKeys:["command","path","url","targetUrl","targetId","ref","element","node","nodeId","id","requestId","to","channelId","guildId","userId","name","query","pattern","messageId"]},Vu={bash:{icon:"wrench",title:"Bash",detailKeys:["command"]},process:{icon:"wrench",title:"Process",detailKeys:["sessionId"]},read:{icon:"fileText",title:"Read",detailKeys:["path"]},write:{icon:"edit",title:"Write",detailKeys:["path"]},edit:{icon:"penLine",title:"Edit",detailKeys:["path"]},attach:{icon:"paperclip",title:"Attach",detailKeys:["path","url","fileName"]},browser:{icon:"globe",title:"Browser",actions:{status:{label:"status"},start:{label:"start"},stop:{label:"stop"},tabs:{label:"tabs"},open:{label:"open",detailKeys:["targetUrl"]},focus:{label:"focus",detailKeys:["targetId"]},close:{label:"close",detailKeys:["targetId"]},snapshot:{label:"snapshot",detailKeys:["targetUrl","targetId","ref","element","format"]},screenshot:{label:"screenshot",detailKeys:["targetUrl","targetId","ref","element"]},navigate:{label:"navigate",detailKeys:["targetUrl","targetId"]},console:{label:"console",detailKeys:["level","targetId"]},pdf:{label:"pdf",detailKeys:["targetId"]},upload:{label:"upload",detailKeys:["paths","ref","inputRef","element","targetId"]},dialog:{label:"dialog",detailKeys:["accept","promptText","targetId"]},act:{label:"act",detailKeys:["request.kind","request.ref","request.selector","request.text","request.value"]}}},canvas:{icon:"image",title:"Canvas",actions:{present:{label:"present",detailKeys:["target","node","nodeId"]},hide:{label:"hide",detailKeys:["node","nodeId"]},navigate:{label:"navigate",detailKeys:["url","node","nodeId"]},eval:{label:"eval",detailKeys:["javaScript","node","nodeId"]},snapshot:{label:"snapshot",detailKeys:["format","node","nodeId"]},a2ui_push:{label:"A2UI push",detailKeys:["jsonlPath","node","nodeId"]},a2ui_reset:{label:"A2UI reset",detailKeys:["node","nodeId"]}}},nodes:{icon:"smartphone",title:"Nodes",actions:{status:{label:"status"},describe:{label:"describe",detailKeys:["node","nodeId"]},pending:{label:"pending"},approve:{label:"approve",detailKeys:["requestId"]},reject:{label:"reject",detailKeys:["requestId"]},notify:{label:"notify",detailKeys:["node","nodeId","title","body"]},camera_snap:{label:"camera snap",detailKeys:["node","nodeId","facing","deviceId"]},camera_list:{label:"camera list",detailKeys:["node","nodeId"]},camera_clip:{label:"camera clip",detailKeys:["node","nodeId","facing","duration","durationMs"]},screen_record:{label:"screen record",detailKeys:["node","nodeId","duration","durationMs","fps","screenIndex"]}}},cron:{icon:"loader",title:"Cron",actions:{status:{label:"status"},list:{label:"list"},add:{label:"add",detailKeys:["job.name","job.id","job.schedule","job.cron"]},update:{label:"update",detailKeys:["id"]},remove:{label:"remove",detailKeys:["id"]},run:{label:"run",detailKeys:["id"]},runs:{label:"runs",detailKeys:["id"]},wake:{label:"wake",detailKeys:["text","mode"]}}},gateway:{icon:"plug",title:"Gateway",actions:{restart:{label:"restart",detailKeys:["reason","delayMs"]},"config.get":{label:"config get"},"config.schema":{label:"config schema"},"config.apply":{label:"config apply",detailKeys:["restartDelayMs"]},"update.run":{label:"update run",detailKeys:["restartDelayMs"]}}},whatsapp_login:{icon:"circle",title:"WhatsApp Login",actions:{start:{label:"start"},wait:{label:"wait"}}},discord:{icon:"messageSquare",title:"Discord",actions:{react:{label:"react",detailKeys:["channelId","messageId","emoji"]},reactions:{label:"reactions",detailKeys:["channelId","messageId"]},sticker:{label:"sticker",detailKeys:["to","stickerIds"]},poll:{label:"poll",detailKeys:["question","to"]},permissions:{label:"permissions",detailKeys:["channelId"]},readMessages:{label:"read messages",detailKeys:["channelId","limit"]},sendMessage:{label:"send",detailKeys:["to","content"]},editMessage:{label:"edit",detailKeys:["channelId","messageId"]},deleteMessage:{label:"delete",detailKeys:["channelId","messageId"]},threadCreate:{label:"thread create",detailKeys:["channelId","name"]},threadList:{label:"thread list",detailKeys:["guildId","channelId"]},threadReply:{label:"thread reply",detailKeys:["channelId","content"]},pinMessage:{label:"pin",detailKeys:["channelId","messageId"]},unpinMessage:{label:"unpin",detailKeys:["channelId","messageId"]},listPins:{label:"list pins",detailKeys:["channelId"]},searchMessages:{label:"search",detailKeys:["guildId","content"]},memberInfo:{label:"member",detailKeys:["guildId","userId"]},roleInfo:{label:"roles",detailKeys:["guildId"]},emojiList:{label:"emoji list",detailKeys:["guildId"]},roleAdd:{label:"role add",detailKeys:["guildId","userId","roleId"]},roleRemove:{label:"role remove",detailKeys:["guildId","userId","roleId"]},channelInfo:{label:"channel",detailKeys:["channelId"]},channelList:{label:"channels",detailKeys:["guildId"]},voiceStatus:{label:"voice",detailKeys:["guildId","userId"]},eventList:{label:"events",detailKeys:["guildId"]},eventCreate:{label:"event create",detailKeys:["guildId","name"]},timeout:{label:"timeout",detailKeys:["guildId","userId"]},kick:{label:"kick",detailKeys:["guildId","userId"]},ban:{label:"ban",detailKeys:["guildId","userId"]}}},slack:{icon:"messageSquare",title:"Slack",actions:{react:{label:"react",detailKeys:["channelId","messageId","emoji"]},reactions:{label:"reactions",detailKeys:["channelId","messageId"]},sendMessage:{label:"send",detailKeys:["to","content"]},editMessage:{label:"edit",detailKeys:["channelId","messageId"]},deleteMessage:{label:"delete",detailKeys:["channelId","messageId"]},readMessages:{label:"read messages",detailKeys:["channelId","limit"]},pinMessage:{label:"pin",detailKeys:["channelId","messageId"]},unpinMessage:{label:"unpin",detailKeys:["channelId","messageId"]},listPins:{label:"list pins",detailKeys:["channelId"]},memberInfo:{label:"member",detailKeys:["userId"]},emojiList:{label:"emoji list"}}}},Wu={fallback:qu,tools:Vu},rr=Wu,Sa=rr.fallback??{icon:"puzzle"},Gu=rr.tools??{};function Yu(e){return(e??"tool").trim()}function Qu(e){const t=e.replace(/_/g," ").trim();return t?t.split(/\s+/).map(n=>n.length<=2&&n.toUpperCase()===n?n:`${n.at(0)?.toUpperCase()??""}${n.slice(1)}`).join(" "):"Tool"}function Zu(e){const t=e?.trim();if(t)return t.replace(/_/g," ")}function lr(e){if(e!=null){if(typeof e=="string"){const t=e.trim();if(!t)return;const n=t.split(/\r?\n/)[0]?.trim()??"";return n?n.length>160?`${n.slice(0,157)}…`:n:void 0}if(typeof e=="number"||typeof e=="boolean")return String(e);if(Array.isArray(e)){const t=e.map(s=>lr(s)).filter(s=>!!s);if(t.length===0)return;const n=t.slice(0,3).join(", ");return t.length>3?`${n}…`:n}}}function Ju(e,t){if(!e||typeof e!="object")return;let n=e;for(const s of t.split(".")){if(!s||!n||typeof n!="object")return;n=n[s]}return n}function Xu(e,t){for(const n of t){const s=Ju(e,n),i=lr(s);if(i)return i}}function ep(e){if(!e||typeof e!="object")return;const t=e,n=typeof t.path=="string"?t.path:void 0;if(!n)return;const s=typeof t.offset=="number"?t.offset:void 0,i=typeof t.limit=="number"?t.limit:void 0;return s!==void 0&&i!==void 0?`${n}:${s}-${s+i}`:n}function tp(e){if(!e||typeof e!="object")return;const t=e;return typeof t.path=="string"?t.path:void 0}function np(e,t){if(!(!e||!t))return e.actions?.[t]??void 0}function sp(e){const t=Yu(e.name),n=t.toLowerCase(),s=Gu[n],i=s?.icon??Sa.icon??"puzzle",a=s?.title??Qu(t),o=s?.label??t,c=e.args&&typeof e.args=="object"?e.args.action:void 0,l=typeof c=="string"?c.trim():void 0,p=np(s,l),d=Zu(p?.label??l);let u;n==="read"&&(u=ep(e.args)),!u&&(n==="write"||n==="edit"||n==="attach")&&(u=tp(e.args));const h=p?.detailKeys??s?.detailKeys??Sa.detailKeys??[];return!u&&h.length>0&&(u=Xu(e.args,h)),!u&&e.meta&&(u=e.meta),u&&(u=ap(u)),{name:t,icon:i,title:a,label:o,verb:d,detail:u}}function ip(e){const t=[];if(e.verb&&t.push(e.verb),e.detail&&t.push(e.detail),t.length!==0)return t.join(" · ")}function ap(e){return e&&e.replace(/\/Users\/[^/]+/g,"~").replace(/\/home\/[^/]+/g,"~")}const op=80,rp=2,_a=100;function lp(e){const t=e.trim();if(t.startsWith("{")||t.startsWith("["))try{const n=JSON.parse(t);return"```json\n"+JSON.stringify(n,null,2)+"\n```"}catch{}return e}function cp(e){const t=e.split(` +`),n=t.slice(0,rp),s=n.join(` +`);return s.length>_a?s.slice(0,_a)+"…":n.lengthi.kind==="result")){const i=typeof t.toolName=="string"&&t.toolName||typeof t.tool_name=="string"&&t.tool_name||"tool",a=oo(e)??void 0;s.push({kind:"result",name:i,text:a})}return s}function Ta(e,t){const n=sp({name:e.name,args:e.args}),s=ip(n),i=!!e.text?.trim(),a=!!t,o=a?()=>{if(i){t(lp(e.text));return}const u=`## ${n.label} + +${s?`**Command:** \`${s}\` + +`:""}*No output — tool completed successfully.*`;t(u)}:void 0,c=i&&(e.text?.length??0)<=op,l=i&&!c,p=i&&c,d=!i;return r` +
    {u.key!=="Enter"&&u.key!==" "||(u.preventDefault(),o?.())}:g} + > +
    +
    + ${Q[n.icon]} + ${n.label} +
    + ${a?r`${i?"View":""} ${Q.check}`:g} + ${d&&!a?r`${Q.check}`:g} +
    + ${s?r`
    ${s}
    `:g} + ${d?r`
    Completed
    `:g} + ${l?r`
    ${cp(e.text)}
    `:g} + ${p?r`
    ${e.text}
    `:g} +
    + `}function up(e){return Array.isArray(e)?e.filter(Boolean):[]}function pp(e){if(typeof e!="string")return e;const t=e.trim();if(!t||!t.startsWith("{")&&!t.startsWith("["))return e;try{return JSON.parse(t)}catch{return e}}function fp(e){if(typeof e.text=="string")return e.text;if(typeof e.content=="string")return e.content}function hp(e){return r` +
    + ${li("assistant",e)} +
    + +
    +
    + `}function gp(e,t,n,s){const i=new Date(t).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"}),a=s?.name??"Assistant";return r` +
    + ${li("assistant",s)} +
    + ${cr({role:"assistant",content:[{type:"text",text:e}],timestamp:t},{isStreaming:!0,showReasoning:!1},n)} + +
    +
    + `}function vp(e,t){const n=Js(e.role),s=t.assistantName??"Assistant",i=n==="user"?"You":n==="assistant"?s:n,a=n==="user"?"user":n==="assistant"?"assistant":"other",o=new Date(e.timestamp).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"});return r` +
    + ${li(e.role,{name:s,avatar:t.assistantAvatar??null})} +
    + ${e.messages.map((c,l)=>cr(c.message,{isStreaming:e.isStreaming&&l===e.messages.length-1,showReasoning:t.showReasoning},t.onOpenSidebar))} + +
    +
    + `}function li(e,t){const n=Js(e),s=t?.name?.trim()||"Assistant",i=t?.avatar?.trim()||"",a=n==="user"?"U":n==="assistant"?s.charAt(0).toUpperCase()||"A":n==="tool"?"⚙":"?",o=n==="user"?"user":n==="assistant"?"assistant":n==="tool"?"tool":"other";return i&&n==="assistant"?mp(i)?r`${s}`:r`
    ${i}
    `:r`
    ${a}
    `}function mp(e){return/^https?:\/\//i.test(e)||/^data:image\//i.test(e)||/^\//.test(e)}function cr(e,t,n){const s=e,i=typeof s.role=="string"?s.role:"unknown",a=jo(e)||i.toLowerCase()==="toolresult"||i.toLowerCase()==="tool_result"||typeof s.toolCallId=="string"||typeof s.tool_call_id=="string",o=dp(e),c=o.length>0,l=oo(e),p=t.showReasoning&&i==="assistant"?Al(e):null,d=l?.trim()?l:null,u=p?_l(p):null,h=d,v=i==="assistant"&&!!h?.trim(),w=["chat-bubble",v?"has-copy":"",t.isStreaming?"streaming":"","fade-in"].filter(Boolean).join(" ");return!h&&c&&a?r`${o.map($=>Ta($,n))}`:!h&&!c?g:r` +
    + ${v?ju(h):g} + ${u?r`
    ${gs(ks(u))}
    `:g} + ${h?r`
    ${gs(ks(h))}
    `:g} + ${o.map($=>Ta($,n))} +
    + `}function bp(e){return r` + + `}var yp=Object.defineProperty,wp=Object.getOwnPropertyDescriptor,mn=(e,t,n,s)=>{for(var i=s>1?void 0:s?wp(t,n):t,a=e.length-1,o;a>=0;a--)(o=e[a])&&(i=(s?o(t,n,i):o(i))||i);return s&&i&&yp(t,n,i),i};let nt=class extends Ze{constructor(){super(...arguments),this.splitRatio=.6,this.minRatio=.4,this.maxRatio=.7,this.isDragging=!1,this.startX=0,this.startRatio=0,this.handleMouseDown=e=>{this.isDragging=!0,this.startX=e.clientX,this.startRatio=this.splitRatio,this.classList.add("dragging"),document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("mouseup",this.handleMouseUp),e.preventDefault()},this.handleMouseMove=e=>{if(!this.isDragging)return;const t=this.parentElement;if(!t)return;const n=t.getBoundingClientRect().width,i=(e.clientX-this.startX)/n;let a=this.startRatio+i;a=Math.max(this.minRatio,Math.min(this.maxRatio,a)),this.dispatchEvent(new CustomEvent("resize",{detail:{splitRatio:a},bubbles:!0,composed:!0}))},this.handleMouseUp=()=>{this.isDragging=!1,this.classList.remove("dragging"),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp)}}render(){return r``}connectedCallback(){super.connectedCallback(),this.addEventListener("mousedown",this.handleMouseDown)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("mousedown",this.handleMouseDown),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp)}};nt.styles=Dr` + :host { + width: 4px; + cursor: col-resize; + background: var(--border, #333); + transition: background 150ms ease-out; + flex-shrink: 0; + position: relative; + } + + :host::before { + content: ""; + position: absolute; + top: 0; + left: -4px; + right: -4px; + bottom: 0; + } + + :host(:hover) { + background: var(--accent, #007bff); + } + + :host(.dragging) { + background: var(--accent, #007bff); + } + `;mn([on({type:Number})],nt.prototype,"splitRatio",2);mn([on({type:Number})],nt.prototype,"minRatio",2);mn([on({type:Number})],nt.prototype,"maxRatio",2);nt=mn([Ja("resizable-divider")],nt);const $p=5e3;function xp(e){return e?e.active?r` +
    + ${Q.loader} Compacting context... +
    + `:e.completedAt&&Date.now()-e.completedAt<$p?r` +
    + ${Q.check} Context compacted +
    + `:g:g}function kp(e){const t=e.connected,n=e.sending||e.stream!==null,s=!!(e.canAbort&&e.onAbort),a=e.sessions?.sessions?.find(h=>h.key===e.sessionKey)?.reasoningLevel??"off",o=e.showThinking&&a!=="off",c={name:e.assistantName,avatar:e.assistantAvatar??e.assistantAvatarUrl??null},l=e.connected?"Message (↩ to send, Shift+↩ for line breaks)":"Connect to the gateway to start chatting…",p=e.splitRatio??.6,d=!!(e.sidebarOpen&&e.onCloseSidebar),u=r` +
    + ${e.loading?r`
    Loading chat…
    `:g} + ${Ho(Sp(e),h=>h.key,h=>h.kind==="reading-indicator"?hp(c):h.kind==="stream"?gp(h.text,h.startedAt,e.onOpenSidebar,c):h.kind==="group"?vp(h,{onOpenSidebar:e.onOpenSidebar,showReasoning:o,assistantName:e.assistantName,assistantAvatar:c.avatar}):g)} +
    + `;return r` +
    + ${e.disabledReason?r`
    ${e.disabledReason}
    `:g} + + ${e.error?r`
    ${e.error}
    `:g} + + ${xp(e.compactionStatus)} + + ${e.focusMode?r` + + `:g} + +
    +
    + ${u} +
    + + ${d?r` + e.onSplitRatioChange?.(h.detail.splitRatio)} + > +
    + ${bp({content:e.sidebarContent??null,error:e.sidebarError??null,onClose:e.onCloseSidebar,onViewRawText:()=>{!e.sidebarContent||!e.onOpenSidebar||e.onOpenSidebar(`\`\`\` +${e.sidebarContent} +\`\`\``)}})} +
    + `:g} +
    + + ${e.queue.length?r` +
    +
    Queued (${e.queue.length})
    +
    + ${e.queue.map(h=>r` +
    +
    ${h.text}
    + +
    + `)} +
    +
    + `:g} + +
    + +
    + + +
    +
    +
    + `}const Ca=200;function Ap(e){const t=[];let n=null;for(const s of e){if(s.kind!=="message"){n&&(t.push(n),n=null),t.push(s);continue}const i=zo(s.message),a=Js(i.role),o=i.timestamp||Date.now();!n||n.role!==a?(n&&t.push(n),n={kind:"group",key:`group:${a}:${s.key}`,role:a,messages:[{message:s.message,key:s.key}],timestamp:o,isStreaming:!1}):n.messages.push({message:s.message,key:s.key})}return n&&t.push(n),t}function Sp(e){const t=[],n=Array.isArray(e.messages)?e.messages:[],s=Array.isArray(e.toolMessages)?e.toolMessages:[],i=Math.max(0,n.length-Ca);i>0&&t.push({kind:"message",key:"chat:history:notice",message:{role:"system",content:`Showing last ${Ca} messages (${i} hidden).`,timestamp:Date.now()}});for(let a=i;a0?t.push({kind:"stream",key:a,text:e.stream,startedAt:e.streamStartedAt??Date.now()}):t.push({kind:"reading-indicator",key:a})}return Ap(t)}function Ea(e,t){const n=e,s=typeof n.toolCallId=="string"?n.toolCallId:"";if(s)return`tool:${s}`;const i=typeof n.id=="string"?n.id:"";if(i)return`msg:${i}`;const a=typeof n.messageId=="string"?n.messageId:"";if(a)return`msg:${a}`;const o=typeof n.timestamp=="number"?n.timestamp:null,c=typeof n.role=="string"?n.role:"unknown";return o!=null?`msg:${c}:${o}:${t}`:`msg:${c}:${t}`}function ue(e){if(e)return Array.isArray(e.type)?e.type.filter(n=>n!=="null")[0]??e.type[0]:e.type}function dr(e){if(!e)return"";if(e.default!==void 0)return e.default;switch(ue(e)){case"object":return{};case"array":return[];case"boolean":return!1;case"number":case"integer":return 0;case"string":return"";default:return""}}function bn(e){return e.filter(t=>typeof t=="string").join(".")}function te(e,t){const n=bn(e),s=t[n];if(s)return s;const i=n.split(".");for(const[a,o]of Object.entries(t)){if(!a.includes("*"))continue;const c=a.split(".");if(c.length!==i.length)continue;let l=!0;for(let p=0;pt.toUpperCase())}function _p(e){const t=bn(e).toLowerCase();return t.includes("token")||t.includes("password")||t.includes("secret")||t.includes("apikey")||t.endsWith("key")}const Tp=new Set(["title","description","default","nullable"]);function Cp(e){return Object.keys(e??{}).filter(n=>!Tp.has(n)).length===0}function Ep(e){if(e===void 0)return"";try{return JSON.stringify(e,null,2)??""}catch{return""}}const _t={chevronDown:r``,plus:r``,minus:r``,trash:r``,edit:r``};function ye(e){const{schema:t,value:n,path:s,hints:i,unsupported:a,disabled:o,onPatch:c}=e,l=e.showLabel??!0,p=ue(t),d=te(s,i),u=d?.label??t.title??we(String(s.at(-1))),h=d?.help??t.description,v=bn(s);if(a.has(v))return r`
    +
    ${u}
    +
    Unsupported schema node. Use Raw mode.
    +
    `;if(t.anyOf||t.oneOf){const $=(t.anyOf??t.oneOf??[]).filter(C=>!(C.type==="null"||Array.isArray(C.type)&&C.type.includes("null")));if($.length===1)return ye({...e,schema:$[0]});const k=C=>{if(C.const!==void 0)return C.const;if(C.enum&&C.enum.length===1)return C.enum[0]},T=$.map(k),M=T.every(C=>C!==void 0);if(M&&T.length>0&&T.length<=5){const C=n??t.default;return r` +
    + ${l?r``:g} + ${h?r`
    ${h}
    `:g} +
    + ${T.map((E,pe)=>r` + + `)} +
    +
    + `}if(M&&T.length>5)return Ma({...e,options:T,value:n??t.default});const P=new Set($.map(C=>ue(C)).filter(Boolean)),L=new Set([...P].map(C=>C==="integer"?"number":C));if([...L].every(C=>["string","number","boolean"].includes(C))){const C=L.has("string"),E=L.has("number");if(L.has("boolean")&&L.size===1)return ye({...e,schema:{...t,type:"boolean",anyOf:void 0,oneOf:void 0}});if(C||E)return La({...e,inputType:E&&!C?"number":"text"})}}if(t.enum){const w=t.enum;if(w.length<=5){const $=n??t.default;return r` +
    + ${l?r``:g} + ${h?r`
    ${h}
    `:g} +
    + ${w.map(k=>r` + + `)} +
    +
    + `}return Ma({...e,options:w,value:n??t.default})}if(p==="object")return Mp(e);if(p==="array")return Ip(e);if(p==="boolean"){const w=typeof n=="boolean"?n:typeof t.default=="boolean"?t.default:!1;return r` + + `}return p==="number"||p==="integer"?Lp(e):p==="string"?La({...e,inputType:"text"}):r` +
    +
    ${u}
    +
    Unsupported type: ${p}. Use Raw mode.
    +
    + `}function La(e){const{schema:t,value:n,path:s,hints:i,disabled:a,onPatch:o,inputType:c}=e,l=e.showLabel??!0,p=te(s,i),d=p?.label??t.title??we(String(s.at(-1))),u=p?.help??t.description,h=p?.sensitive??_p(s),v=p?.placeholder??(h?"••••":t.default!==void 0?`Default: ${t.default}`:""),w=n??"";return r` +
    + ${l?r``:g} + ${u?r`
    ${u}
    `:g} +
    + {const k=$.target.value;if(c==="number"){if(k.trim()===""){o(s,void 0);return}const T=Number(k);o(s,Number.isNaN(T)?k:T);return}o(s,k)}} + /> + ${t.default!==void 0?r` + + `:g} +
    +
    + `}function Lp(e){const{schema:t,value:n,path:s,hints:i,disabled:a,onPatch:o}=e,c=e.showLabel??!0,l=te(s,i),p=l?.label??t.title??we(String(s.at(-1))),d=l?.help??t.description,u=n??t.default??"",h=typeof u=="number"?u:0;return r` +
    + ${c?r``:g} + ${d?r`
    ${d}
    `:g} +
    + + {const w=v.target.value,$=w===""?void 0:Number(w);o(s,$)}} + /> + +
    +
    + `}function Ma(e){const{schema:t,value:n,path:s,hints:i,disabled:a,options:o,onPatch:c}=e,l=e.showLabel??!0,p=te(s,i),d=p?.label??t.title??we(String(s.at(-1))),u=p?.help??t.description,h=n??t.default,v=o.findIndex($=>$===h||String($)===String(h)),w="__unset__";return r` +
    + ${l?r``:g} + ${u?r`
    ${u}
    `:g} + +
    + `}function Mp(e){const{schema:t,value:n,path:s,hints:i,unsupported:a,disabled:o,onPatch:c}=e;e.showLabel;const l=te(s,i),p=l?.label??t.title??we(String(s.at(-1))),d=l?.help??t.description,u=n??t.default,h=u&&typeof u=="object"&&!Array.isArray(u)?u:{},v=t.properties??{},$=Object.entries(v).sort((P,L)=>{const C=te([...s,P[0]],i)?.order??0,E=te([...s,L[0]],i)?.order??0;return C!==E?C-E:P[0].localeCompare(L[0])}),k=new Set(Object.keys(v)),T=t.additionalProperties,M=!!T&&typeof T=="object";return s.length===1?r` +
    + ${$.map(([P,L])=>ye({schema:L,value:h[P],path:[...s,P],hints:i,unsupported:a,disabled:o,onPatch:c}))} + ${M?Ia({schema:T,value:h,path:s,hints:i,unsupported:a,disabled:o,reservedKeys:k,onPatch:c}):g} +
    + `:r` +
    + + ${p} + ${_t.chevronDown} + + ${d?r`
    ${d}
    `:g} +
    + ${$.map(([P,L])=>ye({schema:L,value:h[P],path:[...s,P],hints:i,unsupported:a,disabled:o,onPatch:c}))} + ${M?Ia({schema:T,value:h,path:s,hints:i,unsupported:a,disabled:o,reservedKeys:k,onPatch:c}):g} +
    +
    + `}function Ip(e){const{schema:t,value:n,path:s,hints:i,unsupported:a,disabled:o,onPatch:c}=e,l=e.showLabel??!0,p=te(s,i),d=p?.label??t.title??we(String(s.at(-1))),u=p?.help??t.description,h=Array.isArray(t.items)?t.items[0]:t.items;if(!h)return r` +
    +
    ${d}
    +
    Unsupported array schema. Use Raw mode.
    +
    + `;const v=Array.isArray(n)?n:Array.isArray(t.default)?t.default:[];return r` +
    +
    + ${l?r`${d}`:g} + ${v.length} item${v.length!==1?"s":""} + +
    + ${u?r`
    ${u}
    `:g} + + ${v.length===0?r` +
    + No items yet. Click "Add" to create one. +
    + `:r` +
    + ${v.map((w,$)=>r` +
    +
    + #${$+1} + +
    +
    + ${ye({schema:h,value:w,path:[...s,$],hints:i,unsupported:a,disabled:o,showLabel:!1,onPatch:c})} +
    +
    + `)} +
    + `} +
    + `}function Ia(e){const{schema:t,value:n,path:s,hints:i,unsupported:a,disabled:o,reservedKeys:c,onPatch:l}=e,p=Cp(t),d=Object.entries(n??{}).filter(([u])=>!c.has(u));return r` +
    +
    + Custom entries + +
    + + ${d.length===0?r` +
    No custom entries.
    + `:r` +
    + ${d.map(([u,h])=>{const v=[...s,u],w=Ep(h);return r` +
    +
    + {const k=$.target.value.trim();if(!k||k===u)return;const T={...n??{}};k in T||(T[k]=T[u],delete T[u],l(s,T))}} + /> +
    +
    + ${p?r` + + `:ye({schema:t,value:h,path:v,hints:i,unsupported:a,disabled:o,showLabel:!1,onPatch:l})} +
    + +
    + `})} +
    + `} +
    + `}const Ra={env:r``,update:r``,agents:r``,auth:r``,channels:r``,messages:r``,commands:r``,hooks:r``,skills:r``,tools:r``,gateway:r``,wizard:r``,meta:r``,logging:r``,browser:r``,ui:r``,models:r``,bindings:r``,broadcast:r``,audio:r``,session:r``,cron:r``,web:r``,discovery:r``,canvasHost:r``,talk:r``,plugins:r``,default:r``},ci={env:{label:"Environment Variables",description:"Environment variables passed to the gateway process"},update:{label:"Updates",description:"Auto-update settings and release channel"},agents:{label:"Agents",description:"Agent configurations, models, and identities"},auth:{label:"Authentication",description:"API keys and authentication profiles"},channels:{label:"Channels",description:"Messaging channels (Telegram, Discord, Slack, etc.)"},messages:{label:"Messages",description:"Message handling and routing settings"},commands:{label:"Commands",description:"Custom slash commands"},hooks:{label:"Hooks",description:"Webhooks and event hooks"},skills:{label:"Skills",description:"Skill packs and capabilities"},tools:{label:"Tools",description:"Tool configurations (browser, search, etc.)"},gateway:{label:"Gateway",description:"Gateway server settings (port, auth, binding)"},wizard:{label:"Setup Wizard",description:"Setup wizard state and history"},meta:{label:"Metadata",description:"Gateway metadata and version information"},logging:{label:"Logging",description:"Log levels and output configuration"},browser:{label:"Browser",description:"Browser automation settings"},ui:{label:"UI",description:"User interface preferences"},models:{label:"Models",description:"AI model configurations and providers"},bindings:{label:"Bindings",description:"Key bindings and shortcuts"},broadcast:{label:"Broadcast",description:"Broadcast and notification settings"},audio:{label:"Audio",description:"Audio input/output settings"},session:{label:"Session",description:"Session management and persistence"},cron:{label:"Cron",description:"Scheduled tasks and automation"},web:{label:"Web",description:"Web server and API settings"},discovery:{label:"Discovery",description:"Service discovery and networking"},canvasHost:{label:"Canvas Host",description:"Canvas rendering and display"},talk:{label:"Talk",description:"Voice and speech settings"},plugins:{label:"Plugins",description:"Plugin management and extensions"}};function Pa(e){return Ra[e]??Ra.default}function Rp(e,t,n){if(!n)return!0;const s=n.toLowerCase(),i=ci[e];return e.toLowerCase().includes(s)||i&&(i.label.toLowerCase().includes(s)||i.description.toLowerCase().includes(s))?!0:mt(t,s)}function mt(e,t){if(e.title?.toLowerCase().includes(t)||e.description?.toLowerCase().includes(t)||e.enum?.some(s=>String(s).toLowerCase().includes(t)))return!0;if(e.properties){for(const[s,i]of Object.entries(e.properties))if(s.toLowerCase().includes(t)||mt(i,t))return!0}if(e.items){const s=Array.isArray(e.items)?e.items:[e.items];for(const i of s)if(i&&mt(i,t))return!0}if(e.additionalProperties&&typeof e.additionalProperties=="object"&&mt(e.additionalProperties,t))return!0;const n=e.anyOf??e.oneOf??e.allOf;if(n){for(const s of n)if(s&&mt(s,t))return!0}return!1}function Pp(e){if(!e.schema)return r`
    Schema unavailable.
    `;const t=e.schema,n=e.value??{};if(ue(t)!=="object"||!t.properties)return r`
    Unsupported schema. Use Raw.
    `;const s=new Set(e.unsupportedPaths??[]),i=t.properties,a=e.searchQuery??"",o=e.activeSection,c=e.activeSubsection??null,p=Object.entries(i).sort((u,h)=>{const v=te([u[0]],e.uiHints)?.order??50,w=te([h[0]],e.uiHints)?.order??50;return v!==w?v-w:u[0].localeCompare(h[0])}).filter(([u,h])=>!(o&&u!==o||a&&!Rp(u,h,a)));let d=null;if(o&&c&&p.length===1){const u=p[0]?.[1];u&&ue(u)==="object"&&u.properties&&u.properties[c]&&(d={sectionKey:o,subsectionKey:c,schema:u.properties[c]})}return p.length===0?r` +
    +
    ${Q.search}
    +
    + ${a?`No settings match "${a}"`:"No settings in this section"} +
    +
    + `:r` +
    + ${d?(()=>{const{sectionKey:u,subsectionKey:h,schema:v}=d,w=te([u,h],e.uiHints),$=w?.label??v.title??we(h),k=w?.help??v.description??"",T=n[u],M=T&&typeof T=="object"?T[h]:void 0,P=`config-section-${u}-${h}`;return r` +
    +
    + ${Pa(u)} +
    +

    ${$}

    + ${k?r`

    ${k}

    `:g} +
    +
    +
    + ${ye({schema:v,value:M,path:[u,h],hints:e.uiHints,unsupported:s,disabled:e.disabled??!1,showLabel:!1,onPatch:e.onPatch})} +
    +
    + `})():p.map(([u,h])=>{const v=ci[u]??{label:u.charAt(0).toUpperCase()+u.slice(1),description:h.description??""};return r` +
    +
    + ${Pa(u)} +
    +

    ${v.label}

    + ${v.description?r`

    ${v.description}

    `:g} +
    +
    +
    + ${ye({schema:h,value:n[u],path:[u],hints:e.uiHints,unsupported:s,disabled:e.disabled??!1,showLabel:!1,onPatch:e.onPatch})} +
    +
    + `})} +
    + `}const Np=new Set(["title","description","default","nullable"]);function Op(e){return Object.keys(e??{}).filter(n=>!Np.has(n)).length===0}function ur(e){const t=e.filter(i=>i!=null),n=t.length!==e.length,s=[];for(const i of t)s.some(a=>Object.is(a,i))||s.push(i);return{enumValues:s,nullable:n}}function pr(e){return!e||typeof e!="object"?{schema:null,unsupportedPaths:[""]}:wt(e,[])}function wt(e,t){const n=new Set,s={...e},i=bn(t)||"";if(e.anyOf||e.oneOf||e.allOf){const c=Dp(e,t);return c||{schema:e,unsupportedPaths:[i]}}const a=Array.isArray(e.type)&&e.type.includes("null"),o=ue(e)??(e.properties||e.additionalProperties?"object":void 0);if(s.type=o??e.type,s.nullable=a||e.nullable,s.enum){const{enumValues:c,nullable:l}=ur(s.enum);s.enum=c,l&&(s.nullable=!0),c.length===0&&n.add(i)}if(o==="object"){const c=e.properties??{},l={};for(const[p,d]of Object.entries(c)){const u=wt(d,[...t,p]);u.schema&&(l[p]=u.schema);for(const h of u.unsupportedPaths)n.add(h)}if(s.properties=l,e.additionalProperties===!0)n.add(i);else if(e.additionalProperties===!1)s.additionalProperties=!1;else if(e.additionalProperties&&typeof e.additionalProperties=="object"&&!Op(e.additionalProperties)){const p=wt(e.additionalProperties,[...t,"*"]);s.additionalProperties=p.schema??e.additionalProperties,p.unsupportedPaths.length>0&&n.add(i)}}else if(o==="array"){const c=Array.isArray(e.items)?e.items[0]:e.items;if(!c)n.add(i);else{const l=wt(c,[...t,"*"]);s.items=l.schema??c,l.unsupportedPaths.length>0&&n.add(i)}}else o!=="string"&&o!=="number"&&o!=="integer"&&o!=="boolean"&&!s.enum&&n.add(i);return{schema:s,unsupportedPaths:Array.from(n)}}function Dp(e,t){if(e.allOf)return null;const n=e.anyOf??e.oneOf;if(!n)return null;const s=[],i=[];let a=!1;for(const c of n){if(!c||typeof c!="object")return null;if(Array.isArray(c.enum)){const{enumValues:l,nullable:p}=ur(c.enum);s.push(...l),p&&(a=!0);continue}if("const"in c){if(c.const==null){a=!0;continue}s.push(c.const);continue}if(ue(c)==="null"){a=!0;continue}i.push(c)}if(s.length>0&&i.length===0){const c=[];for(const l of s)c.some(p=>Object.is(p,l))||c.push(l);return{schema:{...e,enum:c,nullable:a,anyOf:void 0,oneOf:void 0,allOf:void 0},unsupportedPaths:[]}}if(i.length===1){const c=wt(i[0],t);return c.schema&&(c.schema.nullable=a||c.schema.nullable),c}const o=["string","number","integer","boolean"];return i.length>0&&s.length===0&&i.every(c=>c.type&&o.includes(String(c.type)))?{schema:{...e,nullable:a},unsupportedPaths:[]}:null}const As={all:r``,env:r``,update:r``,agents:r``,auth:r``,channels:r``,messages:r``,commands:r``,hooks:r``,skills:r``,tools:r``,gateway:r``,wizard:r``,meta:r``,logging:r``,browser:r``,ui:r``,models:r``,bindings:r``,broadcast:r``,audio:r``,session:r``,cron:r``,web:r``,discovery:r``,canvasHost:r``,talk:r``,plugins:r``,default:r``},Na=[{key:"env",label:"Environment"},{key:"update",label:"Updates"},{key:"agents",label:"Agents"},{key:"auth",label:"Authentication"},{key:"channels",label:"Channels"},{key:"messages",label:"Messages"},{key:"commands",label:"Commands"},{key:"hooks",label:"Hooks"},{key:"skills",label:"Skills"},{key:"tools",label:"Tools"},{key:"gateway",label:"Gateway"},{key:"wizard",label:"Setup Wizard"}],Oa="__all__";function Da(e){return As[e]??As.default}function Bp(e,t){const n=ci[e];return n||{label:t?.title??we(e),description:t?.description??""}}function Fp(e){const{key:t,schema:n,uiHints:s}=e;if(!n||ue(n)!=="object"||!n.properties)return[];const i=Object.entries(n.properties).map(([a,o])=>{const c=te([t,a],s),l=c?.label??o.title??we(a),p=c?.help??o.description??"",d=c?.order??50;return{key:a,label:l,description:p,order:d}});return i.sort((a,o)=>a.order!==o.order?a.order-o.order:a.key.localeCompare(o.key)),i}function Up(e,t){if(!e||!t)return[];const n=[];function s(i,a,o){if(i===a)return;if(typeof i!=typeof a){n.push({path:o,from:i,to:a});return}if(typeof i!="object"||i===null||a===null){i!==a&&n.push({path:o,from:i,to:a});return}if(Array.isArray(i)&&Array.isArray(a)){JSON.stringify(i)!==JSON.stringify(a)&&n.push({path:o,from:i,to:a});return}const c=i,l=a,p=new Set([...Object.keys(c),...Object.keys(l)]);for(const d of p)s(c[d],l[d],o?`${o}.${d}`:d)}return s(e,t,""),n}function Ba(e,t=40){let n;try{n=JSON.stringify(e)??String(e)}catch{n=String(e)}return n.length<=t?n:n.slice(0,t-3)+"..."}function Kp(e){const t=e.valid==null?"unknown":e.valid?"valid":"invalid",n=pr(e.schema),s=n.schema?n.unsupportedPaths.length>0:!1,i=n.schema?.properties??{},a=Na.filter(E=>E.key in i),o=new Set(Na.map(E=>E.key)),c=Object.keys(i).filter(E=>!o.has(E)).map(E=>({key:E,label:E.charAt(0).toUpperCase()+E.slice(1)})),l=[...a,...c],p=e.activeSection&&n.schema&&ue(n.schema)==="object"?n.schema.properties?.[e.activeSection]:void 0,d=e.activeSection?Bp(e.activeSection,p):null,u=e.activeSection?Fp({key:e.activeSection,schema:p,uiHints:e.uiHints}):[],h=e.formMode==="form"&&!!e.activeSection&&u.length>0,v=e.activeSubsection===Oa,w=e.searchQuery||v?null:e.activeSubsection??u[0]?.key??null,$=e.formMode==="form"?Up(e.originalValue,e.formValue):[],k=e.formMode==="raw"&&e.raw!==e.originalRaw,T=e.formMode==="form"?$.length>0:k,M=!!e.formValue&&!e.loading&&!!n.schema,P=e.connected&&!e.saving&&T&&(e.formMode==="raw"?!0:M),L=e.connected&&!e.applying&&!e.updating&&T&&(e.formMode==="raw"?!0:M),C=e.connected&&!e.applying&&!e.updating;return r` +
    + + + + +
    + +
    +
    + ${T?r` + ${e.formMode==="raw"?"Unsaved changes":`${$.length} unsaved change${$.length!==1?"s":""}`} + `:r` + No changes + `} +
    +
    + + + + +
    +
    + + + ${T&&e.formMode==="form"?r` +
    + + View ${$.length} pending change${$.length!==1?"s":""} + + + + +
    + ${$.map(E=>r` +
    +
    ${E.path}
    +
    + ${Ba(E.from)} + + ${Ba(E.to)} +
    +
    + `)} +
    +
    + `:g} + + ${d&&e.formMode==="form"?r` +
    +
    ${Da(e.activeSection??"")}
    +
    +
    ${d.label}
    + ${d.description?r`
    ${d.description}
    `:g} +
    +
    + `:g} + + ${h?r` +
    + + ${u.map(E=>r` + + `)} +
    + `:g} + + +
    + ${e.formMode==="form"?r` + ${e.schemaLoading?r`
    +
    + Loading schema… +
    `:Pp({schema:n.schema,uiHints:e.uiHints,value:e.formValue,disabled:e.loading||!e.formValue,unsupportedPaths:n.unsupportedPaths,onPatch:e.onFormPatch,searchQuery:e.searchQuery,activeSection:e.activeSection,activeSubsection:w})} + ${s?r`
    + Form view can't safely edit some fields. + Use Raw to avoid losing config entries. +
    `:g} + `:r` + + `} +
    + + ${e.issues.length>0?r`
    +
    ${JSON.stringify(e.issues,null,2)}
    +
    `:g} +
    +
    + `}function Hp(e){if(!e&&e!==0)return"n/a";const t=Math.round(e/1e3);if(t<60)return`${t}s`;const n=Math.round(t/60);return n<60?`${n}m`:`${Math.round(n/60)}h`}function zp(e,t){const n=t.snapshot,s=n?.channels;if(!n||!s)return!1;const i=s[e],a=typeof i?.configured=="boolean"&&i.configured,o=typeof i?.running=="boolean"&&i.running,c=typeof i?.connected=="boolean"&&i.connected,p=(n.channelAccounts?.[e]??[]).some(d=>d.configured||d.running||d.connected);return a||o||c||p}function jp(e,t){return t?.[e]?.length??0}function fr(e,t){const n=jp(e,t);return n<2?g:r``}function qp(e,t){let n=e;for(const s of t){if(!n)return null;const i=ue(n);if(i==="object"){const a=n.properties??{};if(typeof s=="string"&&a[s]){n=a[s];continue}const o=n.additionalProperties;if(typeof s=="string"&&o&&typeof o=="object"){n=o;continue}return null}if(i==="array"){if(typeof s!="number")return null;n=(Array.isArray(n.items)?n.items[0]:n.items)??null;continue}return null}return n}function Vp(e,t){const s=(e.channels??{})[t],i=e[t];return(s&&typeof s=="object"?s:null)??(i&&typeof i=="object"?i:null)??{}}function Wp(e){const t=pr(e.schema),n=t.schema;if(!n)return r`
    Schema unavailable. Use Raw.
    `;const s=qp(n,["channels",e.channelId]);if(!s)return r`
    Channel config schema unavailable.
    `;const i=e.configValue??{},a=Vp(i,e.channelId);return r` +
    + ${ye({schema:s,value:a,path:["channels",e.channelId],hints:e.uiHints,unsupported:new Set(t.unsupportedPaths),disabled:e.disabled,showLabel:!1,onPatch:e.onPatch})} +
    + `}function $e(e){const{channelId:t,props:n}=e,s=n.configSaving||n.configSchemaLoading;return r` +
    + ${n.configSchemaLoading?r`
    Loading config schema…
    `:Wp({channelId:t,configValue:n.configForm,schema:n.configSchema,uiHints:n.configUiHints,disabled:s,onPatch:n.onConfigPatch})} +
    + + +
    +
    + `}function Gp(e){const{props:t,discord:n,accountCountLabel:s}=e;return r` +
    +
    Discord
    +
    Bot status and channel configuration.
    + ${s} + +
    +
    + Configured + ${n?.configured?"Yes":"No"} +
    +
    + Running + ${n?.running?"Yes":"No"} +
    +
    + Last start + ${n?.lastStartAt?O(n.lastStartAt):"n/a"} +
    +
    + Last probe + ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} +
    +
    + + ${n?.lastError?r`
    + ${n.lastError} +
    `:g} + + ${n?.probe?r`
    + Probe ${n.probe.ok?"ok":"failed"} · + ${n.probe.status??""} ${n.probe.error??""} +
    `:g} + + ${$e({channelId:"discord",props:t})} + +
    + +
    +
    + `}function Yp(e){const{props:t,googleChat:n,accountCountLabel:s}=e;return r` +
    +
    Google Chat
    +
    Chat API webhook status and channel configuration.
    + ${s} + +
    +
    + Configured + ${n?n.configured?"Yes":"No":"n/a"} +
    +
    + Running + ${n?n.running?"Yes":"No":"n/a"} +
    +
    + Credential + ${n?.credentialSource??"n/a"} +
    +
    + Audience + + ${n?.audienceType?`${n.audienceType}${n.audience?` · ${n.audience}`:""}`:"n/a"} + +
    +
    + Last start + ${n?.lastStartAt?O(n.lastStartAt):"n/a"} +
    +
    + Last probe + ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} +
    +
    + + ${n?.lastError?r`
    + ${n.lastError} +
    `:g} + + ${n?.probe?r`
    + Probe ${n.probe.ok?"ok":"failed"} · + ${n.probe.status??""} ${n.probe.error??""} +
    `:g} + + ${$e({channelId:"googlechat",props:t})} + +
    + +
    +
    + `}function Qp(e){const{props:t,imessage:n,accountCountLabel:s}=e;return r` +
    +
    iMessage
    +
    macOS bridge status and channel configuration.
    + ${s} + +
    +
    + Configured + ${n?.configured?"Yes":"No"} +
    +
    + Running + ${n?.running?"Yes":"No"} +
    +
    + Last start + ${n?.lastStartAt?O(n.lastStartAt):"n/a"} +
    +
    + Last probe + ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} +
    +
    + + ${n?.lastError?r`
    + ${n.lastError} +
    `:g} + + ${n?.probe?r`
    + Probe ${n.probe.ok?"ok":"failed"} · + ${n.probe.error??""} +
    `:g} + + ${$e({channelId:"imessage",props:t})} + +
    + +
    +
    + `}function Zp(e){const{values:t,original:n}=e;return t.name!==n.name||t.displayName!==n.displayName||t.about!==n.about||t.picture!==n.picture||t.banner!==n.banner||t.website!==n.website||t.nip05!==n.nip05||t.lud16!==n.lud16}function Jp(e){const{state:t,callbacks:n,accountId:s}=e,i=Zp(t),a=(c,l,p={})=>{const{type:d="text",placeholder:u,maxLength:h,help:v}=p,w=t.values[c]??"",$=t.fieldErrors[c],k=`nostr-profile-${c}`;return d==="textarea"?r` +
    + + + ${v?r`
    ${v}
    `:g} + ${$?r`
    ${$}
    `:g} +
    + `:r` +
    + + {const M=T.target;n.onFieldChange(c,M.value)}} + ?disabled=${t.saving} + /> + ${v?r`
    ${v}
    `:g} + ${$?r`
    ${$}
    `:g} +
    + `},o=()=>{const c=t.values.picture;return c?r` +
    + Profile picture preview{const p=l.target;p.style.display="none"}} + @load=${l=>{const p=l.target;p.style.display="block"}} + /> +
    + `:g};return r` +
    +
    +
    Edit Profile
    +
    Account: ${s}
    +
    + + ${t.error?r`
    ${t.error}
    `:g} + + ${t.success?r`
    ${t.success}
    `:g} + + ${o()} + + ${a("name","Username",{placeholder:"satoshi",maxLength:256,help:"Short username (e.g., satoshi)"})} + + ${a("displayName","Display Name",{placeholder:"Satoshi Nakamoto",maxLength:256,help:"Your full display name"})} + + ${a("about","Bio",{type:"textarea",placeholder:"Tell people about yourself...",maxLength:2e3,help:"A brief bio or description"})} + + ${a("picture","Avatar URL",{type:"url",placeholder:"https://example.com/avatar.jpg",help:"HTTPS URL to your profile picture"})} + + ${t.showAdvanced?r` +
    +
    Advanced
    + + ${a("banner","Banner URL",{type:"url",placeholder:"https://example.com/banner.jpg",help:"HTTPS URL to a banner image"})} + + ${a("website","Website",{type:"url",placeholder:"https://example.com",help:"Your personal website"})} + + ${a("nip05","NIP-05 Identifier",{placeholder:"you@example.com",help:"Verifiable identifier (e.g., you@domain.com)"})} + + ${a("lud16","Lightning Address",{placeholder:"you@getalby.com",help:"Lightning address for tips (LUD-16)"})} +
    + `:g} + +
    + + + + + + + +
    + + ${i?r`
    + You have unsaved changes +
    `:g} +
    + `}function Xp(e){const t={name:e?.name??"",displayName:e?.displayName??"",about:e?.about??"",picture:e?.picture??"",banner:e?.banner??"",website:e?.website??"",nip05:e?.nip05??"",lud16:e?.lud16??""};return{values:t,original:{...t},saving:!1,importing:!1,error:null,success:null,fieldErrors:{},showAdvanced:!!(e?.banner||e?.website||e?.nip05||e?.lud16)}}function Fa(e){return e?e.length<=20?e:`${e.slice(0,8)}...${e.slice(-8)}`:"n/a"}function ef(e){const{props:t,nostr:n,nostrAccounts:s,accountCountLabel:i,profileFormState:a,profileFormCallbacks:o,onEditProfile:c}=e,l=s[0],p=n?.configured??l?.configured??!1,d=n?.running??l?.running??!1,u=n?.publicKey??l?.publicKey,h=n?.lastStartAt??l?.lastStartAt??null,v=n?.lastError??l?.lastError??null,w=s.length>1,$=a!=null,k=M=>{const P=M.publicKey,L=M.profile,C=L?.displayName??L?.name??M.name??M.accountId;return r` + + `},T=()=>{if($&&o)return Jp({state:a,callbacks:o,accountId:s[0]?.accountId??"default"});const M=l?.profile??n?.profile,{name:P,displayName:L,about:C,picture:E,nip05:pe}=M??{},yn=P||L||C||E||pe;return r` +
    +
    +
    Profile
    + ${p?r` + + `:g} +
    + ${yn?r` +
    + ${E?r` +
    + Profile picture{wn.target.style.display="none"}} + /> +
    + `:g} + ${P?r`
    Name${P}
    `:g} + ${L?r`
    Display Name${L}
    `:g} + ${C?r`
    About${C}
    `:g} + ${pe?r`
    NIP-05${pe}
    `:g} +
    + `:r` +
    + No profile set. Click "Edit Profile" to add your name, bio, and avatar. +
    + `} +
    + `};return r` +
    +
    Nostr
    +
    Decentralized DMs via Nostr relays (NIP-04).
    + ${i} + + ${w?r` + + `:r` +
    +
    + Configured + ${p?"Yes":"No"} +
    +
    + Running + ${d?"Yes":"No"} +
    +
    + Public Key + ${Fa(u)} +
    +
    + Last start + ${h?O(h):"n/a"} +
    +
    + `} + + ${v?r`
    ${v}
    `:g} + + ${T()} + + ${$e({channelId:"nostr",props:t})} + +
    + +
    +
    + `}function tf(e){const{props:t,signal:n,accountCountLabel:s}=e;return r` +
    +
    Signal
    +
    signal-cli status and channel configuration.
    + ${s} + +
    +
    + Configured + ${n?.configured?"Yes":"No"} +
    +
    + Running + ${n?.running?"Yes":"No"} +
    +
    + Base URL + ${n?.baseUrl??"n/a"} +
    +
    + Last start + ${n?.lastStartAt?O(n.lastStartAt):"n/a"} +
    +
    + Last probe + ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} +
    +
    + + ${n?.lastError?r`
    + ${n.lastError} +
    `:g} + + ${n?.probe?r`
    + Probe ${n.probe.ok?"ok":"failed"} · + ${n.probe.status??""} ${n.probe.error??""} +
    `:g} + + ${$e({channelId:"signal",props:t})} + +
    + +
    +
    + `}function nf(e){const{props:t,slack:n,accountCountLabel:s}=e;return r` +
    +
    Slack
    +
    Socket mode status and channel configuration.
    + ${s} + +
    +
    + Configured + ${n?.configured?"Yes":"No"} +
    +
    + Running + ${n?.running?"Yes":"No"} +
    +
    + Last start + ${n?.lastStartAt?O(n.lastStartAt):"n/a"} +
    +
    + Last probe + ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} +
    +
    + + ${n?.lastError?r`
    + ${n.lastError} +
    `:g} + + ${n?.probe?r`
    + Probe ${n.probe.ok?"ok":"failed"} · + ${n.probe.status??""} ${n.probe.error??""} +
    `:g} + + ${$e({channelId:"slack",props:t})} + +
    + +
    +
    + `}function sf(e){const{props:t,telegram:n,telegramAccounts:s,accountCountLabel:i}=e,a=s.length>1,o=c=>{const p=c.probe?.bot?.username,d=c.name||c.accountId;return r` + + `};return r` +
    +
    Telegram
    +
    Bot status and channel configuration.
    + ${i} + + ${a?r` + + `:r` +
    +
    + Configured + ${n?.configured?"Yes":"No"} +
    +
    + Running + ${n?.running?"Yes":"No"} +
    +
    + Mode + ${n?.mode??"n/a"} +
    +
    + Last start + ${n?.lastStartAt?O(n.lastStartAt):"n/a"} +
    +
    + Last probe + ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} +
    +
    + `} + + ${n?.lastError?r`
    + ${n.lastError} +
    `:g} + + ${n?.probe?r`
    + Probe ${n.probe.ok?"ok":"failed"} · + ${n.probe.status??""} ${n.probe.error??""} +
    `:g} + + ${$e({channelId:"telegram",props:t})} + +
    + +
    +
    + `}function af(e){const{props:t,whatsapp:n,accountCountLabel:s}=e;return r` +
    +
    WhatsApp
    +
    Link WhatsApp Web and monitor connection health.
    + ${s} + +
    +
    + Configured + ${n?.configured?"Yes":"No"} +
    +
    + Linked + ${n?.linked?"Yes":"No"} +
    +
    + Running + ${n?.running?"Yes":"No"} +
    +
    + Connected + ${n?.connected?"Yes":"No"} +
    +
    + Last connect + + ${n?.lastConnectedAt?O(n.lastConnectedAt):"n/a"} + +
    +
    + Last message + + ${n?.lastMessageAt?O(n.lastMessageAt):"n/a"} + +
    +
    + Auth age + + ${n?.authAgeMs!=null?Hp(n.authAgeMs):"n/a"} + +
    +
    + + ${n?.lastError?r`
    + ${n.lastError} +
    `:g} + + ${t.whatsappMessage?r`
    + ${t.whatsappMessage} +
    `:g} + + ${t.whatsappQrDataUrl?r`
    + WhatsApp QR +
    `:g} + +
    + + + + + +
    + + ${$e({channelId:"whatsapp",props:t})} +
    + `}function of(e){const t=e.snapshot?.channels,n=t?.whatsapp??void 0,s=t?.telegram??void 0,i=t?.discord??null;t?.googlechat;const a=t?.slack??null,o=t?.signal??null,c=t?.imessage??null,l=t?.nostr??null,d=rf(e.snapshot).map((u,h)=>({key:u,enabled:zp(u,e),order:h})).sort((u,h)=>u.enabled!==h.enabled?u.enabled?-1:1:u.order-h.order);return r` +
    + ${d.map(u=>lf(u.key,e,{whatsapp:n,telegram:s,discord:i,slack:a,signal:o,imessage:c,nostr:l,channelAccounts:e.snapshot?.channelAccounts??null}))} +
    + +
    +
    +
    +
    Channel health
    +
    Channel status snapshots from the gateway.
    +
    +
    ${e.lastSuccessAt?O(e.lastSuccessAt):"n/a"}
    +
    + ${e.lastError?r`
    + ${e.lastError} +
    `:g} +
    +${e.snapshot?JSON.stringify(e.snapshot,null,2):"No snapshot yet."}
    +      
    +
    + `}function rf(e){return e?.channelMeta?.length?e.channelMeta.map(t=>t.id):e?.channelOrder?.length?e.channelOrder:["whatsapp","telegram","discord","googlechat","slack","signal","imessage","nostr"]}function lf(e,t,n){const s=fr(e,n.channelAccounts);switch(e){case"whatsapp":return af({props:t,whatsapp:n.whatsapp,accountCountLabel:s});case"telegram":return sf({props:t,telegram:n.telegram,telegramAccounts:n.channelAccounts?.telegram??[],accountCountLabel:s});case"discord":return Gp({props:t,discord:n.discord,accountCountLabel:s});case"googlechat":return Yp({props:t,accountCountLabel:s});case"slack":return nf({props:t,slack:n.slack,accountCountLabel:s});case"signal":return tf({props:t,signal:n.signal,accountCountLabel:s});case"imessage":return Qp({props:t,imessage:n.imessage,accountCountLabel:s});case"nostr":{const i=n.channelAccounts?.nostr??[],a=i[0],o=a?.accountId??"default",c=a?.profile??null,l=t.nostrProfileAccountId===o?t.nostrProfileFormState:null,p=l?{onFieldChange:t.onNostrProfileFieldChange,onSave:t.onNostrProfileSave,onImport:t.onNostrProfileImport,onCancel:t.onNostrProfileCancel,onToggleAdvanced:t.onNostrProfileToggleAdvanced}:null;return ef({props:t,nostr:n.nostr,nostrAccounts:i,accountCountLabel:s,profileFormState:l,profileFormCallbacks:p,onEditProfile:()=>t.onNostrProfileEdit(o,c)})}default:return cf(e,t,n.channelAccounts??{})}}function cf(e,t,n){const s=uf(t.snapshot,e),i=t.snapshot?.channels?.[e],a=typeof i?.configured=="boolean"?i.configured:void 0,o=typeof i?.running=="boolean"?i.running:void 0,c=typeof i?.connected=="boolean"?i.connected:void 0,l=typeof i?.lastError=="string"?i.lastError:void 0,p=n[e]??[],d=fr(e,n);return r` +
    +
    ${s}
    +
    Channel status and configuration.
    + ${d} + + ${p.length>0?r` + + `:r` +
    +
    + Configured + ${a==null?"n/a":a?"Yes":"No"} +
    +
    + Running + ${o==null?"n/a":o?"Yes":"No"} +
    +
    + Connected + ${c==null?"n/a":c?"Yes":"No"} +
    +
    + `} + + ${l?r`
    + ${l} +
    `:g} + + ${$e({channelId:e,props:t})} +
    + `}function df(e){return e?.channelMeta?.length?Object.fromEntries(e.channelMeta.map(t=>[t.id,t])):{}}function uf(e,t){return df(e)[t]?.label??e?.channelLabels?.[t]??t}const pf=600*1e3;function hr(e){return e.lastInboundAt?Date.now()-e.lastInboundAt
    + `:r` +
    + Auth failed. Re-copy a tokenized URL with + clawdbot dashboard --no-open, or update the token, + then click Connect. + +
    + `})(),a=(()=>{if(e.connected||!e.lastError||(typeof window<"u"?window.isSecureContext:!0)!==!1)return null;const c=e.lastError.toLowerCase();return!c.includes("secure context")&&!c.includes("device identity required")?null:r` +
    + This page is HTTP, so the browser blocks device identity. Use HTTPS (Tailscale Serve) or + open http://127.0.0.1:18789 on the gateway host. +
    + If you must stay on HTTP, set + gateway.controlUi.allowInsecureAuth: true (token-only). +
    + +
    + `})();return r` +
    +
    +
    Gateway Access
    +
    Where the dashboard connects and how it authenticates.
    +
    + + + + +
    +
    + + + Click Connect to apply connection changes. +
    +
    + +
    +
    Snapshot
    +
    Latest gateway handshake information.
    +
    +
    +
    Status
    +
    + ${e.connected?"Connected":"Disconnected"} +
    +
    +
    +
    Uptime
    +
    ${n}
    +
    +
    +
    Tick Interval
    +
    ${s}
    +
    +
    +
    Last Channels Refresh
    +
    + ${e.lastChannelsRefresh?O(e.lastChannelsRefresh):"n/a"} +
    +
    +
    + ${e.lastError?r`
    +
    ${e.lastError}
    + ${i??""} + ${a??""} +
    `:r`
    + Use Channels to link WhatsApp, Telegram, Discord, Signal, or iMessage. +
    `} +
    +
    + +
    +
    +
    Instances
    +
    ${e.presenceCount}
    +
    Presence beacons in the last 5 minutes.
    +
    +
    +
    Sessions
    +
    ${e.sessionsCount??"n/a"}
    +
    Recent session keys tracked by the gateway.
    +
    +
    +
    Cron
    +
    + ${e.cronEnabled==null?"n/a":e.cronEnabled?"Enabled":"Disabled"} +
    +
    Next wake ${gr(e.cronNext)}
    +
    +
    + +
    +
    Notes
    +
    Quick reminders for remote control setups.
    +
    +
    +
    Tailscale serve
    +
    + Prefer serve mode to keep the gateway on loopback with tailnet auth. +
    +
    +
    +
    Session hygiene
    +
    Use /new or sessions.patch to reset context.
    +
    +
    +
    Cron reminders
    +
    Use isolated sessions for recurring runs.
    +
    +
    +
    + `}const rh=["","off","minimal","low","medium","high"],lh=["","off","on"],ch=[{value:"",label:"inherit"},{value:"off",label:"off (explicit)"},{value:"on",label:"on"}],dh=["","off","on","stream"];function uh(e){if(!e)return"";const t=e.trim().toLowerCase();return t==="z.ai"||t==="z-ai"?"zai":t}function vr(e){return uh(e)==="zai"}function ph(e){return vr(e)?lh:rh}function fh(e,t){return!t||!e||e==="off"?e:"on"}function hh(e,t){return e?t&&e==="on"?"low":e:null}function gh(e){const t=e.result?.sessions??[];return r` +
    +
    +
    +
    Sessions
    +
    Active session keys and per-session overrides.
    +
    + +
    + +
    + + + + +
    + + ${e.error?r`
    ${e.error}
    `:g} + +
    + ${e.result?`Store: ${e.result.path}`:""} +
    + +
    +
    +
    Key
    +
    Label
    +
    Kind
    +
    Updated
    +
    Tokens
    +
    Thinking
    +
    Verbose
    +
    Reasoning
    +
    Actions
    +
    + ${t.length===0?r`
    No sessions found.
    `:t.map(n=>vh(n,e.basePath,e.onPatch,e.onDelete,e.loading))} +
    +
    + `}function vh(e,t,n,s,i){const a=e.updatedAt?O(e.updatedAt):"n/a",o=e.thinkingLevel??"",c=vr(e.modelProvider),l=fh(o,c),p=ph(e.modelProvider),d=e.verboseLevel??"",u=e.reasoningLevel??"",h=e.displayName??e.key,v=e.kind!=="global",w=v?`${Rs("chat",t)}?session=${encodeURIComponent(e.key)}`:null;return r` +
    +
    ${v?r`${h}`:h}
    +
    + {const k=$.target.value.trim();n(e.key,{label:k||null})}} + /> +
    +
    ${e.kind}
    +
    ${a}
    +
    ${bf(e)}
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + `}function mh(e){const t=Math.max(0,e),n=Math.floor(t/1e3);if(n<60)return`${n}s`;const s=Math.floor(n/60);return s<60?`${s}m`:`${Math.floor(s/60)}h`}function Ie(e,t){return t?r`
    ${e}${t}
    `:g}function bh(e){const t=e.execApprovalQueue[0];if(!t)return g;const n=t.request,s=t.expiresAtMs-Date.now(),i=s>0?`expires in ${mh(s)}`:"expired",a=e.execApprovalQueue.length;return r` + + `}function yh(e){const t=e.report?.skills??[],n=e.filter.trim().toLowerCase(),s=n?t.filter(i=>[i.name,i.description,i.source].join(" ").toLowerCase().includes(n)):t;return r` +
    +
    +
    +
    Skills
    +
    Bundled, managed, and workspace skills.
    +
    + +
    + +
    + +
    ${s.length} shown
    +
    + + ${e.error?r`
    ${e.error}
    `:g} + + ${s.length===0?r`
    No skills found.
    `:r` +
    + ${s.map(i=>wh(i,e))} +
    + `} +
    + `}function wh(e,t){const n=t.busyKey===e.skillKey,s=t.edits[e.skillKey]??"",i=t.messages[e.skillKey]??null,a=e.install.length>0&&e.missing.bins.length>0,o=[...e.missing.bins.map(l=>`bin:${l}`),...e.missing.env.map(l=>`env:${l}`),...e.missing.config.map(l=>`config:${l}`),...e.missing.os.map(l=>`os:${l}`)],c=[];return e.disabled&&c.push("disabled"),e.blockedByAllowlist&&c.push("blocked by allowlist"),r` +
    +
    +
    + ${e.emoji?`${e.emoji} `:""}${e.name} +
    +
    ${as(e.description,140)}
    +
    + ${e.source} + + ${e.eligible?"eligible":"blocked"} + + ${e.disabled?r`disabled`:g} +
    + ${o.length>0?r` +
    + Missing: ${o.join(", ")} +
    + `:g} + ${c.length>0?r` +
    + Reason: ${c.join(", ")} +
    + `:g} +
    +
    +
    + + ${a?r``:g} +
    + ${i?r`
    + ${i.message} +
    `:g} + ${e.primaryEnv?r` +
    + API key + t.onEdit(e.skillKey,l.target.value)} + /> +
    + + `:g} +
    +
    + `}function $h(e,t){const n=Rs(t,e.basePath);return r` + {s.defaultPrevented||s.button!==0||s.metaKey||s.ctrlKey||s.shiftKey||s.altKey||(s.preventDefault(),e.setTab(t))}} + title=${ss(t)} + > + + ${ss(t)} + + `}function xh(e){const t=kh(e.sessionKey,e.sessionsResult),n=e.onboarding,s=e.onboarding,i=e.onboarding?!1:e.settings.chatShowThinking,a=e.onboarding?!0:e.settings.chatFocusMode,o=r``,c=r``;return r` +
    + + + | + + +
    + `}function kh(e,t){const n=new Set,s=[],i=t?.sessions?.find(a=>a.key===e);if(n.add(e),s.push({key:e,displayName:i?.displayName}),t?.sessions)for(const a of t.sessions)n.has(a.key)||(n.add(a.key),s.push({key:a.key,displayName:a.displayName}));return s}const Ah=["system","light","dark"];function Sh(e){const t=Math.max(0,Ah.indexOf(e.theme)),n=s=>i=>{const o={element:i.currentTarget};(i.clientX||i.clientY)&&(o.pointerClientX=i.clientX,o.pointerClientY=i.clientY),e.setTheme(s,o)};return r` +
    +
    + + + + +
    +
    + `}function _h(){return r` + + `}function Th(){return r` + + `}function Ch(){return r` + + `}const Eh=/^data:/i,Lh=/^https?:\/\//i;function Mh(e){const t=e.agentsList?.agents??[],s=eo(e.sessionKey)?.agentId??e.agentsList?.defaultId??"main",a=t.find(c=>c.id===s)?.identity,o=a?.avatarUrl??a?.avatar;if(o)return Eh.test(o)||Lh.test(o)?o:a?.avatarUrl}function Ih(e){const t=e.presenceEntries.length,n=e.sessionsResult?.count??null,s=e.cronStatus?.nextWakeAtMs??null,i=e.connected?null:"Disconnected from gateway.",a=e.tab==="chat",o=a&&(e.settings.chatFocusMode||e.onboarding),c=e.onboarding?!1:e.settings.chatShowThinking,l=Mh(e),p=e.chatAvatarUrl??l??null;return r` +
    +
    +
    + +
    + +
    +
    CLAWDBOT
    +
    Gateway Dashboard
    +
    +
    +
    +
    +
    + + Health + ${e.connected?"OK":"Offline"} +
    + ${Sh(e)} +
    +
    + +
    +
    +
    +
    ${ss(e.tab)}
    +
    ${ml(e.tab)}
    +
    +
    + ${e.lastError?r`
    ${e.lastError}
    `:g} + ${a?xh(e):g} +
    +
    + + ${e.tab==="overview"?oh({connected:e.connected,hello:e.hello,settings:e.settings,password:e.password,lastError:e.lastError,presenceCount:t,sessionsCount:n,cronEnabled:e.cronStatus?.enabled??null,cronNext:s,lastChannelsRefresh:e.channelsLastSuccess,onSettingsChange:d=>e.applySettings(d),onPasswordChange:d=>e.password=d,onSessionKeyChange:d=>{e.sessionKey=d,e.chatMessage="",e.resetToolStream(),e.applySettings({...e.settings,sessionKey:d,lastActiveSessionKey:d}),e.loadAssistantIdentity()},onConnect:()=>e.connect(),onRefresh:()=>e.loadOverview()}):g} + + ${e.tab==="channels"?of({connected:e.connected,loading:e.channelsLoading,snapshot:e.channelsSnapshot,lastError:e.channelsError,lastSuccessAt:e.channelsLastSuccess,whatsappMessage:e.whatsappLoginMessage,whatsappQrDataUrl:e.whatsappLoginQrDataUrl,whatsappConnected:e.whatsappLoginConnected,whatsappBusy:e.whatsappBusy,configSchema:e.configSchema,configSchemaLoading:e.configSchemaLoading,configForm:e.configForm,configUiHints:e.configUiHints,configSaving:e.configSaving,configFormDirty:e.configFormDirty,nostrProfileFormState:e.nostrProfileFormState,nostrProfileAccountId:e.nostrProfileAccountId,onRefresh:d=>oe(e,d),onWhatsAppStart:d=>e.handleWhatsAppStart(d),onWhatsAppWait:()=>e.handleWhatsAppWait(),onWhatsAppLogout:()=>e.handleWhatsAppLogout(),onConfigPatch:(d,u)=>Bt(e,d,u),onConfigSave:()=>e.handleChannelConfigSave(),onConfigReload:()=>e.handleChannelConfigReload(),onNostrProfileEdit:(d,u)=>e.handleNostrProfileEdit(d,u),onNostrProfileCancel:()=>e.handleNostrProfileCancel(),onNostrProfileFieldChange:(d,u)=>e.handleNostrProfileFieldChange(d,u),onNostrProfileSave:()=>e.handleNostrProfileSave(),onNostrProfileImport:()=>e.handleNostrProfileImport(),onNostrProfileToggleAdvanced:()=>e.handleNostrProfileToggleAdvanced()}):g} + + ${e.tab==="instances"?Lf({loading:e.presenceLoading,entries:e.presenceEntries,lastError:e.presenceError,statusMessage:e.presenceStatus,onRefresh:()=>js(e)}):g} + + ${e.tab==="sessions"?gh({loading:e.sessionsLoading,result:e.sessionsResult,error:e.sessionsError,activeMinutes:e.sessionsFilterActive,limit:e.sessionsFilterLimit,includeGlobal:e.sessionsIncludeGlobal,includeUnknown:e.sessionsIncludeUnknown,basePath:e.basePath,onFiltersChange:d=>{e.sessionsFilterActive=d.activeMinutes,e.sessionsFilterLimit=d.limit,e.sessionsIncludeGlobal=d.includeGlobal,e.sessionsIncludeUnknown=d.includeUnknown},onRefresh:()=>st(e),onPatch:(d,u)=>Ml(e,d,u),onDelete:d=>Il(e,d)}):g} + + ${e.tab==="cron"?Sf({loading:e.cronLoading,status:e.cronStatus,jobs:e.cronJobs,error:e.cronError,busy:e.cronBusy,form:e.cronForm,channels:e.channelsSnapshot?.channelMeta?.length?e.channelsSnapshot.channelMeta.map(d=>d.id):e.channelsSnapshot?.channelOrder??[],channelLabels:e.channelsSnapshot?.channelLabels??{},channelMeta:e.channelsSnapshot?.channelMeta??[],runsJobId:e.cronRunsJobId,runs:e.cronRuns,onFormChange:d=>e.cronForm={...e.cronForm,...d},onRefresh:()=>e.loadCron(),onAdd:()=>ec(e),onToggle:(d,u)=>tc(e,d,u),onRun:d=>nc(e,d),onRemove:d=>sc(e,d),onLoadRuns:d=>po(e,d)}):g} + + ${e.tab==="skills"?yh({loading:e.skillsLoading,report:e.skillsReport,error:e.skillsError,filter:e.skillsFilter,edits:e.skillEdits,messages:e.skillMessages,busyKey:e.skillsBusyKey,onFilterChange:d=>e.skillsFilter=d,onRefresh:()=>Ct(e,{clearMessages:!0}),onToggle:(d,u)=>Zc(e,d,u),onEdit:(d,u)=>Qc(e,d,u),onSaveKey:d=>Jc(e,d),onInstall:(d,u,h)=>Xc(e,d,u,h)}):g} + + ${e.tab==="nodes"?Nf({loading:e.nodesLoading,nodes:e.nodes,devicesLoading:e.devicesLoading,devicesError:e.devicesError,devicesList:e.devicesList,configForm:e.configForm??e.configSnapshot?.config,configLoading:e.configLoading,configSaving:e.configSaving,configDirty:e.configFormDirty,configFormMode:e.configFormMode,execApprovalsLoading:e.execApprovalsLoading,execApprovalsSaving:e.execApprovalsSaving,execApprovalsDirty:e.execApprovalsDirty,execApprovalsSnapshot:e.execApprovalsSnapshot,execApprovalsForm:e.execApprovalsForm,execApprovalsSelectedAgent:e.execApprovalsSelectedAgent,execApprovalsTarget:e.execApprovalsTarget,execApprovalsTargetNodeId:e.execApprovalsTargetNodeId,onRefresh:()=>pn(e),onDevicesRefresh:()=>Te(e),onDeviceApprove:d=>Uc(e,d),onDeviceReject:d=>Kc(e,d),onDeviceRotate:(d,u,h)=>Hc(e,{deviceId:d,role:u,scopes:h}),onDeviceRevoke:(d,u)=>zc(e,{deviceId:d,role:u}),onLoadConfig:()=>be(e),onLoadExecApprovals:()=>{const d=e.execApprovalsTarget==="node"&&e.execApprovalsTargetNodeId?{kind:"node",nodeId:e.execApprovalsTargetNodeId}:{kind:"gateway"};return zs(e,d)},onBindDefault:d=>{d?Bt(e,["tools","exec","node"],d):Zi(e,["tools","exec","node"])},onBindAgent:(d,u)=>{const h=["agents","list",d,"tools","exec","node"];u?Bt(e,h,u):Zi(e,h)},onSaveBindings:()=>ls(e),onExecApprovalsTargetChange:(d,u)=>{e.execApprovalsTarget=d,e.execApprovalsTargetNodeId=u,e.execApprovalsSnapshot=null,e.execApprovalsForm=null,e.execApprovalsDirty=!1,e.execApprovalsSelectedAgent=null},onExecApprovalsSelectAgent:d=>{e.execApprovalsSelectedAgent=d},onExecApprovalsPatch:(d,u)=>Gc(e,d,u),onExecApprovalsRemove:d=>Yc(e,d),onSaveExecApprovals:()=>{const d=e.execApprovalsTarget==="node"&&e.execApprovalsTargetNodeId?{kind:"node",nodeId:e.execApprovalsTargetNodeId}:{kind:"gateway"};return Wc(e,d)}}):g} + + ${e.tab==="chat"?kp({sessionKey:e.sessionKey,onSessionKeyChange:d=>{e.sessionKey=d,e.chatMessage="",e.chatStream=null,e.chatStreamStartedAt=null,e.chatRunId=null,e.chatQueue=[],e.resetToolStream(),e.resetChatScroll(),e.applySettings({...e.settings,sessionKey:d,lastActiveSessionKey:d}),e.loadAssistantIdentity(),Xe(e),fs(e)},thinkingLevel:e.chatThinkingLevel,showThinking:c,loading:e.chatLoading,sending:e.chatSending,compactionStatus:e.compactionStatus,assistantAvatarUrl:p,messages:e.chatMessages,toolMessages:e.chatToolMessages,stream:e.chatStream,streamStartedAt:e.chatStreamStartedAt,draft:e.chatMessage,queue:e.chatQueue,connected:e.connected,canSend:e.connected,disabledReason:i,error:e.lastError,sessions:e.sessionsResult,focusMode:o,onRefresh:()=>(e.resetToolStream(),Promise.all([Xe(e),fs(e)])),onToggleFocusMode:()=>{e.onboarding||e.applySettings({...e.settings,chatFocusMode:!e.settings.chatFocusMode})},onChatScroll:d=>e.handleChatScroll(d),onDraftChange:d=>e.chatMessage=d,onSend:()=>e.handleSendChat(),canAbort:!!e.chatRunId,onAbort:()=>{e.handleAbortChat()},onQueueRemove:d=>e.removeQueuedMessage(d),onNewSession:()=>e.handleSendChat("/new",{restoreDraft:!0}),sidebarOpen:e.sidebarOpen,sidebarContent:e.sidebarContent,sidebarError:e.sidebarError,splitRatio:e.splitRatio,onOpenSidebar:d=>e.handleOpenSidebar(d),onCloseSidebar:()=>e.handleCloseSidebar(),onSplitRatioChange:d=>e.handleSplitRatioChange(d),assistantName:e.assistantName,assistantAvatar:e.assistantAvatar}):g} + + ${e.tab==="config"?Kp({raw:e.configRaw,originalRaw:e.configRawOriginal,valid:e.configValid,issues:e.configIssues,loading:e.configLoading,saving:e.configSaving,applying:e.configApplying,updating:e.updateRunning,connected:e.connected,schema:e.configSchema,schemaLoading:e.configSchemaLoading,uiHints:e.configUiHints,formMode:e.configFormMode,formValue:e.configForm,originalValue:e.configFormOriginal,searchQuery:e.configSearchQuery,activeSection:e.configActiveSection,activeSubsection:e.configActiveSubsection,onRawChange:d=>{e.configRaw=d},onFormModeChange:d=>e.configFormMode=d,onFormPatch:(d,u)=>Bt(e,d,u),onSearchChange:d=>e.configSearchQuery=d,onSectionChange:d=>{e.configActiveSection=d,e.configActiveSubsection=null},onSubsectionChange:d=>e.configActiveSubsection=d,onReload:()=>be(e),onSave:()=>ls(e),onApply:()=>Ql(e),onUpdate:()=>Zl(e)}):g} + + ${e.tab==="debug"?Ef({loading:e.debugLoading,status:e.debugStatus,health:e.debugHealth,models:e.debugModels,heartbeat:e.debugHeartbeat,eventLog:e.eventLog,callMethod:e.debugCallMethod,callParams:e.debugCallParams,callResult:e.debugCallResult,callError:e.debugCallError,onCallMethodChange:d=>e.debugCallMethod=d,onCallParamsChange:d=>e.debugCallParams=d,onRefresh:()=>dn(e),onCall:()=>rc(e)}):g} + + ${e.tab==="logs"?Pf({loading:e.logsLoading,error:e.logsError,file:e.logsFile,entries:e.logsEntries,filterText:e.logsFilterText,levelFilters:e.logsLevelFilters,autoFollow:e.logsAutoFollow,truncated:e.logsTruncated,onFilterTextChange:d=>e.logsFilterText=d,onLevelToggle:(d,u)=>{e.logsLevelFilters={...e.logsLevelFilters,[d]:u}},onToggleAutoFollow:d=>e.logsAutoFollow=d,onRefresh:()=>Os(e,{reset:!0}),onExport:(d,u)=>e.exportLogs(d,u),onScroll:d=>e.handleLogsScroll(d)}):g} +
    + ${bh(e)} +
    + `}const Rh={trace:!0,debug:!0,info:!0,warn:!0,error:!0,fatal:!0},Ph={name:"",description:"",agentId:"",enabled:!0,scheduleKind:"every",scheduleAt:"",everyAmount:"30",everyUnit:"minutes",cronExpr:"0 7 * * *",cronTz:"",sessionTarget:"main",wakeMode:"next-heartbeat",payloadKind:"systemEvent",payloadText:"",deliver:!1,channel:"last",to:"",timeoutSeconds:"",postToMainPrefix:""};async function Nh(e){if(!(!e.client||!e.connected)&&!e.agentsLoading){e.agentsLoading=!0,e.agentsError=null;try{const t=await e.client.request("agents.list",{});t&&(e.agentsList=t)}catch(t){e.agentsError=String(t)}finally{e.agentsLoading=!1}}}const mr={WEBCHAT_UI:"webchat-ui",CONTROL_UI:"clawdbot-control-ui",WEBCHAT:"webchat",CLI:"cli",GATEWAY_CLIENT:"gateway-client",MACOS_APP:"clawdbot-macos",IOS_APP:"clawdbot-ios",ANDROID_APP:"clawdbot-android",NODE_HOST:"node-host",TEST:"test",FINGERPRINT:"fingerprint",PROBE:"clawdbot-probe"},za=mr,Ss={WEBCHAT:"webchat",CLI:"cli",UI:"ui",BACKEND:"backend",NODE:"node",PROBE:"probe",TEST:"test"};new Set(Object.values(mr));new Set(Object.values(Ss));function Oh(e){const t=e.version??(e.nonce?"v2":"v1"),n=e.scopes.join(","),s=e.token??"",i=[t,e.deviceId,e.clientId,e.clientMode,e.role,n,String(e.signedAtMs),s];return t==="v2"&&i.push(e.nonce??""),i.join("|")}const Dh=4008;class Bh{constructor(t){this.opts=t,this.ws=null,this.pending=new Map,this.closed=!1,this.lastSeq=null,this.connectNonce=null,this.connectSent=!1,this.connectTimer=null,this.backoffMs=800}start(){this.closed=!1,this.connect()}stop(){this.closed=!0,this.ws?.close(),this.ws=null,this.flushPending(new Error("gateway client stopped"))}get connected(){return this.ws?.readyState===WebSocket.OPEN}connect(){this.closed||(this.ws=new WebSocket(this.opts.url),this.ws.onopen=()=>this.queueConnect(),this.ws.onmessage=t=>this.handleMessage(String(t.data??"")),this.ws.onclose=t=>{const n=String(t.reason??"");this.ws=null,this.flushPending(new Error(`gateway closed (${t.code}): ${n}`)),this.opts.onClose?.({code:t.code,reason:n}),this.scheduleReconnect()},this.ws.onerror=()=>{})}scheduleReconnect(){if(this.closed)return;const t=this.backoffMs;this.backoffMs=Math.min(this.backoffMs*1.7,15e3),window.setTimeout(()=>this.connect(),t)}flushPending(t){for(const[,n]of this.pending)n.reject(t);this.pending.clear()}async sendConnect(){if(this.connectSent)return;this.connectSent=!0,this.connectTimer!==null&&(window.clearTimeout(this.connectTimer),this.connectTimer=null);const t=typeof crypto<"u"&&!!crypto.subtle,n=["operator.admin","operator.approvals","operator.pairing"],s="operator";let i=null,a=!1,o=this.opts.token;if(t){i=await Us();const d=Fc({deviceId:i.deviceId,role:s})?.token;o=d??this.opts.token,a=!!(d&&this.opts.token)}const c=o||this.opts.password?{token:o,password:this.opts.password}:void 0;let l;if(t&&i){const d=Date.now(),u=this.connectNonce??void 0,h=Oh({deviceId:i.deviceId,clientId:this.opts.clientName??za.CONTROL_UI,clientMode:this.opts.mode??Ss.WEBCHAT,role:s,scopes:n,signedAtMs:d,token:o??null,nonce:u}),v=await Dc(i.privateKey,h);l={id:i.deviceId,publicKey:i.publicKey,signature:v,signedAt:d,nonce:u}}const p={minProtocol:3,maxProtocol:3,client:{id:this.opts.clientName??za.CONTROL_UI,version:this.opts.clientVersion??"dev",platform:this.opts.platform??navigator.platform??"web",mode:this.opts.mode??Ss.WEBCHAT,instanceId:this.opts.instanceId},role:s,scopes:n,device:l,caps:[],auth:c,userAgent:navigator.userAgent,locale:navigator.language};this.request("connect",p).then(d=>{d?.auth?.deviceToken&&i&&Eo({deviceId:i.deviceId,role:d.auth.role??s,token:d.auth.deviceToken,scopes:d.auth.scopes??[]}),this.backoffMs=800,this.opts.onHello?.(d)}).catch(()=>{a&&i&&Lo({deviceId:i.deviceId,role:s}),this.ws?.close(Dh,"connect failed")})}handleMessage(t){let n;try{n=JSON.parse(t)}catch{return}const s=n;if(s.type==="event"){const i=n;if(i.event==="connect.challenge"){const o=i.payload,c=o&&typeof o.nonce=="string"?o.nonce:null;c&&(this.connectNonce=c,this.sendConnect());return}const a=typeof i.seq=="number"?i.seq:null;a!==null&&(this.lastSeq!==null&&a>this.lastSeq+1&&this.opts.onGap?.({expected:this.lastSeq+1,received:a}),this.lastSeq=a);try{this.opts.onEvent?.(i)}catch(o){console.error("[gateway] event handler error:",o)}return}if(s.type==="res"){const i=n,a=this.pending.get(i.id);if(!a)return;this.pending.delete(i.id),i.ok?a.resolve(i.payload):a.reject(new Error(i.error?.message??"request failed"));return}}request(t,n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return Promise.reject(new Error("gateway not connected"));const s=Ps(),i={type:"req",id:s,method:t,params:n},a=new Promise((o,c)=>{this.pending.set(s,{resolve:l=>o(l),reject:c})});return this.ws.send(JSON.stringify(i)),a}queueConnect(){this.connectNonce=null,this.connectSent=!1,this.connectTimer!==null&&window.clearTimeout(this.connectTimer),this.connectTimer=window.setTimeout(()=>{this.sendConnect()},750)}}function _s(e){return typeof e=="object"&&e!==null}function Fh(e){if(!_s(e))return null;const t=typeof e.id=="string"?e.id.trim():"",n=e.request;if(!t||!_s(n))return null;const s=typeof n.command=="string"?n.command.trim():"";if(!s)return null;const i=typeof e.createdAtMs=="number"?e.createdAtMs:0,a=typeof e.expiresAtMs=="number"?e.expiresAtMs:0;return!i||!a?null:{id:t,request:{command:s,cwd:typeof n.cwd=="string"?n.cwd:null,host:typeof n.host=="string"?n.host:null,security:typeof n.security=="string"?n.security:null,ask:typeof n.ask=="string"?n.ask:null,agentId:typeof n.agentId=="string"?n.agentId:null,resolvedPath:typeof n.resolvedPath=="string"?n.resolvedPath:null,sessionKey:typeof n.sessionKey=="string"?n.sessionKey:null},createdAtMs:i,expiresAtMs:a}}function Uh(e){if(!_s(e))return null;const t=typeof e.id=="string"?e.id.trim():"";return t?{id:t,decision:typeof e.decision=="string"?e.decision:null,resolvedBy:typeof e.resolvedBy=="string"?e.resolvedBy:null,ts:typeof e.ts=="number"?e.ts:null}:null}function br(e){const t=Date.now();return e.filter(n=>n.expiresAtMs>t)}function Kh(e,t){const n=br(e).filter(s=>s.id!==t.id);return n.push(t),n}function ja(e,t){return br(e).filter(n=>n.id!==t)}async function yr(e,t){if(!e.client||!e.connected)return;const n=e.sessionKey.trim(),s=n?{sessionKey:n}:{};try{const i=await e.client.request("agent.identity.get",s);if(!i)return;const a=ns(i);e.assistantName=a.name,e.assistantAvatar=a.avatar,e.assistantAgentId=a.agentId??null}catch{}}function Xn(e,t){const n=(e??"").trim(),s=t.mainSessionKey?.trim();if(!s)return n;if(!n)return s;const i=t.mainKey?.trim()||"main",a=t.defaultAgentId?.trim();return n==="main"||n===i||a&&(n===`agent:${a}:main`||n===`agent:${a}:${i}`)?s:n}function Hh(e,t){if(!t?.mainSessionKey)return;const n=Xn(e.sessionKey,t),s=Xn(e.settings.sessionKey,t),i=Xn(e.settings.lastActiveSessionKey,t),a=n||s||e.sessionKey,o={...e.settings,sessionKey:s||a,lastActiveSessionKey:i||a},c=o.sessionKey!==e.settings.sessionKey||o.lastActiveSessionKey!==e.settings.lastActiveSessionKey;a!==e.sessionKey&&(e.sessionKey=a),c&&ke(e,o)}function wr(e){e.lastError=null,e.hello=null,e.connected=!1,e.execApprovalQueue=[],e.execApprovalError=null,e.client?.stop(),e.client=new Bh({url:e.settings.gatewayUrl,token:e.settings.token.trim()?e.settings.token:void 0,password:e.password.trim()?e.password:void 0,clientName:"clawdbot-control-ui",mode:"webchat",onHello:t=>{e.connected=!0,e.lastError=null,e.hello=t,qh(e,t),yr(e),Nh(e),pn(e,{quiet:!0}),Te(e,{quiet:!0}),Qs(e)},onClose:({code:t,reason:n})=>{e.connected=!1,t!==1012&&(e.lastError=`disconnected (${t}): ${n||"no reason"}`)},onEvent:t=>zh(e,t),onGap:({expected:t,received:n})=>{e.lastError=`event gap detected (expected seq ${t}, got ${n}); refresh recommended`}}),e.client.start()}function zh(e,t){try{jh(e,t)}catch(n){console.error("[gateway] handleGatewayEvent error:",t.event,n)}}function jh(e,t){if(e.eventLogBuffer=[{ts:Date.now(),event:t.event,payload:t.payload},...e.eventLogBuffer].slice(0,250),e.tab==="debug"&&(e.eventLog=e.eventLogBuffer),t.event==="agent"){if(e.onboarding)return;Hl(e,t.payload);return}if(t.event==="chat"){const n=t.payload;n?.sessionKey&&Mo(e,n.sessionKey);const s=Ll(e,n);(s==="final"||s==="error"||s==="aborted")&&(Ns(e),$d(e)),s==="final"&&Xe(e);return}if(t.event==="presence"){const n=t.payload;n?.presence&&Array.isArray(n.presence)&&(e.presenceEntries=n.presence,e.presenceError=null,e.presenceStatus=null);return}if(t.event==="cron"&&e.tab==="cron"&&Zs(e),(t.event==="device.pair.requested"||t.event==="device.pair.resolved")&&Te(e,{quiet:!0}),t.event==="exec.approval.requested"){const n=Fh(t.payload);if(n){e.execApprovalQueue=Kh(e.execApprovalQueue,n),e.execApprovalError=null;const s=Math.max(0,n.expiresAtMs-Date.now()+500);window.setTimeout(()=>{e.execApprovalQueue=ja(e.execApprovalQueue,n.id)},s)}return}if(t.event==="exec.approval.resolved"){const n=Uh(t.payload);n&&(e.execApprovalQueue=ja(e.execApprovalQueue,n.id))}}function qh(e,t){const n=t.snapshot;n?.presence&&Array.isArray(n.presence)&&(e.presenceEntries=n.presence),n?.health&&(e.debugHealth=n.health),n?.sessionDefaults&&Hh(e,n.sessionDefaults)}function Vh(e){e.basePath=ld(),pd(e,!0),cd(e),dd(e),window.addEventListener("popstate",e.popStateHandler),ad(e),wr(e),sd(e),e.tab==="logs"&&Vs(e),e.tab==="debug"&&Gs(e)}function Wh(e){Wl(e)}function Gh(e){window.removeEventListener("popstate",e.popStateHandler),id(e),Ws(e),Ys(e),ud(e),e.topbarObserver?.disconnect(),e.topbarObserver=null}function Yh(e,t){if(e.tab==="chat"&&(t.has("chatMessages")||t.has("chatToolMessages")||t.has("chatStream")||t.has("chatLoading")||t.has("tab"))){const n=t.has("tab"),s=t.has("chatLoading")&&t.get("chatLoading")===!0&&e.chatLoading===!1;ln(e,n||s||!e.chatHasAutoScrolled)}e.tab==="logs"&&(t.has("logsEntries")||t.has("logsAutoFollow")||t.has("tab"))&&e.logsAutoFollow&&e.logsAtBottom&&ro(e,t.has("tab")||t.has("logsAutoFollow"))}async function Qh(e,t){await ic(e,t),await oe(e,!0)}async function Zh(e){await ac(e),await oe(e,!0)}async function Jh(e){await oc(e),await oe(e,!0)}async function Xh(e){await ls(e),await be(e),await oe(e,!0)}async function eg(e){await be(e),await oe(e,!0)}function tg(e){if(!Array.isArray(e))return{};const t={};for(const n of e){if(typeof n!="string")continue;const[s,...i]=n.split(":");if(!s||i.length===0)continue;const a=s.trim(),o=i.join(":").trim();a&&o&&(t[a]=o)}return t}function $r(e){return(e.channelsSnapshot?.channelAccounts?.nostr??[])[0]?.accountId??e.nostrProfileAccountId??"default"}function xr(e,t=""){return`/api/channels/nostr/${encodeURIComponent(e)}/profile${t}`}function ng(e,t,n){e.nostrProfileAccountId=t,e.nostrProfileFormState=Xp(n??void 0)}function sg(e){e.nostrProfileFormState=null,e.nostrProfileAccountId=null}function ig(e,t,n){const s=e.nostrProfileFormState;s&&(e.nostrProfileFormState={...s,values:{...s.values,[t]:n},fieldErrors:{...s.fieldErrors,[t]:""}})}function ag(e){const t=e.nostrProfileFormState;t&&(e.nostrProfileFormState={...t,showAdvanced:!t.showAdvanced})}async function og(e){const t=e.nostrProfileFormState;if(!t||t.saving)return;const n=$r(e);e.nostrProfileFormState={...t,saving:!0,error:null,success:null,fieldErrors:{}};try{const s=await fetch(xr(n),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(t.values)}),i=await s.json().catch(()=>null);if(!s.ok||i?.ok===!1||!i){const a=i?.error??`Profile update failed (${s.status})`;e.nostrProfileFormState={...t,saving:!1,error:a,success:null,fieldErrors:tg(i?.details)};return}if(!i.persisted){e.nostrProfileFormState={...t,saving:!1,error:"Profile publish failed on all relays.",success:null};return}e.nostrProfileFormState={...t,saving:!1,error:null,success:"Profile published to relays.",fieldErrors:{},original:{...t.values}},await oe(e,!0)}catch(s){e.nostrProfileFormState={...t,saving:!1,error:`Profile update failed: ${String(s)}`,success:null}}}async function rg(e){const t=e.nostrProfileFormState;if(!t||t.importing)return;const n=$r(e);e.nostrProfileFormState={...t,importing:!0,error:null,success:null};try{const s=await fetch(xr(n,"/import"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({autoMerge:!0})}),i=await s.json().catch(()=>null);if(!s.ok||i?.ok===!1||!i){const l=i?.error??`Profile import failed (${s.status})`;e.nostrProfileFormState={...t,importing:!1,error:l,success:null};return}const a=i.merged??i.imported??null,o=a?{...t.values,...a}:t.values,c=!!(o.banner||o.website||o.nip05||o.lud16);e.nostrProfileFormState={...t,importing:!1,values:o,error:null,success:i.saved?"Profile imported from relays. Review and publish.":"Profile imported. Review and publish.",showAdvanced:c},i.saved&&await oe(e,!0)}catch(s){e.nostrProfileFormState={...t,importing:!1,error:`Profile import failed: ${String(s)}`,success:null}}}var lg=Object.defineProperty,cg=Object.getOwnPropertyDescriptor,b=(e,t,n,s)=>{for(var i=s>1?void 0:s?cg(t,n):t,a=e.length-1,o;a>=0;a--)(o=e[a])&&(i=(s?o(t,n,i):o(i))||i);return s&&i&&lg(t,n,i),i};const es=ul();function dg(){if(!window.location.search)return!1;const t=new URLSearchParams(window.location.search).get("onboarding");if(!t)return!1;const n=t.trim().toLowerCase();return n==="1"||n==="true"||n==="yes"||n==="on"}let m=class extends Ze{constructor(){super(...arguments),this.settings=pl(),this.password="",this.tab="chat",this.onboarding=dg(),this.connected=!1,this.theme=this.settings.theme??"system",this.themeResolved="dark",this.hello=null,this.lastError=null,this.eventLog=[],this.eventLogBuffer=[],this.toolStreamSyncTimer=null,this.sidebarCloseTimer=null,this.assistantName=es.name,this.assistantAvatar=es.avatar,this.assistantAgentId=es.agentId??null,this.sessionKey=this.settings.sessionKey,this.chatLoading=!1,this.chatSending=!1,this.chatMessage="",this.chatMessages=[],this.chatToolMessages=[],this.chatStream=null,this.chatStreamStartedAt=null,this.chatRunId=null,this.compactionStatus=null,this.chatAvatarUrl=null,this.chatThinkingLevel=null,this.chatQueue=[],this.sidebarOpen=!1,this.sidebarContent=null,this.sidebarError=null,this.splitRatio=this.settings.splitRatio,this.nodesLoading=!1,this.nodes=[],this.devicesLoading=!1,this.devicesError=null,this.devicesList=null,this.execApprovalsLoading=!1,this.execApprovalsSaving=!1,this.execApprovalsDirty=!1,this.execApprovalsSnapshot=null,this.execApprovalsForm=null,this.execApprovalsSelectedAgent=null,this.execApprovalsTarget="gateway",this.execApprovalsTargetNodeId=null,this.execApprovalQueue=[],this.execApprovalBusy=!1,this.execApprovalError=null,this.configLoading=!1,this.configRaw=`{ +} +`,this.configRawOriginal="",this.configValid=null,this.configIssues=[],this.configSaving=!1,this.configApplying=!1,this.updateRunning=!1,this.applySessionKey=this.settings.lastActiveSessionKey,this.configSnapshot=null,this.configSchema=null,this.configSchemaVersion=null,this.configSchemaLoading=!1,this.configUiHints={},this.configForm=null,this.configFormOriginal=null,this.configFormDirty=!1,this.configFormMode="form",this.configSearchQuery="",this.configActiveSection=null,this.configActiveSubsection=null,this.channelsLoading=!1,this.channelsSnapshot=null,this.channelsError=null,this.channelsLastSuccess=null,this.whatsappLoginMessage=null,this.whatsappLoginQrDataUrl=null,this.whatsappLoginConnected=null,this.whatsappBusy=!1,this.nostrProfileFormState=null,this.nostrProfileAccountId=null,this.presenceLoading=!1,this.presenceEntries=[],this.presenceError=null,this.presenceStatus=null,this.agentsLoading=!1,this.agentsList=null,this.agentsError=null,this.sessionsLoading=!1,this.sessionsResult=null,this.sessionsError=null,this.sessionsFilterActive="",this.sessionsFilterLimit="120",this.sessionsIncludeGlobal=!0,this.sessionsIncludeUnknown=!1,this.cronLoading=!1,this.cronJobs=[],this.cronStatus=null,this.cronError=null,this.cronForm={...Ph},this.cronRunsJobId=null,this.cronRuns=[],this.cronBusy=!1,this.skillsLoading=!1,this.skillsReport=null,this.skillsError=null,this.skillsFilter="",this.skillEdits={},this.skillsBusyKey=null,this.skillMessages={},this.debugLoading=!1,this.debugStatus=null,this.debugHealth=null,this.debugModels=[],this.debugHeartbeat=null,this.debugCallMethod="",this.debugCallParams="{}",this.debugCallResult=null,this.debugCallError=null,this.logsLoading=!1,this.logsError=null,this.logsFile=null,this.logsEntries=[],this.logsFilterText="",this.logsLevelFilters={...Rh},this.logsAutoFollow=!0,this.logsTruncated=!1,this.logsCursor=null,this.logsLastFetchAt=null,this.logsLimit=500,this.logsMaxBytes=25e4,this.logsAtBottom=!0,this.client=null,this.chatScrollFrame=null,this.chatScrollTimeout=null,this.chatHasAutoScrolled=!1,this.chatUserNearBottom=!0,this.nodesPollInterval=null,this.logsPollInterval=null,this.debugPollInterval=null,this.logsScrollFrame=null,this.toolStreamById=new Map,this.toolStreamOrder=[],this.basePath="",this.popStateHandler=()=>fd(this),this.themeMedia=null,this.themeMediaHandler=null,this.topbarObserver=null}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),Vh(this)}firstUpdated(){Wh(this)}disconnectedCallback(){Gh(this),super.disconnectedCallback()}updated(e){Yh(this,e)}connect(){wr(this)}handleChatScroll(e){zl(this,e)}handleLogsScroll(e){jl(this,e)}exportLogs(e,t){Vl(e,t)}resetToolStream(){Ns(this)}resetChatScroll(){ql(this)}async loadAssistantIdentity(){await yr(this)}applySettings(e){ke(this,e)}setTab(e){od(this,e)}setTheme(e,t){rd(this,e,t)}async loadOverview(){await Po(this)}async loadCron(){await Zs(this)}async handleAbortChat(){await Oo(this)}removeQueuedMessage(e){bd(this,e)}async handleSendChat(e,t){await yd(this,e,t)}async handleWhatsAppStart(e){await Qh(this,e)}async handleWhatsAppWait(){await Zh(this)}async handleWhatsAppLogout(){await Jh(this)}async handleChannelConfigSave(){await Xh(this)}async handleChannelConfigReload(){await eg(this)}handleNostrProfileEdit(e,t){ng(this,e,t)}handleNostrProfileCancel(){sg(this)}handleNostrProfileFieldChange(e,t){ig(this,e,t)}async handleNostrProfileSave(){await og(this)}async handleNostrProfileImport(){await rg(this)}handleNostrProfileToggleAdvanced(){ag(this)}async handleExecApprovalDecision(e){const t=this.execApprovalQueue[0];if(!(!t||!this.client||this.execApprovalBusy)){this.execApprovalBusy=!0,this.execApprovalError=null;try{await this.client.request("exec.approval.resolve",{id:t.id,decision:e}),this.execApprovalQueue=this.execApprovalQueue.filter(n=>n.id!==t.id)}catch(n){this.execApprovalError=`Exec approval failed: ${String(n)}`}finally{this.execApprovalBusy=!1}}}handleOpenSidebar(e){this.sidebarCloseTimer!=null&&(window.clearTimeout(this.sidebarCloseTimer),this.sidebarCloseTimer=null),this.sidebarContent=e,this.sidebarError=null,this.sidebarOpen=!0}handleCloseSidebar(){this.sidebarOpen=!1,this.sidebarCloseTimer!=null&&window.clearTimeout(this.sidebarCloseTimer),this.sidebarCloseTimer=window.setTimeout(()=>{this.sidebarOpen||(this.sidebarContent=null,this.sidebarError=null,this.sidebarCloseTimer=null)},200)}handleSplitRatioChange(e){const t=Math.max(.4,Math.min(.7,e));this.splitRatio=t,this.applySettings({...this.settings,splitRatio:t})}render(){return Ih(this)}};b([y()],m.prototype,"settings",2);b([y()],m.prototype,"password",2);b([y()],m.prototype,"tab",2);b([y()],m.prototype,"onboarding",2);b([y()],m.prototype,"connected",2);b([y()],m.prototype,"theme",2);b([y()],m.prototype,"themeResolved",2);b([y()],m.prototype,"hello",2);b([y()],m.prototype,"lastError",2);b([y()],m.prototype,"eventLog",2);b([y()],m.prototype,"assistantName",2);b([y()],m.prototype,"assistantAvatar",2);b([y()],m.prototype,"assistantAgentId",2);b([y()],m.prototype,"sessionKey",2);b([y()],m.prototype,"chatLoading",2);b([y()],m.prototype,"chatSending",2);b([y()],m.prototype,"chatMessage",2);b([y()],m.prototype,"chatMessages",2);b([y()],m.prototype,"chatToolMessages",2);b([y()],m.prototype,"chatStream",2);b([y()],m.prototype,"chatStreamStartedAt",2);b([y()],m.prototype,"chatRunId",2);b([y()],m.prototype,"compactionStatus",2);b([y()],m.prototype,"chatAvatarUrl",2);b([y()],m.prototype,"chatThinkingLevel",2);b([y()],m.prototype,"chatQueue",2);b([y()],m.prototype,"sidebarOpen",2);b([y()],m.prototype,"sidebarContent",2);b([y()],m.prototype,"sidebarError",2);b([y()],m.prototype,"splitRatio",2);b([y()],m.prototype,"nodesLoading",2);b([y()],m.prototype,"nodes",2);b([y()],m.prototype,"devicesLoading",2);b([y()],m.prototype,"devicesError",2);b([y()],m.prototype,"devicesList",2);b([y()],m.prototype,"execApprovalsLoading",2);b([y()],m.prototype,"execApprovalsSaving",2);b([y()],m.prototype,"execApprovalsDirty",2);b([y()],m.prototype,"execApprovalsSnapshot",2);b([y()],m.prototype,"execApprovalsForm",2);b([y()],m.prototype,"execApprovalsSelectedAgent",2);b([y()],m.prototype,"execApprovalsTarget",2);b([y()],m.prototype,"execApprovalsTargetNodeId",2);b([y()],m.prototype,"execApprovalQueue",2);b([y()],m.prototype,"execApprovalBusy",2);b([y()],m.prototype,"execApprovalError",2);b([y()],m.prototype,"configLoading",2);b([y()],m.prototype,"configRaw",2);b([y()],m.prototype,"configRawOriginal",2);b([y()],m.prototype,"configValid",2);b([y()],m.prototype,"configIssues",2);b([y()],m.prototype,"configSaving",2);b([y()],m.prototype,"configApplying",2);b([y()],m.prototype,"updateRunning",2);b([y()],m.prototype,"applySessionKey",2);b([y()],m.prototype,"configSnapshot",2);b([y()],m.prototype,"configSchema",2);b([y()],m.prototype,"configSchemaVersion",2);b([y()],m.prototype,"configSchemaLoading",2);b([y()],m.prototype,"configUiHints",2);b([y()],m.prototype,"configForm",2);b([y()],m.prototype,"configFormOriginal",2);b([y()],m.prototype,"configFormDirty",2);b([y()],m.prototype,"configFormMode",2);b([y()],m.prototype,"configSearchQuery",2);b([y()],m.prototype,"configActiveSection",2);b([y()],m.prototype,"configActiveSubsection",2);b([y()],m.prototype,"channelsLoading",2);b([y()],m.prototype,"channelsSnapshot",2);b([y()],m.prototype,"channelsError",2);b([y()],m.prototype,"channelsLastSuccess",2);b([y()],m.prototype,"whatsappLoginMessage",2);b([y()],m.prototype,"whatsappLoginQrDataUrl",2);b([y()],m.prototype,"whatsappLoginConnected",2);b([y()],m.prototype,"whatsappBusy",2);b([y()],m.prototype,"nostrProfileFormState",2);b([y()],m.prototype,"nostrProfileAccountId",2);b([y()],m.prototype,"presenceLoading",2);b([y()],m.prototype,"presenceEntries",2);b([y()],m.prototype,"presenceError",2);b([y()],m.prototype,"presenceStatus",2);b([y()],m.prototype,"agentsLoading",2);b([y()],m.prototype,"agentsList",2);b([y()],m.prototype,"agentsError",2);b([y()],m.prototype,"sessionsLoading",2);b([y()],m.prototype,"sessionsResult",2);b([y()],m.prototype,"sessionsError",2);b([y()],m.prototype,"sessionsFilterActive",2);b([y()],m.prototype,"sessionsFilterLimit",2);b([y()],m.prototype,"sessionsIncludeGlobal",2);b([y()],m.prototype,"sessionsIncludeUnknown",2);b([y()],m.prototype,"cronLoading",2);b([y()],m.prototype,"cronJobs",2);b([y()],m.prototype,"cronStatus",2);b([y()],m.prototype,"cronError",2);b([y()],m.prototype,"cronForm",2);b([y()],m.prototype,"cronRunsJobId",2);b([y()],m.prototype,"cronRuns",2);b([y()],m.prototype,"cronBusy",2);b([y()],m.prototype,"skillsLoading",2);b([y()],m.prototype,"skillsReport",2);b([y()],m.prototype,"skillsError",2);b([y()],m.prototype,"skillsFilter",2);b([y()],m.prototype,"skillEdits",2);b([y()],m.prototype,"skillsBusyKey",2);b([y()],m.prototype,"skillMessages",2);b([y()],m.prototype,"debugLoading",2);b([y()],m.prototype,"debugStatus",2);b([y()],m.prototype,"debugHealth",2);b([y()],m.prototype,"debugModels",2);b([y()],m.prototype,"debugHeartbeat",2);b([y()],m.prototype,"debugCallMethod",2);b([y()],m.prototype,"debugCallParams",2);b([y()],m.prototype,"debugCallResult",2);b([y()],m.prototype,"debugCallError",2);b([y()],m.prototype,"logsLoading",2);b([y()],m.prototype,"logsError",2);b([y()],m.prototype,"logsFile",2);b([y()],m.prototype,"logsEntries",2);b([y()],m.prototype,"logsFilterText",2);b([y()],m.prototype,"logsLevelFilters",2);b([y()],m.prototype,"logsAutoFollow",2);b([y()],m.prototype,"logsTruncated",2);b([y()],m.prototype,"logsCursor",2);b([y()],m.prototype,"logsLastFetchAt",2);b([y()],m.prototype,"logsLimit",2);b([y()],m.prototype,"logsMaxBytes",2);b([y()],m.prototype,"logsAtBottom",2);m=b([Ja("clawdbot-app")],m); +//# sourceMappingURL=index-DQcOTEYz.js.map diff --git a/dist/control-ui/assets/index-DQcOTEYz.js.map b/dist/control-ui/assets/index-DQcOTEYz.js.map new file mode 100644 index 00000000000..d4822247282 --- /dev/null +++ b/dist/control-ui/assets/index-DQcOTEYz.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index-DQcOTEYz.js","sources":["../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/css-tag.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/reactive-element.js","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/lit-html.js","../../../node_modules/.pnpm/lit-element@4.2.2/node_modules/lit-element/lit-element.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/custom-element.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/property.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/state.js","../../../ui/src/ui/assistant-identity.ts","../../../ui/src/ui/storage.ts","../../../src/sessions/session-key-utils.ts","../../../ui/src/ui/navigation.ts","../../../ui/src/ui/icons.ts","../../../src/shared/text/reasoning-tags.ts","../../../ui/src/ui/format.ts","../../../ui/src/ui/chat/message-extract.ts","../../../ui/src/ui/uuid.ts","../../../ui/src/ui/controllers/chat.ts","../../../ui/src/ui/controllers/sessions.ts","../../../ui/src/ui/app-tool-stream.ts","../../../ui/src/ui/app-scroll.ts","../../../ui/src/ui/controllers/config/form-utils.ts","../../../ui/src/ui/controllers/config.ts","../../../ui/src/ui/controllers/cron.ts","../../../ui/src/ui/controllers/channels.ts","../../../ui/src/ui/controllers/debug.ts","../../../ui/src/ui/controllers/logs.ts","../../../node_modules/.pnpm/@noble+ed25519@3.0.0/node_modules/@noble/ed25519/index.js","../../../ui/src/ui/device-identity.ts","../../../ui/src/ui/device-auth.ts","../../../ui/src/ui/controllers/devices.ts","../../../ui/src/ui/controllers/nodes.ts","../../../ui/src/ui/controllers/exec-approvals.ts","../../../ui/src/ui/controllers/presence.ts","../../../ui/src/ui/controllers/skills.ts","../../../ui/src/ui/theme.ts","../../../ui/src/ui/theme-transition.ts","../../../ui/src/ui/app-polling.ts","../../../ui/src/ui/app-settings.ts","../../../ui/src/ui/app-chat.ts","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directive.js","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directive-helpers.js","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directives/repeat.js","../../../ui/src/ui/chat/message-normalizer.ts","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directives/unsafe-html.js","../../../node_modules/.pnpm/dompurify@3.3.1/node_modules/dompurify/dist/purify.es.mjs","../../../node_modules/.pnpm/marked@17.0.1/node_modules/marked/lib/marked.esm.js","../../../ui/src/ui/markdown.ts","../../../ui/src/ui/chat/copy-as-markdown.ts","../../../ui/src/ui/tool-display.ts","../../../ui/src/ui/chat/constants.ts","../../../ui/src/ui/chat/tool-helpers.ts","../../../ui/src/ui/chat/tool-cards.ts","../../../ui/src/ui/chat/grouped-render.ts","../../../ui/src/ui/views/markdown-sidebar.ts","../../../ui/src/ui/components/resizable-divider.ts","../../../ui/src/ui/views/chat.ts","../../../ui/src/ui/views/config-form.shared.ts","../../../ui/src/ui/views/config-form.node.ts","../../../ui/src/ui/views/config-form.render.ts","../../../ui/src/ui/views/config-form.analyze.ts","../../../ui/src/ui/views/config.ts","../../../ui/src/ui/views/channels.shared.ts","../../../ui/src/ui/views/channels.config.ts","../../../ui/src/ui/views/channels.discord.ts","../../../ui/src/ui/views/channels.googlechat.ts","../../../ui/src/ui/views/channels.imessage.ts","../../../ui/src/ui/views/channels.nostr-profile-form.ts","../../../ui/src/ui/views/channels.nostr.ts","../../../ui/src/ui/views/channels.signal.ts","../../../ui/src/ui/views/channels.slack.ts","../../../ui/src/ui/views/channels.telegram.ts","../../../ui/src/ui/views/channels.whatsapp.ts","../../../ui/src/ui/views/channels.ts","../../../ui/src/ui/presenter.ts","../../../ui/src/ui/views/cron.ts","../../../ui/src/ui/views/debug.ts","../../../ui/src/ui/views/instances.ts","../../../ui/src/ui/views/logs.ts","../../../ui/src/ui/views/nodes.ts","../../../ui/src/ui/views/overview.ts","../../../ui/src/ui/views/sessions.ts","../../../ui/src/ui/views/exec-approval.ts","../../../ui/src/ui/views/skills.ts","../../../ui/src/ui/app-render.helpers.ts","../../../ui/src/ui/app-render.ts","../../../ui/src/ui/app-defaults.ts","../../../ui/src/ui/controllers/agents.ts","../../../src/gateway/protocol/client-info.ts","../../../src/gateway/device-auth.ts","../../../ui/src/ui/gateway.ts","../../../ui/src/ui/controllers/exec-approval.ts","../../../ui/src/ui/controllers/assistant-identity.ts","../../../ui/src/ui/app-gateway.ts","../../../ui/src/ui/app-lifecycle.ts","../../../ui/src/ui/app-channels.ts","../../../ui/src/ui/app.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2019 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&\"adoptedStyleSheets\"in Document.prototype&&\"replace\"in CSSStyleSheet.prototype,s=Symbol(),o=new WeakMap;class n{constructor(t,e,o){if(this._$cssResult$=!0,o!==s)throw Error(\"CSSResult is not constructable. Use `unsafeCSS` or `css` instead.\");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const s=this.t;if(e&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=o.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&o.set(s,t))}return t}toString(){return this.cssText}}const r=t=>new n(\"string\"==typeof t?t:t+\"\",void 0,s),i=(t,...e)=>{const o=1===t.length?t[0]:e.reduce((e,s,o)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if(\"number\"==typeof t)return t;throw Error(\"Value passed to 'css' function must be a 'css' function result: \"+t+\". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.\")})(s)+t[o+1],t[0]);return new n(o,t,s)},S=(s,o)=>{if(e)s.adoptedStyleSheets=o.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of o){const o=document.createElement(\"style\"),n=t.litNonce;void 0!==n&&o.setAttribute(\"nonce\",n),o.textContent=e.cssText,s.appendChild(o)}},c=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e=\"\";for(const s of t.cssRules)e+=s.cssText;return r(e)})(t):t;export{n as CSSResult,S as adoptStyles,i as css,c as getCompatibleStyle,e as supportsAdoptingStyleSheets,r as unsafeCSS};\n//# sourceMappingURL=css-tag.js.map\n","import{getCompatibleStyle as t,adoptStyles as s}from\"./css-tag.js\";export{CSSResult,css,supportsAdoptingStyleSheets,unsafeCSS}from\"./css-tag.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const{is:i,defineProperty:e,getOwnPropertyDescriptor:h,getOwnPropertyNames:r,getOwnPropertySymbols:o,getPrototypeOf:n}=Object,a=globalThis,c=a.trustedTypes,l=c?c.emptyScript:\"\",p=a.reactiveElementPolyfillSupport,d=(t,s)=>t,u={toAttribute(t,s){switch(s){case Boolean:t=t?l:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,s){let i=t;switch(s){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},f=(t,s)=>!i(t,s),b={attribute:!0,type:String,converter:u,reflect:!1,useDefault:!1,hasChanged:f};Symbol.metadata??=Symbol(\"metadata\"),a.litPropertyMetadata??=new WeakMap;class y extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,s=b){if(s.state&&(s.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((s=Object.create(s)).wrapped=!0),this.elementProperties.set(t,s),!s.noAccessor){const i=Symbol(),h=this.getPropertyDescriptor(t,i,s);void 0!==h&&e(this.prototype,t,h)}}static getPropertyDescriptor(t,s,i){const{get:e,set:r}=h(this.prototype,t)??{get(){return this[s]},set(t){this[s]=t}};return{get:e,set(s){const h=e?.call(this);r?.call(this,s),this.requestUpdate(t,h,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(d(\"elementProperties\")))return;const t=n(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(d(\"finalized\")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(d(\"properties\"))){const t=this.properties,s=[...r(t),...o(t)];for(const i of s)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const s=litPropertyMetadata.get(t);if(void 0!==s)for(const[t,i]of s)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,s]of this.elementProperties){const i=this._$Eu(t,s);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(s){const i=[];if(Array.isArray(s)){const e=new Set(s.flat(1/0).reverse());for(const s of e)i.unshift(t(s))}else void 0!==s&&i.push(t(s));return i}static _$Eu(t,s){const i=s.attribute;return!1===i?void 0:\"string\"==typeof i?i:\"string\"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,s=this.constructor.elementProperties;for(const i of s.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return s(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,s,i){this._$AK(t,i)}_$ET(t,s){const i=this.constructor.elementProperties.get(t),e=this.constructor._$Eu(t,i);if(void 0!==e&&!0===i.reflect){const h=(void 0!==i.converter?.toAttribute?i.converter:u).toAttribute(s,i.type);this._$Em=t,null==h?this.removeAttribute(e):this.setAttribute(e,h),this._$Em=null}}_$AK(t,s){const i=this.constructor,e=i._$Eh.get(t);if(void 0!==e&&this._$Em!==e){const t=i.getPropertyOptions(e),h=\"function\"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:u;this._$Em=e;const r=h.fromAttribute(s,t.type);this[e]=r??this._$Ej?.get(e)??r,this._$Em=null}}requestUpdate(t,s,i,e=!1,h){if(void 0!==t){const r=this.constructor;if(!1===e&&(h=this[t]),i??=r.getPropertyOptions(t),!((i.hasChanged??f)(h,s)||i.useDefault&&i.reflect&&h===this._$Ej?.get(t)&&!this.hasAttribute(r._$Eu(t,i))))return;this.C(t,s,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,s,{useDefault:i,reflect:e,wrapped:h},r){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,r??s??this[t]),!0!==h||void 0!==r)||(this._$AL.has(t)||(this.hasUpdated||i||(s=void 0),this._$AL.set(t,s)),!0===e&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,s]of this._$Ep)this[t]=s;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[s,i]of t){const{wrapped:t}=i,e=this[s];!0!==t||this._$AL.has(s)||void 0===e||this.C(s,void 0,i,e)}}let t=!1;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(s)):this._$EM()}catch(s){throw t=!1,this._$EM(),s}t&&this._$AE(s)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}}y.elementStyles=[],y.shadowRootOptions={mode:\"open\"},y[d(\"elementProperties\")]=new Map,y[d(\"finalized\")]=new Map,p?.({ReactiveElement:y}),(a.reactiveElementVersions??=[]).push(\"2.1.2\");export{y as ReactiveElement,s as adoptStyles,u as defaultConverter,t as getCompatibleStyle,f as notEqual};\n//# sourceMappingURL=reactive-element.js.map\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=globalThis,i=t=>t,s=t.trustedTypes,e=s?s.createPolicy(\"lit-html\",{createHTML:t=>t}):void 0,h=\"$lit$\",o=`lit$${Math.random().toFixed(9).slice(2)}$`,n=\"?\"+o,r=`<${n}>`,l=document,c=()=>l.createComment(\"\"),a=t=>null===t||\"object\"!=typeof t&&\"function\"!=typeof t,u=Array.isArray,d=t=>u(t)||\"function\"==typeof t?.[Symbol.iterator],f=\"[ \\t\\n\\f\\r]\",v=/<(?:(!--|\\/[^a-zA-Z])|(\\/?[a-zA-Z][^>\\s]*)|(\\/?$))/g,_=/-->/g,m=/>/g,p=RegExp(`>|${f}(?:([^\\\\s\"'>=/]+)(${f}*=${f}*(?:[^ \\t\\n\\f\\r\"'\\`<>=]|(\"|')|))|$)`,\"g\"),g=/'/g,$=/\"/g,y=/^(?:script|style|textarea|title)$/i,x=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),b=x(1),w=x(2),T=x(3),E=Symbol.for(\"lit-noChange\"),A=Symbol.for(\"lit-nothing\"),C=new WeakMap,P=l.createTreeWalker(l,129);function V(t,i){if(!u(t)||!t.hasOwnProperty(\"raw\"))throw Error(\"invalid template strings array\");return void 0!==e?e.createHTML(i):i}const N=(t,i)=>{const s=t.length-1,e=[];let n,l=2===i?\"\":3===i?\"\":\"\",c=v;for(let i=0;i\"===u[0]?(c=n??v,d=-1):void 0===u[1]?d=-2:(d=c.lastIndex-u[2].length,a=u[1],c=void 0===u[3]?p:'\"'===u[3]?$:g):c===$||c===g?c=p:c===_||c===m?c=v:(c=p,n=void 0);const x=c===p&&t[i+1].startsWith(\"/>\")?\" \":\"\";l+=c===v?s+r:d>=0?(e.push(a),s.slice(0,d)+h+s.slice(d)+o+x):s+o+(-2===d?i:x)}return[V(t,l+(t[s]||\"\")+(2===i?\"\":3===i?\"\":\"\")),e]};class S{constructor({strings:t,_$litType$:i},e){let r;this.parts=[];let l=0,a=0;const u=t.length-1,d=this.parts,[f,v]=N(t,i);if(this.el=S.createElement(f,e),P.currentNode=this.el.content,2===i||3===i){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(r=P.nextNode())&&d.length0){r.textContent=s?s.emptyScript:\"\";for(let s=0;s2||\"\"!==s[0]||\"\"!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=A}_$AI(t,i=this,s,e){const h=this.strings;let o=!1;if(void 0===h)t=M(this,t,i,0),o=!a(t)||t!==this._$AH&&t!==E,o&&(this._$AH=t);else{const e=t;let n,r;for(t=h[0],n=0;n{const e=s?.renderBefore??i;let h=e._$litPart$;if(void 0===h){const t=s?.renderBefore??null;e._$litPart$=h=new k(i.insertBefore(c(),t),t,void 0,s??{})}return h._$AI(t),h};export{j as _$LH,b as html,T as mathml,E as noChange,A as nothing,D as render,w as svg};\n//# sourceMappingURL=lit-html.js.map\n","import{ReactiveElement as t}from\"@lit/reactive-element\";export*from\"@lit/reactive-element\";import{render as e,noChange as r}from\"lit-html\";export*from\"lit-html\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const s=globalThis;class i extends t{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const r=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=e(r,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return r}}i._$litElement$=!0,i[\"finalized\"]=!0,s.litElementHydrateSupport?.({LitElement:i});const o=s.litElementPolyfillSupport;o?.({LitElement:i});const n={_$AK:(t,e,r)=>{t._$AK(e,r)},_$AL:t=>t._$AL};(s.litElementVersions??=[]).push(\"4.2.2\");export{i as LitElement,n as _$LE};\n//# sourceMappingURL=lit-element.js.map\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=t=>(e,o)=>{void 0!==o?o.addInitializer(()=>{customElements.define(t,e)}):customElements.define(t,e)};export{t as customElement};\n//# sourceMappingURL=custom-element.js.map\n","import{notEqual as t,defaultConverter as e}from\"../reactive-element.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const o={attribute:!0,type:String,converter:e,reflect:!1,hasChanged:t},r=(t=o,e,r)=>{const{kind:n,metadata:i}=r;let s=globalThis.litPropertyMetadata.get(i);if(void 0===s&&globalThis.litPropertyMetadata.set(i,s=new Map),\"setter\"===n&&((t=Object.create(t)).wrapped=!0),s.set(r.name,t),\"accessor\"===n){const{name:o}=r;return{set(r){const n=e.get.call(this);e.set.call(this,r),this.requestUpdate(o,n,t,!0,r)},init(e){return void 0!==e&&this.C(o,void 0,t,e),e}}}if(\"setter\"===n){const{name:o}=r;return function(r){const n=this[o];e.call(this,r),this.requestUpdate(o,n,t,!0,r)}}throw Error(\"Unsupported decorator location: \"+n)};function n(t){return(e,o)=>\"object\"==typeof o?r(t,e,o):((t,e,o)=>{const r=e.hasOwnProperty(o);return e.constructor.createProperty(o,t),r?Object.getOwnPropertyDescriptor(e,o):void 0})(t,e,o)}export{n as property,r as standardProperty};\n//# sourceMappingURL=property.js.map\n","import{property as t}from\"./property.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */function r(r){return t({...r,state:!0,attribute:!1})}export{r as state};\n//# sourceMappingURL=state.js.map\n","const MAX_ASSISTANT_NAME = 50;\nconst MAX_ASSISTANT_AVATAR = 200;\n\nexport const DEFAULT_ASSISTANT_NAME = \"Assistant\";\nexport const DEFAULT_ASSISTANT_AVATAR = \"A\";\n\nexport type AssistantIdentity = {\n agentId?: string | null;\n name: string;\n avatar: string | null;\n};\n\ndeclare global {\n interface Window {\n __CLAWDBOT_ASSISTANT_NAME__?: string;\n __CLAWDBOT_ASSISTANT_AVATAR__?: string;\n }\n}\n\nfunction coerceIdentityValue(value: string | undefined, maxLength: number): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n if (trimmed.length <= maxLength) return trimmed;\n return trimmed.slice(0, maxLength);\n}\n\nexport function normalizeAssistantIdentity(\n input?: Partial | null,\n): AssistantIdentity {\n const name =\n coerceIdentityValue(input?.name, MAX_ASSISTANT_NAME) ?? DEFAULT_ASSISTANT_NAME;\n const avatar = coerceIdentityValue(input?.avatar ?? undefined, MAX_ASSISTANT_AVATAR) ?? null;\n const agentId =\n typeof input?.agentId === \"string\" && input.agentId.trim()\n ? input.agentId.trim()\n : null;\n return { agentId, name, avatar };\n}\n\nexport function resolveInjectedAssistantIdentity(): AssistantIdentity {\n if (typeof window === \"undefined\") {\n return normalizeAssistantIdentity({});\n }\n return normalizeAssistantIdentity({\n name: window.__CLAWDBOT_ASSISTANT_NAME__,\n avatar: window.__CLAWDBOT_ASSISTANT_AVATAR__,\n });\n}\n","const KEY = \"clawdbot.control.settings.v1\";\n\nimport type { ThemeMode } from \"./theme\";\n\nexport type UiSettings = {\n gatewayUrl: string;\n token: string;\n sessionKey: string;\n lastActiveSessionKey: string;\n theme: ThemeMode;\n chatFocusMode: boolean;\n chatShowThinking: boolean;\n splitRatio: number; // Sidebar split ratio (0.4 to 0.7, default 0.6)\n navCollapsed: boolean; // Collapsible sidebar state\n navGroupsCollapsed: Record; // Which nav groups are collapsed\n};\n\nexport function loadSettings(): UiSettings {\n const defaultUrl = (() => {\n const proto = location.protocol === \"https:\" ? \"wss\" : \"ws\";\n return `${proto}://${location.host}`;\n })();\n\n const defaults: UiSettings = {\n gatewayUrl: defaultUrl,\n token: \"\",\n sessionKey: \"main\",\n lastActiveSessionKey: \"main\",\n theme: \"system\",\n chatFocusMode: false,\n chatShowThinking: true,\n splitRatio: 0.6,\n navCollapsed: false,\n navGroupsCollapsed: {},\n };\n\n try {\n const raw = localStorage.getItem(KEY);\n if (!raw) return defaults;\n const parsed = JSON.parse(raw) as Partial;\n return {\n gatewayUrl:\n typeof parsed.gatewayUrl === \"string\" && parsed.gatewayUrl.trim()\n ? parsed.gatewayUrl.trim()\n : defaults.gatewayUrl,\n token: typeof parsed.token === \"string\" ? parsed.token : defaults.token,\n sessionKey:\n typeof parsed.sessionKey === \"string\" && parsed.sessionKey.trim()\n ? parsed.sessionKey.trim()\n : defaults.sessionKey,\n lastActiveSessionKey:\n typeof parsed.lastActiveSessionKey === \"string\" &&\n parsed.lastActiveSessionKey.trim()\n ? parsed.lastActiveSessionKey.trim()\n : (typeof parsed.sessionKey === \"string\" &&\n parsed.sessionKey.trim()) ||\n defaults.lastActiveSessionKey,\n theme:\n parsed.theme === \"light\" ||\n parsed.theme === \"dark\" ||\n parsed.theme === \"system\"\n ? parsed.theme\n : defaults.theme,\n chatFocusMode:\n typeof parsed.chatFocusMode === \"boolean\"\n ? parsed.chatFocusMode\n : defaults.chatFocusMode,\n chatShowThinking:\n typeof parsed.chatShowThinking === \"boolean\"\n ? parsed.chatShowThinking\n : defaults.chatShowThinking,\n splitRatio:\n typeof parsed.splitRatio === \"number\" &&\n parsed.splitRatio >= 0.4 &&\n parsed.splitRatio <= 0.7\n ? parsed.splitRatio\n : defaults.splitRatio,\n navCollapsed:\n typeof parsed.navCollapsed === \"boolean\"\n ? parsed.navCollapsed\n : defaults.navCollapsed,\n navGroupsCollapsed:\n typeof parsed.navGroupsCollapsed === \"object\" &&\n parsed.navGroupsCollapsed !== null\n ? parsed.navGroupsCollapsed\n : defaults.navGroupsCollapsed,\n };\n } catch {\n return defaults;\n }\n}\n\nexport function saveSettings(next: UiSettings) {\n localStorage.setItem(KEY, JSON.stringify(next));\n}\n","export type ParsedAgentSessionKey = {\n agentId: string;\n rest: string;\n};\n\nexport function parseAgentSessionKey(\n sessionKey: string | undefined | null,\n): ParsedAgentSessionKey | null {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return null;\n const parts = raw.split(\":\").filter(Boolean);\n if (parts.length < 3) return null;\n if (parts[0] !== \"agent\") return null;\n const agentId = parts[1]?.trim();\n const rest = parts.slice(2).join(\":\");\n if (!agentId || !rest) return null;\n return { agentId, rest };\n}\n\nexport function isSubagentSessionKey(sessionKey: string | undefined | null): boolean {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return false;\n if (raw.toLowerCase().startsWith(\"subagent:\")) return true;\n const parsed = parseAgentSessionKey(raw);\n return Boolean((parsed?.rest ?? \"\").toLowerCase().startsWith(\"subagent:\"));\n}\n\nexport function isAcpSessionKey(sessionKey: string | undefined | null): boolean {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return false;\n const normalized = raw.toLowerCase();\n if (normalized.startsWith(\"acp:\")) return true;\n const parsed = parseAgentSessionKey(raw);\n return Boolean((parsed?.rest ?? \"\").toLowerCase().startsWith(\"acp:\"));\n}\n\nconst THREAD_SESSION_MARKERS = [\":thread:\", \":topic:\"];\n\nexport function resolveThreadParentSessionKey(\n sessionKey: string | undefined | null,\n): string | null {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return null;\n const normalized = raw.toLowerCase();\n let idx = -1;\n for (const marker of THREAD_SESSION_MARKERS) {\n const candidate = normalized.lastIndexOf(marker);\n if (candidate > idx) idx = candidate;\n }\n if (idx <= 0) return null;\n const parent = raw.slice(0, idx).trim();\n return parent ? parent : null;\n}\n","import type { IconName } from \"./icons.js\";\n\nexport const TAB_GROUPS = [\n { label: \"Chat\", tabs: [\"chat\"] },\n {\n label: \"Control\",\n tabs: [\"overview\", \"channels\", \"instances\", \"sessions\", \"cron\"],\n },\n { label: \"Agent\", tabs: [\"skills\", \"nodes\"] },\n { label: \"Settings\", tabs: [\"config\", \"debug\", \"logs\"] },\n] as const;\n\nexport type Tab =\n | \"overview\"\n | \"channels\"\n | \"instances\"\n | \"sessions\"\n | \"cron\"\n | \"skills\"\n | \"nodes\"\n | \"chat\"\n | \"config\"\n | \"debug\"\n | \"logs\";\n\nconst TAB_PATHS: Record = {\n overview: \"/overview\",\n channels: \"/channels\",\n instances: \"/instances\",\n sessions: \"/sessions\",\n cron: \"/cron\",\n skills: \"/skills\",\n nodes: \"/nodes\",\n chat: \"/chat\",\n config: \"/config\",\n debug: \"/debug\",\n logs: \"/logs\",\n};\n\nconst PATH_TO_TAB = new Map(\n Object.entries(TAB_PATHS).map(([tab, path]) => [path, tab as Tab]),\n);\n\nexport function normalizeBasePath(basePath: string): string {\n if (!basePath) return \"\";\n let base = basePath.trim();\n if (!base.startsWith(\"/\")) base = `/${base}`;\n if (base === \"/\") return \"\";\n if (base.endsWith(\"/\")) base = base.slice(0, -1);\n return base;\n}\n\nexport function normalizePath(path: string): string {\n if (!path) return \"/\";\n let normalized = path.trim();\n if (!normalized.startsWith(\"/\")) normalized = `/${normalized}`;\n if (normalized.length > 1 && normalized.endsWith(\"/\")) {\n normalized = normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function pathForTab(tab: Tab, basePath = \"\"): string {\n const base = normalizeBasePath(basePath);\n const path = TAB_PATHS[tab];\n return base ? `${base}${path}` : path;\n}\n\nexport function tabFromPath(pathname: string, basePath = \"\"): Tab | null {\n const base = normalizeBasePath(basePath);\n let path = pathname || \"/\";\n if (base) {\n if (path === base) {\n path = \"/\";\n } else if (path.startsWith(`${base}/`)) {\n path = path.slice(base.length);\n }\n }\n let normalized = normalizePath(path).toLowerCase();\n if (normalized.endsWith(\"/index.html\")) normalized = \"/\";\n if (normalized === \"/\") return \"chat\";\n return PATH_TO_TAB.get(normalized) ?? null;\n}\n\nexport function inferBasePathFromPathname(pathname: string): string {\n let normalized = normalizePath(pathname);\n if (normalized.endsWith(\"/index.html\")) {\n normalized = normalizePath(normalized.slice(0, -\"/index.html\".length));\n }\n if (normalized === \"/\") return \"\";\n const segments = normalized.split(\"/\").filter(Boolean);\n if (segments.length === 0) return \"\";\n for (let i = 0; i < segments.length; i++) {\n const candidate = `/${segments.slice(i).join(\"/\")}`.toLowerCase();\n if (PATH_TO_TAB.has(candidate)) {\n const prefix = segments.slice(0, i);\n return prefix.length ? `/${prefix.join(\"/\")}` : \"\";\n }\n }\n return `/${segments.join(\"/\")}`;\n}\n\nexport function iconForTab(tab: Tab): IconName {\n switch (tab) {\n case \"chat\":\n return \"messageSquare\";\n case \"overview\":\n return \"barChart\";\n case \"channels\":\n return \"link\";\n case \"instances\":\n return \"radio\";\n case \"sessions\":\n return \"fileText\";\n case \"cron\":\n return \"loader\";\n case \"skills\":\n return \"zap\";\n case \"nodes\":\n return \"monitor\";\n case \"config\":\n return \"settings\";\n case \"debug\":\n return \"bug\";\n case \"logs\":\n return \"scrollText\";\n default:\n return \"folder\";\n }\n}\n\nexport function titleForTab(tab: Tab) {\n switch (tab) {\n case \"overview\":\n return \"Overview\";\n case \"channels\":\n return \"Channels\";\n case \"instances\":\n return \"Instances\";\n case \"sessions\":\n return \"Sessions\";\n case \"cron\":\n return \"Cron Jobs\";\n case \"skills\":\n return \"Skills\";\n case \"nodes\":\n return \"Nodes\";\n case \"chat\":\n return \"Chat\";\n case \"config\":\n return \"Config\";\n case \"debug\":\n return \"Debug\";\n case \"logs\":\n return \"Logs\";\n default:\n return \"Control\";\n }\n}\n\nexport function subtitleForTab(tab: Tab) {\n switch (tab) {\n case \"overview\":\n return \"Gateway status, entry points, and a fast health read.\";\n case \"channels\":\n return \"Manage channels and settings.\";\n case \"instances\":\n return \"Presence beacons from connected clients and nodes.\";\n case \"sessions\":\n return \"Inspect active sessions and adjust per-session defaults.\";\n case \"cron\":\n return \"Schedule wakeups and recurring agent runs.\";\n case \"skills\":\n return \"Manage skill availability and API key injection.\";\n case \"nodes\":\n return \"Paired devices, capabilities, and command exposure.\";\n case \"chat\":\n return \"Direct gateway chat session for quick interventions.\";\n case \"config\":\n return \"Edit ~/.clawdbot/clawdbot.json safely.\";\n case \"debug\":\n return \"Gateway snapshots, events, and manual RPC calls.\";\n case \"logs\":\n return \"Live tail of the gateway file logs.\";\n default:\n return \"\";\n }\n}\n","import { html, type TemplateResult } from \"lit\";\n\n// Lucide-style SVG icons\n// All icons use currentColor for stroke\n\nexport const icons = {\n // Navigation icons\n messageSquare: html``,\n barChart: html``,\n link: html``,\n radio: html``,\n fileText: html``,\n zap: html``,\n monitor: html``,\n settings: html``,\n bug: html``,\n scrollText: html``,\n folder: html``,\n\n // UI icons\n menu: html``,\n x: html``,\n check: html``,\n copy: html``,\n search: html``,\n brain: html``,\n book: html``,\n loader: html``,\n\n // Tool icons\n wrench: html``,\n fileCode: html``,\n edit: html``,\n penLine: html``,\n paperclip: html``,\n globe: html``,\n image: html``,\n smartphone: html``,\n plug: html``,\n circle: html``,\n puzzle: html``,\n} as const;\n\nexport type IconName = keyof typeof icons;\n\nexport function icon(name: IconName): TemplateResult {\n return icons[name];\n}\n\nexport function renderIcon(name: IconName, className = \"nav-item__icon\"): TemplateResult {\n return html`${icons[name]}`;\n}\n\n// Legacy function for compatibility\nexport function renderEmojiIcon(iconContent: string | TemplateResult, className: string): TemplateResult {\n return html`${iconContent}`;\n}\n\nexport function setEmojiIcon(target: HTMLElement | null, icon: string): void {\n if (!target) return;\n target.textContent = icon;\n}\n","export type ReasoningTagMode = \"strict\" | \"preserve\";\nexport type ReasoningTagTrim = \"none\" | \"start\" | \"both\";\n\nconst QUICK_TAG_RE = /<\\s*\\/?\\s*(?:think(?:ing)?|thought|antthinking|final)\\b/i;\nconst FINAL_TAG_RE = /<\\s*\\/?\\s*final\\b[^>]*>/gi;\nconst THINKING_TAG_RE = /<\\s*(\\/?)\\s*(?:think(?:ing)?|thought|antthinking)\\b[^>]*>/gi;\n\nfunction applyTrim(value: string, mode: ReasoningTagTrim): string {\n if (mode === \"none\") return value;\n if (mode === \"start\") return value.trimStart();\n return value.trim();\n}\n\nexport function stripReasoningTagsFromText(\n text: string,\n options?: {\n mode?: ReasoningTagMode;\n trim?: ReasoningTagTrim;\n },\n): string {\n if (!text) return text;\n if (!QUICK_TAG_RE.test(text)) return text;\n\n const mode = options?.mode ?? \"strict\";\n const trimMode = options?.trim ?? \"both\";\n\n let cleaned = text;\n if (FINAL_TAG_RE.test(cleaned)) {\n FINAL_TAG_RE.lastIndex = 0;\n cleaned = cleaned.replace(FINAL_TAG_RE, \"\");\n } else {\n FINAL_TAG_RE.lastIndex = 0;\n }\n\n THINKING_TAG_RE.lastIndex = 0;\n let result = \"\";\n let lastIndex = 0;\n let inThinking = false;\n\n for (const match of cleaned.matchAll(THINKING_TAG_RE)) {\n const idx = match.index ?? 0;\n const isClose = match[1] === \"/\";\n\n if (!inThinking) {\n result += cleaned.slice(lastIndex, idx);\n if (!isClose) {\n inThinking = true;\n }\n } else if (isClose) {\n inThinking = false;\n }\n\n lastIndex = idx + match[0].length;\n }\n\n if (!inThinking || mode === \"preserve\") {\n result += cleaned.slice(lastIndex);\n }\n\n return applyTrim(result, trimMode);\n}\n","import { stripReasoningTagsFromText } from \"../../../src/shared/text/reasoning-tags.js\";\n\nexport function formatMs(ms?: number | null): string {\n if (!ms && ms !== 0) return \"n/a\";\n return new Date(ms).toLocaleString();\n}\n\nexport function formatAgo(ms?: number | null): string {\n if (!ms && ms !== 0) return \"n/a\";\n const diff = Date.now() - ms;\n if (diff < 0) return \"just now\";\n const sec = Math.round(diff / 1000);\n if (sec < 60) return `${sec}s ago`;\n const min = Math.round(sec / 60);\n if (min < 60) return `${min}m ago`;\n const hr = Math.round(min / 60);\n if (hr < 48) return `${hr}h ago`;\n const day = Math.round(hr / 24);\n return `${day}d ago`;\n}\n\nexport function formatDurationMs(ms?: number | null): string {\n if (!ms && ms !== 0) return \"n/a\";\n if (ms < 1000) return `${ms}ms`;\n const sec = Math.round(ms / 1000);\n if (sec < 60) return `${sec}s`;\n const min = Math.round(sec / 60);\n if (min < 60) return `${min}m`;\n const hr = Math.round(min / 60);\n if (hr < 48) return `${hr}h`;\n const day = Math.round(hr / 24);\n return `${day}d`;\n}\n\nexport function formatList(values?: Array): string {\n if (!values || values.length === 0) return \"none\";\n return values.filter((v): v is string => Boolean(v && v.trim())).join(\", \");\n}\n\nexport function clampText(value: string, max = 120): string {\n if (value.length <= max) return value;\n return `${value.slice(0, Math.max(0, max - 1))}…`;\n}\n\nexport function truncateText(value: string, max: number): {\n text: string;\n truncated: boolean;\n total: number;\n} {\n if (value.length <= max) {\n return { text: value, truncated: false, total: value.length };\n }\n return {\n text: value.slice(0, Math.max(0, max)),\n truncated: true,\n total: value.length,\n };\n}\n\nexport function toNumber(value: string, fallback: number): number {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\nexport function parseList(input: string): string[] {\n return input\n .split(/[,\\n]/)\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n}\n\nexport function stripThinkingTags(value: string): string {\n return stripReasoningTagsFromText(value, { mode: \"preserve\", trim: \"start\" });\n}\n","import { stripThinkingTags } from \"../format\";\n\nconst ENVELOPE_PREFIX = /^\\[([^\\]]+)\\]\\s*/;\nconst ENVELOPE_CHANNELS = [\n \"WebChat\",\n \"WhatsApp\",\n \"Telegram\",\n \"Signal\",\n \"Slack\",\n \"Discord\",\n \"iMessage\",\n \"Teams\",\n \"Matrix\",\n \"Zalo\",\n \"Zalo Personal\",\n \"BlueBubbles\",\n];\n\nconst textCache = new WeakMap();\nconst thinkingCache = new WeakMap();\n\nfunction looksLikeEnvelopeHeader(header: string): boolean {\n if (/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}Z\\b/.test(header)) return true;\n if (/\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}\\b/.test(header)) return true;\n return ENVELOPE_CHANNELS.some((label) => header.startsWith(`${label} `));\n}\n\nexport function stripEnvelope(text: string): string {\n const match = text.match(ENVELOPE_PREFIX);\n if (!match) return text;\n const header = match[1] ?? \"\";\n if (!looksLikeEnvelopeHeader(header)) return text;\n return text.slice(match[0].length);\n}\n\nexport function extractText(message: unknown): string | null {\n const m = message as Record;\n const role = typeof m.role === \"string\" ? m.role : \"\";\n const content = m.content;\n if (typeof content === \"string\") {\n const processed = role === \"assistant\" ? stripThinkingTags(content) : stripEnvelope(content);\n return processed;\n }\n if (Array.isArray(content)) {\n const parts = content\n .map((p) => {\n const item = p as Record;\n if (item.type === \"text\" && typeof item.text === \"string\") return item.text;\n return null;\n })\n .filter((v): v is string => typeof v === \"string\");\n if (parts.length > 0) {\n const joined = parts.join(\"\\n\");\n const processed = role === \"assistant\" ? stripThinkingTags(joined) : stripEnvelope(joined);\n return processed;\n }\n }\n if (typeof m.text === \"string\") {\n const processed = role === \"assistant\" ? stripThinkingTags(m.text) : stripEnvelope(m.text);\n return processed;\n }\n return null;\n}\n\nexport function extractTextCached(message: unknown): string | null {\n if (!message || typeof message !== \"object\") return extractText(message);\n const obj = message as object;\n if (textCache.has(obj)) return textCache.get(obj) ?? null;\n const value = extractText(message);\n textCache.set(obj, value);\n return value;\n}\n\nexport function extractThinking(message: unknown): string | null {\n const m = message as Record;\n const content = m.content;\n const parts: string[] = [];\n if (Array.isArray(content)) {\n for (const p of content) {\n const item = p as Record;\n if (item.type === \"thinking\" && typeof item.thinking === \"string\") {\n const cleaned = item.thinking.trim();\n if (cleaned) parts.push(cleaned);\n }\n }\n }\n if (parts.length > 0) return parts.join(\"\\n\");\n\n // Back-compat: older logs may still have tags inside text blocks.\n const rawText = extractRawText(message);\n if (!rawText) return null;\n const matches = [\n ...rawText.matchAll(\n /<\\s*think(?:ing)?\\s*>([\\s\\S]*?)<\\s*\\/\\s*think(?:ing)?\\s*>/gi,\n ),\n ];\n const extracted = matches\n .map((m) => (m[1] ?? \"\").trim())\n .filter(Boolean);\n return extracted.length > 0 ? extracted.join(\"\\n\") : null;\n}\n\nexport function extractThinkingCached(message: unknown): string | null {\n if (!message || typeof message !== \"object\") return extractThinking(message);\n const obj = message as object;\n if (thinkingCache.has(obj)) return thinkingCache.get(obj) ?? null;\n const value = extractThinking(message);\n thinkingCache.set(obj, value);\n return value;\n}\n\nexport function extractRawText(message: unknown): string | null {\n const m = message as Record;\n const content = m.content;\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n const parts = content\n .map((p) => {\n const item = p as Record;\n if (item.type === \"text\" && typeof item.text === \"string\") return item.text;\n return null;\n })\n .filter((v): v is string => typeof v === \"string\");\n if (parts.length > 0) return parts.join(\"\\n\");\n }\n if (typeof m.text === \"string\") return m.text;\n return null;\n}\n\nexport function formatReasoningMarkdown(text: string): string {\n const trimmed = text.trim();\n if (!trimmed) return \"\";\n const lines = trimmed\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => `_${line}_`);\n return lines.length ? [\"_Reasoning:_\", ...lines].join(\"\\n\") : \"\";\n}\n","export type CryptoLike = {\n randomUUID?: (() => string) | undefined;\n getRandomValues?: ((array: Uint8Array) => Uint8Array) | undefined;\n};\n\nfunction uuidFromBytes(bytes: Uint8Array): string {\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1\n\n let hex = \"\";\n for (let i = 0; i < bytes.length; i++) {\n hex += bytes[i]!.toString(16).padStart(2, \"0\");\n }\n\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(\n 16,\n 20,\n )}-${hex.slice(20)}`;\n}\n\nfunction weakRandomBytes(): Uint8Array {\n const bytes = new Uint8Array(16);\n const now = Date.now();\n for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);\n bytes[0] ^= now & 0xff;\n bytes[1] ^= (now >>> 8) & 0xff;\n bytes[2] ^= (now >>> 16) & 0xff;\n bytes[3] ^= (now >>> 24) & 0xff;\n return bytes;\n}\n\nexport function generateUUID(cryptoLike: CryptoLike | null = globalThis.crypto): string {\n if (cryptoLike && typeof cryptoLike.randomUUID === \"function\") return cryptoLike.randomUUID();\n\n if (cryptoLike && typeof cryptoLike.getRandomValues === \"function\") {\n const bytes = new Uint8Array(16);\n cryptoLike.getRandomValues(bytes);\n return uuidFromBytes(bytes);\n }\n\n return uuidFromBytes(weakRandomBytes());\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { extractText } from \"../chat/message-extract\";\nimport { generateUUID } from \"../uuid\";\n\nexport type ChatState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n sessionKey: string;\n chatLoading: boolean;\n chatMessages: unknown[];\n chatThinkingLevel: string | null;\n chatSending: boolean;\n chatMessage: string;\n chatRunId: string | null;\n chatStream: string | null;\n chatStreamStartedAt: number | null;\n lastError: string | null;\n};\n\nexport type ChatEventPayload = {\n runId: string;\n sessionKey: string;\n state: \"delta\" | \"final\" | \"aborted\" | \"error\";\n message?: unknown;\n errorMessage?: string;\n};\n\nexport async function loadChatHistory(state: ChatState) {\n if (!state.client || !state.connected) return;\n state.chatLoading = true;\n state.lastError = null;\n try {\n const res = (await state.client.request(\"chat.history\", {\n sessionKey: state.sessionKey,\n limit: 200,\n })) as { messages?: unknown[]; thinkingLevel?: string | null };\n state.chatMessages = Array.isArray(res.messages) ? res.messages : [];\n state.chatThinkingLevel = res.thinkingLevel ?? null;\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.chatLoading = false;\n }\n}\n\nexport async function sendChatMessage(state: ChatState, message: string): Promise {\n if (!state.client || !state.connected) return false;\n const msg = message.trim();\n if (!msg) return false;\n\n const now = Date.now();\n state.chatMessages = [\n ...state.chatMessages,\n {\n role: \"user\",\n content: [{ type: \"text\", text: msg }],\n timestamp: now,\n },\n ];\n\n state.chatSending = true;\n state.lastError = null;\n const runId = generateUUID();\n state.chatRunId = runId;\n state.chatStream = \"\";\n state.chatStreamStartedAt = now;\n try {\n await state.client.request(\"chat.send\", {\n sessionKey: state.sessionKey,\n message: msg,\n deliver: false,\n idempotencyKey: runId,\n });\n return true;\n } catch (err) {\n const error = String(err);\n state.chatRunId = null;\n state.chatStream = null;\n state.chatStreamStartedAt = null;\n state.lastError = error;\n state.chatMessages = [\n ...state.chatMessages,\n {\n role: \"assistant\",\n content: [{ type: \"text\", text: \"Error: \" + error }],\n timestamp: Date.now(),\n },\n ];\n return false;\n } finally {\n state.chatSending = false;\n }\n}\n\nexport async function abortChatRun(state: ChatState): Promise {\n if (!state.client || !state.connected) return false;\n const runId = state.chatRunId;\n try {\n await state.client.request(\n \"chat.abort\",\n runId\n ? { sessionKey: state.sessionKey, runId }\n : { sessionKey: state.sessionKey },\n );\n return true;\n } catch (err) {\n state.lastError = String(err);\n return false;\n }\n}\n\nexport function handleChatEvent(\n state: ChatState,\n payload?: ChatEventPayload,\n) {\n if (!payload) return null;\n if (payload.sessionKey !== state.sessionKey) return null;\n if (payload.runId && state.chatRunId && payload.runId !== state.chatRunId)\n return null;\n\n if (payload.state === \"delta\") {\n const next = extractText(payload.message);\n if (typeof next === \"string\") {\n const current = state.chatStream ?? \"\";\n if (!current || next.length >= current.length) {\n state.chatStream = next;\n }\n }\n } else if (payload.state === \"final\") {\n state.chatStream = null;\n state.chatRunId = null;\n state.chatStreamStartedAt = null;\n } else if (payload.state === \"aborted\") {\n state.chatStream = null;\n state.chatRunId = null;\n state.chatStreamStartedAt = null;\n } else if (payload.state === \"error\") {\n state.chatStream = null;\n state.chatRunId = null;\n state.chatStreamStartedAt = null;\n state.lastError = payload.errorMessage ?? \"chat error\";\n }\n return payload.state;\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { toNumber } from \"../format\";\nimport type { SessionsListResult } from \"../types\";\n\nexport type SessionsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n sessionsLoading: boolean;\n sessionsResult: SessionsListResult | null;\n sessionsError: string | null;\n sessionsFilterActive: string;\n sessionsFilterLimit: string;\n sessionsIncludeGlobal: boolean;\n sessionsIncludeUnknown: boolean;\n};\n\nexport async function loadSessions(state: SessionsState) {\n if (!state.client || !state.connected) return;\n if (state.sessionsLoading) return;\n state.sessionsLoading = true;\n state.sessionsError = null;\n try {\n const params: Record = {\n includeGlobal: state.sessionsIncludeGlobal,\n includeUnknown: state.sessionsIncludeUnknown,\n };\n const activeMinutes = toNumber(state.sessionsFilterActive, 0);\n const limit = toNumber(state.sessionsFilterLimit, 0);\n if (activeMinutes > 0) params.activeMinutes = activeMinutes;\n if (limit > 0) params.limit = limit;\n const res = (await state.client.request(\"sessions.list\", params)) as\n | SessionsListResult\n | undefined;\n if (res) state.sessionsResult = res;\n } catch (err) {\n state.sessionsError = String(err);\n } finally {\n state.sessionsLoading = false;\n }\n}\n\nexport async function patchSession(\n state: SessionsState,\n key: string,\n patch: {\n label?: string | null;\n thinkingLevel?: string | null;\n verboseLevel?: string | null;\n reasoningLevel?: string | null;\n },\n) {\n if (!state.client || !state.connected) return;\n const params: Record = { key };\n if (\"label\" in patch) params.label = patch.label;\n if (\"thinkingLevel\" in patch) params.thinkingLevel = patch.thinkingLevel;\n if (\"verboseLevel\" in patch) params.verboseLevel = patch.verboseLevel;\n if (\"reasoningLevel\" in patch) params.reasoningLevel = patch.reasoningLevel;\n try {\n await state.client.request(\"sessions.patch\", params);\n await loadSessions(state);\n } catch (err) {\n state.sessionsError = String(err);\n }\n}\n\nexport async function deleteSession(state: SessionsState, key: string) {\n if (!state.client || !state.connected) return;\n if (state.sessionsLoading) return;\n const confirmed = window.confirm(\n `Delete session \"${key}\"?\\n\\nDeletes the session entry and archives its transcript.`,\n );\n if (!confirmed) return;\n state.sessionsLoading = true;\n state.sessionsError = null;\n try {\n await state.client.request(\"sessions.delete\", { key, deleteTranscript: true });\n await loadSessions(state);\n } catch (err) {\n state.sessionsError = String(err);\n } finally {\n state.sessionsLoading = false;\n }\n}\n","import { truncateText } from \"./format\";\n\nconst TOOL_STREAM_LIMIT = 50;\nconst TOOL_STREAM_THROTTLE_MS = 80;\nconst TOOL_OUTPUT_CHAR_LIMIT = 120_000;\n\nexport type AgentEventPayload = {\n runId: string;\n seq: number;\n stream: string;\n ts: number;\n sessionKey?: string;\n data: Record;\n};\n\nexport type ToolStreamEntry = {\n toolCallId: string;\n runId: string;\n sessionKey?: string;\n name: string;\n args?: unknown;\n output?: string;\n startedAt: number;\n updatedAt: number;\n message: Record;\n};\n\ntype ToolStreamHost = {\n sessionKey: string;\n chatRunId: string | null;\n toolStreamById: Map;\n toolStreamOrder: string[];\n chatToolMessages: Record[];\n toolStreamSyncTimer: number | null;\n};\n\nfunction extractToolOutputText(value: unknown): string | null {\n if (!value || typeof value !== \"object\") return null;\n const record = value as Record;\n if (typeof record.text === \"string\") return record.text;\n const content = record.content;\n if (!Array.isArray(content)) return null;\n const parts = content\n .map((item) => {\n if (!item || typeof item !== \"object\") return null;\n const entry = item as Record;\n if (entry.type === \"text\" && typeof entry.text === \"string\") return entry.text;\n return null;\n })\n .filter((part): part is string => Boolean(part));\n if (parts.length === 0) return null;\n return parts.join(\"\\n\");\n}\n\nfunction formatToolOutput(value: unknown): string | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n const contentText = extractToolOutputText(value);\n let text: string;\n if (typeof value === \"string\") {\n text = value;\n } else if (contentText) {\n text = contentText;\n } else {\n try {\n text = JSON.stringify(value, null, 2);\n } catch {\n text = String(value);\n }\n }\n const truncated = truncateText(text, TOOL_OUTPUT_CHAR_LIMIT);\n if (!truncated.truncated) return truncated.text;\n return `${truncated.text}\\n\\n… truncated (${truncated.total} chars, showing first ${truncated.text.length}).`;\n}\n\nfunction buildToolStreamMessage(entry: ToolStreamEntry): Record {\n const content: Array> = [];\n content.push({\n type: \"toolcall\",\n name: entry.name,\n arguments: entry.args ?? {},\n });\n if (entry.output) {\n content.push({\n type: \"toolresult\",\n name: entry.name,\n text: entry.output,\n });\n }\n return {\n role: \"assistant\",\n toolCallId: entry.toolCallId,\n runId: entry.runId,\n content,\n timestamp: entry.startedAt,\n };\n}\n\nfunction trimToolStream(host: ToolStreamHost) {\n if (host.toolStreamOrder.length <= TOOL_STREAM_LIMIT) return;\n const overflow = host.toolStreamOrder.length - TOOL_STREAM_LIMIT;\n const removed = host.toolStreamOrder.splice(0, overflow);\n for (const id of removed) host.toolStreamById.delete(id);\n}\n\nfunction syncToolStreamMessages(host: ToolStreamHost) {\n host.chatToolMessages = host.toolStreamOrder\n .map((id) => host.toolStreamById.get(id)?.message)\n .filter((msg): msg is Record => Boolean(msg));\n}\n\nexport function flushToolStreamSync(host: ToolStreamHost) {\n if (host.toolStreamSyncTimer != null) {\n clearTimeout(host.toolStreamSyncTimer);\n host.toolStreamSyncTimer = null;\n }\n syncToolStreamMessages(host);\n}\n\nexport function scheduleToolStreamSync(host: ToolStreamHost, force = false) {\n if (force) {\n flushToolStreamSync(host);\n return;\n }\n if (host.toolStreamSyncTimer != null) return;\n host.toolStreamSyncTimer = window.setTimeout(\n () => flushToolStreamSync(host),\n TOOL_STREAM_THROTTLE_MS,\n );\n}\n\nexport function resetToolStream(host: ToolStreamHost) {\n host.toolStreamById.clear();\n host.toolStreamOrder = [];\n host.chatToolMessages = [];\n flushToolStreamSync(host);\n}\n\nexport type CompactionStatus = {\n active: boolean;\n startedAt: number | null;\n completedAt: number | null;\n};\n\ntype CompactionHost = ToolStreamHost & {\n compactionStatus?: CompactionStatus | null;\n compactionClearTimer?: number | null;\n};\n\nconst COMPACTION_TOAST_DURATION_MS = 5000;\n\nexport function handleCompactionEvent(host: CompactionHost, payload: AgentEventPayload) {\n const data = payload.data ?? {};\n const phase = typeof data.phase === \"string\" ? data.phase : \"\";\n \n // Clear any existing timer\n if (host.compactionClearTimer != null) {\n window.clearTimeout(host.compactionClearTimer);\n host.compactionClearTimer = null;\n }\n \n if (phase === \"start\") {\n host.compactionStatus = {\n active: true,\n startedAt: Date.now(),\n completedAt: null,\n };\n } else if (phase === \"end\") {\n host.compactionStatus = {\n active: false,\n startedAt: host.compactionStatus?.startedAt ?? null,\n completedAt: Date.now(),\n };\n // Auto-clear the toast after duration\n host.compactionClearTimer = window.setTimeout(() => {\n host.compactionStatus = null;\n host.compactionClearTimer = null;\n }, COMPACTION_TOAST_DURATION_MS);\n }\n}\n\nexport function handleAgentEvent(host: ToolStreamHost, payload?: AgentEventPayload) {\n if (!payload) return;\n \n // Handle compaction events\n if (payload.stream === \"compaction\") {\n handleCompactionEvent(host as CompactionHost, payload);\n return;\n }\n \n if (payload.stream !== \"tool\") return;\n const sessionKey =\n typeof payload.sessionKey === \"string\" ? payload.sessionKey : undefined;\n if (sessionKey && sessionKey !== host.sessionKey) return;\n // Fallback: only accept session-less events for the active run.\n if (!sessionKey && host.chatRunId && payload.runId !== host.chatRunId) return;\n if (host.chatRunId && payload.runId !== host.chatRunId) return;\n if (!host.chatRunId) return;\n\n const data = payload.data ?? {};\n const toolCallId = typeof data.toolCallId === \"string\" ? data.toolCallId : \"\";\n if (!toolCallId) return;\n const name = typeof data.name === \"string\" ? data.name : \"tool\";\n const phase = typeof data.phase === \"string\" ? data.phase : \"\";\n const args = phase === \"start\" ? data.args : undefined;\n const output =\n phase === \"update\"\n ? formatToolOutput(data.partialResult)\n : phase === \"result\"\n ? formatToolOutput(data.result)\n : undefined;\n\n const now = Date.now();\n let entry = host.toolStreamById.get(toolCallId);\n if (!entry) {\n entry = {\n toolCallId,\n runId: payload.runId,\n sessionKey,\n name,\n args,\n output,\n startedAt: typeof payload.ts === \"number\" ? payload.ts : now,\n updatedAt: now,\n message: {},\n };\n host.toolStreamById.set(toolCallId, entry);\n host.toolStreamOrder.push(toolCallId);\n } else {\n entry.name = name;\n if (args !== undefined) entry.args = args;\n if (output !== undefined) entry.output = output;\n entry.updatedAt = now;\n }\n\n entry.message = buildToolStreamMessage(entry);\n trimToolStream(host);\n scheduleToolStreamSync(host, phase === \"result\");\n}\n","type ScrollHost = {\n updateComplete: Promise;\n querySelector: (selectors: string) => Element | null;\n style: CSSStyleDeclaration;\n chatScrollFrame: number | null;\n chatScrollTimeout: number | null;\n chatHasAutoScrolled: boolean;\n chatUserNearBottom: boolean;\n logsScrollFrame: number | null;\n logsAtBottom: boolean;\n topbarObserver: ResizeObserver | null;\n};\n\nexport function scheduleChatScroll(host: ScrollHost, force = false) {\n if (host.chatScrollFrame) cancelAnimationFrame(host.chatScrollFrame);\n if (host.chatScrollTimeout != null) {\n clearTimeout(host.chatScrollTimeout);\n host.chatScrollTimeout = null;\n }\n const pickScrollTarget = () => {\n const container = host.querySelector(\".chat-thread\") as HTMLElement | null;\n if (container) {\n const overflowY = getComputedStyle(container).overflowY;\n const canScroll =\n overflowY === \"auto\" ||\n overflowY === \"scroll\" ||\n container.scrollHeight - container.clientHeight > 1;\n if (canScroll) return container;\n }\n return (document.scrollingElement ?? document.documentElement) as HTMLElement | null;\n };\n // Wait for Lit render to complete, then scroll\n void host.updateComplete.then(() => {\n host.chatScrollFrame = requestAnimationFrame(() => {\n host.chatScrollFrame = null;\n const target = pickScrollTarget();\n if (!target) return;\n const distanceFromBottom =\n target.scrollHeight - target.scrollTop - target.clientHeight;\n const shouldStick = force || host.chatUserNearBottom || distanceFromBottom < 200;\n if (!shouldStick) return;\n if (force) host.chatHasAutoScrolled = true;\n target.scrollTop = target.scrollHeight;\n host.chatUserNearBottom = true;\n const retryDelay = force ? 150 : 120;\n host.chatScrollTimeout = window.setTimeout(() => {\n host.chatScrollTimeout = null;\n const latest = pickScrollTarget();\n if (!latest) return;\n const latestDistanceFromBottom =\n latest.scrollHeight - latest.scrollTop - latest.clientHeight;\n const shouldStickRetry =\n force || host.chatUserNearBottom || latestDistanceFromBottom < 200;\n if (!shouldStickRetry) return;\n latest.scrollTop = latest.scrollHeight;\n host.chatUserNearBottom = true;\n }, retryDelay);\n });\n });\n}\n\nexport function scheduleLogsScroll(host: ScrollHost, force = false) {\n if (host.logsScrollFrame) cancelAnimationFrame(host.logsScrollFrame);\n void host.updateComplete.then(() => {\n host.logsScrollFrame = requestAnimationFrame(() => {\n host.logsScrollFrame = null;\n const container = host.querySelector(\".log-stream\") as HTMLElement | null;\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n const shouldStick = force || distanceFromBottom < 80;\n if (!shouldStick) return;\n container.scrollTop = container.scrollHeight;\n });\n });\n}\n\nexport function handleChatScroll(host: ScrollHost, event: Event) {\n const container = event.currentTarget as HTMLElement | null;\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n host.chatUserNearBottom = distanceFromBottom < 200;\n}\n\nexport function handleLogsScroll(host: ScrollHost, event: Event) {\n const container = event.currentTarget as HTMLElement | null;\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n host.logsAtBottom = distanceFromBottom < 80;\n}\n\nexport function resetChatScroll(host: ScrollHost) {\n host.chatHasAutoScrolled = false;\n host.chatUserNearBottom = true;\n}\n\nexport function exportLogs(lines: string[], label: string) {\n if (lines.length === 0) return;\n const blob = new Blob([`${lines.join(\"\\n\")}\\n`], { type: \"text/plain\" });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n const stamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, \"-\");\n anchor.href = url;\n anchor.download = `clawdbot-logs-${label}-${stamp}.log`;\n anchor.click();\n URL.revokeObjectURL(url);\n}\n\nexport function observeTopbar(host: ScrollHost) {\n if (typeof ResizeObserver === \"undefined\") return;\n const topbar = host.querySelector(\".topbar\");\n if (!topbar) return;\n const update = () => {\n const { height } = topbar.getBoundingClientRect();\n host.style.setProperty(\"--topbar-height\", `${height}px`);\n };\n update();\n host.topbarObserver = new ResizeObserver(() => update());\n host.topbarObserver.observe(topbar);\n}\n","export function cloneConfigObject(value: T): T {\n if (typeof structuredClone === \"function\") {\n return structuredClone(value);\n }\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nexport function serializeConfigForm(form: Record): string {\n return `${JSON.stringify(form, null, 2).trimEnd()}\\n`;\n}\n\nexport function setPathValue(\n obj: Record | unknown[],\n path: Array,\n value: unknown,\n) {\n if (path.length === 0) return;\n let current: Record | unknown[] = obj;\n for (let i = 0; i < path.length - 1; i += 1) {\n const key = path[i];\n const nextKey = path[i + 1];\n if (typeof key === \"number\") {\n if (!Array.isArray(current)) return;\n if (current[key] == null) {\n current[key] =\n typeof nextKey === \"number\" ? [] : ({} as Record);\n }\n current = current[key] as Record | unknown[];\n } else {\n if (typeof current !== \"object\" || current == null) return;\n const record = current as Record;\n if (record[key] == null) {\n record[key] =\n typeof nextKey === \"number\" ? [] : ({} as Record);\n }\n current = record[key] as Record | unknown[];\n }\n }\n const lastKey = path[path.length - 1];\n if (typeof lastKey === \"number\") {\n if (Array.isArray(current)) current[lastKey] = value;\n return;\n }\n if (typeof current === \"object\" && current != null) {\n (current as Record)[lastKey] = value;\n }\n}\n\nexport function removePathValue(\n obj: Record | unknown[],\n path: Array,\n) {\n if (path.length === 0) return;\n let current: Record | unknown[] = obj;\n for (let i = 0; i < path.length - 1; i += 1) {\n const key = path[i];\n if (typeof key === \"number\") {\n if (!Array.isArray(current)) return;\n current = current[key] as Record | unknown[];\n } else {\n if (typeof current !== \"object\" || current == null) return;\n current = (current as Record)[key] as\n | Record\n | unknown[];\n }\n if (current == null) return;\n }\n const lastKey = path[path.length - 1];\n if (typeof lastKey === \"number\") {\n if (Array.isArray(current)) current.splice(lastKey, 1);\n return;\n }\n if (typeof current === \"object\" && current != null) {\n delete (current as Record)[lastKey];\n }\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type {\n ConfigSchemaResponse,\n ConfigSnapshot,\n ConfigUiHints,\n} from \"../types\";\nimport {\n cloneConfigObject,\n removePathValue,\n serializeConfigForm,\n setPathValue,\n} from \"./config/form-utils\";\n\nexport type ConfigState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n applySessionKey: string;\n configLoading: boolean;\n configRaw: string;\n configRawOriginal: string;\n configValid: boolean | null;\n configIssues: unknown[];\n configSaving: boolean;\n configApplying: boolean;\n updateRunning: boolean;\n configSnapshot: ConfigSnapshot | null;\n configSchema: unknown | null;\n configSchemaVersion: string | null;\n configSchemaLoading: boolean;\n configUiHints: ConfigUiHints;\n configForm: Record | null;\n configFormOriginal: Record | null;\n configFormDirty: boolean;\n configFormMode: \"form\" | \"raw\";\n configSearchQuery: string;\n configActiveSection: string | null;\n configActiveSubsection: string | null;\n lastError: string | null;\n};\n\nexport async function loadConfig(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.configLoading = true;\n state.lastError = null;\n try {\n const res = (await state.client.request(\"config.get\", {})) as ConfigSnapshot;\n applyConfigSnapshot(state, res);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configLoading = false;\n }\n}\n\nexport async function loadConfigSchema(state: ConfigState) {\n if (!state.client || !state.connected) return;\n if (state.configSchemaLoading) return;\n state.configSchemaLoading = true;\n try {\n const res = (await state.client.request(\n \"config.schema\",\n {},\n )) as ConfigSchemaResponse;\n applyConfigSchema(state, res);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configSchemaLoading = false;\n }\n}\n\nexport function applyConfigSchema(\n state: ConfigState,\n res: ConfigSchemaResponse,\n) {\n state.configSchema = res.schema ?? null;\n state.configUiHints = res.uiHints ?? {};\n state.configSchemaVersion = res.version ?? null;\n}\n\nexport function applyConfigSnapshot(state: ConfigState, snapshot: ConfigSnapshot) {\n state.configSnapshot = snapshot;\n const rawFromSnapshot =\n typeof snapshot.raw === \"string\"\n ? snapshot.raw\n : snapshot.config && typeof snapshot.config === \"object\"\n ? serializeConfigForm(snapshot.config as Record)\n : state.configRaw;\n if (!state.configFormDirty || state.configFormMode === \"raw\") {\n state.configRaw = rawFromSnapshot;\n } else if (state.configForm) {\n state.configRaw = serializeConfigForm(state.configForm);\n } else {\n state.configRaw = rawFromSnapshot;\n }\n state.configValid = typeof snapshot.valid === \"boolean\" ? snapshot.valid : null;\n state.configIssues = Array.isArray(snapshot.issues) ? snapshot.issues : [];\n\n if (!state.configFormDirty) {\n state.configForm = cloneConfigObject(snapshot.config ?? {});\n state.configFormOriginal = cloneConfigObject(snapshot.config ?? {});\n state.configRawOriginal = rawFromSnapshot;\n }\n}\n\nexport async function saveConfig(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.configSaving = true;\n state.lastError = null;\n try {\n const raw =\n state.configFormMode === \"form\" && state.configForm\n ? serializeConfigForm(state.configForm)\n : state.configRaw;\n const baseHash = state.configSnapshot?.hash;\n if (!baseHash) {\n state.lastError = \"Config hash missing; reload and retry.\";\n return;\n }\n await state.client.request(\"config.set\", { raw, baseHash });\n state.configFormDirty = false;\n await loadConfig(state);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configSaving = false;\n }\n}\n\nexport async function applyConfig(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.configApplying = true;\n state.lastError = null;\n try {\n const raw =\n state.configFormMode === \"form\" && state.configForm\n ? serializeConfigForm(state.configForm)\n : state.configRaw;\n const baseHash = state.configSnapshot?.hash;\n if (!baseHash) {\n state.lastError = \"Config hash missing; reload and retry.\";\n return;\n }\n await state.client.request(\"config.apply\", {\n raw,\n baseHash,\n sessionKey: state.applySessionKey,\n });\n state.configFormDirty = false;\n await loadConfig(state);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configApplying = false;\n }\n}\n\nexport async function runUpdate(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.updateRunning = true;\n state.lastError = null;\n try {\n await state.client.request(\"update.run\", {\n sessionKey: state.applySessionKey,\n });\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.updateRunning = false;\n }\n}\n\nexport function updateConfigFormValue(\n state: ConfigState,\n path: Array,\n value: unknown,\n) {\n const base = cloneConfigObject(\n state.configForm ?? state.configSnapshot?.config ?? {},\n );\n setPathValue(base, path, value);\n state.configForm = base;\n state.configFormDirty = true;\n if (state.configFormMode === \"form\") {\n state.configRaw = serializeConfigForm(base);\n }\n}\n\nexport function removeConfigFormValue(\n state: ConfigState,\n path: Array,\n) {\n const base = cloneConfigObject(\n state.configForm ?? state.configSnapshot?.config ?? {},\n );\n removePathValue(base, path);\n state.configForm = base;\n state.configFormDirty = true;\n if (state.configFormMode === \"form\") {\n state.configRaw = serializeConfigForm(base);\n }\n}\n","import { toNumber } from \"../format\";\nimport type { GatewayBrowserClient } from \"../gateway\";\nimport type { CronJob, CronRunLogEntry, CronStatus } from \"../types\";\nimport type { CronFormState } from \"../ui-types\";\n\nexport type CronState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n cronLoading: boolean;\n cronJobs: CronJob[];\n cronStatus: CronStatus | null;\n cronError: string | null;\n cronForm: CronFormState;\n cronRunsJobId: string | null;\n cronRuns: CronRunLogEntry[];\n cronBusy: boolean;\n};\n\nexport async function loadCronStatus(state: CronState) {\n if (!state.client || !state.connected) return;\n try {\n const res = (await state.client.request(\"cron.status\", {})) as CronStatus;\n state.cronStatus = res;\n } catch (err) {\n state.cronError = String(err);\n }\n}\n\nexport async function loadCronJobs(state: CronState) {\n if (!state.client || !state.connected) return;\n if (state.cronLoading) return;\n state.cronLoading = true;\n state.cronError = null;\n try {\n const res = (await state.client.request(\"cron.list\", {\n includeDisabled: true,\n })) as { jobs?: CronJob[] };\n state.cronJobs = Array.isArray(res.jobs) ? res.jobs : [];\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronLoading = false;\n }\n}\n\nexport function buildCronSchedule(form: CronFormState) {\n if (form.scheduleKind === \"at\") {\n const ms = Date.parse(form.scheduleAt);\n if (!Number.isFinite(ms)) throw new Error(\"Invalid run time.\");\n return { kind: \"at\" as const, atMs: ms };\n }\n if (form.scheduleKind === \"every\") {\n const amount = toNumber(form.everyAmount, 0);\n if (amount <= 0) throw new Error(\"Invalid interval amount.\");\n const unit = form.everyUnit;\n const mult = unit === \"minutes\" ? 60_000 : unit === \"hours\" ? 3_600_000 : 86_400_000;\n return { kind: \"every\" as const, everyMs: amount * mult };\n }\n const expr = form.cronExpr.trim();\n if (!expr) throw new Error(\"Cron expression required.\");\n return { kind: \"cron\" as const, expr, tz: form.cronTz.trim() || undefined };\n}\n\nexport function buildCronPayload(form: CronFormState) {\n if (form.payloadKind === \"systemEvent\") {\n const text = form.payloadText.trim();\n if (!text) throw new Error(\"System event text required.\");\n return { kind: \"systemEvent\" as const, text };\n }\n const message = form.payloadText.trim();\n if (!message) throw new Error(\"Agent message required.\");\n const payload: {\n kind: \"agentTurn\";\n message: string;\n deliver?: boolean;\n channel?: string;\n to?: string;\n timeoutSeconds?: number;\n } = { kind: \"agentTurn\", message };\n if (form.deliver) payload.deliver = true;\n if (form.channel) payload.channel = form.channel;\n if (form.to.trim()) payload.to = form.to.trim();\n const timeoutSeconds = toNumber(form.timeoutSeconds, 0);\n if (timeoutSeconds > 0) payload.timeoutSeconds = timeoutSeconds;\n return payload;\n}\n\nexport async function addCronJob(state: CronState) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n const schedule = buildCronSchedule(state.cronForm);\n const payload = buildCronPayload(state.cronForm);\n const agentId = state.cronForm.agentId.trim();\n const job = {\n name: state.cronForm.name.trim(),\n description: state.cronForm.description.trim() || undefined,\n agentId: agentId || undefined,\n enabled: state.cronForm.enabled,\n schedule,\n sessionTarget: state.cronForm.sessionTarget,\n wakeMode: state.cronForm.wakeMode,\n payload,\n isolation:\n state.cronForm.postToMainPrefix.trim() &&\n state.cronForm.sessionTarget === \"isolated\"\n ? { postToMainPrefix: state.cronForm.postToMainPrefix.trim() }\n : undefined,\n };\n if (!job.name) throw new Error(\"Name required.\");\n await state.client.request(\"cron.add\", job);\n state.cronForm = {\n ...state.cronForm,\n name: \"\",\n description: \"\",\n payloadText: \"\",\n };\n await loadCronJobs(state);\n await loadCronStatus(state);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function toggleCronJob(\n state: CronState,\n job: CronJob,\n enabled: boolean,\n) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n await state.client.request(\"cron.update\", { id: job.id, patch: { enabled } });\n await loadCronJobs(state);\n await loadCronStatus(state);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function runCronJob(state: CronState, job: CronJob) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n await state.client.request(\"cron.run\", { id: job.id, mode: \"force\" });\n await loadCronRuns(state, job.id);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function removeCronJob(state: CronState, job: CronJob) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n await state.client.request(\"cron.remove\", { id: job.id });\n if (state.cronRunsJobId === job.id) {\n state.cronRunsJobId = null;\n state.cronRuns = [];\n }\n await loadCronJobs(state);\n await loadCronStatus(state);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function loadCronRuns(state: CronState, jobId: string) {\n if (!state.client || !state.connected) return;\n try {\n const res = (await state.client.request(\"cron.runs\", {\n id: jobId,\n limit: 50,\n })) as { entries?: CronRunLogEntry[] };\n state.cronRunsJobId = jobId;\n state.cronRuns = Array.isArray(res.entries) ? res.entries : [];\n } catch (err) {\n state.cronError = String(err);\n }\n}\n","import type { ChannelsStatusSnapshot } from \"../types\";\nimport type { ChannelsState } from \"./channels.types\";\n\nexport type { ChannelsState };\n\nexport async function loadChannels(state: ChannelsState, probe: boolean) {\n if (!state.client || !state.connected) return;\n if (state.channelsLoading) return;\n state.channelsLoading = true;\n state.channelsError = null;\n try {\n const res = (await state.client.request(\"channels.status\", {\n probe,\n timeoutMs: 8000,\n })) as ChannelsStatusSnapshot;\n state.channelsSnapshot = res;\n state.channelsLastSuccess = Date.now();\n } catch (err) {\n state.channelsError = String(err);\n } finally {\n state.channelsLoading = false;\n }\n}\n\nexport async function startWhatsAppLogin(state: ChannelsState, force: boolean) {\n if (!state.client || !state.connected || state.whatsappBusy) return;\n state.whatsappBusy = true;\n try {\n const res = (await state.client.request(\"web.login.start\", {\n force,\n timeoutMs: 30000,\n })) as { message?: string; qrDataUrl?: string };\n state.whatsappLoginMessage = res.message ?? null;\n state.whatsappLoginQrDataUrl = res.qrDataUrl ?? null;\n state.whatsappLoginConnected = null;\n } catch (err) {\n state.whatsappLoginMessage = String(err);\n state.whatsappLoginQrDataUrl = null;\n state.whatsappLoginConnected = null;\n } finally {\n state.whatsappBusy = false;\n }\n}\n\nexport async function waitWhatsAppLogin(state: ChannelsState) {\n if (!state.client || !state.connected || state.whatsappBusy) return;\n state.whatsappBusy = true;\n try {\n const res = (await state.client.request(\"web.login.wait\", {\n timeoutMs: 120000,\n })) as { connected?: boolean; message?: string };\n state.whatsappLoginMessage = res.message ?? null;\n state.whatsappLoginConnected = res.connected ?? null;\n if (res.connected) state.whatsappLoginQrDataUrl = null;\n } catch (err) {\n state.whatsappLoginMessage = String(err);\n state.whatsappLoginConnected = null;\n } finally {\n state.whatsappBusy = false;\n }\n}\n\nexport async function logoutWhatsApp(state: ChannelsState) {\n if (!state.client || !state.connected || state.whatsappBusy) return;\n state.whatsappBusy = true;\n try {\n await state.client.request(\"channels.logout\", { channel: \"whatsapp\" });\n state.whatsappLoginMessage = \"Logged out.\";\n state.whatsappLoginQrDataUrl = null;\n state.whatsappLoginConnected = null;\n } catch (err) {\n state.whatsappLoginMessage = String(err);\n } finally {\n state.whatsappBusy = false;\n }\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { HealthSnapshot, StatusSummary } from \"../types\";\n\nexport type DebugState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n debugLoading: boolean;\n debugStatus: StatusSummary | null;\n debugHealth: HealthSnapshot | null;\n debugModels: unknown[];\n debugHeartbeat: unknown | null;\n debugCallMethod: string;\n debugCallParams: string;\n debugCallResult: string | null;\n debugCallError: string | null;\n};\n\nexport async function loadDebug(state: DebugState) {\n if (!state.client || !state.connected) return;\n if (state.debugLoading) return;\n state.debugLoading = true;\n try {\n const [status, health, models, heartbeat] = await Promise.all([\n state.client.request(\"status\", {}),\n state.client.request(\"health\", {}),\n state.client.request(\"models.list\", {}),\n state.client.request(\"last-heartbeat\", {}),\n ]);\n state.debugStatus = status as StatusSummary;\n state.debugHealth = health as HealthSnapshot;\n const modelPayload = models as { models?: unknown[] } | undefined;\n state.debugModels = Array.isArray(modelPayload?.models)\n ? modelPayload?.models\n : [];\n state.debugHeartbeat = heartbeat as unknown;\n } catch (err) {\n state.debugCallError = String(err);\n } finally {\n state.debugLoading = false;\n }\n}\n\nexport async function callDebugMethod(state: DebugState) {\n if (!state.client || !state.connected) return;\n state.debugCallError = null;\n state.debugCallResult = null;\n try {\n const params = state.debugCallParams.trim()\n ? (JSON.parse(state.debugCallParams) as unknown)\n : {};\n const res = await state.client.request(state.debugCallMethod.trim(), params);\n state.debugCallResult = JSON.stringify(res, null, 2);\n } catch (err) {\n state.debugCallError = String(err);\n }\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { LogEntry, LogLevel } from \"../types\";\n\nexport type LogsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n logsLoading: boolean;\n logsError: string | null;\n logsCursor: number | null;\n logsFile: string | null;\n logsEntries: LogEntry[];\n logsTruncated: boolean;\n logsLastFetchAt: number | null;\n logsLimit: number;\n logsMaxBytes: number;\n};\n\nconst LOG_BUFFER_LIMIT = 2000;\nconst LEVELS = new Set([\n \"trace\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\",\n \"fatal\",\n]);\n\nfunction parseMaybeJsonString(value: unknown) {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"{\") || !trimmed.endsWith(\"}\")) return null;\n try {\n const parsed = JSON.parse(trimmed) as unknown;\n if (!parsed || typeof parsed !== \"object\") return null;\n return parsed as Record;\n } catch {\n return null;\n }\n}\n\nfunction normalizeLevel(value: unknown): LogLevel | null {\n if (typeof value !== \"string\") return null;\n const lowered = value.toLowerCase() as LogLevel;\n return LEVELS.has(lowered) ? lowered : null;\n}\n\nexport function parseLogLine(line: string): LogEntry {\n if (!line.trim()) return { raw: line, message: line };\n try {\n const obj = JSON.parse(line) as Record;\n const meta =\n obj && typeof obj._meta === \"object\" && obj._meta !== null\n ? (obj._meta as Record)\n : null;\n const time =\n typeof obj.time === \"string\"\n ? obj.time\n : typeof meta?.date === \"string\"\n ? meta?.date\n : null;\n const level = normalizeLevel(meta?.logLevelName ?? meta?.level);\n\n const contextCandidate =\n typeof obj[\"0\"] === \"string\"\n ? (obj[\"0\"] as string)\n : typeof meta?.name === \"string\"\n ? (meta?.name as string)\n : null;\n const contextObj = parseMaybeJsonString(contextCandidate);\n let subsystem: string | null = null;\n if (contextObj) {\n if (typeof contextObj.subsystem === \"string\") subsystem = contextObj.subsystem;\n else if (typeof contextObj.module === \"string\") subsystem = contextObj.module;\n }\n if (!subsystem && contextCandidate && contextCandidate.length < 120) {\n subsystem = contextCandidate;\n }\n\n let message: string | null = null;\n if (typeof obj[\"1\"] === \"string\") message = obj[\"1\"] as string;\n else if (!contextObj && typeof obj[\"0\"] === \"string\") message = obj[\"0\"] as string;\n else if (typeof obj.message === \"string\") message = obj.message as string;\n\n return {\n raw: line,\n time,\n level,\n subsystem,\n message: message ?? line,\n meta: meta ?? undefined,\n };\n } catch {\n return { raw: line, message: line };\n }\n}\n\nexport async function loadLogs(\n state: LogsState,\n opts?: { reset?: boolean; quiet?: boolean },\n) {\n if (!state.client || !state.connected) return;\n if (state.logsLoading && !opts?.quiet) return;\n if (!opts?.quiet) state.logsLoading = true;\n state.logsError = null;\n try {\n const res = await state.client.request(\"logs.tail\", {\n cursor: opts?.reset ? undefined : state.logsCursor ?? undefined,\n limit: state.logsLimit,\n maxBytes: state.logsMaxBytes,\n });\n const payload = res as {\n file?: string;\n cursor?: number;\n size?: number;\n lines?: unknown;\n truncated?: boolean;\n reset?: boolean;\n };\n const lines = Array.isArray(payload.lines)\n ? (payload.lines.filter((line) => typeof line === \"string\") as string[])\n : [];\n const entries = lines.map(parseLogLine);\n const shouldReset = Boolean(opts?.reset || payload.reset || state.logsCursor == null);\n state.logsEntries = shouldReset\n ? entries\n : [...state.logsEntries, ...entries].slice(-LOG_BUFFER_LIMIT);\n if (typeof payload.cursor === \"number\") state.logsCursor = payload.cursor;\n if (typeof payload.file === \"string\") state.logsFile = payload.file;\n state.logsTruncated = Boolean(payload.truncated);\n state.logsLastFetchAt = Date.now();\n } catch (err) {\n state.logsError = String(err);\n } finally {\n if (!opts?.quiet) state.logsLoading = false;\n }\n}\n","/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */\n/**\n * 5KB JS implementation of ed25519 EdDSA signatures.\n * Compliant with RFC8032, FIPS 186-5 & ZIP215.\n * @module\n * @example\n * ```js\nimport * as ed from '@noble/ed25519';\n(async () => {\n const secretKey = ed.utils.randomSecretKey();\n const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);\n const pubKey = await ed.getPublicKeyAsync(secretKey); // Sync methods are also present\n const signature = await ed.signAsync(message, secretKey);\n const isValid = await ed.verifyAsync(signature, message, pubKey);\n})();\n```\n */\n/**\n * Curve params. ed25519 is twisted edwards curve. Equation is −x² + y² = -a + dx²y².\n * * P = `2n**255n - 19n` // field over which calculations are done\n * * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points\n * * h = 8 // cofactor\n * * a = `Fp.create(BigInt(-1))` // equation param\n * * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param\n * * Gx, Gy are coordinates of Generator / base point\n */\nconst ed25519_CURVE = {\n p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,\n n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,\n h: 8n,\n a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,\n d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,\n Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,\n Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,\n};\nconst { p: P, n: N, Gx, Gy, a: _a, d: _d, h } = ed25519_CURVE;\nconst L = 32; // field / group byte length\nconst L2 = 64;\n// Helpers and Precomputes sections are reused between libraries\n// ## Helpers\n// ----------\nconst captureTrace = (...args) => {\n if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(...args);\n }\n};\nconst err = (message = '') => {\n const e = new Error(message);\n captureTrace(e, err);\n throw e;\n};\nconst isBig = (n) => typeof n === 'bigint'; // is big integer\nconst isStr = (s) => typeof s === 'string'; // is string\nconst isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n/** Asserts something is Uint8Array. */\nconst abytes = (value, length, title = '') => {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n err(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);\n }\n return value;\n};\n/** create Uint8Array */\nconst u8n = (len) => new Uint8Array(len);\nconst u8fr = (buf) => Uint8Array.from(buf);\nconst padh = (n, pad) => n.toString(16).padStart(pad, '0');\nconst bytesToHex = (b) => Array.from(abytes(b))\n .map((e) => padh(e, 2))\n .join('');\nconst C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters\nconst _ch = (ch) => {\n if (ch >= C._0 && ch <= C._9)\n return ch - C._0; // '2' => 50-48\n if (ch >= C.A && ch <= C.F)\n return ch - (C.A - 10); // 'B' => 66-(65-10)\n if (ch >= C.a && ch <= C.f)\n return ch - (C.a - 10); // 'b' => 98-(97-10)\n return;\n};\nconst hexToBytes = (hex) => {\n const e = 'hex invalid';\n if (!isStr(hex))\n return err(e);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2)\n return err(e);\n const array = u8n(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n // treat each char as ASCII\n const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16\n const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char\n if (n1 === undefined || n2 === undefined)\n return err(e);\n array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9\n }\n return array;\n};\nconst cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments\nconst subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined, consider polyfill');\n// prettier-ignore\nconst concatBytes = (...arrs) => {\n const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); // create u8a of summed length\n let pad = 0; // walk through each array,\n arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type\n return r;\n};\n/** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */\nconst randomBytes = (len = L) => {\n const c = cr();\n return c.getRandomValues(u8n(len));\n};\nconst big = BigInt;\nconst assertRange = (n, min, max, msg = 'bad number: out of range') => (isBig(n) && min <= n && n < max ? n : err(msg));\n/** modular division */\nconst M = (a, b = P) => {\n const r = a % b;\n return r >= 0n ? r : b + r;\n};\nconst modN = (a) => M(a, N);\n/** Modular inversion using euclidean GCD (non-CT). No negative exponent for now. */\n// prettier-ignore\nconst invert = (num, md) => {\n if (num === 0n || md <= 0n)\n err('no inverse n=' + num + ' mod=' + md);\n let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;\n while (a !== 0n) {\n const q = b / a, r = b % a;\n const m = x - u * q, n = y - v * q;\n b = a, a = r, x = u, y = v, u = m, v = n;\n }\n return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point\n};\nconst callHash = (name) => {\n // @ts-ignore\n const fn = hashes[name];\n if (typeof fn !== 'function')\n err('hashes.' + name + ' not set');\n return fn;\n};\nconst hash = (msg) => callHash('sha512')(msg);\nconst apoint = (p) => (p instanceof Point ? p : err('Point expected'));\n// ## End of Helpers\n// -----------------\nconst B256 = 2n ** 256n;\n/** Point in XYZT extended coordinates. */\nclass Point {\n static BASE;\n static ZERO;\n X;\n Y;\n Z;\n T;\n constructor(X, Y, Z, T) {\n const max = B256;\n this.X = assertRange(X, 0n, max);\n this.Y = assertRange(Y, 0n, max);\n this.Z = assertRange(Z, 1n, max);\n this.T = assertRange(T, 0n, max);\n Object.freeze(this);\n }\n static CURVE() {\n return ed25519_CURVE;\n }\n static fromAffine(p) {\n return new Point(p.x, p.y, 1n, M(p.x * p.y));\n }\n /** RFC8032 5.1.3: Uint8Array to Point. */\n static fromBytes(hex, zip215 = false) {\n const d = _d;\n // Copy array to not mess it up.\n const normed = u8fr(abytes(hex, L));\n // adjust first LE byte = last BE byte\n const lastByte = hex[31];\n normed[31] = lastByte & ~0x80;\n const y = bytesToNumLE(normed);\n // zip215=true: 0 <= y < 2^256\n // zip215=false, RFC8032: 0 <= y < 2^255-19\n const max = zip215 ? B256 : P;\n assertRange(y, 0n, max);\n const y2 = M(y * y); // y²\n const u = M(y2 - 1n); // u=y²-1\n const v = M(d * y2 + 1n); // v=dy²+1\n let { isValid, value: x } = uvRatio(u, v); // (uv³)(uv⁷)^(p-5)/8; square root\n if (!isValid)\n err('bad point: y not sqrt'); // not square root: bad point\n const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate\n const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit\n if (!zip215 && x === 0n && isLastByteOdd)\n err('bad point: x==0, isLastByteOdd'); // x=0, x_0=1\n if (isLastByteOdd !== isXOdd)\n x = M(-x);\n return new Point(x, y, 1n, M(x * y)); // Z=1, T=xy\n }\n static fromHex(hex, zip215) {\n return Point.fromBytes(hexToBytes(hex), zip215);\n }\n get x() {\n return this.toAffine().x;\n }\n get y() {\n return this.toAffine().y;\n }\n /** Checks if the point is valid and on-curve. */\n assertValidity() {\n const a = _a;\n const d = _d;\n const p = this;\n if (p.is0())\n return err('bad point: ZERO'); // TODO: optimize, with vars below?\n // Equation in affine coordinates: ax² + y² = 1 + dx²y²\n // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²\n const { X, Y, Z, T } = p;\n const X2 = M(X * X); // X²\n const Y2 = M(Y * Y); // Y²\n const Z2 = M(Z * Z); // Z²\n const Z4 = M(Z2 * Z2); // Z⁴\n const aX2 = M(X2 * a); // aX²\n const left = M(Z2 * M(aX2 + Y2)); // (aX² + Y²)Z²\n const right = M(Z4 + M(d * M(X2 * Y2))); // Z⁴ + dX²Y²\n if (left !== right)\n return err('bad point: equation left != right (1)');\n // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T\n const XY = M(X * Y);\n const ZT = M(Z * T);\n if (XY !== ZT)\n return err('bad point: equation left != right (2)');\n return this;\n }\n /** Equality check: compare points P&Q. */\n equals(other) {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality\n const X1Z2 = M(X1 * Z2);\n const X2Z1 = M(X2 * Z1);\n const Y1Z2 = M(Y1 * Z2);\n const Y2Z1 = M(Y2 * Z1);\n return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;\n }\n is0() {\n return this.equals(I);\n }\n /** Flip point over y coordinate. */\n negate() {\n return new Point(M(-this.X), this.Y, this.Z, M(-this.T));\n }\n /** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */\n double() {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const a = _a;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd\n const A = M(X1 * X1);\n const B = M(Y1 * Y1);\n const C = M(2n * M(Z1 * Z1));\n const D = M(a * A);\n const x1y1 = X1 + Y1;\n const E = M(M(x1y1 * x1y1) - A - B);\n const G = D + B;\n const F = G - C;\n const H = D - B;\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n /** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */\n add(other) {\n const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;\n const { X: X2, Y: Y2, Z: Z2, T: T2 } = apoint(other); // doesn't check if other on-curve\n const a = _a;\n const d = _d;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3\n const A = M(X1 * X2);\n const B = M(Y1 * Y2);\n const C = M(T1 * d * T2);\n const D = M(Z1 * Z2);\n const E = M((X1 + Y1) * (X2 + Y2) - A - B);\n const F = M(D - C);\n const G = M(D + C);\n const H = M(B - a * A);\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n subtract(other) {\n return this.add(apoint(other).negate());\n }\n /**\n * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.\n * Uses {@link wNAF} for base point.\n * Uses fake point to mitigate side-channel leakage.\n * @param n scalar by which point is multiplied\n * @param safe safe mode guards against timing attacks; unsafe mode is faster\n */\n multiply(n, safe = true) {\n if (!safe && (n === 0n || this.is0()))\n return I;\n assertRange(n, 1n, N);\n if (n === 1n)\n return this;\n if (this.equals(G))\n return wNAF(n).p;\n // init result point & fake point\n let p = I;\n let f = G;\n for (let d = this; n > 0n; d = d.double(), n >>= 1n) {\n // if bit is present, add to point\n // if not present, add to fake, for timing safety\n if (n & 1n)\n p = p.add(d);\n else if (safe)\n f = f.add(d);\n }\n return p;\n }\n multiplyUnsafe(scalar) {\n return this.multiply(scalar, false);\n }\n /** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */\n toAffine() {\n const { X, Y, Z } = this;\n // fast-paths for ZERO point OR Z=1\n if (this.equals(I))\n return { x: 0n, y: 1n };\n const iz = invert(Z, P);\n // (Z * Z^-1) must be 1, otherwise bad math\n if (M(Z * iz) !== 1n)\n err('invalid inverse');\n // x = X*Z^-1; y = Y*Z^-1\n const x = M(X * iz);\n const y = M(Y * iz);\n return { x, y };\n }\n toBytes() {\n const { x, y } = this.assertValidity().toAffine();\n const b = numTo32bLE(y);\n // store sign in first LE byte\n b[31] |= x & 1n ? 0x80 : 0;\n return b;\n }\n toHex() {\n return bytesToHex(this.toBytes());\n }\n clearCofactor() {\n return this.multiply(big(h), false);\n }\n isSmallOrder() {\n return this.clearCofactor().is0();\n }\n isTorsionFree() {\n // Multiply by big number N. We can't `mul(N)` because of checks. Instead, we `mul(N/2)*2+1`\n let p = this.multiply(N / 2n, false).double();\n if (N % 2n)\n p = p.add(this);\n return p.is0();\n }\n}\n/** Generator / base point */\nconst G = new Point(Gx, Gy, 1n, M(Gx * Gy));\n/** Identity / zero point */\nconst I = new Point(0n, 1n, 1n, 0n);\n// Static aliases\nPoint.BASE = G;\nPoint.ZERO = I;\nconst numTo32bLE = (num) => hexToBytes(padh(assertRange(num, 0n, B256), L2)).reverse();\nconst bytesToNumLE = (b) => big('0x' + bytesToHex(u8fr(abytes(b)).reverse()));\nconst pow2 = (x, power) => {\n // pow2(x, 4) == x^(2^4)\n let r = x;\n while (power-- > 0n) {\n r *= r;\n r %= P;\n }\n return r;\n};\n// prettier-ignore\nconst pow_2_252_3 = (x) => {\n const x2 = (x * x) % P; // x^2, bits 1\n const b2 = (x2 * x) % P; // x^3, bits 11\n const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111\n const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111\n const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)\n const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)\n const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)\n const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)\n const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)\n const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)\n const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)\n const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.\n return { pow_p_5_8, b2 };\n};\nconst RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; // √-1\n// for sqrt comp\n// prettier-ignore\nconst uvRatio = (u, v) => {\n const v3 = M(v * v * v); // v³\n const v7 = M(v3 * v3 * v); // v⁷\n const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv⁷)^(p-5)/8\n let x = M(u * v3 * pow); // (uv³)(uv⁷)^(p-5)/8\n const vx2 = M(v * x * x); // vx²\n const root1 = x; // First root candidate\n const root2 = M(x * RM1); // Second root candidate; RM1 is √-1\n const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root\n const useRoot2 = vx2 === M(-u); // If vx² = -u, set x <-- x * 2^((p-1)/4)\n const noRoot = vx2 === M(-u * RM1); // There is no valid root, vx² = -u√-1\n if (useRoot1)\n x = root1;\n if (useRoot2 || noRoot)\n x = root2; // We return root2 anyway, for const-time\n if ((M(x) & 1n) === 1n)\n x = M(-x); // edIsNegative\n return { isValid: useRoot1 || useRoot2, value: x };\n};\n// N == L, just weird naming\nconst modL_LE = (hash) => modN(bytesToNumLE(hash)); // modulo L; but little-endian\n/** hashes.sha512 should conform to the interface. */\n// TODO: rename\nconst sha512a = (...m) => hashes.sha512Async(concatBytes(...m)); // Async SHA512\nconst sha512s = (...m) => callHash('sha512')(concatBytes(...m));\n// RFC8032 5.1.5\nconst hash2extK = (hashed) => {\n // slice creates a copy, unlike subarray\n const head = hashed.slice(0, L);\n head[0] &= 248; // Clamp bits: 0b1111_1000\n head[31] &= 127; // 0b0111_1111\n head[31] |= 64; // 0b0100_0000\n const prefix = hashed.slice(L, L2); // secret key \"prefix\"\n const scalar = modL_LE(head); // modular division over curve order\n const point = G.multiply(scalar); // public key point\n const pointBytes = point.toBytes(); // point serialized to Uint8Array\n return { head, prefix, scalar, point, pointBytes };\n};\n// RFC8032 5.1.5; getPublicKey async, sync. Hash priv key and extract point.\nconst getExtendedPublicKeyAsync = (secretKey) => sha512a(abytes(secretKey, L)).then(hash2extK);\nconst getExtendedPublicKey = (secretKey) => hash2extK(sha512s(abytes(secretKey, L)));\n/** Creates 32-byte ed25519 public key from 32-byte secret key. Async. */\nconst getPublicKeyAsync = (secretKey) => getExtendedPublicKeyAsync(secretKey).then((p) => p.pointBytes);\n/** Creates 32-byte ed25519 public key from 32-byte secret key. To use, set `hashes.sha512` first. */\nconst getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;\nconst hashFinishA = (res) => sha512a(res.hashable).then(res.finish);\nconst hashFinishS = (res) => res.finish(sha512s(res.hashable));\n// Code, shared between sync & async sign\nconst _sign = (e, rBytes, msg) => {\n const { pointBytes: P, scalar: s } = e;\n const r = modL_LE(rBytes); // r was created outside, reduce it modulo L\n const R = G.multiply(r).toBytes(); // R = [r]B\n const hashable = concatBytes(R, P, msg); // dom2(F, C) || R || A || PH(M)\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n const S = modN(r + modL_LE(hashed) * s); // S = (r + k * s) mod L; 0 <= s < l\n return abytes(concatBytes(R, numTo32bLE(S)), L2); // 64-byte sig: 32b R.x + 32b LE(S)\n };\n return { hashable, finish };\n};\n/**\n * Signs message using secret key. Async.\n * Follows RFC8032 5.1.6.\n */\nconst signAsync = async (message, secretKey) => {\n const m = abytes(message);\n const e = await getExtendedPublicKeyAsync(secretKey);\n const rBytes = await sha512a(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishA(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\n/**\n * Signs message using secret key. To use, set `hashes.sha512` first.\n * Follows RFC8032 5.1.6.\n */\nconst sign = (message, secretKey) => {\n const m = abytes(message);\n const e = getExtendedPublicKey(secretKey);\n const rBytes = sha512s(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishS(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\nconst defaultVerifyOpts = { zip215: true };\nconst _verify = (sig, msg, pub, opts = defaultVerifyOpts) => {\n sig = abytes(sig, L2); // Signature hex str/Bytes, must be 64 bytes\n msg = abytes(msg); // Message hex str/Bytes\n pub = abytes(pub, L);\n const { zip215 } = opts; // switch between zip215 and rfc8032 verif\n let A;\n let R;\n let s;\n let SB;\n let hashable = Uint8Array.of();\n try {\n A = Point.fromBytes(pub, zip215); // public key A decoded\n R = Point.fromBytes(sig.slice(0, L), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P\n s = bytesToNumLE(sig.slice(L, L2)); // Decode second half as an integer S\n SB = G.multiply(s, false); // in the range 0 <= s < L\n hashable = concatBytes(R.toBytes(), A.toBytes(), msg); // dom2(F, C) || R || A || PH(M)\n }\n catch (error) { }\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n if (SB == null)\n return false; // false if try-catch catched an error\n if (!zip215 && A.isSmallOrder())\n return false; // false for SBS: Strongly Binding Signature\n const k = modL_LE(hashed); // decode in little-endian, modulo L\n const RkA = R.add(A.multiply(k, false)); // [8]R + [8][k]A'\n return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A'\n };\n return { hashable, finish };\n};\n/** Verifies signature on message and public key. Async. Follows RFC8032 5.1.7. */\nconst verifyAsync = async (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishA(_verify(signature, message, publicKey, opts));\n/** Verifies signature on message and public key. To use, set `hashes.sha512` first. Follows RFC8032 5.1.7. */\nconst verify = (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishS(_verify(signature, message, publicKey, opts));\n/** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */\nconst etc = {\n bytesToHex: bytesToHex,\n hexToBytes: hexToBytes,\n concatBytes: concatBytes,\n mod: M,\n invert: invert,\n randomBytes: randomBytes,\n};\nconst hashes = {\n sha512Async: async (message) => {\n const s = subtle();\n const m = concatBytes(message);\n return u8n(await s.digest('SHA-512', m.buffer));\n },\n sha512: undefined,\n};\n// FIPS 186 B.4.1 compliant key generation produces private keys\n// with modulo bias being neglible. takes >N+16 bytes, returns (hash mod n-1)+1\nconst randomSecretKey = (seed = randomBytes(L)) => seed;\nconst keygen = (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = getPublicKey(secretKey);\n return { secretKey, publicKey };\n};\nconst keygenAsync = async (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = await getPublicKeyAsync(secretKey);\n return { secretKey, publicKey };\n};\n/** ed25519-specific key utilities. */\nconst utils = {\n getExtendedPublicKeyAsync: getExtendedPublicKeyAsync,\n getExtendedPublicKey: getExtendedPublicKey,\n randomSecretKey: randomSecretKey,\n};\n// ## Precomputes\n// --------------\nconst W = 8; // W is window size\nconst scalarBits = 256;\nconst pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8, NOT 32 - see wNAF loop\nconst pwindowSize = 2 ** (W - 1); // 128 for W=8\nconst precompute = () => {\n const points = [];\n let p = G;\n let b = p;\n for (let w = 0; w < pwindows; w++) {\n b = p;\n points.push(b);\n for (let i = 1; i < pwindowSize; i++) {\n b = b.add(p);\n points.push(b);\n } // i=1, bc we skip 0\n p = b.double();\n }\n return points;\n};\nlet Gpows = undefined; // precomputes for base point G\n// const-time negate\nconst ctneg = (cnd, p) => {\n const n = p.negate();\n return cnd ? n : p;\n};\n/**\n * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by\n * caching multiples of G (base point). Cache is stored in 32MB of RAM.\n * Any time `G.multiply` is done, precomputes are used.\n * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.\n *\n * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,\n * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.\n *\n * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().\n */\nconst wNAF = (n) => {\n const comp = Gpows || (Gpows = precompute());\n let p = I;\n let f = G; // f must be G, or could become I in the end\n const pow_2_w = 2 ** W; // 256 for W=8\n const maxNum = pow_2_w; // 256 for W=8\n const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111\n const shiftBy = big(W); // 8 for W=8\n for (let w = 0; w < pwindows; w++) {\n let wbits = Number(n & mask); // extract W bits.\n n >>= shiftBy; // shift number by W bits.\n // We use negative indexes to reduce size of precomputed table by 2x.\n // Instead of needing precomputes 0..256, we only calculate them for 0..128.\n // If an index > 128 is found, we do (256-index) - where 256 is next window.\n // Naive: index +127 => 127, +224 => 224\n // Optimized: index +127 => 127, +224 => 256-32\n if (wbits > pwindowSize) {\n wbits -= maxNum;\n n += 1n;\n }\n const off = w * pwindowSize;\n const offF = off; // offsets, evaluate both\n const offP = off + Math.abs(wbits) - 1;\n const isEven = w % 2 !== 0; // conditions, evaluate both\n const isNeg = wbits < 0;\n if (wbits === 0) {\n // off == I: can't add it. Adding random offF instead.\n f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point\n }\n else {\n p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point\n }\n }\n if (n !== 0n)\n err('invalid wnaf');\n return { p, f }; // return both real and fake points for JIT\n};\n// !! Remove the export to easily use in REPL / browser console\nexport { etc, getPublicKey, getPublicKeyAsync, hash, hashes, keygen, keygenAsync, Point, sign, signAsync, utils, verify, verifyAsync, };\n","import { getPublicKeyAsync, signAsync, utils } from \"@noble/ed25519\";\n\ntype StoredIdentity = {\n version: 1;\n deviceId: string;\n publicKey: string;\n privateKey: string;\n createdAtMs: number;\n};\n\nexport type DeviceIdentity = {\n deviceId: string;\n publicKey: string;\n privateKey: string;\n};\n\nconst STORAGE_KEY = \"clawdbot-device-identity-v1\";\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/g, \"\");\n}\n\nfunction base64UrlDecode(input: string): Uint8Array {\n const normalized = input.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\");\n const padded = normalized + \"=\".repeat((4 - (normalized.length % 4)) % 4);\n const binary = atob(padded);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);\n return out;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nasync function fingerprintPublicKey(publicKey: Uint8Array): Promise {\n const hash = await crypto.subtle.digest(\"SHA-256\", publicKey);\n return bytesToHex(new Uint8Array(hash));\n}\n\nasync function generateIdentity(): Promise {\n const privateKey = utils.randomSecretKey();\n const publicKey = await getPublicKeyAsync(privateKey);\n const deviceId = await fingerprintPublicKey(publicKey);\n return {\n deviceId,\n publicKey: base64UrlEncode(publicKey),\n privateKey: base64UrlEncode(privateKey),\n };\n}\n\nexport async function loadOrCreateDeviceIdentity(): Promise {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (raw) {\n const parsed = JSON.parse(raw) as StoredIdentity;\n if (\n parsed?.version === 1 &&\n typeof parsed.deviceId === \"string\" &&\n typeof parsed.publicKey === \"string\" &&\n typeof parsed.privateKey === \"string\"\n ) {\n const derivedId = await fingerprintPublicKey(base64UrlDecode(parsed.publicKey));\n if (derivedId !== parsed.deviceId) {\n const updated: StoredIdentity = {\n ...parsed,\n deviceId: derivedId,\n };\n localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));\n return {\n deviceId: derivedId,\n publicKey: parsed.publicKey,\n privateKey: parsed.privateKey,\n };\n }\n return {\n deviceId: parsed.deviceId,\n publicKey: parsed.publicKey,\n privateKey: parsed.privateKey,\n };\n }\n }\n } catch {\n // fall through to regenerate\n }\n\n const identity = await generateIdentity();\n const stored: StoredIdentity = {\n version: 1,\n deviceId: identity.deviceId,\n publicKey: identity.publicKey,\n privateKey: identity.privateKey,\n createdAtMs: Date.now(),\n };\n localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));\n return identity;\n}\n\nexport async function signDevicePayload(privateKeyBase64Url: string, payload: string) {\n const key = base64UrlDecode(privateKeyBase64Url);\n const data = new TextEncoder().encode(payload);\n const sig = await signAsync(data, key);\n return base64UrlEncode(sig);\n}\n","export type DeviceAuthEntry = {\n token: string;\n role: string;\n scopes: string[];\n updatedAtMs: number;\n};\n\ntype DeviceAuthStore = {\n version: 1;\n deviceId: string;\n tokens: Record;\n};\n\nconst STORAGE_KEY = \"clawdbot.device.auth.v1\";\n\nfunction normalizeRole(role: string): string {\n return role.trim();\n}\n\nfunction normalizeScopes(scopes: string[] | undefined): string[] {\n if (!Array.isArray(scopes)) return [];\n const out = new Set();\n for (const scope of scopes) {\n const trimmed = scope.trim();\n if (trimmed) out.add(trimmed);\n }\n return [...out].sort();\n}\n\nfunction readStore(): DeviceAuthStore | null {\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as DeviceAuthStore;\n if (!parsed || parsed.version !== 1) return null;\n if (!parsed.deviceId || typeof parsed.deviceId !== \"string\") return null;\n if (!parsed.tokens || typeof parsed.tokens !== \"object\") return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction writeStore(store: DeviceAuthStore) {\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(store));\n } catch {\n // best-effort\n }\n}\n\nexport function loadDeviceAuthToken(params: {\n deviceId: string;\n role: string;\n}): DeviceAuthEntry | null {\n const store = readStore();\n if (!store || store.deviceId !== params.deviceId) return null;\n const role = normalizeRole(params.role);\n const entry = store.tokens[role];\n if (!entry || typeof entry.token !== \"string\") return null;\n return entry;\n}\n\nexport function storeDeviceAuthToken(params: {\n deviceId: string;\n role: string;\n token: string;\n scopes?: string[];\n}): DeviceAuthEntry {\n const role = normalizeRole(params.role);\n const next: DeviceAuthStore = {\n version: 1,\n deviceId: params.deviceId,\n tokens: {},\n };\n const existing = readStore();\n if (existing && existing.deviceId === params.deviceId) {\n next.tokens = { ...existing.tokens };\n }\n const entry: DeviceAuthEntry = {\n token: params.token,\n role,\n scopes: normalizeScopes(params.scopes),\n updatedAtMs: Date.now(),\n };\n next.tokens[role] = entry;\n writeStore(next);\n return entry;\n}\n\nexport function clearDeviceAuthToken(params: { deviceId: string; role: string }) {\n const store = readStore();\n if (!store || store.deviceId !== params.deviceId) return;\n const role = normalizeRole(params.role);\n if (!store.tokens[role]) return;\n const next = { ...store, tokens: { ...store.tokens } };\n delete next.tokens[role];\n writeStore(next);\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { loadOrCreateDeviceIdentity } from \"../device-identity\";\nimport { clearDeviceAuthToken, storeDeviceAuthToken } from \"../device-auth\";\n\nexport type DeviceTokenSummary = {\n role: string;\n scopes?: string[];\n createdAtMs?: number;\n rotatedAtMs?: number;\n revokedAtMs?: number;\n lastUsedAtMs?: number;\n};\n\nexport type PendingDevice = {\n requestId: string;\n deviceId: string;\n displayName?: string;\n role?: string;\n remoteIp?: string;\n isRepair?: boolean;\n ts?: number;\n};\n\nexport type PairedDevice = {\n deviceId: string;\n displayName?: string;\n roles?: string[];\n scopes?: string[];\n remoteIp?: string;\n tokens?: DeviceTokenSummary[];\n createdAtMs?: number;\n approvedAtMs?: number;\n};\n\nexport type DevicePairingList = {\n pending: PendingDevice[];\n paired: PairedDevice[];\n};\n\nexport type DevicesState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n devicesLoading: boolean;\n devicesError: string | null;\n devicesList: DevicePairingList | null;\n};\n\nexport async function loadDevices(state: DevicesState, opts?: { quiet?: boolean }) {\n if (!state.client || !state.connected) return;\n if (state.devicesLoading) return;\n state.devicesLoading = true;\n if (!opts?.quiet) state.devicesError = null;\n try {\n const res = (await state.client.request(\"device.pair.list\", {})) as DevicePairingList | null;\n state.devicesList = {\n pending: Array.isArray(res?.pending) ? res!.pending : [],\n paired: Array.isArray(res?.paired) ? res!.paired : [],\n };\n } catch (err) {\n if (!opts?.quiet) state.devicesError = String(err);\n } finally {\n state.devicesLoading = false;\n }\n}\n\nexport async function approveDevicePairing(state: DevicesState, requestId: string) {\n if (!state.client || !state.connected) return;\n try {\n await state.client.request(\"device.pair.approve\", { requestId });\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n\nexport async function rejectDevicePairing(state: DevicesState, requestId: string) {\n if (!state.client || !state.connected) return;\n const confirmed = window.confirm(\"Reject this device pairing request?\");\n if (!confirmed) return;\n try {\n await state.client.request(\"device.pair.reject\", { requestId });\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n\nexport async function rotateDeviceToken(\n state: DevicesState,\n params: { deviceId: string; role: string; scopes?: string[] },\n) {\n if (!state.client || !state.connected) return;\n try {\n const res = (await state.client.request(\"device.token.rotate\", params)) as\n | { token?: string; role?: string; deviceId?: string; scopes?: string[] }\n | undefined;\n if (res?.token) {\n const identity = await loadOrCreateDeviceIdentity();\n const role = res.role ?? params.role;\n if (res.deviceId === identity.deviceId || params.deviceId === identity.deviceId) {\n storeDeviceAuthToken({\n deviceId: identity.deviceId,\n role,\n token: res.token,\n scopes: res.scopes ?? params.scopes ?? [],\n });\n }\n window.prompt(\"New device token (copy and store securely):\", res.token);\n }\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n\nexport async function revokeDeviceToken(\n state: DevicesState,\n params: { deviceId: string; role: string },\n) {\n if (!state.client || !state.connected) return;\n const confirmed = window.confirm(\n `Revoke token for ${params.deviceId} (${params.role})?`,\n );\n if (!confirmed) return;\n try {\n await state.client.request(\"device.token.revoke\", params);\n const identity = await loadOrCreateDeviceIdentity();\n if (params.deviceId === identity.deviceId) {\n clearDeviceAuthToken({ deviceId: identity.deviceId, role: params.role });\n }\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\n\nexport type NodesState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n nodesLoading: boolean;\n nodes: Array>;\n lastError: string | null;\n};\n\nexport async function loadNodes(\n state: NodesState,\n opts?: { quiet?: boolean },\n) {\n if (!state.client || !state.connected) return;\n if (state.nodesLoading) return;\n state.nodesLoading = true;\n if (!opts?.quiet) state.lastError = null;\n try {\n const res = (await state.client.request(\"node.list\", {})) as {\n nodes?: Array>;\n };\n state.nodes = Array.isArray(res.nodes) ? res.nodes : [];\n } catch (err) {\n if (!opts?.quiet) state.lastError = String(err);\n } finally {\n state.nodesLoading = false;\n }\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { cloneConfigObject, removePathValue, setPathValue } from \"./config/form-utils\";\n\nexport type ExecApprovalsDefaults = {\n security?: string;\n ask?: string;\n askFallback?: string;\n autoAllowSkills?: boolean;\n};\n\nexport type ExecApprovalsAllowlistEntry = {\n id?: string;\n pattern: string;\n lastUsedAt?: number;\n lastUsedCommand?: string;\n lastResolvedPath?: string;\n};\n\nexport type ExecApprovalsAgent = ExecApprovalsDefaults & {\n allowlist?: ExecApprovalsAllowlistEntry[];\n};\n\nexport type ExecApprovalsFile = {\n version?: number;\n socket?: { path?: string };\n defaults?: ExecApprovalsDefaults;\n agents?: Record;\n};\n\nexport type ExecApprovalsSnapshot = {\n path: string;\n exists: boolean;\n hash: string;\n file: ExecApprovalsFile;\n};\n\nexport type ExecApprovalsTarget =\n | { kind: \"gateway\" }\n | { kind: \"node\"; nodeId: string };\n\nexport type ExecApprovalsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n execApprovalsLoading: boolean;\n execApprovalsSaving: boolean;\n execApprovalsDirty: boolean;\n execApprovalsSnapshot: ExecApprovalsSnapshot | null;\n execApprovalsForm: ExecApprovalsFile | null;\n execApprovalsSelectedAgent: string | null;\n lastError: string | null;\n};\n\nfunction resolveExecApprovalsRpc(target?: ExecApprovalsTarget | null): {\n method: string;\n params: Record;\n} | null {\n if (!target || target.kind === \"gateway\") {\n return { method: \"exec.approvals.get\", params: {} };\n }\n const nodeId = target.nodeId.trim();\n if (!nodeId) return null;\n return { method: \"exec.approvals.node.get\", params: { nodeId } };\n}\n\nfunction resolveExecApprovalsSaveRpc(\n target: ExecApprovalsTarget | null | undefined,\n params: { file: ExecApprovalsFile; baseHash: string },\n): { method: string; params: Record } | null {\n if (!target || target.kind === \"gateway\") {\n return { method: \"exec.approvals.set\", params };\n }\n const nodeId = target.nodeId.trim();\n if (!nodeId) return null;\n return { method: \"exec.approvals.node.set\", params: { ...params, nodeId } };\n}\n\nexport async function loadExecApprovals(\n state: ExecApprovalsState,\n target?: ExecApprovalsTarget | null,\n) {\n if (!state.client || !state.connected) return;\n if (state.execApprovalsLoading) return;\n state.execApprovalsLoading = true;\n state.lastError = null;\n try {\n const rpc = resolveExecApprovalsRpc(target);\n if (!rpc) {\n state.lastError = \"Select a node before loading exec approvals.\";\n return;\n }\n const res = (await state.client.request(rpc.method, rpc.params)) as ExecApprovalsSnapshot;\n applyExecApprovalsSnapshot(state, res);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.execApprovalsLoading = false;\n }\n}\n\nexport function applyExecApprovalsSnapshot(\n state: ExecApprovalsState,\n snapshot: ExecApprovalsSnapshot,\n) {\n state.execApprovalsSnapshot = snapshot;\n if (!state.execApprovalsDirty) {\n state.execApprovalsForm = cloneConfigObject(snapshot.file ?? {});\n }\n}\n\nexport async function saveExecApprovals(\n state: ExecApprovalsState,\n target?: ExecApprovalsTarget | null,\n) {\n if (!state.client || !state.connected) return;\n state.execApprovalsSaving = true;\n state.lastError = null;\n try {\n const baseHash = state.execApprovalsSnapshot?.hash;\n if (!baseHash) {\n state.lastError = \"Exec approvals hash missing; reload and retry.\";\n return;\n }\n const file =\n state.execApprovalsForm ??\n state.execApprovalsSnapshot?.file ??\n {};\n const rpc = resolveExecApprovalsSaveRpc(target, { file, baseHash });\n if (!rpc) {\n state.lastError = \"Select a node before saving exec approvals.\";\n return;\n }\n await state.client.request(rpc.method, rpc.params);\n state.execApprovalsDirty = false;\n await loadExecApprovals(state, target);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.execApprovalsSaving = false;\n }\n}\n\nexport function updateExecApprovalsFormValue(\n state: ExecApprovalsState,\n path: Array,\n value: unknown,\n) {\n const base = cloneConfigObject(\n state.execApprovalsForm ?? state.execApprovalsSnapshot?.file ?? {},\n );\n setPathValue(base, path, value);\n state.execApprovalsForm = base;\n state.execApprovalsDirty = true;\n}\n\nexport function removeExecApprovalsFormValue(\n state: ExecApprovalsState,\n path: Array,\n) {\n const base = cloneConfigObject(\n state.execApprovalsForm ?? state.execApprovalsSnapshot?.file ?? {},\n );\n removePathValue(base, path);\n state.execApprovalsForm = base;\n state.execApprovalsDirty = true;\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { PresenceEntry } from \"../types\";\n\nexport type PresenceState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n presenceLoading: boolean;\n presenceEntries: PresenceEntry[];\n presenceError: string | null;\n presenceStatus: string | null;\n};\n\nexport async function loadPresence(state: PresenceState) {\n if (!state.client || !state.connected) return;\n if (state.presenceLoading) return;\n state.presenceLoading = true;\n state.presenceError = null;\n state.presenceStatus = null;\n try {\n const res = (await state.client.request(\"system-presence\", {})) as\n | PresenceEntry[]\n | undefined;\n if (Array.isArray(res)) {\n state.presenceEntries = res;\n state.presenceStatus = res.length === 0 ? \"No instances yet.\" : null;\n } else {\n state.presenceEntries = [];\n state.presenceStatus = \"No presence payload.\";\n }\n } catch (err) {\n state.presenceError = String(err);\n } finally {\n state.presenceLoading = false;\n }\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { SkillStatusReport } from \"../types\";\n\nexport type SkillsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n skillsLoading: boolean;\n skillsReport: SkillStatusReport | null;\n skillsError: string | null;\n skillsBusyKey: string | null;\n skillEdits: Record;\n skillMessages: SkillMessageMap;\n};\n\nexport type SkillMessage = {\n kind: \"success\" | \"error\";\n message: string;\n};\n\nexport type SkillMessageMap = Record;\n\ntype LoadSkillsOptions = {\n clearMessages?: boolean;\n};\n\nfunction setSkillMessage(state: SkillsState, key: string, message?: SkillMessage) {\n if (!key.trim()) return;\n const next = { ...state.skillMessages };\n if (message) next[key] = message;\n else delete next[key];\n state.skillMessages = next;\n}\n\nfunction getErrorMessage(err: unknown) {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\nexport async function loadSkills(state: SkillsState, options?: LoadSkillsOptions) {\n if (options?.clearMessages && Object.keys(state.skillMessages).length > 0) {\n state.skillMessages = {};\n }\n if (!state.client || !state.connected) return;\n if (state.skillsLoading) return;\n state.skillsLoading = true;\n state.skillsError = null;\n try {\n const res = (await state.client.request(\"skills.status\", {})) as\n | SkillStatusReport\n | undefined;\n if (res) state.skillsReport = res;\n } catch (err) {\n state.skillsError = getErrorMessage(err);\n } finally {\n state.skillsLoading = false;\n }\n}\n\nexport function updateSkillEdit(\n state: SkillsState,\n skillKey: string,\n value: string,\n) {\n state.skillEdits = { ...state.skillEdits, [skillKey]: value };\n}\n\nexport async function updateSkillEnabled(\n state: SkillsState,\n skillKey: string,\n enabled: boolean,\n) {\n if (!state.client || !state.connected) return;\n state.skillsBusyKey = skillKey;\n state.skillsError = null;\n try {\n await state.client.request(\"skills.update\", { skillKey, enabled });\n await loadSkills(state);\n setSkillMessage(state, skillKey, {\n kind: \"success\",\n message: enabled ? \"Skill enabled\" : \"Skill disabled\",\n });\n } catch (err) {\n const message = getErrorMessage(err);\n state.skillsError = message;\n setSkillMessage(state, skillKey, {\n kind: \"error\",\n message,\n });\n } finally {\n state.skillsBusyKey = null;\n }\n}\n\nexport async function saveSkillApiKey(state: SkillsState, skillKey: string) {\n if (!state.client || !state.connected) return;\n state.skillsBusyKey = skillKey;\n state.skillsError = null;\n try {\n const apiKey = state.skillEdits[skillKey] ?? \"\";\n await state.client.request(\"skills.update\", { skillKey, apiKey });\n await loadSkills(state);\n setSkillMessage(state, skillKey, {\n kind: \"success\",\n message: \"API key saved\",\n });\n } catch (err) {\n const message = getErrorMessage(err);\n state.skillsError = message;\n setSkillMessage(state, skillKey, {\n kind: \"error\",\n message,\n });\n } finally {\n state.skillsBusyKey = null;\n }\n}\n\nexport async function installSkill(\n state: SkillsState,\n skillKey: string,\n name: string,\n installId: string,\n) {\n if (!state.client || !state.connected) return;\n state.skillsBusyKey = skillKey;\n state.skillsError = null;\n try {\n const result = (await state.client.request(\"skills.install\", {\n name,\n installId,\n timeoutMs: 120000,\n })) as { ok?: boolean; message?: string };\n await loadSkills(state);\n setSkillMessage(state, skillKey, {\n kind: \"success\",\n message: result?.message ?? \"Installed\",\n });\n } catch (err) {\n const message = getErrorMessage(err);\n state.skillsError = message;\n setSkillMessage(state, skillKey, {\n kind: \"error\",\n message,\n });\n } finally {\n state.skillsBusyKey = null;\n }\n}\n","export type ThemeMode = \"system\" | \"light\" | \"dark\";\nexport type ResolvedTheme = \"light\" | \"dark\";\n\nexport function getSystemTheme(): ResolvedTheme {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return \"dark\";\n }\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n ? \"dark\"\n : \"light\";\n}\n\nexport function resolveTheme(mode: ThemeMode): ResolvedTheme {\n if (mode === \"system\") return getSystemTheme();\n return mode;\n}\n","import type { ThemeMode } from \"./theme\";\n\nexport type ThemeTransitionContext = {\n element?: HTMLElement | null;\n pointerClientX?: number;\n pointerClientY?: number;\n};\n\nexport type ThemeTransitionOptions = {\n nextTheme: ThemeMode;\n applyTheme: () => void;\n context?: ThemeTransitionContext;\n currentTheme?: ThemeMode | null;\n};\n\ntype DocumentWithViewTransition = Document & {\n startViewTransition?: (callback: () => void) => { finished: Promise };\n};\n\nconst clamp01 = (value: number) => {\n if (Number.isNaN(value)) return 0.5;\n if (value <= 0) return 0;\n if (value >= 1) return 1;\n return value;\n};\n\nconst hasReducedMotionPreference = () => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return false;\n }\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ?? false;\n};\n\nconst cleanupThemeTransition = (root: HTMLElement) => {\n root.classList.remove(\"theme-transition\");\n root.style.removeProperty(\"--theme-switch-x\");\n root.style.removeProperty(\"--theme-switch-y\");\n};\n\nexport const startThemeTransition = ({\n nextTheme,\n applyTheme,\n context,\n currentTheme,\n}: ThemeTransitionOptions) => {\n if (currentTheme === nextTheme) return;\n\n const documentReference = globalThis.document ?? null;\n if (!documentReference) {\n applyTheme();\n return;\n }\n\n const root = documentReference.documentElement;\n const document_ = documentReference as DocumentWithViewTransition;\n const prefersReducedMotion = hasReducedMotionPreference();\n\n const canUseViewTransition =\n Boolean(document_.startViewTransition) && !prefersReducedMotion;\n\n if (canUseViewTransition) {\n let xPercent = 0.5;\n let yPercent = 0.5;\n\n if (\n context?.pointerClientX !== undefined &&\n context?.pointerClientY !== undefined &&\n typeof window !== \"undefined\"\n ) {\n xPercent = clamp01(context.pointerClientX / window.innerWidth);\n yPercent = clamp01(context.pointerClientY / window.innerHeight);\n } else if (context?.element) {\n const rect = context.element.getBoundingClientRect();\n if (\n rect.width > 0 &&\n rect.height > 0 &&\n typeof window !== \"undefined\"\n ) {\n xPercent = clamp01((rect.left + rect.width / 2) / window.innerWidth);\n yPercent = clamp01((rect.top + rect.height / 2) / window.innerHeight);\n }\n }\n\n root.style.setProperty(\"--theme-switch-x\", `${xPercent * 100}%`);\n root.style.setProperty(\"--theme-switch-y\", `${yPercent * 100}%`);\n root.classList.add(\"theme-transition\");\n\n try {\n const transition = document_.startViewTransition?.(() => {\n applyTheme();\n });\n if (transition?.finished) {\n void transition.finished.finally(() => cleanupThemeTransition(root));\n } else {\n cleanupThemeTransition(root);\n }\n } catch {\n cleanupThemeTransition(root);\n applyTheme();\n }\n return;\n }\n\n applyTheme();\n cleanupThemeTransition(root);\n};\n","import { loadLogs } from \"./controllers/logs\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadDebug } from \"./controllers/debug\";\nimport type { ClawdbotApp } from \"./app\";\n\ntype PollingHost = {\n nodesPollInterval: number | null;\n logsPollInterval: number | null;\n debugPollInterval: number | null;\n tab: string;\n};\n\nexport function startNodesPolling(host: PollingHost) {\n if (host.nodesPollInterval != null) return;\n host.nodesPollInterval = window.setInterval(\n () => void loadNodes(host as unknown as ClawdbotApp, { quiet: true }),\n 5000,\n );\n}\n\nexport function stopNodesPolling(host: PollingHost) {\n if (host.nodesPollInterval == null) return;\n clearInterval(host.nodesPollInterval);\n host.nodesPollInterval = null;\n}\n\nexport function startLogsPolling(host: PollingHost) {\n if (host.logsPollInterval != null) return;\n host.logsPollInterval = window.setInterval(() => {\n if (host.tab !== \"logs\") return;\n void loadLogs(host as unknown as ClawdbotApp, { quiet: true });\n }, 2000);\n}\n\nexport function stopLogsPolling(host: PollingHost) {\n if (host.logsPollInterval == null) return;\n clearInterval(host.logsPollInterval);\n host.logsPollInterval = null;\n}\n\nexport function startDebugPolling(host: PollingHost) {\n if (host.debugPollInterval != null) return;\n host.debugPollInterval = window.setInterval(() => {\n if (host.tab !== \"debug\") return;\n void loadDebug(host as unknown as ClawdbotApp);\n }, 3000);\n}\n\nexport function stopDebugPolling(host: PollingHost) {\n if (host.debugPollInterval == null) return;\n clearInterval(host.debugPollInterval);\n host.debugPollInterval = null;\n}\n","import { loadConfig, loadConfigSchema } from \"./controllers/config\";\nimport { loadCronJobs, loadCronStatus } from \"./controllers/cron\";\nimport { loadChannels } from \"./controllers/channels\";\nimport { loadDebug } from \"./controllers/debug\";\nimport { loadLogs } from \"./controllers/logs\";\nimport { loadDevices } from \"./controllers/devices\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadExecApprovals } from \"./controllers/exec-approvals\";\nimport { loadPresence } from \"./controllers/presence\";\nimport { loadSessions } from \"./controllers/sessions\";\nimport { loadSkills } from \"./controllers/skills\";\nimport { inferBasePathFromPathname, normalizeBasePath, normalizePath, pathForTab, tabFromPath, type Tab } from \"./navigation\";\nimport { saveSettings, type UiSettings } from \"./storage\";\nimport { resolveTheme, type ResolvedTheme, type ThemeMode } from \"./theme\";\nimport { startThemeTransition, type ThemeTransitionContext } from \"./theme-transition\";\nimport { scheduleChatScroll, scheduleLogsScroll } from \"./app-scroll\";\nimport { startLogsPolling, stopLogsPolling, startDebugPolling, stopDebugPolling } from \"./app-polling\";\nimport { refreshChat } from \"./app-chat\";\nimport type { ClawdbotApp } from \"./app\";\n\ntype SettingsHost = {\n settings: UiSettings;\n theme: ThemeMode;\n themeResolved: ResolvedTheme;\n applySessionKey: string;\n sessionKey: string;\n tab: Tab;\n connected: boolean;\n chatHasAutoScrolled: boolean;\n logsAtBottom: boolean;\n eventLog: unknown[];\n eventLogBuffer: unknown[];\n basePath: string;\n themeMedia: MediaQueryList | null;\n themeMediaHandler: ((event: MediaQueryListEvent) => void) | null;\n};\n\nexport function applySettings(host: SettingsHost, next: UiSettings) {\n const normalized = {\n ...next,\n lastActiveSessionKey: next.lastActiveSessionKey?.trim() || next.sessionKey.trim() || \"main\",\n };\n host.settings = normalized;\n saveSettings(normalized);\n if (next.theme !== host.theme) {\n host.theme = next.theme;\n applyResolvedTheme(host, resolveTheme(next.theme));\n }\n host.applySessionKey = host.settings.lastActiveSessionKey;\n}\n\nexport function setLastActiveSessionKey(host: SettingsHost, next: string) {\n const trimmed = next.trim();\n if (!trimmed) return;\n if (host.settings.lastActiveSessionKey === trimmed) return;\n applySettings(host, { ...host.settings, lastActiveSessionKey: trimmed });\n}\n\nexport function applySettingsFromUrl(host: SettingsHost) {\n if (!window.location.search) return;\n const params = new URLSearchParams(window.location.search);\n const tokenRaw = params.get(\"token\");\n const passwordRaw = params.get(\"password\");\n const sessionRaw = params.get(\"session\");\n const gatewayUrlRaw = params.get(\"gatewayUrl\");\n let shouldCleanUrl = false;\n\n if (tokenRaw != null) {\n const token = tokenRaw.trim();\n if (token && token !== host.settings.token) {\n applySettings(host, { ...host.settings, token });\n }\n params.delete(\"token\");\n shouldCleanUrl = true;\n }\n\n if (passwordRaw != null) {\n const password = passwordRaw.trim();\n if (password) {\n (host as { password: string }).password = password;\n }\n params.delete(\"password\");\n shouldCleanUrl = true;\n }\n\n if (sessionRaw != null) {\n const session = sessionRaw.trim();\n if (session) {\n host.sessionKey = session;\n applySettings(host, {\n ...host.settings,\n sessionKey: session,\n lastActiveSessionKey: session,\n });\n }\n }\n\n if (gatewayUrlRaw != null) {\n const gatewayUrl = gatewayUrlRaw.trim();\n if (gatewayUrl && gatewayUrl !== host.settings.gatewayUrl) {\n applySettings(host, { ...host.settings, gatewayUrl });\n }\n params.delete(\"gatewayUrl\");\n shouldCleanUrl = true;\n }\n\n if (!shouldCleanUrl) return;\n const url = new URL(window.location.href);\n url.search = params.toString();\n window.history.replaceState({}, \"\", url.toString());\n}\n\nexport function setTab(host: SettingsHost, next: Tab) {\n if (host.tab !== next) host.tab = next;\n if (next === \"chat\") host.chatHasAutoScrolled = false;\n if (next === \"logs\")\n startLogsPolling(host as unknown as Parameters[0]);\n else stopLogsPolling(host as unknown as Parameters[0]);\n if (next === \"debug\")\n startDebugPolling(host as unknown as Parameters[0]);\n else stopDebugPolling(host as unknown as Parameters[0]);\n void refreshActiveTab(host);\n syncUrlWithTab(host, next, false);\n}\n\nexport function setTheme(\n host: SettingsHost,\n next: ThemeMode,\n context?: ThemeTransitionContext,\n) {\n const applyTheme = () => {\n host.theme = next;\n applySettings(host, { ...host.settings, theme: next });\n applyResolvedTheme(host, resolveTheme(next));\n };\n startThemeTransition({\n nextTheme: next,\n applyTheme,\n context,\n currentTheme: host.theme,\n });\n}\n\nexport async function refreshActiveTab(host: SettingsHost) {\n if (host.tab === \"overview\") await loadOverview(host);\n if (host.tab === \"channels\") await loadChannelsTab(host);\n if (host.tab === \"instances\") await loadPresence(host as unknown as ClawdbotApp);\n if (host.tab === \"sessions\") await loadSessions(host as unknown as ClawdbotApp);\n if (host.tab === \"cron\") await loadCron(host);\n if (host.tab === \"skills\") await loadSkills(host as unknown as ClawdbotApp);\n if (host.tab === \"nodes\") {\n await loadNodes(host as unknown as ClawdbotApp);\n await loadDevices(host as unknown as ClawdbotApp);\n await loadConfig(host as unknown as ClawdbotApp);\n await loadExecApprovals(host as unknown as ClawdbotApp);\n }\n if (host.tab === \"chat\") {\n await refreshChat(host as unknown as Parameters[0]);\n scheduleChatScroll(\n host as unknown as Parameters[0],\n !host.chatHasAutoScrolled,\n );\n }\n if (host.tab === \"config\") {\n await loadConfigSchema(host as unknown as ClawdbotApp);\n await loadConfig(host as unknown as ClawdbotApp);\n }\n if (host.tab === \"debug\") {\n await loadDebug(host as unknown as ClawdbotApp);\n host.eventLog = host.eventLogBuffer;\n }\n if (host.tab === \"logs\") {\n host.logsAtBottom = true;\n await loadLogs(host as unknown as ClawdbotApp, { reset: true });\n scheduleLogsScroll(\n host as unknown as Parameters[0],\n true,\n );\n }\n}\n\nexport function inferBasePath() {\n if (typeof window === \"undefined\") return \"\";\n const configured = window.__CLAWDBOT_CONTROL_UI_BASE_PATH__;\n if (typeof configured === \"string\" && configured.trim()) {\n return normalizeBasePath(configured);\n }\n return inferBasePathFromPathname(window.location.pathname);\n}\n\nexport function syncThemeWithSettings(host: SettingsHost) {\n host.theme = host.settings.theme ?? \"system\";\n applyResolvedTheme(host, resolveTheme(host.theme));\n}\n\nexport function applyResolvedTheme(host: SettingsHost, resolved: ResolvedTheme) {\n host.themeResolved = resolved;\n if (typeof document === \"undefined\") return;\n const root = document.documentElement;\n root.dataset.theme = resolved;\n root.style.colorScheme = resolved;\n}\n\nexport function attachThemeListener(host: SettingsHost) {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n host.themeMedia = window.matchMedia(\"(prefers-color-scheme: dark)\");\n host.themeMediaHandler = (event) => {\n if (host.theme !== \"system\") return;\n applyResolvedTheme(host, event.matches ? \"dark\" : \"light\");\n };\n if (typeof host.themeMedia.addEventListener === \"function\") {\n host.themeMedia.addEventListener(\"change\", host.themeMediaHandler);\n return;\n }\n const legacy = host.themeMedia as MediaQueryList & {\n addListener: (cb: (event: MediaQueryListEvent) => void) => void;\n };\n legacy.addListener(host.themeMediaHandler);\n}\n\nexport function detachThemeListener(host: SettingsHost) {\n if (!host.themeMedia || !host.themeMediaHandler) return;\n if (typeof host.themeMedia.removeEventListener === \"function\") {\n host.themeMedia.removeEventListener(\"change\", host.themeMediaHandler);\n return;\n }\n const legacy = host.themeMedia as MediaQueryList & {\n removeListener: (cb: (event: MediaQueryListEvent) => void) => void;\n };\n legacy.removeListener(host.themeMediaHandler);\n host.themeMedia = null;\n host.themeMediaHandler = null;\n}\n\nexport function syncTabWithLocation(host: SettingsHost, replace: boolean) {\n if (typeof window === \"undefined\") return;\n const resolved = tabFromPath(window.location.pathname, host.basePath) ?? \"chat\";\n setTabFromRoute(host, resolved);\n syncUrlWithTab(host, resolved, replace);\n}\n\nexport function onPopState(host: SettingsHost) {\n if (typeof window === \"undefined\") return;\n const resolved = tabFromPath(window.location.pathname, host.basePath);\n if (!resolved) return;\n\n const url = new URL(window.location.href);\n const session = url.searchParams.get(\"session\")?.trim();\n if (session) {\n host.sessionKey = session;\n applySettings(host, {\n ...host.settings,\n sessionKey: session,\n lastActiveSessionKey: session,\n });\n }\n\n setTabFromRoute(host, resolved);\n}\n\nexport function setTabFromRoute(host: SettingsHost, next: Tab) {\n if (host.tab !== next) host.tab = next;\n if (next === \"chat\") host.chatHasAutoScrolled = false;\n if (next === \"logs\")\n startLogsPolling(host as unknown as Parameters[0]);\n else stopLogsPolling(host as unknown as Parameters[0]);\n if (next === \"debug\")\n startDebugPolling(host as unknown as Parameters[0]);\n else stopDebugPolling(host as unknown as Parameters[0]);\n if (host.connected) void refreshActiveTab(host);\n}\n\nexport function syncUrlWithTab(host: SettingsHost, tab: Tab, replace: boolean) {\n if (typeof window === \"undefined\") return;\n const targetPath = normalizePath(pathForTab(tab, host.basePath));\n const currentPath = normalizePath(window.location.pathname);\n const url = new URL(window.location.href);\n\n if (tab === \"chat\" && host.sessionKey) {\n url.searchParams.set(\"session\", host.sessionKey);\n } else {\n url.searchParams.delete(\"session\");\n }\n\n if (currentPath !== targetPath) {\n url.pathname = targetPath;\n }\n\n if (replace) {\n window.history.replaceState({}, \"\", url.toString());\n } else {\n window.history.pushState({}, \"\", url.toString());\n }\n}\n\nexport function syncUrlWithSessionKey(\n host: SettingsHost,\n sessionKey: string,\n replace: boolean,\n) {\n if (typeof window === \"undefined\") return;\n const url = new URL(window.location.href);\n url.searchParams.set(\"session\", sessionKey);\n if (replace) window.history.replaceState({}, \"\", url.toString());\n else window.history.pushState({}, \"\", url.toString());\n}\n\nexport async function loadOverview(host: SettingsHost) {\n await Promise.all([\n loadChannels(host as unknown as ClawdbotApp, false),\n loadPresence(host as unknown as ClawdbotApp),\n loadSessions(host as unknown as ClawdbotApp),\n loadCronStatus(host as unknown as ClawdbotApp),\n loadDebug(host as unknown as ClawdbotApp),\n ]);\n}\n\nexport async function loadChannelsTab(host: SettingsHost) {\n await Promise.all([\n loadChannels(host as unknown as ClawdbotApp, true),\n loadConfigSchema(host as unknown as ClawdbotApp),\n loadConfig(host as unknown as ClawdbotApp),\n ]);\n}\n\nexport async function loadCron(host: SettingsHost) {\n await Promise.all([\n loadChannels(host as unknown as ClawdbotApp, false),\n loadCronStatus(host as unknown as ClawdbotApp),\n loadCronJobs(host as unknown as ClawdbotApp),\n ]);\n}\n","import { abortChatRun, loadChatHistory, sendChatMessage } from \"./controllers/chat\";\nimport { loadSessions } from \"./controllers/sessions\";\nimport { generateUUID } from \"./uuid\";\nimport { resetToolStream } from \"./app-tool-stream\";\nimport { scheduleChatScroll } from \"./app-scroll\";\nimport { setLastActiveSessionKey } from \"./app-settings\";\nimport { normalizeBasePath } from \"./navigation\";\nimport type { GatewayHelloOk } from \"./gateway\";\nimport { parseAgentSessionKey } from \"../../../src/sessions/session-key-utils.js\";\nimport type { ClawdbotApp } from \"./app\";\n\ntype ChatHost = {\n connected: boolean;\n chatMessage: string;\n chatQueue: Array<{ id: string; text: string; createdAt: number }>;\n chatRunId: string | null;\n chatSending: boolean;\n sessionKey: string;\n basePath: string;\n hello: GatewayHelloOk | null;\n chatAvatarUrl: string | null;\n};\n\nexport function isChatBusy(host: ChatHost) {\n return host.chatSending || Boolean(host.chatRunId);\n}\n\nexport function isChatStopCommand(text: string) {\n const trimmed = text.trim();\n if (!trimmed) return false;\n const normalized = trimmed.toLowerCase();\n if (normalized === \"/stop\") return true;\n return (\n normalized === \"stop\" ||\n normalized === \"esc\" ||\n normalized === \"abort\" ||\n normalized === \"wait\" ||\n normalized === \"exit\"\n );\n}\n\nexport async function handleAbortChat(host: ChatHost) {\n if (!host.connected) return;\n host.chatMessage = \"\";\n await abortChatRun(host as unknown as ClawdbotApp);\n}\n\nfunction enqueueChatMessage(host: ChatHost, text: string) {\n const trimmed = text.trim();\n if (!trimmed) return;\n host.chatQueue = [\n ...host.chatQueue,\n {\n id: generateUUID(),\n text: trimmed,\n createdAt: Date.now(),\n },\n ];\n}\n\nasync function sendChatMessageNow(\n host: ChatHost,\n message: string,\n opts?: { previousDraft?: string; restoreDraft?: boolean },\n) {\n resetToolStream(host as unknown as Parameters[0]);\n const ok = await sendChatMessage(host as unknown as ClawdbotApp, message);\n if (!ok && opts?.previousDraft != null) {\n host.chatMessage = opts.previousDraft;\n }\n if (ok) {\n setLastActiveSessionKey(host as unknown as Parameters[0], host.sessionKey);\n }\n if (ok && opts?.restoreDraft && opts.previousDraft?.trim()) {\n host.chatMessage = opts.previousDraft;\n }\n scheduleChatScroll(host as unknown as Parameters[0]);\n if (ok && !host.chatRunId) {\n void flushChatQueue(host);\n }\n return ok;\n}\n\nasync function flushChatQueue(host: ChatHost) {\n if (!host.connected || isChatBusy(host)) return;\n const [next, ...rest] = host.chatQueue;\n if (!next) return;\n host.chatQueue = rest;\n const ok = await sendChatMessageNow(host, next.text);\n if (!ok) {\n host.chatQueue = [next, ...host.chatQueue];\n }\n}\n\nexport function removeQueuedMessage(host: ChatHost, id: string) {\n host.chatQueue = host.chatQueue.filter((item) => item.id !== id);\n}\n\nexport async function handleSendChat(\n host: ChatHost,\n messageOverride?: string,\n opts?: { restoreDraft?: boolean },\n) {\n if (!host.connected) return;\n const previousDraft = host.chatMessage;\n const message = (messageOverride ?? host.chatMessage).trim();\n if (!message) return;\n\n if (isChatStopCommand(message)) {\n await handleAbortChat(host);\n return;\n }\n\n if (messageOverride == null) {\n host.chatMessage = \"\";\n }\n\n if (isChatBusy(host)) {\n enqueueChatMessage(host, message);\n return;\n }\n\n await sendChatMessageNow(host, message, {\n previousDraft: messageOverride == null ? previousDraft : undefined,\n restoreDraft: Boolean(messageOverride && opts?.restoreDraft),\n });\n}\n\nexport async function refreshChat(host: ChatHost) {\n await Promise.all([\n loadChatHistory(host as unknown as ClawdbotApp),\n loadSessions(host as unknown as ClawdbotApp),\n refreshChatAvatar(host),\n ]);\n scheduleChatScroll(host as unknown as Parameters[0], true);\n}\n\nexport const flushChatQueueForEvent = flushChatQueue;\n\ntype SessionDefaultsSnapshot = {\n defaultAgentId?: string;\n};\n\nfunction resolveAgentIdForSession(host: ChatHost): string | null {\n const parsed = parseAgentSessionKey(host.sessionKey);\n if (parsed?.agentId) return parsed.agentId;\n const snapshot = host.hello?.snapshot as { sessionDefaults?: SessionDefaultsSnapshot } | undefined;\n const fallback = snapshot?.sessionDefaults?.defaultAgentId?.trim();\n return fallback || \"main\";\n}\n\nfunction buildAvatarMetaUrl(basePath: string, agentId: string): string {\n const base = normalizeBasePath(basePath);\n const encoded = encodeURIComponent(agentId);\n return base ? `${base}/avatar/${encoded}?meta=1` : `/avatar/${encoded}?meta=1`;\n}\n\nexport async function refreshChatAvatar(host: ChatHost) {\n if (!host.connected) {\n host.chatAvatarUrl = null;\n return;\n }\n const agentId = resolveAgentIdForSession(host);\n if (!agentId) {\n host.chatAvatarUrl = null;\n return;\n }\n host.chatAvatarUrl = null;\n const url = buildAvatarMetaUrl(host.basePath, agentId);\n try {\n const res = await fetch(url, { method: \"GET\" });\n if (!res.ok) {\n host.chatAvatarUrl = null;\n return;\n }\n const data = (await res.json()) as { avatarUrl?: unknown };\n const avatarUrl = typeof data.avatarUrl === \"string\" ? data.avatarUrl.trim() : \"\";\n host.chatAvatarUrl = avatarUrl || null;\n } catch {\n host.chatAvatarUrl = null;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},e=t=>(...e)=>({_$litDirective$:t,values:e});class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}export{i as Directive,t as PartType,e as directive};\n//# sourceMappingURL=directive.js.map\n","import{_$LH as o}from\"./lit-html.js\";\n/**\n * @license\n * Copyright 2020 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const{I:t}=o,i=o=>o,n=o=>null===o||\"object\"!=typeof o&&\"function\"!=typeof o,e={HTML:1,SVG:2,MATHML:3},l=(o,t)=>void 0===t?void 0!==o?._$litType$:o?._$litType$===t,d=o=>null!=o?._$litType$?.h,c=o=>void 0!==o?._$litDirective$,f=o=>o?._$litDirective$,r=o=>void 0===o.strings,s=()=>document.createComment(\"\"),v=(o,n,e)=>{const l=o._$AA.parentNode,d=void 0===n?o._$AB:n._$AA;if(void 0===e){const i=l.insertBefore(s(),d),n=l.insertBefore(s(),d);e=new t(i,n,o,o.options)}else{const t=e._$AB.nextSibling,n=e._$AM,c=n!==o;if(c){let t;e._$AQ?.(o),e._$AM=o,void 0!==e._$AP&&(t=o._$AU)!==n._$AU&&e._$AP(t)}if(t!==d||c){let o=e._$AA;for(;o!==t;){const t=i(o).nextSibling;i(l).insertBefore(o,d),o=t}}}return e},u=(o,t,i=o)=>(o._$AI(t,i),o),m={},p=(o,t=m)=>o._$AH=t,M=o=>o._$AH,h=o=>{o._$AR(),o._$AA.remove()},j=o=>{o._$AR()};export{e as TemplateResultType,j as clearPart,M as getCommittedValue,f as getDirectiveClass,v as insertPart,d as isCompiledTemplateResult,c as isDirectiveResult,n as isPrimitive,r as isSingleExpression,l as isTemplateResult,h as removePart,u as setChildPartValue,p as setCommittedValue};\n//# sourceMappingURL=directive-helpers.js.map\n","import{noChange as e}from\"../lit-html.js\";import{directive as s,Directive as t,PartType as r}from\"../directive.js\";import{getCommittedValue as l,setChildPartValue as o,insertPart as i,removePart as n,setCommittedValue as f}from\"../directive-helpers.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst u=(e,s,t)=>{const r=new Map;for(let l=s;l<=t;l++)r.set(e[l],l);return r},c=s(class extends t{constructor(e){if(super(e),e.type!==r.CHILD)throw Error(\"repeat() can only be used in text expressions\")}dt(e,s,t){let r;void 0===t?t=s:void 0!==s&&(r=s);const l=[],o=[];let i=0;for(const s of e)l[i]=r?r(s,i):i,o[i]=t(s,i),i++;return{values:o,keys:l}}render(e,s,t){return this.dt(e,s,t).values}update(s,[t,r,c]){const d=l(s),{values:p,keys:a}=this.dt(t,r,c);if(!Array.isArray(d))return this.ut=a,p;const h=this.ut??=[],v=[];let m,y,x=0,j=d.length-1,k=0,w=p.length-1;for(;x<=j&&k<=w;)if(null===d[x])x++;else if(null===d[j])j--;else if(h[x]===a[k])v[k]=o(d[x],p[k]),x++,k++;else if(h[j]===a[w])v[w]=o(d[j],p[w]),j--,w--;else if(h[x]===a[w])v[w]=o(d[x],p[w]),i(s,v[w+1],d[x]),x++,w--;else if(h[j]===a[k])v[k]=o(d[j],p[k]),i(s,d[x],d[j]),j--,k++;else if(void 0===m&&(m=u(a,k,w),y=u(h,x,j)),m.has(h[x]))if(m.has(h[j])){const e=y.get(a[k]),t=void 0!==e?d[e]:null;if(null===t){const e=i(s,d[x]);o(e,p[k]),v[k]=e}else v[k]=o(t,p[k]),i(s,d[x],t),d[e]=null;k++}else n(d[j]),j--;else n(d[x]),x++;for(;k<=w;){const e=i(s,v[w+1]);o(e,p[k]),v[k++]=e}for(;x<=j;){const e=d[x++];null!==e&&n(e)}return this.ut=a,f(s,v),e}});export{c as repeat};\n//# sourceMappingURL=repeat.js.map\n","/**\n * Message normalization utilities for chat rendering.\n */\n\nimport type {\n NormalizedMessage,\n MessageContentItem,\n} from \"../types/chat-types\";\n\n/**\n * Normalize a raw message object into a consistent structure.\n */\nexport function normalizeMessage(message: unknown): NormalizedMessage {\n const m = message as Record;\n let role = typeof m.role === \"string\" ? m.role : \"unknown\";\n\n // Detect tool messages by common gateway shapes.\n // Some tool events come through as assistant role with tool_* items in the content array.\n const hasToolId =\n typeof m.toolCallId === \"string\" || typeof m.tool_call_id === \"string\";\n\n const contentRaw = m.content;\n const contentItems = Array.isArray(contentRaw) ? contentRaw : null;\n const hasToolContent =\n Array.isArray(contentItems) &&\n contentItems.some((item) => {\n const x = item as Record;\n const t = String(x.type ?? \"\").toLowerCase();\n return t === \"toolresult\" || t === \"tool_result\";\n });\n\n const hasToolName =\n typeof (m as Record).toolName === \"string\" ||\n typeof (m as Record).tool_name === \"string\";\n\n if (hasToolId || hasToolContent || hasToolName) {\n role = \"toolResult\";\n }\n\n // Extract content\n let content: MessageContentItem[] = [];\n\n if (typeof m.content === \"string\") {\n content = [{ type: \"text\", text: m.content }];\n } else if (Array.isArray(m.content)) {\n content = m.content.map((item: Record) => ({\n type: (item.type as MessageContentItem[\"type\"]) || \"text\",\n text: item.text as string | undefined,\n name: item.name as string | undefined,\n args: item.args || item.arguments,\n }));\n } else if (typeof m.text === \"string\") {\n content = [{ type: \"text\", text: m.text }];\n }\n\n const timestamp = typeof m.timestamp === \"number\" ? m.timestamp : Date.now();\n const id = typeof m.id === \"string\" ? m.id : undefined;\n\n return { role, content, timestamp, id };\n}\n\n/**\n * Normalize role for grouping purposes.\n */\nexport function normalizeRoleForGrouping(role: string): string {\n const lower = role.toLowerCase();\n // Preserve original casing when it's already a core role.\n if (role === \"user\" || role === \"User\") return role;\n if (role === \"assistant\") return \"assistant\";\n if (role === \"system\") return \"system\";\n // Keep tool-related roles distinct so the UI can style/toggle them.\n if (\n lower === \"toolresult\" ||\n lower === \"tool_result\" ||\n lower === \"tool\" ||\n lower === \"function\"\n ) {\n return \"tool\";\n }\n return role;\n}\n\n/**\n * Check if a message is a tool result message based on its role.\n */\nexport function isToolResultMessage(message: unknown): boolean {\n const m = message as Record;\n const role = typeof m.role === \"string\" ? m.role.toLowerCase() : \"\";\n return role === \"toolresult\" || role === \"tool_result\";\n}\n","import{nothing as t,noChange as i}from\"../lit-html.js\";import{directive as r,Directive as s,PartType as n}from\"../directive.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */class e extends s{constructor(i){if(super(i),this.it=t,i.type!==n.CHILD)throw Error(this.constructor.directiveName+\"() can only be used in child bindings\")}render(r){if(r===t||null==r)return this._t=void 0,this.it=r;if(r===i)return r;if(\"string\"!=typeof r)throw Error(this.constructor.directiveName+\"() called with a non-string value\");if(r===this.it)return this._t;this.it=r;const s=[r];return s.raw=s,this._t={_$litType$:this.constructor.resultType,strings:s,values:[]}}}e.directiveName=\"unsafeHTML\",e.resultType=1;const o=r(e);export{e as UnsafeHTMLDirective,o as unsafeHTML};\n//# sourceMappingURL=unsafe-html.js.map\n","/*! @license DOMPurify 3.3.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.3.1/LICENSE */\n\nconst {\n entries,\n setPrototypeOf,\n isFrozen,\n getPrototypeOf,\n getOwnPropertyDescriptor\n} = Object;\nlet {\n freeze,\n seal,\n create\n} = Object; // eslint-disable-line import/no-mutable-exports\nlet {\n apply,\n construct\n} = typeof Reflect !== 'undefined' && Reflect;\nif (!freeze) {\n freeze = function freeze(x) {\n return x;\n };\n}\nif (!seal) {\n seal = function seal(x) {\n return x;\n };\n}\nif (!apply) {\n apply = function apply(func, thisArg) {\n for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n args[_key - 2] = arguments[_key];\n }\n return func.apply(thisArg, args);\n };\n}\nif (!construct) {\n construct = function construct(Func) {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n return new Func(...args);\n };\n}\nconst arrayForEach = unapply(Array.prototype.forEach);\nconst arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);\nconst arrayPop = unapply(Array.prototype.pop);\nconst arrayPush = unapply(Array.prototype.push);\nconst arraySplice = unapply(Array.prototype.splice);\nconst stringToLowerCase = unapply(String.prototype.toLowerCase);\nconst stringToString = unapply(String.prototype.toString);\nconst stringMatch = unapply(String.prototype.match);\nconst stringReplace = unapply(String.prototype.replace);\nconst stringIndexOf = unapply(String.prototype.indexOf);\nconst stringTrim = unapply(String.prototype.trim);\nconst objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);\nconst regExpTest = unapply(RegExp.prototype.test);\nconst typeErrorCreate = unconstruct(TypeError);\n/**\n * Creates a new function that calls the given function with a specified thisArg and arguments.\n *\n * @param func - The function to be wrapped and called.\n * @returns A new function that calls the given function with a specified thisArg and arguments.\n */\nfunction unapply(func) {\n return function (thisArg) {\n if (thisArg instanceof RegExp) {\n thisArg.lastIndex = 0;\n }\n for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {\n args[_key3 - 1] = arguments[_key3];\n }\n return apply(func, thisArg, args);\n };\n}\n/**\n * Creates a new function that constructs an instance of the given constructor function with the provided arguments.\n *\n * @param func - The constructor function to be wrapped and called.\n * @returns A new function that constructs an instance of the given constructor function with the provided arguments.\n */\nfunction unconstruct(Func) {\n return function () {\n for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {\n args[_key4] = arguments[_key4];\n }\n return construct(Func, args);\n };\n}\n/**\n * Add properties to a lookup table\n *\n * @param set - The set to which elements will be added.\n * @param array - The array containing elements to be added to the set.\n * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.\n * @returns The modified set with added elements.\n */\nfunction addToSet(set, array) {\n let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;\n if (setPrototypeOf) {\n // Make 'in' and truthy checks like Boolean(set.constructor)\n // independent of any properties defined on Object.prototype.\n // Prevent prototype setters from intercepting set as a this value.\n setPrototypeOf(set, null);\n }\n let l = array.length;\n while (l--) {\n let element = array[l];\n if (typeof element === 'string') {\n const lcElement = transformCaseFunc(element);\n if (lcElement !== element) {\n // Config presets (e.g. tags.js, attrs.js) are immutable.\n if (!isFrozen(array)) {\n array[l] = lcElement;\n }\n element = lcElement;\n }\n }\n set[element] = true;\n }\n return set;\n}\n/**\n * Clean up an array to harden against CSPP\n *\n * @param array - The array to be cleaned.\n * @returns The cleaned version of the array\n */\nfunction cleanArray(array) {\n for (let index = 0; index < array.length; index++) {\n const isPropertyExist = objectHasOwnProperty(array, index);\n if (!isPropertyExist) {\n array[index] = null;\n }\n }\n return array;\n}\n/**\n * Shallow clone an object\n *\n * @param object - The object to be cloned.\n * @returns A new object that copies the original.\n */\nfunction clone(object) {\n const newObject = create(null);\n for (const [property, value] of entries(object)) {\n const isPropertyExist = objectHasOwnProperty(object, property);\n if (isPropertyExist) {\n if (Array.isArray(value)) {\n newObject[property] = cleanArray(value);\n } else if (value && typeof value === 'object' && value.constructor === Object) {\n newObject[property] = clone(value);\n } else {\n newObject[property] = value;\n }\n }\n }\n return newObject;\n}\n/**\n * This method automatically checks if the prop is function or getter and behaves accordingly.\n *\n * @param object - The object to look up the getter function in its prototype chain.\n * @param prop - The property name for which to find the getter function.\n * @returns The getter function found in the prototype chain or a fallback function.\n */\nfunction lookupGetter(object, prop) {\n while (object !== null) {\n const desc = getOwnPropertyDescriptor(object, prop);\n if (desc) {\n if (desc.get) {\n return unapply(desc.get);\n }\n if (typeof desc.value === 'function') {\n return unapply(desc.value);\n }\n }\n object = getPrototypeOf(object);\n }\n function fallbackValue() {\n return null;\n }\n return fallbackValue;\n}\n\nconst html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);\nconst svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);\nconst svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);\n// List of SVG elements that are disallowed by default.\n// We still need to know them so that we can do namespace\n// checks properly in case one wants to add them to\n// allow-list.\nconst svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);\nconst mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);\n// Similarly to SVG, we want to know all MathML elements,\n// even those that we disallow by default.\nconst mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);\nconst text = freeze(['#text']);\n\nconst html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);\nconst svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);\nconst mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);\nconst xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);\n\n// eslint-disable-next-line unicorn/better-regex\nconst MUSTACHE_EXPR = seal(/\\{\\{[\\w\\W]*|[\\w\\W]*\\}\\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode\nconst ERB_EXPR = seal(/<%[\\w\\W]*|[\\w\\W]*%>/gm);\nconst TMPLIT_EXPR = seal(/\\$\\{[\\w\\W]*/gm); // eslint-disable-line unicorn/better-regex\nconst DATA_ATTR = seal(/^data-[\\-\\w.\\u00B7-\\uFFFF]+$/); // eslint-disable-line no-useless-escape\nconst ARIA_ATTR = seal(/^aria-[\\-\\w]+$/); // eslint-disable-line no-useless-escape\nconst IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i // eslint-disable-line no-useless-escape\n);\nconst IS_SCRIPT_OR_DATA = seal(/^(?:\\w+script|data):/i);\nconst ATTR_WHITESPACE = seal(/[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g // eslint-disable-line no-control-regex\n);\nconst DOCTYPE_NAME = seal(/^html$/i);\nconst CUSTOM_ELEMENT = seal(/^[a-z][.\\w]*(-[.\\w]+)+$/i);\n\nvar EXPRESSIONS = /*#__PURE__*/Object.freeze({\n __proto__: null,\n ARIA_ATTR: ARIA_ATTR,\n ATTR_WHITESPACE: ATTR_WHITESPACE,\n CUSTOM_ELEMENT: CUSTOM_ELEMENT,\n DATA_ATTR: DATA_ATTR,\n DOCTYPE_NAME: DOCTYPE_NAME,\n ERB_EXPR: ERB_EXPR,\n IS_ALLOWED_URI: IS_ALLOWED_URI,\n IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,\n MUSTACHE_EXPR: MUSTACHE_EXPR,\n TMPLIT_EXPR: TMPLIT_EXPR\n});\n\n/* eslint-disable @typescript-eslint/indent */\n// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType\nconst NODE_TYPE = {\n element: 1,\n attribute: 2,\n text: 3,\n cdataSection: 4,\n entityReference: 5,\n // Deprecated\n entityNode: 6,\n // Deprecated\n progressingInstruction: 7,\n comment: 8,\n document: 9,\n documentType: 10,\n documentFragment: 11,\n notation: 12 // Deprecated\n};\nconst getGlobal = function getGlobal() {\n return typeof window === 'undefined' ? null : window;\n};\n/**\n * Creates a no-op policy for internal use only.\n * Don't export this function outside this module!\n * @param trustedTypes The policy factory.\n * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).\n * @return The policy created (or null, if Trusted Types\n * are not supported or creating the policy failed).\n */\nconst _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {\n if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {\n return null;\n }\n // Allow the callers to control the unique policy name\n // by adding a data-tt-policy-suffix to the script element with the DOMPurify.\n // Policy creation with duplicate names throws in Trusted Types.\n let suffix = null;\n const ATTR_NAME = 'data-tt-policy-suffix';\n if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {\n suffix = purifyHostElement.getAttribute(ATTR_NAME);\n }\n const policyName = 'dompurify' + (suffix ? '#' + suffix : '');\n try {\n return trustedTypes.createPolicy(policyName, {\n createHTML(html) {\n return html;\n },\n createScriptURL(scriptUrl) {\n return scriptUrl;\n }\n });\n } catch (_) {\n // Policy creation failed (most likely another DOMPurify script has\n // already run). Skip creating the policy, as this will only cause errors\n // if TT are enforced.\n console.warn('TrustedTypes policy ' + policyName + ' could not be created.');\n return null;\n }\n};\nconst _createHooksMap = function _createHooksMap() {\n return {\n afterSanitizeAttributes: [],\n afterSanitizeElements: [],\n afterSanitizeShadowDOM: [],\n beforeSanitizeAttributes: [],\n beforeSanitizeElements: [],\n beforeSanitizeShadowDOM: [],\n uponSanitizeAttribute: [],\n uponSanitizeElement: [],\n uponSanitizeShadowNode: []\n };\n};\nfunction createDOMPurify() {\n let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();\n const DOMPurify = root => createDOMPurify(root);\n DOMPurify.version = '3.3.1';\n DOMPurify.removed = [];\n if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {\n // Not running in a browser, provide a factory function\n // so that you can pass your own Window\n DOMPurify.isSupported = false;\n return DOMPurify;\n }\n let {\n document\n } = window;\n const originalDocument = document;\n const currentScript = originalDocument.currentScript;\n const {\n DocumentFragment,\n HTMLTemplateElement,\n Node,\n Element,\n NodeFilter,\n NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,\n HTMLFormElement,\n DOMParser,\n trustedTypes\n } = window;\n const ElementPrototype = Element.prototype;\n const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');\n const remove = lookupGetter(ElementPrototype, 'remove');\n const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');\n const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');\n const getParentNode = lookupGetter(ElementPrototype, 'parentNode');\n // As per issue #47, the web-components registry is inherited by a\n // new document created via createHTMLDocument. As per the spec\n // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)\n // a new empty registry is used when creating a template contents owner\n // document, so we use that as our parent document to ensure nothing\n // is inherited.\n if (typeof HTMLTemplateElement === 'function') {\n const template = document.createElement('template');\n if (template.content && template.content.ownerDocument) {\n document = template.content.ownerDocument;\n }\n }\n let trustedTypesPolicy;\n let emptyHTML = '';\n const {\n implementation,\n createNodeIterator,\n createDocumentFragment,\n getElementsByTagName\n } = document;\n const {\n importNode\n } = originalDocument;\n let hooks = _createHooksMap();\n /**\n * Expose whether this browser supports running the full DOMPurify.\n */\n DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;\n const {\n MUSTACHE_EXPR,\n ERB_EXPR,\n TMPLIT_EXPR,\n DATA_ATTR,\n ARIA_ATTR,\n IS_SCRIPT_OR_DATA,\n ATTR_WHITESPACE,\n CUSTOM_ELEMENT\n } = EXPRESSIONS;\n let {\n IS_ALLOWED_URI: IS_ALLOWED_URI$1\n } = EXPRESSIONS;\n /**\n * We consider the elements and attributes below to be safe. Ideally\n * don't add any new ones but feel free to remove unwanted ones.\n */\n /* allowed element names */\n let ALLOWED_TAGS = null;\n const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);\n /* Allowed attribute names */\n let ALLOWED_ATTR = null;\n const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);\n /*\n * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.\n * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)\n * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)\n * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.\n */\n let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {\n tagNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n allowCustomizedBuiltInElements: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: false\n }\n }));\n /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */\n let FORBID_TAGS = null;\n /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */\n let FORBID_ATTR = null;\n /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */\n const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {\n tagCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n }\n }));\n /* Decide if ARIA attributes are okay */\n let ALLOW_ARIA_ATTR = true;\n /* Decide if custom data attributes are okay */\n let ALLOW_DATA_ATTR = true;\n /* Decide if unknown protocols are okay */\n let ALLOW_UNKNOWN_PROTOCOLS = false;\n /* Decide if self-closing tags in attributes are allowed.\n * Usually removed due to a mXSS issue in jQuery 3.0 */\n let ALLOW_SELF_CLOSE_IN_ATTR = true;\n /* Output should be safe for common template engines.\n * This means, DOMPurify removes data attributes, mustaches and ERB\n */\n let SAFE_FOR_TEMPLATES = false;\n /* Output should be safe even for XML used within HTML and alike.\n * This means, DOMPurify removes comments when containing risky content.\n */\n let SAFE_FOR_XML = true;\n /* Decide if document with ... should be returned */\n let WHOLE_DOCUMENT = false;\n /* Track whether config is already set on this instance of DOMPurify. */\n let SET_CONFIG = false;\n /* Decide if all elements (e.g. style, script) must be children of\n * document.body. By default, browsers might move them to document.head */\n let FORCE_BODY = false;\n /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported).\n * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead\n */\n let RETURN_DOM = false;\n /* Decide if a DOM `DocumentFragment` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported) */\n let RETURN_DOM_FRAGMENT = false;\n /* Try to return a Trusted Type object instead of a string, return a string in\n * case Trusted Types are not supported */\n let RETURN_TRUSTED_TYPE = false;\n /* Output should be free from DOM clobbering attacks?\n * This sanitizes markups named with colliding, clobberable built-in DOM APIs.\n */\n let SANITIZE_DOM = true;\n /* Achieve full DOM Clobbering protection by isolating the namespace of named\n * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.\n *\n * HTML/DOM spec rules that enable DOM Clobbering:\n * - Named Access on Window (§7.3.3)\n * - DOM Tree Accessors (§3.1.5)\n * - Form Element Parent-Child Relations (§4.10.3)\n * - Iframe srcdoc / Nested WindowProxies (§4.8.5)\n * - HTMLCollection (§4.2.10.2)\n *\n * Namespace isolation is implemented by prefixing `id` and `name` attributes\n * with a constant string, i.e., `user-content-`\n */\n let SANITIZE_NAMED_PROPS = false;\n const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';\n /* Keep element content when removing element? */\n let KEEP_CONTENT = true;\n /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead\n * of importing it into a new Document and returning a sanitized copy */\n let IN_PLACE = false;\n /* Allow usage of profiles like html, svg and mathMl */\n let USE_PROFILES = {};\n /* Tags to ignore content of when KEEP_CONTENT is true */\n let FORBID_CONTENTS = null;\n const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);\n /* Tags that are safe for data: URIs */\n let DATA_URI_TAGS = null;\n const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);\n /* Attributes safe for values like \"javascript:\" */\n let URI_SAFE_ATTRIBUTES = null;\n const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);\n const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n /* Document namespace */\n let NAMESPACE = HTML_NAMESPACE;\n let IS_EMPTY_INPUT = false;\n /* Allowed XHTML+XML namespaces */\n let ALLOWED_NAMESPACES = null;\n const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);\n let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);\n let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);\n // Certain elements are allowed in both SVG and HTML\n // namespace. We need to specify them explicitly\n // so that they don't get erroneously deleted from\n // HTML namespace.\n const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);\n /* Parsing of strict XHTML documents */\n let PARSER_MEDIA_TYPE = null;\n const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];\n const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';\n let transformCaseFunc = null;\n /* Keep a reference to config to pass to hooks */\n let CONFIG = null;\n /* Ideally, do not touch anything below this line */\n /* ______________________________________________ */\n const formElement = document.createElement('form');\n const isRegexOrFunction = function isRegexOrFunction(testValue) {\n return testValue instanceof RegExp || testValue instanceof Function;\n };\n /**\n * _parseConfig\n *\n * @param cfg optional config literal\n */\n // eslint-disable-next-line complexity\n const _parseConfig = function _parseConfig() {\n let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n if (CONFIG && CONFIG === cfg) {\n return;\n }\n /* Shield configuration object from tampering */\n if (!cfg || typeof cfg !== 'object') {\n cfg = {};\n }\n /* Shield configuration object from prototype pollution */\n cfg = clone(cfg);\n PARSER_MEDIA_TYPE =\n // eslint-disable-next-line unicorn/prefer-includes\n SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;\n // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.\n transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;\n /* Set configuration parameters */\n ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;\n ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;\n ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;\n URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;\n DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;\n FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;\n FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});\n FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});\n USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;\n ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true\n ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true\n ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false\n ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true\n SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false\n SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true\n WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false\n RETURN_DOM = cfg.RETURN_DOM || false; // Default false\n RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false\n RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false\n FORCE_BODY = cfg.FORCE_BODY || false; // Default false\n SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true\n SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false\n KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true\n IN_PLACE = cfg.IN_PLACE || false; // Default false\n IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;\n NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;\n MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;\n HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;\n CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};\n if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {\n CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;\n }\n if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {\n CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;\n }\n if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {\n CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;\n }\n if (SAFE_FOR_TEMPLATES) {\n ALLOW_DATA_ATTR = false;\n }\n if (RETURN_DOM_FRAGMENT) {\n RETURN_DOM = true;\n }\n /* Parse profile info */\n if (USE_PROFILES) {\n ALLOWED_TAGS = addToSet({}, text);\n ALLOWED_ATTR = [];\n if (USE_PROFILES.html === true) {\n addToSet(ALLOWED_TAGS, html$1);\n addToSet(ALLOWED_ATTR, html);\n }\n if (USE_PROFILES.svg === true) {\n addToSet(ALLOWED_TAGS, svg$1);\n addToSet(ALLOWED_ATTR, svg);\n addToSet(ALLOWED_ATTR, xml);\n }\n if (USE_PROFILES.svgFilters === true) {\n addToSet(ALLOWED_TAGS, svgFilters);\n addToSet(ALLOWED_ATTR, svg);\n addToSet(ALLOWED_ATTR, xml);\n }\n if (USE_PROFILES.mathMl === true) {\n addToSet(ALLOWED_TAGS, mathMl$1);\n addToSet(ALLOWED_ATTR, mathMl);\n addToSet(ALLOWED_ATTR, xml);\n }\n }\n /* Merge configuration parameters */\n if (cfg.ADD_TAGS) {\n if (typeof cfg.ADD_TAGS === 'function') {\n EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;\n } else {\n if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {\n ALLOWED_TAGS = clone(ALLOWED_TAGS);\n }\n addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);\n }\n }\n if (cfg.ADD_ATTR) {\n if (typeof cfg.ADD_ATTR === 'function') {\n EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;\n } else {\n if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {\n ALLOWED_ATTR = clone(ALLOWED_ATTR);\n }\n addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);\n }\n }\n if (cfg.ADD_URI_SAFE_ATTR) {\n addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);\n }\n if (cfg.FORBID_CONTENTS) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);\n }\n if (cfg.ADD_FORBID_CONTENTS) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);\n }\n /* Add #text in case KEEP_CONTENT is set to true */\n if (KEEP_CONTENT) {\n ALLOWED_TAGS['#text'] = true;\n }\n /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */\n if (WHOLE_DOCUMENT) {\n addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);\n }\n /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */\n if (ALLOWED_TAGS.table) {\n addToSet(ALLOWED_TAGS, ['tbody']);\n delete FORBID_TAGS.tbody;\n }\n if (cfg.TRUSTED_TYPES_POLICY) {\n if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {\n throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a \"createHTML\" hook.');\n }\n if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {\n throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a \"createScriptURL\" hook.');\n }\n // Overwrite existing TrustedTypes policy.\n trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;\n // Sign local variables required by `sanitize`.\n emptyHTML = trustedTypesPolicy.createHTML('');\n } else {\n // Uninitialized policy, attempt to initialize the internal dompurify policy.\n if (trustedTypesPolicy === undefined) {\n trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);\n }\n // If creating the internal policy succeeded sign internal variables.\n if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {\n emptyHTML = trustedTypesPolicy.createHTML('');\n }\n }\n // Prevent further manipulation of configuration.\n // Not available in IE8, Safari 5, etc.\n if (freeze) {\n freeze(cfg);\n }\n CONFIG = cfg;\n };\n /* Keep track of all possible SVG and MathML tags\n * so that we can perform the namespace checks\n * correctly. */\n const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);\n const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);\n /**\n * @param element a DOM element whose namespace is being checked\n * @returns Return false if the element has a\n * namespace that a spec-compliant parser would never\n * return. Return true otherwise.\n */\n const _checkValidNamespace = function _checkValidNamespace(element) {\n let parent = getParentNode(element);\n // In JSDOM, if we're inside shadow DOM, then parentNode\n // can be null. We just simulate parent in this case.\n if (!parent || !parent.tagName) {\n parent = {\n namespaceURI: NAMESPACE,\n tagName: 'template'\n };\n }\n const tagName = stringToLowerCase(element.tagName);\n const parentTagName = stringToLowerCase(parent.tagName);\n if (!ALLOWED_NAMESPACES[element.namespaceURI]) {\n return false;\n }\n if (element.namespaceURI === SVG_NAMESPACE) {\n // The only way to switch from HTML namespace to SVG\n // is via . If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'svg';\n }\n // The only way to switch from MathML to SVG is via`\n // svg if parent is either or MathML\n // text integration points.\n if (parent.namespaceURI === MATHML_NAMESPACE) {\n return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);\n }\n // We only allow elements that are defined in SVG\n // spec. All others are disallowed in SVG namespace.\n return Boolean(ALL_SVG_TAGS[tagName]);\n }\n if (element.namespaceURI === MATHML_NAMESPACE) {\n // The only way to switch from HTML namespace to MathML\n // is via . If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'math';\n }\n // The only way to switch from SVG to MathML is via\n // and HTML integration points\n if (parent.namespaceURI === SVG_NAMESPACE) {\n return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];\n }\n // We only allow elements that are defined in MathML\n // spec. All others are disallowed in MathML namespace.\n return Boolean(ALL_MATHML_TAGS[tagName]);\n }\n if (element.namespaceURI === HTML_NAMESPACE) {\n // The only way to switch from SVG to HTML is via\n // HTML integration points, and from MathML to HTML\n // is via MathML text integration points\n if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {\n return false;\n }\n if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {\n return false;\n }\n // We disallow tags that are specific for MathML\n // or SVG and should never appear in HTML namespace\n return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);\n }\n // For XHTML and XML documents that support custom namespaces\n if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {\n return true;\n }\n // The code should never reach this place (this means\n // that the element somehow got namespace that is not\n // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).\n // Return false just in case.\n return false;\n };\n /**\n * _forceRemove\n *\n * @param node a DOM node\n */\n const _forceRemove = function _forceRemove(node) {\n arrayPush(DOMPurify.removed, {\n element: node\n });\n try {\n // eslint-disable-next-line unicorn/prefer-dom-node-remove\n getParentNode(node).removeChild(node);\n } catch (_) {\n remove(node);\n }\n };\n /**\n * _removeAttribute\n *\n * @param name an Attribute name\n * @param element a DOM node\n */\n const _removeAttribute = function _removeAttribute(name, element) {\n try {\n arrayPush(DOMPurify.removed, {\n attribute: element.getAttributeNode(name),\n from: element\n });\n } catch (_) {\n arrayPush(DOMPurify.removed, {\n attribute: null,\n from: element\n });\n }\n element.removeAttribute(name);\n // We void attribute values for unremovable \"is\" attributes\n if (name === 'is') {\n if (RETURN_DOM || RETURN_DOM_FRAGMENT) {\n try {\n _forceRemove(element);\n } catch (_) {}\n } else {\n try {\n element.setAttribute(name, '');\n } catch (_) {}\n }\n }\n };\n /**\n * _initDocument\n *\n * @param dirty - a string of dirty markup\n * @return a DOM, filled with the dirty markup\n */\n const _initDocument = function _initDocument(dirty) {\n /* Create a HTML document */\n let doc = null;\n let leadingWhitespace = null;\n if (FORCE_BODY) {\n dirty = '' + dirty;\n } else {\n /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */\n const matches = stringMatch(dirty, /^[\\r\\n\\t ]+/);\n leadingWhitespace = matches && matches[0];\n }\n if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {\n // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)\n dirty = '' + dirty + '';\n }\n const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;\n /*\n * Use the DOMParser API by default, fallback later if needs be\n * DOMParser not work for svg when has multiple root element.\n */\n if (NAMESPACE === HTML_NAMESPACE) {\n try {\n doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);\n } catch (_) {}\n }\n /* Use createHTMLDocument in case DOMParser is not available */\n if (!doc || !doc.documentElement) {\n doc = implementation.createDocument(NAMESPACE, 'template', null);\n try {\n doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;\n } catch (_) {\n // Syntax error if dirtyPayload is invalid xml\n }\n }\n const body = doc.body || doc.documentElement;\n if (dirty && leadingWhitespace) {\n body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);\n }\n /* Work on whole document or just its body */\n if (NAMESPACE === HTML_NAMESPACE) {\n return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];\n }\n return WHOLE_DOCUMENT ? doc.documentElement : body;\n };\n /**\n * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.\n *\n * @param root The root element or node to start traversing on.\n * @return The created NodeIterator\n */\n const _createNodeIterator = function _createNodeIterator(root) {\n return createNodeIterator.call(root.ownerDocument || root, root,\n // eslint-disable-next-line no-bitwise\n NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);\n };\n /**\n * _isClobbered\n *\n * @param element element to check for clobbering attacks\n * @return true if clobbered, false if safe\n */\n const _isClobbered = function _isClobbered(element) {\n return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');\n };\n /**\n * Checks whether the given object is a DOM node.\n *\n * @param value object to check whether it's a DOM node\n * @return true is object is a DOM node\n */\n const _isNode = function _isNode(value) {\n return typeof Node === 'function' && value instanceof Node;\n };\n function _executeHooks(hooks, currentNode, data) {\n arrayForEach(hooks, hook => {\n hook.call(DOMPurify, currentNode, data, CONFIG);\n });\n }\n /**\n * _sanitizeElements\n *\n * @protect nodeName\n * @protect textContent\n * @protect removeChild\n * @param currentNode to check for permission to exist\n * @return true if node was killed, false if left alive\n */\n const _sanitizeElements = function _sanitizeElements(currentNode) {\n let content = null;\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeElements, currentNode, null);\n /* Check if element is clobbered or can clobber */\n if (_isClobbered(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Now let's check the element's type and name */\n const tagName = transformCaseFunc(currentNode.nodeName);\n /* Execute a hook if present */\n _executeHooks(hooks.uponSanitizeElement, currentNode, {\n tagName,\n allowedTags: ALLOWED_TAGS\n });\n /* Detect mXSS attempts abusing namespace confusion */\n if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\\w!]/g, currentNode.textContent)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove any occurrence of processing instructions */\n if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove any kind of possibly harmful comments */\n if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\\w]/g, currentNode.data)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove element if anything forbids its presence */\n if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {\n /* Check if we have a custom element to handle */\n if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {\n if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {\n return false;\n }\n if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {\n return false;\n }\n }\n /* Keep content except for bad-listed elements */\n if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {\n const parentNode = getParentNode(currentNode) || currentNode.parentNode;\n const childNodes = getChildNodes(currentNode) || currentNode.childNodes;\n if (childNodes && parentNode) {\n const childCount = childNodes.length;\n for (let i = childCount - 1; i >= 0; --i) {\n const childClone = cloneNode(childNodes[i], true);\n childClone.__removalCount = (currentNode.__removalCount || 0) + 1;\n parentNode.insertBefore(childClone, getNextSibling(currentNode));\n }\n }\n }\n _forceRemove(currentNode);\n return true;\n }\n /* Check whether element has a valid namespace */\n if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Make sure that older browsers don't get fallback-tag mXSS */\n if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\\/no(script|embed|frames)/i, currentNode.innerHTML)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Sanitize element content to be template-safe */\n if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {\n /* Get the element's text content */\n content = currentNode.textContent;\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n content = stringReplace(content, expr, ' ');\n });\n if (currentNode.textContent !== content) {\n arrayPush(DOMPurify.removed, {\n element: currentNode.cloneNode()\n });\n currentNode.textContent = content;\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeElements, currentNode, null);\n return false;\n };\n /**\n * _isValidAttribute\n *\n * @param lcTag Lowercase tag name of containing element.\n * @param lcName Lowercase attribute name.\n * @param value Attribute value.\n * @return Returns true if `value` is valid, otherwise false.\n */\n // eslint-disable-next-line complexity\n const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {\n /* Make sure attribute cannot clobber */\n if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {\n return false;\n }\n /* Allow valid data-* attributes: At least one character after \"-\"\n (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)\n XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)\n We don't need to check the value; it's always URI safe. */\n if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {\n if (\n // First condition does a very basic check if a) it's basically a valid custom element tagname AND\n // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck\n // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck\n _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName, lcTag)) ||\n // Alternative, second condition checks if it's an `is`-attribute, AND\n // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck\n lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {\n return false;\n }\n /* Check value is safe. First, is attr inert? If so, is safe */\n } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {\n return false;\n } else ;\n return true;\n };\n /**\n * _isBasicCustomElement\n * checks if at least one dash is included in tagName, and it's not the first char\n * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name\n *\n * @param tagName name of the tag of the node to sanitize\n * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.\n */\n const _isBasicCustomElement = function _isBasicCustomElement(tagName) {\n return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);\n };\n /**\n * _sanitizeAttributes\n *\n * @protect attributes\n * @protect nodeName\n * @protect removeAttribute\n * @protect setAttribute\n *\n * @param currentNode to sanitize\n */\n const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);\n const {\n attributes\n } = currentNode;\n /* Check if we have attributes; if not we might have a text node */\n if (!attributes || _isClobbered(currentNode)) {\n return;\n }\n const hookEvent = {\n attrName: '',\n attrValue: '',\n keepAttr: true,\n allowedAttributes: ALLOWED_ATTR,\n forceKeepAttr: undefined\n };\n let l = attributes.length;\n /* Go backwards over all attributes; safely remove bad ones */\n while (l--) {\n const attr = attributes[l];\n const {\n name,\n namespaceURI,\n value: attrValue\n } = attr;\n const lcName = transformCaseFunc(name);\n const initValue = attrValue;\n let value = name === 'value' ? initValue : stringTrim(initValue);\n /* Execute a hook if present */\n hookEvent.attrName = lcName;\n hookEvent.attrValue = value;\n hookEvent.keepAttr = true;\n hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set\n _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);\n value = hookEvent.attrValue;\n /* Full DOM Clobbering protection via namespace isolation,\n * Prefix id and name attributes with `user-content-`\n */\n if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {\n // Remove the attribute with this value\n _removeAttribute(name, currentNode);\n // Prefix the value and later re-create the attribute with the sanitized value\n value = SANITIZE_NAMED_PROPS_PREFIX + value;\n }\n /* Work around a security issue with comments inside attributes */\n if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\\/(style|title|textarea)/i, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Make sure we cannot easily use animated hrefs, even if animations are allowed */\n if (lcName === 'attributename' && stringMatch(value, 'href')) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Did the hooks approve of the attribute? */\n if (hookEvent.forceKeepAttr) {\n continue;\n }\n /* Did the hooks approve of the attribute? */\n if (!hookEvent.keepAttr) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Work around a security issue in jQuery 3.0 */\n if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\\/>/i, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Sanitize attribute content to be template-safe */\n if (SAFE_FOR_TEMPLATES) {\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n value = stringReplace(value, expr, ' ');\n });\n }\n /* Is `value` valid for this attribute? */\n const lcTag = transformCaseFunc(currentNode.nodeName);\n if (!_isValidAttribute(lcTag, lcName, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Handle attributes that require Trusted Types */\n if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {\n if (namespaceURI) ; else {\n switch (trustedTypes.getAttributeType(lcTag, lcName)) {\n case 'TrustedHTML':\n {\n value = trustedTypesPolicy.createHTML(value);\n break;\n }\n case 'TrustedScriptURL':\n {\n value = trustedTypesPolicy.createScriptURL(value);\n break;\n }\n }\n }\n }\n /* Handle invalid data-* attribute set by try-catching it */\n if (value !== initValue) {\n try {\n if (namespaceURI) {\n currentNode.setAttributeNS(namespaceURI, name, value);\n } else {\n /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. \"x-schema\". */\n currentNode.setAttribute(name, value);\n }\n if (_isClobbered(currentNode)) {\n _forceRemove(currentNode);\n } else {\n arrayPop(DOMPurify.removed);\n }\n } catch (_) {\n _removeAttribute(name, currentNode);\n }\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);\n };\n /**\n * _sanitizeShadowDOM\n *\n * @param fragment to iterate over recursively\n */\n const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {\n let shadowNode = null;\n const shadowIterator = _createNodeIterator(fragment);\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);\n while (shadowNode = shadowIterator.nextNode()) {\n /* Execute a hook if present */\n _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);\n /* Sanitize tags and elements */\n _sanitizeElements(shadowNode);\n /* Check attributes next */\n _sanitizeAttributes(shadowNode);\n /* Deep shadow DOM detected */\n if (shadowNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(shadowNode.content);\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);\n };\n // eslint-disable-next-line complexity\n DOMPurify.sanitize = function (dirty) {\n let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n let body = null;\n let importedNode = null;\n let currentNode = null;\n let returnNode = null;\n /* Make sure we have a string to sanitize.\n DO NOT return early, as this will return the wrong type if\n the user has requested a DOM object rather than a string */\n IS_EMPTY_INPUT = !dirty;\n if (IS_EMPTY_INPUT) {\n dirty = '';\n }\n /* Stringify, in case dirty is an object */\n if (typeof dirty !== 'string' && !_isNode(dirty)) {\n if (typeof dirty.toString === 'function') {\n dirty = dirty.toString();\n if (typeof dirty !== 'string') {\n throw typeErrorCreate('dirty is not a string, aborting');\n }\n } else {\n throw typeErrorCreate('toString is not a function');\n }\n }\n /* Return dirty HTML if DOMPurify cannot run */\n if (!DOMPurify.isSupported) {\n return dirty;\n }\n /* Assign config vars */\n if (!SET_CONFIG) {\n _parseConfig(cfg);\n }\n /* Clean up removed elements */\n DOMPurify.removed = [];\n /* Check if dirty is correctly typed for IN_PLACE */\n if (typeof dirty === 'string') {\n IN_PLACE = false;\n }\n if (IN_PLACE) {\n /* Do some early pre-sanitization to avoid unsafe root nodes */\n if (dirty.nodeName) {\n const tagName = transformCaseFunc(dirty.nodeName);\n if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {\n throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');\n }\n }\n } else if (dirty instanceof Node) {\n /* If dirty is a DOM element, append to an empty document to avoid\n elements being stripped by the parser */\n body = _initDocument('');\n importedNode = body.ownerDocument.importNode(dirty, true);\n if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {\n /* Node is already a body, use as is */\n body = importedNode;\n } else if (importedNode.nodeName === 'HTML') {\n body = importedNode;\n } else {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n body.appendChild(importedNode);\n }\n } else {\n /* Exit directly if we have nothing to do */\n if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&\n // eslint-disable-next-line unicorn/prefer-includes\n dirty.indexOf('<') === -1) {\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;\n }\n /* Initialize the document to work on */\n body = _initDocument(dirty);\n /* Check we have a DOM node from the data */\n if (!body) {\n return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';\n }\n }\n /* Remove first element node (ours) if FORCE_BODY is set */\n if (body && FORCE_BODY) {\n _forceRemove(body.firstChild);\n }\n /* Get node iterator */\n const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);\n /* Now start iterating over the created document */\n while (currentNode = nodeIterator.nextNode()) {\n /* Sanitize tags and elements */\n _sanitizeElements(currentNode);\n /* Check attributes next */\n _sanitizeAttributes(currentNode);\n /* Shadow DOM detected, sanitize it */\n if (currentNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(currentNode.content);\n }\n }\n /* If we sanitized `dirty` in-place, return it. */\n if (IN_PLACE) {\n return dirty;\n }\n /* Return sanitized string or DOM */\n if (RETURN_DOM) {\n if (RETURN_DOM_FRAGMENT) {\n returnNode = createDocumentFragment.call(body.ownerDocument);\n while (body.firstChild) {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n returnNode.appendChild(body.firstChild);\n }\n } else {\n returnNode = body;\n }\n if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {\n /*\n AdoptNode() is not used because internal state is not reset\n (e.g. the past names map of a HTMLFormElement), this is safe\n in theory but we would rather not risk another attack vector.\n The state that is cloned by importNode() is explicitly defined\n by the specs.\n */\n returnNode = importNode.call(originalDocument, returnNode, true);\n }\n return returnNode;\n }\n let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;\n /* Serialize doctype if allowed */\n if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {\n serializedHTML = '\\n' + serializedHTML;\n }\n /* Sanitize final string template-safe */\n if (SAFE_FOR_TEMPLATES) {\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n serializedHTML = stringReplace(serializedHTML, expr, ' ');\n });\n }\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;\n };\n DOMPurify.setConfig = function () {\n let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _parseConfig(cfg);\n SET_CONFIG = true;\n };\n DOMPurify.clearConfig = function () {\n CONFIG = null;\n SET_CONFIG = false;\n };\n DOMPurify.isValidAttribute = function (tag, attr, value) {\n /* Initialize shared config vars if necessary. */\n if (!CONFIG) {\n _parseConfig({});\n }\n const lcTag = transformCaseFunc(tag);\n const lcName = transformCaseFunc(attr);\n return _isValidAttribute(lcTag, lcName, value);\n };\n DOMPurify.addHook = function (entryPoint, hookFunction) {\n if (typeof hookFunction !== 'function') {\n return;\n }\n arrayPush(hooks[entryPoint], hookFunction);\n };\n DOMPurify.removeHook = function (entryPoint, hookFunction) {\n if (hookFunction !== undefined) {\n const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);\n return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];\n }\n return arrayPop(hooks[entryPoint]);\n };\n DOMPurify.removeHooks = function (entryPoint) {\n hooks[entryPoint] = [];\n };\n DOMPurify.removeAllHooks = function () {\n hooks = _createHooksMap();\n };\n return DOMPurify;\n}\nvar purify = createDOMPurify();\n\nexport { purify as default };\n//# sourceMappingURL=purify.es.mjs.map\n","/**\n * marked v17.0.1 - a markdown parser\n * Copyright (c) 2018-2025, MarkedJS. (MIT License)\n * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License)\n * https://github.com/markedjs/marked\n */\n\n/**\n * DO NOT EDIT THIS FILE\n * The code in this file is generated from files in ./src/\n */\n\nfunction L(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var T=L();function Z(u){T=u}var C={exec:()=>null};function k(u,e=\"\"){let t=typeof u==\"string\"?u:u.source,n={replace:(r,i)=>{let s=typeof i==\"string\"?i:i.source;return s=s.replace(m.caret,\"$1\"),t=t.replace(r,s),n},getRegex:()=>new RegExp(t,e)};return n}var me=(()=>{try{return!!new RegExp(\"(?<=1)(?/,blockquoteSetextReplace:/\\n {0,3}((?:=+|-+) *)(?=\\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \\t]?/gm,listReplaceTabs:/^\\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\\[[ xX]\\] +\\S/,listReplaceTask:/^\\[[ xX]\\] +/,listTaskCheckbox:/\\[[ xX]\\]/,anyLine:/\\n.*\\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\\||\\| *$/g,tableRowBlankLine:/\\n[ \\t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\\s|>)/i,endPreScriptTag:/^<\\/(pre|code|kbd|script)(\\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'\"]*[^\\s])\\s+(['\"])(.*)\\2/,unicodeAlphaNumeric:/[\\p{L}\\p{N}]/u,escapeTest:/[&<>\"']/,escapeReplace:/[&<>\"']/g,escapeTestNoEncode:/[<>\"']|&(?!(#\\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\\w+);)/,escapeReplaceNoEncode:/[<>\"']|&(?!(#\\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\\w+);)/g,unescapeTest:/&(#(?:\\d+)|(?:#x[0-9A-Fa-f]+)|(?:\\w+));?/ig,caret:/(^|[^\\[])\\^/g,percentDecode:/%25/g,findPipe:/\\|/g,splitPipe:/ \\|/,slashPipe:/\\\\\\|/g,carriageReturn:/\\r\\n|\\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\\S*/,endingNewline:/\\n$/,listItemRegex:u=>new RegExp(`^( {0,3}${u})((?:[\t ][^\\\\n]*)?(?:\\\\n|$))`),nextBulletRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}(?:[*+-]|\\\\d{1,9}[.)])((?:[ \t][^\\\\n]*)?(?:\\\\n|$))`),hrRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\\\* *){3,})(?:\\\\n+|$)`),fencesBeginRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}(?:\\`\\`\\`|~~~)`),headingBeginRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}#`),htmlBeginRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}<(?:[a-z].*>|!--)`,\"i\")},xe=/^(?:[ \\t]*(?:\\n|$))+/,be=/^((?: {4}| {0,3}\\t)[^\\n]+(?:\\n(?:[ \\t]*(?:\\n|$))*)?)+/,Re=/^ {0,3}(`{3,}(?=[^`\\n]*(?:\\n|$))|~{3,})([^\\n]*)(?:\\n|$)(?:|([\\s\\S]*?)(?:\\n|$))(?: {0,3}\\1[~`]* *(?=\\n|$)|$)/,I=/^ {0,3}((?:-[\\t ]*){3,}|(?:_[ \\t]*){3,}|(?:\\*[ \\t]*){3,})(?:\\n+|$)/,Te=/^ {0,3}(#{1,6})(?=\\s|$)(.*)(?:\\n+|$)/,N=/(?:[*+-]|\\d{1,9}[.)])/,re=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\\n(?!\\s*?\\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,se=k(re).replace(/bull/g,N).replace(/blockCode/g,/(?: {4}| {0,3}\\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\\n>]+>\\n/).replace(/\\|table/g,\"\").getRegex(),Oe=k(re).replace(/bull/g,N).replace(/blockCode/g,/(?: {4}| {0,3}\\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\\n>]+>\\n/).replace(/table/g,/ {0,3}\\|?(?:[:\\- ]*\\|)+[\\:\\- ]*\\n/).getRegex(),Q=/^([^\\n]+(?:\\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\\n)[^\\n]+)*)/,we=/^[^\\n]+/,F=/(?!\\s*\\])(?:\\\\[\\s\\S]|[^\\[\\]\\\\])+/,ye=k(/^ {0,3}\\[(label)\\]: *(?:\\n[ \\t]*)?([^<\\s][^\\s]*|<.*?>)(?:(?: +(?:\\n[ \\t]*)?| *\\n[ \\t]*)(title))? *(?:\\n+|$)/).replace(\"label\",F).replace(\"title\",/(?:\"(?:\\\\\"?|[^\"\\\\])*\"|'[^'\\n]*(?:\\n[^'\\n]+)*\\n?'|\\([^()]*\\))/).getRegex(),Pe=k(/^( {0,3}bull)([ \\t][^\\n]+?)?(?:\\n|$)/).replace(/bull/g,N).getRegex(),v=\"address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul\",j=/|$))/,Se=k(\"^ {0,3}(?:<(script|pre|style|textarea)[\\\\s>][\\\\s\\\\S]*?(?:[^\\\\n]*\\\\n+|$)|comment[^\\\\n]*(\\\\n+|$)|<\\\\?[\\\\s\\\\S]*?(?:\\\\?>\\\\n*|$)|\\\\n*|$)|\\\\n*|$)|)[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$)|<(?!script|pre|style|textarea)([a-z][\\\\w-]*)(?:attribute)*? */?>(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$)|(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$))\",\"i\").replace(\"comment\",j).replace(\"tag\",v).replace(\"attribute\",/ +[a-zA-Z:_][\\w.:-]*(?: *= *\"[^\"\\n]*\"| *= *'[^'\\n]*'| *= *[^\\s\"'=<>`]+)?/).getRegex(),ie=k(Q).replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"|lheading\",\"\").replace(\"|table\",\"\").replace(\"blockquote\",\" {0,3}>\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)]) \").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex(),$e=k(/^( {0,3}> ?(paragraph|[^\\n]*)(?:\\n|$))+/).replace(\"paragraph\",ie).getRegex(),U={blockquote:$e,code:be,def:ye,fences:Re,heading:Te,hr:I,html:Se,lheading:se,list:Pe,newline:xe,paragraph:ie,table:C,text:we},te=k(\"^ *([^\\\\n ].*)\\\\n {0,3}((?:\\\\| *)?:?-+:? *(?:\\\\| *:?-+:? *)*(?:\\\\| *)?)(?:\\\\n((?:(?! *\\\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\\\n|$))*)\\\\n*|$)\").replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"blockquote\",\" {0,3}>\").replace(\"code\",\"(?: {4}| {0,3}\t)[^\\\\n]\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)]) \").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex(),_e={...U,lheading:Oe,table:te,paragraph:k(Q).replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"|lheading\",\"\").replace(\"table\",te).replace(\"blockquote\",\" {0,3}>\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)]) \").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex()},Le={...U,html:k(`^ *(?:comment *(?:\\\\n|\\\\s*$)|<(tag)[\\\\s\\\\S]+? *(?:\\\\n{2,}|\\\\s*$)|\\\\s]*)*?/?> *(?:\\\\n{2,}|\\\\s*$))`).replace(\"comment\",j).replace(/tag/g,\"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\\\b)\\\\w+(?!:|[^\\\\w\\\\s@]*@)\\\\b\").getRegex(),def:/^ *\\[([^\\]]+)\\]: *]+)>?(?: +([\"(][^\\n]+[\")]))? *(?:\\n+|$)/,heading:/^(#{1,6})(.*)(?:\\n+|$)/,fences:C,lheading:/^(.+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,paragraph:k(Q).replace(\"hr\",I).replace(\"heading\",` *#{1,6} *[^\n]`).replace(\"lheading\",se).replace(\"|table\",\"\").replace(\"blockquote\",\" {0,3}>\").replace(\"|fences\",\"\").replace(\"|list\",\"\").replace(\"|html\",\"\").replace(\"|tag\",\"\").getRegex()},Me=/^\\\\([!\"#$%&'()*+,\\-./:;<=>?@\\[\\]\\\\^_`{|}~])/,ze=/^(`+)([^`]|[^`][\\s\\S]*?[^`])\\1(?!`)/,oe=/^( {2,}|\\\\)\\n(?!\\s*$)/,Ae=/^(`+|[^`])(?:(?= {2,}\\n)|[\\s\\S]*?(?:(?=[\\\\`+)[^`]+\\k(?!`))*?\\]\\((?:\\\\[\\s\\S]|[^\\\\\\(\\)]|\\((?:\\\\[\\s\\S]|[^\\\\\\(\\)])*\\))*\\)/).replace(\"precode-\",me?\"(?`+)[^`]+\\k(?!`)/).replace(\"html\",/<(?! )[^<>]*?>/).getRegex(),ue=/^(?:\\*+(?:((?!\\*)punct)|[^\\s*]))|^_+(?:((?!_)punct)|([^\\s_]))/,qe=k(ue,\"u\").replace(/punct/g,D).getRegex(),ve=k(ue,\"u\").replace(/punct/g,le).getRegex(),pe=\"^[^_*]*?__[^_*]*?\\\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\\\*)punct(\\\\*+)(?=[\\\\s]|$)|notPunctSpace(\\\\*+)(?!\\\\*)(?=punctSpace|$)|(?!\\\\*)punctSpace(\\\\*+)(?=notPunctSpace)|[\\\\s](\\\\*+)(?!\\\\*)(?=punct)|(?!\\\\*)punct(\\\\*+)(?!\\\\*)(?=punct)|notPunctSpace(\\\\*+)(?=notPunctSpace)\",De=k(pe,\"gu\").replace(/notPunctSpace/g,ae).replace(/punctSpace/g,K).replace(/punct/g,D).getRegex(),He=k(pe,\"gu\").replace(/notPunctSpace/g,Ee).replace(/punctSpace/g,Ie).replace(/punct/g,le).getRegex(),Ze=k(\"^[^_*]*?\\\\*\\\\*[^_*]*?_[^_*]*?(?=\\\\*\\\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)\",\"gu\").replace(/notPunctSpace/g,ae).replace(/punctSpace/g,K).replace(/punct/g,D).getRegex(),Ge=k(/\\\\(punct)/,\"gu\").replace(/punct/g,D).getRegex(),Ne=k(/^<(scheme:[^\\s\\x00-\\x1f<>]*|email)>/).replace(\"scheme\",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace(\"email\",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),Qe=k(j).replace(\"(?:-->|$)\",\"-->\").getRegex(),Fe=k(\"^comment|^|^<[a-zA-Z][\\\\w-]*(?:attribute)*?\\\\s*/?>|^<\\\\?[\\\\s\\\\S]*?\\\\?>|^|^\").replace(\"comment\",Qe).replace(\"attribute\",/\\s+[a-zA-Z:_][\\w.:-]*(?:\\s*=\\s*\"[^\"]*\"|\\s*=\\s*'[^']*'|\\s*=\\s*[^\\s\"'=<>`]+)?/).getRegex(),q=/(?:\\[(?:\\\\[\\s\\S]|[^\\[\\]\\\\])*\\]|\\\\[\\s\\S]|`+[^`]*?`+(?!`)|[^\\[\\]\\\\`])*?/,je=k(/^!?\\[(label)\\]\\(\\s*(href)(?:(?:[ \\t]*(?:\\n[ \\t]*)?)(title))?\\s*\\)/).replace(\"label\",q).replace(\"href\",/<(?:\\\\.|[^\\n<>\\\\])+>|[^ \\t\\n\\x00-\\x1f]*/).replace(\"title\",/\"(?:\\\\\"?|[^\"\\\\])*\"|'(?:\\\\'?|[^'\\\\])*'|\\((?:\\\\\\)?|[^)\\\\])*\\)/).getRegex(),ce=k(/^!?\\[(label)\\]\\[(ref)\\]/).replace(\"label\",q).replace(\"ref\",F).getRegex(),he=k(/^!?\\[(ref)\\](?:\\[\\])?/).replace(\"ref\",F).getRegex(),Ue=k(\"reflink|nolink(?!\\\\()\",\"g\").replace(\"reflink\",ce).replace(\"nolink\",he).getRegex(),ne=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,W={_backpedal:C,anyPunctuation:Ge,autolink:Ne,blockSkip:Be,br:oe,code:ze,del:C,emStrongLDelim:qe,emStrongRDelimAst:De,emStrongRDelimUnd:Ze,escape:Me,link:je,nolink:he,punctuation:Ce,reflink:ce,reflinkSearch:Ue,tag:Fe,text:Ae,url:C},Ke={...W,link:k(/^!?\\[(label)\\]\\((.*?)\\)/).replace(\"label\",q).getRegex(),reflink:k(/^!?\\[(label)\\]\\s*\\[([^\\]]*)\\]/).replace(\"label\",q).getRegex()},G={...W,emStrongRDelimAst:He,emStrongLDelim:ve,url:k(/^((?:protocol):\\/\\/|www\\.)(?:[a-zA-Z0-9\\-]+\\.?)+[^\\s<]*|^email/).replace(\"protocol\",ne).replace(\"email\",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'\"~()&]+|\\([^)]*\\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'\"~)]+(?!$))+/,del:/^(~~?)(?=[^\\s~])((?:\\\\[\\s\\S]|[^\\\\])*?(?:\\\\[\\s\\S]|[^\\s~\\\\]))\\1(?=[^~]|$)/,text:k(/^([`~]+|[^`~])(?:(?= {2,}\\n)|(?=[a-zA-Z0-9.!#$%&'*+\\/=?_`{\\|}~-]+@)|[\\s\\S]*?(?:(?=[\\\\\":\">\",'\"':\""\",\"'\":\"'\"},ke=u=>Xe[u];function w(u,e){if(e){if(m.escapeTest.test(u))return u.replace(m.escapeReplace,ke)}else if(m.escapeTestNoEncode.test(u))return u.replace(m.escapeReplaceNoEncode,ke);return u}function X(u){try{u=encodeURI(u).replace(m.percentDecode,\"%\")}catch{return null}return u}function J(u,e){let t=u.replace(m.findPipe,(i,s,a)=>{let o=!1,l=s;for(;--l>=0&&a[l]===\"\\\\\";)o=!o;return o?\"|\":\" |\"}),n=t.split(m.splitPipe),r=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length0?-2:-1}function ge(u,e,t,n,r){let i=e.href,s=e.title||null,a=u[1].replace(r.other.outputLinkReplace,\"$1\");n.state.inLink=!0;let o={type:u[0].charAt(0)===\"!\"?\"image\":\"link\",raw:t,href:i,title:s,text:a,tokens:n.inlineTokens(a)};return n.state.inLink=!1,o}function Je(u,e,t){let n=u.match(t.other.indentCodeCompensation);if(n===null)return e;let r=n[1];return e.split(`\n`).map(i=>{let s=i.match(t.other.beginningSpace);if(s===null)return i;let[a]=s;return a.length>=r.length?i.slice(r.length):i}).join(`\n`)}var y=class{options;rules;lexer;constructor(e){this.options=e||T}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:\"space\",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=t[0].replace(this.rules.other.codeRemoveIndent,\"\");return{type:\"code\",raw:t[0],codeBlockStyle:\"indented\",text:this.options.pedantic?n:z(n,`\n`)}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],r=Je(n,t[3]||\"\",this.rules);return{type:\"code\",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,\"$1\"):t[2],text:r}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let r=z(n,\"#\");(this.options.pedantic||!r||this.rules.other.endingSpaceChar.test(r))&&(n=r.trim())}return{type:\"heading\",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:\"hr\",raw:z(t[0],`\n`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=z(t[0],`\n`).split(`\n`),r=\"\",i=\"\",s=[];for(;n.length>0;){let a=!1,o=[],l;for(l=0;l1,i={type:\"list\",raw:\"\",ordered:r,start:r?+n.slice(0,-1):\"\",loose:!1,items:[]};n=r?`\\\\d{1,9}\\\\${n.slice(-1)}`:`\\\\${n}`,this.options.pedantic&&(n=r?n:\"[*+-]\");let s=this.rules.other.listItemRegex(n),a=!1;for(;e;){let l=!1,p=\"\",c=\"\";if(!(t=s.exec(e))||this.rules.block.hr.test(e))break;p=t[0],e=e.substring(p.length);let g=t[2].split(`\n`,1)[0].replace(this.rules.other.listReplaceTabs,O=>\" \".repeat(3*O.length)),h=e.split(`\n`,1)[0],R=!g.trim(),f=0;if(this.options.pedantic?(f=2,c=g.trimStart()):R?f=t[1].length+1:(f=t[2].search(this.rules.other.nonSpaceChar),f=f>4?1:f,c=g.slice(f),f+=t[1].length),R&&this.rules.other.blankLine.test(h)&&(p+=h+`\n`,e=e.substring(h.length+1),l=!0),!l){let O=this.rules.other.nextBulletRegex(f),V=this.rules.other.hrRegex(f),Y=this.rules.other.fencesBeginRegex(f),ee=this.rules.other.headingBeginRegex(f),fe=this.rules.other.htmlBeginRegex(f);for(;e;){let H=e.split(`\n`,1)[0],A;if(h=H,this.options.pedantic?(h=h.replace(this.rules.other.listReplaceNesting,\" \"),A=h):A=h.replace(this.rules.other.tabCharGlobal,\" \"),Y.test(h)||ee.test(h)||fe.test(h)||O.test(h)||V.test(h))break;if(A.search(this.rules.other.nonSpaceChar)>=f||!h.trim())c+=`\n`+A.slice(f);else{if(R||g.replace(this.rules.other.tabCharGlobal,\" \").search(this.rules.other.nonSpaceChar)>=4||Y.test(g)||ee.test(g)||V.test(g))break;c+=`\n`+h}!R&&!h.trim()&&(R=!0),p+=H+`\n`,e=e.substring(H.length+1),g=A.slice(f)}}i.loose||(a?i.loose=!0:this.rules.other.doubleBlankLine.test(p)&&(a=!0)),i.items.push({type:\"list_item\",raw:p,task:!!this.options.gfm&&this.rules.other.listIsTask.test(c),loose:!1,text:c,tokens:[]}),i.raw+=p}let o=i.items.at(-1);if(o)o.raw=o.raw.trimEnd(),o.text=o.text.trimEnd();else return;i.raw=i.raw.trimEnd();for(let l of i.items){if(this.lexer.state.top=!1,l.tokens=this.lexer.blockTokens(l.text,[]),l.task){if(l.text=l.text.replace(this.rules.other.listReplaceTask,\"\"),l.tokens[0]?.type===\"text\"||l.tokens[0]?.type===\"paragraph\"){l.tokens[0].raw=l.tokens[0].raw.replace(this.rules.other.listReplaceTask,\"\"),l.tokens[0].text=l.tokens[0].text.replace(this.rules.other.listReplaceTask,\"\");for(let c=this.lexer.inlineQueue.length-1;c>=0;c--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[c].src)){this.lexer.inlineQueue[c].src=this.lexer.inlineQueue[c].src.replace(this.rules.other.listReplaceTask,\"\");break}}let p=this.rules.other.listTaskCheckbox.exec(l.raw);if(p){let c={type:\"checkbox\",raw:p[0]+\" \",checked:p[0]!==\"[ ]\"};l.checked=c.checked,i.loose?l.tokens[0]&&[\"paragraph\",\"text\"].includes(l.tokens[0].type)&&\"tokens\"in l.tokens[0]&&l.tokens[0].tokens?(l.tokens[0].raw=c.raw+l.tokens[0].raw,l.tokens[0].text=c.raw+l.tokens[0].text,l.tokens[0].tokens.unshift(c)):l.tokens.unshift({type:\"paragraph\",raw:c.raw,text:c.raw,tokens:[c]}):l.tokens.unshift(c)}}if(!i.loose){let p=l.tokens.filter(g=>g.type===\"space\"),c=p.length>0&&p.some(g=>this.rules.other.anyLine.test(g.raw));i.loose=c}}if(i.loose)for(let l of i.items){l.loose=!0;for(let p of l.tokens)p.type===\"text\"&&(p.type=\"paragraph\")}return i}}html(e){let t=this.rules.block.html.exec(e);if(t)return{type:\"html\",block:!0,raw:t[0],pre:t[1]===\"pre\"||t[1]===\"script\"||t[1]===\"style\",text:t[0]}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal,\" \"),r=t[2]?t[2].replace(this.rules.other.hrefBrackets,\"$1\").replace(this.rules.inline.anyPunctuation,\"$1\"):\"\",i=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,\"$1\"):t[3];return{type:\"def\",tag:n,raw:t[0],href:r,title:i}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=J(t[1]),r=t[2].replace(this.rules.other.tableAlignChars,\"\").split(\"|\"),i=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,\"\").split(`\n`):[],s={type:\"table\",raw:t[0],header:[],align:[],rows:[]};if(n.length===r.length){for(let a of r)this.rules.other.tableAlignRight.test(a)?s.align.push(\"right\"):this.rules.other.tableAlignCenter.test(a)?s.align.push(\"center\"):this.rules.other.tableAlignLeft.test(a)?s.align.push(\"left\"):s.align.push(null);for(let a=0;a({text:o,tokens:this.lexer.inline(o),header:!1,align:s.align[l]})));return s}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t)return{type:\"heading\",raw:t[0],depth:t[2].charAt(0)===\"=\"?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===`\n`?t[1].slice(0,-1):t[1];return{type:\"paragraph\",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:\"text\",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:\"escape\",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:\"html\",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let s=z(n.slice(0,-1),\"\\\\\");if((n.length-s.length)%2===0)return}else{let s=de(t[2],\"()\");if(s===-2)return;if(s>-1){let o=(t[0].indexOf(\"!\")===0?5:4)+t[1].length+s;t[2]=t[2].substring(0,s),t[0]=t[0].substring(0,o).trim(),t[3]=\"\"}}let r=t[2],i=\"\";if(this.options.pedantic){let s=this.rules.other.pedanticHrefTitle.exec(r);s&&(r=s[1],i=s[3])}else i=t[3]?t[3].slice(1,-1):\"\";return r=r.trim(),this.rules.other.startAngleBracket.test(r)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?r=r.slice(1):r=r.slice(1,-1)),ge(t,{href:r&&r.replace(this.rules.inline.anyPunctuation,\"$1\"),title:i&&i.replace(this.rules.inline.anyPunctuation,\"$1\")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let r=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal,\" \"),i=t[r.toLowerCase()];if(!i){let s=n[0].charAt(0);return{type:\"text\",raw:s,text:s}}return ge(n,i,n[0],this.lexer,this.rules)}}emStrong(e,t,n=\"\"){let r=this.rules.inline.emStrongLDelim.exec(e);if(!r||r[3]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(r[1]||r[2]||\"\")||!n||this.rules.inline.punctuation.exec(n)){let s=[...r[0]].length-1,a,o,l=s,p=0,c=r[0][0]===\"*\"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(c.lastIndex=0,t=t.slice(-1*e.length+s);(r=c.exec(t))!=null;){if(a=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!a)continue;if(o=[...a].length,r[3]||r[4]){l+=o;continue}else if((r[5]||r[6])&&s%3&&!((s+o)%3)){p+=o;continue}if(l-=o,l>0)continue;o=Math.min(o,o+l+p);let g=[...r[0]][0].length,h=e.slice(0,s+r.index+g+o);if(Math.min(s,o)%2){let f=h.slice(1,-1);return{type:\"em\",raw:h,text:f,tokens:this.lexer.inlineTokens(f)}}let R=h.slice(2,-2);return{type:\"strong\",raw:h,text:R,tokens:this.lexer.inlineTokens(R)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal,\" \"),r=this.rules.other.nonSpaceChar.test(n),i=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return r&&i&&(n=n.substring(1,n.length-1)),{type:\"codespan\",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:\"br\",raw:t[0]}}del(e){let t=this.rules.inline.del.exec(e);if(t)return{type:\"del\",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,r;return t[2]===\"@\"?(n=t[1],r=\"mailto:\"+n):(n=t[1],r=n),{type:\"link\",raw:t[0],text:n,href:r,tokens:[{type:\"text\",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,r;if(t[2]===\"@\")n=t[0],r=\"mailto:\"+n;else{let i;do i=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??\"\";while(i!==t[0]);n=t[0],t[1]===\"www.\"?r=\"http://\"+t[0]:r=t[0]}return{type:\"link\",raw:t[0],text:n,href:r,tokens:[{type:\"text\",raw:n,text:n}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:\"text\",raw:t[0],text:t[0],escaped:n}}}};var x=class u{tokens;options;state;inlineQueue;tokenizer;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||T,this.options.tokenizer=this.options.tokenizer||new y,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let t={other:m,block:E.normal,inline:M.normal};this.options.pedantic?(t.block=E.pedantic,t.inline=M.pedantic):this.options.gfm&&(t.block=E.gfm,this.options.breaks?t.inline=M.breaks:t.inline=M.gfm),this.tokenizer.rules=t}static get rules(){return{block:E,inline:M}}static lex(e,t){return new u(t).lex(e)}static lexInline(e,t){return new u(t).inlineTokens(e)}lex(e){e=e.replace(m.carriageReturn,`\n`),this.blockTokens(e,this.tokens);for(let t=0;t(r=s.call({lexer:this},e,t))?(e=e.substring(r.raw.length),t.push(r),!0):!1))continue;if(r=this.tokenizer.space(e)){e=e.substring(r.raw.length);let s=t.at(-1);r.raw.length===1&&s!==void 0?s.raw+=`\n`:t.push(r);continue}if(r=this.tokenizer.code(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type===\"paragraph\"||s?.type===\"text\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.text,this.inlineQueue.at(-1).src=s.text):t.push(r);continue}if(r=this.tokenizer.fences(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.heading(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.hr(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.blockquote(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.list(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.html(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.def(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type===\"paragraph\"||s?.type===\"text\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.raw,this.inlineQueue.at(-1).src=s.text):this.tokens.links[r.tag]||(this.tokens.links[r.tag]={href:r.href,title:r.title},t.push(r));continue}if(r=this.tokenizer.table(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.lheading(e)){e=e.substring(r.raw.length),t.push(r);continue}let i=e;if(this.options.extensions?.startBlock){let s=1/0,a=e.slice(1),o;this.options.extensions.startBlock.forEach(l=>{o=l.call({lexer:this},a),typeof o==\"number\"&&o>=0&&(s=Math.min(s,o))}),s<1/0&&s>=0&&(i=e.substring(0,s+1))}if(this.state.top&&(r=this.tokenizer.paragraph(i))){let s=t.at(-1);n&&s?.type===\"paragraph\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=s.text):t.push(r),n=i.length!==e.length,e=e.substring(r.raw.length);continue}if(r=this.tokenizer.text(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type===\"text\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=s.text):t.push(r);continue}if(e){let s=\"Infinite loop on byte: \"+e.charCodeAt(0);if(this.options.silent){console.error(s);break}else throw new Error(s)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,r=null;if(this.tokens.links){let o=Object.keys(this.tokens.links);if(o.length>0)for(;(r=this.tokenizer.rules.inline.reflinkSearch.exec(n))!=null;)o.includes(r[0].slice(r[0].lastIndexOf(\"[\")+1,-1))&&(n=n.slice(0,r.index)+\"[\"+\"a\".repeat(r[0].length-2)+\"]\"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(r=this.tokenizer.rules.inline.anyPunctuation.exec(n))!=null;)n=n.slice(0,r.index)+\"++\"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let i;for(;(r=this.tokenizer.rules.inline.blockSkip.exec(n))!=null;)i=r[2]?r[2].length:0,n=n.slice(0,r.index+i)+\"[\"+\"a\".repeat(r[0].length-i-2)+\"]\"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);n=this.options.hooks?.emStrongMask?.call({lexer:this},n)??n;let s=!1,a=\"\";for(;e;){s||(a=\"\"),s=!1;let o;if(this.options.extensions?.inline?.some(p=>(o=p.call({lexer:this},e,t))?(e=e.substring(o.raw.length),t.push(o),!0):!1))continue;if(o=this.tokenizer.escape(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.tag(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.link(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(o.raw.length);let p=t.at(-1);o.type===\"text\"&&p?.type===\"text\"?(p.raw+=o.raw,p.text+=o.text):t.push(o);continue}if(o=this.tokenizer.emStrong(e,n,a)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.codespan(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.br(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.del(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.autolink(e)){e=e.substring(o.raw.length),t.push(o);continue}if(!this.state.inLink&&(o=this.tokenizer.url(e))){e=e.substring(o.raw.length),t.push(o);continue}let l=e;if(this.options.extensions?.startInline){let p=1/0,c=e.slice(1),g;this.options.extensions.startInline.forEach(h=>{g=h.call({lexer:this},c),typeof g==\"number\"&&g>=0&&(p=Math.min(p,g))}),p<1/0&&p>=0&&(l=e.substring(0,p+1))}if(o=this.tokenizer.inlineText(l)){e=e.substring(o.raw.length),o.raw.slice(-1)!==\"_\"&&(a=o.raw.slice(-1)),s=!0;let p=t.at(-1);p?.type===\"text\"?(p.raw+=o.raw,p.text+=o.text):t.push(o);continue}if(e){let p=\"Infinite loop on byte: \"+e.charCodeAt(0);if(this.options.silent){console.error(p);break}else throw new Error(p)}}return t}};var P=class{options;parser;constructor(e){this.options=e||T}space(e){return\"\"}code({text:e,lang:t,escaped:n}){let r=(t||\"\").match(m.notSpaceStart)?.[0],i=e.replace(m.endingNewline,\"\")+`\n`;return r?'
    '+(n?i:w(i,!0))+`
    \n`:\"
    \"+(n?i:w(i,!0))+`
    \n`}blockquote({tokens:e}){return`
    \n${this.parser.parse(e)}
    \n`}html({text:e}){return e}def(e){return\"\"}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)}\n`}hr(e){return`
    \n`}list(e){let t=e.ordered,n=e.start,r=\"\";for(let a=0;a\n`+r+\"\n`}listitem(e){return`
  • ${this.parser.parse(e.tokens)}
  • \n`}checkbox({checked:e}){return\" '}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    \n`}table(e){let t=\"\",n=\"\";for(let i=0;i${r}`),`\n\n`+t+`\n`+r+`
    \n`}tablerow({text:e}){return`\n${e}\n`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?\"th\":\"td\";return(e.align?`<${n} align=\"${e.align}\">`:`<${n}>`)+t+`\n`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${w(e,!0)}`}br(e){return\"
    \"}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let r=this.parser.parseInline(n),i=X(e);if(i===null)return r;e=i;let s='
    \"+r+\"\",s}image({href:e,title:t,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let i=X(e);if(i===null)return w(n);e=i;let s=`\"${n}\"`;return\",s}text(e){return\"tokens\"in e&&e.tokens?this.parser.parseInline(e.tokens):\"escaped\"in e&&e.escaped?e.text:w(e.text)}};var $=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return\"\"+e}image({text:e}){return\"\"+e}br(){return\"\"}checkbox({raw:e}){return e}};var b=class u{options;renderer;textRenderer;constructor(e){this.options=e||T,this.options.renderer=this.options.renderer||new P,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new $}static parse(e,t){return new u(t).parse(e)}static parseInline(e,t){return new u(t).parseInline(e)}parse(e){let t=\"\";for(let n=0;n{let a=i[s].flat(1/0);n=n.concat(this.walkTokens(a,t))}):i.tokens&&(n=n.concat(this.walkTokens(i.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let r={...n};if(r.async=this.defaults.async||r.async||!1,n.extensions&&(n.extensions.forEach(i=>{if(!i.name)throw new Error(\"extension name required\");if(\"renderer\"in i){let s=t.renderers[i.name];s?t.renderers[i.name]=function(...a){let o=i.renderer.apply(this,a);return o===!1&&(o=s.apply(this,a)),o}:t.renderers[i.name]=i.renderer}if(\"tokenizer\"in i){if(!i.level||i.level!==\"block\"&&i.level!==\"inline\")throw new Error(\"extension level must be 'block' or 'inline'\");let s=t[i.level];s?s.unshift(i.tokenizer):t[i.level]=[i.tokenizer],i.start&&(i.level===\"block\"?t.startBlock?t.startBlock.push(i.start):t.startBlock=[i.start]:i.level===\"inline\"&&(t.startInline?t.startInline.push(i.start):t.startInline=[i.start]))}\"childTokens\"in i&&i.childTokens&&(t.childTokens[i.name]=i.childTokens)}),r.extensions=t),n.renderer){let i=this.defaults.renderer||new P(this.defaults);for(let s in n.renderer){if(!(s in i))throw new Error(`renderer '${s}' does not exist`);if([\"options\",\"parser\"].includes(s))continue;let a=s,o=n.renderer[a],l=i[a];i[a]=(...p)=>{let c=o.apply(i,p);return c===!1&&(c=l.apply(i,p)),c||\"\"}}r.renderer=i}if(n.tokenizer){let i=this.defaults.tokenizer||new y(this.defaults);for(let s in n.tokenizer){if(!(s in i))throw new Error(`tokenizer '${s}' does not exist`);if([\"options\",\"rules\",\"lexer\"].includes(s))continue;let a=s,o=n.tokenizer[a],l=i[a];i[a]=(...p)=>{let c=o.apply(i,p);return c===!1&&(c=l.apply(i,p)),c}}r.tokenizer=i}if(n.hooks){let i=this.defaults.hooks||new S;for(let s in n.hooks){if(!(s in i))throw new Error(`hook '${s}' does not exist`);if([\"options\",\"block\"].includes(s))continue;let a=s,o=n.hooks[a],l=i[a];S.passThroughHooks.has(s)?i[a]=p=>{if(this.defaults.async&&S.passThroughHooksRespectAsync.has(s))return(async()=>{let g=await o.call(i,p);return l.call(i,g)})();let c=o.call(i,p);return l.call(i,c)}:i[a]=(...p)=>{if(this.defaults.async)return(async()=>{let g=await o.apply(i,p);return g===!1&&(g=await l.apply(i,p)),g})();let c=o.apply(i,p);return c===!1&&(c=l.apply(i,p)),c}}r.hooks=i}if(n.walkTokens){let i=this.defaults.walkTokens,s=n.walkTokens;r.walkTokens=function(a){let o=[];return o.push(s.call(this,a)),i&&(o=o.concat(i.call(this,a))),o}}this.defaults={...this.defaults,...r}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return x.lex(e,t??this.defaults)}parser(e,t){return b.parse(e,t??this.defaults)}parseMarkdown(e){return(n,r)=>{let i={...r},s={...this.defaults,...i},a=this.onError(!!s.silent,!!s.async);if(this.defaults.async===!0&&i.async===!1)return a(new Error(\"marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.\"));if(typeof n>\"u\"||n===null)return a(new Error(\"marked(): input parameter is undefined or null\"));if(typeof n!=\"string\")return a(new Error(\"marked(): input parameter is of type \"+Object.prototype.toString.call(n)+\", string expected\"));if(s.hooks&&(s.hooks.options=s,s.hooks.block=e),s.async)return(async()=>{let o=s.hooks?await s.hooks.preprocess(n):n,p=await(s.hooks?await s.hooks.provideLexer():e?x.lex:x.lexInline)(o,s),c=s.hooks?await s.hooks.processAllTokens(p):p;s.walkTokens&&await Promise.all(this.walkTokens(c,s.walkTokens));let h=await(s.hooks?await s.hooks.provideParser():e?b.parse:b.parseInline)(c,s);return s.hooks?await s.hooks.postprocess(h):h})().catch(a);try{s.hooks&&(n=s.hooks.preprocess(n));let l=(s.hooks?s.hooks.provideLexer():e?x.lex:x.lexInline)(n,s);s.hooks&&(l=s.hooks.processAllTokens(l)),s.walkTokens&&this.walkTokens(l,s.walkTokens);let c=(s.hooks?s.hooks.provideParser():e?b.parse:b.parseInline)(l,s);return s.hooks&&(c=s.hooks.postprocess(c)),c}catch(o){return a(o)}}}onError(e,t){return n=>{if(n.message+=`\nPlease report this to https://github.com/markedjs/marked.`,e){let r=\"

    An error occurred:

    \"+w(n.message+\"\",!0)+\"
    \";return t?Promise.resolve(r):r}if(t)return Promise.reject(n);throw n}}};var _=new B;function d(u,e){return _.parse(u,e)}d.options=d.setOptions=function(u){return _.setOptions(u),d.defaults=_.defaults,Z(d.defaults),d};d.getDefaults=L;d.defaults=T;d.use=function(...u){return _.use(...u),d.defaults=_.defaults,Z(d.defaults),d};d.walkTokens=function(u,e){return _.walkTokens(u,e)};d.parseInline=_.parseInline;d.Parser=b;d.parser=b.parse;d.Renderer=P;d.TextRenderer=$;d.Lexer=x;d.lexer=x.lex;d.Tokenizer=y;d.Hooks=S;d.parse=d;var Dt=d.options,Ht=d.setOptions,Zt=d.use,Gt=d.walkTokens,Nt=d.parseInline,Qt=d,Ft=b.parse,jt=x.lex;export{S as Hooks,x as Lexer,B as Marked,b as Parser,P as Renderer,$ as TextRenderer,y as Tokenizer,T as defaults,L as getDefaults,jt as lexer,d as marked,Dt as options,Qt as parse,Nt as parseInline,Ft as parser,Ht as setOptions,Zt as use,Gt as walkTokens};\n//# sourceMappingURL=marked.esm.js.map\n","import DOMPurify from \"dompurify\";\nimport { marked } from \"marked\";\nimport { truncateText } from \"./format\";\n\nmarked.setOptions({\n gfm: true,\n breaks: true,\n mangle: false,\n});\n\nconst allowedTags = [\n \"a\",\n \"b\",\n \"blockquote\",\n \"br\",\n \"code\",\n \"del\",\n \"em\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"hr\",\n \"i\",\n \"li\",\n \"ol\",\n \"p\",\n \"pre\",\n \"strong\",\n \"table\",\n \"tbody\",\n \"td\",\n \"th\",\n \"thead\",\n \"tr\",\n \"ul\",\n];\n\nconst allowedAttrs = [\"class\", \"href\", \"rel\", \"target\", \"title\", \"start\"];\n\nlet hooksInstalled = false;\nconst MARKDOWN_CHAR_LIMIT = 140_000;\nconst MARKDOWN_PARSE_LIMIT = 40_000;\nconst MARKDOWN_CACHE_LIMIT = 200;\nconst MARKDOWN_CACHE_MAX_CHARS = 50_000;\nconst markdownCache = new Map();\n\nfunction getCachedMarkdown(key: string): string | null {\n const cached = markdownCache.get(key);\n if (cached === undefined) return null;\n markdownCache.delete(key);\n markdownCache.set(key, cached);\n return cached;\n}\n\nfunction setCachedMarkdown(key: string, value: string) {\n markdownCache.set(key, value);\n if (markdownCache.size <= MARKDOWN_CACHE_LIMIT) return;\n const oldest = markdownCache.keys().next().value;\n if (oldest) markdownCache.delete(oldest);\n}\n\nfunction installHooks() {\n if (hooksInstalled) return;\n hooksInstalled = true;\n\n DOMPurify.addHook(\"afterSanitizeAttributes\", (node) => {\n if (!(node instanceof HTMLAnchorElement)) return;\n const href = node.getAttribute(\"href\");\n if (!href) return;\n node.setAttribute(\"rel\", \"noreferrer noopener\");\n node.setAttribute(\"target\", \"_blank\");\n });\n}\n\nexport function toSanitizedMarkdownHtml(markdown: string): string {\n const input = markdown.trim();\n if (!input) return \"\";\n installHooks();\n if (input.length <= MARKDOWN_CACHE_MAX_CHARS) {\n const cached = getCachedMarkdown(input);\n if (cached !== null) return cached;\n }\n const truncated = truncateText(input, MARKDOWN_CHAR_LIMIT);\n const suffix = truncated.truncated\n ? `\\n\\n… truncated (${truncated.total} chars, showing first ${truncated.text.length}).`\n : \"\";\n if (truncated.text.length > MARKDOWN_PARSE_LIMIT) {\n const escaped = escapeHtml(`${truncated.text}${suffix}`);\n const html = `
    ${escaped}
    `;\n const sanitized = DOMPurify.sanitize(html, {\n ALLOWED_TAGS: allowedTags,\n ALLOWED_ATTR: allowedAttrs,\n });\n if (input.length <= MARKDOWN_CACHE_MAX_CHARS) {\n setCachedMarkdown(input, sanitized);\n }\n return sanitized;\n }\n const rendered = marked.parse(`${truncated.text}${suffix}`) as string;\n const sanitized = DOMPurify.sanitize(rendered, {\n ALLOWED_TAGS: allowedTags,\n ALLOWED_ATTR: allowedAttrs,\n });\n if (input.length <= MARKDOWN_CACHE_MAX_CHARS) {\n setCachedMarkdown(input, sanitized);\n }\n return sanitized;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(//g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n","import { html, type TemplateResult } from \"lit\";\nimport { icons } from \"../icons\";\n\nconst COPIED_FOR_MS = 1500;\nconst ERROR_FOR_MS = 2000;\nconst COPY_LABEL = \"Copy as markdown\";\nconst COPIED_LABEL = \"Copied\";\nconst ERROR_LABEL = \"Copy failed\";\n\ntype CopyButtonOptions = {\n text: () => string;\n label?: string;\n};\n\nasync function copyTextToClipboard(text: string): Promise {\n if (!text) return false;\n\n try {\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction setButtonLabel(button: HTMLButtonElement, label: string) {\n button.title = label;\n button.setAttribute(\"aria-label\", label);\n}\n\nfunction createCopyButton(options: CopyButtonOptions): TemplateResult {\n const idleLabel = options.label ?? COPY_LABEL;\n return html`\n {\n const btn = e.currentTarget as HTMLButtonElement | null;\n const iconContainer = btn?.querySelector(\n \".chat-copy-btn__icon\",\n ) as HTMLElement | null;\n\n if (!btn || btn.dataset.copying === \"1\") return;\n\n btn.dataset.copying = \"1\";\n btn.setAttribute(\"aria-busy\", \"true\");\n btn.disabled = true;\n\n const copied = await copyTextToClipboard(options.text());\n if (!btn.isConnected) return;\n\n delete btn.dataset.copying;\n btn.removeAttribute(\"aria-busy\");\n btn.disabled = false;\n\n if (!copied) {\n btn.dataset.error = \"1\";\n setButtonLabel(btn, ERROR_LABEL);\n\n window.setTimeout(() => {\n if (!btn.isConnected) return;\n delete btn.dataset.error;\n setButtonLabel(btn, idleLabel);\n }, ERROR_FOR_MS);\n return;\n }\n\n btn.dataset.copied = \"1\";\n setButtonLabel(btn, COPIED_LABEL);\n\n window.setTimeout(() => {\n if (!btn.isConnected) return;\n delete btn.dataset.copied;\n setButtonLabel(btn, idleLabel);\n }, COPIED_FOR_MS);\n }}\n >\n \n ${icons.copy}\n ${icons.check}\n \n \n `;\n}\n\nexport function renderCopyAsMarkdownButton(markdown: string): TemplateResult {\n return createCopyButton({ text: () => markdown, label: COPY_LABEL });\n}\n","import rawConfig from \"./tool-display.json\";\nimport type { IconName } from \"./icons\";\n\ntype ToolDisplayActionSpec = {\n label?: string;\n detailKeys?: string[];\n};\n\ntype ToolDisplaySpec = {\n icon?: string;\n title?: string;\n label?: string;\n detailKeys?: string[];\n actions?: Record;\n};\n\ntype ToolDisplayConfig = {\n version?: number;\n fallback?: ToolDisplaySpec;\n tools?: Record;\n};\n\nexport type ToolDisplay = {\n name: string;\n icon: IconName;\n title: string;\n label: string;\n verb?: string;\n detail?: string;\n};\n\nconst TOOL_DISPLAY_CONFIG = rawConfig as ToolDisplayConfig;\nconst FALLBACK = TOOL_DISPLAY_CONFIG.fallback ?? { icon: \"puzzle\" };\nconst TOOL_MAP = TOOL_DISPLAY_CONFIG.tools ?? {};\n\nfunction normalizeToolName(name?: string): string {\n return (name ?? \"tool\").trim();\n}\n\nfunction defaultTitle(name: string): string {\n const cleaned = name.replace(/_/g, \" \").trim();\n if (!cleaned) return \"Tool\";\n return cleaned\n .split(/\\s+/)\n .map((part) =>\n part.length <= 2 && part.toUpperCase() === part\n ? part\n : `${part.at(0)?.toUpperCase() ?? \"\"}${part.slice(1)}`,\n )\n .join(\" \");\n}\n\nfunction normalizeVerb(value?: string): string | undefined {\n const trimmed = value?.trim();\n if (!trimmed) return undefined;\n return trimmed.replace(/_/g, \" \");\n}\n\nfunction coerceDisplayValue(value: unknown): string | undefined {\n if (value === null || value === undefined) return undefined;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const firstLine = trimmed.split(/\\r?\\n/)[0]?.trim() ?? \"\";\n if (!firstLine) return undefined;\n return firstLine.length > 160 ? `${firstLine.slice(0, 157)}…` : firstLine;\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (Array.isArray(value)) {\n const values = value\n .map((item) => coerceDisplayValue(item))\n .filter((item): item is string => Boolean(item));\n if (values.length === 0) return undefined;\n const preview = values.slice(0, 3).join(\", \");\n return values.length > 3 ? `${preview}…` : preview;\n }\n return undefined;\n}\n\nfunction lookupValueByPath(args: unknown, path: string): unknown {\n if (!args || typeof args !== \"object\") return undefined;\n let current: unknown = args;\n for (const segment of path.split(\".\")) {\n if (!segment) return undefined;\n if (!current || typeof current !== \"object\") return undefined;\n const record = current as Record;\n current = record[segment];\n }\n return current;\n}\n\nfunction resolveDetailFromKeys(args: unknown, keys: string[]): string | undefined {\n for (const key of keys) {\n const value = lookupValueByPath(args, key);\n const display = coerceDisplayValue(value);\n if (display) return display;\n }\n return undefined;\n}\n\nfunction resolveReadDetail(args: unknown): string | undefined {\n if (!args || typeof args !== \"object\") return undefined;\n const record = args as Record;\n const path = typeof record.path === \"string\" ? record.path : undefined;\n if (!path) return undefined;\n const offset = typeof record.offset === \"number\" ? record.offset : undefined;\n const limit = typeof record.limit === \"number\" ? record.limit : undefined;\n if (offset !== undefined && limit !== undefined) {\n return `${path}:${offset}-${offset + limit}`;\n }\n return path;\n}\n\nfunction resolveWriteDetail(args: unknown): string | undefined {\n if (!args || typeof args !== \"object\") return undefined;\n const record = args as Record;\n const path = typeof record.path === \"string\" ? record.path : undefined;\n return path;\n}\n\nfunction resolveActionSpec(\n spec: ToolDisplaySpec | undefined,\n action: string | undefined,\n): ToolDisplayActionSpec | undefined {\n if (!spec || !action) return undefined;\n return spec.actions?.[action] ?? undefined;\n}\n\nexport function resolveToolDisplay(params: {\n name?: string;\n args?: unknown;\n meta?: string;\n}): ToolDisplay {\n const name = normalizeToolName(params.name);\n const key = name.toLowerCase();\n const spec = TOOL_MAP[key];\n const icon = (spec?.icon ?? FALLBACK.icon ?? \"puzzle\") as IconName;\n const title = spec?.title ?? defaultTitle(name);\n const label = spec?.label ?? name;\n const actionRaw =\n params.args && typeof params.args === \"object\"\n ? ((params.args as Record).action as string | undefined)\n : undefined;\n const action = typeof actionRaw === \"string\" ? actionRaw.trim() : undefined;\n const actionSpec = resolveActionSpec(spec, action);\n const verb = normalizeVerb(actionSpec?.label ?? action);\n\n let detail: string | undefined;\n if (key === \"read\") detail = resolveReadDetail(params.args);\n if (!detail && (key === \"write\" || key === \"edit\" || key === \"attach\")) {\n detail = resolveWriteDetail(params.args);\n }\n\n const detailKeys =\n actionSpec?.detailKeys ?? spec?.detailKeys ?? FALLBACK.detailKeys ?? [];\n if (!detail && detailKeys.length > 0) {\n detail = resolveDetailFromKeys(params.args, detailKeys);\n }\n\n if (!detail && params.meta) {\n detail = params.meta;\n }\n\n if (detail) {\n detail = shortenHomeInString(detail);\n }\n\n return {\n name,\n icon,\n title,\n label,\n verb,\n detail,\n };\n}\n\nexport function formatToolDetail(display: ToolDisplay): string | undefined {\n const parts: string[] = [];\n if (display.verb) parts.push(display.verb);\n if (display.detail) parts.push(display.detail);\n if (parts.length === 0) return undefined;\n return parts.join(\" · \");\n}\n\nexport function formatToolSummary(display: ToolDisplay): string {\n const detail = formatToolDetail(display);\n return detail ? `${display.label}: ${detail}` : display.label;\n}\n\nfunction shortenHomeInString(input: string): string {\n if (!input) return input;\n return input\n .replace(/\\/Users\\/[^/]+/g, \"~\")\n .replace(/\\/home\\/[^/]+/g, \"~\");\n}\n","/**\n * Chat-related constants for the UI layer.\n */\n\n/** Character threshold for showing tool output inline vs collapsed */\nexport const TOOL_INLINE_THRESHOLD = 80;\n\n/** Maximum lines to show in collapsed preview */\nexport const PREVIEW_MAX_LINES = 2;\n\n/** Maximum characters to show in collapsed preview */\nexport const PREVIEW_MAX_CHARS = 100;\n","/**\n * Helper functions for tool card rendering.\n */\n\nimport { PREVIEW_MAX_CHARS, PREVIEW_MAX_LINES } from \"./constants\";\n\n/**\n * Format tool output content for display in the sidebar.\n * Detects JSON and wraps it in a code block with formatting.\n */\nexport function formatToolOutputForSidebar(text: string): string {\n const trimmed = text.trim();\n // Try to detect and format JSON\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n const parsed = JSON.parse(trimmed);\n return \"```json\\n\" + JSON.stringify(parsed, null, 2) + \"\\n```\";\n } catch {\n // Not valid JSON, return as-is\n }\n }\n return text;\n}\n\n/**\n * Get a truncated preview of tool output text.\n * Truncates to first N lines or first N characters, whichever is shorter.\n */\nexport function getTruncatedPreview(text: string): string {\n const allLines = text.split(\"\\n\");\n const lines = allLines.slice(0, PREVIEW_MAX_LINES);\n const preview = lines.join(\"\\n\");\n if (preview.length > PREVIEW_MAX_CHARS) {\n return preview.slice(0, PREVIEW_MAX_CHARS) + \"…\";\n }\n return lines.length < allLines.length ? preview + \"…\" : preview;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatToolDetail, resolveToolDisplay } from \"../tool-display\";\nimport { icons } from \"../icons\";\nimport type { ToolCard } from \"../types/chat-types\";\nimport { TOOL_INLINE_THRESHOLD } from \"./constants\";\nimport {\n formatToolOutputForSidebar,\n getTruncatedPreview,\n} from \"./tool-helpers\";\nimport { isToolResultMessage } from \"./message-normalizer\";\nimport { extractTextCached } from \"./message-extract\";\n\nexport function extractToolCards(message: unknown): ToolCard[] {\n const m = message as Record;\n const content = normalizeContent(m.content);\n const cards: ToolCard[] = [];\n\n for (const item of content) {\n const kind = String(item.type ?? \"\").toLowerCase();\n const isToolCall =\n [\"toolcall\", \"tool_call\", \"tooluse\", \"tool_use\"].includes(kind) ||\n (typeof item.name === \"string\" && item.arguments != null);\n if (isToolCall) {\n cards.push({\n kind: \"call\",\n name: (item.name as string) ?? \"tool\",\n args: coerceArgs(item.arguments ?? item.args),\n });\n }\n }\n\n for (const item of content) {\n const kind = String(item.type ?? \"\").toLowerCase();\n if (kind !== \"toolresult\" && kind !== \"tool_result\") continue;\n const text = extractToolText(item);\n const name = typeof item.name === \"string\" ? item.name : \"tool\";\n cards.push({ kind: \"result\", name, text });\n }\n\n if (\n isToolResultMessage(message) &&\n !cards.some((card) => card.kind === \"result\")\n ) {\n const name =\n (typeof m.toolName === \"string\" && m.toolName) ||\n (typeof m.tool_name === \"string\" && m.tool_name) ||\n \"tool\";\n const text = extractTextCached(message) ?? undefined;\n cards.push({ kind: \"result\", name, text });\n }\n\n return cards;\n}\n\nexport function renderToolCardSidebar(\n card: ToolCard,\n onOpenSidebar?: (content: string) => void,\n) {\n const display = resolveToolDisplay({ name: card.name, args: card.args });\n const detail = formatToolDetail(display);\n const hasText = Boolean(card.text?.trim());\n\n const canClick = Boolean(onOpenSidebar);\n const handleClick = canClick\n ? () => {\n if (hasText) {\n onOpenSidebar!(formatToolOutputForSidebar(card.text!));\n return;\n }\n const info = `## ${display.label}\\n\\n${\n detail ? `**Command:** \\`${detail}\\`\\n\\n` : \"\"\n }*No output — tool completed successfully.*`;\n onOpenSidebar!(info);\n }\n : undefined;\n\n const isShort = hasText && (card.text?.length ?? 0) <= TOOL_INLINE_THRESHOLD;\n const showCollapsed = hasText && !isShort;\n const showInline = hasText && isShort;\n const isEmpty = !hasText;\n\n return html`\n {\n if (e.key !== \"Enter\" && e.key !== \" \") return;\n e.preventDefault();\n handleClick?.();\n }\n : nothing}\n >\n
    \n
    \n ${icons[display.icon]}\n ${display.label}\n
    \n ${canClick\n ? html`${hasText ? \"View\" : \"\"} ${icons.check}`\n : nothing}\n ${isEmpty && !canClick ? html`${icons.check}` : nothing}\n
    \n ${detail\n ? html`
    ${detail}
    `\n : nothing}\n ${isEmpty\n ? html`
    Completed
    `\n : nothing}\n ${showCollapsed\n ? html`
    ${getTruncatedPreview(card.text!)}
    `\n : nothing}\n ${showInline\n ? html`
    ${card.text}
    `\n : nothing}\n \n `;\n}\n\nfunction normalizeContent(content: unknown): Array> {\n if (!Array.isArray(content)) return [];\n return content.filter(Boolean) as Array>;\n}\n\nfunction coerceArgs(value: unknown): unknown {\n if (typeof value !== \"string\") return value;\n const trimmed = value.trim();\n if (!trimmed) return value;\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) return value;\n try {\n return JSON.parse(trimmed);\n } catch {\n return value;\n }\n}\n\nfunction extractToolText(item: Record): string | undefined {\n if (typeof item.text === \"string\") return item.text;\n if (typeof item.content === \"string\") return item.content;\n return undefined;\n}\n","import { html, nothing } from \"lit\";\nimport { unsafeHTML } from \"lit/directives/unsafe-html.js\";\n\nimport type { AssistantIdentity } from \"../assistant-identity\";\nimport { toSanitizedMarkdownHtml } from \"../markdown\";\nimport type { MessageGroup } from \"../types/chat-types\";\nimport { renderCopyAsMarkdownButton } from \"./copy-as-markdown\";\nimport { isToolResultMessage, normalizeRoleForGrouping } from \"./message-normalizer\";\nimport {\n extractTextCached,\n extractThinkingCached,\n formatReasoningMarkdown,\n} from \"./message-extract\";\nimport { extractToolCards, renderToolCardSidebar } from \"./tool-cards\";\n\nexport function renderReadingIndicatorGroup(assistant?: AssistantIdentity) {\n return html`\n
    \n ${renderAvatar(\"assistant\", assistant)}\n
    \n
    \n \n \n \n
    \n
    \n
    \n `;\n}\n\nexport function renderStreamingGroup(\n text: string,\n startedAt: number,\n onOpenSidebar?: (content: string) => void,\n assistant?: AssistantIdentity,\n) {\n const timestamp = new Date(startedAt).toLocaleTimeString([], {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const name = assistant?.name ?? \"Assistant\";\n\n return html`\n
    \n ${renderAvatar(\"assistant\", assistant)}\n
    \n ${renderGroupedMessage(\n {\n role: \"assistant\",\n content: [{ type: \"text\", text }],\n timestamp: startedAt,\n },\n { isStreaming: true, showReasoning: false },\n onOpenSidebar,\n )}\n
    \n ${name}\n ${timestamp}\n
    \n
    \n
    \n `;\n}\n\nexport function renderMessageGroup(\n group: MessageGroup,\n opts: {\n onOpenSidebar?: (content: string) => void;\n showReasoning: boolean;\n assistantName?: string;\n assistantAvatar?: string | null;\n },\n) {\n const normalizedRole = normalizeRoleForGrouping(group.role);\n const assistantName = opts.assistantName ?? \"Assistant\";\n const who =\n normalizedRole === \"user\"\n ? \"You\"\n : normalizedRole === \"assistant\"\n ? assistantName\n : normalizedRole;\n const roleClass =\n normalizedRole === \"user\"\n ? \"user\"\n : normalizedRole === \"assistant\"\n ? \"assistant\"\n : \"other\";\n const timestamp = new Date(group.timestamp).toLocaleTimeString([], {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return html`\n
    \n ${renderAvatar(group.role, {\n name: assistantName,\n avatar: opts.assistantAvatar ?? null,\n })}\n
    \n ${group.messages.map((item, index) =>\n renderGroupedMessage(\n item.message,\n {\n isStreaming:\n group.isStreaming && index === group.messages.length - 1,\n showReasoning: opts.showReasoning,\n },\n opts.onOpenSidebar,\n ),\n )}\n
    \n ${who}\n ${timestamp}\n
    \n
    \n
    \n `;\n}\n\nfunction renderAvatar(\n role: string,\n assistant?: Pick,\n) {\n const normalized = normalizeRoleForGrouping(role);\n const assistantName = assistant?.name?.trim() || \"Assistant\";\n const assistantAvatar = assistant?.avatar?.trim() || \"\";\n const initial =\n normalized === \"user\"\n ? \"U\"\n : normalized === \"assistant\"\n ? assistantName.charAt(0).toUpperCase() || \"A\"\n : normalized === \"tool\"\n ? \"⚙\"\n : \"?\";\n const className =\n normalized === \"user\"\n ? \"user\"\n : normalized === \"assistant\"\n ? \"assistant\"\n : normalized === \"tool\"\n ? \"tool\"\n : \"other\";\n\n if (assistantAvatar && normalized === \"assistant\") {\n if (isAvatarUrl(assistantAvatar)) {\n return html``;\n }\n return html`
    ${assistantAvatar}
    `;\n }\n\n return html`
    ${initial}
    `;\n}\n\nfunction isAvatarUrl(value: string): boolean {\n return (\n /^https?:\\/\\//i.test(value) ||\n /^data:image\\//i.test(value) ||\n /^\\//.test(value) // Relative paths from avatar endpoint\n );\n}\n\nfunction renderGroupedMessage(\n message: unknown,\n opts: { isStreaming: boolean; showReasoning: boolean },\n onOpenSidebar?: (content: string) => void,\n) {\n const m = message as Record;\n const role = typeof m.role === \"string\" ? m.role : \"unknown\";\n const isToolResult =\n isToolResultMessage(message) ||\n role.toLowerCase() === \"toolresult\" ||\n role.toLowerCase() === \"tool_result\" ||\n typeof m.toolCallId === \"string\" ||\n typeof m.tool_call_id === \"string\";\n\n const toolCards = extractToolCards(message);\n const hasToolCards = toolCards.length > 0;\n\n const extractedText = extractTextCached(message);\n const extractedThinking =\n opts.showReasoning && role === \"assistant\"\n ? extractThinkingCached(message)\n : null;\n const markdownBase = extractedText?.trim() ? extractedText : null;\n const reasoningMarkdown = extractedThinking\n ? formatReasoningMarkdown(extractedThinking)\n : null;\n const markdown = markdownBase;\n const canCopyMarkdown = role === \"assistant\" && Boolean(markdown?.trim());\n\n const bubbleClasses = [\n \"chat-bubble\",\n canCopyMarkdown ? \"has-copy\" : \"\",\n opts.isStreaming ? \"streaming\" : \"\",\n \"fade-in\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n if (!markdown && hasToolCards && isToolResult) {\n return html`${toolCards.map((card) =>\n renderToolCardSidebar(card, onOpenSidebar),\n )}`;\n }\n\n if (!markdown && !hasToolCards) return nothing;\n\n return html`\n
    \n ${canCopyMarkdown ? renderCopyAsMarkdownButton(markdown!) : nothing}\n ${reasoningMarkdown\n ? html`
    ${unsafeHTML(\n toSanitizedMarkdownHtml(reasoningMarkdown),\n )}
    `\n : nothing}\n ${markdown\n ? html`
    ${unsafeHTML(toSanitizedMarkdownHtml(markdown))}
    `\n : nothing}\n ${toolCards.map((card) => renderToolCardSidebar(card, onOpenSidebar))}\n
    \n `;\n}\n","import { html, nothing } from \"lit\";\nimport { unsafeHTML } from \"lit/directives/unsafe-html.js\";\n\nimport { icons } from \"../icons\";\nimport { toSanitizedMarkdownHtml } from \"../markdown\";\n\nexport type MarkdownSidebarProps = {\n content: string | null;\n error: string | null;\n onClose: () => void;\n onViewRawText: () => void;\n};\n\nexport function renderMarkdownSidebar(props: MarkdownSidebarProps) {\n return html`\n
    \n
    \n
    Tool Output
    \n \n
    \n
    \n ${props.error\n ? html`\n
    ${props.error}
    \n \n `\n : props.content\n ? html`
    ${unsafeHTML(toSanitizedMarkdownHtml(props.content))}
    `\n : html`
    No content available
    `}\n
    \n
    \n `;\n}\n","import { LitElement, html, css } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\n\n/**\n * A draggable divider for resizable split views.\n * Dispatches 'resize' events with { splitRatio: number } detail.\n */\n@customElement(\"resizable-divider\")\nexport class ResizableDivider extends LitElement {\n @property({ type: Number }) splitRatio = 0.6;\n @property({ type: Number }) minRatio = 0.4;\n @property({ type: Number }) maxRatio = 0.7;\n\n private isDragging = false;\n private startX = 0;\n private startRatio = 0;\n\n static styles = css`\n :host {\n width: 4px;\n cursor: col-resize;\n background: var(--border, #333);\n transition: background 150ms ease-out;\n flex-shrink: 0;\n position: relative;\n }\n\n :host::before {\n content: \"\";\n position: absolute;\n top: 0;\n left: -4px;\n right: -4px;\n bottom: 0;\n }\n\n :host(:hover) {\n background: var(--accent, #007bff);\n }\n\n :host(.dragging) {\n background: var(--accent, #007bff);\n }\n `;\n\n render() {\n return html``;\n }\n\n connectedCallback() {\n super.connectedCallback();\n this.addEventListener(\"mousedown\", this.handleMouseDown);\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener(\"mousedown\", this.handleMouseDown);\n document.removeEventListener(\"mousemove\", this.handleMouseMove);\n document.removeEventListener(\"mouseup\", this.handleMouseUp);\n }\n\n private handleMouseDown = (e: MouseEvent) => {\n this.isDragging = true;\n this.startX = e.clientX;\n this.startRatio = this.splitRatio;\n this.classList.add(\"dragging\");\n\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n\n e.preventDefault();\n };\n\n private handleMouseMove = (e: MouseEvent) => {\n if (!this.isDragging) return;\n\n const container = this.parentElement;\n if (!container) return;\n\n const containerWidth = container.getBoundingClientRect().width;\n const deltaX = e.clientX - this.startX;\n const deltaRatio = deltaX / containerWidth;\n\n let newRatio = this.startRatio + deltaRatio;\n newRatio = Math.max(this.minRatio, Math.min(this.maxRatio, newRatio));\n\n this.dispatchEvent(\n new CustomEvent(\"resize\", {\n detail: { splitRatio: newRatio },\n bubbles: true,\n composed: true,\n })\n );\n };\n\n private handleMouseUp = () => {\n this.isDragging = false;\n this.classList.remove(\"dragging\");\n\n document.removeEventListener(\"mousemove\", this.handleMouseMove);\n document.removeEventListener(\"mouseup\", this.handleMouseUp);\n };\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"resizable-divider\": ResizableDivider;\n }\n}\n","import { html, nothing } from \"lit\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport type { SessionsListResult } from \"../types\";\nimport type { ChatQueueItem } from \"../ui-types\";\nimport type { ChatItem, MessageGroup } from \"../types/chat-types\";\nimport { icons } from \"../icons\";\nimport {\n normalizeMessage,\n normalizeRoleForGrouping,\n} from \"../chat/message-normalizer\";\nimport {\n renderMessageGroup,\n renderReadingIndicatorGroup,\n renderStreamingGroup,\n} from \"../chat/grouped-render\";\nimport { renderMarkdownSidebar } from \"./markdown-sidebar\";\nimport \"../components/resizable-divider\";\n\nexport type CompactionIndicatorStatus = {\n active: boolean;\n startedAt: number | null;\n completedAt: number | null;\n};\n\nexport type ChatProps = {\n sessionKey: string;\n onSessionKeyChange: (next: string) => void;\n thinkingLevel: string | null;\n showThinking: boolean;\n loading: boolean;\n sending: boolean;\n canAbort?: boolean;\n compactionStatus?: CompactionIndicatorStatus | null;\n messages: unknown[];\n toolMessages: unknown[];\n stream: string | null;\n streamStartedAt: number | null;\n assistantAvatarUrl?: string | null;\n draft: string;\n queue: ChatQueueItem[];\n connected: boolean;\n canSend: boolean;\n disabledReason: string | null;\n error: string | null;\n sessions: SessionsListResult | null;\n // Focus mode\n focusMode: boolean;\n // Sidebar state\n sidebarOpen?: boolean;\n sidebarContent?: string | null;\n sidebarError?: string | null;\n splitRatio?: number;\n assistantName: string;\n assistantAvatar: string | null;\n // Event handlers\n onRefresh: () => void;\n onToggleFocusMode: () => void;\n onDraftChange: (next: string) => void;\n onSend: () => void;\n onAbort?: () => void;\n onQueueRemove: (id: string) => void;\n onNewSession: () => void;\n onOpenSidebar?: (content: string) => void;\n onCloseSidebar?: () => void;\n onSplitRatioChange?: (ratio: number) => void;\n onChatScroll?: (event: Event) => void;\n};\n\nconst COMPACTION_TOAST_DURATION_MS = 5000;\n\nfunction renderCompactionIndicator(status: CompactionIndicatorStatus | null | undefined) {\n if (!status) return nothing;\n \n // Show \"compacting...\" while active\n if (status.active) {\n return html`\n
    \n ${icons.loader} Compacting context...\n
    \n `;\n }\n\n // Show \"compaction complete\" briefly after completion\n if (status.completedAt) {\n const elapsed = Date.now() - status.completedAt;\n if (elapsed < COMPACTION_TOAST_DURATION_MS) {\n return html`\n
    \n ${icons.check} Context compacted\n
    \n `;\n }\n }\n \n return nothing;\n}\n\nexport function renderChat(props: ChatProps) {\n const canCompose = props.connected;\n const isBusy = props.sending || props.stream !== null;\n const canAbort = Boolean(props.canAbort && props.onAbort);\n const activeSession = props.sessions?.sessions?.find(\n (row) => row.key === props.sessionKey,\n );\n const reasoningLevel = activeSession?.reasoningLevel ?? \"off\";\n const showReasoning = props.showThinking && reasoningLevel !== \"off\";\n const assistantIdentity = {\n name: props.assistantName,\n avatar: props.assistantAvatar ?? props.assistantAvatarUrl ?? null,\n };\n\n const composePlaceholder = props.connected\n ? \"Message (↩ to send, Shift+↩ for line breaks)\"\n : \"Connect to the gateway to start chatting…\";\n\n const splitRatio = props.splitRatio ?? 0.6;\n const sidebarOpen = Boolean(props.sidebarOpen && props.onCloseSidebar);\n const thread = html`\n \n ${props.loading ? html`
    Loading chat…
    ` : nothing}\n ${repeat(buildChatItems(props), (item) => item.key, (item) => {\n if (item.kind === \"reading-indicator\") {\n return renderReadingIndicatorGroup(assistantIdentity);\n }\n\n if (item.kind === \"stream\") {\n return renderStreamingGroup(\n item.text,\n item.startedAt,\n props.onOpenSidebar,\n assistantIdentity,\n );\n }\n\n if (item.kind === \"group\") {\n return renderMessageGroup(item, {\n onOpenSidebar: props.onOpenSidebar,\n showReasoning,\n assistantName: props.assistantName,\n assistantAvatar: assistantIdentity.avatar,\n });\n }\n\n return nothing;\n })}\n \n `;\n\n return html`\n
    \n ${props.disabledReason\n ? html`
    ${props.disabledReason}
    `\n : nothing}\n\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n ${renderCompactionIndicator(props.compactionStatus)}\n\n ${props.focusMode\n ? html`\n \n ${icons.x}\n \n `\n : nothing}\n\n \n \n ${thread}\n \n\n ${sidebarOpen\n ? html`\n \n props.onSplitRatioChange?.(e.detail.splitRatio)}\n >\n
    \n ${renderMarkdownSidebar({\n content: props.sidebarContent ?? null,\n error: props.sidebarError ?? null,\n onClose: props.onCloseSidebar!,\n onViewRawText: () => {\n if (!props.sidebarContent || !props.onOpenSidebar) return;\n props.onOpenSidebar(`\\`\\`\\`\\n${props.sidebarContent}\\n\\`\\`\\``);\n },\n })}\n
    \n `\n : nothing}\n \n\n ${props.queue.length\n ? html`\n
    \n
    Queued (${props.queue.length})
    \n
    \n ${props.queue.map(\n (item) => html`\n
    \n
    ${item.text}
    \n props.onQueueRemove(item.id)}\n >\n ${icons.x}\n \n
    \n `,\n )}\n
    \n
    \n `\n : nothing}\n\n
    \n \n
    \n \n ${canAbort ? \"Stop\" : \"New session\"}\n \n \n ${isBusy ? \"Queue\" : \"Send\"}\n \n
    \n
    \n
    \n `;\n}\n\nconst CHAT_HISTORY_RENDER_LIMIT = 200;\n\nfunction groupMessages(items: ChatItem[]): Array {\n const result: Array = [];\n let currentGroup: MessageGroup | null = null;\n\n for (const item of items) {\n if (item.kind !== \"message\") {\n if (currentGroup) {\n result.push(currentGroup);\n currentGroup = null;\n }\n result.push(item);\n continue;\n }\n\n const normalized = normalizeMessage(item.message);\n const role = normalizeRoleForGrouping(normalized.role);\n const timestamp = normalized.timestamp || Date.now();\n\n if (!currentGroup || currentGroup.role !== role) {\n if (currentGroup) result.push(currentGroup);\n currentGroup = {\n kind: \"group\",\n key: `group:${role}:${item.key}`,\n role,\n messages: [{ message: item.message, key: item.key }],\n timestamp,\n isStreaming: false,\n };\n } else {\n currentGroup.messages.push({ message: item.message, key: item.key });\n }\n }\n\n if (currentGroup) result.push(currentGroup);\n return result;\n}\n\nfunction buildChatItems(props: ChatProps): Array {\n const items: ChatItem[] = [];\n const history = Array.isArray(props.messages) ? props.messages : [];\n const tools = Array.isArray(props.toolMessages) ? props.toolMessages : [];\n const historyStart = Math.max(0, history.length - CHAT_HISTORY_RENDER_LIMIT);\n if (historyStart > 0) {\n items.push({\n kind: \"message\",\n key: \"chat:history:notice\",\n message: {\n role: \"system\",\n content: `Showing last ${CHAT_HISTORY_RENDER_LIMIT} messages (${historyStart} hidden).`,\n timestamp: Date.now(),\n },\n });\n }\n for (let i = historyStart; i < history.length; i++) {\n const msg = history[i];\n const normalized = normalizeMessage(msg);\n\n if (!props.showThinking && normalized.role.toLowerCase() === \"toolresult\") {\n continue;\n }\n\n items.push({\n kind: \"message\",\n key: messageKey(msg, i),\n message: msg,\n });\n }\n if (props.showThinking) {\n for (let i = 0; i < tools.length; i++) {\n items.push({\n kind: \"message\",\n key: messageKey(tools[i], i + history.length),\n message: tools[i],\n });\n }\n }\n\n if (props.stream !== null) {\n const key = `stream:${props.sessionKey}:${props.streamStartedAt ?? \"live\"}`;\n if (props.stream.trim().length > 0) {\n items.push({\n kind: \"stream\",\n key,\n text: props.stream,\n startedAt: props.streamStartedAt ?? Date.now(),\n });\n } else {\n items.push({ kind: \"reading-indicator\", key });\n }\n }\n\n return groupMessages(items);\n}\n\nfunction messageKey(message: unknown, index: number): string {\n const m = message as Record;\n const toolCallId = typeof m.toolCallId === \"string\" ? m.toolCallId : \"\";\n if (toolCallId) return `tool:${toolCallId}`;\n const id = typeof m.id === \"string\" ? m.id : \"\";\n if (id) return `msg:${id}`;\n const messageId = typeof m.messageId === \"string\" ? m.messageId : \"\";\n if (messageId) return `msg:${messageId}`;\n const timestamp = typeof m.timestamp === \"number\" ? m.timestamp : null;\n const role = typeof m.role === \"string\" ? m.role : \"unknown\";\n if (timestamp != null) return `msg:${role}:${timestamp}:${index}`;\n return `msg:${role}:${index}`;\n}\n","import type { ConfigUiHints } from \"../types\";\n\nexport type JsonSchema = {\n type?: string | string[];\n title?: string;\n description?: string;\n properties?: Record;\n items?: JsonSchema | JsonSchema[];\n additionalProperties?: JsonSchema | boolean;\n enum?: unknown[];\n const?: unknown;\n default?: unknown;\n anyOf?: JsonSchema[];\n oneOf?: JsonSchema[];\n allOf?: JsonSchema[];\n nullable?: boolean;\n};\n\nexport function schemaType(schema: JsonSchema): string | undefined {\n if (!schema) return undefined;\n if (Array.isArray(schema.type)) {\n const filtered = schema.type.filter((t) => t !== \"null\");\n return filtered[0] ?? schema.type[0];\n }\n return schema.type;\n}\n\nexport function defaultValue(schema?: JsonSchema): unknown {\n if (!schema) return \"\";\n if (schema.default !== undefined) return schema.default;\n const type = schemaType(schema);\n switch (type) {\n case \"object\":\n return {};\n case \"array\":\n return [];\n case \"boolean\":\n return false;\n case \"number\":\n case \"integer\":\n return 0;\n case \"string\":\n return \"\";\n default:\n return \"\";\n }\n}\n\nexport function pathKey(path: Array): string {\n return path.filter((segment) => typeof segment === \"string\").join(\".\");\n}\n\nexport function hintForPath(path: Array, hints: ConfigUiHints) {\n const key = pathKey(path);\n const direct = hints[key];\n if (direct) return direct;\n const segments = key.split(\".\");\n for (const [hintKey, hint] of Object.entries(hints)) {\n if (!hintKey.includes(\"*\")) continue;\n const hintSegments = hintKey.split(\".\");\n if (hintSegments.length !== segments.length) continue;\n let match = true;\n for (let i = 0; i < segments.length; i += 1) {\n if (hintSegments[i] !== \"*\" && hintSegments[i] !== segments[i]) {\n match = false;\n break;\n }\n }\n if (match) return hint;\n }\n return undefined;\n}\n\nexport function humanize(raw: string) {\n return raw\n .replace(/_/g, \" \")\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .replace(/\\s+/g, \" \")\n .replace(/^./, (m) => m.toUpperCase());\n}\n\nexport function isSensitivePath(path: Array): boolean {\n const key = pathKey(path).toLowerCase();\n return (\n key.includes(\"token\") ||\n key.includes(\"password\") ||\n key.includes(\"secret\") ||\n key.includes(\"apikey\") ||\n key.endsWith(\"key\")\n );\n}\n\n","import { html, nothing, type TemplateResult } from \"lit\";\nimport type { ConfigUiHints } from \"../types\";\nimport {\n defaultValue,\n hintForPath,\n humanize,\n isSensitivePath,\n pathKey,\n schemaType,\n type JsonSchema,\n} from \"./config-form.shared\";\n\nconst META_KEYS = new Set([\"title\", \"description\", \"default\", \"nullable\"]);\n\nfunction isAnySchema(schema: JsonSchema): boolean {\n const keys = Object.keys(schema ?? {}).filter((key) => !META_KEYS.has(key));\n return keys.length === 0;\n}\n\nfunction jsonValue(value: unknown): string {\n if (value === undefined) return \"\";\n try {\n return JSON.stringify(value, null, 2) ?? \"\";\n } catch {\n return \"\";\n }\n}\n\n// SVG Icons as template literals\nconst icons = {\n chevronDown: html``,\n plus: html``,\n minus: html``,\n trash: html``,\n edit: html``,\n};\n\nexport function renderNode(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult | typeof nothing {\n const { schema, value, path, hints, unsupported, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const type = schemaType(schema);\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const key = pathKey(path);\n\n if (unsupported.has(key)) {\n return html`
    \n
    ${label}
    \n
    Unsupported schema node. Use Raw mode.
    \n
    `;\n }\n\n // Handle anyOf/oneOf unions\n if (schema.anyOf || schema.oneOf) {\n const variants = schema.anyOf ?? schema.oneOf ?? [];\n const nonNull = variants.filter(\n (v) => !(v.type === \"null\" || (Array.isArray(v.type) && v.type.includes(\"null\")))\n );\n\n if (nonNull.length === 1) {\n return renderNode({ ...params, schema: nonNull[0] });\n }\n\n // Check if it's a set of literal values (enum-like)\n const extractLiteral = (v: JsonSchema): unknown | undefined => {\n if (v.const !== undefined) return v.const;\n if (v.enum && v.enum.length === 1) return v.enum[0];\n return undefined;\n };\n const literals = nonNull.map(extractLiteral);\n const allLiterals = literals.every((v) => v !== undefined);\n\n if (allLiterals && literals.length > 0 && literals.length <= 5) {\n // Use segmented control for small sets\n const resolvedValue = value ?? schema.default;\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n ${literals.map((lit, idx) => html`\n onPatch(path, lit)}\n >\n ${String(lit)}\n \n `)}\n
    \n
    \n `;\n }\n\n if (allLiterals && literals.length > 5) {\n // Use dropdown for larger sets\n return renderSelect({ ...params, options: literals, value: value ?? schema.default });\n }\n\n // Handle mixed primitive types\n const primitiveTypes = new Set(\n nonNull.map((variant) => schemaType(variant)).filter(Boolean)\n );\n const normalizedTypes = new Set(\n [...primitiveTypes].map((v) => (v === \"integer\" ? \"number\" : v))\n );\n\n if ([...normalizedTypes].every((v) => [\"string\", \"number\", \"boolean\"].includes(v as string))) {\n const hasString = normalizedTypes.has(\"string\");\n const hasNumber = normalizedTypes.has(\"number\");\n const hasBoolean = normalizedTypes.has(\"boolean\");\n \n if (hasBoolean && normalizedTypes.size === 1) {\n return renderNode({\n ...params,\n schema: { ...schema, type: \"boolean\", anyOf: undefined, oneOf: undefined },\n });\n }\n\n if (hasString || hasNumber) {\n return renderTextInput({\n ...params,\n inputType: hasNumber && !hasString ? \"number\" : \"text\",\n });\n }\n }\n }\n\n // Enum - use segmented for small, dropdown for large\n if (schema.enum) {\n const options = schema.enum;\n if (options.length <= 5) {\n const resolvedValue = value ?? schema.default;\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n ${options.map((opt) => html`\n onPatch(path, opt)}\n >\n ${String(opt)}\n \n `)}\n
    \n
    \n `;\n }\n return renderSelect({ ...params, options, value: value ?? schema.default });\n }\n\n // Object type - collapsible section\n if (type === \"object\") {\n return renderObject(params);\n }\n\n // Array type\n if (type === \"array\") {\n return renderArray(params);\n }\n\n // Boolean - toggle row\n if (type === \"boolean\") {\n const displayValue = typeof value === \"boolean\" ? value : typeof schema.default === \"boolean\" ? schema.default : false;\n return html`\n \n `;\n }\n\n // Number/Integer\n if (type === \"number\" || type === \"integer\") {\n return renderNumberInput(params);\n }\n\n // String\n if (type === \"string\") {\n return renderTextInput({ ...params, inputType: \"text\" });\n }\n\n // Fallback\n return html`\n
    \n
    ${label}
    \n
    Unsupported type: ${type}. Use Raw mode.
    \n
    \n `;\n}\n\nfunction renderTextInput(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n disabled: boolean;\n showLabel?: boolean;\n inputType: \"text\" | \"number\";\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, disabled, onPatch, inputType } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const isSensitive = hint?.sensitive ?? isSensitivePath(path);\n const placeholder =\n hint?.placeholder ??\n (isSensitive ? \"••••\" : schema.default !== undefined ? `Default: ${schema.default}` : \"\");\n const displayValue = value ?? \"\";\n\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n {\n const raw = (e.target as HTMLInputElement).value;\n if (inputType === \"number\") {\n if (raw.trim() === \"\") {\n onPatch(path, undefined);\n return;\n }\n const parsed = Number(raw);\n onPatch(path, Number.isNaN(parsed) ? raw : parsed);\n return;\n }\n onPatch(path, raw);\n }}\n />\n ${schema.default !== undefined ? html`\n onPatch(path, schema.default)}\n >↺\n ` : nothing}\n
    \n
    \n `;\n}\n\nfunction renderNumberInput(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const displayValue = value ?? schema.default ?? \"\";\n const numValue = typeof displayValue === \"number\" ? displayValue : 0;\n\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n onPatch(path, numValue - 1)}\n >−\n {\n const raw = (e.target as HTMLInputElement).value;\n const parsed = raw === \"\" ? undefined : Number(raw);\n onPatch(path, parsed);\n }}\n />\n onPatch(path, numValue + 1)}\n >+\n
    \n
    \n `;\n}\n\nfunction renderSelect(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n disabled: boolean;\n showLabel?: boolean;\n options: unknown[];\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, disabled, options, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const resolvedValue = value ?? schema.default;\n const currentIndex = options.findIndex(\n (opt) => opt === resolvedValue || String(opt) === String(resolvedValue),\n );\n const unset = \"__unset__\";\n\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n = 0 ? String(currentIndex) : unset}\n @change=${(e: Event) => {\n const val = (e.target as HTMLSelectElement).value;\n onPatch(path, val === unset ? undefined : options[Number(val)]);\n }}\n >\n \n ${options.map((opt, idx) => html`\n \n `)}\n \n
    \n `;\n}\n\nfunction renderObject(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, unsupported, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n \n const fallback = value ?? schema.default;\n const obj = fallback && typeof fallback === \"object\" && !Array.isArray(fallback)\n ? (fallback as Record)\n : {};\n const props = schema.properties ?? {};\n const entries = Object.entries(props);\n \n // Sort by hint order\n const sorted = entries.sort((a, b) => {\n const orderA = hintForPath([...path, a[0]], hints)?.order ?? 0;\n const orderB = hintForPath([...path, b[0]], hints)?.order ?? 0;\n if (orderA !== orderB) return orderA - orderB;\n return a[0].localeCompare(b[0]);\n });\n\n const reserved = new Set(Object.keys(props));\n const additional = schema.additionalProperties;\n const allowExtra = Boolean(additional) && typeof additional === \"object\";\n\n // For top-level, don't wrap in collapsible\n if (path.length === 1) {\n return html`\n
    \n ${sorted.map(([propKey, node]) =>\n renderNode({\n schema: node,\n value: obj[propKey],\n path: [...path, propKey],\n hints,\n unsupported,\n disabled,\n onPatch,\n })\n )}\n ${allowExtra ? renderMapField({\n schema: additional as JsonSchema,\n value: obj,\n path,\n hints,\n unsupported,\n disabled,\n reservedKeys: reserved,\n onPatch,\n }) : nothing}\n
    \n `;\n }\n\n // Nested objects get collapsible treatment\n return html`\n
    \n \n ${label}\n ${icons.chevronDown}\n \n ${help ? html`
    ${help}
    ` : nothing}\n
    \n ${sorted.map(([propKey, node]) =>\n renderNode({\n schema: node,\n value: obj[propKey],\n path: [...path, propKey],\n hints,\n unsupported,\n disabled,\n onPatch,\n })\n )}\n ${allowExtra ? renderMapField({\n schema: additional as JsonSchema,\n value: obj,\n path,\n hints,\n unsupported,\n disabled,\n reservedKeys: reserved,\n onPatch,\n }) : nothing}\n
    \n
    \n `;\n}\n\nfunction renderArray(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, unsupported, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n\n const itemsSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;\n if (!itemsSchema) {\n return html`\n
    \n
    ${label}
    \n
    Unsupported array schema. Use Raw mode.
    \n
    \n `;\n }\n\n const arr = Array.isArray(value) ? value : Array.isArray(schema.default) ? schema.default : [];\n\n return html`\n
    \n
    \n ${showLabel ? html`${label}` : nothing}\n ${arr.length} item${arr.length !== 1 ? 's' : ''}\n {\n const next = [...arr, defaultValue(itemsSchema)];\n onPatch(path, next);\n }}\n >\n ${icons.plus}\n Add\n \n
    \n ${help ? html`
    ${help}
    ` : nothing}\n \n ${arr.length === 0 ? html`\n
    \n No items yet. Click \"Add\" to create one.\n
    \n ` : html`\n
    \n ${arr.map((item, idx) => html`\n
    \n
    \n #${idx + 1}\n {\n const next = [...arr];\n next.splice(idx, 1);\n onPatch(path, next);\n }}\n >\n ${icons.trash}\n \n
    \n
    \n ${renderNode({\n schema: itemsSchema,\n value: item,\n path: [...path, idx],\n hints,\n unsupported,\n disabled,\n showLabel: false,\n onPatch,\n })}\n
    \n
    \n `)}\n
    \n `}\n
    \n `;\n}\n\nfunction renderMapField(params: {\n schema: JsonSchema;\n value: Record;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n reservedKeys: Set;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, unsupported, disabled, reservedKeys, onPatch } = params;\n const anySchema = isAnySchema(schema);\n const entries = Object.entries(value ?? {}).filter(([key]) => !reservedKeys.has(key));\n\n return html`\n
    \n
    \n Custom entries\n {\n const next = { ...(value ?? {}) };\n let index = 1;\n let key = `custom-${index}`;\n while (key in next) {\n index += 1;\n key = `custom-${index}`;\n }\n next[key] = anySchema ? {} : defaultValue(schema);\n onPatch(path, next);\n }}\n >\n ${icons.plus}\n Add Entry\n \n
    \n \n ${entries.length === 0 ? html`\n
    No custom entries.
    \n ` : html`\n
    \n ${entries.map(([key, entryValue]) => {\n const valuePath = [...path, key];\n const fallback = jsonValue(entryValue);\n return html`\n
    \n
    \n {\n const nextKey = (e.target as HTMLInputElement).value.trim();\n if (!nextKey || nextKey === key) return;\n const next = { ...(value ?? {}) };\n if (nextKey in next) return;\n next[nextKey] = next[key];\n delete next[key];\n onPatch(path, next);\n }}\n />\n
    \n
    \n ${anySchema\n ? html`\n {\n const target = e.target as HTMLTextAreaElement;\n const raw = target.value.trim();\n if (!raw) {\n onPatch(valuePath, undefined);\n return;\n }\n try {\n onPatch(valuePath, JSON.parse(raw));\n } catch {\n target.value = fallback;\n }\n }}\n >\n `\n : renderNode({\n schema,\n value: entryValue,\n path: valuePath,\n hints,\n unsupported,\n disabled,\n showLabel: false,\n onPatch,\n })}\n
    \n {\n const next = { ...(value ?? {}) };\n delete next[key];\n onPatch(path, next);\n }}\n >\n ${icons.trash}\n \n
    \n `;\n })}\n
    \n `}\n
    \n `;\n}\n","import { html, nothing } from \"lit\";\nimport type { ConfigUiHints } from \"../types\";\nimport { icons } from \"../icons\";\nimport {\n hintForPath,\n humanize,\n schemaType,\n type JsonSchema,\n} from \"./config-form.shared\";\nimport { renderNode } from \"./config-form.node\";\n\nexport type ConfigFormProps = {\n schema: JsonSchema | null;\n uiHints: ConfigUiHints;\n value: Record | null;\n disabled?: boolean;\n unsupportedPaths?: string[];\n searchQuery?: string;\n activeSection?: string | null;\n activeSubsection?: string | null;\n onPatch: (path: Array, value: unknown) => void;\n};\n\n// SVG Icons for section cards (Lucide-style)\nconst sectionIcons = {\n env: html``,\n update: html``,\n agents: html``,\n auth: html``,\n channels: html``,\n messages: html``,\n commands: html``,\n hooks: html``,\n skills: html``,\n tools: html``,\n gateway: html``,\n wizard: html``,\n // Additional sections\n meta: html``,\n logging: html``,\n browser: html``,\n ui: html``,\n models: html``,\n bindings: html``,\n broadcast: html``,\n audio: html``,\n session: html``,\n cron: html``,\n web: html``,\n discovery: html``,\n canvasHost: html``,\n talk: html``,\n plugins: html``,\n default: html``,\n};\n\n// Section metadata\nexport const SECTION_META: Record = {\n env: { label: \"Environment Variables\", description: \"Environment variables passed to the gateway process\" },\n update: { label: \"Updates\", description: \"Auto-update settings and release channel\" },\n agents: { label: \"Agents\", description: \"Agent configurations, models, and identities\" },\n auth: { label: \"Authentication\", description: \"API keys and authentication profiles\" },\n channels: { label: \"Channels\", description: \"Messaging channels (Telegram, Discord, Slack, etc.)\" },\n messages: { label: \"Messages\", description: \"Message handling and routing settings\" },\n commands: { label: \"Commands\", description: \"Custom slash commands\" },\n hooks: { label: \"Hooks\", description: \"Webhooks and event hooks\" },\n skills: { label: \"Skills\", description: \"Skill packs and capabilities\" },\n tools: { label: \"Tools\", description: \"Tool configurations (browser, search, etc.)\" },\n gateway: { label: \"Gateway\", description: \"Gateway server settings (port, auth, binding)\" },\n wizard: { label: \"Setup Wizard\", description: \"Setup wizard state and history\" },\n // Additional sections\n meta: { label: \"Metadata\", description: \"Gateway metadata and version information\" },\n logging: { label: \"Logging\", description: \"Log levels and output configuration\" },\n browser: { label: \"Browser\", description: \"Browser automation settings\" },\n ui: { label: \"UI\", description: \"User interface preferences\" },\n models: { label: \"Models\", description: \"AI model configurations and providers\" },\n bindings: { label: \"Bindings\", description: \"Key bindings and shortcuts\" },\n broadcast: { label: \"Broadcast\", description: \"Broadcast and notification settings\" },\n audio: { label: \"Audio\", description: \"Audio input/output settings\" },\n session: { label: \"Session\", description: \"Session management and persistence\" },\n cron: { label: \"Cron\", description: \"Scheduled tasks and automation\" },\n web: { label: \"Web\", description: \"Web server and API settings\" },\n discovery: { label: \"Discovery\", description: \"Service discovery and networking\" },\n canvasHost: { label: \"Canvas Host\", description: \"Canvas rendering and display\" },\n talk: { label: \"Talk\", description: \"Voice and speech settings\" },\n plugins: { label: \"Plugins\", description: \"Plugin management and extensions\" },\n};\n\nfunction getSectionIcon(key: string) {\n return sectionIcons[key as keyof typeof sectionIcons] ?? sectionIcons.default;\n}\n\nfunction matchesSearch(key: string, schema: JsonSchema, query: string): boolean {\n if (!query) return true;\n const q = query.toLowerCase();\n const meta = SECTION_META[key];\n \n // Check key name\n if (key.toLowerCase().includes(q)) return true;\n \n // Check label and description\n if (meta) {\n if (meta.label.toLowerCase().includes(q)) return true;\n if (meta.description.toLowerCase().includes(q)) return true;\n }\n \n return schemaMatches(schema, q);\n}\n\nfunction schemaMatches(schema: JsonSchema, query: string): boolean {\n if (schema.title?.toLowerCase().includes(query)) return true;\n if (schema.description?.toLowerCase().includes(query)) return true;\n if (schema.enum?.some((value) => String(value).toLowerCase().includes(query))) return true;\n\n if (schema.properties) {\n for (const [propKey, propSchema] of Object.entries(schema.properties)) {\n if (propKey.toLowerCase().includes(query)) return true;\n if (schemaMatches(propSchema, query)) return true;\n }\n }\n\n if (schema.items) {\n const items = Array.isArray(schema.items) ? schema.items : [schema.items];\n for (const item of items) {\n if (item && schemaMatches(item, query)) return true;\n }\n }\n\n if (schema.additionalProperties && typeof schema.additionalProperties === \"object\") {\n if (schemaMatches(schema.additionalProperties, query)) return true;\n }\n\n const unions = schema.anyOf ?? schema.oneOf ?? schema.allOf;\n if (unions) {\n for (const entry of unions) {\n if (entry && schemaMatches(entry, query)) return true;\n }\n }\n\n return false;\n}\n\nexport function renderConfigForm(props: ConfigFormProps) {\n if (!props.schema) {\n return html`
    Schema unavailable.
    `;\n }\n const schema = props.schema;\n const value = props.value ?? {};\n if (schemaType(schema) !== \"object\" || !schema.properties) {\n return html`
    Unsupported schema. Use Raw.
    `;\n }\n const unsupported = new Set(props.unsupportedPaths ?? []);\n const properties = schema.properties;\n const searchQuery = props.searchQuery ?? \"\";\n const activeSection = props.activeSection;\n const activeSubsection = props.activeSubsection ?? null;\n\n const entries = Object.entries(properties).sort((a, b) => {\n const orderA = hintForPath([a[0]], props.uiHints)?.order ?? 50;\n const orderB = hintForPath([b[0]], props.uiHints)?.order ?? 50;\n if (orderA !== orderB) return orderA - orderB;\n return a[0].localeCompare(b[0]);\n });\n\n const filteredEntries = entries.filter(([key, node]) => {\n if (activeSection && key !== activeSection) return false;\n if (searchQuery && !matchesSearch(key, node, searchQuery)) return false;\n return true;\n });\n\n let subsectionContext:\n | { sectionKey: string; subsectionKey: string; schema: JsonSchema }\n | null = null;\n if (activeSection && activeSubsection && filteredEntries.length === 1) {\n const sectionSchema = filteredEntries[0]?.[1];\n if (\n sectionSchema &&\n schemaType(sectionSchema) === \"object\" &&\n sectionSchema.properties &&\n sectionSchema.properties[activeSubsection]\n ) {\n subsectionContext = {\n sectionKey: activeSection,\n subsectionKey: activeSubsection,\n schema: sectionSchema.properties[activeSubsection],\n };\n }\n }\n\n if (filteredEntries.length === 0) {\n return html`\n
    \n
    ${icons.search}
    \n
    \n ${searchQuery \n ? `No settings match \"${searchQuery}\"` \n : \"No settings in this section\"}\n
    \n
    \n `;\n }\n\n return html`\n
    \n ${subsectionContext\n ? (() => {\n const { sectionKey, subsectionKey, schema: node } = subsectionContext;\n const hint = hintForPath([sectionKey, subsectionKey], props.uiHints);\n const label = hint?.label ?? node.title ?? humanize(subsectionKey);\n const description = hint?.help ?? node.description ?? \"\";\n const sectionValue = (value as Record)[sectionKey];\n const scopedValue =\n sectionValue && typeof sectionValue === \"object\"\n ? (sectionValue as Record)[subsectionKey]\n : undefined;\n const id = `config-section-${sectionKey}-${subsectionKey}`;\n return html`\n
    \n
    \n ${getSectionIcon(sectionKey)}\n
    \n

    ${label}

    \n ${description\n ? html`

    ${description}

    `\n : nothing}\n
    \n
    \n
    \n ${renderNode({\n schema: node,\n value: scopedValue,\n path: [sectionKey, subsectionKey],\n hints: props.uiHints,\n unsupported,\n disabled: props.disabled ?? false,\n showLabel: false,\n onPatch: props.onPatch,\n })}\n
    \n
    \n `;\n })()\n : filteredEntries.map(([key, node]) => {\n const meta = SECTION_META[key] ?? {\n label: key.charAt(0).toUpperCase() + key.slice(1),\n description: node.description ?? \"\",\n };\n\n return html`\n
    \n
    \n ${getSectionIcon(key)}\n
    \n

    ${meta.label}

    \n ${meta.description\n ? html`

    ${meta.description}

    `\n : nothing}\n
    \n
    \n
    \n ${renderNode({\n schema: node,\n value: (value as Record)[key],\n path: [key],\n hints: props.uiHints,\n unsupported,\n disabled: props.disabled ?? false,\n showLabel: false,\n onPatch: props.onPatch,\n })}\n
    \n
    \n `;\n })}\n
    \n `;\n}\n","import { pathKey, schemaType, type JsonSchema } from \"./config-form.shared\";\n\nexport type ConfigSchemaAnalysis = {\n schema: JsonSchema | null;\n unsupportedPaths: string[];\n};\n\nconst META_KEYS = new Set([\"title\", \"description\", \"default\", \"nullable\"]);\n\nfunction isAnySchema(schema: JsonSchema): boolean {\n const keys = Object.keys(schema ?? {}).filter((key) => !META_KEYS.has(key));\n return keys.length === 0;\n}\n\nfunction normalizeEnum(values: unknown[]): { enumValues: unknown[]; nullable: boolean } {\n const filtered = values.filter((value) => value != null);\n const nullable = filtered.length !== values.length;\n const enumValues: unknown[] = [];\n for (const value of filtered) {\n if (!enumValues.some((existing) => Object.is(existing, value))) {\n enumValues.push(value);\n }\n }\n return { enumValues, nullable };\n}\n\nexport function analyzeConfigSchema(raw: unknown): ConfigSchemaAnalysis {\n if (!raw || typeof raw !== \"object\") {\n return { schema: null, unsupportedPaths: [\"\"] };\n }\n return normalizeSchemaNode(raw as JsonSchema, []);\n}\n\nfunction normalizeSchemaNode(\n schema: JsonSchema,\n path: Array,\n): ConfigSchemaAnalysis {\n const unsupported = new Set();\n const normalized: JsonSchema = { ...schema };\n const pathLabel = pathKey(path) || \"\";\n\n if (schema.anyOf || schema.oneOf || schema.allOf) {\n const union = normalizeUnion(schema, path);\n if (union) return union;\n return { schema, unsupportedPaths: [pathLabel] };\n }\n\n const nullable = Array.isArray(schema.type) && schema.type.includes(\"null\");\n const type =\n schemaType(schema) ??\n (schema.properties || schema.additionalProperties ? \"object\" : undefined);\n normalized.type = type ?? schema.type;\n normalized.nullable = nullable || schema.nullable;\n\n if (normalized.enum) {\n const { enumValues, nullable: enumNullable } = normalizeEnum(normalized.enum);\n normalized.enum = enumValues;\n if (enumNullable) normalized.nullable = true;\n if (enumValues.length === 0) unsupported.add(pathLabel);\n }\n\n if (type === \"object\") {\n const properties = schema.properties ?? {};\n const normalizedProps: Record = {};\n for (const [key, value] of Object.entries(properties)) {\n const res = normalizeSchemaNode(value, [...path, key]);\n if (res.schema) normalizedProps[key] = res.schema;\n for (const entry of res.unsupportedPaths) unsupported.add(entry);\n }\n normalized.properties = normalizedProps;\n\n if (schema.additionalProperties === true) {\n unsupported.add(pathLabel);\n } else if (schema.additionalProperties === false) {\n normalized.additionalProperties = false;\n } else if (\n schema.additionalProperties &&\n typeof schema.additionalProperties === \"object\"\n ) {\n if (!isAnySchema(schema.additionalProperties as JsonSchema)) {\n const res = normalizeSchemaNode(\n schema.additionalProperties as JsonSchema,\n [...path, \"*\"],\n );\n normalized.additionalProperties =\n res.schema ?? (schema.additionalProperties as JsonSchema);\n if (res.unsupportedPaths.length > 0) unsupported.add(pathLabel);\n }\n }\n } else if (type === \"array\") {\n const itemsSchema = Array.isArray(schema.items)\n ? schema.items[0]\n : schema.items;\n if (!itemsSchema) {\n unsupported.add(pathLabel);\n } else {\n const res = normalizeSchemaNode(itemsSchema, [...path, \"*\"]);\n normalized.items = res.schema ?? itemsSchema;\n if (res.unsupportedPaths.length > 0) unsupported.add(pathLabel);\n }\n } else if (\n type !== \"string\" &&\n type !== \"number\" &&\n type !== \"integer\" &&\n type !== \"boolean\" &&\n !normalized.enum\n ) {\n unsupported.add(pathLabel);\n }\n\n return {\n schema: normalized,\n unsupportedPaths: Array.from(unsupported),\n };\n}\n\nfunction normalizeUnion(\n schema: JsonSchema,\n path: Array,\n): ConfigSchemaAnalysis | null {\n if (schema.allOf) return null;\n const union = schema.anyOf ?? schema.oneOf;\n if (!union) return null;\n\n const literals: unknown[] = [];\n const remaining: JsonSchema[] = [];\n let nullable = false;\n\n for (const entry of union) {\n if (!entry || typeof entry !== \"object\") return null;\n if (Array.isArray(entry.enum)) {\n const { enumValues, nullable: enumNullable } = normalizeEnum(entry.enum);\n literals.push(...enumValues);\n if (enumNullable) nullable = true;\n continue;\n }\n if (\"const\" in entry) {\n if (entry.const == null) {\n nullable = true;\n continue;\n }\n literals.push(entry.const);\n continue;\n }\n if (schemaType(entry) === \"null\") {\n nullable = true;\n continue;\n }\n remaining.push(entry);\n }\n\n if (literals.length > 0 && remaining.length === 0) {\n const unique: unknown[] = [];\n for (const value of literals) {\n if (!unique.some((existing) => Object.is(existing, value))) {\n unique.push(value);\n }\n }\n return {\n schema: {\n ...schema,\n enum: unique,\n nullable,\n anyOf: undefined,\n oneOf: undefined,\n allOf: undefined,\n },\n unsupportedPaths: [],\n };\n }\n\n if (remaining.length === 1) {\n const res = normalizeSchemaNode(remaining[0], path);\n if (res.schema) {\n res.schema.nullable = nullable || res.schema.nullable;\n }\n return res;\n }\n\n const primitiveTypes = [\"string\", \"number\", \"integer\", \"boolean\"];\n if (\n remaining.length > 0 &&\n literals.length === 0 &&\n remaining.every((entry) => entry.type && primitiveTypes.includes(String(entry.type)))\n ) {\n return {\n schema: {\n ...schema,\n nullable,\n },\n unsupportedPaths: [],\n };\n }\n\n return null;\n}\n","import { html, nothing } from \"lit\";\nimport type { ConfigUiHints } from \"../types\";\nimport { analyzeConfigSchema, renderConfigForm, SECTION_META } from \"./config-form\";\nimport {\n hintForPath,\n humanize,\n schemaType,\n type JsonSchema,\n} from \"./config-form.shared\";\n\nexport type ConfigProps = {\n raw: string;\n originalRaw: string;\n valid: boolean | null;\n issues: unknown[];\n loading: boolean;\n saving: boolean;\n applying: boolean;\n updating: boolean;\n connected: boolean;\n schema: unknown | null;\n schemaLoading: boolean;\n uiHints: ConfigUiHints;\n formMode: \"form\" | \"raw\";\n formValue: Record | null;\n originalValue: Record | null;\n searchQuery: string;\n activeSection: string | null;\n activeSubsection: string | null;\n onRawChange: (next: string) => void;\n onFormModeChange: (mode: \"form\" | \"raw\") => void;\n onFormPatch: (path: Array, value: unknown) => void;\n onSearchChange: (query: string) => void;\n onSectionChange: (section: string | null) => void;\n onSubsectionChange: (section: string | null) => void;\n onReload: () => void;\n onSave: () => void;\n onApply: () => void;\n onUpdate: () => void;\n};\n\n// SVG Icons for sidebar (Lucide-style)\nconst sidebarIcons = {\n all: html``,\n env: html``,\n update: html``,\n agents: html``,\n auth: html``,\n channels: html``,\n messages: html``,\n commands: html``,\n hooks: html``,\n skills: html``,\n tools: html``,\n gateway: html``,\n wizard: html``,\n // Additional sections\n meta: html``,\n logging: html``,\n browser: html``,\n ui: html``,\n models: html``,\n bindings: html``,\n broadcast: html``,\n audio: html``,\n session: html``,\n cron: html``,\n web: html``,\n discovery: html``,\n canvasHost: html``,\n talk: html``,\n plugins: html``,\n default: html``,\n};\n\n// Section definitions\nconst SECTIONS: Array<{ key: string; label: string }> = [\n { key: \"env\", label: \"Environment\" },\n { key: \"update\", label: \"Updates\" },\n { key: \"agents\", label: \"Agents\" },\n { key: \"auth\", label: \"Authentication\" },\n { key: \"channels\", label: \"Channels\" },\n { key: \"messages\", label: \"Messages\" },\n { key: \"commands\", label: \"Commands\" },\n { key: \"hooks\", label: \"Hooks\" },\n { key: \"skills\", label: \"Skills\" },\n { key: \"tools\", label: \"Tools\" },\n { key: \"gateway\", label: \"Gateway\" },\n { key: \"wizard\", label: \"Setup Wizard\" },\n];\n\ntype SubsectionEntry = {\n key: string;\n label: string;\n description?: string;\n order: number;\n};\n\nconst ALL_SUBSECTION = \"__all__\";\n\nfunction getSectionIcon(key: string) {\n return sidebarIcons[key as keyof typeof sidebarIcons] ?? sidebarIcons.default;\n}\n\nfunction resolveSectionMeta(key: string, schema?: JsonSchema): {\n label: string;\n description?: string;\n} {\n const meta = SECTION_META[key];\n if (meta) return meta;\n return {\n label: schema?.title ?? humanize(key),\n description: schema?.description ?? \"\",\n };\n}\n\nfunction resolveSubsections(params: {\n key: string;\n schema: JsonSchema | undefined;\n uiHints: ConfigUiHints;\n}): SubsectionEntry[] {\n const { key, schema, uiHints } = params;\n if (!schema || schemaType(schema) !== \"object\" || !schema.properties) return [];\n const entries = Object.entries(schema.properties).map(([subKey, node]) => {\n const hint = hintForPath([key, subKey], uiHints);\n const label = hint?.label ?? node.title ?? humanize(subKey);\n const description = hint?.help ?? node.description ?? \"\";\n const order = hint?.order ?? 50;\n return { key: subKey, label, description, order };\n });\n entries.sort((a, b) => (a.order !== b.order ? a.order - b.order : a.key.localeCompare(b.key)));\n return entries;\n}\n\nfunction computeDiff(\n original: Record | null,\n current: Record | null\n): Array<{ path: string; from: unknown; to: unknown }> {\n if (!original || !current) return [];\n const changes: Array<{ path: string; from: unknown; to: unknown }> = [];\n \n function compare(orig: unknown, curr: unknown, path: string) {\n if (orig === curr) return;\n if (typeof orig !== typeof curr) {\n changes.push({ path, from: orig, to: curr });\n return;\n }\n if (typeof orig !== \"object\" || orig === null || curr === null) {\n if (orig !== curr) {\n changes.push({ path, from: orig, to: curr });\n }\n return;\n }\n if (Array.isArray(orig) && Array.isArray(curr)) {\n if (JSON.stringify(orig) !== JSON.stringify(curr)) {\n changes.push({ path, from: orig, to: curr });\n }\n return;\n }\n const origObj = orig as Record;\n const currObj = curr as Record;\n const allKeys = new Set([...Object.keys(origObj), ...Object.keys(currObj)]);\n for (const key of allKeys) {\n compare(origObj[key], currObj[key], path ? `${path}.${key}` : key);\n }\n }\n \n compare(original, current, \"\");\n return changes;\n}\n\nfunction truncateValue(value: unknown, maxLen = 40): string {\n let str: string;\n try {\n const json = JSON.stringify(value);\n str = json ?? String(value);\n } catch {\n str = String(value);\n }\n if (str.length <= maxLen) return str;\n return str.slice(0, maxLen - 3) + \"...\";\n}\n\nexport function renderConfig(props: ConfigProps) {\n const validity =\n props.valid == null ? \"unknown\" : props.valid ? \"valid\" : \"invalid\";\n const analysis = analyzeConfigSchema(props.schema);\n const formUnsafe = analysis.schema\n ? analysis.unsupportedPaths.length > 0\n : false;\n\n // Get available sections from schema\n const schemaProps = analysis.schema?.properties ?? {};\n const availableSections = SECTIONS.filter(s => s.key in schemaProps);\n\n // Add any sections in schema but not in our list\n const knownKeys = new Set(SECTIONS.map(s => s.key));\n const extraSections = Object.keys(schemaProps)\n .filter(k => !knownKeys.has(k))\n .map(k => ({ key: k, label: k.charAt(0).toUpperCase() + k.slice(1) }));\n\n const allSections = [...availableSections, ...extraSections];\n\n const activeSectionSchema =\n props.activeSection && analysis.schema && schemaType(analysis.schema) === \"object\"\n ? (analysis.schema.properties?.[props.activeSection] as JsonSchema | undefined)\n : undefined;\n const activeSectionMeta = props.activeSection\n ? resolveSectionMeta(props.activeSection, activeSectionSchema)\n : null;\n const subsections = props.activeSection\n ? resolveSubsections({\n key: props.activeSection,\n schema: activeSectionSchema,\n uiHints: props.uiHints,\n })\n : [];\n const allowSubnav =\n props.formMode === \"form\" &&\n Boolean(props.activeSection) &&\n subsections.length > 0;\n const isAllSubsection = props.activeSubsection === ALL_SUBSECTION;\n const effectiveSubsection = props.searchQuery\n ? null\n : isAllSubsection\n ? null\n : props.activeSubsection ?? (subsections[0]?.key ?? null);\n\n // Compute diff for showing changes (works for both form and raw modes)\n const diff = props.formMode === \"form\"\n ? computeDiff(props.originalValue, props.formValue)\n : [];\n const hasRawChanges = props.formMode === \"raw\" && props.raw !== props.originalRaw;\n const hasChanges = props.formMode === \"form\" ? diff.length > 0 : hasRawChanges;\n\n // Save/apply buttons require actual changes to be enabled.\n // Note: formUnsafe warns about unsupported schema paths but shouldn't block saving.\n const canSaveForm =\n Boolean(props.formValue) && !props.loading && Boolean(analysis.schema);\n const canSave =\n props.connected &&\n !props.saving &&\n hasChanges &&\n (props.formMode === \"raw\" ? true : canSaveForm);\n const canApply =\n props.connected &&\n !props.applying &&\n !props.updating &&\n hasChanges &&\n (props.formMode === \"raw\" ? true : canSaveForm);\n const canUpdate = props.connected && !props.applying && !props.updating;\n\n return html`\n
    \n \n \n \n \n
    \n \n
    \n
    \n ${hasChanges ? html`\n ${props.formMode === \"raw\" ? \"Unsaved changes\" : `${diff.length} unsaved change${diff.length !== 1 ? \"s\" : \"\"}`}\n ` : html`\n No changes\n `}\n
    \n
    \n \n \n ${props.saving ? \"Saving…\" : \"Save\"}\n \n \n ${props.applying ? \"Applying…\" : \"Apply\"}\n \n \n ${props.updating ? \"Updating…\" : \"Update\"}\n \n
    \n
    \n \n \n ${hasChanges && props.formMode === \"form\" ? html`\n
    \n \n View ${diff.length} pending change${diff.length !== 1 ? \"s\" : \"\"}\n \n \n \n \n
    \n ${diff.map(change => html`\n
    \n
    ${change.path}
    \n
    \n ${truncateValue(change.from)}\n \n ${truncateValue(change.to)}\n
    \n
    \n `)}\n
    \n
    \n ` : nothing}\n\n ${activeSectionMeta && props.formMode === \"form\"\n ? html`\n
    \n
    ${getSectionIcon(props.activeSection ?? \"\")}
    \n
    \n
    ${activeSectionMeta.label}
    \n ${activeSectionMeta.description\n ? html`
    ${activeSectionMeta.description}
    `\n : nothing}\n
    \n
    \n `\n : nothing}\n\n ${allowSubnav\n ? html`\n
    \n props.onSubsectionChange(ALL_SUBSECTION)}\n >\n All\n \n ${subsections.map(\n (entry) => html`\n props.onSubsectionChange(entry.key)}\n >\n ${entry.label}\n \n `,\n )}\n
    \n `\n : nothing}\n\n \n
    \n ${props.formMode === \"form\"\n ? html`\n ${props.schemaLoading\n ? html`
    \n
    \n Loading schema…\n
    `\n : renderConfigForm({\n schema: analysis.schema,\n uiHints: props.uiHints,\n value: props.formValue,\n disabled: props.loading || !props.formValue,\n unsupportedPaths: analysis.unsupportedPaths,\n onPatch: props.onFormPatch,\n searchQuery: props.searchQuery,\n activeSection: props.activeSection,\n activeSubsection: effectiveSubsection,\n })}\n ${formUnsafe\n ? html`
    \n Form view can't safely edit some fields.\n Use Raw to avoid losing config entries.\n
    `\n : nothing}\n `\n : html`\n \n `}\n
    \n\n ${props.issues.length > 0\n ? html`
    \n
    ${JSON.stringify(props.issues, null, 2)}
    \n
    `\n : nothing}\n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { ChannelAccountSnapshot } from \"../types\";\nimport type { ChannelKey, ChannelsProps } from \"./channels.types\";\n\nexport function formatDuration(ms?: number | null) {\n if (!ms && ms !== 0) return \"n/a\";\n const sec = Math.round(ms / 1000);\n if (sec < 60) return `${sec}s`;\n const min = Math.round(sec / 60);\n if (min < 60) return `${min}m`;\n const hr = Math.round(min / 60);\n return `${hr}h`;\n}\n\nexport function channelEnabled(key: ChannelKey, props: ChannelsProps) {\n const snapshot = props.snapshot;\n const channels = snapshot?.channels as Record | null;\n if (!snapshot || !channels) return false;\n const channelStatus = channels[key] as Record | undefined;\n const configured = typeof channelStatus?.configured === \"boolean\" && channelStatus.configured;\n const running = typeof channelStatus?.running === \"boolean\" && channelStatus.running;\n const connected = typeof channelStatus?.connected === \"boolean\" && channelStatus.connected;\n const accounts = snapshot.channelAccounts?.[key] ?? [];\n const accountActive = accounts.some(\n (account) => account.configured || account.running || account.connected,\n );\n return configured || running || connected || accountActive;\n}\n\nexport function getChannelAccountCount(\n key: ChannelKey,\n channelAccounts?: Record | null,\n): number {\n return channelAccounts?.[key]?.length ?? 0;\n}\n\nexport function renderChannelAccountCount(\n key: ChannelKey,\n channelAccounts?: Record | null,\n) {\n const count = getChannelAccountCount(key, channelAccounts);\n if (count < 2) return nothing;\n return html`
    Accounts (${count})
    `;\n}\n\n","import { html } from \"lit\";\n\nimport type { ConfigUiHints } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport {\n analyzeConfigSchema,\n renderNode,\n schemaType,\n type JsonSchema,\n} from \"./config-form\";\n\ntype ChannelConfigFormProps = {\n channelId: string;\n configValue: Record | null;\n schema: unknown | null;\n uiHints: ConfigUiHints;\n disabled: boolean;\n onPatch: (path: Array, value: unknown) => void;\n};\n\nfunction resolveSchemaNode(\n schema: JsonSchema | null,\n path: Array,\n): JsonSchema | null {\n let current = schema;\n for (const key of path) {\n if (!current) return null;\n const type = schemaType(current);\n if (type === \"object\") {\n const properties = current.properties ?? {};\n if (typeof key === \"string\" && properties[key]) {\n current = properties[key];\n continue;\n }\n const additional = current.additionalProperties;\n if (typeof key === \"string\" && additional && typeof additional === \"object\") {\n current = additional as JsonSchema;\n continue;\n }\n return null;\n }\n if (type === \"array\") {\n if (typeof key !== \"number\") return null;\n const items = Array.isArray(current.items) ? current.items[0] : current.items;\n current = items ?? null;\n continue;\n }\n return null;\n }\n return current;\n}\n\nfunction resolveChannelValue(\n config: Record,\n channelId: string,\n): Record {\n const channels = (config.channels ?? {}) as Record;\n const fromChannels = channels[channelId];\n const fallback = config[channelId];\n const resolved =\n (fromChannels && typeof fromChannels === \"object\"\n ? (fromChannels as Record)\n : null) ??\n (fallback && typeof fallback === \"object\"\n ? (fallback as Record)\n : null);\n return resolved ?? {};\n}\n\nexport function renderChannelConfigForm(props: ChannelConfigFormProps) {\n const analysis = analyzeConfigSchema(props.schema);\n const normalized = analysis.schema;\n if (!normalized) {\n return html`
    Schema unavailable. Use Raw.
    `;\n }\n const node = resolveSchemaNode(normalized, [\"channels\", props.channelId]);\n if (!node) {\n return html`
    Channel config schema unavailable.
    `;\n }\n const configValue = props.configValue ?? {};\n const value = resolveChannelValue(configValue, props.channelId);\n return html`\n
    \n ${renderNode({\n schema: node,\n value,\n path: [\"channels\", props.channelId],\n hints: props.uiHints,\n unsupported: new Set(analysis.unsupportedPaths),\n disabled: props.disabled,\n showLabel: false,\n onPatch: props.onPatch,\n })}\n
    \n `;\n}\n\nexport function renderChannelConfigSection(params: {\n channelId: string;\n props: ChannelsProps;\n}) {\n const { channelId, props } = params;\n const disabled = props.configSaving || props.configSchemaLoading;\n return html`\n
    \n ${props.configSchemaLoading\n ? html`
    Loading config schema…
    `\n : renderChannelConfigForm({\n channelId,\n configValue: props.configForm,\n schema: props.configSchema,\n uiHints: props.configUiHints,\n disabled,\n onPatch: props.onConfigPatch,\n })}\n
    \n props.onConfigSave()}\n >\n ${props.configSaving ? \"Saving…\" : \"Save\"}\n \n props.onConfigReload()}\n >\n Reload\n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { DiscordStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderDiscordCard(params: {\n props: ChannelsProps;\n discord?: DiscordStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, discord, accountCountLabel } = params;\n\n return html`\n
    \n
    Discord
    \n
    Bot status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${discord?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${discord?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Last start\n ${discord?.lastStartAt ? formatAgo(discord.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${discord?.lastProbeAt ? formatAgo(discord.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${discord?.lastError\n ? html`
    \n ${discord.lastError}\n
    `\n : nothing}\n\n ${discord?.probe\n ? html`
    \n Probe ${discord.probe.ok ? \"ok\" : \"failed\"} ·\n ${discord.probe.status ?? \"\"} ${discord.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"discord\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { GoogleChatStatus } from \"../types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\nimport type { ChannelsProps } from \"./channels.types\";\n\nexport function renderGoogleChatCard(params: {\n props: ChannelsProps;\n googleChat?: GoogleChatStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, googleChat, accountCountLabel } = params;\n\n return html`\n
    \n
    Google Chat
    \n
    Chat API webhook status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${googleChat ? (googleChat.configured ? \"Yes\" : \"No\") : \"n/a\"}\n
    \n
    \n Running\n ${googleChat ? (googleChat.running ? \"Yes\" : \"No\") : \"n/a\"}\n
    \n
    \n Credential\n ${googleChat?.credentialSource ?? \"n/a\"}\n
    \n
    \n Audience\n \n ${googleChat?.audienceType\n ? `${googleChat.audienceType}${googleChat.audience ? ` · ${googleChat.audience}` : \"\"}`\n : \"n/a\"}\n \n
    \n
    \n Last start\n ${googleChat?.lastStartAt ? formatAgo(googleChat.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${googleChat?.lastProbeAt ? formatAgo(googleChat.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${googleChat?.lastError\n ? html`
    \n ${googleChat.lastError}\n
    `\n : nothing}\n\n ${googleChat?.probe\n ? html`
    \n Probe ${googleChat.probe.ok ? \"ok\" : \"failed\"} ·\n ${googleChat.probe.status ?? \"\"} ${googleChat.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"googlechat\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { IMessageStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderIMessageCard(params: {\n props: ChannelsProps;\n imessage?: IMessageStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, imessage, accountCountLabel } = params;\n\n return html`\n
    \n
    iMessage
    \n
    macOS bridge status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${imessage?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${imessage?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Last start\n ${imessage?.lastStartAt ? formatAgo(imessage.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${imessage?.lastProbeAt ? formatAgo(imessage.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${imessage?.lastError\n ? html`
    \n ${imessage.lastError}\n
    `\n : nothing}\n\n ${imessage?.probe\n ? html`
    \n Probe ${imessage.probe.ok ? \"ok\" : \"failed\"} ·\n ${imessage.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"imessage\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","/**\n * Nostr Profile Edit Form\n *\n * Provides UI for editing and publishing Nostr profile (kind:0).\n */\n\nimport { html, nothing, type TemplateResult } from \"lit\";\n\nimport type { NostrProfile as NostrProfileType } from \"../types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface NostrProfileFormState {\n /** Current form values */\n values: NostrProfileType;\n /** Original values for dirty detection */\n original: NostrProfileType;\n /** Whether the form is currently submitting */\n saving: boolean;\n /** Whether import is in progress */\n importing: boolean;\n /** Last error message */\n error: string | null;\n /** Last success message */\n success: string | null;\n /** Validation errors per field */\n fieldErrors: Record;\n /** Whether to show advanced fields */\n showAdvanced: boolean;\n}\n\nexport interface NostrProfileFormCallbacks {\n /** Called when a field value changes */\n onFieldChange: (field: keyof NostrProfileType, value: string) => void;\n /** Called when save is clicked */\n onSave: () => void;\n /** Called when import is clicked */\n onImport: () => void;\n /** Called when cancel is clicked */\n onCancel: () => void;\n /** Called when toggle advanced is clicked */\n onToggleAdvanced: () => void;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction isFormDirty(state: NostrProfileFormState): boolean {\n const { values, original } = state;\n return (\n values.name !== original.name ||\n values.displayName !== original.displayName ||\n values.about !== original.about ||\n values.picture !== original.picture ||\n values.banner !== original.banner ||\n values.website !== original.website ||\n values.nip05 !== original.nip05 ||\n values.lud16 !== original.lud16\n );\n}\n\n// ============================================================================\n// Form Rendering\n// ============================================================================\n\nexport function renderNostrProfileForm(params: {\n state: NostrProfileFormState;\n callbacks: NostrProfileFormCallbacks;\n accountId: string;\n}): TemplateResult {\n const { state, callbacks, accountId } = params;\n const isDirty = isFormDirty(state);\n\n const renderField = (\n field: keyof NostrProfileType,\n label: string,\n opts: {\n type?: \"text\" | \"url\" | \"textarea\";\n placeholder?: string;\n maxLength?: number;\n help?: string;\n } = {}\n ) => {\n const { type = \"text\", placeholder, maxLength, help } = opts;\n const value = state.values[field] ?? \"\";\n const error = state.fieldErrors[field];\n\n const inputId = `nostr-profile-${field}`;\n\n if (type === \"textarea\") {\n return html`\n
    \n \n {\n const target = e.target as HTMLTextAreaElement;\n callbacks.onFieldChange(field, target.value);\n }}\n ?disabled=${state.saving}\n >\n ${help ? html`
    ${help}
    ` : nothing}\n ${error ? html`
    ${error}
    ` : nothing}\n
    \n `;\n }\n\n return html`\n
    \n \n {\n const target = e.target as HTMLInputElement;\n callbacks.onFieldChange(field, target.value);\n }}\n ?disabled=${state.saving}\n />\n ${help ? html`
    ${help}
    ` : nothing}\n ${error ? html`
    ${error}
    ` : nothing}\n
    \n `;\n };\n\n const renderPicturePreview = () => {\n const picture = state.values.picture;\n if (!picture) return nothing;\n\n return html`\n
    \n {\n const img = e.target as HTMLImageElement;\n img.style.display = \"none\";\n }}\n @load=${(e: Event) => {\n const img = e.target as HTMLImageElement;\n img.style.display = \"block\";\n }}\n />\n
    \n `;\n };\n\n return html`\n
    \n
    \n
    Edit Profile
    \n
    Account: ${accountId}
    \n
    \n\n ${state.error\n ? html`
    ${state.error}
    `\n : nothing}\n\n ${state.success\n ? html`
    ${state.success}
    `\n : nothing}\n\n ${renderPicturePreview()}\n\n ${renderField(\"name\", \"Username\", {\n placeholder: \"satoshi\",\n maxLength: 256,\n help: \"Short username (e.g., satoshi)\",\n })}\n\n ${renderField(\"displayName\", \"Display Name\", {\n placeholder: \"Satoshi Nakamoto\",\n maxLength: 256,\n help: \"Your full display name\",\n })}\n\n ${renderField(\"about\", \"Bio\", {\n type: \"textarea\",\n placeholder: \"Tell people about yourself...\",\n maxLength: 2000,\n help: \"A brief bio or description\",\n })}\n\n ${renderField(\"picture\", \"Avatar URL\", {\n type: \"url\",\n placeholder: \"https://example.com/avatar.jpg\",\n help: \"HTTPS URL to your profile picture\",\n })}\n\n ${state.showAdvanced\n ? html`\n
    \n
    Advanced
    \n\n ${renderField(\"banner\", \"Banner URL\", {\n type: \"url\",\n placeholder: \"https://example.com/banner.jpg\",\n help: \"HTTPS URL to a banner image\",\n })}\n\n ${renderField(\"website\", \"Website\", {\n type: \"url\",\n placeholder: \"https://example.com\",\n help: \"Your personal website\",\n })}\n\n ${renderField(\"nip05\", \"NIP-05 Identifier\", {\n placeholder: \"you@example.com\",\n help: \"Verifiable identifier (e.g., you@domain.com)\",\n })}\n\n ${renderField(\"lud16\", \"Lightning Address\", {\n placeholder: \"you@getalby.com\",\n help: \"Lightning address for tips (LUD-16)\",\n })}\n
    \n `\n : nothing}\n\n
    \n \n ${state.saving ? \"Saving...\" : \"Save & Publish\"}\n \n\n \n ${state.importing ? \"Importing...\" : \"Import from Relays\"}\n \n\n \n ${state.showAdvanced ? \"Hide Advanced\" : \"Show Advanced\"}\n \n\n \n Cancel\n \n
    \n\n ${isDirty\n ? html`
    \n You have unsaved changes\n
    `\n : nothing}\n
    \n `;\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create initial form state from existing profile\n */\nexport function createNostrProfileFormState(\n profile: NostrProfileType | undefined\n): NostrProfileFormState {\n const values: NostrProfileType = {\n name: profile?.name ?? \"\",\n displayName: profile?.displayName ?? \"\",\n about: profile?.about ?? \"\",\n picture: profile?.picture ?? \"\",\n banner: profile?.banner ?? \"\",\n website: profile?.website ?? \"\",\n nip05: profile?.nip05 ?? \"\",\n lud16: profile?.lud16 ?? \"\",\n };\n\n return {\n values,\n original: { ...values },\n saving: false,\n importing: false,\n error: null,\n success: null,\n fieldErrors: {},\n showAdvanced: Boolean(\n profile?.banner || profile?.website || profile?.nip05 || profile?.lud16\n ),\n };\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { ChannelAccountSnapshot, NostrStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\nimport {\n renderNostrProfileForm,\n type NostrProfileFormState,\n type NostrProfileFormCallbacks,\n} from \"./channels.nostr-profile-form\";\n\n/**\n * Truncate a pubkey for display (shows first and last 8 chars)\n */\nfunction truncatePubkey(pubkey: string | null | undefined): string {\n if (!pubkey) return \"n/a\";\n if (pubkey.length <= 20) return pubkey;\n return `${pubkey.slice(0, 8)}...${pubkey.slice(-8)}`;\n}\n\nexport function renderNostrCard(params: {\n props: ChannelsProps;\n nostr?: NostrStatus | null;\n nostrAccounts: ChannelAccountSnapshot[];\n accountCountLabel: unknown;\n /** Profile form state (optional - if provided, shows form) */\n profileFormState?: NostrProfileFormState | null;\n /** Profile form callbacks */\n profileFormCallbacks?: NostrProfileFormCallbacks | null;\n /** Called when Edit Profile is clicked */\n onEditProfile?: () => void;\n}) {\n const {\n props,\n nostr,\n nostrAccounts,\n accountCountLabel,\n profileFormState,\n profileFormCallbacks,\n onEditProfile,\n } = params;\n const primaryAccount = nostrAccounts[0];\n const summaryConfigured = nostr?.configured ?? primaryAccount?.configured ?? false;\n const summaryRunning = nostr?.running ?? primaryAccount?.running ?? false;\n const summaryPublicKey =\n nostr?.publicKey ??\n (primaryAccount as { publicKey?: string } | undefined)?.publicKey;\n const summaryLastStartAt = nostr?.lastStartAt ?? primaryAccount?.lastStartAt ?? null;\n const summaryLastError = nostr?.lastError ?? primaryAccount?.lastError ?? null;\n const hasMultipleAccounts = nostrAccounts.length > 1;\n const showingForm = profileFormState !== null && profileFormState !== undefined;\n\n const renderAccountCard = (account: ChannelAccountSnapshot) => {\n const publicKey = (account as { publicKey?: string }).publicKey;\n const profile = (account as { profile?: { name?: string; displayName?: string } }).profile;\n const displayName = profile?.displayName ?? profile?.name ?? account.name ?? account.accountId;\n\n return html`\n
    \n
    \n
    ${displayName}
    \n
    ${account.accountId}
    \n
    \n
    \n
    \n Running\n ${account.running ? \"Yes\" : \"No\"}\n
    \n
    \n Configured\n ${account.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Public Key\n ${truncatePubkey(publicKey)}\n
    \n
    \n Last inbound\n ${account.lastInboundAt ? formatAgo(account.lastInboundAt) : \"n/a\"}\n
    \n ${account.lastError\n ? html`\n
    ${account.lastError}
    \n `\n : nothing}\n
    \n
    \n `;\n };\n\n const renderProfileSection = () => {\n // If showing form, render the form instead of the read-only view\n if (showingForm && profileFormCallbacks) {\n return renderNostrProfileForm({\n state: profileFormState,\n callbacks: profileFormCallbacks,\n accountId: nostrAccounts[0]?.accountId ?? \"default\",\n });\n }\n\n const profile =\n (primaryAccount as\n | {\n profile?: {\n name?: string;\n displayName?: string;\n about?: string;\n picture?: string;\n nip05?: string;\n };\n }\n | undefined)?.profile ?? nostr?.profile;\n const { name, displayName, about, picture, nip05 } = profile ?? {};\n const hasAnyProfileData = name || displayName || about || picture || nip05;\n\n return html`\n
    \n
    \n
    Profile
    \n ${summaryConfigured\n ? html`\n \n Edit Profile\n \n `\n : nothing}\n
    \n ${hasAnyProfileData\n ? html`\n
    \n ${picture\n ? html`\n
    \n {\n (e.target as HTMLImageElement).style.display = \"none\";\n }}\n />\n
    \n `\n : nothing}\n ${name ? html`
    Name${name}
    ` : nothing}\n ${displayName\n ? html`
    Display Name${displayName}
    `\n : nothing}\n ${about\n ? html`
    About${about}
    `\n : nothing}\n ${nip05 ? html`
    NIP-05${nip05}
    ` : nothing}\n
    \n `\n : html`\n
    \n No profile set. Click \"Edit Profile\" to add your name, bio, and avatar.\n
    \n `}\n
    \n `;\n };\n\n return html`\n
    \n
    Nostr
    \n
    Decentralized DMs via Nostr relays (NIP-04).
    \n ${accountCountLabel}\n\n ${hasMultipleAccounts\n ? html`\n
    \n ${nostrAccounts.map((account) => renderAccountCard(account))}\n
    \n `\n : html`\n
    \n
    \n Configured\n ${summaryConfigured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${summaryRunning ? \"Yes\" : \"No\"}\n
    \n
    \n Public Key\n ${truncatePubkey(summaryPublicKey)}\n
    \n
    \n Last start\n ${summaryLastStartAt ? formatAgo(summaryLastStartAt) : \"n/a\"}\n
    \n
    \n `}\n\n ${summaryLastError\n ? html`
    ${summaryLastError}
    `\n : nothing}\n\n ${renderProfileSection()}\n\n ${renderChannelConfigSection({ channelId: \"nostr\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { SignalStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderSignalCard(params: {\n props: ChannelsProps;\n signal?: SignalStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, signal, accountCountLabel } = params;\n\n return html`\n
    \n
    Signal
    \n
    signal-cli status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${signal?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${signal?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Base URL\n ${signal?.baseUrl ?? \"n/a\"}\n
    \n
    \n Last start\n ${signal?.lastStartAt ? formatAgo(signal.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${signal?.lastProbeAt ? formatAgo(signal.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${signal?.lastError\n ? html`
    \n ${signal.lastError}\n
    `\n : nothing}\n\n ${signal?.probe\n ? html`
    \n Probe ${signal.probe.ok ? \"ok\" : \"failed\"} ·\n ${signal.probe.status ?? \"\"} ${signal.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"signal\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { SlackStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderSlackCard(params: {\n props: ChannelsProps;\n slack?: SlackStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, slack, accountCountLabel } = params;\n\n return html`\n
    \n
    Slack
    \n
    Socket mode status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${slack?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${slack?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Last start\n ${slack?.lastStartAt ? formatAgo(slack.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${slack?.lastProbeAt ? formatAgo(slack.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${slack?.lastError\n ? html`
    \n ${slack.lastError}\n
    `\n : nothing}\n\n ${slack?.probe\n ? html`
    \n Probe ${slack.probe.ok ? \"ok\" : \"failed\"} ·\n ${slack.probe.status ?? \"\"} ${slack.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"slack\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { ChannelAccountSnapshot, TelegramStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderTelegramCard(params: {\n props: ChannelsProps;\n telegram?: TelegramStatus;\n telegramAccounts: ChannelAccountSnapshot[];\n accountCountLabel: unknown;\n}) {\n const { props, telegram, telegramAccounts, accountCountLabel } = params;\n const hasMultipleAccounts = telegramAccounts.length > 1;\n\n const renderAccountCard = (account: ChannelAccountSnapshot) => {\n const probe = account.probe as { bot?: { username?: string } } | undefined;\n const botUsername = probe?.bot?.username;\n const label = account.name || account.accountId;\n return html`\n
    \n
    \n
    \n ${botUsername ? `@${botUsername}` : label}\n
    \n
    ${account.accountId}
    \n
    \n
    \n
    \n Running\n ${account.running ? \"Yes\" : \"No\"}\n
    \n
    \n Configured\n ${account.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Last inbound\n ${account.lastInboundAt ? formatAgo(account.lastInboundAt) : \"n/a\"}\n
    \n ${account.lastError\n ? html`\n
    \n ${account.lastError}\n
    \n `\n : nothing}\n
    \n
    \n `;\n };\n\n return html`\n
    \n
    Telegram
    \n
    Bot status and channel configuration.
    \n ${accountCountLabel}\n\n ${hasMultipleAccounts\n ? html`\n
    \n ${telegramAccounts.map((account) => renderAccountCard(account))}\n
    \n `\n : html`\n
    \n
    \n Configured\n ${telegram?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${telegram?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Mode\n ${telegram?.mode ?? \"n/a\"}\n
    \n
    \n Last start\n ${telegram?.lastStartAt ? formatAgo(telegram.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${telegram?.lastProbeAt ? formatAgo(telegram.lastProbeAt) : \"n/a\"}\n
    \n
    \n `}\n\n ${telegram?.lastError\n ? html`
    \n ${telegram.lastError}\n
    `\n : nothing}\n\n ${telegram?.probe\n ? html`
    \n Probe ${telegram.probe.ok ? \"ok\" : \"failed\"} ·\n ${telegram.probe.status ?? \"\"} ${telegram.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"telegram\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { WhatsAppStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\nimport { formatDuration } from \"./channels.shared\";\n\nexport function renderWhatsAppCard(params: {\n props: ChannelsProps;\n whatsapp?: WhatsAppStatus;\n accountCountLabel: unknown;\n}) {\n const { props, whatsapp, accountCountLabel } = params;\n\n return html`\n
    \n
    WhatsApp
    \n
    Link WhatsApp Web and monitor connection health.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${whatsapp?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Linked\n ${whatsapp?.linked ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${whatsapp?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Connected\n ${whatsapp?.connected ? \"Yes\" : \"No\"}\n
    \n
    \n Last connect\n \n ${whatsapp?.lastConnectedAt\n ? formatAgo(whatsapp.lastConnectedAt)\n : \"n/a\"}\n \n
    \n
    \n Last message\n \n ${whatsapp?.lastMessageAt ? formatAgo(whatsapp.lastMessageAt) : \"n/a\"}\n \n
    \n
    \n Auth age\n \n ${whatsapp?.authAgeMs != null\n ? formatDuration(whatsapp.authAgeMs)\n : \"n/a\"}\n \n
    \n
    \n\n ${whatsapp?.lastError\n ? html`
    \n ${whatsapp.lastError}\n
    `\n : nothing}\n\n ${props.whatsappMessage\n ? html`
    \n ${props.whatsappMessage}\n
    `\n : nothing}\n\n ${props.whatsappQrDataUrl\n ? html`
    \n \"WhatsApp\n
    `\n : nothing}\n\n
    \n props.onWhatsAppStart(false)}\n >\n ${props.whatsappBusy ? \"Working…\" : \"Show QR\"}\n \n props.onWhatsAppStart(true)}\n >\n Relink\n \n props.onWhatsAppWait()}\n >\n Wait for scan\n \n props.onWhatsAppLogout()}\n >\n Logout\n \n \n
    \n\n ${renderChannelConfigSection({ channelId: \"whatsapp\", props })}\n
    \n `;\n}\n\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type {\n ChannelAccountSnapshot,\n ChannelUiMetaEntry,\n ChannelsStatusSnapshot,\n DiscordStatus,\n GoogleChatStatus,\n IMessageStatus,\n NostrProfile,\n NostrStatus,\n SignalStatus,\n SlackStatus,\n TelegramStatus,\n WhatsAppStatus,\n} from \"../types\";\nimport type {\n ChannelKey,\n ChannelsChannelData,\n ChannelsProps,\n} from \"./channels.types\";\nimport { channelEnabled, renderChannelAccountCount } from \"./channels.shared\";\nimport { renderChannelConfigSection } from \"./channels.config\";\nimport { renderDiscordCard } from \"./channels.discord\";\nimport { renderGoogleChatCard } from \"./channels.googlechat\";\nimport { renderIMessageCard } from \"./channels.imessage\";\nimport { renderNostrCard } from \"./channels.nostr\";\nimport { renderSignalCard } from \"./channels.signal\";\nimport { renderSlackCard } from \"./channels.slack\";\nimport { renderTelegramCard } from \"./channels.telegram\";\nimport { renderWhatsAppCard } from \"./channels.whatsapp\";\n\nexport function renderChannels(props: ChannelsProps) {\n const channels = props.snapshot?.channels as Record | null;\n const whatsapp = (channels?.whatsapp ?? undefined) as\n | WhatsAppStatus\n | undefined;\n const telegram = (channels?.telegram ?? undefined) as\n | TelegramStatus\n | undefined;\n const discord = (channels?.discord ?? null) as DiscordStatus | null;\n const googlechat = (channels?.googlechat ?? null) as GoogleChatStatus | null;\n const slack = (channels?.slack ?? null) as SlackStatus | null;\n const signal = (channels?.signal ?? null) as SignalStatus | null;\n const imessage = (channels?.imessage ?? null) as IMessageStatus | null;\n const nostr = (channels?.nostr ?? null) as NostrStatus | null;\n const channelOrder = resolveChannelOrder(props.snapshot);\n const orderedChannels = channelOrder\n .map((key, index) => ({\n key,\n enabled: channelEnabled(key, props),\n order: index,\n }))\n .sort((a, b) => {\n if (a.enabled !== b.enabled) return a.enabled ? -1 : 1;\n return a.order - b.order;\n });\n\n return html`\n
    \n ${orderedChannels.map((channel) =>\n renderChannel(channel.key, props, {\n whatsapp,\n telegram,\n discord,\n googlechat,\n slack,\n signal,\n imessage,\n nostr,\n channelAccounts: props.snapshot?.channelAccounts ?? null,\n }),\n )}\n
    \n\n
    \n
    \n
    \n
    Channel health
    \n
    Channel status snapshots from the gateway.
    \n
    \n
    ${props.lastSuccessAt ? formatAgo(props.lastSuccessAt) : \"n/a\"}
    \n
    \n ${props.lastError\n ? html`
    \n ${props.lastError}\n
    `\n : nothing}\n
    \n${props.snapshot ? JSON.stringify(props.snapshot, null, 2) : \"No snapshot yet.\"}\n      
    \n
    \n `;\n}\n\nfunction resolveChannelOrder(snapshot: ChannelsStatusSnapshot | null): ChannelKey[] {\n if (snapshot?.channelMeta?.length) {\n return snapshot.channelMeta.map((entry) => entry.id) as ChannelKey[];\n }\n if (snapshot?.channelOrder?.length) {\n return snapshot.channelOrder;\n }\n return [\n \"whatsapp\",\n \"telegram\",\n \"discord\",\n \"googlechat\",\n \"slack\",\n \"signal\",\n \"imessage\",\n \"nostr\",\n ];\n}\n\nfunction renderChannel(\n key: ChannelKey,\n props: ChannelsProps,\n data: ChannelsChannelData,\n) {\n const accountCountLabel = renderChannelAccountCount(\n key,\n data.channelAccounts,\n );\n switch (key) {\n case \"whatsapp\":\n return renderWhatsAppCard({\n props,\n whatsapp: data.whatsapp,\n accountCountLabel,\n });\n case \"telegram\":\n return renderTelegramCard({\n props,\n telegram: data.telegram,\n telegramAccounts: data.channelAccounts?.telegram ?? [],\n accountCountLabel,\n });\n case \"discord\":\n return renderDiscordCard({\n props,\n discord: data.discord,\n accountCountLabel,\n });\n case \"googlechat\":\n return renderGoogleChatCard({\n props,\n googlechat: data.googlechat,\n accountCountLabel,\n });\n case \"slack\":\n return renderSlackCard({\n props,\n slack: data.slack,\n accountCountLabel,\n });\n case \"signal\":\n return renderSignalCard({\n props,\n signal: data.signal,\n accountCountLabel,\n });\n case \"imessage\":\n return renderIMessageCard({\n props,\n imessage: data.imessage,\n accountCountLabel,\n });\n case \"nostr\": {\n const nostrAccounts = data.channelAccounts?.nostr ?? [];\n const primaryAccount = nostrAccounts[0];\n const accountId = primaryAccount?.accountId ?? \"default\";\n const profile =\n (primaryAccount as { profile?: NostrProfile | null } | undefined)?.profile ?? null;\n const showForm =\n props.nostrProfileAccountId === accountId ? props.nostrProfileFormState : null;\n const profileFormCallbacks = showForm\n ? {\n onFieldChange: props.onNostrProfileFieldChange,\n onSave: props.onNostrProfileSave,\n onImport: props.onNostrProfileImport,\n onCancel: props.onNostrProfileCancel,\n onToggleAdvanced: props.onNostrProfileToggleAdvanced,\n }\n : null;\n return renderNostrCard({\n props,\n nostr: data.nostr,\n nostrAccounts,\n accountCountLabel,\n profileFormState: showForm,\n profileFormCallbacks,\n onEditProfile: () => props.onNostrProfileEdit(accountId, profile),\n });\n }\n default:\n return renderGenericChannelCard(key, props, data.channelAccounts ?? {});\n }\n}\n\nfunction renderGenericChannelCard(\n key: ChannelKey,\n props: ChannelsProps,\n channelAccounts: Record,\n) {\n const label = resolveChannelLabel(props.snapshot, key);\n const status = props.snapshot?.channels?.[key] as Record | undefined;\n const configured = typeof status?.configured === \"boolean\" ? status.configured : undefined;\n const running = typeof status?.running === \"boolean\" ? status.running : undefined;\n const connected = typeof status?.connected === \"boolean\" ? status.connected : undefined;\n const lastError = typeof status?.lastError === \"string\" ? status.lastError : undefined;\n const accounts = channelAccounts[key] ?? [];\n const accountCountLabel = renderChannelAccountCount(key, channelAccounts);\n\n return html`\n
    \n
    ${label}
    \n
    Channel status and configuration.
    \n ${accountCountLabel}\n\n ${accounts.length > 0\n ? html`\n
    \n ${accounts.map((account) => renderGenericAccount(account))}\n
    \n `\n : html`\n
    \n
    \n Configured\n ${configured == null ? \"n/a\" : configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${running == null ? \"n/a\" : running ? \"Yes\" : \"No\"}\n
    \n
    \n Connected\n ${connected == null ? \"n/a\" : connected ? \"Yes\" : \"No\"}\n
    \n
    \n `}\n\n ${lastError\n ? html`
    \n ${lastError}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: key, props })}\n
    \n `;\n}\n\nfunction resolveChannelMetaMap(\n snapshot: ChannelsStatusSnapshot | null,\n): Record {\n if (!snapshot?.channelMeta?.length) return {};\n return Object.fromEntries(snapshot.channelMeta.map((entry) => [entry.id, entry]));\n}\n\nfunction resolveChannelLabel(\n snapshot: ChannelsStatusSnapshot | null,\n key: string,\n): string {\n const meta = resolveChannelMetaMap(snapshot)[key];\n return meta?.label ?? snapshot?.channelLabels?.[key] ?? key;\n}\n\nconst RECENT_ACTIVITY_THRESHOLD_MS = 10 * 60 * 1000; // 10 minutes\n\nfunction hasRecentActivity(account: ChannelAccountSnapshot): boolean {\n if (!account.lastInboundAt) return false;\n return Date.now() - account.lastInboundAt < RECENT_ACTIVITY_THRESHOLD_MS;\n}\n\nfunction deriveRunningStatus(account: ChannelAccountSnapshot): \"Yes\" | \"No\" | \"Active\" {\n if (account.running) return \"Yes\";\n // If we have recent inbound activity, the channel is effectively running\n if (hasRecentActivity(account)) return \"Active\";\n return \"No\";\n}\n\nfunction deriveConnectedStatus(account: ChannelAccountSnapshot): \"Yes\" | \"No\" | \"Active\" | \"n/a\" {\n if (account.connected === true) return \"Yes\";\n if (account.connected === false) return \"No\";\n // If connected is null/undefined but we have recent activity, show as active\n if (hasRecentActivity(account)) return \"Active\";\n return \"n/a\";\n}\n\nfunction renderGenericAccount(account: ChannelAccountSnapshot) {\n const runningStatus = deriveRunningStatus(account);\n const connectedStatus = deriveConnectedStatus(account);\n\n return html`\n
    \n
    \n
    ${account.name || account.accountId}
    \n
    ${account.accountId}
    \n
    \n
    \n
    \n Running\n ${runningStatus}\n
    \n
    \n Configured\n ${account.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Connected\n ${connectedStatus}\n
    \n
    \n Last inbound\n ${account.lastInboundAt ? formatAgo(account.lastInboundAt) : \"n/a\"}\n
    \n ${account.lastError\n ? html`\n
    \n ${account.lastError}\n
    \n `\n : nothing}\n
    \n
    \n `;\n}\n","import { formatAgo, formatDurationMs, formatMs } from \"./format\";\nimport type { CronJob, GatewaySessionRow, PresenceEntry } from \"./types\";\n\nexport function formatPresenceSummary(entry: PresenceEntry): string {\n const host = entry.host ?? \"unknown\";\n const ip = entry.ip ? `(${entry.ip})` : \"\";\n const mode = entry.mode ?? \"\";\n const version = entry.version ?? \"\";\n return `${host} ${ip} ${mode} ${version}`.trim();\n}\n\nexport function formatPresenceAge(entry: PresenceEntry): string {\n const ts = entry.ts ?? null;\n return ts ? formatAgo(ts) : \"n/a\";\n}\n\nexport function formatNextRun(ms?: number | null) {\n if (!ms) return \"n/a\";\n return `${formatMs(ms)} (${formatAgo(ms)})`;\n}\n\nexport function formatSessionTokens(row: GatewaySessionRow) {\n if (row.totalTokens == null) return \"n/a\";\n const total = row.totalTokens ?? 0;\n const ctx = row.contextTokens ?? 0;\n return ctx ? `${total} / ${ctx}` : String(total);\n}\n\nexport function formatEventPayload(payload: unknown): string {\n if (payload == null) return \"\";\n try {\n return JSON.stringify(payload, null, 2);\n } catch {\n return String(payload);\n }\n}\n\nexport function formatCronState(job: CronJob) {\n const state = job.state ?? {};\n const next = state.nextRunAtMs ? formatMs(state.nextRunAtMs) : \"n/a\";\n const last = state.lastRunAtMs ? formatMs(state.lastRunAtMs) : \"n/a\";\n const status = state.lastStatus ?? \"n/a\";\n return `${status} · next ${next} · last ${last}`;\n}\n\nexport function formatCronSchedule(job: CronJob) {\n const s = job.schedule;\n if (s.kind === \"at\") return `At ${formatMs(s.atMs)}`;\n if (s.kind === \"every\") return `Every ${formatDurationMs(s.everyMs)}`;\n return `Cron ${s.expr}${s.tz ? ` (${s.tz})` : \"\"}`;\n}\n\nexport function formatCronPayload(job: CronJob) {\n const p = job.payload;\n if (p.kind === \"systemEvent\") return `System: ${p.text}`;\n return `Agent: ${p.message}`;\n}\n\n","import { html, nothing } from \"lit\";\n\nimport { formatMs } from \"../format\";\nimport {\n formatCronPayload,\n formatCronSchedule,\n formatCronState,\n formatNextRun,\n} from \"../presenter\";\nimport type { ChannelUiMetaEntry, CronJob, CronRunLogEntry, CronStatus } from \"../types\";\nimport type { CronFormState } from \"../ui-types\";\n\nexport type CronProps = {\n loading: boolean;\n status: CronStatus | null;\n jobs: CronJob[];\n error: string | null;\n busy: boolean;\n form: CronFormState;\n channels: string[];\n channelLabels?: Record;\n channelMeta?: ChannelUiMetaEntry[];\n runsJobId: string | null;\n runs: CronRunLogEntry[];\n onFormChange: (patch: Partial) => void;\n onRefresh: () => void;\n onAdd: () => void;\n onToggle: (job: CronJob, enabled: boolean) => void;\n onRun: (job: CronJob) => void;\n onRemove: (job: CronJob) => void;\n onLoadRuns: (jobId: string) => void;\n};\n\nfunction buildChannelOptions(props: CronProps): string[] {\n const options = [\"last\", ...props.channels.filter(Boolean)];\n const current = props.form.channel?.trim();\n if (current && !options.includes(current)) {\n options.push(current);\n }\n const seen = new Set();\n return options.filter((value) => {\n if (seen.has(value)) return false;\n seen.add(value);\n return true;\n });\n}\n\nfunction resolveChannelLabel(props: CronProps, channel: string): string {\n if (channel === \"last\") return \"last\";\n const meta = props.channelMeta?.find((entry) => entry.id === channel);\n if (meta?.label) return meta.label;\n return props.channelLabels?.[channel] ?? channel;\n}\n\nexport function renderCron(props: CronProps) {\n const channelOptions = buildChannelOptions(props);\n return html`\n
    \n
    \n
    Scheduler
    \n
    Gateway-owned cron scheduler status.
    \n
    \n
    \n
    Enabled
    \n
    \n ${props.status\n ? props.status.enabled\n ? \"Yes\"\n : \"No\"\n : \"n/a\"}\n
    \n
    \n
    \n
    Jobs
    \n
    ${props.status?.jobs ?? \"n/a\"}
    \n
    \n
    \n
    Next wake
    \n
    ${formatNextRun(props.status?.nextWakeAtMs ?? null)}
    \n
    \n
    \n
    \n \n ${props.error ? html`${props.error}` : nothing}\n
    \n
    \n\n
    \n
    New Job
    \n
    Create a scheduled wakeup or agent run.
    \n
    \n \n \n \n \n \n
    \n ${renderScheduleFields(props)}\n
    \n \n \n \n
    \n \n\t ${props.form.payloadKind === \"agentTurn\"\n\t ? html`\n\t
    \n \n\t \n \n \n ${props.form.sessionTarget === \"isolated\"\n ? html`\n \n `\n : nothing}\n
    \n `\n : nothing}\n
    \n \n
    \n
    \n
    \n\n
    \n
    Jobs
    \n
    All scheduled jobs stored in the gateway.
    \n ${props.jobs.length === 0\n ? html`
    No jobs yet.
    `\n : html`\n
    \n ${props.jobs.map((job) => renderJob(job, props))}\n
    \n `}\n
    \n\n
    \n
    Run history
    \n
    Latest runs for ${props.runsJobId ?? \"(select a job)\"}.
    \n ${props.runsJobId == null\n ? html`\n
    \n Select a job to inspect run history.\n
    \n `\n : props.runs.length === 0\n ? html`
    No runs yet.
    `\n : html`\n
    \n ${props.runs.map((entry) => renderRun(entry))}\n
    \n `}\n
    \n `;\n}\n\nfunction renderScheduleFields(props: CronProps) {\n const form = props.form;\n if (form.scheduleKind === \"at\") {\n return html`\n \n `;\n }\n if (form.scheduleKind === \"every\") {\n return html`\n
    \n \n \n
    \n `;\n }\n return html`\n
    \n \n \n
    \n `;\n}\n\nfunction renderJob(job: CronJob, props: CronProps) {\n const isSelected = props.runsJobId === job.id;\n const itemClass = `list-item list-item-clickable${isSelected ? \" list-item-selected\" : \"\"}`;\n return html`\n
    props.onLoadRuns(job.id)}>\n
    \n
    ${job.name}
    \n
    ${formatCronSchedule(job)}
    \n
    ${formatCronPayload(job)}
    \n ${job.agentId ? html`
    Agent: ${job.agentId}
    ` : nothing}\n
    \n ${job.enabled ? \"enabled\" : \"disabled\"}\n ${job.sessionTarget}\n ${job.wakeMode}\n
    \n
    \n
    \n
    ${formatCronState(job)}
    \n
    \n {\n event.stopPropagation();\n props.onToggle(job, !job.enabled);\n }}\n >\n ${job.enabled ? \"Disable\" : \"Enable\"}\n \n {\n event.stopPropagation();\n props.onRun(job);\n }}\n >\n Run\n \n {\n event.stopPropagation();\n props.onLoadRuns(job.id);\n }}\n >\n Runs\n \n {\n event.stopPropagation();\n props.onRemove(job);\n }}\n >\n Remove\n \n
    \n
    \n
    \n `;\n}\n\nfunction renderRun(entry: CronRunLogEntry) {\n return html`\n
    \n
    \n
    ${entry.status}
    \n
    ${entry.summary ?? \"\"}
    \n
    \n
    \n
    ${formatMs(entry.ts)}
    \n
    ${entry.durationMs ?? 0}ms
    \n ${entry.error ? html`
    ${entry.error}
    ` : nothing}\n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatEventPayload } from \"../presenter\";\nimport type { EventLogEntry } from \"../app-events\";\n\nexport type DebugProps = {\n loading: boolean;\n status: Record | null;\n health: Record | null;\n models: unknown[];\n heartbeat: unknown;\n eventLog: EventLogEntry[];\n callMethod: string;\n callParams: string;\n callResult: string | null;\n callError: string | null;\n onCallMethodChange: (next: string) => void;\n onCallParamsChange: (next: string) => void;\n onRefresh: () => void;\n onCall: () => void;\n};\n\nexport function renderDebug(props: DebugProps) {\n return html`\n
    \n
    \n
    \n
    \n
    Snapshots
    \n
    Status, health, and heartbeat data.
    \n
    \n \n
    \n
    \n
    \n
    Status
    \n
    ${JSON.stringify(props.status ?? {}, null, 2)}
    \n
    \n
    \n
    Health
    \n
    ${JSON.stringify(props.health ?? {}, null, 2)}
    \n
    \n
    \n
    Last heartbeat
    \n
    ${JSON.stringify(props.heartbeat ?? {}, null, 2)}
    \n
    \n
    \n
    \n\n
    \n
    Manual RPC
    \n
    Send a raw gateway method with JSON params.
    \n
    \n \n \n
    \n
    \n \n
    \n ${props.callError\n ? html`
    \n ${props.callError}\n
    `\n : nothing}\n ${props.callResult\n ? html`
    ${props.callResult}
    `\n : nothing}\n
    \n
    \n\n
    \n
    Models
    \n
    Catalog from models.list.
    \n
    ${JSON.stringify(\n        props.models ?? [],\n        null,\n        2,\n      )}
    \n
    \n\n
    \n
    Event Log
    \n
    Latest gateway events.
    \n ${props.eventLog.length === 0\n ? html`
    No events yet.
    `\n : html`\n
    \n ${props.eventLog.map(\n (evt) => html`\n
    \n
    \n
    ${evt.event}
    \n
    ${new Date(evt.ts).toLocaleTimeString()}
    \n
    \n
    \n
    ${formatEventPayload(evt.payload)}
    \n
    \n
    \n `,\n )}\n
    \n `}\n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatPresenceAge, formatPresenceSummary } from \"../presenter\";\nimport type { PresenceEntry } from \"../types\";\n\nexport type InstancesProps = {\n loading: boolean;\n entries: PresenceEntry[];\n lastError: string | null;\n statusMessage: string | null;\n onRefresh: () => void;\n};\n\nexport function renderInstances(props: InstancesProps) {\n return html`\n
    \n
    \n
    \n
    Connected Instances
    \n
    Presence beacons from the gateway and clients.
    \n
    \n \n
    \n ${props.lastError\n ? html`
    \n ${props.lastError}\n
    `\n : nothing}\n ${props.statusMessage\n ? html`
    \n ${props.statusMessage}\n
    `\n : nothing}\n
    \n ${props.entries.length === 0\n ? html`
    No instances reported yet.
    `\n : props.entries.map((entry) => renderEntry(entry))}\n
    \n
    \n `;\n}\n\nfunction renderEntry(entry: PresenceEntry) {\n const lastInput =\n entry.lastInputSeconds != null\n ? `${entry.lastInputSeconds}s ago`\n : \"n/a\";\n const mode = entry.mode ?? \"unknown\";\n const roles = Array.isArray(entry.roles) ? entry.roles.filter(Boolean) : [];\n const scopes = Array.isArray(entry.scopes) ? entry.scopes.filter(Boolean) : [];\n const scopesLabel =\n scopes.length > 0\n ? scopes.length > 3\n ? `${scopes.length} scopes`\n : `scopes: ${scopes.join(\", \")}`\n : null;\n return html`\n
    \n
    \n
    ${entry.host ?? \"unknown host\"}
    \n
    ${formatPresenceSummary(entry)}
    \n
    \n ${mode}\n ${roles.map((role) => html`${role}`)}\n ${scopesLabel ? html`${scopesLabel}` : nothing}\n ${entry.platform ? html`${entry.platform}` : nothing}\n ${entry.deviceFamily\n ? html`${entry.deviceFamily}`\n : nothing}\n ${entry.modelIdentifier\n ? html`${entry.modelIdentifier}`\n : nothing}\n ${entry.version ? html`${entry.version}` : nothing}\n
    \n
    \n
    \n
    ${formatPresenceAge(entry)}
    \n
    Last input ${lastInput}
    \n
    Reason ${entry.reason ?? \"\"}
    \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { LogEntry, LogLevel } from \"../types\";\n\nconst LEVELS: LogLevel[] = [\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"];\n\nexport type LogsProps = {\n loading: boolean;\n error: string | null;\n file: string | null;\n entries: LogEntry[];\n filterText: string;\n levelFilters: Record;\n autoFollow: boolean;\n truncated: boolean;\n onFilterTextChange: (next: string) => void;\n onLevelToggle: (level: LogLevel, enabled: boolean) => void;\n onToggleAutoFollow: (next: boolean) => void;\n onRefresh: () => void;\n onExport: (lines: string[], label: string) => void;\n onScroll: (event: Event) => void;\n};\n\nfunction formatTime(value?: string | null) {\n if (!value) return \"\";\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return value;\n return date.toLocaleTimeString();\n}\n\nfunction matchesFilter(entry: LogEntry, needle: string) {\n if (!needle) return true;\n const haystack = [entry.message, entry.subsystem, entry.raw]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n return haystack.includes(needle);\n}\n\nexport function renderLogs(props: LogsProps) {\n const needle = props.filterText.trim().toLowerCase();\n const levelFiltered = LEVELS.some((level) => !props.levelFilters[level]);\n const filtered = props.entries.filter((entry) => {\n if (entry.level && !props.levelFilters[entry.level]) return false;\n return matchesFilter(entry, needle);\n });\n const exportLabel = needle || levelFiltered ? \"filtered\" : \"visible\";\n\n return html`\n
    \n
    \n
    \n
    Logs
    \n
    Gateway file logs (JSONL).
    \n
    \n
    \n \n props.onExport(filtered.map((entry) => entry.raw), exportLabel)}\n >\n Export ${exportLabel}\n \n
    \n
    \n\n
    \n \n \n
    \n\n
    \n ${LEVELS.map(\n (level) => html`\n \n `,\n )}\n
    \n\n ${props.file\n ? html`
    File: ${props.file}
    `\n : nothing}\n ${props.truncated\n ? html`
    \n Log output truncated; showing latest chunk.\n
    `\n : nothing}\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n
    \n ${filtered.length === 0\n ? html`
    No log entries.
    `\n : filtered.map(\n (entry) => html`\n
    \n
    ${formatTime(entry.time)}
    \n
    ${entry.level ?? \"\"}
    \n
    ${entry.subsystem ?? \"\"}
    \n
    ${entry.message ?? entry.raw}
    \n
    \n `,\n )}\n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { clampText, formatAgo, formatList } from \"../format\";\nimport type {\n ExecApprovalsAllowlistEntry,\n ExecApprovalsFile,\n ExecApprovalsSnapshot,\n} from \"../controllers/exec-approvals\";\nimport type {\n DevicePairingList,\n DeviceTokenSummary,\n PairedDevice,\n PendingDevice,\n} from \"../controllers/devices\";\n\nexport type NodesProps = {\n loading: boolean;\n nodes: Array>;\n devicesLoading: boolean;\n devicesError: string | null;\n devicesList: DevicePairingList | null;\n configForm: Record | null;\n configLoading: boolean;\n configSaving: boolean;\n configDirty: boolean;\n configFormMode: \"form\" | \"raw\";\n execApprovalsLoading: boolean;\n execApprovalsSaving: boolean;\n execApprovalsDirty: boolean;\n execApprovalsSnapshot: ExecApprovalsSnapshot | null;\n execApprovalsForm: ExecApprovalsFile | null;\n execApprovalsSelectedAgent: string | null;\n execApprovalsTarget: \"gateway\" | \"node\";\n execApprovalsTargetNodeId: string | null;\n onRefresh: () => void;\n onDevicesRefresh: () => void;\n onDeviceApprove: (requestId: string) => void;\n onDeviceReject: (requestId: string) => void;\n onDeviceRotate: (deviceId: string, role: string, scopes?: string[]) => void;\n onDeviceRevoke: (deviceId: string, role: string) => void;\n onLoadConfig: () => void;\n onLoadExecApprovals: () => void;\n onBindDefault: (nodeId: string | null) => void;\n onBindAgent: (agentIndex: number, nodeId: string | null) => void;\n onSaveBindings: () => void;\n onExecApprovalsTargetChange: (kind: \"gateway\" | \"node\", nodeId: string | null) => void;\n onExecApprovalsSelectAgent: (agentId: string) => void;\n onExecApprovalsPatch: (path: Array, value: unknown) => void;\n onExecApprovalsRemove: (path: Array) => void;\n onSaveExecApprovals: () => void;\n};\n\nexport function renderNodes(props: NodesProps) {\n const bindingState = resolveBindingsState(props);\n const approvalsState = resolveExecApprovalsState(props);\n return html`\n ${renderExecApprovals(approvalsState)}\n ${renderBindings(bindingState)}\n ${renderDevices(props)}\n
    \n
    \n
    \n
    Nodes
    \n
    Paired devices and live links.
    \n
    \n \n
    \n
    \n ${props.nodes.length === 0\n ? html`
    No nodes found.
    `\n : props.nodes.map((n) => renderNode(n))}\n
    \n
    \n `;\n}\n\nfunction renderDevices(props: NodesProps) {\n const list = props.devicesList ?? { pending: [], paired: [] };\n const pending = Array.isArray(list.pending) ? list.pending : [];\n const paired = Array.isArray(list.paired) ? list.paired : [];\n return html`\n
    \n
    \n
    \n
    Devices
    \n
    Pairing requests + role tokens.
    \n
    \n \n
    \n ${props.devicesError\n ? html`
    ${props.devicesError}
    `\n : nothing}\n
    \n ${pending.length > 0\n ? html`\n
    Pending
    \n ${pending.map((req) => renderPendingDevice(req, props))}\n `\n : nothing}\n ${paired.length > 0\n ? html`\n
    Paired
    \n ${paired.map((device) => renderPairedDevice(device, props))}\n `\n : nothing}\n ${pending.length === 0 && paired.length === 0\n ? html`
    No paired devices.
    `\n : nothing}\n
    \n
    \n `;\n}\n\nfunction renderPendingDevice(req: PendingDevice, props: NodesProps) {\n const name = req.displayName?.trim() || req.deviceId;\n const age = typeof req.ts === \"number\" ? formatAgo(req.ts) : \"n/a\";\n const role = req.role?.trim() ? `role: ${req.role}` : \"role: -\";\n const repair = req.isRepair ? \" · repair\" : \"\";\n const ip = req.remoteIp ? ` · ${req.remoteIp}` : \"\";\n return html`\n
    \n
    \n
    ${name}
    \n
    ${req.deviceId}${ip}
    \n
    \n ${role} · requested ${age}${repair}\n
    \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n `;\n}\n\nfunction renderPairedDevice(device: PairedDevice, props: NodesProps) {\n const name = device.displayName?.trim() || device.deviceId;\n const ip = device.remoteIp ? ` · ${device.remoteIp}` : \"\";\n const roles = `roles: ${formatList(device.roles)}`;\n const scopes = `scopes: ${formatList(device.scopes)}`;\n const tokens = Array.isArray(device.tokens) ? device.tokens : [];\n return html`\n
    \n
    \n
    ${name}
    \n
    ${device.deviceId}${ip}
    \n
    ${roles} · ${scopes}
    \n ${tokens.length === 0\n ? html`
    Tokens: none
    `\n : html`\n
    Tokens
    \n
    \n ${tokens.map((token) => renderTokenRow(device.deviceId, token, props))}\n
    \n `}\n
    \n
    \n `;\n}\n\nfunction renderTokenRow(deviceId: string, token: DeviceTokenSummary, props: NodesProps) {\n const status = token.revokedAtMs ? \"revoked\" : \"active\";\n const scopes = `scopes: ${formatList(token.scopes)}`;\n const when = formatAgo(token.rotatedAtMs ?? token.createdAtMs ?? token.lastUsedAtMs ?? null);\n return html`\n
    \n
    ${token.role} · ${status} · ${scopes} · ${when}
    \n
    \n props.onDeviceRotate(deviceId, token.role, token.scopes)}\n >\n Rotate\n \n ${token.revokedAtMs\n ? nothing\n : html`\n props.onDeviceRevoke(deviceId, token.role)}\n >\n Revoke\n \n `}\n
    \n
    \n `;\n}\n\ntype BindingAgent = {\n id: string;\n name?: string;\n index: number;\n isDefault: boolean;\n binding?: string | null;\n};\n\ntype BindingNode = {\n id: string;\n label: string;\n};\n\ntype BindingState = {\n ready: boolean;\n disabled: boolean;\n configDirty: boolean;\n configLoading: boolean;\n configSaving: boolean;\n defaultBinding?: string | null;\n agents: BindingAgent[];\n nodes: BindingNode[];\n onBindDefault: (nodeId: string | null) => void;\n onBindAgent: (agentIndex: number, nodeId: string | null) => void;\n onSave: () => void;\n onLoadConfig: () => void;\n formMode: \"form\" | \"raw\";\n};\n\ntype ExecSecurity = \"deny\" | \"allowlist\" | \"full\";\ntype ExecAsk = \"off\" | \"on-miss\" | \"always\";\n\ntype ExecApprovalsResolvedDefaults = {\n security: ExecSecurity;\n ask: ExecAsk;\n askFallback: ExecSecurity;\n autoAllowSkills: boolean;\n};\n\ntype ExecApprovalsAgentOption = {\n id: string;\n name?: string;\n isDefault?: boolean;\n};\n\ntype ExecApprovalsTargetNode = {\n id: string;\n label: string;\n};\n\ntype ExecApprovalsState = {\n ready: boolean;\n disabled: boolean;\n dirty: boolean;\n loading: boolean;\n saving: boolean;\n form: ExecApprovalsFile | null;\n defaults: ExecApprovalsResolvedDefaults;\n selectedScope: string;\n selectedAgent: Record | null;\n agents: ExecApprovalsAgentOption[];\n allowlist: ExecApprovalsAllowlistEntry[];\n target: \"gateway\" | \"node\";\n targetNodeId: string | null;\n targetNodes: ExecApprovalsTargetNode[];\n onSelectScope: (agentId: string) => void;\n onSelectTarget: (kind: \"gateway\" | \"node\", nodeId: string | null) => void;\n onPatch: (path: Array, value: unknown) => void;\n onRemove: (path: Array) => void;\n onLoad: () => void;\n onSave: () => void;\n};\n\nconst EXEC_APPROVALS_DEFAULT_SCOPE = \"__defaults__\";\n\nconst SECURITY_OPTIONS: Array<{ value: ExecSecurity; label: string }> = [\n { value: \"deny\", label: \"Deny\" },\n { value: \"allowlist\", label: \"Allowlist\" },\n { value: \"full\", label: \"Full\" },\n];\n\nconst ASK_OPTIONS: Array<{ value: ExecAsk; label: string }> = [\n { value: \"off\", label: \"Off\" },\n { value: \"on-miss\", label: \"On miss\" },\n { value: \"always\", label: \"Always\" },\n];\n\nfunction resolveBindingsState(props: NodesProps): BindingState {\n const config = props.configForm;\n const nodes = resolveExecNodes(props.nodes);\n const { defaultBinding, agents } = resolveAgentBindings(config);\n const ready = Boolean(config);\n const disabled = props.configSaving || props.configFormMode === \"raw\";\n return {\n ready,\n disabled,\n configDirty: props.configDirty,\n configLoading: props.configLoading,\n configSaving: props.configSaving,\n defaultBinding,\n agents,\n nodes,\n onBindDefault: props.onBindDefault,\n onBindAgent: props.onBindAgent,\n onSave: props.onSaveBindings,\n onLoadConfig: props.onLoadConfig,\n formMode: props.configFormMode,\n };\n}\n\nfunction normalizeSecurity(value?: string): ExecSecurity {\n if (value === \"allowlist\" || value === \"full\" || value === \"deny\") return value;\n return \"deny\";\n}\n\nfunction normalizeAsk(value?: string): ExecAsk {\n if (value === \"always\" || value === \"off\" || value === \"on-miss\") return value;\n return \"on-miss\";\n}\n\nfunction resolveExecApprovalsDefaults(\n form: ExecApprovalsFile | null,\n): ExecApprovalsResolvedDefaults {\n const defaults = form?.defaults ?? {};\n return {\n security: normalizeSecurity(defaults.security),\n ask: normalizeAsk(defaults.ask),\n askFallback: normalizeSecurity(defaults.askFallback ?? \"deny\"),\n autoAllowSkills: Boolean(defaults.autoAllowSkills ?? false),\n };\n}\n\nfunction resolveConfigAgents(config: Record | null): ExecApprovalsAgentOption[] {\n const agentsNode = (config?.agents ?? {}) as Record;\n const list = Array.isArray(agentsNode.list) ? agentsNode.list : [];\n const agents: ExecApprovalsAgentOption[] = [];\n list.forEach((entry) => {\n if (!entry || typeof entry !== \"object\") return;\n const record = entry as Record;\n const id = typeof record.id === \"string\" ? record.id.trim() : \"\";\n if (!id) return;\n const name = typeof record.name === \"string\" ? record.name.trim() : undefined;\n const isDefault = record.default === true;\n agents.push({ id, name: name || undefined, isDefault });\n });\n return agents;\n}\n\nfunction resolveExecApprovalsAgents(\n config: Record | null,\n form: ExecApprovalsFile | null,\n): ExecApprovalsAgentOption[] {\n const configAgents = resolveConfigAgents(config);\n const approvalsAgents = Object.keys(form?.agents ?? {});\n const merged = new Map();\n configAgents.forEach((agent) => merged.set(agent.id, agent));\n approvalsAgents.forEach((id) => {\n if (merged.has(id)) return;\n merged.set(id, { id });\n });\n const agents = Array.from(merged.values());\n if (agents.length === 0) {\n agents.push({ id: \"main\", isDefault: true });\n }\n agents.sort((a, b) => {\n if (a.isDefault && !b.isDefault) return -1;\n if (!a.isDefault && b.isDefault) return 1;\n const aLabel = a.name?.trim() ? a.name : a.id;\n const bLabel = b.name?.trim() ? b.name : b.id;\n return aLabel.localeCompare(bLabel);\n });\n return agents;\n}\n\nfunction resolveExecApprovalsScope(\n selected: string | null,\n agents: ExecApprovalsAgentOption[],\n): string {\n if (selected === EXEC_APPROVALS_DEFAULT_SCOPE) return EXEC_APPROVALS_DEFAULT_SCOPE;\n if (selected && agents.some((agent) => agent.id === selected)) return selected;\n return EXEC_APPROVALS_DEFAULT_SCOPE;\n}\n\nfunction resolveExecApprovalsState(props: NodesProps): ExecApprovalsState {\n const form = props.execApprovalsForm ?? props.execApprovalsSnapshot?.file ?? null;\n const ready = Boolean(form);\n const defaults = resolveExecApprovalsDefaults(form);\n const agents = resolveExecApprovalsAgents(props.configForm, form);\n const targetNodes = resolveExecApprovalsNodes(props.nodes);\n const target = props.execApprovalsTarget;\n let targetNodeId =\n target === \"node\" && props.execApprovalsTargetNodeId\n ? props.execApprovalsTargetNodeId\n : null;\n if (target === \"node\" && targetNodeId && !targetNodes.some((node) => node.id === targetNodeId)) {\n targetNodeId = null;\n }\n const selectedScope = resolveExecApprovalsScope(props.execApprovalsSelectedAgent, agents);\n const selectedAgent =\n selectedScope !== EXEC_APPROVALS_DEFAULT_SCOPE\n ? ((form?.agents ?? {})[selectedScope] as Record | undefined) ??\n null\n : null;\n const allowlist = Array.isArray((selectedAgent as { allowlist?: unknown })?.allowlist)\n ? ((selectedAgent as { allowlist?: ExecApprovalsAllowlistEntry[] }).allowlist ??\n [])\n : [];\n return {\n ready,\n disabled: props.execApprovalsSaving || props.execApprovalsLoading,\n dirty: props.execApprovalsDirty,\n loading: props.execApprovalsLoading,\n saving: props.execApprovalsSaving,\n form,\n defaults,\n selectedScope,\n selectedAgent,\n agents,\n allowlist,\n target,\n targetNodeId,\n targetNodes,\n onSelectScope: props.onExecApprovalsSelectAgent,\n onSelectTarget: props.onExecApprovalsTargetChange,\n onPatch: props.onExecApprovalsPatch,\n onRemove: props.onExecApprovalsRemove,\n onLoad: props.onLoadExecApprovals,\n onSave: props.onSaveExecApprovals,\n };\n}\n\nfunction renderBindings(state: BindingState) {\n const supportsBinding = state.nodes.length > 0;\n const defaultValue = state.defaultBinding ?? \"\";\n return html`\n
    \n
    \n
    \n
    Exec node binding
    \n
    \n Pin agents to a specific node when using exec host=node.\n
    \n
    \n \n ${state.configSaving ? \"Saving…\" : \"Save\"}\n \n
    \n\n ${state.formMode === \"raw\"\n ? html`
    \n Switch the Config tab to Form mode to edit bindings here.\n
    `\n : nothing}\n\n ${!state.ready\n ? html`
    \n
    Load config to edit bindings.
    \n \n
    `\n : html`\n
    \n
    \n
    \n
    Default binding
    \n
    Used when agents do not override a node binding.
    \n
    \n
    \n \n ${!supportsBinding\n ? html`
    No nodes with system.run available.
    `\n : nothing}\n
    \n
    \n\n ${state.agents.length === 0\n ? html`
    No agents found.
    `\n : state.agents.map((agent) =>\n renderAgentBinding(agent, state),\n )}\n
    \n `}\n
    \n `;\n}\n\nfunction renderExecApprovals(state: ExecApprovalsState) {\n const ready = state.ready;\n const targetReady = state.target !== \"node\" || Boolean(state.targetNodeId);\n return html`\n
    \n
    \n
    \n
    Exec approvals
    \n
    \n Allowlist and approval policy for exec host=gateway/node.\n
    \n
    \n \n ${state.saving ? \"Saving…\" : \"Save\"}\n \n
    \n\n ${renderExecApprovalsTarget(state)}\n\n ${!ready\n ? html`
    \n
    Load exec approvals to edit allowlists.
    \n \n
    `\n : html`\n ${renderExecApprovalsTabs(state)}\n ${renderExecApprovalsPolicy(state)}\n ${state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE\n ? nothing\n : renderExecApprovalsAllowlist(state)}\n `}\n
    \n `;\n}\n\nfunction renderExecApprovalsTarget(state: ExecApprovalsState) {\n const hasNodes = state.targetNodes.length > 0;\n const nodeValue = state.targetNodeId ?? \"\";\n return html`\n
    \n
    \n
    \n
    Target
    \n
    \n Gateway edits local approvals; node edits the selected node.\n
    \n
    \n
    \n \n ${state.target === \"node\"\n ? html`\n \n `\n : nothing}\n
    \n
    \n ${state.target === \"node\" && !hasNodes\n ? html`
    No nodes advertise exec approvals yet.
    `\n : nothing}\n
    \n `;\n}\n\nfunction renderExecApprovalsTabs(state: ExecApprovalsState) {\n return html`\n
    \n Scope\n
    \n state.onSelectScope(EXEC_APPROVALS_DEFAULT_SCOPE)}\n >\n Defaults\n \n ${state.agents.map((agent) => {\n const label = agent.name?.trim() ? `${agent.name} (${agent.id})` : agent.id;\n return html`\n state.onSelectScope(agent.id)}\n >\n ${label}\n \n `;\n })}\n
    \n
    \n `;\n}\n\nfunction renderExecApprovalsPolicy(state: ExecApprovalsState) {\n const isDefaults = state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE;\n const defaults = state.defaults;\n const agent = state.selectedAgent ?? {};\n const basePath = isDefaults ? [\"defaults\"] : [\"agents\", state.selectedScope];\n const agentSecurity = typeof agent.security === \"string\" ? agent.security : undefined;\n const agentAsk = typeof agent.ask === \"string\" ? agent.ask : undefined;\n const agentAskFallback =\n typeof agent.askFallback === \"string\" ? agent.askFallback : undefined;\n const securityValue = isDefaults ? defaults.security : agentSecurity ?? \"__default__\";\n const askValue = isDefaults ? defaults.ask : agentAsk ?? \"__default__\";\n const askFallbackValue = isDefaults\n ? defaults.askFallback\n : agentAskFallback ?? \"__default__\";\n const autoOverride =\n typeof agent.autoAllowSkills === \"boolean\" ? agent.autoAllowSkills : undefined;\n const autoEffective = autoOverride ?? defaults.autoAllowSkills;\n const autoIsDefault = autoOverride == null;\n\n return html`\n
    \n
    \n
    \n
    Security
    \n
    \n ${isDefaults\n ? \"Default security mode.\"\n : `Default: ${defaults.security}.`}\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    Ask
    \n
    \n ${isDefaults ? \"Default prompt policy.\" : `Default: ${defaults.ask}.`}\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    Ask fallback
    \n
    \n ${isDefaults\n ? \"Applied when the UI prompt is unavailable.\"\n : `Default: ${defaults.askFallback}.`}\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    Auto-allow skill CLIs
    \n
    \n ${isDefaults\n ? \"Allow skill executables listed by the Gateway.\"\n : autoIsDefault\n ? `Using default (${defaults.autoAllowSkills ? \"on\" : \"off\"}).`\n : `Override (${autoEffective ? \"on\" : \"off\"}).`}\n
    \n
    \n
    \n \n ${!isDefaults && !autoIsDefault\n ? html` state.onRemove([...basePath, \"autoAllowSkills\"])}\n >\n Use default\n `\n : nothing}\n
    \n
    \n
    \n `;\n}\n\nfunction renderExecApprovalsAllowlist(state: ExecApprovalsState) {\n const allowlistPath = [\"agents\", state.selectedScope, \"allowlist\"];\n const entries = state.allowlist;\n return html`\n
    \n
    \n
    Allowlist
    \n
    Case-insensitive glob patterns.
    \n
    \n {\n const next = [...entries, { pattern: \"\" }];\n state.onPatch(allowlistPath, next);\n }}\n >\n Add pattern\n \n
    \n
    \n ${entries.length === 0\n ? html`
    No allowlist entries yet.
    `\n : entries.map((entry, index) =>\n renderAllowlistEntry(state, entry, index),\n )}\n
    \n `;\n}\n\nfunction renderAllowlistEntry(\n state: ExecApprovalsState,\n entry: ExecApprovalsAllowlistEntry,\n index: number,\n) {\n const lastUsed = entry.lastUsedAt ? formatAgo(entry.lastUsedAt) : \"never\";\n const lastCommand = entry.lastUsedCommand\n ? clampText(entry.lastUsedCommand, 120)\n : null;\n const lastPath = entry.lastResolvedPath\n ? clampText(entry.lastResolvedPath, 120)\n : null;\n return html`\n
    \n
    \n
    ${entry.pattern?.trim() ? entry.pattern : \"New pattern\"}
    \n
    Last used: ${lastUsed}
    \n ${lastCommand ? html`
    ${lastCommand}
    ` : nothing}\n ${lastPath ? html`
    ${lastPath}
    ` : nothing}\n
    \n
    \n \n {\n if (state.allowlist.length <= 1) {\n state.onRemove([\"agents\", state.selectedScope, \"allowlist\"]);\n return;\n }\n state.onRemove([\"agents\", state.selectedScope, \"allowlist\", index]);\n }}\n >\n Remove\n \n
    \n
    \n `;\n}\n\nfunction renderAgentBinding(agent: BindingAgent, state: BindingState) {\n const bindingValue = agent.binding ?? \"__default__\";\n const label = agent.name?.trim() ? `${agent.name} (${agent.id})` : agent.id;\n const supportsBinding = state.nodes.length > 0;\n return html`\n
    \n
    \n
    ${label}
    \n
    \n ${agent.isDefault ? \"default agent\" : \"agent\"} ·\n ${bindingValue === \"__default__\"\n ? `uses default (${state.defaultBinding ?? \"any\"})`\n : `override: ${agent.binding}`}\n
    \n
    \n
    \n \n
    \n
    \n `;\n}\n\nfunction resolveExecNodes(nodes: Array>): BindingNode[] {\n const list: BindingNode[] = [];\n for (const node of nodes) {\n const commands = Array.isArray(node.commands) ? node.commands : [];\n const supports = commands.some((cmd) => String(cmd) === \"system.run\");\n if (!supports) continue;\n const nodeId = typeof node.nodeId === \"string\" ? node.nodeId.trim() : \"\";\n if (!nodeId) continue;\n const displayName =\n typeof node.displayName === \"string\" && node.displayName.trim()\n ? node.displayName.trim()\n : nodeId;\n list.push({ id: nodeId, label: displayName === nodeId ? nodeId : `${displayName} · ${nodeId}` });\n }\n list.sort((a, b) => a.label.localeCompare(b.label));\n return list;\n}\n\nfunction resolveExecApprovalsNodes(nodes: Array>): ExecApprovalsTargetNode[] {\n const list: ExecApprovalsTargetNode[] = [];\n for (const node of nodes) {\n const commands = Array.isArray(node.commands) ? node.commands : [];\n const supports = commands.some(\n (cmd) => String(cmd) === \"system.execApprovals.get\" || String(cmd) === \"system.execApprovals.set\",\n );\n if (!supports) continue;\n const nodeId = typeof node.nodeId === \"string\" ? node.nodeId.trim() : \"\";\n if (!nodeId) continue;\n const displayName =\n typeof node.displayName === \"string\" && node.displayName.trim()\n ? node.displayName.trim()\n : nodeId;\n list.push({ id: nodeId, label: displayName === nodeId ? nodeId : `${displayName} · ${nodeId}` });\n }\n list.sort((a, b) => a.label.localeCompare(b.label));\n return list;\n}\n\nfunction resolveAgentBindings(config: Record | null): {\n defaultBinding?: string | null;\n agents: BindingAgent[];\n} {\n const fallbackAgent: BindingAgent = {\n id: \"main\",\n name: undefined,\n index: 0,\n isDefault: true,\n binding: null,\n };\n if (!config || typeof config !== \"object\") {\n return { defaultBinding: null, agents: [fallbackAgent] };\n }\n const tools = (config.tools ?? {}) as Record;\n const exec = (tools.exec ?? {}) as Record;\n const defaultBinding =\n typeof exec.node === \"string\" && exec.node.trim() ? exec.node.trim() : null;\n\n const agentsNode = (config.agents ?? {}) as Record;\n const list = Array.isArray(agentsNode.list) ? agentsNode.list : [];\n if (list.length === 0) {\n return { defaultBinding, agents: [fallbackAgent] };\n }\n\n const agents: BindingAgent[] = [];\n list.forEach((entry, index) => {\n if (!entry || typeof entry !== \"object\") return;\n const record = entry as Record;\n const id = typeof record.id === \"string\" ? record.id.trim() : \"\";\n if (!id) return;\n const name = typeof record.name === \"string\" ? record.name.trim() : undefined;\n const isDefault = record.default === true;\n const toolsEntry = (record.tools ?? {}) as Record;\n const execEntry = (toolsEntry.exec ?? {}) as Record;\n const binding =\n typeof execEntry.node === \"string\" && execEntry.node.trim()\n ? execEntry.node.trim()\n : null;\n agents.push({\n id,\n name: name || undefined,\n index,\n isDefault,\n binding,\n });\n });\n\n if (agents.length === 0) {\n agents.push(fallbackAgent);\n }\n\n return { defaultBinding, agents };\n}\n\nfunction renderNode(node: Record) {\n const connected = Boolean(node.connected);\n const paired = Boolean(node.paired);\n const title =\n (typeof node.displayName === \"string\" && node.displayName.trim()) ||\n (typeof node.nodeId === \"string\" ? node.nodeId : \"unknown\");\n const caps = Array.isArray(node.caps) ? (node.caps as unknown[]) : [];\n const commands = Array.isArray(node.commands) ? (node.commands as unknown[]) : [];\n return html`\n
    \n
    \n
    ${title}
    \n
    \n ${typeof node.nodeId === \"string\" ? node.nodeId : \"\"}\n ${typeof node.remoteIp === \"string\" ? ` · ${node.remoteIp}` : \"\"}\n ${typeof node.version === \"string\" ? ` · ${node.version}` : \"\"}\n
    \n
    \n ${paired ? \"paired\" : \"unpaired\"}\n \n ${connected ? \"connected\" : \"offline\"}\n \n ${caps.slice(0, 12).map((c) => html`${String(c)}`)}\n ${commands\n .slice(0, 8)\n .map((c) => html`${String(c)}`)}\n
    \n
    \n
    \n `;\n}\n","import { html } from \"lit\";\n\nimport type { GatewayHelloOk } from \"../gateway\";\nimport { formatAgo, formatDurationMs } from \"../format\";\nimport { formatNextRun } from \"../presenter\";\nimport type { UiSettings } from \"../storage\";\n\nexport type OverviewProps = {\n connected: boolean;\n hello: GatewayHelloOk | null;\n settings: UiSettings;\n password: string;\n lastError: string | null;\n presenceCount: number;\n sessionsCount: number | null;\n cronEnabled: boolean | null;\n cronNext: number | null;\n lastChannelsRefresh: number | null;\n onSettingsChange: (next: UiSettings) => void;\n onPasswordChange: (next: string) => void;\n onSessionKeyChange: (next: string) => void;\n onConnect: () => void;\n onRefresh: () => void;\n};\n\nexport function renderOverview(props: OverviewProps) {\n const snapshot = props.hello?.snapshot as\n | { uptimeMs?: number; policy?: { tickIntervalMs?: number } }\n | undefined;\n const uptime = snapshot?.uptimeMs ? formatDurationMs(snapshot.uptimeMs) : \"n/a\";\n const tick = snapshot?.policy?.tickIntervalMs\n ? `${snapshot.policy.tickIntervalMs}ms`\n : \"n/a\";\n const authHint = (() => {\n if (props.connected || !props.lastError) return null;\n const lower = props.lastError.toLowerCase();\n const authFailed = lower.includes(\"unauthorized\") || lower.includes(\"connect failed\");\n if (!authFailed) return null;\n const hasToken = Boolean(props.settings.token.trim());\n const hasPassword = Boolean(props.password.trim());\n if (!hasToken && !hasPassword) {\n return html`\n
    \n This gateway requires auth. Add a token or password, then click Connect.\n
    \n clawdbot dashboard --no-open → tokenized URL
    \n clawdbot doctor --generate-gateway-token → set token\n
    \n
    \n Docs: Control UI auth\n
    \n
    \n `;\n }\n return html`\n
    \n Auth failed. Re-copy a tokenized URL with\n clawdbot dashboard --no-open, or update the token,\n then click Connect.\n
    \n Docs: Control UI auth\n
    \n
    \n `;\n })();\n const insecureContextHint = (() => {\n if (props.connected || !props.lastError) return null;\n const isSecureContext = typeof window !== \"undefined\" ? window.isSecureContext : true;\n if (isSecureContext !== false) return null;\n const lower = props.lastError.toLowerCase();\n if (!lower.includes(\"secure context\") && !lower.includes(\"device identity required\")) {\n return null;\n }\n return html`\n
    \n This page is HTTP, so the browser blocks device identity. Use HTTPS (Tailscale Serve) or\n open http://127.0.0.1:18789 on the gateway host.\n
    \n If you must stay on HTTP, set\n gateway.controlUi.allowInsecureAuth: true (token-only).\n
    \n
    \n Docs: Tailscale Serve\n · \n Docs: Insecure HTTP\n
    \n
    \n `;\n })();\n\n return html`\n
    \n
    \n
    Gateway Access
    \n
    Where the dashboard connects and how it authenticates.
    \n
    \n \n \n \n \n
    \n
    \n \n \n Click Connect to apply connection changes.\n
    \n
    \n\n
    \n
    Snapshot
    \n
    Latest gateway handshake information.
    \n
    \n
    \n
    Status
    \n
    \n ${props.connected ? \"Connected\" : \"Disconnected\"}\n
    \n
    \n
    \n
    Uptime
    \n
    ${uptime}
    \n
    \n
    \n
    Tick Interval
    \n
    ${tick}
    \n
    \n
    \n
    Last Channels Refresh
    \n
    \n ${props.lastChannelsRefresh\n ? formatAgo(props.lastChannelsRefresh)\n : \"n/a\"}\n
    \n
    \n
    \n ${props.lastError\n ? html`
    \n
    ${props.lastError}
    \n ${authHint ?? \"\"}\n ${insecureContextHint ?? \"\"}\n
    `\n : html`
    \n Use Channels to link WhatsApp, Telegram, Discord, Signal, or iMessage.\n
    `}\n
    \n
    \n\n
    \n
    \n
    Instances
    \n
    ${props.presenceCount}
    \n
    Presence beacons in the last 5 minutes.
    \n
    \n
    \n
    Sessions
    \n
    ${props.sessionsCount ?? \"n/a\"}
    \n
    Recent session keys tracked by the gateway.
    \n
    \n
    \n
    Cron
    \n
    \n ${props.cronEnabled == null\n ? \"n/a\"\n : props.cronEnabled\n ? \"Enabled\"\n : \"Disabled\"}\n
    \n
    Next wake ${formatNextRun(props.cronNext)}
    \n
    \n
    \n\n
    \n
    Notes
    \n
    Quick reminders for remote control setups.
    \n
    \n
    \n
    Tailscale serve
    \n
    \n Prefer serve mode to keep the gateway on loopback with tailnet auth.\n
    \n
    \n
    \n
    Session hygiene
    \n
    Use /new or sessions.patch to reset context.
    \n
    \n
    \n
    Cron reminders
    \n
    Use isolated sessions for recurring runs.
    \n
    \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport { formatSessionTokens } from \"../presenter\";\nimport { pathForTab } from \"../navigation\";\nimport type { GatewaySessionRow, SessionsListResult } from \"../types\";\n\nexport type SessionsProps = {\n loading: boolean;\n result: SessionsListResult | null;\n error: string | null;\n activeMinutes: string;\n limit: string;\n includeGlobal: boolean;\n includeUnknown: boolean;\n basePath: string;\n onFiltersChange: (next: {\n activeMinutes: string;\n limit: string;\n includeGlobal: boolean;\n includeUnknown: boolean;\n }) => void;\n onRefresh: () => void;\n onPatch: (\n key: string,\n patch: {\n label?: string | null;\n thinkingLevel?: string | null;\n verboseLevel?: string | null;\n reasoningLevel?: string | null;\n },\n ) => void;\n onDelete: (key: string) => void;\n};\n\nconst THINK_LEVELS = [\"\", \"off\", \"minimal\", \"low\", \"medium\", \"high\"] as const;\nconst BINARY_THINK_LEVELS = [\"\", \"off\", \"on\"] as const;\nconst VERBOSE_LEVELS = [\n { value: \"\", label: \"inherit\" },\n { value: \"off\", label: \"off (explicit)\" },\n { value: \"on\", label: \"on\" },\n] as const;\nconst REASONING_LEVELS = [\"\", \"off\", \"on\", \"stream\"] as const;\n\nfunction normalizeProviderId(provider?: string | null): string {\n if (!provider) return \"\";\n const normalized = provider.trim().toLowerCase();\n if (normalized === \"z.ai\" || normalized === \"z-ai\") return \"zai\";\n return normalized;\n}\n\nfunction isBinaryThinkingProvider(provider?: string | null): boolean {\n return normalizeProviderId(provider) === \"zai\";\n}\n\nfunction resolveThinkLevelOptions(provider?: string | null): readonly string[] {\n return isBinaryThinkingProvider(provider) ? BINARY_THINK_LEVELS : THINK_LEVELS;\n}\n\nfunction resolveThinkLevelDisplay(value: string, isBinary: boolean): string {\n if (!isBinary) return value;\n if (!value || value === \"off\") return value;\n return \"on\";\n}\n\nfunction resolveThinkLevelPatchValue(value: string, isBinary: boolean): string | null {\n if (!value) return null;\n if (!isBinary) return value;\n if (value === \"on\") return \"low\";\n return value;\n}\n\nexport function renderSessions(props: SessionsProps) {\n const rows = props.result?.sessions ?? [];\n return html`\n
    \n
    \n
    \n
    Sessions
    \n
    Active session keys and per-session overrides.
    \n
    \n \n
    \n\n
    \n \n \n \n \n
    \n\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n
    \n ${props.result ? `Store: ${props.result.path}` : \"\"}\n
    \n\n
    \n
    \n
    Key
    \n
    Label
    \n
    Kind
    \n
    Updated
    \n
    Tokens
    \n
    Thinking
    \n
    Verbose
    \n
    Reasoning
    \n
    Actions
    \n
    \n ${rows.length === 0\n ? html`
    No sessions found.
    `\n : rows.map((row) =>\n renderRow(row, props.basePath, props.onPatch, props.onDelete, props.loading),\n )}\n
    \n
    \n `;\n}\n\nfunction renderRow(\n row: GatewaySessionRow,\n basePath: string,\n onPatch: SessionsProps[\"onPatch\"],\n onDelete: SessionsProps[\"onDelete\"],\n disabled: boolean,\n) {\n const updated = row.updatedAt ? formatAgo(row.updatedAt) : \"n/a\";\n const rawThinking = row.thinkingLevel ?? \"\";\n const isBinaryThinking = isBinaryThinkingProvider(row.modelProvider);\n const thinking = resolveThinkLevelDisplay(rawThinking, isBinaryThinking);\n const thinkLevels = resolveThinkLevelOptions(row.modelProvider);\n const verbose = row.verboseLevel ?? \"\";\n const reasoning = row.reasoningLevel ?? \"\";\n const displayName = row.displayName ?? row.key;\n const canLink = row.kind !== \"global\";\n const chatUrl = canLink\n ? `${pathForTab(\"chat\", basePath)}?session=${encodeURIComponent(row.key)}`\n : null;\n\n return html`\n
    \n
    ${canLink\n ? html`${displayName}`\n : displayName}
    \n
    \n {\n const value = (e.target as HTMLInputElement).value.trim();\n onPatch(row.key, { label: value || null });\n }}\n />\n
    \n
    ${row.kind}
    \n
    ${updated}
    \n
    ${formatSessionTokens(row)}
    \n
    \n {\n const value = (e.target as HTMLSelectElement).value;\n onPatch(row.key, {\n thinkingLevel: resolveThinkLevelPatchValue(value, isBinaryThinking),\n });\n }}\n >\n ${thinkLevels.map((level) =>\n html``,\n )}\n \n
    \n
    \n {\n const value = (e.target as HTMLSelectElement).value;\n onPatch(row.key, { verboseLevel: value || null });\n }}\n >\n ${VERBOSE_LEVELS.map(\n (level) => html``,\n )}\n \n
    \n
    \n {\n const value = (e.target as HTMLSelectElement).value;\n onPatch(row.key, { reasoningLevel: value || null });\n }}\n >\n ${REASONING_LEVELS.map((level) =>\n html``,\n )}\n \n
    \n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { AppViewState } from \"../app-view-state\";\n\nfunction formatRemaining(ms: number): string {\n const remaining = Math.max(0, ms);\n const totalSeconds = Math.floor(remaining / 1000);\n if (totalSeconds < 60) return `${totalSeconds}s`;\n const minutes = Math.floor(totalSeconds / 60);\n if (minutes < 60) return `${minutes}m`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h`;\n}\n\nfunction renderMetaRow(label: string, value?: string | null) {\n if (!value) return nothing;\n return html`
    ${label}${value}
    `;\n}\n\nexport function renderExecApprovalPrompt(state: AppViewState) {\n const active = state.execApprovalQueue[0];\n if (!active) return nothing;\n const request = active.request;\n const remainingMs = active.expiresAtMs - Date.now();\n const remaining = remainingMs > 0 ? `expires in ${formatRemaining(remainingMs)}` : \"expired\";\n const queueCount = state.execApprovalQueue.length;\n return html`\n
    \n
    \n
    \n
    \n
    Exec approval needed
    \n
    ${remaining}
    \n
    \n ${queueCount > 1\n ? html`
    ${queueCount} pending
    `\n : nothing}\n
    \n
    ${request.command}
    \n
    \n ${renderMetaRow(\"Host\", request.host)}\n ${renderMetaRow(\"Agent\", request.agentId)}\n ${renderMetaRow(\"Session\", request.sessionKey)}\n ${renderMetaRow(\"CWD\", request.cwd)}\n ${renderMetaRow(\"Resolved\", request.resolvedPath)}\n ${renderMetaRow(\"Security\", request.security)}\n ${renderMetaRow(\"Ask\", request.ask)}\n
    \n ${state.execApprovalError\n ? html`
    ${state.execApprovalError}
    `\n : nothing}\n
    \n state.handleExecApprovalDecision(\"allow-once\")}\n >\n Allow once\n \n state.handleExecApprovalDecision(\"allow-always\")}\n >\n Always allow\n \n state.handleExecApprovalDecision(\"deny\")}\n >\n Deny\n \n
    \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { clampText } from \"../format\";\nimport type { SkillStatusEntry, SkillStatusReport } from \"../types\";\nimport type { SkillMessageMap } from \"../controllers/skills\";\n\nexport type SkillsProps = {\n loading: boolean;\n report: SkillStatusReport | null;\n error: string | null;\n filter: string;\n edits: Record;\n busyKey: string | null;\n messages: SkillMessageMap;\n onFilterChange: (next: string) => void;\n onRefresh: () => void;\n onToggle: (skillKey: string, enabled: boolean) => void;\n onEdit: (skillKey: string, value: string) => void;\n onSaveKey: (skillKey: string) => void;\n onInstall: (skillKey: string, name: string, installId: string) => void;\n};\n\nexport function renderSkills(props: SkillsProps) {\n const skills = props.report?.skills ?? [];\n const filter = props.filter.trim().toLowerCase();\n const filtered = filter\n ? skills.filter((skill) =>\n [skill.name, skill.description, skill.source]\n .join(\" \")\n .toLowerCase()\n .includes(filter),\n )\n : skills;\n\n return html`\n
    \n
    \n
    \n
    Skills
    \n
    Bundled, managed, and workspace skills.
    \n
    \n \n
    \n\n
    \n \n
    ${filtered.length} shown
    \n
    \n\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n ${filtered.length === 0\n ? html`
    No skills found.
    `\n : html`\n
    \n ${filtered.map((skill) => renderSkill(skill, props))}\n
    \n `}\n
    \n `;\n}\n\nfunction renderSkill(skill: SkillStatusEntry, props: SkillsProps) {\n const busy = props.busyKey === skill.skillKey;\n const apiKey = props.edits[skill.skillKey] ?? \"\";\n const message = props.messages[skill.skillKey] ?? null;\n const canInstall =\n skill.install.length > 0 && skill.missing.bins.length > 0;\n const missing = [\n ...skill.missing.bins.map((b) => `bin:${b}`),\n ...skill.missing.env.map((e) => `env:${e}`),\n ...skill.missing.config.map((c) => `config:${c}`),\n ...skill.missing.os.map((o) => `os:${o}`),\n ];\n const reasons: string[] = [];\n if (skill.disabled) reasons.push(\"disabled\");\n if (skill.blockedByAllowlist) reasons.push(\"blocked by allowlist\");\n return html`\n
    \n
    \n
    \n ${skill.emoji ? `${skill.emoji} ` : \"\"}${skill.name}\n
    \n
    ${clampText(skill.description, 140)}
    \n
    \n ${skill.source}\n \n ${skill.eligible ? \"eligible\" : \"blocked\"}\n \n ${skill.disabled ? html`disabled` : nothing}\n
    \n ${missing.length > 0\n ? html`\n
    \n Missing: ${missing.join(\", \")}\n
    \n `\n : nothing}\n ${reasons.length > 0\n ? html`\n
    \n Reason: ${reasons.join(\", \")}\n
    \n `\n : nothing}\n
    \n
    \n
    \n props.onToggle(skill.skillKey, skill.disabled)}\n >\n ${skill.disabled ? \"Enable\" : \"Disable\"}\n \n ${canInstall\n ? html`\n props.onInstall(skill.skillKey, skill.name, skill.install[0].id)}\n >\n ${busy ? \"Installing…\" : skill.install[0].label}\n `\n : nothing}\n
    \n ${message\n ? html`\n ${message.message}\n
    `\n : nothing}\n ${skill.primaryEnv\n ? html`\n
    \n API key\n \n props.onEdit(skill.skillKey, (e.target as HTMLInputElement).value)}\n />\n
    \n props.onSaveKey(skill.skillKey)}\n >\n Save key\n \n `\n : nothing}\n
    \n \n `;\n}\n","import { html } from \"lit\";\nimport { repeat } from \"lit/directives/repeat.js\";\n\nimport type { AppViewState } from \"./app-view-state\";\nimport { iconForTab, pathForTab, titleForTab, type Tab } from \"./navigation\";\nimport { icons } from \"./icons\";\nimport { loadChatHistory } from \"./controllers/chat\";\nimport { syncUrlWithSessionKey } from \"./app-settings\";\nimport type { SessionsListResult } from \"./types\";\nimport type { ThemeMode } from \"./theme\";\nimport type { ThemeTransitionContext } from \"./theme-transition\";\n\nexport function renderTab(state: AppViewState, tab: Tab) {\n const href = pathForTab(tab, state.basePath);\n return html`\n {\n if (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey\n ) {\n return;\n }\n event.preventDefault();\n state.setTab(tab);\n }}\n title=${titleForTab(tab)}\n >\n ${icons[iconForTab(tab)]}\n ${titleForTab(tab)}\n \n `;\n}\n\nexport function renderChatControls(state: AppViewState) {\n const sessionOptions = resolveSessionOptions(state.sessionKey, state.sessionsResult);\n const disableThinkingToggle = state.onboarding;\n const disableFocusToggle = state.onboarding;\n const showThinking = state.onboarding ? false : state.settings.chatShowThinking;\n const focusActive = state.onboarding ? true : state.settings.chatFocusMode;\n // Refresh icon\n const refreshIcon = html``;\n const focusIcon = html``;\n return html`\n
    \n \n {\n state.resetToolStream();\n void loadChatHistory(state);\n }}\n title=\"Refresh chat history\"\n >\n ${refreshIcon}\n \n |\n {\n if (disableThinkingToggle) return;\n state.applySettings({\n ...state.settings,\n chatShowThinking: !state.settings.chatShowThinking,\n });\n }}\n aria-pressed=${showThinking}\n title=${disableThinkingToggle\n ? \"Disabled during onboarding\"\n : \"Toggle assistant thinking/working output\"}\n >\n ${icons.brain}\n \n {\n if (disableFocusToggle) return;\n state.applySettings({\n ...state.settings,\n chatFocusMode: !state.settings.chatFocusMode,\n });\n }}\n aria-pressed=${focusActive}\n title=${disableFocusToggle\n ? \"Disabled during onboarding\"\n : \"Toggle focus mode (hide sidebar + page header)\"}\n >\n ${focusIcon}\n \n
    \n `;\n}\n\nfunction resolveSessionOptions(sessionKey: string, sessions: SessionsListResult | null) {\n const seen = new Set();\n const options: Array<{ key: string; displayName?: string }> = [];\n\n const resolvedCurrent = sessions?.sessions?.find((s) => s.key === sessionKey);\n\n // Add current session key first\n seen.add(sessionKey);\n options.push({ key: sessionKey, displayName: resolvedCurrent?.displayName });\n\n // Add sessions from the result\n if (sessions?.sessions) {\n for (const s of sessions.sessions) {\n if (!seen.has(s.key)) {\n seen.add(s.key);\n options.push({ key: s.key, displayName: s.displayName });\n }\n }\n }\n\n return options;\n}\n\nconst THEME_ORDER: ThemeMode[] = [\"system\", \"light\", \"dark\"];\n\nexport function renderThemeToggle(state: AppViewState) {\n const index = Math.max(0, THEME_ORDER.indexOf(state.theme));\n const applyTheme = (next: ThemeMode) => (event: MouseEvent) => {\n const element = event.currentTarget as HTMLElement;\n const context: ThemeTransitionContext = { element };\n if (event.clientX || event.clientY) {\n context.pointerClientX = event.clientX;\n context.pointerClientY = event.clientY;\n }\n state.setTheme(next, context);\n };\n\n return html`\n
    \n
    \n \n \n ${renderMonitorIcon()}\n \n \n ${renderSunIcon()}\n \n \n ${renderMoonIcon()}\n \n
    \n
    \n `;\n}\n\nfunction renderSunIcon() {\n return html`\n \n \n \n \n \n \n \n \n \n \n \n `;\n}\n\nfunction renderMoonIcon() {\n return html`\n \n
    \n \n `;\n}\n\nfunction renderMonitorIcon() {\n return html`\n \n \n \n \n \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { GatewayBrowserClient, GatewayHelloOk } from \"./gateway\";\nimport type { AppViewState } from \"./app-view-state\";\nimport { parseAgentSessionKey } from \"../../../src/routing/session-key.js\";\nimport {\n TAB_GROUPS,\n iconForTab,\n pathForTab,\n subtitleForTab,\n titleForTab,\n type Tab,\n} from \"./navigation\";\nimport { icons } from \"./icons\";\nimport type { UiSettings } from \"./storage\";\nimport type { ThemeMode } from \"./theme\";\nimport type { ThemeTransitionContext } from \"./theme-transition\";\nimport type {\n ConfigSnapshot,\n CronJob,\n CronRunLogEntry,\n CronStatus,\n HealthSnapshot,\n LogEntry,\n LogLevel,\n PresenceEntry,\n ChannelsStatusSnapshot,\n SessionsListResult,\n SkillStatusReport,\n StatusSummary,\n} from \"./types\";\nimport type { ChatQueueItem, CronFormState } from \"./ui-types\";\nimport { refreshChatAvatar } from \"./app-chat\";\nimport { renderChat } from \"./views/chat\";\nimport { renderConfig } from \"./views/config\";\nimport { renderChannels } from \"./views/channels\";\nimport { renderCron } from \"./views/cron\";\nimport { renderDebug } from \"./views/debug\";\nimport { renderInstances } from \"./views/instances\";\nimport { renderLogs } from \"./views/logs\";\nimport { renderNodes } from \"./views/nodes\";\nimport { renderOverview } from \"./views/overview\";\nimport { renderSessions } from \"./views/sessions\";\nimport { renderExecApprovalPrompt } from \"./views/exec-approval\";\nimport {\n approveDevicePairing,\n loadDevices,\n rejectDevicePairing,\n revokeDeviceToken,\n rotateDeviceToken,\n} from \"./controllers/devices\";\nimport { renderSkills } from \"./views/skills\";\nimport { renderChatControls, renderTab, renderThemeToggle } from \"./app-render.helpers\";\nimport { loadChannels } from \"./controllers/channels\";\nimport { loadPresence } from \"./controllers/presence\";\nimport { deleteSession, loadSessions, patchSession } from \"./controllers/sessions\";\nimport {\n installSkill,\n loadSkills,\n saveSkillApiKey,\n updateSkillEdit,\n updateSkillEnabled,\n type SkillMessage,\n} from \"./controllers/skills\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadChatHistory } from \"./controllers/chat\";\nimport {\n applyConfig,\n loadConfig,\n runUpdate,\n saveConfig,\n updateConfigFormValue,\n removeConfigFormValue,\n} from \"./controllers/config\";\nimport {\n loadExecApprovals,\n removeExecApprovalsFormValue,\n saveExecApprovals,\n updateExecApprovalsFormValue,\n} from \"./controllers/exec-approvals\";\nimport { loadCronRuns, toggleCronJob, runCronJob, removeCronJob, addCronJob } from \"./controllers/cron\";\nimport { loadDebug, callDebugMethod } from \"./controllers/debug\";\nimport { loadLogs } from \"./controllers/logs\";\n\nconst AVATAR_DATA_RE = /^data:/i;\nconst AVATAR_HTTP_RE = /^https?:\\/\\//i;\n\nfunction resolveAssistantAvatarUrl(state: AppViewState): string | undefined {\n const list = state.agentsList?.agents ?? [];\n const parsed = parseAgentSessionKey(state.sessionKey);\n const agentId =\n parsed?.agentId ??\n state.agentsList?.defaultId ??\n \"main\";\n const agent = list.find((entry) => entry.id === agentId);\n const identity = agent?.identity;\n const candidate = identity?.avatarUrl ?? identity?.avatar;\n if (!candidate) return undefined;\n if (AVATAR_DATA_RE.test(candidate) || AVATAR_HTTP_RE.test(candidate)) return candidate;\n return identity?.avatarUrl;\n}\n\nexport function renderApp(state: AppViewState) {\n const presenceCount = state.presenceEntries.length;\n const sessionsCount = state.sessionsResult?.count ?? null;\n const cronNext = state.cronStatus?.nextWakeAtMs ?? null;\n const chatDisabledReason = state.connected ? null : \"Disconnected from gateway.\";\n const isChat = state.tab === \"chat\";\n const chatFocus = isChat && (state.settings.chatFocusMode || state.onboarding);\n const showThinking = state.onboarding ? false : state.settings.chatShowThinking;\n const assistantAvatarUrl = resolveAssistantAvatarUrl(state);\n const chatAvatarUrl = state.chatAvatarUrl ?? assistantAvatarUrl ?? null;\n\n return html`\n
    \n
    \n
    \n \n state.applySettings({\n ...state.settings,\n navCollapsed: !state.settings.navCollapsed,\n })}\n title=\"${state.settings.navCollapsed ? \"Expand sidebar\" : \"Collapse sidebar\"}\"\n aria-label=\"${state.settings.navCollapsed ? \"Expand sidebar\" : \"Collapse sidebar\"}\"\n >\n ${icons.menu}\n \n
    \n
    \n \"Clawdbot\"\n
    \n
    \n
    CLAWDBOT
    \n
    Gateway Dashboard
    \n
    \n
    \n
    \n
    \n
    \n \n Health\n ${state.connected ? \"OK\" : \"Offline\"}\n
    \n ${renderThemeToggle(state)}\n
    \n
    \n \n
    \n
    \n
    \n
    ${titleForTab(state.tab)}
    \n
    ${subtitleForTab(state.tab)}
    \n
    \n
    \n ${state.lastError\n ? html`
    ${state.lastError}
    `\n : nothing}\n ${isChat ? renderChatControls(state) : nothing}\n
    \n
    \n\n ${state.tab === \"overview\"\n ? renderOverview({\n connected: state.connected,\n hello: state.hello,\n settings: state.settings,\n password: state.password,\n lastError: state.lastError,\n presenceCount,\n sessionsCount,\n cronEnabled: state.cronStatus?.enabled ?? null,\n cronNext,\n lastChannelsRefresh: state.channelsLastSuccess,\n onSettingsChange: (next) => state.applySettings(next),\n onPasswordChange: (next) => (state.password = next),\n onSessionKeyChange: (next) => {\n state.sessionKey = next;\n state.chatMessage = \"\";\n state.resetToolStream();\n state.applySettings({\n ...state.settings,\n sessionKey: next,\n lastActiveSessionKey: next,\n });\n void state.loadAssistantIdentity();\n },\n onConnect: () => state.connect(),\n onRefresh: () => state.loadOverview(),\n })\n : nothing}\n\n ${state.tab === \"channels\"\n ? renderChannels({\n connected: state.connected,\n loading: state.channelsLoading,\n snapshot: state.channelsSnapshot,\n lastError: state.channelsError,\n lastSuccessAt: state.channelsLastSuccess,\n whatsappMessage: state.whatsappLoginMessage,\n whatsappQrDataUrl: state.whatsappLoginQrDataUrl,\n whatsappConnected: state.whatsappLoginConnected,\n whatsappBusy: state.whatsappBusy,\n configSchema: state.configSchema,\n configSchemaLoading: state.configSchemaLoading,\n configForm: state.configForm,\n configUiHints: state.configUiHints,\n configSaving: state.configSaving,\n configFormDirty: state.configFormDirty,\n nostrProfileFormState: state.nostrProfileFormState,\n nostrProfileAccountId: state.nostrProfileAccountId,\n onRefresh: (probe) => loadChannels(state, probe),\n onWhatsAppStart: (force) => state.handleWhatsAppStart(force),\n onWhatsAppWait: () => state.handleWhatsAppWait(),\n onWhatsAppLogout: () => state.handleWhatsAppLogout(),\n onConfigPatch: (path, value) => updateConfigFormValue(state, path, value),\n onConfigSave: () => state.handleChannelConfigSave(),\n onConfigReload: () => state.handleChannelConfigReload(),\n onNostrProfileEdit: (accountId, profile) =>\n state.handleNostrProfileEdit(accountId, profile),\n onNostrProfileCancel: () => state.handleNostrProfileCancel(),\n onNostrProfileFieldChange: (field, value) =>\n state.handleNostrProfileFieldChange(field, value),\n onNostrProfileSave: () => state.handleNostrProfileSave(),\n onNostrProfileImport: () => state.handleNostrProfileImport(),\n onNostrProfileToggleAdvanced: () => state.handleNostrProfileToggleAdvanced(),\n })\n : nothing}\n\n ${state.tab === \"instances\"\n ? renderInstances({\n loading: state.presenceLoading,\n entries: state.presenceEntries,\n lastError: state.presenceError,\n statusMessage: state.presenceStatus,\n onRefresh: () => loadPresence(state),\n })\n : nothing}\n\n ${state.tab === \"sessions\"\n ? renderSessions({\n loading: state.sessionsLoading,\n result: state.sessionsResult,\n error: state.sessionsError,\n activeMinutes: state.sessionsFilterActive,\n limit: state.sessionsFilterLimit,\n includeGlobal: state.sessionsIncludeGlobal,\n includeUnknown: state.sessionsIncludeUnknown,\n basePath: state.basePath,\n onFiltersChange: (next) => {\n state.sessionsFilterActive = next.activeMinutes;\n state.sessionsFilterLimit = next.limit;\n state.sessionsIncludeGlobal = next.includeGlobal;\n state.sessionsIncludeUnknown = next.includeUnknown;\n\t },\n\t onRefresh: () => loadSessions(state),\n\t onPatch: (key, patch) => patchSession(state, key, patch),\n\t onDelete: (key) => deleteSession(state, key),\n\t })\n\t : nothing}\n\n ${state.tab === \"cron\"\n ? renderCron({\n loading: state.cronLoading,\n status: state.cronStatus,\n jobs: state.cronJobs,\n error: state.cronError,\n busy: state.cronBusy,\n form: state.cronForm,\n channels: state.channelsSnapshot?.channelMeta?.length\n ? state.channelsSnapshot.channelMeta.map((entry) => entry.id)\n : state.channelsSnapshot?.channelOrder ?? [],\n channelLabels: state.channelsSnapshot?.channelLabels ?? {},\n channelMeta: state.channelsSnapshot?.channelMeta ?? [],\n runsJobId: state.cronRunsJobId,\n runs: state.cronRuns,\n onFormChange: (patch) => (state.cronForm = { ...state.cronForm, ...patch }),\n onRefresh: () => state.loadCron(),\n onAdd: () => addCronJob(state),\n onToggle: (job, enabled) => toggleCronJob(state, job, enabled),\n onRun: (job) => runCronJob(state, job),\n onRemove: (job) => removeCronJob(state, job),\n onLoadRuns: (jobId) => loadCronRuns(state, jobId),\n })\n : nothing}\n\n ${state.tab === \"skills\"\n ? renderSkills({\n loading: state.skillsLoading,\n report: state.skillsReport,\n error: state.skillsError,\n filter: state.skillsFilter,\n edits: state.skillEdits,\n messages: state.skillMessages,\n busyKey: state.skillsBusyKey,\n onFilterChange: (next) => (state.skillsFilter = next),\n onRefresh: () => loadSkills(state, { clearMessages: true }),\n onToggle: (key, enabled) => updateSkillEnabled(state, key, enabled),\n onEdit: (key, value) => updateSkillEdit(state, key, value),\n onSaveKey: (key) => saveSkillApiKey(state, key),\n onInstall: (skillKey, name, installId) =>\n installSkill(state, skillKey, name, installId),\n })\n : nothing}\n\n ${state.tab === \"nodes\"\n ? renderNodes({\n loading: state.nodesLoading,\n nodes: state.nodes,\n devicesLoading: state.devicesLoading,\n devicesError: state.devicesError,\n devicesList: state.devicesList,\n configForm: state.configForm ?? (state.configSnapshot?.config as Record | null),\n configLoading: state.configLoading,\n configSaving: state.configSaving,\n configDirty: state.configFormDirty,\n configFormMode: state.configFormMode,\n execApprovalsLoading: state.execApprovalsLoading,\n execApprovalsSaving: state.execApprovalsSaving,\n execApprovalsDirty: state.execApprovalsDirty,\n execApprovalsSnapshot: state.execApprovalsSnapshot,\n execApprovalsForm: state.execApprovalsForm,\n execApprovalsSelectedAgent: state.execApprovalsSelectedAgent,\n execApprovalsTarget: state.execApprovalsTarget,\n execApprovalsTargetNodeId: state.execApprovalsTargetNodeId,\n onRefresh: () => loadNodes(state),\n onDevicesRefresh: () => loadDevices(state),\n onDeviceApprove: (requestId) => approveDevicePairing(state, requestId),\n onDeviceReject: (requestId) => rejectDevicePairing(state, requestId),\n onDeviceRotate: (deviceId, role, scopes) =>\n rotateDeviceToken(state, { deviceId, role, scopes }),\n onDeviceRevoke: (deviceId, role) =>\n revokeDeviceToken(state, { deviceId, role }),\n onLoadConfig: () => loadConfig(state),\n onLoadExecApprovals: () => {\n const target =\n state.execApprovalsTarget === \"node\" && state.execApprovalsTargetNodeId\n ? { kind: \"node\" as const, nodeId: state.execApprovalsTargetNodeId }\n : { kind: \"gateway\" as const };\n return loadExecApprovals(state, target);\n },\n onBindDefault: (nodeId) => {\n if (nodeId) {\n updateConfigFormValue(state, [\"tools\", \"exec\", \"node\"], nodeId);\n } else {\n removeConfigFormValue(state, [\"tools\", \"exec\", \"node\"]);\n }\n },\n onBindAgent: (agentIndex, nodeId) => {\n const basePath = [\"agents\", \"list\", agentIndex, \"tools\", \"exec\", \"node\"];\n if (nodeId) {\n updateConfigFormValue(state, basePath, nodeId);\n } else {\n removeConfigFormValue(state, basePath);\n }\n },\n onSaveBindings: () => saveConfig(state),\n onExecApprovalsTargetChange: (kind, nodeId) => {\n state.execApprovalsTarget = kind;\n state.execApprovalsTargetNodeId = nodeId;\n state.execApprovalsSnapshot = null;\n state.execApprovalsForm = null;\n state.execApprovalsDirty = false;\n state.execApprovalsSelectedAgent = null;\n },\n onExecApprovalsSelectAgent: (agentId) => {\n state.execApprovalsSelectedAgent = agentId;\n },\n onExecApprovalsPatch: (path, value) =>\n updateExecApprovalsFormValue(state, path, value),\n onExecApprovalsRemove: (path) =>\n removeExecApprovalsFormValue(state, path),\n onSaveExecApprovals: () => {\n const target =\n state.execApprovalsTarget === \"node\" && state.execApprovalsTargetNodeId\n ? { kind: \"node\" as const, nodeId: state.execApprovalsTargetNodeId }\n : { kind: \"gateway\" as const };\n return saveExecApprovals(state, target);\n },\n })\n : nothing}\n\n ${state.tab === \"chat\"\n ? renderChat({\n sessionKey: state.sessionKey,\n onSessionKeyChange: (next) => {\n state.sessionKey = next;\n state.chatMessage = \"\";\n state.chatStream = null;\n state.chatStreamStartedAt = null;\n state.chatRunId = null;\n state.chatQueue = [];\n state.resetToolStream();\n state.resetChatScroll();\n state.applySettings({\n ...state.settings,\n sessionKey: next,\n lastActiveSessionKey: next,\n });\n void state.loadAssistantIdentity();\n void loadChatHistory(state);\n void refreshChatAvatar(state);\n },\n thinkingLevel: state.chatThinkingLevel,\n showThinking,\n loading: state.chatLoading,\n sending: state.chatSending,\n compactionStatus: state.compactionStatus,\n assistantAvatarUrl: chatAvatarUrl,\n messages: state.chatMessages,\n toolMessages: state.chatToolMessages,\n stream: state.chatStream,\n streamStartedAt: state.chatStreamStartedAt,\n draft: state.chatMessage,\n queue: state.chatQueue,\n connected: state.connected,\n canSend: state.connected,\n disabledReason: chatDisabledReason,\n error: state.lastError,\n sessions: state.sessionsResult,\n focusMode: chatFocus,\n onRefresh: () => {\n state.resetToolStream();\n return Promise.all([loadChatHistory(state), refreshChatAvatar(state)]);\n },\n onToggleFocusMode: () => {\n if (state.onboarding) return;\n state.applySettings({\n ...state.settings,\n chatFocusMode: !state.settings.chatFocusMode,\n });\n },\n onChatScroll: (event) => state.handleChatScroll(event),\n onDraftChange: (next) => (state.chatMessage = next),\n onSend: () => state.handleSendChat(),\n canAbort: Boolean(state.chatRunId),\n onAbort: () => void state.handleAbortChat(),\n onQueueRemove: (id) => state.removeQueuedMessage(id),\n onNewSession: () =>\n state.handleSendChat(\"/new\", { restoreDraft: true }),\n // Sidebar props for tool output viewing\n sidebarOpen: state.sidebarOpen,\n sidebarContent: state.sidebarContent,\n sidebarError: state.sidebarError,\n splitRatio: state.splitRatio,\n onOpenSidebar: (content: string) => state.handleOpenSidebar(content),\n onCloseSidebar: () => state.handleCloseSidebar(),\n onSplitRatioChange: (ratio: number) => state.handleSplitRatioChange(ratio),\n assistantName: state.assistantName,\n assistantAvatar: state.assistantAvatar,\n })\n : nothing}\n\n ${state.tab === \"config\"\n ? renderConfig({\n raw: state.configRaw,\n originalRaw: state.configRawOriginal,\n valid: state.configValid,\n issues: state.configIssues,\n loading: state.configLoading,\n saving: state.configSaving,\n applying: state.configApplying,\n updating: state.updateRunning,\n connected: state.connected,\n schema: state.configSchema,\n schemaLoading: state.configSchemaLoading,\n uiHints: state.configUiHints,\n formMode: state.configFormMode,\n formValue: state.configForm,\n originalValue: state.configFormOriginal,\n searchQuery: state.configSearchQuery,\n activeSection: state.configActiveSection,\n activeSubsection: state.configActiveSubsection,\n onRawChange: (next) => {\n state.configRaw = next;\n },\n onFormModeChange: (mode) => (state.configFormMode = mode),\n onFormPatch: (path, value) => updateConfigFormValue(state, path, value),\n onSearchChange: (query) => (state.configSearchQuery = query),\n onSectionChange: (section) => {\n state.configActiveSection = section;\n state.configActiveSubsection = null;\n },\n onSubsectionChange: (section) => (state.configActiveSubsection = section),\n onReload: () => loadConfig(state),\n onSave: () => saveConfig(state),\n onApply: () => applyConfig(state),\n onUpdate: () => runUpdate(state),\n })\n : nothing}\n\n ${state.tab === \"debug\"\n ? renderDebug({\n loading: state.debugLoading,\n status: state.debugStatus,\n health: state.debugHealth,\n models: state.debugModels,\n heartbeat: state.debugHeartbeat,\n eventLog: state.eventLog,\n callMethod: state.debugCallMethod,\n callParams: state.debugCallParams,\n callResult: state.debugCallResult,\n callError: state.debugCallError,\n onCallMethodChange: (next) => (state.debugCallMethod = next),\n onCallParamsChange: (next) => (state.debugCallParams = next),\n onRefresh: () => loadDebug(state),\n onCall: () => callDebugMethod(state),\n })\n : nothing}\n\n ${state.tab === \"logs\"\n ? renderLogs({\n loading: state.logsLoading,\n error: state.logsError,\n file: state.logsFile,\n entries: state.logsEntries,\n filterText: state.logsFilterText,\n levelFilters: state.logsLevelFilters,\n autoFollow: state.logsAutoFollow,\n truncated: state.logsTruncated,\n onFilterTextChange: (next) => (state.logsFilterText = next),\n onLevelToggle: (level, enabled) => {\n state.logsLevelFilters = { ...state.logsLevelFilters, [level]: enabled };\n },\n onToggleAutoFollow: (next) => (state.logsAutoFollow = next),\n onRefresh: () => loadLogs(state, { reset: true }),\n onExport: (lines, label) => state.exportLogs(lines, label),\n onScroll: (event) => state.handleLogsScroll(event),\n })\n : nothing}\n
    \n ${renderExecApprovalPrompt(state)}\n
    \n `;\n}\n","import type { LogLevel } from \"./types\";\nimport type { CronFormState } from \"./ui-types\";\n\nexport const DEFAULT_LOG_LEVEL_FILTERS: Record = {\n trace: true,\n debug: true,\n info: true,\n warn: true,\n error: true,\n fatal: true,\n};\n\nexport const DEFAULT_CRON_FORM: CronFormState = {\n name: \"\",\n description: \"\",\n agentId: \"\",\n enabled: true,\n scheduleKind: \"every\",\n scheduleAt: \"\",\n everyAmount: \"30\",\n everyUnit: \"minutes\",\n cronExpr: \"0 7 * * *\",\n cronTz: \"\",\n sessionTarget: \"main\",\n wakeMode: \"next-heartbeat\",\n payloadKind: \"systemEvent\",\n payloadText: \"\",\n deliver: false,\n channel: \"last\",\n to: \"\",\n timeoutSeconds: \"\",\n postToMainPrefix: \"\",\n};\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { AgentsListResult } from \"../types\";\n\nexport type AgentsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n agentsLoading: boolean;\n agentsError: string | null;\n agentsList: AgentsListResult | null;\n};\n\nexport async function loadAgents(state: AgentsState) {\n if (!state.client || !state.connected) return;\n if (state.agentsLoading) return;\n state.agentsLoading = true;\n state.agentsError = null;\n try {\n const res = (await state.client.request(\"agents.list\", {})) as AgentsListResult | undefined;\n if (res) state.agentsList = res;\n } catch (err) {\n state.agentsError = String(err);\n } finally {\n state.agentsLoading = false;\n }\n}\n","export const GATEWAY_CLIENT_IDS = {\n WEBCHAT_UI: \"webchat-ui\",\n CONTROL_UI: \"clawdbot-control-ui\",\n WEBCHAT: \"webchat\",\n CLI: \"cli\",\n GATEWAY_CLIENT: \"gateway-client\",\n MACOS_APP: \"clawdbot-macos\",\n IOS_APP: \"clawdbot-ios\",\n ANDROID_APP: \"clawdbot-android\",\n NODE_HOST: \"node-host\",\n TEST: \"test\",\n FINGERPRINT: \"fingerprint\",\n PROBE: \"clawdbot-probe\",\n} as const;\n\nexport type GatewayClientId = (typeof GATEWAY_CLIENT_IDS)[keyof typeof GATEWAY_CLIENT_IDS];\n\n// Back-compat naming (internal): these values are IDs, not display names.\nexport const GATEWAY_CLIENT_NAMES = GATEWAY_CLIENT_IDS;\nexport type GatewayClientName = GatewayClientId;\n\nexport const GATEWAY_CLIENT_MODES = {\n WEBCHAT: \"webchat\",\n CLI: \"cli\",\n UI: \"ui\",\n BACKEND: \"backend\",\n NODE: \"node\",\n PROBE: \"probe\",\n TEST: \"test\",\n} as const;\n\nexport type GatewayClientMode = (typeof GATEWAY_CLIENT_MODES)[keyof typeof GATEWAY_CLIENT_MODES];\n\nexport type GatewayClientInfo = {\n id: GatewayClientId;\n displayName?: string;\n version: string;\n platform: string;\n deviceFamily?: string;\n modelIdentifier?: string;\n mode: GatewayClientMode;\n instanceId?: string;\n};\n\nconst GATEWAY_CLIENT_ID_SET = new Set(Object.values(GATEWAY_CLIENT_IDS));\nconst GATEWAY_CLIENT_MODE_SET = new Set(Object.values(GATEWAY_CLIENT_MODES));\n\nexport function normalizeGatewayClientId(raw?: string | null): GatewayClientId | undefined {\n const normalized = raw?.trim().toLowerCase();\n if (!normalized) return undefined;\n return GATEWAY_CLIENT_ID_SET.has(normalized as GatewayClientId)\n ? (normalized as GatewayClientId)\n : undefined;\n}\n\nexport function normalizeGatewayClientName(raw?: string | null): GatewayClientName | undefined {\n return normalizeGatewayClientId(raw);\n}\n\nexport function normalizeGatewayClientMode(raw?: string | null): GatewayClientMode | undefined {\n const normalized = raw?.trim().toLowerCase();\n if (!normalized) return undefined;\n return GATEWAY_CLIENT_MODE_SET.has(normalized as GatewayClientMode)\n ? (normalized as GatewayClientMode)\n : undefined;\n}\n","export type DeviceAuthPayloadParams = {\n deviceId: string;\n clientId: string;\n clientMode: string;\n role: string;\n scopes: string[];\n signedAtMs: number;\n token?: string | null;\n nonce?: string | null;\n version?: \"v1\" | \"v2\";\n};\n\nexport function buildDeviceAuthPayload(params: DeviceAuthPayloadParams): string {\n const version = params.version ?? (params.nonce ? \"v2\" : \"v1\");\n const scopes = params.scopes.join(\",\");\n const token = params.token ?? \"\";\n const base = [\n version,\n params.deviceId,\n params.clientId,\n params.clientMode,\n params.role,\n scopes,\n String(params.signedAtMs),\n token,\n ];\n if (version === \"v2\") {\n base.push(params.nonce ?? \"\");\n }\n return base.join(\"|\");\n}\n","import { generateUUID } from \"./uuid\";\nimport {\n GATEWAY_CLIENT_MODES,\n GATEWAY_CLIENT_NAMES,\n type GatewayClientMode,\n type GatewayClientName,\n} from \"../../../src/gateway/protocol/client-info.js\";\nimport { buildDeviceAuthPayload } from \"../../../src/gateway/device-auth.js\";\nimport { loadOrCreateDeviceIdentity, signDevicePayload } from \"./device-identity\";\nimport { clearDeviceAuthToken, loadDeviceAuthToken, storeDeviceAuthToken } from \"./device-auth\";\n\nexport type GatewayEventFrame = {\n type: \"event\";\n event: string;\n payload?: unknown;\n seq?: number;\n stateVersion?: { presence: number; health: number };\n};\n\nexport type GatewayResponseFrame = {\n type: \"res\";\n id: string;\n ok: boolean;\n payload?: unknown;\n error?: { code: string; message: string; details?: unknown };\n};\n\nexport type GatewayHelloOk = {\n type: \"hello-ok\";\n protocol: number;\n features?: { methods?: string[]; events?: string[] };\n snapshot?: unknown;\n auth?: {\n deviceToken?: string;\n role?: string;\n scopes?: string[];\n issuedAtMs?: number;\n };\n policy?: { tickIntervalMs?: number };\n};\n\ntype Pending = {\n resolve: (value: unknown) => void;\n reject: (err: unknown) => void;\n};\n\nexport type GatewayBrowserClientOptions = {\n url: string;\n token?: string;\n password?: string;\n clientName?: GatewayClientName;\n clientVersion?: string;\n platform?: string;\n mode?: GatewayClientMode;\n instanceId?: string;\n onHello?: (hello: GatewayHelloOk) => void;\n onEvent?: (evt: GatewayEventFrame) => void;\n onClose?: (info: { code: number; reason: string }) => void;\n onGap?: (info: { expected: number; received: number }) => void;\n};\n\n// 4008 = application-defined code (browser rejects 1008 \"Policy Violation\")\nconst CONNECT_FAILED_CLOSE_CODE = 4008;\n\nexport class GatewayBrowserClient {\n private ws: WebSocket | null = null;\n private pending = new Map();\n private closed = false;\n private lastSeq: number | null = null;\n private connectNonce: string | null = null;\n private connectSent = false;\n private connectTimer: number | null = null;\n private backoffMs = 800;\n\n constructor(private opts: GatewayBrowserClientOptions) {}\n\n start() {\n this.closed = false;\n this.connect();\n }\n\n stop() {\n this.closed = true;\n this.ws?.close();\n this.ws = null;\n this.flushPending(new Error(\"gateway client stopped\"));\n }\n\n get connected() {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n private connect() {\n if (this.closed) return;\n this.ws = new WebSocket(this.opts.url);\n this.ws.onopen = () => this.queueConnect();\n this.ws.onmessage = (ev) => this.handleMessage(String(ev.data ?? \"\"));\n this.ws.onclose = (ev) => {\n const reason = String(ev.reason ?? \"\");\n this.ws = null;\n this.flushPending(new Error(`gateway closed (${ev.code}): ${reason}`));\n this.opts.onClose?.({ code: ev.code, reason });\n this.scheduleReconnect();\n };\n this.ws.onerror = () => {\n // ignored; close handler will fire\n };\n }\n\n private scheduleReconnect() {\n if (this.closed) return;\n const delay = this.backoffMs;\n this.backoffMs = Math.min(this.backoffMs * 1.7, 15_000);\n window.setTimeout(() => this.connect(), delay);\n }\n\n private flushPending(err: Error) {\n for (const [, p] of this.pending) p.reject(err);\n this.pending.clear();\n }\n\n private async sendConnect() {\n if (this.connectSent) return;\n this.connectSent = true;\n if (this.connectTimer !== null) {\n window.clearTimeout(this.connectTimer);\n this.connectTimer = null;\n }\n\n // crypto.subtle is only available in secure contexts (HTTPS, localhost).\n // Over plain HTTP, we skip device identity and fall back to token-only auth.\n // Gateways may reject this unless gateway.controlUi.allowInsecureAuth is enabled.\n const isSecureContext = typeof crypto !== \"undefined\" && !!crypto.subtle;\n\n const scopes = [\"operator.admin\", \"operator.approvals\", \"operator.pairing\"];\n const role = \"operator\";\n let deviceIdentity: Awaited> | null = null;\n let canFallbackToShared = false;\n let authToken = this.opts.token;\n\n if (isSecureContext) {\n deviceIdentity = await loadOrCreateDeviceIdentity();\n const storedToken = loadDeviceAuthToken({\n deviceId: deviceIdentity.deviceId,\n role,\n })?.token;\n authToken = storedToken ?? this.opts.token;\n canFallbackToShared = Boolean(storedToken && this.opts.token);\n }\n const auth =\n authToken || this.opts.password\n ? {\n token: authToken,\n password: this.opts.password,\n }\n : undefined;\n\n let device:\n | {\n id: string;\n publicKey: string;\n signature: string;\n signedAt: number;\n nonce: string | undefined;\n }\n | undefined;\n\n if (isSecureContext && deviceIdentity) {\n const signedAtMs = Date.now();\n const nonce = this.connectNonce ?? undefined;\n const payload = buildDeviceAuthPayload({\n deviceId: deviceIdentity.deviceId,\n clientId: this.opts.clientName ?? GATEWAY_CLIENT_NAMES.CONTROL_UI,\n clientMode: this.opts.mode ?? GATEWAY_CLIENT_MODES.WEBCHAT,\n role,\n scopes,\n signedAtMs,\n token: authToken ?? null,\n nonce,\n });\n const signature = await signDevicePayload(deviceIdentity.privateKey, payload);\n device = {\n id: deviceIdentity.deviceId,\n publicKey: deviceIdentity.publicKey,\n signature,\n signedAt: signedAtMs,\n nonce,\n };\n }\n const params = {\n minProtocol: 3,\n maxProtocol: 3,\n client: {\n id: this.opts.clientName ?? GATEWAY_CLIENT_NAMES.CONTROL_UI,\n version: this.opts.clientVersion ?? \"dev\",\n platform: this.opts.platform ?? navigator.platform ?? \"web\",\n mode: this.opts.mode ?? GATEWAY_CLIENT_MODES.WEBCHAT,\n instanceId: this.opts.instanceId,\n },\n role,\n scopes,\n device,\n caps: [],\n auth,\n userAgent: navigator.userAgent,\n locale: navigator.language,\n };\n\n void this.request(\"connect\", params)\n .then((hello) => {\n if (hello?.auth?.deviceToken && deviceIdentity) {\n storeDeviceAuthToken({\n deviceId: deviceIdentity.deviceId,\n role: hello.auth.role ?? role,\n token: hello.auth.deviceToken,\n scopes: hello.auth.scopes ?? [],\n });\n }\n this.backoffMs = 800;\n this.opts.onHello?.(hello);\n })\n .catch(() => {\n if (canFallbackToShared && deviceIdentity) {\n clearDeviceAuthToken({ deviceId: deviceIdentity.deviceId, role });\n }\n this.ws?.close(CONNECT_FAILED_CLOSE_CODE, \"connect failed\");\n });\n }\n\n private handleMessage(raw: string) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return;\n }\n\n const frame = parsed as { type?: unknown };\n if (frame.type === \"event\") {\n const evt = parsed as GatewayEventFrame;\n if (evt.event === \"connect.challenge\") {\n const payload = evt.payload as { nonce?: unknown } | undefined;\n const nonce = payload && typeof payload.nonce === \"string\" ? payload.nonce : null;\n if (nonce) {\n this.connectNonce = nonce;\n void this.sendConnect();\n }\n return;\n }\n const seq = typeof evt.seq === \"number\" ? evt.seq : null;\n if (seq !== null) {\n if (this.lastSeq !== null && seq > this.lastSeq + 1) {\n this.opts.onGap?.({ expected: this.lastSeq + 1, received: seq });\n }\n this.lastSeq = seq;\n }\n try {\n this.opts.onEvent?.(evt);\n } catch (err) {\n console.error(\"[gateway] event handler error:\", err);\n }\n return;\n }\n\n if (frame.type === \"res\") {\n const res = parsed as GatewayResponseFrame;\n const pending = this.pending.get(res.id);\n if (!pending) return;\n this.pending.delete(res.id);\n if (res.ok) pending.resolve(res.payload);\n else pending.reject(new Error(res.error?.message ?? \"request failed\"));\n return;\n }\n }\n\n request(method: string, params?: unknown): Promise {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return Promise.reject(new Error(\"gateway not connected\"));\n }\n const id = generateUUID();\n const frame = { type: \"req\", id, method, params };\n const p = new Promise((resolve, reject) => {\n this.pending.set(id, { resolve: (v) => resolve(v as T), reject });\n });\n this.ws.send(JSON.stringify(frame));\n return p;\n }\n\n private queueConnect() {\n this.connectNonce = null;\n this.connectSent = false;\n if (this.connectTimer !== null) window.clearTimeout(this.connectTimer);\n this.connectTimer = window.setTimeout(() => {\n void this.sendConnect();\n }, 750);\n }\n}\n","export type ExecApprovalRequestPayload = {\n command: string;\n cwd?: string | null;\n host?: string | null;\n security?: string | null;\n ask?: string | null;\n agentId?: string | null;\n resolvedPath?: string | null;\n sessionKey?: string | null;\n};\n\nexport type ExecApprovalRequest = {\n id: string;\n request: ExecApprovalRequestPayload;\n createdAtMs: number;\n expiresAtMs: number;\n};\n\nexport type ExecApprovalResolved = {\n id: string;\n decision?: string | null;\n resolvedBy?: string | null;\n ts?: number | null;\n};\n\nfunction isRecord(value: unknown): value is Record {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function parseExecApprovalRequested(payload: unknown): ExecApprovalRequest | null {\n if (!isRecord(payload)) return null;\n const id = typeof payload.id === \"string\" ? payload.id.trim() : \"\";\n const request = payload.request;\n if (!id || !isRecord(request)) return null;\n const command = typeof request.command === \"string\" ? request.command.trim() : \"\";\n if (!command) return null;\n const createdAtMs = typeof payload.createdAtMs === \"number\" ? payload.createdAtMs : 0;\n const expiresAtMs = typeof payload.expiresAtMs === \"number\" ? payload.expiresAtMs : 0;\n if (!createdAtMs || !expiresAtMs) return null;\n return {\n id,\n request: {\n command,\n cwd: typeof request.cwd === \"string\" ? request.cwd : null,\n host: typeof request.host === \"string\" ? request.host : null,\n security: typeof request.security === \"string\" ? request.security : null,\n ask: typeof request.ask === \"string\" ? request.ask : null,\n agentId: typeof request.agentId === \"string\" ? request.agentId : null,\n resolvedPath: typeof request.resolvedPath === \"string\" ? request.resolvedPath : null,\n sessionKey: typeof request.sessionKey === \"string\" ? request.sessionKey : null,\n },\n createdAtMs,\n expiresAtMs,\n };\n}\n\nexport function parseExecApprovalResolved(payload: unknown): ExecApprovalResolved | null {\n if (!isRecord(payload)) return null;\n const id = typeof payload.id === \"string\" ? payload.id.trim() : \"\";\n if (!id) return null;\n return {\n id,\n decision: typeof payload.decision === \"string\" ? payload.decision : null,\n resolvedBy: typeof payload.resolvedBy === \"string\" ? payload.resolvedBy : null,\n ts: typeof payload.ts === \"number\" ? payload.ts : null,\n };\n}\n\nexport function pruneExecApprovalQueue(queue: ExecApprovalRequest[]): ExecApprovalRequest[] {\n const now = Date.now();\n return queue.filter((entry) => entry.expiresAtMs > now);\n}\n\nexport function addExecApproval(\n queue: ExecApprovalRequest[],\n entry: ExecApprovalRequest,\n): ExecApprovalRequest[] {\n const next = pruneExecApprovalQueue(queue).filter((item) => item.id !== entry.id);\n next.push(entry);\n return next;\n}\n\nexport function removeExecApproval(queue: ExecApprovalRequest[], id: string): ExecApprovalRequest[] {\n return pruneExecApprovalQueue(queue).filter((entry) => entry.id !== id);\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport {\n normalizeAssistantIdentity,\n type AssistantIdentity,\n} from \"../assistant-identity\";\n\nexport type AssistantIdentityState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n sessionKey: string;\n assistantName: string;\n assistantAvatar: string | null;\n assistantAgentId: string | null;\n};\n\nexport async function loadAssistantIdentity(\n state: AssistantIdentityState,\n opts?: { sessionKey?: string },\n) {\n if (!state.client || !state.connected) return;\n const sessionKey = opts?.sessionKey?.trim() || state.sessionKey.trim();\n const params = sessionKey ? { sessionKey } : {};\n try {\n const res = (await state.client.request(\"agent.identity.get\", params)) as\n | Partial\n | undefined;\n if (!res) return;\n const normalized = normalizeAssistantIdentity(res);\n state.assistantName = normalized.name;\n state.assistantAvatar = normalized.avatar;\n state.assistantAgentId = normalized.agentId ?? null;\n } catch {\n // Ignore errors; keep last known identity.\n }\n}\n","import { loadChatHistory } from \"./controllers/chat\";\nimport { loadDevices } from \"./controllers/devices\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadAgents } from \"./controllers/agents\";\nimport type { GatewayEventFrame, GatewayHelloOk } from \"./gateway\";\nimport { GatewayBrowserClient } from \"./gateway\";\nimport type { EventLogEntry } from \"./app-events\";\nimport type { AgentsListResult, PresenceEntry, HealthSnapshot, StatusSummary } from \"./types\";\nimport type { Tab } from \"./navigation\";\nimport type { UiSettings } from \"./storage\";\nimport { handleAgentEvent, resetToolStream, type AgentEventPayload } from \"./app-tool-stream\";\nimport { flushChatQueueForEvent } from \"./app-chat\";\nimport {\n applySettings,\n loadCron,\n refreshActiveTab,\n setLastActiveSessionKey,\n} from \"./app-settings\";\nimport { handleChatEvent, type ChatEventPayload } from \"./controllers/chat\";\nimport {\n addExecApproval,\n parseExecApprovalRequested,\n parseExecApprovalResolved,\n removeExecApproval,\n} from \"./controllers/exec-approval\";\nimport type { ClawdbotApp } from \"./app\";\nimport type { ExecApprovalRequest } from \"./controllers/exec-approval\";\nimport { loadAssistantIdentity } from \"./controllers/assistant-identity\";\n\ntype GatewayHost = {\n settings: UiSettings;\n password: string;\n client: GatewayBrowserClient | null;\n connected: boolean;\n hello: GatewayHelloOk | null;\n lastError: string | null;\n onboarding?: boolean;\n eventLogBuffer: EventLogEntry[];\n eventLog: EventLogEntry[];\n tab: Tab;\n presenceEntries: PresenceEntry[];\n presenceError: string | null;\n presenceStatus: StatusSummary | null;\n agentsLoading: boolean;\n agentsList: AgentsListResult | null;\n agentsError: string | null;\n debugHealth: HealthSnapshot | null;\n assistantName: string;\n assistantAvatar: string | null;\n assistantAgentId: string | null;\n sessionKey: string;\n chatRunId: string | null;\n execApprovalQueue: ExecApprovalRequest[];\n execApprovalError: string | null;\n};\n\ntype SessionDefaultsSnapshot = {\n defaultAgentId?: string;\n mainKey?: string;\n mainSessionKey?: string;\n scope?: string;\n};\n\nfunction normalizeSessionKeyForDefaults(\n value: string | undefined,\n defaults: SessionDefaultsSnapshot,\n): string {\n const raw = (value ?? \"\").trim();\n const mainSessionKey = defaults.mainSessionKey?.trim();\n if (!mainSessionKey) return raw;\n if (!raw) return mainSessionKey;\n const mainKey = defaults.mainKey?.trim() || \"main\";\n const defaultAgentId = defaults.defaultAgentId?.trim();\n const isAlias =\n raw === \"main\" ||\n raw === mainKey ||\n (defaultAgentId &&\n (raw === `agent:${defaultAgentId}:main` ||\n raw === `agent:${defaultAgentId}:${mainKey}`));\n return isAlias ? mainSessionKey : raw;\n}\n\nfunction applySessionDefaults(host: GatewayHost, defaults?: SessionDefaultsSnapshot) {\n if (!defaults?.mainSessionKey) return;\n const resolvedSessionKey = normalizeSessionKeyForDefaults(host.sessionKey, defaults);\n const resolvedSettingsSessionKey = normalizeSessionKeyForDefaults(\n host.settings.sessionKey,\n defaults,\n );\n const resolvedLastActiveSessionKey = normalizeSessionKeyForDefaults(\n host.settings.lastActiveSessionKey,\n defaults,\n );\n const nextSessionKey = resolvedSessionKey || resolvedSettingsSessionKey || host.sessionKey;\n const nextSettings = {\n ...host.settings,\n sessionKey: resolvedSettingsSessionKey || nextSessionKey,\n lastActiveSessionKey: resolvedLastActiveSessionKey || nextSessionKey,\n };\n const shouldUpdateSettings =\n nextSettings.sessionKey !== host.settings.sessionKey ||\n nextSettings.lastActiveSessionKey !== host.settings.lastActiveSessionKey;\n if (nextSessionKey !== host.sessionKey) {\n host.sessionKey = nextSessionKey;\n }\n if (shouldUpdateSettings) {\n applySettings(host as unknown as Parameters[0], nextSettings);\n }\n}\n\nexport function connectGateway(host: GatewayHost) {\n host.lastError = null;\n host.hello = null;\n host.connected = false;\n host.execApprovalQueue = [];\n host.execApprovalError = null;\n\n host.client?.stop();\n host.client = new GatewayBrowserClient({\n url: host.settings.gatewayUrl,\n token: host.settings.token.trim() ? host.settings.token : undefined,\n password: host.password.trim() ? host.password : undefined,\n clientName: \"clawdbot-control-ui\",\n mode: \"webchat\",\n onHello: (hello) => {\n host.connected = true;\n host.lastError = null;\n host.hello = hello;\n applySnapshot(host, hello);\n void loadAssistantIdentity(host as unknown as ClawdbotApp);\n void loadAgents(host as unknown as ClawdbotApp);\n void loadNodes(host as unknown as ClawdbotApp, { quiet: true });\n void loadDevices(host as unknown as ClawdbotApp, { quiet: true });\n void refreshActiveTab(host as unknown as Parameters[0]);\n },\n onClose: ({ code, reason }) => {\n host.connected = false;\n // Code 1012 = Service Restart (expected during config saves, don't show as error)\n if (code !== 1012) {\n host.lastError = `disconnected (${code}): ${reason || \"no reason\"}`;\n }\n },\n onEvent: (evt) => handleGatewayEvent(host, evt),\n onGap: ({ expected, received }) => {\n host.lastError = `event gap detected (expected seq ${expected}, got ${received}); refresh recommended`;\n },\n });\n host.client.start();\n}\n\nexport function handleGatewayEvent(host: GatewayHost, evt: GatewayEventFrame) {\n try {\n handleGatewayEventUnsafe(host, evt);\n } catch (err) {\n console.error(\"[gateway] handleGatewayEvent error:\", evt.event, err);\n }\n}\n\nfunction handleGatewayEventUnsafe(host: GatewayHost, evt: GatewayEventFrame) {\n host.eventLogBuffer = [\n { ts: Date.now(), event: evt.event, payload: evt.payload },\n ...host.eventLogBuffer,\n ].slice(0, 250);\n if (host.tab === \"debug\") {\n host.eventLog = host.eventLogBuffer;\n }\n\n if (evt.event === \"agent\") {\n if (host.onboarding) return;\n handleAgentEvent(\n host as unknown as Parameters[0],\n evt.payload as AgentEventPayload | undefined,\n );\n return;\n }\n\n if (evt.event === \"chat\") {\n const payload = evt.payload as ChatEventPayload | undefined;\n if (payload?.sessionKey) {\n setLastActiveSessionKey(\n host as unknown as Parameters[0],\n payload.sessionKey,\n );\n }\n const state = handleChatEvent(host as unknown as ClawdbotApp, payload);\n if (state === \"final\" || state === \"error\" || state === \"aborted\") {\n resetToolStream(host as unknown as Parameters[0]);\n void flushChatQueueForEvent(\n host as unknown as Parameters[0],\n );\n }\n if (state === \"final\") void loadChatHistory(host as unknown as ClawdbotApp);\n return;\n }\n\n if (evt.event === \"presence\") {\n const payload = evt.payload as { presence?: PresenceEntry[] } | undefined;\n if (payload?.presence && Array.isArray(payload.presence)) {\n host.presenceEntries = payload.presence;\n host.presenceError = null;\n host.presenceStatus = null;\n }\n return;\n }\n\n if (evt.event === \"cron\" && host.tab === \"cron\") {\n void loadCron(host as unknown as Parameters[0]);\n }\n\n if (evt.event === \"device.pair.requested\" || evt.event === \"device.pair.resolved\") {\n void loadDevices(host as unknown as ClawdbotApp, { quiet: true });\n }\n\n if (evt.event === \"exec.approval.requested\") {\n const entry = parseExecApprovalRequested(evt.payload);\n if (entry) {\n host.execApprovalQueue = addExecApproval(host.execApprovalQueue, entry);\n host.execApprovalError = null;\n const delay = Math.max(0, entry.expiresAtMs - Date.now() + 500);\n window.setTimeout(() => {\n host.execApprovalQueue = removeExecApproval(host.execApprovalQueue, entry.id);\n }, delay);\n }\n return;\n }\n\n if (evt.event === \"exec.approval.resolved\") {\n const resolved = parseExecApprovalResolved(evt.payload);\n if (resolved) {\n host.execApprovalQueue = removeExecApproval(host.execApprovalQueue, resolved.id);\n }\n }\n}\n\nexport function applySnapshot(host: GatewayHost, hello: GatewayHelloOk) {\n const snapshot = hello.snapshot as\n | {\n presence?: PresenceEntry[];\n health?: HealthSnapshot;\n sessionDefaults?: SessionDefaultsSnapshot;\n }\n | undefined;\n if (snapshot?.presence && Array.isArray(snapshot.presence)) {\n host.presenceEntries = snapshot.presence;\n }\n if (snapshot?.health) {\n host.debugHealth = snapshot.health;\n }\n if (snapshot?.sessionDefaults) {\n applySessionDefaults(host, snapshot.sessionDefaults);\n }\n}\n","import type { Tab } from \"./navigation\";\nimport { connectGateway } from \"./app-gateway\";\nimport {\n applySettingsFromUrl,\n attachThemeListener,\n detachThemeListener,\n inferBasePath,\n syncTabWithLocation,\n syncThemeWithSettings,\n} from \"./app-settings\";\nimport { observeTopbar, scheduleChatScroll, scheduleLogsScroll } from \"./app-scroll\";\nimport {\n startLogsPolling,\n startNodesPolling,\n stopLogsPolling,\n stopNodesPolling,\n startDebugPolling,\n stopDebugPolling,\n} from \"./app-polling\";\n\ntype LifecycleHost = {\n basePath: string;\n tab: Tab;\n chatHasAutoScrolled: boolean;\n chatLoading: boolean;\n chatMessages: unknown[];\n chatToolMessages: unknown[];\n chatStream: string;\n logsAutoFollow: boolean;\n logsAtBottom: boolean;\n logsEntries: unknown[];\n popStateHandler: () => void;\n topbarObserver: ResizeObserver | null;\n};\n\nexport function handleConnected(host: LifecycleHost) {\n host.basePath = inferBasePath();\n syncTabWithLocation(\n host as unknown as Parameters[0],\n true,\n );\n syncThemeWithSettings(\n host as unknown as Parameters[0],\n );\n attachThemeListener(\n host as unknown as Parameters[0],\n );\n window.addEventListener(\"popstate\", host.popStateHandler);\n applySettingsFromUrl(\n host as unknown as Parameters[0],\n );\n connectGateway(host as unknown as Parameters[0]);\n startNodesPolling(host as unknown as Parameters[0]);\n if (host.tab === \"logs\") {\n startLogsPolling(host as unknown as Parameters[0]);\n }\n if (host.tab === \"debug\") {\n startDebugPolling(host as unknown as Parameters[0]);\n }\n}\n\nexport function handleFirstUpdated(host: LifecycleHost) {\n observeTopbar(host as unknown as Parameters[0]);\n}\n\nexport function handleDisconnected(host: LifecycleHost) {\n window.removeEventListener(\"popstate\", host.popStateHandler);\n stopNodesPolling(host as unknown as Parameters[0]);\n stopLogsPolling(host as unknown as Parameters[0]);\n stopDebugPolling(host as unknown as Parameters[0]);\n detachThemeListener(\n host as unknown as Parameters[0],\n );\n host.topbarObserver?.disconnect();\n host.topbarObserver = null;\n}\n\nexport function handleUpdated(\n host: LifecycleHost,\n changed: Map,\n) {\n if (\n host.tab === \"chat\" &&\n (changed.has(\"chatMessages\") ||\n changed.has(\"chatToolMessages\") ||\n changed.has(\"chatStream\") ||\n changed.has(\"chatLoading\") ||\n changed.has(\"tab\"))\n ) {\n const forcedByTab = changed.has(\"tab\");\n const forcedByLoad =\n changed.has(\"chatLoading\") &&\n changed.get(\"chatLoading\") === true &&\n host.chatLoading === false;\n scheduleChatScroll(\n host as unknown as Parameters[0],\n forcedByTab || forcedByLoad || !host.chatHasAutoScrolled,\n );\n }\n if (\n host.tab === \"logs\" &&\n (changed.has(\"logsEntries\") || changed.has(\"logsAutoFollow\") || changed.has(\"tab\"))\n ) {\n if (host.logsAutoFollow && host.logsAtBottom) {\n scheduleLogsScroll(\n host as unknown as Parameters[0],\n changed.has(\"tab\") || changed.has(\"logsAutoFollow\"),\n );\n }\n }\n}\n","import {\n loadChannels,\n logoutWhatsApp,\n startWhatsAppLogin,\n waitWhatsAppLogin,\n} from \"./controllers/channels\";\nimport { loadConfig, saveConfig } from \"./controllers/config\";\nimport type { ClawdbotApp } from \"./app\";\nimport type { NostrProfile } from \"./types\";\nimport { createNostrProfileFormState } from \"./views/channels.nostr-profile-form\";\n\nexport async function handleWhatsAppStart(host: ClawdbotApp, force: boolean) {\n await startWhatsAppLogin(host, force);\n await loadChannels(host, true);\n}\n\nexport async function handleWhatsAppWait(host: ClawdbotApp) {\n await waitWhatsAppLogin(host);\n await loadChannels(host, true);\n}\n\nexport async function handleWhatsAppLogout(host: ClawdbotApp) {\n await logoutWhatsApp(host);\n await loadChannels(host, true);\n}\n\nexport async function handleChannelConfigSave(host: ClawdbotApp) {\n await saveConfig(host);\n await loadConfig(host);\n await loadChannels(host, true);\n}\n\nexport async function handleChannelConfigReload(host: ClawdbotApp) {\n await loadConfig(host);\n await loadChannels(host, true);\n}\n\nfunction parseValidationErrors(details: unknown): Record {\n if (!Array.isArray(details)) return {};\n const errors: Record = {};\n for (const entry of details) {\n if (typeof entry !== \"string\") continue;\n const [rawField, ...rest] = entry.split(\":\");\n if (!rawField || rest.length === 0) continue;\n const field = rawField.trim();\n const message = rest.join(\":\").trim();\n if (field && message) errors[field] = message;\n }\n return errors;\n}\n\nfunction resolveNostrAccountId(host: ClawdbotApp): string {\n const accounts = host.channelsSnapshot?.channelAccounts?.nostr ?? [];\n return accounts[0]?.accountId ?? host.nostrProfileAccountId ?? \"default\";\n}\n\nfunction buildNostrProfileUrl(accountId: string, suffix = \"\"): string {\n return `/api/channels/nostr/${encodeURIComponent(accountId)}/profile${suffix}`;\n}\n\nexport function handleNostrProfileEdit(\n host: ClawdbotApp,\n accountId: string,\n profile: NostrProfile | null,\n) {\n host.nostrProfileAccountId = accountId;\n host.nostrProfileFormState = createNostrProfileFormState(profile ?? undefined);\n}\n\nexport function handleNostrProfileCancel(host: ClawdbotApp) {\n host.nostrProfileFormState = null;\n host.nostrProfileAccountId = null;\n}\n\nexport function handleNostrProfileFieldChange(\n host: ClawdbotApp,\n field: keyof NostrProfile,\n value: string,\n) {\n const state = host.nostrProfileFormState;\n if (!state) return;\n host.nostrProfileFormState = {\n ...state,\n values: {\n ...state.values,\n [field]: value,\n },\n fieldErrors: {\n ...state.fieldErrors,\n [field]: \"\",\n },\n };\n}\n\nexport function handleNostrProfileToggleAdvanced(host: ClawdbotApp) {\n const state = host.nostrProfileFormState;\n if (!state) return;\n host.nostrProfileFormState = {\n ...state,\n showAdvanced: !state.showAdvanced,\n };\n}\n\nexport async function handleNostrProfileSave(host: ClawdbotApp) {\n const state = host.nostrProfileFormState;\n if (!state || state.saving) return;\n const accountId = resolveNostrAccountId(host);\n\n host.nostrProfileFormState = {\n ...state,\n saving: true,\n error: null,\n success: null,\n fieldErrors: {},\n };\n\n try {\n const response = await fetch(buildNostrProfileUrl(accountId), {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(state.values),\n });\n const data = (await response.json().catch(() => null)) as\n | { ok?: boolean; error?: string; details?: unknown; persisted?: boolean }\n | null;\n\n if (!response.ok || data?.ok === false || !data) {\n const errorMessage = data?.error ?? `Profile update failed (${response.status})`;\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: errorMessage,\n success: null,\n fieldErrors: parseValidationErrors(data?.details),\n };\n return;\n }\n\n if (!data.persisted) {\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: \"Profile publish failed on all relays.\",\n success: null,\n };\n return;\n }\n\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: null,\n success: \"Profile published to relays.\",\n fieldErrors: {},\n original: { ...state.values },\n };\n await loadChannels(host, true);\n } catch (err) {\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: `Profile update failed: ${String(err)}`,\n success: null,\n };\n }\n}\n\nexport async function handleNostrProfileImport(host: ClawdbotApp) {\n const state = host.nostrProfileFormState;\n if (!state || state.importing) return;\n const accountId = resolveNostrAccountId(host);\n\n host.nostrProfileFormState = {\n ...state,\n importing: true,\n error: null,\n success: null,\n };\n\n try {\n const response = await fetch(buildNostrProfileUrl(accountId, \"/import\"), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ autoMerge: true }),\n });\n const data = (await response.json().catch(() => null)) as\n | { ok?: boolean; error?: string; imported?: NostrProfile; merged?: NostrProfile; saved?: boolean }\n | null;\n\n if (!response.ok || data?.ok === false || !data) {\n const errorMessage = data?.error ?? `Profile import failed (${response.status})`;\n host.nostrProfileFormState = {\n ...state,\n importing: false,\n error: errorMessage,\n success: null,\n };\n return;\n }\n\n const merged = data.merged ?? data.imported ?? null;\n const nextValues = merged ? { ...state.values, ...merged } : state.values;\n const showAdvanced = Boolean(\n nextValues.banner || nextValues.website || nextValues.nip05 || nextValues.lud16,\n );\n\n host.nostrProfileFormState = {\n ...state,\n importing: false,\n values: nextValues,\n error: null,\n success: data.saved\n ? \"Profile imported from relays. Review and publish.\"\n : \"Profile imported. Review and publish.\",\n showAdvanced,\n };\n\n if (data.saved) {\n await loadChannels(host, true);\n }\n } catch (err) {\n host.nostrProfileFormState = {\n ...state,\n importing: false,\n error: `Profile import failed: ${String(err)}`,\n success: null,\n };\n }\n}\n","import { LitElement, html, nothing } from \"lit\";\nimport { customElement, state } from \"lit/decorators.js\";\n\nimport type { GatewayBrowserClient, GatewayHelloOk } from \"./gateway\";\nimport { resolveInjectedAssistantIdentity } from \"./assistant-identity\";\nimport { loadSettings, type UiSettings } from \"./storage\";\nimport { renderApp } from \"./app-render\";\nimport type { Tab } from \"./navigation\";\nimport type { ResolvedTheme, ThemeMode } from \"./theme\";\nimport type {\n AgentsListResult,\n ConfigSnapshot,\n ConfigUiHints,\n CronJob,\n CronRunLogEntry,\n CronStatus,\n HealthSnapshot,\n LogEntry,\n LogLevel,\n PresenceEntry,\n ChannelsStatusSnapshot,\n SessionsListResult,\n SkillStatusReport,\n StatusSummary,\n NostrProfile,\n} from \"./types\";\nimport { type ChatQueueItem, type CronFormState } from \"./ui-types\";\nimport type { EventLogEntry } from \"./app-events\";\nimport { DEFAULT_CRON_FORM, DEFAULT_LOG_LEVEL_FILTERS } from \"./app-defaults\";\nimport type {\n ExecApprovalsFile,\n ExecApprovalsSnapshot,\n} from \"./controllers/exec-approvals\";\nimport type { DevicePairingList } from \"./controllers/devices\";\nimport type { ExecApprovalRequest } from \"./controllers/exec-approval\";\nimport {\n resetToolStream as resetToolStreamInternal,\n type ToolStreamEntry,\n} from \"./app-tool-stream\";\nimport {\n exportLogs as exportLogsInternal,\n handleChatScroll as handleChatScrollInternal,\n handleLogsScroll as handleLogsScrollInternal,\n resetChatScroll as resetChatScrollInternal,\n} from \"./app-scroll\";\nimport { connectGateway as connectGatewayInternal } from \"./app-gateway\";\nimport {\n handleConnected,\n handleDisconnected,\n handleFirstUpdated,\n handleUpdated,\n} from \"./app-lifecycle\";\nimport {\n applySettings as applySettingsInternal,\n loadCron as loadCronInternal,\n loadOverview as loadOverviewInternal,\n setTab as setTabInternal,\n setTheme as setThemeInternal,\n onPopState as onPopStateInternal,\n} from \"./app-settings\";\nimport {\n handleAbortChat as handleAbortChatInternal,\n handleSendChat as handleSendChatInternal,\n removeQueuedMessage as removeQueuedMessageInternal,\n} from \"./app-chat\";\nimport {\n handleChannelConfigReload as handleChannelConfigReloadInternal,\n handleChannelConfigSave as handleChannelConfigSaveInternal,\n handleNostrProfileCancel as handleNostrProfileCancelInternal,\n handleNostrProfileEdit as handleNostrProfileEditInternal,\n handleNostrProfileFieldChange as handleNostrProfileFieldChangeInternal,\n handleNostrProfileImport as handleNostrProfileImportInternal,\n handleNostrProfileSave as handleNostrProfileSaveInternal,\n handleNostrProfileToggleAdvanced as handleNostrProfileToggleAdvancedInternal,\n handleWhatsAppLogout as handleWhatsAppLogoutInternal,\n handleWhatsAppStart as handleWhatsAppStartInternal,\n handleWhatsAppWait as handleWhatsAppWaitInternal,\n} from \"./app-channels\";\nimport type { NostrProfileFormState } from \"./views/channels.nostr-profile-form\";\nimport { loadAssistantIdentity as loadAssistantIdentityInternal } from \"./controllers/assistant-identity\";\n\ndeclare global {\n interface Window {\n __CLAWDBOT_CONTROL_UI_BASE_PATH__?: string;\n }\n}\n\nconst injectedAssistantIdentity = resolveInjectedAssistantIdentity();\n\nfunction resolveOnboardingMode(): boolean {\n if (!window.location.search) return false;\n const params = new URLSearchParams(window.location.search);\n const raw = params.get(\"onboarding\");\n if (!raw) return false;\n const normalized = raw.trim().toLowerCase();\n return normalized === \"1\" || normalized === \"true\" || normalized === \"yes\" || normalized === \"on\";\n}\n\n@customElement(\"clawdbot-app\")\nexport class ClawdbotApp extends LitElement {\n @state() settings: UiSettings = loadSettings();\n @state() password = \"\";\n @state() tab: Tab = \"chat\";\n @state() onboarding = resolveOnboardingMode();\n @state() connected = false;\n @state() theme: ThemeMode = this.settings.theme ?? \"system\";\n @state() themeResolved: ResolvedTheme = \"dark\";\n @state() hello: GatewayHelloOk | null = null;\n @state() lastError: string | null = null;\n @state() eventLog: EventLogEntry[] = [];\n private eventLogBuffer: EventLogEntry[] = [];\n private toolStreamSyncTimer: number | null = null;\n private sidebarCloseTimer: number | null = null;\n\n @state() assistantName = injectedAssistantIdentity.name;\n @state() assistantAvatar = injectedAssistantIdentity.avatar;\n @state() assistantAgentId = injectedAssistantIdentity.agentId ?? null;\n\n @state() sessionKey = this.settings.sessionKey;\n @state() chatLoading = false;\n @state() chatSending = false;\n @state() chatMessage = \"\";\n @state() chatMessages: unknown[] = [];\n @state() chatToolMessages: unknown[] = [];\n @state() chatStream: string | null = null;\n @state() chatStreamStartedAt: number | null = null;\n @state() chatRunId: string | null = null;\n @state() compactionStatus: import(\"./app-tool-stream\").CompactionStatus | null = null;\n @state() chatAvatarUrl: string | null = null;\n @state() chatThinkingLevel: string | null = null;\n @state() chatQueue: ChatQueueItem[] = [];\n // Sidebar state for tool output viewing\n @state() sidebarOpen = false;\n @state() sidebarContent: string | null = null;\n @state() sidebarError: string | null = null;\n @state() splitRatio = this.settings.splitRatio;\n\n @state() nodesLoading = false;\n @state() nodes: Array> = [];\n @state() devicesLoading = false;\n @state() devicesError: string | null = null;\n @state() devicesList: DevicePairingList | null = null;\n @state() execApprovalsLoading = false;\n @state() execApprovalsSaving = false;\n @state() execApprovalsDirty = false;\n @state() execApprovalsSnapshot: ExecApprovalsSnapshot | null = null;\n @state() execApprovalsForm: ExecApprovalsFile | null = null;\n @state() execApprovalsSelectedAgent: string | null = null;\n @state() execApprovalsTarget: \"gateway\" | \"node\" = \"gateway\";\n @state() execApprovalsTargetNodeId: string | null = null;\n @state() execApprovalQueue: ExecApprovalRequest[] = [];\n @state() execApprovalBusy = false;\n @state() execApprovalError: string | null = null;\n\n @state() configLoading = false;\n @state() configRaw = \"{\\n}\\n\";\n @state() configRawOriginal = \"\";\n @state() configValid: boolean | null = null;\n @state() configIssues: unknown[] = [];\n @state() configSaving = false;\n @state() configApplying = false;\n @state() updateRunning = false;\n @state() applySessionKey = this.settings.lastActiveSessionKey;\n @state() configSnapshot: ConfigSnapshot | null = null;\n @state() configSchema: unknown | null = null;\n @state() configSchemaVersion: string | null = null;\n @state() configSchemaLoading = false;\n @state() configUiHints: ConfigUiHints = {};\n @state() configForm: Record | null = null;\n @state() configFormOriginal: Record | null = null;\n @state() configFormDirty = false;\n @state() configFormMode: \"form\" | \"raw\" = \"form\";\n @state() configSearchQuery = \"\";\n @state() configActiveSection: string | null = null;\n @state() configActiveSubsection: string | null = null;\n\n @state() channelsLoading = false;\n @state() channelsSnapshot: ChannelsStatusSnapshot | null = null;\n @state() channelsError: string | null = null;\n @state() channelsLastSuccess: number | null = null;\n @state() whatsappLoginMessage: string | null = null;\n @state() whatsappLoginQrDataUrl: string | null = null;\n @state() whatsappLoginConnected: boolean | null = null;\n @state() whatsappBusy = false;\n @state() nostrProfileFormState: NostrProfileFormState | null = null;\n @state() nostrProfileAccountId: string | null = null;\n\n @state() presenceLoading = false;\n @state() presenceEntries: PresenceEntry[] = [];\n @state() presenceError: string | null = null;\n @state() presenceStatus: string | null = null;\n\n @state() agentsLoading = false;\n @state() agentsList: AgentsListResult | null = null;\n @state() agentsError: string | null = null;\n\n @state() sessionsLoading = false;\n @state() sessionsResult: SessionsListResult | null = null;\n @state() sessionsError: string | null = null;\n @state() sessionsFilterActive = \"\";\n @state() sessionsFilterLimit = \"120\";\n @state() sessionsIncludeGlobal = true;\n @state() sessionsIncludeUnknown = false;\n\n @state() cronLoading = false;\n @state() cronJobs: CronJob[] = [];\n @state() cronStatus: CronStatus | null = null;\n @state() cronError: string | null = null;\n @state() cronForm: CronFormState = { ...DEFAULT_CRON_FORM };\n @state() cronRunsJobId: string | null = null;\n @state() cronRuns: CronRunLogEntry[] = [];\n @state() cronBusy = false;\n\n @state() skillsLoading = false;\n @state() skillsReport: SkillStatusReport | null = null;\n @state() skillsError: string | null = null;\n @state() skillsFilter = \"\";\n @state() skillEdits: Record = {};\n @state() skillsBusyKey: string | null = null;\n @state() skillMessages: Record = {};\n\n @state() debugLoading = false;\n @state() debugStatus: StatusSummary | null = null;\n @state() debugHealth: HealthSnapshot | null = null;\n @state() debugModels: unknown[] = [];\n @state() debugHeartbeat: unknown | null = null;\n @state() debugCallMethod = \"\";\n @state() debugCallParams = \"{}\";\n @state() debugCallResult: string | null = null;\n @state() debugCallError: string | null = null;\n\n @state() logsLoading = false;\n @state() logsError: string | null = null;\n @state() logsFile: string | null = null;\n @state() logsEntries: LogEntry[] = [];\n @state() logsFilterText = \"\";\n @state() logsLevelFilters: Record = {\n ...DEFAULT_LOG_LEVEL_FILTERS,\n };\n @state() logsAutoFollow = true;\n @state() logsTruncated = false;\n @state() logsCursor: number | null = null;\n @state() logsLastFetchAt: number | null = null;\n @state() logsLimit = 500;\n @state() logsMaxBytes = 250_000;\n @state() logsAtBottom = true;\n\n client: GatewayBrowserClient | null = null;\n private chatScrollFrame: number | null = null;\n private chatScrollTimeout: number | null = null;\n private chatHasAutoScrolled = false;\n private chatUserNearBottom = true;\n private nodesPollInterval: number | null = null;\n private logsPollInterval: number | null = null;\n private debugPollInterval: number | null = null;\n private logsScrollFrame: number | null = null;\n private toolStreamById = new Map();\n private toolStreamOrder: string[] = [];\n basePath = \"\";\n private popStateHandler = () =>\n onPopStateInternal(\n this as unknown as Parameters[0],\n );\n private themeMedia: MediaQueryList | null = null;\n private themeMediaHandler: ((event: MediaQueryListEvent) => void) | null = null;\n private topbarObserver: ResizeObserver | null = null;\n\n createRenderRoot() {\n return this;\n }\n\n connectedCallback() {\n super.connectedCallback();\n handleConnected(this as unknown as Parameters[0]);\n }\n\n protected firstUpdated() {\n handleFirstUpdated(this as unknown as Parameters[0]);\n }\n\n disconnectedCallback() {\n handleDisconnected(this as unknown as Parameters[0]);\n super.disconnectedCallback();\n }\n\n protected updated(changed: Map) {\n handleUpdated(\n this as unknown as Parameters[0],\n changed,\n );\n }\n\n connect() {\n connectGatewayInternal(\n this as unknown as Parameters[0],\n );\n }\n\n handleChatScroll(event: Event) {\n handleChatScrollInternal(\n this as unknown as Parameters[0],\n event,\n );\n }\n\n handleLogsScroll(event: Event) {\n handleLogsScrollInternal(\n this as unknown as Parameters[0],\n event,\n );\n }\n\n exportLogs(lines: string[], label: string) {\n exportLogsInternal(lines, label);\n }\n\n resetToolStream() {\n resetToolStreamInternal(\n this as unknown as Parameters[0],\n );\n }\n\n resetChatScroll() {\n resetChatScrollInternal(\n this as unknown as Parameters[0],\n );\n }\n\n async loadAssistantIdentity() {\n await loadAssistantIdentityInternal(this);\n }\n\n applySettings(next: UiSettings) {\n applySettingsInternal(\n this as unknown as Parameters[0],\n next,\n );\n }\n\n setTab(next: Tab) {\n setTabInternal(this as unknown as Parameters[0], next);\n }\n\n setTheme(next: ThemeMode, context?: Parameters[2]) {\n setThemeInternal(\n this as unknown as Parameters[0],\n next,\n context,\n );\n }\n\n async loadOverview() {\n await loadOverviewInternal(\n this as unknown as Parameters[0],\n );\n }\n\n async loadCron() {\n await loadCronInternal(\n this as unknown as Parameters[0],\n );\n }\n\n async handleAbortChat() {\n await handleAbortChatInternal(\n this as unknown as Parameters[0],\n );\n }\n\n removeQueuedMessage(id: string) {\n removeQueuedMessageInternal(\n this as unknown as Parameters[0],\n id,\n );\n }\n\n async handleSendChat(\n messageOverride?: string,\n opts?: Parameters[2],\n ) {\n await handleSendChatInternal(\n this as unknown as Parameters[0],\n messageOverride,\n opts,\n );\n }\n\n async handleWhatsAppStart(force: boolean) {\n await handleWhatsAppStartInternal(this, force);\n }\n\n async handleWhatsAppWait() {\n await handleWhatsAppWaitInternal(this);\n }\n\n async handleWhatsAppLogout() {\n await handleWhatsAppLogoutInternal(this);\n }\n\n async handleChannelConfigSave() {\n await handleChannelConfigSaveInternal(this);\n }\n\n async handleChannelConfigReload() {\n await handleChannelConfigReloadInternal(this);\n }\n\n handleNostrProfileEdit(accountId: string, profile: NostrProfile | null) {\n handleNostrProfileEditInternal(this, accountId, profile);\n }\n\n handleNostrProfileCancel() {\n handleNostrProfileCancelInternal(this);\n }\n\n handleNostrProfileFieldChange(field: keyof NostrProfile, value: string) {\n handleNostrProfileFieldChangeInternal(this, field, value);\n }\n\n async handleNostrProfileSave() {\n await handleNostrProfileSaveInternal(this);\n }\n\n async handleNostrProfileImport() {\n await handleNostrProfileImportInternal(this);\n }\n\n handleNostrProfileToggleAdvanced() {\n handleNostrProfileToggleAdvancedInternal(this);\n }\n\n async handleExecApprovalDecision(decision: \"allow-once\" | \"allow-always\" | \"deny\") {\n const active = this.execApprovalQueue[0];\n if (!active || !this.client || this.execApprovalBusy) return;\n this.execApprovalBusy = true;\n this.execApprovalError = null;\n try {\n await this.client.request(\"exec.approval.resolve\", {\n id: active.id,\n decision,\n });\n this.execApprovalQueue = this.execApprovalQueue.filter((entry) => entry.id !== active.id);\n } catch (err) {\n this.execApprovalError = `Exec approval failed: ${String(err)}`;\n } finally {\n this.execApprovalBusy = false;\n }\n }\n\n // Sidebar handlers for tool output viewing\n handleOpenSidebar(content: string) {\n if (this.sidebarCloseTimer != null) {\n window.clearTimeout(this.sidebarCloseTimer);\n this.sidebarCloseTimer = null;\n }\n this.sidebarContent = content;\n this.sidebarError = null;\n this.sidebarOpen = true;\n }\n\n handleCloseSidebar() {\n this.sidebarOpen = false;\n // Clear content after transition\n if (this.sidebarCloseTimer != null) {\n window.clearTimeout(this.sidebarCloseTimer);\n }\n this.sidebarCloseTimer = window.setTimeout(() => {\n if (this.sidebarOpen) return;\n this.sidebarContent = null;\n this.sidebarError = null;\n this.sidebarCloseTimer = null;\n }, 200);\n }\n\n handleSplitRatioChange(ratio: number) {\n const newRatio = Math.max(0.4, Math.min(0.7, ratio));\n this.splitRatio = newRatio;\n this.applySettings({ ...this.settings, splitRatio: newRatio });\n }\n\n render() {\n return renderApp(this);\n }\n}\n"],"names":["t","e","s","o","n$3","r","n","i","S","c","h","a","l","p","d","u","f","b","y$2","y","v","_","m","g","$","x","E","A","C","P","V","N","S$1","I","L","z","H","M","R","k","Z","I$2","Z$1","j","B","D","MAX_ASSISTANT_NAME","MAX_ASSISTANT_AVATAR","DEFAULT_ASSISTANT_NAME","coerceIdentityValue","value","maxLength","trimmed","normalizeAssistantIdentity","input","name","avatar","resolveInjectedAssistantIdentity","KEY","loadSettings","defaults","raw","parsed","saveSettings","next","parseAgentSessionKey","sessionKey","parts","agentId","rest","TAB_GROUPS","TAB_PATHS","PATH_TO_TAB","tab","path","normalizeBasePath","basePath","base","normalizePath","normalized","pathForTab","tabFromPath","pathname","inferBasePathFromPathname","segments","candidate","prefix","iconForTab","titleForTab","subtitleForTab","icons","html","QUICK_TAG_RE","FINAL_TAG_RE","THINKING_TAG_RE","applyTrim","mode","stripReasoningTagsFromText","text","options","cleaned","result","lastIndex","inThinking","match","idx","isClose","formatMs","ms","formatAgo","diff","sec","min","hr","formatDurationMs","formatList","values","clampText","max","truncateText","toNumber","fallback","stripThinkingTags","ENVELOPE_PREFIX","ENVELOPE_CHANNELS","textCache","thinkingCache","looksLikeEnvelopeHeader","header","label","stripEnvelope","extractText","message","role","content","item","joined","extractTextCached","obj","extractThinking","rawText","extractRawText","extracted","extractThinkingCached","formatReasoningMarkdown","lines","line","uuidFromBytes","bytes","hex","weakRandomBytes","now","generateUUID","cryptoLike","loadChatHistory","state","res","err","sendChatMessage","msg","runId","error","abortChatRun","handleChatEvent","payload","current","loadSessions","params","activeMinutes","limit","patchSession","key","patch","deleteSession","TOOL_STREAM_LIMIT","TOOL_STREAM_THROTTLE_MS","TOOL_OUTPUT_CHAR_LIMIT","extractToolOutputText","record","entry","part","formatToolOutput","contentText","truncated","buildToolStreamMessage","trimToolStream","host","overflow","removed","id","syncToolStreamMessages","flushToolStreamSync","scheduleToolStreamSync","force","resetToolStream","COMPACTION_TOAST_DURATION_MS","handleCompactionEvent","data","phase","handleAgentEvent","toolCallId","args","output","scheduleChatScroll","pickScrollTarget","container","overflowY","target","distanceFromBottom","retryDelay","latest","latestDistanceFromBottom","scheduleLogsScroll","handleChatScroll","event","handleLogsScroll","resetChatScroll","exportLogs","blob","url","anchor","stamp","observeTopbar","topbar","update","height","cloneConfigObject","serializeConfigForm","form","setPathValue","nextKey","lastKey","removePathValue","loadConfig","applyConfigSnapshot","loadConfigSchema","applyConfigSchema","snapshot","rawFromSnapshot","saveConfig","baseHash","applyConfig","runUpdate","updateConfigFormValue","removeConfigFormValue","loadCronStatus","loadCronJobs","buildCronSchedule","amount","unit","expr","buildCronPayload","timeoutSeconds","addCronJob","schedule","job","toggleCronJob","enabled","runCronJob","loadCronRuns","removeCronJob","jobId","loadChannels","probe","startWhatsAppLogin","waitWhatsAppLogin","logoutWhatsApp","loadDebug","status","health","models","heartbeat","modelPayload","callDebugMethod","LOG_BUFFER_LIMIT","LEVELS","parseMaybeJsonString","normalizeLevel","lowered","parseLogLine","meta","time","level","contextCandidate","contextObj","subsystem","loadLogs","opts","entries","shouldReset","ed25519_CURVE","Gx","Gy","_a","_d","L2","captureTrace","isBig","isStr","isBytes","abytes","length","title","len","needsLen","ofLen","got","u8n","u8fr","buf","padh","pad","bytesToHex","_ch","ch","hexToBytes","hl","al","array","ai","hi","n1","n2","cr","subtle","concatBytes","arrs","sum","randomBytes","big","assertRange","modN","invert","num","md","q","callHash","fn","hashes","apoint","Point","B256","X","Y","T","zip215","normed","lastByte","bytesToNumLE","y2","isValid","uvRatio","isXOdd","isLastByteOdd","X2","Y2","Z2","Z4","aX2","left","right","XY","ZT","other","X1","Y1","Z1","X1Z2","X2Z1","Y1Z2","Y2Z1","x1y1","G","F","X3","Y3","T3","Z3","T1","T2","safe","wNAF","scalar","iz","numTo32bLE","pow2","power","pow_2_252_3","b2","b4","b5","b10","b20","b40","b80","b160","b240","b250","RM1","v3","v7","pow","vx2","root1","root2","useRoot1","useRoot2","noRoot","modL_LE","hash","sha512a","sha512s","hash2extK","hashed","head","point","pointBytes","getExtendedPublicKeyAsync","secretKey","getExtendedPublicKey","getPublicKeyAsync","hashFinishA","_sign","rBytes","signAsync","randomSecretKey","seed","utils","W","scalarBits","pwindows","pwindowSize","precompute","points","w","Gpows","ctneg","cnd","comp","pow_2_w","maxNum","mask","shiftBy","wbits","off","offF","offP","isEven","isNeg","STORAGE_KEY","base64UrlEncode","binary","byte","base64UrlDecode","padded","out","fingerprintPublicKey","publicKey","generateIdentity","privateKey","loadOrCreateDeviceIdentity","derivedId","updated","identity","stored","signDevicePayload","privateKeyBase64Url","sig","normalizeRole","normalizeScopes","scopes","scope","readStore","writeStore","store","loadDeviceAuthToken","storeDeviceAuthToken","existing","clearDeviceAuthToken","loadDevices","approveDevicePairing","requestId","rejectDevicePairing","rotateDeviceToken","revokeDeviceToken","loadNodes","resolveExecApprovalsRpc","nodeId","resolveExecApprovalsSaveRpc","loadExecApprovals","rpc","applyExecApprovalsSnapshot","saveExecApprovals","file","updateExecApprovalsFormValue","removeExecApprovalsFormValue","loadPresence","setSkillMessage","getErrorMessage","loadSkills","updateSkillEdit","skillKey","updateSkillEnabled","saveSkillApiKey","apiKey","installSkill","installId","getSystemTheme","resolveTheme","clamp01","hasReducedMotionPreference","cleanupThemeTransition","root","startThemeTransition","nextTheme","applyTheme","context","currentTheme","documentReference","document_","prefersReducedMotion","xPercent","yPercent","rect","transition","startNodesPolling","stopNodesPolling","startLogsPolling","stopLogsPolling","startDebugPolling","stopDebugPolling","applySettings","applyResolvedTheme","setLastActiveSessionKey","applySettingsFromUrl","tokenRaw","passwordRaw","sessionRaw","gatewayUrlRaw","shouldCleanUrl","token","password","session","gatewayUrl","setTab","refreshActiveTab","syncUrlWithTab","setTheme","loadOverview","loadChannelsTab","loadCron","refreshChat","inferBasePath","configured","syncThemeWithSettings","resolved","attachThemeListener","detachThemeListener","syncTabWithLocation","replace","setTabFromRoute","onPopState","targetPath","currentPath","syncUrlWithSessionKey","isChatBusy","isChatStopCommand","handleAbortChat","enqueueChatMessage","sendChatMessageNow","ok","flushChatQueue","removeQueuedMessage","handleSendChat","messageOverride","previousDraft","refreshChatAvatar","flushChatQueueForEvent","resolveAgentIdForSession","buildAvatarMetaUrl","encoded","avatarUrl","i$1","normalizeMessage","hasToolId","contentRaw","contentItems","hasToolContent","hasToolName","timestamp","normalizeRoleForGrouping","lower","isToolResultMessage","setPrototypeOf","isFrozen","getPrototypeOf","getOwnPropertyDescriptor","freeze","seal","create","apply","construct","func","thisArg","_len","_key","Func","_len2","_key2","arrayForEach","unapply","arrayLastIndexOf","arrayPop","arrayPush","arraySplice","stringToLowerCase","stringToString","stringMatch","stringReplace","stringIndexOf","stringTrim","objectHasOwnProperty","regExpTest","typeErrorCreate","unconstruct","_len3","_key3","_len4","_key4","addToSet","set","transformCaseFunc","element","lcElement","cleanArray","index","clone","object","newObject","property","lookupGetter","prop","desc","fallbackValue","html$1","svg$1","svgFilters","svgDisallowed","mathMl$1","mathMlDisallowed","svg","mathMl","xml","MUSTACHE_EXPR","ERB_EXPR","TMPLIT_EXPR","DATA_ATTR","ARIA_ATTR","IS_ALLOWED_URI","IS_SCRIPT_OR_DATA","ATTR_WHITESPACE","DOCTYPE_NAME","CUSTOM_ELEMENT","EXPRESSIONS","NODE_TYPE","getGlobal","_createTrustedTypesPolicy","trustedTypes","purifyHostElement","suffix","ATTR_NAME","policyName","scriptUrl","_createHooksMap","createDOMPurify","window","DOMPurify","document","originalDocument","currentScript","DocumentFragment","HTMLTemplateElement","Node","Element","NodeFilter","NamedNodeMap","HTMLFormElement","DOMParser","ElementPrototype","cloneNode","remove","getNextSibling","getChildNodes","getParentNode","template","trustedTypesPolicy","emptyHTML","implementation","createNodeIterator","createDocumentFragment","getElementsByTagName","importNode","hooks","IS_ALLOWED_URI$1","ALLOWED_TAGS","DEFAULT_ALLOWED_TAGS","ALLOWED_ATTR","DEFAULT_ALLOWED_ATTR","CUSTOM_ELEMENT_HANDLING","FORBID_TAGS","FORBID_ATTR","EXTRA_ELEMENT_HANDLING","ALLOW_ARIA_ATTR","ALLOW_DATA_ATTR","ALLOW_UNKNOWN_PROTOCOLS","ALLOW_SELF_CLOSE_IN_ATTR","SAFE_FOR_TEMPLATES","SAFE_FOR_XML","WHOLE_DOCUMENT","SET_CONFIG","FORCE_BODY","RETURN_DOM","RETURN_DOM_FRAGMENT","RETURN_TRUSTED_TYPE","SANITIZE_DOM","SANITIZE_NAMED_PROPS","SANITIZE_NAMED_PROPS_PREFIX","KEEP_CONTENT","IN_PLACE","USE_PROFILES","FORBID_CONTENTS","DEFAULT_FORBID_CONTENTS","DATA_URI_TAGS","DEFAULT_DATA_URI_TAGS","URI_SAFE_ATTRIBUTES","DEFAULT_URI_SAFE_ATTRIBUTES","MATHML_NAMESPACE","SVG_NAMESPACE","HTML_NAMESPACE","NAMESPACE","IS_EMPTY_INPUT","ALLOWED_NAMESPACES","DEFAULT_ALLOWED_NAMESPACES","MATHML_TEXT_INTEGRATION_POINTS","HTML_INTEGRATION_POINTS","COMMON_SVG_AND_HTML_ELEMENTS","PARSER_MEDIA_TYPE","SUPPORTED_PARSER_MEDIA_TYPES","DEFAULT_PARSER_MEDIA_TYPE","CONFIG","formElement","isRegexOrFunction","testValue","_parseConfig","cfg","ALL_SVG_TAGS","ALL_MATHML_TAGS","_checkValidNamespace","parent","tagName","parentTagName","_forceRemove","node","_removeAttribute","_initDocument","dirty","doc","leadingWhitespace","matches","dirtyPayload","body","_createNodeIterator","_isClobbered","_isNode","_executeHooks","currentNode","hook","_sanitizeElements","_isBasicCustomElement","parentNode","childNodes","childCount","childClone","_isValidAttribute","lcTag","lcName","_sanitizeAttributes","attributes","hookEvent","attr","namespaceURI","attrValue","initValue","_sanitizeShadowDOM","fragment","shadowNode","shadowIterator","importedNode","returnNode","nodeIterator","serializedHTML","tag","entryPoint","hookFunction","purify","me","xe","be","Re","Te","re","se","Oe","Q","we","ye","Pe","Se","ie","$e","U","te","_e","Le","Me","ze","oe","Ae","K","ae","Ce","le","Ie","Ee","Be","ue","qe","ve","pe","De","He","Ze","Ge","Ne","Qe","Fe","je","ce","he","Ue","ne","Ke","We","Xe","ke","J","de","ge","Je","O","ee","fe","marked","allowedTags","allowedAttrs","hooksInstalled","MARKDOWN_CHAR_LIMIT","MARKDOWN_PARSE_LIMIT","MARKDOWN_CACHE_LIMIT","MARKDOWN_CACHE_MAX_CHARS","markdownCache","getCachedMarkdown","cached","setCachedMarkdown","oldest","installHooks","toSanitizedMarkdownHtml","markdown","escapeHtml","sanitized","rendered","COPIED_FOR_MS","ERROR_FOR_MS","COPY_LABEL","COPIED_LABEL","ERROR_LABEL","copyTextToClipboard","setButtonLabel","button","createCopyButton","idleLabel","btn","copied","renderCopyAsMarkdownButton","TOOL_DISPLAY_CONFIG","rawConfig","FALLBACK","TOOL_MAP","normalizeToolName","defaultTitle","normalizeVerb","coerceDisplayValue","firstLine","preview","lookupValueByPath","segment","resolveDetailFromKeys","keys","display","resolveReadDetail","offset","resolveWriteDetail","resolveActionSpec","spec","action","resolveToolDisplay","icon","actionRaw","actionSpec","verb","detail","detailKeys","shortenHomeInString","formatToolDetail","TOOL_INLINE_THRESHOLD","PREVIEW_MAX_LINES","PREVIEW_MAX_CHARS","formatToolOutputForSidebar","getTruncatedPreview","allLines","extractToolCards","normalizeContent","cards","kind","coerceArgs","extractToolText","card","renderToolCardSidebar","onOpenSidebar","hasText","canClick","handleClick","info","isShort","showCollapsed","showInline","isEmpty","nothing","renderReadingIndicatorGroup","assistant","renderAvatar","renderStreamingGroup","startedAt","renderGroupedMessage","renderMessageGroup","group","normalizedRole","assistantName","who","roleClass","assistantAvatar","initial","className","isAvatarUrl","isToolResult","toolCards","hasToolCards","extractedText","extractedThinking","markdownBase","reasoningMarkdown","canCopyMarkdown","bubbleClasses","unsafeHTML","renderMarkdownSidebar","props","ResizableDivider","LitElement","containerWidth","deltaRatio","newRatio","css","__decorateClass","customElement","renderCompactionIndicator","renderChat","canCompose","isBusy","canAbort","reasoningLevel","row","showReasoning","assistantIdentity","composePlaceholder","splitRatio","sidebarOpen","thread","repeat","buildChatItems","CHAT_HISTORY_RENDER_LIMIT","groupMessages","items","currentGroup","history","tools","historyStart","messageKey","messageId","schemaType","schema","defaultValue","pathKey","hintForPath","hints","direct","hintKey","hint","hintSegments","humanize","isSensitivePath","META_KEYS","isAnySchema","jsonValue","renderNode","unsupported","disabled","onPatch","showLabel","type","help","nonNull","extractLiteral","literals","allLiterals","resolvedValue","lit","renderSelect","primitiveTypes","variant","normalizedTypes","hasString","hasNumber","renderTextInput","opt","renderObject","renderArray","displayValue","renderNumberInput","inputType","isSensitive","placeholder","numValue","currentIndex","unset","val","sorted","orderA","orderB","reserved","additional","allowExtra","propKey","renderMapField","itemsSchema","arr","reservedKeys","anySchema","entryValue","valuePath","sectionIcons","SECTION_META","getSectionIcon","matchesSearch","query","schemaMatches","propSchema","unions","renderConfigForm","properties","searchQuery","activeSection","activeSubsection","filteredEntries","subsectionContext","sectionSchema","sectionKey","subsectionKey","description","sectionValue","scopedValue","normalizeEnum","filtered","nullable","enumValues","analyzeConfigSchema","normalizeSchemaNode","pathLabel","union","normalizeUnion","enumNullable","normalizedProps","remaining","unique","sidebarIcons","SECTIONS","ALL_SUBSECTION","resolveSectionMeta","resolveSubsections","uiHints","subKey","order","computeDiff","original","changes","compare","orig","curr","origObj","currObj","allKeys","truncateValue","maxLen","str","renderConfig","validity","analysis","formUnsafe","schemaProps","availableSections","knownKeys","extraSections","allSections","activeSectionSchema","activeSectionMeta","subsections","allowSubnav","isAllSubsection","effectiveSubsection","hasRawChanges","hasChanges","canSaveForm","canSave","canApply","canUpdate","section","change","formatDuration","channelEnabled","channels","channelStatus","running","connected","accountActive","account","getChannelAccountCount","channelAccounts","renderChannelAccountCount","count","resolveSchemaNode","resolveChannelValue","config","channelId","fromChannels","renderChannelConfigForm","configValue","renderChannelConfigSection","renderDiscordCard","discord","accountCountLabel","renderGoogleChatCard","googleChat","renderIMessageCard","imessage","isFormDirty","renderNostrProfileForm","callbacks","accountId","isDirty","renderField","field","inputId","renderPicturePreview","picture","img","createNostrProfileFormState","profile","truncatePubkey","pubkey","renderNostrCard","nostr","nostrAccounts","profileFormState","profileFormCallbacks","onEditProfile","primaryAccount","summaryConfigured","summaryRunning","summaryPublicKey","summaryLastStartAt","summaryLastError","hasMultipleAccounts","showingForm","renderAccountCard","displayName","renderProfileSection","about","nip05","hasAnyProfileData","renderSignalCard","signal","renderSlackCard","slack","renderTelegramCard","telegram","telegramAccounts","botUsername","renderWhatsAppCard","whatsapp","renderChannels","orderedChannels","resolveChannelOrder","channel","renderChannel","showForm","renderGenericChannelCard","resolveChannelLabel","lastError","accounts","renderGenericAccount","resolveChannelMetaMap","RECENT_ACTIVITY_THRESHOLD_MS","hasRecentActivity","deriveRunningStatus","deriveConnectedStatus","runningStatus","connectedStatus","formatPresenceSummary","ip","version","formatPresenceAge","ts","formatNextRun","formatSessionTokens","total","ctx","formatEventPayload","formatCronState","last","formatCronSchedule","formatCronPayload","buildChannelOptions","seen","renderCron","channelOptions","renderScheduleFields","renderJob","renderRun","itemClass","renderDebug","evt","renderInstances","renderEntry","lastInput","roles","scopesLabel","formatTime","date","matchesFilter","needle","renderLogs","levelFiltered","exportLabel","renderNodes","bindingState","resolveBindingsState","approvalsState","resolveExecApprovalsState","renderExecApprovals","renderBindings","renderDevices","list","pending","paired","req","renderPendingDevice","device","renderPairedDevice","age","repair","tokens","renderTokenRow","deviceId","when","EXEC_APPROVALS_DEFAULT_SCOPE","SECURITY_OPTIONS","ASK_OPTIONS","nodes","resolveExecNodes","defaultBinding","agents","resolveAgentBindings","ready","normalizeSecurity","normalizeAsk","resolveExecApprovalsDefaults","resolveConfigAgents","agentsNode","isDefault","resolveExecApprovalsAgents","configAgents","approvalsAgents","merged","agent","aLabel","bLabel","resolveExecApprovalsScope","selected","targetNodes","resolveExecApprovalsNodes","targetNodeId","selectedScope","selectedAgent","allowlist","supportsBinding","renderAgentBinding","targetReady","renderExecApprovalsTarget","renderExecApprovalsTabs","renderExecApprovalsPolicy","renderExecApprovalsAllowlist","hasNodes","nodeValue","first","isDefaults","agentSecurity","agentAsk","agentAskFallback","securityValue","askValue","askFallbackValue","autoOverride","autoEffective","autoIsDefault","option","allowlistPath","renderAllowlistEntry","lastUsed","lastCommand","lastPath","bindingValue","cmd","fallbackAgent","exec","execEntry","binding","caps","commands","renderOverview","uptime","tick","authHint","hasToken","hasPassword","insecureContextHint","THINK_LEVELS","BINARY_THINK_LEVELS","VERBOSE_LEVELS","REASONING_LEVELS","normalizeProviderId","provider","isBinaryThinkingProvider","resolveThinkLevelOptions","resolveThinkLevelDisplay","isBinary","resolveThinkLevelPatchValue","renderSessions","rows","renderRow","onDelete","rawThinking","isBinaryThinking","thinking","thinkLevels","verbose","reasoning","canLink","chatUrl","formatRemaining","totalSeconds","minutes","renderMetaRow","renderExecApprovalPrompt","active","request","remainingMs","queueCount","renderSkills","skills","filter","skill","renderSkill","busy","canInstall","missing","reasons","renderTab","href","renderChatControls","sessionOptions","resolveSessionOptions","disableThinkingToggle","disableFocusToggle","showThinking","focusActive","refreshIcon","focusIcon","sessions","resolvedCurrent","THEME_ORDER","renderThemeToggle","renderMonitorIcon","renderSunIcon","renderMoonIcon","AVATAR_DATA_RE","AVATAR_HTTP_RE","resolveAssistantAvatarUrl","renderApp","presenceCount","sessionsCount","cronNext","chatDisabledReason","isChat","chatFocus","assistantAvatarUrl","chatAvatarUrl","isGroupCollapsed","hasActiveTab","agentIndex","ratio","DEFAULT_LOG_LEVEL_FILTERS","DEFAULT_CRON_FORM","loadAgents","GATEWAY_CLIENT_IDS","GATEWAY_CLIENT_NAMES","GATEWAY_CLIENT_MODES","buildDeviceAuthPayload","CONNECT_FAILED_CLOSE_CODE","GatewayBrowserClient","ev","reason","delay","isSecureContext","deviceIdentity","canFallbackToShared","authToken","storedToken","auth","signedAtMs","nonce","signature","hello","frame","seq","method","resolve","reject","isRecord","parseExecApprovalRequested","command","createdAtMs","expiresAtMs","parseExecApprovalResolved","pruneExecApprovalQueue","queue","addExecApproval","removeExecApproval","loadAssistantIdentity","normalizeSessionKeyForDefaults","mainSessionKey","mainKey","defaultAgentId","applySessionDefaults","resolvedSessionKey","resolvedSettingsSessionKey","resolvedLastActiveSessionKey","nextSessionKey","nextSettings","shouldUpdateSettings","connectGateway","applySnapshot","code","handleGatewayEvent","expected","received","handleGatewayEventUnsafe","handleConnected","handleFirstUpdated","handleDisconnected","handleUpdated","changed","forcedByTab","forcedByLoad","handleWhatsAppStart","handleWhatsAppWait","handleWhatsAppLogout","handleChannelConfigSave","handleChannelConfigReload","parseValidationErrors","details","errors","rawField","resolveNostrAccountId","buildNostrProfileUrl","handleNostrProfileEdit","handleNostrProfileCancel","handleNostrProfileFieldChange","handleNostrProfileToggleAdvanced","handleNostrProfileSave","response","errorMessage","handleNostrProfileImport","nextValues","showAdvanced","injectedAssistantIdentity","resolveOnboardingMode","ClawdbotApp","onPopStateInternal","connectGatewayInternal","handleChatScrollInternal","handleLogsScrollInternal","exportLogsInternal","resetToolStreamInternal","resetChatScrollInternal","loadAssistantIdentityInternal","applySettingsInternal","setTabInternal","setThemeInternal","loadOverviewInternal","loadCronInternal","handleAbortChatInternal","removeQueuedMessageInternal","handleSendChatInternal","handleWhatsAppStartInternal","handleWhatsAppWaitInternal","handleWhatsAppLogoutInternal","handleChannelConfigSaveInternal","handleChannelConfigReloadInternal","handleNostrProfileEditInternal","handleNostrProfileCancelInternal","handleNostrProfileFieldChangeInternal","handleNostrProfileSaveInternal","handleNostrProfileImportInternal","handleNostrProfileToggleAdvancedInternal","decision"],"mappings":"ssBAKA,MAAMA,GAAE,WAAWC,GAAED,GAAE,aAAsBA,GAAE,WAAX,QAAqBA,GAAE,SAAS,eAAe,uBAAuB,SAAS,WAAW,YAAY,cAAc,UAAUE,GAAE,OAAM,EAAGC,GAAE,IAAI,QAAO,IAAAC,GAAC,KAAO,CAAC,YAAY,EAAEH,EAAEE,EAAE,CAAC,GAAG,KAAK,aAAa,GAAGA,IAAID,GAAE,MAAM,MAAM,mEAAmE,EAAE,KAAK,QAAQ,EAAE,KAAK,EAAED,CAAC,CAAC,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,MAAMC,EAAE,KAAK,EAAE,GAAGD,IAAY,IAAT,OAAW,CAAC,MAAMA,EAAWC,IAAT,QAAgBA,EAAE,SAAN,EAAaD,IAAI,EAAEE,GAAE,IAAID,CAAC,GAAY,IAAT,UAAc,KAAK,EAAE,EAAE,IAAI,eAAe,YAAY,KAAK,OAAO,EAAED,GAAGE,GAAE,IAAID,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,OAAO,KAAK,OAAO,CAAC,EAAC,MAAMG,GAAEL,GAAG,IAAIM,GAAY,OAAON,GAAjB,SAAmBA,EAAEA,EAAE,GAAG,OAAOE,EAAC,EAAEK,GAAE,CAACP,KAAKC,IAAI,CAAC,MAAME,EAAMH,EAAE,SAAN,EAAaA,EAAE,CAAC,EAAEC,EAAE,OAAO,CAACA,EAAEC,EAAEC,IAAIF,GAAGD,GAAG,CAAC,GAAQA,EAAE,eAAP,GAAoB,OAAOA,EAAE,QAAQ,GAAa,OAAOA,GAAjB,SAAmB,OAAOA,EAAE,MAAM,MAAM,mEAAmEA,EAAE,sFAAsF,CAAC,GAAGE,CAAC,EAAEF,EAAEG,EAAE,CAAC,EAAEH,EAAE,CAAC,CAAC,EAAE,OAAO,IAAIM,GAAEH,EAAEH,EAAEE,EAAC,CAAC,EAAEM,GAAE,CAACN,EAAEC,IAAI,CAAC,GAAGF,GAAEC,EAAE,mBAAmBC,EAAE,IAAIH,GAAGA,aAAa,cAAcA,EAAEA,EAAE,UAAU,MAAO,WAAUC,KAAKE,EAAE,CAAC,MAAMA,EAAE,SAAS,cAAc,OAAO,EAAEG,EAAEN,GAAE,SAAkBM,IAAT,QAAYH,EAAE,aAAa,QAAQG,CAAC,EAAEH,EAAE,YAAYF,EAAE,QAAQC,EAAE,YAAYC,CAAC,CAAC,CAAC,EAAEM,GAAER,GAAED,GAAGA,EAAEA,GAAGA,aAAa,eAAe,GAAG,CAAC,IAAIC,EAAE,GAAG,UAAU,KAAK,EAAE,SAASA,GAAG,EAAE,QAAQ,OAAOI,GAAEJ,CAAC,CAAC,GAAGD,CAAC,EAAEA,ECApzC,KAAK,CAAC,GAAGO,GAAE,eAAeN,GAAE,yBAAyBS,GAAE,oBAAoBL,GAAE,sBAAsBF,GAAE,eAAeG,EAAC,EAAE,OAAOK,GAAE,WAAWF,GAAEE,GAAE,aAAaC,GAAEH,GAAEA,GAAE,YAAY,GAAGI,GAAEF,GAAE,+BAA+BG,GAAE,CAACd,EAAEE,IAAIF,EAAEe,GAAE,CAAC,YAAYf,EAAEE,EAAE,CAAC,OAAOA,EAAC,CAAE,KAAK,QAAQF,EAAEA,EAAEY,GAAE,KAAK,MAAM,KAAK,OAAO,KAAK,MAAMZ,EAAQA,GAAN,KAAQA,EAAE,KAAK,UAAUA,CAAC,CAAC,CAAC,OAAOA,CAAC,EAAE,cAAcA,EAAEE,EAAE,CAAC,IAAIK,EAAEP,EAAE,OAAOE,EAAC,CAAE,KAAK,QAAQK,EAASP,IAAP,KAAS,MAAM,KAAK,OAAOO,EAASP,IAAP,KAAS,KAAK,OAAOA,CAAC,EAAE,MAAM,KAAK,OAAO,KAAK,MAAM,GAAG,CAACO,EAAE,KAAK,MAAMP,CAAC,CAAC,MAAS,CAACO,EAAE,IAAI,CAAC,CAAC,OAAOA,CAAC,CAAC,EAAES,GAAE,CAAChB,EAAEE,IAAI,CAACK,GAAEP,EAAEE,CAAC,EAAEe,GAAE,CAAC,UAAU,GAAG,KAAK,OAAO,UAAUF,GAAE,QAAQ,GAAG,WAAW,GAAG,WAAWC,EAAC,EAAE,OAAO,WAAW,OAAO,UAAU,EAAEL,GAAE,sBAAsB,IAAI,QAAO,IAAAO,GAAC,cAAgB,WAAW,CAAC,OAAO,eAAe,EAAE,CAAC,KAAK,KAAI,GAAI,KAAK,IAAI,CAAA,GAAI,KAAK,CAAC,CAAC,CAAC,WAAW,oBAAoB,CAAC,OAAO,KAAK,SAAQ,EAAG,KAAK,MAAM,CAAC,GAAG,KAAK,KAAK,KAAI,CAAE,CAAC,CAAC,OAAO,eAAe,EAAEhB,EAAEe,GAAE,CAAC,GAAGf,EAAE,QAAQA,EAAE,UAAU,IAAI,KAAK,KAAI,EAAG,KAAK,UAAU,eAAe,CAAC,KAAKA,EAAE,OAAO,OAAOA,CAAC,GAAG,QAAQ,IAAI,KAAK,kBAAkB,IAAI,EAAEA,CAAC,EAAE,CAACA,EAAE,WAAW,CAAC,MAAMK,EAAE,OAAM,EAAGG,EAAE,KAAK,sBAAsB,EAAEH,EAAEL,CAAC,EAAWQ,IAAT,QAAYT,GAAE,KAAK,UAAU,EAAES,CAAC,CAAC,CAAC,CAAC,OAAO,sBAAsB,EAAER,EAAEK,EAAE,CAAC,KAAK,CAAC,IAAIN,EAAE,IAAII,CAAC,EAAEK,GAAE,KAAK,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,KAAKR,CAAC,CAAC,EAAE,IAAIF,EAAE,CAAC,KAAKE,CAAC,EAAEF,CAAC,CAAC,EAAE,MAAM,CAAC,IAAIC,EAAE,IAAIC,EAAE,CAAC,MAAMQ,EAAET,GAAG,KAAK,IAAI,EAAEI,GAAG,KAAK,KAAKH,CAAC,EAAE,KAAK,cAAc,EAAEQ,EAAEH,CAAC,CAAC,EAAE,aAAa,GAAG,WAAW,EAAE,CAAC,CAAC,OAAO,mBAAmB,EAAE,CAAC,OAAO,KAAK,kBAAkB,IAAI,CAAC,GAAGU,EAAC,CAAC,OAAO,MAAM,CAAC,GAAG,KAAK,eAAeH,GAAE,mBAAmB,CAAC,EAAE,OAAO,MAAM,EAAER,GAAE,IAAI,EAAE,EAAE,SAAQ,EAAY,EAAE,IAAX,SAAe,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,KAAK,kBAAkB,IAAI,IAAI,EAAE,iBAAiB,CAAC,CAAC,OAAO,UAAU,CAAC,GAAG,KAAK,eAAeQ,GAAE,WAAW,CAAC,EAAE,OAAO,GAAG,KAAK,UAAU,GAAG,KAAK,KAAI,EAAG,KAAK,eAAeA,GAAE,YAAY,CAAC,EAAE,CAAC,MAAMd,EAAE,KAAK,WAAW,EAAE,CAAC,GAAGK,GAAEL,CAAC,EAAE,GAAGG,GAAEH,CAAC,CAAC,EAAE,UAAU,KAAK,EAAE,KAAK,eAAe,EAAEA,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,OAAO,QAAQ,EAAE,GAAU,IAAP,KAAS,CAAC,MAAME,EAAE,oBAAoB,IAAI,CAAC,EAAE,GAAYA,IAAT,OAAW,SAAS,CAACF,EAAE,CAAC,IAAIE,EAAE,KAAK,kBAAkB,IAAIF,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,SAAS,CAACA,EAAE,CAAC,IAAI,KAAK,kBAAkB,CAAC,MAAM,EAAE,KAAK,KAAKA,EAAE,CAAC,EAAW,IAAT,QAAY,KAAK,KAAK,IAAI,EAAEA,CAAC,CAAC,CAAC,KAAK,cAAc,KAAK,eAAe,KAAK,MAAM,CAAC,CAAC,OAAO,eAAeE,EAAE,CAAC,MAAMK,EAAE,CAAA,EAAG,GAAG,MAAM,QAAQL,CAAC,EAAE,CAAC,MAAMD,EAAE,IAAI,IAAIC,EAAE,KAAK,GAAG,EAAE,QAAO,CAAE,EAAE,UAAUA,KAAKD,EAAEM,EAAE,QAAQP,GAAEE,CAAC,CAAC,CAAC,MAAeA,IAAT,QAAYK,EAAE,KAAKP,GAAEE,CAAC,CAAC,EAAE,OAAOK,CAAC,CAAC,OAAO,KAAK,EAAEL,EAAE,CAAC,MAAMK,EAAEL,EAAE,UAAU,OAAWK,IAAL,GAAO,OAAiB,OAAOA,GAAjB,SAAmBA,EAAY,OAAO,GAAjB,SAAmB,EAAE,YAAW,EAAG,MAAM,CAAC,aAAa,CAAC,MAAK,EAAG,KAAK,KAAK,OAAO,KAAK,gBAAgB,GAAG,KAAK,WAAW,GAAG,KAAK,KAAK,KAAK,KAAK,KAAI,CAAE,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,QAAQ,GAAG,KAAK,eAAe,CAAC,EAAE,KAAK,KAAK,IAAI,IAAI,KAAK,KAAI,EAAG,KAAK,cAAa,EAAG,KAAK,YAAY,GAAG,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,KAAK,OAAO,IAAI,KAAK,IAAI,CAAC,EAAW,KAAK,aAAd,QAA0B,KAAK,aAAa,EAAE,gBAAa,CAAI,CAAC,iBAAiB,EAAE,CAAC,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,IAAIL,EAAE,KAAK,YAAY,kBAAkB,UAAUK,KAAKL,EAAE,KAAI,EAAG,KAAK,eAAeK,CAAC,IAAI,EAAE,IAAIA,EAAE,KAAKA,CAAC,CAAC,EAAE,OAAO,KAAKA,CAAC,GAAG,EAAE,KAAK,IAAI,KAAK,KAAK,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,KAAK,YAAY,KAAK,aAAa,KAAK,YAAY,iBAAiB,EAAE,OAAOL,GAAE,EAAE,KAAK,YAAY,aAAa,EAAE,CAAC,CAAC,mBAAmB,CAAC,KAAK,aAAa,KAAK,iBAAgB,EAAG,KAAK,eAAe,EAAE,EAAE,KAAK,MAAM,QAAQ,GAAG,EAAE,gBAAa,CAAI,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,sBAAsB,CAAC,KAAK,MAAM,QAAQ,GAAG,EAAE,mBAAgB,CAAI,CAAC,CAAC,yBAAyB,EAAEA,EAAEK,EAAE,CAAC,KAAK,KAAK,EAAEA,CAAC,CAAC,CAAC,KAAK,EAAEL,EAAE,CAAC,MAAMK,EAAE,KAAK,YAAY,kBAAkB,IAAI,CAAC,EAAEN,EAAE,KAAK,YAAY,KAAK,EAAEM,CAAC,EAAE,GAAYN,IAAT,QAAiBM,EAAE,UAAP,GAAe,CAAC,MAAMG,GAAYH,EAAE,WAAW,cAAtB,OAAkCA,EAAE,UAAUQ,IAAG,YAAYb,EAAEK,EAAE,IAAI,EAAE,KAAK,KAAK,EAAQG,GAAN,KAAQ,KAAK,gBAAgBT,CAAC,EAAE,KAAK,aAAaA,EAAES,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC,KAAK,EAAER,EAAE,CAAC,MAAMK,EAAE,KAAK,YAAYN,EAAEM,EAAE,KAAK,IAAI,CAAC,EAAE,GAAYN,IAAT,QAAY,KAAK,OAAOA,EAAE,CAAC,MAAMD,EAAEO,EAAE,mBAAmBN,CAAC,EAAES,EAAc,OAAOV,EAAE,WAArB,WAA+B,CAAC,cAAcA,EAAE,SAAS,EAAWA,EAAE,WAAW,gBAAtB,OAAoCA,EAAE,UAAUe,GAAE,KAAK,KAAKd,EAAE,MAAMI,EAAEK,EAAE,cAAcR,EAAEF,EAAE,IAAI,EAAE,KAAKC,CAAC,EAAEI,GAAG,KAAK,MAAM,IAAIJ,CAAC,GAAGI,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC,cAAc,EAAEH,EAAEK,EAAEN,EAAE,GAAGS,EAAE,CAAC,GAAY,IAAT,OAAW,CAAC,MAAML,EAAE,KAAK,YAAY,GAAQJ,IAAL,KAASS,EAAE,KAAK,CAAC,GAAGH,IAAIF,EAAE,mBAAmB,CAAC,EAAE,GAAGE,EAAE,YAAYS,IAAGN,EAAER,CAAC,GAAGK,EAAE,YAAYA,EAAE,SAASG,IAAI,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,aAAaL,EAAE,KAAK,EAAEE,CAAC,CAAC,GAAG,OAAO,KAAK,EAAE,EAAEL,EAAEK,CAAC,CAAC,CAAM,KAAK,kBAAV,KAA4B,KAAK,KAAK,KAAK,KAAI,EAAG,CAAC,EAAE,EAAEL,EAAE,CAAC,WAAWK,EAAE,QAAQN,EAAE,QAAQS,CAAC,EAAEL,EAAE,CAACE,GAAG,EAAE,KAAK,OAAO,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,EAAEF,GAAGH,GAAG,KAAK,CAAC,CAAC,EAAOQ,IAAL,IAAiBL,IAAT,UAAc,KAAK,KAAK,IAAI,CAAC,IAAI,KAAK,YAAYE,IAAIL,EAAE,QAAQ,KAAK,KAAK,IAAI,EAAEA,CAAC,GAAQD,IAAL,IAAQ,KAAK,OAAO,IAAI,KAAK,OAAO,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,KAAK,gBAAgB,GAAG,GAAG,CAAC,MAAM,KAAK,IAAI,OAAOD,EAAE,CAAC,QAAQ,OAAOA,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,eAAc,EAAG,OAAa,GAAN,MAAS,MAAM,EAAE,CAAC,KAAK,eAAe,CAAC,gBAAgB,CAAC,OAAO,KAAK,cAAa,CAAE,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,gBAAgB,OAAO,GAAG,CAAC,KAAK,WAAW,CAAC,GAAG,KAAK,aAAa,KAAK,iBAAgB,EAAG,KAAK,KAAK,CAAC,SAAS,CAACA,EAAEE,CAAC,IAAI,KAAK,KAAK,KAAKF,CAAC,EAAEE,EAAE,KAAK,KAAK,MAAM,CAAC,MAAMF,EAAE,KAAK,YAAY,kBAAkB,GAAGA,EAAE,KAAK,EAAE,SAAS,CAACE,EAAEK,CAAC,IAAIP,EAAE,CAAC,KAAK,CAAC,QAAQA,CAAC,EAAEO,EAAEN,EAAE,KAAKC,CAAC,EAAOF,IAAL,IAAQ,KAAK,KAAK,IAAIE,CAAC,GAAYD,IAAT,QAAY,KAAK,EAAEC,EAAE,OAAOK,EAAEN,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,MAAMC,EAAE,KAAK,KAAK,GAAG,CAAC,EAAE,KAAK,aAAaA,CAAC,EAAE,GAAG,KAAK,WAAWA,CAAC,EAAE,KAAK,MAAM,QAAQF,GAAGA,EAAE,cAAc,EAAE,KAAK,OAAOE,CAAC,GAAG,KAAK,KAAI,CAAE,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,KAAI,EAAG,CAAC,CAAC,GAAG,KAAK,KAAKA,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,QAAQF,GAAGA,EAAE,cAAW,CAAI,EAAE,KAAK,aAAa,KAAK,WAAW,GAAG,KAAK,aAAa,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC,IAAI,gBAAgB,CAAC,OAAO,KAAK,kBAAiB,CAAE,CAAC,mBAAmB,CAAC,OAAO,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,KAAK,KAAK,QAAQA,GAAG,KAAK,KAAKA,EAAE,KAAKA,CAAC,CAAC,CAAC,EAAE,KAAK,KAAI,CAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,EAACmB,GAAE,cAAc,CAAA,EAAGA,GAAE,kBAAkB,CAAC,KAAK,MAAM,EAAEA,GAAEL,GAAE,mBAAmB,CAAC,EAAE,IAAI,IAAIK,GAAEL,GAAE,WAAW,CAAC,EAAE,IAAI,IAAID,KAAI,CAAC,gBAAgBM,EAAC,CAAC,GAAGR,GAAE,0BAA0B,CAAA,GAAI,KAAK,OAAO,ECA3xL,MAACX,GAAE,WAAWO,GAAEP,GAAGA,EAAEE,GAAEF,GAAE,aAAaC,GAAEC,GAAEA,GAAE,aAAa,WAAW,CAAC,WAAWF,GAAGA,CAAC,CAAC,EAAE,OAAOU,GAAE,QAAQP,GAAE,OAAO,KAAK,OAAM,EAAG,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC,IAAIG,GAAE,IAAIH,GAAEE,GAAE,IAAIC,EAAC,IAAIM,GAAE,SAASH,GAAE,IAAIG,GAAE,cAAc,EAAE,EAAED,GAAEX,GAAUA,IAAP,MAAoB,OAAOA,GAAjB,UAAgC,OAAOA,GAAnB,WAAqBe,GAAE,MAAM,QAAQD,GAAEd,GAAGe,GAAEf,CAAC,GAAe,OAAOA,IAAI,OAAO,QAAQ,GAAtC,WAAwCgB,GAAE;AAAA,OAAcI,GAAE,sDAAsDC,GAAE,OAAOC,GAAE,KAAKT,GAAE,OAAO,KAAKG,EAAC,qBAAqBA,EAAC,KAAKA,EAAC;AAAA,0BAAsC,GAAG,EAAEO,GAAE,KAAKC,GAAE,KAAKL,GAAE,qCAAqCM,GAAEzB,GAAG,CAACO,KAAKL,KAAK,CAAC,WAAWF,EAAE,QAAQO,EAAE,OAAOL,CAAC,GAAGe,EAAEQ,GAAE,CAAC,EAAgBC,GAAE,OAAO,IAAI,cAAc,EAAEC,EAAE,OAAO,IAAI,aAAa,EAAEC,GAAE,IAAI,QAAQC,GAAEjB,GAAE,iBAAiBA,GAAE,GAAG,EAAE,SAASkB,GAAE9B,EAAEO,EAAE,CAAC,GAAG,CAACQ,GAAEf,CAAC,GAAG,CAACA,EAAE,eAAe,KAAK,EAAE,MAAM,MAAM,gCAAgC,EAAE,OAAgBC,KAAT,OAAWA,GAAE,WAAWM,CAAC,EAAEA,CAAC,CAAC,MAAMwB,GAAE,CAAC/B,EAAEO,IAAI,CAAC,MAAML,EAAEF,EAAE,OAAO,EAAEC,EAAE,CAAA,EAAG,IAAIK,EAAEM,EAAML,IAAJ,EAAM,QAAYA,IAAJ,EAAM,SAAS,GAAGE,EAAEW,GAAE,QAAQb,EAAE,EAAEA,EAAEL,EAAEK,IAAI,CAAC,MAAML,EAAEF,EAAEO,CAAC,EAAE,IAAII,EAAEI,EAAED,EAAE,GAAGE,EAAE,EAAE,KAAKA,EAAEd,EAAE,SAASO,EAAE,UAAUO,EAAED,EAAEN,EAAE,KAAKP,CAAC,EAASa,IAAP,OAAWC,EAAEP,EAAE,UAAUA,IAAIW,GAAUL,EAAE,CAAC,IAAX,MAAaN,EAAEY,GAAWN,EAAE,CAAC,IAAZ,OAAcN,EAAEa,GAAWP,EAAE,CAAC,IAAZ,QAAeI,GAAE,KAAKJ,EAAE,CAAC,CAAC,IAAIT,EAAE,OAAO,KAAKS,EAAE,CAAC,EAAE,GAAG,GAAGN,EAAEI,IAAYE,EAAE,CAAC,IAAZ,SAAgBN,EAAEI,IAAGJ,IAAII,GAAQE,EAAE,CAAC,IAAT,KAAYN,EAAEH,GAAGc,GAAEN,EAAE,IAAaC,EAAE,CAAC,IAAZ,OAAcD,EAAE,IAAIA,EAAEL,EAAE,UAAUM,EAAE,CAAC,EAAE,OAAOJ,EAAEI,EAAE,CAAC,EAAEN,EAAWM,EAAE,CAAC,IAAZ,OAAcF,GAAQE,EAAE,CAAC,IAAT,IAAWS,GAAED,IAAGd,IAAIe,IAAGf,IAAIc,GAAEd,EAAEI,GAAEJ,IAAIY,IAAGZ,IAAIa,GAAEb,EAAEW,IAAGX,EAAEI,GAAEP,EAAE,QAAQ,MAAMmB,EAAEhB,IAAII,IAAGb,EAAEO,EAAE,CAAC,EAAE,WAAW,IAAI,EAAE,IAAI,GAAGK,GAAGH,IAAIW,GAAElB,EAAEG,GAAES,GAAG,GAAGb,EAAE,KAAKU,CAAC,EAAET,EAAE,MAAM,EAAEY,CAAC,EAAEJ,GAAER,EAAE,MAAMY,CAAC,EAAEX,GAAEsB,GAAGvB,EAAEC,IAAQW,IAAL,GAAOP,EAAEkB,EAAE,CAAC,MAAM,CAACK,GAAE9B,EAAEY,GAAGZ,EAAEE,CAAC,GAAG,QAAYK,IAAJ,EAAM,SAAaA,IAAJ,EAAM,UAAU,GAAG,EAAEN,CAAC,CAAC,EAAC,IAAA+B,GAAC,MAAMxB,EAAC,CAAC,YAAY,CAAC,QAAQ,EAAE,WAAWD,CAAC,EAAEN,EAAE,CAAC,IAAII,EAAE,KAAK,MAAM,CAAA,EAAG,IAAIO,EAAE,EAAED,EAAE,EAAE,MAAMI,EAAE,EAAE,OAAO,EAAED,EAAE,KAAK,MAAM,CAACE,EAAEI,CAAC,EAAEW,GAAE,EAAExB,CAAC,EAAE,GAAG,KAAK,GAAGC,GAAE,cAAcQ,EAAEf,CAAC,EAAE4B,GAAE,YAAY,KAAK,GAAG,QAAYtB,IAAJ,GAAWA,IAAJ,EAAM,CAAC,MAAMP,EAAE,KAAK,GAAG,QAAQ,WAAWA,EAAE,YAAY,GAAGA,EAAE,UAAU,CAAC,CAAC,MAAaK,EAAEwB,GAAE,SAAQ,KAApB,MAAyBf,EAAE,OAAOC,GAAG,CAAC,GAAOV,EAAE,WAAN,EAAe,CAAC,GAAGA,EAAE,gBAAgB,UAAUL,KAAKK,EAAE,kBAAiB,EAAG,GAAGL,EAAE,SAASU,EAAC,EAAE,CAAC,MAAMH,EAAEa,EAAET,GAAG,EAAET,EAAEG,EAAE,aAAaL,CAAC,EAAE,MAAMG,EAAC,EAAEF,EAAE,eAAe,KAAKM,CAAC,EAAEO,EAAE,KAAK,CAAC,KAAK,EAAE,MAAMF,EAAE,KAAKX,EAAE,CAAC,EAAE,QAAQC,EAAE,KAAWD,EAAE,CAAC,IAAT,IAAWgC,GAAQhC,EAAE,CAAC,IAAT,IAAWiC,GAAQjC,EAAE,CAAC,IAAT,IAAWkC,GAAEC,EAAC,CAAC,EAAE/B,EAAE,gBAAgBL,CAAC,CAAC,MAAMA,EAAE,WAAWG,EAAC,IAAIW,EAAE,KAAK,CAAC,KAAK,EAAE,MAAMF,CAAC,CAAC,EAAEP,EAAE,gBAAgBL,CAAC,GAAG,GAAGmB,GAAE,KAAKd,EAAE,OAAO,EAAE,CAAC,MAAML,EAAEK,EAAE,YAAY,MAAMF,EAAC,EAAEI,EAAEP,EAAE,OAAO,EAAE,GAAGO,EAAE,EAAE,CAACF,EAAE,YAAYH,GAAEA,GAAE,YAAY,GAAG,QAAQA,EAAE,EAAEA,EAAEK,EAAEL,IAAIG,EAAE,OAAOL,EAAEE,CAAC,EAAEO,GAAC,CAAE,EAAEoB,GAAE,SAAQ,EAAGf,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAEF,CAAC,CAAC,EAAEP,EAAE,OAAOL,EAAEO,CAAC,EAAEE,GAAC,CAAE,CAAC,CAAC,CAAC,SAAaJ,EAAE,WAAN,EAAe,GAAGA,EAAE,OAAOC,GAAEQ,EAAE,KAAK,CAAC,KAAK,EAAE,MAAMF,CAAC,CAAC,MAAM,CAAC,IAAIZ,EAAE,GAAG,MAAWA,EAAEK,EAAE,KAAK,QAAQF,GAAEH,EAAE,CAAC,KAA5B,IAAgCc,EAAE,KAAK,CAAC,KAAK,EAAE,MAAMF,CAAC,CAAC,EAAEZ,GAAGG,GAAE,OAAO,CAAC,CAACS,GAAG,CAAC,CAAC,OAAO,cAAc,EAAEL,EAAE,CAAC,MAAM,EAAEK,GAAE,cAAc,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,EAAC,SAASyB,GAAErC,EAAEO,EAAEL,EAAEF,EAAEC,EAAE,CAAC,GAAGM,IAAImB,GAAE,OAAOnB,EAAE,IAAIG,EAAWT,IAAT,OAAWC,EAAE,OAAOD,CAAC,EAAEC,EAAE,KAAK,MAAMC,EAAEQ,GAAEJ,CAAC,EAAE,OAAOA,EAAE,gBAAgB,OAAOG,GAAG,cAAcP,IAAIO,GAAG,OAAO,EAAE,EAAWP,IAAT,OAAWO,EAAE,QAAQA,EAAE,IAAIP,EAAEH,CAAC,EAAEU,EAAE,KAAKV,EAAEE,EAAED,CAAC,GAAYA,IAAT,QAAYC,EAAE,OAAO,CAAA,GAAID,CAAC,EAAES,EAAER,EAAE,KAAKQ,GAAYA,IAAT,SAAaH,EAAE8B,GAAErC,EAAEU,EAAE,KAAKV,EAAEO,EAAE,MAAM,EAAEG,EAAET,CAAC,GAAGM,CAAC,CAAC,MAAM+B,EAAC,CAAC,YAAY,EAAE/B,EAAE,CAAC,KAAK,KAAK,CAAA,EAAG,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,KAAK,KAAKA,CAAC,CAAC,IAAI,YAAY,CAAC,OAAO,KAAK,KAAK,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQA,CAAC,EAAE,MAAM,CAAC,EAAE,KAAK,KAAKN,GAAG,GAAG,eAAeW,IAAG,WAAWL,EAAE,EAAE,EAAEsB,GAAE,YAAY5B,EAAE,IAAIS,EAAEmB,GAAE,WAAW,EAAE,EAAEvB,EAAE,EAAED,EAAE,EAAE,CAAC,EAAE,KAAcA,IAAT,QAAY,CAAC,GAAG,IAAIA,EAAE,MAAM,CAAC,IAAIE,EAAMF,EAAE,OAAN,EAAWE,EAAE,IAAIgC,GAAE7B,EAAEA,EAAE,YAAY,KAAK,CAAC,EAAML,EAAE,OAAN,EAAWE,EAAE,IAAIF,EAAE,KAAKK,EAAEL,EAAE,KAAKA,EAAE,QAAQ,KAAK,CAAC,EAAMA,EAAE,OAAN,IAAaE,EAAE,IAAIiC,GAAE9B,EAAE,KAAK,CAAC,GAAG,KAAK,KAAK,KAAKH,CAAC,EAAEF,EAAE,EAAE,EAAEC,CAAC,CAAC,CAAC,IAAID,GAAG,QAAQK,EAAEmB,GAAE,SAAQ,EAAG,IAAI,CAAC,OAAOA,GAAE,YAAYjB,GAAEX,CAAC,CAAC,EAAE,EAAE,CAAC,IAAIM,EAAE,EAAE,UAAU,KAAK,KAAK,KAAc,IAAT,SAAsB,EAAE,UAAX,QAAoB,EAAE,KAAK,EAAE,EAAEA,CAAC,EAAEA,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,KAAK,EAAEA,CAAC,CAAC,GAAGA,GAAG,CAAC,QAAC,MAAMgC,EAAC,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC,YAAY,EAAEhC,EAAE,EAAEN,EAAE,CAAC,KAAK,KAAK,EAAE,KAAK,KAAK0B,EAAE,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,KAAK,KAAKpB,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQN,EAAE,KAAK,KAAKA,GAAG,aAAa,EAAE,CAAC,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,KAAK,WAAW,MAAMM,EAAE,KAAK,KAAK,OAAgBA,IAAT,QAAiB,GAAG,WAAR,KAAmB,EAAEA,EAAE,YAAY,CAAC,CAAC,IAAI,WAAW,CAAC,OAAO,KAAK,IAAI,CAAC,IAAI,SAAS,CAAC,OAAO,KAAK,IAAI,CAAC,KAAK,EAAEA,EAAE,KAAK,CAAC,EAAE8B,GAAE,KAAK,EAAE9B,CAAC,EAAEI,GAAE,CAAC,EAAE,IAAIgB,GAAS,GAAN,MAAc,IAAL,IAAQ,KAAK,OAAOA,GAAG,KAAK,KAAI,EAAG,KAAK,KAAKA,GAAG,IAAI,KAAK,MAAM,IAAID,IAAG,KAAK,EAAE,CAAC,EAAW,EAAE,aAAX,OAAsB,KAAK,EAAE,CAAC,EAAW,EAAE,WAAX,OAAoB,KAAK,EAAE,CAAC,EAAEZ,GAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,WAAW,aAAa,EAAE,KAAK,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,OAAO,IAAI,KAAK,KAAI,EAAG,KAAK,KAAK,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,OAAOa,GAAGhB,GAAE,KAAK,IAAI,EAAE,KAAK,KAAK,YAAY,KAAK,EAAE,KAAK,EAAEC,GAAE,eAAe,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAOL,EAAE,WAAW,CAAC,EAAE,EAAEN,EAAY,OAAO,GAAjB,SAAmB,KAAK,KAAK,CAAC,GAAY,EAAE,KAAX,SAAgB,EAAE,GAAGO,GAAE,cAAcsB,GAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,OAAO,GAAG,GAAG,GAAG,KAAK,MAAM,OAAO7B,EAAE,KAAK,KAAK,EAAEM,CAAC,MAAM,CAAC,MAAMP,EAAE,IAAIsC,GAAErC,EAAE,IAAI,EAAEC,EAAEF,EAAE,EAAE,KAAK,OAAO,EAAEA,EAAE,EAAEO,CAAC,EAAE,KAAK,EAAEL,CAAC,EAAE,KAAK,KAAKF,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAIO,EAAEqB,GAAE,IAAI,EAAE,OAAO,EAAE,OAAgBrB,IAAT,QAAYqB,GAAE,IAAI,EAAE,QAAQrB,EAAE,IAAIC,GAAE,CAAC,CAAC,EAAED,CAAC,CAAC,EAAE,EAAE,CAACQ,GAAE,KAAK,IAAI,IAAI,KAAK,KAAK,CAAA,EAAG,KAAK,QAAQ,MAAMR,EAAE,KAAK,KAAK,IAAI,EAAEN,EAAE,EAAE,UAAUS,KAAK,EAAET,IAAIM,EAAE,OAAOA,EAAE,KAAK,EAAE,IAAIgC,GAAE,KAAK,EAAE9B,GAAC,CAAE,EAAE,KAAK,EAAEA,IAAG,EAAE,KAAK,KAAK,OAAO,CAAC,EAAE,EAAEF,EAAEN,CAAC,EAAE,EAAE,KAAKS,CAAC,EAAET,IAAIA,EAAEM,EAAE,SAAS,KAAK,KAAK,GAAG,EAAE,KAAK,YAAYN,CAAC,EAAEM,EAAE,OAAON,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,YAAYC,EAAE,CAAC,IAAI,KAAK,OAAO,GAAG,GAAGA,CAAC,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,EAAEK,GAAE,CAAC,EAAE,YAAYA,GAAE,CAAC,EAAE,OAAM,EAAG,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAU,KAAK,OAAd,SAAqB,KAAK,KAAK,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC,EAAC,MAAM6B,EAAC,CAAC,IAAI,SAAS,CAAC,OAAO,KAAK,QAAQ,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,YAAY,EAAE7B,EAAE,EAAEN,EAAES,EAAE,CAAC,KAAK,KAAK,EAAE,KAAK,KAAKiB,EAAE,KAAK,KAAK,OAAO,KAAK,QAAQ,EAAE,KAAK,KAAKpB,EAAE,KAAK,KAAKN,EAAE,KAAK,QAAQS,EAAE,EAAE,OAAO,GAAQ,EAAE,CAAC,IAAR,IAAgB,EAAE,CAAC,IAAR,IAAW,KAAK,KAAK,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,IAAI,MAAM,EAAE,KAAK,QAAQ,GAAG,KAAK,KAAKiB,CAAC,CAAC,KAAK,EAAEpB,EAAE,KAAK,EAAEN,EAAE,CAAC,MAAMS,EAAE,KAAK,QAAQ,IAAI,EAAE,GAAG,GAAYA,IAAT,OAAW,EAAE2B,GAAE,KAAK,EAAE9B,EAAE,CAAC,EAAE,EAAE,CAACI,GAAE,CAAC,GAAG,IAAI,KAAK,MAAM,IAAIe,GAAE,IAAI,KAAK,KAAK,OAAO,CAAC,MAAMzB,EAAE,EAAE,IAAIK,EAAED,EAAE,IAAI,EAAEK,EAAE,CAAC,EAAEJ,EAAE,EAAEA,EAAEI,EAAE,OAAO,EAAEJ,IAAID,EAAEgC,GAAE,KAAKpC,EAAE,EAAEK,CAAC,EAAEC,EAAED,CAAC,EAAED,IAAIqB,KAAIrB,EAAE,KAAK,KAAKC,CAAC,GAAG,IAAI,CAACK,GAAEN,CAAC,GAAGA,IAAI,KAAK,KAAKC,CAAC,EAAED,IAAIsB,EAAE,EAAEA,EAAE,IAAIA,IAAI,IAAItB,GAAG,IAAIK,EAAEJ,EAAE,CAAC,GAAG,KAAK,KAAKA,CAAC,EAAED,CAAC,CAAC,GAAG,CAACJ,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI0B,EAAE,KAAK,QAAQ,gBAAgB,KAAK,IAAI,EAAE,KAAK,QAAQ,aAAa,KAAK,KAAK,GAAG,EAAE,CAAC,CAAC,CAAA,IAAAc,GAAC,cAAgBL,EAAC,CAAC,aAAa,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,QAAQ,KAAK,IAAI,EAAE,IAAIT,EAAE,OAAO,CAAC,CAAC,KAAC,cAAgBS,EAAC,CAAC,aAAa,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,QAAQ,gBAAgB,KAAK,KAAK,CAAC,CAAC,GAAG,IAAIT,CAAC,CAAC,CAAC,KAAC,cAAgBS,EAAC,CAAC,YAAY,EAAE7B,EAAE,EAAEN,EAAES,EAAE,CAAC,MAAM,EAAEH,EAAE,EAAEN,EAAES,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,KAAK,EAAEH,EAAE,KAAK,CAAC,IAAI,EAAE8B,GAAE,KAAK,EAAE9B,EAAE,CAAC,GAAGoB,KAAKD,GAAE,OAAO,MAAM,EAAE,KAAK,KAAKzB,EAAE,IAAI0B,GAAG,IAAIA,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQjB,EAAE,IAAIiB,IAAI,IAAIA,GAAG1B,GAAGA,GAAG,KAAK,QAAQ,oBAAoB,KAAK,KAAK,KAAK,CAAC,EAAES,GAAG,KAAK,QAAQ,iBAAiB,KAAK,KAAK,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,YAAY,EAAE,CAAa,OAAO,KAAK,MAAxB,WAA6B,KAAK,KAAK,KAAK,KAAK,SAAS,MAAM,KAAK,QAAQ,CAAC,EAAE,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,EAAAgC,GAAC,KAAO,CAAC,YAAY,EAAEnC,EAAE,EAAE,CAAC,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK,OAAO,KAAK,KAAKA,EAAE,KAAK,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC8B,GAAE,KAAK,CAAC,CAAC,CAAC,EAAC,MAAMM,GAAE,CAA+B,EAAEJ,EAAmB,EAAEK,GAAE5C,GAAE,uBAAuB4C,KAAIpC,GAAE+B,EAAC,GAAGvC,GAAE,kBAAkB,CAAA,GAAI,KAAK,OAAO,EAAE,MAAM6C,GAAE,CAAC7C,EAAEO,EAAEL,IAAI,CAAC,MAAMD,EAAEC,GAAG,cAAcK,EAAE,IAAIG,EAAET,EAAE,WAAW,GAAYS,IAAT,OAAW,CAAC,MAAMV,EAAEE,GAAG,cAAc,KAAKD,EAAE,WAAWS,EAAE,IAAI6B,GAAEhC,EAAE,aAAaE,GAAC,EAAGT,CAAC,EAAEA,EAAE,OAAOE,GAAG,CAAA,CAAE,CAAC,CAAC,OAAOQ,EAAE,KAAKV,CAAC,EAAEU,CAAC,ECAh7N,MAAMR,GAAE,kBAAW,cAAgBF,EAAC,CAAC,aAAa,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,cAAc,CAAC,KAAK,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,iBAAgB,EAAG,OAAO,KAAK,cAAc,eAAe,EAAE,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,MAAMK,EAAE,KAAK,OAAM,EAAG,KAAK,aAAa,KAAK,cAAc,YAAY,KAAK,aAAa,MAAM,OAAO,CAAC,EAAE,KAAK,KAAKJ,GAAEI,EAAE,KAAK,WAAW,KAAK,aAAa,CAAC,CAAC,mBAAmB,CAAC,MAAM,kBAAiB,EAAG,KAAK,MAAM,aAAa,EAAE,CAAC,CAAC,sBAAsB,CAAC,MAAM,qBAAoB,EAAG,KAAK,MAAM,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAOA,EAAC,CAAC,EAACE,GAAE,cAAc,GAAGA,GAAE,UAAa,GAAGL,GAAE,2BAA2B,CAAC,WAAWK,EAAC,CAAC,EAAE,MAAMJ,GAAED,GAAE,0BAA0BC,KAAI,CAAC,WAAWI,EAAC,CAAC,GAAwDL,GAAE,qBAAqB,IAAI,KAAK,OAAO,ECA/xB,MAAMF,GAAEA,GAAG,CAACC,EAAEE,IAAI,CAAUA,WAAEA,EAAE,eAAe,IAAI,CAAC,eAAe,OAAOH,EAAEC,CAAC,CAAC,CAAC,EAAE,eAAe,OAAOD,EAAEC,CAAC,CAAC,ECAxG,MAAME,GAAE,CAAC,UAAU,GAAG,KAAK,OAAO,UAAUF,GAAE,QAAQ,GAAG,WAAWD,EAAC,EAAEK,GAAE,CAACL,EAAEG,GAAEF,EAAEI,IAAI,CAAC,KAAK,CAAC,KAAKC,EAAE,SAAS,CAAC,EAAED,EAAE,IAAIH,EAAE,WAAW,oBAAoB,IAAI,CAAC,EAAE,GAAYA,IAAT,QAAY,WAAW,oBAAoB,IAAI,EAAEA,EAAE,IAAI,GAAG,EAAaI,IAAX,YAAgBN,EAAE,OAAO,OAAOA,CAAC,GAAG,QAAQ,IAAIE,EAAE,IAAIG,EAAE,KAAKL,CAAC,EAAeM,IAAb,WAAe,CAAC,KAAK,CAAC,KAAK,CAAC,EAAED,EAAE,MAAM,CAAC,IAAIA,EAAE,CAAC,MAAMC,EAAEL,EAAE,IAAI,KAAK,IAAI,EAAEA,EAAE,IAAI,KAAK,KAAKI,CAAC,EAAE,KAAK,cAAc,EAAEC,EAAEN,EAAE,GAAGK,CAAC,CAAC,EAAE,KAAKJ,EAAE,CAAC,OAAgBA,IAAT,QAAY,KAAK,EAAE,EAAE,OAAOD,EAAEC,CAAC,EAAEA,CAAC,CAAC,CAAC,CAAC,GAAcK,IAAX,SAAa,CAAC,KAAK,CAAC,KAAK,CAAC,EAAED,EAAE,OAAO,SAASA,EAAE,CAAC,MAAMC,EAAE,KAAK,CAAC,EAAEL,EAAE,KAAK,KAAKI,CAAC,EAAE,KAAK,cAAc,EAAEC,EAAEN,EAAE,GAAGK,CAAC,CAAC,CAAC,CAAC,MAAM,MAAM,mCAAmCC,CAAC,CAAC,EAAE,SAASA,GAAEN,EAAE,CAAC,MAAM,CAACC,EAAEE,IAAc,OAAOA,GAAjB,SAAmBE,GAAEL,EAAEC,EAAEE,CAAC,GAAG,CAACH,EAAEC,EAAEE,IAAI,CAAC,MAAME,EAAEJ,EAAE,eAAeE,CAAC,EAAE,OAAOF,EAAE,YAAY,eAAeE,EAAEH,CAAC,EAAEK,EAAE,OAAO,yBAAyBJ,EAAEE,CAAC,EAAE,MAAM,GAAGH,EAAEC,EAAEE,CAAC,CAAC,CCA5yB,SAASE,EAAEA,EAAE,CAAC,OAAOL,GAAE,CAAC,GAAGK,EAAE,MAAM,GAAG,UAAU,EAAE,CAAC,CAAC,CCLvD,MAAMyC,GAAqB,GACrBC,GAAuB,IAEhBC,GAAyB,YAgBtC,SAASC,GAAoBC,EAA2BC,EAAuC,CAC7F,GAAI,OAAOD,GAAU,SAAU,OAC/B,MAAME,EAAUF,EAAM,KAAA,EACtB,GAAKE,EACL,OAAIA,EAAQ,QAAUD,EAAkBC,EACjCA,EAAQ,MAAM,EAAGD,CAAS,CACnC,CAEO,SAASE,GACdC,EACmB,CACnB,MAAMC,EACJN,GAAoBK,GAAO,KAAMR,EAAkB,GAAKE,GACpDQ,EAASP,GAAoBK,GAAO,QAAU,OAAWP,EAAoB,GAAK,KAKxF,MAAO,CAAE,QAHP,OAAOO,GAAO,SAAY,UAAYA,EAAM,QAAQ,KAAA,EAChDA,EAAM,QAAQ,KAAA,EACd,KACY,KAAAC,EAAM,OAAAC,CAAA,CAC1B,CAEO,SAASC,IAAsD,CACpE,OACSJ,GADL,OAAO,OAAW,IACc,CAAA,EAEF,CAChC,KAAM,OAAO,4BACb,OAAQ,OAAO,6BAAA,CAJqB,CAMxC,CChDA,MAAMK,GAAM,+BAiBL,SAASC,IAA2B,CAMzC,MAAMC,EAAuB,CAC3B,WAJO,GADO,SAAS,WAAa,SAAW,MAAQ,IACxC,MAAM,SAAS,IAAI,GAKlC,MAAO,GACP,WAAY,OACZ,qBAAsB,OACtB,MAAO,SACP,cAAe,GACf,iBAAkB,GAClB,WAAY,GACZ,aAAc,GACd,mBAAoB,CAAA,CAAC,EAGvB,GAAI,CACF,MAAMC,EAAM,aAAa,QAAQH,EAAG,EACpC,GAAI,CAACG,EAAK,OAAOD,EACjB,MAAME,EAAS,KAAK,MAAMD,CAAG,EAC7B,MAAO,CACL,WACE,OAAOC,EAAO,YAAe,UAAYA,EAAO,WAAW,KAAA,EACvDA,EAAO,WAAW,KAAA,EAClBF,EAAS,WACf,MAAO,OAAOE,EAAO,OAAU,SAAWA,EAAO,MAAQF,EAAS,MAClE,WACE,OAAOE,EAAO,YAAe,UAAYA,EAAO,WAAW,KAAA,EACvDA,EAAO,WAAW,KAAA,EAClBF,EAAS,WACf,qBACE,OAAOE,EAAO,sBAAyB,UACvCA,EAAO,qBAAqB,OACxBA,EAAO,qBAAqB,OAC3B,OAAOA,EAAO,YAAe,UAC5BA,EAAO,WAAW,QACpBF,EAAS,qBACf,MACEE,EAAO,QAAU,SACjBA,EAAO,QAAU,QACjBA,EAAO,QAAU,SACbA,EAAO,MACPF,EAAS,MACf,cACE,OAAOE,EAAO,eAAkB,UAC5BA,EAAO,cACPF,EAAS,cACf,iBACE,OAAOE,EAAO,kBAAqB,UAC/BA,EAAO,iBACPF,EAAS,iBACf,WACE,OAAOE,EAAO,YAAe,UAC7BA,EAAO,YAAc,IACrBA,EAAO,YAAc,GACjBA,EAAO,WACPF,EAAS,WACf,aACE,OAAOE,EAAO,cAAiB,UAC3BA,EAAO,aACPF,EAAS,aACf,mBACE,OAAOE,EAAO,oBAAuB,UACrCA,EAAO,qBAAuB,KAC1BA,EAAO,mBACPF,EAAS,kBAAA,CAEnB,MAAQ,CACN,OAAOA,CACT,CACF,CAEO,SAASG,GAAaC,EAAkB,CAC7C,aAAa,QAAQN,GAAK,KAAK,UAAUM,CAAI,CAAC,CAChD,CCzFO,SAASC,GACdC,EAC8B,CAC9B,MAAML,GAAOK,GAAc,IAAI,KAAA,EAC/B,GAAI,CAACL,EAAK,OAAO,KACjB,MAAMM,EAAQN,EAAI,MAAM,GAAG,EAAE,OAAO,OAAO,EAE3C,GADIM,EAAM,OAAS,GACfA,EAAM,CAAC,IAAM,QAAS,OAAO,KACjC,MAAMC,EAAUD,EAAM,CAAC,GAAG,KAAA,EACpBE,EAAOF,EAAM,MAAM,CAAC,EAAE,KAAK,GAAG,EACpC,MAAI,CAACC,GAAW,CAACC,EAAa,KACvB,CAAE,QAAAD,EAAS,KAAAC,CAAA,CACpB,CCfO,MAAMC,GAAa,CACxB,CAAE,MAAO,OAAQ,KAAM,CAAC,MAAM,CAAA,EAC9B,CACE,MAAO,UACP,KAAM,CAAC,WAAY,WAAY,YAAa,WAAY,MAAM,CAAA,EAEhE,CAAE,MAAO,QAAS,KAAM,CAAC,SAAU,OAAO,CAAA,EAC1C,CAAE,MAAO,WAAY,KAAM,CAAC,SAAU,QAAS,MAAM,CAAA,CACvD,EAeMC,GAAiC,CACrC,SAAU,YACV,SAAU,YACV,UAAW,aACX,SAAU,YACV,KAAM,QACN,OAAQ,UACR,MAAO,SACP,KAAM,QACN,OAAQ,UACR,MAAO,SACP,KAAM,OACR,EAEMC,GAAc,IAAI,IACtB,OAAO,QAAQD,EAAS,EAAE,IAAI,CAAC,CAACE,EAAKC,CAAI,IAAM,CAACA,EAAMD,CAAU,CAAC,CACnE,EAEO,SAASE,GAAkBC,EAA0B,CAC1D,GAAI,CAACA,EAAU,MAAO,GACtB,IAAIC,EAAOD,EAAS,KAAA,EAEpB,OADKC,EAAK,WAAW,GAAG,IAAGA,EAAO,IAAIA,CAAI,IACtCA,IAAS,IAAY,IACrBA,EAAK,SAAS,GAAG,MAAUA,EAAK,MAAM,EAAG,EAAE,GACxCA,EACT,CAEO,SAASC,GAAcJ,EAAsB,CAClD,GAAI,CAACA,EAAM,MAAO,IAClB,IAAIK,EAAaL,EAAK,KAAA,EACtB,OAAKK,EAAW,WAAW,GAAG,IAAGA,EAAa,IAAIA,CAAU,IACxDA,EAAW,OAAS,GAAKA,EAAW,SAAS,GAAG,IAClDA,EAAaA,EAAW,MAAM,EAAG,EAAE,GAE9BA,CACT,CAEO,SAASC,GAAWP,EAAUG,EAAW,GAAY,CAC1D,MAAMC,EAAOF,GAAkBC,CAAQ,EACjCF,EAAOH,GAAUE,CAAG,EAC1B,OAAOI,EAAO,GAAGA,CAAI,GAAGH,CAAI,GAAKA,CACnC,CAEO,SAASO,GAAYC,EAAkBN,EAAW,GAAgB,CACvE,MAAMC,EAAOF,GAAkBC,CAAQ,EACvC,IAAIF,EAAOQ,GAAY,IACnBL,IACEH,IAASG,EACXH,EAAO,IACEA,EAAK,WAAW,GAAGG,CAAI,GAAG,IACnCH,EAAOA,EAAK,MAAMG,EAAK,MAAM,IAGjC,IAAIE,EAAaD,GAAcJ,CAAI,EAAE,YAAA,EAErC,OADIK,EAAW,SAAS,aAAa,IAAGA,EAAa,KACjDA,IAAe,IAAY,OACxBP,GAAY,IAAIO,CAAU,GAAK,IACxC,CAEO,SAASI,GAA0BD,EAA0B,CAClE,IAAIH,EAAaD,GAAcI,CAAQ,EAIvC,GAHIH,EAAW,SAAS,aAAa,IACnCA,EAAaD,GAAcC,EAAW,MAAM,EAAG,GAAqB,CAAC,GAEnEA,IAAe,IAAK,MAAO,GAC/B,MAAMK,EAAWL,EAAW,MAAM,GAAG,EAAE,OAAO,OAAO,EACrD,GAAIK,EAAS,SAAW,EAAG,MAAO,GAClC,QAAS7E,EAAI,EAAGA,EAAI6E,EAAS,OAAQ7E,IAAK,CACxC,MAAM8E,EAAY,IAAID,EAAS,MAAM7E,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,YAAA,EACpD,GAAIiE,GAAY,IAAIa,CAAS,EAAG,CAC9B,MAAMC,EAASF,EAAS,MAAM,EAAG7E,CAAC,EAClC,OAAO+E,EAAO,OAAS,IAAIA,EAAO,KAAK,GAAG,CAAC,GAAK,EAClD,CACF,CACA,MAAO,IAAIF,EAAS,KAAK,GAAG,CAAC,EAC/B,CAEO,SAASG,GAAWd,EAAoB,CAC7C,OAAQA,EAAA,CACN,IAAK,OACH,MAAO,gBACT,IAAK,WACH,MAAO,WACT,IAAK,WACH,MAAO,OACT,IAAK,YACH,MAAO,QACT,IAAK,WACH,MAAO,WACT,IAAK,OACH,MAAO,SACT,IAAK,SACH,MAAO,MACT,IAAK,QACH,MAAO,UACT,IAAK,SACH,MAAO,WACT,IAAK,QACH,MAAO,MACT,IAAK,OACH,MAAO,aACT,QACE,MAAO,QAAA,CAEb,CAEO,SAASe,GAAYf,EAAU,CACpC,OAAQA,EAAA,CACN,IAAK,WACH,MAAO,WACT,IAAK,WACH,MAAO,WACT,IAAK,YACH,MAAO,YACT,IAAK,WACH,MAAO,WACT,IAAK,OACH,MAAO,YACT,IAAK,SACH,MAAO,SACT,IAAK,QACH,MAAO,QACT,IAAK,OACH,MAAO,OACT,IAAK,SACH,MAAO,SACT,IAAK,QACH,MAAO,QACT,IAAK,OACH,MAAO,OACT,QACE,MAAO,SAAA,CAEb,CAEO,SAASgB,GAAehB,EAAU,CACvC,OAAQA,EAAA,CACN,IAAK,WACH,MAAO,wDACT,IAAK,WACH,MAAO,gCACT,IAAK,YACH,MAAO,qDACT,IAAK,WACH,MAAO,2DACT,IAAK,OACH,MAAO,6CACT,IAAK,SACH,MAAO,mDACT,IAAK,QACH,MAAO,sDACT,IAAK,OACH,MAAO,uDACT,IAAK,SACH,MAAO,yCACT,IAAK,QACH,MAAO,mDACT,IAAK,OACH,MAAO,sCACT,QACE,MAAO,EAAA,CAEb,CCtLO,MAAMiB,EAAQ,CAEnB,cAAeC,4GACf,SAAUA,qJACV,KAAMA,kLACN,MAAOA,iMACP,SAAUA,uQACV,IAAKA,6FACL,QAASA,iKACT,SAAUA,moBACV,IAAKA,obACL,WAAYA,4LACZ,OAAQA,qKAGR,KAAMA,mJACN,EAAGA,+EACH,MAAOA,8DACP,KAAMA,8JACN,OAAQA,4FACR,MAAOA,yhBACP,KAAMA,6GACN,OAAQA,qOAGR,OAAQA,uMACR,SAAUA,2MACV,KAAMA,4KACN,QAASA,0HACT,UAAWA,8JACX,MAAOA,kJACP,MAAOA,6KACP,WAAYA,iHACZ,KAAMA,kJACN,OAAQA,mEACR,OAAQA,63BACV,ECtCMC,GAAe,2DACfC,GAAe,4BACfC,GAAkB,8DAExB,SAASC,GAAU7C,EAAe8C,EAAgC,CAE1C,OAAO9C,EAAM,UAAA,CAErC,CAEO,SAAS+C,GACdC,EACAC,EAIQ,CAER,GADI,CAACD,GACD,CAACN,GAAa,KAAKM,CAAI,EAAG,OAAOA,EAKrC,IAAIE,EAAUF,EACVL,GAAa,KAAKO,CAAO,GAC3BP,GAAa,UAAY,EACzBO,EAAUA,EAAQ,QAAQP,GAAc,EAAE,GAE1CA,GAAa,UAAY,EAG3BC,GAAgB,UAAY,EAC5B,IAAIO,EAAS,GACTC,EAAY,EACZC,EAAa,GAEjB,UAAWC,KAASJ,EAAQ,SAASN,EAAe,EAAG,CACrD,MAAMW,EAAMD,EAAM,OAAS,EACrBE,EAAUF,EAAM,CAAC,IAAM,IAExBD,EAKMG,IACTH,EAAa,KALbF,GAAUD,EAAQ,MAAME,EAAWG,CAAG,EACjCC,IACHH,EAAa,KAMjBD,EAAYG,EAAMD,EAAM,CAAC,EAAE,MAC7B,CAGE,OAAAH,GAAUD,EAAQ,MAAME,CAAS,EAG5BP,GAAUM,CAAgB,CACnC,CC1DO,SAASM,GAASC,EAA4B,CACnD,MAAI,CAACA,GAAMA,IAAO,EAAU,MACrB,IAAI,KAAKA,CAAE,EAAE,eAAA,CACtB,CAEO,SAASC,EAAUD,EAA4B,CACpD,GAAI,CAACA,GAAMA,IAAO,EAAG,MAAO,MAC5B,MAAME,EAAO,KAAK,IAAA,EAAQF,EAC1B,GAAIE,EAAO,EAAG,MAAO,WACrB,MAAMC,EAAM,KAAK,MAAMD,EAAO,GAAI,EAClC,GAAIC,EAAM,GAAI,MAAO,GAAGA,CAAG,QAC3B,MAAMC,EAAM,KAAK,MAAMD,EAAM,EAAE,EAC/B,GAAIC,EAAM,GAAI,MAAO,GAAGA,CAAG,QAC3B,MAAMC,EAAK,KAAK,MAAMD,EAAM,EAAE,EAC9B,OAAIC,EAAK,GAAW,GAAGA,CAAE,QAElB,GADK,KAAK,MAAMA,EAAK,EAAE,CACjB,OACf,CAEO,SAASC,GAAiBN,EAA4B,CAC3D,GAAI,CAACA,GAAMA,IAAO,EAAG,MAAO,MAC5B,GAAIA,EAAK,IAAM,MAAO,GAAGA,CAAE,KAC3B,MAAMG,EAAM,KAAK,MAAMH,EAAK,GAAI,EAChC,GAAIG,EAAM,GAAI,MAAO,GAAGA,CAAG,IAC3B,MAAMC,EAAM,KAAK,MAAMD,EAAM,EAAE,EAC/B,GAAIC,EAAM,GAAI,MAAO,GAAGA,CAAG,IAC3B,MAAMC,EAAK,KAAK,MAAMD,EAAM,EAAE,EAC9B,OAAIC,EAAK,GAAW,GAAGA,CAAE,IAElB,GADK,KAAK,MAAMA,EAAK,EAAE,CACjB,GACf,CAEO,SAASE,GAAWC,EAAmD,CAC5E,MAAI,CAACA,GAAUA,EAAO,SAAW,EAAU,OACpCA,EAAO,OAAQhG,GAAmB,GAAQA,GAAKA,EAAE,KAAA,EAAO,EAAE,KAAK,IAAI,CAC5E,CAEO,SAASiG,GAAUnE,EAAeoE,EAAM,IAAa,CAC1D,OAAIpE,EAAM,QAAUoE,EAAYpE,EACzB,GAAGA,EAAM,MAAM,EAAG,KAAK,IAAI,EAAGoE,EAAM,CAAC,CAAC,CAAC,GAChD,CAEO,SAASC,GAAarE,EAAeoE,EAI1C,CACA,OAAIpE,EAAM,QAAUoE,EACX,CAAE,KAAMpE,EAAO,UAAW,GAAO,MAAOA,EAAM,MAAA,EAEhD,CACL,KAAMA,EAAM,MAAM,EAAG,KAAK,IAAI,EAAGoE,CAAG,CAAC,EACrC,UAAW,GACX,MAAOpE,EAAM,MAAA,CAEjB,CAEO,SAASsE,GAAStE,EAAeuE,EAA0B,CAChE,MAAM,EAAI,OAAOvE,CAAK,EACtB,OAAO,OAAO,SAAS,CAAC,EAAI,EAAIuE,CAClC,CASO,SAASC,GAAkBxE,EAAuB,CACvD,OAAO+C,GAA2B/C,CAA0C,CAC9E,CCvEA,MAAMyE,GAAkB,mBAClBC,GAAoB,CACxB,UACA,WACA,WACA,SACA,QACA,UACA,WACA,QACA,SACA,OACA,gBACA,aACF,EAEMC,OAAgB,QAChBC,OAAoB,QAE1B,SAASC,GAAwBC,EAAyB,CAExD,MADI,mCAAmC,KAAKA,CAAM,GAC9C,kCAAkC,KAAKA,CAAM,EAAU,GACpDJ,GAAkB,KAAMK,GAAUD,EAAO,WAAW,GAAGC,CAAK,GAAG,CAAC,CACzE,CAEO,SAASC,GAAchC,EAAsB,CAClD,MAAMM,EAAQN,EAAK,MAAMyB,EAAe,EACxC,GAAI,CAACnB,EAAO,OAAON,EACnB,MAAM8B,EAASxB,EAAM,CAAC,GAAK,GAC3B,OAAKuB,GAAwBC,CAAM,EAC5B9B,EAAK,MAAMM,EAAM,CAAC,EAAE,MAAM,EADYN,CAE/C,CAEO,SAASiC,GAAYC,EAAiC,CAC3D,MAAM9G,EAAI8G,EACJC,EAAO,OAAO/G,EAAE,MAAS,SAAWA,EAAE,KAAO,GAC7CgH,EAAUhH,EAAE,QAClB,GAAI,OAAOgH,GAAY,SAErB,OADkBD,IAAS,YAAcX,GAAkBY,CAAO,EAAIJ,GAAcI,CAAO,EAG7F,GAAI,MAAM,QAAQA,CAAO,EAAG,CAC1B,MAAMnE,EAAQmE,EACX,IAAKzH,GAAM,CACV,MAAM0H,EAAO1H,EACb,OAAI0H,EAAK,OAAS,QAAU,OAAOA,EAAK,MAAS,SAAiBA,EAAK,KAChE,IACT,CAAC,EACA,OAAQnH,GAAmB,OAAOA,GAAM,QAAQ,EACnD,GAAI+C,EAAM,OAAS,EAAG,CACpB,MAAMqE,EAASrE,EAAM,KAAK;AAAA,CAAI,EAE9B,OADkBkE,IAAS,YAAcX,GAAkBc,CAAM,EAAIN,GAAcM,CAAM,CAE3F,CACF,CACA,OAAI,OAAOlH,EAAE,MAAS,SACF+G,IAAS,YAAcX,GAAkBpG,EAAE,IAAI,EAAI4G,GAAc5G,EAAE,IAAI,EAGpF,IACT,CAEO,SAASmH,GAAkBL,EAAiC,CACjE,GAAI,CAACA,GAAW,OAAOA,GAAY,SAAU,OAAOD,GAAYC,CAAO,EACvE,MAAMM,EAAMN,EACZ,GAAIP,GAAU,IAAIa,CAAG,SAAUb,GAAU,IAAIa,CAAG,GAAK,KACrD,MAAMxF,EAAQiF,GAAYC,CAAO,EACjC,OAAAP,GAAU,IAAIa,EAAKxF,CAAK,EACjBA,CACT,CAEO,SAASyF,GAAgBP,EAAiC,CAE/D,MAAME,EADIF,EACQ,QACZjE,EAAkB,CAAA,EACxB,GAAI,MAAM,QAAQmE,CAAO,EACvB,UAAWzH,KAAKyH,EAAS,CACvB,MAAMC,EAAO1H,EACb,GAAI0H,EAAK,OAAS,YAAc,OAAOA,EAAK,UAAa,SAAU,CACjE,MAAMnC,EAAUmC,EAAK,SAAS,KAAA,EAC1BnC,GAASjC,EAAM,KAAKiC,CAAO,CACjC,CACF,CAEF,GAAIjC,EAAM,OAAS,EAAG,OAAOA,EAAM,KAAK;AAAA,CAAI,EAG5C,MAAMyE,EAAUC,GAAeT,CAAO,EACtC,GAAI,CAACQ,EAAS,OAAO,KAMrB,MAAME,EALU,CACd,GAAGF,EAAQ,SACT,6DAAA,CACF,EAGC,IAAKtH,IAAOA,EAAE,CAAC,GAAK,IAAI,KAAA,CAAM,EAC9B,OAAO,OAAO,EACjB,OAAOwH,EAAU,OAAS,EAAIA,EAAU,KAAK;AAAA,CAAI,EAAI,IACvD,CAEO,SAASC,GAAsBX,EAAiC,CACrE,GAAI,CAACA,GAAW,OAAOA,GAAY,SAAU,OAAOO,GAAgBP,CAAO,EAC3E,MAAMM,EAAMN,EACZ,GAAIN,GAAc,IAAIY,CAAG,SAAUZ,GAAc,IAAIY,CAAG,GAAK,KAC7D,MAAMxF,EAAQyF,GAAgBP,CAAO,EACrC,OAAAN,GAAc,IAAIY,EAAKxF,CAAK,EACrBA,CACT,CAEO,SAAS2F,GAAeT,EAAiC,CAC9D,MAAM9G,EAAI8G,EACJE,EAAUhH,EAAE,QAClB,GAAI,OAAOgH,GAAY,SAAU,OAAOA,EACxC,GAAI,MAAM,QAAQA,CAAO,EAAG,CAC1B,MAAMnE,EAAQmE,EACX,IAAKzH,GAAM,CACV,MAAM0H,EAAO1H,EACb,OAAI0H,EAAK,OAAS,QAAU,OAAOA,EAAK,MAAS,SAAiBA,EAAK,KAChE,IACT,CAAC,EACA,OAAQnH,GAAmB,OAAOA,GAAM,QAAQ,EACnD,GAAI+C,EAAM,OAAS,EAAG,OAAOA,EAAM,KAAK;AAAA,CAAI,CAC9C,CACA,OAAI,OAAO7C,EAAE,MAAS,SAAiBA,EAAE,KAClC,IACT,CAEO,SAAS0H,GAAwB9C,EAAsB,CAC5D,MAAM9C,EAAU8C,EAAK,KAAA,EACrB,GAAI,CAAC9C,EAAS,MAAO,GACrB,MAAM6F,EAAQ7F,EACX,MAAM,OAAO,EACb,IAAK8F,GAASA,EAAK,KAAA,CAAM,EACzB,OAAO,OAAO,EACd,IAAKA,GAAS,IAAIA,CAAI,GAAG,EAC5B,OAAOD,EAAM,OAAS,CAAC,eAAgB,GAAGA,CAAK,EAAE,KAAK;AAAA,CAAI,EAAI,EAChE,CCrIA,SAASE,GAAcC,EAA2B,CAChDA,EAAM,CAAC,EAAKA,EAAM,CAAC,EAAI,GAAQ,GAC/BA,EAAM,CAAC,EAAKA,EAAM,CAAC,EAAI,GAAQ,IAE/B,IAAIC,EAAM,GACV,QAAS9I,EAAI,EAAGA,EAAI6I,EAAM,OAAQ7I,IAChC8I,GAAOD,EAAM7I,CAAC,EAAG,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,EAG/C,MAAO,GAAG8I,EAAI,MAAM,EAAG,CAAC,CAAC,IAAIA,EAAI,MAAM,EAAG,EAAE,CAAC,IAAIA,EAAI,MAAM,GAAI,EAAE,CAAC,IAAIA,EAAI,MACxE,GACA,EAAA,CACD,IAAIA,EAAI,MAAM,EAAE,CAAC,EACpB,CAEA,SAASC,IAA8B,CACrC,MAAMF,EAAQ,IAAI,WAAW,EAAE,EACzBG,EAAM,KAAK,IAAA,EACjB,QAAShJ,EAAI,EAAGA,EAAI6I,EAAM,OAAQ7I,IAAK6I,EAAM7I,CAAC,EAAI,KAAK,MAAM,KAAK,OAAA,EAAW,GAAG,EAChF,OAAA6I,EAAM,CAAC,GAAKG,EAAM,IAClBH,EAAM,CAAC,GAAMG,IAAQ,EAAK,IAC1BH,EAAM,CAAC,GAAMG,IAAQ,GAAM,IAC3BH,EAAM,CAAC,GAAMG,IAAQ,GAAM,IACpBH,CACT,CAEO,SAASI,GAAaC,EAAgC,WAAW,OAAgB,CACtF,GAAIA,GAAc,OAAOA,EAAW,YAAe,WAAY,OAAOA,EAAW,WAAA,EAEjF,GAAIA,GAAc,OAAOA,EAAW,iBAAoB,WAAY,CAClE,MAAML,EAAQ,IAAI,WAAW,EAAE,EAC/B,OAAAK,EAAW,gBAAgBL,CAAK,EACzBD,GAAcC,CAAK,CAC5B,CAEA,OAAOD,GAAcG,IAAiB,CACxC,CCdA,eAAsBI,GAAgBC,EAAkB,CACtD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,YAAc,GACpBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,eAAgB,CACtD,WAAYA,EAAM,WAClB,MAAO,GAAA,CACR,EACDA,EAAM,aAAe,MAAM,QAAQC,EAAI,QAAQ,EAAIA,EAAI,SAAW,CAAA,EAClED,EAAM,kBAAoBC,EAAI,eAAiB,IACjD,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,YAAc,EACtB,EACF,CAEA,eAAsBG,GAAgBH,EAAkBvB,EAAmC,CACzF,GAAI,CAACuB,EAAM,QAAU,CAACA,EAAM,UAAW,MAAO,GAC9C,MAAMI,EAAM3B,EAAQ,KAAA,EACpB,GAAI,CAAC2B,EAAK,MAAO,GAEjB,MAAMR,EAAM,KAAK,IAAA,EACjBI,EAAM,aAAe,CACnB,GAAGA,EAAM,aACT,CACE,KAAM,OACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAMI,EAAK,EACrC,UAAWR,CAAA,CACb,EAGFI,EAAM,YAAc,GACpBA,EAAM,UAAY,KAClB,MAAMK,EAAQR,GAAA,EACdG,EAAM,UAAYK,EAClBL,EAAM,WAAa,GACnBA,EAAM,oBAAsBJ,EAC5B,GAAI,CACF,aAAMI,EAAM,OAAO,QAAQ,YAAa,CACtC,WAAYA,EAAM,WAClB,QAASI,EACT,QAAS,GACT,eAAgBC,CAAA,CACjB,EACM,EACT,OAASH,EAAK,CACZ,MAAMI,EAAQ,OAAOJ,CAAG,EACxB,OAAAF,EAAM,UAAY,KAClBA,EAAM,WAAa,KACnBA,EAAM,oBAAsB,KAC5BA,EAAM,UAAYM,EAClBN,EAAM,aAAe,CACnB,GAAGA,EAAM,aACT,CACE,KAAM,YACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,UAAYM,EAAO,EACnD,UAAW,KAAK,IAAA,CAAI,CACtB,EAEK,EACT,QAAA,CACEN,EAAM,YAAc,EACtB,CACF,CAEA,eAAsBO,GAAaP,EAAoC,CACrE,GAAI,CAACA,EAAM,QAAU,CAACA,EAAM,UAAW,MAAO,GAC9C,MAAMK,EAAQL,EAAM,UACpB,GAAI,CACF,aAAMA,EAAM,OAAO,QACjB,aACAK,EACI,CAAE,WAAYL,EAAM,WAAY,MAAAK,GAChC,CAAE,WAAYL,EAAM,UAAA,CAAW,EAE9B,EACT,OAASE,EAAK,CACZ,OAAAF,EAAM,UAAY,OAAOE,CAAG,EACrB,EACT,CACF,CAEO,SAASM,GACdR,EACAS,EACA,CAGA,GAFI,CAACA,GACDA,EAAQ,aAAeT,EAAM,YAC7BS,EAAQ,OAAST,EAAM,WAAaS,EAAQ,QAAUT,EAAM,UAC9D,OAAO,KAET,GAAIS,EAAQ,QAAU,QAAS,CAC7B,MAAMpG,EAAOmE,GAAYiC,EAAQ,OAAO,EACxC,GAAI,OAAOpG,GAAS,SAAU,CAC5B,MAAMqG,EAAUV,EAAM,YAAc,IAChC,CAACU,GAAWrG,EAAK,QAAUqG,EAAQ,UACrCV,EAAM,WAAa3F,EAEvB,CACF,MAAWoG,EAAQ,QAAU,SAIlBA,EAAQ,QAAU,WAH3BT,EAAM,WAAa,KACnBA,EAAM,UAAY,KAClBA,EAAM,oBAAsB,MAKnBS,EAAQ,QAAU,UAC3BT,EAAM,WAAa,KACnBA,EAAM,UAAY,KAClBA,EAAM,oBAAsB,KAC5BA,EAAM,UAAYS,EAAQ,cAAgB,cAE5C,OAAOA,EAAQ,KACjB,CC/HA,eAAsBE,GAAaX,EAAsB,CACvD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,gBACV,CAAAA,EAAM,gBAAkB,GACxBA,EAAM,cAAgB,KACtB,GAAI,CACF,MAAMY,EAAkC,CACtC,cAAeZ,EAAM,sBACrB,eAAgBA,EAAM,sBAAA,EAElBa,EAAgBhD,GAASmC,EAAM,qBAAsB,CAAC,EACtDc,EAAQjD,GAASmC,EAAM,oBAAqB,CAAC,EAC/Ca,EAAgB,IAAGD,EAAO,cAAgBC,GAC1CC,EAAQ,IAAGF,EAAO,MAAQE,GAC9B,MAAMb,EAAO,MAAMD,EAAM,OAAO,QAAQ,gBAAiBY,CAAM,EAG3DX,MAAW,eAAiBA,EAClC,OAASC,EAAK,CACZF,EAAM,cAAgB,OAAOE,CAAG,CAClC,QAAA,CACEF,EAAM,gBAAkB,EAC1B,EACF,CAEA,eAAsBe,GACpBf,EACAgB,EACAC,EAMA,CACA,GAAI,CAACjB,EAAM,QAAU,CAACA,EAAM,UAAW,OACvC,MAAMY,EAAkC,CAAE,IAAAI,CAAA,EACtC,UAAWC,IAAOL,EAAO,MAAQK,EAAM,OACvC,kBAAmBA,IAAOL,EAAO,cAAgBK,EAAM,eACvD,iBAAkBA,IAAOL,EAAO,aAAeK,EAAM,cACrD,mBAAoBA,IAAOL,EAAO,eAAiBK,EAAM,gBAC7D,GAAI,CACF,MAAMjB,EAAM,OAAO,QAAQ,iBAAkBY,CAAM,EACnD,MAAMD,GAAaX,CAAK,CAC1B,OAASE,EAAK,CACZF,EAAM,cAAgB,OAAOE,CAAG,CAClC,CACF,CAEA,eAAsBgB,GAAclB,EAAsBgB,EAAa,CAMrE,GALI,GAAChB,EAAM,QAAU,CAACA,EAAM,WACxBA,EAAM,iBAIN,CAHc,OAAO,QACvB,mBAAmBgB,CAAG;AAAA;AAAA,uDAAA,GAGxB,CAAAhB,EAAM,gBAAkB,GACxBA,EAAM,cAAgB,KACtB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,kBAAmB,CAAE,IAAAgB,EAAK,iBAAkB,GAAM,EAC7E,MAAML,GAAaX,CAAK,CAC1B,OAASE,EAAK,CACZF,EAAM,cAAgB,OAAOE,CAAG,CAClC,QAAA,CACEF,EAAM,gBAAkB,EAC1B,EACF,CChFA,MAAMmB,GAAoB,GACpBC,GAA0B,GAC1BC,GAAyB,KAgC/B,SAASC,GAAsB/H,EAA+B,CAC5D,GAAI,CAACA,GAAS,OAAOA,GAAU,SAAU,OAAO,KAChD,MAAMgI,EAAShI,EACf,GAAI,OAAOgI,EAAO,MAAS,gBAAiBA,EAAO,KACnD,MAAM5C,EAAU4C,EAAO,QACvB,GAAI,CAAC,MAAM,QAAQ5C,CAAO,EAAG,OAAO,KACpC,MAAMnE,EAAQmE,EACX,IAAKC,GAAS,CACb,GAAI,CAACA,GAAQ,OAAOA,GAAS,SAAU,OAAO,KAC9C,MAAM4C,EAAQ5C,EACd,OAAI4C,EAAM,OAAS,QAAU,OAAOA,EAAM,MAAS,SAAiBA,EAAM,KACnE,IACT,CAAC,EACA,OAAQC,GAAyB,EAAQA,CAAK,EACjD,OAAIjH,EAAM,SAAW,EAAU,KACxBA,EAAM,KAAK;AAAA,CAAI,CACxB,CAEA,SAASkH,GAAiBnI,EAA+B,CACvD,GAAIA,GAAU,KAA6B,OAAO,KAClD,GAAI,OAAOA,GAAU,UAAY,OAAOA,GAAU,UAChD,OAAO,OAAOA,CAAK,EAErB,MAAMoI,EAAcL,GAAsB/H,CAAK,EAC/C,IAAIgD,EACJ,GAAI,OAAOhD,GAAU,SACnBgD,EAAOhD,UACEoI,EACTpF,EAAOoF,MAEP,IAAI,CACFpF,EAAO,KAAK,UAAUhD,EAAO,KAAM,CAAC,CACtC,MAAQ,CACNgD,EAAO,OAAOhD,CAAK,CACrB,CAEF,MAAMqI,EAAYhE,GAAarB,EAAM8E,EAAsB,EAC3D,OAAKO,EAAU,UACR,GAAGA,EAAU,IAAI;AAAA;AAAA,eAAoBA,EAAU,KAAK,yBAAyBA,EAAU,KAAK,MAAM,KADxEA,EAAU,IAE7C,CAEA,SAASC,GAAuBL,EAAiD,CAC/E,MAAM7C,EAA0C,CAAA,EAChD,OAAAA,EAAQ,KAAK,CACX,KAAM,WACN,KAAM6C,EAAM,KACZ,UAAWA,EAAM,MAAQ,CAAA,CAAC,CAC3B,EACGA,EAAM,QACR7C,EAAQ,KAAK,CACX,KAAM,aACN,KAAM6C,EAAM,KACZ,KAAMA,EAAM,MAAA,CACb,EAEI,CACL,KAAM,YACN,WAAYA,EAAM,WAClB,MAAOA,EAAM,MACb,QAAA7C,EACA,UAAW6C,EAAM,SAAA,CAErB,CAEA,SAASM,GAAeC,EAAsB,CAC5C,GAAIA,EAAK,gBAAgB,QAAUZ,GAAmB,OACtD,MAAMa,EAAWD,EAAK,gBAAgB,OAASZ,GACzCc,EAAUF,EAAK,gBAAgB,OAAO,EAAGC,CAAQ,EACvD,UAAWE,KAAMD,EAASF,EAAK,eAAe,OAAOG,CAAE,CACzD,CAEA,SAASC,GAAuBJ,EAAsB,CACpDA,EAAK,iBAAmBA,EAAK,gBAC1B,IAAKG,GAAOH,EAAK,eAAe,IAAIG,CAAE,GAAG,OAAO,EAChD,OAAQ9B,GAAwC,EAAQA,CAAI,CACjE,CAEO,SAASgC,GAAoBL,EAAsB,CACpDA,EAAK,qBAAuB,OAC9B,aAAaA,EAAK,mBAAmB,EACrCA,EAAK,oBAAsB,MAE7BI,GAAuBJ,CAAI,CAC7B,CAEO,SAASM,GAAuBN,EAAsBO,EAAQ,GAAO,CAC1E,GAAIA,EAAO,CACTF,GAAoBL,CAAI,EACxB,MACF,CACIA,EAAK,qBAAuB,OAChCA,EAAK,oBAAsB,OAAO,WAChC,IAAMK,GAAoBL,CAAI,EAC9BX,EAAA,EAEJ,CAEO,SAASmB,GAAgBR,EAAsB,CACpDA,EAAK,eAAe,MAAA,EACpBA,EAAK,gBAAkB,CAAA,EACvBA,EAAK,iBAAmB,CAAA,EACxBK,GAAoBL,CAAI,CAC1B,CAaA,MAAMS,GAA+B,IAE9B,SAASC,GAAsBV,EAAsBtB,EAA4B,CACtF,MAAMiC,EAAOjC,EAAQ,MAAQ,CAAA,EACvBkC,EAAQ,OAAOD,EAAK,OAAU,SAAWA,EAAK,MAAQ,GAGxDX,EAAK,sBAAwB,OAC/B,OAAO,aAAaA,EAAK,oBAAoB,EAC7CA,EAAK,qBAAuB,MAG1BY,IAAU,QACZZ,EAAK,iBAAmB,CACtB,OAAQ,GACR,UAAW,KAAK,IAAA,EAChB,YAAa,IAAA,EAENY,IAAU,QACnBZ,EAAK,iBAAmB,CACtB,OAAQ,GACR,UAAWA,EAAK,kBAAkB,WAAa,KAC/C,YAAa,KAAK,IAAA,CAAI,EAGxBA,EAAK,qBAAuB,OAAO,WAAW,IAAM,CAClDA,EAAK,iBAAmB,KACxBA,EAAK,qBAAuB,IAC9B,EAAGS,EAA4B,EAEnC,CAEO,SAASI,GAAiBb,EAAsBtB,EAA6B,CAClF,GAAI,CAACA,EAAS,OAGd,GAAIA,EAAQ,SAAW,aAAc,CACnCgC,GAAsBV,EAAwBtB,CAAO,EACrD,MACF,CAEA,GAAIA,EAAQ,SAAW,OAAQ,OAC/B,MAAMlG,EACJ,OAAOkG,EAAQ,YAAe,SAAWA,EAAQ,WAAa,OAKhE,GAJIlG,GAAcA,IAAewH,EAAK,YAElC,CAACxH,GAAcwH,EAAK,WAAatB,EAAQ,QAAUsB,EAAK,WACxDA,EAAK,WAAatB,EAAQ,QAAUsB,EAAK,WACzC,CAACA,EAAK,UAAW,OAErB,MAAMW,EAAOjC,EAAQ,MAAQ,CAAA,EACvBoC,EAAa,OAAOH,EAAK,YAAe,SAAWA,EAAK,WAAa,GAC3E,GAAI,CAACG,EAAY,OACjB,MAAMjJ,EAAO,OAAO8I,EAAK,MAAS,SAAWA,EAAK,KAAO,OACnDC,EAAQ,OAAOD,EAAK,OAAU,SAAWA,EAAK,MAAQ,GACtDI,EAAOH,IAAU,QAAUD,EAAK,KAAO,OACvCK,EACJJ,IAAU,SACNjB,GAAiBgB,EAAK,aAAa,EACnCC,IAAU,SACRjB,GAAiBgB,EAAK,MAAM,EAC5B,OAEF9C,EAAM,KAAK,IAAA,EACjB,IAAI4B,EAAQO,EAAK,eAAe,IAAIc,CAAU,EACzCrB,GAeHA,EAAM,KAAO5H,EACTkJ,IAAS,SAAWtB,EAAM,KAAOsB,GACjCC,IAAW,SAAWvB,EAAM,OAASuB,GACzCvB,EAAM,UAAY5B,IAjBlB4B,EAAQ,CACN,WAAAqB,EACA,MAAOpC,EAAQ,MACf,WAAAlG,EACA,KAAAX,EACA,KAAAkJ,EACA,OAAAC,EACA,UAAW,OAAOtC,EAAQ,IAAO,SAAWA,EAAQ,GAAKb,EACzD,UAAWA,EACX,QAAS,CAAA,CAAC,EAEZmC,EAAK,eAAe,IAAIc,EAAYrB,CAAK,EACzCO,EAAK,gBAAgB,KAAKc,CAAU,GAQtCrB,EAAM,QAAUK,GAAuBL,CAAK,EAC5CM,GAAeC,CAAI,EACnBM,GAAuBN,EAAMY,IAAU,QAAQ,CACjD,CCnOO,SAASK,GAAmBjB,EAAkBO,EAAQ,GAAO,CAC9DP,EAAK,iBAAiB,qBAAqBA,EAAK,eAAe,EAC/DA,EAAK,mBAAqB,OAC5B,aAAaA,EAAK,iBAAiB,EACnCA,EAAK,kBAAoB,MAE3B,MAAMkB,EAAmB,IAAM,CAC7B,MAAMC,EAAYnB,EAAK,cAAc,cAAc,EACnD,GAAImB,EAAW,CACb,MAAMC,EAAY,iBAAiBD,CAAS,EAAE,UAK9C,GAHEC,IAAc,QACdA,IAAc,UACdD,EAAU,aAAeA,EAAU,aAAe,EACrC,OAAOA,CACxB,CACA,OAAQ,SAAS,kBAAoB,SAAS,eAChD,EAEKnB,EAAK,eAAe,KAAK,IAAM,CAClCA,EAAK,gBAAkB,sBAAsB,IAAM,CACjDA,EAAK,gBAAkB,KACvB,MAAMqB,EAASH,EAAA,EACf,GAAI,CAACG,EAAQ,OACb,MAAMC,EACJD,EAAO,aAAeA,EAAO,UAAYA,EAAO,aAElD,GAAI,EADgBd,GAASP,EAAK,oBAAsBsB,EAAqB,KAC3D,OACdf,MAAY,oBAAsB,IACtCc,EAAO,UAAYA,EAAO,aAC1BrB,EAAK,mBAAqB,GAC1B,MAAMuB,EAAahB,EAAQ,IAAM,IACjCP,EAAK,kBAAoB,OAAO,WAAW,IAAM,CAC/CA,EAAK,kBAAoB,KACzB,MAAMwB,EAASN,EAAA,EACf,GAAI,CAACM,EAAQ,OACb,MAAMC,EACJD,EAAO,aAAeA,EAAO,UAAYA,EAAO,cAEhDjB,GAASP,EAAK,oBAAsByB,EAA2B,OAEjED,EAAO,UAAYA,EAAO,aAC1BxB,EAAK,mBAAqB,GAC5B,EAAGuB,CAAU,CACf,CAAC,CACH,CAAC,CACH,CAEO,SAASG,GAAmB1B,EAAkBO,EAAQ,GAAO,CAC9DP,EAAK,iBAAiB,qBAAqBA,EAAK,eAAe,EAC9DA,EAAK,eAAe,KAAK,IAAM,CAClCA,EAAK,gBAAkB,sBAAsB,IAAM,CACjDA,EAAK,gBAAkB,KACvB,MAAMmB,EAAYnB,EAAK,cAAc,aAAa,EAClD,GAAI,CAACmB,EAAW,OAChB,MAAMG,EACJH,EAAU,aAAeA,EAAU,UAAYA,EAAU,cACvCZ,GAASe,EAAqB,MAElDH,EAAU,UAAYA,EAAU,aAClC,CAAC,CACH,CAAC,CACH,CAEO,SAASQ,GAAiB3B,EAAkB4B,EAAc,CAC/D,MAAMT,EAAYS,EAAM,cACxB,GAAI,CAACT,EAAW,OAChB,MAAMG,EACJH,EAAU,aAAeA,EAAU,UAAYA,EAAU,aAC3DnB,EAAK,mBAAqBsB,EAAqB,GACjD,CAEO,SAASO,GAAiB7B,EAAkB4B,EAAc,CAC/D,MAAMT,EAAYS,EAAM,cACxB,GAAI,CAACT,EAAW,OAChB,MAAMG,EACJH,EAAU,aAAeA,EAAU,UAAYA,EAAU,aAC3DnB,EAAK,aAAesB,EAAqB,EAC3C,CAEO,SAASQ,GAAgB9B,EAAkB,CAChDA,EAAK,oBAAsB,GAC3BA,EAAK,mBAAqB,EAC5B,CAEO,SAAS+B,GAAWxE,EAAiBhB,EAAe,CACzD,GAAIgB,EAAM,SAAW,EAAG,OACxB,MAAMyE,EAAO,IAAI,KAAK,CAAC,GAAGzE,EAAM,KAAK;AAAA,CAAI,CAAC;AAAA,CAAI,EAAG,CAAE,KAAM,aAAc,EACjE0E,EAAM,IAAI,gBAAgBD,CAAI,EAC9BE,EAAS,SAAS,cAAc,GAAG,EACnCC,EAAQ,IAAI,KAAA,EAAO,YAAA,EAAc,MAAM,EAAG,EAAE,EAAE,QAAQ,QAAS,GAAG,EACxED,EAAO,KAAOD,EACdC,EAAO,SAAW,iBAAiB3F,CAAK,IAAI4F,CAAK,OACjDD,EAAO,MAAA,EACP,IAAI,gBAAgBD,CAAG,CACzB,CAEO,SAASG,GAAcpC,EAAkB,CAC9C,GAAI,OAAO,eAAmB,IAAa,OAC3C,MAAMqC,EAASrC,EAAK,cAAc,SAAS,EAC3C,GAAI,CAACqC,EAAQ,OACb,MAAMC,EAAS,IAAM,CACnB,KAAM,CAAE,OAAAC,CAAA,EAAWF,EAAO,sBAAA,EAC1BrC,EAAK,MAAM,YAAY,kBAAmB,GAAGuC,CAAM,IAAI,CACzD,EACAD,EAAA,EACAtC,EAAK,eAAiB,IAAI,eAAe,IAAMsC,GAAQ,EACvDtC,EAAK,eAAe,QAAQqC,CAAM,CACpC,CCzHO,SAASG,GAAqBhL,EAAa,CAChD,OAAI,OAAO,iBAAoB,WACtB,gBAAgBA,CAAK,EAEvB,KAAK,MAAM,KAAK,UAAUA,CAAK,CAAC,CACzC,CAEO,SAASiL,GAAoBC,EAAuC,CACzE,MAAO,GAAG,KAAK,UAAUA,EAAM,KAAM,CAAC,EAAE,SAAS;AAAA,CACnD,CAEO,SAASC,GACd3F,EACAhE,EACAxB,EACA,CACA,GAAIwB,EAAK,SAAW,EAAG,OACvB,IAAI2F,EAA+C3B,EACnD,QAASnI,EAAI,EAAGA,EAAImE,EAAK,OAAS,EAAGnE,GAAK,EAAG,CAC3C,MAAMoK,EAAMjG,EAAKnE,CAAC,EACZ+N,EAAU5J,EAAKnE,EAAI,CAAC,EAC1B,GAAI,OAAOoK,GAAQ,SAAU,CAC3B,GAAI,CAAC,MAAM,QAAQN,CAAO,EAAG,OACzBA,EAAQM,CAAG,GAAK,OAClBN,EAAQM,CAAG,EACT,OAAO2D,GAAY,SAAW,CAAA,EAAM,CAAA,GAExCjE,EAAUA,EAAQM,CAAG,CACvB,KAAO,CACL,GAAI,OAAON,GAAY,UAAYA,GAAW,KAAM,OACpD,MAAMa,EAASb,EACXa,EAAOP,CAAG,GAAK,OACjBO,EAAOP,CAAG,EACR,OAAO2D,GAAY,SAAW,CAAA,EAAM,CAAA,GAExCjE,EAAUa,EAAOP,CAAG,CACtB,CACF,CACA,MAAM4D,EAAU7J,EAAKA,EAAK,OAAS,CAAC,EACpC,GAAI,OAAO6J,GAAY,SAAU,CAC3B,MAAM,QAAQlE,CAAO,IAAGA,EAAQkE,CAAO,EAAIrL,GAC/C,MACF,CACI,OAAOmH,GAAY,UAAYA,GAAW,OAC3CA,EAAoCkE,CAAO,EAAIrL,EAEpD,CAEO,SAASsL,GACd9F,EACAhE,EACA,CACA,GAAIA,EAAK,SAAW,EAAG,OACvB,IAAI2F,EAA+C3B,EACnD,QAAS,EAAI,EAAG,EAAIhE,EAAK,OAAS,EAAG,GAAK,EAAG,CAC3C,MAAMiG,EAAMjG,EAAK,CAAC,EAClB,GAAI,OAAOiG,GAAQ,SAAU,CAC3B,GAAI,CAAC,MAAM,QAAQN,CAAO,EAAG,OAC7BA,EAAUA,EAAQM,CAAG,CACvB,KAAO,CACL,GAAI,OAAON,GAAY,UAAYA,GAAW,KAAM,OACpDA,EAAWA,EAAoCM,CAAG,CAGpD,CACA,GAAIN,GAAW,KAAM,MACvB,CACA,MAAMkE,EAAU7J,EAAKA,EAAK,OAAS,CAAC,EACpC,GAAI,OAAO6J,GAAY,SAAU,CAC3B,MAAM,QAAQlE,CAAO,GAAGA,EAAQ,OAAOkE,EAAS,CAAC,EACrD,MACF,CACI,OAAOlE,GAAY,UAAYA,GAAW,MAC5C,OAAQA,EAAoCkE,CAAO,CAEvD,CCnCA,eAAsBE,GAAW9E,EAAoB,CACnD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,cAAgB,GACtBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,aAAc,EAAE,EACxD+E,GAAoB/E,EAAOC,CAAG,CAChC,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,cAAgB,EACxB,EACF,CAEA,eAAsBgF,GAAiBhF,EAAoB,CACzD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,oBACV,CAAAA,EAAM,oBAAsB,GAC5B,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAC9B,gBACA,CAAA,CAAC,EAEHiF,GAAkBjF,EAAOC,CAAG,CAC9B,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,oBAAsB,EAC9B,EACF,CAEO,SAASiF,GACdjF,EACAC,EACA,CACAD,EAAM,aAAeC,EAAI,QAAU,KACnCD,EAAM,cAAgBC,EAAI,SAAW,CAAA,EACrCD,EAAM,oBAAsBC,EAAI,SAAW,IAC7C,CAEO,SAAS8E,GAAoB/E,EAAoBkF,EAA0B,CAChFlF,EAAM,eAAiBkF,EACvB,MAAMC,EACJ,OAAOD,EAAS,KAAQ,SACpBA,EAAS,IACTA,EAAS,QAAU,OAAOA,EAAS,QAAW,SAC5CV,GAAoBU,EAAS,MAAiC,EAC9DlF,EAAM,UACV,CAACA,EAAM,iBAAmBA,EAAM,iBAAmB,MACrDA,EAAM,UAAYmF,EACTnF,EAAM,WACfA,EAAM,UAAYwE,GAAoBxE,EAAM,UAAU,EAEtDA,EAAM,UAAYmF,EAEpBnF,EAAM,YAAc,OAAOkF,EAAS,OAAU,UAAYA,EAAS,MAAQ,KAC3ElF,EAAM,aAAe,MAAM,QAAQkF,EAAS,MAAM,EAAIA,EAAS,OAAS,CAAA,EAEnElF,EAAM,kBACTA,EAAM,WAAauE,GAAkBW,EAAS,QAAU,CAAA,CAAE,EAC1DlF,EAAM,mBAAqBuE,GAAkBW,EAAS,QAAU,CAAA,CAAE,EAClElF,EAAM,kBAAoBmF,EAE9B,CAEA,eAAsBC,GAAWpF,EAAoB,CACnD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,aAAe,GACrBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAM9F,EACJ8F,EAAM,iBAAmB,QAAUA,EAAM,WACrCwE,GAAoBxE,EAAM,UAAU,EACpCA,EAAM,UACNqF,EAAWrF,EAAM,gBAAgB,KACvC,GAAI,CAACqF,EAAU,CACbrF,EAAM,UAAY,yCAClB,MACF,CACA,MAAMA,EAAM,OAAO,QAAQ,aAAc,CAAE,IAAA9F,EAAK,SAAAmL,EAAU,EAC1DrF,EAAM,gBAAkB,GACxB,MAAM8E,GAAW9E,CAAK,CACxB,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,aAAe,EACvB,EACF,CAEA,eAAsBsF,GAAYtF,EAAoB,CACpD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,eAAiB,GACvBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAM9F,EACJ8F,EAAM,iBAAmB,QAAUA,EAAM,WACrCwE,GAAoBxE,EAAM,UAAU,EACpCA,EAAM,UACNqF,EAAWrF,EAAM,gBAAgB,KACvC,GAAI,CAACqF,EAAU,CACbrF,EAAM,UAAY,yCAClB,MACF,CACA,MAAMA,EAAM,OAAO,QAAQ,eAAgB,CACzC,IAAA9F,EACA,SAAAmL,EACA,WAAYrF,EAAM,eAAA,CACnB,EACDA,EAAM,gBAAkB,GACxB,MAAM8E,GAAW9E,CAAK,CACxB,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,eAAiB,EACzB,EACF,CAEA,eAAsBuF,GAAUvF,EAAoB,CAClD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,cAAgB,GACtBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,aAAc,CACvC,WAAYA,EAAM,eAAA,CACnB,CACH,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,cAAgB,EACxB,EACF,CAEO,SAASwF,GACdxF,EACAjF,EACAxB,EACA,CACA,MAAM2B,EAAOqJ,GACXvE,EAAM,YAAcA,EAAM,gBAAgB,QAAU,CAAA,CAAC,EAEvD0E,GAAaxJ,EAAMH,EAAMxB,CAAK,EAC9ByG,EAAM,WAAa9E,EACnB8E,EAAM,gBAAkB,GACpBA,EAAM,iBAAmB,SAC3BA,EAAM,UAAYwE,GAAoBtJ,CAAI,EAE9C,CAEO,SAASuK,GACdzF,EACAjF,EACA,CACA,MAAMG,EAAOqJ,GACXvE,EAAM,YAAcA,EAAM,gBAAgB,QAAU,CAAA,CAAC,EAEvD6E,GAAgB3J,EAAMH,CAAI,EAC1BiF,EAAM,WAAa9E,EACnB8E,EAAM,gBAAkB,GACpBA,EAAM,iBAAmB,SAC3BA,EAAM,UAAYwE,GAAoBtJ,CAAI,EAE9C,CCvLA,eAAsBwK,GAAe1F,EAAkB,CACrD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,cAAe,EAAE,EACzDA,EAAM,WAAaC,CACrB,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,CACF,CAEA,eAAsByF,GAAa3F,EAAkB,CACnD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,YACV,CAAAA,EAAM,YAAc,GACpBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,YAAa,CACnD,gBAAiB,EAAA,CAClB,EACDA,EAAM,SAAW,MAAM,QAAQC,EAAI,IAAI,EAAIA,EAAI,KAAO,CAAA,CACxD,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,YAAc,EACtB,EACF,CAEO,SAAS4F,GAAkBnB,EAAqB,CACrD,GAAIA,EAAK,eAAiB,KAAM,CAC9B,MAAMxH,EAAK,KAAK,MAAMwH,EAAK,UAAU,EACrC,GAAI,CAAC,OAAO,SAASxH,CAAE,EAAG,MAAM,IAAI,MAAM,mBAAmB,EAC7D,MAAO,CAAE,KAAM,KAAe,KAAMA,CAAA,CACtC,CACA,GAAIwH,EAAK,eAAiB,QAAS,CACjC,MAAMoB,EAAShI,GAAS4G,EAAK,YAAa,CAAC,EAC3C,GAAIoB,GAAU,EAAG,MAAM,IAAI,MAAM,0BAA0B,EAC3D,MAAMC,EAAOrB,EAAK,UAElB,MAAO,CAAE,KAAM,QAAkB,QAASoB,GAD7BC,IAAS,UAAY,IAASA,IAAS,QAAU,KAAY,MACvB,CACrD,CACA,MAAMC,EAAOtB,EAAK,SAAS,KAAA,EAC3B,GAAI,CAACsB,EAAM,MAAM,IAAI,MAAM,2BAA2B,EACtD,MAAO,CAAE,KAAM,OAAiB,KAAAA,EAAM,GAAItB,EAAK,OAAO,KAAA,GAAU,MAAA,CAClE,CAEO,SAASuB,GAAiBvB,EAAqB,CACpD,GAAIA,EAAK,cAAgB,cAAe,CACtC,MAAMlI,EAAOkI,EAAK,YAAY,KAAA,EAC9B,GAAI,CAAClI,EAAM,MAAM,IAAI,MAAM,6BAA6B,EACxD,MAAO,CAAE,KAAM,cAAwB,KAAAA,CAAA,CACzC,CACA,MAAMkC,EAAUgG,EAAK,YAAY,KAAA,EACjC,GAAI,CAAChG,EAAS,MAAM,IAAI,MAAM,yBAAyB,EACvD,MAAMgC,EAOF,CAAE,KAAM,YAAa,QAAAhC,CAAA,EACrBgG,EAAK,UAAShE,EAAQ,QAAU,IAChCgE,EAAK,UAAShE,EAAQ,QAAUgE,EAAK,SACrCA,EAAK,GAAG,KAAA,MAAgB,GAAKA,EAAK,GAAG,KAAA,GACzC,MAAMwB,EAAiBpI,GAAS4G,EAAK,eAAgB,CAAC,EACtD,OAAIwB,EAAiB,IAAGxF,EAAQ,eAAiBwF,GAC1CxF,CACT,CAEA,eAAsByF,GAAWlG,EAAkB,CACjD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,UAC/C,CAAAA,EAAM,SAAW,GACjBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMmG,EAAWP,GAAkB5F,EAAM,QAAQ,EAC3CS,EAAUuF,GAAiBhG,EAAM,QAAQ,EACzCvF,EAAUuF,EAAM,SAAS,QAAQ,KAAA,EACjCoG,EAAM,CACV,KAAMpG,EAAM,SAAS,KAAK,KAAA,EAC1B,YAAaA,EAAM,SAAS,YAAY,QAAU,OAClD,QAASvF,GAAW,OACpB,QAASuF,EAAM,SAAS,QACxB,SAAAmG,EACA,cAAenG,EAAM,SAAS,cAC9B,SAAUA,EAAM,SAAS,SACzB,QAAAS,EACA,UACET,EAAM,SAAS,iBAAiB,KAAA,GAChCA,EAAM,SAAS,gBAAkB,WAC7B,CAAE,iBAAkBA,EAAM,SAAS,iBAAiB,KAAA,GACpD,MAAA,EAER,GAAI,CAACoG,EAAI,KAAM,MAAM,IAAI,MAAM,gBAAgB,EAC/C,MAAMpG,EAAM,OAAO,QAAQ,WAAYoG,CAAG,EAC1CpG,EAAM,SAAW,CACf,GAAGA,EAAM,SACT,KAAM,GACN,YAAa,GACb,YAAa,EAAA,EAEf,MAAM2F,GAAa3F,CAAK,EACxB,MAAM0F,GAAe1F,CAAK,CAC5B,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,SAAW,EACnB,EACF,CAEA,eAAsBqG,GACpBrG,EACAoG,EACAE,EACA,CACA,GAAI,GAACtG,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,UAC/C,CAAAA,EAAM,SAAW,GACjBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,cAAe,CAAE,GAAIoG,EAAI,GAAI,MAAO,CAAE,QAAAE,CAAA,CAAQ,CAAG,EAC5E,MAAMX,GAAa3F,CAAK,EACxB,MAAM0F,GAAe1F,CAAK,CAC5B,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,SAAW,EACnB,EACF,CAEA,eAAsBuG,GAAWvG,EAAkBoG,EAAc,CAC/D,GAAI,GAACpG,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,UAC/C,CAAAA,EAAM,SAAW,GACjBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,WAAY,CAAE,GAAIoG,EAAI,GAAI,KAAM,QAAS,EACpE,MAAMI,GAAaxG,EAAOoG,EAAI,EAAE,CAClC,OAASlG,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,SAAW,EACnB,EACF,CAEA,eAAsByG,GAAczG,EAAkBoG,EAAc,CAClE,GAAI,GAACpG,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,UAC/C,CAAAA,EAAM,SAAW,GACjBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,cAAe,CAAE,GAAIoG,EAAI,GAAI,EACpDpG,EAAM,gBAAkBoG,EAAI,KAC9BpG,EAAM,cAAgB,KACtBA,EAAM,SAAW,CAAA,GAEnB,MAAM2F,GAAa3F,CAAK,EACxB,MAAM0F,GAAe1F,CAAK,CAC5B,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,SAAW,EACnB,EACF,CAEA,eAAsBwG,GAAaxG,EAAkB0G,EAAe,CAClE,GAAI,GAAC1G,EAAM,QAAU,CAACA,EAAM,WAC5B,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,YAAa,CACnD,GAAI0G,EACJ,MAAO,EAAA,CACR,EACD1G,EAAM,cAAgB0G,EACtB1G,EAAM,SAAW,MAAM,QAAQC,EAAI,OAAO,EAAIA,EAAI,QAAU,CAAA,CAC9D,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,CACF,CC1LA,eAAsByG,GAAa3G,EAAsB4G,EAAgB,CACvE,GAAI,GAAC5G,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,gBACV,CAAAA,EAAM,gBAAkB,GACxBA,EAAM,cAAgB,KACtB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,kBAAmB,CACzD,MAAA4G,EACA,UAAW,GAAA,CACZ,EACD5G,EAAM,iBAAmBC,EACzBD,EAAM,oBAAsB,KAAK,IAAA,CACnC,OAASE,EAAK,CACZF,EAAM,cAAgB,OAAOE,CAAG,CAClC,QAAA,CACEF,EAAM,gBAAkB,EAC1B,EACF,CAEA,eAAsB6G,GAAmB7G,EAAsBsC,EAAgB,CAC7E,GAAI,GAACtC,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,cAC/C,CAAAA,EAAM,aAAe,GACrB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,kBAAmB,CACzD,MAAAsC,EACA,UAAW,GAAA,CACZ,EACDtC,EAAM,qBAAuBC,EAAI,SAAW,KAC5CD,EAAM,uBAAyBC,EAAI,WAAa,KAChDD,EAAM,uBAAyB,IACjC,OAASE,EAAK,CACZF,EAAM,qBAAuB,OAAOE,CAAG,EACvCF,EAAM,uBAAyB,KAC/BA,EAAM,uBAAyB,IACjC,QAAA,CACEA,EAAM,aAAe,EACvB,EACF,CAEA,eAAsB8G,GAAkB9G,EAAsB,CAC5D,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,cAC/C,CAAAA,EAAM,aAAe,GACrB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,iBAAkB,CACxD,UAAW,IAAA,CACZ,EACDA,EAAM,qBAAuBC,EAAI,SAAW,KAC5CD,EAAM,uBAAyBC,EAAI,WAAa,KAC5CA,EAAI,YAAWD,EAAM,uBAAyB,KACpD,OAASE,EAAK,CACZF,EAAM,qBAAuB,OAAOE,CAAG,EACvCF,EAAM,uBAAyB,IACjC,QAAA,CACEA,EAAM,aAAe,EACvB,EACF,CAEA,eAAsB+G,GAAe/G,EAAsB,CACzD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,cAC/C,CAAAA,EAAM,aAAe,GACrB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,kBAAmB,CAAE,QAAS,WAAY,EACrEA,EAAM,qBAAuB,cAC7BA,EAAM,uBAAyB,KAC/BA,EAAM,uBAAyB,IACjC,OAASE,EAAK,CACZF,EAAM,qBAAuB,OAAOE,CAAG,CACzC,QAAA,CACEF,EAAM,aAAe,EACvB,EACF,CC1DA,eAAsBgH,GAAUhH,EAAmB,CACjD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,aACV,CAAAA,EAAM,aAAe,GACrB,GAAI,CACF,KAAM,CAACiH,EAAQC,EAAQC,EAAQC,CAAS,EAAI,MAAM,QAAQ,IAAI,CAC5DpH,EAAM,OAAO,QAAQ,SAAU,CAAA,CAAE,EACjCA,EAAM,OAAO,QAAQ,SAAU,CAAA,CAAE,EACjCA,EAAM,OAAO,QAAQ,cAAe,CAAA,CAAE,EACtCA,EAAM,OAAO,QAAQ,iBAAkB,CAAA,CAAE,CAAA,CAC1C,EACDA,EAAM,YAAciH,EACpBjH,EAAM,YAAckH,EACpB,MAAMG,EAAeF,EACrBnH,EAAM,YAAc,MAAM,QAAQqH,GAAc,MAAM,EAClDA,GAAc,OACd,CAAA,EACJrH,EAAM,eAAiBoH,CACzB,OAASlH,EAAK,CACZF,EAAM,eAAiB,OAAOE,CAAG,CACnC,QAAA,CACEF,EAAM,aAAe,EACvB,EACF,CAEA,eAAsBsH,GAAgBtH,EAAmB,CACvD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,eAAiB,KACvBA,EAAM,gBAAkB,KACxB,GAAI,CACF,MAAMY,EAASZ,EAAM,gBAAgB,KAAA,EAChC,KAAK,MAAMA,EAAM,eAAe,EACjC,CAAA,EACEC,EAAM,MAAMD,EAAM,OAAO,QAAQA,EAAM,gBAAgB,KAAA,EAAQY,CAAM,EAC3EZ,EAAM,gBAAkB,KAAK,UAAUC,EAAK,KAAM,CAAC,CACrD,OAASC,EAAK,CACZF,EAAM,eAAiB,OAAOE,CAAG,CACnC,EACF,CCtCA,MAAMqH,GAAmB,IACnBC,OAAa,IAAc,CAC/B,QACA,QACA,OACA,OACA,QACA,OACF,CAAC,EAED,SAASC,GAAqBlO,EAAgB,CAC5C,GAAI,OAAOA,GAAU,SAAU,OAAO,KACtC,MAAME,EAAUF,EAAM,KAAA,EACtB,GAAI,CAACE,EAAQ,WAAW,GAAG,GAAK,CAACA,EAAQ,SAAS,GAAG,EAAG,OAAO,KAC/D,GAAI,CACF,MAAMU,EAAS,KAAK,MAAMV,CAAO,EACjC,MAAI,CAACU,GAAU,OAAOA,GAAW,SAAiB,KAC3CA,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAASuN,GAAenO,EAAiC,CACvD,GAAI,OAAOA,GAAU,SAAU,OAAO,KACtC,MAAMoO,EAAUpO,EAAM,YAAA,EACtB,OAAOiO,GAAO,IAAIG,CAAO,EAAIA,EAAU,IACzC,CAEO,SAASC,GAAarI,EAAwB,CACnD,GAAI,CAACA,EAAK,aAAe,CAAE,IAAKA,EAAM,QAASA,CAAA,EAC/C,GAAI,CACF,MAAMR,EAAM,KAAK,MAAMQ,CAAI,EACrBsI,EACJ9I,GAAO,OAAOA,EAAI,OAAU,UAAYA,EAAI,QAAU,KACjDA,EAAI,MACL,KACA+I,EACJ,OAAO/I,EAAI,MAAS,SAChBA,EAAI,KACJ,OAAO8I,GAAM,MAAS,SACpBA,GAAM,KACN,KACFE,EAAQL,GAAeG,GAAM,cAAgBA,GAAM,KAAK,EAExDG,EACJ,OAAOjJ,EAAI,CAAG,GAAM,SACfA,EAAI,CAAG,EACR,OAAO8I,GAAM,MAAS,SACnBA,GAAM,KACP,KACFI,EAAaR,GAAqBO,CAAgB,EACxD,IAAIE,EAA2B,KAC3BD,IACE,OAAOA,EAAW,WAAc,WAAsBA,EAAW,UAC5D,OAAOA,EAAW,QAAW,aAAsBA,EAAW,SAErE,CAACC,GAAaF,GAAoBA,EAAiB,OAAS,MAC9DE,EAAYF,GAGd,IAAIvJ,EAAyB,KAC7B,OAAI,OAAOM,EAAI,CAAG,GAAM,SAAUN,EAAUM,EAAI,CAAG,EAC1C,CAACkJ,GAAc,OAAOlJ,EAAI,CAAG,GAAM,SAAUN,EAAUM,EAAI,CAAG,EAC9D,OAAOA,EAAI,SAAY,aAAoBA,EAAI,SAEjD,CACL,IAAKQ,EACL,KAAAuI,EACA,MAAAC,EACA,UAAAG,EACA,QAASzJ,GAAWc,EACpB,KAAMsI,GAAQ,MAAA,CAElB,MAAQ,CACN,MAAO,CAAE,IAAKtI,EAAM,QAASA,CAAA,CAC/B,CACF,CAEA,eAAsB4I,GACpBnI,EACAoI,EACA,CACA,GAAI,GAACpI,EAAM,QAAU,CAACA,EAAM,YACxB,EAAAA,EAAM,aAAe,CAACoI,GAAM,OAChC,CAAKA,GAAM,QAAOpI,EAAM,YAAc,IACtCA,EAAM,UAAY,KAClB,GAAI,CAMF,MAAMS,EALM,MAAMT,EAAM,OAAO,QAAQ,YAAa,CAClD,OAAQoI,GAAM,MAAQ,OAAYpI,EAAM,YAAc,OACtD,MAAOA,EAAM,UACb,SAAUA,EAAM,YAAA,CACjB,EAYKqI,GAHQ,MAAM,QAAQ5H,EAAQ,KAAK,EACpCA,EAAQ,MAAM,OAAQlB,GAAS,OAAOA,GAAS,QAAQ,EACxD,CAAA,GACkB,IAAIqI,EAAY,EAChCU,EAAc,GAAQF,GAAM,OAAS3H,EAAQ,OAAST,EAAM,YAAc,MAChFA,EAAM,YAAcsI,EAChBD,EACA,CAAC,GAAGrI,EAAM,YAAa,GAAGqI,CAAO,EAAE,MAAM,CAACd,EAAgB,EAC1D,OAAO9G,EAAQ,QAAW,WAAUT,EAAM,WAAaS,EAAQ,QAC/D,OAAOA,EAAQ,MAAS,WAAUT,EAAM,SAAWS,EAAQ,MAC/DT,EAAM,cAAgB,EAAQS,EAAQ,UACtCT,EAAM,gBAAkB,KAAK,IAAA,CAC/B,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACOkI,GAAM,QAAOpI,EAAM,YAAc,GACxC,EACF,CC7GA,MAAMuI,GAAgB,CAClB,EAAG,oEACH,EAAG,oEACH,EAAG,GACH,EAAG,oEACH,EAAG,oEACH,GAAI,oEACJ,GAAI,mEACR,EACM,CAAE,EAAGrQ,EAAG,EAAGE,GAAG,GAAAoQ,GAAI,GAAAC,GAAI,EAAGC,GAAI,EAAGC,GAAE,EAAE5R,EAAC,EAAKwR,GAC1ChQ,GAAI,GACJqQ,GAAK,GAILC,GAAe,IAAI/F,IAAS,CAC1B,sBAAuB,OAAS,OAAO,MAAM,mBAAsB,YACnE,MAAM,kBAAkB,GAAGA,CAAI,CAEvC,EACM5C,EAAM,CAACzB,EAAU,KAAO,CAC1B,MAAMnI,EAAI,IAAI,MAAMmI,CAAO,EAC3B,MAAAoK,GAAavS,EAAG4J,CAAG,EACb5J,CACV,EACMwS,GAASnS,GAAM,OAAOA,GAAM,SAC5BoS,GAASxS,GAAM,OAAOA,GAAM,SAC5ByS,GAAWhS,GAAMA,aAAa,YAAe,YAAY,OAAOA,CAAC,GAAKA,EAAE,YAAY,OAAS,aAE7FiS,GAAS,CAAC1P,EAAO2P,EAAQC,EAAQ,KAAO,CAC1C,MAAM1J,EAAQuJ,GAAQzP,CAAK,EACrB6P,EAAM7P,GAAO,OACb8P,EAAWH,IAAW,OAC5B,GAAI,CAACzJ,GAAU4J,GAAYD,IAAQF,EAAS,CACxC,MAAMvN,EAASwN,GAAS,IAAIA,CAAK,KAC3BG,EAAQD,EAAW,cAAcH,CAAM,GAAK,GAC5CK,EAAM9J,EAAQ,UAAU2J,CAAG,GAAK,QAAQ,OAAO7P,CAAK,GAC1D2G,EAAIvE,EAAS,sBAAwB2N,EAAQ,SAAWC,CAAG,CAC/D,CACA,OAAOhQ,CACX,EAEMiQ,GAAOJ,GAAQ,IAAI,WAAWA,CAAG,EACjCK,GAAQC,GAAQ,WAAW,KAAKA,CAAG,EACnCC,GAAO,CAAChT,EAAGiT,IAAQjT,EAAE,SAAS,EAAE,EAAE,SAASiT,EAAK,GAAG,EACnDC,GAAcvS,GAAM,MAAM,KAAK2R,GAAO3R,CAAC,CAAC,EACzC,IAAKhB,GAAMqT,GAAKrT,EAAG,CAAC,CAAC,EACrB,KAAK,EAAE,EACN2B,GAAI,CAAE,GAAI,GAAI,GAAI,GAAI,EAAG,GAAI,EAAG,GAAI,EAAG,GAAI,EAAG,GAAG,EACjD6R,GAAOC,GAAO,CAChB,GAAIA,GAAM9R,GAAE,IAAM8R,GAAM9R,GAAE,GACtB,OAAO8R,EAAK9R,GAAE,GAClB,GAAI8R,GAAM9R,GAAE,GAAK8R,GAAM9R,GAAE,EACrB,OAAO8R,GAAM9R,GAAE,EAAI,IACvB,GAAI8R,GAAM9R,GAAE,GAAK8R,GAAM9R,GAAE,EACrB,OAAO8R,GAAM9R,GAAE,EAAI,GAE3B,EACM+R,GAActK,GAAQ,CACxB,MAAMpJ,EAAI,cACV,GAAI,CAACyS,GAAMrJ,CAAG,EACV,OAAOQ,EAAI5J,CAAC,EAChB,MAAM2T,EAAKvK,EAAI,OACTwK,EAAKD,EAAK,EAChB,GAAIA,EAAK,EACL,OAAO/J,EAAI5J,CAAC,EAChB,MAAM6T,EAAQX,GAAIU,CAAE,EACpB,QAASE,EAAK,EAAGC,EAAK,EAAGD,EAAKF,EAAIE,IAAMC,GAAM,EAAG,CAE7C,MAAMC,EAAKR,GAAIpK,EAAI,WAAW2K,CAAE,CAAC,EAC3BE,EAAKT,GAAIpK,EAAI,WAAW2K,EAAK,CAAC,CAAC,EACrC,GAAIC,IAAO,QAAaC,IAAO,OAC3B,OAAOrK,EAAI5J,CAAC,EAChB6T,EAAMC,CAAE,EAAIE,EAAK,GAAKC,CAC1B,CACA,OAAOJ,CACX,EACMK,GAAK,IAAM,YAAY,OACvBC,GAAS,IAAMD,GAAE,GAAI,QAAUtK,EAAI,kDAAkD,EAErFwK,GAAc,IAAIC,IAAS,CAC7B,MAAMjU,EAAI8S,GAAImB,EAAK,OAAO,CAACC,EAAK5T,IAAM4T,EAAM3B,GAAOjS,CAAC,EAAE,OAAQ,CAAC,CAAC,EAChE,IAAI4S,EAAM,EACV,OAAAe,EAAK,QAAQ3T,GAAK,CAAEN,EAAE,IAAIM,EAAG4S,CAAG,EAAGA,GAAO5S,EAAE,MAAQ,CAAC,EAC9CN,CACX,EAEMmU,GAAc,CAACzB,EAAM7Q,KACbiS,GAAE,EACH,gBAAgBhB,GAAIJ,CAAG,CAAC,EAE/B0B,GAAM,OACNC,GAAc,CAACpU,EAAG0G,EAAKM,EAAKyC,EAAM,6BAAgC0I,GAAMnS,CAAC,GAAK0G,GAAO1G,GAAKA,EAAIgH,EAAMhH,EAAIuJ,EAAIE,CAAG,EAE/G1H,EAAI,CAAC1B,EAAGM,EAAIY,IAAM,CACpB,MAAMxB,EAAIM,EAAIM,EACd,OAAOZ,GAAK,GAAKA,EAAIY,EAAIZ,CAC7B,EACMsU,GAAQhU,GAAM0B,EAAE1B,EAAGoB,EAAC,EAGpB6S,GAAS,CAACC,EAAKC,IAAO,EACpBD,IAAQ,IAAMC,GAAM,KACpBjL,EAAI,gBAAkBgL,EAAM,QAAUC,CAAE,EACzC,IAACnU,EAAI0B,EAAEwS,EAAKC,CAAE,EAAG7T,EAAI6T,EAAIrT,EAAI,GAAYV,EAAI,GAChD,KAAOJ,IAAM,IAAI,CACb,MAAMoU,EAAI9T,EAAIN,EAAGN,EAAIY,EAAIN,EACnBW,EAAIG,EAAIV,EAAIgU,EAClB9T,EAAIN,EAAGA,EAAIN,EAAGoB,EAAIV,EAAUA,EAAIO,CACpC,CACA,OAAOL,IAAM,GAAKoB,EAAEZ,EAAGqT,CAAE,EAAIjL,EAAI,YAAY,CACjD,EACMmL,GAAYzR,GAAS,CAEvB,MAAM0R,EAAKC,GAAO3R,CAAI,EACtB,OAAI,OAAO0R,GAAO,YACdpL,EAAI,UAAYtG,EAAO,UAAU,EAC9B0R,CACX,EAEME,GAAUtU,GAAOA,aAAauU,GAAQvU,EAAIgJ,EAAI,gBAAgB,EAG9DwL,GAAO,IAAM,KAEnB,MAAMD,EAAM,CACR,OAAO,KACP,OAAO,KACP,EACA,EACA,EACA,EACA,YAAYE,EAAGC,EAAG/S,EAAGgT,EAAG,CACpB,MAAMlO,EAAM+N,GACZ,KAAK,EAAIX,GAAYY,EAAG,GAAIhO,CAAG,EAC/B,KAAK,EAAIoN,GAAYa,EAAG,GAAIjO,CAAG,EAC/B,KAAK,EAAIoN,GAAYlS,EAAG,GAAI8E,CAAG,EAC/B,KAAK,EAAIoN,GAAYc,EAAG,GAAIlO,CAAG,EAC/B,OAAO,OAAO,IAAI,CACtB,CACA,OAAO,OAAQ,CACX,OAAO4K,EACX,CACA,OAAO,WAAWrR,EAAG,CACjB,OAAO,IAAIuU,GAAMvU,EAAE,EAAGA,EAAE,EAAG,GAAIwB,EAAExB,EAAE,EAAIA,EAAE,CAAC,CAAC,CAC/C,CAEA,OAAO,UAAUwI,EAAKoM,EAAS,GAAO,CAClC,MAAM3U,EAAIwR,GAEJoD,EAAStC,GAAKR,GAAOvJ,EAAKnH,EAAC,CAAC,EAE5ByT,EAAWtM,EAAI,EAAE,EACvBqM,EAAO,EAAE,EAAIC,EAAW,KACxB,MAAMxU,EAAIyU,GAAaF,CAAM,EAI7BhB,GAAYvT,EAAG,GADHsU,EAASJ,GAAOxT,CACN,EACtB,MAAMgU,EAAKxT,EAAElB,EAAIA,CAAC,EACZJ,EAAIsB,EAAEwT,EAAK,EAAE,EACbzU,EAAIiB,EAAEvB,EAAI+U,EAAK,EAAE,EACvB,GAAI,CAAE,QAAAC,EAAS,MAAOrU,CAAC,EAAKsU,GAAQhV,EAAGK,CAAC,EACnC0U,GACDjM,EAAI,uBAAuB,EAC/B,MAAMmM,GAAUvU,EAAI,MAAQ,GACtBwU,GAAiBN,EAAW,OAAU,EAC5C,MAAI,CAACF,GAAUhU,IAAM,IAAMwU,GACvBpM,EAAI,gCAAgC,EACpCoM,IAAkBD,IAClBvU,EAAIY,EAAE,CAACZ,CAAC,GACL,IAAI2T,GAAM3T,EAAGN,EAAG,GAAIkB,EAAEZ,EAAIN,CAAC,CAAC,CACvC,CACA,OAAO,QAAQkI,EAAKoM,EAAQ,CACxB,OAAOL,GAAM,UAAUzB,GAAWtK,CAAG,EAAGoM,CAAM,CAClD,CACA,IAAI,GAAI,CACJ,OAAO,KAAK,SAAQ,EAAG,CAC3B,CACA,IAAI,GAAI,CACJ,OAAO,KAAK,SAAQ,EAAG,CAC3B,CAEA,gBAAiB,CACb,MAAM9U,EAAI0R,GACJvR,EAAIwR,GACJzR,EAAI,KACV,GAAIA,EAAE,IAAG,EACL,OAAOgJ,EAAI,iBAAiB,EAGhC,KAAM,CAAE,EAAAyL,EAAG,EAAAC,EAAG,EAAA/S,EAAG,EAAAgT,CAAC,EAAK3U,EACjBqV,EAAK7T,EAAEiT,EAAIA,CAAC,EACZa,EAAK9T,EAAEkT,EAAIA,CAAC,EACZa,EAAK/T,EAAEG,EAAIA,CAAC,EACZ6T,EAAKhU,EAAE+T,EAAKA,CAAE,EACdE,EAAMjU,EAAE6T,EAAKvV,CAAC,EACd4V,EAAOlU,EAAE+T,EAAK/T,EAAEiU,EAAMH,CAAE,CAAC,EACzBK,EAAQnU,EAAEgU,EAAKhU,EAAEvB,EAAIuB,EAAE6T,EAAKC,CAAE,CAAC,CAAC,EACtC,GAAII,IAASC,EACT,OAAO3M,EAAI,uCAAuC,EAEtD,MAAM4M,EAAKpU,EAAEiT,EAAIC,CAAC,EACZmB,EAAKrU,EAAEG,EAAIgT,CAAC,EAClB,OAAIiB,IAAOC,EACA7M,EAAI,uCAAuC,EAC/C,IACX,CAEA,OAAO8M,EAAO,CACV,KAAM,CAAE,EAAGC,EAAI,EAAGC,EAAI,EAAGC,CAAE,EAAK,KAC1B,CAAE,EAAGZ,EAAI,EAAGC,EAAI,EAAGC,CAAE,EAAKjB,GAAOwB,CAAK,EACtCI,EAAO1U,EAAEuU,EAAKR,CAAE,EAChBY,EAAO3U,EAAE6T,EAAKY,CAAE,EAChBG,EAAO5U,EAAEwU,EAAKT,CAAE,EAChBc,EAAO7U,EAAE8T,EAAKW,CAAE,EACtB,OAAOC,IAASC,GAAQC,IAASC,CACrC,CACA,KAAM,CACF,OAAO,KAAK,OAAOjV,EAAC,CACxB,CAEA,QAAS,CACL,OAAO,IAAImT,GAAM/S,EAAE,CAAC,KAAK,CAAC,EAAG,KAAK,EAAG,KAAK,EAAGA,EAAE,CAAC,KAAK,CAAC,CAAC,CAC3D,CAEA,QAAS,CACL,KAAM,CAAE,EAAGuU,EAAI,EAAGC,EAAI,EAAGC,CAAE,EAAK,KAC1BnW,EAAI0R,GAEJ1Q,EAAIU,EAAEuU,EAAKA,CAAE,EACbhU,EAAIP,EAAEwU,EAAKA,CAAE,EACbjV,EAAIS,EAAE,GAAKA,EAAEyU,EAAKA,CAAE,CAAC,EACrBjU,EAAIR,EAAE1B,EAAIgB,CAAC,EACXwV,EAAOP,EAAKC,EACZnV,EAAIW,EAAEA,EAAE8U,EAAOA,CAAI,EAAIxV,EAAIiB,CAAC,EAC5BwU,EAAIvU,EAAID,EACRyU,EAAID,EAAIxV,EACRQ,EAAIS,EAAID,EACR0U,EAAKjV,EAAEX,EAAI2V,CAAC,EACZE,EAAKlV,EAAE+U,EAAIhV,CAAC,EACZoV,EAAKnV,EAAEX,EAAIU,CAAC,EACZqV,EAAKpV,EAAEgV,EAAID,CAAC,EAClB,OAAO,IAAIhC,GAAMkC,EAAIC,EAAIE,EAAID,CAAE,CACnC,CAEA,IAAIb,EAAO,CACP,KAAM,CAAE,EAAGC,EAAI,EAAGC,EAAI,EAAGC,EAAI,EAAGY,CAAE,EAAK,KACjC,CAAE,EAAGxB,EAAI,EAAGC,EAAI,EAAGC,EAAI,EAAGuB,CAAE,EAAKxC,GAAOwB,CAAK,EAC7ChW,EAAI0R,GACJvR,EAAIwR,GAEJ3Q,EAAIU,EAAEuU,EAAKV,CAAE,EACbtT,EAAIP,EAAEwU,EAAKV,CAAE,EACbvU,EAAIS,EAAEqV,EAAK5W,EAAI6W,CAAE,EACjB9U,EAAIR,EAAEyU,EAAKV,CAAE,EACb1U,EAAIW,GAAGuU,EAAKC,IAAOX,EAAKC,GAAMxU,EAAIiB,CAAC,EACnCyU,EAAIhV,EAAEQ,EAAIjB,CAAC,EACXwV,EAAI/U,EAAEQ,EAAIjB,CAAC,EACXQ,EAAIC,EAAEO,EAAIjC,EAAIgB,CAAC,EACf2V,EAAKjV,EAAEX,EAAI2V,CAAC,EACZE,EAAKlV,EAAE+U,EAAIhV,CAAC,EACZoV,EAAKnV,EAAEX,EAAIU,CAAC,EACZqV,GAAKpV,EAAEgV,EAAID,CAAC,EAClB,OAAO,IAAIhC,GAAMkC,EAAIC,EAAIE,GAAID,CAAE,CACnC,CACA,SAASb,EAAO,CACZ,OAAO,KAAK,IAAIxB,GAAOwB,CAAK,EAAE,OAAM,CAAE,CAC1C,CAQA,SAASrW,EAAGsX,EAAO,GAAM,CACrB,GAAI,CAACA,IAAStX,IAAM,IAAM,KAAK,IAAG,GAC9B,OAAO2B,GAEX,GADAyS,GAAYpU,EAAG,GAAIyB,EAAC,EAChBzB,IAAM,GACN,OAAO,KACX,GAAI,KAAK,OAAO8W,EAAC,EACb,OAAOS,GAAKvX,CAAC,EAAE,EAEnB,IAAIO,EAAIoB,GACJjB,EAAIoW,GACR,QAAStW,EAAI,KAAMR,EAAI,GAAIQ,EAAIA,EAAE,OAAM,EAAIR,IAAM,GAGzCA,EAAI,GACJO,EAAIA,EAAE,IAAIC,CAAC,EACN8W,IACL5W,EAAIA,EAAE,IAAIF,CAAC,GAEnB,OAAOD,CACX,CACA,eAAeiX,EAAQ,CACnB,OAAO,KAAK,SAASA,EAAQ,EAAK,CACtC,CAEA,UAAW,CACP,KAAM,CAAE,EAAAxC,EAAG,EAAAC,EAAG,EAAA/S,CAAC,EAAK,KAEpB,GAAI,KAAK,OAAOP,EAAC,EACb,MAAO,CAAE,EAAG,GAAI,EAAG,EAAE,EACzB,MAAM8V,EAAKnD,GAAOpS,EAAGX,CAAC,EAElBQ,EAAEG,EAAIuV,CAAE,IAAM,IACdlO,EAAI,iBAAiB,EAEzB,MAAMpI,EAAIY,EAAEiT,EAAIyC,CAAE,EACZ5W,EAAIkB,EAAEkT,EAAIwC,CAAE,EAClB,MAAO,CAAE,EAAAtW,EAAG,EAAAN,CAAC,CACjB,CACA,SAAU,CACN,KAAM,CAAE,EAAAM,EAAG,EAAAN,CAAC,EAAK,KAAK,eAAc,EAAG,SAAQ,EACzCF,EAAI+W,GAAW7W,CAAC,EAEtB,OAAAF,EAAE,EAAE,GAAKQ,EAAI,GAAK,IAAO,EAClBR,CACX,CACA,OAAQ,CACJ,OAAOuS,GAAW,KAAK,SAAS,CACpC,CACA,eAAgB,CACZ,OAAO,KAAK,SAASiB,GAAI/T,EAAC,EAAG,EAAK,CACtC,CACA,cAAe,CACX,OAAO,KAAK,cAAa,EAAG,IAAG,CACnC,CACA,eAAgB,CAEZ,IAAIG,EAAI,KAAK,SAASkB,GAAI,GAAI,EAAK,EAAE,OAAM,EAC3C,OAAIA,GAAI,KACJlB,EAAIA,EAAE,IAAI,IAAI,GACXA,EAAE,IAAG,CAChB,CACJ,CAEA,MAAMuW,GAAI,IAAIhC,GAAMjD,GAAIC,GAAI,GAAI/P,EAAE8P,GAAKC,EAAE,CAAC,EAEpCnQ,GAAI,IAAImT,GAAM,GAAI,GAAI,GAAI,EAAE,EAElCA,GAAM,KAAOgC,GACbhC,GAAM,KAAOnT,GACb,MAAM+V,GAAcnD,GAAQlB,GAAWL,GAAKoB,GAAYG,EAAK,GAAIQ,EAAI,EAAG9C,EAAE,CAAC,EAAE,QAAO,EAC9EqD,GAAgB3U,GAAMwT,GAAI,KAAOjB,GAAWJ,GAAKR,GAAO3R,CAAC,CAAC,EAAE,QAAO,CAAE,CAAC,EACtEgX,GAAO,CAACxW,EAAGyW,IAAU,CAEvB,IAAI7X,EAAIoB,EACR,KAAOyW,KAAU,IACb7X,GAAKA,EACLA,GAAKwB,EAET,OAAOxB,CACX,EAEM8X,GAAe1W,GAAM,CAEvB,MAAM2W,EADM3W,EAAIA,EAAKI,EACJJ,EAAKI,EAChBwW,EAAMJ,GAAKG,EAAI,EAAE,EAAIA,EAAMvW,EAC3ByW,EAAML,GAAKI,EAAI,EAAE,EAAI5W,EAAKI,EAC1B0W,EAAON,GAAKK,EAAI,EAAE,EAAIA,EAAMzW,EAC5B2W,EAAOP,GAAKM,EAAK,GAAG,EAAIA,EAAO1W,EAC/B4W,EAAOR,GAAKO,EAAK,GAAG,EAAIA,EAAO3W,EAC/B6W,EAAOT,GAAKQ,EAAK,GAAG,EAAIA,EAAO5W,EAC/B8W,EAAQV,GAAKS,EAAK,GAAG,EAAIA,EAAO7W,EAChC+W,EAAQX,GAAKU,EAAM,GAAG,EAAID,EAAO7W,EACjCgX,EAAQZ,GAAKW,EAAM,GAAG,EAAIL,EAAO1W,EAEvC,MAAO,CAAE,UADUoW,GAAKY,EAAM,EAAE,EAAIpX,EAAKI,EACrB,GAAAuW,CAAE,CAC1B,EACMU,GAAM,oEAGN/C,GAAU,CAAChV,EAAGK,IAAM,CACtB,MAAM2X,EAAK1W,EAAEjB,EAAIA,EAAIA,CAAC,EAChB4X,EAAK3W,EAAE0W,EAAKA,EAAK3X,CAAC,EAClB6X,EAAMd,GAAYpX,EAAIiY,CAAE,EAAE,UAChC,IAAIvX,EAAIY,EAAEtB,EAAIgY,EAAKE,CAAG,EACtB,MAAMC,EAAM7W,EAAEjB,EAAIK,EAAIA,CAAC,EACjB0X,EAAQ1X,EACR2X,EAAQ/W,EAAEZ,EAAIqX,EAAG,EACjBO,EAAWH,IAAQnY,EACnBuY,EAAWJ,IAAQ7W,EAAE,CAACtB,CAAC,EACvBwY,EAASL,IAAQ7W,EAAE,CAACtB,EAAI+X,EAAG,EACjC,OAAIO,IACA5X,EAAI0X,IACJG,GAAYC,KACZ9X,EAAI2X,IACH/W,EAAEZ,CAAC,EAAI,MAAQ,KAChBA,EAAIY,EAAE,CAACZ,CAAC,GACL,CAAE,QAAS4X,GAAYC,EAAU,MAAO7X,CAAC,CACpD,EAEM+X,GAAWC,GAAS9E,GAAKiB,GAAa6D,CAAI,CAAC,EAG3CC,GAAU,IAAIpY,IAAM4T,GAAO,YAAYb,GAAY,GAAG/S,CAAC,CAAC,EACxDqY,GAAU,IAAIrY,IAAM0T,GAAS,QAAQ,EAAEX,GAAY,GAAG/S,CAAC,CAAC,EAExDsY,GAAaC,GAAW,CAE1B,MAAMC,EAAOD,EAAO,MAAM,EAAG3X,EAAC,EAC9B4X,EAAK,CAAC,GAAK,IACXA,EAAK,EAAE,GAAK,IACZA,EAAK,EAAE,GAAK,GACZ,MAAMxU,EAASuU,EAAO,MAAM3X,GAAGqQ,EAAE,EAC3BuF,EAAS0B,GAAQM,CAAI,EACrBC,EAAQ3C,GAAE,SAASU,CAAM,EACzBkC,EAAaD,EAAM,UACzB,MAAO,CAAE,KAAAD,EAAM,OAAAxU,EAAQ,OAAAwS,EAAQ,MAAAiC,EAAO,WAAAC,CAAU,CACpD,EAEMC,GAA6BC,GAAcR,GAAQ9G,GAAOsH,EAAWhY,EAAC,CAAC,EAAE,KAAK0X,EAAS,EACvFO,GAAwBD,GAAcN,GAAUD,GAAQ/G,GAAOsH,EAAWhY,EAAC,CAAC,CAAC,EAE7EkY,GAAqBF,GAAcD,GAA0BC,CAAS,EAAE,KAAMrZ,GAAMA,EAAE,UAAU,EAGhGwZ,GAAezQ,GAAQ8P,GAAQ9P,EAAI,QAAQ,EAAE,KAAKA,EAAI,MAAM,EAG5D0Q,GAAQ,CAAC,EAAGC,EAAQxQ,IAAQ,CAC9B,KAAM,CAAE,WAAYlI,EAAG,OAAQ3B,CAAC,EAAK,EAC/BG,EAAImZ,GAAQe,CAAM,EAClBjY,EAAI8U,GAAE,SAAS/W,CAAC,EAAE,QAAO,EAO/B,MAAO,CAAE,SANQgU,GAAY/R,EAAGT,EAAGkI,CAAG,EAMnB,OALH8P,GAAW,CAEvB,MAAMrZ,EAAImU,GAAKtU,EAAImZ,GAAQK,CAAM,EAAI3Z,CAAC,EACtC,OAAO0S,GAAOyB,GAAY/R,EAAG0V,GAAWxX,CAAC,CAAC,EAAG+R,EAAE,CACnD,CACyB,CAC7B,EAKMiI,GAAY,MAAOpS,EAAS8R,IAAc,CAC5C,MAAM5Y,EAAIsR,GAAOxK,CAAO,EAClBnI,EAAI,MAAMga,GAA0BC,CAAS,EAC7CK,EAAS,MAAMb,GAAQzZ,EAAE,OAAQqB,CAAC,EACxC,OAAO+Y,GAAYC,GAAMra,EAAGsa,EAAQjZ,CAAC,CAAC,CAC1C,EAuDM4T,GAAS,CACX,YAAa,MAAO9M,GAAY,CAC5B,MAAMlI,EAAIkU,GAAM,EACV9S,EAAI+S,GAAYjM,CAAO,EAC7B,OAAO+K,GAAI,MAAMjT,EAAE,OAAO,UAAWoB,EAAE,MAAM,CAAC,CAClD,EACA,OAAQ,MACZ,EAGMmZ,GAAkB,CAACC,EAAOlG,GAAYtS,EAAC,IAAMwY,EAY7CC,GAAQ,CACV,0BAA2BV,GAC3B,qBAAsBE,GACtB,gBAAiBM,EACrB,EAGMG,GAAI,EACJC,GAAa,IACbC,GAAW,KAAK,KAAKD,GAAaD,EAAC,EAAI,EACvCG,GAAc,IAAMH,GAAI,GACxBI,GAAa,IAAM,CACrB,MAAMC,EAAS,CAAA,EACf,IAAIpa,EAAIuW,GACJnW,EAAIJ,EACR,QAASqa,EAAI,EAAGA,EAAIJ,GAAUI,IAAK,CAC/Bja,EAAIJ,EACJoa,EAAO,KAAKha,CAAC,EACb,QAAS,EAAI,EAAG,EAAI8Z,GAAa,IAC7B9Z,EAAIA,EAAE,IAAIJ,CAAC,EACXoa,EAAO,KAAKha,CAAC,EAEjBJ,EAAII,EAAE,OAAM,CAChB,CACA,OAAOga,CACX,EACA,IAAIE,GAEJ,MAAMC,GAAQ,CAACC,EAAKxa,IAAM,CACtB,MAAM,EAAIA,EAAE,OAAM,EAClB,OAAOwa,EAAM,EAAIxa,CACrB,EAYMgX,GAAQvX,GAAM,CAChB,MAAMgb,EAAOH,KAAUA,GAAQH,GAAU,GACzC,IAAIna,EAAIoB,GACJjB,EAAIoW,GACR,MAAMmE,EAAU,GAAKX,GACfY,EAASD,EACTE,EAAOhH,GAAI8G,EAAU,CAAC,EACtBG,EAAUjH,GAAImG,EAAC,EACrB,QAASM,EAAI,EAAGA,EAAIJ,GAAUI,IAAK,CAC/B,IAAIS,EAAQ,OAAOrb,EAAImb,CAAI,EAC3Bnb,IAAMob,EAMFC,EAAQZ,KACRY,GAASH,EACTlb,GAAK,IAET,MAAMsb,EAAMV,EAAIH,GACVc,EAAOD,EACPE,EAAOF,EAAM,KAAK,IAAID,CAAK,EAAI,EAC/BI,EAASb,EAAI,IAAM,EACnBc,EAAQL,EAAQ,EAClBA,IAAU,EAEV3a,EAAIA,EAAE,IAAIoa,GAAMW,EAAQT,EAAKO,CAAI,CAAC,CAAC,EAGnChb,EAAIA,EAAE,IAAIua,GAAMY,EAAOV,EAAKQ,CAAI,CAAC,CAAC,CAE1C,CACA,OAAIxb,IAAM,IACNuJ,EAAI,cAAc,EACf,CAAE,EAAAhJ,EAAG,EAAAG,EAChB,ECnmBMib,GAAc,8BAEpB,SAASC,GAAgB9S,EAA2B,CAClD,IAAI+S,EAAS,GACb,UAAWC,KAAQhT,EAAO+S,GAAU,OAAO,aAAaC,CAAI,EAC5D,OAAO,KAAKD,CAAM,EAAE,WAAW,IAAK,GAAG,EAAE,WAAW,IAAK,GAAG,EAAE,QAAQ,OAAQ,EAAE,CAClF,CAEA,SAASE,GAAgB/Y,EAA2B,CAClD,MAAMyB,EAAazB,EAAM,WAAW,IAAK,GAAG,EAAE,WAAW,IAAK,GAAG,EAC3DgZ,EAASvX,EAAa,IAAI,QAAQ,EAAKA,EAAW,OAAS,GAAM,CAAC,EAClEoX,EAAS,KAAKG,CAAM,EACpBC,EAAM,IAAI,WAAWJ,EAAO,MAAM,EACxC,QAAS5b,EAAI,EAAGA,EAAI4b,EAAO,OAAQ5b,GAAK,EAAGgc,EAAIhc,CAAC,EAAI4b,EAAO,WAAW5b,CAAC,EACvE,OAAOgc,CACT,CAEA,SAAS/I,GAAWpK,EAA2B,CAC7C,OAAO,MAAM,KAAKA,CAAK,EACpB,IAAKnI,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAC1C,KAAK,EAAE,CACZ,CAEA,eAAeub,GAAqBC,EAAwC,CAC1E,MAAMhD,EAAO,MAAM,OAAO,OAAO,OAAO,UAAWgD,CAAS,EAC5D,OAAOjJ,GAAW,IAAI,WAAWiG,CAAI,CAAC,CACxC,CAEA,eAAeiD,IAA4C,CACzD,MAAMC,EAAahC,GAAM,gBAAA,EACnB8B,EAAY,MAAMrC,GAAkBuC,CAAU,EAEpD,MAAO,CACL,SAFe,MAAMH,GAAqBC,CAAS,EAGnD,UAAWP,GAAgBO,CAAS,EACpC,WAAYP,GAAgBS,CAAU,CAAA,CAE1C,CAEA,eAAsBC,IAAsD,CAC1E,GAAI,CACF,MAAM/Y,EAAM,aAAa,QAAQoY,EAAW,EAC5C,GAAIpY,EAAK,CACP,MAAMC,EAAS,KAAK,MAAMD,CAAG,EAC7B,GACEC,GAAQ,UAAY,GACpB,OAAOA,EAAO,UAAa,UAC3B,OAAOA,EAAO,WAAc,UAC5B,OAAOA,EAAO,YAAe,SAC7B,CACA,MAAM+Y,EAAY,MAAML,GAAqBH,GAAgBvY,EAAO,SAAS,CAAC,EAC9E,GAAI+Y,IAAc/Y,EAAO,SAAU,CACjC,MAAMgZ,EAA0B,CAC9B,GAAGhZ,EACH,SAAU+Y,CAAA,EAEZ,oBAAa,QAAQZ,GAAa,KAAK,UAAUa,CAAO,CAAC,EAClD,CACL,SAAUD,EACV,UAAW/Y,EAAO,UAClB,WAAYA,EAAO,UAAA,CAEvB,CACA,MAAO,CACL,SAAUA,EAAO,SACjB,UAAWA,EAAO,UAClB,WAAYA,EAAO,UAAA,CAEvB,CACF,CACF,MAAQ,CAER,CAEA,MAAMiZ,EAAW,MAAML,GAAA,EACjBM,EAAyB,CAC7B,QAAS,EACT,SAAUD,EAAS,SACnB,UAAWA,EAAS,UACpB,WAAYA,EAAS,WACrB,YAAa,KAAK,IAAA,CAAI,EAExB,oBAAa,QAAQd,GAAa,KAAK,UAAUe,CAAM,CAAC,EACjDD,CACT,CAEA,eAAsBE,GAAkBC,EAA6B9S,EAAiB,CACpF,MAAMO,EAAM0R,GAAgBa,CAAmB,EACzC7Q,EAAO,IAAI,cAAc,OAAOjC,CAAO,EACvC+S,EAAM,MAAM3C,GAAUnO,EAAM1B,CAAG,EACrC,OAAOuR,GAAgBiB,CAAG,CAC5B,CC9FA,MAAMlB,GAAc,0BAEpB,SAASmB,GAAc/U,EAAsB,CAC3C,OAAOA,EAAK,KAAA,CACd,CAEA,SAASgV,GAAgBC,EAAwC,CAC/D,GAAI,CAAC,MAAM,QAAQA,CAAM,QAAU,CAAA,EACnC,MAAMf,MAAU,IAChB,UAAWgB,KAASD,EAAQ,CAC1B,MAAMla,EAAUma,EAAM,KAAA,EAClBna,GAASmZ,EAAI,IAAInZ,CAAO,CAC9B,CACA,MAAO,CAAC,GAAGmZ,CAAG,EAAE,KAAA,CAClB,CAEA,SAASiB,IAAoC,CAC3C,GAAI,CACF,MAAM3Z,EAAM,OAAO,aAAa,QAAQoY,EAAW,EACnD,GAAI,CAACpY,EAAK,OAAO,KACjB,MAAMC,EAAS,KAAK,MAAMD,CAAG,EAG7B,MAFI,CAACC,GAAUA,EAAO,UAAY,GAC9B,CAACA,EAAO,UAAY,OAAOA,EAAO,UAAa,UAC/C,CAACA,EAAO,QAAU,OAAOA,EAAO,QAAW,SAAiB,KACzDA,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAS2Z,GAAWC,EAAwB,CAC1C,GAAI,CACF,OAAO,aAAa,QAAQzB,GAAa,KAAK,UAAUyB,CAAK,CAAC,CAChE,MAAQ,CAER,CACF,CAEO,SAASC,GAAoBpT,EAGT,CACzB,MAAMmT,EAAQF,GAAA,EACd,GAAI,CAACE,GAASA,EAAM,WAAanT,EAAO,SAAU,OAAO,KACzD,MAAMlC,EAAO+U,GAAc7S,EAAO,IAAI,EAChCY,EAAQuS,EAAM,OAAOrV,CAAI,EAC/B,MAAI,CAAC8C,GAAS,OAAOA,EAAM,OAAU,SAAiB,KAC/CA,CACT,CAEO,SAASyS,GAAqBrT,EAKjB,CAClB,MAAMlC,EAAO+U,GAAc7S,EAAO,IAAI,EAChCvG,EAAwB,CAC5B,QAAS,EACT,SAAUuG,EAAO,SACjB,OAAQ,CAAA,CAAC,EAELsT,EAAWL,GAAA,EACbK,GAAYA,EAAS,WAAatT,EAAO,WAC3CvG,EAAK,OAAS,CAAE,GAAG6Z,EAAS,MAAA,GAE9B,MAAM1S,EAAyB,CAC7B,MAAOZ,EAAO,MACd,KAAAlC,EACA,OAAQgV,GAAgB9S,EAAO,MAAM,EACrC,YAAa,KAAK,IAAA,CAAI,EAExB,OAAAvG,EAAK,OAAOqE,CAAI,EAAI8C,EACpBsS,GAAWzZ,CAAI,EACRmH,CACT,CAEO,SAAS2S,GAAqBvT,EAA4C,CAC/E,MAAMmT,EAAQF,GAAA,EACd,GAAI,CAACE,GAASA,EAAM,WAAanT,EAAO,SAAU,OAClD,MAAMlC,EAAO+U,GAAc7S,EAAO,IAAI,EACtC,GAAI,CAACmT,EAAM,OAAOrV,CAAI,EAAG,OACzB,MAAMrE,EAAO,CAAE,GAAG0Z,EAAO,OAAQ,CAAE,GAAGA,EAAM,OAAO,EACnD,OAAO1Z,EAAK,OAAOqE,CAAI,EACvBoV,GAAWzZ,CAAI,CACjB,CCnDA,eAAsB+Z,GAAYpU,EAAqBoI,EAA4B,CACjF,GAAI,GAACpI,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,eACV,CAAAA,EAAM,eAAiB,GAClBoI,GAAM,QAAOpI,EAAM,aAAe,MACvC,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,mBAAoB,EAAE,EAC9DA,EAAM,YAAc,CAClB,QAAS,MAAM,QAAQC,GAAK,OAAO,EAAIA,EAAK,QAAU,CAAA,EACtD,OAAQ,MAAM,QAAQA,GAAK,MAAM,EAAIA,EAAK,OAAS,CAAA,CAAC,CAExD,OAASC,EAAK,CACPkI,GAAM,QAAOpI,EAAM,aAAe,OAAOE,CAAG,EACnD,QAAA,CACEF,EAAM,eAAiB,EACzB,EACF,CAEA,eAAsBqU,GAAqBrU,EAAqBsU,EAAmB,CACjF,GAAI,GAACtU,EAAM,QAAU,CAACA,EAAM,WAC5B,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,sBAAuB,CAAE,UAAAsU,EAAW,EAC/D,MAAMF,GAAYpU,CAAK,CACzB,OAASE,EAAK,CACZF,EAAM,aAAe,OAAOE,CAAG,CACjC,CACF,CAEA,eAAsBqU,GAAoBvU,EAAqBsU,EAAmB,CAGhF,GAFI,GAACtU,EAAM,QAAU,CAACA,EAAM,WAExB,CADc,OAAO,QAAQ,qCAAqC,GAEtE,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,qBAAsB,CAAE,UAAAsU,EAAW,EAC9D,MAAMF,GAAYpU,CAAK,CACzB,OAASE,EAAK,CACZF,EAAM,aAAe,OAAOE,CAAG,CACjC,CACF,CAEA,eAAsBsU,GACpBxU,EACAY,EACA,CACA,GAAI,GAACZ,EAAM,QAAU,CAACA,EAAM,WAC5B,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,sBAAuBY,CAAM,EAGrE,GAAIX,GAAK,MAAO,CACd,MAAMmT,EAAW,MAAMH,GAAA,EACjBvU,EAAOuB,EAAI,MAAQW,EAAO,MAC5BX,EAAI,WAAamT,EAAS,UAAYxS,EAAO,WAAawS,EAAS,WACrEa,GAAqB,CACnB,SAAUb,EAAS,SACnB,KAAA1U,EACA,MAAOuB,EAAI,MACX,OAAQA,EAAI,QAAUW,EAAO,QAAU,CAAA,CAAC,CACzC,EAEH,OAAO,OAAO,8CAA+CX,EAAI,KAAK,CACxE,CACA,MAAMmU,GAAYpU,CAAK,CACzB,OAASE,EAAK,CACZF,EAAM,aAAe,OAAOE,CAAG,CACjC,CACF,CAEA,eAAsBuU,GACpBzU,EACAY,EACA,CAKA,GAJI,GAACZ,EAAM,QAAU,CAACA,EAAM,WAIxB,CAHc,OAAO,QACvB,oBAAoBY,EAAO,QAAQ,KAAKA,EAAO,IAAI,IAAA,GAGrD,GAAI,CACF,MAAMZ,EAAM,OAAO,QAAQ,sBAAuBY,CAAM,EACxD,MAAMwS,EAAW,MAAMH,GAAA,EACnBrS,EAAO,WAAawS,EAAS,UAC/Be,GAAqB,CAAE,SAAUf,EAAS,SAAU,KAAMxS,EAAO,KAAM,EAEzE,MAAMwT,GAAYpU,CAAK,CACzB,OAASE,EAAK,CACZF,EAAM,aAAe,OAAOE,CAAG,CACjC,CACF,CC5HA,eAAsBwU,GACpB1U,EACAoI,EACA,CACA,GAAI,GAACpI,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,aACV,CAAAA,EAAM,aAAe,GAChBoI,GAAM,QAAOpI,EAAM,UAAY,MACpC,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,YAAa,EAAE,EAGvDA,EAAM,MAAQ,MAAM,QAAQC,EAAI,KAAK,EAAIA,EAAI,MAAQ,CAAA,CACvD,OAASC,EAAK,CACPkI,GAAM,QAAOpI,EAAM,UAAY,OAAOE,CAAG,EAChD,QAAA,CACEF,EAAM,aAAe,EACvB,EACF,CCwBA,SAAS2U,GAAwBvR,EAGxB,CACP,GAAI,CAACA,GAAUA,EAAO,OAAS,UAC7B,MAAO,CAAE,OAAQ,qBAAsB,OAAQ,CAAA,CAAC,EAElD,MAAMwR,EAASxR,EAAO,OAAO,KAAA,EAC7B,OAAKwR,EACE,CAAE,OAAQ,0BAA2B,OAAQ,CAAE,OAAAA,EAAO,EADzC,IAEtB,CAEA,SAASC,GACPzR,EACAxC,EAC4D,CAC5D,GAAI,CAACwC,GAAUA,EAAO,OAAS,UAC7B,MAAO,CAAE,OAAQ,qBAAsB,OAAAxC,CAAA,EAEzC,MAAMgU,EAASxR,EAAO,OAAO,KAAA,EAC7B,OAAKwR,EACE,CAAE,OAAQ,0BAA2B,OAAQ,CAAE,GAAGhU,EAAQ,OAAAgU,EAAO,EADpD,IAEtB,CAEA,eAAsBE,GACpB9U,EACAoD,EACA,CACA,GAAI,GAACpD,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,qBACV,CAAAA,EAAM,qBAAuB,GAC7BA,EAAM,UAAY,KAClB,GAAI,CACF,MAAM+U,EAAMJ,GAAwBvR,CAAM,EAC1C,GAAI,CAAC2R,EAAK,CACR/U,EAAM,UAAY,+CAClB,MACF,CACA,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ+U,EAAI,OAAQA,EAAI,MAAM,EAC9DC,GAA2BhV,EAAOC,CAAG,CACvC,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,qBAAuB,EAC/B,EACF,CAEO,SAASgV,GACdhV,EACAkF,EACA,CACAlF,EAAM,sBAAwBkF,EACzBlF,EAAM,qBACTA,EAAM,kBAAoBuE,GAAkBW,EAAS,MAAQ,CAAA,CAAE,EAEnE,CAEA,eAAsB+P,GACpBjV,EACAoD,EACA,CACA,GAAI,GAACpD,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,oBAAsB,GAC5BA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMqF,EAAWrF,EAAM,uBAAuB,KAC9C,GAAI,CAACqF,EAAU,CACbrF,EAAM,UAAY,iDAClB,MACF,CACA,MAAMkV,EACJlV,EAAM,mBACNA,EAAM,uBAAuB,MAC7B,CAAA,EACI+U,EAAMF,GAA4BzR,EAAQ,CAAE,KAAA8R,EAAM,SAAA7P,EAAU,EAClE,GAAI,CAAC0P,EAAK,CACR/U,EAAM,UAAY,8CAClB,MACF,CACA,MAAMA,EAAM,OAAO,QAAQ+U,EAAI,OAAQA,EAAI,MAAM,EACjD/U,EAAM,mBAAqB,GAC3B,MAAM8U,GAAkB9U,EAAOoD,CAAM,CACvC,OAASlD,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,oBAAsB,EAC9B,EACF,CAEO,SAASmV,GACdnV,EACAjF,EACAxB,EACA,CACA,MAAM2B,EAAOqJ,GACXvE,EAAM,mBAAqBA,EAAM,uBAAuB,MAAQ,CAAA,CAAC,EAEnE0E,GAAaxJ,EAAMH,EAAMxB,CAAK,EAC9ByG,EAAM,kBAAoB9E,EAC1B8E,EAAM,mBAAqB,EAC7B,CAEO,SAASoV,GACdpV,EACAjF,EACA,CACA,MAAMG,EAAOqJ,GACXvE,EAAM,mBAAqBA,EAAM,uBAAuB,MAAQ,CAAA,CAAC,EAEnE6E,GAAgB3J,EAAMH,CAAI,EAC1BiF,EAAM,kBAAoB9E,EAC1B8E,EAAM,mBAAqB,EAC7B,CCxJA,eAAsBqV,GAAarV,EAAsB,CACvD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,gBACV,CAAAA,EAAM,gBAAkB,GACxBA,EAAM,cAAgB,KACtBA,EAAM,eAAiB,KACvB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,kBAAmB,EAAE,EAGzD,MAAM,QAAQC,CAAG,GACnBD,EAAM,gBAAkBC,EACxBD,EAAM,eAAiBC,EAAI,SAAW,EAAI,oBAAsB,OAEhED,EAAM,gBAAkB,CAAA,EACxBA,EAAM,eAAiB,uBAE3B,OAASE,EAAK,CACZF,EAAM,cAAgB,OAAOE,CAAG,CAClC,QAAA,CACEF,EAAM,gBAAkB,EAC1B,EACF,CCTA,SAASsV,GAAgBtV,EAAoBgB,EAAavC,EAAwB,CAChF,GAAI,CAACuC,EAAI,OAAQ,OACjB,MAAM3G,EAAO,CAAE,GAAG2F,EAAM,aAAA,EACpBvB,EAASpE,EAAK2G,CAAG,EAAIvC,EACpB,OAAOpE,EAAK2G,CAAG,EACpBhB,EAAM,cAAgB3F,CACxB,CAEA,SAASkb,GAAgBrV,EAAc,CACrC,OAAIA,aAAe,MAAcA,EAAI,QAC9B,OAAOA,CAAG,CACnB,CAEA,eAAsBsV,GAAWxV,EAAoBxD,EAA6B,CAIhF,GAHIA,GAAS,eAAiB,OAAO,KAAKwD,EAAM,aAAa,EAAE,OAAS,IACtEA,EAAM,cAAgB,CAAA,GAEpB,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,cACV,CAAAA,EAAM,cAAgB,GACtBA,EAAM,YAAc,KACpB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,gBAAiB,EAAE,EAGvDC,MAAW,aAAeA,EAChC,OAASC,EAAK,CACZF,EAAM,YAAcuV,GAAgBrV,CAAG,CACzC,QAAA,CACEF,EAAM,cAAgB,EACxB,EACF,CAEO,SAASyV,GACdzV,EACA0V,EACAnc,EACA,CACAyG,EAAM,WAAa,CAAE,GAAGA,EAAM,WAAY,CAAC0V,CAAQ,EAAGnc,CAAA,CACxD,CAEA,eAAsBoc,GACpB3V,EACA0V,EACApP,EACA,CACA,GAAI,GAACtG,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,cAAgB0V,EACtB1V,EAAM,YAAc,KACpB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,gBAAiB,CAAE,SAAA0V,EAAU,QAAApP,EAAS,EACjE,MAAMkP,GAAWxV,CAAK,EACtBsV,GAAgBtV,EAAO0V,EAAU,CAC/B,KAAM,UACN,QAASpP,EAAU,gBAAkB,gBAAA,CACtC,CACH,OAASpG,EAAK,CACZ,MAAMzB,EAAU8W,GAAgBrV,CAAG,EACnCF,EAAM,YAAcvB,EACpB6W,GAAgBtV,EAAO0V,EAAU,CAC/B,KAAM,QACN,QAAAjX,CAAA,CACD,CACH,QAAA,CACEuB,EAAM,cAAgB,IACxB,EACF,CAEA,eAAsB4V,GAAgB5V,EAAoB0V,EAAkB,CAC1E,GAAI,GAAC1V,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,cAAgB0V,EACtB1V,EAAM,YAAc,KACpB,GAAI,CACF,MAAM6V,EAAS7V,EAAM,WAAW0V,CAAQ,GAAK,GAC7C,MAAM1V,EAAM,OAAO,QAAQ,gBAAiB,CAAE,SAAA0V,EAAU,OAAAG,EAAQ,EAChE,MAAML,GAAWxV,CAAK,EACtBsV,GAAgBtV,EAAO0V,EAAU,CAC/B,KAAM,UACN,QAAS,eAAA,CACV,CACH,OAASxV,EAAK,CACZ,MAAMzB,EAAU8W,GAAgBrV,CAAG,EACnCF,EAAM,YAAcvB,EACpB6W,GAAgBtV,EAAO0V,EAAU,CAC/B,KAAM,QACN,QAAAjX,CAAA,CACD,CACH,QAAA,CACEuB,EAAM,cAAgB,IACxB,EACF,CAEA,eAAsB8V,GACpB9V,EACA0V,EACA9b,EACAmc,EACA,CACA,GAAI,GAAC/V,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,cAAgB0V,EACtB1V,EAAM,YAAc,KACpB,GAAI,CACF,MAAMtD,EAAU,MAAMsD,EAAM,OAAO,QAAQ,iBAAkB,CAC3D,KAAApG,EACA,UAAAmc,EACA,UAAW,IAAA,CACZ,EACD,MAAMP,GAAWxV,CAAK,EACtBsV,GAAgBtV,EAAO0V,EAAU,CAC/B,KAAM,UACN,QAAShZ,GAAQ,SAAW,WAAA,CAC7B,CACH,OAASwD,EAAK,CACZ,MAAMzB,EAAU8W,GAAgBrV,CAAG,EACnCF,EAAM,YAAcvB,EACpB6W,GAAgBtV,EAAO0V,EAAU,CAC/B,KAAM,QACN,QAAAjX,CAAA,CACD,CACH,QAAA,CACEuB,EAAM,cAAgB,IACxB,EACF,CChJO,SAASgW,IAAgC,CAC9C,OAAI,OAAO,OAAW,KAAe,OAAO,OAAO,YAAe,YAG3D,OAAO,WAAW,8BAA8B,EAAE,QAFhD,OAIL,OACN,CAEO,SAASC,GAAa5Z,EAAgC,CAC3D,OAAIA,IAAS,SAAiB2Z,GAAA,EACvB3Z,CACT,CCIA,MAAM6Z,GAAW3c,GACX,OAAO,MAAMA,CAAK,EAAU,GAC5BA,GAAS,EAAU,EACnBA,GAAS,EAAU,EAChBA,EAGH4c,GAA6B,IAC7B,OAAO,OAAW,KAAe,OAAO,OAAO,YAAe,WACzD,GAEF,OAAO,WAAW,kCAAkC,EAAE,SAAW,GAGpEC,GAA0BC,GAAsB,CACpDA,EAAK,UAAU,OAAO,kBAAkB,EACxCA,EAAK,MAAM,eAAe,kBAAkB,EAC5CA,EAAK,MAAM,eAAe,kBAAkB,CAC9C,EAEaC,GAAuB,CAAC,CACnC,UAAAC,EACA,WAAAC,EACA,QAAAC,EACA,aAAAC,CACF,IAA8B,CAC5B,GAAIA,IAAiBH,EAAW,OAEhC,MAAMI,EAAoB,WAAW,UAAY,KACjD,GAAI,CAACA,EAAmB,CACtBH,EAAA,EACA,MACF,CAEA,MAAMH,EAAOM,EAAkB,gBACzBC,EAAYD,EACZE,EAAuBV,GAAA,EAK7B,GAFE,EAAQS,EAAU,qBAAwB,CAACC,EAEnB,CACxB,IAAIC,EAAW,GACXC,EAAW,GAEf,GACEN,GAAS,iBAAmB,QAC5BA,GAAS,iBAAmB,QAC5B,OAAO,OAAW,IAElBK,EAAWZ,GAAQO,EAAQ,eAAiB,OAAO,UAAU,EAC7DM,EAAWb,GAAQO,EAAQ,eAAiB,OAAO,WAAW,UACrDA,GAAS,QAAS,CAC3B,MAAMO,EAAOP,EAAQ,QAAQ,sBAAA,EAE3BO,EAAK,MAAQ,GACbA,EAAK,OAAS,GACd,OAAO,OAAW,MAElBF,EAAWZ,IAASc,EAAK,KAAOA,EAAK,MAAQ,GAAK,OAAO,UAAU,EACnED,EAAWb,IAASc,EAAK,IAAMA,EAAK,OAAS,GAAK,OAAO,WAAW,EAExE,CAEAX,EAAK,MAAM,YAAY,mBAAoB,GAAGS,EAAW,GAAG,GAAG,EAC/DT,EAAK,MAAM,YAAY,mBAAoB,GAAGU,EAAW,GAAG,GAAG,EAC/DV,EAAK,UAAU,IAAI,kBAAkB,EAErC,GAAI,CACF,MAAMY,EAAaL,EAAU,sBAAsB,IAAM,CACvDJ,EAAA,CACF,CAAC,EACGS,GAAY,SACTA,EAAW,SAAS,QAAQ,IAAMb,GAAuBC,CAAI,CAAC,EAEnED,GAAuBC,CAAI,CAE/B,MAAQ,CACND,GAAuBC,CAAI,EAC3BG,EAAA,CACF,CACA,MACF,CAEAA,EAAA,EACAJ,GAAuBC,CAAI,CAC7B,EC7FO,SAASa,GAAkBnV,EAAmB,CAC/CA,EAAK,mBAAqB,OAC9BA,EAAK,kBAAoB,OAAO,YAC9B,IAAA,CAAW2S,GAAU3S,EAAgC,CAAE,MAAO,GAAM,GACpE,GAAA,EAEJ,CAEO,SAASoV,GAAiBpV,EAAmB,CAC9CA,EAAK,mBAAqB,OAC9B,cAAcA,EAAK,iBAAiB,EACpCA,EAAK,kBAAoB,KAC3B,CAEO,SAASqV,GAAiBrV,EAAmB,CAC9CA,EAAK,kBAAoB,OAC7BA,EAAK,iBAAmB,OAAO,YAAY,IAAM,CAC3CA,EAAK,MAAQ,QACZoG,GAASpG,EAAgC,CAAE,MAAO,GAAM,CAC/D,EAAG,GAAI,EACT,CAEO,SAASsV,GAAgBtV,EAAmB,CAC7CA,EAAK,kBAAoB,OAC7B,cAAcA,EAAK,gBAAgB,EACnCA,EAAK,iBAAmB,KAC1B,CAEO,SAASuV,GAAkBvV,EAAmB,CAC/CA,EAAK,mBAAqB,OAC9BA,EAAK,kBAAoB,OAAO,YAAY,IAAM,CAC5CA,EAAK,MAAQ,SACZiF,GAAUjF,CAA8B,CAC/C,EAAG,GAAI,EACT,CAEO,SAASwV,GAAiBxV,EAAmB,CAC9CA,EAAK,mBAAqB,OAC9B,cAAcA,EAAK,iBAAiB,EACpCA,EAAK,kBAAoB,KAC3B,CCfO,SAASyV,GAAczV,EAAoB1H,EAAkB,CAClE,MAAMe,EAAa,CACjB,GAAGf,EACH,qBAAsBA,EAAK,sBAAsB,KAAA,GAAUA,EAAK,WAAW,QAAU,MAAA,EAEvF0H,EAAK,SAAW3G,EAChBhB,GAAagB,CAAU,EACnBf,EAAK,QAAU0H,EAAK,QACtBA,EAAK,MAAQ1H,EAAK,MAClBod,GAAmB1V,EAAMkU,GAAa5b,EAAK,KAAK,CAAC,GAEnD0H,EAAK,gBAAkBA,EAAK,SAAS,oBACvC,CAEO,SAAS2V,GAAwB3V,EAAoB1H,EAAc,CACxE,MAAMZ,EAAUY,EAAK,KAAA,EAChBZ,GACDsI,EAAK,SAAS,uBAAyBtI,GAC3C+d,GAAczV,EAAM,CAAE,GAAGA,EAAK,SAAU,qBAAsBtI,EAAS,CACzE,CAEO,SAASke,GAAqB5V,EAAoB,CACvD,GAAI,CAAC,OAAO,SAAS,OAAQ,OAC7B,MAAMnB,EAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM,EACnDgX,EAAWhX,EAAO,IAAI,OAAO,EAC7BiX,EAAcjX,EAAO,IAAI,UAAU,EACnCkX,EAAalX,EAAO,IAAI,SAAS,EACjCmX,EAAgBnX,EAAO,IAAI,YAAY,EAC7C,IAAIoX,EAAiB,GAErB,GAAIJ,GAAY,KAAM,CACpB,MAAMK,EAAQL,EAAS,KAAA,EACnBK,GAASA,IAAUlW,EAAK,SAAS,OACnCyV,GAAczV,EAAM,CAAE,GAAGA,EAAK,SAAU,MAAAkW,EAAO,EAEjDrX,EAAO,OAAO,OAAO,EACrBoX,EAAiB,EACnB,CAEA,GAAIH,GAAe,KAAM,CACvB,MAAMK,EAAWL,EAAY,KAAA,EACzBK,IACDnW,EAA8B,SAAWmW,GAE5CtX,EAAO,OAAO,UAAU,EACxBoX,EAAiB,EACnB,CAEA,GAAIF,GAAc,KAAM,CACtB,MAAMK,EAAUL,EAAW,KAAA,EACvBK,IACFpW,EAAK,WAAaoW,EAClBX,GAAczV,EAAM,CAClB,GAAGA,EAAK,SACR,WAAYoW,EACZ,qBAAsBA,CAAA,CACvB,EAEL,CAEA,GAAIJ,GAAiB,KAAM,CACzB,MAAMK,EAAaL,EAAc,KAAA,EAC7BK,GAAcA,IAAerW,EAAK,SAAS,YAC7CyV,GAAczV,EAAM,CAAE,GAAGA,EAAK,SAAU,WAAAqW,EAAY,EAEtDxX,EAAO,OAAO,YAAY,EAC1BoX,EAAiB,EACnB,CAEA,GAAI,CAACA,EAAgB,OACrB,MAAMhU,EAAM,IAAI,IAAI,OAAO,SAAS,IAAI,EACxCA,EAAI,OAASpD,EAAO,SAAA,EACpB,OAAO,QAAQ,aAAa,CAAA,EAAI,GAAIoD,EAAI,UAAU,CACpD,CAEO,SAASqU,GAAOtW,EAAoB1H,EAAW,CAChD0H,EAAK,MAAQ1H,IAAM0H,EAAK,IAAM1H,GAC9BA,IAAS,SAAQ0H,EAAK,oBAAsB,IAC5C1H,IAAS,OACX+c,GAAiBrV,CAAyD,KACvDA,CAAwD,EACzE1H,IAAS,QACXid,GAAkBvV,CAA0D,KACxDA,CAAyD,EAC1EuW,GAAiBvW,CAAI,EAC1BwW,GAAexW,EAAM1H,EAAM,EAAK,CAClC,CAEO,SAASme,GACdzW,EACA1H,EACAoc,EACA,CAMAH,GAAqB,CACnB,UAAWjc,EACX,WAPiB,IAAM,CACvB0H,EAAK,MAAQ1H,EACbmd,GAAczV,EAAM,CAAE,GAAGA,EAAK,SAAU,MAAO1H,EAAM,EACrDod,GAAmB1V,EAAMkU,GAAa5b,CAAI,CAAC,CAC7C,EAIE,QAAAoc,EACA,aAAc1U,EAAK,KAAA,CACpB,CACH,CAEA,eAAsBuW,GAAiBvW,EAAoB,CACrDA,EAAK,MAAQ,YAAY,MAAM0W,GAAa1W,CAAI,EAChDA,EAAK,MAAQ,YAAY,MAAM2W,GAAgB3W,CAAI,EACnDA,EAAK,MAAQ,aAAa,MAAMsT,GAAatT,CAA8B,EAC3EA,EAAK,MAAQ,YAAY,MAAMpB,GAAaoB,CAA8B,EAC1EA,EAAK,MAAQ,QAAQ,MAAM4W,GAAS5W,CAAI,EACxCA,EAAK,MAAQ,UAAU,MAAMyT,GAAWzT,CAA8B,EACtEA,EAAK,MAAQ,UACf,MAAM2S,GAAU3S,CAA8B,EAC9C,MAAMqS,GAAYrS,CAA8B,EAChD,MAAM+C,GAAW/C,CAA8B,EAC/C,MAAM+S,GAAkB/S,CAA8B,GAEpDA,EAAK,MAAQ,SACf,MAAM6W,GAAY7W,CAAoD,EACtEiB,GACEjB,EACA,CAACA,EAAK,mBAAA,GAGNA,EAAK,MAAQ,WACf,MAAMiD,GAAiBjD,CAA8B,EACrD,MAAM+C,GAAW/C,CAA8B,GAE7CA,EAAK,MAAQ,UACf,MAAMiF,GAAUjF,CAA8B,EAC9CA,EAAK,SAAWA,EAAK,gBAEnBA,EAAK,MAAQ,SACfA,EAAK,aAAe,GACpB,MAAMoG,GAASpG,EAAgC,CAAE,MAAO,GAAM,EAC9D0B,GACE1B,EACA,EAAA,EAGN,CAEO,SAAS8W,IAAgB,CAC9B,GAAI,OAAO,OAAW,IAAa,MAAO,GAC1C,MAAMC,EAAa,OAAO,kCAC1B,OAAI,OAAOA,GAAe,UAAYA,EAAW,OACxC9d,GAAkB8d,CAAU,EAE9Btd,GAA0B,OAAO,SAAS,QAAQ,CAC3D,CAEO,SAASud,GAAsBhX,EAAoB,CACxDA,EAAK,MAAQA,EAAK,SAAS,OAAS,SACpC0V,GAAmB1V,EAAMkU,GAAalU,EAAK,KAAK,CAAC,CACnD,CAEO,SAAS0V,GAAmB1V,EAAoBiX,EAAyB,CAE9E,GADAjX,EAAK,cAAgBiX,EACjB,OAAO,SAAa,IAAa,OACrC,MAAM3C,EAAO,SAAS,gBACtBA,EAAK,QAAQ,MAAQ2C,EACrB3C,EAAK,MAAM,YAAc2C,CAC3B,CAEO,SAASC,GAAoBlX,EAAoB,CACtD,GAAI,OAAO,OAAW,KAAe,OAAO,OAAO,YAAe,WAAY,OAM9E,GALAA,EAAK,WAAa,OAAO,WAAW,8BAA8B,EAClEA,EAAK,kBAAqB4B,GAAU,CAC9B5B,EAAK,QAAU,UACnB0V,GAAmB1V,EAAM4B,EAAM,QAAU,OAAS,OAAO,CAC3D,EACI,OAAO5B,EAAK,WAAW,kBAAqB,WAAY,CAC1DA,EAAK,WAAW,iBAAiB,SAAUA,EAAK,iBAAiB,EACjE,MACF,CACeA,EAAK,WAGb,YAAYA,EAAK,iBAAiB,CAC3C,CAEO,SAASmX,GAAoBnX,EAAoB,CACtD,GAAI,CAACA,EAAK,YAAc,CAACA,EAAK,kBAAmB,OACjD,GAAI,OAAOA,EAAK,WAAW,qBAAwB,WAAY,CAC7DA,EAAK,WAAW,oBAAoB,SAAUA,EAAK,iBAAiB,EACpE,MACF,CACeA,EAAK,WAGb,eAAeA,EAAK,iBAAiB,EAC5CA,EAAK,WAAa,KAClBA,EAAK,kBAAoB,IAC3B,CAEO,SAASoX,GAAoBpX,EAAoBqX,EAAkB,CACxE,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMJ,EAAW1d,GAAY,OAAO,SAAS,SAAUyG,EAAK,QAAQ,GAAK,OACzEsX,GAAgBtX,EAAMiX,CAAQ,EAC9BT,GAAexW,EAAMiX,EAAUI,CAAO,CACxC,CAEO,SAASE,GAAWvX,EAAoB,CAC7C,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMiX,EAAW1d,GAAY,OAAO,SAAS,SAAUyG,EAAK,QAAQ,EACpE,GAAI,CAACiX,EAAU,OAGf,MAAMb,EADM,IAAI,IAAI,OAAO,SAAS,IAAI,EACpB,aAAa,IAAI,SAAS,GAAG,KAAA,EAC7CA,IACFpW,EAAK,WAAaoW,EAClBX,GAAczV,EAAM,CAClB,GAAGA,EAAK,SACR,WAAYoW,EACZ,qBAAsBA,CAAA,CACvB,GAGHkB,GAAgBtX,EAAMiX,CAAQ,CAChC,CAEO,SAASK,GAAgBtX,EAAoB1H,EAAW,CACzD0H,EAAK,MAAQ1H,IAAM0H,EAAK,IAAM1H,GAC9BA,IAAS,SAAQ0H,EAAK,oBAAsB,IAC5C1H,IAAS,OACX+c,GAAiBrV,CAAyD,KACvDA,CAAwD,EACzE1H,IAAS,QACXid,GAAkBvV,CAA0D,KACxDA,CAAyD,EAC3EA,EAAK,WAAgBuW,GAAiBvW,CAAI,CAChD,CAEO,SAASwW,GAAexW,EAAoBjH,EAAUse,EAAkB,CAC7E,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMG,EAAape,GAAcE,GAAWP,EAAKiH,EAAK,QAAQ,CAAC,EACzDyX,EAAcre,GAAc,OAAO,SAAS,QAAQ,EACpD6I,EAAM,IAAI,IAAI,OAAO,SAAS,IAAI,EAEpClJ,IAAQ,QAAUiH,EAAK,WACzBiC,EAAI,aAAa,IAAI,UAAWjC,EAAK,UAAU,EAE/CiC,EAAI,aAAa,OAAO,SAAS,EAG/BwV,IAAgBD,IAClBvV,EAAI,SAAWuV,GAGbH,EACF,OAAO,QAAQ,aAAa,CAAA,EAAI,GAAIpV,EAAI,UAAU,EAElD,OAAO,QAAQ,UAAU,CAAA,EAAI,GAAIA,EAAI,UAAU,CAEnD,CAEO,SAASyV,GACd1X,EACAxH,EACA6e,EACA,CACA,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMpV,EAAM,IAAI,IAAI,OAAO,SAAS,IAAI,EACxCA,EAAI,aAAa,IAAI,UAAWzJ,CAAU,SACtB,QAAQ,aAAa,CAAA,EAAI,GAAIyJ,EAAI,UAAU,CAEjE,CAEA,eAAsByU,GAAa1W,EAAoB,CACrD,MAAM,QAAQ,IAAI,CAChB4E,GAAa5E,EAAgC,EAAK,EAClDsT,GAAatT,CAA8B,EAC3CpB,GAAaoB,CAA8B,EAC3C2D,GAAe3D,CAA8B,EAC7CiF,GAAUjF,CAA8B,CAAA,CACzC,CACH,CAEA,eAAsB2W,GAAgB3W,EAAoB,CACxD,MAAM,QAAQ,IAAI,CAChB4E,GAAa5E,EAAgC,EAAI,EACjDiD,GAAiBjD,CAA8B,EAC/C+C,GAAW/C,CAA8B,CAAA,CAC1C,CACH,CAEA,eAAsB4W,GAAS5W,EAAoB,CACjD,MAAM,QAAQ,IAAI,CAChB4E,GAAa5E,EAAgC,EAAK,EAClD2D,GAAe3D,CAA8B,EAC7C4D,GAAa5D,CAA8B,CAAA,CAC5C,CACH,CCpTO,SAAS2X,GAAW3X,EAAgB,CACzC,OAAOA,EAAK,aAAe,EAAQA,EAAK,SAC1C,CAEO,SAAS4X,GAAkBpd,EAAc,CAC9C,MAAM9C,EAAU8C,EAAK,KAAA,EACrB,GAAI,CAAC9C,EAAS,MAAO,GACrB,MAAM2B,EAAa3B,EAAQ,YAAA,EAC3B,OAAI2B,IAAe,QAAgB,GAEjCA,IAAe,QACfA,IAAe,OACfA,IAAe,SACfA,IAAe,QACfA,IAAe,MAEnB,CAEA,eAAsBwe,GAAgB7X,EAAgB,CAC/CA,EAAK,YACVA,EAAK,YAAc,GACnB,MAAMxB,GAAawB,CAA8B,EACnD,CAEA,SAAS8X,GAAmB9X,EAAgBxF,EAAc,CACxD,MAAM9C,EAAU8C,EAAK,KAAA,EAChB9C,IACLsI,EAAK,UAAY,CACf,GAAGA,EAAK,UACR,CACE,GAAIlC,GAAA,EACJ,KAAMpG,EACN,UAAW,KAAK,IAAA,CAAI,CACtB,EAEJ,CAEA,eAAeqgB,GACb/X,EACAtD,EACA2J,EACA,CACA7F,GAAgBR,CAAwD,EACxE,MAAMgY,EAAK,MAAM5Z,GAAgB4B,EAAgCtD,CAAO,EACxE,MAAI,CAACsb,GAAM3R,GAAM,eAAiB,OAChCrG,EAAK,YAAcqG,EAAK,eAEtB2R,GACFrC,GAAwB3V,EAAkEA,EAAK,UAAU,EAEvGgY,GAAM3R,GAAM,cAAgBA,EAAK,eAAe,SAClDrG,EAAK,YAAcqG,EAAK,eAE1BpF,GAAmBjB,CAA2D,EAC1EgY,GAAM,CAAChY,EAAK,WACTiY,GAAejY,CAAI,EAEnBgY,CACT,CAEA,eAAeC,GAAejY,EAAgB,CAC5C,GAAI,CAACA,EAAK,WAAa2X,GAAW3X,CAAI,EAAG,OACzC,KAAM,CAAC1H,EAAM,GAAGK,CAAI,EAAIqH,EAAK,UAC7B,GAAI,CAAC1H,EAAM,OACX0H,EAAK,UAAYrH,EACN,MAAMof,GAAmB/X,EAAM1H,EAAK,IAAI,IAEjD0H,EAAK,UAAY,CAAC1H,EAAM,GAAG0H,EAAK,SAAS,EAE7C,CAEO,SAASkY,GAAoBlY,EAAgBG,EAAY,CAC9DH,EAAK,UAAYA,EAAK,UAAU,OAAQnD,GAASA,EAAK,KAAOsD,CAAE,CACjE,CAEA,eAAsBgY,GACpBnY,EACAoY,EACA/R,EACA,CACA,GAAI,CAACrG,EAAK,UAAW,OACrB,MAAMqY,EAAgBrY,EAAK,YACrBtD,GAAW0b,GAAmBpY,EAAK,aAAa,KAAA,EACtD,GAAKtD,EAEL,IAAIkb,GAAkBlb,CAAO,EAAG,CAC9B,MAAMmb,GAAgB7X,CAAI,EAC1B,MACF,CAMA,GAJIoY,GAAmB,OACrBpY,EAAK,YAAc,IAGjB2X,GAAW3X,CAAI,EAAG,CACpB8X,GAAmB9X,EAAMtD,CAAO,EAChC,MACF,CAEA,MAAMqb,GAAmB/X,EAAMtD,EAAS,CACtC,cAAe0b,GAAmB,KAAOC,EAAgB,OACzD,aAAc,GAAQD,GAAmB/R,GAAM,aAAY,CAC5D,EACH,CAEA,eAAsBwQ,GAAY7W,EAAgB,CAChD,MAAM,QAAQ,IAAI,CAChBhC,GAAgBgC,CAA8B,EAC9CpB,GAAaoB,CAA8B,EAC3CsY,GAAkBtY,CAAI,CAAA,CACvB,EACDiB,GAAmBjB,EAA6D,EAAI,CACtF,CAEO,MAAMuY,GAAyBN,GAMtC,SAASO,GAAyBxY,EAA+B,CAC/D,MAAM5H,EAASG,GAAqByH,EAAK,UAAU,EACnD,OAAI5H,GAAQ,QAAgBA,EAAO,QAClB4H,EAAK,OAAO,UACF,iBAAiB,gBAAgB,KAAA,GACzC,MACrB,CAEA,SAASyY,GAAmBvf,EAAkBR,EAAyB,CACrE,MAAMS,EAAOF,GAAkBC,CAAQ,EACjCwf,EAAU,mBAAmBhgB,CAAO,EAC1C,OAAOS,EAAO,GAAGA,CAAI,WAAWuf,CAAO,UAAY,WAAWA,CAAO,SACvE,CAEA,eAAsBJ,GAAkBtY,EAAgB,CACtD,GAAI,CAACA,EAAK,UAAW,CACnBA,EAAK,cAAgB,KACrB,MACF,CACA,MAAMtH,EAAU8f,GAAyBxY,CAAI,EAC7C,GAAI,CAACtH,EAAS,CACZsH,EAAK,cAAgB,KACrB,MACF,CACAA,EAAK,cAAgB,KACrB,MAAMiC,EAAMwW,GAAmBzY,EAAK,SAAUtH,CAAO,EACrD,GAAI,CACF,MAAMwF,EAAM,MAAM,MAAM+D,EAAK,CAAE,OAAQ,MAAO,EAC9C,GAAI,CAAC/D,EAAI,GAAI,CACX8B,EAAK,cAAgB,KACrB,MACF,CACA,MAAMW,EAAQ,MAAMzC,EAAI,KAAA,EAClBya,EAAY,OAAOhY,EAAK,WAAc,SAAWA,EAAK,UAAU,OAAS,GAC/EX,EAAK,cAAgB2Y,GAAa,IACpC,MAAQ,CACN3Y,EAAK,cAAgB,IACvB,CACF,CChLA,MAAM1L,GAAE,CAAa,MAAM,CAAkD,EAAEC,GAAED,GAAG,IAAIC,KAAK,CAAC,gBAAgBD,EAAE,OAAOC,CAAC,GAAE,IAAAqkB,GAAC,KAAO,CAAC,YAAY,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,KAAK,EAAErkB,EAAEM,EAAE,CAAC,KAAK,KAAK,EAAE,KAAK,KAAKN,EAAE,KAAK,KAAKM,CAAC,CAAC,KAAK,EAAEN,EAAE,CAAC,OAAO,KAAK,OAAO,EAAEA,CAAC,CAAC,CAAC,OAAO,EAAEA,EAAE,CAAC,OAAO,KAAK,OAAO,GAAGA,CAAC,CAAC,CAAC,ECApS,KAAC,CAAC,EAAED,EAAC,EAAEG,GAAEI,GAAEJ,GAAGA,EAA8PD,GAAE,IAAI,SAAS,cAAc,EAAE,EAAEkB,GAAE,CAACjB,EAAEG,EAAEL,IAAI,CAAC,MAAMW,EAAET,EAAE,KAAK,WAAWW,EAAWR,IAAT,OAAWH,EAAE,KAAKG,EAAE,KAAK,GAAYL,IAAT,OAAW,CAAC,MAAMM,EAAEK,EAAE,aAAaV,GAAC,EAAGY,CAAC,EAAER,EAAEM,EAAE,aAAaV,GAAC,EAAGY,CAAC,EAAEb,EAAE,IAAID,GAAEO,EAAED,EAAEH,EAAEA,EAAE,OAAO,CAAC,KAAK,CAAC,MAAMH,EAAEC,EAAE,KAAK,YAAYK,EAAEL,EAAE,KAAK,EAAEK,IAAIH,EAAE,GAAG,EAAE,CAAC,IAAIH,EAAEC,EAAE,OAAOE,CAAC,EAAEF,EAAE,KAAKE,EAAWF,EAAE,OAAX,SAAkBD,EAAEG,EAAE,QAAQG,EAAE,MAAML,EAAE,KAAKD,CAAC,CAAC,CAAC,GAAGA,IAAIc,GAAG,EAAE,CAAC,IAAIX,EAAEF,EAAE,KAAK,KAAKE,IAAIH,GAAG,CAAC,MAAMA,EAAEO,GAAEJ,CAAC,EAAE,YAAYI,GAAEK,CAAC,EAAE,aAAaT,EAAEW,CAAC,EAAEX,EAAEH,CAAC,CAAC,CAAC,CAAC,OAAOC,CAAC,EAAEc,GAAE,CAACZ,EAAE,EAAEI,EAAEJ,KAAKA,EAAE,KAAK,EAAEI,CAAC,EAAEJ,GAAGmB,GAAE,CAAA,EAAGT,GAAE,CAACV,EAAE,EAAEmB,KAAInB,EAAE,KAAK,EAAEkC,GAAElC,GAAGA,EAAE,KAAKO,GAAEP,GAAG,CAACA,EAAE,KAAI,EAAGA,EAAE,KAAK,QAAQ,ECC5xB,MAAMY,GAAE,CAAC,EAAEb,EAAEF,IAAI,CAAC,MAAMK,EAAE,IAAI,IAAI,QAAQO,EAAEV,EAAEU,GAAGZ,EAAEY,IAAIP,EAAE,IAAI,EAAEO,CAAC,EAAEA,CAAC,EAAE,OAAOP,CAAC,EAAEI,GAAEP,GAAE,cAAcF,EAAC,CAAC,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,EAAE,OAAOK,GAAE,MAAM,MAAM,MAAM,+CAA+C,CAAC,CAAC,GAAG,EAAEH,EAAEF,EAAE,CAAC,IAAIK,EAAWL,IAAT,OAAWA,EAAEE,EAAWA,IAAT,SAAaG,EAAEH,GAAG,MAAMU,EAAE,CAAA,EAAGT,EAAE,GAAG,IAAII,EAAE,EAAE,UAAUL,KAAK,EAAEU,EAAEL,CAAC,EAAEF,EAAEA,EAAEH,EAAEK,CAAC,EAAEA,EAAEJ,EAAEI,CAAC,EAAEP,EAAEE,EAAEK,CAAC,EAAEA,IAAI,MAAM,CAAC,OAAOJ,EAAE,KAAKS,CAAC,CAAC,CAAC,OAAO,EAAEV,EAAEF,EAAE,CAAC,OAAO,KAAK,GAAG,EAAEE,EAAEF,CAAC,EAAE,MAAM,CAAC,OAAOE,EAAE,CAAC,EAAEG,EAAEI,CAAC,EAAE,CAAC,MAAMK,EAAEF,GAAEV,CAAC,EAAE,CAAC,OAAOW,EAAE,KAAKF,CAAC,EAAE,KAAK,GAAG,EAAEN,EAAEI,CAAC,EAAE,GAAG,CAAC,MAAM,QAAQK,CAAC,EAAE,OAAO,KAAK,GAAGH,EAAEE,EAAE,MAAMH,EAAE,KAAK,KAAK,CAAA,EAAGU,EAAE,GAAG,IAAIE,EAAEH,EAAEM,EAAE,EAAEkB,EAAE7B,EAAE,OAAO,EAAEyB,EAAE,EAAE,EAAE1B,EAAE,OAAO,EAAE,KAAKY,GAAGkB,GAAGJ,GAAG,GAAG,GAAUzB,EAAEW,CAAC,IAAV,KAAYA,YAAmBX,EAAE6B,CAAC,IAAV,KAAYA,YAAYjC,EAAEe,CAAC,IAAId,EAAE4B,CAAC,EAAEnB,EAAEmB,CAAC,EAAEpC,GAAEW,EAAEW,CAAC,EAAEZ,EAAE0B,CAAC,CAAC,EAAEd,IAAIc,YAAY7B,EAAEiC,CAAC,IAAIhC,EAAE,CAAC,EAAES,EAAE,CAAC,EAAEjB,GAAEW,EAAE6B,CAAC,EAAE9B,EAAE,CAAC,CAAC,EAAE8B,IAAI,YAAYjC,EAAEe,CAAC,IAAId,EAAE,CAAC,EAAES,EAAE,CAAC,EAAEjB,GAAEW,EAAEW,CAAC,EAAEZ,EAAE,CAAC,CAAC,EAAEN,GAAEL,EAAEkB,EAAE,EAAE,CAAC,EAAEN,EAAEW,CAAC,CAAC,EAAEA,IAAI,YAAYf,EAAEiC,CAAC,IAAIhC,EAAE4B,CAAC,EAAEnB,EAAEmB,CAAC,EAAEpC,GAAEW,EAAE6B,CAAC,EAAE9B,EAAE0B,CAAC,CAAC,EAAEhC,GAAEL,EAAEY,EAAEW,CAAC,EAAEX,EAAE6B,CAAC,CAAC,EAAEA,IAAIJ,YAAqBjB,IAAT,SAAaA,EAAEP,GAAEJ,EAAE4B,EAAE,CAAC,EAAEpB,EAAEJ,GAAEL,EAAEe,EAAEkB,CAAC,GAAGrB,EAAE,IAAIZ,EAAEe,CAAC,CAAC,EAAE,GAAGH,EAAE,IAAIZ,EAAEiC,CAAC,CAAC,EAAE,CAAC,MAAM1C,EAAEkB,EAAE,IAAIR,EAAE4B,CAAC,CAAC,EAAEvC,EAAWC,IAAT,OAAWa,EAAEb,CAAC,EAAE,KAAK,GAAUD,IAAP,KAAS,CAAC,MAAMC,EAAEM,GAAEL,EAAEY,EAAEW,CAAC,CAAC,EAAEtB,GAAEF,EAAEY,EAAE0B,CAAC,CAAC,EAAEnB,EAAEmB,CAAC,EAAEtC,CAAC,MAAMmB,EAAEmB,CAAC,EAAEpC,GAAEH,EAAEa,EAAE0B,CAAC,CAAC,EAAEhC,GAAEL,EAAEY,EAAEW,CAAC,EAAEzB,CAAC,EAAEc,EAAEb,CAAC,EAAE,KAAKsC,GAAG,MAAMjC,GAAEQ,EAAE6B,CAAC,CAAC,EAAEA,SAASrC,GAAEQ,EAAEW,CAAC,CAAC,EAAEA,IAAI,KAAKc,GAAG,GAAG,CAAC,MAAMtC,EAAEM,GAAEL,EAAEkB,EAAE,EAAE,CAAC,CAAC,EAAEjB,GAAEF,EAAEY,EAAE0B,CAAC,CAAC,EAAEnB,EAAEmB,GAAG,EAAEtC,CAAC,CAAC,KAAKwB,GAAGkB,GAAG,CAAC,MAAM1C,EAAEa,EAAEW,GAAG,EAASxB,IAAP,MAAUK,GAAEL,CAAC,CAAC,CAAC,OAAO,KAAK,GAAGU,EAAEK,GAAEd,EAAEkB,CAAC,EAAEnB,EAAC,CAAC,CAAC,ECM7qC,SAASskB,GAAiBnc,EAAqC,CACpE,MAAM9G,EAAI8G,EACV,IAAIC,EAAO,OAAO/G,EAAE,MAAS,SAAWA,EAAE,KAAO,UAIjD,MAAMkjB,EACJ,OAAOljB,EAAE,YAAe,UAAY,OAAOA,EAAE,cAAiB,SAE1DmjB,EAAanjB,EAAE,QACfojB,EAAe,MAAM,QAAQD,CAAU,EAAIA,EAAa,KACxDE,EACJ,MAAM,QAAQD,CAAY,GAC1BA,EAAa,KAAMnc,GAAS,CAE1B,MAAMvI,EAAI,OADAuI,EACS,MAAQ,EAAE,EAAE,YAAA,EAC/B,OAAOvI,IAAM,cAAgBA,IAAM,aACrC,CAAC,EAEG4kB,EACJ,OAAQtjB,EAA8B,UAAa,UACnD,OAAQA,EAA8B,WAAc,UAElDkjB,GAAaG,GAAkBC,KACjCvc,EAAO,cAIT,IAAIC,EAAgC,CAAA,EAEhC,OAAOhH,EAAE,SAAY,SACvBgH,EAAU,CAAC,CAAE,KAAM,OAAQ,KAAMhH,EAAE,QAAS,EACnC,MAAM,QAAQA,EAAE,OAAO,EAChCgH,EAAUhH,EAAE,QAAQ,IAAKiH,IAAmC,CAC1D,KAAOA,EAAK,MAAuC,OACnD,KAAMA,EAAK,KACX,KAAMA,EAAK,KACX,KAAMA,EAAK,MAAQA,EAAK,SAAA,EACxB,EACO,OAAOjH,EAAE,MAAS,WAC3BgH,EAAU,CAAC,CAAE,KAAM,OAAQ,KAAMhH,EAAE,KAAM,GAG3C,MAAMujB,EAAY,OAAOvjB,EAAE,WAAc,SAAWA,EAAE,UAAY,KAAK,IAAA,EACjEuK,EAAK,OAAOvK,EAAE,IAAO,SAAWA,EAAE,GAAK,OAE7C,MAAO,CAAE,KAAA+G,EAAM,QAAAC,EAAS,UAAAuc,EAAW,GAAAhZ,CAAA,CACrC,CAKO,SAASiZ,GAAyBzc,EAAsB,CAC7D,MAAM0c,EAAQ1c,EAAK,YAAA,EAEnB,OAAIA,IAAS,QAAUA,IAAS,OAAeA,EAC3CA,IAAS,YAAoB,YAC7BA,IAAS,SAAiB,SAG5B0c,IAAU,cACVA,IAAU,eACVA,IAAU,QACVA,IAAU,WAEH,OAEF1c,CACT,CAKO,SAAS2c,GAAoB5c,EAA2B,CAC7D,MAAM9G,EAAI8G,EACJC,EAAO,OAAO/G,EAAE,MAAS,SAAWA,EAAE,KAAK,cAAgB,GACjE,OAAO+G,IAAS,cAAgBA,IAAS,aAC3C,CCpFG,MAAMpI,WAAUC,EAAC,CAAC,YAAYK,EAAE,CAAC,GAAG,MAAMA,CAAC,EAAE,KAAK,GAAGP,EAAEO,EAAE,OAAOD,GAAE,MAAM,MAAM,MAAM,KAAK,YAAY,cAAc,uCAAuC,CAAC,CAAC,OAAOD,EAAE,CAAC,GAAGA,IAAIL,GAASK,GAAN,KAAQ,OAAO,KAAK,GAAG,OAAO,KAAK,GAAGA,EAAE,GAAGA,IAAIE,GAAE,OAAOF,EAAE,GAAa,OAAOA,GAAjB,SAAmB,MAAM,MAAM,KAAK,YAAY,cAAc,mCAAmC,EAAE,GAAGA,IAAI,KAAK,GAAG,OAAO,KAAK,GAAG,KAAK,GAAGA,EAAE,MAAMH,EAAE,CAACG,CAAC,EAAE,OAAOH,EAAE,IAAIA,EAAE,KAAK,GAAG,CAAC,WAAW,KAAK,YAAY,WAAW,QAAQA,EAAE,OAAO,CAAA,CAAE,CAAC,CAAC,CAACD,GAAE,cAAc,aAAaA,GAAE,WAAW,EAAE,MAAME,GAAEE,GAAEJ,EAAC,ECHnhB,KAAM,CACJ,QAAA+R,GACA,eAAAiT,GACA,SAAAC,GACA,eAAAC,GACA,yBAAAC,EACF,EAAI,OACJ,GAAI,CACF,OAAAC,EACA,KAAAC,GACA,OAAAC,EACF,EAAI,OACA,CACF,MAAAC,GACA,UAAAC,EACF,EAAI,OAAO,QAAY,KAAe,QACjCJ,IACHA,EAAS,SAAgB5jB,EAAG,CAC1B,OAAOA,CACT,GAEG6jB,KACHA,GAAO,SAAc7jB,EAAG,CACtB,OAAOA,CACT,GAEG+jB,KACHA,GAAQ,SAAeE,EAAMC,EAAS,CACpC,QAASC,EAAO,UAAU,OAAQnZ,EAAO,IAAI,MAAMmZ,EAAO,EAAIA,EAAO,EAAI,CAAC,EAAGC,EAAO,EAAGA,EAAOD,EAAMC,IAClGpZ,EAAKoZ,EAAO,CAAC,EAAI,UAAUA,CAAI,EAEjC,OAAOH,EAAK,MAAMC,EAASlZ,CAAI,CACjC,GAEGgZ,KACHA,GAAY,SAAmBK,EAAM,CACnC,QAASC,EAAQ,UAAU,OAAQtZ,EAAO,IAAI,MAAMsZ,EAAQ,EAAIA,EAAQ,EAAI,CAAC,EAAGC,EAAQ,EAAGA,EAAQD,EAAOC,IACxGvZ,EAAKuZ,EAAQ,CAAC,EAAI,UAAUA,CAAK,EAEnC,OAAO,IAAIF,EAAK,GAAGrZ,CAAI,CACzB,GAEF,MAAMwZ,GAAeC,EAAQ,MAAM,UAAU,OAAO,EAC9CC,GAAmBD,EAAQ,MAAM,UAAU,WAAW,EACtDE,GAAWF,EAAQ,MAAM,UAAU,GAAG,EACtCG,GAAYH,EAAQ,MAAM,UAAU,IAAI,EACxCI,GAAcJ,EAAQ,MAAM,UAAU,MAAM,EAC5CK,GAAoBL,EAAQ,OAAO,UAAU,WAAW,EACxDM,GAAiBN,EAAQ,OAAO,UAAU,QAAQ,EAClDO,GAAcP,EAAQ,OAAO,UAAU,KAAK,EAC5CQ,GAAgBR,EAAQ,OAAO,UAAU,OAAO,EAChDS,GAAgBT,EAAQ,OAAO,UAAU,OAAO,EAChDU,GAAaV,EAAQ,OAAO,UAAU,IAAI,EAC1CW,GAAuBX,EAAQ,OAAO,UAAU,cAAc,EAC9DY,EAAaZ,EAAQ,OAAO,UAAU,IAAI,EAC1Ca,GAAkBC,GAAY,SAAS,EAO7C,SAASd,EAAQR,EAAM,CACrB,OAAO,SAAUC,EAAS,CACpBA,aAAmB,SACrBA,EAAQ,UAAY,GAEtB,QAASsB,EAAQ,UAAU,OAAQxa,EAAO,IAAI,MAAMwa,EAAQ,EAAIA,EAAQ,EAAI,CAAC,EAAGC,EAAQ,EAAGA,EAAQD,EAAOC,IACxGza,EAAKya,EAAQ,CAAC,EAAI,UAAUA,CAAK,EAEnC,OAAO1B,GAAME,EAAMC,EAASlZ,CAAI,CAClC,CACF,CAOA,SAASua,GAAYlB,EAAM,CACzB,OAAO,UAAY,CACjB,QAASqB,EAAQ,UAAU,OAAQ1a,EAAO,IAAI,MAAM0a,CAAK,EAAGC,EAAQ,EAAGA,EAAQD,EAAOC,IACpF3a,EAAK2a,CAAK,EAAI,UAAUA,CAAK,EAE/B,OAAO3B,GAAUK,EAAMrZ,CAAI,CAC7B,CACF,CASA,SAAS4a,EAASC,EAAKxT,EAAO,CAC5B,IAAIyT,EAAoB,UAAU,OAAS,GAAK,UAAU,CAAC,IAAM,OAAY,UAAU,CAAC,EAAIhB,GACxFtB,IAIFA,GAAeqC,EAAK,IAAI,EAE1B,IAAI1mB,EAAIkT,EAAM,OACd,KAAOlT,KAAK,CACV,IAAI4mB,EAAU1T,EAAMlT,CAAC,EACrB,GAAI,OAAO4mB,GAAY,SAAU,CAC/B,MAAMC,EAAYF,EAAkBC,CAAO,EACvCC,IAAcD,IAEXtC,GAASpR,CAAK,IACjBA,EAAMlT,CAAC,EAAI6mB,GAEbD,EAAUC,EAEd,CACAH,EAAIE,CAAO,EAAI,EACjB,CACA,OAAOF,CACT,CAOA,SAASI,GAAW5T,EAAO,CACzB,QAAS6T,EAAQ,EAAGA,EAAQ7T,EAAM,OAAQ6T,IAChBd,GAAqB/S,EAAO6T,CAAK,IAEvD7T,EAAM6T,CAAK,EAAI,MAGnB,OAAO7T,CACT,CAOA,SAAS8T,GAAMC,EAAQ,CACrB,MAAMC,EAAYvC,GAAO,IAAI,EAC7B,SAAW,CAACwC,EAAU7kB,CAAK,IAAK8O,GAAQ6V,CAAM,EACpBhB,GAAqBgB,EAAQE,CAAQ,IAEvD,MAAM,QAAQ7kB,CAAK,EACrB4kB,EAAUC,CAAQ,EAAIL,GAAWxkB,CAAK,EAC7BA,GAAS,OAAOA,GAAU,UAAYA,EAAM,cAAgB,OACrE4kB,EAAUC,CAAQ,EAAIH,GAAM1kB,CAAK,EAEjC4kB,EAAUC,CAAQ,EAAI7kB,GAI5B,OAAO4kB,CACT,CAQA,SAASE,GAAaH,EAAQI,EAAM,CAClC,KAAOJ,IAAW,MAAM,CACtB,MAAMK,EAAO9C,GAAyByC,EAAQI,CAAI,EAClD,GAAIC,EAAM,CACR,GAAIA,EAAK,IACP,OAAOhC,EAAQgC,EAAK,GAAG,EAEzB,GAAI,OAAOA,EAAK,OAAU,WACxB,OAAOhC,EAAQgC,EAAK,KAAK,CAE7B,CACAL,EAAS1C,GAAe0C,CAAM,CAChC,CACA,SAASM,GAAgB,CACvB,OAAO,IACT,CACA,OAAOA,CACT,CAEA,MAAMC,GAAS/C,EAAO,CAAC,IAAK,OAAQ,UAAW,UAAW,OAAQ,UAAW,QAAS,QAAS,IAAK,MAAO,MAAO,MAAO,QAAS,aAAc,OAAQ,KAAM,SAAU,SAAU,UAAW,SAAU,OAAQ,OAAQ,MAAO,WAAY,UAAW,OAAQ,WAAY,KAAM,YAAa,MAAO,UAAW,MAAO,SAAU,MAAO,MAAO,KAAM,KAAM,UAAW,KAAM,WAAY,aAAc,SAAU,OAAQ,SAAU,OAAQ,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,OAAQ,SAAU,SAAU,KAAM,OAAQ,IAAK,MAAO,QAAS,MAAO,MAAO,QAAS,SAAU,KAAM,OAAQ,MAAO,OAAQ,UAAW,OAAQ,WAAY,QAAS,MAAO,OAAQ,KAAM,WAAY,SAAU,SAAU,IAAK,UAAW,MAAO,WAAY,IAAK,KAAM,KAAM,OAAQ,IAAK,OAAQ,SAAU,UAAW,SAAU,SAAU,OAAQ,QAAS,SAAU,SAAU,OAAQ,SAAU,SAAU,QAAS,MAAO,UAAW,MAAO,QAAS,QAAS,KAAM,WAAY,WAAY,QAAS,KAAM,QAAS,OAAQ,KAAM,QAAS,KAAM,IAAK,KAAM,MAAO,QAAS,KAAK,CAAC,EAC3/BgD,GAAQhD,EAAO,CAAC,MAAO,IAAK,WAAY,cAAe,eAAgB,eAAgB,gBAAiB,mBAAoB,SAAU,WAAY,OAAQ,OAAQ,UAAW,eAAgB,cAAe,SAAU,OAAQ,IAAK,QAAS,WAAY,QAAS,QAAS,YAAa,OAAQ,iBAAkB,SAAU,OAAQ,WAAY,QAAS,OAAQ,OAAQ,UAAW,UAAW,WAAY,iBAAkB,OAAQ,OAAQ,QAAS,SAAU,SAAU,OAAQ,WAAY,QAAS,OAAQ,QAAS,OAAQ,OAAO,CAAC,EACvgBiD,GAAajD,EAAO,CAAC,UAAW,gBAAiB,sBAAuB,cAAe,mBAAoB,oBAAqB,oBAAqB,iBAAkB,eAAgB,UAAW,UAAW,UAAW,UAAW,UAAW,iBAAkB,UAAW,UAAW,cAAe,eAAgB,WAAY,eAAgB,qBAAsB,cAAe,SAAU,cAAc,CAAC,EAK/YkD,GAAgBlD,EAAO,CAAC,UAAW,gBAAiB,SAAU,UAAW,YAAa,mBAAoB,iBAAkB,gBAAiB,gBAAiB,gBAAiB,QAAS,YAAa,OAAQ,eAAgB,YAAa,UAAW,gBAAiB,SAAU,MAAO,aAAc,UAAW,KAAK,CAAC,EACtTmD,GAAWnD,EAAO,CAAC,OAAQ,WAAY,SAAU,UAAW,QAAS,SAAU,KAAM,aAAc,gBAAiB,KAAM,KAAM,QAAS,UAAW,WAAY,QAAS,OAAQ,KAAM,SAAU,QAAS,SAAU,OAAQ,OAAQ,UAAW,SAAU,MAAO,QAAS,MAAO,SAAU,aAAc,aAAa,CAAC,EAGtToD,GAAmBpD,EAAO,CAAC,UAAW,cAAe,aAAc,WAAY,YAAa,UAAW,UAAW,SAAU,SAAU,QAAS,YAAa,aAAc,iBAAkB,cAAe,MAAM,CAAC,EAClNnf,GAAOmf,EAAO,CAAC,OAAO,CAAC,EAEvB1f,GAAO0f,EAAO,CAAC,SAAU,SAAU,QAAS,MAAO,iBAAkB,eAAgB,uBAAwB,WAAY,aAAc,UAAW,SAAU,UAAW,cAAe,cAAe,UAAW,OAAQ,QAAS,QAAS,QAAS,OAAQ,UAAW,WAAY,eAAgB,SAAU,cAAe,WAAY,WAAY,UAAW,MAAO,WAAY,0BAA2B,wBAAyB,WAAY,YAAa,UAAW,eAAgB,cAAe,OAAQ,MAAO,UAAW,SAAU,SAAU,OAAQ,OAAQ,WAAY,KAAM,QAAS,YAAa,YAAa,QAAS,OAAQ,QAAS,OAAQ,OAAQ,UAAW,OAAQ,MAAO,MAAO,YAAa,QAAS,SAAU,MAAO,YAAa,WAAY,QAAS,OAAQ,QAAS,UAAW,aAAc,SAAU,OAAQ,UAAW,OAAQ,UAAW,cAAe,cAAe,UAAW,gBAAiB,sBAAuB,SAAU,UAAW,UAAW,aAAc,WAAY,MAAO,WAAY,MAAO,WAAY,OAAQ,OAAQ,UAAW,aAAc,QAAS,WAAY,QAAS,OAAQ,QAAS,OAAQ,OAAQ,UAAW,QAAS,MAAO,SAAU,OAAQ,QAAS,UAAW,WAAY,QAAS,YAAa,OAAQ,SAAU,SAAU,QAAS,QAAS,OAAQ,QAAS,MAAM,CAAC,EAC3wCqD,GAAMrD,EAAO,CAAC,gBAAiB,aAAc,WAAY,qBAAsB,YAAa,SAAU,gBAAiB,gBAAiB,UAAW,gBAAiB,iBAAkB,QAAS,OAAQ,KAAM,QAAS,OAAQ,gBAAiB,YAAa,YAAa,QAAS,sBAAuB,8BAA+B,gBAAiB,kBAAmB,KAAM,KAAM,IAAK,KAAM,KAAM,kBAAmB,YAAa,UAAW,UAAW,MAAO,WAAY,YAAa,MAAO,WAAY,OAAQ,eAAgB,YAAa,SAAU,cAAe,cAAe,gBAAiB,cAAe,YAAa,mBAAoB,eAAgB,aAAc,eAAgB,cAAe,KAAM,KAAM,KAAM,KAAM,aAAc,WAAY,gBAAiB,oBAAqB,SAAU,OAAQ,KAAM,kBAAmB,KAAM,MAAO,YAAa,IAAK,KAAM,KAAM,KAAM,KAAM,UAAW,YAAa,aAAc,WAAY,OAAQ,eAAgB,iBAAkB,eAAgB,mBAAoB,iBAAkB,QAAS,aAAc,aAAc,eAAgB,eAAgB,cAAe,cAAe,mBAAoB,YAAa,MAAO,OAAQ,YAAa,QAAS,SAAU,OAAQ,MAAO,OAAQ,aAAc,SAAU,WAAY,UAAW,QAAS,SAAU,cAAe,SAAU,WAAY,cAAe,OAAQ,aAAc,sBAAuB,mBAAoB,eAAgB,SAAU,gBAAiB,sBAAuB,iBAAkB,IAAK,KAAM,KAAM,SAAU,OAAQ,OAAQ,cAAe,YAAa,UAAW,SAAU,SAAU,QAAS,OAAQ,kBAAmB,QAAS,mBAAoB,mBAAoB,eAAgB,cAAe,eAAgB,cAAe,aAAc,eAAgB,mBAAoB,oBAAqB,iBAAkB,kBAAmB,oBAAqB,iBAAkB,SAAU,eAAgB,QAAS,eAAgB,iBAAkB,WAAY,cAAe,UAAW,UAAW,YAAa,mBAAoB,cAAe,kBAAmB,iBAAkB,aAAc,OAAQ,KAAM,KAAM,UAAW,SAAU,UAAW,aAAc,UAAW,aAAc,gBAAiB,gBAAiB,QAAS,eAAgB,OAAQ,eAAgB,mBAAoB,mBAAoB,IAAK,KAAM,KAAM,QAAS,IAAK,KAAM,KAAM,IAAK,YAAY,CAAC,EACt1EsD,GAAStD,EAAO,CAAC,SAAU,cAAe,QAAS,WAAY,QAAS,eAAgB,cAAe,aAAc,aAAc,QAAS,MAAO,UAAW,eAAgB,WAAY,QAAS,QAAS,SAAU,OAAQ,KAAM,UAAW,SAAU,gBAAiB,SAAU,SAAU,iBAAkB,YAAa,WAAY,cAAe,UAAW,UAAW,gBAAiB,WAAY,WAAY,OAAQ,WAAY,WAAY,aAAc,UAAW,SAAU,SAAU,cAAe,gBAAiB,uBAAwB,YAAa,YAAa,aAAc,WAAY,iBAAkB,iBAAkB,YAAa,UAAW,QAAS,OAAO,CAAC,EAC7pBuD,GAAMvD,EAAO,CAAC,aAAc,SAAU,cAAe,YAAa,aAAa,CAAC,EAGhFwD,GAAgBvD,GAAK,2BAA2B,EAChDwD,GAAWxD,GAAK,uBAAuB,EACvCyD,GAAczD,GAAK,eAAe,EAClC0D,GAAY1D,GAAK,8BAA8B,EAC/C2D,GAAY3D,GAAK,gBAAgB,EACjC4D,GAAiB5D,GAAK,kGAC5B,EACM6D,GAAoB7D,GAAK,uBAAuB,EAChD8D,GAAkB9D,GAAK,6DAC7B,EACM+D,GAAe/D,GAAK,SAAS,EAC7BgE,GAAiBhE,GAAK,0BAA0B,EAEtD,IAAIiE,GAA2B,OAAO,OAAO,CAC3C,UAAW,KACX,UAAWN,GACX,gBAAiBG,GACjB,eAAgBE,GAChB,UAAWN,GACX,aAAcK,GACd,SAAUP,GACV,eAAgBI,GAChB,kBAAmBC,GACnB,cAAeN,GACf,YAAaE,EACf,CAAC,EAID,MAAMS,GAAY,CAChB,QAAS,EAET,KAAM,EAMN,uBAAwB,EACxB,QAAS,EACT,SAAU,CAIZ,EACMC,GAAY,UAAqB,CACrC,OAAO,OAAO,OAAW,IAAc,KAAO,MAChD,EASMC,GAA4B,SAAmCC,EAAcC,EAAmB,CACpG,GAAI,OAAOD,GAAiB,UAAY,OAAOA,EAAa,cAAiB,WAC3E,OAAO,KAKT,IAAIE,EAAS,KACb,MAAMC,EAAY,wBACdF,GAAqBA,EAAkB,aAAaE,CAAS,IAC/DD,EAASD,EAAkB,aAAaE,CAAS,GAEnD,MAAMC,EAAa,aAAeF,EAAS,IAAMA,EAAS,IAC1D,GAAI,CACF,OAAOF,EAAa,aAAaI,EAAY,CAC3C,WAAWpkB,EAAM,CACf,OAAOA,CACT,EACA,gBAAgBqkB,EAAW,CACzB,OAAOA,CACT,CACN,CAAK,CACH,MAAY,CAIV,eAAQ,KAAK,uBAAyBD,EAAa,wBAAwB,EACpE,IACT,CACF,EACME,GAAkB,UAA2B,CACjD,MAAO,CACL,wBAAyB,CAAA,EACzB,sBAAuB,CAAA,EACvB,uBAAwB,CAAA,EACxB,yBAA0B,CAAA,EAC1B,uBAAwB,CAAA,EACxB,wBAAyB,CAAA,EACzB,sBAAuB,CAAA,EACvB,oBAAqB,CAAA,EACrB,uBAAwB,CAAA,CAC5B,CACA,EACA,SAASC,IAAkB,CACzB,IAAIC,EAAS,UAAU,OAAS,GAAK,UAAU,CAAC,IAAM,OAAY,UAAU,CAAC,EAAIV,GAAS,EAC1F,MAAMW,EAAYpK,GAAQkK,GAAgBlK,CAAI,EAG9C,GAFAoK,EAAU,QAAU,QACpBA,EAAU,QAAU,CAAA,EAChB,CAACD,GAAU,CAACA,EAAO,UAAYA,EAAO,SAAS,WAAaX,GAAU,UAAY,CAACW,EAAO,QAG5F,OAAAC,EAAU,YAAc,GACjBA,EAET,GAAI,CACF,SAAAC,CACJ,EAAMF,EACJ,MAAMG,EAAmBD,EACnBE,EAAgBD,EAAiB,cACjC,CACJ,iBAAAE,EACA,oBAAAC,EACA,KAAAC,EACA,QAAAC,EACA,WAAAC,EACA,aAAAC,EAAeV,EAAO,cAAgBA,EAAO,gBAC7C,gBAAAW,EACA,UAAAC,EACA,aAAApB,CACJ,EAAMQ,EACEa,EAAmBL,EAAQ,UAC3BM,EAAYjD,GAAagD,EAAkB,WAAW,EACtDE,EAASlD,GAAagD,EAAkB,QAAQ,EAChDG,EAAiBnD,GAAagD,EAAkB,aAAa,EAC7DI,EAAgBpD,GAAagD,EAAkB,YAAY,EAC3DK,EAAgBrD,GAAagD,EAAkB,YAAY,EAOjE,GAAI,OAAOP,GAAwB,WAAY,CAC7C,MAAMa,EAAWjB,EAAS,cAAc,UAAU,EAC9CiB,EAAS,SAAWA,EAAS,QAAQ,gBACvCjB,EAAWiB,EAAS,QAAQ,cAEhC,CACA,IAAIC,EACAC,EAAY,GAChB,KAAM,CACJ,eAAAC,EACA,mBAAAC,GACA,uBAAAC,GACA,qBAAAC,EACJ,EAAMvB,EACE,CACJ,WAAAwB,EACJ,EAAMvB,EACJ,IAAIwB,EAAQ7B,GAAe,EAI3BG,EAAU,YAAc,OAAOpY,IAAY,YAAc,OAAOqZ,GAAkB,YAAcI,GAAkBA,EAAe,qBAAuB,OACxJ,KAAM,CACJ,cAAA5C,GACA,SAAAC,GACA,YAAAC,GACA,UAAAC,GACA,UAAAC,GACA,kBAAAE,GACA,gBAAAC,GACA,eAAAE,EACJ,EAAMC,GACJ,GAAI,CACF,eAAgBwC,EACpB,EAAMxC,GAMAyC,EAAe,KACnB,MAAMC,GAAuB5E,EAAS,CAAA,EAAI,CAAC,GAAGe,GAAQ,GAAGC,GAAO,GAAGC,GAAY,GAAGE,GAAU,GAAGtiB,EAAI,CAAC,EAEpG,IAAIgmB,EAAe,KACnB,MAAMC,GAAuB9E,EAAS,CAAA,EAAI,CAAC,GAAG1hB,GAAM,GAAG+iB,GAAK,GAAGC,GAAQ,GAAGC,EAAG,CAAC,EAO9E,IAAIwD,EAA0B,OAAO,KAAK7G,GAAO,KAAM,CACrD,aAAc,CACZ,SAAU,GACV,aAAc,GACd,WAAY,GACZ,MAAO,IACb,EACI,mBAAoB,CAClB,SAAU,GACV,aAAc,GACd,WAAY,GACZ,MAAO,IACb,EACI,+BAAgC,CAC9B,SAAU,GACV,aAAc,GACd,WAAY,GACZ,MAAO,EACb,CACA,CAAG,CAAC,EAEE8G,GAAc,KAEdC,GAAc,KAElB,MAAMC,GAAyB,OAAO,KAAKhH,GAAO,KAAM,CACtD,SAAU,CACR,SAAU,GACV,aAAc,GACd,WAAY,GACZ,MAAO,IACb,EACI,eAAgB,CACd,SAAU,GACV,aAAc,GACd,WAAY,GACZ,MAAO,IACb,CACA,CAAG,CAAC,EAEF,IAAIiH,GAAkB,GAElBC,GAAkB,GAElBC,GAA0B,GAG1BC,GAA2B,GAI3BC,GAAqB,GAIrBC,GAAe,GAEfC,GAAiB,GAEjBC,GAAa,GAGbC,GAAa,GAKbC,GAAa,GAGbC,GAAsB,GAGtBC,GAAsB,GAItBC,GAAe,GAcfC,GAAuB,GAC3B,MAAMC,GAA8B,gBAEpC,IAAIC,GAAe,GAGfC,GAAW,GAEXC,GAAe,CAAA,EAEfC,GAAkB,KACtB,MAAMC,GAA0BtG,EAAS,CAAA,EAAI,CAAC,iBAAkB,QAAS,WAAY,OAAQ,gBAAiB,OAAQ,SAAU,OAAQ,KAAM,KAAM,KAAM,KAAM,QAAS,UAAW,WAAY,WAAY,YAAa,SAAU,QAAS,MAAO,WAAY,QAAS,QAAS,QAAS,KAAK,CAAC,EAEhS,IAAIuG,GAAgB,KACpB,MAAMC,GAAwBxG,EAAS,CAAA,EAAI,CAAC,QAAS,QAAS,MAAO,SAAU,QAAS,OAAO,CAAC,EAEhG,IAAIyG,GAAsB,KAC1B,MAAMC,GAA8B1G,EAAS,GAAI,CAAC,MAAO,QAAS,MAAO,KAAM,QAAS,OAAQ,UAAW,cAAe,OAAQ,UAAW,QAAS,QAAS,QAAS,OAAO,CAAC,EAC1K2G,GAAmB,qCACnBC,GAAgB,6BAChBC,GAAiB,+BAEvB,IAAIC,GAAYD,GACZE,GAAiB,GAEjBC,GAAqB,KACzB,MAAMC,GAA6BjH,EAAS,GAAI,CAAC2G,GAAkBC,GAAeC,EAAc,EAAG1H,EAAc,EACjH,IAAI+H,GAAiClH,EAAS,CAAA,EAAI,CAAC,KAAM,KAAM,KAAM,KAAM,OAAO,CAAC,EAC/EmH,GAA0BnH,EAAS,GAAI,CAAC,gBAAgB,CAAC,EAK7D,MAAMoH,GAA+BpH,EAAS,CAAA,EAAI,CAAC,QAAS,QAAS,OAAQ,IAAK,QAAQ,CAAC,EAE3F,IAAIqH,GAAoB,KACxB,MAAMC,GAA+B,CAAC,wBAAyB,WAAW,EACpEC,GAA4B,YAClC,IAAIrH,EAAoB,KAEpBsH,GAAS,KAGb,MAAMC,GAAczE,EAAS,cAAc,MAAM,EAC3C0E,GAAoB,SAA2BC,EAAW,CAC9D,OAAOA,aAAqB,QAAUA,aAAqB,QAC7D,EAOMC,GAAe,UAAwB,CAC3C,IAAIC,EAAM,UAAU,OAAS,GAAK,UAAU,CAAC,IAAM,OAAY,UAAU,CAAC,EAAI,CAAA,EAC9E,GAAI,EAAAL,IAAUA,KAAWK,GAoIzB,KAhII,CAACA,GAAO,OAAOA,GAAQ,YACzBA,EAAM,CAAA,GAGRA,EAAMtH,GAAMsH,CAAG,EACfR,GAEAC,GAA6B,QAAQO,EAAI,iBAAiB,IAAM,GAAKN,GAA4BM,EAAI,kBAErG3H,EAAoBmH,KAAsB,wBAA0BlI,GAAiBD,GAErFyF,EAAenF,GAAqBqI,EAAK,cAAc,EAAI7H,EAAS,CAAA,EAAI6H,EAAI,aAAc3H,CAAiB,EAAI0E,GAC/GC,EAAerF,GAAqBqI,EAAK,cAAc,EAAI7H,EAAS,CAAA,EAAI6H,EAAI,aAAc3H,CAAiB,EAAI4E,GAC/GkC,GAAqBxH,GAAqBqI,EAAK,oBAAoB,EAAI7H,EAAS,CAAA,EAAI6H,EAAI,mBAAoB1I,EAAc,EAAI8H,GAC9HR,GAAsBjH,GAAqBqI,EAAK,mBAAmB,EAAI7H,EAASO,GAAMmG,EAA2B,EAAGmB,EAAI,kBAAmB3H,CAAiB,EAAIwG,GAChKH,GAAgB/G,GAAqBqI,EAAK,mBAAmB,EAAI7H,EAASO,GAAMiG,EAAqB,EAAGqB,EAAI,kBAAmB3H,CAAiB,EAAIsG,GACpJH,GAAkB7G,GAAqBqI,EAAK,iBAAiB,EAAI7H,EAAS,CAAA,EAAI6H,EAAI,gBAAiB3H,CAAiB,EAAIoG,GACxHtB,GAAcxF,GAAqBqI,EAAK,aAAa,EAAI7H,EAAS,GAAI6H,EAAI,YAAa3H,CAAiB,EAAIK,GAAM,CAAA,CAAE,EACpH0E,GAAczF,GAAqBqI,EAAK,aAAa,EAAI7H,EAAS,GAAI6H,EAAI,YAAa3H,CAAiB,EAAIK,GAAM,CAAA,CAAE,EACpH6F,GAAe5G,GAAqBqI,EAAK,cAAc,EAAIA,EAAI,aAAe,GAC9E1C,GAAkB0C,EAAI,kBAAoB,GAC1CzC,GAAkByC,EAAI,kBAAoB,GAC1CxC,GAA0BwC,EAAI,yBAA2B,GACzDvC,GAA2BuC,EAAI,2BAA6B,GAC5DtC,GAAqBsC,EAAI,oBAAsB,GAC/CrC,GAAeqC,EAAI,eAAiB,GACpCpC,GAAiBoC,EAAI,gBAAkB,GACvCjC,GAAaiC,EAAI,YAAc,GAC/BhC,GAAsBgC,EAAI,qBAAuB,GACjD/B,GAAsB+B,EAAI,qBAAuB,GACjDlC,GAAakC,EAAI,YAAc,GAC/B9B,GAAe8B,EAAI,eAAiB,GACpC7B,GAAuB6B,EAAI,sBAAwB,GACnD3B,GAAe2B,EAAI,eAAiB,GACpC1B,GAAW0B,EAAI,UAAY,GAC3BnD,GAAmBmD,EAAI,oBAAsBhG,GAC7CiF,GAAYe,EAAI,WAAahB,GAC7BK,GAAiCW,EAAI,gCAAkCX,GACvEC,GAA0BU,EAAI,yBAA2BV,GACzDpC,EAA0B8C,EAAI,yBAA2B,CAAA,EACrDA,EAAI,yBAA2BH,GAAkBG,EAAI,wBAAwB,YAAY,IAC3F9C,EAAwB,aAAe8C,EAAI,wBAAwB,cAEjEA,EAAI,yBAA2BH,GAAkBG,EAAI,wBAAwB,kBAAkB,IACjG9C,EAAwB,mBAAqB8C,EAAI,wBAAwB,oBAEvEA,EAAI,yBAA2B,OAAOA,EAAI,wBAAwB,gCAAmC,YACvG9C,EAAwB,+BAAiC8C,EAAI,wBAAwB,gCAEnFtC,KACFH,GAAkB,IAEhBS,KACFD,GAAa,IAGXQ,KACFzB,EAAe3E,EAAS,CAAA,EAAInhB,EAAI,EAChCgmB,EAAe,CAAA,EACXuB,GAAa,OAAS,KACxBpG,EAAS2E,EAAc5D,EAAM,EAC7Bf,EAAS6E,EAAcvmB,EAAI,GAEzB8nB,GAAa,MAAQ,KACvBpG,EAAS2E,EAAc3D,EAAK,EAC5BhB,EAAS6E,EAAcxD,EAAG,EAC1BrB,EAAS6E,EAActD,EAAG,GAExB6E,GAAa,aAAe,KAC9BpG,EAAS2E,EAAc1D,EAAU,EACjCjB,EAAS6E,EAAcxD,EAAG,EAC1BrB,EAAS6E,EAActD,EAAG,GAExB6E,GAAa,SAAW,KAC1BpG,EAAS2E,EAAcxD,EAAQ,EAC/BnB,EAAS6E,EAAcvD,EAAM,EAC7BtB,EAAS6E,EAActD,EAAG,IAI1BsG,EAAI,WACF,OAAOA,EAAI,UAAa,WAC1B3C,GAAuB,SAAW2C,EAAI,UAElClD,IAAiBC,KACnBD,EAAepE,GAAMoE,CAAY,GAEnC3E,EAAS2E,EAAckD,EAAI,SAAU3H,CAAiB,IAGtD2H,EAAI,WACF,OAAOA,EAAI,UAAa,WAC1B3C,GAAuB,eAAiB2C,EAAI,UAExChD,IAAiBC,KACnBD,EAAetE,GAAMsE,CAAY,GAEnC7E,EAAS6E,EAAcgD,EAAI,SAAU3H,CAAiB,IAGtD2H,EAAI,mBACN7H,EAASyG,GAAqBoB,EAAI,kBAAmB3H,CAAiB,EAEpE2H,EAAI,kBACFxB,KAAoBC,KACtBD,GAAkB9F,GAAM8F,EAAe,GAEzCrG,EAASqG,GAAiBwB,EAAI,gBAAiB3H,CAAiB,GAE9D2H,EAAI,sBACFxB,KAAoBC,KACtBD,GAAkB9F,GAAM8F,EAAe,GAEzCrG,EAASqG,GAAiBwB,EAAI,oBAAqB3H,CAAiB,GAGlEgG,KACFvB,EAAa,OAAO,EAAI,IAGtBc,IACFzF,EAAS2E,EAAc,CAAC,OAAQ,OAAQ,MAAM,CAAC,EAG7CA,EAAa,QACf3E,EAAS2E,EAAc,CAAC,OAAO,CAAC,EAChC,OAAOK,GAAY,OAEjB6C,EAAI,qBAAsB,CAC5B,GAAI,OAAOA,EAAI,qBAAqB,YAAe,WACjD,MAAMnI,GAAgB,6EAA6E,EAErG,GAAI,OAAOmI,EAAI,qBAAqB,iBAAoB,WACtD,MAAMnI,GAAgB,kFAAkF,EAG1GwE,EAAqB2D,EAAI,qBAEzB1D,EAAYD,EAAmB,WAAW,EAAE,CAC9C,MAEMA,IAAuB,SACzBA,EAAqB7B,GAA0BC,EAAcY,CAAa,GAGxEgB,IAAuB,MAAQ,OAAOC,GAAc,WACtDA,EAAYD,EAAmB,WAAW,EAAE,GAK5ClG,GACFA,EAAO6J,CAAG,EAEZL,GAASK,EACX,EAIMC,GAAe9H,EAAS,GAAI,CAAC,GAAGgB,GAAO,GAAGC,GAAY,GAAGC,EAAa,CAAC,EACvE6G,GAAkB/H,EAAS,CAAA,EAAI,CAAC,GAAGmB,GAAU,GAAGC,EAAgB,CAAC,EAOjE4G,GAAuB,SAA8B7H,EAAS,CAClE,IAAI8H,EAASjE,EAAc7D,CAAO,GAG9B,CAAC8H,GAAU,CAACA,EAAO,WACrBA,EAAS,CACP,aAAcnB,GACd,QAAS,UACjB,GAEI,MAAMoB,EAAUhJ,GAAkBiB,EAAQ,OAAO,EAC3CgI,EAAgBjJ,GAAkB+I,EAAO,OAAO,EACtD,OAAKjB,GAAmB7G,EAAQ,YAAY,EAGxCA,EAAQ,eAAiByG,GAIvBqB,EAAO,eAAiBpB,GACnBqB,IAAY,MAKjBD,EAAO,eAAiBtB,GACnBuB,IAAY,QAAUC,IAAkB,kBAAoBjB,GAA+BiB,CAAa,GAI1G,EAAQL,GAAaI,CAAO,EAEjC/H,EAAQ,eAAiBwG,GAIvBsB,EAAO,eAAiBpB,GACnBqB,IAAY,OAIjBD,EAAO,eAAiBrB,GACnBsB,IAAY,QAAUf,GAAwBgB,CAAa,EAI7D,EAAQJ,GAAgBG,CAAO,EAEpC/H,EAAQ,eAAiB0G,GAIvBoB,EAAO,eAAiBrB,IAAiB,CAACO,GAAwBgB,CAAa,GAG/EF,EAAO,eAAiBtB,IAAoB,CAACO,GAA+BiB,CAAa,EACpF,GAIF,CAACJ,GAAgBG,CAAO,IAAMd,GAA6Bc,CAAO,GAAK,CAACJ,GAAaI,CAAO,GAGjG,GAAAb,KAAsB,yBAA2BL,GAAmB7G,EAAQ,YAAY,GAlDnF,EA0DX,EAMMiI,GAAe,SAAsBC,EAAM,CAC/CrJ,GAAU+D,EAAU,QAAS,CAC3B,QAASsF,CACf,CAAK,EACD,GAAI,CAEFrE,EAAcqE,CAAI,EAAE,YAAYA,CAAI,CACtC,MAAY,CACVxE,EAAOwE,CAAI,CACb,CACF,EAOMC,GAAmB,SAA0BpsB,EAAMikB,EAAS,CAChE,GAAI,CACFnB,GAAU+D,EAAU,QAAS,CAC3B,UAAW5C,EAAQ,iBAAiBjkB,CAAI,EACxC,KAAMikB,CACd,CAAO,CACH,MAAY,CACVnB,GAAU+D,EAAU,QAAS,CAC3B,UAAW,KACX,KAAM5C,CACd,CAAO,CACH,CAGA,GAFAA,EAAQ,gBAAgBjkB,CAAI,EAExBA,IAAS,KACX,GAAI0pB,IAAcC,GAChB,GAAI,CACFuC,GAAajI,CAAO,CACtB,MAAY,CAAC,KAEb,IAAI,CACFA,EAAQ,aAAajkB,EAAM,EAAE,CAC/B,MAAY,CAAC,CAGnB,EAOMqsB,GAAgB,SAAuBC,EAAO,CAElD,IAAIC,EAAM,KACNC,EAAoB,KACxB,GAAI/C,GACF6C,EAAQ,oBAAsBA,MACzB,CAEL,MAAMG,EAAUvJ,GAAYoJ,EAAO,aAAa,EAChDE,EAAoBC,GAAWA,EAAQ,CAAC,CAC1C,CACItB,KAAsB,yBAA2BP,KAAcD,KAEjE2B,EAAQ,iEAAmEA,EAAQ,kBAErF,MAAMI,EAAe1E,EAAqBA,EAAmB,WAAWsE,CAAK,EAAIA,EAKjF,GAAI1B,KAAcD,GAChB,GAAI,CACF4B,EAAM,IAAI/E,EAAS,EAAG,gBAAgBkF,EAAcvB,EAAiB,CACvE,MAAY,CAAC,CAGf,GAAI,CAACoB,GAAO,CAACA,EAAI,gBAAiB,CAChCA,EAAMrE,EAAe,eAAe0C,GAAW,WAAY,IAAI,EAC/D,GAAI,CACF2B,EAAI,gBAAgB,UAAY1B,GAAiB5C,EAAYyE,CAC/D,MAAY,CAEZ,CACF,CACA,MAAMC,EAAOJ,EAAI,MAAQA,EAAI,gBAK7B,OAJID,GAASE,GACXG,EAAK,aAAa7F,EAAS,eAAe0F,CAAiB,EAAGG,EAAK,WAAW,CAAC,GAAK,IAAI,EAGtF/B,KAAcD,GACTtC,GAAqB,KAAKkE,EAAKhD,GAAiB,OAAS,MAAM,EAAE,CAAC,EAEpEA,GAAiBgD,EAAI,gBAAkBI,CAChD,EAOMC,GAAsB,SAA6BnQ,EAAM,CAC7D,OAAO0L,GAAmB,KAAK1L,EAAK,eAAiBA,EAAMA,EAE3D4K,EAAW,aAAeA,EAAW,aAAeA,EAAW,UAAYA,EAAW,4BAA8BA,EAAW,mBAAoB,IAAI,CACzJ,EAOMwF,GAAe,SAAsB5I,EAAS,CAClD,OAAOA,aAAmBsD,IAAoB,OAAOtD,EAAQ,UAAa,UAAY,OAAOA,EAAQ,aAAgB,UAAY,OAAOA,EAAQ,aAAgB,YAAc,EAAEA,EAAQ,sBAAsBqD,IAAiB,OAAOrD,EAAQ,iBAAoB,YAAc,OAAOA,EAAQ,cAAiB,YAAc,OAAOA,EAAQ,cAAiB,UAAY,OAAOA,EAAQ,cAAiB,YAAc,OAAOA,EAAQ,eAAkB,WAC3b,EAOM6I,GAAU,SAAiBntB,EAAO,CACtC,OAAO,OAAOwnB,GAAS,YAAcxnB,aAAiBwnB,CACxD,EACA,SAAS4F,GAAcxE,EAAOyE,EAAalkB,EAAM,CAC/C4Z,GAAa6F,EAAO0E,GAAQ,CAC1BA,EAAK,KAAKpG,EAAWmG,EAAalkB,EAAMwiB,EAAM,CAChD,CAAC,CACH,CAUA,MAAM4B,GAAoB,SAA2BF,EAAa,CAChE,IAAIjoB,EAAU,KAId,GAFAgoB,GAAcxE,EAAM,uBAAwByE,EAAa,IAAI,EAEzDH,GAAaG,CAAW,EAC1B,OAAAd,GAAac,CAAW,EACjB,GAGT,MAAMhB,EAAUhI,EAAkBgJ,EAAY,QAAQ,EAiBtD,GAfAD,GAAcxE,EAAM,oBAAqByE,EAAa,CACpD,QAAAhB,EACA,YAAavD,CACnB,CAAK,EAEGa,IAAgB0D,EAAY,cAAa,GAAM,CAACF,GAAQE,EAAY,iBAAiB,GAAKzJ,EAAW,WAAYyJ,EAAY,SAAS,GAAKzJ,EAAW,WAAYyJ,EAAY,WAAW,GAKzLA,EAAY,WAAa/G,GAAU,wBAKnCqD,IAAgB0D,EAAY,WAAa/G,GAAU,SAAW1C,EAAW,UAAWyJ,EAAY,IAAI,EACtG,OAAAd,GAAac,CAAW,EACjB,GAGT,GAAI,EAAEhE,GAAuB,oBAAoB,UAAYA,GAAuB,SAASgD,CAAO,KAAO,CAACvD,EAAauD,CAAO,GAAKlD,GAAYkD,CAAO,GAAI,CAE1J,GAAI,CAAClD,GAAYkD,CAAO,GAAKmB,GAAsBnB,CAAO,IACpDnD,EAAwB,wBAAwB,QAAUtF,EAAWsF,EAAwB,aAAcmD,CAAO,GAGlHnD,EAAwB,wBAAwB,UAAYA,EAAwB,aAAamD,CAAO,GAC1G,MAAO,GAIX,GAAIhC,IAAgB,CAACG,GAAgB6B,CAAO,EAAG,CAC7C,MAAMoB,EAAatF,EAAckF,CAAW,GAAKA,EAAY,WACvDK,EAAaxF,EAAcmF,CAAW,GAAKA,EAAY,WAC7D,GAAIK,GAAcD,EAAY,CAC5B,MAAME,EAAaD,EAAW,OAC9B,QAASrwB,EAAIswB,EAAa,EAAGtwB,GAAK,EAAG,EAAEA,EAAG,CACxC,MAAMuwB,GAAa7F,EAAU2F,EAAWrwB,CAAC,EAAG,EAAI,EAChDuwB,GAAW,gBAAkBP,EAAY,gBAAkB,GAAK,EAChEI,EAAW,aAAaG,GAAY3F,EAAeoF,CAAW,CAAC,CACjE,CACF,CACF,CACA,OAAAd,GAAac,CAAW,EACjB,EACT,CAOA,OALIA,aAAuB5F,GAAW,CAAC0E,GAAqBkB,CAAW,IAKlEhB,IAAY,YAAcA,IAAY,WAAaA,IAAY,aAAezI,EAAW,8BAA+ByJ,EAAY,SAAS,GAChJd,GAAac,CAAW,EACjB,KAGL3D,IAAsB2D,EAAY,WAAa/G,GAAU,OAE3DlhB,EAAUioB,EAAY,YACtBtK,GAAa,CAAC4C,GAAeC,GAAUC,EAAW,EAAGrZ,GAAQ,CAC3DpH,EAAUoe,GAAcpe,EAASoH,EAAM,GAAG,CAC5C,CAAC,EACG6gB,EAAY,cAAgBjoB,IAC9B+d,GAAU+D,EAAU,QAAS,CAC3B,QAASmG,EAAY,UAAS,CACxC,CAAS,EACDA,EAAY,YAAcjoB,IAI9BgoB,GAAcxE,EAAM,sBAAuByE,EAAa,IAAI,EACrD,GACT,EAUMQ,GAAoB,SAA2BC,EAAOC,EAAQ/tB,EAAO,CAEzE,GAAIkqB,KAAiB6D,IAAW,MAAQA,IAAW,UAAY/tB,KAASmnB,GAAYnnB,KAAS4rB,IAC3F,MAAO,GAMT,GAAI,EAAArC,IAAmB,CAACH,GAAY2E,CAAM,GAAKnK,EAAWkC,GAAWiI,CAAM,IAAU,GAAI,EAAAzE,IAAmB1F,EAAWmC,GAAWgI,CAAM,IAAU,GAAI,EAAA1E,GAAuB,0BAA0B,UAAYA,GAAuB,eAAe0E,EAAQD,CAAK,IAAU,GAAI,CAAC9E,EAAa+E,CAAM,GAAK3E,GAAY2E,CAAM,GAC7T,GAIA,EAAAP,GAAsBM,CAAK,IAAM5E,EAAwB,wBAAwB,QAAUtF,EAAWsF,EAAwB,aAAc4E,CAAK,GAAK5E,EAAwB,wBAAwB,UAAYA,EAAwB,aAAa4E,CAAK,KAAO5E,EAAwB,8BAA8B,QAAUtF,EAAWsF,EAAwB,mBAAoB6E,CAAM,GAAK7E,EAAwB,8BAA8B,UAAYA,EAAwB,mBAAmB6E,EAAQD,CAAK,IAG/fC,IAAW,MAAQ7E,EAAwB,iCAAmCA,EAAwB,wBAAwB,QAAUtF,EAAWsF,EAAwB,aAAclpB,CAAK,GAAKkpB,EAAwB,wBAAwB,UAAYA,EAAwB,aAAalpB,CAAK,IACvS,MAAO,WAGA,CAAA4qB,GAAoBmD,CAAM,GAAU,GAAI,CAAAnK,EAAWiF,GAAkBrF,GAAcxjB,EAAOkmB,GAAiB,EAAE,CAAC,GAAU,GAAK,GAAA6H,IAAW,OAASA,IAAW,cAAgBA,IAAW,SAAWD,IAAU,UAAYrK,GAAczjB,EAAO,OAAO,IAAM,GAAK0qB,GAAcoD,CAAK,IAAU,GAAI,EAAAtE,IAA2B,CAAC5F,EAAWqC,GAAmBzC,GAAcxjB,EAAOkmB,GAAiB,EAAE,CAAC,IAAU,GAAIlmB,EAC1Z,MAAO,SAET,MAAO,EACT,EASMwtB,GAAwB,SAA+BnB,EAAS,CACpE,OAAOA,IAAY,kBAAoB9I,GAAY8I,EAASjG,EAAc,CAC5E,EAWM4H,GAAsB,SAA6BX,EAAa,CAEpED,GAAcxE,EAAM,yBAA0ByE,EAAa,IAAI,EAC/D,KAAM,CACJ,WAAAY,CACN,EAAQZ,EAEJ,GAAI,CAACY,GAAcf,GAAaG,CAAW,EACzC,OAEF,MAAMa,EAAY,CAChB,SAAU,GACV,UAAW,GACX,SAAU,GACV,kBAAmBlF,EACnB,cAAe,MACrB,EACI,IAAItrB,EAAIuwB,EAAW,OAEnB,KAAOvwB,KAAK,CACV,MAAMywB,EAAOF,EAAWvwB,CAAC,EACnB,CACJ,KAAA2C,EACA,aAAA+tB,EACA,MAAOC,EACf,EAAUF,EACEJ,GAAS1J,EAAkBhkB,CAAI,EAC/BiuB,GAAYD,GAClB,IAAIruB,EAAQK,IAAS,QAAUiuB,GAAY5K,GAAW4K,EAAS,EAkB/D,GAhBAJ,EAAU,SAAWH,GACrBG,EAAU,UAAYluB,EACtBkuB,EAAU,SAAW,GACrBA,EAAU,cAAgB,OAC1Bd,GAAcxE,EAAM,sBAAuByE,EAAaa,CAAS,EACjEluB,EAAQkuB,EAAU,UAId/D,KAAyB4D,KAAW,MAAQA,KAAW,UAEzDtB,GAAiBpsB,EAAMgtB,CAAW,EAElCrtB,EAAQoqB,GAA8BpqB,GAGpC2pB,IAAgB/F,EAAW,yCAA0C5jB,CAAK,EAAG,CAC/EysB,GAAiBpsB,EAAMgtB,CAAW,EAClC,QACF,CAEA,GAAIU,KAAW,iBAAmBxK,GAAYvjB,EAAO,MAAM,EAAG,CAC5DysB,GAAiBpsB,EAAMgtB,CAAW,EAClC,QACF,CAEA,GAAIa,EAAU,cACZ,SAGF,GAAI,CAACA,EAAU,SAAU,CACvBzB,GAAiBpsB,EAAMgtB,CAAW,EAClC,QACF,CAEA,GAAI,CAAC5D,IAA4B7F,EAAW,OAAQ5jB,CAAK,EAAG,CAC1DysB,GAAiBpsB,EAAMgtB,CAAW,EAClC,QACF,CAEI3D,IACF3G,GAAa,CAAC4C,GAAeC,GAAUC,EAAW,EAAGrZ,IAAQ,CAC3DxM,EAAQwjB,GAAcxjB,EAAOwM,GAAM,GAAG,CACxC,CAAC,EAGH,MAAMshB,GAAQzJ,EAAkBgJ,EAAY,QAAQ,EACpD,GAAI,CAACQ,GAAkBC,GAAOC,GAAQ/tB,CAAK,EAAG,CAC5CysB,GAAiBpsB,EAAMgtB,CAAW,EAClC,QACF,CAEA,GAAIhF,GAAsB,OAAO5B,GAAiB,UAAY,OAAOA,EAAa,kBAAqB,YACjG,CAAA2H,EACF,OAAQ3H,EAAa,iBAAiBqH,GAAOC,EAAM,EAAC,CAClD,IAAK,cACH,CACE/tB,EAAQqoB,EAAmB,WAAWroB,CAAK,EAC3C,KACF,CACF,IAAK,mBACH,CACEA,EAAQqoB,EAAmB,gBAAgBroB,CAAK,EAChD,KACF,CACd,CAIM,GAAIA,IAAUsuB,GACZ,GAAI,CACEF,EACFf,EAAY,eAAee,EAAc/tB,EAAML,CAAK,EAGpDqtB,EAAY,aAAahtB,EAAML,CAAK,EAElCktB,GAAaG,CAAW,EAC1Bd,GAAac,CAAW,EAExBnK,GAASgE,EAAU,OAAO,CAE9B,MAAY,CACVuF,GAAiBpsB,EAAMgtB,CAAW,CACpC,CAEJ,CAEAD,GAAcxE,EAAM,wBAAyByE,EAAa,IAAI,CAChE,EAMMkB,GAAqB,SAASA,EAAmBC,EAAU,CAC/D,IAAIC,EAAa,KACjB,MAAMC,EAAiBzB,GAAoBuB,CAAQ,EAGnD,IADApB,GAAcxE,EAAM,wBAAyB4F,EAAU,IAAI,EACpDC,EAAaC,EAAe,YAEjCtB,GAAcxE,EAAM,uBAAwB6F,EAAY,IAAI,EAE5DlB,GAAkBkB,CAAU,EAE5BT,GAAoBS,CAAU,EAE1BA,EAAW,mBAAmBnH,GAChCiH,EAAmBE,EAAW,OAAO,EAIzCrB,GAAcxE,EAAM,uBAAwB4F,EAAU,IAAI,CAC5D,EAEA,OAAAtH,EAAU,SAAW,SAAUyF,EAAO,CACpC,IAAIX,EAAM,UAAU,OAAS,GAAK,UAAU,CAAC,IAAM,OAAY,UAAU,CAAC,EAAI,CAAA,EAC1EgB,EAAO,KACP2B,EAAe,KACftB,EAAc,KACduB,EAAa,KASjB,GALA1D,GAAiB,CAACyB,EACdzB,KACFyB,EAAQ,SAGN,OAAOA,GAAU,UAAY,CAACQ,GAAQR,CAAK,EAC7C,GAAI,OAAOA,EAAM,UAAa,YAE5B,GADAA,EAAQA,EAAM,SAAQ,EAClB,OAAOA,GAAU,SACnB,MAAM9I,GAAgB,iCAAiC,MAGzD,OAAMA,GAAgB,4BAA4B,EAItD,GAAI,CAACqD,EAAU,YACb,OAAOyF,EAYT,GATK9C,IACHkC,GAAaC,CAAG,EAGlB9E,EAAU,QAAU,CAAA,EAEhB,OAAOyF,GAAU,WACnBrC,GAAW,IAETA,IAEF,GAAIqC,EAAM,SAAU,CAClB,MAAMN,GAAUhI,EAAkBsI,EAAM,QAAQ,EAChD,GAAI,CAAC7D,EAAauD,EAAO,GAAKlD,GAAYkD,EAAO,EAC/C,MAAMxI,GAAgB,yDAAyD,CAEnF,UACS8I,aAAiBnF,EAG1BwF,EAAON,GAAc,SAAS,EAC9BiC,EAAe3B,EAAK,cAAc,WAAWL,EAAO,EAAI,EACpDgC,EAAa,WAAarI,GAAU,SAAWqI,EAAa,WAAa,QAGlEA,EAAa,WAAa,OADnC3B,EAAO2B,EAKP3B,EAAK,YAAY2B,CAAY,MAE1B,CAEL,GAAI,CAAC5E,IAAc,CAACL,IAAsB,CAACE,IAE3C+C,EAAM,QAAQ,GAAG,IAAM,GACrB,OAAOtE,GAAsB4B,GAAsB5B,EAAmB,WAAWsE,CAAK,EAAIA,EAK5F,GAFAK,EAAON,GAAcC,CAAK,EAEtB,CAACK,EACH,OAAOjD,GAAa,KAAOE,GAAsB3B,EAAY,EAEjE,CAEI0E,GAAQlD,IACVyC,GAAaS,EAAK,UAAU,EAG9B,MAAM6B,EAAe5B,GAAoB3C,GAAWqC,EAAQK,CAAI,EAEhE,KAAOK,EAAcwB,EAAa,YAEhCtB,GAAkBF,CAAW,EAE7BW,GAAoBX,CAAW,EAE3BA,EAAY,mBAAmB/F,GACjCiH,GAAmBlB,EAAY,OAAO,EAI1C,GAAI/C,GACF,OAAOqC,EAGT,GAAI5C,GAAY,CACd,GAAIC,GAEF,IADA4E,EAAanG,GAAuB,KAAKuE,EAAK,aAAa,EACpDA,EAAK,YAEV4B,EAAW,YAAY5B,EAAK,UAAU,OAGxC4B,EAAa5B,EAEf,OAAIhE,EAAa,YAAcA,EAAa,kBAQ1C4F,EAAajG,GAAW,KAAKvB,EAAkBwH,EAAY,EAAI,GAE1DA,CACT,CACA,IAAIE,EAAiBlF,GAAiBoD,EAAK,UAAYA,EAAK,UAE5D,OAAIpD,IAAkBd,EAAa,UAAU,GAAKkE,EAAK,eAAiBA,EAAK,cAAc,SAAWA,EAAK,cAAc,QAAQ,MAAQpJ,EAAWuC,GAAc6G,EAAK,cAAc,QAAQ,IAAI,IAC/L8B,EAAiB,aAAe9B,EAAK,cAAc,QAAQ,KAAO;AAAA,EAAQ8B,GAGxEpF,IACF3G,GAAa,CAAC4C,GAAeC,GAAUC,EAAW,EAAGrZ,IAAQ,CAC3DsiB,EAAiBtL,GAAcsL,EAAgBtiB,GAAM,GAAG,CAC1D,CAAC,EAEI6b,GAAsB4B,GAAsB5B,EAAmB,WAAWyG,CAAc,EAAIA,CACrG,EACA5H,EAAU,UAAY,UAAY,CAChC,IAAI8E,EAAM,UAAU,OAAS,GAAK,UAAU,CAAC,IAAM,OAAY,UAAU,CAAC,EAAI,CAAA,EAC9ED,GAAaC,CAAG,EAChBnC,GAAa,EACf,EACA3C,EAAU,YAAc,UAAY,CAClCyE,GAAS,KACT9B,GAAa,EACf,EACA3C,EAAU,iBAAmB,SAAU6H,EAAKZ,EAAMnuB,EAAO,CAElD2rB,IACHI,GAAa,CAAA,CAAE,EAEjB,MAAM+B,EAAQzJ,EAAkB0K,CAAG,EAC7BhB,EAAS1J,EAAkB8J,CAAI,EACrC,OAAON,GAAkBC,EAAOC,EAAQ/tB,CAAK,CAC/C,EACAknB,EAAU,QAAU,SAAU8H,EAAYC,EAAc,CAClD,OAAOA,GAAiB,YAG5B9L,GAAUyF,EAAMoG,CAAU,EAAGC,CAAY,CAC3C,EACA/H,EAAU,WAAa,SAAU8H,EAAYC,EAAc,CACzD,GAAIA,IAAiB,OAAW,CAC9B,MAAMxK,EAAQxB,GAAiB2F,EAAMoG,CAAU,EAAGC,CAAY,EAC9D,OAAOxK,IAAU,GAAK,OAAYrB,GAAYwF,EAAMoG,CAAU,EAAGvK,EAAO,CAAC,EAAE,CAAC,CAC9E,CACA,OAAOvB,GAAS0F,EAAMoG,CAAU,CAAC,CACnC,EACA9H,EAAU,YAAc,SAAU8H,EAAY,CAC5CpG,EAAMoG,CAAU,EAAI,CAAA,CACtB,EACA9H,EAAU,eAAiB,UAAY,CACrC0B,EAAQ7B,GAAe,CACzB,EACOG,CACT,CACA,IAAIgI,GAASlI,GAAe,EC11C5B,SAAShoB,IAAG,CAAC,MAAM,CAAC,MAAM,GAAG,OAAO,GAAG,WAAW,KAAK,IAAI,GAAG,MAAM,KAAK,SAAS,GAAG,SAAS,KAAK,OAAO,GAAG,UAAU,KAAK,WAAW,IAAI,CAAC,CAAC,IAAIsT,GAAEtT,GAAC,EAAG,SAASM,GAAEzB,EAAE,CAACyU,GAAEzU,CAAC,CAAC,IAAIa,GAAE,CAAC,KAAK,IAAI,IAAI,EAAE,SAASW,EAAExB,EAAEd,EAAE,GAAG,CAAC,IAAID,EAAE,OAAOe,GAAG,SAASA,EAAEA,EAAE,OAAOT,EAAE,CAAC,QAAQ,CAACD,EAAEE,IAAI,CAAC,IAAIL,EAAE,OAAOK,GAAG,SAASA,EAAEA,EAAE,OAAO,OAAOL,EAAEA,EAAE,QAAQoB,EAAE,MAAM,IAAI,EAAEtB,EAAEA,EAAE,QAAQK,EAAEH,CAAC,EAAEI,CAAC,EAAE,SAAS,IAAI,IAAI,OAAON,EAAEC,CAAC,CAAC,EAAE,OAAOK,CAAC,CAAC,IAAI+xB,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,OAAO,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAC,EAAI/wB,EAAE,CAAC,iBAAiB,yBAAyB,kBAAkB,cAAc,uBAAuB,gBAAgB,eAAe,OAAO,WAAW,KAAK,kBAAkB,KAAK,gBAAgB,KAAK,aAAa,OAAO,kBAAkB,MAAM,cAAc,MAAM,oBAAoB,OAAO,UAAU,WAAW,gBAAgB,oBAAoB,gBAAgB,WAAW,wBAAwB,iCAAiC,yBAAyB,mBAAmB,gBAAgB,OAAO,mBAAmB,0BAA0B,WAAW,iBAAiB,gBAAgB,eAAe,iBAAiB,YAAY,QAAQ,SAAS,aAAa,WAAW,eAAe,OAAO,gBAAgB,aAAa,kBAAkB,YAAY,gBAAgB,YAAY,iBAAiB,aAAa,eAAe,YAAY,UAAU,QAAQ,QAAQ,UAAU,kBAAkB,iCAAiC,gBAAgB,mCAAmC,kBAAkB,KAAK,gBAAgB,KAAK,kBAAkB,gCAAgC,oBAAoB,gBAAgB,WAAW,UAAU,cAAc,WAAW,mBAAmB,oDAAoD,sBAAsB,qDAAqD,aAAa,6CAA6C,MAAM,eAAe,cAAc,OAAO,SAAS,MAAM,UAAU,MAAM,UAAU,QAAQ,eAAe,WAAW,UAAU,SAAS,cAAc,OAAO,cAAc,MAAM,cAAcP,GAAG,IAAI,OAAO,WAAWA,CAAC,8BAA8B,EAAE,gBAAgBA,GAAG,IAAI,OAAO,QAAQ,KAAK,IAAI,EAAEA,EAAE,CAAC,CAAC,oDAAoD,EAAE,QAAQA,GAAG,IAAI,OAAO,QAAQ,KAAK,IAAI,EAAEA,EAAE,CAAC,CAAC,oDAAoD,EAAE,iBAAiBA,GAAG,IAAI,OAAO,QAAQ,KAAK,IAAI,EAAEA,EAAE,CAAC,CAAC,iBAAiB,EAAE,kBAAkBA,GAAG,IAAI,OAAO,QAAQ,KAAK,IAAI,EAAEA,EAAE,CAAC,CAAC,IAAI,EAAE,eAAeA,GAAG,IAAI,OAAO,QAAQ,KAAK,IAAI,EAAEA,EAAE,CAAC,CAAC,qBAAqB,GAAG,CAAC,EAAEuxB,GAAG,uBAAuBC,GAAG,wDAAwDC,GAAG,8GAA8GvwB,GAAE,qEAAqEwwB,GAAG,uCAAuC1wB,GAAE,wBAAwB2wB,GAAG,iKAAiKC,GAAGpwB,EAAEmwB,EAAE,EAAE,QAAQ,QAAQ3wB,EAAC,EAAE,QAAQ,aAAa,mBAAmB,EAAE,QAAQ,UAAU,uBAAuB,EAAE,QAAQ,cAAc,SAAS,EAAE,QAAQ,WAAW,cAAc,EAAE,QAAQ,QAAQ,mBAAmB,EAAE,QAAQ,WAAW,EAAE,EAAE,SAAQ,EAAG6wB,GAAGrwB,EAAEmwB,EAAE,EAAE,QAAQ,QAAQ3wB,EAAC,EAAE,QAAQ,aAAa,mBAAmB,EAAE,QAAQ,UAAU,uBAAuB,EAAE,QAAQ,cAAc,SAAS,EAAE,QAAQ,WAAW,cAAc,EAAE,QAAQ,QAAQ,mBAAmB,EAAE,QAAQ,SAAS,mCAAmC,EAAE,SAAQ,EAAG8wB,GAAE,uFAAuFC,GAAG,UAAUzb,GAAE,mCAAmC0b,GAAGxwB,EAAE,6GAA6G,EAAE,QAAQ,QAAQ8U,EAAC,EAAE,QAAQ,QAAQ,8DAA8D,EAAE,SAAQ,EAAG2b,GAAGzwB,EAAE,sCAAsC,EAAE,QAAQ,QAAQR,EAAC,EAAE,SAAQ,EAAGX,GAAE,gWAAgWuB,GAAE,gCAAgCswB,GAAG1wB,EAAE,4dAA4d,GAAG,EAAE,QAAQ,UAAUI,EAAC,EAAE,QAAQ,MAAMvB,EAAC,EAAE,QAAQ,YAAY,0EAA0E,EAAE,SAAQ,EAAG8xB,GAAG3wB,EAAEswB,EAAC,EAAE,QAAQ,KAAK5wB,EAAC,EAAE,QAAQ,UAAU,uBAAuB,EAAE,QAAQ,YAAY,EAAE,EAAE,QAAQ,SAAS,EAAE,EAAE,QAAQ,aAAa,SAAS,EAAE,QAAQ,SAAS,gDAAgD,EAAE,QAAQ,OAAO,wBAAwB,EAAE,QAAQ,OAAO,6DAA6D,EAAE,QAAQ,MAAMb,EAAC,EAAE,SAAQ,EAAG+xB,GAAG5wB,EAAE,yCAAyC,EAAE,QAAQ,YAAY2wB,EAAE,EAAE,SAAQ,EAAGE,GAAE,CAAC,WAAWD,GAAG,KAAKZ,GAAG,IAAIQ,GAAG,OAAOP,GAAG,QAAQC,GAAG,GAAGxwB,GAAE,KAAKgxB,GAAG,SAASN,GAAG,KAAKK,GAAG,QAAQV,GAAG,UAAUY,GAAG,MAAMtxB,GAAE,KAAKkxB,EAAE,EAAEO,GAAG9wB,EAAE,6JAA6J,EAAE,QAAQ,KAAKN,EAAC,EAAE,QAAQ,UAAU,uBAAuB,EAAE,QAAQ,aAAa,SAAS,EAAE,QAAQ,OAAO,wBAAwB,EAAE,QAAQ,SAAS,gDAAgD,EAAE,QAAQ,OAAO,wBAAwB,EAAE,QAAQ,OAAO,6DAA6D,EAAE,QAAQ,MAAMb,EAAC,EAAE,SAAQ,EAAGkyB,GAAG,CAAC,GAAGF,GAAE,SAASR,GAAG,MAAMS,GAAG,UAAU9wB,EAAEswB,EAAC,EAAE,QAAQ,KAAK5wB,EAAC,EAAE,QAAQ,UAAU,uBAAuB,EAAE,QAAQ,YAAY,EAAE,EAAE,QAAQ,QAAQoxB,EAAE,EAAE,QAAQ,aAAa,SAAS,EAAE,QAAQ,SAAS,gDAAgD,EAAE,QAAQ,OAAO,wBAAwB,EAAE,QAAQ,OAAO,6DAA6D,EAAE,QAAQ,MAAMjyB,EAAC,EAAE,SAAQ,CAAE,EAAEmyB,GAAG,CAAC,GAAGH,GAAE,KAAK7wB,EAAE,wIAAwI,EAAE,QAAQ,UAAUI,EAAC,EAAE,QAAQ,OAAO,mKAAmK,EAAE,SAAQ,EAAG,IAAI,oEAAoE,QAAQ,yBAAyB,OAAOf,GAAE,SAAS,mCAAmC,UAAUW,EAAEswB,EAAC,EAAE,QAAQ,KAAK5wB,EAAC,EAAE,QAAQ,UAAU;AAAA,EACn3N,EAAE,QAAQ,WAAW0wB,EAAE,EAAE,QAAQ,SAAS,EAAE,EAAE,QAAQ,aAAa,SAAS,EAAE,QAAQ,UAAU,EAAE,EAAE,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE,EAAE,QAAQ,OAAO,EAAE,EAAE,SAAQ,CAAE,EAAEa,GAAG,8CAA8CC,GAAG,sCAAsCC,GAAG,wBAAwBC,GAAG,8EAA8E9wB,GAAE,gBAAgB+wB,GAAE,kBAAkBC,GAAG,mBAAmBC,GAAGvxB,EAAE,wBAAwB,GAAG,EAAE,QAAQ,cAAcqxB,EAAC,EAAE,SAAQ,EAAGG,GAAG,qBAAqBC,GAAG,uBAAuBC,GAAG,yBAAyBC,GAAG3xB,EAAE,yBAAyB,GAAG,EAAE,QAAQ,OAAO,mGAAmG,EAAE,QAAQ,WAAW8vB,GAAG,WAAW,WAAW,EAAE,QAAQ,OAAO,yBAAyB,EAAE,QAAQ,OAAO,gBAAgB,EAAE,WAAW8B,GAAG,gEAAgEC,GAAG7xB,EAAE4xB,GAAG,GAAG,EAAE,QAAQ,SAAStxB,EAAC,EAAE,SAAQ,EAAGwxB,GAAG9xB,EAAE4xB,GAAG,GAAG,EAAE,QAAQ,SAASJ,EAAE,EAAE,SAAQ,EAAGO,GAAG,wQAAwQC,GAAGhyB,EAAE+xB,GAAG,IAAI,EAAE,QAAQ,iBAAiBT,EAAE,EAAE,QAAQ,cAAcD,EAAC,EAAE,QAAQ,SAAS/wB,EAAC,EAAE,SAAQ,EAAG2xB,GAAGjyB,EAAE+xB,GAAG,IAAI,EAAE,QAAQ,iBAAiBL,EAAE,EAAE,QAAQ,cAAcD,EAAE,EAAE,QAAQ,SAASD,EAAE,EAAE,SAAQ,EAAGU,GAAGlyB,EAAE,mNAAmN,IAAI,EAAE,QAAQ,iBAAiBsxB,EAAE,EAAE,QAAQ,cAAcD,EAAC,EAAE,QAAQ,SAAS/wB,EAAC,EAAE,SAAQ,EAAG6xB,GAAGnyB,EAAE,YAAY,IAAI,EAAE,QAAQ,SAASM,EAAC,EAAE,SAAQ,EAAG8xB,GAAGpyB,EAAE,qCAAqC,EAAE,QAAQ,SAAS,8BAA8B,EAAE,QAAQ,QAAQ,8IAA8I,EAAE,SAAQ,EAAGqyB,GAAGryB,EAAEI,EAAC,EAAE,QAAQ,YAAY,KAAK,EAAE,SAAQ,EAAGkyB,GAAGtyB,EAAE,0JAA0J,EAAE,QAAQ,UAAUqyB,EAAE,EAAE,QAAQ,YAAY,6EAA6E,EAAE,SAAQ,EAAG7f,GAAE,wEAAwE+f,GAAGvyB,EAAE,mEAAmE,EAAE,QAAQ,QAAQwS,EAAC,EAAE,QAAQ,OAAO,yCAAyC,EAAE,QAAQ,QAAQ,6DAA6D,EAAE,WAAWggB,GAAGxyB,EAAE,yBAAyB,EAAE,QAAQ,QAAQwS,EAAC,EAAE,QAAQ,MAAMsC,EAAC,EAAE,SAAQ,EAAG2d,GAAGzyB,EAAE,uBAAuB,EAAE,QAAQ,MAAM8U,EAAC,EAAE,WAAW4d,GAAG1yB,EAAE,wBAAwB,GAAG,EAAE,QAAQ,UAAUwyB,EAAE,EAAE,QAAQ,SAASC,EAAE,EAAE,SAAQ,EAAGE,GAAG,qCAAqCta,GAAE,CAAC,WAAWhZ,GAAE,eAAe8yB,GAAG,SAASC,GAAG,UAAUT,GAAG,GAAGR,GAAG,KAAKD,GAAG,IAAI7xB,GAAE,eAAewyB,GAAG,kBAAkBG,GAAG,kBAAkBE,GAAG,OAAOjB,GAAG,KAAKsB,GAAG,OAAOE,GAAG,YAAYlB,GAAG,QAAQiB,GAAG,cAAcE,GAAG,IAAIJ,GAAG,KAAKlB,GAAG,IAAI/xB,EAAC,EAAEuzB,GAAG,CAAC,GAAGva,GAAE,KAAKrY,EAAE,yBAAyB,EAAE,QAAQ,QAAQwS,EAAC,EAAE,SAAQ,EAAG,QAAQxS,EAAE,+BAA+B,EAAE,QAAQ,QAAQwS,EAAC,EAAE,SAAQ,CAAE,EAAEqC,GAAE,CAAC,GAAGwD,GAAE,kBAAkB4Z,GAAG,eAAeH,GAAG,IAAI9xB,EAAE,gEAAgE,EAAE,QAAQ,WAAW2yB,EAAE,EAAE,QAAQ,QAAQ,2EAA2E,EAAE,SAAQ,EAAG,WAAW,6EAA6E,IAAI,0EAA0E,KAAK3yB,EAAE,qNAAqN,EAAE,QAAQ,WAAW2yB,EAAE,EAAE,SAAQ,CAAE,EAAEE,GAAG,CAAC,GAAGhe,GAAE,GAAG7U,EAAEmxB,EAAE,EAAE,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAKnxB,EAAE6U,GAAE,IAAI,EAAE,QAAQ,OAAO,eAAe,EAAE,QAAQ,UAAU,GAAG,EAAE,SAAQ,CAAE,EAAE1V,GAAE,CAAC,OAAO0xB,GAAE,IAAIE,GAAG,SAASC,EAAE,EAAElxB,GAAE,CAAC,OAAOuY,GAAE,IAAIxD,GAAE,OAAOge,GAAG,SAASD,EAAE,EAAME,GAAG,CAAC,IAAI,QAAQ,IAAI,OAAO,IAAI,OAAO,IAAI,SAAS,IAAI,OAAO,EAAEC,GAAGv0B,GAAGs0B,GAAGt0B,CAAC,EAAE,SAASma,GAAEna,EAAEd,EAAE,CAAC,GAAGA,GAAG,GAAGqB,EAAE,WAAW,KAAKP,CAAC,EAAE,OAAOA,EAAE,QAAQO,EAAE,cAAcg0B,EAAE,UAAUh0B,EAAE,mBAAmB,KAAKP,CAAC,EAAE,OAAOA,EAAE,QAAQO,EAAE,sBAAsBg0B,EAAE,EAAE,OAAOv0B,CAAC,CAAC,SAASuU,GAAEvU,EAAE,CAAC,GAAG,CAACA,EAAE,UAAUA,CAAC,EAAE,QAAQO,EAAE,cAAc,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,OAAOP,CAAC,CAAC,SAASw0B,GAAEx0B,EAAEd,EAAE,CAAC,IAAID,EAAEe,EAAE,QAAQO,EAAE,SAAS,CAACf,EAAEL,EAAES,IAAI,CAAC,IAAIR,EAAE,GAAGS,EAAEV,EAAE,KAAK,EAAEU,GAAG,GAAGD,EAAEC,CAAC,IAAI,MAAMT,EAAE,CAACA,EAAE,OAAOA,EAAE,IAAI,IAAI,CAAC,EAAEG,EAAEN,EAAE,MAAMsB,EAAE,SAAS,EAAEjB,EAAE,EAAE,GAAGC,EAAE,CAAC,EAAE,KAAI,GAAIA,EAAE,MAAK,EAAGA,EAAE,OAAO,GAAG,CAACA,EAAE,GAAG,EAAE,GAAG,KAAI,GAAIA,EAAE,IAAG,EAAGL,EAAE,GAAGK,EAAE,OAAOL,EAAEK,EAAE,OAAOL,CAAC,MAAO,MAAKK,EAAE,OAAOL,GAAGK,EAAE,KAAK,EAAE,EAAE,KAAKD,EAAEC,EAAE,OAAOD,IAAIC,EAAED,CAAC,EAAEC,EAAED,CAAC,EAAE,OAAO,QAAQiB,EAAE,UAAU,GAAG,EAAE,OAAOhB,CAAC,CAAC,SAAS6B,GAAEpB,EAAEd,EAAED,EAAE,CAAC,IAAIM,EAAES,EAAE,OAAO,GAAGT,IAAI,EAAE,MAAM,GAAG,IAAID,EAAE,EAAE,KAAKA,EAAEC,GAAUS,EAAE,OAAOT,EAAED,EAAE,CAAC,IAASJ,GAAMI,IAAoC,OAAOU,EAAE,MAAM,EAAET,EAAED,CAAC,CAAC,CAAC,SAASm1B,GAAGz0B,EAAEd,EAAE,CAAC,GAAGc,EAAE,QAAQd,EAAE,CAAC,CAAC,IAAI,GAAG,MAAM,GAAG,IAAID,EAAE,EAAE,QAAQM,EAAE,EAAEA,EAAES,EAAE,OAAOT,IAAI,GAAGS,EAAET,CAAC,IAAI,KAAKA,YAAYS,EAAET,CAAC,IAAIL,EAAE,CAAC,EAAED,YAAYe,EAAET,CAAC,IAAIL,EAAE,CAAC,IAAID,IAAIA,EAAE,GAAG,OAAOM,EAAE,OAAON,EAAE,EAAE,GAAG,EAAE,CAAC,SAASy1B,GAAG10B,EAAEd,EAAED,EAAEM,EAAED,EAAE,CAAC,IAAIE,EAAEN,EAAE,KAAKC,EAAED,EAAE,OAAO,KAAKU,EAAEI,EAAE,CAAC,EAAE,QAAQV,EAAE,MAAM,kBAAkB,IAAI,EAAEC,EAAE,MAAM,OAAO,GAAG,IAAIH,EAAE,CAAC,KAAKY,EAAE,CAAC,EAAE,OAAO,CAAC,IAAI,IAAI,QAAQ,OAAO,IAAIf,EAAE,KAAKO,EAAE,MAAML,EAAE,KAAKS,EAAE,OAAOL,EAAE,aAAaK,CAAC,CAAC,EAAE,OAAOL,EAAE,MAAM,OAAO,GAAGH,CAAC,CAAC,SAASu1B,GAAG30B,EAAEd,EAAED,EAAE,CAAC,IAAIM,EAAES,EAAE,MAAMf,EAAE,MAAM,sBAAsB,EAAE,GAAGM,IAAI,KAAK,OAAOL,EAAE,IAAII,EAAEC,EAAE,CAAC,EAAE,OAAOL,EAAE,MAAM;AAAA,CACtiL,EAAE,IAAIM,GAAG,CAAC,IAAIL,EAAEK,EAAE,MAAMP,EAAE,MAAM,cAAc,EAAE,GAAGE,IAAI,KAAK,OAAOK,EAAE,GAAG,CAACI,CAAC,EAAET,EAAE,OAAOS,EAAE,QAAQN,EAAE,OAAOE,EAAE,MAAMF,EAAE,MAAM,EAAEE,CAAC,CAAC,EAAE,KAAK;AAAA,CACnI,CAAC,CAAC,IAAIY,GAAE,KAAK,CAAC,QAAQ,MAAM,MAAM,YAAY,EAAE,CAAC,KAAK,QAAQ,GAAGqU,EAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,QAAQ,KAAK,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,iBAAiB,EAAE,EAAE,MAAM,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,eAAe,WAAW,KAAK,KAAK,QAAQ,SAAS,EAAErT,GAAE,EAAE;AAAA,CACvW,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,OAAO,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE9B,EAAEq1B,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,KAAK,EAAE,MAAM,CAAC,KAAK,OAAO,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAI,EAAG,QAAQ,KAAK,MAAM,OAAO,eAAe,IAAI,EAAE,EAAE,CAAC,EAAE,KAAKr1B,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,QAAQ,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAI,EAAG,GAAG,KAAK,MAAM,MAAM,WAAW,KAAK,CAAC,EAAE,CAAC,IAAIA,EAAE8B,GAAE,EAAE,GAAG,GAAG,KAAK,QAAQ,UAAU,CAAC9B,GAAG,KAAK,MAAM,MAAM,gBAAgB,KAAKA,CAAC,KAAK,EAAEA,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,UAAU,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI8B,GAAE,EAAE,CAAC,EAAE;AAAA,CACjkB,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,WAAW,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAEA,GAAE,EAAE,CAAC,EAAE;AAAA,CAC9E,EAAE,MAAM;AAAA,CACR,EAAE9B,EAAE,GAAG,EAAE,GAAGH,EAAE,GAAG,KAAK,EAAE,OAAO,GAAG,CAAC,IAAIS,EAAE,GAAGR,EAAE,CAAA,EAAG,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,IAAI,GAAG,KAAK,MAAM,MAAM,gBAAgB,KAAK,EAAE,CAAC,CAAC,EAAEA,EAAE,KAAK,EAAE,CAAC,CAAC,EAAEQ,EAAE,WAAW,CAACA,EAAER,EAAE,KAAK,EAAE,CAAC,CAAC,MAAO,OAAM,EAAE,EAAE,MAAM,CAAC,EAAE,IAAI,EAAEA,EAAE,KAAK;AAAA,CACxM,EAAEM,EAAE,EAAE,QAAQ,KAAK,MAAM,MAAM,wBAAwB;AAAA,OACjD,EAAE,QAAQ,KAAK,MAAM,MAAM,yBAAyB,EAAE,EAAEJ,EAAEA,EAAE,GAAGA,CAAC;AAAA,EACrE,CAAC,GAAG,EAAE,EAAE,EAAE,GAAG,CAAC;AAAA,EACdI,CAAC,GAAGA,EAAE,IAAIc,EAAE,KAAK,MAAM,MAAM,IAAI,GAAG,KAAK,MAAM,MAAM,IAAI,GAAG,KAAK,MAAM,YAAYd,EAAEP,EAAE,EAAE,EAAE,KAAK,MAAM,MAAM,IAAIqB,EAAE,EAAE,SAAS,EAAE,MAAM,IAAI,EAAErB,EAAE,GAAG,EAAE,EAAE,GAAG,GAAG,OAAO,OAAO,MAAM,GAAG,GAAG,OAAO,aAAa,CAAC,IAAIoC,EAAE,EAAEtB,EAAEsB,EAAE,IAAI;AAAA,EACzN,EAAE,KAAK;AAAA,CACR,EAAEqzB,EAAE,KAAK,WAAW30B,CAAC,EAAEd,EAAEA,EAAE,OAAO,CAAC,EAAEy1B,EAAEt1B,EAAEA,EAAE,UAAU,EAAEA,EAAE,OAAOiC,EAAE,IAAI,MAAM,EAAEqzB,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,OAAOrzB,EAAE,KAAK,MAAM,EAAEqzB,EAAE,KAAK,KAAK,SAAS,GAAG,OAAO,OAAO,CAAC,IAAIrzB,EAAE,EAAEtB,EAAEsB,EAAE,IAAI;AAAA,EAClL,EAAE,KAAK;AAAA,CACR,EAAEqzB,EAAE,KAAK,KAAK30B,CAAC,EAAEd,EAAEA,EAAE,OAAO,CAAC,EAAEy1B,EAAEt1B,EAAEA,EAAE,UAAU,EAAEA,EAAE,OAAO,EAAE,IAAI,MAAM,EAAEs1B,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,OAAOrzB,EAAE,IAAI,MAAM,EAAEqzB,EAAE,IAAI,EAAE30B,EAAE,UAAUd,EAAE,GAAG,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM;AAAA,CACpK,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,aAAa,IAAIG,EAAE,OAAOH,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAI,EAAGG,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,IAAI,GAAG,QAAQA,EAAE,MAAMA,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,GAAG,MAAM,CAAA,CAAE,EAAE,EAAEA,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,QAAQ,WAAW,EAAEA,EAAE,EAAE,SAAS,IAAIH,EAAE,KAAK,MAAM,MAAM,cAAc,CAAC,EAAES,EAAE,GAAG,KAAK,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAGF,EAAE,GAAG,GAAG,EAAE,EAAEP,EAAE,KAAK,CAAC,IAAI,KAAK,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,IAAIqB,EAAE,EAAE,CAAC,EAAE,MAAM;AAAA,EACvd,CAAC,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,gBAAgBo0B,GAAG,IAAI,OAAO,EAAEA,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM;AAAA,EACpF,CAAC,EAAE,CAAC,EAAErzB,EAAE,CAACf,EAAE,KAAI,EAAGP,EAAE,EAAE,GAAG,KAAK,QAAQ,UAAUA,EAAE,EAAEP,EAAEc,EAAE,UAAS,GAAIe,EAAEtB,EAAE,EAAE,CAAC,EAAE,OAAO,GAAGA,EAAE,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,MAAM,YAAY,EAAEA,EAAEA,EAAE,EAAE,EAAEA,EAAEP,EAAEc,EAAE,MAAMP,CAAC,EAAEA,GAAG,EAAE,CAAC,EAAE,QAAQsB,GAAG,KAAK,MAAM,MAAM,UAAU,KAAK,CAAC,IAAI,GAAG,EAAE;AAAA,EACzN,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,IAAIqzB,EAAE,KAAK,MAAM,MAAM,gBAAgB30B,CAAC,EAAEc,EAAE,KAAK,MAAM,MAAM,QAAQd,CAAC,EAAEuU,EAAE,KAAK,MAAM,MAAM,iBAAiBvU,CAAC,EAAE40B,EAAG,KAAK,MAAM,MAAM,kBAAkB50B,CAAC,EAAE60B,EAAG,KAAK,MAAM,MAAM,eAAe70B,CAAC,EAAE,KAAK,GAAG,CAAC,IAAIoB,EAAE,EAAE,MAAM;AAAA,EACzP,CAAC,EAAE,CAAC,EAAET,EAAE,GAAG,EAAES,EAAE,KAAK,QAAQ,UAAU,EAAE,EAAE,QAAQ,KAAK,MAAM,MAAM,mBAAmB,IAAI,EAAET,EAAE,GAAGA,EAAE,EAAE,QAAQ,KAAK,MAAM,MAAM,cAAc,MAAM,EAAE4T,EAAE,KAAK,CAAC,GAAGqgB,EAAG,KAAK,CAAC,GAAGC,EAAG,KAAK,CAAC,GAAGF,EAAE,KAAK,CAAC,GAAG7zB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAGH,EAAE,OAAO,KAAK,MAAM,MAAM,YAAY,GAAGX,GAAG,CAAC,EAAE,KAAI,EAAGP,GAAG;AAAA,EAC9QkB,EAAE,MAAMX,CAAC,MAAM,CAAC,GAAGsB,GAAGf,EAAE,QAAQ,KAAK,MAAM,MAAM,cAAc,MAAM,EAAE,OAAO,KAAK,MAAM,MAAM,YAAY,GAAG,GAAGgU,EAAE,KAAKhU,CAAC,GAAGq0B,EAAG,KAAKr0B,CAAC,GAAGO,EAAE,KAAKP,CAAC,EAAE,MAAMd,GAAG;AAAA,EAC3J,CAAC,CAAC,CAAC6B,GAAG,CAAC,EAAE,SAASA,EAAE,IAAI,GAAGF,EAAE;AAAA,EAC7B,EAAE,EAAE,UAAUA,EAAE,OAAO,CAAC,EAAEb,EAAEI,EAAE,MAAMX,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQL,EAAE,EAAE,MAAM,GAAG,KAAK,MAAM,MAAM,gBAAgB,KAAK,CAAC,IAAIA,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,KAAK,YAAY,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,QAAQ,KAAK,KAAK,MAAM,MAAM,WAAW,KAAKF,CAAC,EAAE,MAAM,GAAG,KAAKA,EAAE,OAAO,CAAA,CAAE,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,IAAIN,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,GAAGA,EAAEA,EAAE,IAAIA,EAAE,IAAI,QAAO,EAAGA,EAAE,KAAKA,EAAE,KAAK,QAAO,MAAQ,QAAO,EAAE,IAAI,EAAE,IAAI,QAAO,EAAG,QAAQ,KAAK,EAAE,MAAM,CAAC,GAAG,KAAK,MAAM,MAAM,IAAI,GAAG,EAAE,OAAO,KAAK,MAAM,YAAY,EAAE,KAAK,CAAA,CAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,QAAQ,KAAK,MAAM,MAAM,gBAAgB,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,OAAO,QAAQ,EAAE,OAAO,CAAC,GAAG,OAAO,YAAY,CAAC,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,QAAQ,KAAK,MAAM,MAAM,gBAAgB,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK,QAAQ,KAAK,MAAM,MAAM,gBAAgB,EAAE,EAAE,QAAQM,EAAE,KAAK,MAAM,YAAY,OAAO,EAAEA,GAAG,EAAEA,IAAI,GAAG,KAAK,MAAM,MAAM,WAAW,KAAK,KAAK,MAAM,YAAYA,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,MAAM,YAAYA,CAAC,EAAE,IAAI,KAAK,MAAM,YAAYA,CAAC,EAAE,IAAI,QAAQ,KAAK,MAAM,MAAM,gBAAgB,EAAE,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,iBAAiB,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAIA,EAAE,CAAC,KAAK,WAAW,IAAI,EAAE,CAAC,EAAE,IAAI,QAAQ,EAAE,CAAC,IAAI,KAAK,EAAE,EAAE,QAAQA,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,IAAI,GAAG,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,IAAIA,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,KAAKA,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,OAAO,QAAQA,CAAC,GAAG,EAAE,OAAO,QAAQ,CAAC,KAAK,YAAY,IAAIA,EAAE,IAAI,KAAKA,EAAE,IAAI,OAAO,CAACA,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,QAAQA,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,OAAO,OAAOc,GAAGA,EAAE,OAAO,OAAO,EAAEd,EAAE,EAAE,OAAO,GAAG,EAAE,KAAKc,GAAG,KAAK,MAAM,MAAM,QAAQ,KAAKA,EAAE,GAAG,CAAC,EAAE,EAAE,MAAMd,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,QAAQ,KAAK,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,KAAK,EAAE,OAAO,EAAE,OAAO,SAAS,EAAE,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,OAAO,MAAM,GAAG,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,OAAO,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,IAAI,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,YAAW,EAAG,QAAQ,KAAK,MAAM,MAAM,oBAAoB,GAAG,EAAEJ,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,aAAa,IAAI,EAAE,QAAQ,KAAK,MAAM,OAAO,eAAe,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,QAAQ,KAAK,MAAM,OAAO,eAAe,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,KAAKA,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,MAAM,KAAK,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,MAAM,MAAM,eAAe,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,IAAI,EAAEk1B,GAAE,EAAE,CAAC,CAAC,EAAEl1B,EAAE,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,gBAAgB,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,EAAE,CAAC,GAAG,KAAI,EAAG,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,kBAAkB,EAAE,EAAE,MAAM;AAAA,CAC53E,EAAE,CAAA,EAAGH,EAAE,CAAC,KAAK,QAAQ,IAAI,EAAE,CAAC,EAAE,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,KAAK,CAAA,CAAE,EAAE,GAAG,EAAE,SAASG,EAAE,OAAO,CAAC,QAAQM,KAAKN,EAAE,KAAK,MAAM,MAAM,gBAAgB,KAAKM,CAAC,EAAET,EAAE,MAAM,KAAK,OAAO,EAAE,KAAK,MAAM,MAAM,iBAAiB,KAAKS,CAAC,EAAET,EAAE,MAAM,KAAK,QAAQ,EAAE,KAAK,MAAM,MAAM,eAAe,KAAKS,CAAC,EAAET,EAAE,MAAM,KAAK,MAAM,EAAEA,EAAE,MAAM,KAAK,IAAI,EAAE,QAAQS,EAAE,EAAEA,EAAE,EAAE,OAAOA,IAAIT,EAAE,OAAO,KAAK,CAAC,KAAK,EAAES,CAAC,EAAE,OAAO,KAAK,MAAM,OAAO,EAAEA,CAAC,CAAC,EAAE,OAAO,GAAG,MAAMT,EAAE,MAAMS,CAAC,CAAC,CAAC,EAAE,QAAQA,KAAK,EAAET,EAAE,KAAK,KAAKq1B,GAAE50B,EAAET,EAAE,OAAO,MAAM,EAAE,IAAI,CAACC,EAAE,KAAK,CAAC,KAAKA,EAAE,OAAO,KAAK,MAAM,OAAOA,CAAC,EAAE,OAAO,GAAG,MAAMD,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,OAAOA,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,SAAS,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,UAAU,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,UAAU,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,IAAI;AAAA,EACzyB,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,YAAY,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,OAAO,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,SAAS,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,MAAM,MAAM,QAAQ,KAAK,MAAM,MAAM,UAAU,KAAK,EAAE,CAAC,CAAC,EAAE,KAAK,MAAM,MAAM,OAAO,GAAG,KAAK,MAAM,MAAM,QAAQ,KAAK,MAAM,MAAM,QAAQ,KAAK,EAAE,CAAC,CAAC,IAAI,KAAK,MAAM,MAAM,OAAO,IAAI,CAAC,KAAK,MAAM,MAAM,YAAY,KAAK,MAAM,MAAM,kBAAkB,KAAK,EAAE,CAAC,CAAC,EAAE,KAAK,MAAM,MAAM,WAAW,GAAG,KAAK,MAAM,MAAM,YAAY,KAAK,MAAM,MAAM,gBAAgB,KAAK,EAAE,CAAC,CAAC,IAAI,KAAK,MAAM,MAAM,WAAW,IAAI,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,MAAM,OAAO,WAAW,KAAK,MAAM,MAAM,WAAW,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAI,EAAG,GAAG,CAAC,KAAK,QAAQ,UAAU,KAAK,MAAM,MAAM,kBAAkB,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,MAAM,MAAM,gBAAgB,KAAK,CAAC,EAAE,OAAO,IAAIA,EAAEiC,GAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAOjC,EAAE,QAAQ,IAAI,EAAE,MAAM,KAAK,CAAC,IAAIA,EAAEs1B,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,GAAGt1B,IAAI,GAAG,OAAO,GAAGA,EAAE,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,OAAOA,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,UAAU,EAAEA,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAI,EAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAIG,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,KAAK,QAAQ,SAAS,CAAC,IAAIH,EAAE,KAAK,MAAM,MAAM,kBAAkB,KAAKG,CAAC,EAAEH,IAAIG,EAAEH,EAAE,CAAC,EAAE,EAAEA,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAOG,EAAEA,EAAE,KAAI,EAAG,KAAK,MAAM,MAAM,kBAAkB,KAAKA,CAAC,IAAI,KAAK,QAAQ,UAAU,CAAC,KAAK,MAAM,MAAM,gBAAgB,KAAK,CAAC,EAAEA,EAAEA,EAAE,MAAM,CAAC,EAAEA,EAAEA,EAAE,MAAM,EAAE,EAAE,GAAGo1B,GAAG,EAAE,CAAC,KAAKp1B,GAAGA,EAAE,QAAQ,KAAK,MAAM,OAAO,eAAe,IAAI,EAAE,MAAM,GAAG,EAAE,QAAQ,KAAK,MAAM,OAAO,eAAe,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,MAAM,OAAO,QAAQ,KAAK,CAAC,KAAK,EAAE,KAAK,MAAM,OAAO,OAAO,KAAK,CAAC,GAAG,CAAC,IAAIA,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,QAAQ,KAAK,MAAM,MAAM,oBAAoB,GAAG,EAAE,EAAE,EAAEA,EAAE,YAAW,CAAE,EAAE,GAAG,CAAC,EAAE,CAAC,IAAIH,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,KAAK,OAAO,IAAIA,EAAE,KAAKA,CAAC,CAAC,CAAC,OAAOu1B,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,EAAE,GAAG,CAAC,IAAIp1B,EAAE,KAAK,MAAM,OAAO,eAAe,KAAK,CAAC,EAAE,GAAG,GAACA,GAAGA,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,MAAM,mBAAmB,KAAY,EAAEA,EAAE,CAAC,GAAGA,EAAE,CAAC,IAAQ,CAAC,GAAG,KAAK,MAAM,OAAO,YAAY,KAAK,CAAC,GAAE,CAAC,IAAIH,EAAE,CAAC,GAAGG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAEO,EAAEV,EAAEW,EAAE,EAAEJ,EAAEJ,EAAE,CAAC,EAAE,CAAC,IAAI,IAAI,KAAK,MAAM,OAAO,kBAAkB,KAAK,MAAM,OAAO,kBAAkB,IAAII,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,EAAE,OAAOP,CAAC,GAAGG,EAAEI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,EAAEJ,EAAE,CAAC,GAAGA,EAAE,CAAC,GAAGA,EAAE,CAAC,GAAGA,EAAE,CAAC,GAAGA,EAAE,CAAC,GAAGA,EAAE,CAAC,EAAE,CAAC,EAAE,SAAS,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,OAAOA,EAAE,CAAC,GAAGA,EAAE,CAAC,EAAE,CAACO,GAAG,EAAE,QAAQ,UAAUP,EAAE,CAAC,GAAGA,EAAE,CAAC,IAAIH,EAAE,GAAG,GAAGA,EAAE,GAAG,GAAG,CAACW,GAAG,EAAE,QAAQ,CAAC,GAAGD,GAAG,EAAEA,EAAE,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE,EAAEA,EAAEC,CAAC,EAAE,IAAIU,EAAE,CAAC,GAAGlB,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,OAAOK,EAAE,EAAE,MAAM,EAAER,EAAEG,EAAE,MAAMkB,EAAE,CAAC,EAAE,GAAG,KAAK,IAAIrB,EAAE,CAAC,EAAE,EAAE,CAAC,IAAIc,EAAEN,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,KAAK,IAAIA,EAAE,KAAKM,EAAE,OAAO,KAAK,MAAM,aAAaA,CAAC,CAAC,CAAC,CAAC,IAAIsB,EAAE5B,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,SAAS,IAAIA,EAAE,KAAK4B,EAAE,OAAO,KAAK,MAAM,aAAaA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,kBAAkB,GAAG,EAAEjC,EAAE,KAAK,MAAM,MAAM,aAAa,KAAK,CAAC,EAAE,EAAE,KAAK,MAAM,MAAM,kBAAkB,KAAK,CAAC,GAAG,KAAK,MAAM,MAAM,gBAAgB,KAAK,CAAC,EAAE,OAAOA,GAAG,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,WAAW,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,MAAM,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,SAAS,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAEA,EAAE,OAAO,EAAE,CAAC,IAAI,KAAK,EAAE,EAAE,CAAC,EAAEA,EAAE,UAAU,IAAI,EAAE,EAAE,CAAC,EAAEA,EAAE,GAAG,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAKA,EAAE,OAAO,CAAC,CAAC,KAAK,OAAO,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,EAAE,CAAC,IAAI,EAAEA,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,EAAEA,EAAE,UAAU,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,MAAM,OAAO,WAAW,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,OAAOA,EAAE,UAAU,EAAE,CAAC,EAAEA,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAKA,EAAE,OAAO,CAAC,CAAC,KAAK,OAAO,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,WAAW,MAAM,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAMoB,GAAE,MAAMV,EAAC,CAAC,OAAO,QAAQ,MAAM,YAAY,UAAU,YAAYd,EAAE,CAAC,KAAK,OAAO,CAAA,EAAG,KAAK,OAAO,MAAM,OAAO,OAAO,IAAI,EAAE,KAAK,QAAQA,GAAGuV,GAAE,KAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW,IAAIrU,GAAE,KAAK,UAAU,KAAK,QAAQ,UAAU,KAAK,UAAU,QAAQ,KAAK,QAAQ,KAAK,UAAU,MAAM,KAAK,KAAK,YAAY,CAAA,EAAG,KAAK,MAAM,CAAC,OAAO,GAAG,WAAW,GAAG,IAAI,EAAE,EAAE,IAAInB,EAAE,CAAC,MAAMsB,EAAE,MAAMI,GAAE,OAAO,OAAOW,GAAE,MAAM,EAAE,KAAK,QAAQ,UAAUrC,EAAE,MAAM0B,GAAE,SAAS1B,EAAE,OAAOqC,GAAE,UAAU,KAAK,QAAQ,MAAMrC,EAAE,MAAM0B,GAAE,IAAI,KAAK,QAAQ,OAAO1B,EAAE,OAAOqC,GAAE,OAAOrC,EAAE,OAAOqC,GAAE,KAAK,KAAK,UAAU,MAAMrC,CAAC,CAAC,WAAW,OAAO,CAAC,MAAM,CAAC,MAAM0B,GAAE,OAAOW,EAAC,CAAC,CAAC,OAAO,IAAIpC,EAAED,EAAE,CAAC,OAAO,IAAIe,GAAEf,CAAC,EAAE,IAAIC,CAAC,CAAC,CAAC,OAAO,UAAUA,EAAED,EAAE,CAAC,OAAO,IAAIe,GAAEf,CAAC,EAAE,aAAaC,CAAC,CAAC,CAAC,IAAIA,EAAE,CAACA,EAAEA,EAAE,QAAQqB,EAAE,eAAe;AAAA,CACvqJ,EAAE,KAAK,YAAYrB,EAAE,KAAK,MAAM,EAAE,QAAQD,EAAE,EAAEA,EAAE,KAAK,YAAY,OAAOA,IAAI,CAAC,IAAIM,EAAE,KAAK,YAAYN,CAAC,EAAE,KAAK,aAAaM,EAAE,IAAIA,EAAE,MAAM,CAAC,CAAC,OAAO,KAAK,YAAY,CAAA,EAAG,KAAK,MAAM,CAAC,YAAYL,EAAED,EAAE,CAAA,EAAGM,EAAE,GAAG,CAAC,IAAI,KAAK,QAAQ,WAAWL,EAAEA,EAAE,QAAQqB,EAAE,cAAc,MAAM,EAAE,QAAQA,EAAE,UAAU,EAAE,GAAGrB,GAAG,CAAC,IAAII,EAAE,GAAG,KAAK,QAAQ,YAAY,OAAO,KAAKH,IAAIG,EAAEH,EAAE,KAAK,CAAC,MAAM,IAAI,EAAED,EAAED,CAAC,IAAIC,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,GAAGA,EAAE,KAAK,UAAU,MAAMJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAE,IAAIH,EAAEF,EAAE,GAAG,EAAE,EAAEK,EAAE,IAAI,SAAS,GAAGH,IAAI,OAAOA,EAAE,KAAK;AAAA,EACxhBF,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,KAAKJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAE,IAAIH,EAAEF,EAAE,GAAG,EAAE,EAAEE,GAAG,OAAO,aAAaA,GAAG,OAAO,QAAQA,EAAE,MAAMA,EAAE,IAAI,SAAS;AAAA,CAC5J,EAAE,GAAG;AAAA,GACHG,EAAE,IAAIH,EAAE,MAAM;AAAA,EACfG,EAAE,KAAK,KAAK,YAAY,GAAG,EAAE,EAAE,IAAIH,EAAE,MAAMF,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,OAAOJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,QAAQJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,GAAGJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,WAAWJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,KAAKJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,KAAKJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,IAAIJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAE,IAAIH,EAAEF,EAAE,GAAG,EAAE,EAAEE,GAAG,OAAO,aAAaA,GAAG,OAAO,QAAQA,EAAE,MAAMA,EAAE,IAAI,SAAS;AAAA,CACvpB,EAAE,GAAG;AAAA,GACHG,EAAE,IAAIH,EAAE,MAAM;AAAA,EACfG,EAAE,IAAI,KAAK,YAAY,GAAG,EAAE,EAAE,IAAIH,EAAE,MAAM,KAAK,OAAO,MAAMG,EAAE,GAAG,IAAI,KAAK,OAAO,MAAMA,EAAE,GAAG,EAAE,CAAC,KAAKA,EAAE,KAAK,MAAMA,EAAE,KAAK,EAAEL,EAAE,KAAKK,CAAC,GAAG,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,MAAMJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,SAASJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,IAAIE,EAAEN,EAAE,GAAG,KAAK,QAAQ,YAAY,WAAW,CAAC,IAAIC,EAAE,IAAIS,EAAEV,EAAE,MAAM,CAAC,EAAEE,EAAE,KAAK,QAAQ,WAAW,WAAW,QAAQS,GAAG,CAACT,EAAES,EAAE,KAAK,CAAC,MAAM,IAAI,EAAED,CAAC,EAAE,OAAOR,GAAG,UAAUA,GAAG,IAAID,EAAE,KAAK,IAAIA,EAAEC,CAAC,EAAE,CAAC,EAAED,EAAE,KAAKA,GAAG,IAAIK,EAAEN,EAAE,UAAU,EAAEC,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,MAAM,MAAMG,EAAE,KAAK,UAAU,UAAUE,CAAC,GAAG,CAAC,IAAIL,EAAEF,EAAE,GAAG,EAAE,EAAEM,GAAGJ,GAAG,OAAO,aAAaA,EAAE,MAAMA,EAAE,IAAI,SAAS;AAAA,CACnoB,EAAE,GAAG;AAAA,GACHG,EAAE,IAAIH,EAAE,MAAM;AAAA,EACfG,EAAE,KAAK,KAAK,YAAY,IAAG,EAAG,KAAK,YAAY,GAAG,EAAE,EAAE,IAAIH,EAAE,MAAMF,EAAE,KAAKK,CAAC,EAAEC,EAAEC,EAAE,SAASN,EAAE,OAAOA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,KAAKJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAE,IAAIH,EAAEF,EAAE,GAAG,EAAE,EAAEE,GAAG,OAAO,QAAQA,EAAE,MAAMA,EAAE,IAAI,SAAS;AAAA,CACzP,EAAE,GAAG;AAAA,GACHG,EAAE,IAAIH,EAAE,MAAM;AAAA,EACfG,EAAE,KAAK,KAAK,YAAY,IAAG,EAAG,KAAK,YAAY,GAAG,EAAE,EAAE,IAAIH,EAAE,MAAMF,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGJ,EAAE,CAAC,IAAIC,EAAE,0BAA0BD,EAAE,WAAW,CAAC,EAAE,GAAG,KAAK,QAAQ,OAAO,CAAC,QAAQ,MAAMC,CAAC,EAAE,KAAK,KAAM,OAAM,IAAI,MAAMA,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,GAAGF,CAAC,CAAC,OAAOC,EAAED,EAAE,CAAA,EAAG,CAAC,OAAO,KAAK,YAAY,KAAK,CAAC,IAAIC,EAAE,OAAOD,CAAC,CAAC,EAAEA,CAAC,CAAC,aAAaC,EAAED,EAAE,CAAA,EAAG,CAAC,IAAIM,EAAEL,EAAEI,EAAE,KAAK,GAAG,KAAK,OAAO,MAAM,CAAC,IAAIF,EAAE,OAAO,KAAK,KAAK,OAAO,KAAK,EAAE,GAAGA,EAAE,OAAO,EAAE,MAAME,EAAE,KAAK,UAAU,MAAM,OAAO,cAAc,KAAKC,CAAC,IAAI,MAAMH,EAAE,SAASE,EAAE,CAAC,EAAE,MAAMA,EAAE,CAAC,EAAE,YAAY,GAAG,EAAE,EAAE,EAAE,CAAC,IAAIC,EAAEA,EAAE,MAAM,EAAED,EAAE,KAAK,EAAE,IAAI,IAAI,OAAOA,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,IAAIC,EAAE,MAAM,KAAK,UAAU,MAAM,OAAO,cAAc,SAAS,EAAE,CAAC,MAAMD,EAAE,KAAK,UAAU,MAAM,OAAO,eAAe,KAAKC,CAAC,IAAI,MAAMA,EAAEA,EAAE,MAAM,EAAED,EAAE,KAAK,EAAE,KAAKC,EAAE,MAAM,KAAK,UAAU,MAAM,OAAO,eAAe,SAAS,EAAE,IAAIC,EAAE,MAAMF,EAAE,KAAK,UAAU,MAAM,OAAO,UAAU,KAAKC,CAAC,IAAI,MAAMC,EAAEF,EAAE,CAAC,EAAEA,EAAE,CAAC,EAAE,OAAO,EAAEC,EAAEA,EAAE,MAAM,EAAED,EAAE,MAAME,CAAC,EAAE,IAAI,IAAI,OAAOF,EAAE,CAAC,EAAE,OAAOE,EAAE,CAAC,EAAE,IAAID,EAAE,MAAM,KAAK,UAAU,MAAM,OAAO,UAAU,SAAS,EAAEA,EAAE,KAAK,QAAQ,OAAO,cAAc,KAAK,CAAC,MAAM,IAAI,EAAEA,CAAC,GAAGA,EAAE,IAAIJ,EAAE,GAAGS,EAAE,GAAG,KAAKV,GAAG,CAACC,IAAIS,EAAE,IAAIT,EAAE,GAAG,IAAIC,EAAE,GAAG,KAAK,QAAQ,YAAY,QAAQ,KAAKU,IAAIV,EAAEU,EAAE,KAAK,CAAC,MAAM,IAAI,EAAEZ,EAAED,CAAC,IAAIC,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,GAAGA,EAAE,KAAK,UAAU,OAAOF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,IAAIF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,KAAKF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,QAAQF,EAAE,KAAK,OAAO,KAAK,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAE,IAAIU,EAAEb,EAAE,GAAG,EAAE,EAAEG,EAAE,OAAO,QAAQU,GAAG,OAAO,QAAQA,EAAE,KAAKV,EAAE,IAAIU,EAAE,MAAMV,EAAE,MAAMH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,SAASF,EAAEK,EAAEK,CAAC,EAAE,CAACV,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,SAASF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,GAAGF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,IAAIF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,SAASF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,MAAM,SAASA,EAAE,KAAK,UAAU,IAAIF,CAAC,GAAG,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,IAAIS,EAAEX,EAAE,GAAG,KAAK,QAAQ,YAAY,YAAY,CAAC,IAAIY,EAAE,IAAIJ,EAAER,EAAE,MAAM,CAAC,EAAEsB,EAAE,KAAK,QAAQ,WAAW,YAAY,QAAQb,GAAG,CAACa,EAAEb,EAAE,KAAK,CAAC,MAAM,IAAI,EAAED,CAAC,EAAE,OAAOc,GAAG,UAAUA,GAAG,IAAIV,EAAE,KAAK,IAAIA,EAAEU,CAAC,EAAE,CAAC,EAAEV,EAAE,KAAKA,GAAG,IAAID,EAAEX,EAAE,UAAU,EAAEY,EAAE,CAAC,EAAE,CAAC,GAAGV,EAAE,KAAK,UAAU,WAAWS,CAAC,EAAE,CAACX,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEA,EAAE,IAAI,MAAM,EAAE,IAAI,MAAMQ,EAAER,EAAE,IAAI,MAAM,EAAE,GAAGD,EAAE,GAAG,IAAIW,EAAEb,EAAE,GAAG,EAAE,EAAEa,GAAG,OAAO,QAAQA,EAAE,KAAKV,EAAE,IAAIU,EAAE,MAAMV,EAAE,MAAMH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGF,EAAE,CAAC,IAAIY,EAAE,0BAA0BZ,EAAE,WAAW,CAAC,EAAE,GAAG,KAAK,QAAQ,OAAO,CAAC,QAAQ,MAAMY,CAAC,EAAE,KAAK,KAAM,OAAM,IAAI,MAAMA,CAAC,CAAC,CAAC,CAAC,OAAOb,CAAC,CAAC,EAAM6B,GAAE,KAAK,CAAC,QAAQ,OAAO,YAAY,EAAE,CAAC,KAAK,QAAQ,GAAG2T,EAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,IAAInV,GAAG,GAAG,IAAI,MAAMiB,EAAE,aAAa,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQA,EAAE,cAAc,EAAE,EAAE;AAAA,EAC7zF,OAAOjB,EAAE,8BAA8B6a,GAAE7a,CAAC,EAAE,MAAM,EAAE,EAAE6a,GAAE,EAAE,EAAE,GAAG;AAAA,EAC/D,eAAe,EAAE,EAAEA,GAAE,EAAE,EAAE,GAAG;AAAA,CAC7B,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM;AAAA,EAC7B,KAAK,OAAO,MAAM,CAAC,CAAC;AAAA,CACrB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC;AAAA,CACtH,CAAC,GAAG,EAAE,CAAC,MAAM;AAAA,CACb,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM7a,EAAE,GAAG,QAAQM,EAAE,EAAEA,EAAE,EAAE,MAAM,OAAOA,IAAI,CAAC,IAAIR,EAAE,EAAE,MAAMQ,CAAC,EAAEN,GAAG,KAAK,SAASF,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,KAAKD,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,IAAI,GAAG,MAAM,IAAI,EAAEA,EAAE;AAAA,EAC7KG,EAAE,KAAK,EAAE;AAAA,CACV,CAAC,SAAS,EAAE,CAAC,MAAM,OAAO,KAAK,OAAO,MAAM,EAAE,MAAM,CAAC;AAAA,CACrD,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,WAAW,EAAE,cAAc,IAAI,+BAA+B,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,MAAM,KAAK,OAAO,YAAY,CAAC,CAAC;AAAA,CACxJ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAE,EAAE,OAAO,OAAO,IAAI,GAAG,KAAK,UAAU,EAAE,OAAO,CAAC,CAAC,EAAE,GAAG,KAAK,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,IAAIA,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,OAAO,IAAI,CAAC,IAAIH,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,GAAG,QAAQS,EAAE,EAAEA,EAAET,EAAE,OAAOS,IAAI,GAAG,KAAK,UAAUT,EAAES,CAAC,CAAC,EAAEN,GAAG,KAAK,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAOA,IAAIA,EAAE,UAAUA,CAAC,YAAY;AAAA;AAAA,EAEpS,EAAE;AAAA,EACFA,EAAE;AAAA,CACH,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM;AAAA,EACzB,CAAC;AAAA,CACF,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,OAAO,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,KAAK,KAAK,OAAO,EAAE,MAAM,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC;AAAA,CACxI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,WAAW,KAAK,OAAO,YAAY,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,OAAO,KAAK,OAAO,YAAY,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,SAAS6a,GAAE,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,QAAQ,KAAK,OAAO,YAAY,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,IAAI7a,EAAE,KAAK,OAAO,YAAY,CAAC,EAAE,EAAEiV,GAAE,CAAC,EAAE,GAAG,IAAI,KAAK,OAAOjV,EAAE,EAAE,EAAE,IAAIH,EAAE,YAAY,EAAE,IAAI,OAAO,IAAIA,GAAG,WAAWgb,GAAE,CAAC,EAAE,KAAKhb,GAAG,IAAIG,EAAE,OAAOH,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAOG,CAAC,EAAE,CAACA,IAAI,EAAE,KAAK,OAAO,YAAYA,EAAE,KAAK,OAAO,YAAY,GAAG,IAAI,EAAEiV,GAAE,CAAC,EAAE,GAAG,IAAI,KAAK,OAAO4F,GAAE,CAAC,EAAE,EAAE,EAAE,IAAIhb,EAAE,aAAa,CAAC,UAAU,CAAC,IAAI,OAAO,IAAIA,GAAG,WAAWgb,GAAE,CAAC,CAAC,KAAKhb,GAAG,IAAIA,CAAC,CAAC,KAAK,EAAE,CAAC,MAAM,WAAW,GAAG,EAAE,OAAO,KAAK,OAAO,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,EAAE,QAAQ,EAAE,KAAKgb,GAAE,EAAE,IAAI,CAAC,CAAC,EAAM1Z,GAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,EAAMP,GAAE,MAAMF,EAAC,CAAC,QAAQ,SAAS,aAAa,YAAYd,EAAE,CAAC,KAAK,QAAQA,GAAGuV,GAAE,KAAK,QAAQ,SAAS,KAAK,QAAQ,UAAU,IAAI3T,GAAE,KAAK,SAAS,KAAK,QAAQ,SAAS,KAAK,SAAS,QAAQ,KAAK,QAAQ,KAAK,SAAS,OAAO,KAAK,KAAK,aAAa,IAAIL,EAAC,CAAC,OAAO,MAAMvB,EAAED,EAAE,CAAC,OAAO,IAAIe,GAAEf,CAAC,EAAE,MAAMC,CAAC,CAAC,CAAC,OAAO,YAAYA,EAAED,EAAE,CAAC,OAAO,IAAIe,GAAEf,CAAC,EAAE,YAAYC,CAAC,CAAC,CAAC,MAAMA,EAAE,CAAC,IAAID,EAAE,GAAG,QAAQM,EAAE,EAAEA,EAAEL,EAAE,OAAOK,IAAI,CAAC,IAAID,EAAEJ,EAAEK,CAAC,EAAE,GAAG,KAAK,QAAQ,YAAY,YAAYD,EAAE,IAAI,EAAE,CAAC,IAAIH,EAAEG,EAAEM,EAAE,KAAK,QAAQ,WAAW,UAAUT,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,IAAI,EAAEA,CAAC,EAAE,GAAGS,IAAI,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,OAAO,QAAQ,aAAa,OAAO,OAAO,MAAM,YAAY,MAAM,EAAE,SAAST,EAAE,IAAI,EAAE,CAACF,GAAGW,GAAG,GAAG,QAAQ,CAAC,CAAC,IAAIJ,EAAEF,EAAE,OAAOE,EAAE,MAAM,IAAI,QAAQ,CAACP,GAAG,KAAK,SAAS,MAAMO,CAAC,EAAE,KAAK,CAAC,IAAI,KAAK,CAACP,GAAG,KAAK,SAAS,GAAGO,CAAC,EAAE,KAAK,CAAC,IAAI,UAAU,CAACP,GAAG,KAAK,SAAS,QAAQO,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACP,GAAG,KAAK,SAAS,KAAKO,CAAC,EAAE,KAAK,CAAC,IAAI,QAAQ,CAACP,GAAG,KAAK,SAAS,MAAMO,CAAC,EAAE,KAAK,CAAC,IAAI,aAAa,CAACP,GAAG,KAAK,SAAS,WAAWO,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACP,GAAG,KAAK,SAAS,KAAKO,CAAC,EAAE,KAAK,CAAC,IAAI,WAAW,CAACP,GAAG,KAAK,SAAS,SAASO,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACP,GAAG,KAAK,SAAS,KAAKO,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,CAACP,GAAG,KAAK,SAAS,IAAIO,CAAC,EAAE,KAAK,CAAC,IAAI,YAAY,CAACP,GAAG,KAAK,SAAS,UAAUO,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACP,GAAG,KAAK,SAAS,KAAKO,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAIL,EAAE,eAAeK,EAAE,KAAK,wBAAwB,GAAG,KAAK,QAAQ,OAAO,OAAO,QAAQ,MAAML,CAAC,EAAE,GAAG,MAAM,IAAI,MAAMA,CAAC,CAAC,CAAC,CAAC,CAAC,OAAOF,CAAC,CAAC,YAAYC,EAAED,EAAE,KAAK,SAAS,CAAC,IAAIM,EAAE,GAAG,QAAQD,EAAE,EAAEA,EAAEJ,EAAE,OAAOI,IAAI,CAAC,IAAIE,EAAEN,EAAEI,CAAC,EAAE,GAAG,KAAK,QAAQ,YAAY,YAAYE,EAAE,IAAI,EAAE,CAAC,IAAII,EAAE,KAAK,QAAQ,WAAW,UAAUJ,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,IAAI,EAAEA,CAAC,EAAE,GAAGI,IAAI,IAAI,CAAC,CAAC,SAAS,OAAO,OAAO,QAAQ,SAAS,KAAK,WAAW,KAAK,MAAM,MAAM,EAAE,SAASJ,EAAE,IAAI,EAAE,CAACD,GAAGK,GAAG,GAAG,QAAQ,CAAC,CAAC,IAAIT,EAAEK,EAAE,OAAOL,EAAE,KAAI,CAAE,IAAI,SAAS,CAACI,GAAGN,EAAE,KAAKE,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACI,GAAGN,EAAE,KAAKE,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACI,GAAGN,EAAE,KAAKE,CAAC,EAAE,KAAK,CAAC,IAAI,QAAQ,CAACI,GAAGN,EAAE,MAAME,CAAC,EAAE,KAAK,CAAC,IAAI,WAAW,CAACI,GAAGN,EAAE,SAASE,CAAC,EAAE,KAAK,CAAC,IAAI,SAAS,CAACI,GAAGN,EAAE,OAAOE,CAAC,EAAE,KAAK,CAAC,IAAI,KAAK,CAACI,GAAGN,EAAE,GAAGE,CAAC,EAAE,KAAK,CAAC,IAAI,WAAW,CAACI,GAAGN,EAAE,SAASE,CAAC,EAAE,KAAK,CAAC,IAAI,KAAK,CAACI,GAAGN,EAAE,GAAGE,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,CAACI,GAAGN,EAAE,IAAIE,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACI,GAAGN,EAAE,KAAKE,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAIS,EAAE,eAAeT,EAAE,KAAK,wBAAwB,GAAG,KAAK,QAAQ,OAAO,OAAO,QAAQ,MAAMS,CAAC,EAAE,GAAG,MAAM,IAAI,MAAMA,CAAC,CAAC,CAAC,CAAC,CAAC,OAAOL,CAAC,CAAC,EAAME,GAAE,KAAK,CAAC,QAAQ,MAAM,YAAY,EAAE,CAAC,KAAK,QAAQ,GAAGgV,EAAC,CAAC,OAAO,iBAAiB,IAAI,IAAI,CAAC,aAAa,cAAc,mBAAmB,cAAc,CAAC,EAAE,OAAO,6BAA6B,IAAI,IAAI,CAAC,aAAa,cAAc,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,iBAAiB,EAAE,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,KAAK,MAAM/T,GAAE,IAAIA,GAAE,SAAS,CAAC,eAAe,CAAC,OAAO,KAAK,MAAMR,GAAE,MAAMA,GAAE,WAAW,CAAC,EAAM2B,GAAE,KAAK,CAAC,SAASV,GAAC,EAAG,QAAQ,KAAK,WAAW,MAAM,KAAK,cAAc,EAAE,EAAE,YAAY,KAAK,cAAc,EAAE,EAAE,OAAOjB,GAAE,SAASY,GAAE,aAAaL,GAAE,MAAMC,GAAE,UAAUN,GAAE,MAAMX,GAAE,eAAe,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,QAAQH,KAAK,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,KAAKA,CAAC,CAAC,EAAEA,EAAE,MAAM,IAAI,QAAQ,CAAC,IAAI,EAAEA,EAAE,QAAQH,KAAK,EAAE,OAAO,EAAE,EAAE,OAAO,KAAK,WAAWA,EAAE,OAAO,CAAC,CAAC,EAAE,QAAQA,KAAK,EAAE,KAAK,QAAQS,KAAKT,EAAE,EAAE,EAAE,OAAO,KAAK,WAAWS,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,EAAEN,EAAE,EAAE,EAAE,OAAO,KAAK,WAAW,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAEA,EAAE,KAAK,SAAS,YAAY,cAAc,EAAE,IAAI,EAAE,KAAK,SAAS,WAAW,YAAY,EAAE,IAAI,EAAE,QAAQH,GAAG,CAAC,IAAIS,EAAE,EAAET,CAAC,EAAE,KAAK,GAAG,EAAE,EAAE,EAAE,OAAO,KAAK,WAAWS,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,OAAO,KAAK,WAAW,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,SAAS,YAAY,CAAC,UAAU,CAAA,EAAG,YAAY,CAAA,CAAE,EAAE,OAAO,EAAE,QAAQ,GAAG,CAAC,IAAIN,EAAE,CAAC,GAAG,CAAC,EAAE,GAAGA,EAAE,MAAM,KAAK,SAAS,OAAOA,EAAE,OAAO,GAAG,EAAE,aAAa,EAAE,WAAW,QAAQ,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM,IAAI,MAAM,yBAAyB,EAAE,GAAG,aAAa,EAAE,CAAC,IAAIH,EAAE,EAAE,UAAU,EAAE,IAAI,EAAEA,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,YAAYS,EAAE,CAAC,IAAIR,EAAE,EAAE,SAAS,MAAM,KAAKQ,CAAC,EAAE,OAAOR,IAAI,KAAKA,EAAED,EAAE,MAAM,KAAKS,CAAC,GAAGR,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC,GAAG,cAAc,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,QAAQ,SAAS,EAAE,QAAQ,SAAS,MAAM,IAAI,MAAM,6CAA6C,EAAE,IAAID,EAAE,EAAE,EAAE,KAAK,EAAEA,EAAEA,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,QAAQ,QAAQ,EAAE,WAAW,EAAE,WAAW,KAAK,EAAE,KAAK,EAAE,EAAE,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,WAAW,EAAE,YAAY,EAAE,YAAY,KAAK,EAAE,KAAK,EAAE,EAAE,YAAY,CAAC,EAAE,KAAK,GAAG,CAAC,gBAAgB,GAAG,EAAE,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,YAAY,CAAC,EAAEG,EAAE,WAAW,GAAG,EAAE,SAAS,CAAC,IAAI,EAAE,KAAK,SAAS,UAAU,IAAIwB,GAAE,KAAK,QAAQ,EAAE,QAAQ3B,KAAK,EAAE,SAAS,CAAC,GAAG,EAAEA,KAAK,GAAG,MAAM,IAAI,MAAM,aAAaA,CAAC,kBAAkB,EAAE,GAAG,CAAC,UAAU,QAAQ,EAAE,SAASA,CAAC,EAAE,SAAS,IAAIS,EAAET,EAAEC,EAAE,EAAE,SAASQ,CAAC,EAAE,EAAE,EAAEA,CAAC,EAAE,EAAEA,CAAC,EAAE,IAAI,IAAI,CAAC,IAAIF,EAAEN,EAAE,MAAM,EAAE,CAAC,EAAE,OAAOM,IAAI,KAAKA,EAAE,EAAE,MAAM,EAAE,CAAC,GAAGA,GAAG,EAAE,CAAC,CAACJ,EAAE,SAAS,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,SAAS,WAAW,IAAIc,GAAE,KAAK,QAAQ,EAAE,QAAQjB,KAAK,EAAE,UAAU,CAAC,GAAG,EAAEA,KAAK,GAAG,MAAM,IAAI,MAAM,cAAcA,CAAC,kBAAkB,EAAE,GAAG,CAAC,UAAU,QAAQ,OAAO,EAAE,SAASA,CAAC,EAAE,SAAS,IAAIS,EAAET,EAAEC,EAAE,EAAE,UAAUQ,CAAC,EAAE,EAAE,EAAEA,CAAC,EAAE,EAAEA,CAAC,EAAE,IAAI,IAAI,CAAC,IAAIF,EAAEN,EAAE,MAAM,EAAE,CAAC,EAAE,OAAOM,IAAI,KAAKA,EAAE,EAAE,MAAM,EAAE,CAAC,GAAGA,CAAC,CAAC,CAACJ,EAAE,UAAU,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,SAAS,OAAO,IAAIG,GAAE,QAAQN,KAAK,EAAE,MAAM,CAAC,GAAG,EAAEA,KAAK,GAAG,MAAM,IAAI,MAAM,SAASA,CAAC,kBAAkB,EAAE,GAAG,CAAC,UAAU,OAAO,EAAE,SAASA,CAAC,EAAE,SAAS,IAAIS,EAAET,EAAEC,EAAE,EAAE,MAAMQ,CAAC,EAAE,EAAE,EAAEA,CAAC,EAAEH,GAAE,iBAAiB,IAAIN,CAAC,EAAE,EAAES,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,SAAS,OAAOH,GAAE,6BAA6B,IAAIN,CAAC,EAAE,OAAO,SAAS,CAAC,IAAIqB,EAAE,MAAMpB,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAEoB,CAAC,CAAC,GAAC,EAAI,IAAId,EAAEN,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAEM,CAAC,CAAC,EAAE,EAAEE,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,MAAM,OAAO,SAAS,CAAC,IAAIY,EAAE,MAAMpB,EAAE,MAAM,EAAE,CAAC,EAAE,OAAOoB,IAAI,KAAKA,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAGA,CAAC,GAAC,EAAI,IAAId,EAAEN,EAAE,MAAM,EAAE,CAAC,EAAE,OAAOM,IAAI,KAAKA,EAAE,EAAE,MAAM,EAAE,CAAC,GAAGA,CAAC,CAAC,CAACJ,EAAE,MAAM,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,EAAE,KAAK,SAAS,WAAWH,EAAE,EAAE,WAAWG,EAAE,WAAW,SAASM,EAAE,CAAC,IAAIR,EAAE,CAAA,EAAG,OAAOA,EAAE,KAAKD,EAAE,KAAK,KAAKS,CAAC,CAAC,EAAE,IAAIR,EAAEA,EAAE,OAAO,EAAE,KAAK,KAAKQ,CAAC,CAAC,GAAGR,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,GAAG,KAAK,SAAS,GAAGE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,KAAK,SAAS,CAAC,GAAG,KAAK,SAAS,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,OAAOoB,GAAE,IAAI,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAOR,GAAE,MAAM,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC,cAAc,EAAE,CAAC,MAAM,CAACX,EAAED,IAAI,CAAC,IAAIE,EAAE,CAAC,GAAGF,CAAC,EAAEH,EAAE,CAAC,GAAG,KAAK,SAAS,GAAGK,CAAC,EAAE,EAAE,KAAK,QAAQ,CAAC,CAACL,EAAE,OAAO,CAAC,CAACA,EAAE,KAAK,EAAE,GAAG,KAAK,SAAS,QAAQ,IAAIK,EAAE,QAAQ,GAAG,OAAO,EAAE,IAAI,MAAM,oIAAoI,CAAC,EAAE,GAAG,OAAOD,EAAE,KAAKA,IAAI,KAAK,OAAO,EAAE,IAAI,MAAM,gDAAgD,CAAC,EAAE,GAAG,OAAOA,GAAG,SAAS,OAAO,EAAE,IAAI,MAAM,wCAAwC,OAAO,UAAU,SAAS,KAAKA,CAAC,EAAE,mBAAmB,CAAC,EAAE,GAAGJ,EAAE,QAAQA,EAAE,MAAM,QAAQA,EAAEA,EAAE,MAAM,MAAM,GAAGA,EAAE,MAAM,OAAO,SAAS,CAAC,IAAI,EAAEA,EAAE,MAAM,MAAMA,EAAE,MAAM,WAAWI,CAAC,EAAEA,EAAEO,EAAE,MAAMX,EAAE,MAAM,MAAMA,EAAE,MAAM,aAAY,EAAG,EAAEuB,GAAE,IAAIA,GAAE,WAAW,EAAEvB,CAAC,EAAEO,EAAEP,EAAE,MAAM,MAAMA,EAAE,MAAM,iBAAiBW,CAAC,EAAEA,EAAEX,EAAE,YAAY,MAAM,QAAQ,IAAI,KAAK,WAAWO,EAAEP,EAAE,UAAU,CAAC,EAAE,IAAIQ,EAAE,MAAMR,EAAE,MAAM,MAAMA,EAAE,MAAM,gBAAgB,EAAEe,GAAE,MAAMA,GAAE,aAAaR,EAAEP,CAAC,EAAE,OAAOA,EAAE,MAAM,MAAMA,EAAE,MAAM,YAAYQ,CAAC,EAAEA,CAAC,KAAK,MAAM,CAAC,EAAE,GAAG,CAACR,EAAE,QAAQI,EAAEJ,EAAE,MAAM,WAAWI,CAAC,GAAG,IAAIM,GAAGV,EAAE,MAAMA,EAAE,MAAM,eAAe,EAAEuB,GAAE,IAAIA,GAAE,WAAWnB,EAAEJ,CAAC,EAAEA,EAAE,QAAQU,EAAEV,EAAE,MAAM,iBAAiBU,CAAC,GAAGV,EAAE,YAAY,KAAK,WAAWU,EAAEV,EAAE,UAAU,EAAE,IAAI,GAAGA,EAAE,MAAMA,EAAE,MAAM,cAAa,EAAG,EAAEe,GAAE,MAAMA,GAAE,aAAaL,EAAEV,CAAC,EAAE,OAAOA,EAAE,QAAQ,EAAEA,EAAE,MAAM,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,GAAG,CAAC,GAAG,EAAE,SAAS;AAAA,2DAC5iQ,EAAE,CAAC,IAAIG,EAAE,iCAAiC6a,GAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,SAAS,OAAO,EAAE,QAAQ,QAAQ7a,CAAC,EAAEA,CAAC,CAAC,GAAG,EAAE,OAAO,QAAQ,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAMgB,GAAE,IAAIuB,GAAE,SAAS9B,EAAEC,EAAEd,EAAE,CAAC,OAAOoB,GAAE,MAAMN,EAAEd,CAAC,CAAC,CAACa,EAAE,QAAQA,EAAE,WAAW,SAASC,EAAE,CAAC,OAAOM,GAAE,WAAWN,CAAC,EAAED,EAAE,SAASO,GAAE,SAASmB,GAAE1B,EAAE,QAAQ,EAAEA,CAAC,EAAEA,EAAE,YAAYoB,GAAEpB,EAAE,SAAS0U,GAAE1U,EAAE,IAAI,YAAYC,EAAE,CAAC,OAAOM,GAAE,IAAI,GAAGN,CAAC,EAAED,EAAE,SAASO,GAAE,SAASmB,GAAE1B,EAAE,QAAQ,EAAEA,CAAC,EAAEA,EAAE,WAAW,SAASC,EAAEd,EAAE,CAAC,OAAOoB,GAAE,WAAWN,EAAEd,CAAC,CAAC,EAAEa,EAAE,YAAYO,GAAE,YAAYP,EAAE,OAAOG,GAAEH,EAAE,OAAOG,GAAE,MAAMH,EAAE,SAASe,GAAEf,EAAE,aAAaU,GAAEV,EAAE,MAAMW,GAAEX,EAAE,MAAMW,GAAE,IAAIX,EAAE,UAAUK,GAAEL,EAAE,MAAMN,GAAEM,EAAE,MAAMA,EAASA,EAAE,QAAWA,EAAE,WAAcA,EAAE,IAAOA,EAAE,WAAcA,EAAE,YAAoBG,GAAE,MAASQ,GAAE,IClE1uBq0B,EAAO,WAAW,CAChB,IAAK,GACL,OAAQ,GACR,OAAQ,EACV,CAAC,EAED,MAAMC,GAAc,CAClB,IACA,IACA,aACA,KACA,OACA,MACA,KACA,KACA,KACA,KACA,KACA,KACA,IACA,KACA,KACA,IACA,MACA,SACA,QACA,QACA,KACA,KACA,QACA,KACA,IACF,EAEMC,GAAe,CAAC,QAAS,OAAQ,MAAO,SAAU,QAAS,OAAO,EAExE,IAAIC,GAAiB,GACrB,MAAMC,GAAsB,KACtBC,GAAuB,IACvBC,GAAuB,IACvBC,GAA2B,IAC3BC,OAAoB,IAE1B,SAASC,GAAkB5rB,EAA4B,CACrD,MAAM6rB,EAASF,GAAc,IAAI3rB,CAAG,EACpC,OAAI6rB,IAAW,OAAkB,MACjCF,GAAc,OAAO3rB,CAAG,EACxB2rB,GAAc,IAAI3rB,EAAK6rB,CAAM,EACtBA,EACT,CAEA,SAASC,GAAkB9rB,EAAazH,EAAe,CAErD,GADAozB,GAAc,IAAI3rB,EAAKzH,CAAK,EACxBozB,GAAc,MAAQF,GAAsB,OAChD,MAAMM,EAASJ,GAAc,KAAA,EAAO,OAAO,MACvCI,GAAQJ,GAAc,OAAOI,CAAM,CACzC,CAEA,SAASC,IAAe,CAClBV,KACJA,GAAiB,GAEjB7L,GAAU,QAAQ,0BAA4BsF,GAAS,CACjD,EAAEA,aAAgB,oBAElB,CADSA,EAAK,aAAa,MAAM,IAErCA,EAAK,aAAa,MAAO,qBAAqB,EAC9CA,EAAK,aAAa,SAAU,QAAQ,EACtC,CAAC,EACH,CAEO,SAASkH,GAAwBC,EAA0B,CAChE,MAAMvzB,EAAQuzB,EAAS,KAAA,EACvB,GAAI,CAACvzB,EAAO,MAAO,GAEnB,GADAqzB,GAAA,EACIrzB,EAAM,QAAU+yB,GAA0B,CAC5C,MAAMG,EAASD,GAAkBjzB,CAAK,EACtC,GAAIkzB,IAAW,KAAM,OAAOA,CAC9B,CACA,MAAMjrB,EAAYhE,GAAajE,EAAO4yB,EAAmB,EACnDrM,EAASte,EAAU,UACrB;AAAA;AAAA,eAAoBA,EAAU,KAAK,yBAAyBA,EAAU,KAAK,MAAM,KACjF,GACJ,GAAIA,EAAU,KAAK,OAAS4qB,GAAsB,CAEhD,MAAMxwB,EAAO,2BADGmxB,GAAW,GAAGvrB,EAAU,IAAI,GAAGse,CAAM,EAAE,CACR,SACzCkN,EAAY3M,GAAU,SAASzkB,EAAM,CACzC,aAAcowB,GACd,aAAcC,EAAA,CACf,EACD,OAAI1yB,EAAM,QAAU+yB,IAClBI,GAAkBnzB,EAAOyzB,CAAS,EAE7BA,CACT,CACA,MAAMC,EAAWlB,EAAO,MAAM,GAAGvqB,EAAU,IAAI,GAAGse,CAAM,EAAE,EACpDkN,EAAY3M,GAAU,SAAS4M,EAAU,CAC7C,aAAcjB,GACd,aAAcC,EAAA,CACf,EACD,OAAI1yB,EAAM,QAAU+yB,IAClBI,GAAkBnzB,EAAOyzB,CAAS,EAE7BA,CACT,CAEA,SAASD,GAAW5zB,EAAuB,CACzC,OAAOA,EACJ,QAAQ,KAAM,OAAO,EACrB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,QAAQ,EACtB,QAAQ,KAAM,OAAO,CAC1B,CClHA,MAAM+zB,GAAgB,KAChBC,GAAe,IACfC,GAAa,mBACbC,GAAe,SACfC,GAAc,cAOpB,eAAeC,GAAoBpxB,EAAgC,CACjE,GAAI,CAACA,EAAM,MAAO,GAElB,GAAI,CACF,aAAM,UAAU,UAAU,UAAUA,CAAI,EACjC,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAEA,SAASqxB,GAAeC,EAA2BvvB,EAAe,CAChEuvB,EAAO,MAAQvvB,EACfuvB,EAAO,aAAa,aAAcvvB,CAAK,CACzC,CAEA,SAASwvB,GAAiBtxB,EAA4C,CACpE,MAAMuxB,EAAYvxB,EAAQ,OAASgxB,GACnC,OAAOxxB;AAAAA;AAAAA;AAAAA;AAAAA,cAIK+xB,CAAS;AAAA,mBACJA,CAAS;AAAA,eACb,MAAOz3B,GAAa,CAC3B,MAAM03B,EAAM13B,EAAE,cAKd,GAJsB03B,GAAK,cACzB,sBAAA,EAGE,CAACA,GAAOA,EAAI,QAAQ,UAAY,IAAK,OAEzCA,EAAI,QAAQ,QAAU,IACtBA,EAAI,aAAa,YAAa,MAAM,EACpCA,EAAI,SAAW,GAEf,MAAMC,EAAS,MAAMN,GAAoBnxB,EAAQ,MAAM,EACvD,GAAKwxB,EAAI,YAMT,IAJA,OAAOA,EAAI,QAAQ,QACnBA,EAAI,gBAAgB,WAAW,EAC/BA,EAAI,SAAW,GAEX,CAACC,EAAQ,CACXD,EAAI,QAAQ,MAAQ,IACpBJ,GAAeI,EAAKN,EAAW,EAE/B,OAAO,WAAW,IAAM,CACjBM,EAAI,cACT,OAAOA,EAAI,QAAQ,MACnBJ,GAAeI,EAAKD,CAAS,EAC/B,EAAGR,EAAY,EACf,MACF,CAEAS,EAAI,QAAQ,OAAS,IACrBJ,GAAeI,EAAKP,EAAY,EAEhC,OAAO,WAAW,IAAM,CACjBO,EAAI,cACT,OAAOA,EAAI,QAAQ,OACnBJ,GAAeI,EAAKD,CAAS,EAC/B,EAAGT,EAAa,EAClB,CAAC;AAAA;AAAA;AAAA,iDAG0CvxB,EAAM,IAAI;AAAA,kDACTA,EAAM,KAAK;AAAA;AAAA;AAAA,GAI7D,CAEO,SAASmyB,GAA2BhB,EAAkC,CAC3E,OAAOY,GAAiB,CAAE,KAAM,IAAMZ,EAAU,MAAOM,GAAY,CACrE,0zLC1DMW,GAAsBC,GACtBC,GAAWF,GAAoB,UAAY,CAAE,KAAM,QAAA,EACnDG,GAAWH,GAAoB,OAAS,CAAA,EAE9C,SAASI,GAAkB30B,EAAuB,CAChD,OAAQA,GAAQ,QAAQ,KAAA,CAC1B,CAEA,SAAS40B,GAAa50B,EAAsB,CAC1C,MAAM6C,EAAU7C,EAAK,QAAQ,KAAM,GAAG,EAAE,KAAA,EACxC,OAAK6C,EACEA,EACJ,MAAM,KAAK,EACX,IAAKgF,GACJA,EAAK,QAAU,GAAKA,EAAK,YAAA,IAAkBA,EACvCA,EACA,GAAGA,EAAK,GAAG,CAAC,GAAG,YAAA,GAAiB,EAAE,GAAGA,EAAK,MAAM,CAAC,CAAC,EAAA,EAEvD,KAAK,GAAG,EARU,MASvB,CAEA,SAASgtB,GAAcl1B,EAAoC,CACzD,MAAME,EAAUF,GAAO,KAAA,EACvB,GAAKE,EACL,OAAOA,EAAQ,QAAQ,KAAM,GAAG,CAClC,CAEA,SAASi1B,GAAmBn1B,EAAoC,CAC9D,GAAIA,GAAU,KACd,IAAI,OAAOA,GAAU,SAAU,CAC7B,MAAME,EAAUF,EAAM,KAAA,EACtB,GAAI,CAACE,EAAS,OACd,MAAMk1B,EAAYl1B,EAAQ,MAAM,OAAO,EAAE,CAAC,GAAG,QAAU,GACvD,OAAKk1B,EACEA,EAAU,OAAS,IAAM,GAAGA,EAAU,MAAM,EAAG,GAAG,CAAC,IAAMA,EADhD,MAElB,CACA,GAAI,OAAOp1B,GAAU,UAAY,OAAOA,GAAU,UAChD,OAAO,OAAOA,CAAK,EAErB,GAAI,MAAM,QAAQA,CAAK,EAAG,CACxB,MAAMkE,EAASlE,EACZ,IAAKqF,GAAS8vB,GAAmB9vB,CAAI,CAAC,EACtC,OAAQA,GAAyB,EAAQA,CAAK,EACjD,GAAInB,EAAO,SAAW,EAAG,OACzB,MAAMmxB,EAAUnxB,EAAO,MAAM,EAAG,CAAC,EAAE,KAAK,IAAI,EAC5C,OAAOA,EAAO,OAAS,EAAI,GAAGmxB,CAAO,IAAMA,CAC7C,EAEF,CAEA,SAASC,GAAkB/rB,EAAe/H,EAAuB,CAC/D,GAAI,CAAC+H,GAAQ,OAAOA,GAAS,SAAU,OACvC,IAAIpC,EAAmBoC,EACvB,UAAWgsB,KAAW/zB,EAAK,MAAM,GAAG,EAAG,CAErC,GADI,CAAC+zB,GACD,CAACpuB,GAAW,OAAOA,GAAY,SAAU,OAE7CA,EADeA,EACEouB,CAAO,CAC1B,CACA,OAAOpuB,CACT,CAEA,SAASquB,GAAsBjsB,EAAeksB,EAAoC,CAChF,UAAWhuB,KAAOguB,EAAM,CACtB,MAAMz1B,EAAQs1B,GAAkB/rB,EAAM9B,CAAG,EACnCiuB,EAAUP,GAAmBn1B,CAAK,EACxC,GAAI01B,EAAS,OAAOA,CACtB,CAEF,CAEA,SAASC,GAAkBpsB,EAAmC,CAC5D,GAAI,CAACA,GAAQ,OAAOA,GAAS,SAAU,OACvC,MAAMvB,EAASuB,EACT/H,EAAO,OAAOwG,EAAO,MAAS,SAAWA,EAAO,KAAO,OAC7D,GAAI,CAACxG,EAAM,OACX,MAAMo0B,EAAS,OAAO5tB,EAAO,QAAW,SAAWA,EAAO,OAAS,OAC7DT,EAAQ,OAAOS,EAAO,OAAU,SAAWA,EAAO,MAAQ,OAChE,OAAI4tB,IAAW,QAAaruB,IAAU,OAC7B,GAAG/F,CAAI,IAAIo0B,CAAM,IAAIA,EAASruB,CAAK,GAErC/F,CACT,CAEA,SAASq0B,GAAmBtsB,EAAmC,CAC7D,GAAI,CAACA,GAAQ,OAAOA,GAAS,SAAU,OACvC,MAAMvB,EAASuB,EAEf,OADa,OAAOvB,EAAO,MAAS,SAAWA,EAAO,KAAO,MAE/D,CAEA,SAAS8tB,GACPC,EACAC,EACmC,CACnC,GAAI,GAACD,GAAQ,CAACC,GACd,OAAOD,EAAK,UAAUC,CAAM,GAAK,MACnC,CAEO,SAASC,GAAmB5uB,EAInB,CACd,MAAMhH,EAAO20B,GAAkB3tB,EAAO,IAAI,EACpCI,EAAMpH,EAAK,YAAA,EACX01B,EAAOhB,GAASttB,CAAG,EACnByuB,EAAQH,GAAM,MAAQjB,GAAS,MAAQ,SACvCllB,EAAQmmB,GAAM,OAASd,GAAa50B,CAAI,EACxC0E,EAAQgxB,GAAM,OAAS11B,EACvB81B,EACJ9uB,EAAO,MAAQ,OAAOA,EAAO,MAAS,SAChCA,EAAO,KAAiC,OAC1C,OACA2uB,EAAS,OAAOG,GAAc,SAAWA,EAAU,OAAS,OAC5DC,EAAaN,GAAkBC,EAAMC,CAAM,EAC3CK,EAAOnB,GAAckB,GAAY,OAASJ,CAAM,EAEtD,IAAIM,EACA7uB,IAAQ,SAAQ6uB,EAASX,GAAkBtuB,EAAO,IAAI,GACtD,CAACivB,IAAW7uB,IAAQ,SAAWA,IAAQ,QAAUA,IAAQ,YAC3D6uB,EAAST,GAAmBxuB,EAAO,IAAI,GAGzC,MAAMkvB,EACJH,GAAY,YAAcL,GAAM,YAAcjB,GAAS,YAAc,CAAA,EACvE,MAAI,CAACwB,GAAUC,EAAW,OAAS,IACjCD,EAASd,GAAsBnuB,EAAO,KAAMkvB,CAAU,GAGpD,CAACD,GAAUjvB,EAAO,OACpBivB,EAASjvB,EAAO,MAGdivB,IACFA,EAASE,GAAoBF,CAAM,GAG9B,CACL,KAAAj2B,EACA,KAAA61B,EACA,MAAAtmB,EACA,MAAA7K,EACA,KAAAsxB,EACA,OAAAC,CAAA,CAEJ,CAEO,SAASG,GAAiBf,EAA0C,CACzE,MAAMz0B,EAAkB,CAAA,EAGxB,GAFIy0B,EAAQ,MAAMz0B,EAAM,KAAKy0B,EAAQ,IAAI,EACrCA,EAAQ,QAAQz0B,EAAM,KAAKy0B,EAAQ,MAAM,EACzCz0B,EAAM,SAAW,EACrB,OAAOA,EAAM,KAAK,KAAK,CACzB,CAOA,SAASu1B,GAAoBp2B,EAAuB,CAClD,OAAKA,GACEA,EACJ,QAAQ,kBAAmB,GAAG,EAC9B,QAAQ,iBAAkB,GAAG,CAClC,CChMO,MAAMs2B,GAAwB,GAGxBC,GAAoB,EAGpBC,GAAoB,ICD1B,SAASC,GAA2B7zB,EAAsB,CAC/D,MAAM9C,EAAU8C,EAAK,KAAA,EAErB,GAAI9C,EAAQ,WAAW,GAAG,GAAKA,EAAQ,WAAW,GAAG,EACnD,GAAI,CACF,MAAMU,EAAS,KAAK,MAAMV,CAAO,EACjC,MAAO,YAAc,KAAK,UAAUU,EAAQ,KAAM,CAAC,EAAI,OACzD,MAAQ,CAER,CAEF,OAAOoC,CACT,CAMO,SAAS8zB,GAAoB9zB,EAAsB,CACxD,MAAM+zB,EAAW/zB,EAAK,MAAM;AAAA,CAAI,EAC1B+C,EAAQgxB,EAAS,MAAM,EAAGJ,EAAiB,EAC3CtB,EAAUtvB,EAAM,KAAK;AAAA,CAAI,EAC/B,OAAIsvB,EAAQ,OAASuB,GACZvB,EAAQ,MAAM,EAAGuB,EAAiB,EAAI,IAExC7wB,EAAM,OAASgxB,EAAS,OAAS1B,EAAU,IAAMA,CAC1D,CCvBO,SAAS2B,GAAiB9xB,EAA8B,CAC7D,MAAM9G,EAAI8G,EACJE,EAAU6xB,GAAiB74B,EAAE,OAAO,EACpC84B,EAAoB,CAAA,EAE1B,UAAW7xB,KAAQD,EAAS,CAC1B,MAAM+xB,EAAO,OAAO9xB,EAAK,MAAQ,EAAE,EAAE,YAAA,GAEnC,CAAC,WAAY,YAAa,UAAW,UAAU,EAAE,SAAS8xB,CAAI,GAC7D,OAAO9xB,EAAK,MAAS,UAAYA,EAAK,WAAa,OAEpD6xB,EAAM,KAAK,CACT,KAAM,OACN,KAAO7xB,EAAK,MAAmB,OAC/B,KAAM+xB,GAAW/xB,EAAK,WAAaA,EAAK,IAAI,CAAA,CAC7C,CAEL,CAEA,UAAWA,KAAQD,EAAS,CAC1B,MAAM+xB,EAAO,OAAO9xB,EAAK,MAAQ,EAAE,EAAE,YAAA,EACrC,GAAI8xB,IAAS,cAAgBA,IAAS,cAAe,SACrD,MAAMn0B,EAAOq0B,GAAgBhyB,CAAI,EAC3BhF,EAAO,OAAOgF,EAAK,MAAS,SAAWA,EAAK,KAAO,OACzD6xB,EAAM,KAAK,CAAE,KAAM,SAAU,KAAA72B,EAAM,KAAA2C,EAAM,CAC3C,CAEA,GACE8e,GAAoB5c,CAAO,GAC3B,CAACgyB,EAAM,KAAMI,GAASA,EAAK,OAAS,QAAQ,EAC5C,CACA,MAAMj3B,EACH,OAAOjC,EAAE,UAAa,UAAYA,EAAE,UACpC,OAAOA,EAAE,WAAc,UAAYA,EAAE,WACtC,OACI4E,EAAOuC,GAAkBL,CAAO,GAAK,OAC3CgyB,EAAM,KAAK,CAAE,KAAM,SAAU,KAAA72B,EAAM,KAAA2C,EAAM,CAC3C,CAEA,OAAOk0B,CACT,CAEO,SAASK,GACdD,EACAE,EACA,CACA,MAAM9B,EAAUO,GAAmB,CAAE,KAAMqB,EAAK,KAAM,KAAMA,EAAK,KAAM,EACjEhB,EAASG,GAAiBf,CAAO,EACjC+B,EAAU,EAAQH,EAAK,MAAM,OAE7BI,EAAW,EAAQF,EACnBG,EAAcD,EAChB,IAAM,CACJ,GAAID,EAAS,CACXD,EAAeX,GAA2BS,EAAK,IAAK,CAAC,EACrD,MACF,CACA,MAAMM,EAAO,MAAMlC,EAAQ,KAAK;AAAA;AAAA,EAC9BY,EAAS,kBAAkBA,CAAM;AAAA;AAAA,EAAW,EAC9C,6CACAkB,EAAeI,CAAI,CACrB,EACA,OAEEC,EAAUJ,IAAYH,EAAK,MAAM,QAAU,IAAMZ,GACjDoB,EAAgBL,GAAW,CAACI,EAC5BE,EAAaN,GAAWI,EACxBG,EAAU,CAACP,EAEjB,OAAOh1B;AAAAA;AAAAA,8BAEqBi1B,EAAW,4BAA8B,EAAE;AAAA,eAC1DC,CAAW;AAAA,aACbD,EAAW,SAAWO,CAAO;AAAA,iBACzBP,EAAW,IAAMO,CAAO;AAAA,iBACxBP,EACN36B,GAAqB,CAChBA,EAAE,MAAQ,SAAWA,EAAE,MAAQ,MACnCA,EAAE,eAAA,EACF46B,IAAA,EACF,EACAM,CAAO;AAAA;AAAA;AAAA;AAAA,+CAI8Bz1B,EAAMkzB,EAAQ,IAAI,CAAC;AAAA,kBAChDA,EAAQ,KAAK;AAAA;AAAA,UAErBgC,EACEj1B,yCAA4Cg1B,EAAU,OAAS,EAAE,IAAIj1B,EAAM,KAAK,UAChFy1B,CAAO;AAAA,UACTD,GAAW,CAACN,EAAWj1B,yCAA4CD,EAAM,KAAK,UAAYy1B,CAAO;AAAA;AAAA,QAEnG3B,EACE7zB,wCAA2C6zB,CAAM,SACjD2B,CAAO;AAAA,QACTD,EACEv1B,kEACAw1B,CAAO;AAAA,QACTH,EACEr1B,8CAAiDq0B,GAAoBQ,EAAK,IAAK,CAAC,SAChFW,CAAO;AAAA,QACTF,EACEt1B,6CAAgD60B,EAAK,IAAI,SACzDW,CAAO;AAAA;AAAA,GAGjB,CAEA,SAAShB,GAAiB7xB,EAAkD,CAC1E,OAAK,MAAM,QAAQA,CAAO,EACnBA,EAAQ,OAAO,OAAO,EADO,CAAA,CAEtC,CAEA,SAASgyB,GAAWp3B,EAAyB,CAC3C,GAAI,OAAOA,GAAU,SAAU,OAAOA,EACtC,MAAME,EAAUF,EAAM,KAAA,EAEtB,GADI,CAACE,GACD,CAACA,EAAQ,WAAW,GAAG,GAAK,CAACA,EAAQ,WAAW,GAAG,EAAG,OAAOF,EACjE,GAAI,CACF,OAAO,KAAK,MAAME,CAAO,CAC3B,MAAQ,CACN,OAAOF,CACT,CACF,CAEA,SAASq3B,GAAgBhyB,EAAmD,CAC1E,GAAI,OAAOA,EAAK,MAAS,gBAAiBA,EAAK,KAC/C,GAAI,OAAOA,EAAK,SAAY,gBAAiBA,EAAK,OAEpD,CChIO,SAAS6yB,GAA4BC,EAA+B,CACzE,OAAO11B;AAAAA;AAAAA,QAED21B,GAAa,YAAaD,CAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAU5C,CAEO,SAASE,GACdr1B,EACAs1B,EACAd,EACAW,EACA,CACA,MAAMxW,EAAY,IAAI,KAAK2W,CAAS,EAAE,mBAAmB,CAAA,EAAI,CAC3D,KAAM,UACN,OAAQ,SAAA,CACT,EACKj4B,EAAO83B,GAAW,MAAQ,YAEhC,OAAO11B;AAAAA;AAAAA,QAED21B,GAAa,YAAaD,CAAS,CAAC;AAAA;AAAA,UAElCI,GACA,CACE,KAAM,YACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAAv1B,EAAM,EAChC,UAAWs1B,CAAA,EAEb,CAAE,YAAa,GAAM,cAAe,EAAA,EACpCd,CAAA,CACD;AAAA;AAAA,2CAEkCn3B,CAAI;AAAA,+CACAshB,CAAS;AAAA;AAAA;AAAA;AAAA,GAKxD,CAEO,SAAS6W,GACdC,EACA5pB,EAMA,CACA,MAAM6pB,EAAiB9W,GAAyB6W,EAAM,IAAI,EACpDE,EAAgB9pB,EAAK,eAAiB,YACtC+pB,EACJF,IAAmB,OACf,MACAA,IAAmB,YACjBC,EACAD,EACFG,EACJH,IAAmB,OACf,OACAA,IAAmB,YACjB,YACA,QACF/W,EAAY,IAAI,KAAK8W,EAAM,SAAS,EAAE,mBAAmB,GAAI,CACjE,KAAM,UACN,OAAQ,SAAA,CACT,EAED,OAAOh2B;AAAAA,6BACoBo2B,CAAS;AAAA,QAC9BT,GAAaK,EAAM,KAAM,CACzB,KAAME,EACN,OAAQ9pB,EAAK,iBAAmB,IAAA,CACjC,CAAC;AAAA;AAAA,UAEE4pB,EAAM,SAAS,IAAI,CAACpzB,EAAMof,IAC1B8T,GACElzB,EAAK,QACL,CACE,YACEozB,EAAM,aAAehU,IAAUgU,EAAM,SAAS,OAAS,EACzD,cAAe5pB,EAAK,aAAA,EAEtBA,EAAK,aAAA,CACP,CACD;AAAA;AAAA,2CAEkC+pB,CAAG;AAAA,+CACCjX,CAAS;AAAA;AAAA;AAAA;AAAA,GAKxD,CAEA,SAASyW,GACPjzB,EACAgzB,EACA,CACA,MAAMt2B,EAAa+f,GAAyBzc,CAAI,EAC1CwzB,EAAgBR,GAAW,MAAM,KAAA,GAAU,YAC3CW,EAAkBX,GAAW,QAAQ,KAAA,GAAU,GAC/CY,EACJl3B,IAAe,OACX,IACAA,IAAe,YACb82B,EAAc,OAAO,CAAC,EAAE,eAAiB,IACzC92B,IAAe,OACb,IACA,IACJm3B,EACJn3B,IAAe,OACX,OACAA,IAAe,YACb,YACFA,IAAe,OACX,OACA,QAEV,OAAIi3B,GAAmBj3B,IAAe,YAChCo3B,GAAYH,CAAe,EACtBr2B;AAAAA,6BACgBu2B,CAAS;AAAA,eACvBF,CAAe;AAAA,eACfH,CAAa;AAAA,UAGjBl2B,4BAA+Bu2B,CAAS,KAAKF,CAAe,SAG9Dr2B,4BAA+Bu2B,CAAS,KAAKD,CAAO,QAC7D,CAEA,SAASE,GAAYj5B,EAAwB,CAC3C,MACE,gBAAgB,KAAKA,CAAK,GAC1B,iBAAiB,KAAKA,CAAK,GAC3B,MAAM,KAAKA,CAAK,CAEpB,CAEA,SAASu4B,GACPrzB,EACA2J,EACA2oB,EACA,CACA,MAAMp5B,EAAI8G,EACJC,EAAO,OAAO/G,EAAE,MAAS,SAAWA,EAAE,KAAO,UAC7C86B,EACJpX,GAAoB5c,CAAO,GAC3BC,EAAK,YAAA,IAAkB,cACvBA,EAAK,YAAA,IAAkB,eACvB,OAAO/G,EAAE,YAAe,UACxB,OAAOA,EAAE,cAAiB,SAEtB+6B,EAAYnC,GAAiB9xB,CAAO,EACpCk0B,EAAeD,EAAU,OAAS,EAElCE,EAAgB9zB,GAAkBL,CAAO,EACzCo0B,EACJzqB,EAAK,eAAiB1J,IAAS,YAC3BU,GAAsBX,CAAO,EAC7B,KACAq0B,EAAeF,GAAe,KAAA,EAASA,EAAgB,KACvDG,EAAoBF,EACtBxzB,GAAwBwzB,CAAiB,EACzC,KACE3F,EAAW4F,EACXE,EAAkBt0B,IAAS,aAAe,EAAQwuB,GAAU,OAE5D+F,EAAgB,CACpB,cACAD,EAAkB,WAAa,GAC/B5qB,EAAK,YAAc,YAAc,GACjC,SAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG,EAEX,MAAI,CAAC8kB,GAAYyF,GAAgBF,EACxBz2B,IAAO02B,EAAU,IAAK7B,GAC3BC,GAAsBD,EAAME,CAAa,CAAA,CAC1C,GAGC,CAAC7D,GAAY,CAACyF,EAAqBnB,EAEhCx1B;AAAAA,kBACSi3B,CAAa;AAAA,QACvBD,EAAkB9E,GAA2BhB,CAAS,EAAIsE,CAAO;AAAA,QACjEuB,EACE/2B,+BAAkCk3B,GAChCjG,GAAwB8F,CAAiB,CAAA,CAC1C,SACDvB,CAAO;AAAA,QACTtE,EACElxB,2BAA8Bk3B,GAAWjG,GAAwBC,CAAQ,CAAC,CAAC,SAC3EsE,CAAO;AAAA,QACTkB,EAAU,IAAK7B,GAASC,GAAsBD,EAAME,CAAa,CAAC,CAAC;AAAA;AAAA,GAG3E,CCpNO,SAASoC,GAAsBC,EAA6B,CACjE,OAAOp3B;AAAAA;AAAAA;AAAAA;AAAAA,yBAIgBo3B,EAAM,OAAO;AAAA,YAC1Br3B,EAAM,CAAC;AAAA;AAAA;AAAA;AAAA,UAITq3B,EAAM,MACJp3B;AAAAA,4CACgCo3B,EAAM,KAAK;AAAA,+BACxBA,EAAM,aAAa;AAAA;AAAA;AAAA,cAItCA,EAAM,QACJp3B,kCAAqCk3B,GAAWjG,GAAwBmG,EAAM,OAAO,CAAC,CAAC,SACvFp3B,gDAAmD;AAAA;AAAA;AAAA,GAIjE,sMC5BO,IAAMq3B,GAAN,cAA+BC,EAAW,CAA1C,aAAA,CAAA,MAAA,GAAA,SAAA,EACuB,KAAA,WAAa,GACb,KAAA,SAAW,GACX,KAAA,SAAW,GAEvC,KAAQ,WAAa,GACrB,KAAQ,OAAS,EACjB,KAAQ,WAAa,EA8CrB,KAAQ,gBAAmB,GAAkB,CAC3C,KAAK,WAAa,GAClB,KAAK,OAAS,EAAE,QAChB,KAAK,WAAa,KAAK,WACvB,KAAK,UAAU,IAAI,UAAU,EAE7B,SAAS,iBAAiB,YAAa,KAAK,eAAe,EAC3D,SAAS,iBAAiB,UAAW,KAAK,aAAa,EAEvD,EAAE,eAAA,CACJ,EAEA,KAAQ,gBAAmB,GAAkB,CAC3C,GAAI,CAAC,KAAK,WAAY,OAEtB,MAAMpwB,EAAY,KAAK,cACvB,GAAI,CAACA,EAAW,OAEhB,MAAMqwB,EAAiBrwB,EAAU,sBAAA,EAAwB,MAEnDswB,GADS,EAAE,QAAU,KAAK,QACJD,EAE5B,IAAIE,EAAW,KAAK,WAAaD,EACjCC,EAAW,KAAK,IAAI,KAAK,SAAU,KAAK,IAAI,KAAK,SAAUA,CAAQ,CAAC,EAEpE,KAAK,cACH,IAAI,YAAY,SAAU,CACxB,OAAQ,CAAE,WAAYA,CAAA,EACtB,QAAS,GACT,SAAU,EAAA,CACX,CAAA,CAEL,EAEA,KAAQ,cAAgB,IAAM,CAC5B,KAAK,WAAa,GAClB,KAAK,UAAU,OAAO,UAAU,EAEhC,SAAS,oBAAoB,YAAa,KAAK,eAAe,EAC9D,SAAS,oBAAoB,UAAW,KAAK,aAAa,CAC5D,CAAA,CAxDA,QAAS,CACP,OAAOz3B,GACT,CAEA,mBAAoB,CAClB,MAAM,kBAAA,EACN,KAAK,iBAAiB,YAAa,KAAK,eAAe,CACzD,CAEA,sBAAuB,CACrB,MAAM,qBAAA,EACN,KAAK,oBAAoB,YAAa,KAAK,eAAe,EAC1D,SAAS,oBAAoB,YAAa,KAAK,eAAe,EAC9D,SAAS,oBAAoB,UAAW,KAAK,aAAa,CAC5D,CA2CF,EA9Faq3B,GASJ,OAASK;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,IARYC,GAAA,CAA3BvV,GAAS,CAAE,KAAM,MAAA,CAAQ,CAAA,EADfiV,GACiB,UAAA,aAAA,CAAA,EACAM,GAAA,CAA3BvV,GAAS,CAAE,KAAM,MAAA,CAAQ,CAAA,EAFfiV,GAEiB,UAAA,WAAA,CAAA,EACAM,GAAA,CAA3BvV,GAAS,CAAE,KAAM,MAAA,CAAQ,CAAA,EAHfiV,GAGiB,UAAA,WAAA,CAAA,EAHjBA,GAANM,GAAA,CADNC,GAAc,mBAAmB,CAAA,EACrBP,EAAA,EC4Db,MAAM7wB,GAA+B,IAErC,SAASqxB,GAA0B5sB,EAAsD,CACvF,OAAKA,EAGDA,EAAO,OACFjL;AAAAA;AAAAA,UAEDD,EAAM,MAAM;AAAA;AAAA,MAMhBkL,EAAO,aACO,KAAK,IAAA,EAAQA,EAAO,YACtBzE,GACLxG;AAAAA;AAAAA,YAEDD,EAAM,KAAK;AAAA;AAAA,QAMdy1B,EAvBaA,CAwBtB,CAEO,SAASsC,GAAWV,EAAkB,CAC3C,MAAMW,EAAaX,EAAM,UACnBY,EAASZ,EAAM,SAAWA,EAAM,SAAW,KAC3Ca,EAAW,GAAQb,EAAM,UAAYA,EAAM,SAI3Cc,EAHgBd,EAAM,UAAU,UAAU,KAC7Ce,GAAQA,EAAI,MAAQf,EAAM,UAAA,GAES,gBAAkB,MAClDgB,EAAgBhB,EAAM,cAAgBc,IAAmB,MACzDG,EAAoB,CACxB,KAAMjB,EAAM,cACZ,OAAQA,EAAM,iBAAmBA,EAAM,oBAAsB,IAAA,EAGzDkB,EAAqBlB,EAAM,UAC7B,+CACA,4CAEEmB,EAAanB,EAAM,YAAc,GACjCoB,EAAc,GAAQpB,EAAM,aAAeA,EAAM,gBACjDqB,EAASz4B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,gBAKDo3B,EAAM,YAAY;AAAA;AAAA,QAE1BA,EAAM,QAAUp3B,0CAA+Cw1B,CAAO;AAAA,QACtEkD,GAAOC,GAAevB,CAAK,EAAIx0B,GAASA,EAAK,IAAMA,GAC/CA,EAAK,OAAS,oBACT6yB,GAA4B4C,CAAiB,EAGlDz1B,EAAK,OAAS,SACTgzB,GACLhzB,EAAK,KACLA,EAAK,UACLw0B,EAAM,cACNiB,CAAA,EAIAz1B,EAAK,OAAS,QACTmzB,GAAmBnzB,EAAM,CAC9B,cAAew0B,EAAM,cACrB,cAAAgB,EACA,cAAehB,EAAM,cACrB,gBAAiBiB,EAAkB,MAAA,CACpC,EAGI7C,CACR,CAAC;AAAA;AAAA,IAIN,OAAOx1B;AAAAA;AAAAA,QAEDo3B,EAAM,eACJp3B,yBAA4Bo3B,EAAM,cAAc,SAChD5B,CAAO;AAAA;AAAA,QAET4B,EAAM,MACJp3B,gCAAmCo3B,EAAM,KAAK,SAC9C5B,CAAO;AAAA;AAAA,QAETqC,GAA0BT,EAAM,gBAAgB,CAAC;AAAA;AAAA,QAEjDA,EAAM,UACJp3B;AAAAA;AAAAA;AAAAA;AAAAA,uBAIao3B,EAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA,gBAI9Br3B,EAAM,CAAC;AAAA;AAAA,YAGby1B,CAAO;AAAA;AAAA;AAAA,sCAGqBgD,EAAc,6BAA+B,EAAE;AAAA;AAAA;AAAA;AAAA,yBAI5DA,EAAc,OAAOD,EAAa,GAAG,IAAM,UAAU;AAAA;AAAA,YAElEE,CAAM;AAAA;AAAA;AAAA,UAGRD,EACEx4B;AAAAA;AAAAA,8BAEkBu4B,CAAU;AAAA,0BACbj+B,GACT88B,EAAM,qBAAqB98B,EAAE,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA,kBAG/C68B,GAAsB,CACtB,QAASC,EAAM,gBAAkB,KACjC,MAAOA,EAAM,cAAgB,KAC7B,QAASA,EAAM,eACf,cAAe,IAAM,CACf,CAACA,EAAM,gBAAkB,CAACA,EAAM,eACpCA,EAAM,cAAc;AAAA,EAAWA,EAAM,cAAc;AAAA,OAAU,CAC/D,CAAA,CACD,CAAC;AAAA;AAAA,cAGN5B,CAAO;AAAA;AAAA;AAAA,QAGX4B,EAAM,MAAM,OACVp3B;AAAAA;AAAAA,uDAE6Co3B,EAAM,MAAM,MAAM;AAAA;AAAA,kBAEvDA,EAAM,MAAM,IACXx0B,GAAS5C;AAAAA;AAAAA,sDAE0B4C,EAAK,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,iCAK9B,IAAMw0B,EAAM,cAAcx0B,EAAK,EAAE,CAAC;AAAA;AAAA,0BAEzC7C,EAAM,CAAC;AAAA;AAAA;AAAA,mBAAA,CAIhB;AAAA;AAAA;AAAA,YAIPy1B,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAMI4B,EAAM,KAAK;AAAA,wBACR,CAACA,EAAM,SAAS;AAAA,uBAChB98B,GAAqB,CAC3BA,EAAE,MAAQ,UACVA,EAAE,aAAeA,EAAE,UAAY,KAC/BA,EAAE,UACD88B,EAAM,YACX98B,EAAE,eAAA,EACEy9B,KAAkB,OAAA,GACxB,CAAC;AAAA,qBACSz9B,GACR88B,EAAM,cAAe98B,EAAE,OAA+B,KAAK,CAAC;AAAA,0BAChDg+B,CAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMpB,CAAClB,EAAM,WAAc,CAACa,GAAYb,EAAM,OAAQ;AAAA,qBACnDa,EAAWb,EAAM,QAAUA,EAAM,YAAY;AAAA;AAAA,cAEpDa,EAAW,OAAS,aAAa;AAAA;AAAA;AAAA;AAAA,wBAIvB,CAACb,EAAM,SAAS;AAAA,qBACnBA,EAAM,MAAM;AAAA;AAAA,cAEnBY,EAAS,QAAU,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,GAMvC,CAEA,MAAMY,GAA4B,IAElC,SAASC,GAAcC,EAAmD,CACxE,MAAMp4B,EAAyC,CAAA,EAC/C,IAAIq4B,EAAoC,KAExC,UAAWn2B,KAAQk2B,EAAO,CACxB,GAAIl2B,EAAK,OAAS,UAAW,CACvBm2B,IACFr4B,EAAO,KAAKq4B,CAAY,EACxBA,EAAe,MAEjBr4B,EAAO,KAAKkC,CAAI,EAChB,QACF,CAEA,MAAMxD,EAAawf,GAAiBhc,EAAK,OAAO,EAC1CF,EAAOyc,GAAyB/f,EAAW,IAAI,EAC/C8f,EAAY9f,EAAW,WAAa,KAAK,IAAA,EAE3C,CAAC25B,GAAgBA,EAAa,OAASr2B,GACrCq2B,GAAcr4B,EAAO,KAAKq4B,CAAY,EAC1CA,EAAe,CACb,KAAM,QACN,IAAK,SAASr2B,CAAI,IAAIE,EAAK,GAAG,GAC9B,KAAAF,EACA,SAAU,CAAC,CAAE,QAASE,EAAK,QAAS,IAAKA,EAAK,IAAK,EACnD,UAAAsc,EACA,YAAa,EAAA,GAGf6Z,EAAa,SAAS,KAAK,CAAE,QAASn2B,EAAK,QAAS,IAAKA,EAAK,IAAK,CAEvE,CAEA,OAAIm2B,GAAcr4B,EAAO,KAAKq4B,CAAY,EACnCr4B,CACT,CAEA,SAASi4B,GAAevB,EAAkD,CACxE,MAAM0B,EAAoB,CAAA,EACpBE,EAAU,MAAM,QAAQ5B,EAAM,QAAQ,EAAIA,EAAM,SAAW,CAAA,EAC3D6B,EAAQ,MAAM,QAAQ7B,EAAM,YAAY,EAAIA,EAAM,aAAe,CAAA,EACjE8B,EAAe,KAAK,IAAI,EAAGF,EAAQ,OAASJ,EAAyB,EACvEM,EAAe,GACjBJ,EAAM,KAAK,CACT,KAAM,UACN,IAAK,sBACL,QAAS,CACP,KAAM,SACN,QAAS,gBAAgBF,EAAyB,cAAcM,CAAY,YAC5E,UAAW,KAAK,IAAA,CAAI,CACtB,CACD,EAEH,QAASt+B,EAAIs+B,EAAct+B,EAAIo+B,EAAQ,OAAQp+B,IAAK,CAClD,MAAMwJ,EAAM40B,EAAQp+B,CAAC,EACfwE,EAAawf,GAAiBxa,CAAG,EAEnC,CAACgzB,EAAM,cAAgBh4B,EAAW,KAAK,YAAA,IAAkB,cAI7D05B,EAAM,KAAK,CACT,KAAM,UACN,IAAKK,GAAW/0B,EAAKxJ,CAAC,EACtB,QAASwJ,CAAA,CACV,CACH,CACA,GAAIgzB,EAAM,aACR,QAASx8B,EAAI,EAAGA,EAAIq+B,EAAM,OAAQr+B,IAChCk+B,EAAM,KAAK,CACT,KAAM,UACN,IAAKK,GAAWF,EAAMr+B,CAAC,EAAGA,EAAIo+B,EAAQ,MAAM,EAC5C,QAASC,EAAMr+B,CAAC,CAAA,CACjB,EAIL,GAAIw8B,EAAM,SAAW,KAAM,CACzB,MAAMpyB,EAAM,UAAUoyB,EAAM,UAAU,IAAIA,EAAM,iBAAmB,MAAM,GACrEA,EAAM,OAAO,KAAA,EAAO,OAAS,EAC/B0B,EAAM,KAAK,CACT,KAAM,SACN,IAAA9zB,EACA,KAAMoyB,EAAM,OACZ,UAAWA,EAAM,iBAAmB,KAAK,IAAA,CAAI,CAC9C,EAED0B,EAAM,KAAK,CAAE,KAAM,oBAAqB,IAAA9zB,EAAK,CAEjD,CAEA,OAAO6zB,GAAcC,CAAK,CAC5B,CAEA,SAASK,GAAW12B,EAAkBuf,EAAuB,CAC3D,MAAMrmB,EAAI8G,EACJoE,EAAa,OAAOlL,EAAE,YAAe,SAAWA,EAAE,WAAa,GACrE,GAAIkL,EAAY,MAAO,QAAQA,CAAU,GACzC,MAAMX,EAAK,OAAOvK,EAAE,IAAO,SAAWA,EAAE,GAAK,GAC7C,GAAIuK,EAAI,MAAO,OAAOA,CAAE,GACxB,MAAMkzB,EAAY,OAAOz9B,EAAE,WAAc,SAAWA,EAAE,UAAY,GAClE,GAAIy9B,EAAW,MAAO,OAAOA,CAAS,GACtC,MAAMla,EAAY,OAAOvjB,EAAE,WAAc,SAAWA,EAAE,UAAY,KAC5D+G,EAAO,OAAO/G,EAAE,MAAS,SAAWA,EAAE,KAAO,UACnD,OAAIujB,GAAa,KAAa,OAAOxc,CAAI,IAAIwc,CAAS,IAAI8C,CAAK,GACxD,OAAOtf,CAAI,IAAIsf,CAAK,EAC7B,CC9WO,SAASqX,GAAWC,EAAwC,CACjE,GAAKA,EACL,OAAI,MAAM,QAAQA,EAAO,IAAI,EACVA,EAAO,KAAK,OAAQj/B,GAAMA,IAAM,MAAM,EACvC,CAAC,GAAKi/B,EAAO,KAAK,CAAC,EAE9BA,EAAO,IAChB,CAEO,SAASC,GAAaD,EAA8B,CACzD,GAAI,CAACA,EAAQ,MAAO,GACpB,GAAIA,EAAO,UAAY,OAAW,OAAOA,EAAO,QAEhD,OADaD,GAAWC,CAAM,EACtB,CACN,IAAK,SACH,MAAO,CAAA,EACT,IAAK,QACH,MAAO,CAAA,EACT,IAAK,UACH,MAAO,GACT,IAAK,SACL,IAAK,UACH,MAAO,GACT,IAAK,SACH,MAAO,GACT,QACE,MAAO,EAAA,CAEb,CAEO,SAASE,GAAQz6B,EAAsC,CAC5D,OAAOA,EAAK,OAAQ+zB,GAAY,OAAOA,GAAY,QAAQ,EAAE,KAAK,GAAG,CACvE,CAEO,SAAS2G,GAAY16B,EAA8B26B,EAAsB,CAC9E,MAAM10B,EAAMw0B,GAAQz6B,CAAI,EAClB46B,EAASD,EAAM10B,CAAG,EACxB,GAAI20B,EAAQ,OAAOA,EACnB,MAAMl6B,EAAWuF,EAAI,MAAM,GAAG,EAC9B,SAAW,CAAC40B,EAASC,CAAI,IAAK,OAAO,QAAQH,CAAK,EAAG,CACnD,GAAI,CAACE,EAAQ,SAAS,GAAG,EAAG,SAC5B,MAAME,EAAeF,EAAQ,MAAM,GAAG,EACtC,GAAIE,EAAa,SAAWr6B,EAAS,OAAQ,SAC7C,IAAIoB,EAAQ,GACZ,QAASjG,EAAI,EAAGA,EAAI6E,EAAS,OAAQ7E,GAAK,EACxC,GAAIk/B,EAAal/B,CAAC,IAAM,KAAOk/B,EAAal/B,CAAC,IAAM6E,EAAS7E,CAAC,EAAG,CAC9DiG,EAAQ,GACR,KACF,CAEF,GAAIA,EAAO,OAAOg5B,CACpB,CAEF,CAEO,SAASE,GAAS77B,EAAa,CACpC,OAAOA,EACJ,QAAQ,KAAM,GAAG,EACjB,QAAQ,qBAAsB,OAAO,EACrC,QAAQ,OAAQ,GAAG,EACnB,QAAQ,KAAOvC,GAAMA,EAAE,aAAa,CACzC,CAEO,SAASq+B,GAAgBj7B,EAAuC,CACrE,MAAMiG,EAAMw0B,GAAQz6B,CAAI,EAAE,YAAA,EAC1B,OACEiG,EAAI,SAAS,OAAO,GACpBA,EAAI,SAAS,UAAU,GACvBA,EAAI,SAAS,QAAQ,GACrBA,EAAI,SAAS,QAAQ,GACrBA,EAAI,SAAS,KAAK,CAEtB,CC9EA,MAAMi1B,OAAgB,IAAI,CAAC,QAAS,cAAe,UAAW,UAAU,CAAC,EAEzE,SAASC,GAAYZ,EAA6B,CAEhD,OADa,OAAO,KAAKA,GAAU,CAAA,CAAE,EAAE,OAAQt0B,GAAQ,CAACi1B,GAAU,IAAIj1B,CAAG,CAAC,EAC9D,SAAW,CACzB,CAEA,SAASm1B,GAAU58B,EAAwB,CACzC,GAAIA,IAAU,OAAW,MAAO,GAChC,GAAI,CACF,OAAO,KAAK,UAAUA,EAAO,KAAM,CAAC,GAAK,EAC3C,MAAQ,CACN,MAAO,EACT,CACF,CAGA,MAAMwC,GAAQ,CACZ,YAAaC,kLACb,KAAMA,6NACN,MAAOA,iLACP,MAAOA,gRACP,KAAMA,yRACR,EAEO,SAASo6B,GAAWx1B,EASS,CAClC,KAAM,CAAE,OAAA00B,EAAQ,MAAA/7B,EAAO,KAAAwB,EAAM,MAAA26B,EAAO,YAAAW,EAAa,SAAAC,EAAU,QAAAC,GAAY31B,EACjE41B,EAAY51B,EAAO,WAAa,GAChC61B,EAAOpB,GAAWC,CAAM,EACxBO,EAAOJ,GAAY16B,EAAM26B,CAAK,EAC9Bp3B,EAAQu3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOh7B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnE27B,EAAOb,GAAM,MAAQP,EAAO,YAC5Bt0B,EAAMw0B,GAAQz6B,CAAI,EAExB,GAAIs7B,EAAY,IAAIr1B,CAAG,EACrB,OAAOhF;AAAAA,sCAC2BsC,CAAK;AAAA;AAAA,YAMzC,GAAIg3B,EAAO,OAASA,EAAO,MAAO,CAEhC,MAAMqB,GADWrB,EAAO,OAASA,EAAO,OAAS,CAAA,GACxB,OACtB79B,GAAM,EAAEA,EAAE,OAAS,QAAW,MAAM,QAAQA,EAAE,IAAI,GAAKA,EAAE,KAAK,SAAS,MAAM,EAAA,EAGhF,GAAIk/B,EAAQ,SAAW,EACrB,OAAOP,GAAW,CAAE,GAAGx1B,EAAQ,OAAQ+1B,EAAQ,CAAC,EAAG,EAIrD,MAAMC,EAAkBn/B,GAAuC,CAC7D,GAAIA,EAAE,QAAU,OAAW,OAAOA,EAAE,MACpC,GAAIA,EAAE,MAAQA,EAAE,KAAK,SAAW,EAAG,OAAOA,EAAE,KAAK,CAAC,CAEpD,EACMo/B,EAAWF,EAAQ,IAAIC,CAAc,EACrCE,EAAcD,EAAS,MAAOp/B,GAAMA,IAAM,MAAS,EAEzD,GAAIq/B,GAAeD,EAAS,OAAS,GAAKA,EAAS,QAAU,EAAG,CAE9D,MAAME,EAAgBx9B,GAAS+7B,EAAO,QACtC,OAAOt5B;AAAAA;AAAAA,YAEDw6B,EAAYx6B,oCAAuCsC,CAAK,WAAakzB,CAAO;AAAA,YAC5EkF,EAAO16B,iCAAoC06B,CAAI,SAAWlF,CAAO;AAAA;AAAA,cAE/DqF,EAAS,IAAI,CAACG,EAAKl6B,KAAQd;AAAAA;AAAAA;AAAAA,4CAGGg7B,IAAQD,GAAiB,OAAOC,CAAG,IAAM,OAAOD,CAAa,EAAI,SAAW,EAAE;AAAA,4BAC9FT,CAAQ;AAAA,yBACX,IAAMC,EAAQx7B,EAAMi8B,CAAG,CAAC;AAAA;AAAA,kBAE/B,OAAOA,CAAG,CAAC;AAAA;AAAA,aAEhB,CAAC;AAAA;AAAA;AAAA,OAIV,CAEA,GAAIF,GAAeD,EAAS,OAAS,EAEnC,OAAOI,GAAa,CAAE,GAAGr2B,EAAQ,QAASi2B,EAAU,MAAOt9B,GAAS+7B,EAAO,QAAS,EAItF,MAAM4B,EAAiB,IAAI,IACzBP,EAAQ,IAAKQ,GAAY9B,GAAW8B,CAAO,CAAC,EAAE,OAAO,OAAO,CAAA,EAExDC,EAAkB,IAAI,IAC1B,CAAC,GAAGF,CAAc,EAAE,IAAKz/B,GAAOA,IAAM,UAAY,SAAWA,CAAE,CAAA,EAGjE,GAAI,CAAC,GAAG2/B,CAAe,EAAE,MAAO3/B,GAAM,CAAC,SAAU,SAAU,SAAS,EAAE,SAASA,CAAW,CAAC,EAAG,CAC5F,MAAM4/B,EAAYD,EAAgB,IAAI,QAAQ,EACxCE,EAAYF,EAAgB,IAAI,QAAQ,EAG9C,GAFmBA,EAAgB,IAAI,SAAS,GAE9BA,EAAgB,OAAS,EACzC,OAAOhB,GAAW,CAChB,GAAGx1B,EACH,OAAQ,CAAE,GAAG00B,EAAQ,KAAM,UAAW,MAAO,OAAW,MAAO,MAAA,CAAU,CAC1E,EAGH,GAAI+B,GAAaC,EACf,OAAOC,GAAgB,CACrB,GAAG32B,EACH,UAAW02B,GAAa,CAACD,EAAY,SAAW,MAAA,CACjD,CAEL,CACF,CAGA,GAAI/B,EAAO,KAAM,CACf,MAAM94B,EAAU84B,EAAO,KACvB,GAAI94B,EAAQ,QAAU,EAAG,CACvB,MAAMu6B,EAAgBx9B,GAAS+7B,EAAO,QACtC,OAAOt5B;AAAAA;AAAAA,YAEDw6B,EAAYx6B,oCAAuCsC,CAAK,WAAakzB,CAAO;AAAA,YAC5EkF,EAAO16B,iCAAoC06B,CAAI,SAAWlF,CAAO;AAAA;AAAA,cAE/Dh1B,EAAQ,IAAKg7B,GAAQx7B;AAAAA;AAAAA;AAAAA,4CAGSw7B,IAAQT,GAAiB,OAAOS,CAAG,IAAM,OAAOT,CAAa,EAAI,SAAW,EAAE;AAAA,4BAC9FT,CAAQ;AAAA,yBACX,IAAMC,EAAQx7B,EAAMy8B,CAAG,CAAC;AAAA;AAAA,kBAE/B,OAAOA,CAAG,CAAC;AAAA;AAAA,aAEhB,CAAC;AAAA;AAAA;AAAA,OAIV,CACA,OAAOP,GAAa,CAAE,GAAGr2B,EAAQ,QAAApE,EAAS,MAAOjD,GAAS+7B,EAAO,QAAS,CAC5E,CAGA,GAAImB,IAAS,SACX,OAAOgB,GAAa72B,CAAM,EAI5B,GAAI61B,IAAS,QACX,OAAOiB,GAAY92B,CAAM,EAI3B,GAAI61B,IAAS,UAAW,CACtB,MAAMkB,EAAe,OAAOp+B,GAAU,UAAYA,EAAQ,OAAO+7B,EAAO,SAAY,UAAYA,EAAO,QAAU,GACjH,OAAOt5B;AAAAA,qCAC0Bs6B,EAAW,WAAa,EAAE;AAAA;AAAA,gDAEfh4B,CAAK;AAAA,YACzCo4B,EAAO16B,uCAA0C06B,CAAI,UAAYlF,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA,uBAK7DmG,CAAY;AAAA,wBACXrB,CAAQ;AAAA,sBACThgC,GAAaigC,EAAQx7B,EAAOzE,EAAE,OAA4B,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,KAMvF,CAGA,OAAImgC,IAAS,UAAYA,IAAS,UACzBmB,GAAkBh3B,CAAM,EAI7B61B,IAAS,SACJc,GAAgB,CAAE,GAAG32B,EAAQ,UAAW,OAAQ,EAIlD5E;AAAAA;AAAAA,sCAE6BsC,CAAK;AAAA,wDACam4B,CAAI;AAAA;AAAA,GAG5D,CAEA,SAASc,GAAgB32B,EASN,CACjB,KAAM,CAAE,OAAA00B,EAAQ,MAAA/7B,EAAO,KAAAwB,EAAM,MAAA26B,EAAO,SAAAY,EAAU,QAAAC,EAAS,UAAAsB,GAAcj3B,EAC/D41B,EAAY51B,EAAO,WAAa,GAChCi1B,EAAOJ,GAAY16B,EAAM26B,CAAK,EAC9Bp3B,EAAQu3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOh7B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnE27B,EAAOb,GAAM,MAAQP,EAAO,YAC5BwC,EAAcjC,GAAM,WAAaG,GAAgBj7B,CAAI,EACrDg9B,EACJlC,GAAM,cACLiC,EAAc,OAASxC,EAAO,UAAY,OAAY,YAAYA,EAAO,OAAO,GAAK,IAClFqC,EAAep+B,GAAS,GAE9B,OAAOyC;AAAAA;AAAAA,QAEDw6B,EAAYx6B,oCAAuCsC,CAAK,WAAakzB,CAAO;AAAA,QAC5EkF,EAAO16B,iCAAoC06B,CAAI,SAAWlF,CAAO;AAAA;AAAA;AAAA,iBAGxDsG,EAAc,WAAaD,CAAS;AAAA;AAAA,wBAE7BE,CAAW;AAAA,mBAChBJ,GAAgB,KAAO,GAAK,OAAOA,CAAY,CAAC;AAAA,sBAC7CrB,CAAQ;AAAA,mBACVhgC,GAAa,CACrB,MAAM4D,EAAO5D,EAAE,OAA4B,MAC3C,GAAIuhC,IAAc,SAAU,CAC1B,GAAI39B,EAAI,KAAA,IAAW,GAAI,CACrBq8B,EAAQx7B,EAAM,MAAS,EACvB,MACF,CACA,MAAMZ,EAAS,OAAOD,CAAG,EACzBq8B,EAAQx7B,EAAM,OAAO,MAAMZ,CAAM,EAAID,EAAMC,CAAM,EACjD,MACF,CACAo8B,EAAQx7B,EAAMb,CAAG,CACnB,CAAC;AAAA;AAAA,UAEDo7B,EAAO,UAAY,OAAYt5B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,wBAKjBs6B,CAAQ;AAAA,qBACX,IAAMC,EAAQx7B,EAAMu6B,EAAO,OAAO,CAAC;AAAA;AAAA,UAE5C9D,CAAO;AAAA;AAAA;AAAA,GAInB,CAEA,SAASoG,GAAkBh3B,EAQR,CACjB,KAAM,CAAE,OAAA00B,EAAQ,MAAA/7B,EAAO,KAAAwB,EAAM,MAAA26B,EAAO,SAAAY,EAAU,QAAAC,GAAY31B,EACpD41B,EAAY51B,EAAO,WAAa,GAChCi1B,EAAOJ,GAAY16B,EAAM26B,CAAK,EAC9Bp3B,EAAQu3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOh7B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnE27B,EAAOb,GAAM,MAAQP,EAAO,YAC5BqC,EAAep+B,GAAS+7B,EAAO,SAAW,GAC1C0C,EAAW,OAAOL,GAAiB,SAAWA,EAAe,EAEnE,OAAO37B;AAAAA;AAAAA,QAEDw6B,EAAYx6B,oCAAuCsC,CAAK,WAAakzB,CAAO;AAAA,QAC5EkF,EAAO16B,iCAAoC06B,CAAI,SAAWlF,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKnD8E,CAAQ;AAAA,mBACX,IAAMC,EAAQx7B,EAAMi9B,EAAW,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKjCL,GAAgB,KAAO,GAAK,OAAOA,CAAY,CAAC;AAAA,sBAC7CrB,CAAQ;AAAA,mBACVhgC,GAAa,CACrB,MAAM4D,EAAO5D,EAAE,OAA4B,MACrC6D,EAASD,IAAQ,GAAK,OAAY,OAAOA,CAAG,EAClDq8B,EAAQx7B,EAAMZ,CAAM,CACtB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKWm8B,CAAQ;AAAA,mBACX,IAAMC,EAAQx7B,EAAMi9B,EAAW,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,GAKpD,CAEA,SAASf,GAAar2B,EASH,CACjB,KAAM,CAAE,OAAA00B,EAAQ,MAAA/7B,EAAO,KAAAwB,EAAM,MAAA26B,EAAO,SAAAY,EAAU,QAAA95B,EAAS,QAAA+5B,GAAY31B,EAC7D41B,EAAY51B,EAAO,WAAa,GAChCi1B,EAAOJ,GAAY16B,EAAM26B,CAAK,EAC9Bp3B,EAAQu3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOh7B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnE27B,EAAOb,GAAM,MAAQP,EAAO,YAC5ByB,EAAgBx9B,GAAS+7B,EAAO,QAChC2C,EAAez7B,EAAQ,UAC1Bg7B,GAAQA,IAAQT,GAAiB,OAAOS,CAAG,IAAM,OAAOT,CAAa,CAAA,EAElEmB,EAAQ,YAEd,OAAOl8B;AAAAA;AAAAA,QAEDw6B,EAAYx6B,oCAAuCsC,CAAK,WAAakzB,CAAO;AAAA,QAC5EkF,EAAO16B,iCAAoC06B,CAAI,SAAWlF,CAAO;AAAA;AAAA;AAAA,oBAGrD8E,CAAQ;AAAA,iBACX2B,GAAgB,EAAI,OAAOA,CAAY,EAAIC,CAAK;AAAA,kBAC9C5hC,GAAa,CACtB,MAAM6hC,EAAO7hC,EAAE,OAA6B,MAC5CigC,EAAQx7B,EAAMo9B,IAAQD,EAAQ,OAAY17B,EAAQ,OAAO27B,CAAG,CAAC,CAAC,CAChE,CAAC;AAAA;AAAA,wBAEeD,CAAK;AAAA,UACnB17B,EAAQ,IAAI,CAACg7B,EAAK16B,IAAQd;AAAAA,0BACV,OAAOc,CAAG,CAAC,IAAI,OAAO06B,CAAG,CAAC;AAAA,SAC3C,CAAC;AAAA;AAAA;AAAA,GAIV,CAEA,SAASC,GAAa72B,EASH,CACjB,KAAM,CAAE,OAAA00B,EAAQ,MAAA/7B,EAAO,KAAAwB,EAAM,MAAA26B,EAAO,YAAAW,EAAa,SAAAC,EAAU,QAAAC,GAAY31B,EACrDA,EAAO,UACzB,MAAMi1B,EAAOJ,GAAY16B,EAAM26B,CAAK,EAC9Bp3B,EAAQu3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOh7B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnE27B,EAAOb,GAAM,MAAQP,EAAO,YAE5Bx3B,EAAWvE,GAAS+7B,EAAO,QAC3Bv2B,EAAMjB,GAAY,OAAOA,GAAa,UAAY,CAAC,MAAM,QAAQA,CAAQ,EAC1EA,EACD,CAAA,EACEs1B,EAAQkC,EAAO,YAAc,CAAA,EAI7B8C,EAHU,OAAO,QAAQhF,CAAK,EAGb,KAAK,CAACp8B,EAAGM,IAAM,CACpC,MAAM+gC,EAAS5C,GAAY,CAAC,GAAG16B,EAAM/D,EAAE,CAAC,CAAC,EAAG0+B,CAAK,GAAG,OAAS,EACvD4C,EAAS7C,GAAY,CAAC,GAAG16B,EAAMzD,EAAE,CAAC,CAAC,EAAGo+B,CAAK,GAAG,OAAS,EAC7D,OAAI2C,IAAWC,EAAeD,EAASC,EAChCthC,EAAE,CAAC,EAAE,cAAcM,EAAE,CAAC,CAAC,CAChC,CAAC,EAEKihC,EAAW,IAAI,IAAI,OAAO,KAAKnF,CAAK,CAAC,EACrCoF,EAAalD,EAAO,qBACpBmD,EAAa,EAAQD,GAAe,OAAOA,GAAe,SAGhE,OAAIz9B,EAAK,SAAW,EACXiB;AAAAA;AAAAA,UAEDo8B,EAAO,IAAI,CAAC,CAACM,EAAS3S,CAAI,IAC1BqQ,GAAW,CACT,OAAQrQ,EACR,MAAOhnB,EAAI25B,CAAO,EAClB,KAAM,CAAC,GAAG39B,EAAM29B,CAAO,EACvB,MAAAhD,EACA,YAAAW,EACA,SAAAC,EACA,QAAAC,CAAA,CACD,CAAA,CACF;AAAA,UACCkC,EAAaE,GAAe,CAC5B,OAAQH,EACR,MAAOz5B,EACP,KAAAhE,EACA,MAAA26B,EACA,YAAAW,EACA,SAAAC,EACA,aAAciC,EACd,QAAAhC,CAAA,CACD,EAAI/E,CAAO;AAAA;AAAA,MAMXx1B;AAAAA;AAAAA;AAAAA,0CAGiCsC,CAAK;AAAA,4CACHvC,GAAM,WAAW;AAAA;AAAA,QAErD26B,EAAO16B,kCAAqC06B,CAAI,SAAWlF,CAAO;AAAA;AAAA,UAEhE4G,EAAO,IAAI,CAAC,CAACM,EAAS3S,CAAI,IAC1BqQ,GAAW,CACT,OAAQrQ,EACR,MAAOhnB,EAAI25B,CAAO,EAClB,KAAM,CAAC,GAAG39B,EAAM29B,CAAO,EACvB,MAAAhD,EACA,YAAAW,EACA,SAAAC,EACA,QAAAC,CAAA,CACD,CAAA,CACF;AAAA,UACCkC,EAAaE,GAAe,CAC5B,OAAQH,EACR,MAAOz5B,EACP,KAAAhE,EACA,MAAA26B,EACA,YAAAW,EACA,SAAAC,EACA,aAAciC,EACd,QAAAhC,CAAA,CACD,EAAI/E,CAAO;AAAA;AAAA;AAAA,GAIpB,CAEA,SAASkG,GAAY92B,EASF,CACjB,KAAM,CAAE,OAAA00B,EAAQ,MAAA/7B,EAAO,KAAAwB,EAAM,MAAA26B,EAAO,YAAAW,EAAa,SAAAC,EAAU,QAAAC,GAAY31B,EACjE41B,EAAY51B,EAAO,WAAa,GAChCi1B,EAAOJ,GAAY16B,EAAM26B,CAAK,EAC9Bp3B,EAAQu3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOh7B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnE27B,EAAOb,GAAM,MAAQP,EAAO,YAE5BsD,EAAc,MAAM,QAAQtD,EAAO,KAAK,EAAIA,EAAO,MAAM,CAAC,EAAIA,EAAO,MAC3E,GAAI,CAACsD,EACH,OAAO58B;AAAAA;AAAAA,wCAE6BsC,CAAK;AAAA;AAAA;AAAA,MAM3C,MAAMu6B,EAAM,MAAM,QAAQt/B,CAAK,EAAIA,EAAQ,MAAM,QAAQ+7B,EAAO,OAAO,EAAIA,EAAO,QAAU,CAAA,EAE5F,OAAOt5B;AAAAA;AAAAA;AAAAA,UAGCw6B,EAAYx6B,mCAAsCsC,CAAK,UAAYkzB,CAAO;AAAA,yCAC3CqH,EAAI,MAAM,QAAQA,EAAI,SAAW,EAAI,IAAM,EAAE;AAAA;AAAA;AAAA;AAAA,sBAIhEvC,CAAQ;AAAA,mBACX,IAAM,CACb,MAAMj8B,EAAO,CAAC,GAAGw+B,EAAKtD,GAAaqD,CAAW,CAAC,EAC/CrC,EAAQx7B,EAAMV,CAAI,CACpB,CAAC;AAAA;AAAA,8CAEmC0B,GAAM,IAAI;AAAA;AAAA;AAAA;AAAA,QAIhD26B,EAAO16B,iCAAoC06B,CAAI,SAAWlF,CAAO;AAAA;AAAA,QAEjEqH,EAAI,SAAW,EAAI78B;AAAAA;AAAAA;AAAAA;AAAAA,QAIjBA;AAAAA;AAAAA,YAEE68B,EAAI,IAAI,CAACj6B,EAAM9B,IAAQd;AAAAA;AAAAA;AAAAA,uDAGoBc,EAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,8BAKhCw5B,CAAQ;AAAA,2BACX,IAAM,CACb,MAAMj8B,EAAO,CAAC,GAAGw+B,CAAG,EACpBx+B,EAAK,OAAOyC,EAAK,CAAC,EAClBy5B,EAAQx7B,EAAMV,CAAI,CACpB,CAAC;AAAA;AAAA,oBAEC0B,GAAM,KAAK;AAAA;AAAA;AAAA;AAAA,kBAIbq6B,GAAW,CACX,OAAQwC,EACR,MAAOh6B,EACP,KAAM,CAAC,GAAG7D,EAAM+B,CAAG,EACnB,MAAA44B,EACA,YAAAW,EACA,SAAAC,EACA,UAAW,GACX,QAAAC,CAAA,CACD,CAAC;AAAA;AAAA;AAAA,WAGP,CAAC;AAAA;AAAA,OAEL;AAAA;AAAA,GAGP,CAEA,SAASoC,GAAe/3B,EASL,CACjB,KAAM,CAAE,OAAA00B,EAAQ,MAAA/7B,EAAO,KAAAwB,EAAM,MAAA26B,EAAO,YAAAW,EAAa,SAAAC,EAAU,aAAAwC,EAAc,QAAAvC,CAAA,EAAY31B,EAC/Em4B,EAAY7C,GAAYZ,CAAM,EAC9BjtB,EAAU,OAAO,QAAQ9O,GAAS,CAAA,CAAE,EAAE,OAAO,CAAC,CAACyH,CAAG,IAAM,CAAC83B,EAAa,IAAI93B,CAAG,CAAC,EAEpF,OAAOhF;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,sBAOas6B,CAAQ;AAAA,mBACX,IAAM,CACb,MAAMj8B,EAAO,CAAE,GAAId,GAAS,EAAC,EAC7B,IAAIykB,EAAQ,EACRhd,EAAM,UAAUgd,CAAK,GACzB,KAAOhd,KAAO3G,GACZ2jB,GAAS,EACThd,EAAM,UAAUgd,CAAK,GAEvB3jB,EAAK2G,CAAG,EAAI+3B,EAAY,CAAA,EAAKxD,GAAaD,CAAM,EAChDiB,EAAQx7B,EAAMV,CAAI,CACpB,CAAC;AAAA;AAAA,4CAEiC0B,GAAM,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,QAK9CsM,EAAQ,SAAW,EAAIrM;AAAAA;AAAAA,QAErBA;AAAAA;AAAAA,YAEEqM,EAAQ,IAAI,CAAC,CAACrH,EAAKg4B,CAAU,IAAM,CACnC,MAAMC,EAAY,CAAC,GAAGl+B,EAAMiG,CAAG,EACzBlD,EAAWq4B,GAAU6C,CAAU,EACrC,OAAOh9B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,6BAOUgF,CAAG;AAAA,gCACAs1B,CAAQ;AAAA,8BACThgC,GAAa,CACtB,MAAMqO,EAAWrO,EAAE,OAA4B,MAAM,KAAA,EACrD,GAAI,CAACqO,GAAWA,IAAY3D,EAAK,OACjC,MAAM3G,EAAO,CAAE,GAAId,GAAS,EAAC,EACzBoL,KAAWtK,IACfA,EAAKsK,CAAO,EAAItK,EAAK2G,CAAG,EACxB,OAAO3G,EAAK2G,CAAG,EACfu1B,EAAQx7B,EAAMV,CAAI,EACpB,CAAC;AAAA;AAAA;AAAA;AAAA,oBAID0+B,EACE/8B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,mCAKa8B,CAAQ;AAAA,sCACLw4B,CAAQ;AAAA,oCACThgC,GAAa,CACtB,MAAM8M,EAAS9M,EAAE,OACX4D,EAAMkJ,EAAO,MAAM,KAAA,EACzB,GAAI,CAAClJ,EAAK,CACRq8B,EAAQ0C,EAAW,MAAS,EAC5B,MACF,CACA,GAAI,CACF1C,EAAQ0C,EAAW,KAAK,MAAM/+B,CAAG,CAAC,CACpC,MAAQ,CACNkJ,EAAO,MAAQtF,CACjB,CACF,CAAC;AAAA;AAAA,wBAGLs4B,GAAW,CACT,OAAAd,EACA,MAAO0D,EACP,KAAMC,EACN,MAAAvD,EACA,YAAAW,EACA,SAAAC,EACA,UAAW,GACX,QAAAC,CAAA,CACD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAMMD,CAAQ;AAAA,2BACX,IAAM,CACb,MAAMj8B,EAAO,CAAE,GAAId,GAAS,EAAC,EAC7B,OAAOc,EAAK2G,CAAG,EACfu1B,EAAQx7B,EAAMV,CAAI,CACpB,CAAC;AAAA;AAAA,oBAEC0B,GAAM,KAAK;AAAA;AAAA;AAAA,aAIrB,CAAC,CAAC;AAAA;AAAA,OAEL;AAAA;AAAA,GAGP,CClpBA,MAAMm9B,GAAe,CACnB,IAAKl9B,+2BACL,OAAQA,8OACR,OAAQA,mZACR,KAAMA,iMACN,SAAUA,uKACV,SAAUA,kOACV,SAAUA,kLACV,MAAOA,mPACP,OAAQA,mNACR,MAAOA,kQACP,QAASA,wRACT,OAAQA,mVAER,KAAMA,gLACN,QAASA,oVACT,QAASA,8TACT,GAAIA,0OACJ,OAAQA,+UACR,SAAUA,6SACV,UAAWA,oUACX,MAAOA,sMACP,QAASA,+QACT,KAAMA,+KACN,IAAKA,wRACL,UAAWA,kLACX,WAAYA,gPACZ,KAAMA,mSACN,QAASA,8VACT,QAASA,gNACX,EAGam9B,GAAuE,CAClF,IAAK,CAAE,MAAO,wBAAyB,YAAa,qDAAA,EACpD,OAAQ,CAAE,MAAO,UAAW,YAAa,0CAAA,EACzC,OAAQ,CAAE,MAAO,SAAU,YAAa,8CAAA,EACxC,KAAM,CAAE,MAAO,iBAAkB,YAAa,sCAAA,EAC9C,SAAU,CAAE,MAAO,WAAY,YAAa,qDAAA,EAC5C,SAAU,CAAE,MAAO,WAAY,YAAa,uCAAA,EAC5C,SAAU,CAAE,MAAO,WAAY,YAAa,uBAAA,EAC5C,MAAO,CAAE,MAAO,QAAS,YAAa,0BAAA,EACtC,OAAQ,CAAE,MAAO,SAAU,YAAa,8BAAA,EACxC,MAAO,CAAE,MAAO,QAAS,YAAa,6CAAA,EACtC,QAAS,CAAE,MAAO,UAAW,YAAa,+CAAA,EAC1C,OAAQ,CAAE,MAAO,eAAgB,YAAa,gCAAA,EAE9C,KAAM,CAAE,MAAO,WAAY,YAAa,0CAAA,EACxC,QAAS,CAAE,MAAO,UAAW,YAAa,qCAAA,EAC1C,QAAS,CAAE,MAAO,UAAW,YAAa,6BAAA,EAC1C,GAAI,CAAE,MAAO,KAAM,YAAa,4BAAA,EAChC,OAAQ,CAAE,MAAO,SAAU,YAAa,uCAAA,EACxC,SAAU,CAAE,MAAO,WAAY,YAAa,4BAAA,EAC5C,UAAW,CAAE,MAAO,YAAa,YAAa,qCAAA,EAC9C,MAAO,CAAE,MAAO,QAAS,YAAa,6BAAA,EACtC,QAAS,CAAE,MAAO,UAAW,YAAa,oCAAA,EAC1C,KAAM,CAAE,MAAO,OAAQ,YAAa,gCAAA,EACpC,IAAK,CAAE,MAAO,MAAO,YAAa,6BAAA,EAClC,UAAW,CAAE,MAAO,YAAa,YAAa,kCAAA,EAC9C,WAAY,CAAE,MAAO,cAAe,YAAa,8BAAA,EACjD,KAAM,CAAE,MAAO,OAAQ,YAAa,2BAAA,EACpC,QAAS,CAAE,MAAO,UAAW,YAAa,kCAAA,CAC5C,EAEA,SAASC,GAAep4B,EAAa,CACnC,OAAOk4B,GAAal4B,CAAgC,GAAKk4B,GAAa,OACxE,CAEA,SAASG,GAAcr4B,EAAas0B,EAAoBgE,EAAwB,CAC9E,GAAI,CAACA,EAAO,MAAO,GACnB,MAAMluB,EAAIkuB,EAAM,YAAA,EACVzxB,EAAOsxB,GAAan4B,CAAG,EAM7B,OAHIA,EAAI,YAAA,EAAc,SAASoK,CAAC,GAG5BvD,IACEA,EAAK,MAAM,YAAA,EAAc,SAASuD,CAAC,GACnCvD,EAAK,YAAY,YAAA,EAAc,SAASuD,CAAC,GAAU,GAGlDmuB,GAAcjE,EAAQlqB,CAAC,CAChC,CAEA,SAASmuB,GAAcjE,EAAoBgE,EAAwB,CAGjE,GAFIhE,EAAO,OAAO,YAAA,EAAc,SAASgE,CAAK,GAC1ChE,EAAO,aAAa,YAAA,EAAc,SAASgE,CAAK,GAChDhE,EAAO,MAAM,KAAM/7B,GAAU,OAAOA,CAAK,EAAE,YAAA,EAAc,SAAS+/B,CAAK,CAAC,EAAG,MAAO,GAEtF,GAAIhE,EAAO,YACT,SAAW,CAACoD,EAASc,CAAU,IAAK,OAAO,QAAQlE,EAAO,UAAU,EAElE,GADIoD,EAAQ,YAAA,EAAc,SAASY,CAAK,GACpCC,GAAcC,EAAYF,CAAK,EAAG,MAAO,GAIjD,GAAIhE,EAAO,MAAO,CAChB,MAAMR,EAAQ,MAAM,QAAQQ,EAAO,KAAK,EAAIA,EAAO,MAAQ,CAACA,EAAO,KAAK,EACxE,UAAW12B,KAAQk2B,EACjB,GAAIl2B,GAAQ26B,GAAc36B,EAAM06B,CAAK,EAAG,MAAO,EAEnD,CAEA,GAAIhE,EAAO,sBAAwB,OAAOA,EAAO,sBAAyB,UACpEiE,GAAcjE,EAAO,qBAAsBgE,CAAK,EAAG,MAAO,GAGhE,MAAMG,EAASnE,EAAO,OAASA,EAAO,OAASA,EAAO,MACtD,GAAImE,GACF,UAAWj4B,KAASi4B,EAClB,GAAIj4B,GAAS+3B,GAAc/3B,EAAO83B,CAAK,EAAG,MAAO,GAIrD,MAAO,EACT,CAEO,SAASI,GAAiBtG,EAAwB,CACvD,GAAI,CAACA,EAAM,OACT,OAAOp3B,gDAET,MAAMs5B,EAASlC,EAAM,OACf75B,EAAQ65B,EAAM,OAAS,CAAA,EAC7B,GAAIiC,GAAWC,CAAM,IAAM,UAAY,CAACA,EAAO,WAC7C,OAAOt5B,kEAET,MAAMq6B,EAAc,IAAI,IAAIjD,EAAM,kBAAoB,CAAA,CAAE,EAClDuG,EAAarE,EAAO,WACpBsE,EAAcxG,EAAM,aAAe,GACnCyG,EAAgBzG,EAAM,cACtB0G,EAAmB1G,EAAM,kBAAoB,KAS7C2G,EAPU,OAAO,QAAQJ,CAAU,EAAE,KAAK,CAAC3iC,EAAGM,IAAM,CACxD,MAAM+gC,EAAS5C,GAAY,CAACz+B,EAAE,CAAC,CAAC,EAAGo8B,EAAM,OAAO,GAAG,OAAS,GACtDkF,EAAS7C,GAAY,CAACn+B,EAAE,CAAC,CAAC,EAAG87B,EAAM,OAAO,GAAG,OAAS,GAC5D,OAAIiF,IAAWC,EAAeD,EAASC,EAChCthC,EAAE,CAAC,EAAE,cAAcM,EAAE,CAAC,CAAC,CAChC,CAAC,EAE+B,OAAO,CAAC,CAAC0J,EAAK+kB,CAAI,IAC5C,EAAA8T,GAAiB74B,IAAQ64B,GACzBD,GAAe,CAACP,GAAcr4B,EAAK+kB,EAAM6T,CAAW,EAEzD,EAED,IAAII,EAEO,KACX,GAAIH,GAAiBC,GAAoBC,EAAgB,SAAW,EAAG,CACrE,MAAME,EAAgBF,EAAgB,CAAC,IAAI,CAAC,EAE1CE,GACA5E,GAAW4E,CAAa,IAAM,UAC9BA,EAAc,YACdA,EAAc,WAAWH,CAAgB,IAEzCE,EAAoB,CAClB,WAAYH,EACZ,cAAeC,EACf,OAAQG,EAAc,WAAWH,CAAgB,CAAA,EAGvD,CAEA,OAAIC,EAAgB,SAAW,EACtB/9B;AAAAA;AAAAA,0CAE+BD,EAAM,MAAM;AAAA;AAAA,YAE1C69B,EACE,sBAAsBA,CAAW,IACjC,6BAA6B;AAAA;AAAA;AAAA,MAMlC59B;AAAAA;AAAAA,QAEDg+B,GACG,IAAM,CACL,KAAM,CAAE,WAAAE,EAAY,cAAAC,EAAe,OAAQpU,GAASiU,EAC9CnE,EAAOJ,GAAY,CAACyE,EAAYC,CAAa,EAAG/G,EAAM,OAAO,EAC7D90B,EAAQu3B,GAAM,OAAS9P,EAAK,OAASgQ,GAASoE,CAAa,EAC3DC,EAAcvE,GAAM,MAAQ9P,EAAK,aAAe,GAChDsU,EAAgB9gC,EAAkC2gC,CAAU,EAC5DI,EACJD,GAAgB,OAAOA,GAAiB,SACnCA,EAAyCF,CAAa,EACvD,OACAj4B,EAAK,kBAAkBg4B,CAAU,IAAIC,CAAa,GACxD,OAAOn+B;AAAAA,wDACqCkG,CAAE;AAAA;AAAA,4DAEEk3B,GAAec,CAAU,CAAC;AAAA;AAAA,6DAEzB57B,CAAK;AAAA,sBAC5C87B,EACEp+B,yCAA4Co+B,CAAW,OACvD5I,CAAO;AAAA;AAAA;AAAA;AAAA,oBAIX4E,GAAW,CACX,OAAQrQ,EACR,MAAOuU,EACP,KAAM,CAACJ,EAAYC,CAAa,EAChC,MAAO/G,EAAM,QACb,YAAAiD,EACA,SAAUjD,EAAM,UAAY,GAC5B,UAAW,GACX,QAASA,EAAM,OAAA,CAChB,CAAC;AAAA;AAAA;AAAA,aAIV,GAAA,EACA2G,EAAgB,IAAI,CAAC,CAAC/4B,EAAK+kB,CAAI,IAAM,CACnC,MAAMle,EAAOsxB,GAAan4B,CAAG,GAAK,CAChC,MAAOA,EAAI,OAAO,CAAC,EAAE,cAAgBA,EAAI,MAAM,CAAC,EAChD,YAAa+kB,EAAK,aAAe,EAAA,EAGnC,OAAO/pB;AAAAA,wEACqDgF,CAAG;AAAA;AAAA,4DAEfo4B,GAAep4B,CAAG,CAAC;AAAA;AAAA,6DAElB6G,EAAK,KAAK;AAAA,sBACjDA,EAAK,YACH7L,yCAA4C6L,EAAK,WAAW,OAC5D2pB,CAAO;AAAA;AAAA;AAAA;AAAA,oBAIX4E,GAAW,CACX,OAAQrQ,EACR,MAAQxsB,EAAkCyH,CAAG,EAC7C,KAAM,CAACA,CAAG,EACV,MAAOoyB,EAAM,QACb,YAAAiD,EACA,SAAUjD,EAAM,UAAY,GAC5B,UAAW,GACX,QAASA,EAAM,OAAA,CAChB,CAAC;AAAA;AAAA;AAAA,aAIV,CAAC,CAAC;AAAA;AAAA,GAGZ,CC7QA,MAAM6C,OAAgB,IAAI,CAAC,QAAS,cAAe,UAAW,UAAU,CAAC,EAEzE,SAASC,GAAYZ,EAA6B,CAEhD,OADa,OAAO,KAAKA,GAAU,CAAA,CAAE,EAAE,OAAQt0B,GAAQ,CAACi1B,GAAU,IAAIj1B,CAAG,CAAC,EAC9D,SAAW,CACzB,CAEA,SAASu5B,GAAc98B,EAAiE,CACtF,MAAM+8B,EAAW/8B,EAAO,OAAQlE,GAAUA,GAAS,IAAI,EACjDkhC,EAAWD,EAAS,SAAW/8B,EAAO,OACtCi9B,EAAwB,CAAA,EAC9B,UAAWnhC,KAASihC,EACbE,EAAW,KAAMxmB,GAAa,OAAO,GAAGA,EAAU3a,CAAK,CAAC,GAC3DmhC,EAAW,KAAKnhC,CAAK,EAGzB,MAAO,CAAE,WAAAmhC,EAAY,SAAAD,CAAA,CACvB,CAEO,SAASE,GAAoBzgC,EAAoC,CACtE,MAAI,CAACA,GAAO,OAAOA,GAAQ,SAClB,CAAE,OAAQ,KAAM,iBAAkB,CAAC,QAAQ,CAAA,EAE7C0gC,GAAoB1gC,EAAmB,EAAE,CAClD,CAEA,SAAS0gC,GACPtF,EACAv6B,EACsB,CACtB,MAAMs7B,MAAkB,IAClBj7B,EAAyB,CAAE,GAAGk6B,CAAA,EAC9BuF,EAAYrF,GAAQz6B,CAAI,GAAK,SAEnC,GAAIu6B,EAAO,OAASA,EAAO,OAASA,EAAO,MAAO,CAChD,MAAMwF,EAAQC,GAAezF,EAAQv6B,CAAI,EACzC,OAAI+/B,GACG,CAAE,OAAAxF,EAAQ,iBAAkB,CAACuF,CAAS,CAAA,CAC/C,CAEA,MAAMJ,EAAW,MAAM,QAAQnF,EAAO,IAAI,GAAKA,EAAO,KAAK,SAAS,MAAM,EACpEmB,EACJpB,GAAWC,CAAM,IAChBA,EAAO,YAAcA,EAAO,qBAAuB,SAAW,QAIjE,GAHAl6B,EAAW,KAAOq7B,GAAQnB,EAAO,KACjCl6B,EAAW,SAAWq/B,GAAYnF,EAAO,SAErCl6B,EAAW,KAAM,CACnB,KAAM,CAAE,WAAAs/B,EAAY,SAAUM,GAAiBT,GAAcn/B,EAAW,IAAI,EAC5EA,EAAW,KAAOs/B,EACdM,MAAyB,SAAW,IACpCN,EAAW,SAAW,GAAGrE,EAAY,IAAIwE,CAAS,CACxD,CAEA,GAAIpE,IAAS,SAAU,CACrB,MAAMkD,EAAarE,EAAO,YAAc,CAAA,EAClC2F,EAA8C,CAAA,EACpD,SAAW,CAACj6B,EAAKzH,CAAK,IAAK,OAAO,QAAQogC,CAAU,EAAG,CACrD,MAAM15B,EAAM26B,GAAoBrhC,EAAO,CAAC,GAAGwB,EAAMiG,CAAG,CAAC,EACjDf,EAAI,SAAQg7B,EAAgBj6B,CAAG,EAAIf,EAAI,QAC3C,UAAWuB,KAASvB,EAAI,iBAAkBo2B,EAAY,IAAI70B,CAAK,CACjE,CAGA,GAFApG,EAAW,WAAa6/B,EAEpB3F,EAAO,uBAAyB,GAClCe,EAAY,IAAIwE,CAAS,UAChBvF,EAAO,uBAAyB,GACzCl6B,EAAW,qBAAuB,WAElCk6B,EAAO,sBACP,OAAOA,EAAO,sBAAyB,UAEnC,CAACY,GAAYZ,EAAO,oBAAkC,EAAG,CAC3D,MAAMr1B,EAAM26B,GACVtF,EAAO,qBACP,CAAC,GAAGv6B,EAAM,GAAG,CAAA,EAEfK,EAAW,qBACT6E,EAAI,QAAWq1B,EAAO,qBACpBr1B,EAAI,iBAAiB,OAAS,GAAGo2B,EAAY,IAAIwE,CAAS,CAChE,CAEJ,SAAWpE,IAAS,QAAS,CAC3B,MAAMmC,EAAc,MAAM,QAAQtD,EAAO,KAAK,EAC1CA,EAAO,MAAM,CAAC,EACdA,EAAO,MACX,GAAI,CAACsD,EACHvC,EAAY,IAAIwE,CAAS,MACpB,CACL,MAAM56B,EAAM26B,GAAoBhC,EAAa,CAAC,GAAG79B,EAAM,GAAG,CAAC,EAC3DK,EAAW,MAAQ6E,EAAI,QAAU24B,EAC7B34B,EAAI,iBAAiB,OAAS,GAAGo2B,EAAY,IAAIwE,CAAS,CAChE,CACF,MACEpE,IAAS,UACTA,IAAS,UACTA,IAAS,WACTA,IAAS,WACT,CAACr7B,EAAW,MAEZi7B,EAAY,IAAIwE,CAAS,EAG3B,MAAO,CACL,OAAQz/B,EACR,iBAAkB,MAAM,KAAKi7B,CAAW,CAAA,CAE5C,CAEA,SAAS0E,GACPzF,EACAv6B,EAC6B,CAC7B,GAAIu6B,EAAO,MAAO,OAAO,KACzB,MAAMwF,EAAQxF,EAAO,OAASA,EAAO,MACrC,GAAI,CAACwF,EAAO,OAAO,KAEnB,MAAMjE,EAAsB,CAAA,EACtBqE,EAA0B,CAAA,EAChC,IAAIT,EAAW,GAEf,UAAWj5B,KAASs5B,EAAO,CACzB,GAAI,CAACt5B,GAAS,OAAOA,GAAU,SAAU,OAAO,KAChD,GAAI,MAAM,QAAQA,EAAM,IAAI,EAAG,CAC7B,KAAM,CAAE,WAAAk5B,EAAY,SAAUM,GAAiBT,GAAc/4B,EAAM,IAAI,EACvEq1B,EAAS,KAAK,GAAG6D,CAAU,EACvBM,IAAcP,EAAW,IAC7B,QACF,CACA,GAAI,UAAWj5B,EAAO,CACpB,GAAIA,EAAM,OAAS,KAAM,CACvBi5B,EAAW,GACX,QACF,CACA5D,EAAS,KAAKr1B,EAAM,KAAK,EACzB,QACF,CACA,GAAI6zB,GAAW7zB,CAAK,IAAM,OAAQ,CAChCi5B,EAAW,GACX,QACF,CACAS,EAAU,KAAK15B,CAAK,CACtB,CAEA,GAAIq1B,EAAS,OAAS,GAAKqE,EAAU,SAAW,EAAG,CACjD,MAAMC,EAAoB,CAAA,EAC1B,UAAW5hC,KAASs9B,EACbsE,EAAO,KAAMjnB,GAAa,OAAO,GAAGA,EAAU3a,CAAK,CAAC,GACvD4hC,EAAO,KAAK5hC,CAAK,EAGrB,MAAO,CACL,OAAQ,CACN,GAAG+7B,EACH,KAAM6F,EACN,SAAAV,EACA,MAAO,OACP,MAAO,OACP,MAAO,MAAA,EAET,iBAAkB,CAAA,CAAC,CAEvB,CAEA,GAAIS,EAAU,SAAW,EAAG,CAC1B,MAAMj7B,EAAM26B,GAAoBM,EAAU,CAAC,EAAGngC,CAAI,EAClD,OAAIkF,EAAI,SACNA,EAAI,OAAO,SAAWw6B,GAAYx6B,EAAI,OAAO,UAExCA,CACT,CAEA,MAAMi3B,EAAiB,CAAC,SAAU,SAAU,UAAW,SAAS,EAChE,OACEgE,EAAU,OAAS,GACnBrE,EAAS,SAAW,GACpBqE,EAAU,MAAO15B,GAAUA,EAAM,MAAQ01B,EAAe,SAAS,OAAO11B,EAAM,IAAI,CAAC,CAAC,EAE7E,CACL,OAAQ,CACN,GAAG8zB,EACH,SAAAmF,CAAA,EAEF,iBAAkB,CAAA,CAAC,EAIhB,IACT,CCzJA,MAAMW,GAAe,CACnB,IAAKp/B,kRACL,IAAKA,62BACL,OAAQA,4OACR,OAAQA,iZACR,KAAMA,+LACN,SAAUA,qKACV,SAAUA,gOACV,SAAUA,gLACV,MAAOA,iPACP,OAAQA,iNACR,MAAOA,gQACP,QAASA,sRACT,OAAQA,iVAER,KAAMA,8KACN,QAASA,kVACT,QAASA,4TACT,GAAIA,wOACJ,OAAQA,6UACR,SAAUA,2SACV,UAAWA,kUACX,MAAOA,oMACP,QAASA,6QACT,KAAMA,6KACN,IAAKA,sRACL,UAAWA,gLACX,WAAYA,8OACZ,KAAMA,iSACN,QAASA,4VACT,QAASA,8MACX,EAGMq/B,GAAkD,CACtD,CAAE,IAAK,MAAO,MAAO,aAAA,EACrB,CAAE,IAAK,SAAU,MAAO,SAAA,EACxB,CAAE,IAAK,SAAU,MAAO,QAAA,EACxB,CAAE,IAAK,OAAQ,MAAO,gBAAA,EACtB,CAAE,IAAK,WAAY,MAAO,UAAA,EAC1B,CAAE,IAAK,WAAY,MAAO,UAAA,EAC1B,CAAE,IAAK,WAAY,MAAO,UAAA,EAC1B,CAAE,IAAK,QAAS,MAAO,OAAA,EACvB,CAAE,IAAK,SAAU,MAAO,QAAA,EACxB,CAAE,IAAK,QAAS,MAAO,OAAA,EACvB,CAAE,IAAK,UAAW,MAAO,SAAA,EACzB,CAAE,IAAK,SAAU,MAAO,cAAA,CAC1B,EASMC,GAAiB,UAEvB,SAASlC,GAAep4B,EAAa,CACnC,OAAOo6B,GAAap6B,CAAgC,GAAKo6B,GAAa,OACxE,CAEA,SAASG,GAAmBv6B,EAAas0B,EAGvC,CACA,MAAMztB,EAAOsxB,GAAan4B,CAAG,EAC7B,OAAI6G,GACG,CACL,MAAOytB,GAAQ,OAASS,GAAS/0B,CAAG,EACpC,YAAas0B,GAAQ,aAAe,EAAA,CAExC,CAEA,SAASkG,GAAmB56B,EAIN,CACpB,KAAM,CAAE,IAAAI,EAAK,OAAAs0B,EAAQ,QAAAmG,CAAA,EAAY76B,EACjC,GAAI,CAAC00B,GAAUD,GAAWC,CAAM,IAAM,UAAY,CAACA,EAAO,WAAY,MAAO,CAAA,EAC7E,MAAMjtB,EAAU,OAAO,QAAQitB,EAAO,UAAU,EAAE,IAAI,CAAC,CAACoG,EAAQ3V,CAAI,IAAM,CACxE,MAAM8P,EAAOJ,GAAY,CAACz0B,EAAK06B,CAAM,EAAGD,CAAO,EACzCn9B,EAAQu3B,GAAM,OAAS9P,EAAK,OAASgQ,GAAS2F,CAAM,EACpDtB,EAAcvE,GAAM,MAAQ9P,EAAK,aAAe,GAChD4V,EAAQ9F,GAAM,OAAS,GAC7B,MAAO,CAAE,IAAK6F,EAAQ,MAAAp9B,EAAO,YAAA87B,EAAa,MAAAuB,CAAA,CAC5C,CAAC,EACD,OAAAtzB,EAAQ,KAAK,CAAC,EAAG/Q,IAAO,EAAE,QAAUA,EAAE,MAAQ,EAAE,MAAQA,EAAE,MAAQ,EAAE,IAAI,cAAcA,EAAE,GAAG,CAAE,EACtF+Q,CACT,CAEA,SAASuzB,GACPC,EACAn7B,EACqD,CACrD,GAAI,CAACm7B,GAAY,CAACn7B,QAAgB,CAAA,EAClC,MAAMo7B,EAA+D,CAAA,EAErE,SAASC,EAAQC,EAAeC,EAAelhC,EAAc,CAC3D,GAAIihC,IAASC,EAAM,OACnB,GAAI,OAAOD,GAAS,OAAOC,EAAM,CAC/BH,EAAQ,KAAK,CAAE,KAAA/gC,EAAM,KAAMihC,EAAM,GAAIC,EAAM,EAC3C,MACF,CACA,GAAI,OAAOD,GAAS,UAAYA,IAAS,MAAQC,IAAS,KAAM,CAC1DD,IAASC,GACXH,EAAQ,KAAK,CAAE,KAAA/gC,EAAM,KAAMihC,EAAM,GAAIC,EAAM,EAE7C,MACF,CACA,GAAI,MAAM,QAAQD,CAAI,GAAK,MAAM,QAAQC,CAAI,EAAG,CAC1C,KAAK,UAAUD,CAAI,IAAM,KAAK,UAAUC,CAAI,GAC9CH,EAAQ,KAAK,CAAE,KAAA/gC,EAAM,KAAMihC,EAAM,GAAIC,EAAM,EAE7C,MACF,CACA,MAAMC,EAAUF,EACVG,EAAUF,EACVG,EAAU,IAAI,IAAI,CAAC,GAAG,OAAO,KAAKF,CAAO,EAAG,GAAG,OAAO,KAAKC,CAAO,CAAC,CAAC,EAC1E,UAAWn7B,KAAOo7B,EAChBL,EAAQG,EAAQl7B,CAAG,EAAGm7B,EAAQn7B,CAAG,EAAGjG,EAAO,GAAGA,CAAI,IAAIiG,CAAG,GAAKA,CAAG,CAErE,CAEA,OAAA+6B,EAAQF,EAAUn7B,EAAS,EAAE,EACtBo7B,CACT,CAEA,SAASO,GAAc9iC,EAAgB+iC,EAAS,GAAY,CAC1D,IAAIC,EACJ,GAAI,CAEFA,EADa,KAAK,UAAUhjC,CAAK,GACnB,OAAOA,CAAK,CAC5B,MAAQ,CACNgjC,EAAM,OAAOhjC,CAAK,CACpB,CACA,OAAIgjC,EAAI,QAAUD,EAAeC,EAC1BA,EAAI,MAAM,EAAGD,EAAS,CAAC,EAAI,KACpC,CAEO,SAASE,GAAapJ,EAAoB,CAC/C,MAAMqJ,EACJrJ,EAAM,OAAS,KAAO,UAAYA,EAAM,MAAQ,QAAU,UACtDsJ,EAAW/B,GAAoBvH,EAAM,MAAM,EAC3CuJ,EAAaD,EAAS,OACxBA,EAAS,iBAAiB,OAAS,EACnC,GAGEE,EAAcF,EAAS,QAAQ,YAAc,CAAA,EAC7CG,EAAoBxB,GAAS,OAAO9kC,GAAKA,EAAE,OAAOqmC,CAAW,EAG7DE,EAAY,IAAI,IAAIzB,GAAS,IAAI9kC,GAAKA,EAAE,GAAG,CAAC,EAC5CwmC,EAAgB,OAAO,KAAKH,CAAW,EAC1C,OAAOhkC,GAAK,CAACkkC,EAAU,IAAIlkC,CAAC,CAAC,EAC7B,IAAIA,IAAM,CAAE,IAAKA,EAAG,MAAOA,EAAE,OAAO,CAAC,EAAE,YAAA,EAAgBA,EAAE,MAAM,CAAC,GAAI,EAEjEokC,EAAc,CAAC,GAAGH,EAAmB,GAAGE,CAAa,EAErDE,EACJ7J,EAAM,eAAiBsJ,EAAS,QAAUrH,GAAWqH,EAAS,MAAM,IAAM,SACrEA,EAAS,OAAO,aAAatJ,EAAM,aAAa,EACjD,OACA8J,EAAoB9J,EAAM,cAC5BmI,GAAmBnI,EAAM,cAAe6J,CAAmB,EAC3D,KACEE,EAAc/J,EAAM,cACtBoI,GAAmB,CACjB,IAAKpI,EAAM,cACX,OAAQ6J,EACR,QAAS7J,EAAM,OAAA,CAChB,EACD,CAAA,EACEgK,EACJhK,EAAM,WAAa,QACnB,EAAQA,EAAM,eACd+J,EAAY,OAAS,EACjBE,EAAkBjK,EAAM,mBAAqBkI,GAC7CgC,EAAsBlK,EAAM,aAE9BiK,EADA,KAGEjK,EAAM,kBAAqB+J,EAAY,CAAC,GAAG,KAAO,KAGlDhgC,EAAOi2B,EAAM,WAAa,OAC5BwI,GAAYxI,EAAM,cAAeA,EAAM,SAAS,EAChD,CAAA,EACEmK,EAAgBnK,EAAM,WAAa,OAASA,EAAM,MAAQA,EAAM,YAChEoK,EAAapK,EAAM,WAAa,OAASj2B,EAAK,OAAS,EAAIogC,EAI3DE,EACJ,EAAQrK,EAAM,WAAc,CAACA,EAAM,SAAW,EAAQsJ,EAAS,OAC3DgB,EACJtK,EAAM,WACN,CAACA,EAAM,QACPoK,IACCpK,EAAM,WAAa,MAAQ,GAAOqK,GAC/BE,EACJvK,EAAM,WACN,CAACA,EAAM,UACP,CAACA,EAAM,UACPoK,IACCpK,EAAM,WAAa,MAAQ,GAAOqK,GAC/BG,EAAYxK,EAAM,WAAa,CAACA,EAAM,UAAY,CAACA,EAAM,SAE/D,OAAOp3B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,uCAM8BygC,IAAa,QAAU,WAAaA,IAAa,UAAY,eAAiB,EAAE,KAAKA,CAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAa/GrJ,EAAM,WAAW;AAAA,qBAChB98B,GAAa88B,EAAM,eAAgB98B,EAAE,OAA4B,KAAK,CAAC;AAAA;AAAA,YAEjF88B,EAAM,YAAcp3B;AAAAA;AAAAA;AAAAA,uBAGT,IAAMo3B,EAAM,eAAe,EAAE,CAAC;AAAA;AAAA,YAEvC5B,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAMiB4B,EAAM,gBAAkB,KAAO,SAAW,EAAE;AAAA,qBAC7D,IAAMA,EAAM,gBAAgB,IAAI,CAAC;AAAA;AAAA,6CAETgI,GAAa,GAAG;AAAA;AAAA;AAAA,YAGjD4B,EAAY,IAAIa,GAAW7hC;AAAAA;AAAAA,wCAECo3B,EAAM,gBAAkByK,EAAQ,IAAM,SAAW,EAAE;AAAA,uBACpE,IAAMzK,EAAM,gBAAgByK,EAAQ,GAAG,CAAC;AAAA;AAAA,+CAEhBzE,GAAeyE,EAAQ,GAAG,CAAC;AAAA,gDAC1BA,EAAQ,KAAK;AAAA;AAAA,WAElD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+CAOmCzK,EAAM,WAAa,OAAS,SAAW,EAAE;AAAA,0BAC9DA,EAAM,eAAiB,CAACA,EAAM,MAAM;AAAA,uBACvC,IAAMA,EAAM,iBAAiB,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,+CAKZA,EAAM,WAAa,MAAQ,SAAW,EAAE;AAAA,uBAChE,IAAMA,EAAM,iBAAiB,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAa5CoK,EAAaxhC;AAAAA,mDACwBo3B,EAAM,WAAa,MAAQ,kBAAoB,GAAGj2B,EAAK,MAAM,kBAAkBA,EAAK,SAAW,EAAI,IAAM,EAAE,EAAE;AAAA,cAChJnB;AAAAA;AAAAA,aAEH;AAAA;AAAA;AAAA,oDAGuCo3B,EAAM,OAAO,WAAWA,EAAM,QAAQ;AAAA,gBAC1EA,EAAM,QAAU,WAAa,QAAQ;AAAA;AAAA;AAAA;AAAA,0BAI3B,CAACsK,CAAO;AAAA,uBACXtK,EAAM,MAAM;AAAA;AAAA,gBAEnBA,EAAM,OAAS,UAAY,MAAM;AAAA;AAAA;AAAA;AAAA,0BAIvB,CAACuK,CAAQ;AAAA,uBACZvK,EAAM,OAAO;AAAA;AAAA,gBAEpBA,EAAM,SAAW,YAAc,OAAO;AAAA;AAAA;AAAA;AAAA,0BAI5B,CAACwK,CAAS;AAAA,uBACbxK,EAAM,QAAQ;AAAA;AAAA,gBAErBA,EAAM,SAAW,YAAc,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAM7CoK,GAAcpK,EAAM,WAAa,OAASp3B;AAAAA;AAAAA;AAAAA,2BAGzBmB,EAAK,MAAM,kBAAkBA,EAAK,SAAW,EAAI,IAAM,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAMpEA,EAAK,IAAI2gC,GAAU9hC;AAAAA;AAAAA,mDAEgB8hC,EAAO,IAAI;AAAA;AAAA,sDAERzB,GAAcyB,EAAO,IAAI,CAAC;AAAA;AAAA,oDAE5BzB,GAAcyB,EAAO,EAAE,CAAC;AAAA;AAAA;AAAA,eAG7D,CAAC;AAAA;AAAA;AAAA,UAGJtM,CAAO;AAAA;AAAA,UAET0L,GAAqB9J,EAAM,WAAa,OACtCp3B;AAAAA;AAAAA,yDAE6Co9B,GAAehG,EAAM,eAAiB,EAAE,CAAC;AAAA;AAAA,4DAEtC8J,EAAkB,KAAK;AAAA,oBAC/DA,EAAkB,YAChBlhC,2CAA8CkhC,EAAkB,WAAW,SAC3E1L,CAAO;AAAA;AAAA;AAAA,cAIjBA,CAAO;AAAA;AAAA,UAET4L,EACEphC;AAAAA;AAAAA;AAAAA,+CAGmCshC,IAAwB,KAAO,SAAW,EAAE;AAAA,2BAChE,IAAMlK,EAAM,mBAAmBkI,EAAc,CAAC;AAAA;AAAA;AAAA;AAAA,kBAIvD6B,EAAY,IACX37B,GAAUxF;AAAAA;AAAAA,mDAGLshC,IAAwB97B,EAAM,IAAM,SAAW,EACjD;AAAA,8BACQA,EAAM,aAAeA,EAAM,KAAK;AAAA,+BAC/B,IAAM4xB,EAAM,mBAAmB5xB,EAAM,GAAG,CAAC;AAAA;AAAA,wBAEhDA,EAAM,KAAK;AAAA;AAAA,mBAAA,CAGlB;AAAA;AAAA,cAGLgwB,CAAO;AAAA;AAAA;AAAA;AAAA,YAIP4B,EAAM,WAAa,OACjBp3B;AAAAA,kBACIo3B,EAAM,cACJp3B;AAAAA;AAAAA;AAAAA,4BAIA09B,GAAiB,CACf,OAAQgD,EAAS,OACjB,QAAStJ,EAAM,QACf,MAAOA,EAAM,UACb,SAAUA,EAAM,SAAW,CAACA,EAAM,UAClC,iBAAkBsJ,EAAS,iBAC3B,QAAStJ,EAAM,YACf,YAAaA,EAAM,YACnB,cAAeA,EAAM,cACrB,iBAAkBkK,CAAA,CACnB,CAAC;AAAA,kBACJX,EACE3gC;AAAAA;AAAAA;AAAAA,4BAIAw1B,CAAO;AAAA,gBAEbx1B;AAAAA;AAAAA;AAAAA;AAAAA,6BAIeo3B,EAAM,GAAG;AAAA,6BACR98B,GACR88B,EAAM,YAAa98B,EAAE,OAA+B,KAAK,CAAC;AAAA;AAAA;AAAA,eAGjE;AAAA;AAAA;AAAA,UAGL88B,EAAM,OAAO,OAAS,EACpBp3B;AAAAA,wCAC4B,KAAK,UAAUo3B,EAAM,OAAQ,KAAM,CAAC,CAAC;AAAA,oBAEjE5B,CAAO;AAAA;AAAA;AAAA,GAInB,CCndO,SAASuM,GAAe9gC,EAAoB,CACjD,GAAI,CAACA,GAAMA,IAAO,EAAG,MAAO,MAC5B,MAAMG,EAAM,KAAK,MAAMH,EAAK,GAAI,EAChC,GAAIG,EAAM,GAAI,MAAO,GAAGA,CAAG,IAC3B,MAAMC,EAAM,KAAK,MAAMD,EAAM,EAAE,EAC/B,OAAIC,EAAM,GAAW,GAAGA,CAAG,IAEpB,GADI,KAAK,MAAMA,EAAM,EAAE,CAClB,GACd,CAEO,SAAS2gC,GAAeh9B,EAAiBoyB,EAAsB,CACpE,MAAMluB,EAAWkuB,EAAM,SACjB6K,EAAW/4B,GAAU,SAC3B,GAAI,CAACA,GAAY,CAAC+4B,EAAU,MAAO,GACnC,MAAMC,EAAgBD,EAASj9B,CAAG,EAC5B8X,EAAa,OAAOolB,GAAe,YAAe,WAAaA,EAAc,WAC7EC,EAAU,OAAOD,GAAe,SAAY,WAAaA,EAAc,QACvEE,EAAY,OAAOF,GAAe,WAAc,WAAaA,EAAc,UAE3EG,GADWn5B,EAAS,kBAAkBlE,CAAG,GAAK,CAAA,GACrB,KAC5Bs9B,GAAYA,EAAQ,YAAcA,EAAQ,SAAWA,EAAQ,SAAA,EAEhE,OAAOxlB,GAAcqlB,GAAWC,GAAaC,CAC/C,CAEO,SAASE,GACdv9B,EACAw9B,EACQ,CACR,OAAOA,IAAkBx9B,CAAG,GAAG,QAAU,CAC3C,CAEO,SAASy9B,GACdz9B,EACAw9B,EACA,CACA,MAAME,EAAQH,GAAuBv9B,EAAKw9B,CAAe,EACzD,OAAIE,EAAQ,EAAUlN,EACfx1B,yCAA4C0iC,CAAK,SAC1D,CCxBA,SAASC,GACPrJ,EACAv6B,EACmB,CACnB,IAAI2F,EAAU40B,EACd,UAAWt0B,KAAOjG,EAAM,CACtB,GAAI,CAAC2F,EAAS,OAAO,KACrB,MAAM+1B,EAAOpB,GAAW30B,CAAO,EAC/B,GAAI+1B,IAAS,SAAU,CACrB,MAAMkD,EAAaj5B,EAAQ,YAAc,CAAA,EACzC,GAAI,OAAOM,GAAQ,UAAY24B,EAAW34B,CAAG,EAAG,CAC9CN,EAAUi5B,EAAW34B,CAAG,EACxB,QACF,CACA,MAAMw3B,EAAa93B,EAAQ,qBAC3B,GAAI,OAAOM,GAAQ,UAAYw3B,GAAc,OAAOA,GAAe,SAAU,CAC3E93B,EAAU83B,EACV,QACF,CACA,OAAO,IACT,CACA,GAAI/B,IAAS,QAAS,CACpB,GAAI,OAAOz1B,GAAQ,SAAU,OAAO,KAEpCN,GADc,MAAM,QAAQA,EAAQ,KAAK,EAAIA,EAAQ,MAAM,CAAC,EAAIA,EAAQ,QACrD,KACnB,QACF,CACA,OAAO,IACT,CACA,OAAOA,CACT,CAEA,SAASk+B,GACPC,EACAC,EACyB,CAEzB,MAAMC,GADYF,EAAO,UAAY,CAAA,GACPC,CAAS,EACjChhC,EAAW+gC,EAAOC,CAAS,EAQjC,OANGC,GAAgB,OAAOA,GAAiB,SACpCA,EACD,QACHjhC,GAAY,OAAOA,GAAa,SAC5BA,EACD,OACa,CAAA,CACrB,CAEO,SAASkhC,GAAwB5L,EAA+B,CACrE,MAAMsJ,EAAW/B,GAAoBvH,EAAM,MAAM,EAC3Ch4B,EAAashC,EAAS,OAC5B,GAAI,CAACthC,EACH,OAAOY,kEAET,MAAM+pB,EAAO4Y,GAAkBvjC,EAAY,CAAC,WAAYg4B,EAAM,SAAS,CAAC,EACxE,GAAI,CAACrN,EACH,OAAO/pB,wEAET,MAAMijC,EAAc7L,EAAM,aAAe,CAAA,EACnC75B,EAAQqlC,GAAoBK,EAAa7L,EAAM,SAAS,EAC9D,OAAOp3B;AAAAA;AAAAA,QAEDo6B,GAAW,CACX,OAAQrQ,EACR,MAAAxsB,EACA,KAAM,CAAC,WAAY65B,EAAM,SAAS,EAClC,MAAOA,EAAM,QACb,YAAa,IAAI,IAAIsJ,EAAS,gBAAgB,EAC9C,SAAUtJ,EAAM,SAChB,UAAW,GACX,QAASA,EAAM,OAAA,CAChB,CAAC;AAAA;AAAA,GAGR,CAEO,SAAS8L,GAA2Bt+B,EAGxC,CACD,KAAM,CAAE,UAAAk+B,EAAW,MAAA1L,CAAA,EAAUxyB,EACvB01B,EAAWlD,EAAM,cAAgBA,EAAM,oBAC7C,OAAOp3B;AAAAA;AAAAA,QAEDo3B,EAAM,oBACJp3B,mDACAgjC,GAAwB,CACtB,UAAAF,EACA,YAAa1L,EAAM,WACnB,OAAQA,EAAM,aACd,QAASA,EAAM,cACf,SAAAkD,EACA,QAASlD,EAAM,aAAA,CAChB,CAAC;AAAA;AAAA;AAAA;AAAA,sBAIUkD,GAAY,CAAClD,EAAM,eAAe;AAAA,mBACrC,IAAMA,EAAM,aAAA,CAAc;AAAA;AAAA,YAEjCA,EAAM,aAAe,UAAY,MAAM;AAAA;AAAA;AAAA;AAAA,sBAI7BkD,CAAQ;AAAA,mBACX,IAAMlD,EAAM,eAAA,CAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAO/C,CC9HO,SAAS+L,GAAkBv+B,EAI/B,CACD,KAAM,CAAE,MAAAwyB,EAAO,QAAAgM,EAAS,kBAAAC,CAAA,EAAsBz+B,EAE9C,OAAO5E;AAAAA;AAAAA;AAAAA;AAAAA,QAIDqjC,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKPD,GAAS,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIlCA,GAAS,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAI/BA,GAAS,YAAcliC,EAAUkiC,EAAQ,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,kBAI7DA,GAAS,YAAcliC,EAAUkiC,EAAQ,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,QAIvEA,GAAS,UACPpjC;AAAAA,cACIojC,EAAQ,SAAS;AAAA,kBAErB5N,CAAO;AAAA;AAAA,QAET4N,GAAS,MACPpjC;AAAAA,oBACUojC,EAAQ,MAAM,GAAK,KAAO,QAAQ;AAAA,cACxCA,EAAQ,MAAM,QAAU,EAAE,IAAIA,EAAQ,MAAM,OAAS,EAAE;AAAA,kBAE3D5N,CAAO;AAAA;AAAA,QAET0N,GAA2B,CAAE,UAAW,UAAW,MAAA9L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG9B,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CCtDO,SAASkM,GAAqB1+B,EAIlC,CACD,KAAM,CAAE,MAAAwyB,EAAO,WAAAmM,EAAY,kBAAAF,CAAA,EAAsBz+B,EAEjD,OAAO5E;AAAAA;AAAAA;AAAAA;AAAAA,QAIDqjC,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKPE,EAAcA,EAAW,WAAa,MAAQ,KAAQ,KAAK;AAAA;AAAA;AAAA;AAAA,kBAI3DA,EAAcA,EAAW,QAAU,MAAQ,KAAQ,KAAK;AAAA;AAAA;AAAA;AAAA,kBAIxDA,GAAY,kBAAoB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,cAKzCA,GAAY,aACV,GAAGA,EAAW,YAAY,GAAGA,EAAW,SAAW,MAAMA,EAAW,QAAQ,GAAK,EAAE,GACnF,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKHA,GAAY,YAAcriC,EAAUqiC,EAAW,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,kBAInEA,GAAY,YAAcriC,EAAUqiC,EAAW,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,QAI7EA,GAAY,UACVvjC;AAAAA,cACIujC,EAAW,SAAS;AAAA,kBAExB/N,CAAO;AAAA;AAAA,QAET+N,GAAY,MACVvjC;AAAAA,oBACUujC,EAAW,MAAM,GAAK,KAAO,QAAQ;AAAA,cAC3CA,EAAW,MAAM,QAAU,EAAE,IAAIA,EAAW,MAAM,OAAS,EAAE;AAAA,kBAEjE/N,CAAO;AAAA;AAAA,QAET0N,GAA2B,CAAE,UAAW,aAAc,MAAA9L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAGjC,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CClEO,SAASoM,GAAmB5+B,EAIhC,CACD,KAAM,CAAE,MAAAwyB,EAAO,SAAAqM,EAAU,kBAAAJ,CAAA,EAAsBz+B,EAE/C,OAAO5E;AAAAA;AAAAA;AAAAA;AAAAA,QAIDqjC,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKPI,GAAU,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAInCA,GAAU,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIhCA,GAAU,YAAcviC,EAAUuiC,EAAS,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,kBAI/DA,GAAU,YAAcviC,EAAUuiC,EAAS,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,QAIzEA,GAAU,UACRzjC;AAAAA,cACIyjC,EAAS,SAAS;AAAA,kBAEtBjO,CAAO;AAAA;AAAA,QAETiO,GAAU,MACRzjC;AAAAA,oBACUyjC,EAAS,MAAM,GAAK,KAAO,QAAQ;AAAA,cACzCA,EAAS,MAAM,OAAS,EAAE;AAAA,kBAE9BjO,CAAO;AAAA;AAAA,QAET0N,GAA2B,CAAE,UAAW,WAAY,MAAA9L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG/B,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CCXA,SAASsM,GAAY1/B,EAAuC,CAC1D,KAAM,CAAE,OAAAvC,EAAQ,SAAAo+B,CAAA,EAAa77B,EAC7B,OACEvC,EAAO,OAASo+B,EAAS,MACzBp+B,EAAO,cAAgBo+B,EAAS,aAChCp+B,EAAO,QAAUo+B,EAAS,OAC1Bp+B,EAAO,UAAYo+B,EAAS,SAC5Bp+B,EAAO,SAAWo+B,EAAS,QAC3Bp+B,EAAO,UAAYo+B,EAAS,SAC5Bp+B,EAAO,QAAUo+B,EAAS,OAC1Bp+B,EAAO,QAAUo+B,EAAS,KAE9B,CAMO,SAAS8D,GAAuB/+B,EAIpB,CACjB,KAAM,CAAE,MAAAZ,EAAO,UAAA4/B,EAAW,UAAAC,CAAA,EAAcj/B,EAClCk/B,EAAUJ,GAAY1/B,CAAK,EAE3B+/B,EAAc,CAClBC,EACA1hC,EACA8J,EAKI,CAAA,IACD,CACH,KAAM,CAAE,KAAAquB,EAAO,OAAQ,YAAAsB,EAAa,UAAAv+B,EAAW,KAAAk9B,GAAStuB,EAClD7O,EAAQyG,EAAM,OAAOggC,CAAK,GAAK,GAC/B1/B,EAAQN,EAAM,YAAYggC,CAAK,EAE/BC,EAAU,iBAAiBD,CAAK,GAEtC,OAAIvJ,IAAS,WACJz6B;AAAAA;AAAAA,wBAEWikC,CAAO;AAAA,cACjB3hC,CAAK;AAAA;AAAA;AAAA,kBAGD2hC,CAAO;AAAA,qBACJ1mC,CAAK;AAAA,0BACAw+B,GAAe,EAAE;AAAA,wBACnBv+B,GAAa,GAAI;AAAA;AAAA;AAAA,qBAGnBlD,GAAkB,CAC1B,MAAM8M,EAAS9M,EAAE,OACjBspC,EAAU,cAAcI,EAAO58B,EAAO,KAAK,CAC7C,CAAC;AAAA,wBACWpD,EAAM,MAAM;AAAA;AAAA,YAExB02B,EAAO16B,6EAAgF06B,CAAI,SAAWlF,CAAO;AAAA,YAC7GlxB,EAAQtE,+EAAkFsE,CAAK,SAAWkxB,CAAO;AAAA;AAAA,QAKlHx1B;AAAAA;AAAAA,sBAEWikC,CAAO;AAAA,YACjB3hC,CAAK;AAAA;AAAA;AAAA,gBAGD2hC,CAAO;AAAA,iBACNxJ,CAAI;AAAA,mBACFl9B,CAAK;AAAA,wBACAw+B,GAAe,EAAE;AAAA,sBACnBv+B,GAAa,GAAG;AAAA;AAAA,mBAElBlD,GAAkB,CAC1B,MAAM8M,EAAS9M,EAAE,OACjBspC,EAAU,cAAcI,EAAO58B,EAAO,KAAK,CAC7C,CAAC;AAAA,sBACWpD,EAAM,MAAM;AAAA;AAAA,UAExB02B,EAAO16B,6EAAgF06B,CAAI,SAAWlF,CAAO;AAAA,UAC7GlxB,EAAQtE,+EAAkFsE,CAAK,SAAWkxB,CAAO;AAAA;AAAA,KAGzH,EAEM0O,EAAuB,IAAM,CACjC,MAAMC,EAAUngC,EAAM,OAAO,QAC7B,OAAKmgC,EAEEnkC;AAAAA;AAAAA;AAAAA,gBAGKmkC,CAAO;AAAA;AAAA;AAAA,mBAGH7pC,GAAa,CACrB,MAAM8pC,EAAM9pC,EAAE,OACd8pC,EAAI,MAAM,QAAU,MACtB,CAAC;AAAA,kBACQ9pC,GAAa,CACpB,MAAM8pC,EAAM9pC,EAAE,OACd8pC,EAAI,MAAM,QAAU,OACtB,CAAC;AAAA;AAAA;AAAA,MAfc5O,CAmBvB,EAEA,OAAOx1B;AAAAA;AAAAA;AAAAA;AAAAA,2EAIkE6jC,CAAS;AAAA;AAAA;AAAA,QAG5E7/B,EAAM,MACJhE,6DAAgEgE,EAAM,KAAK,SAC3EwxB,CAAO;AAAA;AAAA,QAETxxB,EAAM,QACJhE,8DAAiEgE,EAAM,OAAO,SAC9EwxB,CAAO;AAAA;AAAA,QAET0O,GAAsB;AAAA;AAAA,QAEtBH,EAAY,OAAQ,WAAY,CAChC,YAAa,UACb,UAAW,IACX,KAAM,gCAAA,CACP,CAAC;AAAA;AAAA,QAEAA,EAAY,cAAe,eAAgB,CAC3C,YAAa,mBACb,UAAW,IACX,KAAM,wBAAA,CACP,CAAC;AAAA;AAAA,QAEAA,EAAY,QAAS,MAAO,CAC5B,KAAM,WACN,YAAa,gCACb,UAAW,IACX,KAAM,4BAAA,CACP,CAAC;AAAA;AAAA,QAEAA,EAAY,UAAW,aAAc,CACrC,KAAM,MACN,YAAa,iCACb,KAAM,mCAAA,CACP,CAAC;AAAA;AAAA,QAEA//B,EAAM,aACJhE;AAAAA;AAAAA;AAAAA;AAAAA,gBAIM+jC,EAAY,SAAU,aAAc,CACpC,KAAM,MACN,YAAa,iCACb,KAAM,6BAAA,CACP,CAAC;AAAA;AAAA,gBAEAA,EAAY,UAAW,UAAW,CAClC,KAAM,MACN,YAAa,sBACb,KAAM,uBAAA,CACP,CAAC;AAAA;AAAA,gBAEAA,EAAY,QAAS,oBAAqB,CAC1C,YAAa,kBACb,KAAM,8CAAA,CACP,CAAC;AAAA;AAAA,gBAEAA,EAAY,QAAS,oBAAqB,CAC1C,YAAa,kBACb,KAAM,qCAAA,CACP,CAAC;AAAA;AAAA,YAGNvO,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKEoO,EAAU,MAAM;AAAA,sBACb5/B,EAAM,QAAU,CAAC8/B,CAAO;AAAA;AAAA,YAElC9/B,EAAM,OAAS,YAAc,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKtC4/B,EAAU,QAAQ;AAAA,sBACf5/B,EAAM,WAAaA,EAAM,MAAM;AAAA;AAAA,YAEzCA,EAAM,UAAY,eAAiB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKhD4/B,EAAU,gBAAgB;AAAA;AAAA,YAEjC5/B,EAAM,aAAe,gBAAkB,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,mBAK/C4/B,EAAU,QAAQ;AAAA,sBACf5/B,EAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAM1B8/B,EACE9jC;AAAAA;AAAAA,kBAGAw1B,CAAO;AAAA;AAAA,GAGjB,CASO,SAAS6O,GACdC,EACuB,CACvB,MAAM7iC,EAA2B,CAC/B,KAAM6iC,GAAS,MAAQ,GACvB,YAAaA,GAAS,aAAe,GACrC,MAAOA,GAAS,OAAS,GACzB,QAASA,GAAS,SAAW,GAC7B,OAAQA,GAAS,QAAU,GAC3B,QAASA,GAAS,SAAW,GAC7B,MAAOA,GAAS,OAAS,GACzB,MAAOA,GAAS,OAAS,EAAA,EAG3B,MAAO,CACL,OAAA7iC,EACA,SAAU,CAAE,GAAGA,CAAA,EACf,OAAQ,GACR,UAAW,GACX,MAAO,KACP,QAAS,KACT,YAAa,CAAA,EACb,aAAc,GACZ6iC,GAAS,QAAUA,GAAS,SAAWA,GAAS,OAASA,GAAS,MACpE,CAEJ,CCxSA,SAASC,GAAeC,EAA2C,CACjE,OAAKA,EACDA,EAAO,QAAU,GAAWA,EACzB,GAAGA,EAAO,MAAM,EAAG,CAAC,CAAC,MAAMA,EAAO,MAAM,EAAE,CAAC,GAF9B,KAGtB,CAEO,SAASC,GAAgB7/B,EAW7B,CACD,KAAM,CACJ,MAAAwyB,EACA,MAAAsN,EACA,cAAAC,EACA,kBAAAtB,EACA,iBAAAuB,EACA,qBAAAC,EACA,cAAAC,CAAA,EACElgC,EACEmgC,EAAiBJ,EAAc,CAAC,EAChCK,EAAoBN,GAAO,YAAcK,GAAgB,YAAc,GACvEE,EAAiBP,GAAO,SAAWK,GAAgB,SAAW,GAC9DG,EACJR,GAAO,WACNK,GAAuD,UACpDI,EAAqBT,GAAO,aAAeK,GAAgB,aAAe,KAC1EK,EAAmBV,GAAO,WAAaK,GAAgB,WAAa,KACpEM,EAAsBV,EAAc,OAAS,EAC7CW,EAAcV,GAAqB,KAEnCW,EAAqBjD,GAAoC,CAC7D,MAAMxrB,EAAawrB,EAAmC,UAChDgC,EAAWhC,EAAkE,QAC7EkD,EAAclB,GAAS,aAAeA,GAAS,MAAQhC,EAAQ,MAAQA,EAAQ,UAErF,OAAOtiC;AAAAA;AAAAA;AAAAA,4CAGiCwlC,CAAW;AAAA,yCACdlD,EAAQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,oBAKtCA,EAAQ,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,oBAI9BA,EAAQ,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,6CAIRxrB,GAAa,EAAE,KAAKytB,GAAeztB,CAAS,CAAC;AAAA;AAAA;AAAA;AAAA,oBAItEwrB,EAAQ,cAAgBphC,EAAUohC,EAAQ,aAAa,EAAI,KAAK;AAAA;AAAA,YAExEA,EAAQ,UACNtiC;AAAAA,kDACoCsiC,EAAQ,SAAS;AAAA,gBAErD9M,CAAO;AAAA;AAAA;AAAA,KAInB,EAEMiQ,EAAuB,IAAM,CAEjC,GAAIH,GAAeT,EACjB,OAAOlB,GAAuB,CAC5B,MAAOiB,EACP,UAAWC,EACX,UAAWF,EAAc,CAAC,GAAG,WAAa,SAAA,CAC3C,EAGH,MAAML,EACHS,GAUe,SAAWL,GAAO,QAC9B,CAAE,KAAA9mC,EAAM,YAAA4nC,EAAa,MAAAE,EAAO,QAAAvB,EAAS,MAAAwB,EAAA,EAAUrB,GAAW,CAAA,EAC1DsB,GAAoBhoC,GAAQ4nC,GAAeE,GAASvB,GAAWwB,GAErE,OAAO3lC;AAAAA;AAAAA;AAAAA;AAAAA,YAICglC,EACEhlC;AAAAA;AAAAA;AAAAA,2BAGa8kC,CAAa;AAAA;AAAA;AAAA;AAAA;AAAA,gBAM1BtP,CAAO;AAAA;AAAA,UAEXoQ,GACE5lC;AAAAA;AAAAA,kBAEMmkC,EACEnkC;AAAAA;AAAAA;AAAAA,gCAGYmkC,CAAO;AAAA;AAAA;AAAA,mCAGH7pC,IAAa,CACpBA,GAAE,OAA4B,MAAM,QAAU,MACjD,CAAC;AAAA;AAAA;AAAA,sBAIPk7B,CAAO;AAAA,kBACT53B,EAAOoC,8CAAiDpC,CAAI,gBAAkB43B,CAAO;AAAA,kBACrFgQ,EACExlC,sDAAyDwlC,CAAW,gBACpEhQ,CAAO;AAAA,kBACTkQ,EACE1lC,oHAAuH0lC,CAAK,gBAC5HlQ,CAAO;AAAA,kBACTmQ,GAAQ3lC,gDAAmD2lC,EAAK,gBAAkBnQ,CAAO;AAAA;AAAA,cAG/Fx1B;AAAAA;AAAAA;AAAAA;AAAAA,aAIC;AAAA;AAAA,KAGX,EAEA,OAAOA;AAAAA;AAAAA;AAAAA;AAAAA,QAIDqjC,CAAiB;AAAA;AAAA,QAEjBgC,EACErlC;AAAAA;AAAAA,gBAEM2kC,EAAc,IAAKrC,GAAYiD,EAAkBjD,CAAO,CAAC,CAAC;AAAA;AAAA,YAGhEtiC;AAAAA;AAAAA;AAAAA;AAAAA,wBAIcglC,EAAoB,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,wBAIhCC,EAAiB,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,iDAIJC,GAAoB,EAAE;AAAA,qBAClDX,GAAeW,CAAgB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,wBAK7BC,EAAqBjkC,EAAUikC,CAAkB,EAAI,KAAK;AAAA;AAAA;AAAA,WAGvE;AAAA;AAAA,QAEHC,EACEplC,0DAA6DolC,CAAgB,SAC7E5P,CAAO;AAAA;AAAA,QAETiQ,GAAsB;AAAA;AAAA,QAEtBvC,GAA2B,CAAE,UAAW,QAAS,MAAA9L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG5B,IAAMA,EAAM,UAAU,EAAK,CAAC;AAAA;AAAA;AAAA,GAIjE,CCjNO,SAASyO,GAAiBjhC,EAI9B,CACD,KAAM,CAAE,MAAAwyB,EAAO,OAAA0O,EAAQ,kBAAAzC,CAAA,EAAsBz+B,EAE7C,OAAO5E;AAAAA;AAAAA;AAAAA;AAAAA,QAIDqjC,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKPyC,GAAQ,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIjCA,GAAQ,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAI9BA,GAAQ,SAAW,KAAK;AAAA;AAAA;AAAA;AAAA,kBAIxBA,GAAQ,YAAc5kC,EAAU4kC,EAAO,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,kBAI3DA,GAAQ,YAAc5kC,EAAU4kC,EAAO,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,QAIrEA,GAAQ,UACN9lC;AAAAA,cACI8lC,EAAO,SAAS;AAAA,kBAEpBtQ,CAAO;AAAA;AAAA,QAETsQ,GAAQ,MACN9lC;AAAAA,oBACU8lC,EAAO,MAAM,GAAK,KAAO,QAAQ;AAAA,cACvCA,EAAO,MAAM,QAAU,EAAE,IAAIA,EAAO,MAAM,OAAS,EAAE;AAAA,kBAEzDtQ,CAAO;AAAA;AAAA,QAET0N,GAA2B,CAAE,UAAW,SAAU,MAAA9L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG7B,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CC1DO,SAAS2O,GAAgBnhC,EAI7B,CACD,KAAM,CAAE,MAAAwyB,EAAO,MAAA4O,EAAO,kBAAA3C,CAAA,EAAsBz+B,EAE5C,OAAO5E;AAAAA;AAAAA;AAAAA;AAAAA,QAIDqjC,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKP2C,GAAO,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIhCA,GAAO,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAI7BA,GAAO,YAAc9kC,EAAU8kC,EAAM,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,kBAIzDA,GAAO,YAAc9kC,EAAU8kC,EAAM,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,QAInEA,GAAO,UACLhmC;AAAAA,cACIgmC,EAAM,SAAS;AAAA,kBAEnBxQ,CAAO;AAAA;AAAA,QAETwQ,GAAO,MACLhmC;AAAAA,oBACUgmC,EAAM,MAAM,GAAK,KAAO,QAAQ;AAAA,cACtCA,EAAM,MAAM,QAAU,EAAE,IAAIA,EAAM,MAAM,OAAS,EAAE;AAAA,kBAEvDxQ,CAAO;AAAA;AAAA,QAET0N,GAA2B,CAAE,UAAW,QAAS,MAAA9L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG5B,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CCtDO,SAAS6O,GAAmBrhC,EAKhC,CACD,KAAM,CAAE,MAAAwyB,EAAO,SAAA8O,EAAU,iBAAAC,EAAkB,kBAAA9C,GAAsBz+B,EAC3DygC,EAAsBc,EAAiB,OAAS,EAEhDZ,EAAqBjD,GAAoC,CAE7D,MAAM8D,EADQ9D,EAAQ,OACK,KAAK,SAC1BhgC,EAAQggC,EAAQ,MAAQA,EAAQ,UACtC,OAAOtiC;AAAAA;AAAAA;AAAAA;AAAAA,cAIGomC,EAAc,IAAIA,CAAW,GAAK9jC,CAAK;AAAA;AAAA,yCAEZggC,EAAQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,oBAKtCA,EAAQ,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,oBAI9BA,EAAQ,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,oBAIjCA,EAAQ,cAAgBphC,EAAUohC,EAAQ,aAAa,EAAI,KAAK;AAAA;AAAA,YAExEA,EAAQ,UACNtiC;AAAAA;AAAAA,oBAEMsiC,EAAQ,SAAS;AAAA;AAAA,gBAGvB9M,CAAO;AAAA;AAAA;AAAA,KAInB,EAEA,OAAOx1B;AAAAA;AAAAA;AAAAA;AAAAA,QAIDqjC,CAAiB;AAAA;AAAA,QAEjBgC,EACErlC;AAAAA;AAAAA,gBAEMmmC,EAAiB,IAAK7D,GAAYiD,EAAkBjD,CAAO,CAAC,CAAC;AAAA;AAAA,YAGnEtiC;AAAAA;AAAAA;AAAAA;AAAAA,wBAIckmC,GAAU,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,wBAInCA,GAAU,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,wBAIhCA,GAAU,MAAQ,KAAK;AAAA;AAAA;AAAA;AAAA,wBAIvBA,GAAU,YAAchlC,EAAUglC,EAAS,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,wBAI/DA,GAAU,YAAchlC,EAAUglC,EAAS,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA,WAG5E;AAAA;AAAA,QAEHA,GAAU,UACRlmC;AAAAA,cACIkmC,EAAS,SAAS;AAAA,kBAEtB1Q,CAAO;AAAA;AAAA,QAET0Q,GAAU,MACRlmC;AAAAA,oBACUkmC,EAAS,MAAM,GAAK,KAAO,QAAQ;AAAA,cACzCA,EAAS,MAAM,QAAU,EAAE,IAAIA,EAAS,MAAM,OAAS,EAAE;AAAA,kBAE7D1Q,CAAO;AAAA;AAAA,QAET0N,GAA2B,CAAE,UAAW,WAAY,MAAA9L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG/B,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CCxGO,SAASiP,GAAmBzhC,EAIhC,CACD,KAAM,CAAE,MAAAwyB,EAAO,SAAAkP,EAAU,kBAAAjD,CAAA,EAAsBz+B,EAE/C,OAAO5E;AAAAA;AAAAA;AAAAA;AAAAA,QAIDqjC,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKPiD,GAAU,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAInCA,GAAU,OAAS,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAI/BA,GAAU,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIhCA,GAAU,UAAY,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,cAKtCA,GAAU,gBACRplC,EAAUolC,EAAS,eAAe,EAClC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAMPA,GAAU,cAAgBplC,EAAUolC,EAAS,aAAa,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAMnEA,GAAU,WAAa,KACrBvE,GAAeuE,EAAS,SAAS,EACjC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKbA,GAAU,UACRtmC;AAAAA,cACIsmC,EAAS,SAAS;AAAA,kBAEtB9Q,CAAO;AAAA;AAAA,QAET4B,EAAM,gBACJp3B;AAAAA,cACIo3B,EAAM,eAAe;AAAA,kBAEzB5B,CAAO;AAAA;AAAA,QAET4B,EAAM,kBACJp3B;AAAAA,uBACao3B,EAAM,iBAAiB;AAAA,kBAEpC5B,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKK4B,EAAM,YAAY;AAAA,mBACrB,IAAMA,EAAM,gBAAgB,EAAK,CAAC;AAAA;AAAA,YAEzCA,EAAM,aAAe,WAAa,SAAS;AAAA;AAAA;AAAA;AAAA,sBAIjCA,EAAM,YAAY;AAAA,mBACrB,IAAMA,EAAM,gBAAgB,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAM9BA,EAAM,YAAY;AAAA,mBACrB,IAAMA,EAAM,eAAA,CAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAMzBA,EAAM,YAAY;AAAA,mBACrB,IAAMA,EAAM,iBAAA,CAAkB;AAAA;AAAA;AAAA;AAAA,qCAIZ,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,QAKxD8L,GAA2B,CAAE,UAAW,WAAY,MAAA9L,CAAA,CAAO,CAAC;AAAA;AAAA,GAGpE,CCpFO,SAASmP,GAAenP,EAAsB,CACnD,MAAM6K,EAAW7K,EAAM,UAAU,SAC3BkP,EAAYrE,GAAU,UAAY,OAGlCiE,EAAYjE,GAAU,UAAY,OAGlCmB,EAAWnB,GAAU,SAAW,KAClBA,GAAU,WAC9B,MAAM+D,EAAS/D,GAAU,OAAS,KAC5B6D,EAAU7D,GAAU,QAAU,KAC9BwB,EAAYxB,GAAU,UAAY,KAClCyC,EAASzC,GAAU,OAAS,KAE5BuE,EADeC,GAAoBrP,EAAM,QAAQ,EAEpD,IAAI,CAACpyB,EAAKgd,KAAW,CACpB,IAAAhd,EACA,QAASg9B,GAAeh9B,EAAKoyB,CAAK,EAClC,MAAOpV,CAAA,EACP,EACD,KAAK,CAAChnB,EAAGM,IACJN,EAAE,UAAYM,EAAE,QAAgBN,EAAE,QAAU,GAAK,EAC9CA,EAAE,MAAQM,EAAE,KACpB,EAEH,OAAO0E;AAAAA;AAAAA,QAEDwmC,EAAgB,IAAKE,GACrBC,GAAcD,EAAQ,IAAKtP,EAAO,CAChC,SAAAkP,EACA,SAAAJ,EACA,QAAA9C,EAEA,MAAA4C,EACA,OAAAF,EACA,SAAArC,EACA,MAAAiB,EACA,gBAAiBtN,EAAM,UAAU,iBAAmB,IAAA,CACrD,CAAA,CACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BASsBA,EAAM,cAAgBl2B,EAAUk2B,EAAM,aAAa,EAAI,KAAK;AAAA;AAAA,QAEjFA,EAAM,UACJp3B;AAAAA,cACIo3B,EAAM,SAAS;AAAA,kBAEnB5B,CAAO;AAAA;AAAA,EAEf4B,EAAM,SAAW,KAAK,UAAUA,EAAM,SAAU,KAAM,CAAC,EAAI,kBAAkB;AAAA;AAAA;AAAA,GAI/E,CAEA,SAASqP,GAAoBv9B,EAAuD,CAClF,OAAIA,GAAU,aAAa,OAClBA,EAAS,YAAY,IAAK1D,GAAUA,EAAM,EAAE,EAEjD0D,GAAU,cAAc,OACnBA,EAAS,aAEX,CACL,WACA,WACA,UACA,aACA,QACA,SACA,WACA,OAAA,CAEJ,CAEA,SAASy9B,GACP3hC,EACAoyB,EACA1wB,EACA,CACA,MAAM28B,EAAoBZ,GACxBz9B,EACA0B,EAAK,eAAA,EAEP,OAAQ1B,EAAA,CACN,IAAK,WACH,OAAOqhC,GAAmB,CACxB,MAAAjP,EACA,SAAU1wB,EAAK,SACf,kBAAA28B,CAAA,CACD,EACH,IAAK,WACH,OAAO4C,GAAmB,CACxB,MAAA7O,EACA,SAAU1wB,EAAK,SACf,iBAAkBA,EAAK,iBAAiB,UAAY,CAAA,EACpD,kBAAA28B,CAAA,CACD,EACH,IAAK,UACH,OAAOF,GAAkB,CACvB,MAAA/L,EACA,QAAS1wB,EAAK,QACd,kBAAA28B,CAAA,CACD,EACH,IAAK,aACH,OAAOC,GAAqB,CAC1B,MAAAlM,EAEA,kBAAAiM,CAAA,CACD,EACH,IAAK,QACH,OAAO0C,GAAgB,CACrB,MAAA3O,EACA,MAAO1wB,EAAK,MACZ,kBAAA28B,CAAA,CACD,EACH,IAAK,SACH,OAAOwC,GAAiB,CACtB,MAAAzO,EACA,OAAQ1wB,EAAK,OACb,kBAAA28B,CAAA,CACD,EACH,IAAK,WACH,OAAOG,GAAmB,CACxB,MAAApM,EACA,SAAU1wB,EAAK,SACf,kBAAA28B,CAAA,CACD,EACH,IAAK,QAAS,CACZ,MAAMsB,EAAgBj+B,EAAK,iBAAiB,OAAS,CAAA,EAC/Cq+B,EAAiBJ,EAAc,CAAC,EAChCd,EAAYkB,GAAgB,WAAa,UACzCT,EACHS,GAAkE,SAAW,KAC1E6B,EACJxP,EAAM,wBAA0ByM,EAAYzM,EAAM,sBAAwB,KACtEyN,EAAuB+B,EACzB,CACE,cAAexP,EAAM,0BACrB,OAAQA,EAAM,mBACd,SAAUA,EAAM,qBAChB,SAAUA,EAAM,qBAChB,iBAAkBA,EAAM,4BAAA,EAE1B,KACJ,OAAOqN,GAAgB,CACrB,MAAArN,EACA,MAAO1wB,EAAK,MACZ,cAAAi+B,EACA,kBAAAtB,EACA,iBAAkBuD,EAClB,qBAAA/B,EACA,cAAe,IAAMzN,EAAM,mBAAmByM,EAAWS,CAAO,CAAA,CACjE,CACH,CACA,QACE,OAAOuC,GAAyB7hC,EAAKoyB,EAAO1wB,EAAK,iBAAmB,CAAA,CAAE,CAAA,CAE5E,CAEA,SAASmgC,GACP7hC,EACAoyB,EACAoL,EACA,CACA,MAAMlgC,EAAQwkC,GAAoB1P,EAAM,SAAUpyB,CAAG,EAC/CiG,EAASmsB,EAAM,UAAU,WAAWpyB,CAAG,EACvC8X,EAAa,OAAO7R,GAAQ,YAAe,UAAYA,EAAO,WAAa,OAC3Ek3B,EAAU,OAAOl3B,GAAQ,SAAY,UAAYA,EAAO,QAAU,OAClEm3B,EAAY,OAAOn3B,GAAQ,WAAc,UAAYA,EAAO,UAAY,OACxE87B,EAAY,OAAO97B,GAAQ,WAAc,SAAWA,EAAO,UAAY,OACvE+7B,EAAWxE,EAAgBx9B,CAAG,GAAK,CAAA,EACnCq+B,EAAoBZ,GAA0Bz9B,EAAKw9B,CAAe,EAExE,OAAOxiC;AAAAA;AAAAA,gCAEuBsC,CAAK;AAAA;AAAA,QAE7B+gC,CAAiB;AAAA;AAAA,QAEjB2D,EAAS,OAAS,EAChBhnC;AAAAA;AAAAA,gBAEMgnC,EAAS,IAAK1E,GAAY2E,GAAqB3E,CAAO,CAAC,CAAC;AAAA;AAAA,YAG9DtiC;AAAAA;AAAAA;AAAAA;AAAAA,wBAIc8c,GAAc,KAAO,MAAQA,EAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,wBAItDqlB,GAAW,KAAO,MAAQA,EAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,wBAIhDC,GAAa,KAAO,MAAQA,EAAY,MAAQ,IAAI;AAAA;AAAA;AAAA,WAGjE;AAAA;AAAA,QAEH2E,EACE/mC;AAAAA,cACI+mC,CAAS;AAAA,kBAEbvR,CAAO;AAAA;AAAA,QAET0N,GAA2B,CAAE,UAAWl+B,EAAK,MAAAoyB,CAAA,CAAO,CAAC;AAAA;AAAA,GAG7D,CAEA,SAAS8P,GACPh+B,EACoC,CACpC,OAAKA,GAAU,aAAa,OACrB,OAAO,YAAYA,EAAS,YAAY,IAAK1D,GAAU,CAACA,EAAM,GAAIA,CAAK,CAAC,CAAC,EADrC,CAAA,CAE7C,CAEA,SAASshC,GACP59B,EACAlE,EACQ,CAER,OADakiC,GAAsBh+B,CAAQ,EAAElE,CAAG,GACnC,OAASkE,GAAU,gBAAgBlE,CAAG,GAAKA,CAC1D,CAEA,MAAMmiC,GAA+B,IAAU,IAE/C,SAASC,GAAkB9E,EAA0C,CACnE,OAAKA,EAAQ,cACN,KAAK,IAAA,EAAQA,EAAQ,cAAgB6E,GADT,EAErC,CAEA,SAASE,GAAoB/E,EAA0D,CACrF,OAAIA,EAAQ,QAAgB,MAExB8E,GAAkB9E,CAAO,EAAU,SAChC,IACT,CAEA,SAASgF,GAAsBhF,EAAkE,CAC/F,OAAIA,EAAQ,YAAc,GAAa,MACnCA,EAAQ,YAAc,GAAc,KAEpC8E,GAAkB9E,CAAO,EAAU,SAChC,KACT,CAEA,SAAS2E,GAAqB3E,EAAiC,CAC7D,MAAMiF,EAAgBF,GAAoB/E,CAAO,EAC3CkF,EAAkBF,GAAsBhF,CAAO,EAErD,OAAOtiC;AAAAA;AAAAA;AAAAA,0CAGiCsiC,EAAQ,MAAQA,EAAQ,SAAS;AAAA,uCACpCA,EAAQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKtCiF,CAAa;AAAA;AAAA;AAAA;AAAA,kBAIbjF,EAAQ,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIjCkF,CAAe;AAAA;AAAA;AAAA;AAAA,kBAIflF,EAAQ,cAAgBphC,EAAUohC,EAAQ,aAAa,EAAI,KAAK;AAAA;AAAA,UAExEA,EAAQ,UACNtiC;AAAAA;AAAAA,kBAEMsiC,EAAQ,SAAS;AAAA;AAAA,cAGvB9M,CAAO;AAAA;AAAA;AAAA,GAInB,CCrUO,SAASiS,GAAsBjiC,EAA8B,CAClE,MAAMO,EAAOP,EAAM,MAAQ,UACrBkiC,EAAKliC,EAAM,GAAK,IAAIA,EAAM,EAAE,IAAM,GAClCnF,EAAOmF,EAAM,MAAQ,GACrBmiC,EAAUniC,EAAM,SAAW,GACjC,MAAO,GAAGO,CAAI,IAAI2hC,CAAE,IAAIrnC,CAAI,IAAIsnC,CAAO,GAAG,KAAA,CAC5C,CAEO,SAASC,GAAkBpiC,EAA8B,CAC9D,MAAMqiC,EAAKriC,EAAM,IAAM,KACvB,OAAOqiC,EAAK3mC,EAAU2mC,CAAE,EAAI,KAC9B,CAEO,SAASC,GAAc7mC,EAAoB,CAChD,OAAKA,EACE,GAAGD,GAASC,CAAE,CAAC,KAAKC,EAAUD,CAAE,CAAC,IADxB,KAElB,CAEO,SAAS8mC,GAAoB5P,EAAwB,CAC1D,GAAIA,EAAI,aAAe,KAAM,MAAO,MACpC,MAAM6P,EAAQ7P,EAAI,aAAe,EAC3B8P,EAAM9P,EAAI,eAAiB,EACjC,OAAO8P,EAAM,GAAGD,CAAK,MAAMC,CAAG,GAAK,OAAOD,CAAK,CACjD,CAEO,SAASE,GAAmBzjC,EAA0B,CAC3D,GAAIA,GAAW,KAAM,MAAO,GAC5B,GAAI,CACF,OAAO,KAAK,UAAUA,EAAS,KAAM,CAAC,CACxC,MAAQ,CACN,OAAO,OAAOA,CAAO,CACvB,CACF,CAEO,SAAS0jC,GAAgB/9B,EAAc,CAC5C,MAAMpG,EAAQoG,EAAI,OAAS,CAAA,EACrB/L,EAAO2F,EAAM,YAAchD,GAASgD,EAAM,WAAW,EAAI,MACzDokC,EAAOpkC,EAAM,YAAchD,GAASgD,EAAM,WAAW,EAAI,MAE/D,MAAO,GADQA,EAAM,YAAc,KACnB,WAAW3F,CAAI,WAAW+pC,CAAI,EAChD,CAEO,SAASC,GAAmBj+B,EAAc,CAC/C,MAAM7P,EAAI6P,EAAI,SACd,OAAI7P,EAAE,OAAS,KAAa,MAAMyG,GAASzG,EAAE,IAAI,CAAC,GAC9CA,EAAE,OAAS,QAAgB,SAASgH,GAAiBhH,EAAE,OAAO,CAAC,GAC5D,QAAQA,EAAE,IAAI,GAAGA,EAAE,GAAK,KAAKA,EAAE,EAAE,IAAM,EAAE,EAClD,CAEO,SAAS+tC,GAAkBl+B,EAAc,CAC9C,MAAMlP,EAAIkP,EAAI,QACd,OAAIlP,EAAE,OAAS,cAAsB,WAAWA,EAAE,IAAI,GAC/C,UAAUA,EAAE,OAAO,EAC5B,CCvBA,SAASqtC,GAAoBnR,EAA4B,CACvD,MAAM52B,EAAU,CAAC,OAAQ,GAAG42B,EAAM,SAAS,OAAO,OAAO,CAAC,EACpD1yB,EAAU0yB,EAAM,KAAK,SAAS,KAAA,EAChC1yB,GAAW,CAAClE,EAAQ,SAASkE,CAAO,GACtClE,EAAQ,KAAKkE,CAAO,EAEtB,MAAM8jC,MAAW,IACjB,OAAOhoC,EAAQ,OAAQjD,GACjBirC,EAAK,IAAIjrC,CAAK,EAAU,IAC5BirC,EAAK,IAAIjrC,CAAK,EACP,GACR,CACH,CAEA,SAASupC,GAAoB1P,EAAkBsP,EAAyB,CACtE,GAAIA,IAAY,OAAQ,MAAO,OAC/B,MAAM76B,EAAOurB,EAAM,aAAa,KAAM5xB,GAAUA,EAAM,KAAOkhC,CAAO,EACpE,OAAI76B,GAAM,MAAcA,EAAK,MACtBurB,EAAM,gBAAgBsP,CAAO,GAAKA,CAC3C,CAEO,SAAS+B,GAAWrR,EAAkB,CAC3C,MAAMsR,EAAiBH,GAAoBnR,CAAK,EAChD,OAAOp3B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,gBASOo3B,EAAM,OACJA,EAAM,OAAO,QACX,MACA,KACF,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,sCAKeA,EAAM,QAAQ,MAAQ,KAAK;AAAA;AAAA;AAAA;AAAA,sCAI3B0Q,GAAc1Q,EAAM,QAAQ,cAAgB,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,0CAI7CA,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,cACnEA,EAAM,QAAU,cAAgB,SAAS;AAAA;AAAA,YAE3CA,EAAM,MAAQp3B,wBAA2Bo3B,EAAM,KAAK,UAAY5B,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAW5D4B,EAAM,KAAK,IAAI;AAAA,uBACd98B,GACR88B,EAAM,aAAa,CAAE,KAAO98B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAM3D88B,EAAM,KAAK,WAAW;AAAA,uBACrB98B,GACR88B,EAAM,aAAa,CAAE,YAAc98B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAMlE88B,EAAM,KAAK,OAAO;AAAA,uBACjB98B,GACR88B,EAAM,aAAa,CAAE,QAAU98B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAQ5D88B,EAAM,KAAK,OAAO;AAAA,wBAClB98B,GACT88B,EAAM,aAAa,CAAE,QAAU98B,EAAE,OAA4B,QAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAMhE88B,EAAM,KAAK,YAAY;AAAA,wBACrB98B,GACT88B,EAAM,aAAa,CACjB,aAAe98B,EAAE,OAA6B,KAAA,CAC/C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAQRquC,GAAqBvR,CAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,uBAKdA,EAAM,KAAK,aAAa;AAAA,wBACtB98B,GACT88B,EAAM,aAAa,CACjB,cAAgB98B,EAAE,OAA6B,KAAA,CAChD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBASK88B,EAAM,KAAK,QAAQ;AAAA,wBACjB98B,GACT88B,EAAM,aAAa,CACjB,SAAW98B,EAAE,OAA6B,KAAA,CAC3C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBASK88B,EAAM,KAAK,WAAW;AAAA,wBACpB98B,GACT88B,EAAM,aAAa,CACjB,YAAc98B,EAAE,OAA6B,KAAA,CAC9C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAQA88B,EAAM,KAAK,cAAgB,cAAgB,cAAgB,eAAe;AAAA;AAAA,qBAEvEA,EAAM,KAAK,WAAW;AAAA,qBACrB98B,GACR88B,EAAM,aAAa,CACjB,YAAc98B,EAAE,OAA+B,KAAA,CAChD,CAAC;AAAA;AAAA;AAAA;AAAA,aAIH88B,EAAM,KAAK,cAAgB,YAC3Bp3B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,+BAMkBo3B,EAAM,KAAK,OAAO;AAAA,8BAClB98B,GACT88B,EAAM,aAAa,CACjB,QAAU98B,EAAE,OAA4B,OAAA,CACzC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAMM88B,EAAM,KAAK,SAAW,MAAM;AAAA,+BAC1B98B,GACT88B,EAAM,aAAa,CACjB,QAAU98B,EAAE,OAA6B,KAAA,CAC1C,CAAC;AAAA;AAAA,uBAEFouC,EAAe,IACbhC,GACC1mC,kBAAqB0mC,CAAO;AAAA,8BACxBI,GAAoB1P,EAAOsP,CAAO,CAAC;AAAA,oCAAA,CAE1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAMMtP,EAAM,KAAK,EAAE;AAAA,6BACZ98B,GACR88B,EAAM,aAAa,CAAE,GAAK98B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAOzD88B,EAAM,KAAK,cAAc;AAAA,6BACxB98B,GACR88B,EAAM,aAAa,CACjB,eAAiB98B,EAAE,OAA4B,KAAA,CAChD,CAAC;AAAA;AAAA;AAAA,kBAGN88B,EAAM,KAAK,gBAAkB,WAC3Bp3B;AAAAA;AAAAA;AAAAA;AAAAA,mCAIeo3B,EAAM,KAAK,gBAAgB;AAAA,mCAC1B98B,GACR88B,EAAM,aAAa,CACjB,iBAAmB98B,EAAE,OAA4B,KAAA,CAClD,CAAC;AAAA;AAAA;AAAA,sBAIVk7B,CAAO;AAAA;AAAA,cAGfA,CAAO;AAAA;AAAA,kDAE+B4B,EAAM,IAAI,WAAWA,EAAM,KAAK;AAAA,cACpEA,EAAM,KAAO,UAAY,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QASxCA,EAAM,KAAK,SAAW,EACpBp3B,mEACAA;AAAAA;AAAAA,gBAEMo3B,EAAM,KAAK,IAAKhtB,GAAQw+B,GAAUx+B,EAAKgtB,CAAK,CAAC,CAAC;AAAA;AAAA,WAEnD;AAAA;AAAA;AAAA;AAAA;AAAA,8CAKmCA,EAAM,WAAa,gBAAgB;AAAA,QACzEA,EAAM,WAAa,KACjBp3B;AAAAA;AAAAA;AAAAA;AAAAA,YAKAo3B,EAAM,KAAK,SAAW,EACpBp3B,mEACAA;AAAAA;AAAAA,kBAEMo3B,EAAM,KAAK,IAAK5xB,GAAUqjC,GAAUrjC,CAAK,CAAC,CAAC;AAAA;AAAA,aAEhD;AAAA;AAAA,GAGb,CAEA,SAASmjC,GAAqBvR,EAAkB,CAC9C,MAAM3uB,EAAO2uB,EAAM,KACnB,OAAI3uB,EAAK,eAAiB,KACjBzI;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,mBAKQyI,EAAK,UAAU;AAAA,mBACdnO,GACR88B,EAAM,aAAa,CACjB,WAAa98B,EAAE,OAA4B,KAAA,CAC5C,CAAC;AAAA;AAAA;AAAA,MAKRmO,EAAK,eAAiB,QACjBzI;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,qBAKUyI,EAAK,WAAW;AAAA,qBACfnO,GACR88B,EAAM,aAAa,CACjB,YAAc98B,EAAE,OAA4B,KAAA,CAC7C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAMKmO,EAAK,SAAS;AAAA,sBACZnO,GACT88B,EAAM,aAAa,CACjB,UAAY98B,EAAE,OAA6B,KAAA,CAC5C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAUP0F;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,mBAKUyI,EAAK,QAAQ;AAAA,mBACZnO,GACR88B,EAAM,aAAa,CAAE,SAAW98B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAM/DmO,EAAK,MAAM;AAAA,mBACVnO,GACR88B,EAAM,aAAa,CAAE,OAAS98B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA,GAKhF,CAEA,SAASsuC,GAAUx+B,EAAcgtB,EAAkB,CAEjD,MAAM0R,EAAY,gCADC1R,EAAM,YAAchtB,EAAI,GACoB,sBAAwB,EAAE,GACzF,OAAOpK;AAAAA,iBACQ8oC,CAAS,WAAW,IAAM1R,EAAM,WAAWhtB,EAAI,EAAE,CAAC;AAAA;AAAA,kCAEjCA,EAAI,IAAI;AAAA,gCACVi+B,GAAmBj+B,CAAG,CAAC;AAAA,6BAC1Bk+B,GAAkBl+B,CAAG,CAAC;AAAA,UACzCA,EAAI,QAAUpK,8BAAiCoK,EAAI,OAAO,SAAWorB,CAAO;AAAA;AAAA,+BAEvDprB,EAAI,QAAU,UAAY,UAAU;AAAA,+BACpCA,EAAI,aAAa;AAAA,+BACjBA,EAAI,QAAQ;AAAA;AAAA;AAAA;AAAA,eAI5B+9B,GAAgB/9B,CAAG,CAAC;AAAA;AAAA;AAAA;AAAA,wBAIXgtB,EAAM,IAAI;AAAA,qBACZzvB,GAAiB,CACzBA,EAAM,gBAAA,EACNyvB,EAAM,SAAShtB,EAAK,CAACA,EAAI,OAAO,CAClC,CAAC;AAAA;AAAA,cAECA,EAAI,QAAU,UAAY,QAAQ;AAAA;AAAA;AAAA;AAAA,wBAIxBgtB,EAAM,IAAI;AAAA,qBACZzvB,GAAiB,CACzBA,EAAM,gBAAA,EACNyvB,EAAM,MAAMhtB,CAAG,CACjB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMWgtB,EAAM,IAAI;AAAA,qBACZzvB,GAAiB,CACzBA,EAAM,gBAAA,EACNyvB,EAAM,WAAWhtB,EAAI,EAAE,CACzB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMWgtB,EAAM,IAAI;AAAA,qBACZzvB,GAAiB,CACzBA,EAAM,gBAAA,EACNyvB,EAAM,SAAShtB,CAAG,CACpB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQb,CAEA,SAASy+B,GAAUrjC,EAAwB,CACzC,OAAOxF;AAAAA;AAAAA;AAAAA,kCAGyBwF,EAAM,MAAM;AAAA,gCACdA,EAAM,SAAW,EAAE;AAAA;AAAA;AAAA,eAGpCxE,GAASwE,EAAM,EAAE,CAAC;AAAA,6BACJA,EAAM,YAAc,CAAC;AAAA,UACxCA,EAAM,MAAQxF,uBAA0BwF,EAAM,KAAK,SAAWgwB,CAAO;AAAA;AAAA;AAAA,GAI/E,CC5aO,SAASuT,GAAY3R,EAAmB,CAC7C,OAAOp3B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,0CAQiCo3B,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,cACnEA,EAAM,QAAU,cAAgB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAMjB,KAAK,UAAUA,EAAM,QAAU,GAAI,KAAM,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,sCAI3C,KAAK,UAAUA,EAAM,QAAU,GAAI,KAAM,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,sCAI3C,KAAK,UAAUA,EAAM,WAAa,GAAI,KAAM,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAY7DA,EAAM,UAAU;AAAA,uBACf98B,GACR88B,EAAM,mBAAoB98B,EAAE,OAA4B,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAOvD88B,EAAM,UAAU;AAAA,uBACf98B,GACR88B,EAAM,mBAAoB98B,EAAE,OAA+B,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+CAMlC88B,EAAM,MAAM;AAAA;AAAA,UAEjDA,EAAM,UACJp3B;AAAAA,gBACIo3B,EAAM,SAAS;AAAA,oBAEnB5B,CAAO;AAAA,UACT4B,EAAM,WACJp3B,sDAAyDo3B,EAAM,UAAU,SACzE5B,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0DAOuC,KAAK,UACvD4B,EAAM,QAAU,CAAA,EAChB,KACA,CAAA,CACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMCA,EAAM,SAAS,SAAW,EACxBp3B,qEACAA;AAAAA;AAAAA,gBAEMo3B,EAAM,SAAS,IACd4R,GAAQhpC;AAAAA;AAAAA;AAAAA,gDAGuBgpC,EAAI,KAAK;AAAA,8CACX,IAAI,KAAKA,EAAI,EAAE,EAAE,oBAAoB;AAAA;AAAA;AAAA,gDAGnCd,GAAmBc,EAAI,OAAO,CAAC;AAAA;AAAA;AAAA,iBAAA,CAIhE;AAAA;AAAA,WAEJ;AAAA;AAAA,GAGX,CC7GO,SAASC,GAAgB7R,EAAuB,CACrD,OAAOp3B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,wCAO+Bo3B,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,YACnEA,EAAM,QAAU,WAAa,SAAS;AAAA;AAAA;AAAA,QAG1CA,EAAM,UACJp3B;AAAAA,cACIo3B,EAAM,SAAS;AAAA,kBAEnB5B,CAAO;AAAA,QACT4B,EAAM,cACJp3B;AAAAA,cACIo3B,EAAM,aAAa;AAAA,kBAEvB5B,CAAO;AAAA;AAAA,UAEP4B,EAAM,QAAQ,SAAW,EACvBp3B,uDACAo3B,EAAM,QAAQ,IAAK5xB,GAAU0jC,GAAY1jC,CAAK,CAAC,CAAC;AAAA;AAAA;AAAA,GAI5D,CAEA,SAAS0jC,GAAY1jC,EAAsB,CACzC,MAAM2jC,EACJ3jC,EAAM,kBAAoB,KACtB,GAAGA,EAAM,gBAAgB,QACzB,MACAnF,EAAOmF,EAAM,MAAQ,UACrB4jC,EAAQ,MAAM,QAAQ5jC,EAAM,KAAK,EAAIA,EAAM,MAAM,OAAO,OAAO,EAAI,CAAA,EACnEmS,EAAS,MAAM,QAAQnS,EAAM,MAAM,EAAIA,EAAM,OAAO,OAAO,OAAO,EAAI,CAAA,EACtE6jC,EACJ1xB,EAAO,OAAS,EACZA,EAAO,OAAS,EACd,GAAGA,EAAO,MAAM,UAChB,WAAWA,EAAO,KAAK,IAAI,CAAC,GAC9B,KACN,OAAO3X;AAAAA;AAAAA;AAAAA,kCAGyBwF,EAAM,MAAQ,cAAc;AAAA,gCAC9BiiC,GAAsBjiC,CAAK,CAAC;AAAA;AAAA,+BAE7BnF,CAAI;AAAA,YACvB+oC,EAAM,IAAK1mC,GAAS1C,uBAA0B0C,CAAI,SAAS,CAAC;AAAA,YAC5D2mC,EAAcrpC,uBAA0BqpC,CAAW,UAAY7T,CAAO;AAAA,YACtEhwB,EAAM,SAAWxF,uBAA0BwF,EAAM,QAAQ,UAAYgwB,CAAO;AAAA,YAC5EhwB,EAAM,aACJxF,uBAA0BwF,EAAM,YAAY,UAC5CgwB,CAAO;AAAA,YACThwB,EAAM,gBACJxF,uBAA0BwF,EAAM,eAAe,UAC/CgwB,CAAO;AAAA,YACThwB,EAAM,QAAUxF,uBAA0BwF,EAAM,OAAO,UAAYgwB,CAAO;AAAA;AAAA;AAAA;AAAA,eAIvEoS,GAAkBpiC,CAAK,CAAC;AAAA,wCACC2jC,CAAS;AAAA,oCACb3jC,EAAM,QAAU,EAAE;AAAA;AAAA;AAAA,GAItD,CChFA,MAAMgG,GAAqB,CAAC,QAAS,QAAS,OAAQ,OAAQ,QAAS,OAAO,EAmB9E,SAAS89B,GAAW/rC,EAAuB,CACzC,GAAI,CAACA,EAAO,MAAO,GACnB,MAAMgsC,EAAO,IAAI,KAAKhsC,CAAK,EAC3B,OAAI,OAAO,MAAMgsC,EAAK,QAAA,CAAS,EAAUhsC,EAClCgsC,EAAK,mBAAA,CACd,CAEA,SAASC,GAAchkC,EAAiBikC,EAAgB,CACtD,OAAKA,EACY,CAACjkC,EAAM,QAASA,EAAM,UAAWA,EAAM,GAAG,EACxD,OAAO,OAAO,EACd,KAAK,GAAG,EACR,YAAA,EACa,SAASikC,CAAM,EALX,EAMtB,CAEO,SAASC,GAAWtS,EAAkB,CAC3C,MAAMqS,EAASrS,EAAM,WAAW,KAAA,EAAO,YAAA,EACjCuS,EAAgBn+B,GAAO,KAAMO,GAAU,CAACqrB,EAAM,aAAarrB,CAAK,CAAC,EACjEyyB,EAAWpH,EAAM,QAAQ,OAAQ5xB,GACjCA,EAAM,OAAS,CAAC4xB,EAAM,aAAa5xB,EAAM,KAAK,EAAU,GACrDgkC,GAAchkC,EAAOikC,CAAM,CACnC,EACKG,EAAcH,GAAUE,EAAgB,WAAa,UAE3D,OAAO3pC;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,0CAQiCo3B,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,cACnEA,EAAM,QAAU,WAAa,SAAS;AAAA;AAAA;AAAA;AAAA,wBAI5BoH,EAAS,SAAW,CAAC;AAAA,qBACxB,IAAMpH,EAAM,SAASoH,EAAS,IAAKh5B,GAAUA,EAAM,GAAG,EAAGokC,CAAW,CAAC;AAAA;AAAA,qBAErEA,CAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBASXxS,EAAM,UAAU;AAAA,qBACf98B,GACR88B,EAAM,mBAAoB98B,EAAE,OAA4B,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAQrD88B,EAAM,UAAU;AAAA,sBAChB98B,GACT88B,EAAM,mBAAoB98B,EAAE,OAA4B,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMpEkR,GAAO,IACNO,GAAU/L;AAAAA,0CACqB+L,CAAK;AAAA;AAAA;AAAA,2BAGpBqrB,EAAM,aAAarrB,CAAK,CAAC;AAAA,0BACzBzR,GACT88B,EAAM,cAAcrrB,EAAQzR,EAAE,OAA4B,OAAO,CAAC;AAAA;AAAA,sBAE9DyR,CAAK;AAAA;AAAA,WAAA,CAGlB;AAAA;AAAA;AAAA,QAGDqrB,EAAM,KACJp3B,uDAA0Do3B,EAAM,IAAI,SACpE5B,CAAO;AAAA,QACT4B,EAAM,UACJp3B;AAAAA;AAAAA,kBAGAw1B,CAAO;AAAA,QACT4B,EAAM,MACJp3B,0DAA6Do3B,EAAM,KAAK,SACxE5B,CAAO;AAAA;AAAA,kEAEiD4B,EAAM,QAAQ;AAAA,UACtEoH,EAAS,SAAW,EAClBx+B,mEACAw+B,EAAS,IACNh5B,GAAUxF;AAAAA;AAAAA,+CAEsBspC,GAAW9jC,EAAM,IAAI,CAAC;AAAA,0CAC3BA,EAAM,OAAS,EAAE,KAAKA,EAAM,OAAS,EAAE;AAAA,oDAC7BA,EAAM,WAAa,EAAE;AAAA,kDACvBA,EAAM,SAAWA,EAAM,GAAG;AAAA;AAAA,eAAA,CAG/D;AAAA;AAAA;AAAA,GAIb,CClFO,SAASqkC,GAAYzS,EAAmB,CAC7C,MAAM0S,EAAeC,GAAqB3S,CAAK,EACzC4S,EAAiBC,GAA0B7S,CAAK,EACtD,OAAOp3B;AAAAA,MACHkqC,GAAoBF,CAAc,CAAC;AAAA,MACnCG,GAAeL,CAAY,CAAC;AAAA,MAC5BM,GAAchT,CAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAOcA,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,YACnEA,EAAM,QAAU,WAAa,SAAS;AAAA;AAAA;AAAA;AAAA,UAIxCA,EAAM,MAAM,SAAW,EACrBp3B,4CACAo3B,EAAM,MAAM,IAAKz8B,GAAMy/B,GAAWz/B,CAAC,CAAC,CAAC;AAAA;AAAA;AAAA,GAIjD,CAEA,SAASyvC,GAAchT,EAAmB,CACxC,MAAMiT,EAAOjT,EAAM,aAAe,CAAE,QAAS,CAAA,EAAI,OAAQ,EAAC,EACpDkT,EAAU,MAAM,QAAQD,EAAK,OAAO,EAAIA,EAAK,QAAU,CAAA,EACvDE,EAAS,MAAM,QAAQF,EAAK,MAAM,EAAIA,EAAK,OAAS,CAAA,EAC1D,OAAOrqC;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,wCAO+Bo3B,EAAM,cAAc,WAAWA,EAAM,gBAAgB;AAAA,YACjFA,EAAM,eAAiB,WAAa,SAAS;AAAA;AAAA;AAAA,QAGjDA,EAAM,aACJp3B,0DAA6Do3B,EAAM,YAAY,SAC/E5B,CAAO;AAAA;AAAA,UAEP8U,EAAQ,OAAS,EACftqC;AAAAA;AAAAA,gBAEIsqC,EAAQ,IAAKE,GAAQC,GAAoBD,EAAKpT,CAAK,CAAC,CAAC;AAAA,cAEzD5B,CAAO;AAAA,UACT+U,EAAO,OAAS,EACdvqC;AAAAA;AAAAA,gBAEIuqC,EAAO,IAAKG,GAAWC,GAAmBD,EAAQtT,CAAK,CAAC,CAAC;AAAA,cAE7D5B,CAAO;AAAA,UACT8U,EAAQ,SAAW,GAAKC,EAAO,SAAW,EACxCvqC,+CACAw1B,CAAO;AAAA;AAAA;AAAA,GAInB,CAEA,SAASiV,GAAoBD,EAAoBpT,EAAmB,CAClE,MAAMx5B,EAAO4sC,EAAI,aAAa,KAAA,GAAUA,EAAI,SACtCI,EAAM,OAAOJ,EAAI,IAAO,SAAWtpC,EAAUspC,EAAI,EAAE,EAAI,MACvD9nC,EAAO8nC,EAAI,MAAM,KAAA,EAAS,SAASA,EAAI,IAAI,GAAK,UAChDK,EAASL,EAAI,SAAW,YAAc,GACtC9C,EAAK8C,EAAI,SAAW,MAAMA,EAAI,QAAQ,GAAK,GACjD,OAAOxqC;AAAAA;AAAAA;AAAAA,kCAGyBpC,CAAI;AAAA,gCACN4sC,EAAI,QAAQ,GAAG9C,CAAE;AAAA;AAAA,YAErChlC,CAAI,gBAAgBkoC,CAAG,GAAGC,CAAM;AAAA;AAAA;AAAA;AAAA;AAAA,uDAKW,IAAMzT,EAAM,gBAAgBoT,EAAI,SAAS,CAAC;AAAA;AAAA;AAAA,+CAGlD,IAAMpT,EAAM,eAAeoT,EAAI,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOxF,CAEA,SAASG,GAAmBD,EAAsBtT,EAAmB,CACnE,MAAMx5B,EAAO8sC,EAAO,aAAa,KAAA,GAAUA,EAAO,SAC5ChD,EAAKgD,EAAO,SAAW,MAAMA,EAAO,QAAQ,GAAK,GACjDtB,EAAQ,UAAU5nC,GAAWkpC,EAAO,KAAK,CAAC,GAC1C/yB,EAAS,WAAWnW,GAAWkpC,EAAO,MAAM,CAAC,GAC7CI,EAAS,MAAM,QAAQJ,EAAO,MAAM,EAAIA,EAAO,OAAS,CAAA,EAC9D,OAAO1qC;AAAAA;AAAAA;AAAAA,kCAGyBpC,CAAI;AAAA,gCACN8sC,EAAO,QAAQ,GAAGhD,CAAE;AAAA,sDACE0B,CAAK,MAAMzxB,CAAM;AAAA,UAC7DmzB,EAAO,SAAW,EAChB9qC,kEACAA;AAAAA;AAAAA;AAAAA,kBAGM8qC,EAAO,IAAK7uB,GAAU8uB,GAAeL,EAAO,SAAUzuB,EAAOmb,CAAK,CAAC,CAAC;AAAA;AAAA,aAEzE;AAAA;AAAA;AAAA,GAIb,CAEA,SAAS2T,GAAeC,EAAkB/uB,EAA2Bmb,EAAmB,CACtF,MAAMnsB,EAASgR,EAAM,YAAc,UAAY,SACzCtE,EAAS,WAAWnW,GAAWya,EAAM,MAAM,CAAC,GAC5CgvB,EAAO/pC,EAAU+a,EAAM,aAAeA,EAAM,aAAeA,EAAM,cAAgB,IAAI,EAC3F,OAAOjc;AAAAA;AAAAA,8BAEqBic,EAAM,IAAI,MAAMhR,CAAM,MAAM0M,CAAM,MAAMszB,CAAI;AAAA;AAAA;AAAA;AAAA,mBAIvD,IAAM7T,EAAM,eAAe4T,EAAU/uB,EAAM,KAAMA,EAAM,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA,UAIvEA,EAAM,YACJuZ,EACAx1B;AAAAA;AAAAA;AAAAA,yBAGa,IAAMo3B,EAAM,eAAe4T,EAAU/uB,EAAM,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,aAI5D;AAAA;AAAA;AAAA,GAIb,CA2EA,MAAMivB,GAA+B,eAE/BC,GAAkE,CACtE,CAAE,MAAO,OAAQ,MAAO,MAAA,EACxB,CAAE,MAAO,YAAa,MAAO,WAAA,EAC7B,CAAE,MAAO,OAAQ,MAAO,MAAA,CAC1B,EAEMC,GAAwD,CAC5D,CAAE,MAAO,MAAO,MAAO,KAAA,EACvB,CAAE,MAAO,UAAW,MAAO,SAAA,EAC3B,CAAE,MAAO,SAAU,MAAO,QAAA,CAC5B,EAEA,SAASrB,GAAqB3S,EAAiC,CAC7D,MAAMyL,EAASzL,EAAM,WACfiU,EAAQC,GAAiBlU,EAAM,KAAK,EACpC,CAAE,eAAAmU,EAAgB,OAAAC,GAAWC,GAAqB5I,CAAM,EACxD6I,EAAQ,EAAQ7I,EAChBvI,EAAWlD,EAAM,cAAgBA,EAAM,iBAAmB,MAChE,MAAO,CACL,MAAAsU,EACA,SAAApR,EACA,YAAalD,EAAM,YACnB,cAAeA,EAAM,cACrB,aAAcA,EAAM,aACpB,eAAAmU,EACA,OAAAC,EACA,MAAAH,EACA,cAAejU,EAAM,cACrB,YAAaA,EAAM,YACnB,OAAQA,EAAM,eACd,aAAcA,EAAM,aACpB,SAAUA,EAAM,cAAA,CAEpB,CAEA,SAASuU,GAAkBpuC,EAA8B,CACvD,OAAIA,IAAU,aAAeA,IAAU,QAAUA,IAAU,OAAeA,EACnE,MACT,CAEA,SAASquC,GAAaruC,EAAyB,CAC7C,OAAIA,IAAU,UAAYA,IAAU,OAASA,IAAU,UAAkBA,EAClE,SACT,CAEA,SAASsuC,GACPpjC,EAC+B,CAC/B,MAAMxK,EAAWwK,GAAM,UAAY,CAAA,EACnC,MAAO,CACL,SAAUkjC,GAAkB1tC,EAAS,QAAQ,EAC7C,IAAK2tC,GAAa3tC,EAAS,GAAG,EAC9B,YAAa0tC,GAAkB1tC,EAAS,aAAe,MAAM,EAC7D,gBAAiB,GAAQA,EAAS,iBAAmB,GAAK,CAE9D,CAEA,SAAS6tC,GAAoBjJ,EAAoE,CAC/F,MAAMkJ,EAAclJ,GAAQ,QAAU,CAAA,EAChCwH,EAAO,MAAM,QAAQ0B,EAAW,IAAI,EAAIA,EAAW,KAAO,CAAA,EAC1DP,EAAqC,CAAA,EAC3C,OAAAnB,EAAK,QAAS7kC,GAAU,CACtB,GAAI,CAACA,GAAS,OAAOA,GAAU,SAAU,OACzC,MAAMD,EAASC,EACTU,EAAK,OAAOX,EAAO,IAAO,SAAWA,EAAO,GAAG,OAAS,GAC9D,GAAI,CAACW,EAAI,OACT,MAAMtI,EAAO,OAAO2H,EAAO,MAAS,SAAWA,EAAO,KAAK,OAAS,OAC9DymC,EAAYzmC,EAAO,UAAY,GACrCimC,EAAO,KAAK,CAAE,GAAAtlC,EAAI,KAAMtI,GAAQ,OAAW,UAAAouC,EAAW,CACxD,CAAC,EACMR,CACT,CAEA,SAASS,GACPpJ,EACAp6B,EAC4B,CAC5B,MAAMyjC,EAAeJ,GAAoBjJ,CAAM,EACzCsJ,EAAkB,OAAO,KAAK1jC,GAAM,QAAU,CAAA,CAAE,EAChD2jC,MAAa,IACnBF,EAAa,QAASG,GAAUD,EAAO,IAAIC,EAAM,GAAIA,CAAK,CAAC,EAC3DF,EAAgB,QAASjmC,GAAO,CAC1BkmC,EAAO,IAAIlmC,CAAE,GACjBkmC,EAAO,IAAIlmC,EAAI,CAAE,GAAAA,CAAA,CAAI,CACvB,CAAC,EACD,MAAMslC,EAAS,MAAM,KAAKY,EAAO,QAAQ,EACzC,OAAIZ,EAAO,SAAW,GACpBA,EAAO,KAAK,CAAE,GAAI,OAAQ,UAAW,GAAM,EAE7CA,EAAO,KAAK,CAACxwC,EAAGM,IAAM,CACpB,GAAIN,EAAE,WAAa,CAACM,EAAE,UAAW,MAAO,GACxC,GAAI,CAACN,EAAE,WAAaM,EAAE,UAAW,MAAO,GACxC,MAAMgxC,EAAStxC,EAAE,MAAM,OAASA,EAAE,KAAOA,EAAE,GACrCuxC,EAASjxC,EAAE,MAAM,OAASA,EAAE,KAAOA,EAAE,GAC3C,OAAOgxC,EAAO,cAAcC,CAAM,CACpC,CAAC,EACMf,CACT,CAEA,SAASgB,GACPC,EACAjB,EACQ,CACR,OAAIiB,IAAavB,GAAqCA,GAClDuB,GAAYjB,EAAO,KAAMa,GAAUA,EAAM,KAAOI,CAAQ,EAAUA,EAC/DvB,EACT,CAEA,SAASjB,GAA0B7S,EAAuC,CACxE,MAAM3uB,EAAO2uB,EAAM,mBAAqBA,EAAM,uBAAuB,MAAQ,KACvEsU,EAAQ,EAAQjjC,EAChBxK,EAAW4tC,GAA6BpjC,CAAI,EAC5C+iC,EAASS,GAA2B7U,EAAM,WAAY3uB,CAAI,EAC1DikC,EAAcC,GAA0BvV,EAAM,KAAK,EACnDhwB,EAASgwB,EAAM,oBACrB,IAAIwV,EACFxlC,IAAW,QAAUgwB,EAAM,0BACvBA,EAAM,0BACN,KACFhwB,IAAW,QAAUwlC,GAAgB,CAACF,EAAY,KAAM3iB,GAASA,EAAK,KAAO6iB,CAAY,IAC3FA,EAAe,MAEjB,MAAMC,EAAgBL,GAA0BpV,EAAM,2BAA4BoU,CAAM,EAClFsB,EACJD,IAAkB3B,IACZziC,GAAM,QAAU,IAAIokC,CAAa,GACnC,KACA,KACAE,EAAY,MAAM,QAASD,GAA2C,SAAS,EAC/EA,EAAgE,WAChE,CAAA,EACF,CAAA,EACJ,MAAO,CACL,MAAApB,EACA,SAAUtU,EAAM,qBAAuBA,EAAM,qBAC7C,MAAOA,EAAM,mBACb,QAASA,EAAM,qBACf,OAAQA,EAAM,oBACd,KAAA3uB,EACA,SAAAxK,EACA,cAAA4uC,EACA,cAAAC,EACA,OAAAtB,EACA,UAAAuB,EACA,OAAA3lC,EACA,aAAAwlC,EACA,YAAAF,EACA,cAAetV,EAAM,2BACrB,eAAgBA,EAAM,4BACtB,QAASA,EAAM,qBACf,SAAUA,EAAM,sBAChB,OAAQA,EAAM,oBACd,OAAQA,EAAM,mBAAA,CAElB,CAEA,SAAS+S,GAAenmC,EAAqB,CAC3C,MAAMgpC,EAAkBhpC,EAAM,MAAM,OAAS,EACvCu1B,EAAev1B,EAAM,gBAAkB,GAC7C,OAAOhE;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,sBAWagE,EAAM,UAAY,CAACA,EAAM,WAAW;AAAA,mBACvCA,EAAM,MAAM;AAAA;AAAA,YAEnBA,EAAM,aAAe,UAAY,MAAM;AAAA;AAAA;AAAA;AAAA,QAI3CA,EAAM,WAAa,MACjBhE;AAAAA;AAAAA,kBAGAw1B,CAAO;AAAA;AAAA,QAERxxB,EAAM,MAOLhE;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,kCAWwBgE,EAAM,UAAY,CAACgpC,CAAe;AAAA,gCACnCrlC,GAAiB,CAE1B,MAAMpK,EADSoK,EAAM,OACA,MAAM,KAAA,EAC3B3D,EAAM,cAAczG,GAAgB,IAAI,CAC1C,CAAC;AAAA;AAAA,mDAE4Bg8B,IAAiB,EAAE;AAAA,wBAC9Cv1B,EAAM,MAAM,IACX+lB,GACC/pB;AAAAA,oCACU+pB,EAAK,EAAE;AAAA,wCACHwP,IAAiBxP,EAAK,EAAE;AAAA;AAAA,8BAElCA,EAAK,KAAK;AAAA,oCAAA,CAEjB;AAAA;AAAA;AAAA,oBAGFijB,EAECxX,EADAx1B,+DACO;AAAA;AAAA;AAAA;AAAA,gBAIbgE,EAAM,OAAO,SAAW,EACtBhE,6CACAgE,EAAM,OAAO,IAAKqoC,GAChBY,GAAmBZ,EAAOroC,CAAK,CAAA,CAChC;AAAA;AAAA,YA9CThE;AAAAA;AAAAA,4CAEkCgE,EAAM,aAAa,WAAWA,EAAM,YAAY;AAAA,gBAC5EA,EAAM,cAAgB,WAAa,aAAa;AAAA;AAAA,iBA6CrD;AAAA;AAAA,GAGX,CAEA,SAASkmC,GAAoBlmC,EAA2B,CACtD,MAAM0nC,EAAQ1nC,EAAM,MACdkpC,EAAclpC,EAAM,SAAW,QAAU,EAAQA,EAAM,aAC7D,OAAOhE;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,sBAWagE,EAAM,UAAY,CAACA,EAAM,OAAS,CAACkpC,CAAW;AAAA,mBACjDlpC,EAAM,MAAM;AAAA;AAAA,YAEnBA,EAAM,OAAS,UAAY,MAAM;AAAA;AAAA;AAAA;AAAA,QAIrCmpC,GAA0BnpC,CAAK,CAAC;AAAA;AAAA,QAE/B0nC,EAOC1rC;AAAAA,cACIotC,GAAwBppC,CAAK,CAAC;AAAA,cAC9BqpC,GAA0BrpC,CAAK,CAAC;AAAA,cAChCA,EAAM,gBAAkBknC,GACtB1V,EACA8X,GAA6BtpC,CAAK,CAAC;AAAA,YAXzChE;AAAAA;AAAAA,4CAEkCgE,EAAM,SAAW,CAACkpC,CAAW,WAAWlpC,EAAM,MAAM;AAAA,gBAChFA,EAAM,QAAU,WAAa,gBAAgB;AAAA;AAAA,iBASlD;AAAA;AAAA,GAGX,CAEA,SAASmpC,GAA0BnpC,EAA2B,CAC5D,MAAMupC,EAAWvpC,EAAM,YAAY,OAAS,EACtCwpC,EAAYxpC,EAAM,cAAgB,GACxC,OAAOhE;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,0BAaiBgE,EAAM,QAAQ;AAAA,wBACf2D,GAAiB,CAG1B,GAFeA,EAAM,OACA,QACP,OAAQ,CACpB,MAAM8lC,EAAQzpC,EAAM,YAAY,CAAC,GAAG,IAAM,KAC1CA,EAAM,eAAe,OAAQwpC,GAAaC,CAAK,CACjD,MACEzpC,EAAM,eAAe,UAAW,IAAI,CAExC,CAAC;AAAA;AAAA,kDAEmCA,EAAM,SAAW,SAAS;AAAA,+CAC7BA,EAAM,SAAW,MAAM;AAAA;AAAA;AAAA,YAG1DA,EAAM,SAAW,OACfhE;AAAAA;AAAAA;AAAAA;AAAAA,gCAIkBgE,EAAM,UAAY,CAACupC,CAAQ;AAAA,8BAC5B5lC,GAAiB,CAE1B,MAAMpK,EADSoK,EAAM,OACA,MAAM,KAAA,EAC3B3D,EAAM,eAAe,OAAQzG,GAAgB,IAAI,CACnD,CAAC;AAAA;AAAA,iDAE4BiwC,IAAc,EAAE;AAAA,sBAC3CxpC,EAAM,YAAY,IACjB+lB,GACC/pB;AAAAA,kCACU+pB,EAAK,EAAE;AAAA,sCACHyjB,IAAczjB,EAAK,EAAE;AAAA;AAAA,4BAE/BA,EAAK,KAAK;AAAA,kCAAA,CAEjB;AAAA;AAAA;AAAA,gBAIPyL,CAAO;AAAA;AAAA;AAAA,QAGbxxB,EAAM,SAAW,QAAU,CAACupC,EAC1BvtC,mEACAw1B,CAAO;AAAA;AAAA,GAGjB,CAEA,SAAS4X,GAAwBppC,EAA2B,CAC1D,OAAOhE;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,+BAKsBgE,EAAM,gBAAkBknC,GAA+B,SAAW,EAAE;AAAA,mBAChF,IAAMlnC,EAAM,cAAcknC,EAA4B,CAAC;AAAA;AAAA;AAAA;AAAA,UAIhElnC,EAAM,OAAO,IAAKqoC,GAAU,CAC5B,MAAM/pC,EAAQ+pC,EAAM,MAAM,KAAA,EAAS,GAAGA,EAAM,IAAI,KAAKA,EAAM,EAAE,IAAMA,EAAM,GACzE,OAAOrsC;AAAAA;AAAAA,mCAEkBgE,EAAM,gBAAkBqoC,EAAM,GAAK,SAAW,EAAE;AAAA,uBAC5D,IAAMroC,EAAM,cAAcqoC,EAAM,EAAE,CAAC;AAAA;AAAA,gBAE1C/pC,CAAK;AAAA;AAAA,WAGb,CAAC,CAAC;AAAA;AAAA;AAAA,GAIV,CAEA,SAAS+qC,GAA0BrpC,EAA2B,CAC5D,MAAM0pC,EAAa1pC,EAAM,gBAAkBknC,GACrCjtC,EAAW+F,EAAM,SACjBqoC,EAAQroC,EAAM,eAAiB,CAAA,EAC/B/E,EAAWyuC,EAAa,CAAC,UAAU,EAAI,CAAC,SAAU1pC,EAAM,aAAa,EACrE2pC,EAAgB,OAAOtB,EAAM,UAAa,SAAWA,EAAM,SAAW,OACtEuB,EAAW,OAAOvB,EAAM,KAAQ,SAAWA,EAAM,IAAM,OACvDwB,EACJ,OAAOxB,EAAM,aAAgB,SAAWA,EAAM,YAAc,OACxDyB,EAAgBJ,EAAazvC,EAAS,SAAW0vC,GAAiB,cAClEI,EAAWL,EAAazvC,EAAS,IAAM2vC,GAAY,cACnDI,EAAmBN,EACrBzvC,EAAS,YACT4vC,GAAoB,cAClBI,EACJ,OAAO5B,EAAM,iBAAoB,UAAYA,EAAM,gBAAkB,OACjE6B,EAAgBD,GAAgBhwC,EAAS,gBACzCkwC,EAAgBF,GAAgB,KAEtC,OAAOjuC;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,cAMK0tC,EACE,yBACA,YAAYzvC,EAAS,QAAQ,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAOtB+F,EAAM,QAAQ;AAAA,wBACf2D,GAAiB,CAE1B,MAAMpK,EADSoK,EAAM,OACA,MACjB,CAAC+lC,GAAcnwC,IAAU,cAC3ByG,EAAM,SAAS,CAAC,GAAG/E,EAAU,UAAU,CAAC,EAExC+E,EAAM,QAAQ,CAAC,GAAG/E,EAAU,UAAU,EAAG1B,CAAK,CAElD,CAAC;AAAA;AAAA,gBAEEmwC,EAIClY,EAHAx1B,0CAA6C8tC,IAAkB,aAAa;AAAA,mCAC3D7vC,EAAS,QAAQ;AAAA,4BAE3B;AAAA,gBACTktC,GAAiB,IAChBiD,GACCpuC;AAAAA,4BACUouC,EAAO,KAAK;AAAA,gCACRN,IAAkBM,EAAO,KAAK;AAAA;AAAA,sBAExCA,EAAO,KAAK;AAAA,4BAAA,CAEnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAUDV,EAAa,yBAA2B,YAAYzvC,EAAS,GAAG,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAOvD+F,EAAM,QAAQ;AAAA,wBACf2D,GAAiB,CAE1B,MAAMpK,EADSoK,EAAM,OACA,MACjB,CAAC+lC,GAAcnwC,IAAU,cAC3ByG,EAAM,SAAS,CAAC,GAAG/E,EAAU,KAAK,CAAC,EAEnC+E,EAAM,QAAQ,CAAC,GAAG/E,EAAU,KAAK,EAAG1B,CAAK,CAE7C,CAAC;AAAA;AAAA,gBAEEmwC,EAIClY,EAHAx1B,0CAA6C+tC,IAAa,aAAa;AAAA,mCACtD9vC,EAAS,GAAG;AAAA,4BAEtB;AAAA,gBACTmtC,GAAY,IACXgD,GACCpuC;AAAAA,4BACUouC,EAAO,KAAK;AAAA,gCACRL,IAAaK,EAAO,KAAK;AAAA;AAAA,sBAEnCA,EAAO,KAAK;AAAA,4BAAA,CAEnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAUDV,EACE,6CACA,YAAYzvC,EAAS,WAAW,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAOzB+F,EAAM,QAAQ;AAAA,wBACf2D,GAAiB,CAE1B,MAAMpK,EADSoK,EAAM,OACA,MACjB,CAAC+lC,GAAcnwC,IAAU,cAC3ByG,EAAM,SAAS,CAAC,GAAG/E,EAAU,aAAa,CAAC,EAE3C+E,EAAM,QAAQ,CAAC,GAAG/E,EAAU,aAAa,EAAG1B,CAAK,CAErD,CAAC;AAAA;AAAA,gBAEEmwC,EAIClY,EAHAx1B,0CAA6CguC,IAAqB,aAAa;AAAA,mCAC9D/vC,EAAS,WAAW;AAAA,4BAE9B;AAAA,gBACTktC,GAAiB,IAChBiD,GACCpuC;AAAAA,4BACUouC,EAAO,KAAK;AAAA,gCACRJ,IAAqBI,EAAO,KAAK;AAAA;AAAA,sBAE3CA,EAAO,KAAK;AAAA,4BAAA,CAEnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAUDV,EACE,iDACAS,EACE,kBAAkBlwC,EAAS,gBAAkB,KAAO,KAAK,KACzD,aAAaiwC,EAAgB,KAAO,KAAK,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAQrClqC,EAAM,QAAQ;AAAA,yBACfkqC,CAAa;AAAA,wBACbvmC,GAAiB,CAC1B,MAAMP,EAASO,EAAM,OACrB3D,EAAM,QAAQ,CAAC,GAAG/E,EAAU,iBAAiB,EAAGmI,EAAO,OAAO,CAChE,CAAC;AAAA;AAAA;AAAA,YAGH,CAACsmC,GAAc,CAACS,EACdnuC;AAAAA;AAAAA,4BAEcgE,EAAM,QAAQ;AAAA,yBACjB,IAAMA,EAAM,SAAS,CAAC,GAAG/E,EAAU,iBAAiB,CAAC,CAAC;AAAA;AAAA;AAAA,yBAIjEu2B,CAAO;AAAA;AAAA;AAAA;AAAA,GAKrB,CAEA,SAAS8X,GAA6BtpC,EAA2B,CAC/D,MAAMqqC,EAAgB,CAAC,SAAUrqC,EAAM,cAAe,WAAW,EAC3DqI,EAAUrI,EAAM,UACtB,OAAOhE;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,oBAQWgE,EAAM,QAAQ;AAAA,iBACjB,IAAM,CACb,MAAM3F,EAAO,CAAC,GAAGgO,EAAS,CAAE,QAAS,GAAI,EACzCrI,EAAM,QAAQqqC,EAAehwC,CAAI,CACnC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMDgO,EAAQ,SAAW,EACjBrM,sDACAqM,EAAQ,IAAI,CAAC7G,EAAOwc,IAClBssB,GAAqBtqC,EAAOwB,EAAOwc,CAAK,CAAA,CACzC;AAAA;AAAA,GAGX,CAEA,SAASssB,GACPtqC,EACAwB,EACAwc,EACA,CACA,MAAMusB,EAAW/oC,EAAM,WAAatE,EAAUsE,EAAM,UAAU,EAAI,QAC5DgpC,EAAchpC,EAAM,gBACtB9D,GAAU8D,EAAM,gBAAiB,GAAG,EACpC,KACEipC,EAAWjpC,EAAM,iBACnB9D,GAAU8D,EAAM,iBAAkB,GAAG,EACrC,KACJ,OAAOxF;AAAAA;AAAAA;AAAAA,kCAGyBwF,EAAM,SAAS,KAAA,EAASA,EAAM,QAAU,aAAa;AAAA,2CAC5C+oC,CAAQ;AAAA,UACzCC,EAAcxuC,+BAAkCwuC,CAAW,SAAWhZ,CAAO;AAAA,UAC7EiZ,EAAWzuC,+BAAkCyuC,CAAQ,SAAWjZ,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAO5DhwB,EAAM,SAAW,EAAE;AAAA,wBAChBxB,EAAM,QAAQ;AAAA,qBAChB2D,GAAiB,CACzB,MAAMP,EAASO,EAAM,OACrB3D,EAAM,QACJ,CAAC,SAAUA,EAAM,cAAe,YAAage,EAAO,SAAS,EAC7D5a,EAAO,KAAA,CAEX,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKSpD,EAAM,QAAQ;AAAA,mBACjB,IAAM,CACb,GAAIA,EAAM,UAAU,QAAU,EAAG,CAC/BA,EAAM,SAAS,CAAC,SAAUA,EAAM,cAAe,WAAW,CAAC,EAC3D,MACF,CACAA,EAAM,SAAS,CAAC,SAAUA,EAAM,cAAe,YAAage,CAAK,CAAC,CACpE,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOX,CAEA,SAASirB,GAAmBZ,EAAqBroC,EAAqB,CACpE,MAAM0qC,EAAerC,EAAM,SAAW,cAChC/pC,EAAQ+pC,EAAM,MAAM,KAAA,EAAS,GAAGA,EAAM,IAAI,KAAKA,EAAM,EAAE,IAAMA,EAAM,GACnEW,EAAkBhpC,EAAM,MAAM,OAAS,EAC7C,OAAOhE;AAAAA;AAAAA;AAAAA,kCAGyBsC,CAAK;AAAA;AAAA,YAE3B+pC,EAAM,UAAY,gBAAkB,OAAO;AAAA,YAC3CqC,IAAiB,cACf,iBAAiB1qC,EAAM,gBAAkB,KAAK,IAC9C,aAAaqoC,EAAM,OAAO,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAOlBroC,EAAM,UAAY,CAACgpC,CAAe;AAAA,sBACnCrlC,GAAiB,CAE1B,MAAMpK,EADSoK,EAAM,OACA,MAAM,KAAA,EAC3B3D,EAAM,YAAYqoC,EAAM,MAAO9uC,IAAU,cAAgB,KAAOA,CAAK,CACvE,CAAC;AAAA;AAAA,oDAEuCmxC,IAAiB,aAAa;AAAA;AAAA;AAAA,cAGpE1qC,EAAM,MAAM,IACX+lB,GACC/pB;AAAAA,0BACU+pB,EAAK,EAAE;AAAA,8BACH2kB,IAAiB3kB,EAAK,EAAE;AAAA;AAAA,oBAElCA,EAAK,KAAK;AAAA,0BAAA,CAEjB;AAAA;AAAA;AAAA;AAAA;AAAA,GAMb,CAEA,SAASuhB,GAAiBD,EAAsD,CAC9E,MAAMhB,EAAsB,CAAA,EAC5B,UAAWtgB,KAAQshB,EAAO,CAGxB,GAAI,EAFa,MAAM,QAAQthB,EAAK,QAAQ,EAAIA,EAAK,SAAW,CAAA,GACtC,KAAM4kB,GAAQ,OAAOA,CAAG,IAAM,YAAY,EACrD,SACf,MAAM/1B,EAAS,OAAOmR,EAAK,QAAW,SAAWA,EAAK,OAAO,OAAS,GACtE,GAAI,CAACnR,EAAQ,SACb,MAAM4sB,EACJ,OAAOzb,EAAK,aAAgB,UAAYA,EAAK,YAAY,KAAA,EACrDA,EAAK,YAAY,KAAA,EACjBnR,EACNyxB,EAAK,KAAK,CAAE,GAAIzxB,EAAQ,MAAO4sB,IAAgB5sB,EAASA,EAAS,GAAG4sB,CAAW,MAAM5sB,CAAM,GAAI,CACjG,CACA,OAAAyxB,EAAK,KAAK,CAACrvC,EAAGM,IAAMN,EAAE,MAAM,cAAcM,EAAE,KAAK,CAAC,EAC3C+uC,CACT,CAEA,SAASsC,GAA0BtB,EAAkE,CACnG,MAAMhB,EAAkC,CAAA,EACxC,UAAWtgB,KAAQshB,EAAO,CAKxB,GAAI,EAJa,MAAM,QAAQthB,EAAK,QAAQ,EAAIA,EAAK,SAAW,CAAA,GACtC,KACvB4kB,GAAQ,OAAOA,CAAG,IAAM,4BAA8B,OAAOA,CAAG,IAAM,0BAAA,EAE1D,SACf,MAAM/1B,EAAS,OAAOmR,EAAK,QAAW,SAAWA,EAAK,OAAO,OAAS,GACtE,GAAI,CAACnR,EAAQ,SACb,MAAM4sB,EACJ,OAAOzb,EAAK,aAAgB,UAAYA,EAAK,YAAY,KAAA,EACrDA,EAAK,YAAY,KAAA,EACjBnR,EACNyxB,EAAK,KAAK,CAAE,GAAIzxB,EAAQ,MAAO4sB,IAAgB5sB,EAASA,EAAS,GAAG4sB,CAAW,MAAM5sB,CAAM,GAAI,CACjG,CACA,OAAAyxB,EAAK,KAAK,CAACrvC,EAAGM,IAAMN,EAAE,MAAM,cAAcM,EAAE,KAAK,CAAC,EAC3C+uC,CACT,CAEA,SAASoB,GAAqB5I,EAG5B,CACA,MAAM+L,EAA8B,CAClC,GAAI,OACJ,KAAM,OACN,MAAO,EACP,UAAW,GACX,QAAS,IAAA,EAEX,GAAI,CAAC/L,GAAU,OAAOA,GAAW,SAC/B,MAAO,CAAE,eAAgB,KAAM,OAAQ,CAAC+L,CAAa,CAAA,EAGvD,MAAMC,GADShM,EAAO,OAAS,CAAA,GACX,MAAQ,CAAA,EACtB0I,EACJ,OAAOsD,EAAK,MAAS,UAAYA,EAAK,KAAK,KAAA,EAASA,EAAK,KAAK,KAAA,EAAS,KAEnE9C,EAAclJ,EAAO,QAAU,CAAA,EAC/BwH,EAAO,MAAM,QAAQ0B,EAAW,IAAI,EAAIA,EAAW,KAAO,CAAA,EAChE,GAAI1B,EAAK,SAAW,EAClB,MAAO,CAAE,eAAAkB,EAAgB,OAAQ,CAACqD,CAAa,CAAA,EAGjD,MAAMpD,EAAyB,CAAA,EAC/B,OAAAnB,EAAK,QAAQ,CAAC7kC,EAAOwc,IAAU,CAC7B,GAAI,CAACxc,GAAS,OAAOA,GAAU,SAAU,OACzC,MAAMD,EAASC,EACTU,EAAK,OAAOX,EAAO,IAAO,SAAWA,EAAO,GAAG,OAAS,GAC9D,GAAI,CAACW,EAAI,OACT,MAAMtI,EAAO,OAAO2H,EAAO,MAAS,SAAWA,EAAO,KAAK,OAAS,OAC9DymC,EAAYzmC,EAAO,UAAY,GAE/BupC,GADcvpC,EAAO,OAAS,CAAA,GACN,MAAQ,CAAA,EAChCwpC,EACJ,OAAOD,EAAU,MAAS,UAAYA,EAAU,KAAK,KAAA,EACjDA,EAAU,KAAK,KAAA,EACf,KACNtD,EAAO,KAAK,CACV,GAAAtlC,EACA,KAAMtI,GAAQ,OACd,MAAAokB,EACA,UAAAgqB,EACA,QAAA+C,CAAA,CACD,CACH,CAAC,EAEGvD,EAAO,SAAW,GACpBA,EAAO,KAAKoD,CAAa,EAGpB,CAAE,eAAArD,EAAgB,OAAAC,CAAA,CAC3B,CAEA,SAASpR,GAAWrQ,EAA+B,CACjD,MAAMqY,EAAY,EAAQrY,EAAK,UACzBwgB,EAAS,EAAQxgB,EAAK,OACtB5c,EACH,OAAO4c,EAAK,aAAgB,UAAYA,EAAK,YAAY,KAAA,IACzD,OAAOA,EAAK,QAAW,SAAWA,EAAK,OAAS,WAC7CilB,EAAO,MAAM,QAAQjlB,EAAK,IAAI,EAAKA,EAAK,KAAqB,CAAA,EAC7DklB,EAAW,MAAM,QAAQllB,EAAK,QAAQ,EAAKA,EAAK,SAAyB,CAAA,EAC/E,OAAO/pB;AAAAA;AAAAA;AAAAA,kCAGyBmN,CAAK;AAAA;AAAA,YAE3B,OAAO4c,EAAK,QAAW,SAAWA,EAAK,OAAS,EAAE;AAAA,YAClD,OAAOA,EAAK,UAAa,SAAW,MAAMA,EAAK,QAAQ,GAAK,EAAE;AAAA,YAC9D,OAAOA,EAAK,SAAY,SAAW,MAAMA,EAAK,OAAO,GAAK,EAAE;AAAA;AAAA;AAAA,+BAGzCwgB,EAAS,SAAW,UAAU;AAAA,8BAC/BnI,EAAY,UAAY,WAAW;AAAA,cACnDA,EAAY,YAAc,SAAS;AAAA;AAAA,YAErC4M,EAAK,MAAM,EAAG,EAAE,EAAE,IAAKl0C,GAAMkF,uBAA0B,OAAOlF,CAAC,CAAC,SAAS,CAAC;AAAA,YAC1Em0C,EACC,MAAM,EAAG,CAAC,EACV,IAAKn0C,GAAMkF,uBAA0B,OAAOlF,CAAC,CAAC,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA,GAKrE,CCriCO,SAASo0C,GAAe9X,EAAsB,CACnD,MAAMluB,EAAWkuB,EAAM,OAAO,SAGxB+X,EAASjmC,GAAU,SAAW3H,GAAiB2H,EAAS,QAAQ,EAAI,MACpEkmC,EAAOlmC,GAAU,QAAQ,eAC3B,GAAGA,EAAS,OAAO,cAAc,KACjC,MACEmmC,GAAY,IAAM,CACtB,GAAIjY,EAAM,WAAa,CAACA,EAAM,UAAW,OAAO,KAChD,MAAMhY,EAAQgY,EAAM,UAAU,YAAA,EAE9B,GAAI,EADehY,EAAM,SAAS,cAAc,GAAKA,EAAM,SAAS,gBAAgB,GACnE,OAAO,KACxB,MAAMkwB,EAAW,EAAQlY,EAAM,SAAS,MAAM,OACxCmY,EAAc,EAAQnY,EAAM,SAAS,OAC3C,MAAI,CAACkY,GAAY,CAACC,EACTvvC;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,QAoBFA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,KAiBT,GAAA,EACMwvC,GAAuB,IAAM,CAGjC,GAFIpY,EAAM,WAAa,CAACA,EAAM,YACN,OAAO,OAAW,IAAc,OAAO,gBAAkB,MACzD,GAAO,OAAO,KACtC,MAAMhY,EAAQgY,EAAM,UAAU,YAAA,EAC9B,MAAI,CAAChY,EAAM,SAAS,gBAAgB,GAAK,CAACA,EAAM,SAAS,0BAA0B,EAC1E,KAEFpf;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,KA6BT,GAAA,EAEA,OAAOA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,uBASco3B,EAAM,SAAS,UAAU;AAAA,uBACxB98B,GAAa,CACrB,MAAMmB,EAAKnB,EAAE,OAA4B,MACzC88B,EAAM,iBAAiB,CAAE,GAAGA,EAAM,SAAU,WAAY37B,EAAG,CAC7D,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAOQ27B,EAAM,SAAS,KAAK;AAAA,uBACnB98B,GAAa,CACrB,MAAMmB,EAAKnB,EAAE,OAA4B,MACzC88B,EAAM,iBAAiB,CAAE,GAAGA,EAAM,SAAU,MAAO37B,EAAG,CACxD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAQQ27B,EAAM,QAAQ;AAAA,uBACb98B,GAAa,CACrB,MAAMmB,EAAKnB,EAAE,OAA4B,MACzC88B,EAAM,iBAAiB37B,CAAC,CAC1B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAOQ27B,EAAM,SAAS,UAAU;AAAA,uBACxB98B,GAAa,CACrB,MAAMmB,EAAKnB,EAAE,OAA4B,MACzC88B,EAAM,mBAAmB37B,CAAC,CAC5B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,uCAKwB,IAAM27B,EAAM,WAAW;AAAA,uCACvB,IAAMA,EAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCAWzBA,EAAM,UAAY,KAAO,MAAM;AAAA,gBACpDA,EAAM,UAAY,YAAc,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,sCAKxB+X,CAAM;AAAA;AAAA;AAAA;AAAA,sCAINC,CAAI;AAAA;AAAA;AAAA;AAAA;AAAA,gBAK1BhY,EAAM,oBACJl2B,EAAUk2B,EAAM,mBAAmB,EACnC,KAAK;AAAA;AAAA;AAAA;AAAA,UAIbA,EAAM,UACJp3B;AAAAA,qBACSo3B,EAAM,SAAS;AAAA,gBACpBiY,GAAY,EAAE;AAAA,gBACdG,GAAuB,EAAE;AAAA,oBAE7BxvC;AAAAA;AAAAA,mBAEO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kCAOeo3B,EAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,kCAKnBA,EAAM,eAAiB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMlDA,EAAM,aAAe,KACnB,MACAA,EAAM,YACJ,UACA,UAAU;AAAA;AAAA,uCAEa0Q,GAAc1Q,EAAM,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAyBpE,CCjOA,MAAMqY,GAAe,CAAC,GAAI,MAAO,UAAW,MAAO,SAAU,MAAM,EAC7DC,GAAsB,CAAC,GAAI,MAAO,IAAI,EACtCC,GAAiB,CACrB,CAAE,MAAO,GAAI,MAAO,SAAA,EACpB,CAAE,MAAO,MAAO,MAAO,gBAAA,EACvB,CAAE,MAAO,KAAM,MAAO,IAAA,CACxB,EACMC,GAAmB,CAAC,GAAI,MAAO,KAAM,QAAQ,EAEnD,SAASC,GAAoBC,EAAkC,CAC7D,GAAI,CAACA,EAAU,MAAO,GACtB,MAAM1wC,EAAa0wC,EAAS,KAAA,EAAO,YAAA,EACnC,OAAI1wC,IAAe,QAAUA,IAAe,OAAe,MACpDA,CACT,CAEA,SAAS2wC,GAAyBD,EAAmC,CACnE,OAAOD,GAAoBC,CAAQ,IAAM,KAC3C,CAEA,SAASE,GAAyBF,EAA6C,CAC7E,OAAOC,GAAyBD,CAAQ,EAAIJ,GAAsBD,EACpE,CAEA,SAASQ,GAAyB1yC,EAAe2yC,EAA2B,CAE1E,MADI,CAACA,GACD,CAAC3yC,GAASA,IAAU,MAAcA,EAC/B,IACT,CAEA,SAAS4yC,GAA4B5yC,EAAe2yC,EAAkC,CACpF,OAAK3yC,EACA2yC,GACD3yC,IAAU,KAAa,MADLA,EADH,IAIrB,CAEO,SAAS6yC,GAAehZ,EAAsB,CACnD,MAAMiZ,EAAOjZ,EAAM,QAAQ,UAAY,CAAA,EACvC,OAAOp3B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,wCAO+Bo3B,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,YACnEA,EAAM,QAAU,WAAa,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAQ7BA,EAAM,aAAa;AAAA,qBAClB98B,GACR88B,EAAM,gBAAgB,CACpB,cAAgB98B,EAAE,OAA4B,MAC9C,MAAO88B,EAAM,MACb,cAAeA,EAAM,cACrB,eAAgBA,EAAM,cAAA,CACvB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAMKA,EAAM,KAAK;AAAA,qBACV98B,GACR88B,EAAM,gBAAgB,CACpB,cAAeA,EAAM,cACrB,MAAQ98B,EAAE,OAA4B,MACtC,cAAe88B,EAAM,cACrB,eAAgBA,EAAM,cAAA,CACvB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAOOA,EAAM,aAAa;AAAA,sBACnB98B,GACT88B,EAAM,gBAAgB,CACpB,cAAeA,EAAM,cACrB,MAAOA,EAAM,MACb,cAAgB98B,EAAE,OAA4B,QAC9C,eAAgB88B,EAAM,cAAA,CACvB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAOOA,EAAM,cAAc;AAAA,sBACpB98B,GACT88B,EAAM,gBAAgB,CACpB,cAAeA,EAAM,cACrB,MAAOA,EAAM,MACb,cAAeA,EAAM,cACrB,eAAiB98B,EAAE,OAA4B,OAAA,CAChD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,QAKR88B,EAAM,MACJp3B,0DAA6Do3B,EAAM,KAAK,SACxE5B,CAAO;AAAA;AAAA;AAAA,UAGP4B,EAAM,OAAS,UAAUA,EAAM,OAAO,IAAI,GAAK,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAejDiZ,EAAK,SAAW,EACdrwC,+CACAqwC,EAAK,IAAKlY,GACRmY,GAAUnY,EAAKf,EAAM,SAAUA,EAAM,QAASA,EAAM,SAAUA,EAAM,OAAO,CAAA,CAC5E;AAAA;AAAA;AAAA,GAIb,CAEA,SAASkZ,GACPnY,EACAl5B,EACAs7B,EACAgW,EACAjW,EACA,CACA,MAAMnjB,EAAUghB,EAAI,UAAYj3B,EAAUi3B,EAAI,SAAS,EAAI,MACrDqY,EAAcrY,EAAI,eAAiB,GACnCsY,EAAmBV,GAAyB5X,EAAI,aAAa,EAC7DuY,EAAWT,GAAyBO,EAAaC,CAAgB,EACjEE,EAAcX,GAAyB7X,EAAI,aAAa,EACxDyY,EAAUzY,EAAI,cAAgB,GAC9B0Y,EAAY1Y,EAAI,gBAAkB,GAClCqN,EAAcrN,EAAI,aAAeA,EAAI,IACrC2Y,EAAU3Y,EAAI,OAAS,SACvB4Y,EAAUD,EACZ,GAAGzxC,GAAW,OAAQJ,CAAQ,CAAC,YAAY,mBAAmBk5B,EAAI,GAAG,CAAC,GACtE,KAEJ,OAAOn4B;AAAAA;AAAAA,0BAEiB8wC,EAChB9wC,YAAe+wC,CAAO,yBAAyBvL,CAAW,OAC1DA,CAAW;AAAA;AAAA;AAAA,mBAGFrN,EAAI,OAAS,EAAE;AAAA,sBACZmC,CAAQ;AAAA;AAAA,oBAEThgC,GAAa,CACtB,MAAMiD,EAASjD,EAAE,OAA4B,MAAM,KAAA,EACnDigC,EAAQpC,EAAI,IAAK,CAAE,MAAO56B,GAAS,KAAM,CAC3C,CAAC;AAAA;AAAA;AAAA,aAGE46B,EAAI,IAAI;AAAA,aACRhhB,CAAO;AAAA,aACP4wB,GAAoB5P,CAAG,CAAC;AAAA;AAAA;AAAA,mBAGlBuY,CAAQ;AAAA,sBACLpW,CAAQ;AAAA,oBACThgC,GAAa,CACtB,MAAMiD,EAASjD,EAAE,OAA6B,MAC9CigC,EAAQpC,EAAI,IAAK,CACf,cAAegY,GAA4B5yC,EAAOkzC,CAAgB,CAAA,CACnE,CACH,CAAC;AAAA;AAAA,YAECE,EAAY,IAAK5kC,GACjB/L,kBAAqB+L,CAAK,IAAIA,GAAS,SAAS,WAAA,CACjD;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKQ6kC,CAAO;AAAA,sBACJtW,CAAQ;AAAA,oBACThgC,GAAa,CACtB,MAAMiD,EAASjD,EAAE,OAA6B,MAC9CigC,EAAQpC,EAAI,IAAK,CAAE,aAAc56B,GAAS,KAAM,CAClD,CAAC;AAAA;AAAA,YAECoyC,GAAe,IACd5jC,GAAU/L,kBAAqB+L,EAAM,KAAK,IAAIA,EAAM,KAAK,WAAA,CAC3D;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKQ8kC,CAAS;AAAA,sBACNvW,CAAQ;AAAA,oBACThgC,GAAa,CACtB,MAAMiD,EAASjD,EAAE,OAA6B,MAC9CigC,EAAQpC,EAAI,IAAK,CAAE,eAAgB56B,GAAS,KAAM,CACpD,CAAC;AAAA;AAAA,YAECqyC,GAAiB,IAAK7jC,GACtB/L,kBAAqB+L,CAAK,IAAIA,GAAS,SAAS,WAAA,CACjD;AAAA;AAAA;AAAA;AAAA,+CAIoCuuB,CAAQ,WAAW,IAAMiW,EAASpY,EAAI,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMzF,CCnQA,SAAS6Y,GAAgB/vC,EAAoB,CAC3C,MAAMi+B,EAAY,KAAK,IAAI,EAAGj+B,CAAE,EAC1BgwC,EAAe,KAAK,MAAM/R,EAAY,GAAI,EAChD,GAAI+R,EAAe,GAAI,MAAO,GAAGA,CAAY,IAC7C,MAAMC,EAAU,KAAK,MAAMD,EAAe,EAAE,EAC5C,OAAIC,EAAU,GAAW,GAAGA,CAAO,IAE5B,GADO,KAAK,MAAMA,EAAU,EAAE,CACtB,GACjB,CAEA,SAASC,GAAc7uC,EAAe/E,EAAuB,CAC3D,OAAKA,EACEyC,8CAAiDsC,CAAK,gBAAgB/E,CAAK,gBAD/Di4B,CAErB,CAEO,SAAS4b,GAAyBptC,EAAqB,CAC5D,MAAMqtC,EAASrtC,EAAM,kBAAkB,CAAC,EACxC,GAAI,CAACqtC,EAAQ,OAAO7b,EACpB,MAAM8b,EAAUD,EAAO,QACjBE,EAAcF,EAAO,YAAc,KAAK,IAAA,EACxCnS,EAAYqS,EAAc,EAAI,cAAcP,GAAgBO,CAAW,CAAC,GAAK,UAC7EC,EAAaxtC,EAAM,kBAAkB,OAC3C,OAAOhE;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,6CAMoCk/B,CAAS;AAAA;AAAA,YAE1CsS,EAAa,EACXxxC,qCAAwCwxC,CAAU,iBAClDhc,CAAO;AAAA;AAAA,kDAE6B8b,EAAQ,OAAO;AAAA;AAAA,YAErDH,GAAc,OAAQG,EAAQ,IAAI,CAAC;AAAA,YACnCH,GAAc,QAASG,EAAQ,OAAO,CAAC;AAAA,YACvCH,GAAc,UAAWG,EAAQ,UAAU,CAAC;AAAA,YAC5CH,GAAc,MAAOG,EAAQ,GAAG,CAAC;AAAA,YACjCH,GAAc,WAAYG,EAAQ,YAAY,CAAC;AAAA,YAC/CH,GAAc,WAAYG,EAAQ,QAAQ,CAAC;AAAA,YAC3CH,GAAc,MAAOG,EAAQ,GAAG,CAAC;AAAA;AAAA,UAEnCttC,EAAM,kBACJhE,qCAAwCgE,EAAM,iBAAiB,SAC/DwxB,CAAO;AAAA;AAAA;AAAA;AAAA,wBAIKxxB,EAAM,gBAAgB;AAAA,qBACzB,IAAMA,EAAM,2BAA2B,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMjDA,EAAM,gBAAgB;AAAA,qBACzB,IAAMA,EAAM,2BAA2B,cAAc,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMnDA,EAAM,gBAAgB;AAAA,qBACzB,IAAMA,EAAM,2BAA2B,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQnE,CCvDO,SAASytC,GAAara,EAAoB,CAC/C,MAAMsa,EAASta,EAAM,QAAQ,QAAU,CAAA,EACjCua,EAASva,EAAM,OAAO,KAAA,EAAO,YAAA,EAC7BoH,EAAWmT,EACbD,EAAO,OAAQE,GACb,CAACA,EAAM,KAAMA,EAAM,YAAaA,EAAM,MAAM,EACzC,KAAK,GAAG,EACR,YAAA,EACA,SAASD,CAAM,CAAA,EAEpBD,EAEJ,OAAO1xC;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,wCAO+Bo3B,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,YACnEA,EAAM,QAAU,WAAa,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAQ7BA,EAAM,MAAM;AAAA,qBACX98B,GACR88B,EAAM,eAAgB98B,EAAE,OAA4B,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA,6BAI3CkkC,EAAS,MAAM;AAAA;AAAA;AAAA,QAGpCpH,EAAM,MACJp3B,0DAA6Do3B,EAAM,KAAK,SACxE5B,CAAO;AAAA;AAAA,QAETgJ,EAAS,SAAW,EAClBx+B,uEACAA;AAAAA;AAAAA,gBAEMw+B,EAAS,IAAKoT,GAAUC,GAAYD,EAAOxa,CAAK,CAAC,CAAC;AAAA;AAAA,WAEvD;AAAA;AAAA,GAGX,CAEA,SAASya,GAAYD,EAAyBxa,EAAoB,CAChE,MAAM0a,EAAO1a,EAAM,UAAYwa,EAAM,SAC/B/3B,EAASud,EAAM,MAAMwa,EAAM,QAAQ,GAAK,GACxCnvC,EAAU20B,EAAM,SAASwa,EAAM,QAAQ,GAAK,KAC5CG,EACJH,EAAM,QAAQ,OAAS,GAAKA,EAAM,QAAQ,KAAK,OAAS,EACpDI,EAAU,CACd,GAAGJ,EAAM,QAAQ,KAAK,IAAKt2C,GAAM,OAAOA,CAAC,EAAE,EAC3C,GAAGs2C,EAAM,QAAQ,IAAI,IAAKt3C,GAAM,OAAOA,CAAC,EAAE,EAC1C,GAAGs3C,EAAM,QAAQ,OAAO,IAAK92C,GAAM,UAAUA,CAAC,EAAE,EAChD,GAAG82C,EAAM,QAAQ,GAAG,IAAKp3C,GAAM,MAAMA,CAAC,EAAE,CAAA,EAEpCy3C,EAAoB,CAAA,EAC1B,OAAIL,EAAM,UAAUK,EAAQ,KAAK,UAAU,EACvCL,EAAM,oBAAoBK,EAAQ,KAAK,sBAAsB,EAC1DjyC;AAAAA;AAAAA;AAAAA;AAAAA,YAIG4xC,EAAM,MAAQ,GAAGA,EAAM,KAAK,IAAM,EAAE,GAAGA,EAAM,IAAI;AAAA;AAAA,gCAE7BlwC,GAAUkwC,EAAM,YAAa,GAAG,CAAC;AAAA;AAAA,+BAElCA,EAAM,MAAM;AAAA,8BACbA,EAAM,SAAW,UAAY,WAAW;AAAA,cACxDA,EAAM,SAAW,WAAa,SAAS;AAAA;AAAA,YAEzCA,EAAM,SAAW5xC,gDAAqDw1B,CAAO;AAAA;AAAA,UAE/Ewc,EAAQ,OAAS,EACfhyC;AAAAA;AAAAA,2BAEegyC,EAAQ,KAAK,IAAI,CAAC;AAAA;AAAA,cAGjCxc,CAAO;AAAA,UACTyc,EAAQ,OAAS,EACfjyC;AAAAA;AAAAA,0BAEciyC,EAAQ,KAAK,IAAI,CAAC;AAAA;AAAA,cAGhCzc,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMKsc,CAAI;AAAA,qBACP,IAAM1a,EAAM,SAASwa,EAAM,SAAUA,EAAM,QAAQ,CAAC;AAAA;AAAA,cAE3DA,EAAM,SAAW,SAAW,SAAS;AAAA;AAAA,YAEvCG,EACE/xC;AAAAA;AAAAA,4BAEc8xC,CAAI;AAAA,yBACP,IACP1a,EAAM,UAAUwa,EAAM,SAAUA,EAAM,KAAMA,EAAM,QAAQ,CAAC,EAAE,EAAE,CAAC;AAAA;AAAA,kBAEhEE,EAAO,cAAgBF,EAAM,QAAQ,CAAC,EAAE,KAAK;AAAA,yBAEjDpc,CAAO;AAAA;AAAA,UAEX/yB,EACEzC;AAAAA;AAAAA,+CAGIyC,EAAQ,OAAS,QACb,+BACA,+BACN;AAAA;AAAA,gBAEEA,EAAQ,OAAO;AAAA,oBAEnB+yB,CAAO;AAAA,UACToc,EAAM,WACJ5xC;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,2BAKe6Z,CAAM;AAAA,2BACLvf,GACR88B,EAAM,OAAOwa,EAAM,SAAWt3C,EAAE,OAA4B,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAM1Dw3C,CAAI;AAAA,yBACP,IAAM1a,EAAM,UAAUwa,EAAM,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA,cAKlDpc,CAAO;AAAA;AAAA;AAAA,GAInB,CClKO,SAAS0c,GAAUluC,EAAqBlF,EAAU,CACvD,MAAMqzC,EAAO9yC,GAAWP,EAAKkF,EAAM,QAAQ,EAC3C,OAAOhE;AAAAA;AAAAA,aAEImyC,CAAI;AAAA,wBACOnuC,EAAM,MAAQlF,EAAM,SAAW,EAAE;AAAA,eACzC6I,GAAsB,CAE5BA,EAAM,kBACNA,EAAM,SAAW,GACjBA,EAAM,SACNA,EAAM,SACNA,EAAM,UACNA,EAAM,SAIRA,EAAM,eAAA,EACN3D,EAAM,OAAOlF,CAAG,EAClB,CAAC;AAAA,cACOe,GAAYf,CAAG,CAAC;AAAA;AAAA,wDAE0BiB,EAAMH,GAAWd,CAAG,CAAC,CAAC;AAAA,qCACzCe,GAAYf,CAAG,CAAC;AAAA;AAAA,GAGrD,CAEO,SAASszC,GAAmBpuC,EAAqB,CACtD,MAAMquC,EAAiBC,GAAsBtuC,EAAM,WAAYA,EAAM,cAAc,EAC7EuuC,EAAwBvuC,EAAM,WAC9BwuC,EAAqBxuC,EAAM,WAC3ByuC,EAAezuC,EAAM,WAAa,GAAQA,EAAM,SAAS,iBACzD0uC,EAAc1uC,EAAM,WAAa,GAAOA,EAAM,SAAS,cAEvD2uC,EAAc3yC,2PACd4yC,EAAY5yC,iTAClB,OAAOA;AAAAA;AAAAA;AAAAA;AAAAA,mBAIUgE,EAAM,UAAU;AAAA,sBACb,CAACA,EAAM,SAAS;AAAA,oBACjB1J,GAAa,CACtB,MAAM+D,EAAQ/D,EAAE,OAA6B,MAC7C0J,EAAM,WAAa3F,EACnB2F,EAAM,YAAc,GACpBA,EAAM,WAAa,KACnBA,EAAM,oBAAsB,KAC5BA,EAAM,UAAY,KAClBA,EAAM,gBAAA,EACNA,EAAM,gBAAA,EACNA,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,WAAY3F,EACZ,qBAAsBA,CAAA,CACvB,EACI2F,EAAM,sBAAA,EACXyZ,GAAsBzZ,EAAO3F,CAAU,EAClC0F,GAAgBC,CAAK,CAC5B,CAAC;AAAA;AAAA,YAEC00B,GACA2Z,EACC7sC,GAAUA,EAAM,IAChBA,GACCxF,kBAAqBwF,EAAM,GAAG;AAAA,kBAC1BA,EAAM,aAAeA,EAAM,GAAG;AAAA,wBAAA,CAErC;AAAA;AAAA;AAAA;AAAA;AAAA,oBAKSxB,EAAM,aAAe,CAACA,EAAM,SAAS;AAAA,iBACxC,IAAM,CACbA,EAAM,gBAAA,EACDD,GAAgBC,CAAK,CAC5B,CAAC;AAAA;AAAA;AAAA,UAGC2uC,CAAW;AAAA;AAAA;AAAA;AAAA,uCAIkBF,EAAe,SAAW,EAAE;AAAA,oBAC/CF,CAAqB;AAAA,iBACxB,IAAM,CACTA,GACJvuC,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,iBAAkB,CAACA,EAAM,SAAS,gBAAA,CACnC,CACH,CAAC;AAAA,uBACcyuC,CAAY;AAAA,gBACnBF,EACJ,6BACA,0CAA0C;AAAA;AAAA,UAE5CxyC,EAAM,KAAK;AAAA;AAAA;AAAA,uCAGkB2yC,EAAc,SAAW,EAAE;AAAA,oBAC9CF,CAAkB;AAAA,iBACrB,IAAM,CACTA,GACJxuC,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,cAAe,CAACA,EAAM,SAAS,aAAA,CAChC,CACH,CAAC;AAAA,uBACc0uC,CAAW;AAAA,gBAClBF,EACJ,6BACA,gDAAgD;AAAA;AAAA,UAElDI,CAAS;AAAA;AAAA;AAAA,GAInB,CAEA,SAASN,GAAsB/zC,EAAoBs0C,EAAqC,CACtF,MAAMrK,MAAW,IACXhoC,EAAwD,CAAA,EAExDsyC,EAAkBD,GAAU,UAAU,KAAMt4C,GAAMA,EAAE,MAAQgE,CAAU,EAO5E,GAJAiqC,EAAK,IAAIjqC,CAAU,EACnBiC,EAAQ,KAAK,CAAE,IAAKjC,EAAY,YAAau0C,GAAiB,YAAa,EAGvED,GAAU,SACZ,UAAWt4C,KAAKs4C,EAAS,SAClBrK,EAAK,IAAIjuC,EAAE,GAAG,IACjBiuC,EAAK,IAAIjuC,EAAE,GAAG,EACdiG,EAAQ,KAAK,CAAE,IAAKjG,EAAE,IAAK,YAAaA,EAAE,YAAa,GAK7D,OAAOiG,CACT,CAEA,MAAMuyC,GAA2B,CAAC,SAAU,QAAS,MAAM,EAEpD,SAASC,GAAkBhvC,EAAqB,CACrD,MAAMge,EAAQ,KAAK,IAAI,EAAG+wB,GAAY,QAAQ/uC,EAAM,KAAK,CAAC,EACpDwW,EAAcnc,GAAqBsJ,GAAsB,CAE7D,MAAM8S,EAAkC,CAAE,QAD1B9S,EAAM,aACoB,GACtCA,EAAM,SAAWA,EAAM,WACzB8S,EAAQ,eAAiB9S,EAAM,QAC/B8S,EAAQ,eAAiB9S,EAAM,SAEjC3D,EAAM,SAAS3F,EAAMoc,CAAO,CAC9B,EAEA,OAAOza;AAAAA,sDAC6CgiB,CAAK;AAAA;AAAA;AAAA;AAAA,wCAInBhe,EAAM,QAAU,SAAW,SAAW,EAAE;AAAA,mBAC7DwW,EAAW,QAAQ,CAAC;AAAA,yBACdxW,EAAM,QAAU,QAAQ;AAAA;AAAA;AAAA;AAAA,YAIrCivC,IAAmB;AAAA;AAAA;AAAA,wCAGSjvC,EAAM,QAAU,QAAU,SAAW,EAAE;AAAA,mBAC5DwW,EAAW,OAAO,CAAC;AAAA,yBACbxW,EAAM,QAAU,OAAO;AAAA;AAAA;AAAA;AAAA,YAIpCkvC,IAAe;AAAA;AAAA;AAAA,wCAGalvC,EAAM,QAAU,OAAS,SAAW,EAAE;AAAA,mBAC3DwW,EAAW,MAAM,CAAC;AAAA,yBACZxW,EAAM,QAAU,MAAM;AAAA;AAAA;AAAA;AAAA,YAInCmvC,IAAgB;AAAA;AAAA;AAAA;AAAA,GAK5B,CAEA,SAASD,IAAgB,CACvB,OAAOlzC;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,GAaT,CAEA,SAASmzC,IAAiB,CACxB,OAAOnzC;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,GAOT,CAEA,SAASizC,IAAoB,CAC3B,OAAOjzC;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,GAOT,CC7JA,MAAMozC,GAAiB,UACjBC,GAAiB,gBAEvB,SAASC,GAA0BtvC,EAAyC,CAC1E,MAAMqmC,EAAOrmC,EAAM,YAAY,QAAU,CAAA,EAEnCvF,EADSH,GAAqB0F,EAAM,UAAU,GAE1C,SACRA,EAAM,YAAY,WAClB,OAEIoT,EADQizB,EAAK,KAAM7kC,GAAUA,EAAM,KAAO/G,CAAO,GAC/B,SAClBiB,EAAY0X,GAAU,WAAaA,GAAU,OACnD,GAAK1X,EACL,OAAI0zC,GAAe,KAAK1zC,CAAS,GAAK2zC,GAAe,KAAK3zC,CAAS,EAAUA,EACtE0X,GAAU,SACnB,CAEO,SAASm8B,GAAUvvC,EAAqB,CAC7C,MAAMwvC,EAAgBxvC,EAAM,gBAAgB,OACtCyvC,EAAgBzvC,EAAM,gBAAgB,OAAS,KAC/C0vC,EAAW1vC,EAAM,YAAY,cAAgB,KAC7C2vC,EAAqB3vC,EAAM,UAAY,KAAO,6BAC9C4vC,EAAS5vC,EAAM,MAAQ,OACvB6vC,EAAYD,IAAW5vC,EAAM,SAAS,eAAiBA,EAAM,YAC7DyuC,EAAezuC,EAAM,WAAa,GAAQA,EAAM,SAAS,iBACzD8vC,EAAqBR,GAA0BtvC,CAAK,EACpD+vC,EAAgB/vC,EAAM,eAAiB8vC,GAAsB,KAEnE,OAAO9zC;AAAAA,wBACe4zC,EAAS,cAAgB,EAAE,IAAIC,EAAY,oBAAsB,EAAE,IAAI7vC,EAAM,SAAS,aAAe,uBAAyB,EAAE,IAAIA,EAAM,WAAa,oBAAsB,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA,qBAKlL,IACPA,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,aAAc,CAACA,EAAM,SAAS,YAAA,CAC/B,CAAC;AAAA,qBACKA,EAAM,SAAS,aAAe,iBAAmB,kBAAkB;AAAA,0BAC9DA,EAAM,SAAS,aAAe,iBAAmB,kBAAkB;AAAA;AAAA,sDAEvCjE,EAAM,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCAc3BiE,EAAM,UAAY,KAAO,EAAE;AAAA;AAAA,iCAE/BA,EAAM,UAAY,KAAO,SAAS;AAAA;AAAA,YAEvDgvC,GAAkBhvC,CAAK,CAAC;AAAA;AAAA;AAAA,0BAGVA,EAAM,SAAS,aAAe,iBAAmB,EAAE;AAAA,UACnErF,GAAW,IAAKq3B,GAAU,CAC1B,MAAMge,EAAmBhwC,EAAM,SAAS,mBAAmBgyB,EAAM,KAAK,GAAK,GACrEie,EAAeje,EAAM,KAAK,KAAMl3B,GAAQA,IAAQkF,EAAM,GAAG,EAC/D,OAAOhE;AAAAA,oCACmBg0C,GAAoB,CAACC,EAAe,uBAAyB,EAAE;AAAA;AAAA;AAAA,yBAG1E,IAAM,CACb,MAAM51C,EAAO,CAAE,GAAG2F,EAAM,SAAS,kBAAA,EACjC3F,EAAK23B,EAAM,KAAK,EAAI,CAACge,EACrBhwC,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,mBAAoB3F,CAAA,CACrB,CACH,CAAC;AAAA,gCACe,CAAC21C,CAAgB;AAAA;AAAA,gDAEDhe,EAAM,KAAK;AAAA,mDACRge,EAAmB,IAAM,GAAG;AAAA;AAAA;AAAA,kBAG7Dhe,EAAM,KAAK,IAAKl3B,GAAQozC,GAAUluC,EAAOlF,CAAG,CAAC,CAAC;AAAA;AAAA;AAAA,WAIxD,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gEAasDiB,EAAM,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAM7C6zC,EAAS,gBAAkB,EAAE;AAAA;AAAA;AAAA,sCAGpB/zC,GAAYmE,EAAM,GAAG,CAAC;AAAA,oCACxBlE,GAAekE,EAAM,GAAG,CAAC;AAAA;AAAA;AAAA,cAG/CA,EAAM,UACJhE,6BAAgCgE,EAAM,SAAS,SAC/CwxB,CAAO;AAAA,cACToe,EAASxB,GAAmBpuC,CAAK,EAAIwxB,CAAO;AAAA;AAAA;AAAA;AAAA,UAIhDxxB,EAAM,MAAQ,WACZkrC,GAAe,CACb,UAAWlrC,EAAM,UACjB,MAAOA,EAAM,MACb,SAAUA,EAAM,SAChB,SAAUA,EAAM,SAChB,UAAWA,EAAM,UACjB,cAAAwvC,EACA,cAAAC,EACA,YAAazvC,EAAM,YAAY,SAAW,KAC1C,SAAA0vC,EACA,oBAAqB1vC,EAAM,oBAC3B,iBAAmB3F,GAAS2F,EAAM,cAAc3F,CAAI,EACpD,iBAAmBA,GAAU2F,EAAM,SAAW3F,EAC9C,mBAAqBA,GAAS,CAC5B2F,EAAM,WAAa3F,EACnB2F,EAAM,YAAc,GACpBA,EAAM,gBAAA,EACNA,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,WAAY3F,EACZ,qBAAsBA,CAAA,CACvB,EACI2F,EAAM,sBAAA,CACb,EACA,UAAW,IAAMA,EAAM,QAAA,EACvB,UAAW,IAAMA,EAAM,aAAA,CAAa,CACrC,EACDwxB,CAAO;AAAA;AAAA,UAETxxB,EAAM,MAAQ,WACZuiC,GAAe,CACb,UAAWviC,EAAM,UACjB,QAASA,EAAM,gBACf,SAAUA,EAAM,iBAChB,UAAWA,EAAM,cACjB,cAAeA,EAAM,oBACrB,gBAAiBA,EAAM,qBACvB,kBAAmBA,EAAM,uBACzB,kBAAmBA,EAAM,uBACzB,aAAcA,EAAM,aACpB,aAAcA,EAAM,aACpB,oBAAqBA,EAAM,oBAC3B,WAAYA,EAAM,WAClB,cAAeA,EAAM,cACrB,aAAcA,EAAM,aACpB,gBAAiBA,EAAM,gBACvB,sBAAuBA,EAAM,sBAC7B,sBAAuBA,EAAM,sBAC7B,UAAY4G,GAAUD,GAAa3G,EAAO4G,CAAK,EAC/C,gBAAkBtE,GAAUtC,EAAM,oBAAoBsC,CAAK,EAC3D,eAAgB,IAAMtC,EAAM,mBAAA,EAC5B,iBAAkB,IAAMA,EAAM,qBAAA,EAC9B,cAAe,CAACjF,EAAMxB,IAAUiM,GAAsBxF,EAAOjF,EAAMxB,CAAK,EACxE,aAAc,IAAMyG,EAAM,wBAAA,EAC1B,eAAgB,IAAMA,EAAM,0BAAA,EAC5B,mBAAoB,CAAC6/B,EAAWS,IAC9BtgC,EAAM,uBAAuB6/B,EAAWS,CAAO,EACjD,qBAAsB,IAAMtgC,EAAM,yBAAA,EAClC,0BAA2B,CAACggC,EAAOzmC,IACjCyG,EAAM,8BAA8BggC,EAAOzmC,CAAK,EAClD,mBAAoB,IAAMyG,EAAM,uBAAA,EAChC,qBAAsB,IAAMA,EAAM,yBAAA,EAClC,6BAA8B,IAAMA,EAAM,iCAAA,CAAiC,CAC5E,EACDwxB,CAAO;AAAA;AAAA,UAETxxB,EAAM,MAAQ,YACZilC,GAAgB,CACd,QAASjlC,EAAM,gBACf,QAASA,EAAM,gBACf,UAAWA,EAAM,cACjB,cAAeA,EAAM,eACrB,UAAW,IAAMqV,GAAarV,CAAK,CAAA,CACpC,EACDwxB,CAAO;AAAA;AAAA,UAETxxB,EAAM,MAAQ,WACZosC,GAAe,CACb,QAASpsC,EAAM,gBACf,OAAQA,EAAM,eACd,MAAOA,EAAM,cACb,cAAeA,EAAM,qBACrB,MAAOA,EAAM,oBACb,cAAeA,EAAM,sBACrB,eAAgBA,EAAM,uBACtB,SAAUA,EAAM,SAChB,gBAAkB3F,GAAS,CACzB2F,EAAM,qBAAuB3F,EAAK,cAClC2F,EAAM,oBAAsB3F,EAAK,MACjC2F,EAAM,sBAAwB3F,EAAK,cACnC2F,EAAM,uBAAyB3F,EAAK,cACrC,EACA,UAAW,IAAMsG,GAAaX,CAAK,EACnC,QAAS,CAACgB,EAAKC,IAAUF,GAAaf,EAAOgB,EAAKC,CAAK,EACvD,SAAWD,GAAQE,GAAclB,EAAOgB,CAAG,CAAA,CAC5C,EACDwwB,CAAO;AAAA;AAAA,UAEVxxB,EAAM,MAAQ,OACZykC,GAAW,CACT,QAASzkC,EAAM,YACf,OAAQA,EAAM,WACd,KAAMA,EAAM,SACZ,MAAOA,EAAM,UACb,KAAMA,EAAM,SACZ,KAAMA,EAAM,SACZ,SAAUA,EAAM,kBAAkB,aAAa,OAC3CA,EAAM,iBAAiB,YAAY,IAAKwB,GAAUA,EAAM,EAAE,EAC1DxB,EAAM,kBAAkB,cAAgB,CAAA,EAC5C,cAAeA,EAAM,kBAAkB,eAAiB,CAAA,EACxD,YAAaA,EAAM,kBAAkB,aAAe,CAAA,EACpD,UAAWA,EAAM,cACjB,KAAMA,EAAM,SACZ,aAAeiB,GAAWjB,EAAM,SAAW,CAAE,GAAGA,EAAM,SAAU,GAAGiB,CAAA,EACnE,UAAW,IAAMjB,EAAM,SAAA,EACvB,MAAO,IAAMkG,GAAWlG,CAAK,EAC7B,SAAU,CAACoG,EAAKE,IAAYD,GAAcrG,EAAOoG,EAAKE,CAAO,EAC7D,MAAQF,GAAQG,GAAWvG,EAAOoG,CAAG,EACrC,SAAWA,GAAQK,GAAczG,EAAOoG,CAAG,EAC3C,WAAaM,GAAUF,GAAaxG,EAAO0G,CAAK,CAAA,CACjD,EACD8qB,CAAO;AAAA;AAAA,UAETxxB,EAAM,MAAQ,SACZytC,GAAa,CACX,QAASztC,EAAM,cACf,OAAQA,EAAM,aACd,MAAOA,EAAM,YACb,OAAQA,EAAM,aACd,MAAOA,EAAM,WACb,SAAUA,EAAM,cAChB,QAASA,EAAM,cACf,eAAiB3F,GAAU2F,EAAM,aAAe3F,EAChD,UAAW,IAAMmb,GAAWxV,EAAO,CAAE,cAAe,GAAM,EAC1D,SAAU,CAACgB,EAAKsF,IAAYqP,GAAmB3V,EAAOgB,EAAKsF,CAAO,EAClE,OAAQ,CAACtF,EAAKzH,IAAUkc,GAAgBzV,EAAOgB,EAAKzH,CAAK,EACzD,UAAYyH,GAAQ4U,GAAgB5V,EAAOgB,CAAG,EAC9C,UAAW,CAAC0U,EAAU9b,EAAMmc,IAC1BD,GAAa9V,EAAO0V,EAAU9b,EAAMmc,CAAS,CAAA,CAChD,EACDyb,CAAO;AAAA;AAAA,UAETxxB,EAAM,MAAQ,QACZ6lC,GAAY,CACV,QAAS7lC,EAAM,aACf,MAAOA,EAAM,MACb,eAAgBA,EAAM,eACtB,aAAcA,EAAM,aACpB,YAAaA,EAAM,YACnB,WAAYA,EAAM,YAAeA,EAAM,gBAAgB,OACvD,cAAeA,EAAM,cACrB,aAAcA,EAAM,aACpB,YAAaA,EAAM,gBACnB,eAAgBA,EAAM,eACtB,qBAAsBA,EAAM,qBAC5B,oBAAqBA,EAAM,oBAC3B,mBAAoBA,EAAM,mBAC1B,sBAAuBA,EAAM,sBAC7B,kBAAmBA,EAAM,kBACzB,2BAA4BA,EAAM,2BAClC,oBAAqBA,EAAM,oBAC3B,0BAA2BA,EAAM,0BACjC,UAAW,IAAM0U,GAAU1U,CAAK,EAChC,iBAAkB,IAAMoU,GAAYpU,CAAK,EACzC,gBAAkBsU,GAAcD,GAAqBrU,EAAOsU,CAAS,EACrE,eAAiBA,GAAcC,GAAoBvU,EAAOsU,CAAS,EACnE,eAAgB,CAAC0yB,EAAUtoC,EAAMiV,IAC/Ba,GAAkBxU,EAAO,CAAE,SAAAgnC,EAAU,KAAAtoC,EAAM,OAAAiV,EAAQ,EACrD,eAAgB,CAACqzB,EAAUtoC,IACzB+V,GAAkBzU,EAAO,CAAE,SAAAgnC,EAAU,KAAAtoC,EAAM,EAC7C,aAAc,IAAMoG,GAAW9E,CAAK,EACpC,oBAAqB,IAAM,CACzB,MAAMoD,EACJpD,EAAM,sBAAwB,QAAUA,EAAM,0BAC1C,CAAE,KAAM,OAAiB,OAAQA,EAAM,yBAAA,EACvC,CAAE,KAAM,SAAA,EACd,OAAO8U,GAAkB9U,EAAOoD,CAAM,CACxC,EACA,cAAgBwR,GAAW,CACrBA,EACFpP,GAAsBxF,EAAO,CAAC,QAAS,OAAQ,MAAM,EAAG4U,CAAM,EAE9DnP,GAAsBzF,EAAO,CAAC,QAAS,OAAQ,MAAM,CAAC,CAE1D,EACA,YAAa,CAACkwC,EAAYt7B,IAAW,CACnC,MAAM3Z,EAAW,CAAC,SAAU,OAAQi1C,EAAY,QAAS,OAAQ,MAAM,EACnEt7B,EACFpP,GAAsBxF,EAAO/E,EAAU2Z,CAAM,EAE7CnP,GAAsBzF,EAAO/E,CAAQ,CAEzC,EACA,eAAgB,IAAMmK,GAAWpF,CAAK,EACtC,4BAA6B,CAAC0wB,EAAM9b,IAAW,CAC7C5U,EAAM,oBAAsB0wB,EAC5B1wB,EAAM,0BAA4B4U,EAClC5U,EAAM,sBAAwB,KAC9BA,EAAM,kBAAoB,KAC1BA,EAAM,mBAAqB,GAC3BA,EAAM,2BAA6B,IACrC,EACA,2BAA6BvF,GAAY,CACvCuF,EAAM,2BAA6BvF,CACrC,EACA,qBAAsB,CAACM,EAAMxB,IAC3B4b,GAA6BnV,EAAOjF,EAAMxB,CAAK,EACjD,sBAAwBwB,GACtBqa,GAA6BpV,EAAOjF,CAAI,EAC1C,oBAAqB,IAAM,CACzB,MAAMqI,EACJpD,EAAM,sBAAwB,QAAUA,EAAM,0BAC1C,CAAE,KAAM,OAAiB,OAAQA,EAAM,yBAAA,EACvC,CAAE,KAAM,SAAA,EACd,OAAOiV,GAAkBjV,EAAOoD,CAAM,CACxC,CAAA,CACD,EACDouB,CAAO;AAAA;AAAA,UAETxxB,EAAM,MAAQ,OACZ8zB,GAAW,CACT,WAAY9zB,EAAM,WAClB,mBAAqB3F,GAAS,CAC5B2F,EAAM,WAAa3F,EACnB2F,EAAM,YAAc,GACpBA,EAAM,WAAa,KACnBA,EAAM,oBAAsB,KAC5BA,EAAM,UAAY,KAClBA,EAAM,UAAY,CAAA,EAClBA,EAAM,gBAAA,EACNA,EAAM,gBAAA,EACNA,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,WAAY3F,EACZ,qBAAsBA,CAAA,CACvB,EACI2F,EAAM,sBAAA,EACND,GAAgBC,CAAK,EACrBqa,GAAkBra,CAAK,CAC9B,EACA,cAAeA,EAAM,kBACrB,aAAAyuC,EACA,QAASzuC,EAAM,YACf,QAASA,EAAM,YACf,iBAAkBA,EAAM,iBACxB,mBAAoB+vC,EACpB,SAAU/vC,EAAM,aAChB,aAAcA,EAAM,iBACpB,OAAQA,EAAM,WACd,gBAAiBA,EAAM,oBACvB,MAAOA,EAAM,YACb,MAAOA,EAAM,UACb,UAAWA,EAAM,UACjB,QAASA,EAAM,UACf,eAAgB2vC,EAChB,MAAO3vC,EAAM,UACb,SAAUA,EAAM,eAChB,UAAW6vC,EACX,UAAW,KACT7vC,EAAM,gBAAA,EACC,QAAQ,IAAI,CAACD,GAAgBC,CAAK,EAAGqa,GAAkBra,CAAK,CAAC,CAAC,GAEvE,kBAAmB,IAAM,CACnBA,EAAM,YACVA,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,cAAe,CAACA,EAAM,SAAS,aAAA,CAChC,CACH,EACA,aAAe2D,GAAU3D,EAAM,iBAAiB2D,CAAK,EACrD,cAAgBtJ,GAAU2F,EAAM,YAAc3F,EAC9C,OAAQ,IAAM2F,EAAM,eAAA,EACpB,SAAU,EAAQA,EAAM,UACxB,QAAS,IAAA,CAAWA,EAAM,gBAAA,GAC1B,cAAgBkC,GAAOlC,EAAM,oBAAoBkC,CAAE,EACnD,aAAc,IACZlC,EAAM,eAAe,OAAQ,CAAE,aAAc,GAAM,EAErD,YAAaA,EAAM,YACnB,eAAgBA,EAAM,eACtB,aAAcA,EAAM,aACpB,WAAYA,EAAM,WAClB,cAAgBrB,GAAoBqB,EAAM,kBAAkBrB,CAAO,EACnE,eAAgB,IAAMqB,EAAM,mBAAA,EAC5B,mBAAqBmwC,GAAkBnwC,EAAM,uBAAuBmwC,CAAK,EACzE,cAAenwC,EAAM,cACrB,gBAAiBA,EAAM,eAAA,CACxB,EACDwxB,CAAO;AAAA;AAAA,UAETxxB,EAAM,MAAQ,SACZw8B,GAAa,CACX,IAAKx8B,EAAM,UACX,YAAaA,EAAM,kBACnB,MAAOA,EAAM,YACb,OAAQA,EAAM,aACd,QAASA,EAAM,cACf,OAAQA,EAAM,aACd,SAAUA,EAAM,eAChB,SAAUA,EAAM,cAChB,UAAWA,EAAM,UACjB,OAAQA,EAAM,aACd,cAAeA,EAAM,oBACrB,QAASA,EAAM,cACf,SAAUA,EAAM,eAChB,UAAWA,EAAM,WACjB,cAAeA,EAAM,mBACrB,YAAaA,EAAM,kBACnB,cAAeA,EAAM,oBACrB,iBAAkBA,EAAM,uBACxB,YAAc3F,GAAS,CACrB2F,EAAM,UAAY3F,CACpB,EACA,iBAAmBgC,GAAU2D,EAAM,eAAiB3D,EACpD,YAAa,CAACtB,EAAMxB,IAAUiM,GAAsBxF,EAAOjF,EAAMxB,CAAK,EACtE,eAAiB+/B,GAAWt5B,EAAM,kBAAoBs5B,EACtD,gBAAkBuE,GAAY,CAC5B79B,EAAM,oBAAsB69B,EAC5B79B,EAAM,uBAAyB,IACjC,EACA,mBAAqB69B,GAAa79B,EAAM,uBAAyB69B,EACjE,SAAU,IAAM/4B,GAAW9E,CAAK,EAChC,OAAQ,IAAMoF,GAAWpF,CAAK,EAC9B,QAAS,IAAMsF,GAAYtF,CAAK,EAChC,SAAU,IAAMuF,GAAUvF,CAAK,CAAA,CAChC,EACDwxB,CAAO;AAAA;AAAA,UAETxxB,EAAM,MAAQ,QACZ+kC,GAAY,CACV,QAAS/kC,EAAM,aACf,OAAQA,EAAM,YACd,OAAQA,EAAM,YACd,OAAQA,EAAM,YACd,UAAWA,EAAM,eACjB,SAAUA,EAAM,SAChB,WAAYA,EAAM,gBAClB,WAAYA,EAAM,gBAClB,WAAYA,EAAM,gBAClB,UAAWA,EAAM,eACjB,mBAAqB3F,GAAU2F,EAAM,gBAAkB3F,EACvD,mBAAqBA,GAAU2F,EAAM,gBAAkB3F,EACvD,UAAW,IAAM2M,GAAUhH,CAAK,EAChC,OAAQ,IAAMsH,GAAgBtH,CAAK,CAAA,CACpC,EACDwxB,CAAO;AAAA;AAAA,UAETxxB,EAAM,MAAQ,OACZ0lC,GAAW,CACT,QAAS1lC,EAAM,YACf,MAAOA,EAAM,UACb,KAAMA,EAAM,SACZ,QAASA,EAAM,YACf,WAAYA,EAAM,eAClB,aAAcA,EAAM,iBACpB,WAAYA,EAAM,eAClB,UAAWA,EAAM,cACjB,mBAAqB3F,GAAU2F,EAAM,eAAiB3F,EACtD,cAAe,CAAC0N,EAAOzB,IAAY,CACjCtG,EAAM,iBAAmB,CAAE,GAAGA,EAAM,iBAAkB,CAAC+H,CAAK,EAAGzB,CAAA,CACjE,EACA,mBAAqBjM,GAAU2F,EAAM,eAAiB3F,EACtD,UAAW,IAAM8N,GAASnI,EAAO,CAAE,MAAO,GAAM,EAChD,SAAU,CAACV,EAAOhB,IAAU0B,EAAM,WAAWV,EAAOhB,CAAK,EACzD,SAAWqF,GAAU3D,EAAM,iBAAiB2D,CAAK,CAAA,CAClD,EACD6tB,CAAO;AAAA;AAAA,QAEX4b,GAAyBptC,CAAK,CAAC;AAAA;AAAA,GAGvC,CChkBO,MAAMowC,GAAuD,CAClE,MAAO,GACP,MAAO,GACP,KAAM,GACN,KAAM,GACN,MAAO,GACP,MAAO,EACT,EAEaC,GAAmC,CAC9C,KAAM,GACN,YAAa,GACb,QAAS,GACT,QAAS,GACT,aAAc,QACd,WAAY,GACZ,YAAa,KACb,UAAW,UACX,SAAU,YACV,OAAQ,GACR,cAAe,OACf,SAAU,iBACV,YAAa,cACb,YAAa,GACb,QAAS,GACT,QAAS,OACT,GAAI,GACJ,eAAgB,GAChB,iBAAkB,EACpB,ECrBA,eAAsBC,GAAWtwC,EAAoB,CACnD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,cACV,CAAAA,EAAM,cAAgB,GACtBA,EAAM,YAAc,KACpB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,cAAe,EAAE,EACrDC,MAAW,WAAaA,EAC9B,OAASC,EAAK,CACZF,EAAM,YAAc,OAAOE,CAAG,CAChC,QAAA,CACEF,EAAM,cAAgB,EACxB,EACF,CCxBO,MAAMuwC,GAAqB,CAChC,WAAY,aACZ,WAAY,sBACZ,QAAS,UACT,IAAK,MACL,eAAgB,iBAChB,UAAW,iBACX,QAAS,eACT,YAAa,mBACb,UAAW,YACX,KAAM,OACN,YAAa,cACb,MAAO,gBACT,EAKaC,GAAuBD,GAGvBE,GAAuB,CAClC,QAAS,UACT,IAAK,MACL,GAAI,KACJ,QAAS,UACT,KAAM,OACN,MAAO,QACP,KAAM,MACR,EAe8B,IAAI,IAAqB,OAAO,OAAOF,EAAkB,CAAC,EACxD,IAAI,IAAuB,OAAO,OAAOE,EAAoB,CAAC,ECjCvF,SAASC,GAAuB9vC,EAAyC,CAC9E,MAAM+iC,EAAU/iC,EAAO,UAAYA,EAAO,MAAQ,KAAO,MACnD+S,EAAS/S,EAAO,OAAO,KAAK,GAAG,EAC/BqX,EAAQrX,EAAO,OAAS,GACxB1F,EAAO,CACXyoC,EACA/iC,EAAO,SACPA,EAAO,SACPA,EAAO,WACPA,EAAO,KACP+S,EACA,OAAO/S,EAAO,UAAU,EACxBqX,CAAA,EAEF,OAAI0rB,IAAY,MACdzoC,EAAK,KAAK0F,EAAO,OAAS,EAAE,EAEvB1F,EAAK,KAAK,GAAG,CACtB,CCgCA,MAAMy1C,GAA4B,KAE3B,MAAMC,EAAqB,CAUhC,YAAoBxoC,EAAmC,CAAnC,KAAA,KAAAA,EATpB,KAAQ,GAAuB,KAC/B,KAAQ,YAAc,IACtB,KAAQ,OAAS,GACjB,KAAQ,QAAyB,KACjC,KAAQ,aAA8B,KACtC,KAAQ,YAAc,GACtB,KAAQ,aAA8B,KACtC,KAAQ,UAAY,GAEoC,CAExD,OAAQ,CACN,KAAK,OAAS,GACd,KAAK,QAAA,CACP,CAEA,MAAO,CACL,KAAK,OAAS,GACd,KAAK,IAAI,MAAA,EACT,KAAK,GAAK,KACV,KAAK,aAAa,IAAI,MAAM,wBAAwB,CAAC,CACvD,CAEA,IAAI,WAAY,CACd,OAAO,KAAK,IAAI,aAAe,UAAU,IAC3C,CAEQ,SAAU,CACZ,KAAK,SACT,KAAK,GAAK,IAAI,UAAU,KAAK,KAAK,GAAG,EACrC,KAAK,GAAG,OAAS,IAAM,KAAK,aAAA,EAC5B,KAAK,GAAG,UAAayoC,GAAO,KAAK,cAAc,OAAOA,EAAG,MAAQ,EAAE,CAAC,EACpE,KAAK,GAAG,QAAWA,GAAO,CACxB,MAAMC,EAAS,OAAOD,EAAG,QAAU,EAAE,EACrC,KAAK,GAAK,KACV,KAAK,aAAa,IAAI,MAAM,mBAAmBA,EAAG,IAAI,MAAMC,CAAM,EAAE,CAAC,EACrE,KAAK,KAAK,UAAU,CAAE,KAAMD,EAAG,KAAM,OAAAC,EAAQ,EAC7C,KAAK,kBAAA,CACP,EACA,KAAK,GAAG,QAAU,IAAM,CAExB,EACF,CAEQ,mBAAoB,CAC1B,GAAI,KAAK,OAAQ,OACjB,MAAMC,EAAQ,KAAK,UACnB,KAAK,UAAY,KAAK,IAAI,KAAK,UAAY,IAAK,IAAM,EACtD,OAAO,WAAW,IAAM,KAAK,QAAA,EAAWA,CAAK,CAC/C,CAEQ,aAAa7wC,EAAY,CAC/B,SAAW,CAAA,CAAGhJ,CAAC,IAAK,KAAK,QAASA,EAAE,OAAOgJ,CAAG,EAC9C,KAAK,QAAQ,MAAA,CACf,CAEA,MAAc,aAAc,CAC1B,GAAI,KAAK,YAAa,OACtB,KAAK,YAAc,GACf,KAAK,eAAiB,OACxB,OAAO,aAAa,KAAK,YAAY,EACrC,KAAK,aAAe,MAMtB,MAAM8wC,EAAkB,OAAO,OAAW,KAAe,CAAC,CAAC,OAAO,OAE5Dr9B,EAAS,CAAC,iBAAkB,qBAAsB,kBAAkB,EACpEjV,EAAO,WACb,IAAIuyC,EAAgF,KAChFC,EAAsB,GACtBC,EAAY,KAAK,KAAK,MAE1B,GAAIH,EAAiB,CACnBC,EAAiB,MAAMh+B,GAAA,EACvB,MAAMm+B,EAAcp9B,GAAoB,CACtC,SAAUi9B,EAAe,SACzB,KAAAvyC,CAAA,CACD,GAAG,MACJyyC,EAAYC,GAAe,KAAK,KAAK,MACrCF,EAAsB,GAAQE,GAAe,KAAK,KAAK,MACzD,CACA,MAAMC,EACJF,GAAa,KAAK,KAAK,SACnB,CACE,MAAOA,EACP,SAAU,KAAK,KAAK,QAAA,EAEtB,OAEN,IAAIzK,EAUJ,GAAIsK,GAAmBC,EAAgB,CACrC,MAAMK,EAAa,KAAK,IAAA,EAClBC,EAAQ,KAAK,cAAgB,OAC7B9wC,EAAUiwC,GAAuB,CACrC,SAAUO,EAAe,SACzB,SAAU,KAAK,KAAK,YAAcT,GAAqB,WACvD,WAAY,KAAK,KAAK,MAAQC,GAAqB,QACnD,KAAA/xC,EACA,OAAAiV,EACA,WAAA29B,EACA,MAAOH,GAAa,KACpB,MAAAI,CAAA,CACD,EACKC,EAAY,MAAMl+B,GAAkB29B,EAAe,WAAYxwC,CAAO,EAC5EimC,EAAS,CACP,GAAIuK,EAAe,SACnB,UAAWA,EAAe,UAC1B,UAAAO,EACA,SAAUF,EACV,MAAAC,CAAA,CAEJ,CACA,MAAM3wC,EAAS,CACb,YAAa,EACb,YAAa,EACb,OAAQ,CACN,GAAI,KAAK,KAAK,YAAc4vC,GAAqB,WACjD,QAAS,KAAK,KAAK,eAAiB,MACpC,SAAU,KAAK,KAAK,UAAY,UAAU,UAAY,MACtD,KAAM,KAAK,KAAK,MAAQC,GAAqB,QAC7C,WAAY,KAAK,KAAK,UAAA,EAExB,KAAA/xC,EACA,OAAAiV,EACA,OAAA+yB,EACA,KAAM,CAAA,EACN,KAAA2K,EACA,UAAW,UAAU,UACrB,OAAQ,UAAU,QAAA,EAGf,KAAK,QAAwB,UAAWzwC,CAAM,EAChD,KAAM6wC,GAAU,CACXA,GAAO,MAAM,aAAeR,GAC9Bh9B,GAAqB,CACnB,SAAUg9B,EAAe,SACzB,KAAMQ,EAAM,KAAK,MAAQ/yC,EACzB,MAAO+yC,EAAM,KAAK,YAClB,OAAQA,EAAM,KAAK,QAAU,CAAA,CAAC,CAC/B,EAEH,KAAK,UAAY,IACjB,KAAK,KAAK,UAAUA,CAAK,CAC3B,CAAC,EACA,MAAM,IAAM,CACPP,GAAuBD,GACzB98B,GAAqB,CAAE,SAAU88B,EAAe,SAAU,KAAAvyC,EAAM,EAElE,KAAK,IAAI,MAAMiyC,GAA2B,gBAAgB,CAC5D,CAAC,CACL,CAEQ,cAAcz2C,EAAa,CACjC,IAAIC,EACJ,GAAI,CACFA,EAAS,KAAK,MAAMD,CAAG,CACzB,MAAQ,CACN,MACF,CAEA,MAAMw3C,EAAQv3C,EACd,GAAIu3C,EAAM,OAAS,QAAS,CAC1B,MAAM1M,EAAM7qC,EACZ,GAAI6qC,EAAI,QAAU,oBAAqB,CACrC,MAAMvkC,EAAUukC,EAAI,QACduM,EAAQ9wC,GAAW,OAAOA,EAAQ,OAAU,SAAWA,EAAQ,MAAQ,KACzE8wC,IACF,KAAK,aAAeA,EACf,KAAK,YAAA,GAEZ,MACF,CACA,MAAMI,EAAM,OAAO3M,EAAI,KAAQ,SAAWA,EAAI,IAAM,KAChD2M,IAAQ,OACN,KAAK,UAAY,MAAQA,EAAM,KAAK,QAAU,GAChD,KAAK,KAAK,QAAQ,CAAE,SAAU,KAAK,QAAU,EAAG,SAAUA,EAAK,EAEjE,KAAK,QAAUA,GAEjB,GAAI,CACF,KAAK,KAAK,UAAU3M,CAAG,CACzB,OAAS9kC,EAAK,CACZ,QAAQ,MAAM,iCAAkCA,CAAG,CACrD,CACA,MACF,CAEA,GAAIwxC,EAAM,OAAS,MAAO,CACxB,MAAMzxC,EAAM9F,EACNmsC,EAAU,KAAK,QAAQ,IAAIrmC,EAAI,EAAE,EACvC,GAAI,CAACqmC,EAAS,OACd,KAAK,QAAQ,OAAOrmC,EAAI,EAAE,EACtBA,EAAI,GAAIqmC,EAAQ,QAAQrmC,EAAI,OAAO,EAClCqmC,EAAQ,OAAO,IAAI,MAAMrmC,EAAI,OAAO,SAAW,gBAAgB,CAAC,EACrE,MACF,CACF,CAEA,QAAqB2xC,EAAgBhxC,EAA8B,CACjE,GAAI,CAAC,KAAK,IAAM,KAAK,GAAG,aAAe,UAAU,KAC/C,OAAO,QAAQ,OAAO,IAAI,MAAM,uBAAuB,CAAC,EAE1D,MAAMsB,EAAKrC,GAAA,EACL6xC,EAAQ,CAAE,KAAM,MAAO,GAAAxvC,EAAI,OAAA0vC,EAAQ,OAAAhxC,CAAA,EACnC1J,EAAI,IAAI,QAAW,CAAC26C,EAASC,IAAW,CAC5C,KAAK,QAAQ,IAAI5vC,EAAI,CAAE,QAAUzK,GAAMo6C,EAAQp6C,CAAM,EAAG,OAAAq6C,CAAA,CAAQ,CAClE,CAAC,EACD,YAAK,GAAG,KAAK,KAAK,UAAUJ,CAAK,CAAC,EAC3Bx6C,CACT,CAEQ,cAAe,CACrB,KAAK,aAAe,KACpB,KAAK,YAAc,GACf,KAAK,eAAiB,MAAM,OAAO,aAAa,KAAK,YAAY,EACrE,KAAK,aAAe,OAAO,WAAW,IAAM,CACrC,KAAK,YAAA,CACZ,EAAG,GAAG,CACR,CACF,CC/QA,SAAS66C,GAASx4C,EAAkD,CAClE,OAAO,OAAOA,GAAU,UAAYA,IAAU,IAChD,CAEO,SAASy4C,GAA2BvxC,EAA8C,CACvF,GAAI,CAACsxC,GAAStxC,CAAO,EAAG,OAAO,KAC/B,MAAMyB,EAAK,OAAOzB,EAAQ,IAAO,SAAWA,EAAQ,GAAG,OAAS,GAC1D6sC,EAAU7sC,EAAQ,QACxB,GAAI,CAACyB,GAAM,CAAC6vC,GAASzE,CAAO,EAAG,OAAO,KACtC,MAAM2E,EAAU,OAAO3E,EAAQ,SAAY,SAAWA,EAAQ,QAAQ,OAAS,GAC/E,GAAI,CAAC2E,EAAS,OAAO,KACrB,MAAMC,EAAc,OAAOzxC,EAAQ,aAAgB,SAAWA,EAAQ,YAAc,EAC9E0xC,EAAc,OAAO1xC,EAAQ,aAAgB,SAAWA,EAAQ,YAAc,EACpF,MAAI,CAACyxC,GAAe,CAACC,EAAoB,KAClC,CACL,GAAAjwC,EACA,QAAS,CACP,QAAA+vC,EACA,IAAK,OAAO3E,EAAQ,KAAQ,SAAWA,EAAQ,IAAM,KACrD,KAAM,OAAOA,EAAQ,MAAS,SAAWA,EAAQ,KAAO,KACxD,SAAU,OAAOA,EAAQ,UAAa,SAAWA,EAAQ,SAAW,KACpE,IAAK,OAAOA,EAAQ,KAAQ,SAAWA,EAAQ,IAAM,KACrD,QAAS,OAAOA,EAAQ,SAAY,SAAWA,EAAQ,QAAU,KACjE,aAAc,OAAOA,EAAQ,cAAiB,SAAWA,EAAQ,aAAe,KAChF,WAAY,OAAOA,EAAQ,YAAe,SAAWA,EAAQ,WAAa,IAAA,EAE5E,YAAA4E,EACA,YAAAC,CAAA,CAEJ,CAEO,SAASC,GAA0B3xC,EAA+C,CACvF,GAAI,CAACsxC,GAAStxC,CAAO,EAAG,OAAO,KAC/B,MAAMyB,EAAK,OAAOzB,EAAQ,IAAO,SAAWA,EAAQ,GAAG,OAAS,GAChE,OAAKyB,EACE,CACL,GAAAA,EACA,SAAU,OAAOzB,EAAQ,UAAa,SAAWA,EAAQ,SAAW,KACpE,WAAY,OAAOA,EAAQ,YAAe,SAAWA,EAAQ,WAAa,KAC1E,GAAI,OAAOA,EAAQ,IAAO,SAAWA,EAAQ,GAAK,IAAA,EALpC,IAOlB,CAEO,SAAS4xC,GAAuBC,EAAqD,CAC1F,MAAM1yC,EAAM,KAAK,IAAA,EACjB,OAAO0yC,EAAM,OAAQ9wC,GAAUA,EAAM,YAAc5B,CAAG,CACxD,CAEO,SAAS2yC,GACdD,EACA9wC,EACuB,CACvB,MAAMnH,EAAOg4C,GAAuBC,CAAK,EAAE,OAAQ1zC,GAASA,EAAK,KAAO4C,EAAM,EAAE,EAChF,OAAAnH,EAAK,KAAKmH,CAAK,EACRnH,CACT,CAEO,SAASm4C,GAAmBF,EAA8BpwC,EAAmC,CAClG,OAAOmwC,GAAuBC,CAAK,EAAE,OAAQ9wC,GAAUA,EAAM,KAAOU,CAAE,CACxE,CCrEA,eAAsBuwC,GACpBzyC,EACAoI,EACA,CACA,GAAI,CAACpI,EAAM,QAAU,CAACA,EAAM,UAAW,OACvC,MAAMzF,EAAyCyF,EAAM,WAAW,KAAA,EAC1DY,EAASrG,EAAa,CAAE,WAAAA,CAAA,EAAe,CAAA,EAC7C,GAAI,CACF,MAAM0F,EAAO,MAAMD,EAAM,OAAO,QAAQ,qBAAsBY,CAAM,EAGpE,GAAI,CAACX,EAAK,OACV,MAAM7E,EAAa1B,GAA2BuG,CAAG,EACjDD,EAAM,cAAgB5E,EAAW,KACjC4E,EAAM,gBAAkB5E,EAAW,OACnC4E,EAAM,iBAAmB5E,EAAW,SAAW,IACjD,MAAQ,CAER,CACF,CC6BA,SAASs3C,GACPn5C,EACAU,EACQ,CACR,MAAMC,GAAOX,GAAS,IAAI,KAAA,EACpBo5C,EAAiB14C,EAAS,gBAAgB,KAAA,EAChD,GAAI,CAAC04C,EAAgB,OAAOz4C,EAC5B,GAAI,CAACA,EAAK,OAAOy4C,EACjB,MAAMC,EAAU34C,EAAS,SAAS,KAAA,GAAU,OACtC44C,EAAiB54C,EAAS,gBAAgB,KAAA,EAOhD,OALEC,IAAQ,QACRA,IAAQ04C,GACPC,IACE34C,IAAQ,SAAS24C,CAAc,SAC9B34C,IAAQ,SAAS24C,CAAc,IAAID,CAAO,IAC/BD,EAAiBz4C,CACpC,CAEA,SAAS44C,GAAqB/wC,EAAmB9H,EAAoC,CACnF,GAAI,CAACA,GAAU,eAAgB,OAC/B,MAAM84C,EAAqBL,GAA+B3wC,EAAK,WAAY9H,CAAQ,EAC7E+4C,EAA6BN,GACjC3wC,EAAK,SAAS,WACd9H,CAAA,EAEIg5C,EAA+BP,GACnC3wC,EAAK,SAAS,qBACd9H,CAAA,EAEIi5C,EAAiBH,GAAsBC,GAA8BjxC,EAAK,WAC1EoxC,EAAe,CACnB,GAAGpxC,EAAK,SACR,WAAYixC,GAA8BE,EAC1C,qBAAsBD,GAAgCC,CAAA,EAElDE,EACJD,EAAa,aAAepxC,EAAK,SAAS,YAC1CoxC,EAAa,uBAAyBpxC,EAAK,SAAS,qBAClDmxC,IAAmBnxC,EAAK,aAC1BA,EAAK,WAAamxC,GAEhBE,GACF57B,GAAczV,EAAwDoxC,CAAY,CAEtF,CAEO,SAASE,GAAetxC,EAAmB,CAChDA,EAAK,UAAY,KACjBA,EAAK,MAAQ,KACbA,EAAK,UAAY,GACjBA,EAAK,kBAAoB,CAAA,EACzBA,EAAK,kBAAoB,KAEzBA,EAAK,QAAQ,KAAA,EACbA,EAAK,OAAS,IAAI6uC,GAAqB,CACrC,IAAK7uC,EAAK,SAAS,WACnB,MAAOA,EAAK,SAAS,MAAM,OAASA,EAAK,SAAS,MAAQ,OAC1D,SAAUA,EAAK,SAAS,KAAA,EAASA,EAAK,SAAW,OACjD,WAAY,sBACZ,KAAM,UACN,QAAU0vC,GAAU,CAClB1vC,EAAK,UAAY,GACjBA,EAAK,UAAY,KACjBA,EAAK,MAAQ0vC,EACb6B,GAAcvxC,EAAM0vC,CAAK,EACpBgB,GAAsB1wC,CAA8B,EACpDuuC,GAAWvuC,CAA8B,EACzC2S,GAAU3S,EAAgC,CAAE,MAAO,GAAM,EACzDqS,GAAYrS,EAAgC,CAAE,MAAO,GAAM,EAC3DuW,GAAiBvW,CAAyD,CACjF,EACA,QAAS,CAAC,CAAE,KAAAwxC,EAAM,OAAAzC,KAAa,CAC7B/uC,EAAK,UAAY,GAEbwxC,IAAS,OACXxxC,EAAK,UAAY,iBAAiBwxC,CAAI,MAAMzC,GAAU,WAAW,GAErE,EACA,QAAU9L,GAAQwO,GAAmBzxC,EAAMijC,CAAG,EAC9C,MAAO,CAAC,CAAE,SAAAyO,EAAU,SAAAC,KAAe,CACjC3xC,EAAK,UAAY,oCAAoC0xC,CAAQ,SAASC,CAAQ,wBAChF,CAAA,CACD,EACD3xC,EAAK,OAAO,MAAA,CACd,CAEO,SAASyxC,GAAmBzxC,EAAmBijC,EAAwB,CAC5E,GAAI,CACF2O,GAAyB5xC,EAAMijC,CAAG,CACpC,OAAS9kC,EAAK,CACZ,QAAQ,MAAM,sCAAuC8kC,EAAI,MAAO9kC,CAAG,CACrE,CACF,CAEA,SAASyzC,GAAyB5xC,EAAmBijC,EAAwB,CAS3E,GARAjjC,EAAK,eAAiB,CACpB,CAAE,GAAI,KAAK,MAAO,MAAOijC,EAAI,MAAO,QAASA,EAAI,OAAA,EACjD,GAAGjjC,EAAK,cAAA,EACR,MAAM,EAAG,GAAG,EACVA,EAAK,MAAQ,UACfA,EAAK,SAAWA,EAAK,gBAGnBijC,EAAI,QAAU,QAAS,CACzB,GAAIjjC,EAAK,WAAY,OACrBa,GACEb,EACAijC,EAAI,OAAA,EAEN,MACF,CAEA,GAAIA,EAAI,QAAU,OAAQ,CACxB,MAAMvkC,EAAUukC,EAAI,QAChBvkC,GAAS,YACXiX,GACE3V,EACAtB,EAAQ,UAAA,EAGZ,MAAMT,EAAQQ,GAAgBuB,EAAgCtB,CAAO,GACjET,IAAU,SAAWA,IAAU,SAAWA,IAAU,aACtDuC,GAAgBR,CAAwD,EACnEuY,GACHvY,CAAA,GAGA/B,IAAU,SAAcD,GAAgBgC,CAA8B,EAC1E,MACF,CAEA,GAAIijC,EAAI,QAAU,WAAY,CAC5B,MAAMvkC,EAAUukC,EAAI,QAChBvkC,GAAS,UAAY,MAAM,QAAQA,EAAQ,QAAQ,IACrDsB,EAAK,gBAAkBtB,EAAQ,SAC/BsB,EAAK,cAAgB,KACrBA,EAAK,eAAiB,MAExB,MACF,CAUA,GARIijC,EAAI,QAAU,QAAUjjC,EAAK,MAAQ,QAClC4W,GAAS5W,CAAiD,GAG7DijC,EAAI,QAAU,yBAA2BA,EAAI,QAAU,yBACpD5wB,GAAYrS,EAAgC,CAAE,MAAO,GAAM,EAG9DijC,EAAI,QAAU,0BAA2B,CAC3C,MAAMxjC,EAAQwwC,GAA2BhN,EAAI,OAAO,EACpD,GAAIxjC,EAAO,CACTO,EAAK,kBAAoBwwC,GAAgBxwC,EAAK,kBAAmBP,CAAK,EACtEO,EAAK,kBAAoB,KACzB,MAAMgvC,EAAQ,KAAK,IAAI,EAAGvvC,EAAM,YAAc,KAAK,IAAA,EAAQ,GAAG,EAC9D,OAAO,WAAW,IAAM,CACtBO,EAAK,kBAAoBywC,GAAmBzwC,EAAK,kBAAmBP,EAAM,EAAE,CAC9E,EAAGuvC,CAAK,CACV,CACA,MACF,CAEA,GAAI/L,EAAI,QAAU,yBAA0B,CAC1C,MAAMhsB,EAAWo5B,GAA0BpN,EAAI,OAAO,EAClDhsB,IACFjX,EAAK,kBAAoBywC,GAAmBzwC,EAAK,kBAAmBiX,EAAS,EAAE,EAEnF,CACF,CAEO,SAASs6B,GAAcvxC,EAAmB0vC,EAAuB,CACtE,MAAMvsC,EAAWusC,EAAM,SAOnBvsC,GAAU,UAAY,MAAM,QAAQA,EAAS,QAAQ,IACvDnD,EAAK,gBAAkBmD,EAAS,UAE9BA,GAAU,SACZnD,EAAK,YAAcmD,EAAS,QAE1BA,GAAU,iBACZ4tC,GAAqB/wC,EAAMmD,EAAS,eAAe,CAEvD,CCxNO,SAAS0uC,GAAgB7xC,EAAqB,CACnDA,EAAK,SAAW8W,GAAA,EAChBM,GACEpX,EACA,EAAA,EAEFgX,GACEhX,CAAA,EAEFkX,GACElX,CAAA,EAEF,OAAO,iBAAiB,WAAYA,EAAK,eAAe,EACxD4V,GACE5V,CAAA,EAEFsxC,GAAetxC,CAAuD,EACtEmV,GAAkBnV,CAA0D,EACxEA,EAAK,MAAQ,QACfqV,GAAiBrV,CAAyD,EAExEA,EAAK,MAAQ,SACfuV,GAAkBvV,CAA0D,CAEhF,CAEO,SAAS8xC,GAAmB9xC,EAAqB,CACtDoC,GAAcpC,CAAsD,CACtE,CAEO,SAAS+xC,GAAmB/xC,EAAqB,CACtD,OAAO,oBAAoB,WAAYA,EAAK,eAAe,EAC3DoV,GAAiBpV,CAAyD,EAC1EsV,GAAgBtV,CAAwD,EACxEwV,GAAiBxV,CAAyD,EAC1EmX,GACEnX,CAAA,EAEFA,EAAK,gBAAgB,WAAA,EACrBA,EAAK,eAAiB,IACxB,CAEO,SAASgyC,GACdhyC,EACAiyC,EACA,CACA,GACEjyC,EAAK,MAAQ,SACZiyC,EAAQ,IAAI,cAAc,GACzBA,EAAQ,IAAI,kBAAkB,GAC9BA,EAAQ,IAAI,YAAY,GACxBA,EAAQ,IAAI,aAAa,GACzBA,EAAQ,IAAI,KAAK,GACnB,CACA,MAAMC,EAAcD,EAAQ,IAAI,KAAK,EAC/BE,EACJF,EAAQ,IAAI,aAAa,GACzBA,EAAQ,IAAI,aAAa,IAAM,IAC/BjyC,EAAK,cAAgB,GACvBiB,GACEjB,EACAkyC,GAAeC,GAAgB,CAACnyC,EAAK,mBAAA,CAEzC,CAEEA,EAAK,MAAQ,SACZiyC,EAAQ,IAAI,aAAa,GAAKA,EAAQ,IAAI,gBAAgB,GAAKA,EAAQ,IAAI,KAAK,IAE7EjyC,EAAK,gBAAkBA,EAAK,cAC9B0B,GACE1B,EACAiyC,EAAQ,IAAI,KAAK,GAAKA,EAAQ,IAAI,gBAAgB,CAAA,CAI1D,CCnGA,eAAsBG,GAAoBpyC,EAAmBO,EAAgB,CAC3E,MAAMuE,GAAmB9E,EAAMO,CAAK,EACpC,MAAMqE,GAAa5E,EAAM,EAAI,CAC/B,CAEA,eAAsBqyC,GAAmBryC,EAAmB,CAC1D,MAAM+E,GAAkB/E,CAAI,EAC5B,MAAM4E,GAAa5E,EAAM,EAAI,CAC/B,CAEA,eAAsBsyC,GAAqBtyC,EAAmB,CAC5D,MAAMgF,GAAehF,CAAI,EACzB,MAAM4E,GAAa5E,EAAM,EAAI,CAC/B,CAEA,eAAsBuyC,GAAwBvyC,EAAmB,CAC/D,MAAMqD,GAAWrD,CAAI,EACrB,MAAM+C,GAAW/C,CAAI,EACrB,MAAM4E,GAAa5E,EAAM,EAAI,CAC/B,CAEA,eAAsBwyC,GAA0BxyC,EAAmB,CACjE,MAAM+C,GAAW/C,CAAI,EACrB,MAAM4E,GAAa5E,EAAM,EAAI,CAC/B,CAEA,SAASyyC,GAAsBC,EAA0C,CACvE,GAAI,CAAC,MAAM,QAAQA,CAAO,QAAU,CAAA,EACpC,MAAMC,EAAiC,CAAA,EACvC,UAAWlzC,KAASizC,EAAS,CAC3B,GAAI,OAAOjzC,GAAU,SAAU,SAC/B,KAAM,CAACmzC,EAAU,GAAGj6C,CAAI,EAAI8G,EAAM,MAAM,GAAG,EAC3C,GAAI,CAACmzC,GAAYj6C,EAAK,SAAW,EAAG,SACpC,MAAMslC,EAAQ2U,EAAS,KAAA,EACjBl2C,EAAU/D,EAAK,KAAK,GAAG,EAAE,KAAA,EAC3BslC,GAASvhC,IAASi2C,EAAO1U,CAAK,EAAIvhC,EACxC,CACA,OAAOi2C,CACT,CAEA,SAASE,GAAsB7yC,EAA2B,CAExD,OADiBA,EAAK,kBAAkB,iBAAiB,OAAS,CAAA,GAClD,CAAC,GAAG,WAAaA,EAAK,uBAAyB,SACjE,CAEA,SAAS8yC,GAAqBhV,EAAmB3f,EAAS,GAAY,CACpE,MAAO,uBAAuB,mBAAmB2f,CAAS,CAAC,WAAW3f,CAAM,EAC9E,CAEO,SAAS40B,GACd/yC,EACA89B,EACAS,EACA,CACAv+B,EAAK,sBAAwB89B,EAC7B99B,EAAK,sBAAwBs+B,GAA4BC,GAAW,MAAS,CAC/E,CAEO,SAASyU,GAAyBhzC,EAAmB,CAC1DA,EAAK,sBAAwB,KAC7BA,EAAK,sBAAwB,IAC/B,CAEO,SAASizC,GACdjzC,EACAi+B,EACAzmC,EACA,CACA,MAAMyG,EAAQ+B,EAAK,sBACd/B,IACL+B,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,CACN,GAAGA,EAAM,OACT,CAACggC,CAAK,EAAGzmC,CAAA,EAEX,YAAa,CACX,GAAGyG,EAAM,YACT,CAACggC,CAAK,EAAG,EAAA,CACX,EAEJ,CAEO,SAASiV,GAAiClzC,EAAmB,CAClE,MAAM/B,EAAQ+B,EAAK,sBACd/B,IACL+B,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,aAAc,CAACA,EAAM,YAAA,EAEzB,CAEA,eAAsBk1C,GAAuBnzC,EAAmB,CAC9D,MAAM/B,EAAQ+B,EAAK,sBACnB,GAAI,CAAC/B,GAASA,EAAM,OAAQ,OAC5B,MAAM6/B,EAAY+U,GAAsB7yC,CAAI,EAE5CA,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,GACR,MAAO,KACP,QAAS,KACT,YAAa,CAAA,CAAC,EAGhB,GAAI,CACF,MAAMm1C,EAAW,MAAM,MAAMN,GAAqBhV,CAAS,EAAG,CAC5D,OAAQ,MACR,QAAS,CACP,eAAgB,kBAAA,EAElB,KAAM,KAAK,UAAU7/B,EAAM,MAAM,CAAA,CAClC,EACK0C,EAAQ,MAAMyyC,EAAS,OAAO,MAAM,IAAM,IAAI,EAIpD,GAAI,CAACA,EAAS,IAAMzyC,GAAM,KAAO,IAAS,CAACA,EAAM,CAC/C,MAAM0yC,EAAe1yC,GAAM,OAAS,0BAA0ByyC,EAAS,MAAM,IAC7EpzC,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,GACR,MAAOo1C,EACP,QAAS,KACT,YAAaZ,GAAsB9xC,GAAM,OAAO,CAAA,EAElD,MACF,CAEA,GAAI,CAACA,EAAK,UAAW,CACnBX,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,GACR,MAAO,wCACP,QAAS,IAAA,EAEX,MACF,CAEA+B,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,GACR,MAAO,KACP,QAAS,+BACT,YAAa,CAAA,EACb,SAAU,CAAE,GAAGA,EAAM,MAAA,CAAO,EAE9B,MAAM2G,GAAa5E,EAAM,EAAI,CAC/B,OAAS7B,EAAK,CACZ6B,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,GACR,MAAO,0BAA0B,OAAOE,CAAG,CAAC,GAC5C,QAAS,IAAA,CAEb,CACF,CAEA,eAAsBm1C,GAAyBtzC,EAAmB,CAChE,MAAM/B,EAAQ+B,EAAK,sBACnB,GAAI,CAAC/B,GAASA,EAAM,UAAW,OAC/B,MAAM6/B,EAAY+U,GAAsB7yC,CAAI,EAE5CA,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,UAAW,GACX,MAAO,KACP,QAAS,IAAA,EAGX,GAAI,CACF,MAAMm1C,EAAW,MAAM,MAAMN,GAAqBhV,EAAW,SAAS,EAAG,CACvE,OAAQ,OACR,QAAS,CACP,eAAgB,kBAAA,EAElB,KAAM,KAAK,UAAU,CAAE,UAAW,GAAM,CAAA,CACzC,EACKn9B,EAAQ,MAAMyyC,EAAS,OAAO,MAAM,IAAM,IAAI,EAIpD,GAAI,CAACA,EAAS,IAAMzyC,GAAM,KAAO,IAAS,CAACA,EAAM,CAC/C,MAAM0yC,EAAe1yC,GAAM,OAAS,0BAA0ByyC,EAAS,MAAM,IAC7EpzC,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,UAAW,GACX,MAAOo1C,EACP,QAAS,IAAA,EAEX,MACF,CAEA,MAAMhN,EAAS1lC,EAAK,QAAUA,EAAK,UAAY,KACzC4yC,EAAalN,EAAS,CAAE,GAAGpoC,EAAM,OAAQ,GAAGooC,GAAWpoC,EAAM,OAC7Du1C,EAAe,GACnBD,EAAW,QAAUA,EAAW,SAAWA,EAAW,OAASA,EAAW,OAG5EvzC,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,UAAW,GACX,OAAQs1C,EACR,MAAO,KACP,QAAS5yC,EAAK,MACV,oDACA,wCACJ,aAAA6yC,CAAA,EAGE7yC,EAAK,OACP,MAAMiE,GAAa5E,EAAM,EAAI,CAEjC,OAAS7B,EAAK,CACZ6B,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,UAAW,GACX,MAAO,0BAA0B,OAAOE,CAAG,CAAC,GAC5C,QAAS,IAAA,CAEb,CACF,qMCjJA,MAAMs1C,GAA4B17C,GAAA,EAElC,SAAS27C,IAAiC,CACxC,GAAI,CAAC,OAAO,SAAS,OAAQ,MAAO,GAEpC,MAAMv7C,EADS,IAAI,gBAAgB,OAAO,SAAS,MAAM,EACtC,IAAI,YAAY,EACnC,GAAI,CAACA,EAAK,MAAO,GACjB,MAAMkB,EAAalB,EAAI,KAAA,EAAO,YAAA,EAC9B,OAAOkB,IAAe,KAAOA,IAAe,QAAUA,IAAe,OAASA,IAAe,IAC/F,CAGO,IAAMs6C,EAAN,cAA0BpiB,EAAW,CAArC,aAAA,CAAA,MAAA,GAAA,SAAA,EACI,KAAA,SAAuBt5B,GAAA,EACvB,KAAA,SAAW,GACX,KAAA,IAAW,OACX,KAAA,WAAay7C,GAAA,EACb,KAAA,UAAY,GACZ,KAAA,MAAmB,KAAK,SAAS,OAAS,SAC1C,KAAA,cAA+B,OAC/B,KAAA,MAA+B,KAC/B,KAAA,UAA2B,KAC3B,KAAA,SAA4B,CAAA,EACrC,KAAQ,eAAkC,CAAA,EAC1C,KAAQ,oBAAqC,KAC7C,KAAQ,kBAAmC,KAElC,KAAA,cAAgBD,GAA0B,KAC1C,KAAA,gBAAkBA,GAA0B,OAC5C,KAAA,iBAAmBA,GAA0B,SAAW,KAExD,KAAA,WAAa,KAAK,SAAS,WAC3B,KAAA,YAAc,GACd,KAAA,YAAc,GACd,KAAA,YAAc,GACd,KAAA,aAA0B,CAAA,EAC1B,KAAA,iBAA8B,CAAA,EAC9B,KAAA,WAA4B,KAC5B,KAAA,oBAAqC,KACrC,KAAA,UAA2B,KAC3B,KAAA,iBAAwE,KACxE,KAAA,cAA+B,KAC/B,KAAA,kBAAmC,KACnC,KAAA,UAA6B,CAAA,EAE7B,KAAA,YAAc,GACd,KAAA,eAAgC,KAChC,KAAA,aAA8B,KAC9B,KAAA,WAAa,KAAK,SAAS,WAE3B,KAAA,aAAe,GACf,KAAA,MAAwC,CAAA,EACxC,KAAA,eAAiB,GACjB,KAAA,aAA8B,KAC9B,KAAA,YAAwC,KACxC,KAAA,qBAAuB,GACvB,KAAA,oBAAsB,GACtB,KAAA,mBAAqB,GACrB,KAAA,sBAAsD,KACtD,KAAA,kBAA8C,KAC9C,KAAA,2BAA4C,KAC5C,KAAA,oBAA0C,UAC1C,KAAA,0BAA2C,KAC3C,KAAA,kBAA2C,CAAA,EAC3C,KAAA,iBAAmB,GACnB,KAAA,kBAAmC,KAEnC,KAAA,cAAgB,GAChB,KAAA,UAAY;AAAA;AAAA,EACZ,KAAA,kBAAoB,GACpB,KAAA,YAA8B,KAC9B,KAAA,aAA0B,CAAA,EAC1B,KAAA,aAAe,GACf,KAAA,eAAiB,GACjB,KAAA,cAAgB,GAChB,KAAA,gBAAkB,KAAK,SAAS,qBAChC,KAAA,eAAwC,KACxC,KAAA,aAA+B,KAC/B,KAAA,oBAAqC,KACrC,KAAA,oBAAsB,GACtB,KAAA,cAA+B,CAAA,EAC/B,KAAA,WAA6C,KAC7C,KAAA,mBAAqD,KACrD,KAAA,gBAAkB,GAClB,KAAA,eAAiC,OACjC,KAAA,kBAAoB,GACpB,KAAA,oBAAqC,KACrC,KAAA,uBAAwC,KAExC,KAAA,gBAAkB,GAClB,KAAA,iBAAkD,KAClD,KAAA,cAA+B,KAC/B,KAAA,oBAAqC,KACrC,KAAA,qBAAsC,KACtC,KAAA,uBAAwC,KACxC,KAAA,uBAAyC,KACzC,KAAA,aAAe,GACf,KAAA,sBAAsD,KACtD,KAAA,sBAAuC,KAEvC,KAAA,gBAAkB,GAClB,KAAA,gBAAmC,CAAA,EACnC,KAAA,cAA+B,KAC/B,KAAA,eAAgC,KAEhC,KAAA,cAAgB,GAChB,KAAA,WAAsC,KACtC,KAAA,YAA6B,KAE7B,KAAA,gBAAkB,GAClB,KAAA,eAA4C,KAC5C,KAAA,cAA+B,KAC/B,KAAA,qBAAuB,GACvB,KAAA,oBAAsB,MACtB,KAAA,sBAAwB,GACxB,KAAA,uBAAyB,GAEzB,KAAA,YAAc,GACd,KAAA,SAAsB,CAAA,EACtB,KAAA,WAAgC,KAChC,KAAA,UAA2B,KAC3B,KAAA,SAA0B,CAAE,GAAGnF,EAAA,EAC/B,KAAA,cAA+B,KAC/B,KAAA,SAA8B,CAAA,EAC9B,KAAA,SAAW,GAEX,KAAA,cAAgB,GAChB,KAAA,aAAyC,KACzC,KAAA,YAA6B,KAC7B,KAAA,aAAe,GACf,KAAA,WAAqC,CAAA,EACrC,KAAA,cAA+B,KAC/B,KAAA,cAA8C,CAAA,EAE9C,KAAA,aAAe,GACf,KAAA,YAAoC,KACpC,KAAA,YAAqC,KACrC,KAAA,YAAyB,CAAA,EACzB,KAAA,eAAiC,KACjC,KAAA,gBAAkB,GAClB,KAAA,gBAAkB,KAClB,KAAA,gBAAiC,KACjC,KAAA,eAAgC,KAEhC,KAAA,YAAc,GACd,KAAA,UAA2B,KAC3B,KAAA,SAA0B,KAC1B,KAAA,YAA0B,CAAA,EAC1B,KAAA,eAAiB,GACjB,KAAA,iBAA8C,CACrD,GAAGD,EAAA,EAEI,KAAA,eAAiB,GACjB,KAAA,cAAgB,GAChB,KAAA,WAA4B,KAC5B,KAAA,gBAAiC,KACjC,KAAA,UAAY,IACZ,KAAA,aAAe,KACf,KAAA,aAAe,GAExB,KAAA,OAAsC,KACtC,KAAQ,gBAAiC,KACzC,KAAQ,kBAAmC,KAC3C,KAAQ,oBAAsB,GAC9B,KAAQ,mBAAqB,GAC7B,KAAQ,kBAAmC,KAC3C,KAAQ,iBAAkC,KAC1C,KAAQ,kBAAmC,KAC3C,KAAQ,gBAAiC,KACzC,KAAQ,mBAAqB,IAC7B,KAAQ,gBAA4B,CAAA,EACpC,KAAA,SAAW,GACX,KAAQ,gBAAkB,IACxBuF,GACE,IAAA,EAEJ,KAAQ,WAAoC,KAC5C,KAAQ,kBAAmE,KAC3E,KAAQ,eAAwC,IAAA,CAEhD,kBAAmB,CACjB,OAAO,IACT,CAEA,mBAAoB,CAClB,MAAM,kBAAA,EACN/B,GAAgB,IAAwD,CAC1E,CAEU,cAAe,CACvBC,GAAmB,IAA2D,CAChF,CAEA,sBAAuB,CACrBC,GAAmB,IAA2D,EAC9E,MAAM,qBAAA,CACR,CAEU,QAAQE,EAAoC,CACpDD,GACE,KACAC,CAAA,CAEJ,CAEA,SAAU,CACR4B,GACE,IAAA,CAEJ,CAEA,iBAAiBjyC,EAAc,CAC7BkyC,GACE,KACAlyC,CAAA,CAEJ,CAEA,iBAAiBA,EAAc,CAC7BmyC,GACE,KACAnyC,CAAA,CAEJ,CAEA,WAAWrE,EAAiBhB,EAAe,CACzCy3C,GAAmBz2C,EAAOhB,CAAK,CACjC,CAEA,iBAAkB,CAChB03C,GACE,IAAA,CAEJ,CAEA,iBAAkB,CAChBC,GACE,IAAA,CAEJ,CAEA,MAAM,uBAAwB,CAC5B,MAAMC,GAA8B,IAAI,CAC1C,CAEA,cAAc77C,EAAkB,CAC9B87C,GACE,KACA97C,CAAA,CAEJ,CAEA,OAAOA,EAAW,CAChB+7C,GAAe,KAAyD/7C,CAAI,CAC9E,CAEA,SAASA,EAAiBoc,EAAkD,CAC1E4/B,GACE,KACAh8C,EACAoc,CAAA,CAEJ,CAEA,MAAM,cAAe,CACnB,MAAM6/B,GACJ,IAAA,CAEJ,CAEA,MAAM,UAAW,CACf,MAAMC,GACJ,IAAA,CAEJ,CAEA,MAAM,iBAAkB,CACtB,MAAMC,GACJ,IAAA,CAEJ,CAEA,oBAAoBt0C,EAAY,CAC9Bu0C,GACE,KACAv0C,CAAA,CAEJ,CAEA,MAAM,eACJiY,EACA/R,EACA,CACA,MAAMsuC,GACJ,KACAv8B,EACA/R,CAAA,CAEJ,CAEA,MAAM,oBAAoB9F,EAAgB,CACxC,MAAMq0C,GAA4B,KAAMr0C,CAAK,CAC/C,CAEA,MAAM,oBAAqB,CACzB,MAAMs0C,GAA2B,IAAI,CACvC,CAEA,MAAM,sBAAuB,CAC3B,MAAMC,GAA6B,IAAI,CACzC,CAEA,MAAM,yBAA0B,CAC9B,MAAMC,GAAgC,IAAI,CAC5C,CAEA,MAAM,2BAA4B,CAChC,MAAMC,GAAkC,IAAI,CAC9C,CAEA,uBAAuBlX,EAAmBS,EAA8B,CACtE0W,GAA+B,KAAMnX,EAAWS,CAAO,CACzD,CAEA,0BAA2B,CACzB2W,GAAiC,IAAI,CACvC,CAEA,8BAA8BjX,EAA2BzmC,EAAe,CACtE29C,GAAsC,KAAMlX,EAAOzmC,CAAK,CAC1D,CAEA,MAAM,wBAAyB,CAC7B,MAAM49C,GAA+B,IAAI,CAC3C,CAEA,MAAM,0BAA2B,CAC/B,MAAMC,GAAiC,IAAI,CAC7C,CAEA,kCAAmC,CACjCC,GAAyC,IAAI,CAC/C,CAEA,MAAM,2BAA2BC,EAAkD,CACjF,MAAMjK,EAAS,KAAK,kBAAkB,CAAC,EACvC,GAAI,GAACA,GAAU,CAAC,KAAK,QAAU,KAAK,kBACpC,MAAK,iBAAmB,GACxB,KAAK,kBAAoB,KACzB,GAAI,CACF,MAAM,KAAK,OAAO,QAAQ,wBAAyB,CACjD,GAAIA,EAAO,GACX,SAAAiK,CAAA,CACD,EACD,KAAK,kBAAoB,KAAK,kBAAkB,OAAQ91C,GAAUA,EAAM,KAAO6rC,EAAO,EAAE,CAC1F,OAASntC,EAAK,CACZ,KAAK,kBAAoB,yBAAyB,OAAOA,CAAG,CAAC,EAC/D,QAAA,CACE,KAAK,iBAAmB,EAC1B,EACF,CAGA,kBAAkBvB,EAAiB,CAC7B,KAAK,mBAAqB,OAC5B,OAAO,aAAa,KAAK,iBAAiB,EAC1C,KAAK,kBAAoB,MAE3B,KAAK,eAAiBA,EACtB,KAAK,aAAe,KACpB,KAAK,YAAc,EACrB,CAEA,oBAAqB,CACnB,KAAK,YAAc,GAEf,KAAK,mBAAqB,MAC5B,OAAO,aAAa,KAAK,iBAAiB,EAE5C,KAAK,kBAAoB,OAAO,WAAW,IAAM,CAC3C,KAAK,cACT,KAAK,eAAiB,KACtB,KAAK,aAAe,KACpB,KAAK,kBAAoB,KAC3B,EAAG,GAAG,CACR,CAEA,uBAAuBwxC,EAAe,CACpC,MAAM1c,EAAW,KAAK,IAAI,GAAK,KAAK,IAAI,GAAK0c,CAAK,CAAC,EACnD,KAAK,WAAa1c,EAClB,KAAK,cAAc,CAAE,GAAG,KAAK,SAAU,WAAYA,EAAU,CAC/D,CAEA,QAAS,CACP,OAAO8b,GAAU,IAAI,CACvB,CACF,EA/XW5b,EAAA,CAAR3zB,EAAA,CAAM,EADI01C,EACF,UAAA,WAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAFI01C,EAEF,UAAA,WAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAHI01C,EAGF,UAAA,MAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAJI01C,EAIF,UAAA,aAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EALI01C,EAKF,UAAA,YAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EANI01C,EAMF,UAAA,QAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAPI01C,EAOF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EARI01C,EAQF,UAAA,QAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EATI01C,EASF,UAAA,YAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAVI01C,EAUF,UAAA,WAAA,CAAA,EAKA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAfI01C,EAeF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAhBI01C,EAgBF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAjBI01C,EAiBF,UAAA,mBAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAnBI01C,EAmBF,UAAA,aAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EApBI01C,EAoBF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EArBI01C,EAqBF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAtBI01C,EAsBF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAvBI01C,EAuBF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAxBI01C,EAwBF,UAAA,mBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAzBI01C,EAyBF,UAAA,aAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA1BI01C,EA0BF,UAAA,sBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA3BI01C,EA2BF,UAAA,YAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA5BI01C,EA4BF,UAAA,mBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA7BI01C,EA6BF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA9BI01C,EA8BF,UAAA,oBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA/BI01C,EA+BF,UAAA,YAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAjCI01C,EAiCF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAlCI01C,EAkCF,UAAA,iBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAnCI01C,EAmCF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EApCI01C,EAoCF,UAAA,aAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAtCI01C,EAsCF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAvCI01C,EAuCF,UAAA,QAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAxCI01C,EAwCF,UAAA,iBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAzCI01C,EAyCF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA1CI01C,EA0CF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA3CI01C,EA2CF,UAAA,uBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA5CI01C,EA4CF,UAAA,sBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA7CI01C,EA6CF,UAAA,qBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA9CI01C,EA8CF,UAAA,wBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA/CI01C,EA+CF,UAAA,oBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAhDI01C,EAgDF,UAAA,6BAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAjDI01C,EAiDF,UAAA,sBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAlDI01C,EAkDF,UAAA,4BAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAnDI01C,EAmDF,UAAA,oBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EApDI01C,EAoDF,UAAA,mBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EArDI01C,EAqDF,UAAA,oBAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAvDI01C,EAuDF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAxDI01C,EAwDF,UAAA,YAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAzDI01C,EAyDF,UAAA,oBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA1DI01C,EA0DF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA3DI01C,EA2DF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA5DI01C,EA4DF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA7DI01C,EA6DF,UAAA,iBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA9DI01C,EA8DF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA/DI01C,EA+DF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAhEI01C,EAgEF,UAAA,iBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAjEI01C,EAiEF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAlEI01C,EAkEF,UAAA,sBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAnEI01C,EAmEF,UAAA,sBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EApEI01C,EAoEF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EArEI01C,EAqEF,UAAA,aAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAtEI01C,EAsEF,UAAA,qBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAvEI01C,EAuEF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAxEI01C,EAwEF,UAAA,iBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAzEI01C,EAyEF,UAAA,oBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA1EI01C,EA0EF,UAAA,sBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA3EI01C,EA2EF,UAAA,yBAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA7EI01C,EA6EF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA9EI01C,EA8EF,UAAA,mBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA/EI01C,EA+EF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAhFI01C,EAgFF,UAAA,sBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAjFI01C,EAiFF,UAAA,uBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAlFI01C,EAkFF,UAAA,yBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAnFI01C,EAmFF,UAAA,yBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EApFI01C,EAoFF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EArFI01C,EAqFF,UAAA,wBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAtFI01C,EAsFF,UAAA,wBAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAxFI01C,EAwFF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAzFI01C,EAyFF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA1FI01C,EA0FF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA3FI01C,EA2FF,UAAA,iBAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA7FI01C,EA6FF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA9FI01C,EA8FF,UAAA,aAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA/FI01C,EA+FF,UAAA,cAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAjGI01C,EAiGF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAlGI01C,EAkGF,UAAA,iBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAnGI01C,EAmGF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EApGI01C,EAoGF,UAAA,uBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EArGI01C,EAqGF,UAAA,sBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAtGI01C,EAsGF,UAAA,wBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAvGI01C,EAuGF,UAAA,yBAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAzGI01C,EAyGF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA1GI01C,EA0GF,UAAA,WAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA3GI01C,EA2GF,UAAA,aAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA5GI01C,EA4GF,UAAA,YAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA7GI01C,EA6GF,UAAA,WAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA9GI01C,EA8GF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA/GI01C,EA+GF,UAAA,WAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAhHI01C,EAgHF,UAAA,WAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAlHI01C,EAkHF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAnHI01C,EAmHF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EApHI01C,EAoHF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EArHI01C,EAqHF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAtHI01C,EAsHF,UAAA,aAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAvHI01C,EAuHF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAxHI01C,EAwHF,UAAA,gBAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA1HI01C,EA0HF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA3HI01C,EA2HF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA5HI01C,EA4HF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA7HI01C,EA6HF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA9HI01C,EA8HF,UAAA,iBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA/HI01C,EA+HF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAhII01C,EAgIF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAjII01C,EAiIF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAlII01C,EAkIF,UAAA,iBAAA,CAAA,EAEA/hB,EAAA,CAAR3zB,EAAA,CAAM,EApII01C,EAoIF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EArII01C,EAqIF,UAAA,YAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAtII01C,EAsIF,UAAA,WAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAvII01C,EAuIF,UAAA,cAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAxII01C,EAwIF,UAAA,iBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAzII01C,EAyIF,UAAA,mBAAA,CAAA,EAGA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA5II01C,EA4IF,UAAA,iBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA7II01C,EA6IF,UAAA,gBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA9II01C,EA8IF,UAAA,aAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EA/II01C,EA+IF,UAAA,kBAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAhJI01C,EAgJF,UAAA,YAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAjJI01C,EAiJF,UAAA,eAAA,CAAA,EACA/hB,EAAA,CAAR3zB,EAAA,CAAM,EAlJI01C,EAkJF,UAAA,eAAA,CAAA,EAlJEA,EAAN/hB,EAAA,CADNC,GAAc,cAAc,CAAA,EAChB8hB,CAAA","x_google_ignoreList":[0,1,2,3,4,5,6,26,39,40,41,43,44,45]} \ No newline at end of file diff --git a/dist/control-ui/assets/index-DsXRcnEw.js b/dist/control-ui/assets/index-DsXRcnEw.js deleted file mode 100644 index 1b0c3042c99..00000000000 --- a/dist/control-ui/assets/index-DsXRcnEw.js +++ /dev/null @@ -1,3059 +0,0 @@ -(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))s(i);new MutationObserver(i=>{for(const o of i)if(o.type==="childList")for(const a of o.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&s(a)}).observe(document,{childList:!0,subtree:!0});function n(i){const o={};return i.integrity&&(o.integrity=i.integrity),i.referrerPolicy&&(o.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?o.credentials="include":i.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function s(i){if(i.ep)return;i.ep=!0;const o=n(i);fetch(i.href,o)}})();const jt=globalThis,Cs=jt.ShadowRoot&&(jt.ShadyCSS===void 0||jt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Es=Symbol(),Ni=new WeakMap;let Go=class{constructor(t,n,s){if(this._$cssResult$=!0,s!==Es)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=n}get styleSheet(){let t=this.o;const n=this.t;if(Cs&&t===void 0){const s=n!==void 0&&n.length===1;s&&(t=Ni.get(n)),t===void 0&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),s&&Ni.set(n,t))}return t}toString(){return this.cssText}};const Br=e=>new Go(typeof e=="string"?e:e+"",void 0,Es),Fr=(e,...t)=>{const n=e.length===1?e[0]:t.reduce((s,i,o)=>s+(a=>{if(a._$cssResult$===!0)return a.cssText;if(typeof a=="number")return a;throw Error("Value passed to 'css' function must be a 'css' function result: "+a+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[o+1],e[0]);return new Go(n,e,Es)},Ur=(e,t)=>{if(Cs)e.adoptedStyleSheets=t.map(n=>n instanceof CSSStyleSheet?n:n.styleSheet);else for(const n of t){const s=document.createElement("style"),i=jt.litNonce;i!==void 0&&s.setAttribute("nonce",i),s.textContent=n.cssText,e.appendChild(s)}},Oi=Cs?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let n="";for(const s of t.cssRules)n+=s.cssText;return Br(n)})(e):e;const{is:Kr,defineProperty:Hr,getOwnPropertyDescriptor:zr,getOwnPropertyNames:jr,getOwnPropertySymbols:qr,getPrototypeOf:Wr}=Object,tn=globalThis,Di=tn.trustedTypes,Vr=Di?Di.emptyScript:"",Gr=tn.reactiveElementPolyfillSupport,mt=(e,t)=>e,Vt={toAttribute(e,t){switch(t){case Boolean:e=e?Vr:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let n=e;switch(t){case Boolean:n=e!==null;break;case Number:n=e===null?null:Number(e);break;case Object:case Array:try{n=JSON.parse(e)}catch{n=null}}return n}},Is=(e,t)=>!Kr(e,t),Bi={attribute:!0,type:String,converter:Vt,reflect:!1,useDefault:!1,hasChanged:Is};Symbol.metadata??=Symbol("metadata"),tn.litPropertyMetadata??=new WeakMap;let Ge=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,n=Bi){if(n.state&&(n.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((n=Object.create(n)).wrapped=!0),this.elementProperties.set(t,n),!n.noAccessor){const s=Symbol(),i=this.getPropertyDescriptor(t,s,n);i!==void 0&&Hr(this.prototype,t,i)}}static getPropertyDescriptor(t,n,s){const{get:i,set:o}=zr(this.prototype,t)??{get(){return this[n]},set(a){this[n]=a}};return{get:i,set(a){const l=i?.call(this);o?.call(this,a),this.requestUpdate(t,l,s)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??Bi}static _$Ei(){if(this.hasOwnProperty(mt("elementProperties")))return;const t=Wr(this);t.finalize(),t.l!==void 0&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(mt("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(mt("properties"))){const n=this.properties,s=[...jr(n),...qr(n)];for(const i of s)this.createProperty(i,n[i])}const t=this[Symbol.metadata];if(t!==null){const n=litPropertyMetadata.get(t);if(n!==void 0)for(const[s,i]of n)this.elementProperties.set(s,i)}this._$Eh=new Map;for(const[n,s]of this.elementProperties){const i=this._$Eu(n,s);i!==void 0&&this._$Eh.set(i,n)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const n=[];if(Array.isArray(t)){const s=new Set(t.flat(1/0).reverse());for(const i of s)n.unshift(Oi(i))}else t!==void 0&&n.push(Oi(t));return n}static _$Eu(t,n){const s=n.attribute;return s===!1?void 0:typeof s=="string"?s:typeof t=="string"?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),this.renderRoot!==void 0&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,n=this.constructor.elementProperties;for(const s of n.keys())this.hasOwnProperty(s)&&(t.set(s,this[s]),delete this[s]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return Ur(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,n,s){this._$AK(t,s)}_$ET(t,n){const s=this.constructor.elementProperties.get(t),i=this.constructor._$Eu(t,s);if(i!==void 0&&s.reflect===!0){const o=(s.converter?.toAttribute!==void 0?s.converter:Vt).toAttribute(n,s.type);this._$Em=t,o==null?this.removeAttribute(i):this.setAttribute(i,o),this._$Em=null}}_$AK(t,n){const s=this.constructor,i=s._$Eh.get(t);if(i!==void 0&&this._$Em!==i){const o=s.getPropertyOptions(i),a=typeof o.converter=="function"?{fromAttribute:o.converter}:o.converter?.fromAttribute!==void 0?o.converter:Vt;this._$Em=i;const l=a.fromAttribute(n,o.type);this[i]=l??this._$Ej?.get(i)??l,this._$Em=null}}requestUpdate(t,n,s,i=!1,o){if(t!==void 0){const a=this.constructor;if(i===!1&&(o=this[t]),s??=a.getPropertyOptions(t),!((s.hasChanged??Is)(o,n)||s.useDefault&&s.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(a._$Eu(t,s))))return;this.C(t,n,s)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(t,n,{useDefault:s,reflect:i,wrapped:o},a){s&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,a??n??this[t]),o!==!0||a!==void 0)||(this._$AL.has(t)||(this.hasUpdated||s||(n=void 0),this._$AL.set(t,n)),i===!0&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(n){Promise.reject(n)}const t=this.scheduleUpdate();return t!=null&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[i,o]of this._$Ep)this[i]=o;this._$Ep=void 0}const s=this.constructor.elementProperties;if(s.size>0)for(const[i,o]of s){const{wrapped:a}=o,l=this[i];a!==!0||this._$AL.has(i)||l===void 0||this.C(i,void 0,o,l)}}let t=!1;const n=this._$AL;try{t=this.shouldUpdate(n),t?(this.willUpdate(n),this._$EO?.forEach(s=>s.hostUpdate?.()),this.update(n)):this._$EM()}catch(s){throw t=!1,this._$EM(),s}t&&this._$AE(n)}willUpdate(t){}_$AE(t){this._$EO?.forEach(n=>n.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(n=>this._$ET(n,this[n])),this._$EM()}updated(t){}firstUpdated(t){}};Ge.elementStyles=[],Ge.shadowRootOptions={mode:"open"},Ge[mt("elementProperties")]=new Map,Ge[mt("finalized")]=new Map,Gr?.({ReactiveElement:Ge}),(tn.reactiveElementVersions??=[]).push("2.1.2");const Ls=globalThis,Fi=e=>e,Gt=Ls.trustedTypes,Ui=Gt?Gt.createPolicy("lit-html",{createHTML:e=>e}):void 0,Yo="$lit$",we=`lit$${Math.random().toFixed(9).slice(2)}$`,Qo="?"+we,Yr=`<${Qo}>`,Ne=document,wt=()=>Ne.createComment(""),$t=e=>e===null||typeof e!="object"&&typeof e!="function",Rs=Array.isArray,Qr=e=>Rs(e)||typeof e?.[Symbol.iterator]=="function",Nn=`[ -\f\r]`,at=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ki=/-->/g,Hi=/>/g,Ee=RegExp(`>|${Nn}(?:([^\\s"'>=/]+)(${Nn}*=${Nn}*(?:[^ -\f\r"'\`<>=]|("|')|))|$)`,"g"),zi=/'/g,ji=/"/g,Jo=/^(?:script|style|textarea|title)$/i,Jr=e=>(t,...n)=>({_$litType$:e,strings:t,values:n}),c=Jr(1),xe=Symbol.for("lit-noChange"),g=Symbol.for("lit-nothing"),qi=new WeakMap,Me=Ne.createTreeWalker(Ne,129);function Zo(e,t){if(!Rs(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return Ui!==void 0?Ui.createHTML(t):t}const Zr=(e,t)=>{const n=e.length-1,s=[];let i,o=t===2?"":t===3?"":"",a=at;for(let l=0;l"?(a=i??at,u=-1):d[1]===void 0?u=-2:(u=a.lastIndex-d[2].length,p=d[1],a=d[3]===void 0?Ee:d[3]==='"'?ji:zi):a===ji||a===zi?a=Ee:a===Ki||a===Hi?a=at:(a=Ee,i=void 0);const v=a===Ee&&e[l+1].startsWith("/>")?" ":"";o+=a===at?r+Yr:u>=0?(s.push(p),r.slice(0,u)+Yo+r.slice(u)+we+v):r+we+(u===-2?l:v)}return[Zo(e,o+(e[n]||"")+(t===2?"":t===3?"":"")),s]};let ns=class Xo{constructor({strings:t,_$litType$:n},s){let i;this.parts=[];let o=0,a=0;const l=t.length-1,r=this.parts,[p,d]=Zr(t,n);if(this.el=Xo.createElement(p,s),Me.currentNode=this.el.content,n===2||n===3){const u=this.el.content.firstChild;u.replaceWith(...u.childNodes)}for(;(i=Me.nextNode())!==null&&r.length0){i.textContent=Gt?Gt.emptyScript:"";for(let v=0;v2||s[0]!==""||s[1]!==""?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=g}_$AI(t,n=this,s,i){const o=this.strings;let a=!1;if(o===void 0)t=Je(this,t,n,0),a=!$t(t)||t!==this._$AH&&t!==xe,a&&(this._$AH=t);else{const l=t;let r,p;for(t=o[0],r=0;r{const s=n?.renderBefore??t;let i=s._$litPart$;if(i===void 0){const o=n?.renderBefore??null;s._$litPart$=i=new nn(t.insertBefore(wt(),o),o,void 0,n??{})}return i._$AI(e),i};const Ms=globalThis;let Qe=class extends Ge{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const n=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=al(n,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return xe}};Qe._$litElement$=!0,Qe.finalized=!0,Ms.litElementHydrateSupport?.({LitElement:Qe});const rl=Ms.litElementPolyfillSupport;rl?.({LitElement:Qe});(Ms.litElementVersions??=[]).push("4.2.2");const ta=e=>(t,n)=>{n!==void 0?n.addInitializer(()=>{customElements.define(e,t)}):customElements.define(e,t)};const ll={attribute:!0,type:String,converter:Vt,reflect:!1,hasChanged:Is},cl=(e=ll,t,n)=>{const{kind:s,metadata:i}=n;let o=globalThis.litPropertyMetadata.get(i);if(o===void 0&&globalThis.litPropertyMetadata.set(i,o=new Map),s==="setter"&&((e=Object.create(e)).wrapped=!0),o.set(n.name,e),s==="accessor"){const{name:a}=n;return{set(l){const r=t.get.call(this);t.set.call(this,l),this.requestUpdate(a,r,e,!0,l)},init(l){return l!==void 0&&this.C(a,void 0,e,l),l}}}if(s==="setter"){const{name:a}=n;return function(l){const r=this[a];t.call(this,l),this.requestUpdate(a,r,e,!0,l)}}throw Error("Unsupported decorator location: "+s)};function on(e){return(t,n)=>typeof n=="object"?cl(e,t,n):((s,i,o)=>{const a=i.hasOwnProperty(o);return i.constructor.createProperty(o,s),a?Object.getOwnPropertyDescriptor(i,o):void 0})(e,t,n)}function y(e){return on({...e,state:!0,attribute:!1})}const dl=50,ul=200,pl="Assistant";function Wi(e,t){if(typeof e!="string")return;const n=e.trim();if(n)return n.length<=t?n:n.slice(0,t)}function ss(e){const t=Wi(e?.name,dl)??pl,n=Wi(e?.avatar??void 0,ul)??null;return{agentId:typeof e?.agentId=="string"&&e.agentId.trim()?e.agentId.trim():null,name:t,avatar:n}}function fl(){return ss(typeof window>"u"?{}:{name:window.__CLAWDBOT_ASSISTANT_NAME__,avatar:window.__CLAWDBOT_ASSISTANT_AVATAR__})}const na="clawdbot.control.settings.v1";function hl(){const t={gatewayUrl:`${location.protocol==="https:"?"wss":"ws"}://${location.host}`,token:"",sessionKey:"main",lastActiveSessionKey:"main",theme:"system",chatFocusMode:!1,chatShowThinking:!0,splitRatio:.6,navCollapsed:!1,navGroupsCollapsed:{}};try{const n=localStorage.getItem(na);if(!n)return t;const s=JSON.parse(n);return{gatewayUrl:typeof s.gatewayUrl=="string"&&s.gatewayUrl.trim()?s.gatewayUrl.trim():t.gatewayUrl,token:typeof s.token=="string"?s.token:t.token,sessionKey:typeof s.sessionKey=="string"&&s.sessionKey.trim()?s.sessionKey.trim():t.sessionKey,lastActiveSessionKey:typeof s.lastActiveSessionKey=="string"&&s.lastActiveSessionKey.trim()?s.lastActiveSessionKey.trim():typeof s.sessionKey=="string"&&s.sessionKey.trim()||t.lastActiveSessionKey,theme:s.theme==="light"||s.theme==="dark"||s.theme==="system"?s.theme:t.theme,chatFocusMode:typeof s.chatFocusMode=="boolean"?s.chatFocusMode:t.chatFocusMode,chatShowThinking:typeof s.chatShowThinking=="boolean"?s.chatShowThinking:t.chatShowThinking,splitRatio:typeof s.splitRatio=="number"&&s.splitRatio>=.4&&s.splitRatio<=.7?s.splitRatio:t.splitRatio,navCollapsed:typeof s.navCollapsed=="boolean"?s.navCollapsed:t.navCollapsed,navGroupsCollapsed:typeof s.navGroupsCollapsed=="object"&&s.navGroupsCollapsed!==null?s.navGroupsCollapsed:t.navGroupsCollapsed}}catch{return t}}function gl(e){localStorage.setItem(na,JSON.stringify(e))}function sa(e){const t=(e??"").trim();if(!t)return null;const n=t.split(":").filter(Boolean);if(n.length<3||n[0]!=="agent")return null;const s=n[1]?.trim(),i=n.slice(2).join(":");return!s||!i?null:{agentId:s,rest:i}}const vl=[{label:"Chat",tabs:["chat"]},{label:"Control",tabs:["overview","channels","instances","sessions","cron"]},{label:"Agent",tabs:["skills","nodes"]},{label:"Settings",tabs:["config","debug","logs"]}],ia={overview:"/overview",channels:"/channels",instances:"/instances",sessions:"/sessions",cron:"/cron",skills:"/skills",nodes:"/nodes",chat:"/chat",config:"/config",debug:"/debug",logs:"/logs"},oa=new Map(Object.entries(ia).map(([e,t])=>[t,e]));function an(e){if(!e)return"";let t=e.trim();return t.startsWith("/")||(t=`/${t}`),t==="/"?"":(t.endsWith("/")&&(t=t.slice(0,-1)),t)}function kt(e){if(!e)return"/";let t=e.trim();return t.startsWith("/")||(t=`/${t}`),t.length>1&&t.endsWith("/")&&(t=t.slice(0,-1)),t}function Ps(e,t=""){const n=an(t),s=ia[e];return n?`${n}${s}`:s}function aa(e,t=""){const n=an(t);let s=e||"/";n&&(s===n?s="/":s.startsWith(`${n}/`)&&(s=s.slice(n.length)));let i=kt(s).toLowerCase();return i.endsWith("/index.html")&&(i="/"),i==="/"?"chat":oa.get(i)??null}function ml(e){let t=kt(e);if(t.endsWith("/index.html")&&(t=kt(t.slice(0,-11))),t==="/")return"";const n=t.split("/").filter(Boolean);if(n.length===0)return"";for(let s=0;s!!(t&&t.trim())).join(", ")}function as(e,t=120){return e.length<=t?e:`${e.slice(0,Math.max(0,t-1))}…`}function la(e,t){return e.length<=t?{text:e,truncated:!1,total:e.length}:{text:e.slice(0,Math.max(0,t)),truncated:!0,total:e.length}}function Yt(e,t){const n=Number(e);return Number.isFinite(n)?n:t}const On=/<\s*\/?\s*think(?:ing)?\s*>/gi,Vi=/<\s*think(?:ing)?\s*>/i,Gi=/<\s*\/\s*think(?:ing)?\s*>/i;function Dn(e){if(!e)return e;const t=Vi.test(e),n=Gi.test(e);if(!t&&!n)return e;if(t!==n)return t?e.replace(Vi,"").trimStart():e.replace(Gi,"").trimStart();if(!On.test(e))return e;On.lastIndex=0;let s="",i=0,o=!1;for(const a of e.matchAll(On)){const l=a.index??0;o||(s+=e.slice(i,l)),o=!a[0].toLowerCase().includes("/"),i=l+a[0].length}return o||(s+=e.slice(i)),s.trimStart()}const wl=/^\[([^\]]+)\]\s*/,$l=["WebChat","WhatsApp","Telegram","Signal","Slack","Discord","iMessage","Teams","Matrix","Zalo","Zalo Personal","BlueBubbles"],Bn=new WeakMap,Fn=new WeakMap;function kl(e){return/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}Z\b/.test(e)||/\d{4}-\d{2}-\d{2} \d{2}:\d{2}\b/.test(e)?!0:$l.some(t=>e.startsWith(`${t} `))}function Un(e){const t=e.match(wl);if(!t)return e;const n=t[1]??"";return kl(n)?e.slice(t[0].length):e}function rs(e){const t=e,n=typeof t.role=="string"?t.role:"",s=t.content;if(typeof s=="string")return n==="assistant"?Dn(s):Un(s);if(Array.isArray(s)){const i=s.map(o=>{const a=o;return a.type==="text"&&typeof a.text=="string"?a.text:null}).filter(o=>typeof o=="string");if(i.length>0){const o=i.join(` -`);return n==="assistant"?Dn(o):Un(o)}}return typeof t.text=="string"?n==="assistant"?Dn(t.text):Un(t.text):null}function ca(e){if(!e||typeof e!="object")return rs(e);const t=e;if(Bn.has(t))return Bn.get(t)??null;const n=rs(e);return Bn.set(t,n),n}function Yi(e){const n=e.content,s=[];if(Array.isArray(n))for(const l of n){const r=l;if(r.type==="thinking"&&typeof r.thinking=="string"){const p=r.thinking.trim();p&&s.push(p)}}if(s.length>0)return s.join(` -`);const i=Al(e);if(!i)return null;const a=[...i.matchAll(/<\s*think(?:ing)?\s*>([\s\S]*?)<\s*\/\s*think(?:ing)?\s*>/gi)].map(l=>(l[1]??"").trim()).filter(Boolean);return a.length>0?a.join(` -`):null}function xl(e){if(!e||typeof e!="object")return Yi(e);const t=e;if(Fn.has(t))return Fn.get(t)??null;const n=Yi(e);return Fn.set(t,n),n}function Al(e){const t=e,n=t.content;if(typeof n=="string")return n;if(Array.isArray(n)){const s=n.map(i=>{const o=i;return o.type==="text"&&typeof o.text=="string"?o.text:null}).filter(i=>typeof i=="string");if(s.length>0)return s.join(` -`)}return typeof t.text=="string"?t.text:null}function Sl(e){const t=e.trim();if(!t)return"";const n=t.split(/\r?\n/).map(s=>s.trim()).filter(Boolean).map(s=>`_${s}_`);return n.length?["_Reasoning:_",...n].join(` -`):""}function Qi(e){e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t="";for(let n=0;n>>8&255,e[2]^=t>>>16&255,e[3]^=t>>>24&255,e}function Ns(e=globalThis.crypto){if(e&&typeof e.randomUUID=="function")return e.randomUUID();if(e&&typeof e.getRandomValues=="function"){const t=new Uint8Array(16);return e.getRandomValues(t),Qi(t)}return Qi(_l())}async function Ze(e){if(!(!e.client||!e.connected)){e.chatLoading=!0,e.lastError=null;try{const t=await e.client.request("chat.history",{sessionKey:e.sessionKey,limit:200});e.chatMessages=Array.isArray(t.messages)?t.messages:[],e.chatThinkingLevel=t.thinkingLevel??null}catch(t){e.lastError=String(t)}finally{e.chatLoading=!1}}}async function Tl(e,t){if(!e.client||!e.connected)return!1;const n=t.trim();if(!n)return!1;const s=Date.now();e.chatMessages=[...e.chatMessages,{role:"user",content:[{type:"text",text:n}],timestamp:s}],e.chatSending=!0,e.lastError=null;const i=Ns();e.chatRunId=i,e.chatStream="",e.chatStreamStartedAt=s;try{return await e.client.request("chat.send",{sessionKey:e.sessionKey,message:n,deliver:!1,idempotencyKey:i}),!0}catch(o){const a=String(o);return e.chatRunId=null,e.chatStream=null,e.chatStreamStartedAt=null,e.lastError=a,e.chatMessages=[...e.chatMessages,{role:"assistant",content:[{type:"text",text:"Error: "+a}],timestamp:Date.now()}],!1}finally{e.chatSending=!1}}async function Cl(e){if(!e.client||!e.connected)return!1;const t=e.chatRunId;try{return await e.client.request("chat.abort",t?{sessionKey:e.sessionKey,runId:t}:{sessionKey:e.sessionKey}),!0}catch(n){return e.lastError=String(n),!1}}function El(e,t){if(!t||t.sessionKey!==e.sessionKey||t.runId&&e.chatRunId&&t.runId!==e.chatRunId)return null;if(t.state==="delta"){const n=rs(t.message);if(typeof n=="string"){const s=e.chatStream??"";(!s||n.length>=s.length)&&(e.chatStream=n)}}else t.state==="final"||t.state==="aborted"?(e.chatStream=null,e.chatRunId=null,e.chatStreamStartedAt=null):t.state==="error"&&(e.chatStream=null,e.chatRunId=null,e.chatStreamStartedAt=null,e.lastError=t.errorMessage??"chat error");return t.state}async function nt(e){if(!(!e.client||!e.connected)&&!e.sessionsLoading){e.sessionsLoading=!0,e.sessionsError=null;try{const t={includeGlobal:e.sessionsIncludeGlobal,includeUnknown:e.sessionsIncludeUnknown},n=Yt(e.sessionsFilterActive,0),s=Yt(e.sessionsFilterLimit,0);n>0&&(t.activeMinutes=n),s>0&&(t.limit=s);const i=await e.client.request("sessions.list",t);i&&(e.sessionsResult=i)}catch(t){e.sessionsError=String(t)}finally{e.sessionsLoading=!1}}}async function Il(e,t,n){if(!e.client||!e.connected)return;const s={key:t};"label"in n&&(s.label=n.label),"thinkingLevel"in n&&(s.thinkingLevel=n.thinkingLevel),"verboseLevel"in n&&(s.verboseLevel=n.verboseLevel),"reasoningLevel"in n&&(s.reasoningLevel=n.reasoningLevel);try{await e.client.request("sessions.patch",s),await nt(e)}catch(i){e.sessionsError=String(i)}}async function Ll(e,t){if(!(!e.client||!e.connected||e.sessionsLoading||!window.confirm(`Delete session "${t}"? - -Deletes the session entry and archives its transcript.`))){e.sessionsLoading=!0,e.sessionsError=null;try{await e.client.request("sessions.delete",{key:t,deleteTranscript:!0}),await nt(e)}catch(s){e.sessionsError=String(s)}finally{e.sessionsLoading=!1}}}const Ji=50,Rl=80,Ml=12e4;function Pl(e){if(!e||typeof e!="object")return null;const t=e;if(typeof t.text=="string")return t.text;const n=t.content;if(!Array.isArray(n))return null;const s=n.map(i=>{if(!i||typeof i!="object")return null;const o=i;return o.type==="text"&&typeof o.text=="string"?o.text:null}).filter(i=>!!i);return s.length===0?null:s.join(` -`)}function Zi(e){if(e==null)return null;if(typeof e=="number"||typeof e=="boolean")return String(e);const t=Pl(e);let n;if(typeof e=="string")n=e;else if(t)n=t;else try{n=JSON.stringify(e,null,2)}catch{n=String(e)}const s=la(n,Ml);return s.truncated?`${s.text} - -… truncated (${s.total} chars, showing first ${s.text.length}).`:s.text}function Nl(e){const t=[];return t.push({type:"toolcall",name:e.name,arguments:e.args??{}}),e.output&&t.push({type:"toolresult",name:e.name,text:e.output}),{role:"assistant",toolCallId:e.toolCallId,runId:e.runId,content:t,timestamp:e.startedAt}}function Ol(e){if(e.toolStreamOrder.length<=Ji)return;const t=e.toolStreamOrder.length-Ji,n=e.toolStreamOrder.splice(0,t);for(const s of n)e.toolStreamById.delete(s)}function Dl(e){e.chatToolMessages=e.toolStreamOrder.map(t=>e.toolStreamById.get(t)?.message).filter(t=>!!t)}function ls(e){e.toolStreamSyncTimer!=null&&(clearTimeout(e.toolStreamSyncTimer),e.toolStreamSyncTimer=null),Dl(e)}function Bl(e,t=!1){if(t){ls(e);return}e.toolStreamSyncTimer==null&&(e.toolStreamSyncTimer=window.setTimeout(()=>ls(e),Rl))}function Os(e){e.toolStreamById.clear(),e.toolStreamOrder=[],e.chatToolMessages=[],ls(e)}const Fl=5e3;function Ul(e,t){const n=t.data??{},s=typeof n.phase=="string"?n.phase:"";e.compactionClearTimer!=null&&(window.clearTimeout(e.compactionClearTimer),e.compactionClearTimer=null),s==="start"?e.compactionStatus={active:!0,startedAt:Date.now(),completedAt:null}:s==="end"&&(e.compactionStatus={active:!1,startedAt:e.compactionStatus?.startedAt??null,completedAt:Date.now()},e.compactionClearTimer=window.setTimeout(()=>{e.compactionStatus=null,e.compactionClearTimer=null},Fl))}function Kl(e,t){if(!t)return;if(t.stream==="compaction"){Ul(e,t);return}if(t.stream!=="tool")return;const n=typeof t.sessionKey=="string"?t.sessionKey:void 0;if(n&&n!==e.sessionKey||!n&&e.chatRunId&&t.runId!==e.chatRunId||e.chatRunId&&t.runId!==e.chatRunId||!e.chatRunId)return;const s=t.data??{},i=typeof s.toolCallId=="string"?s.toolCallId:"";if(!i)return;const o=typeof s.name=="string"?s.name:"tool",a=typeof s.phase=="string"?s.phase:"",l=a==="start"?s.args:void 0,r=a==="update"?Zi(s.partialResult):a==="result"?Zi(s.result):void 0,p=Date.now();let d=e.toolStreamById.get(i);d?(d.name=o,l!==void 0&&(d.args=l),r!==void 0&&(d.output=r),d.updatedAt=p):(d={toolCallId:i,runId:t.runId,sessionKey:n,name:o,args:l,output:r,startedAt:typeof t.ts=="number"?t.ts:p,updatedAt:p,message:{}},e.toolStreamById.set(i,d),e.toolStreamOrder.push(i)),d.message=Nl(d),Ol(e),Bl(e,a==="result")}function rn(e,t=!1){e.chatScrollFrame&&cancelAnimationFrame(e.chatScrollFrame),e.chatScrollTimeout!=null&&(clearTimeout(e.chatScrollTimeout),e.chatScrollTimeout=null);const n=()=>{const s=e.querySelector(".chat-thread");if(s){const i=getComputedStyle(s).overflowY;if(i==="auto"||i==="scroll"||s.scrollHeight-s.clientHeight>1)return s}return document.scrollingElement??document.documentElement};e.updateComplete.then(()=>{e.chatScrollFrame=requestAnimationFrame(()=>{e.chatScrollFrame=null;const s=n();if(!s)return;const i=s.scrollHeight-s.scrollTop-s.clientHeight;if(!(t||e.chatUserNearBottom||i<200))return;t&&(e.chatHasAutoScrolled=!0),s.scrollTop=s.scrollHeight,e.chatUserNearBottom=!0;const a=t?150:120;e.chatScrollTimeout=window.setTimeout(()=>{e.chatScrollTimeout=null;const l=n();if(!l)return;const r=l.scrollHeight-l.scrollTop-l.clientHeight;(t||e.chatUserNearBottom||r<200)&&(l.scrollTop=l.scrollHeight,e.chatUserNearBottom=!0)},a)})})}function da(e,t=!1){e.logsScrollFrame&&cancelAnimationFrame(e.logsScrollFrame),e.updateComplete.then(()=>{e.logsScrollFrame=requestAnimationFrame(()=>{e.logsScrollFrame=null;const n=e.querySelector(".log-stream");if(!n)return;const s=n.scrollHeight-n.scrollTop-n.clientHeight;(t||s<80)&&(n.scrollTop=n.scrollHeight)})})}function Hl(e,t){const n=t.currentTarget;if(!n)return;const s=n.scrollHeight-n.scrollTop-n.clientHeight;e.chatUserNearBottom=s<200}function zl(e,t){const n=t.currentTarget;if(!n)return;const s=n.scrollHeight-n.scrollTop-n.clientHeight;e.logsAtBottom=s<80}function jl(e){e.chatHasAutoScrolled=!1,e.chatUserNearBottom=!0}function ql(e,t){if(e.length===0)return;const n=new Blob([`${e.join(` -`)} -`],{type:"text/plain"}),s=URL.createObjectURL(n),i=document.createElement("a"),o=new Date().toISOString().slice(0,19).replace(/[:T]/g,"-");i.href=s,i.download=`clawdbot-logs-${t}-${o}.log`,i.click(),URL.revokeObjectURL(s)}function Wl(e){if(typeof ResizeObserver>"u")return;const t=e.querySelector(".topbar");if(!t)return;const n=()=>{const{height:s}=t.getBoundingClientRect();e.style.setProperty("--topbar-height",`${s}px`)};n(),e.topbarObserver=new ResizeObserver(()=>n()),e.topbarObserver.observe(t)}function Oe(e){return typeof structuredClone=="function"?structuredClone(e):JSON.parse(JSON.stringify(e))}function Xe(e){return`${JSON.stringify(e,null,2).trimEnd()} -`}function ua(e,t,n){if(t.length===0)return;let s=e;for(let o=0;o0&&(n.timeoutSeconds=s),n}async function Xl(e){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{const t=Jl(e.cronForm),n=Zl(e.cronForm),s=e.cronForm.agentId.trim(),i={name:e.cronForm.name.trim(),description:e.cronForm.description.trim()||void 0,agentId:s||void 0,enabled:e.cronForm.enabled,schedule:t,sessionTarget:e.cronForm.sessionTarget,wakeMode:e.cronForm.wakeMode,payload:n,isolation:e.cronForm.postToMainPrefix.trim()&&e.cronForm.sessionTarget==="isolated"?{postToMainPrefix:e.cronForm.postToMainPrefix.trim()}:void 0};if(!i.name)throw new Error("Name required.");await e.client.request("cron.add",i),e.cronForm={...e.cronForm,name:"",description:"",payloadText:""},await ln(e),await _t(e)}catch(t){e.cronError=String(t)}finally{e.cronBusy=!1}}}async function ec(e,t,n){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.update",{id:t.id,patch:{enabled:n}}),await ln(e),await _t(e)}catch(s){e.cronError=String(s)}finally{e.cronBusy=!1}}}async function tc(e,t){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.run",{id:t.id,mode:"force"}),await ha(e,t.id)}catch(n){e.cronError=String(n)}finally{e.cronBusy=!1}}}async function nc(e,t){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.remove",{id:t.id}),e.cronRunsJobId===t.id&&(e.cronRunsJobId=null,e.cronRuns=[]),await ln(e),await _t(e)}catch(n){e.cronError=String(n)}finally{e.cronBusy=!1}}}async function ha(e,t){if(!(!e.client||!e.connected))try{const n=await e.client.request("cron.runs",{id:t,limit:50});e.cronRunsJobId=t,e.cronRuns=Array.isArray(n.entries)?n.entries:[]}catch(n){e.cronError=String(n)}}async function oe(e,t){if(!(!e.client||!e.connected)&&!e.channelsLoading){e.channelsLoading=!0,e.channelsError=null;try{const n=await e.client.request("channels.status",{probe:t,timeoutMs:8e3});e.channelsSnapshot=n,e.channelsLastSuccess=Date.now()}catch(n){e.channelsError=String(n)}finally{e.channelsLoading=!1}}}async function sc(e,t){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{const n=await e.client.request("web.login.start",{force:t,timeoutMs:3e4});e.whatsappLoginMessage=n.message??null,e.whatsappLoginQrDataUrl=n.qrDataUrl??null,e.whatsappLoginConnected=null}catch(n){e.whatsappLoginMessage=String(n),e.whatsappLoginQrDataUrl=null,e.whatsappLoginConnected=null}finally{e.whatsappBusy=!1}}}async function ic(e){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{const t=await e.client.request("web.login.wait",{timeoutMs:12e4});e.whatsappLoginMessage=t.message??null,e.whatsappLoginConnected=t.connected??null,t.connected&&(e.whatsappLoginQrDataUrl=null)}catch(t){e.whatsappLoginMessage=String(t),e.whatsappLoginConnected=null}finally{e.whatsappBusy=!1}}}async function oc(e){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{await e.client.request("channels.logout",{channel:"whatsapp"}),e.whatsappLoginMessage="Logged out.",e.whatsappLoginQrDataUrl=null,e.whatsappLoginConnected=null}catch(t){e.whatsappLoginMessage=String(t)}finally{e.whatsappBusy=!1}}}async function cn(e){if(!(!e.client||!e.connected)&&!e.debugLoading){e.debugLoading=!0;try{const[t,n,s,i]=await Promise.all([e.client.request("status",{}),e.client.request("health",{}),e.client.request("models.list",{}),e.client.request("last-heartbeat",{})]);e.debugStatus=t,e.debugHealth=n;const o=s;e.debugModels=Array.isArray(o?.models)?o?.models:[],e.debugHeartbeat=i}catch(t){e.debugCallError=String(t)}finally{e.debugLoading=!1}}}async function ac(e){if(!(!e.client||!e.connected)){e.debugCallError=null,e.debugCallResult=null;try{const t=e.debugCallParams.trim()?JSON.parse(e.debugCallParams):{},n=await e.client.request(e.debugCallMethod.trim(),t);e.debugCallResult=JSON.stringify(n,null,2)}catch(t){e.debugCallError=String(t)}}}const rc=2e3,lc=new Set(["trace","debug","info","warn","error","fatal"]);function cc(e){if(typeof e!="string")return null;const t=e.trim();if(!t.startsWith("{")||!t.endsWith("}"))return null;try{const n=JSON.parse(t);return!n||typeof n!="object"?null:n}catch{return null}}function dc(e){if(typeof e!="string")return null;const t=e.toLowerCase();return lc.has(t)?t:null}function uc(e){if(!e.trim())return{raw:e,message:e};try{const t=JSON.parse(e),n=t&&typeof t._meta=="object"&&t._meta!==null?t._meta:null,s=typeof t.time=="string"?t.time:typeof n?.date=="string"?n?.date:null,i=dc(n?.logLevelName??n?.level),o=typeof t[0]=="string"?t[0]:typeof n?.name=="string"?n?.name:null,a=cc(o);let l=null;a&&(typeof a.subsystem=="string"?l=a.subsystem:typeof a.module=="string"&&(l=a.module)),!l&&o&&o.length<120&&(l=o);let r=null;return typeof t[1]=="string"?r=t[1]:!a&&typeof t[0]=="string"?r=t[0]:typeof t.message=="string"&&(r=t.message),{raw:e,time:s,level:i,subsystem:l,message:r??e,meta:n??void 0}}catch{return{raw:e,message:e}}}async function Ds(e,t){if(!(!e.client||!e.connected)&&!(e.logsLoading&&!t?.quiet)){t?.quiet||(e.logsLoading=!0),e.logsError=null;try{const s=await e.client.request("logs.tail",{cursor:t?.reset?void 0:e.logsCursor??void 0,limit:e.logsLimit,maxBytes:e.logsMaxBytes}),o=(Array.isArray(s.lines)?s.lines.filter(l=>typeof l=="string"):[]).map(uc),a=!!(t?.reset||s.reset||e.logsCursor==null);e.logsEntries=a?o:[...e.logsEntries,...o].slice(-rc),typeof s.cursor=="number"&&(e.logsCursor=s.cursor),typeof s.file=="string"&&(e.logsFile=s.file),e.logsTruncated=!!s.truncated,e.logsLastFetchAt=Date.now()}catch(n){e.logsError=String(n)}finally{t?.quiet||(e.logsLoading=!1)}}}const ga={p:0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,n:0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,h:8n,a:0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,d:0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,Gx:0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,Gy:0x6666666666666666666666666666666666666666666666666666666666666658n},{p:W,n:qt,Gx:eo,Gy:to,a:Kn,d:Hn,h:pc}=ga,De=32,Bs=64,fc=(...e)=>{"captureStackTrace"in Error&&typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(...e)},H=(e="")=>{const t=new Error(e);throw fc(t,H),t},hc=e=>typeof e=="bigint",gc=e=>typeof e=="string",vc=e=>e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array",Ae=(e,t,n="")=>{const s=vc(e),i=e?.length,o=t!==void 0;if(!s||o&&i!==t){const a=n&&`"${n}" `,l=o?` of length ${t}`:"",r=s?`length=${i}`:`type=${typeof e}`;H(a+"expected Uint8Array"+l+", got "+r)}return e},dn=e=>new Uint8Array(e),va=e=>Uint8Array.from(e),ma=(e,t)=>e.toString(16).padStart(t,"0"),ba=e=>Array.from(Ae(e)).map(t=>ma(t,2)).join(""),ge={_0:48,_9:57,A:65,F:70,a:97,f:102},no=e=>{if(e>=ge._0&&e<=ge._9)return e-ge._0;if(e>=ge.A&&e<=ge.F)return e-(ge.A-10);if(e>=ge.a&&e<=ge.f)return e-(ge.a-10)},ya=e=>{const t="hex invalid";if(!gc(e))return H(t);const n=e.length,s=n/2;if(n%2)return H(t);const i=dn(s);for(let o=0,a=0;oglobalThis?.crypto,mc=()=>wa()?.subtle??H("crypto.subtle must be defined, consider polyfill"),At=(...e)=>{const t=dn(e.reduce((s,i)=>s+Ae(i).length,0));let n=0;return e.forEach(s=>{t.set(s,n),n+=s.length}),t},bc=(e=De)=>wa().getRandomValues(dn(e)),Qt=BigInt,Re=(e,t,n,s="bad number: out of range")=>hc(e)&&t<=e&&e{const n=e%t;return n>=0n?n:t+n},$a=e=>S(e,qt),yc=(e,t)=>{(e===0n||t<=0n)&&H("no inverse n="+e+" mod="+t);let n=S(e,t),s=t,i=0n,o=1n;for(;n!==0n;){const a=s/n,l=s%n,r=i-o*a;s=n,n=l,i=o,o=r}return s===1n?S(i,t):H("no inverse")},wc=e=>{const t=Sa[e];return typeof t!="function"&&H("hashes."+e+" not set"),t},zn=e=>e instanceof X?e:H("Point expected"),ds=2n**256n;class X{static BASE;static ZERO;X;Y;Z;T;constructor(t,n,s,i){const o=ds;this.X=Re(t,0n,o),this.Y=Re(n,0n,o),this.Z=Re(s,1n,o),this.T=Re(i,0n,o),Object.freeze(this)}static CURVE(){return ga}static fromAffine(t){return new X(t.x,t.y,1n,S(t.x*t.y))}static fromBytes(t,n=!1){const s=Hn,i=va(Ae(t,De)),o=t[31];i[31]=o&-129;const a=xa(i);Re(a,0n,n?ds:W);const r=S(a*a),p=S(r-1n),d=S(s*r+1n);let{isValid:u,value:h}=kc(p,d);u||H("bad point: y not sqrt");const v=(h&1n)===1n,w=(o&128)!==0;return!n&&h===0n&&w&&H("bad point: x==0, isLastByteOdd"),w!==v&&(h=S(-h)),new X(h,a,1n,S(h*a))}static fromHex(t,n){return X.fromBytes(ya(t),n)}get x(){return this.toAffine().x}get y(){return this.toAffine().y}assertValidity(){const t=Kn,n=Hn,s=this;if(s.is0())return H("bad point: ZERO");const{X:i,Y:o,Z:a,T:l}=s,r=S(i*i),p=S(o*o),d=S(a*a),u=S(d*d),h=S(r*t),v=S(d*S(h+p)),w=S(u+S(n*S(r*p)));if(v!==w)return H("bad point: equation left != right (1)");const $=S(i*o),x=S(a*l);return $!==x?H("bad point: equation left != right (2)"):this}equals(t){const{X:n,Y:s,Z:i}=this,{X:o,Y:a,Z:l}=zn(t),r=S(n*l),p=S(o*i),d=S(s*l),u=S(a*i);return r===p&&d===u}is0(){return this.equals(Ye)}negate(){return new X(S(-this.X),this.Y,this.Z,S(-this.T))}double(){const{X:t,Y:n,Z:s}=this,i=Kn,o=S(t*t),a=S(n*n),l=S(2n*S(s*s)),r=S(i*o),p=t+n,d=S(S(p*p)-o-a),u=r+a,h=u-l,v=r-a,w=S(d*h),$=S(u*v),x=S(d*v),C=S(h*u);return new X(w,$,C,x)}add(t){const{X:n,Y:s,Z:i,T:o}=this,{X:a,Y:l,Z:r,T:p}=zn(t),d=Kn,u=Hn,h=S(n*a),v=S(s*l),w=S(o*u*p),$=S(i*r),x=S((n+s)*(a+l)-h-v),C=S($-w),I=S($+w),R=S(v-d*h),E=S(x*C),A=S(I*R),B=S(x*R),ue=S(C*I);return new X(E,A,ue,B)}subtract(t){return this.add(zn(t).negate())}multiply(t,n=!0){if(!n&&(t===0n||this.is0()))return Ye;if(Re(t,1n,qt),t===1n)return this;if(this.equals(Be))return Mc(t).p;let s=Ye,i=Be;for(let o=this;t>0n;o=o.double(),t>>=1n)t&1n?s=s.add(o):n&&(i=i.add(o));return s}multiplyUnsafe(t){return this.multiply(t,!1)}toAffine(){const{X:t,Y:n,Z:s}=this;if(this.equals(Ye))return{x:0n,y:1n};const i=yc(s,W);S(s*i)!==1n&&H("invalid inverse");const o=S(t*i),a=S(n*i);return{x:o,y:a}}toBytes(){const{x:t,y:n}=this.assertValidity().toAffine(),s=ka(n);return s[31]|=t&1n?128:0,s}toHex(){return ba(this.toBytes())}clearCofactor(){return this.multiply(Qt(pc),!1)}isSmallOrder(){return this.clearCofactor().is0()}isTorsionFree(){let t=this.multiply(qt/2n,!1).double();return qt%2n&&(t=t.add(this)),t.is0()}}const Be=new X(eo,to,1n,S(eo*to)),Ye=new X(0n,1n,1n,0n);X.BASE=Be;X.ZERO=Ye;const ka=e=>ya(ma(Re(e,0n,ds),Bs)).reverse(),xa=e=>Qt("0x"+ba(va(Ae(e)).reverse())),le=(e,t)=>{let n=e;for(;t-- >0n;)n*=n,n%=W;return n},$c=e=>{const n=e*e%W*e%W,s=le(n,2n)*n%W,i=le(s,1n)*e%W,o=le(i,5n)*i%W,a=le(o,10n)*o%W,l=le(a,20n)*a%W,r=le(l,40n)*l%W,p=le(r,80n)*r%W,d=le(p,80n)*r%W,u=le(d,10n)*o%W;return{pow_p_5_8:le(u,2n)*e%W,b2:n}},so=0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n,kc=(e,t)=>{const n=S(t*t*t),s=S(n*n*t),i=$c(e*s).pow_p_5_8;let o=S(e*n*i);const a=S(t*o*o),l=o,r=S(o*so),p=a===e,d=a===S(-e),u=a===S(-e*so);return p&&(o=l),(d||u)&&(o=r),(S(o)&1n)===1n&&(o=S(-o)),{isValid:p||d,value:o}},us=e=>$a(xa(e)),Fs=(...e)=>Sa.sha512Async(At(...e)),xc=(...e)=>wc("sha512")(At(...e)),Aa=e=>{const t=e.slice(0,De);t[0]&=248,t[31]&=127,t[31]|=64;const n=e.slice(De,Bs),s=us(t),i=Be.multiply(s),o=i.toBytes();return{head:t,prefix:n,scalar:s,point:i,pointBytes:o}},Us=e=>Fs(Ae(e,De)).then(Aa),Ac=e=>Aa(xc(Ae(e,De))),Sc=e=>Us(e).then(t=>t.pointBytes),_c=e=>Fs(e.hashable).then(e.finish),Tc=(e,t,n)=>{const{pointBytes:s,scalar:i}=e,o=us(t),a=Be.multiply(o).toBytes();return{hashable:At(a,s,n),finish:p=>{const d=$a(o+us(p)*i);return Ae(At(a,ka(d)),Bs)}}},Cc=async(e,t)=>{const n=Ae(e),s=await Us(t),i=await Fs(s.prefix,n);return _c(Tc(s,i,n))},Sa={sha512Async:async e=>{const t=mc(),n=At(e);return dn(await t.digest("SHA-512",n.buffer))},sha512:void 0},Ec=(e=bc(De))=>e,Ic={getExtendedPublicKeyAsync:Us,getExtendedPublicKey:Ac,randomSecretKey:Ec},Jt=8,Lc=256,_a=Math.ceil(Lc/Jt)+1,ps=2**(Jt-1),Rc=()=>{const e=[];let t=Be,n=t;for(let s=0;s<_a;s++){n=t,e.push(n);for(let i=1;i{const n=t.negate();return e?n:t},Mc=e=>{const t=io||(io=Rc());let n=Ye,s=Be;const i=2**Jt,o=i,a=Qt(i-1),l=Qt(Jt);for(let r=0;r<_a;r++){let p=Number(e&a);e>>=l,p>ps&&(p-=o,e+=1n);const d=r*ps,u=d,h=d+Math.abs(p)-1,v=r%2!==0,w=p<0;p===0?s=s.add(oo(v,t[u])):n=n.add(oo(w,t[h]))}return e!==0n&&H("invalid wnaf"),{p:n,f:s}},jn="clawdbot-device-identity-v1";function fs(e){let t="";for(const n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll("+","-").replaceAll("/","_").replace(/=+$/g,"")}function Ta(e){const t=e.replaceAll("-","+").replaceAll("_","/"),n=t+"=".repeat((4-t.length%4)%4),s=atob(n),i=new Uint8Array(s.length);for(let o=0;ot.toString(16).padStart(2,"0")).join("")}async function Ca(e){const t=await crypto.subtle.digest("SHA-256",e);return Pc(new Uint8Array(t))}async function Nc(){const e=Ic.randomSecretKey(),t=await Sc(e);return{deviceId:await Ca(t),publicKey:fs(t),privateKey:fs(e)}}async function Ks(){try{const n=localStorage.getItem(jn);if(n){const s=JSON.parse(n);if(s?.version===1&&typeof s.deviceId=="string"&&typeof s.publicKey=="string"&&typeof s.privateKey=="string"){const i=await Ca(Ta(s.publicKey));if(i!==s.deviceId){const o={...s,deviceId:i};return localStorage.setItem(jn,JSON.stringify(o)),{deviceId:i,publicKey:s.publicKey,privateKey:s.privateKey}}return{deviceId:s.deviceId,publicKey:s.publicKey,privateKey:s.privateKey}}}}catch{}const e=await Nc(),t={version:1,deviceId:e.deviceId,publicKey:e.publicKey,privateKey:e.privateKey,createdAtMs:Date.now()};return localStorage.setItem(jn,JSON.stringify(t)),e}async function Oc(e,t){const n=Ta(e),s=new TextEncoder().encode(t),i=await Cc(s,n);return fs(i)}const Ea="clawdbot.device.auth.v1";function Hs(e){return e.trim()}function Dc(e){if(!Array.isArray(e))return[];const t=new Set;for(const n of e){const s=n.trim();s&&t.add(s)}return[...t].sort()}function zs(){try{const e=window.localStorage.getItem(Ea);if(!e)return null;const t=JSON.parse(e);return!t||t.version!==1||!t.deviceId||typeof t.deviceId!="string"||!t.tokens||typeof t.tokens!="object"?null:t}catch{return null}}function Ia(e){try{window.localStorage.setItem(Ea,JSON.stringify(e))}catch{}}function Bc(e){const t=zs();if(!t||t.deviceId!==e.deviceId)return null;const n=Hs(e.role),s=t.tokens[n];return!s||typeof s.token!="string"?null:s}function La(e){const t=Hs(e.role),n={version:1,deviceId:e.deviceId,tokens:{}},s=zs();s&&s.deviceId===e.deviceId&&(n.tokens={...s.tokens});const i={token:e.token,role:t,scopes:Dc(e.scopes),updatedAtMs:Date.now()};return n.tokens[t]=i,Ia(n),i}function Ra(e){const t=zs();if(!t||t.deviceId!==e.deviceId)return;const n=Hs(e.role);if(!t.tokens[n])return;const s={...t,tokens:{...t.tokens}};delete s.tokens[n],Ia(s)}async function Se(e,t){if(!(!e.client||!e.connected)&&!e.devicesLoading){e.devicesLoading=!0,t?.quiet||(e.devicesError=null);try{const n=await e.client.request("device.pair.list",{});e.devicesList={pending:Array.isArray(n?.pending)?n.pending:[],paired:Array.isArray(n?.paired)?n.paired:[]}}catch(n){t?.quiet||(e.devicesError=String(n))}finally{e.devicesLoading=!1}}}async function Fc(e,t){if(!(!e.client||!e.connected))try{await e.client.request("device.pair.approve",{requestId:t}),await Se(e)}catch(n){e.devicesError=String(n)}}async function Uc(e,t){if(!(!e.client||!e.connected||!window.confirm("Reject this device pairing request?")))try{await e.client.request("device.pair.reject",{requestId:t}),await Se(e)}catch(s){e.devicesError=String(s)}}async function Kc(e,t){if(!(!e.client||!e.connected))try{const n=await e.client.request("device.token.rotate",t);if(n?.token){const s=await Ks(),i=n.role??t.role;(n.deviceId===s.deviceId||t.deviceId===s.deviceId)&&La({deviceId:s.deviceId,role:i,token:n.token,scopes:n.scopes??t.scopes??[]}),window.prompt("New device token (copy and store securely):",n.token)}await Se(e)}catch(n){e.devicesError=String(n)}}async function Hc(e,t){if(!(!e.client||!e.connected||!window.confirm(`Revoke token for ${t.deviceId} (${t.role})?`)))try{await e.client.request("device.token.revoke",t);const s=await Ks();t.deviceId===s.deviceId&&Ra({deviceId:s.deviceId,role:t.role}),await Se(e)}catch(s){e.devicesError=String(s)}}async function un(e,t){if(!(!e.client||!e.connected)&&!e.nodesLoading){e.nodesLoading=!0,t?.quiet||(e.lastError=null);try{const n=await e.client.request("node.list",{});e.nodes=Array.isArray(n.nodes)?n.nodes:[]}catch(n){t?.quiet||(e.lastError=String(n))}finally{e.nodesLoading=!1}}}function zc(e){if(!e||e.kind==="gateway")return{method:"exec.approvals.get",params:{}};const t=e.nodeId.trim();return t?{method:"exec.approvals.node.get",params:{nodeId:t}}:null}function jc(e,t){if(!e||e.kind==="gateway")return{method:"exec.approvals.set",params:t};const n=e.nodeId.trim();return n?{method:"exec.approvals.node.set",params:{...t,nodeId:n}}:null}async function js(e,t){if(!(!e.client||!e.connected)&&!e.execApprovalsLoading){e.execApprovalsLoading=!0,e.lastError=null;try{const n=zc(t);if(!n){e.lastError="Select a node before loading exec approvals.";return}const s=await e.client.request(n.method,n.params);qc(e,s)}catch(n){e.lastError=String(n)}finally{e.execApprovalsLoading=!1}}}function qc(e,t){e.execApprovalsSnapshot=t,e.execApprovalsDirty||(e.execApprovalsForm=Oe(t.file??{}))}async function Wc(e,t){if(!(!e.client||!e.connected)){e.execApprovalsSaving=!0,e.lastError=null;try{const n=e.execApprovalsSnapshot?.hash;if(!n){e.lastError="Exec approvals hash missing; reload and retry.";return}const s=e.execApprovalsForm??e.execApprovalsSnapshot?.file??{},i=jc(t,{file:s,baseHash:n});if(!i){e.lastError="Select a node before saving exec approvals.";return}await e.client.request(i.method,i.params),e.execApprovalsDirty=!1,await js(e,t)}catch(n){e.lastError=String(n)}finally{e.execApprovalsSaving=!1}}}function Vc(e,t,n){const s=Oe(e.execApprovalsForm??e.execApprovalsSnapshot?.file??{});ua(s,t,n),e.execApprovalsForm=s,e.execApprovalsDirty=!0}function Gc(e,t){const n=Oe(e.execApprovalsForm??e.execApprovalsSnapshot?.file??{});pa(n,t),e.execApprovalsForm=n,e.execApprovalsDirty=!0}async function qs(e){if(!(!e.client||!e.connected)&&!e.presenceLoading){e.presenceLoading=!0,e.presenceError=null,e.presenceStatus=null;try{const t=await e.client.request("system-presence",{});Array.isArray(t)?(e.presenceEntries=t,e.presenceStatus=t.length===0?"No instances yet.":null):(e.presenceEntries=[],e.presenceStatus="No presence payload.")}catch(t){e.presenceError=String(t)}finally{e.presenceLoading=!1}}}function et(e,t,n){if(!t.trim())return;const s={...e.skillMessages};n?s[t]=n:delete s[t],e.skillMessages=s}function pn(e){return e instanceof Error?e.message:String(e)}async function Tt(e,t){if(t?.clearMessages&&Object.keys(e.skillMessages).length>0&&(e.skillMessages={}),!(!e.client||!e.connected)&&!e.skillsLoading){e.skillsLoading=!0,e.skillsError=null;try{const n=await e.client.request("skills.status",{});n&&(e.skillsReport=n)}catch(n){e.skillsError=pn(n)}finally{e.skillsLoading=!1}}}function Yc(e,t,n){e.skillEdits={...e.skillEdits,[t]:n}}async function Qc(e,t,n){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{await e.client.request("skills.update",{skillKey:t,enabled:n}),await Tt(e),et(e,t,{kind:"success",message:n?"Skill enabled":"Skill disabled"})}catch(s){const i=pn(s);e.skillsError=i,et(e,t,{kind:"error",message:i})}finally{e.skillsBusyKey=null}}}async function Jc(e,t){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{const n=e.skillEdits[t]??"";await e.client.request("skills.update",{skillKey:t,apiKey:n}),await Tt(e),et(e,t,{kind:"success",message:"API key saved"})}catch(n){const s=pn(n);e.skillsError=s,et(e,t,{kind:"error",message:s})}finally{e.skillsBusyKey=null}}}async function Zc(e,t,n,s){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{const i=await e.client.request("skills.install",{name:n,installId:s,timeoutMs:12e4});await Tt(e),et(e,t,{kind:"success",message:i?.message??"Installed"})}catch(i){const o=pn(i);e.skillsError=o,et(e,t,{kind:"error",message:o})}finally{e.skillsBusyKey=null}}}function Xc(){return typeof window>"u"||typeof window.matchMedia!="function"||window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function Ws(e){return e==="system"?Xc():e}const Dt=e=>Number.isNaN(e)?.5:e<=0?0:e>=1?1:e,ed=()=>typeof window>"u"||typeof window.matchMedia!="function"?!1:window.matchMedia("(prefers-reduced-motion: reduce)").matches??!1,Bt=e=>{e.classList.remove("theme-transition"),e.style.removeProperty("--theme-switch-x"),e.style.removeProperty("--theme-switch-y")},td=({nextTheme:e,applyTheme:t,context:n,currentTheme:s})=>{if(s===e)return;const i=globalThis.document??null;if(!i){t();return}const o=i.documentElement,a=i,l=ed();if(!!a.startViewTransition&&!l){let p=.5,d=.5;if(n?.pointerClientX!==void 0&&n?.pointerClientY!==void 0&&typeof window<"u")p=Dt(n.pointerClientX/window.innerWidth),d=Dt(n.pointerClientY/window.innerHeight);else if(n?.element){const u=n.element.getBoundingClientRect();u.width>0&&u.height>0&&typeof window<"u"&&(p=Dt((u.left+u.width/2)/window.innerWidth),d=Dt((u.top+u.height/2)/window.innerHeight))}o.style.setProperty("--theme-switch-x",`${p*100}%`),o.style.setProperty("--theme-switch-y",`${d*100}%`),o.classList.add("theme-transition");try{const u=a.startViewTransition?.(()=>{t()});u?.finished?u.finished.finally(()=>Bt(o)):Bt(o)}catch{Bt(o),t()}return}t(),Bt(o)};function nd(e){e.nodesPollInterval==null&&(e.nodesPollInterval=window.setInterval(()=>{un(e,{quiet:!0})},5e3))}function sd(e){e.nodesPollInterval!=null&&(clearInterval(e.nodesPollInterval),e.nodesPollInterval=null)}function Vs(e){e.logsPollInterval==null&&(e.logsPollInterval=window.setInterval(()=>{e.tab==="logs"&&Ds(e,{quiet:!0})},2e3))}function Gs(e){e.logsPollInterval!=null&&(clearInterval(e.logsPollInterval),e.logsPollInterval=null)}function Ys(e){e.debugPollInterval==null&&(e.debugPollInterval=window.setInterval(()=>{e.tab==="debug"&&cn(e)},3e3))}function Qs(e){e.debugPollInterval!=null&&(clearInterval(e.debugPollInterval),e.debugPollInterval=null)}function $e(e,t){const n={...t,lastActiveSessionKey:t.lastActiveSessionKey?.trim()||t.sessionKey.trim()||"main"};e.settings=n,gl(n),t.theme!==e.theme&&(e.theme=t.theme,fn(e,Ws(t.theme))),e.applySessionKey=e.settings.lastActiveSessionKey}function Ma(e,t){const n=t.trim();n&&e.settings.lastActiveSessionKey!==n&&$e(e,{...e.settings,lastActiveSessionKey:n})}function id(e){if(!window.location.search)return;const t=new URLSearchParams(window.location.search),n=t.get("token"),s=t.get("password"),i=t.get("session"),o=t.get("gatewayUrl");let a=!1;if(n!=null){const r=n.trim();r&&r!==e.settings.token&&$e(e,{...e.settings,token:r}),t.delete("token"),a=!0}if(s!=null){const r=s.trim();r&&(e.password=r),t.delete("password"),a=!0}if(i!=null){const r=i.trim();r&&(e.sessionKey=r,$e(e,{...e.settings,sessionKey:r,lastActiveSessionKey:r}))}if(o!=null){const r=o.trim();r&&r!==e.settings.gatewayUrl&&$e(e,{...e.settings,gatewayUrl:r}),t.delete("gatewayUrl"),a=!0}if(!a)return;const l=new URL(window.location.href);l.search=t.toString(),window.history.replaceState({},"",l.toString())}function od(e,t){e.tab!==t&&(e.tab=t),t==="chat"&&(e.chatHasAutoScrolled=!1),t==="logs"?Vs(e):Gs(e),t==="debug"?Ys(e):Qs(e),Js(e),Na(e,t,!1)}function ad(e,t,n){td({nextTheme:t,applyTheme:()=>{e.theme=t,$e(e,{...e.settings,theme:t}),fn(e,Ws(t))},context:n,currentTheme:e.theme})}async function Js(e){e.tab==="overview"&&await Oa(e),e.tab==="channels"&&await hd(e),e.tab==="instances"&&await qs(e),e.tab==="sessions"&&await nt(e),e.tab==="cron"&&await Zs(e),e.tab==="skills"&&await Tt(e),e.tab==="nodes"&&(await un(e),await Se(e),await me(e),await js(e)),e.tab==="chat"&&(await yd(e),rn(e,!e.chatHasAutoScrolled)),e.tab==="config"&&(await fa(e),await me(e)),e.tab==="debug"&&(await cn(e),e.eventLog=e.eventLogBuffer),e.tab==="logs"&&(e.logsAtBottom=!0,await Ds(e,{reset:!0}),da(e,!0))}function rd(){if(typeof window>"u")return"";const e=window.__CLAWDBOT_CONTROL_UI_BASE_PATH__;return typeof e=="string"&&e.trim()?an(e):ml(window.location.pathname)}function ld(e){e.theme=e.settings.theme??"system",fn(e,Ws(e.theme))}function fn(e,t){if(e.themeResolved=t,typeof document>"u")return;const n=document.documentElement;n.dataset.theme=t,n.style.colorScheme=t}function cd(e){if(typeof window>"u"||typeof window.matchMedia!="function")return;if(e.themeMedia=window.matchMedia("(prefers-color-scheme: dark)"),e.themeMediaHandler=n=>{e.theme==="system"&&fn(e,n.matches?"dark":"light")},typeof e.themeMedia.addEventListener=="function"){e.themeMedia.addEventListener("change",e.themeMediaHandler);return}e.themeMedia.addListener(e.themeMediaHandler)}function dd(e){if(!e.themeMedia||!e.themeMediaHandler)return;if(typeof e.themeMedia.removeEventListener=="function"){e.themeMedia.removeEventListener("change",e.themeMediaHandler);return}e.themeMedia.removeListener(e.themeMediaHandler),e.themeMedia=null,e.themeMediaHandler=null}function ud(e,t){if(typeof window>"u")return;const n=aa(window.location.pathname,e.basePath)??"chat";Pa(e,n),Na(e,n,t)}function pd(e){if(typeof window>"u")return;const t=aa(window.location.pathname,e.basePath);if(!t)return;const s=new URL(window.location.href).searchParams.get("session")?.trim();s&&(e.sessionKey=s,$e(e,{...e.settings,sessionKey:s,lastActiveSessionKey:s})),Pa(e,t)}function Pa(e,t){e.tab!==t&&(e.tab=t),t==="chat"&&(e.chatHasAutoScrolled=!1),t==="logs"?Vs(e):Gs(e),t==="debug"?Ys(e):Qs(e),e.connected&&Js(e)}function Na(e,t,n){if(typeof window>"u")return;const s=kt(Ps(t,e.basePath)),i=kt(window.location.pathname),o=new URL(window.location.href);t==="chat"&&e.sessionKey?o.searchParams.set("session",e.sessionKey):o.searchParams.delete("session"),i!==s&&(o.pathname=s),n?window.history.replaceState({},"",o.toString()):window.history.pushState({},"",o.toString())}function fd(e,t,n){if(typeof window>"u")return;const s=new URL(window.location.href);s.searchParams.set("session",t),window.history.replaceState({},"",s.toString())}async function Oa(e){await Promise.all([oe(e,!1),qs(e),nt(e),_t(e),cn(e)])}async function hd(e){await Promise.all([oe(e,!0),fa(e),me(e)])}async function Zs(e){await Promise.all([oe(e,!1),_t(e),ln(e)])}function Da(e){return e.chatSending||!!e.chatRunId}function gd(e){const t=e.trim();if(!t)return!1;const n=t.toLowerCase();return n==="/stop"?!0:n==="stop"||n==="esc"||n==="abort"||n==="wait"||n==="exit"}async function Ba(e){e.connected&&(e.chatMessage="",await Cl(e))}function vd(e,t){const n=t.trim();n&&(e.chatQueue=[...e.chatQueue,{id:Ns(),text:n,createdAt:Date.now()}])}async function Fa(e,t,n){Os(e);const s=await Tl(e,t);return!s&&n?.previousDraft!=null&&(e.chatMessage=n.previousDraft),s&&Ma(e,e.sessionKey),s&&n?.restoreDraft&&n.previousDraft?.trim()&&(e.chatMessage=n.previousDraft),rn(e),s&&!e.chatRunId&&Ua(e),s}async function Ua(e){if(!e.connected||Da(e))return;const[t,...n]=e.chatQueue;if(!t)return;e.chatQueue=n,await Fa(e,t.text)||(e.chatQueue=[t,...e.chatQueue])}function md(e,t){e.chatQueue=e.chatQueue.filter(n=>n.id!==t)}async function bd(e,t,n){if(!e.connected)return;const s=e.chatMessage,i=(t??e.chatMessage).trim();if(i){if(gd(i)){await Ba(e);return}if(t==null&&(e.chatMessage=""),Da(e)){vd(e,i);return}await Fa(e,i,{previousDraft:t==null?s:void 0,restoreDraft:!!(t&&n?.restoreDraft)})}}async function yd(e){await Promise.all([Ze(e),nt(e),hs(e)]),rn(e,!0)}const wd=Ua;function $d(e){const t=sa(e.sessionKey);return t?.agentId?t.agentId:e.hello?.snapshot?.sessionDefaults?.defaultAgentId?.trim()||"main"}function kd(e,t){const n=an(e),s=encodeURIComponent(t);return n?`${n}/avatar/${s}?meta=1`:`/avatar/${s}?meta=1`}async function hs(e){if(!e.connected){e.chatAvatarUrl=null;return}const t=$d(e);if(!t){e.chatAvatarUrl=null;return}e.chatAvatarUrl=null;const n=kd(e.basePath,t);try{const s=await fetch(n,{method:"GET"});if(!s.ok){e.chatAvatarUrl=null;return}const i=await s.json(),o=typeof i.avatarUrl=="string"?i.avatarUrl.trim():"";e.chatAvatarUrl=o||null}catch{e.chatAvatarUrl=null}}const Ka={CHILD:2},Ha=e=>(...t)=>({_$litDirective$:e,values:t});let za=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,n,s){this._$Ct=t,this._$AM=n,this._$Ci=s}_$AS(t,n){return this.update(t,n)}update(t,n){return this.render(...n)}};const{I:xd}=il,ao=e=>e,ro=()=>document.createComment(""),rt=(e,t,n)=>{const s=e._$AA.parentNode,i=t===void 0?e._$AB:t._$AA;if(n===void 0){const o=s.insertBefore(ro(),i),a=s.insertBefore(ro(),i);n=new xd(o,a,e,e.options)}else{const o=n._$AB.nextSibling,a=n._$AM,l=a!==e;if(l){let r;n._$AQ?.(e),n._$AM=e,n._$AP!==void 0&&(r=e._$AU)!==a._$AU&&n._$AP(r)}if(o!==i||l){let r=n._$AA;for(;r!==o;){const p=ao(r).nextSibling;ao(s).insertBefore(r,i),r=p}}}return n},Ie=(e,t,n=e)=>(e._$AI(t,n),e),Ad={},Sd=(e,t=Ad)=>e._$AH=t,_d=e=>e._$AH,qn=e=>{e._$AR(),e._$AA.remove()};const lo=(e,t,n)=>{const s=new Map;for(let i=t;i<=n;i++)s.set(e[i],i);return s},ja=Ha(class extends za{constructor(e){if(super(e),e.type!==Ka.CHILD)throw Error("repeat() can only be used in text expressions")}dt(e,t,n){let s;n===void 0?n=t:t!==void 0&&(s=t);const i=[],o=[];let a=0;for(const l of e)i[a]=s?s(l,a):a,o[a]=n(l,a),a++;return{values:o,keys:i}}render(e,t,n){return this.dt(e,t,n).values}update(e,[t,n,s]){const i=_d(e),{values:o,keys:a}=this.dt(t,n,s);if(!Array.isArray(i))return this.ut=a,o;const l=this.ut??=[],r=[];let p,d,u=0,h=i.length-1,v=0,w=o.length-1;for(;u<=h&&v<=w;)if(i[u]===null)u++;else if(i[h]===null)h--;else if(l[u]===a[v])r[v]=Ie(i[u],o[v]),u++,v++;else if(l[h]===a[w])r[w]=Ie(i[h],o[w]),h--,w--;else if(l[u]===a[w])r[w]=Ie(i[u],o[w]),rt(e,r[w+1],i[u]),u++,w--;else if(l[h]===a[v])r[v]=Ie(i[h],o[v]),rt(e,i[u],i[h]),h--,v++;else if(p===void 0&&(p=lo(a,v,w),d=lo(l,u,h)),p.has(l[u]))if(p.has(l[h])){const $=d.get(a[v]),x=$!==void 0?i[$]:null;if(x===null){const C=rt(e,i[u]);Ie(C,o[v]),r[v]=C}else r[v]=Ie(x,o[v]),rt(e,i[u],x),i[$]=null;v++}else qn(i[h]),h--;else qn(i[u]),u++;for(;v<=w;){const $=rt(e,r[w+1]);Ie($,o[v]),r[v++]=$}for(;u<=h;){const $=i[u++];$!==null&&qn($)}return this.ut=a,Sd(e,r),xe}});function qa(e){const t=e;let n=typeof t.role=="string"?t.role:"unknown";const s=typeof t.toolCallId=="string"||typeof t.tool_call_id=="string",i=t.content,o=Array.isArray(i)?i:null,a=Array.isArray(o)&&o.some(u=>{const v=String(u.type??"").toLowerCase();return v==="toolresult"||v==="tool_result"}),l=typeof t.toolName=="string"||typeof t.tool_name=="string";(s||a||l)&&(n="toolResult");let r=[];typeof t.content=="string"?r=[{type:"text",text:t.content}]:Array.isArray(t.content)?r=t.content.map(u=>({type:u.type||"text",text:u.text,name:u.name,args:u.args||u.arguments})):typeof t.text=="string"&&(r=[{type:"text",text:t.text}]);const p=typeof t.timestamp=="number"?t.timestamp:Date.now(),d=typeof t.id=="string"?t.id:void 0;return{role:n,content:r,timestamp:p,id:d}}function Xs(e){const t=e.toLowerCase();return e==="user"||e==="User"?e:e==="assistant"?"assistant":e==="system"?"system":t==="toolresult"||t==="tool_result"||t==="tool"||t==="function"?"tool":e}function Wa(e){const t=e,n=typeof t.role=="string"?t.role.toLowerCase():"";return n==="toolresult"||n==="tool_result"}class gs extends za{constructor(t){if(super(t),this.it=g,t.type!==Ka.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(t){if(t===g||t==null)return this._t=void 0,this.it=t;if(t===xe)return t;if(typeof t!="string")throw Error(this.constructor.directiveName+"() called with a non-string value");if(t===this.it)return this._t;this.it=t;const n=[t];return n.raw=n,this._t={_$litType$:this.constructor.resultType,strings:n,values:[]}}}gs.directiveName="unsafeHTML",gs.resultType=1;const vs=Ha(gs);const{entries:Va,setPrototypeOf:co,isFrozen:Td,getPrototypeOf:Cd,getOwnPropertyDescriptor:Ed}=Object;let{freeze:Q,seal:te,create:ms}=Object,{apply:bs,construct:ys}=typeof Reflect<"u"&&Reflect;Q||(Q=function(t){return t});te||(te=function(t){return t});bs||(bs=function(t,n){for(var s=arguments.length,i=new Array(s>2?s-2:0),o=2;o1?n-1:0),i=1;i1?n-1:0),i=1;i2&&arguments[2]!==void 0?arguments[2]:Wt;co&&co(e,null);let s=t.length;for(;s--;){let i=t[s];if(typeof i=="string"){const o=n(i);o!==i&&(Td(t)||(t[s]=o),i=o)}e[i]=!0}return e}function Nd(e){for(let t=0;t/gm),Ud=te(/\$\{[\w\W]*/gm),Kd=te(/^data-[\-\w.\u00B7-\uFFFF]+$/),Hd=te(/^aria-[\-\w]+$/),Ga=te(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),zd=te(/^(?:\w+script|data):/i),jd=te(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),Ya=te(/^html$/i),qd=te(/^[a-z][.\w]*(-[.\w]+)+$/i);var vo=Object.freeze({__proto__:null,ARIA_ATTR:Hd,ATTR_WHITESPACE:jd,CUSTOM_ELEMENT:qd,DATA_ATTR:Kd,DOCTYPE_NAME:Ya,ERB_EXPR:Fd,IS_ALLOWED_URI:Ga,IS_SCRIPT_OR_DATA:zd,MUSTACHE_EXPR:Bd,TMPLIT_EXPR:Ud});const pt={element:1,text:3,progressingInstruction:7,comment:8,document:9},Wd=function(){return typeof window>"u"?null:window},Vd=function(t,n){if(typeof t!="object"||typeof t.createPolicy!="function")return null;let s=null;const i="data-tt-policy-suffix";n&&n.hasAttribute(i)&&(s=n.getAttribute(i));const o="dompurify"+(s?"#"+s:"");try{return t.createPolicy(o,{createHTML(a){return a},createScriptURL(a){return a}})}catch{return console.warn("TrustedTypes policy "+o+" could not be created."),null}},mo=function(){return{afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}};function Qa(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:Wd();const t=T=>Qa(T);if(t.version="3.3.1",t.removed=[],!e||!e.document||e.document.nodeType!==pt.document||!e.Element)return t.isSupported=!1,t;let{document:n}=e;const s=n,i=s.currentScript,{DocumentFragment:o,HTMLTemplateElement:a,Node:l,Element:r,NodeFilter:p,NamedNodeMap:d=e.NamedNodeMap||e.MozNamedAttrMap,HTMLFormElement:u,DOMParser:h,trustedTypes:v}=e,w=r.prototype,$=ut(w,"cloneNode"),x=ut(w,"remove"),C=ut(w,"nextSibling"),I=ut(w,"childNodes"),R=ut(w,"parentNode");if(typeof a=="function"){const T=n.createElement("template");T.content&&T.content.ownerDocument&&(n=T.content.ownerDocument)}let E,A="";const{implementation:B,createNodeIterator:ue,createDocumentFragment:bn,getElementsByTagName:yn}=n,{importNode:Sr}=s;let V=mo();t.isSupported=typeof Va=="function"&&typeof R=="function"&&B&&B.createHTMLDocument!==void 0;const{MUSTACHE_EXPR:wn,ERB_EXPR:$n,TMPLIT_EXPR:kn,DATA_ATTR:_r,ARIA_ATTR:Tr,IS_SCRIPT_OR_DATA:Cr,ATTR_WHITESPACE:ui,CUSTOM_ELEMENT:Er}=vo;let{IS_ALLOWED_URI:pi}=vo,K=null;const fi=L({},[...po,...Gn,...Yn,...Qn,...fo]);let z=null;const hi=L({},[...ho,...Jn,...go,...Ut]);let D=Object.seal(ms(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),st=null,xn=null;const Ke=Object.seal(ms(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let gi=!0,An=!0,vi=!1,mi=!0,He=!1,Et=!0,Te=!1,Sn=!1,_n=!1,ze=!1,It=!1,Lt=!1,bi=!0,yi=!1;const Ir="user-content-";let Tn=!0,it=!1,je={},ae=null;const Cn=L({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let wi=null;const $i=L({},["audio","video","img","source","image","track"]);let En=null;const ki=L({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Rt="http://www.w3.org/1998/Math/MathML",Mt="http://www.w3.org/2000/svg",pe="http://www.w3.org/1999/xhtml";let qe=pe,In=!1,Ln=null;const Lr=L({},[Rt,Mt,pe],Wn);let Pt=L({},["mi","mo","mn","ms","mtext"]),Nt=L({},["annotation-xml"]);const Rr=L({},["title","style","font","a","script"]);let ot=null;const Mr=["application/xhtml+xml","text/html"],Pr="text/html";let U=null,We=null;const Nr=n.createElement("form"),xi=function(f){return f instanceof RegExp||f instanceof Function},Rn=function(){let f=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(!(We&&We===f)){if((!f||typeof f!="object")&&(f={}),f=ce(f),ot=Mr.indexOf(f.PARSER_MEDIA_TYPE)===-1?Pr:f.PARSER_MEDIA_TYPE,U=ot==="application/xhtml+xml"?Wn:Wt,K=ne(f,"ALLOWED_TAGS")?L({},f.ALLOWED_TAGS,U):fi,z=ne(f,"ALLOWED_ATTR")?L({},f.ALLOWED_ATTR,U):hi,Ln=ne(f,"ALLOWED_NAMESPACES")?L({},f.ALLOWED_NAMESPACES,Wn):Lr,En=ne(f,"ADD_URI_SAFE_ATTR")?L(ce(ki),f.ADD_URI_SAFE_ATTR,U):ki,wi=ne(f,"ADD_DATA_URI_TAGS")?L(ce($i),f.ADD_DATA_URI_TAGS,U):$i,ae=ne(f,"FORBID_CONTENTS")?L({},f.FORBID_CONTENTS,U):Cn,st=ne(f,"FORBID_TAGS")?L({},f.FORBID_TAGS,U):ce({}),xn=ne(f,"FORBID_ATTR")?L({},f.FORBID_ATTR,U):ce({}),je=ne(f,"USE_PROFILES")?f.USE_PROFILES:!1,gi=f.ALLOW_ARIA_ATTR!==!1,An=f.ALLOW_DATA_ATTR!==!1,vi=f.ALLOW_UNKNOWN_PROTOCOLS||!1,mi=f.ALLOW_SELF_CLOSE_IN_ATTR!==!1,He=f.SAFE_FOR_TEMPLATES||!1,Et=f.SAFE_FOR_XML!==!1,Te=f.WHOLE_DOCUMENT||!1,ze=f.RETURN_DOM||!1,It=f.RETURN_DOM_FRAGMENT||!1,Lt=f.RETURN_TRUSTED_TYPE||!1,_n=f.FORCE_BODY||!1,bi=f.SANITIZE_DOM!==!1,yi=f.SANITIZE_NAMED_PROPS||!1,Tn=f.KEEP_CONTENT!==!1,it=f.IN_PLACE||!1,pi=f.ALLOWED_URI_REGEXP||Ga,qe=f.NAMESPACE||pe,Pt=f.MATHML_TEXT_INTEGRATION_POINTS||Pt,Nt=f.HTML_INTEGRATION_POINTS||Nt,D=f.CUSTOM_ELEMENT_HANDLING||{},f.CUSTOM_ELEMENT_HANDLING&&xi(f.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(D.tagNameCheck=f.CUSTOM_ELEMENT_HANDLING.tagNameCheck),f.CUSTOM_ELEMENT_HANDLING&&xi(f.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(D.attributeNameCheck=f.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),f.CUSTOM_ELEMENT_HANDLING&&typeof f.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(D.allowCustomizedBuiltInElements=f.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),He&&(An=!1),It&&(ze=!0),je&&(K=L({},fo),z=[],je.html===!0&&(L(K,po),L(z,ho)),je.svg===!0&&(L(K,Gn),L(z,Jn),L(z,Ut)),je.svgFilters===!0&&(L(K,Yn),L(z,Jn),L(z,Ut)),je.mathMl===!0&&(L(K,Qn),L(z,go),L(z,Ut))),f.ADD_TAGS&&(typeof f.ADD_TAGS=="function"?Ke.tagCheck=f.ADD_TAGS:(K===fi&&(K=ce(K)),L(K,f.ADD_TAGS,U))),f.ADD_ATTR&&(typeof f.ADD_ATTR=="function"?Ke.attributeCheck=f.ADD_ATTR:(z===hi&&(z=ce(z)),L(z,f.ADD_ATTR,U))),f.ADD_URI_SAFE_ATTR&&L(En,f.ADD_URI_SAFE_ATTR,U),f.FORBID_CONTENTS&&(ae===Cn&&(ae=ce(ae)),L(ae,f.FORBID_CONTENTS,U)),f.ADD_FORBID_CONTENTS&&(ae===Cn&&(ae=ce(ae)),L(ae,f.ADD_FORBID_CONTENTS,U)),Tn&&(K["#text"]=!0),Te&&L(K,["html","head","body"]),K.table&&(L(K,["tbody"]),delete st.tbody),f.TRUSTED_TYPES_POLICY){if(typeof f.TRUSTED_TYPES_POLICY.createHTML!="function")throw dt('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if(typeof f.TRUSTED_TYPES_POLICY.createScriptURL!="function")throw dt('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');E=f.TRUSTED_TYPES_POLICY,A=E.createHTML("")}else E===void 0&&(E=Vd(v,i)),E!==null&&typeof A=="string"&&(A=E.createHTML(""));Q&&Q(f),We=f}},Ai=L({},[...Gn,...Yn,...Od]),Si=L({},[...Qn,...Dd]),Or=function(f){let k=R(f);(!k||!k.tagName)&&(k={namespaceURI:qe,tagName:"template"});const _=Wt(f.tagName),N=Wt(k.tagName);return Ln[f.namespaceURI]?f.namespaceURI===Mt?k.namespaceURI===pe?_==="svg":k.namespaceURI===Rt?_==="svg"&&(N==="annotation-xml"||Pt[N]):!!Ai[_]:f.namespaceURI===Rt?k.namespaceURI===pe?_==="math":k.namespaceURI===Mt?_==="math"&&Nt[N]:!!Si[_]:f.namespaceURI===pe?k.namespaceURI===Mt&&!Nt[N]||k.namespaceURI===Rt&&!Pt[N]?!1:!Si[_]&&(Rr[_]||!Ai[_]):!!(ot==="application/xhtml+xml"&&Ln[f.namespaceURI]):!1},re=function(f){lt(t.removed,{element:f});try{R(f).removeChild(f)}catch{x(f)}},Ce=function(f,k){try{lt(t.removed,{attribute:k.getAttributeNode(f),from:k})}catch{lt(t.removed,{attribute:null,from:k})}if(k.removeAttribute(f),f==="is")if(ze||It)try{re(k)}catch{}else try{k.setAttribute(f,"")}catch{}},_i=function(f){let k=null,_=null;if(_n)f=""+f;else{const F=Vn(f,/^[\r\n\t ]+/);_=F&&F[0]}ot==="application/xhtml+xml"&&qe===pe&&(f=''+f+"");const N=E?E.createHTML(f):f;if(qe===pe)try{k=new h().parseFromString(N,ot)}catch{}if(!k||!k.documentElement){k=B.createDocument(qe,"template",null);try{k.documentElement.innerHTML=In?A:N}catch{}}const q=k.body||k.documentElement;return f&&_&&q.insertBefore(n.createTextNode(_),q.childNodes[0]||null),qe===pe?yn.call(k,Te?"html":"body")[0]:Te?k.documentElement:q},Ti=function(f){return ue.call(f.ownerDocument||f,f,p.SHOW_ELEMENT|p.SHOW_COMMENT|p.SHOW_TEXT|p.SHOW_PROCESSING_INSTRUCTION|p.SHOW_CDATA_SECTION,null)},Mn=function(f){return f instanceof u&&(typeof f.nodeName!="string"||typeof f.textContent!="string"||typeof f.removeChild!="function"||!(f.attributes instanceof d)||typeof f.removeAttribute!="function"||typeof f.setAttribute!="function"||typeof f.namespaceURI!="string"||typeof f.insertBefore!="function"||typeof f.hasChildNodes!="function")},Ci=function(f){return typeof l=="function"&&f instanceof l};function fe(T,f,k){Ft(T,_=>{_.call(t,f,k,We)})}const Ei=function(f){let k=null;if(fe(V.beforeSanitizeElements,f,null),Mn(f))return re(f),!0;const _=U(f.nodeName);if(fe(V.uponSanitizeElement,f,{tagName:_,allowedTags:K}),Et&&f.hasChildNodes()&&!Ci(f.firstElementChild)&&G(/<[/\w!]/g,f.innerHTML)&&G(/<[/\w!]/g,f.textContent)||f.nodeType===pt.progressingInstruction||Et&&f.nodeType===pt.comment&&G(/<[/\w]/g,f.data))return re(f),!0;if(!(Ke.tagCheck instanceof Function&&Ke.tagCheck(_))&&(!K[_]||st[_])){if(!st[_]&&Li(_)&&(D.tagNameCheck instanceof RegExp&&G(D.tagNameCheck,_)||D.tagNameCheck instanceof Function&&D.tagNameCheck(_)))return!1;if(Tn&&!ae[_]){const N=R(f)||f.parentNode,q=I(f)||f.childNodes;if(q&&N){const F=q.length;for(let Z=F-1;Z>=0;--Z){const he=$(q[Z],!0);he.__removalCount=(f.__removalCount||0)+1,N.insertBefore(he,C(f))}}}return re(f),!0}return f instanceof r&&!Or(f)||(_==="noscript"||_==="noembed"||_==="noframes")&&G(/<\/no(script|embed|frames)/i,f.innerHTML)?(re(f),!0):(He&&f.nodeType===pt.text&&(k=f.textContent,Ft([wn,$n,kn],N=>{k=ct(k,N," ")}),f.textContent!==k&&(lt(t.removed,{element:f.cloneNode()}),f.textContent=k)),fe(V.afterSanitizeElements,f,null),!1)},Ii=function(f,k,_){if(bi&&(k==="id"||k==="name")&&(_ in n||_ in Nr))return!1;if(!(An&&!xn[k]&&G(_r,k))){if(!(gi&&G(Tr,k))){if(!(Ke.attributeCheck instanceof Function&&Ke.attributeCheck(k,f))){if(!z[k]||xn[k]){if(!(Li(f)&&(D.tagNameCheck instanceof RegExp&&G(D.tagNameCheck,f)||D.tagNameCheck instanceof Function&&D.tagNameCheck(f))&&(D.attributeNameCheck instanceof RegExp&&G(D.attributeNameCheck,k)||D.attributeNameCheck instanceof Function&&D.attributeNameCheck(k,f))||k==="is"&&D.allowCustomizedBuiltInElements&&(D.tagNameCheck instanceof RegExp&&G(D.tagNameCheck,_)||D.tagNameCheck instanceof Function&&D.tagNameCheck(_))))return!1}else if(!En[k]){if(!G(pi,ct(_,ui,""))){if(!((k==="src"||k==="xlink:href"||k==="href")&&f!=="script"&&Rd(_,"data:")===0&&wi[f])){if(!(vi&&!G(Cr,ct(_,ui,"")))){if(_)return!1}}}}}}}return!0},Li=function(f){return f!=="annotation-xml"&&Vn(f,Er)},Ri=function(f){fe(V.beforeSanitizeAttributes,f,null);const{attributes:k}=f;if(!k||Mn(f))return;const _={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:z,forceKeepAttr:void 0};let N=k.length;for(;N--;){const q=k[N],{name:F,namespaceURI:Z,value:he}=q,Ve=U(F),Pn=he;let j=F==="value"?Pn:Md(Pn);if(_.attrName=Ve,_.attrValue=j,_.keepAttr=!0,_.forceKeepAttr=void 0,fe(V.uponSanitizeAttribute,f,_),j=_.attrValue,yi&&(Ve==="id"||Ve==="name")&&(Ce(F,f),j=Ir+j),Et&&G(/((--!?|])>)|<\/(style|title|textarea)/i,j)){Ce(F,f);continue}if(Ve==="attributename"&&Vn(j,"href")){Ce(F,f);continue}if(_.forceKeepAttr)continue;if(!_.keepAttr){Ce(F,f);continue}if(!mi&&G(/\/>/i,j)){Ce(F,f);continue}He&&Ft([wn,$n,kn],Pi=>{j=ct(j,Pi," ")});const Mi=U(f.nodeName);if(!Ii(Mi,Ve,j)){Ce(F,f);continue}if(E&&typeof v=="object"&&typeof v.getAttributeType=="function"&&!Z)switch(v.getAttributeType(Mi,Ve)){case"TrustedHTML":{j=E.createHTML(j);break}case"TrustedScriptURL":{j=E.createScriptURL(j);break}}if(j!==Pn)try{Z?f.setAttributeNS(Z,F,j):f.setAttribute(F,j),Mn(f)?re(f):uo(t.removed)}catch{Ce(F,f)}}fe(V.afterSanitizeAttributes,f,null)},Dr=function T(f){let k=null;const _=Ti(f);for(fe(V.beforeSanitizeShadowDOM,f,null);k=_.nextNode();)fe(V.uponSanitizeShadowNode,k,null),Ei(k),Ri(k),k.content instanceof o&&T(k.content);fe(V.afterSanitizeShadowDOM,f,null)};return t.sanitize=function(T){let f=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},k=null,_=null,N=null,q=null;if(In=!T,In&&(T=""),typeof T!="string"&&!Ci(T))if(typeof T.toString=="function"){if(T=T.toString(),typeof T!="string")throw dt("dirty is not a string, aborting")}else throw dt("toString is not a function");if(!t.isSupported)return T;if(Sn||Rn(f),t.removed=[],typeof T=="string"&&(it=!1),it){if(T.nodeName){const he=U(T.nodeName);if(!K[he]||st[he])throw dt("root node is forbidden and cannot be sanitized in-place")}}else if(T instanceof l)k=_i(""),_=k.ownerDocument.importNode(T,!0),_.nodeType===pt.element&&_.nodeName==="BODY"||_.nodeName==="HTML"?k=_:k.appendChild(_);else{if(!ze&&!He&&!Te&&T.indexOf("<")===-1)return E&&Lt?E.createHTML(T):T;if(k=_i(T),!k)return ze?null:Lt?A:""}k&&_n&&re(k.firstChild);const F=Ti(it?T:k);for(;N=F.nextNode();)Ei(N),Ri(N),N.content instanceof o&&Dr(N.content);if(it)return T;if(ze){if(It)for(q=bn.call(k.ownerDocument);k.firstChild;)q.appendChild(k.firstChild);else q=k;return(z.shadowroot||z.shadowrootmode)&&(q=Sr.call(s,q,!0)),q}let Z=Te?k.outerHTML:k.innerHTML;return Te&&K["!doctype"]&&k.ownerDocument&&k.ownerDocument.doctype&&k.ownerDocument.doctype.name&&G(Ya,k.ownerDocument.doctype.name)&&(Z=" -`+Z),He&&Ft([wn,$n,kn],he=>{Z=ct(Z,he," ")}),E&&Lt?E.createHTML(Z):Z},t.setConfig=function(){let T=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};Rn(T),Sn=!0},t.clearConfig=function(){We=null,Sn=!1},t.isValidAttribute=function(T,f,k){We||Rn({});const _=U(T),N=U(f);return Ii(_,N,k)},t.addHook=function(T,f){typeof f=="function"&<(V[T],f)},t.removeHook=function(T,f){if(f!==void 0){const k=Id(V[T],f);return k===-1?void 0:Ld(V[T],k,1)[0]}return uo(V[T])},t.removeHooks=function(T){V[T]=[]},t.removeAllHooks=function(){V=mo()},t}var ws=Qa();function ei(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var Ue=ei();function Ja(e){Ue=e}var bt={exec:()=>null};function M(e,t=""){let n=typeof e=="string"?e:e.source,s={replace:(i,o)=>{let a=typeof o=="string"?o:o.source;return a=a.replace(Y.caret,"$1"),n=n.replace(i,a),s},getRegex:()=>new RegExp(n,t)};return s}var Gd=(()=>{try{return!!new RegExp("(?<=1)(?/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] +\S/,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>new RegExp(`^( {0,3}${e})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}#`),htmlBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}<(?:[a-z].*>|!--)`,"i")},Yd=/^(?:[ \t]*(?:\n|$))+/,Qd=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,Jd=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,Ct=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,Zd=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,ti=/(?:[*+-]|\d{1,9}[.)])/,Za=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,Xa=M(Za).replace(/bull/g,ti).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),Xd=M(Za).replace(/bull/g,ti).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),ni=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,eu=/^[^\n]+/,si=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,tu=M(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",si).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),nu=M(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,ti).getRegex(),hn="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",ii=/|$))/,su=M("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",ii).replace("tag",hn).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),er=M(ni).replace("hr",Ct).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",hn).getRegex(),iu=M(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",er).getRegex(),oi={blockquote:iu,code:Qd,def:tu,fences:Jd,heading:Zd,hr:Ct,html:su,lheading:Xa,list:nu,newline:Yd,paragraph:er,table:bt,text:eu},bo=M("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",Ct).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",hn).getRegex(),ou={...oi,lheading:Xd,table:bo,paragraph:M(ni).replace("hr",Ct).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",bo).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",hn).getRegex()},au={...oi,html:M(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",ii).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:bt,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:M(ni).replace("hr",Ct).replace("heading",` *#{1,6} *[^ -]`).replace("lheading",Xa).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},ru=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,lu=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,tr=/^( {2,}|\\)\n(?!\s*$)/,cu=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",Gd?"(?`+)[^`]+\k(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),ir=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,hu=M(ir,"u").replace(/punct/g,gn).getRegex(),gu=M(ir,"u").replace(/punct/g,sr).getRegex(),or="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",vu=M(or,"gu").replace(/notPunctSpace/g,nr).replace(/punctSpace/g,ai).replace(/punct/g,gn).getRegex(),mu=M(or,"gu").replace(/notPunctSpace/g,pu).replace(/punctSpace/g,uu).replace(/punct/g,sr).getRegex(),bu=M("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,nr).replace(/punctSpace/g,ai).replace(/punct/g,gn).getRegex(),yu=M(/\\(punct)/,"gu").replace(/punct/g,gn).getRegex(),wu=M(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),$u=M(ii).replace("(?:-->|$)","-->").getRegex(),ku=M("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",$u).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),Zt=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+[^`]*?`+(?!`)|[^\[\]\\`])*?/,xu=M(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]*(?:\n[ \t]*)?)(title))?\s*\)/).replace("label",Zt).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),ar=M(/^!?\[(label)\]\[(ref)\]/).replace("label",Zt).replace("ref",si).getRegex(),rr=M(/^!?\[(ref)\](?:\[\])?/).replace("ref",si).getRegex(),Au=M("reflink|nolink(?!\\()","g").replace("reflink",ar).replace("nolink",rr).getRegex(),yo=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,ri={_backpedal:bt,anyPunctuation:yu,autolink:wu,blockSkip:fu,br:tr,code:lu,del:bt,emStrongLDelim:hu,emStrongRDelimAst:vu,emStrongRDelimUnd:bu,escape:ru,link:xu,nolink:rr,punctuation:du,reflink:ar,reflinkSearch:Au,tag:ku,text:cu,url:bt},Su={...ri,link:M(/^!?\[(label)\]\((.*?)\)/).replace("label",Zt).getRegex(),reflink:M(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Zt).getRegex()},$s={...ri,emStrongRDelimAst:mu,emStrongLDelim:gu,url:M(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",yo).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:M(/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},wo=e=>Tu[e];function ve(e,t){if(t){if(Y.escapeTest.test(e))return e.replace(Y.escapeReplace,wo)}else if(Y.escapeTestNoEncode.test(e))return e.replace(Y.escapeReplaceNoEncode,wo);return e}function $o(e){try{e=encodeURI(e).replace(Y.percentDecode,"%")}catch{return null}return e}function ko(e,t){let n=e.replace(Y.findPipe,(o,a,l)=>{let r=!1,p=a;for(;--p>=0&&l[p]==="\\";)r=!r;return r?"|":" |"}),s=n.split(Y.splitPipe),i=0;if(s[0].trim()||s.shift(),s.length>0&&!s.at(-1)?.trim()&&s.pop(),t)if(s.length>t)s.splice(t);else for(;s.length0?-2:-1}function xo(e,t,n,s,i){let o=t.href,a=t.title||null,l=e[1].replace(i.other.outputLinkReplace,"$1");s.state.inLink=!0;let r={type:e[0].charAt(0)==="!"?"image":"link",raw:n,href:o,title:a,text:l,tokens:s.inlineTokens(l)};return s.state.inLink=!1,r}function Eu(e,t,n){let s=e.match(n.other.indentCodeCompensation);if(s===null)return t;let i=s[1];return t.split(` -`).map(o=>{let a=o.match(n.other.beginningSpace);if(a===null)return o;let[l]=a;return l.length>=i.length?o.slice(i.length):o}).join(` -`)}var Xt=class{options;rules;lexer;constructor(e){this.options=e||Ue}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?n:ht(n,` -`)}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],s=Eu(n,t[3]||"",this.rules);return{type:"code",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:s}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let s=ht(n,"#");(this.options.pedantic||!s||this.rules.other.endingSpaceChar.test(s))&&(n=s.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:ht(t[0],` -`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=ht(t[0],` -`).split(` -`),s="",i="",o=[];for(;n.length>0;){let a=!1,l=[],r;for(r=0;r1,i={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");let o=this.rules.other.listItemRegex(n),a=!1;for(;e;){let r=!1,p="",d="";if(!(t=o.exec(e))||this.rules.block.hr.test(e))break;p=t[0],e=e.substring(p.length);let u=t[2].split(` -`,1)[0].replace(this.rules.other.listReplaceTabs,$=>" ".repeat(3*$.length)),h=e.split(` -`,1)[0],v=!u.trim(),w=0;if(this.options.pedantic?(w=2,d=u.trimStart()):v?w=t[1].length+1:(w=t[2].search(this.rules.other.nonSpaceChar),w=w>4?1:w,d=u.slice(w),w+=t[1].length),v&&this.rules.other.blankLine.test(h)&&(p+=h+` -`,e=e.substring(h.length+1),r=!0),!r){let $=this.rules.other.nextBulletRegex(w),x=this.rules.other.hrRegex(w),C=this.rules.other.fencesBeginRegex(w),I=this.rules.other.headingBeginRegex(w),R=this.rules.other.htmlBeginRegex(w);for(;e;){let E=e.split(` -`,1)[0],A;if(h=E,this.options.pedantic?(h=h.replace(this.rules.other.listReplaceNesting," "),A=h):A=h.replace(this.rules.other.tabCharGlobal," "),C.test(h)||I.test(h)||R.test(h)||$.test(h)||x.test(h))break;if(A.search(this.rules.other.nonSpaceChar)>=w||!h.trim())d+=` -`+A.slice(w);else{if(v||u.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||C.test(u)||I.test(u)||x.test(u))break;d+=` -`+h}!v&&!h.trim()&&(v=!0),p+=E+` -`,e=e.substring(E.length+1),u=A.slice(w)}}i.loose||(a?i.loose=!0:this.rules.other.doubleBlankLine.test(p)&&(a=!0)),i.items.push({type:"list_item",raw:p,task:!!this.options.gfm&&this.rules.other.listIsTask.test(d),loose:!1,text:d,tokens:[]}),i.raw+=p}let l=i.items.at(-1);if(l)l.raw=l.raw.trimEnd(),l.text=l.text.trimEnd();else return;i.raw=i.raw.trimEnd();for(let r of i.items){if(this.lexer.state.top=!1,r.tokens=this.lexer.blockTokens(r.text,[]),r.task){if(r.text=r.text.replace(this.rules.other.listReplaceTask,""),r.tokens[0]?.type==="text"||r.tokens[0]?.type==="paragraph"){r.tokens[0].raw=r.tokens[0].raw.replace(this.rules.other.listReplaceTask,""),r.tokens[0].text=r.tokens[0].text.replace(this.rules.other.listReplaceTask,"");for(let d=this.lexer.inlineQueue.length-1;d>=0;d--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[d].src)){this.lexer.inlineQueue[d].src=this.lexer.inlineQueue[d].src.replace(this.rules.other.listReplaceTask,"");break}}let p=this.rules.other.listTaskCheckbox.exec(r.raw);if(p){let d={type:"checkbox",raw:p[0]+" ",checked:p[0]!=="[ ]"};r.checked=d.checked,i.loose?r.tokens[0]&&["paragraph","text"].includes(r.tokens[0].type)&&"tokens"in r.tokens[0]&&r.tokens[0].tokens?(r.tokens[0].raw=d.raw+r.tokens[0].raw,r.tokens[0].text=d.raw+r.tokens[0].text,r.tokens[0].tokens.unshift(d)):r.tokens.unshift({type:"paragraph",raw:d.raw,text:d.raw,tokens:[d]}):r.tokens.unshift(d)}}if(!i.loose){let p=r.tokens.filter(u=>u.type==="space"),d=p.length>0&&p.some(u=>this.rules.other.anyLine.test(u.raw));i.loose=d}}if(i.loose)for(let r of i.items){r.loose=!0;for(let p of r.tokens)p.type==="text"&&(p.type="paragraph")}return i}}html(e){let t=this.rules.block.html.exec(e);if(t)return{type:"html",block:!0,raw:t[0],pre:t[1]==="pre"||t[1]==="script"||t[1]==="style",text:t[0]}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),s=t[2]?t[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",i=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:n,raw:t[0],href:s,title:i}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=ko(t[1]),s=t[2].replace(this.rules.other.tableAlignChars,"").split("|"),i=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,"").split(` -`):[],o={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(let a of s)this.rules.other.tableAlignRight.test(a)?o.align.push("right"):this.rules.other.tableAlignCenter.test(a)?o.align.push("center"):this.rules.other.tableAlignLeft.test(a)?o.align.push("left"):o.align.push(null);for(let a=0;a({text:l,tokens:this.lexer.inline(l),header:!1,align:o.align[r]})));return o}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:t[2].charAt(0)==="="?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===` -`?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let o=ht(n.slice(0,-1),"\\");if((n.length-o.length)%2===0)return}else{let o=Cu(t[2],"()");if(o===-2)return;if(o>-1){let a=(t[0].indexOf("!")===0?5:4)+t[1].length+o;t[2]=t[2].substring(0,o),t[0]=t[0].substring(0,a).trim(),t[3]=""}}let s=t[2],i="";if(this.options.pedantic){let o=this.rules.other.pedanticHrefTitle.exec(s);o&&(s=o[1],i=o[3])}else i=t[3]?t[3].slice(1,-1):"";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?s=s.slice(1):s=s.slice(1,-1)),xo(t,{href:s&&s.replace(this.rules.inline.anyPunctuation,"$1"),title:i&&i.replace(this.rules.inline.anyPunctuation,"$1")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let s=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),i=t[s.toLowerCase()];if(!i){let o=n[0].charAt(0);return{type:"text",raw:o,text:o}}return xo(n,i,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!(!s||s[3]&&n.match(this.rules.other.unicodeAlphaNumeric))&&(!(s[1]||s[2])||!n||this.rules.inline.punctuation.exec(n))){let i=[...s[0]].length-1,o,a,l=i,r=0,p=s[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(p.lastIndex=0,t=t.slice(-1*e.length+i);(s=p.exec(t))!=null;){if(o=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!o)continue;if(a=[...o].length,s[3]||s[4]){l+=a;continue}else if((s[5]||s[6])&&i%3&&!((i+a)%3)){r+=a;continue}if(l-=a,l>0)continue;a=Math.min(a,a+l+r);let d=[...s[0]][0].length,u=e.slice(0,i+s.index+d+a);if(Math.min(i,a)%2){let v=u.slice(1,-1);return{type:"em",raw:u,text:v,tokens:this.lexer.inlineTokens(v)}}let h=u.slice(2,-2);return{type:"strong",raw:u,text:h,tokens:this.lexer.inlineTokens(h)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal," "),s=this.rules.other.nonSpaceChar.test(n),i=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return s&&i&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){let t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,s;return t[2]==="@"?(n=t[1],s="mailto:"+n):(n=t[1],s=n),{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,s;if(t[2]==="@")n=t[0],s="mailto:"+n;else{let i;do i=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??"";while(i!==t[0]);n=t[0],t[1]==="www."?s="http://"+t[0]:s=t[0]}return{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:n}}}},se=class ks{tokens;options;state;inlineQueue;tokenizer;constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||Ue,this.options.tokenizer=this.options.tokenizer||new Xt,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let n={other:Y,block:Kt.normal,inline:ft.normal};this.options.pedantic?(n.block=Kt.pedantic,n.inline=ft.pedantic):this.options.gfm&&(n.block=Kt.gfm,this.options.breaks?n.inline=ft.breaks:n.inline=ft.gfm),this.tokenizer.rules=n}static get rules(){return{block:Kt,inline:ft}}static lex(t,n){return new ks(n).lex(t)}static lexInline(t,n){return new ks(n).inlineTokens(t)}lex(t){t=t.replace(Y.carriageReturn,` -`),this.blockTokens(t,this.tokens);for(let n=0;n(i=a.call({lexer:this},t,n))?(t=t.substring(i.raw.length),n.push(i),!0):!1))continue;if(i=this.tokenizer.space(t)){t=t.substring(i.raw.length);let a=n.at(-1);i.raw.length===1&&a!==void 0?a.raw+=` -`:n.push(i);continue}if(i=this.tokenizer.code(t)){t=t.substring(i.raw.length);let a=n.at(-1);a?.type==="paragraph"||a?.type==="text"?(a.raw+=(a.raw.endsWith(` -`)?"":` -`)+i.raw,a.text+=` -`+i.text,this.inlineQueue.at(-1).src=a.text):n.push(i);continue}if(i=this.tokenizer.fences(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.heading(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.hr(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.blockquote(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.list(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.html(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.def(t)){t=t.substring(i.raw.length);let a=n.at(-1);a?.type==="paragraph"||a?.type==="text"?(a.raw+=(a.raw.endsWith(` -`)?"":` -`)+i.raw,a.text+=` -`+i.raw,this.inlineQueue.at(-1).src=a.text):this.tokens.links[i.tag]||(this.tokens.links[i.tag]={href:i.href,title:i.title},n.push(i));continue}if(i=this.tokenizer.table(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.lheading(t)){t=t.substring(i.raw.length),n.push(i);continue}let o=t;if(this.options.extensions?.startBlock){let a=1/0,l=t.slice(1),r;this.options.extensions.startBlock.forEach(p=>{r=p.call({lexer:this},l),typeof r=="number"&&r>=0&&(a=Math.min(a,r))}),a<1/0&&a>=0&&(o=t.substring(0,a+1))}if(this.state.top&&(i=this.tokenizer.paragraph(o))){let a=n.at(-1);s&&a?.type==="paragraph"?(a.raw+=(a.raw.endsWith(` -`)?"":` -`)+i.raw,a.text+=` -`+i.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=a.text):n.push(i),s=o.length!==t.length,t=t.substring(i.raw.length);continue}if(i=this.tokenizer.text(t)){t=t.substring(i.raw.length);let a=n.at(-1);a?.type==="text"?(a.raw+=(a.raw.endsWith(` -`)?"":` -`)+i.raw,a.text+=` -`+i.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=a.text):n.push(i);continue}if(t){let a="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(a);break}else throw new Error(a)}}return this.state.top=!0,n}inline(t,n=[]){return this.inlineQueue.push({src:t,tokens:n}),n}inlineTokens(t,n=[]){let s=t,i=null;if(this.tokens.links){let r=Object.keys(this.tokens.links);if(r.length>0)for(;(i=this.tokenizer.rules.inline.reflinkSearch.exec(s))!=null;)r.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(s=s.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(i=this.tokenizer.rules.inline.anyPunctuation.exec(s))!=null;)s=s.slice(0,i.index)+"++"+s.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let o;for(;(i=this.tokenizer.rules.inline.blockSkip.exec(s))!=null;)o=i[2]?i[2].length:0,s=s.slice(0,i.index+o)+"["+"a".repeat(i[0].length-o-2)+"]"+s.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);s=this.options.hooks?.emStrongMask?.call({lexer:this},s)??s;let a=!1,l="";for(;t;){a||(l=""),a=!1;let r;if(this.options.extensions?.inline?.some(d=>(r=d.call({lexer:this},t,n))?(t=t.substring(r.raw.length),n.push(r),!0):!1))continue;if(r=this.tokenizer.escape(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.tag(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.link(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.reflink(t,this.tokens.links)){t=t.substring(r.raw.length);let d=n.at(-1);r.type==="text"&&d?.type==="text"?(d.raw+=r.raw,d.text+=r.text):n.push(r);continue}if(r=this.tokenizer.emStrong(t,s,l)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.codespan(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.br(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.del(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.autolink(t)){t=t.substring(r.raw.length),n.push(r);continue}if(!this.state.inLink&&(r=this.tokenizer.url(t))){t=t.substring(r.raw.length),n.push(r);continue}let p=t;if(this.options.extensions?.startInline){let d=1/0,u=t.slice(1),h;this.options.extensions.startInline.forEach(v=>{h=v.call({lexer:this},u),typeof h=="number"&&h>=0&&(d=Math.min(d,h))}),d<1/0&&d>=0&&(p=t.substring(0,d+1))}if(r=this.tokenizer.inlineText(p)){t=t.substring(r.raw.length),r.raw.slice(-1)!=="_"&&(l=r.raw.slice(-1)),a=!0;let d=n.at(-1);d?.type==="text"?(d.raw+=r.raw,d.text+=r.text):n.push(r);continue}if(t){let d="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(d);break}else throw new Error(d)}}return n}},en=class{options;parser;constructor(e){this.options=e||Ue}space(e){return""}code({text:e,lang:t,escaped:n}){let s=(t||"").match(Y.notSpaceStart)?.[0],i=e.replace(Y.endingNewline,"")+` -`;return s?'
    '+(n?i:ve(i,!0))+`
    -`:"
    "+(n?i:ve(i,!0))+`
    -`}blockquote({tokens:e}){return`
    -${this.parser.parse(e)}
    -`}html({text:e}){return e}def(e){return""}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)} -`}hr(e){return`
    -`}list(e){let t=e.ordered,n=e.start,s="";for(let a=0;a -`+s+" -`}listitem(e){return`
  • ${this.parser.parse(e.tokens)}
  • -`}checkbox({checked:e}){return" '}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    -`}table(e){let t="",n="";for(let i=0;i${s}`),` - -`+t+` -`+s+`
    -`}tablerow({text:e}){return` -${e} -`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+` -`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${ve(e,!0)}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let s=this.parser.parseInline(n),i=$o(e);if(i===null)return s;e=i;let o='
    ",o}image({href:e,title:t,text:n,tokens:s}){s&&(n=this.parser.parseInline(s,this.parser.textRenderer));let i=$o(e);if(i===null)return ve(n);e=i;let o=`${n}{let a=i[o].flat(1/0);n=n.concat(this.walkTokens(a,t))}):i.tokens&&(n=n.concat(this.walkTokens(i.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let s={...n};if(s.async=this.defaults.async||s.async||!1,n.extensions&&(n.extensions.forEach(i=>{if(!i.name)throw new Error("extension name required");if("renderer"in i){let o=t.renderers[i.name];o?t.renderers[i.name]=function(...a){let l=i.renderer.apply(this,a);return l===!1&&(l=o.apply(this,a)),l}:t.renderers[i.name]=i.renderer}if("tokenizer"in i){if(!i.level||i.level!=="block"&&i.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let o=t[i.level];o?o.unshift(i.tokenizer):t[i.level]=[i.tokenizer],i.start&&(i.level==="block"?t.startBlock?t.startBlock.push(i.start):t.startBlock=[i.start]:i.level==="inline"&&(t.startInline?t.startInline.push(i.start):t.startInline=[i.start]))}"childTokens"in i&&i.childTokens&&(t.childTokens[i.name]=i.childTokens)}),s.extensions=t),n.renderer){let i=this.defaults.renderer||new en(this.defaults);for(let o in n.renderer){if(!(o in i))throw new Error(`renderer '${o}' does not exist`);if(["options","parser"].includes(o))continue;let a=o,l=n.renderer[a],r=i[a];i[a]=(...p)=>{let d=l.apply(i,p);return d===!1&&(d=r.apply(i,p)),d||""}}s.renderer=i}if(n.tokenizer){let i=this.defaults.tokenizer||new Xt(this.defaults);for(let o in n.tokenizer){if(!(o in i))throw new Error(`tokenizer '${o}' does not exist`);if(["options","rules","lexer"].includes(o))continue;let a=o,l=n.tokenizer[a],r=i[a];i[a]=(...p)=>{let d=l.apply(i,p);return d===!1&&(d=r.apply(i,p)),d}}s.tokenizer=i}if(n.hooks){let i=this.defaults.hooks||new gt;for(let o in n.hooks){if(!(o in i))throw new Error(`hook '${o}' does not exist`);if(["options","block"].includes(o))continue;let a=o,l=n.hooks[a],r=i[a];gt.passThroughHooks.has(o)?i[a]=p=>{if(this.defaults.async&>.passThroughHooksRespectAsync.has(o))return(async()=>{let u=await l.call(i,p);return r.call(i,u)})();let d=l.call(i,p);return r.call(i,d)}:i[a]=(...p)=>{if(this.defaults.async)return(async()=>{let u=await l.apply(i,p);return u===!1&&(u=await r.apply(i,p)),u})();let d=l.apply(i,p);return d===!1&&(d=r.apply(i,p)),d}}s.hooks=i}if(n.walkTokens){let i=this.defaults.walkTokens,o=n.walkTokens;s.walkTokens=function(a){let l=[];return l.push(o.call(this,a)),i&&(l=l.concat(i.call(this,a))),l}}this.defaults={...this.defaults,...s}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return se.lex(e,t??this.defaults)}parser(e,t){return ie.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{let s={...n},i={...this.defaults,...s},o=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&s.async===!1)return o(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof t>"u"||t===null)return o(new Error("marked(): input parameter is undefined or null"));if(typeof t!="string")return o(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));if(i.hooks&&(i.hooks.options=i,i.hooks.block=e),i.async)return(async()=>{let a=i.hooks?await i.hooks.preprocess(t):t,l=await(i.hooks?await i.hooks.provideLexer():e?se.lex:se.lexInline)(a,i),r=i.hooks?await i.hooks.processAllTokens(l):l;i.walkTokens&&await Promise.all(this.walkTokens(r,i.walkTokens));let p=await(i.hooks?await i.hooks.provideParser():e?ie.parse:ie.parseInline)(r,i);return i.hooks?await i.hooks.postprocess(p):p})().catch(o);try{i.hooks&&(t=i.hooks.preprocess(t));let a=(i.hooks?i.hooks.provideLexer():e?se.lex:se.lexInline)(t,i);i.hooks&&(a=i.hooks.processAllTokens(a)),i.walkTokens&&this.walkTokens(a,i.walkTokens);let l=(i.hooks?i.hooks.provideParser():e?ie.parse:ie.parseInline)(a,i);return i.hooks&&(l=i.hooks.postprocess(l)),l}catch(a){return o(a)}}}onError(e,t){return n=>{if(n.message+=` -Please report this to https://github.com/markedjs/marked.`,e){let s="

    An error occurred:

    "+ve(n.message+"",!0)+"
    ";return t?Promise.resolve(s):s}if(t)return Promise.reject(n);throw n}}},Fe=new Iu;function P(e,t){return Fe.parse(e,t)}P.options=P.setOptions=function(e){return Fe.setOptions(e),P.defaults=Fe.defaults,Ja(P.defaults),P};P.getDefaults=ei;P.defaults=Ue;P.use=function(...e){return Fe.use(...e),P.defaults=Fe.defaults,Ja(P.defaults),P};P.walkTokens=function(e,t){return Fe.walkTokens(e,t)};P.parseInline=Fe.parseInline;P.Parser=ie;P.parser=ie.parse;P.Renderer=en;P.TextRenderer=li;P.Lexer=se;P.lexer=se.lex;P.Tokenizer=Xt;P.Hooks=gt;P.parse=P;P.options;P.setOptions;P.use;P.walkTokens;P.parseInline;ie.parse;se.lex;P.setOptions({gfm:!0,breaks:!0,mangle:!1});const Ao=["a","b","blockquote","br","code","del","em","h1","h2","h3","h4","hr","i","li","ol","p","pre","strong","table","tbody","td","th","thead","tr","ul"],So=["class","href","rel","target","title","start"];let _o=!1;const Lu=14e4,Ru=4e4,Mu=200,Zn=5e4,Pe=new Map;function Pu(e){const t=Pe.get(e);return t===void 0?null:(Pe.delete(e),Pe.set(e,t),t)}function To(e,t){if(Pe.set(e,t),Pe.size<=Mu)return;const n=Pe.keys().next().value;n&&Pe.delete(n)}function Nu(){_o||(_o=!0,ws.addHook("afterSanitizeAttributes",e=>{!(e instanceof HTMLAnchorElement)||!e.getAttribute("href")||(e.setAttribute("rel","noreferrer noopener"),e.setAttribute("target","_blank"))}))}function As(e){const t=e.trim();if(!t)return"";if(Nu(),t.length<=Zn){const a=Pu(t);if(a!==null)return a}const n=la(t,Lu),s=n.truncated?` - -… truncated (${n.total} chars, showing first ${n.text.length}).`:"";if(n.text.length>Ru){const l=`
    ${Ou(`${n.text}${s}`)}
    `,r=ws.sanitize(l,{ALLOWED_TAGS:Ao,ALLOWED_ATTR:So});return t.length<=Zn&&To(t,r),r}const i=P.parse(`${n.text}${s}`),o=ws.sanitize(i,{ALLOWED_TAGS:Ao,ALLOWED_ATTR:So});return t.length<=Zn&&To(t,o),o}function Ou(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function Du(e,t){return c``}function Ht(e,t){e&&(e.textContent=t)}const Bu=1500,Fu=2e3,lr="Copy as markdown",Uu="Copied",Ku="Copy failed",Xn="📋",Hu="✓",zu="!";async function ju(e){if(!e)return!1;try{return await navigator.clipboard.writeText(e),!0}catch{return!1}}function zt(e,t){e.title=t,e.setAttribute("aria-label",t)}function qu(e){const t=e.label??lr;return c` - - `}function Wu(e){return qu({text:()=>e,label:lr})}const Vu={emoji:"🧩",detailKeys:["command","path","url","targetUrl","targetId","ref","element","node","nodeId","id","requestId","to","channelId","guildId","userId","name","query","pattern","messageId"]},Gu={bash:{emoji:"🛠️",title:"Bash",detailKeys:["command"]},process:{emoji:"🧰",title:"Process",detailKeys:["sessionId"]},read:{emoji:"📖",title:"Read",detailKeys:["path"]},write:{emoji:"✍️",title:"Write",detailKeys:["path"]},edit:{emoji:"📝",title:"Edit",detailKeys:["path"]},attach:{emoji:"📎",title:"Attach",detailKeys:["path","url","fileName"]},browser:{emoji:"🌐",title:"Browser",actions:{status:{label:"status"},start:{label:"start"},stop:{label:"stop"},tabs:{label:"tabs"},open:{label:"open",detailKeys:["targetUrl"]},focus:{label:"focus",detailKeys:["targetId"]},close:{label:"close",detailKeys:["targetId"]},snapshot:{label:"snapshot",detailKeys:["targetUrl","targetId","ref","element","format"]},screenshot:{label:"screenshot",detailKeys:["targetUrl","targetId","ref","element"]},navigate:{label:"navigate",detailKeys:["targetUrl","targetId"]},console:{label:"console",detailKeys:["level","targetId"]},pdf:{label:"pdf",detailKeys:["targetId"]},upload:{label:"upload",detailKeys:["paths","ref","inputRef","element","targetId"]},dialog:{label:"dialog",detailKeys:["accept","promptText","targetId"]},act:{label:"act",detailKeys:["request.kind","request.ref","request.selector","request.text","request.value"]}}},canvas:{emoji:"🖼️",title:"Canvas",actions:{present:{label:"present",detailKeys:["target","node","nodeId"]},hide:{label:"hide",detailKeys:["node","nodeId"]},navigate:{label:"navigate",detailKeys:["url","node","nodeId"]},eval:{label:"eval",detailKeys:["javaScript","node","nodeId"]},snapshot:{label:"snapshot",detailKeys:["format","node","nodeId"]},a2ui_push:{label:"A2UI push",detailKeys:["jsonlPath","node","nodeId"]},a2ui_reset:{label:"A2UI reset",detailKeys:["node","nodeId"]}}},nodes:{emoji:"📱",title:"Nodes",actions:{status:{label:"status"},describe:{label:"describe",detailKeys:["node","nodeId"]},pending:{label:"pending"},approve:{label:"approve",detailKeys:["requestId"]},reject:{label:"reject",detailKeys:["requestId"]},notify:{label:"notify",detailKeys:["node","nodeId","title","body"]},camera_snap:{label:"camera snap",detailKeys:["node","nodeId","facing","deviceId"]},camera_list:{label:"camera list",detailKeys:["node","nodeId"]},camera_clip:{label:"camera clip",detailKeys:["node","nodeId","facing","duration","durationMs"]},screen_record:{label:"screen record",detailKeys:["node","nodeId","duration","durationMs","fps","screenIndex"]}}},cron:{emoji:"⏰",title:"Cron",actions:{status:{label:"status"},list:{label:"list"},add:{label:"add",detailKeys:["job.name","job.id","job.schedule","job.cron"]},update:{label:"update",detailKeys:["id"]},remove:{label:"remove",detailKeys:["id"]},run:{label:"run",detailKeys:["id"]},runs:{label:"runs",detailKeys:["id"]},wake:{label:"wake",detailKeys:["text","mode"]}}},gateway:{emoji:"🔌",title:"Gateway",actions:{restart:{label:"restart",detailKeys:["reason","delayMs"]},"config.get":{label:"config get"},"config.schema":{label:"config schema"},"config.apply":{label:"config apply",detailKeys:["restartDelayMs"]},"update.run":{label:"update run",detailKeys:["restartDelayMs"]}}},whatsapp_login:{emoji:"🟢",title:"WhatsApp Login",actions:{start:{label:"start"},wait:{label:"wait"}}},discord:{emoji:"💬",title:"Discord",actions:{react:{label:"react",detailKeys:["channelId","messageId","emoji"]},reactions:{label:"reactions",detailKeys:["channelId","messageId"]},sticker:{label:"sticker",detailKeys:["to","stickerIds"]},poll:{label:"poll",detailKeys:["question","to"]},permissions:{label:"permissions",detailKeys:["channelId"]},readMessages:{label:"read messages",detailKeys:["channelId","limit"]},sendMessage:{label:"send",detailKeys:["to","content"]},editMessage:{label:"edit",detailKeys:["channelId","messageId"]},deleteMessage:{label:"delete",detailKeys:["channelId","messageId"]},threadCreate:{label:"thread create",detailKeys:["channelId","name"]},threadList:{label:"thread list",detailKeys:["guildId","channelId"]},threadReply:{label:"thread reply",detailKeys:["channelId","content"]},pinMessage:{label:"pin",detailKeys:["channelId","messageId"]},unpinMessage:{label:"unpin",detailKeys:["channelId","messageId"]},listPins:{label:"list pins",detailKeys:["channelId"]},searchMessages:{label:"search",detailKeys:["guildId","content"]},memberInfo:{label:"member",detailKeys:["guildId","userId"]},roleInfo:{label:"roles",detailKeys:["guildId"]},emojiList:{label:"emoji list",detailKeys:["guildId"]},roleAdd:{label:"role add",detailKeys:["guildId","userId","roleId"]},roleRemove:{label:"role remove",detailKeys:["guildId","userId","roleId"]},channelInfo:{label:"channel",detailKeys:["channelId"]},channelList:{label:"channels",detailKeys:["guildId"]},voiceStatus:{label:"voice",detailKeys:["guildId","userId"]},eventList:{label:"events",detailKeys:["guildId"]},eventCreate:{label:"event create",detailKeys:["guildId","name"]},timeout:{label:"timeout",detailKeys:["guildId","userId"]},kick:{label:"kick",detailKeys:["guildId","userId"]},ban:{label:"ban",detailKeys:["guildId","userId"]}}},slack:{emoji:"💬",title:"Slack",actions:{react:{label:"react",detailKeys:["channelId","messageId","emoji"]},reactions:{label:"reactions",detailKeys:["channelId","messageId"]},sendMessage:{label:"send",detailKeys:["to","content"]},editMessage:{label:"edit",detailKeys:["channelId","messageId"]},deleteMessage:{label:"delete",detailKeys:["channelId","messageId"]},readMessages:{label:"read messages",detailKeys:["channelId","limit"]},pinMessage:{label:"pin",detailKeys:["channelId","messageId"]},unpinMessage:{label:"unpin",detailKeys:["channelId","messageId"]},listPins:{label:"list pins",detailKeys:["channelId"]},memberInfo:{label:"member",detailKeys:["userId"]},emojiList:{label:"emoji list"}}}},Yu={fallback:Vu,tools:Gu},cr=Yu,Co=cr.fallback??{emoji:"🧩"},Qu=cr.tools??{};function Ju(e){return(e??"tool").trim()}function Zu(e){const t=e.replace(/_/g," ").trim();return t?t.split(/\s+/).map(n=>n.length<=2&&n.toUpperCase()===n?n:`${n.at(0)?.toUpperCase()??""}${n.slice(1)}`).join(" "):"Tool"}function Xu(e){const t=e?.trim();if(t)return t.replace(/_/g," ")}function dr(e){if(e!=null){if(typeof e=="string"){const t=e.trim();if(!t)return;const n=t.split(/\r?\n/)[0]?.trim()??"";return n?n.length>160?`${n.slice(0,157)}…`:n:void 0}if(typeof e=="number"||typeof e=="boolean")return String(e);if(Array.isArray(e)){const t=e.map(s=>dr(s)).filter(s=>!!s);if(t.length===0)return;const n=t.slice(0,3).join(", ");return t.length>3?`${n}…`:n}}}function ep(e,t){if(!e||typeof e!="object")return;let n=e;for(const s of t.split(".")){if(!s||!n||typeof n!="object")return;n=n[s]}return n}function tp(e,t){for(const n of t){const s=ep(e,n),i=dr(s);if(i)return i}}function np(e){if(!e||typeof e!="object")return;const t=e,n=typeof t.path=="string"?t.path:void 0;if(!n)return;const s=typeof t.offset=="number"?t.offset:void 0,i=typeof t.limit=="number"?t.limit:void 0;return s!==void 0&&i!==void 0?`${n}:${s}-${s+i}`:n}function sp(e){if(!e||typeof e!="object")return;const t=e;return typeof t.path=="string"?t.path:void 0}function ip(e,t){if(!(!e||!t))return e.actions?.[t]??void 0}function op(e){const t=Ju(e.name),n=t.toLowerCase(),s=Qu[n],i=s?.emoji??Co.emoji??"🧩",o=s?.title??Zu(t),a=s?.label??t,l=e.args&&typeof e.args=="object"?e.args.action:void 0,r=typeof l=="string"?l.trim():void 0,p=ip(s,r),d=Xu(p?.label??r);let u;n==="read"&&(u=np(e.args)),!u&&(n==="write"||n==="edit"||n==="attach")&&(u=sp(e.args));const h=p?.detailKeys??s?.detailKeys??Co.detailKeys??[];return!u&&h.length>0&&(u=tp(e.args,h)),!u&&e.meta&&(u=e.meta),u&&(u=rp(u)),{name:t,emoji:i,title:o,label:a,verb:d,detail:u}}function ap(e){const t=[];if(e.verb&&t.push(e.verb),e.detail&&t.push(e.detail),t.length!==0)return t.join(" · ")}function rp(e){return e&&e.replace(/\/Users\/[^/]+/g,"~").replace(/\/home\/[^/]+/g,"~")}const lp=80,cp=2,Eo=100;function dp(e){const t=e.trim();if(t.startsWith("{")||t.startsWith("["))try{const n=JSON.parse(t);return"```json\n"+JSON.stringify(n,null,2)+"\n```"}catch{}return e}function up(e){const t=e.split(` -`),n=t.slice(0,cp),s=n.join(` -`);return s.length>Eo?s.slice(0,Eo)+"…":n.lengthi.kind==="result")){const i=typeof t.toolName=="string"&&t.toolName||typeof t.tool_name=="string"&&t.tool_name||"tool",o=ca(e)??void 0;s.push({kind:"result",name:i,text:o})}return s}function Io(e,t){const n=op({name:e.name,args:e.args}),s=ap(n),i=!!e.text?.trim(),o=!!t,a=o?()=>{if(i){t(dp(e.text));return}const u=`## ${n.label} - -${s?`**Command:** \`${s}\` - -`:""}*No output — tool completed successfully.*`;t(u)}:void 0,l=i&&(e.text?.length??0)<=lp,r=i&&!l,p=i&&l,d=!i;return c` -
    {u.key!=="Enter"&&u.key!==" "||(u.preventDefault(),a?.())}:g} - > -
    -
    - ${n.emoji} - ${n.label} -
    - ${o?c`${i?"View ›":"›"}`:g} - ${d&&!o?c``:g} -
    - ${s?c`
    ${s}
    `:g} - ${d?c`
    Completed
    `:g} - ${r?c`
    ${up(e.text)}
    `:g} - ${p?c`
    ${e.text}
    `:g} -
    - `}function fp(e){return Array.isArray(e)?e.filter(Boolean):[]}function hp(e){if(typeof e!="string")return e;const t=e.trim();if(!t||!t.startsWith("{")&&!t.startsWith("["))return e;try{return JSON.parse(t)}catch{return e}}function gp(e){if(typeof e.text=="string")return e.text;if(typeof e.content=="string")return e.content}function vp(e){return c` -
    - ${ci("assistant",e)} -
    - -
    -
    - `}function mp(e,t,n,s){const i=new Date(t).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"}),o=s?.name??"Assistant";return c` -
    - ${ci("assistant",s)} -
    - ${ur({role:"assistant",content:[{type:"text",text:e}],timestamp:t},{isStreaming:!0,showReasoning:!1},n)} - -
    -
    - `}function bp(e,t){const n=Xs(e.role),s=t.assistantName??"Assistant",i=n==="user"?"You":n==="assistant"?s:n,o=n==="user"?"user":n==="assistant"?"assistant":"other",a=new Date(e.timestamp).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"});return c` -
    - ${ci(e.role,{name:s,avatar:t.assistantAvatar??null})} -
    - ${e.messages.map((l,r)=>ur(l.message,{isStreaming:e.isStreaming&&r===e.messages.length-1,showReasoning:t.showReasoning},t.onOpenSidebar))} - -
    -
    - `}function ci(e,t){const n=Xs(e),s=t?.name?.trim()||"Assistant",i=t?.avatar?.trim()||"",o=n==="user"?"U":n==="assistant"?s.charAt(0).toUpperCase()||"A":n==="tool"?"⚙":"?",a=n==="user"?"user":n==="assistant"?"assistant":n==="tool"?"tool":"other";return i&&n==="assistant"?yp(i)?c`${s}`:c`
    ${i}
    `:c`
    ${o}
    `}function yp(e){return/^https?:\/\//i.test(e)||/^data:image\//i.test(e)||/^\//.test(e)}function ur(e,t,n){const s=e,i=typeof s.role=="string"?s.role:"unknown",o=Wa(e)||i.toLowerCase()==="toolresult"||i.toLowerCase()==="tool_result"||typeof s.toolCallId=="string"||typeof s.tool_call_id=="string",a=pp(e),l=a.length>0,r=ca(e),p=t.showReasoning&&i==="assistant"?xl(e):null,d=r?.trim()?r:null,u=p?Sl(p):null,h=d,v=i==="assistant"&&!!h?.trim(),w=["chat-bubble",v?"has-copy":"",t.isStreaming?"streaming":"","fade-in"].filter(Boolean).join(" ");return!h&&l&&o?c`${a.map($=>Io($,n))}`:!h&&!l?g:c` -
    - ${v?Wu(h):g} - ${u?c`
    ${vs(As(u))}
    `:g} - ${h?c`
    ${vs(As(h))}
    `:g} - ${a.map($=>Io($,n))} -
    - `}function wp(e){return c` - - `}var $p=Object.defineProperty,kp=Object.getOwnPropertyDescriptor,vn=(e,t,n,s)=>{for(var i=s>1?void 0:s?kp(t,n):t,o=e.length-1,a;o>=0;o--)(a=e[o])&&(i=(s?a(t,n,i):a(i))||i);return s&&i&&$p(t,n,i),i};let tt=class extends Qe{constructor(){super(...arguments),this.splitRatio=.6,this.minRatio=.4,this.maxRatio=.7,this.isDragging=!1,this.startX=0,this.startRatio=0,this.handleMouseDown=e=>{this.isDragging=!0,this.startX=e.clientX,this.startRatio=this.splitRatio,this.classList.add("dragging"),document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("mouseup",this.handleMouseUp),e.preventDefault()},this.handleMouseMove=e=>{if(!this.isDragging)return;const t=this.parentElement;if(!t)return;const n=t.getBoundingClientRect().width,i=(e.clientX-this.startX)/n;let o=this.startRatio+i;o=Math.max(this.minRatio,Math.min(this.maxRatio,o)),this.dispatchEvent(new CustomEvent("resize",{detail:{splitRatio:o},bubbles:!0,composed:!0}))},this.handleMouseUp=()=>{this.isDragging=!1,this.classList.remove("dragging"),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp)}}render(){return c``}connectedCallback(){super.connectedCallback(),this.addEventListener("mousedown",this.handleMouseDown)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("mousedown",this.handleMouseDown),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp)}};tt.styles=Fr` - :host { - width: 4px; - cursor: col-resize; - background: var(--border, #333); - transition: background 150ms ease-out; - flex-shrink: 0; - position: relative; - } - - :host::before { - content: ""; - position: absolute; - top: 0; - left: -4px; - right: -4px; - bottom: 0; - } - - :host(:hover) { - background: var(--accent, #007bff); - } - - :host(.dragging) { - background: var(--accent, #007bff); - } - `;vn([on({type:Number})],tt.prototype,"splitRatio",2);vn([on({type:Number})],tt.prototype,"minRatio",2);vn([on({type:Number})],tt.prototype,"maxRatio",2);tt=vn([ta("resizable-divider")],tt);const xp=5e3;function Ap(e){return e?e.active?c` -
    - 🧹 Compacting context... -
    - `:e.completedAt&&Date.now()-e.completedAt - 🧹 Context compacted - - `:g:g}function Sp(e){const t=e.connected,n=e.sending||e.stream!==null,i=e.sessions?.sessions?.find(u=>u.key===e.sessionKey)?.reasoningLevel??"off",o=e.showThinking&&i!=="off",a={name:e.assistantName,avatar:e.assistantAvatar??e.assistantAvatarUrl??null},l=e.connected?"Message (↩ to send, Shift+↩ for line breaks)":"Connect to the gateway to start chatting…",r=e.splitRatio??.6,p=!!(e.sidebarOpen&&e.onCloseSidebar),d=c` -
    - ${e.loading?c`
    Loading chat…
    `:g} - ${ja(Tp(e),u=>u.key,u=>u.kind==="reading-indicator"?vp(a):u.kind==="stream"?mp(u.text,u.startedAt,e.onOpenSidebar,a):u.kind==="group"?bp(u,{onOpenSidebar:e.onOpenSidebar,showReasoning:o,assistantName:e.assistantName,assistantAvatar:a.avatar}):g)} -
    - `;return c` -
    - ${e.disabledReason?c`
    ${e.disabledReason}
    `:g} - - ${e.error?c`
    ${e.error}
    `:g} - - ${Ap(e.compactionStatus)} - - ${e.focusMode?c` - - `:g} - -
    -
    - ${d} -
    - - ${p?c` - e.onSplitRatioChange?.(u.detail.splitRatio)} - > -
    - ${wp({content:e.sidebarContent??null,error:e.sidebarError??null,onClose:e.onCloseSidebar,onViewRawText:()=>{!e.sidebarContent||!e.onOpenSidebar||e.onOpenSidebar(`\`\`\` -${e.sidebarContent} -\`\`\``)}})} -
    - `:g} -
    - - ${e.queue.length?c` -
    -
    Queued (${e.queue.length})
    -
    - ${e.queue.map(u=>c` -
    -
    ${u.text}
    - -
    - `)} -
    -
    - `:g} - -
    - -
    - - -
    -
    -
    - `}const Lo=200;function _p(e){const t=[];let n=null;for(const s of e){if(s.kind!=="message"){n&&(t.push(n),n=null),t.push(s);continue}const i=qa(s.message),o=Xs(i.role),a=i.timestamp||Date.now();!n||n.role!==o?(n&&t.push(n),n={kind:"group",key:`group:${o}:${s.key}`,role:o,messages:[{message:s.message,key:s.key}],timestamp:a,isStreaming:!1}):n.messages.push({message:s.message,key:s.key})}return n&&t.push(n),t}function Tp(e){const t=[],n=Array.isArray(e.messages)?e.messages:[],s=Array.isArray(e.toolMessages)?e.toolMessages:[],i=Math.max(0,n.length-Lo);i>0&&t.push({kind:"message",key:"chat:history:notice",message:{role:"system",content:`Showing last ${Lo} messages (${i} hidden).`,timestamp:Date.now()}});for(let o=i;o0?t.push({kind:"stream",key:o,text:e.stream,startedAt:e.streamStartedAt??Date.now()}):t.push({kind:"reading-indicator",key:o})}return _p(t)}function Ro(e,t){const n=e,s=typeof n.toolCallId=="string"?n.toolCallId:"";if(s)return`tool:${s}`;const i=typeof n.id=="string"?n.id:"";if(i)return`msg:${i}`;const o=typeof n.messageId=="string"?n.messageId:"";if(o)return`msg:${o}`;const a=typeof n.timestamp=="number"?n.timestamp:null,l=typeof n.role=="string"?n.role:"unknown";return a!=null?`msg:${l}:${a}:${t}`:`msg:${l}:${t}`}function de(e){if(e)return Array.isArray(e.type)?e.type.filter(n=>n!=="null")[0]??e.type[0]:e.type}function pr(e){if(!e)return"";if(e.default!==void 0)return e.default;switch(de(e)){case"object":return{};case"array":return[];case"boolean":return!1;case"number":case"integer":return 0;case"string":return"";default:return""}}function mn(e){return e.filter(t=>typeof t=="string").join(".")}function ee(e,t){const n=mn(e),s=t[n];if(s)return s;const i=n.split(".");for(const[o,a]of Object.entries(t)){if(!o.includes("*"))continue;const l=o.split(".");if(l.length!==i.length)continue;let r=!0;for(let p=0;pt.toUpperCase())}function Cp(e){const t=mn(e).toLowerCase();return t.includes("token")||t.includes("password")||t.includes("secret")||t.includes("apikey")||t.endsWith("key")}const Ep=new Set(["title","description","default","nullable"]);function Ip(e){return Object.keys(e??{}).filter(n=>!Ep.has(n)).length===0}function Lp(e){if(e===void 0)return"";try{return JSON.stringify(e,null,2)??""}catch{return""}}const St={chevronDown:c``,plus:c``,minus:c``,trash:c``,edit:c``};function be(e){const{schema:t,value:n,path:s,hints:i,unsupported:o,disabled:a,onPatch:l}=e,r=e.showLabel??!0,p=de(t),d=ee(s,i),u=d?.label??t.title??ye(String(s.at(-1))),h=d?.help??t.description,v=mn(s);if(o.has(v))return c`
    -
    ${u}
    -
    Unsupported schema node. Use Raw mode.
    -
    `;if(t.anyOf||t.oneOf){const $=(t.anyOf??t.oneOf??[]).filter(A=>!(A.type==="null"||Array.isArray(A.type)&&A.type.includes("null")));if($.length===1)return be({...e,schema:$[0]});const x=A=>{if(A.const!==void 0)return A.const;if(A.enum&&A.enum.length===1)return A.enum[0]},C=$.map(x),I=C.every(A=>A!==void 0);if(I&&C.length>0&&C.length<=5){const A=n??t.default;return c` -
    - ${r?c``:g} - ${h?c`
    ${h}
    `:g} -
    - ${C.map((B,ue)=>c` - - `)} -
    -
    - `}if(I&&C.length>5)return Po({...e,options:C,value:n??t.default});const R=new Set($.map(A=>de(A)).filter(Boolean)),E=new Set([...R].map(A=>A==="integer"?"number":A));if([...E].every(A=>["string","number","boolean"].includes(A))){const A=E.has("string"),B=E.has("number");if(E.has("boolean")&&E.size===1)return be({...e,schema:{...t,type:"boolean",anyOf:void 0,oneOf:void 0}});if(A||B)return Mo({...e,inputType:B&&!A?"number":"text"})}}if(t.enum){const w=t.enum;if(w.length<=5){const $=n??t.default;return c` -
    - ${r?c``:g} - ${h?c`
    ${h}
    `:g} -
    - ${w.map(x=>c` - - `)} -
    -
    - `}return Po({...e,options:w,value:n??t.default})}if(p==="object")return Mp(e);if(p==="array")return Pp(e);if(p==="boolean"){const w=typeof n=="boolean"?n:typeof t.default=="boolean"?t.default:!1;return c` - - `}return p==="number"||p==="integer"?Rp(e):p==="string"?Mo({...e,inputType:"text"}):c` -
    -
    ${u}
    -
    Unsupported type: ${p}. Use Raw mode.
    -
    - `}function Mo(e){const{schema:t,value:n,path:s,hints:i,disabled:o,onPatch:a,inputType:l}=e,r=e.showLabel??!0,p=ee(s,i),d=p?.label??t.title??ye(String(s.at(-1))),u=p?.help??t.description,h=p?.sensitive??Cp(s),v=p?.placeholder??(h?"••••":t.default!==void 0?`Default: ${t.default}`:""),w=n??"";return c` -
    - ${r?c``:g} - ${u?c`
    ${u}
    `:g} -
    - {const x=$.target.value;if(l==="number"){if(x.trim()===""){a(s,void 0);return}const C=Number(x);a(s,Number.isNaN(C)?x:C);return}a(s,x)}} - /> - ${t.default!==void 0?c` - - `:g} -
    -
    - `}function Rp(e){const{schema:t,value:n,path:s,hints:i,disabled:o,onPatch:a}=e,l=e.showLabel??!0,r=ee(s,i),p=r?.label??t.title??ye(String(s.at(-1))),d=r?.help??t.description,u=n??t.default??"",h=typeof u=="number"?u:0;return c` -
    - ${l?c``:g} - ${d?c`
    ${d}
    `:g} -
    - - {const w=v.target.value,$=w===""?void 0:Number(w);a(s,$)}} - /> - -
    -
    - `}function Po(e){const{schema:t,value:n,path:s,hints:i,disabled:o,options:a,onPatch:l}=e,r=e.showLabel??!0,p=ee(s,i),d=p?.label??t.title??ye(String(s.at(-1))),u=p?.help??t.description,h=n??t.default,v=a.findIndex($=>$===h||String($)===String(h)),w="__unset__";return c` -
    - ${r?c``:g} - ${u?c`
    ${u}
    `:g} - -
    - `}function Mp(e){const{schema:t,value:n,path:s,hints:i,unsupported:o,disabled:a,onPatch:l}=e;e.showLabel;const r=ee(s,i),p=r?.label??t.title??ye(String(s.at(-1))),d=r?.help??t.description,u=n??t.default,h=u&&typeof u=="object"&&!Array.isArray(u)?u:{},v=t.properties??{},$=Object.entries(v).sort((R,E)=>{const A=ee([...s,R[0]],i)?.order??0,B=ee([...s,E[0]],i)?.order??0;return A!==B?A-B:R[0].localeCompare(E[0])}),x=new Set(Object.keys(v)),C=t.additionalProperties,I=!!C&&typeof C=="object";return s.length===1?c` -
    - ${$.map(([R,E])=>be({schema:E,value:h[R],path:[...s,R],hints:i,unsupported:o,disabled:a,onPatch:l}))} - ${I?No({schema:C,value:h,path:s,hints:i,unsupported:o,disabled:a,reservedKeys:x,onPatch:l}):g} -
    - `:c` -
    - - ${p} - ${St.chevronDown} - - ${d?c`
    ${d}
    `:g} -
    - ${$.map(([R,E])=>be({schema:E,value:h[R],path:[...s,R],hints:i,unsupported:o,disabled:a,onPatch:l}))} - ${I?No({schema:C,value:h,path:s,hints:i,unsupported:o,disabled:a,reservedKeys:x,onPatch:l}):g} -
    -
    - `}function Pp(e){const{schema:t,value:n,path:s,hints:i,unsupported:o,disabled:a,onPatch:l}=e,r=e.showLabel??!0,p=ee(s,i),d=p?.label??t.title??ye(String(s.at(-1))),u=p?.help??t.description,h=Array.isArray(t.items)?t.items[0]:t.items;if(!h)return c` -
    -
    ${d}
    -
    Unsupported array schema. Use Raw mode.
    -
    - `;const v=Array.isArray(n)?n:Array.isArray(t.default)?t.default:[];return c` -
    -
    - ${r?c`${d}`:g} - ${v.length} item${v.length!==1?"s":""} - -
    - ${u?c`
    ${u}
    `:g} - - ${v.length===0?c` -
    - No items yet. Click "Add" to create one. -
    - `:c` -
    - ${v.map((w,$)=>c` -
    -
    - #${$+1} - -
    -
    - ${be({schema:h,value:w,path:[...s,$],hints:i,unsupported:o,disabled:a,showLabel:!1,onPatch:l})} -
    -
    - `)} -
    - `} -
    - `}function No(e){const{schema:t,value:n,path:s,hints:i,unsupported:o,disabled:a,reservedKeys:l,onPatch:r}=e,p=Ip(t),d=Object.entries(n??{}).filter(([u])=>!l.has(u));return c` -
    -
    - Custom entries - -
    - - ${d.length===0?c` -
    No custom entries.
    - `:c` -
    - ${d.map(([u,h])=>{const v=[...s,u],w=Lp(h);return c` -
    -
    - {const x=$.target.value.trim();if(!x||x===u)return;const C={...n??{}};x in C||(C[x]=C[u],delete C[u],r(s,C))}} - /> -
    -
    - ${p?c` - - `:be({schema:t,value:h,path:v,hints:i,unsupported:o,disabled:a,showLabel:!1,onPatch:r})} -
    - -
    - `})} -
    - `} -
    - `}const Oo={env:c``,update:c``,agents:c``,auth:c``,channels:c``,messages:c``,commands:c``,hooks:c``,skills:c``,tools:c``,gateway:c``,wizard:c``,meta:c``,logging:c``,browser:c``,ui:c``,models:c``,bindings:c``,broadcast:c``,audio:c``,session:c``,cron:c``,web:c``,discovery:c``,canvasHost:c``,talk:c``,plugins:c``,default:c``},di={env:{label:"Environment Variables",description:"Environment variables passed to the gateway process"},update:{label:"Updates",description:"Auto-update settings and release channel"},agents:{label:"Agents",description:"Agent configurations, models, and identities"},auth:{label:"Authentication",description:"API keys and authentication profiles"},channels:{label:"Channels",description:"Messaging channels (Telegram, Discord, Slack, etc.)"},messages:{label:"Messages",description:"Message handling and routing settings"},commands:{label:"Commands",description:"Custom slash commands"},hooks:{label:"Hooks",description:"Webhooks and event hooks"},skills:{label:"Skills",description:"Skill packs and capabilities"},tools:{label:"Tools",description:"Tool configurations (browser, search, etc.)"},gateway:{label:"Gateway",description:"Gateway server settings (port, auth, binding)"},wizard:{label:"Setup Wizard",description:"Setup wizard state and history"},meta:{label:"Metadata",description:"Gateway metadata and version information"},logging:{label:"Logging",description:"Log levels and output configuration"},browser:{label:"Browser",description:"Browser automation settings"},ui:{label:"UI",description:"User interface preferences"},models:{label:"Models",description:"AI model configurations and providers"},bindings:{label:"Bindings",description:"Key bindings and shortcuts"},broadcast:{label:"Broadcast",description:"Broadcast and notification settings"},audio:{label:"Audio",description:"Audio input/output settings"},session:{label:"Session",description:"Session management and persistence"},cron:{label:"Cron",description:"Scheduled tasks and automation"},web:{label:"Web",description:"Web server and API settings"},discovery:{label:"Discovery",description:"Service discovery and networking"},canvasHost:{label:"Canvas Host",description:"Canvas rendering and display"},talk:{label:"Talk",description:"Voice and speech settings"},plugins:{label:"Plugins",description:"Plugin management and extensions"}};function Do(e){return Oo[e]??Oo.default}function Np(e,t,n){if(!n)return!0;const s=n.toLowerCase(),i=di[e];return e.toLowerCase().includes(s)||i&&(i.label.toLowerCase().includes(s)||i.description.toLowerCase().includes(s))?!0:vt(t,s)}function vt(e,t){if(e.title?.toLowerCase().includes(t)||e.description?.toLowerCase().includes(t)||e.enum?.some(s=>String(s).toLowerCase().includes(t)))return!0;if(e.properties){for(const[s,i]of Object.entries(e.properties))if(s.toLowerCase().includes(t)||vt(i,t))return!0}if(e.items){const s=Array.isArray(e.items)?e.items:[e.items];for(const i of s)if(i&&vt(i,t))return!0}if(e.additionalProperties&&typeof e.additionalProperties=="object"&&vt(e.additionalProperties,t))return!0;const n=e.anyOf??e.oneOf??e.allOf;if(n){for(const s of n)if(s&&vt(s,t))return!0}return!1}function Op(e){if(!e.schema)return c`
    Schema unavailable.
    `;const t=e.schema,n=e.value??{};if(de(t)!=="object"||!t.properties)return c`
    Unsupported schema. Use Raw.
    `;const s=new Set(e.unsupportedPaths??[]),i=t.properties,o=e.searchQuery??"",a=e.activeSection,l=e.activeSubsection??null,p=Object.entries(i).sort((u,h)=>{const v=ee([u[0]],e.uiHints)?.order??50,w=ee([h[0]],e.uiHints)?.order??50;return v!==w?v-w:u[0].localeCompare(h[0])}).filter(([u,h])=>!(a&&u!==a||o&&!Np(u,h,o)));let d=null;if(a&&l&&p.length===1){const u=p[0]?.[1];u&&de(u)==="object"&&u.properties&&u.properties[l]&&(d={sectionKey:a,subsectionKey:l,schema:u.properties[l]})}return p.length===0?c` -
    -
    🔍
    -
    - ${o?`No settings match "${o}"`:"No settings in this section"} -
    -
    - `:c` -
    - ${d?(()=>{const{sectionKey:u,subsectionKey:h,schema:v}=d,w=ee([u,h],e.uiHints),$=w?.label??v.title??ye(h),x=w?.help??v.description??"",C=n[u],I=C&&typeof C=="object"?C[h]:void 0,R=`config-section-${u}-${h}`;return c` -
    -
    - ${Do(u)} -
    -

    ${$}

    - ${x?c`

    ${x}

    `:g} -
    -
    -
    - ${be({schema:v,value:I,path:[u,h],hints:e.uiHints,unsupported:s,disabled:e.disabled??!1,showLabel:!1,onPatch:e.onPatch})} -
    -
    - `})():p.map(([u,h])=>{const v=di[u]??{label:u.charAt(0).toUpperCase()+u.slice(1),description:h.description??""};return c` -
    -
    - ${Do(u)} -
    -

    ${v.label}

    - ${v.description?c`

    ${v.description}

    `:g} -
    -
    -
    - ${be({schema:h,value:n[u],path:[u],hints:e.uiHints,unsupported:s,disabled:e.disabled??!1,showLabel:!1,onPatch:e.onPatch})} -
    -
    - `})} -
    - `}const Dp=new Set(["title","description","default","nullable"]);function Bp(e){return Object.keys(e??{}).filter(n=>!Dp.has(n)).length===0}function fr(e){const t=e.filter(i=>i!=null),n=t.length!==e.length,s=[];for(const i of t)s.some(o=>Object.is(o,i))||s.push(i);return{enumValues:s,nullable:n}}function hr(e){return!e||typeof e!="object"?{schema:null,unsupportedPaths:[""]}:yt(e,[])}function yt(e,t){const n=new Set,s={...e},i=mn(t)||"";if(e.anyOf||e.oneOf||e.allOf){const l=Fp(e,t);return l||{schema:e,unsupportedPaths:[i]}}const o=Array.isArray(e.type)&&e.type.includes("null"),a=de(e)??(e.properties||e.additionalProperties?"object":void 0);if(s.type=a??e.type,s.nullable=o||e.nullable,s.enum){const{enumValues:l,nullable:r}=fr(s.enum);s.enum=l,r&&(s.nullable=!0),l.length===0&&n.add(i)}if(a==="object"){const l=e.properties??{},r={};for(const[p,d]of Object.entries(l)){const u=yt(d,[...t,p]);u.schema&&(r[p]=u.schema);for(const h of u.unsupportedPaths)n.add(h)}if(s.properties=r,e.additionalProperties===!0)n.add(i);else if(e.additionalProperties===!1)s.additionalProperties=!1;else if(e.additionalProperties&&typeof e.additionalProperties=="object"&&!Bp(e.additionalProperties)){const p=yt(e.additionalProperties,[...t,"*"]);s.additionalProperties=p.schema??e.additionalProperties,p.unsupportedPaths.length>0&&n.add(i)}}else if(a==="array"){const l=Array.isArray(e.items)?e.items[0]:e.items;if(!l)n.add(i);else{const r=yt(l,[...t,"*"]);s.items=r.schema??l,r.unsupportedPaths.length>0&&n.add(i)}}else a!=="string"&&a!=="number"&&a!=="integer"&&a!=="boolean"&&!s.enum&&n.add(i);return{schema:s,unsupportedPaths:Array.from(n)}}function Fp(e,t){if(e.allOf)return null;const n=e.anyOf??e.oneOf;if(!n)return null;const s=[],i=[];let o=!1;for(const l of n){if(!l||typeof l!="object")return null;if(Array.isArray(l.enum)){const{enumValues:r,nullable:p}=fr(l.enum);s.push(...r),p&&(o=!0);continue}if("const"in l){if(l.const==null){o=!0;continue}s.push(l.const);continue}if(de(l)==="null"){o=!0;continue}i.push(l)}if(s.length>0&&i.length===0){const l=[];for(const r of s)l.some(p=>Object.is(p,r))||l.push(r);return{schema:{...e,enum:l,nullable:o,anyOf:void 0,oneOf:void 0,allOf:void 0},unsupportedPaths:[]}}if(i.length===1){const l=yt(i[0],t);return l.schema&&(l.schema.nullable=o||l.schema.nullable),l}const a=["string","number","integer","boolean"];return i.length>0&&s.length===0&&i.every(l=>l.type&&a.includes(String(l.type)))?{schema:{...e,nullable:o},unsupportedPaths:[]}:null}const Ss={all:c``,env:c``,update:c``,agents:c``,auth:c``,channels:c``,messages:c``,commands:c``,hooks:c``,skills:c``,tools:c``,gateway:c``,wizard:c``,meta:c``,logging:c``,browser:c``,ui:c``,models:c``,bindings:c``,broadcast:c``,audio:c``,session:c``,cron:c``,web:c``,discovery:c``,canvasHost:c``,talk:c``,plugins:c``,default:c``},Bo=[{key:"env",label:"Environment"},{key:"update",label:"Updates"},{key:"agents",label:"Agents"},{key:"auth",label:"Authentication"},{key:"channels",label:"Channels"},{key:"messages",label:"Messages"},{key:"commands",label:"Commands"},{key:"hooks",label:"Hooks"},{key:"skills",label:"Skills"},{key:"tools",label:"Tools"},{key:"gateway",label:"Gateway"},{key:"wizard",label:"Setup Wizard"}],Fo="__all__";function Uo(e){return Ss[e]??Ss.default}function Up(e,t){const n=di[e];return n||{label:t?.title??ye(e),description:t?.description??""}}function Kp(e){const{key:t,schema:n,uiHints:s}=e;if(!n||de(n)!=="object"||!n.properties)return[];const i=Object.entries(n.properties).map(([o,a])=>{const l=ee([t,o],s),r=l?.label??a.title??ye(o),p=l?.help??a.description??"",d=l?.order??50;return{key:o,label:r,description:p,order:d}});return i.sort((o,a)=>o.order!==a.order?o.order-a.order:o.key.localeCompare(a.key)),i}function Hp(e,t){if(!e||!t)return[];const n=[];function s(i,o,a){if(i===o)return;if(typeof i!=typeof o){n.push({path:a,from:i,to:o});return}if(typeof i!="object"||i===null||o===null){i!==o&&n.push({path:a,from:i,to:o});return}if(Array.isArray(i)&&Array.isArray(o)){JSON.stringify(i)!==JSON.stringify(o)&&n.push({path:a,from:i,to:o});return}const l=i,r=o,p=new Set([...Object.keys(l),...Object.keys(r)]);for(const d of p)s(l[d],r[d],a?`${a}.${d}`:d)}return s(e,t,""),n}function Ko(e,t=40){let n;try{n=JSON.stringify(e)??String(e)}catch{n=String(e)}return n.length<=t?n:n.slice(0,t-3)+"..."}function zp(e){const t=e.valid==null?"unknown":e.valid?"valid":"invalid",n=hr(e.schema),s=n.schema?n.unsupportedPaths.length>0:!1,i=!!e.formValue&&!e.loading&&!s,o=e.connected&&!e.saving&&(e.formMode==="raw"?!0:i),a=e.connected&&!e.applying&&!e.updating&&(e.formMode==="raw"?!0:i),l=e.connected&&!e.applying&&!e.updating,r=n.schema?.properties??{},p=Bo.filter(A=>A.key in r),d=new Set(Bo.map(A=>A.key)),u=Object.keys(r).filter(A=>!d.has(A)).map(A=>({key:A,label:A.charAt(0).toUpperCase()+A.slice(1)})),h=[...p,...u],v=e.activeSection&&n.schema&&de(n.schema)==="object"?n.schema.properties?.[e.activeSection]:void 0,w=e.activeSection?Up(e.activeSection,v):null,$=e.activeSection?Kp({key:e.activeSection,schema:v,uiHints:e.uiHints}):[],x=e.formMode==="form"&&!!e.activeSection&&$.length>0,C=e.activeSubsection===Fo,I=e.searchQuery||C?null:e.activeSubsection??$[0]?.key??null,R=e.formMode==="form"?Hp(e.originalValue,e.formValue):[],E=R.length>0;return c` -
    - - - - -
    - -
    -
    - ${E?c` - ${R.length} unsaved change${R.length!==1?"s":""} - `:c` - No changes - `} -
    -
    - - - - -
    -
    - - - ${E?c` -
    - - View ${R.length} pending change${R.length!==1?"s":""} - - - - -
    - ${R.map(A=>c` -
    -
    ${A.path}
    -
    - ${Ko(A.from)} - - ${Ko(A.to)} -
    -
    - `)} -
    -
    - `:g} - - ${w&&e.formMode==="form"?c` -
    -
    ${Uo(e.activeSection??"")}
    -
    -
    ${w.label}
    - ${w.description?c`
    ${w.description}
    `:g} -
    -
    - `:g} - - ${x?c` -
    - - ${$.map(A=>c` - - `)} -
    - `:g} - - -
    - ${e.formMode==="form"?c` - ${e.schemaLoading?c`
    -
    - Loading schema… -
    `:Op({schema:n.schema,uiHints:e.uiHints,value:e.formValue,disabled:e.loading||!e.formValue,unsupportedPaths:n.unsupportedPaths,onPatch:e.onFormPatch,searchQuery:e.searchQuery,activeSection:e.activeSection,activeSubsection:I})} - ${s?c`
    - Form view can't safely edit some fields. - Use Raw to avoid losing config entries. -
    `:g} - `:c` - - `} -
    - - ${e.issues.length>0?c`
    -
    ${JSON.stringify(e.issues,null,2)}
    -
    `:g} -
    -
    - `}function jp(e){if(!e&&e!==0)return"n/a";const t=Math.round(e/1e3);if(t<60)return`${t}s`;const n=Math.round(t/60);return n<60?`${n}m`:`${Math.round(n/60)}h`}function qp(e,t){const n=t.snapshot,s=n?.channels;if(!n||!s)return!1;const i=s[e],o=typeof i?.configured=="boolean"&&i.configured,a=typeof i?.running=="boolean"&&i.running,l=typeof i?.connected=="boolean"&&i.connected,p=(n.channelAccounts?.[e]??[]).some(d=>d.configured||d.running||d.connected);return o||a||l||p}function Wp(e,t){return t?.[e]?.length??0}function gr(e,t){const n=Wp(e,t);return n<2?g:c``}function Vp(e,t){let n=e;for(const s of t){if(!n)return null;const i=de(n);if(i==="object"){const o=n.properties??{};if(typeof s=="string"&&o[s]){n=o[s];continue}const a=n.additionalProperties;if(typeof s=="string"&&a&&typeof a=="object"){n=a;continue}return null}if(i==="array"){if(typeof s!="number")return null;n=(Array.isArray(n.items)?n.items[0]:n.items)??null;continue}return null}return n}function Gp(e,t){const s=(e.channels??{})[t],i=e[t];return(s&&typeof s=="object"?s:null)??(i&&typeof i=="object"?i:null)??{}}function Yp(e){const t=hr(e.schema),n=t.schema;if(!n)return c`
    Schema unavailable. Use Raw.
    `;const s=Vp(n,["channels",e.channelId]);if(!s)return c`
    Channel config schema unavailable.
    `;const i=e.configValue??{},o=Gp(i,e.channelId);return c` -
    - ${be({schema:s,value:o,path:["channels",e.channelId],hints:e.uiHints,unsupported:new Set(t.unsupportedPaths),disabled:e.disabled,showLabel:!1,onPatch:e.onPatch})} -
    - `}function _e(e){const{channelId:t,props:n}=e,s=n.configSaving||n.configSchemaLoading;return c` -
    - ${n.configSchemaLoading?c`
    Loading config schema…
    `:Yp({channelId:t,configValue:n.configForm,schema:n.configSchema,uiHints:n.configUiHints,disabled:s,onPatch:n.onConfigPatch})} -
    - - -
    -
    - `}function Qp(e){const{props:t,discord:n,accountCountLabel:s}=e;return c` -
    -
    Discord
    -
    Bot status and channel configuration.
    - ${s} - -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Last start - ${n?.lastStartAt?O(n.lastStartAt):"n/a"} -
    -
    - Last probe - ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} -
    -
    - - ${n?.lastError?c`
    - ${n.lastError} -
    `:g} - - ${n?.probe?c`
    - Probe ${n.probe.ok?"ok":"failed"} · - ${n.probe.status??""} ${n.probe.error??""} -
    `:g} - - ${_e({channelId:"discord",props:t})} - -
    - -
    -
    - `}function Jp(e){const{props:t,imessage:n,accountCountLabel:s}=e;return c` -
    -
    iMessage
    -
    macOS bridge status and channel configuration.
    - ${s} - -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Last start - ${n?.lastStartAt?O(n.lastStartAt):"n/a"} -
    -
    - Last probe - ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} -
    -
    - - ${n?.lastError?c`
    - ${n.lastError} -
    `:g} - - ${n?.probe?c`
    - Probe ${n.probe.ok?"ok":"failed"} · - ${n.probe.error??""} -
    `:g} - - ${_e({channelId:"imessage",props:t})} - -
    - -
    -
    - `}function Zp(e){const{values:t,original:n}=e;return t.name!==n.name||t.displayName!==n.displayName||t.about!==n.about||t.picture!==n.picture||t.banner!==n.banner||t.website!==n.website||t.nip05!==n.nip05||t.lud16!==n.lud16}function Xp(e){const{state:t,callbacks:n,accountId:s}=e,i=Zp(t),o=(l,r,p={})=>{const{type:d="text",placeholder:u,maxLength:h,help:v}=p,w=t.values[l]??"",$=t.fieldErrors[l],x=`nostr-profile-${l}`;return d==="textarea"?c` -
    - - - ${v?c`
    ${v}
    `:g} - ${$?c`
    ${$}
    `:g} -
    - `:c` -
    - - {const I=C.target;n.onFieldChange(l,I.value)}} - ?disabled=${t.saving} - /> - ${v?c`
    ${v}
    `:g} - ${$?c`
    ${$}
    `:g} -
    - `},a=()=>{const l=t.values.picture;return l?c` -
    - Profile picture preview{const p=r.target;p.style.display="none"}} - @load=${r=>{const p=r.target;p.style.display="block"}} - /> -
    - `:g};return c` -
    -
    -
    Edit Profile
    -
    Account: ${s}
    -
    - - ${t.error?c`
    ${t.error}
    `:g} - - ${t.success?c`
    ${t.success}
    `:g} - - ${a()} - - ${o("name","Username",{placeholder:"satoshi",maxLength:256,help:"Short username (e.g., satoshi)"})} - - ${o("displayName","Display Name",{placeholder:"Satoshi Nakamoto",maxLength:256,help:"Your full display name"})} - - ${o("about","Bio",{type:"textarea",placeholder:"Tell people about yourself...",maxLength:2e3,help:"A brief bio or description"})} - - ${o("picture","Avatar URL",{type:"url",placeholder:"https://example.com/avatar.jpg",help:"HTTPS URL to your profile picture"})} - - ${t.showAdvanced?c` -
    -
    Advanced
    - - ${o("banner","Banner URL",{type:"url",placeholder:"https://example.com/banner.jpg",help:"HTTPS URL to a banner image"})} - - ${o("website","Website",{type:"url",placeholder:"https://example.com",help:"Your personal website"})} - - ${o("nip05","NIP-05 Identifier",{placeholder:"you@example.com",help:"Verifiable identifier (e.g., you@domain.com)"})} - - ${o("lud16","Lightning Address",{placeholder:"you@getalby.com",help:"Lightning address for tips (LUD-16)"})} -
    - `:g} - -
    - - - - - - - -
    - - ${i?c`
    - You have unsaved changes -
    `:g} -
    - `}function ef(e){const t={name:e?.name??"",displayName:e?.displayName??"",about:e?.about??"",picture:e?.picture??"",banner:e?.banner??"",website:e?.website??"",nip05:e?.nip05??"",lud16:e?.lud16??""};return{values:t,original:{...t},saving:!1,importing:!1,error:null,success:null,fieldErrors:{},showAdvanced:!!(e?.banner||e?.website||e?.nip05||e?.lud16)}}function Ho(e){return e?e.length<=20?e:`${e.slice(0,8)}...${e.slice(-8)}`:"n/a"}function tf(e){const{props:t,nostr:n,nostrAccounts:s,accountCountLabel:i,profileFormState:o,profileFormCallbacks:a,onEditProfile:l}=e,r=s[0],p=n?.configured??r?.configured??!1,d=n?.running??r?.running??!1,u=n?.publicKey??r?.publicKey,h=n?.lastStartAt??r?.lastStartAt??null,v=n?.lastError??r?.lastError??null,w=s.length>1,$=o!=null,x=I=>{const R=I.publicKey,E=I.profile,A=E?.displayName??E?.name??I.name??I.accountId;return c` - - `},C=()=>{if($&&a)return Xp({state:o,callbacks:a,accountId:s[0]?.accountId??"default"});const I=r?.profile??n?.profile,{name:R,displayName:E,about:A,picture:B,nip05:ue}=I??{},bn=R||E||A||B||ue;return c` -
    -
    -
    Profile
    - ${p?c` - - `:g} -
    - ${bn?c` -
    - ${B?c` -
    - Profile picture{yn.target.style.display="none"}} - /> -
    - `:g} - ${R?c`
    Name${R}
    `:g} - ${E?c`
    Display Name${E}
    `:g} - ${A?c`
    About${A}
    `:g} - ${ue?c`
    NIP-05${ue}
    `:g} -
    - `:c` -
    - No profile set. Click "Edit Profile" to add your name, bio, and avatar. -
    - `} -
    - `};return c` -
    -
    Nostr
    -
    Decentralized DMs via Nostr relays (NIP-04).
    - ${i} - - ${w?c` - - `:c` -
    -
    - Configured - ${p?"Yes":"No"} -
    -
    - Running - ${d?"Yes":"No"} -
    -
    - Public Key - ${Ho(u)} -
    -
    - Last start - ${h?O(h):"n/a"} -
    -
    - `} - - ${v?c`
    ${v}
    `:g} - - ${C()} - - ${_e({channelId:"nostr",props:t})} - -
    - -
    -
    - `}function nf(e){const{props:t,signal:n,accountCountLabel:s}=e;return c` -
    -
    Signal
    -
    signal-cli status and channel configuration.
    - ${s} - -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Base URL - ${n?.baseUrl??"n/a"} -
    -
    - Last start - ${n?.lastStartAt?O(n.lastStartAt):"n/a"} -
    -
    - Last probe - ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} -
    -
    - - ${n?.lastError?c`
    - ${n.lastError} -
    `:g} - - ${n?.probe?c`
    - Probe ${n.probe.ok?"ok":"failed"} · - ${n.probe.status??""} ${n.probe.error??""} -
    `:g} - - ${_e({channelId:"signal",props:t})} - -
    - -
    -
    - `}function sf(e){const{props:t,slack:n,accountCountLabel:s}=e;return c` -
    -
    Slack
    -
    Socket mode status and channel configuration.
    - ${s} - -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Last start - ${n?.lastStartAt?O(n.lastStartAt):"n/a"} -
    -
    - Last probe - ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} -
    -
    - - ${n?.lastError?c`
    - ${n.lastError} -
    `:g} - - ${n?.probe?c`
    - Probe ${n.probe.ok?"ok":"failed"} · - ${n.probe.status??""} ${n.probe.error??""} -
    `:g} - - ${_e({channelId:"slack",props:t})} - -
    - -
    -
    - `}function of(e){const{props:t,telegram:n,telegramAccounts:s,accountCountLabel:i}=e,o=s.length>1,a=l=>{const p=l.probe?.bot?.username,d=l.name||l.accountId;return c` - - `};return c` -
    -
    Telegram
    -
    Bot status and channel configuration.
    - ${i} - - ${o?c` - - `:c` -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Mode - ${n?.mode??"n/a"} -
    -
    - Last start - ${n?.lastStartAt?O(n.lastStartAt):"n/a"} -
    -
    - Last probe - ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} -
    -
    - `} - - ${n?.lastError?c`
    - ${n.lastError} -
    `:g} - - ${n?.probe?c`
    - Probe ${n.probe.ok?"ok":"failed"} · - ${n.probe.status??""} ${n.probe.error??""} -
    `:g} - - ${_e({channelId:"telegram",props:t})} - -
    - -
    -
    - `}function af(e){const{props:t,whatsapp:n,accountCountLabel:s}=e;return c` -
    -
    WhatsApp
    -
    Link WhatsApp Web and monitor connection health.
    - ${s} - -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Linked - ${n?.linked?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Connected - ${n?.connected?"Yes":"No"} -
    -
    - Last connect - - ${n?.lastConnectedAt?O(n.lastConnectedAt):"n/a"} - -
    -
    - Last message - - ${n?.lastMessageAt?O(n.lastMessageAt):"n/a"} - -
    -
    - Auth age - - ${n?.authAgeMs!=null?jp(n.authAgeMs):"n/a"} - -
    -
    - - ${n?.lastError?c`
    - ${n.lastError} -
    `:g} - - ${t.whatsappMessage?c`
    - ${t.whatsappMessage} -
    `:g} - - ${t.whatsappQrDataUrl?c`
    - WhatsApp QR -
    `:g} - -
    - - - - - -
    - - ${_e({channelId:"whatsapp",props:t})} -
    - `}function rf(e){const t=e.snapshot?.channels,n=t?.whatsapp??void 0,s=t?.telegram??void 0,i=t?.discord??null,o=t?.slack??null,a=t?.signal??null,l=t?.imessage??null,r=t?.nostr??null,d=lf(e.snapshot).map((u,h)=>({key:u,enabled:qp(u,e),order:h})).sort((u,h)=>u.enabled!==h.enabled?u.enabled?-1:1:u.order-h.order);return c` -
    - ${d.map(u=>cf(u.key,e,{whatsapp:n,telegram:s,discord:i,slack:o,signal:a,imessage:l,nostr:r,channelAccounts:e.snapshot?.channelAccounts??null}))} -
    - -
    -
    -
    -
    Channel health
    -
    Channel status snapshots from the gateway.
    -
    -
    ${e.lastSuccessAt?O(e.lastSuccessAt):"n/a"}
    -
    - ${e.lastError?c`
    - ${e.lastError} -
    `:g} -
    -${e.snapshot?JSON.stringify(e.snapshot,null,2):"No snapshot yet."}
    -      
    -
    - `}function lf(e){return e?.channelMeta?.length?e.channelMeta.map(t=>t.id):e?.channelOrder?.length?e.channelOrder:["whatsapp","telegram","discord","slack","signal","imessage","nostr"]}function cf(e,t,n){const s=gr(e,n.channelAccounts);switch(e){case"whatsapp":return af({props:t,whatsapp:n.whatsapp,accountCountLabel:s});case"telegram":return of({props:t,telegram:n.telegram,telegramAccounts:n.channelAccounts?.telegram??[],accountCountLabel:s});case"discord":return Qp({props:t,discord:n.discord,accountCountLabel:s});case"slack":return sf({props:t,slack:n.slack,accountCountLabel:s});case"signal":return nf({props:t,signal:n.signal,accountCountLabel:s});case"imessage":return Jp({props:t,imessage:n.imessage,accountCountLabel:s});case"nostr":{const i=n.channelAccounts?.nostr??[],o=i[0],a=o?.accountId??"default",l=o?.profile??null,r=t.nostrProfileAccountId===a?t.nostrProfileFormState:null,p=r?{onFieldChange:t.onNostrProfileFieldChange,onSave:t.onNostrProfileSave,onImport:t.onNostrProfileImport,onCancel:t.onNostrProfileCancel,onToggleAdvanced:t.onNostrProfileToggleAdvanced}:null;return tf({props:t,nostr:n.nostr,nostrAccounts:i,accountCountLabel:s,profileFormState:r,profileFormCallbacks:p,onEditProfile:()=>t.onNostrProfileEdit(a,l)})}default:return df(e,t,n.channelAccounts??{})}}function df(e,t,n){const s=pf(t.snapshot,e),i=t.snapshot?.channels?.[e],o=typeof i?.configured=="boolean"?i.configured:void 0,a=typeof i?.running=="boolean"?i.running:void 0,l=typeof i?.connected=="boolean"?i.connected:void 0,r=typeof i?.lastError=="string"?i.lastError:void 0,p=n[e]??[],d=gr(e,n);return c` -
    -
    ${s}
    -
    Channel status and configuration.
    - ${d} - - ${p.length>0?c` - - `:c` -
    -
    - Configured - ${o==null?"n/a":o?"Yes":"No"} -
    -
    - Running - ${a==null?"n/a":a?"Yes":"No"} -
    -
    - Connected - ${l==null?"n/a":l?"Yes":"No"} -
    -
    - `} - - ${r?c`
    - ${r} -
    `:g} - - ${_e({channelId:e,props:t})} -
    - `}function uf(e){return e?.channelMeta?.length?Object.fromEntries(e.channelMeta.map(t=>[t.id,t])):{}}function pf(e,t){return uf(e)[t]?.label??e?.channelLabels?.[t]??t}const ff=600*1e3;function vr(e){return e.lastInboundAt?Date.now()-e.lastInboundAt
    - `:c` -
    - Auth failed. Re-copy a tokenized URL with - clawdbot dashboard --no-open, or update the token, - then click Connect. - -
    - `})(),o=(()=>{if(e.connected||!e.lastError||(typeof window<"u"?window.isSecureContext:!0)!==!1)return null;const l=e.lastError.toLowerCase();return!l.includes("secure context")&&!l.includes("device identity required")?null:c` -
    - This page is HTTP, so the browser blocks device identity. Use HTTPS (Tailscale Serve) or - open http://127.0.0.1:18789 on the gateway host. -
    - If you must stay on HTTP, set - gateway.controlUi.allowInsecureAuth: true (token-only). -
    - -
    - `})();return c` -
    -
    -
    Gateway Access
    -
    Where the dashboard connects and how it authenticates.
    -
    - - - - -
    -
    - - - Click Connect to apply connection changes. -
    -
    - -
    -
    Snapshot
    -
    Latest gateway handshake information.
    -
    -
    -
    Status
    -
    - ${e.connected?"Connected":"Disconnected"} -
    -
    -
    -
    Uptime
    -
    ${n}
    -
    -
    -
    Tick Interval
    -
    ${s}
    -
    -
    -
    Last Channels Refresh
    -
    - ${e.lastChannelsRefresh?O(e.lastChannelsRefresh):"n/a"} -
    -
    -
    - ${e.lastError?c`
    -
    ${e.lastError}
    - ${i??""} - ${o??""} -
    `:c`
    - Use Channels to link WhatsApp, Telegram, Discord, Signal, or iMessage. -
    `} -
    -
    - -
    -
    -
    Instances
    -
    ${e.presenceCount}
    -
    Presence beacons in the last 5 minutes.
    -
    -
    -
    Sessions
    -
    ${e.sessionsCount??"n/a"}
    -
    Recent session keys tracked by the gateway.
    -
    -
    -
    Cron
    -
    - ${e.cronEnabled==null?"n/a":e.cronEnabled?"Enabled":"Disabled"} -
    -
    Next wake ${mr(e.cronNext)}
    -
    -
    - -
    -
    Notes
    -
    Quick reminders for remote control setups.
    -
    -
    -
    Tailscale serve
    -
    - Prefer serve mode to keep the gateway on loopback with tailnet auth. -
    -
    -
    -
    Session hygiene
    -
    Use /new or sessions.patch to reset context.
    -
    -
    -
    Cron reminders
    -
    Use isolated sessions for recurring runs.
    -
    -
    -
    - `}const lh=["","off","minimal","low","medium","high"],ch=["","off","on"],dh=[{value:"",label:"inherit"},{value:"off",label:"off (explicit)"},{value:"on",label:"on"}],uh=["","off","on","stream"];function ph(e){if(!e)return"";const t=e.trim().toLowerCase();return t==="z.ai"||t==="z-ai"?"zai":t}function br(e){return ph(e)==="zai"}function fh(e){return br(e)?ch:lh}function hh(e,t){return!t||!e||e==="off"?e:"on"}function gh(e,t){return e?t&&e==="on"?"low":e:null}function vh(e){const t=e.result?.sessions??[];return c` -
    -
    -
    -
    Sessions
    -
    Active session keys and per-session overrides.
    -
    - -
    - -
    - - - - -
    - - ${e.error?c`
    ${e.error}
    `:g} - -
    - ${e.result?`Store: ${e.result.path}`:""} -
    - -
    -
    -
    Key
    -
    Label
    -
    Kind
    -
    Updated
    -
    Tokens
    -
    Thinking
    -
    Verbose
    -
    Reasoning
    -
    Actions
    -
    - ${t.length===0?c`
    No sessions found.
    `:t.map(n=>mh(n,e.basePath,e.onPatch,e.onDelete,e.loading))} -
    -
    - `}function mh(e,t,n,s,i){const o=e.updatedAt?O(e.updatedAt):"n/a",a=e.thinkingLevel??"",l=br(e.modelProvider),r=hh(a,l),p=fh(e.modelProvider),d=e.verboseLevel??"",u=e.reasoningLevel??"",h=e.displayName??e.key,v=e.kind!=="global",w=v?`${Ps("chat",t)}?session=${encodeURIComponent(e.key)}`:null;return c` -
    -
    ${v?c`${h}`:h}
    -
    - {const x=$.target.value.trim();n(e.key,{label:x||null})}} - /> -
    -
    ${e.kind}
    -
    ${o}
    -
    ${yf(e)}
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - `}function bh(e){const t=Math.max(0,e),n=Math.floor(t/1e3);if(n<60)return`${n}s`;const s=Math.floor(n/60);return s<60?`${s}m`:`${Math.floor(s/60)}h`}function Le(e,t){return t?c`
    ${e}${t}
    `:g}function yh(e){const t=e.execApprovalQueue[0];if(!t)return g;const n=t.request,s=t.expiresAtMs-Date.now(),i=s>0?`expires in ${bh(s)}`:"expired",o=e.execApprovalQueue.length;return c` - - `}function wh(e){const t=e.report?.skills??[],n=e.filter.trim().toLowerCase(),s=n?t.filter(i=>[i.name,i.description,i.source].join(" ").toLowerCase().includes(n)):t;return c` -
    -
    -
    -
    Skills
    -
    Bundled, managed, and workspace skills.
    -
    - -
    - -
    - -
    ${s.length} shown
    -
    - - ${e.error?c`
    ${e.error}
    `:g} - - ${s.length===0?c`
    No skills found.
    `:c` -
    - ${s.map(i=>$h(i,e))} -
    - `} -
    - `}function $h(e,t){const n=t.busyKey===e.skillKey,s=t.edits[e.skillKey]??"",i=t.messages[e.skillKey]??null,o=e.install.length>0&&e.missing.bins.length>0,a=[...e.missing.bins.map(r=>`bin:${r}`),...e.missing.env.map(r=>`env:${r}`),...e.missing.config.map(r=>`config:${r}`),...e.missing.os.map(r=>`os:${r}`)],l=[];return e.disabled&&l.push("disabled"),e.blockedByAllowlist&&l.push("blocked by allowlist"),c` -
    -
    -
    - ${e.emoji?`${e.emoji} `:""}${e.name} -
    -
    ${as(e.description,140)}
    -
    - ${e.source} - - ${e.eligible?"eligible":"blocked"} - - ${e.disabled?c`disabled`:g} -
    - ${a.length>0?c` -
    - Missing: ${a.join(", ")} -
    - `:g} - ${l.length>0?c` -
    - Reason: ${l.join(", ")} -
    - `:g} -
    -
    -
    - - ${o?c``:g} -
    - ${i?c`
    - ${i.message} -
    `:g} - ${e.primaryEnv?c` -
    - API key - t.onEdit(e.skillKey,r.target.value)} - /> -
    - - `:g} -
    -
    - `}function kh(e,t){const n=Ps(t,e.basePath);return c` - {s.defaultPrevented||s.button!==0||s.metaKey||s.ctrlKey||s.shiftKey||s.altKey||(s.preventDefault(),e.setTab(t))}} - title=${is(t)} - > - - ${is(t)} - - `}function xh(e){const t=Ah(e.sessionKey,e.sessionsResult),n=e.onboarding,s=e.onboarding,i=e.onboarding?!1:e.settings.chatShowThinking,o=e.onboarding?!0:e.settings.chatFocusMode,a=c``,l=c``;return c` -
    - - - | - - -
    - `}function Ah(e,t){const n=new Set,s=[],i=t?.sessions?.find(o=>o.key===e);if(n.add(e),s.push({key:e,displayName:i?.displayName}),t?.sessions)for(const o of t.sessions)n.has(o.key)||(n.add(o.key),s.push({key:o.key,displayName:o.displayName}));return s}const Sh=["system","light","dark"];function _h(e){const t=Math.max(0,Sh.indexOf(e.theme)),n=s=>i=>{const a={element:i.currentTarget};(i.clientX||i.clientY)&&(a.pointerClientX=i.clientX,a.pointerClientY=i.clientY),e.setTheme(s,a)};return c` -
    -
    - - - - -
    -
    - `}function Th(){return c` - - `}function Ch(){return c` - - `}function Eh(){return c` - - `}const Ih=/^data:/i,Lh=/^https?:\/\//i;function Rh(e){const t=e.agentsList?.agents??[],s=sa(e.sessionKey)?.agentId??e.agentsList?.defaultId??"main",o=t.find(l=>l.id===s)?.identity,a=o?.avatarUrl??o?.avatar;if(a)return Ih.test(a)||Lh.test(a)?a:o?.avatarUrl}function Mh(e){const t=e.presenceEntries.length,n=e.sessionsResult?.count??null,s=e.cronStatus?.nextWakeAtMs??null,i=e.connected?null:"Disconnected from gateway.",o=e.tab==="chat",a=o&&(e.settings.chatFocusMode||e.onboarding),l=e.onboarding?!1:e.settings.chatShowThinking,r=Rh(e),p=e.chatAvatarUrl??r??null;return c` -
    -
    -
    - -
    -
    CLAWDBOT
    -
    Gateway Dashboard
    -
    -
    -
    -
    - - Health - ${e.connected?"OK":"Offline"} -
    - ${_h(e)} -
    -
    - -
    -
    -
    -
    ${is(e.tab)}
    -
    ${yl(e.tab)}
    -
    -
    - ${e.lastError?c`
    ${e.lastError}
    `:g} - ${o?xh(e):g} -
    -
    - - ${e.tab==="overview"?rh({connected:e.connected,hello:e.hello,settings:e.settings,password:e.password,lastError:e.lastError,presenceCount:t,sessionsCount:n,cronEnabled:e.cronStatus?.enabled??null,cronNext:s,lastChannelsRefresh:e.channelsLastSuccess,onSettingsChange:d=>e.applySettings(d),onPasswordChange:d=>e.password=d,onSessionKeyChange:d=>{e.sessionKey=d,e.chatMessage="",e.resetToolStream(),e.applySettings({...e.settings,sessionKey:d,lastActiveSessionKey:d}),e.loadAssistantIdentity()},onConnect:()=>e.connect(),onRefresh:()=>e.loadOverview()}):g} - - ${e.tab==="channels"?rf({connected:e.connected,loading:e.channelsLoading,snapshot:e.channelsSnapshot,lastError:e.channelsError,lastSuccessAt:e.channelsLastSuccess,whatsappMessage:e.whatsappLoginMessage,whatsappQrDataUrl:e.whatsappLoginQrDataUrl,whatsappConnected:e.whatsappLoginConnected,whatsappBusy:e.whatsappBusy,configSchema:e.configSchema,configSchemaLoading:e.configSchemaLoading,configForm:e.configForm,configUiHints:e.configUiHints,configSaving:e.configSaving,configFormDirty:e.configFormDirty,nostrProfileFormState:e.nostrProfileFormState,nostrProfileAccountId:e.nostrProfileAccountId,onRefresh:d=>oe(e,d),onWhatsAppStart:d=>e.handleWhatsAppStart(d),onWhatsAppWait:()=>e.handleWhatsAppWait(),onWhatsAppLogout:()=>e.handleWhatsAppLogout(),onConfigPatch:(d,u)=>Ot(e,d,u),onConfigSave:()=>e.handleChannelConfigSave(),onConfigReload:()=>e.handleChannelConfigReload(),onNostrProfileEdit:(d,u)=>e.handleNostrProfileEdit(d,u),onNostrProfileCancel:()=>e.handleNostrProfileCancel(),onNostrProfileFieldChange:(d,u)=>e.handleNostrProfileFieldChange(d,u),onNostrProfileSave:()=>e.handleNostrProfileSave(),onNostrProfileImport:()=>e.handleNostrProfileImport(),onNostrProfileToggleAdvanced:()=>e.handleNostrProfileToggleAdvanced()}):g} - - ${e.tab==="instances"?Lf({loading:e.presenceLoading,entries:e.presenceEntries,lastError:e.presenceError,statusMessage:e.presenceStatus,onRefresh:()=>qs(e)}):g} - - ${e.tab==="sessions"?vh({loading:e.sessionsLoading,result:e.sessionsResult,error:e.sessionsError,activeMinutes:e.sessionsFilterActive,limit:e.sessionsFilterLimit,includeGlobal:e.sessionsIncludeGlobal,includeUnknown:e.sessionsIncludeUnknown,basePath:e.basePath,onFiltersChange:d=>{e.sessionsFilterActive=d.activeMinutes,e.sessionsFilterLimit=d.limit,e.sessionsIncludeGlobal=d.includeGlobal,e.sessionsIncludeUnknown=d.includeUnknown},onRefresh:()=>nt(e),onPatch:(d,u)=>Il(e,d,u),onDelete:d=>Ll(e,d)}):g} - - ${e.tab==="cron"?_f({loading:e.cronLoading,status:e.cronStatus,jobs:e.cronJobs,error:e.cronError,busy:e.cronBusy,form:e.cronForm,channels:e.channelsSnapshot?.channelMeta?.length?e.channelsSnapshot.channelMeta.map(d=>d.id):e.channelsSnapshot?.channelOrder??[],channelLabels:e.channelsSnapshot?.channelLabels??{},channelMeta:e.channelsSnapshot?.channelMeta??[],runsJobId:e.cronRunsJobId,runs:e.cronRuns,onFormChange:d=>e.cronForm={...e.cronForm,...d},onRefresh:()=>e.loadCron(),onAdd:()=>Xl(e),onToggle:(d,u)=>ec(e,d,u),onRun:d=>tc(e,d),onRemove:d=>nc(e,d),onLoadRuns:d=>ha(e,d)}):g} - - ${e.tab==="skills"?wh({loading:e.skillsLoading,report:e.skillsReport,error:e.skillsError,filter:e.skillsFilter,edits:e.skillEdits,messages:e.skillMessages,busyKey:e.skillsBusyKey,onFilterChange:d=>e.skillsFilter=d,onRefresh:()=>Tt(e,{clearMessages:!0}),onToggle:(d,u)=>Qc(e,d,u),onEdit:(d,u)=>Yc(e,d,u),onSaveKey:d=>Jc(e,d),onInstall:(d,u,h)=>Zc(e,d,u,h)}):g} - - ${e.tab==="nodes"?Of({loading:e.nodesLoading,nodes:e.nodes,devicesLoading:e.devicesLoading,devicesError:e.devicesError,devicesList:e.devicesList,configForm:e.configForm??e.configSnapshot?.config,configLoading:e.configLoading,configSaving:e.configSaving,configDirty:e.configFormDirty,configFormMode:e.configFormMode,execApprovalsLoading:e.execApprovalsLoading,execApprovalsSaving:e.execApprovalsSaving,execApprovalsDirty:e.execApprovalsDirty,execApprovalsSnapshot:e.execApprovalsSnapshot,execApprovalsForm:e.execApprovalsForm,execApprovalsSelectedAgent:e.execApprovalsSelectedAgent,execApprovalsTarget:e.execApprovalsTarget,execApprovalsTargetNodeId:e.execApprovalsTargetNodeId,onRefresh:()=>un(e),onDevicesRefresh:()=>Se(e),onDeviceApprove:d=>Fc(e,d),onDeviceReject:d=>Uc(e,d),onDeviceRotate:(d,u,h)=>Kc(e,{deviceId:d,role:u,scopes:h}),onDeviceRevoke:(d,u)=>Hc(e,{deviceId:d,role:u}),onLoadConfig:()=>me(e),onLoadExecApprovals:()=>{const d=e.execApprovalsTarget==="node"&&e.execApprovalsTargetNodeId?{kind:"node",nodeId:e.execApprovalsTargetNodeId}:{kind:"gateway"};return js(e,d)},onBindDefault:d=>{d?Ot(e,["tools","exec","node"],d):Xi(e,["tools","exec","node"])},onBindAgent:(d,u)=>{const h=["agents","list",d,"tools","exec","node"];u?Ot(e,h,u):Xi(e,h)},onSaveBindings:()=>cs(e),onExecApprovalsTargetChange:(d,u)=>{e.execApprovalsTarget=d,e.execApprovalsTargetNodeId=u,e.execApprovalsSnapshot=null,e.execApprovalsForm=null,e.execApprovalsDirty=!1,e.execApprovalsSelectedAgent=null},onExecApprovalsSelectAgent:d=>{e.execApprovalsSelectedAgent=d},onExecApprovalsPatch:(d,u)=>Vc(e,d,u),onExecApprovalsRemove:d=>Gc(e,d),onSaveExecApprovals:()=>{const d=e.execApprovalsTarget==="node"&&e.execApprovalsTargetNodeId?{kind:"node",nodeId:e.execApprovalsTargetNodeId}:{kind:"gateway"};return Wc(e,d)}}):g} - - ${e.tab==="chat"?Sp({sessionKey:e.sessionKey,onSessionKeyChange:d=>{e.sessionKey=d,e.chatMessage="",e.chatStream=null,e.chatStreamStartedAt=null,e.chatRunId=null,e.chatQueue=[],e.resetToolStream(),e.resetChatScroll(),e.applySettings({...e.settings,sessionKey:d,lastActiveSessionKey:d}),e.loadAssistantIdentity(),Ze(e),hs(e)},thinkingLevel:e.chatThinkingLevel,showThinking:l,loading:e.chatLoading,sending:e.chatSending,compactionStatus:e.compactionStatus,assistantAvatarUrl:p,messages:e.chatMessages,toolMessages:e.chatToolMessages,stream:e.chatStream,streamStartedAt:e.chatStreamStartedAt,draft:e.chatMessage,queue:e.chatQueue,connected:e.connected,canSend:e.connected,disabledReason:i,error:e.lastError,sessions:e.sessionsResult,focusMode:a,onRefresh:()=>(e.resetToolStream(),Promise.all([Ze(e),hs(e)])),onToggleFocusMode:()=>{e.onboarding||e.applySettings({...e.settings,chatFocusMode:!e.settings.chatFocusMode})},onChatScroll:d=>e.handleChatScroll(d),onDraftChange:d=>e.chatMessage=d,onSend:()=>e.handleSendChat(),canAbort:!!e.chatRunId,onAbort:()=>{e.handleAbortChat()},onQueueRemove:d=>e.removeQueuedMessage(d),onNewSession:()=>e.handleSendChat("/new",{restoreDraft:!0}),sidebarOpen:e.sidebarOpen,sidebarContent:e.sidebarContent,sidebarError:e.sidebarError,splitRatio:e.splitRatio,onOpenSidebar:d=>e.handleOpenSidebar(d),onCloseSidebar:()=>e.handleCloseSidebar(),onSplitRatioChange:d=>e.handleSplitRatioChange(d),assistantName:e.assistantName,assistantAvatar:e.assistantAvatar}):g} - - ${e.tab==="config"?zp({raw:e.configRaw,valid:e.configValid,issues:e.configIssues,loading:e.configLoading,saving:e.configSaving,applying:e.configApplying,updating:e.updateRunning,connected:e.connected,schema:e.configSchema,schemaLoading:e.configSchemaLoading,uiHints:e.configUiHints,formMode:e.configFormMode,formValue:e.configForm,originalValue:e.configFormOriginal,searchQuery:e.configSearchQuery,activeSection:e.configActiveSection,activeSubsection:e.configActiveSubsection,onRawChange:d=>e.configRaw=d,onFormModeChange:d=>e.configFormMode=d,onFormPatch:(d,u)=>Ot(e,d,u),onSearchChange:d=>e.configSearchQuery=d,onSectionChange:d=>{e.configActiveSection=d,e.configActiveSubsection=null},onSubsectionChange:d=>e.configActiveSubsection=d,onReload:()=>me(e),onSave:()=>cs(e),onApply:()=>Yl(e),onUpdate:()=>Ql(e)}):g} - - ${e.tab==="debug"?If({loading:e.debugLoading,status:e.debugStatus,health:e.debugHealth,models:e.debugModels,heartbeat:e.debugHeartbeat,eventLog:e.eventLog,callMethod:e.debugCallMethod,callParams:e.debugCallParams,callResult:e.debugCallResult,callError:e.debugCallError,onCallMethodChange:d=>e.debugCallMethod=d,onCallParamsChange:d=>e.debugCallParams=d,onRefresh:()=>cn(e),onCall:()=>ac(e)}):g} - - ${e.tab==="logs"?Nf({loading:e.logsLoading,error:e.logsError,file:e.logsFile,entries:e.logsEntries,filterText:e.logsFilterText,levelFilters:e.logsLevelFilters,autoFollow:e.logsAutoFollow,truncated:e.logsTruncated,onFilterTextChange:d=>e.logsFilterText=d,onLevelToggle:(d,u)=>{e.logsLevelFilters={...e.logsLevelFilters,[d]:u}},onToggleAutoFollow:d=>e.logsAutoFollow=d,onRefresh:()=>Ds(e,{reset:!0}),onExport:(d,u)=>e.exportLogs(d,u),onScroll:d=>e.handleLogsScroll(d)}):g} -
    - ${yh(e)} -
    - `}const Ph={trace:!0,debug:!0,info:!0,warn:!0,error:!0,fatal:!0},Nh={name:"",description:"",agentId:"",enabled:!0,scheduleKind:"every",scheduleAt:"",everyAmount:"30",everyUnit:"minutes",cronExpr:"0 7 * * *",cronTz:"",sessionTarget:"main",wakeMode:"next-heartbeat",payloadKind:"systemEvent",payloadText:"",deliver:!1,channel:"last",to:"",timeoutSeconds:"",postToMainPrefix:""};async function Oh(e){if(!(!e.client||!e.connected)&&!e.agentsLoading){e.agentsLoading=!0,e.agentsError=null;try{const t=await e.client.request("agents.list",{});t&&(e.agentsList=t)}catch(t){e.agentsError=String(t)}finally{e.agentsLoading=!1}}}const yr={WEBCHAT_UI:"webchat-ui",CONTROL_UI:"clawdbot-control-ui",WEBCHAT:"webchat",CLI:"cli",GATEWAY_CLIENT:"gateway-client",MACOS_APP:"clawdbot-macos",IOS_APP:"clawdbot-ios",ANDROID_APP:"clawdbot-android",NODE_HOST:"node-host",TEST:"test",FINGERPRINT:"fingerprint",PROBE:"clawdbot-probe"},Wo=yr,_s={WEBCHAT:"webchat",CLI:"cli",UI:"ui",BACKEND:"backend",NODE:"node",PROBE:"probe",TEST:"test"};new Set(Object.values(yr));new Set(Object.values(_s));function Dh(e){const t=e.version??(e.nonce?"v2":"v1"),n=e.scopes.join(","),s=e.token??"",i=[t,e.deviceId,e.clientId,e.clientMode,e.role,n,String(e.signedAtMs),s];return t==="v2"&&i.push(e.nonce??""),i.join("|")}const Bh=4008;class Fh{constructor(t){this.opts=t,this.ws=null,this.pending=new Map,this.closed=!1,this.lastSeq=null,this.connectNonce=null,this.connectSent=!1,this.connectTimer=null,this.backoffMs=800}start(){this.closed=!1,this.connect()}stop(){this.closed=!0,this.ws?.close(),this.ws=null,this.flushPending(new Error("gateway client stopped"))}get connected(){return this.ws?.readyState===WebSocket.OPEN}connect(){this.closed||(this.ws=new WebSocket(this.opts.url),this.ws.onopen=()=>this.queueConnect(),this.ws.onmessage=t=>this.handleMessage(String(t.data??"")),this.ws.onclose=t=>{const n=String(t.reason??"");this.ws=null,this.flushPending(new Error(`gateway closed (${t.code}): ${n}`)),this.opts.onClose?.({code:t.code,reason:n}),this.scheduleReconnect()},this.ws.onerror=()=>{})}scheduleReconnect(){if(this.closed)return;const t=this.backoffMs;this.backoffMs=Math.min(this.backoffMs*1.7,15e3),window.setTimeout(()=>this.connect(),t)}flushPending(t){for(const[,n]of this.pending)n.reject(t);this.pending.clear()}async sendConnect(){if(this.connectSent)return;this.connectSent=!0,this.connectTimer!==null&&(window.clearTimeout(this.connectTimer),this.connectTimer=null);const t=typeof crypto<"u"&&!!crypto.subtle,n=["operator.admin","operator.approvals","operator.pairing"],s="operator";let i=null,o=!1,a=this.opts.token;if(t){i=await Ks();const d=Bc({deviceId:i.deviceId,role:s})?.token;a=d??this.opts.token,o=!!(d&&this.opts.token)}const l=a||this.opts.password?{token:a,password:this.opts.password}:void 0;let r;if(t&&i){const d=Date.now(),u=this.connectNonce??void 0,h=Dh({deviceId:i.deviceId,clientId:this.opts.clientName??Wo.CONTROL_UI,clientMode:this.opts.mode??_s.WEBCHAT,role:s,scopes:n,signedAtMs:d,token:a??null,nonce:u}),v=await Oc(i.privateKey,h);r={id:i.deviceId,publicKey:i.publicKey,signature:v,signedAt:d,nonce:u}}const p={minProtocol:3,maxProtocol:3,client:{id:this.opts.clientName??Wo.CONTROL_UI,version:this.opts.clientVersion??"dev",platform:this.opts.platform??navigator.platform??"web",mode:this.opts.mode??_s.WEBCHAT,instanceId:this.opts.instanceId},role:s,scopes:n,device:r,caps:[],auth:l,userAgent:navigator.userAgent,locale:navigator.language};this.request("connect",p).then(d=>{d?.auth?.deviceToken&&i&&La({deviceId:i.deviceId,role:d.auth.role??s,token:d.auth.deviceToken,scopes:d.auth.scopes??[]}),this.backoffMs=800,this.opts.onHello?.(d)}).catch(()=>{o&&i&&Ra({deviceId:i.deviceId,role:s}),this.ws?.close(Bh,"connect failed")})}handleMessage(t){let n;try{n=JSON.parse(t)}catch{return}const s=n;if(s.type==="event"){const i=n;if(i.event==="connect.challenge"){const a=i.payload,l=a&&typeof a.nonce=="string"?a.nonce:null;l&&(this.connectNonce=l,this.sendConnect());return}const o=typeof i.seq=="number"?i.seq:null;o!==null&&(this.lastSeq!==null&&o>this.lastSeq+1&&this.opts.onGap?.({expected:this.lastSeq+1,received:o}),this.lastSeq=o);try{this.opts.onEvent?.(i)}catch(a){console.error("[gateway] event handler error:",a)}return}if(s.type==="res"){const i=n,o=this.pending.get(i.id);if(!o)return;this.pending.delete(i.id),i.ok?o.resolve(i.payload):o.reject(new Error(i.error?.message??"request failed"));return}}request(t,n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return Promise.reject(new Error("gateway not connected"));const s=Ns(),i={type:"req",id:s,method:t,params:n},o=new Promise((a,l)=>{this.pending.set(s,{resolve:r=>a(r),reject:l})});return this.ws.send(JSON.stringify(i)),o}queueConnect(){this.connectNonce=null,this.connectSent=!1,this.connectTimer!==null&&window.clearTimeout(this.connectTimer),this.connectTimer=window.setTimeout(()=>{this.sendConnect()},750)}}function Ts(e){return typeof e=="object"&&e!==null}function Uh(e){if(!Ts(e))return null;const t=typeof e.id=="string"?e.id.trim():"",n=e.request;if(!t||!Ts(n))return null;const s=typeof n.command=="string"?n.command.trim():"";if(!s)return null;const i=typeof e.createdAtMs=="number"?e.createdAtMs:0,o=typeof e.expiresAtMs=="number"?e.expiresAtMs:0;return!i||!o?null:{id:t,request:{command:s,cwd:typeof n.cwd=="string"?n.cwd:null,host:typeof n.host=="string"?n.host:null,security:typeof n.security=="string"?n.security:null,ask:typeof n.ask=="string"?n.ask:null,agentId:typeof n.agentId=="string"?n.agentId:null,resolvedPath:typeof n.resolvedPath=="string"?n.resolvedPath:null,sessionKey:typeof n.sessionKey=="string"?n.sessionKey:null},createdAtMs:i,expiresAtMs:o}}function Kh(e){if(!Ts(e))return null;const t=typeof e.id=="string"?e.id.trim():"";return t?{id:t,decision:typeof e.decision=="string"?e.decision:null,resolvedBy:typeof e.resolvedBy=="string"?e.resolvedBy:null,ts:typeof e.ts=="number"?e.ts:null}:null}function wr(e){const t=Date.now();return e.filter(n=>n.expiresAtMs>t)}function Hh(e,t){const n=wr(e).filter(s=>s.id!==t.id);return n.push(t),n}function Vo(e,t){return wr(e).filter(n=>n.id!==t)}async function $r(e,t){if(!e.client||!e.connected)return;const n=e.sessionKey.trim(),s=n?{sessionKey:n}:{};try{const i=await e.client.request("agent.identity.get",s);if(!i)return;const o=ss(i);e.assistantName=o.name,e.assistantAvatar=o.avatar,e.assistantAgentId=o.agentId??null}catch{}}function es(e,t){const n=(e??"").trim(),s=t.mainSessionKey?.trim();if(!s)return n;if(!n)return s;const i=t.mainKey?.trim()||"main",o=t.defaultAgentId?.trim();return n==="main"||n===i||o&&(n===`agent:${o}:main`||n===`agent:${o}:${i}`)?s:n}function zh(e,t){if(!t?.mainSessionKey)return;const n=es(e.sessionKey,t),s=es(e.settings.sessionKey,t),i=es(e.settings.lastActiveSessionKey,t),o=n||s||e.sessionKey,a={...e.settings,sessionKey:s||o,lastActiveSessionKey:i||o},l=a.sessionKey!==e.settings.sessionKey||a.lastActiveSessionKey!==e.settings.lastActiveSessionKey;o!==e.sessionKey&&(e.sessionKey=o),l&&$e(e,a)}function kr(e){e.lastError=null,e.hello=null,e.connected=!1,e.execApprovalQueue=[],e.execApprovalError=null,e.client?.stop(),e.client=new Fh({url:e.settings.gatewayUrl,token:e.settings.token.trim()?e.settings.token:void 0,password:e.password.trim()?e.password:void 0,clientName:"clawdbot-control-ui",mode:"webchat",onHello:t=>{e.connected=!0,e.hello=t,Wh(e,t),$r(e),Oh(e),un(e,{quiet:!0}),Se(e,{quiet:!0}),Js(e)},onClose:({code:t,reason:n})=>{e.connected=!1,e.lastError=`disconnected (${t}): ${n||"no reason"}`},onEvent:t=>jh(e,t),onGap:({expected:t,received:n})=>{e.lastError=`event gap detected (expected seq ${t}, got ${n}); refresh recommended`}}),e.client.start()}function jh(e,t){try{qh(e,t)}catch(n){console.error("[gateway] handleGatewayEvent error:",t.event,n)}}function qh(e,t){if(e.eventLogBuffer=[{ts:Date.now(),event:t.event,payload:t.payload},...e.eventLogBuffer].slice(0,250),e.tab==="debug"&&(e.eventLog=e.eventLogBuffer),t.event==="agent"){if(e.onboarding)return;Kl(e,t.payload);return}if(t.event==="chat"){const n=t.payload;n?.sessionKey&&Ma(e,n.sessionKey);const s=El(e,n);(s==="final"||s==="error"||s==="aborted")&&(Os(e),wd(e)),s==="final"&&Ze(e);return}if(t.event==="presence"){const n=t.payload;n?.presence&&Array.isArray(n.presence)&&(e.presenceEntries=n.presence,e.presenceError=null,e.presenceStatus=null);return}if(t.event==="cron"&&e.tab==="cron"&&Zs(e),(t.event==="device.pair.requested"||t.event==="device.pair.resolved")&&Se(e,{quiet:!0}),t.event==="exec.approval.requested"){const n=Uh(t.payload);if(n){e.execApprovalQueue=Hh(e.execApprovalQueue,n),e.execApprovalError=null;const s=Math.max(0,n.expiresAtMs-Date.now()+500);window.setTimeout(()=>{e.execApprovalQueue=Vo(e.execApprovalQueue,n.id)},s)}return}if(t.event==="exec.approval.resolved"){const n=Kh(t.payload);n&&(e.execApprovalQueue=Vo(e.execApprovalQueue,n.id))}}function Wh(e,t){const n=t.snapshot;n?.presence&&Array.isArray(n.presence)&&(e.presenceEntries=n.presence),n?.health&&(e.debugHealth=n.health),n?.sessionDefaults&&zh(e,n.sessionDefaults)}function Vh(e){e.basePath=rd(),ud(e,!0),ld(e),cd(e),window.addEventListener("popstate",e.popStateHandler),id(e),kr(e),nd(e),e.tab==="logs"&&Vs(e),e.tab==="debug"&&Ys(e)}function Gh(e){Wl(e)}function Yh(e){window.removeEventListener("popstate",e.popStateHandler),sd(e),Gs(e),Qs(e),dd(e),e.topbarObserver?.disconnect(),e.topbarObserver=null}function Qh(e,t){if(e.tab==="chat"&&(t.has("chatMessages")||t.has("chatToolMessages")||t.has("chatStream")||t.has("chatLoading")||t.has("tab"))){const n=t.has("tab"),s=t.has("chatLoading")&&t.get("chatLoading")===!0&&e.chatLoading===!1;rn(e,n||s||!e.chatHasAutoScrolled)}e.tab==="logs"&&(t.has("logsEntries")||t.has("logsAutoFollow")||t.has("tab"))&&e.logsAutoFollow&&e.logsAtBottom&&da(e,t.has("tab")||t.has("logsAutoFollow"))}async function Jh(e,t){await sc(e,t),await oe(e,!0)}async function Zh(e){await ic(e),await oe(e,!0)}async function Xh(e){await oc(e),await oe(e,!0)}async function eg(e){await cs(e),await me(e),await oe(e,!0)}async function tg(e){await me(e),await oe(e,!0)}function ng(e){if(!Array.isArray(e))return{};const t={};for(const n of e){if(typeof n!="string")continue;const[s,...i]=n.split(":");if(!s||i.length===0)continue;const o=s.trim(),a=i.join(":").trim();o&&a&&(t[o]=a)}return t}function xr(e){return(e.channelsSnapshot?.channelAccounts?.nostr??[])[0]?.accountId??e.nostrProfileAccountId??"default"}function Ar(e,t=""){return`/api/channels/nostr/${encodeURIComponent(e)}/profile${t}`}function sg(e,t,n){e.nostrProfileAccountId=t,e.nostrProfileFormState=ef(n??void 0)}function ig(e){e.nostrProfileFormState=null,e.nostrProfileAccountId=null}function og(e,t,n){const s=e.nostrProfileFormState;s&&(e.nostrProfileFormState={...s,values:{...s.values,[t]:n},fieldErrors:{...s.fieldErrors,[t]:""}})}function ag(e){const t=e.nostrProfileFormState;t&&(e.nostrProfileFormState={...t,showAdvanced:!t.showAdvanced})}async function rg(e){const t=e.nostrProfileFormState;if(!t||t.saving)return;const n=xr(e);e.nostrProfileFormState={...t,saving:!0,error:null,success:null,fieldErrors:{}};try{const s=await fetch(Ar(n),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(t.values)}),i=await s.json().catch(()=>null);if(!s.ok||i?.ok===!1||!i){const o=i?.error??`Profile update failed (${s.status})`;e.nostrProfileFormState={...t,saving:!1,error:o,success:null,fieldErrors:ng(i?.details)};return}if(!i.persisted){e.nostrProfileFormState={...t,saving:!1,error:"Profile publish failed on all relays.",success:null};return}e.nostrProfileFormState={...t,saving:!1,error:null,success:"Profile published to relays.",fieldErrors:{},original:{...t.values}},await oe(e,!0)}catch(s){e.nostrProfileFormState={...t,saving:!1,error:`Profile update failed: ${String(s)}`,success:null}}}async function lg(e){const t=e.nostrProfileFormState;if(!t||t.importing)return;const n=xr(e);e.nostrProfileFormState={...t,importing:!0,error:null,success:null};try{const s=await fetch(Ar(n,"/import"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({autoMerge:!0})}),i=await s.json().catch(()=>null);if(!s.ok||i?.ok===!1||!i){const r=i?.error??`Profile import failed (${s.status})`;e.nostrProfileFormState={...t,importing:!1,error:r,success:null};return}const o=i.merged??i.imported??null,a=o?{...t.values,...o}:t.values,l=!!(a.banner||a.website||a.nip05||a.lud16);e.nostrProfileFormState={...t,importing:!1,values:a,error:null,success:i.saved?"Profile imported from relays. Review and publish.":"Profile imported. Review and publish.",showAdvanced:l},i.saved&&await oe(e,!0)}catch(s){e.nostrProfileFormState={...t,importing:!1,error:`Profile import failed: ${String(s)}`,success:null}}}var cg=Object.defineProperty,dg=Object.getOwnPropertyDescriptor,b=(e,t,n,s)=>{for(var i=s>1?void 0:s?dg(t,n):t,o=e.length-1,a;o>=0;o--)(a=e[o])&&(i=(s?a(t,n,i):a(i))||i);return s&&i&&cg(t,n,i),i};const ts=fl();function ug(){if(!window.location.search)return!1;const t=new URLSearchParams(window.location.search).get("onboarding");if(!t)return!1;const n=t.trim().toLowerCase();return n==="1"||n==="true"||n==="yes"||n==="on"}let m=class extends Qe{constructor(){super(...arguments),this.settings=hl(),this.password="",this.tab="chat",this.onboarding=ug(),this.connected=!1,this.theme=this.settings.theme??"system",this.themeResolved="dark",this.hello=null,this.lastError=null,this.eventLog=[],this.eventLogBuffer=[],this.toolStreamSyncTimer=null,this.sidebarCloseTimer=null,this.assistantName=ts.name,this.assistantAvatar=ts.avatar,this.assistantAgentId=ts.agentId??null,this.sessionKey=this.settings.sessionKey,this.chatLoading=!1,this.chatSending=!1,this.chatMessage="",this.chatMessages=[],this.chatToolMessages=[],this.chatStream=null,this.chatStreamStartedAt=null,this.chatRunId=null,this.compactionStatus=null,this.chatAvatarUrl=null,this.chatThinkingLevel=null,this.chatQueue=[],this.sidebarOpen=!1,this.sidebarContent=null,this.sidebarError=null,this.splitRatio=this.settings.splitRatio,this.nodesLoading=!1,this.nodes=[],this.devicesLoading=!1,this.devicesError=null,this.devicesList=null,this.execApprovalsLoading=!1,this.execApprovalsSaving=!1,this.execApprovalsDirty=!1,this.execApprovalsSnapshot=null,this.execApprovalsForm=null,this.execApprovalsSelectedAgent=null,this.execApprovalsTarget="gateway",this.execApprovalsTargetNodeId=null,this.execApprovalQueue=[],this.execApprovalBusy=!1,this.execApprovalError=null,this.configLoading=!1,this.configRaw=`{ -} -`,this.configValid=null,this.configIssues=[],this.configSaving=!1,this.configApplying=!1,this.updateRunning=!1,this.applySessionKey=this.settings.lastActiveSessionKey,this.configSnapshot=null,this.configSchema=null,this.configSchemaVersion=null,this.configSchemaLoading=!1,this.configUiHints={},this.configForm=null,this.configFormOriginal=null,this.configFormDirty=!1,this.configFormMode="form",this.configSearchQuery="",this.configActiveSection=null,this.configActiveSubsection=null,this.channelsLoading=!1,this.channelsSnapshot=null,this.channelsError=null,this.channelsLastSuccess=null,this.whatsappLoginMessage=null,this.whatsappLoginQrDataUrl=null,this.whatsappLoginConnected=null,this.whatsappBusy=!1,this.nostrProfileFormState=null,this.nostrProfileAccountId=null,this.presenceLoading=!1,this.presenceEntries=[],this.presenceError=null,this.presenceStatus=null,this.agentsLoading=!1,this.agentsList=null,this.agentsError=null,this.sessionsLoading=!1,this.sessionsResult=null,this.sessionsError=null,this.sessionsFilterActive="",this.sessionsFilterLimit="120",this.sessionsIncludeGlobal=!0,this.sessionsIncludeUnknown=!1,this.cronLoading=!1,this.cronJobs=[],this.cronStatus=null,this.cronError=null,this.cronForm={...Nh},this.cronRunsJobId=null,this.cronRuns=[],this.cronBusy=!1,this.skillsLoading=!1,this.skillsReport=null,this.skillsError=null,this.skillsFilter="",this.skillEdits={},this.skillsBusyKey=null,this.skillMessages={},this.debugLoading=!1,this.debugStatus=null,this.debugHealth=null,this.debugModels=[],this.debugHeartbeat=null,this.debugCallMethod="",this.debugCallParams="{}",this.debugCallResult=null,this.debugCallError=null,this.logsLoading=!1,this.logsError=null,this.logsFile=null,this.logsEntries=[],this.logsFilterText="",this.logsLevelFilters={...Ph},this.logsAutoFollow=!0,this.logsTruncated=!1,this.logsCursor=null,this.logsLastFetchAt=null,this.logsLimit=500,this.logsMaxBytes=25e4,this.logsAtBottom=!0,this.client=null,this.chatScrollFrame=null,this.chatScrollTimeout=null,this.chatHasAutoScrolled=!1,this.chatUserNearBottom=!0,this.nodesPollInterval=null,this.logsPollInterval=null,this.debugPollInterval=null,this.logsScrollFrame=null,this.toolStreamById=new Map,this.toolStreamOrder=[],this.basePath="",this.popStateHandler=()=>pd(this),this.themeMedia=null,this.themeMediaHandler=null,this.topbarObserver=null}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),Vh(this)}firstUpdated(){Gh(this)}disconnectedCallback(){Yh(this),super.disconnectedCallback()}updated(e){Qh(this,e)}connect(){kr(this)}handleChatScroll(e){Hl(this,e)}handleLogsScroll(e){zl(this,e)}exportLogs(e,t){ql(e,t)}resetToolStream(){Os(this)}resetChatScroll(){jl(this)}async loadAssistantIdentity(){await $r(this)}applySettings(e){$e(this,e)}setTab(e){od(this,e)}setTheme(e,t){ad(this,e,t)}async loadOverview(){await Oa(this)}async loadCron(){await Zs(this)}async handleAbortChat(){await Ba(this)}removeQueuedMessage(e){md(this,e)}async handleSendChat(e,t){await bd(this,e,t)}async handleWhatsAppStart(e){await Jh(this,e)}async handleWhatsAppWait(){await Zh(this)}async handleWhatsAppLogout(){await Xh(this)}async handleChannelConfigSave(){await eg(this)}async handleChannelConfigReload(){await tg(this)}handleNostrProfileEdit(e,t){sg(this,e,t)}handleNostrProfileCancel(){ig(this)}handleNostrProfileFieldChange(e,t){og(this,e,t)}async handleNostrProfileSave(){await rg(this)}async handleNostrProfileImport(){await lg(this)}handleNostrProfileToggleAdvanced(){ag(this)}async handleExecApprovalDecision(e){const t=this.execApprovalQueue[0];if(!(!t||!this.client||this.execApprovalBusy)){this.execApprovalBusy=!0,this.execApprovalError=null;try{await this.client.request("exec.approval.resolve",{id:t.id,decision:e}),this.execApprovalQueue=this.execApprovalQueue.filter(n=>n.id!==t.id)}catch(n){this.execApprovalError=`Exec approval failed: ${String(n)}`}finally{this.execApprovalBusy=!1}}}handleOpenSidebar(e){this.sidebarCloseTimer!=null&&(window.clearTimeout(this.sidebarCloseTimer),this.sidebarCloseTimer=null),this.sidebarContent=e,this.sidebarError=null,this.sidebarOpen=!0}handleCloseSidebar(){this.sidebarOpen=!1,this.sidebarCloseTimer!=null&&window.clearTimeout(this.sidebarCloseTimer),this.sidebarCloseTimer=window.setTimeout(()=>{this.sidebarOpen||(this.sidebarContent=null,this.sidebarError=null,this.sidebarCloseTimer=null)},200)}handleSplitRatioChange(e){const t=Math.max(.4,Math.min(.7,e));this.splitRatio=t,this.applySettings({...this.settings,splitRatio:t})}render(){return Mh(this)}};b([y()],m.prototype,"settings",2);b([y()],m.prototype,"password",2);b([y()],m.prototype,"tab",2);b([y()],m.prototype,"onboarding",2);b([y()],m.prototype,"connected",2);b([y()],m.prototype,"theme",2);b([y()],m.prototype,"themeResolved",2);b([y()],m.prototype,"hello",2);b([y()],m.prototype,"lastError",2);b([y()],m.prototype,"eventLog",2);b([y()],m.prototype,"assistantName",2);b([y()],m.prototype,"assistantAvatar",2);b([y()],m.prototype,"assistantAgentId",2);b([y()],m.prototype,"sessionKey",2);b([y()],m.prototype,"chatLoading",2);b([y()],m.prototype,"chatSending",2);b([y()],m.prototype,"chatMessage",2);b([y()],m.prototype,"chatMessages",2);b([y()],m.prototype,"chatToolMessages",2);b([y()],m.prototype,"chatStream",2);b([y()],m.prototype,"chatStreamStartedAt",2);b([y()],m.prototype,"chatRunId",2);b([y()],m.prototype,"compactionStatus",2);b([y()],m.prototype,"chatAvatarUrl",2);b([y()],m.prototype,"chatThinkingLevel",2);b([y()],m.prototype,"chatQueue",2);b([y()],m.prototype,"sidebarOpen",2);b([y()],m.prototype,"sidebarContent",2);b([y()],m.prototype,"sidebarError",2);b([y()],m.prototype,"splitRatio",2);b([y()],m.prototype,"nodesLoading",2);b([y()],m.prototype,"nodes",2);b([y()],m.prototype,"devicesLoading",2);b([y()],m.prototype,"devicesError",2);b([y()],m.prototype,"devicesList",2);b([y()],m.prototype,"execApprovalsLoading",2);b([y()],m.prototype,"execApprovalsSaving",2);b([y()],m.prototype,"execApprovalsDirty",2);b([y()],m.prototype,"execApprovalsSnapshot",2);b([y()],m.prototype,"execApprovalsForm",2);b([y()],m.prototype,"execApprovalsSelectedAgent",2);b([y()],m.prototype,"execApprovalsTarget",2);b([y()],m.prototype,"execApprovalsTargetNodeId",2);b([y()],m.prototype,"execApprovalQueue",2);b([y()],m.prototype,"execApprovalBusy",2);b([y()],m.prototype,"execApprovalError",2);b([y()],m.prototype,"configLoading",2);b([y()],m.prototype,"configRaw",2);b([y()],m.prototype,"configValid",2);b([y()],m.prototype,"configIssues",2);b([y()],m.prototype,"configSaving",2);b([y()],m.prototype,"configApplying",2);b([y()],m.prototype,"updateRunning",2);b([y()],m.prototype,"applySessionKey",2);b([y()],m.prototype,"configSnapshot",2);b([y()],m.prototype,"configSchema",2);b([y()],m.prototype,"configSchemaVersion",2);b([y()],m.prototype,"configSchemaLoading",2);b([y()],m.prototype,"configUiHints",2);b([y()],m.prototype,"configForm",2);b([y()],m.prototype,"configFormOriginal",2);b([y()],m.prototype,"configFormDirty",2);b([y()],m.prototype,"configFormMode",2);b([y()],m.prototype,"configSearchQuery",2);b([y()],m.prototype,"configActiveSection",2);b([y()],m.prototype,"configActiveSubsection",2);b([y()],m.prototype,"channelsLoading",2);b([y()],m.prototype,"channelsSnapshot",2);b([y()],m.prototype,"channelsError",2);b([y()],m.prototype,"channelsLastSuccess",2);b([y()],m.prototype,"whatsappLoginMessage",2);b([y()],m.prototype,"whatsappLoginQrDataUrl",2);b([y()],m.prototype,"whatsappLoginConnected",2);b([y()],m.prototype,"whatsappBusy",2);b([y()],m.prototype,"nostrProfileFormState",2);b([y()],m.prototype,"nostrProfileAccountId",2);b([y()],m.prototype,"presenceLoading",2);b([y()],m.prototype,"presenceEntries",2);b([y()],m.prototype,"presenceError",2);b([y()],m.prototype,"presenceStatus",2);b([y()],m.prototype,"agentsLoading",2);b([y()],m.prototype,"agentsList",2);b([y()],m.prototype,"agentsError",2);b([y()],m.prototype,"sessionsLoading",2);b([y()],m.prototype,"sessionsResult",2);b([y()],m.prototype,"sessionsError",2);b([y()],m.prototype,"sessionsFilterActive",2);b([y()],m.prototype,"sessionsFilterLimit",2);b([y()],m.prototype,"sessionsIncludeGlobal",2);b([y()],m.prototype,"sessionsIncludeUnknown",2);b([y()],m.prototype,"cronLoading",2);b([y()],m.prototype,"cronJobs",2);b([y()],m.prototype,"cronStatus",2);b([y()],m.prototype,"cronError",2);b([y()],m.prototype,"cronForm",2);b([y()],m.prototype,"cronRunsJobId",2);b([y()],m.prototype,"cronRuns",2);b([y()],m.prototype,"cronBusy",2);b([y()],m.prototype,"skillsLoading",2);b([y()],m.prototype,"skillsReport",2);b([y()],m.prototype,"skillsError",2);b([y()],m.prototype,"skillsFilter",2);b([y()],m.prototype,"skillEdits",2);b([y()],m.prototype,"skillsBusyKey",2);b([y()],m.prototype,"skillMessages",2);b([y()],m.prototype,"debugLoading",2);b([y()],m.prototype,"debugStatus",2);b([y()],m.prototype,"debugHealth",2);b([y()],m.prototype,"debugModels",2);b([y()],m.prototype,"debugHeartbeat",2);b([y()],m.prototype,"debugCallMethod",2);b([y()],m.prototype,"debugCallParams",2);b([y()],m.prototype,"debugCallResult",2);b([y()],m.prototype,"debugCallError",2);b([y()],m.prototype,"logsLoading",2);b([y()],m.prototype,"logsError",2);b([y()],m.prototype,"logsFile",2);b([y()],m.prototype,"logsEntries",2);b([y()],m.prototype,"logsFilterText",2);b([y()],m.prototype,"logsLevelFilters",2);b([y()],m.prototype,"logsAutoFollow",2);b([y()],m.prototype,"logsTruncated",2);b([y()],m.prototype,"logsCursor",2);b([y()],m.prototype,"logsLastFetchAt",2);b([y()],m.prototype,"logsLimit",2);b([y()],m.prototype,"logsMaxBytes",2);b([y()],m.prototype,"logsAtBottom",2);m=b([ta("clawdbot-app")],m); -//# sourceMappingURL=index-DsXRcnEw.js.map diff --git a/dist/control-ui/assets/index-DsXRcnEw.js.map b/dist/control-ui/assets/index-DsXRcnEw.js.map deleted file mode 100644 index a46b0b5def3..00000000000 --- a/dist/control-ui/assets/index-DsXRcnEw.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index-DsXRcnEw.js","sources":["../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/css-tag.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/reactive-element.js","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/lit-html.js","../../../node_modules/.pnpm/lit-element@4.2.2/node_modules/lit-element/lit-element.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/custom-element.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/property.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/state.js","../../../ui/src/ui/assistant-identity.ts","../../../ui/src/ui/storage.ts","../../../src/sessions/session-key-utils.ts","../../../ui/src/ui/navigation.ts","../../../ui/src/ui/format.ts","../../../ui/src/ui/chat/message-extract.ts","../../../ui/src/ui/uuid.ts","../../../ui/src/ui/controllers/chat.ts","../../../ui/src/ui/controllers/sessions.ts","../../../ui/src/ui/app-tool-stream.ts","../../../ui/src/ui/app-scroll.ts","../../../ui/src/ui/controllers/config/form-utils.ts","../../../ui/src/ui/controllers/config.ts","../../../ui/src/ui/controllers/cron.ts","../../../ui/src/ui/controllers/channels.ts","../../../ui/src/ui/controllers/debug.ts","../../../ui/src/ui/controllers/logs.ts","../../../node_modules/.pnpm/@noble+ed25519@3.0.0/node_modules/@noble/ed25519/index.js","../../../ui/src/ui/device-identity.ts","../../../ui/src/ui/device-auth.ts","../../../ui/src/ui/controllers/devices.ts","../../../ui/src/ui/controllers/nodes.ts","../../../ui/src/ui/controllers/exec-approvals.ts","../../../ui/src/ui/controllers/presence.ts","../../../ui/src/ui/controllers/skills.ts","../../../ui/src/ui/theme.ts","../../../ui/src/ui/theme-transition.ts","../../../ui/src/ui/app-polling.ts","../../../ui/src/ui/app-settings.ts","../../../ui/src/ui/app-chat.ts","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directive.js","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directive-helpers.js","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directives/repeat.js","../../../ui/src/ui/chat/message-normalizer.ts","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directives/unsafe-html.js","../../../node_modules/.pnpm/dompurify@3.3.1/node_modules/dompurify/dist/purify.es.mjs","../../../node_modules/.pnpm/marked@17.0.1/node_modules/marked/lib/marked.esm.js","../../../ui/src/ui/markdown.ts","../../../ui/src/ui/icons.ts","../../../ui/src/ui/chat/copy-as-markdown.ts","../../../ui/src/ui/tool-display.ts","../../../ui/src/ui/chat/constants.ts","../../../ui/src/ui/chat/tool-helpers.ts","../../../ui/src/ui/chat/tool-cards.ts","../../../ui/src/ui/chat/grouped-render.ts","../../../ui/src/ui/views/markdown-sidebar.ts","../../../ui/src/ui/components/resizable-divider.ts","../../../ui/src/ui/views/chat.ts","../../../ui/src/ui/views/config-form.shared.ts","../../../ui/src/ui/views/config-form.node.ts","../../../ui/src/ui/views/config-form.render.ts","../../../ui/src/ui/views/config-form.analyze.ts","../../../ui/src/ui/views/config.ts","../../../ui/src/ui/views/channels.shared.ts","../../../ui/src/ui/views/channels.config.ts","../../../ui/src/ui/views/channels.discord.ts","../../../ui/src/ui/views/channels.imessage.ts","../../../ui/src/ui/views/channels.nostr-profile-form.ts","../../../ui/src/ui/views/channels.nostr.ts","../../../ui/src/ui/views/channels.signal.ts","../../../ui/src/ui/views/channels.slack.ts","../../../ui/src/ui/views/channels.telegram.ts","../../../ui/src/ui/views/channels.whatsapp.ts","../../../ui/src/ui/views/channels.ts","../../../ui/src/ui/presenter.ts","../../../ui/src/ui/views/cron.ts","../../../ui/src/ui/views/debug.ts","../../../ui/src/ui/views/instances.ts","../../../ui/src/ui/views/logs.ts","../../../ui/src/ui/views/nodes.ts","../../../ui/src/ui/views/overview.ts","../../../ui/src/ui/views/sessions.ts","../../../ui/src/ui/views/exec-approval.ts","../../../ui/src/ui/views/skills.ts","../../../ui/src/ui/app-render.helpers.ts","../../../ui/src/ui/app-render.ts","../../../ui/src/ui/app-defaults.ts","../../../ui/src/ui/controllers/agents.ts","../../../src/gateway/protocol/client-info.ts","../../../src/gateway/device-auth.ts","../../../ui/src/ui/gateway.ts","../../../ui/src/ui/controllers/exec-approval.ts","../../../ui/src/ui/controllers/assistant-identity.ts","../../../ui/src/ui/app-gateway.ts","../../../ui/src/ui/app-lifecycle.ts","../../../ui/src/ui/app-channels.ts","../../../ui/src/ui/app.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2019 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&\"adoptedStyleSheets\"in Document.prototype&&\"replace\"in CSSStyleSheet.prototype,s=Symbol(),o=new WeakMap;class n{constructor(t,e,o){if(this._$cssResult$=!0,o!==s)throw Error(\"CSSResult is not constructable. Use `unsafeCSS` or `css` instead.\");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const s=this.t;if(e&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=o.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&o.set(s,t))}return t}toString(){return this.cssText}}const r=t=>new n(\"string\"==typeof t?t:t+\"\",void 0,s),i=(t,...e)=>{const o=1===t.length?t[0]:e.reduce((e,s,o)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if(\"number\"==typeof t)return t;throw Error(\"Value passed to 'css' function must be a 'css' function result: \"+t+\". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.\")})(s)+t[o+1],t[0]);return new n(o,t,s)},S=(s,o)=>{if(e)s.adoptedStyleSheets=o.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of o){const o=document.createElement(\"style\"),n=t.litNonce;void 0!==n&&o.setAttribute(\"nonce\",n),o.textContent=e.cssText,s.appendChild(o)}},c=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e=\"\";for(const s of t.cssRules)e+=s.cssText;return r(e)})(t):t;export{n as CSSResult,S as adoptStyles,i as css,c as getCompatibleStyle,e as supportsAdoptingStyleSheets,r as unsafeCSS};\n//# sourceMappingURL=css-tag.js.map\n","import{getCompatibleStyle as t,adoptStyles as s}from\"./css-tag.js\";export{CSSResult,css,supportsAdoptingStyleSheets,unsafeCSS}from\"./css-tag.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const{is:i,defineProperty:e,getOwnPropertyDescriptor:h,getOwnPropertyNames:r,getOwnPropertySymbols:o,getPrototypeOf:n}=Object,a=globalThis,c=a.trustedTypes,l=c?c.emptyScript:\"\",p=a.reactiveElementPolyfillSupport,d=(t,s)=>t,u={toAttribute(t,s){switch(s){case Boolean:t=t?l:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,s){let i=t;switch(s){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},f=(t,s)=>!i(t,s),b={attribute:!0,type:String,converter:u,reflect:!1,useDefault:!1,hasChanged:f};Symbol.metadata??=Symbol(\"metadata\"),a.litPropertyMetadata??=new WeakMap;class y extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,s=b){if(s.state&&(s.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((s=Object.create(s)).wrapped=!0),this.elementProperties.set(t,s),!s.noAccessor){const i=Symbol(),h=this.getPropertyDescriptor(t,i,s);void 0!==h&&e(this.prototype,t,h)}}static getPropertyDescriptor(t,s,i){const{get:e,set:r}=h(this.prototype,t)??{get(){return this[s]},set(t){this[s]=t}};return{get:e,set(s){const h=e?.call(this);r?.call(this,s),this.requestUpdate(t,h,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(d(\"elementProperties\")))return;const t=n(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(d(\"finalized\")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(d(\"properties\"))){const t=this.properties,s=[...r(t),...o(t)];for(const i of s)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const s=litPropertyMetadata.get(t);if(void 0!==s)for(const[t,i]of s)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,s]of this.elementProperties){const i=this._$Eu(t,s);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(s){const i=[];if(Array.isArray(s)){const e=new Set(s.flat(1/0).reverse());for(const s of e)i.unshift(t(s))}else void 0!==s&&i.push(t(s));return i}static _$Eu(t,s){const i=s.attribute;return!1===i?void 0:\"string\"==typeof i?i:\"string\"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,s=this.constructor.elementProperties;for(const i of s.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return s(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,s,i){this._$AK(t,i)}_$ET(t,s){const i=this.constructor.elementProperties.get(t),e=this.constructor._$Eu(t,i);if(void 0!==e&&!0===i.reflect){const h=(void 0!==i.converter?.toAttribute?i.converter:u).toAttribute(s,i.type);this._$Em=t,null==h?this.removeAttribute(e):this.setAttribute(e,h),this._$Em=null}}_$AK(t,s){const i=this.constructor,e=i._$Eh.get(t);if(void 0!==e&&this._$Em!==e){const t=i.getPropertyOptions(e),h=\"function\"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:u;this._$Em=e;const r=h.fromAttribute(s,t.type);this[e]=r??this._$Ej?.get(e)??r,this._$Em=null}}requestUpdate(t,s,i,e=!1,h){if(void 0!==t){const r=this.constructor;if(!1===e&&(h=this[t]),i??=r.getPropertyOptions(t),!((i.hasChanged??f)(h,s)||i.useDefault&&i.reflect&&h===this._$Ej?.get(t)&&!this.hasAttribute(r._$Eu(t,i))))return;this.C(t,s,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,s,{useDefault:i,reflect:e,wrapped:h},r){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,r??s??this[t]),!0!==h||void 0!==r)||(this._$AL.has(t)||(this.hasUpdated||i||(s=void 0),this._$AL.set(t,s)),!0===e&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,s]of this._$Ep)this[t]=s;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[s,i]of t){const{wrapped:t}=i,e=this[s];!0!==t||this._$AL.has(s)||void 0===e||this.C(s,void 0,i,e)}}let t=!1;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(s)):this._$EM()}catch(s){throw t=!1,this._$EM(),s}t&&this._$AE(s)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}}y.elementStyles=[],y.shadowRootOptions={mode:\"open\"},y[d(\"elementProperties\")]=new Map,y[d(\"finalized\")]=new Map,p?.({ReactiveElement:y}),(a.reactiveElementVersions??=[]).push(\"2.1.2\");export{y as ReactiveElement,s as adoptStyles,u as defaultConverter,t as getCompatibleStyle,f as notEqual};\n//# sourceMappingURL=reactive-element.js.map\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=globalThis,i=t=>t,s=t.trustedTypes,e=s?s.createPolicy(\"lit-html\",{createHTML:t=>t}):void 0,h=\"$lit$\",o=`lit$${Math.random().toFixed(9).slice(2)}$`,n=\"?\"+o,r=`<${n}>`,l=document,c=()=>l.createComment(\"\"),a=t=>null===t||\"object\"!=typeof t&&\"function\"!=typeof t,u=Array.isArray,d=t=>u(t)||\"function\"==typeof t?.[Symbol.iterator],f=\"[ \\t\\n\\f\\r]\",v=/<(?:(!--|\\/[^a-zA-Z])|(\\/?[a-zA-Z][^>\\s]*)|(\\/?$))/g,_=/-->/g,m=/>/g,p=RegExp(`>|${f}(?:([^\\\\s\"'>=/]+)(${f}*=${f}*(?:[^ \\t\\n\\f\\r\"'\\`<>=]|(\"|')|))|$)`,\"g\"),g=/'/g,$=/\"/g,y=/^(?:script|style|textarea|title)$/i,x=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),b=x(1),w=x(2),T=x(3),E=Symbol.for(\"lit-noChange\"),A=Symbol.for(\"lit-nothing\"),C=new WeakMap,P=l.createTreeWalker(l,129);function V(t,i){if(!u(t)||!t.hasOwnProperty(\"raw\"))throw Error(\"invalid template strings array\");return void 0!==e?e.createHTML(i):i}const N=(t,i)=>{const s=t.length-1,e=[];let n,l=2===i?\"\":3===i?\"\":\"\",c=v;for(let i=0;i\"===u[0]?(c=n??v,d=-1):void 0===u[1]?d=-2:(d=c.lastIndex-u[2].length,a=u[1],c=void 0===u[3]?p:'\"'===u[3]?$:g):c===$||c===g?c=p:c===_||c===m?c=v:(c=p,n=void 0);const x=c===p&&t[i+1].startsWith(\"/>\")?\" \":\"\";l+=c===v?s+r:d>=0?(e.push(a),s.slice(0,d)+h+s.slice(d)+o+x):s+o+(-2===d?i:x)}return[V(t,l+(t[s]||\"\")+(2===i?\"\":3===i?\"\":\"\")),e]};class S{constructor({strings:t,_$litType$:i},e){let r;this.parts=[];let l=0,a=0;const u=t.length-1,d=this.parts,[f,v]=N(t,i);if(this.el=S.createElement(f,e),P.currentNode=this.el.content,2===i||3===i){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(r=P.nextNode())&&d.length0){r.textContent=s?s.emptyScript:\"\";for(let s=0;s2||\"\"!==s[0]||\"\"!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=A}_$AI(t,i=this,s,e){const h=this.strings;let o=!1;if(void 0===h)t=M(this,t,i,0),o=!a(t)||t!==this._$AH&&t!==E,o&&(this._$AH=t);else{const e=t;let n,r;for(t=h[0],n=0;n{const e=s?.renderBefore??i;let h=e._$litPart$;if(void 0===h){const t=s?.renderBefore??null;e._$litPart$=h=new k(i.insertBefore(c(),t),t,void 0,s??{})}return h._$AI(t),h};export{j as _$LH,b as html,T as mathml,E as noChange,A as nothing,D as render,w as svg};\n//# sourceMappingURL=lit-html.js.map\n","import{ReactiveElement as t}from\"@lit/reactive-element\";export*from\"@lit/reactive-element\";import{render as e,noChange as r}from\"lit-html\";export*from\"lit-html\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const s=globalThis;class i extends t{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const r=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=e(r,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return r}}i._$litElement$=!0,i[\"finalized\"]=!0,s.litElementHydrateSupport?.({LitElement:i});const o=s.litElementPolyfillSupport;o?.({LitElement:i});const n={_$AK:(t,e,r)=>{t._$AK(e,r)},_$AL:t=>t._$AL};(s.litElementVersions??=[]).push(\"4.2.2\");export{i as LitElement,n as _$LE};\n//# sourceMappingURL=lit-element.js.map\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=t=>(e,o)=>{void 0!==o?o.addInitializer(()=>{customElements.define(t,e)}):customElements.define(t,e)};export{t as customElement};\n//# sourceMappingURL=custom-element.js.map\n","import{notEqual as t,defaultConverter as e}from\"../reactive-element.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const o={attribute:!0,type:String,converter:e,reflect:!1,hasChanged:t},r=(t=o,e,r)=>{const{kind:n,metadata:i}=r;let s=globalThis.litPropertyMetadata.get(i);if(void 0===s&&globalThis.litPropertyMetadata.set(i,s=new Map),\"setter\"===n&&((t=Object.create(t)).wrapped=!0),s.set(r.name,t),\"accessor\"===n){const{name:o}=r;return{set(r){const n=e.get.call(this);e.set.call(this,r),this.requestUpdate(o,n,t,!0,r)},init(e){return void 0!==e&&this.C(o,void 0,t,e),e}}}if(\"setter\"===n){const{name:o}=r;return function(r){const n=this[o];e.call(this,r),this.requestUpdate(o,n,t,!0,r)}}throw Error(\"Unsupported decorator location: \"+n)};function n(t){return(e,o)=>\"object\"==typeof o?r(t,e,o):((t,e,o)=>{const r=e.hasOwnProperty(o);return e.constructor.createProperty(o,t),r?Object.getOwnPropertyDescriptor(e,o):void 0})(t,e,o)}export{n as property,r as standardProperty};\n//# sourceMappingURL=property.js.map\n","import{property as t}from\"./property.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */function r(r){return t({...r,state:!0,attribute:!1})}export{r as state};\n//# sourceMappingURL=state.js.map\n","const MAX_ASSISTANT_NAME = 50;\nconst MAX_ASSISTANT_AVATAR = 200;\n\nexport const DEFAULT_ASSISTANT_NAME = \"Assistant\";\nexport const DEFAULT_ASSISTANT_AVATAR = \"A\";\n\nexport type AssistantIdentity = {\n agentId?: string | null;\n name: string;\n avatar: string | null;\n};\n\ndeclare global {\n interface Window {\n __CLAWDBOT_ASSISTANT_NAME__?: string;\n __CLAWDBOT_ASSISTANT_AVATAR__?: string;\n }\n}\n\nfunction coerceIdentityValue(value: string | undefined, maxLength: number): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n if (trimmed.length <= maxLength) return trimmed;\n return trimmed.slice(0, maxLength);\n}\n\nexport function normalizeAssistantIdentity(\n input?: Partial | null,\n): AssistantIdentity {\n const name =\n coerceIdentityValue(input?.name, MAX_ASSISTANT_NAME) ?? DEFAULT_ASSISTANT_NAME;\n const avatar = coerceIdentityValue(input?.avatar ?? undefined, MAX_ASSISTANT_AVATAR) ?? null;\n const agentId =\n typeof input?.agentId === \"string\" && input.agentId.trim()\n ? input.agentId.trim()\n : null;\n return { agentId, name, avatar };\n}\n\nexport function resolveInjectedAssistantIdentity(): AssistantIdentity {\n if (typeof window === \"undefined\") {\n return normalizeAssistantIdentity({});\n }\n return normalizeAssistantIdentity({\n name: window.__CLAWDBOT_ASSISTANT_NAME__,\n avatar: window.__CLAWDBOT_ASSISTANT_AVATAR__,\n });\n}\n","const KEY = \"clawdbot.control.settings.v1\";\n\nimport type { ThemeMode } from \"./theme\";\n\nexport type UiSettings = {\n gatewayUrl: string;\n token: string;\n sessionKey: string;\n lastActiveSessionKey: string;\n theme: ThemeMode;\n chatFocusMode: boolean;\n chatShowThinking: boolean;\n splitRatio: number; // Sidebar split ratio (0.4 to 0.7, default 0.6)\n navCollapsed: boolean; // Collapsible sidebar state\n navGroupsCollapsed: Record; // Which nav groups are collapsed\n};\n\nexport function loadSettings(): UiSettings {\n const defaultUrl = (() => {\n const proto = location.protocol === \"https:\" ? \"wss\" : \"ws\";\n return `${proto}://${location.host}`;\n })();\n\n const defaults: UiSettings = {\n gatewayUrl: defaultUrl,\n token: \"\",\n sessionKey: \"main\",\n lastActiveSessionKey: \"main\",\n theme: \"system\",\n chatFocusMode: false,\n chatShowThinking: true,\n splitRatio: 0.6,\n navCollapsed: false,\n navGroupsCollapsed: {},\n };\n\n try {\n const raw = localStorage.getItem(KEY);\n if (!raw) return defaults;\n const parsed = JSON.parse(raw) as Partial;\n return {\n gatewayUrl:\n typeof parsed.gatewayUrl === \"string\" && parsed.gatewayUrl.trim()\n ? parsed.gatewayUrl.trim()\n : defaults.gatewayUrl,\n token: typeof parsed.token === \"string\" ? parsed.token : defaults.token,\n sessionKey:\n typeof parsed.sessionKey === \"string\" && parsed.sessionKey.trim()\n ? parsed.sessionKey.trim()\n : defaults.sessionKey,\n lastActiveSessionKey:\n typeof parsed.lastActiveSessionKey === \"string\" &&\n parsed.lastActiveSessionKey.trim()\n ? parsed.lastActiveSessionKey.trim()\n : (typeof parsed.sessionKey === \"string\" &&\n parsed.sessionKey.trim()) ||\n defaults.lastActiveSessionKey,\n theme:\n parsed.theme === \"light\" ||\n parsed.theme === \"dark\" ||\n parsed.theme === \"system\"\n ? parsed.theme\n : defaults.theme,\n chatFocusMode:\n typeof parsed.chatFocusMode === \"boolean\"\n ? parsed.chatFocusMode\n : defaults.chatFocusMode,\n chatShowThinking:\n typeof parsed.chatShowThinking === \"boolean\"\n ? parsed.chatShowThinking\n : defaults.chatShowThinking,\n splitRatio:\n typeof parsed.splitRatio === \"number\" &&\n parsed.splitRatio >= 0.4 &&\n parsed.splitRatio <= 0.7\n ? parsed.splitRatio\n : defaults.splitRatio,\n navCollapsed:\n typeof parsed.navCollapsed === \"boolean\"\n ? parsed.navCollapsed\n : defaults.navCollapsed,\n navGroupsCollapsed:\n typeof parsed.navGroupsCollapsed === \"object\" &&\n parsed.navGroupsCollapsed !== null\n ? parsed.navGroupsCollapsed\n : defaults.navGroupsCollapsed,\n };\n } catch {\n return defaults;\n }\n}\n\nexport function saveSettings(next: UiSettings) {\n localStorage.setItem(KEY, JSON.stringify(next));\n}\n","export type ParsedAgentSessionKey = {\n agentId: string;\n rest: string;\n};\n\nexport function parseAgentSessionKey(\n sessionKey: string | undefined | null,\n): ParsedAgentSessionKey | null {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return null;\n const parts = raw.split(\":\").filter(Boolean);\n if (parts.length < 3) return null;\n if (parts[0] !== \"agent\") return null;\n const agentId = parts[1]?.trim();\n const rest = parts.slice(2).join(\":\");\n if (!agentId || !rest) return null;\n return { agentId, rest };\n}\n\nexport function isSubagentSessionKey(sessionKey: string | undefined | null): boolean {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return false;\n if (raw.toLowerCase().startsWith(\"subagent:\")) return true;\n const parsed = parseAgentSessionKey(raw);\n return Boolean((parsed?.rest ?? \"\").toLowerCase().startsWith(\"subagent:\"));\n}\n\nexport function isAcpSessionKey(sessionKey: string | undefined | null): boolean {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return false;\n const normalized = raw.toLowerCase();\n if (normalized.startsWith(\"acp:\")) return true;\n const parsed = parseAgentSessionKey(raw);\n return Boolean((parsed?.rest ?? \"\").toLowerCase().startsWith(\"acp:\"));\n}\n\nconst THREAD_SESSION_MARKERS = [\":thread:\", \":topic:\"];\n\nexport function resolveThreadParentSessionKey(\n sessionKey: string | undefined | null,\n): string | null {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return null;\n const normalized = raw.toLowerCase();\n let idx = -1;\n for (const marker of THREAD_SESSION_MARKERS) {\n const candidate = normalized.lastIndexOf(marker);\n if (candidate > idx) idx = candidate;\n }\n if (idx <= 0) return null;\n const parent = raw.slice(0, idx).trim();\n return parent ? parent : null;\n}\n","export const TAB_GROUPS = [\n { label: \"Chat\", tabs: [\"chat\"] },\n {\n label: \"Control\",\n tabs: [\"overview\", \"channels\", \"instances\", \"sessions\", \"cron\"],\n },\n { label: \"Agent\", tabs: [\"skills\", \"nodes\"] },\n { label: \"Settings\", tabs: [\"config\", \"debug\", \"logs\"] },\n] as const;\n\nexport type Tab =\n | \"overview\"\n | \"channels\"\n | \"instances\"\n | \"sessions\"\n | \"cron\"\n | \"skills\"\n | \"nodes\"\n | \"chat\"\n | \"config\"\n | \"debug\"\n | \"logs\";\n\nconst TAB_PATHS: Record = {\n overview: \"/overview\",\n channels: \"/channels\",\n instances: \"/instances\",\n sessions: \"/sessions\",\n cron: \"/cron\",\n skills: \"/skills\",\n nodes: \"/nodes\",\n chat: \"/chat\",\n config: \"/config\",\n debug: \"/debug\",\n logs: \"/logs\",\n};\n\nconst PATH_TO_TAB = new Map(\n Object.entries(TAB_PATHS).map(([tab, path]) => [path, tab as Tab]),\n);\n\nexport function normalizeBasePath(basePath: string): string {\n if (!basePath) return \"\";\n let base = basePath.trim();\n if (!base.startsWith(\"/\")) base = `/${base}`;\n if (base === \"/\") return \"\";\n if (base.endsWith(\"/\")) base = base.slice(0, -1);\n return base;\n}\n\nexport function normalizePath(path: string): string {\n if (!path) return \"/\";\n let normalized = path.trim();\n if (!normalized.startsWith(\"/\")) normalized = `/${normalized}`;\n if (normalized.length > 1 && normalized.endsWith(\"/\")) {\n normalized = normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function pathForTab(tab: Tab, basePath = \"\"): string {\n const base = normalizeBasePath(basePath);\n const path = TAB_PATHS[tab];\n return base ? `${base}${path}` : path;\n}\n\nexport function tabFromPath(pathname: string, basePath = \"\"): Tab | null {\n const base = normalizeBasePath(basePath);\n let path = pathname || \"/\";\n if (base) {\n if (path === base) {\n path = \"/\";\n } else if (path.startsWith(`${base}/`)) {\n path = path.slice(base.length);\n }\n }\n let normalized = normalizePath(path).toLowerCase();\n if (normalized.endsWith(\"/index.html\")) normalized = \"/\";\n if (normalized === \"/\") return \"chat\";\n return PATH_TO_TAB.get(normalized) ?? null;\n}\n\nexport function inferBasePathFromPathname(pathname: string): string {\n let normalized = normalizePath(pathname);\n if (normalized.endsWith(\"/index.html\")) {\n normalized = normalizePath(normalized.slice(0, -\"/index.html\".length));\n }\n if (normalized === \"/\") return \"\";\n const segments = normalized.split(\"/\").filter(Boolean);\n if (segments.length === 0) return \"\";\n for (let i = 0; i < segments.length; i++) {\n const candidate = `/${segments.slice(i).join(\"/\")}`.toLowerCase();\n if (PATH_TO_TAB.has(candidate)) {\n const prefix = segments.slice(0, i);\n return prefix.length ? `/${prefix.join(\"/\")}` : \"\";\n }\n }\n return `/${segments.join(\"/\")}`;\n}\n\nexport function iconForTab(tab: Tab): string {\n switch (tab) {\n case \"chat\":\n return \"💬\";\n case \"overview\":\n return \"📊\";\n case \"channels\":\n return \"🔗\";\n case \"instances\":\n return \"📡\";\n case \"sessions\":\n return \"📄\";\n case \"cron\":\n return \"⏰\";\n case \"skills\":\n return \"⚡️\";\n case \"nodes\":\n return \"🖥️\";\n case \"config\":\n return \"⚙️\";\n case \"debug\":\n return \"🐞\";\n case \"logs\":\n return \"🧾\";\n default:\n return \"📁\";\n }\n}\n\nexport function titleForTab(tab: Tab) {\n switch (tab) {\n case \"overview\":\n return \"Overview\";\n case \"channels\":\n return \"Channels\";\n case \"instances\":\n return \"Instances\";\n case \"sessions\":\n return \"Sessions\";\n case \"cron\":\n return \"Cron Jobs\";\n case \"skills\":\n return \"Skills\";\n case \"nodes\":\n return \"Nodes\";\n case \"chat\":\n return \"Chat\";\n case \"config\":\n return \"Config\";\n case \"debug\":\n return \"Debug\";\n case \"logs\":\n return \"Logs\";\n default:\n return \"Control\";\n }\n}\n\nexport function subtitleForTab(tab: Tab) {\n switch (tab) {\n case \"overview\":\n return \"Gateway status, entry points, and a fast health read.\";\n case \"channels\":\n return \"Manage channels and settings.\";\n case \"instances\":\n return \"Presence beacons from connected clients and nodes.\";\n case \"sessions\":\n return \"Inspect active sessions and adjust per-session defaults.\";\n case \"cron\":\n return \"Schedule wakeups and recurring agent runs.\";\n case \"skills\":\n return \"Manage skill availability and API key injection.\";\n case \"nodes\":\n return \"Paired devices, capabilities, and command exposure.\";\n case \"chat\":\n return \"Direct gateway chat session for quick interventions.\";\n case \"config\":\n return \"Edit ~/.clawdbot/clawdbot.json safely.\";\n case \"debug\":\n return \"Gateway snapshots, events, and manual RPC calls.\";\n case \"logs\":\n return \"Live tail of the gateway file logs.\";\n default:\n return \"\";\n }\n}\n","export function formatMs(ms?: number | null): string {\n if (!ms && ms !== 0) return \"n/a\";\n return new Date(ms).toLocaleString();\n}\n\nexport function formatAgo(ms?: number | null): string {\n if (!ms && ms !== 0) return \"n/a\";\n const diff = Date.now() - ms;\n if (diff < 0) return \"just now\";\n const sec = Math.round(diff / 1000);\n if (sec < 60) return `${sec}s ago`;\n const min = Math.round(sec / 60);\n if (min < 60) return `${min}m ago`;\n const hr = Math.round(min / 60);\n if (hr < 48) return `${hr}h ago`;\n const day = Math.round(hr / 24);\n return `${day}d ago`;\n}\n\nexport function formatDurationMs(ms?: number | null): string {\n if (!ms && ms !== 0) return \"n/a\";\n if (ms < 1000) return `${ms}ms`;\n const sec = Math.round(ms / 1000);\n if (sec < 60) return `${sec}s`;\n const min = Math.round(sec / 60);\n if (min < 60) return `${min}m`;\n const hr = Math.round(min / 60);\n if (hr < 48) return `${hr}h`;\n const day = Math.round(hr / 24);\n return `${day}d`;\n}\n\nexport function formatList(values?: Array): string {\n if (!values || values.length === 0) return \"none\";\n return values.filter((v): v is string => Boolean(v && v.trim())).join(\", \");\n}\n\nexport function clampText(value: string, max = 120): string {\n if (value.length <= max) return value;\n return `${value.slice(0, Math.max(0, max - 1))}…`;\n}\n\nexport function truncateText(value: string, max: number): {\n text: string;\n truncated: boolean;\n total: number;\n} {\n if (value.length <= max) {\n return { text: value, truncated: false, total: value.length };\n }\n return {\n text: value.slice(0, Math.max(0, max)),\n truncated: true,\n total: value.length,\n };\n}\n\nexport function toNumber(value: string, fallback: number): number {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\nexport function parseList(input: string): string[] {\n return input\n .split(/[,\\n]/)\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n}\n\nconst THINKING_TAG_RE = /<\\s*\\/?\\s*think(?:ing)?\\s*>/gi;\nconst THINKING_OPEN_RE = /<\\s*think(?:ing)?\\s*>/i;\nconst THINKING_CLOSE_RE = /<\\s*\\/\\s*think(?:ing)?\\s*>/i;\n\nexport function stripThinkingTags(value: string): string {\n if (!value) return value;\n const hasOpen = THINKING_OPEN_RE.test(value);\n const hasClose = THINKING_CLOSE_RE.test(value);\n if (!hasOpen && !hasClose) return value;\n // If we don't have a balanced pair, avoid dropping trailing content.\n if (hasOpen !== hasClose) {\n if (!hasOpen) return value.replace(THINKING_CLOSE_RE, \"\").trimStart();\n return value.replace(THINKING_OPEN_RE, \"\").trimStart();\n }\n\n if (!THINKING_TAG_RE.test(value)) return value;\n THINKING_TAG_RE.lastIndex = 0;\n\n let result = \"\";\n let lastIndex = 0;\n let inThinking = false;\n for (const match of value.matchAll(THINKING_TAG_RE)) {\n const idx = match.index ?? 0;\n if (!inThinking) {\n result += value.slice(lastIndex, idx);\n }\n const tag = match[0].toLowerCase();\n inThinking = !tag.includes(\"/\");\n lastIndex = idx + match[0].length;\n }\n if (!inThinking) {\n result += value.slice(lastIndex);\n }\n return result.trimStart();\n}\n","import { stripThinkingTags } from \"../format\";\n\nconst ENVELOPE_PREFIX = /^\\[([^\\]]+)\\]\\s*/;\nconst ENVELOPE_CHANNELS = [\n \"WebChat\",\n \"WhatsApp\",\n \"Telegram\",\n \"Signal\",\n \"Slack\",\n \"Discord\",\n \"iMessage\",\n \"Teams\",\n \"Matrix\",\n \"Zalo\",\n \"Zalo Personal\",\n \"BlueBubbles\",\n];\n\nconst textCache = new WeakMap();\nconst thinkingCache = new WeakMap();\n\nfunction looksLikeEnvelopeHeader(header: string): boolean {\n if (/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}Z\\b/.test(header)) return true;\n if (/\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}\\b/.test(header)) return true;\n return ENVELOPE_CHANNELS.some((label) => header.startsWith(`${label} `));\n}\n\nexport function stripEnvelope(text: string): string {\n const match = text.match(ENVELOPE_PREFIX);\n if (!match) return text;\n const header = match[1] ?? \"\";\n if (!looksLikeEnvelopeHeader(header)) return text;\n return text.slice(match[0].length);\n}\n\nexport function extractText(message: unknown): string | null {\n const m = message as Record;\n const role = typeof m.role === \"string\" ? m.role : \"\";\n const content = m.content;\n if (typeof content === \"string\") {\n const processed = role === \"assistant\" ? stripThinkingTags(content) : stripEnvelope(content);\n return processed;\n }\n if (Array.isArray(content)) {\n const parts = content\n .map((p) => {\n const item = p as Record;\n if (item.type === \"text\" && typeof item.text === \"string\") return item.text;\n return null;\n })\n .filter((v): v is string => typeof v === \"string\");\n if (parts.length > 0) {\n const joined = parts.join(\"\\n\");\n const processed = role === \"assistant\" ? stripThinkingTags(joined) : stripEnvelope(joined);\n return processed;\n }\n }\n if (typeof m.text === \"string\") {\n const processed = role === \"assistant\" ? stripThinkingTags(m.text) : stripEnvelope(m.text);\n return processed;\n }\n return null;\n}\n\nexport function extractTextCached(message: unknown): string | null {\n if (!message || typeof message !== \"object\") return extractText(message);\n const obj = message as object;\n if (textCache.has(obj)) return textCache.get(obj) ?? null;\n const value = extractText(message);\n textCache.set(obj, value);\n return value;\n}\n\nexport function extractThinking(message: unknown): string | null {\n const m = message as Record;\n const content = m.content;\n const parts: string[] = [];\n if (Array.isArray(content)) {\n for (const p of content) {\n const item = p as Record;\n if (item.type === \"thinking\" && typeof item.thinking === \"string\") {\n const cleaned = item.thinking.trim();\n if (cleaned) parts.push(cleaned);\n }\n }\n }\n if (parts.length > 0) return parts.join(\"\\n\");\n\n // Back-compat: older logs may still have tags inside text blocks.\n const rawText = extractRawText(message);\n if (!rawText) return null;\n const matches = [\n ...rawText.matchAll(\n /<\\s*think(?:ing)?\\s*>([\\s\\S]*?)<\\s*\\/\\s*think(?:ing)?\\s*>/gi,\n ),\n ];\n const extracted = matches\n .map((m) => (m[1] ?? \"\").trim())\n .filter(Boolean);\n return extracted.length > 0 ? extracted.join(\"\\n\") : null;\n}\n\nexport function extractThinkingCached(message: unknown): string | null {\n if (!message || typeof message !== \"object\") return extractThinking(message);\n const obj = message as object;\n if (thinkingCache.has(obj)) return thinkingCache.get(obj) ?? null;\n const value = extractThinking(message);\n thinkingCache.set(obj, value);\n return value;\n}\n\nexport function extractRawText(message: unknown): string | null {\n const m = message as Record;\n const content = m.content;\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n const parts = content\n .map((p) => {\n const item = p as Record;\n if (item.type === \"text\" && typeof item.text === \"string\") return item.text;\n return null;\n })\n .filter((v): v is string => typeof v === \"string\");\n if (parts.length > 0) return parts.join(\"\\n\");\n }\n if (typeof m.text === \"string\") return m.text;\n return null;\n}\n\nexport function formatReasoningMarkdown(text: string): string {\n const trimmed = text.trim();\n if (!trimmed) return \"\";\n const lines = trimmed\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => `_${line}_`);\n return lines.length ? [\"_Reasoning:_\", ...lines].join(\"\\n\") : \"\";\n}\n","export type CryptoLike = {\n randomUUID?: (() => string) | undefined;\n getRandomValues?: ((array: Uint8Array) => Uint8Array) | undefined;\n};\n\nfunction uuidFromBytes(bytes: Uint8Array): string {\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1\n\n let hex = \"\";\n for (let i = 0; i < bytes.length; i++) {\n hex += bytes[i]!.toString(16).padStart(2, \"0\");\n }\n\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(\n 16,\n 20,\n )}-${hex.slice(20)}`;\n}\n\nfunction weakRandomBytes(): Uint8Array {\n const bytes = new Uint8Array(16);\n const now = Date.now();\n for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);\n bytes[0] ^= now & 0xff;\n bytes[1] ^= (now >>> 8) & 0xff;\n bytes[2] ^= (now >>> 16) & 0xff;\n bytes[3] ^= (now >>> 24) & 0xff;\n return bytes;\n}\n\nexport function generateUUID(cryptoLike: CryptoLike | null = globalThis.crypto): string {\n if (cryptoLike && typeof cryptoLike.randomUUID === \"function\") return cryptoLike.randomUUID();\n\n if (cryptoLike && typeof cryptoLike.getRandomValues === \"function\") {\n const bytes = new Uint8Array(16);\n cryptoLike.getRandomValues(bytes);\n return uuidFromBytes(bytes);\n }\n\n return uuidFromBytes(weakRandomBytes());\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { extractText } from \"../chat/message-extract\";\nimport { generateUUID } from \"../uuid\";\n\nexport type ChatState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n sessionKey: string;\n chatLoading: boolean;\n chatMessages: unknown[];\n chatThinkingLevel: string | null;\n chatSending: boolean;\n chatMessage: string;\n chatRunId: string | null;\n chatStream: string | null;\n chatStreamStartedAt: number | null;\n lastError: string | null;\n};\n\nexport type ChatEventPayload = {\n runId: string;\n sessionKey: string;\n state: \"delta\" | \"final\" | \"aborted\" | \"error\";\n message?: unknown;\n errorMessage?: string;\n};\n\nexport async function loadChatHistory(state: ChatState) {\n if (!state.client || !state.connected) return;\n state.chatLoading = true;\n state.lastError = null;\n try {\n const res = (await state.client.request(\"chat.history\", {\n sessionKey: state.sessionKey,\n limit: 200,\n })) as { messages?: unknown[]; thinkingLevel?: string | null };\n state.chatMessages = Array.isArray(res.messages) ? res.messages : [];\n state.chatThinkingLevel = res.thinkingLevel ?? null;\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.chatLoading = false;\n }\n}\n\nexport async function sendChatMessage(state: ChatState, message: string): Promise {\n if (!state.client || !state.connected) return false;\n const msg = message.trim();\n if (!msg) return false;\n\n const now = Date.now();\n state.chatMessages = [\n ...state.chatMessages,\n {\n role: \"user\",\n content: [{ type: \"text\", text: msg }],\n timestamp: now,\n },\n ];\n\n state.chatSending = true;\n state.lastError = null;\n const runId = generateUUID();\n state.chatRunId = runId;\n state.chatStream = \"\";\n state.chatStreamStartedAt = now;\n try {\n await state.client.request(\"chat.send\", {\n sessionKey: state.sessionKey,\n message: msg,\n deliver: false,\n idempotencyKey: runId,\n });\n return true;\n } catch (err) {\n const error = String(err);\n state.chatRunId = null;\n state.chatStream = null;\n state.chatStreamStartedAt = null;\n state.lastError = error;\n state.chatMessages = [\n ...state.chatMessages,\n {\n role: \"assistant\",\n content: [{ type: \"text\", text: \"Error: \" + error }],\n timestamp: Date.now(),\n },\n ];\n return false;\n } finally {\n state.chatSending = false;\n }\n}\n\nexport async function abortChatRun(state: ChatState): Promise {\n if (!state.client || !state.connected) return false;\n const runId = state.chatRunId;\n try {\n await state.client.request(\n \"chat.abort\",\n runId\n ? { sessionKey: state.sessionKey, runId }\n : { sessionKey: state.sessionKey },\n );\n return true;\n } catch (err) {\n state.lastError = String(err);\n return false;\n }\n}\n\nexport function handleChatEvent(\n state: ChatState,\n payload?: ChatEventPayload,\n) {\n if (!payload) return null;\n if (payload.sessionKey !== state.sessionKey) return null;\n if (payload.runId && state.chatRunId && payload.runId !== state.chatRunId)\n return null;\n\n if (payload.state === \"delta\") {\n const next = extractText(payload.message);\n if (typeof next === \"string\") {\n const current = state.chatStream ?? \"\";\n if (!current || next.length >= current.length) {\n state.chatStream = next;\n }\n }\n } else if (payload.state === \"final\") {\n state.chatStream = null;\n state.chatRunId = null;\n state.chatStreamStartedAt = null;\n } else if (payload.state === \"aborted\") {\n state.chatStream = null;\n state.chatRunId = null;\n state.chatStreamStartedAt = null;\n } else if (payload.state === \"error\") {\n state.chatStream = null;\n state.chatRunId = null;\n state.chatStreamStartedAt = null;\n state.lastError = payload.errorMessage ?? \"chat error\";\n }\n return payload.state;\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { toNumber } from \"../format\";\nimport type { SessionsListResult } from \"../types\";\n\nexport type SessionsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n sessionsLoading: boolean;\n sessionsResult: SessionsListResult | null;\n sessionsError: string | null;\n sessionsFilterActive: string;\n sessionsFilterLimit: string;\n sessionsIncludeGlobal: boolean;\n sessionsIncludeUnknown: boolean;\n};\n\nexport async function loadSessions(state: SessionsState) {\n if (!state.client || !state.connected) return;\n if (state.sessionsLoading) return;\n state.sessionsLoading = true;\n state.sessionsError = null;\n try {\n const params: Record = {\n includeGlobal: state.sessionsIncludeGlobal,\n includeUnknown: state.sessionsIncludeUnknown,\n };\n const activeMinutes = toNumber(state.sessionsFilterActive, 0);\n const limit = toNumber(state.sessionsFilterLimit, 0);\n if (activeMinutes > 0) params.activeMinutes = activeMinutes;\n if (limit > 0) params.limit = limit;\n const res = (await state.client.request(\"sessions.list\", params)) as\n | SessionsListResult\n | undefined;\n if (res) state.sessionsResult = res;\n } catch (err) {\n state.sessionsError = String(err);\n } finally {\n state.sessionsLoading = false;\n }\n}\n\nexport async function patchSession(\n state: SessionsState,\n key: string,\n patch: {\n label?: string | null;\n thinkingLevel?: string | null;\n verboseLevel?: string | null;\n reasoningLevel?: string | null;\n },\n) {\n if (!state.client || !state.connected) return;\n const params: Record = { key };\n if (\"label\" in patch) params.label = patch.label;\n if (\"thinkingLevel\" in patch) params.thinkingLevel = patch.thinkingLevel;\n if (\"verboseLevel\" in patch) params.verboseLevel = patch.verboseLevel;\n if (\"reasoningLevel\" in patch) params.reasoningLevel = patch.reasoningLevel;\n try {\n await state.client.request(\"sessions.patch\", params);\n await loadSessions(state);\n } catch (err) {\n state.sessionsError = String(err);\n }\n}\n\nexport async function deleteSession(state: SessionsState, key: string) {\n if (!state.client || !state.connected) return;\n if (state.sessionsLoading) return;\n const confirmed = window.confirm(\n `Delete session \"${key}\"?\\n\\nDeletes the session entry and archives its transcript.`,\n );\n if (!confirmed) return;\n state.sessionsLoading = true;\n state.sessionsError = null;\n try {\n await state.client.request(\"sessions.delete\", { key, deleteTranscript: true });\n await loadSessions(state);\n } catch (err) {\n state.sessionsError = String(err);\n } finally {\n state.sessionsLoading = false;\n }\n}\n","import { truncateText } from \"./format\";\n\nconst TOOL_STREAM_LIMIT = 50;\nconst TOOL_STREAM_THROTTLE_MS = 80;\nconst TOOL_OUTPUT_CHAR_LIMIT = 120_000;\n\nexport type AgentEventPayload = {\n runId: string;\n seq: number;\n stream: string;\n ts: number;\n sessionKey?: string;\n data: Record;\n};\n\nexport type ToolStreamEntry = {\n toolCallId: string;\n runId: string;\n sessionKey?: string;\n name: string;\n args?: unknown;\n output?: string;\n startedAt: number;\n updatedAt: number;\n message: Record;\n};\n\ntype ToolStreamHost = {\n sessionKey: string;\n chatRunId: string | null;\n toolStreamById: Map;\n toolStreamOrder: string[];\n chatToolMessages: Record[];\n toolStreamSyncTimer: number | null;\n};\n\nfunction extractToolOutputText(value: unknown): string | null {\n if (!value || typeof value !== \"object\") return null;\n const record = value as Record;\n if (typeof record.text === \"string\") return record.text;\n const content = record.content;\n if (!Array.isArray(content)) return null;\n const parts = content\n .map((item) => {\n if (!item || typeof item !== \"object\") return null;\n const entry = item as Record;\n if (entry.type === \"text\" && typeof entry.text === \"string\") return entry.text;\n return null;\n })\n .filter((part): part is string => Boolean(part));\n if (parts.length === 0) return null;\n return parts.join(\"\\n\");\n}\n\nfunction formatToolOutput(value: unknown): string | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n const contentText = extractToolOutputText(value);\n let text: string;\n if (typeof value === \"string\") {\n text = value;\n } else if (contentText) {\n text = contentText;\n } else {\n try {\n text = JSON.stringify(value, null, 2);\n } catch {\n text = String(value);\n }\n }\n const truncated = truncateText(text, TOOL_OUTPUT_CHAR_LIMIT);\n if (!truncated.truncated) return truncated.text;\n return `${truncated.text}\\n\\n… truncated (${truncated.total} chars, showing first ${truncated.text.length}).`;\n}\n\nfunction buildToolStreamMessage(entry: ToolStreamEntry): Record {\n const content: Array> = [];\n content.push({\n type: \"toolcall\",\n name: entry.name,\n arguments: entry.args ?? {},\n });\n if (entry.output) {\n content.push({\n type: \"toolresult\",\n name: entry.name,\n text: entry.output,\n });\n }\n return {\n role: \"assistant\",\n toolCallId: entry.toolCallId,\n runId: entry.runId,\n content,\n timestamp: entry.startedAt,\n };\n}\n\nfunction trimToolStream(host: ToolStreamHost) {\n if (host.toolStreamOrder.length <= TOOL_STREAM_LIMIT) return;\n const overflow = host.toolStreamOrder.length - TOOL_STREAM_LIMIT;\n const removed = host.toolStreamOrder.splice(0, overflow);\n for (const id of removed) host.toolStreamById.delete(id);\n}\n\nfunction syncToolStreamMessages(host: ToolStreamHost) {\n host.chatToolMessages = host.toolStreamOrder\n .map((id) => host.toolStreamById.get(id)?.message)\n .filter((msg): msg is Record => Boolean(msg));\n}\n\nexport function flushToolStreamSync(host: ToolStreamHost) {\n if (host.toolStreamSyncTimer != null) {\n clearTimeout(host.toolStreamSyncTimer);\n host.toolStreamSyncTimer = null;\n }\n syncToolStreamMessages(host);\n}\n\nexport function scheduleToolStreamSync(host: ToolStreamHost, force = false) {\n if (force) {\n flushToolStreamSync(host);\n return;\n }\n if (host.toolStreamSyncTimer != null) return;\n host.toolStreamSyncTimer = window.setTimeout(\n () => flushToolStreamSync(host),\n TOOL_STREAM_THROTTLE_MS,\n );\n}\n\nexport function resetToolStream(host: ToolStreamHost) {\n host.toolStreamById.clear();\n host.toolStreamOrder = [];\n host.chatToolMessages = [];\n flushToolStreamSync(host);\n}\n\nexport type CompactionStatus = {\n active: boolean;\n startedAt: number | null;\n completedAt: number | null;\n};\n\ntype CompactionHost = ToolStreamHost & {\n compactionStatus?: CompactionStatus | null;\n compactionClearTimer?: number | null;\n};\n\nconst COMPACTION_TOAST_DURATION_MS = 5000;\n\nexport function handleCompactionEvent(host: CompactionHost, payload: AgentEventPayload) {\n const data = payload.data ?? {};\n const phase = typeof data.phase === \"string\" ? data.phase : \"\";\n \n // Clear any existing timer\n if (host.compactionClearTimer != null) {\n window.clearTimeout(host.compactionClearTimer);\n host.compactionClearTimer = null;\n }\n \n if (phase === \"start\") {\n host.compactionStatus = {\n active: true,\n startedAt: Date.now(),\n completedAt: null,\n };\n } else if (phase === \"end\") {\n host.compactionStatus = {\n active: false,\n startedAt: host.compactionStatus?.startedAt ?? null,\n completedAt: Date.now(),\n };\n // Auto-clear the toast after duration\n host.compactionClearTimer = window.setTimeout(() => {\n host.compactionStatus = null;\n host.compactionClearTimer = null;\n }, COMPACTION_TOAST_DURATION_MS);\n }\n}\n\nexport function handleAgentEvent(host: ToolStreamHost, payload?: AgentEventPayload) {\n if (!payload) return;\n \n // Handle compaction events\n if (payload.stream === \"compaction\") {\n handleCompactionEvent(host as CompactionHost, payload);\n return;\n }\n \n if (payload.stream !== \"tool\") return;\n const sessionKey =\n typeof payload.sessionKey === \"string\" ? payload.sessionKey : undefined;\n if (sessionKey && sessionKey !== host.sessionKey) return;\n // Fallback: only accept session-less events for the active run.\n if (!sessionKey && host.chatRunId && payload.runId !== host.chatRunId) return;\n if (host.chatRunId && payload.runId !== host.chatRunId) return;\n if (!host.chatRunId) return;\n\n const data = payload.data ?? {};\n const toolCallId = typeof data.toolCallId === \"string\" ? data.toolCallId : \"\";\n if (!toolCallId) return;\n const name = typeof data.name === \"string\" ? data.name : \"tool\";\n const phase = typeof data.phase === \"string\" ? data.phase : \"\";\n const args = phase === \"start\" ? data.args : undefined;\n const output =\n phase === \"update\"\n ? formatToolOutput(data.partialResult)\n : phase === \"result\"\n ? formatToolOutput(data.result)\n : undefined;\n\n const now = Date.now();\n let entry = host.toolStreamById.get(toolCallId);\n if (!entry) {\n entry = {\n toolCallId,\n runId: payload.runId,\n sessionKey,\n name,\n args,\n output,\n startedAt: typeof payload.ts === \"number\" ? payload.ts : now,\n updatedAt: now,\n message: {},\n };\n host.toolStreamById.set(toolCallId, entry);\n host.toolStreamOrder.push(toolCallId);\n } else {\n entry.name = name;\n if (args !== undefined) entry.args = args;\n if (output !== undefined) entry.output = output;\n entry.updatedAt = now;\n }\n\n entry.message = buildToolStreamMessage(entry);\n trimToolStream(host);\n scheduleToolStreamSync(host, phase === \"result\");\n}\n","type ScrollHost = {\n updateComplete: Promise;\n querySelector: (selectors: string) => Element | null;\n style: CSSStyleDeclaration;\n chatScrollFrame: number | null;\n chatScrollTimeout: number | null;\n chatHasAutoScrolled: boolean;\n chatUserNearBottom: boolean;\n logsScrollFrame: number | null;\n logsAtBottom: boolean;\n topbarObserver: ResizeObserver | null;\n};\n\nexport function scheduleChatScroll(host: ScrollHost, force = false) {\n if (host.chatScrollFrame) cancelAnimationFrame(host.chatScrollFrame);\n if (host.chatScrollTimeout != null) {\n clearTimeout(host.chatScrollTimeout);\n host.chatScrollTimeout = null;\n }\n const pickScrollTarget = () => {\n const container = host.querySelector(\".chat-thread\") as HTMLElement | null;\n if (container) {\n const overflowY = getComputedStyle(container).overflowY;\n const canScroll =\n overflowY === \"auto\" ||\n overflowY === \"scroll\" ||\n container.scrollHeight - container.clientHeight > 1;\n if (canScroll) return container;\n }\n return (document.scrollingElement ?? document.documentElement) as HTMLElement | null;\n };\n // Wait for Lit render to complete, then scroll\n void host.updateComplete.then(() => {\n host.chatScrollFrame = requestAnimationFrame(() => {\n host.chatScrollFrame = null;\n const target = pickScrollTarget();\n if (!target) return;\n const distanceFromBottom =\n target.scrollHeight - target.scrollTop - target.clientHeight;\n const shouldStick = force || host.chatUserNearBottom || distanceFromBottom < 200;\n if (!shouldStick) return;\n if (force) host.chatHasAutoScrolled = true;\n target.scrollTop = target.scrollHeight;\n host.chatUserNearBottom = true;\n const retryDelay = force ? 150 : 120;\n host.chatScrollTimeout = window.setTimeout(() => {\n host.chatScrollTimeout = null;\n const latest = pickScrollTarget();\n if (!latest) return;\n const latestDistanceFromBottom =\n latest.scrollHeight - latest.scrollTop - latest.clientHeight;\n const shouldStickRetry =\n force || host.chatUserNearBottom || latestDistanceFromBottom < 200;\n if (!shouldStickRetry) return;\n latest.scrollTop = latest.scrollHeight;\n host.chatUserNearBottom = true;\n }, retryDelay);\n });\n });\n}\n\nexport function scheduleLogsScroll(host: ScrollHost, force = false) {\n if (host.logsScrollFrame) cancelAnimationFrame(host.logsScrollFrame);\n void host.updateComplete.then(() => {\n host.logsScrollFrame = requestAnimationFrame(() => {\n host.logsScrollFrame = null;\n const container = host.querySelector(\".log-stream\") as HTMLElement | null;\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n const shouldStick = force || distanceFromBottom < 80;\n if (!shouldStick) return;\n container.scrollTop = container.scrollHeight;\n });\n });\n}\n\nexport function handleChatScroll(host: ScrollHost, event: Event) {\n const container = event.currentTarget as HTMLElement | null;\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n host.chatUserNearBottom = distanceFromBottom < 200;\n}\n\nexport function handleLogsScroll(host: ScrollHost, event: Event) {\n const container = event.currentTarget as HTMLElement | null;\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n host.logsAtBottom = distanceFromBottom < 80;\n}\n\nexport function resetChatScroll(host: ScrollHost) {\n host.chatHasAutoScrolled = false;\n host.chatUserNearBottom = true;\n}\n\nexport function exportLogs(lines: string[], label: string) {\n if (lines.length === 0) return;\n const blob = new Blob([`${lines.join(\"\\n\")}\\n`], { type: \"text/plain\" });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n const stamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, \"-\");\n anchor.href = url;\n anchor.download = `clawdbot-logs-${label}-${stamp}.log`;\n anchor.click();\n URL.revokeObjectURL(url);\n}\n\nexport function observeTopbar(host: ScrollHost) {\n if (typeof ResizeObserver === \"undefined\") return;\n const topbar = host.querySelector(\".topbar\");\n if (!topbar) return;\n const update = () => {\n const { height } = topbar.getBoundingClientRect();\n host.style.setProperty(\"--topbar-height\", `${height}px`);\n };\n update();\n host.topbarObserver = new ResizeObserver(() => update());\n host.topbarObserver.observe(topbar);\n}\n","export function cloneConfigObject(value: T): T {\n if (typeof structuredClone === \"function\") {\n return structuredClone(value);\n }\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nexport function serializeConfigForm(form: Record): string {\n return `${JSON.stringify(form, null, 2).trimEnd()}\\n`;\n}\n\nexport function setPathValue(\n obj: Record | unknown[],\n path: Array,\n value: unknown,\n) {\n if (path.length === 0) return;\n let current: Record | unknown[] = obj;\n for (let i = 0; i < path.length - 1; i += 1) {\n const key = path[i];\n const nextKey = path[i + 1];\n if (typeof key === \"number\") {\n if (!Array.isArray(current)) return;\n if (current[key] == null) {\n current[key] =\n typeof nextKey === \"number\" ? [] : ({} as Record);\n }\n current = current[key] as Record | unknown[];\n } else {\n if (typeof current !== \"object\" || current == null) return;\n const record = current as Record;\n if (record[key] == null) {\n record[key] =\n typeof nextKey === \"number\" ? [] : ({} as Record);\n }\n current = record[key] as Record | unknown[];\n }\n }\n const lastKey = path[path.length - 1];\n if (typeof lastKey === \"number\") {\n if (Array.isArray(current)) current[lastKey] = value;\n return;\n }\n if (typeof current === \"object\" && current != null) {\n (current as Record)[lastKey] = value;\n }\n}\n\nexport function removePathValue(\n obj: Record | unknown[],\n path: Array,\n) {\n if (path.length === 0) return;\n let current: Record | unknown[] = obj;\n for (let i = 0; i < path.length - 1; i += 1) {\n const key = path[i];\n if (typeof key === \"number\") {\n if (!Array.isArray(current)) return;\n current = current[key] as Record | unknown[];\n } else {\n if (typeof current !== \"object\" || current == null) return;\n current = (current as Record)[key] as\n | Record\n | unknown[];\n }\n if (current == null) return;\n }\n const lastKey = path[path.length - 1];\n if (typeof lastKey === \"number\") {\n if (Array.isArray(current)) current.splice(lastKey, 1);\n return;\n }\n if (typeof current === \"object\" && current != null) {\n delete (current as Record)[lastKey];\n }\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type {\n ConfigSchemaResponse,\n ConfigSnapshot,\n ConfigUiHints,\n} from \"../types\";\nimport {\n cloneConfigObject,\n removePathValue,\n serializeConfigForm,\n setPathValue,\n} from \"./config/form-utils\";\n\nexport type ConfigState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n applySessionKey: string;\n configLoading: boolean;\n configRaw: string;\n configValid: boolean | null;\n configIssues: unknown[];\n configSaving: boolean;\n configApplying: boolean;\n updateRunning: boolean;\n configSnapshot: ConfigSnapshot | null;\n configSchema: unknown | null;\n configSchemaVersion: string | null;\n configSchemaLoading: boolean;\n configUiHints: ConfigUiHints;\n configForm: Record | null;\n configFormOriginal: Record | null;\n configFormDirty: boolean;\n configFormMode: \"form\" | \"raw\";\n configSearchQuery: string;\n configActiveSection: string | null;\n configActiveSubsection: string | null;\n lastError: string | null;\n};\n\nexport async function loadConfig(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.configLoading = true;\n state.lastError = null;\n try {\n const res = (await state.client.request(\"config.get\", {})) as ConfigSnapshot;\n applyConfigSnapshot(state, res);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configLoading = false;\n }\n}\n\nexport async function loadConfigSchema(state: ConfigState) {\n if (!state.client || !state.connected) return;\n if (state.configSchemaLoading) return;\n state.configSchemaLoading = true;\n try {\n const res = (await state.client.request(\n \"config.schema\",\n {},\n )) as ConfigSchemaResponse;\n applyConfigSchema(state, res);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configSchemaLoading = false;\n }\n}\n\nexport function applyConfigSchema(\n state: ConfigState,\n res: ConfigSchemaResponse,\n) {\n state.configSchema = res.schema ?? null;\n state.configUiHints = res.uiHints ?? {};\n state.configSchemaVersion = res.version ?? null;\n}\n\nexport function applyConfigSnapshot(state: ConfigState, snapshot: ConfigSnapshot) {\n state.configSnapshot = snapshot;\n const rawFromSnapshot =\n typeof snapshot.raw === \"string\"\n ? snapshot.raw\n : snapshot.config && typeof snapshot.config === \"object\"\n ? serializeConfigForm(snapshot.config as Record)\n : state.configRaw;\n if (!state.configFormDirty || state.configFormMode === \"raw\") {\n state.configRaw = rawFromSnapshot;\n } else if (state.configForm) {\n state.configRaw = serializeConfigForm(state.configForm);\n } else {\n state.configRaw = rawFromSnapshot;\n }\n state.configValid = typeof snapshot.valid === \"boolean\" ? snapshot.valid : null;\n state.configIssues = Array.isArray(snapshot.issues) ? snapshot.issues : [];\n\n if (!state.configFormDirty) {\n state.configForm = cloneConfigObject(snapshot.config ?? {});\n state.configFormOriginal = cloneConfigObject(snapshot.config ?? {});\n }\n}\n\nexport async function saveConfig(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.configSaving = true;\n state.lastError = null;\n try {\n const raw =\n state.configFormMode === \"form\" && state.configForm\n ? serializeConfigForm(state.configForm)\n : state.configRaw;\n const baseHash = state.configSnapshot?.hash;\n if (!baseHash) {\n state.lastError = \"Config hash missing; reload and retry.\";\n return;\n }\n await state.client.request(\"config.set\", { raw, baseHash });\n state.configFormDirty = false;\n await loadConfig(state);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configSaving = false;\n }\n}\n\nexport async function applyConfig(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.configApplying = true;\n state.lastError = null;\n try {\n const raw =\n state.configFormMode === \"form\" && state.configForm\n ? serializeConfigForm(state.configForm)\n : state.configRaw;\n const baseHash = state.configSnapshot?.hash;\n if (!baseHash) {\n state.lastError = \"Config hash missing; reload and retry.\";\n return;\n }\n await state.client.request(\"config.apply\", {\n raw,\n baseHash,\n sessionKey: state.applySessionKey,\n });\n state.configFormDirty = false;\n await loadConfig(state);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configApplying = false;\n }\n}\n\nexport async function runUpdate(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.updateRunning = true;\n state.lastError = null;\n try {\n await state.client.request(\"update.run\", {\n sessionKey: state.applySessionKey,\n });\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.updateRunning = false;\n }\n}\n\nexport function updateConfigFormValue(\n state: ConfigState,\n path: Array,\n value: unknown,\n) {\n const base = cloneConfigObject(\n state.configForm ?? state.configSnapshot?.config ?? {},\n );\n setPathValue(base, path, value);\n state.configForm = base;\n state.configFormDirty = true;\n if (state.configFormMode === \"form\") {\n state.configRaw = serializeConfigForm(base);\n }\n}\n\nexport function removeConfigFormValue(\n state: ConfigState,\n path: Array,\n) {\n const base = cloneConfigObject(\n state.configForm ?? state.configSnapshot?.config ?? {},\n );\n removePathValue(base, path);\n state.configForm = base;\n state.configFormDirty = true;\n if (state.configFormMode === \"form\") {\n state.configRaw = serializeConfigForm(base);\n }\n}\n","import { toNumber } from \"../format\";\nimport type { GatewayBrowserClient } from \"../gateway\";\nimport type { CronJob, CronRunLogEntry, CronStatus } from \"../types\";\nimport type { CronFormState } from \"../ui-types\";\n\nexport type CronState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n cronLoading: boolean;\n cronJobs: CronJob[];\n cronStatus: CronStatus | null;\n cronError: string | null;\n cronForm: CronFormState;\n cronRunsJobId: string | null;\n cronRuns: CronRunLogEntry[];\n cronBusy: boolean;\n};\n\nexport async function loadCronStatus(state: CronState) {\n if (!state.client || !state.connected) return;\n try {\n const res = (await state.client.request(\"cron.status\", {})) as CronStatus;\n state.cronStatus = res;\n } catch (err) {\n state.cronError = String(err);\n }\n}\n\nexport async function loadCronJobs(state: CronState) {\n if (!state.client || !state.connected) return;\n if (state.cronLoading) return;\n state.cronLoading = true;\n state.cronError = null;\n try {\n const res = (await state.client.request(\"cron.list\", {\n includeDisabled: true,\n })) as { jobs?: CronJob[] };\n state.cronJobs = Array.isArray(res.jobs) ? res.jobs : [];\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronLoading = false;\n }\n}\n\nexport function buildCronSchedule(form: CronFormState) {\n if (form.scheduleKind === \"at\") {\n const ms = Date.parse(form.scheduleAt);\n if (!Number.isFinite(ms)) throw new Error(\"Invalid run time.\");\n return { kind: \"at\" as const, atMs: ms };\n }\n if (form.scheduleKind === \"every\") {\n const amount = toNumber(form.everyAmount, 0);\n if (amount <= 0) throw new Error(\"Invalid interval amount.\");\n const unit = form.everyUnit;\n const mult = unit === \"minutes\" ? 60_000 : unit === \"hours\" ? 3_600_000 : 86_400_000;\n return { kind: \"every\" as const, everyMs: amount * mult };\n }\n const expr = form.cronExpr.trim();\n if (!expr) throw new Error(\"Cron expression required.\");\n return { kind: \"cron\" as const, expr, tz: form.cronTz.trim() || undefined };\n}\n\nexport function buildCronPayload(form: CronFormState) {\n if (form.payloadKind === \"systemEvent\") {\n const text = form.payloadText.trim();\n if (!text) throw new Error(\"System event text required.\");\n return { kind: \"systemEvent\" as const, text };\n }\n const message = form.payloadText.trim();\n if (!message) throw new Error(\"Agent message required.\");\n const payload: {\n kind: \"agentTurn\";\n message: string;\n deliver?: boolean;\n channel?: string;\n to?: string;\n timeoutSeconds?: number;\n } = { kind: \"agentTurn\", message };\n if (form.deliver) payload.deliver = true;\n if (form.channel) payload.channel = form.channel;\n if (form.to.trim()) payload.to = form.to.trim();\n const timeoutSeconds = toNumber(form.timeoutSeconds, 0);\n if (timeoutSeconds > 0) payload.timeoutSeconds = timeoutSeconds;\n return payload;\n}\n\nexport async function addCronJob(state: CronState) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n const schedule = buildCronSchedule(state.cronForm);\n const payload = buildCronPayload(state.cronForm);\n const agentId = state.cronForm.agentId.trim();\n const job = {\n name: state.cronForm.name.trim(),\n description: state.cronForm.description.trim() || undefined,\n agentId: agentId || undefined,\n enabled: state.cronForm.enabled,\n schedule,\n sessionTarget: state.cronForm.sessionTarget,\n wakeMode: state.cronForm.wakeMode,\n payload,\n isolation:\n state.cronForm.postToMainPrefix.trim() &&\n state.cronForm.sessionTarget === \"isolated\"\n ? { postToMainPrefix: state.cronForm.postToMainPrefix.trim() }\n : undefined,\n };\n if (!job.name) throw new Error(\"Name required.\");\n await state.client.request(\"cron.add\", job);\n state.cronForm = {\n ...state.cronForm,\n name: \"\",\n description: \"\",\n payloadText: \"\",\n };\n await loadCronJobs(state);\n await loadCronStatus(state);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function toggleCronJob(\n state: CronState,\n job: CronJob,\n enabled: boolean,\n) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n await state.client.request(\"cron.update\", { id: job.id, patch: { enabled } });\n await loadCronJobs(state);\n await loadCronStatus(state);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function runCronJob(state: CronState, job: CronJob) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n await state.client.request(\"cron.run\", { id: job.id, mode: \"force\" });\n await loadCronRuns(state, job.id);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function removeCronJob(state: CronState, job: CronJob) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n await state.client.request(\"cron.remove\", { id: job.id });\n if (state.cronRunsJobId === job.id) {\n state.cronRunsJobId = null;\n state.cronRuns = [];\n }\n await loadCronJobs(state);\n await loadCronStatus(state);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function loadCronRuns(state: CronState, jobId: string) {\n if (!state.client || !state.connected) return;\n try {\n const res = (await state.client.request(\"cron.runs\", {\n id: jobId,\n limit: 50,\n })) as { entries?: CronRunLogEntry[] };\n state.cronRunsJobId = jobId;\n state.cronRuns = Array.isArray(res.entries) ? res.entries : [];\n } catch (err) {\n state.cronError = String(err);\n }\n}\n","import type { ChannelsStatusSnapshot } from \"../types\";\nimport type { ChannelsState } from \"./channels.types\";\n\nexport type { ChannelsState };\n\nexport async function loadChannels(state: ChannelsState, probe: boolean) {\n if (!state.client || !state.connected) return;\n if (state.channelsLoading) return;\n state.channelsLoading = true;\n state.channelsError = null;\n try {\n const res = (await state.client.request(\"channels.status\", {\n probe,\n timeoutMs: 8000,\n })) as ChannelsStatusSnapshot;\n state.channelsSnapshot = res;\n state.channelsLastSuccess = Date.now();\n } catch (err) {\n state.channelsError = String(err);\n } finally {\n state.channelsLoading = false;\n }\n}\n\nexport async function startWhatsAppLogin(state: ChannelsState, force: boolean) {\n if (!state.client || !state.connected || state.whatsappBusy) return;\n state.whatsappBusy = true;\n try {\n const res = (await state.client.request(\"web.login.start\", {\n force,\n timeoutMs: 30000,\n })) as { message?: string; qrDataUrl?: string };\n state.whatsappLoginMessage = res.message ?? null;\n state.whatsappLoginQrDataUrl = res.qrDataUrl ?? null;\n state.whatsappLoginConnected = null;\n } catch (err) {\n state.whatsappLoginMessage = String(err);\n state.whatsappLoginQrDataUrl = null;\n state.whatsappLoginConnected = null;\n } finally {\n state.whatsappBusy = false;\n }\n}\n\nexport async function waitWhatsAppLogin(state: ChannelsState) {\n if (!state.client || !state.connected || state.whatsappBusy) return;\n state.whatsappBusy = true;\n try {\n const res = (await state.client.request(\"web.login.wait\", {\n timeoutMs: 120000,\n })) as { connected?: boolean; message?: string };\n state.whatsappLoginMessage = res.message ?? null;\n state.whatsappLoginConnected = res.connected ?? null;\n if (res.connected) state.whatsappLoginQrDataUrl = null;\n } catch (err) {\n state.whatsappLoginMessage = String(err);\n state.whatsappLoginConnected = null;\n } finally {\n state.whatsappBusy = false;\n }\n}\n\nexport async function logoutWhatsApp(state: ChannelsState) {\n if (!state.client || !state.connected || state.whatsappBusy) return;\n state.whatsappBusy = true;\n try {\n await state.client.request(\"channels.logout\", { channel: \"whatsapp\" });\n state.whatsappLoginMessage = \"Logged out.\";\n state.whatsappLoginQrDataUrl = null;\n state.whatsappLoginConnected = null;\n } catch (err) {\n state.whatsappLoginMessage = String(err);\n } finally {\n state.whatsappBusy = false;\n }\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { HealthSnapshot, StatusSummary } from \"../types\";\n\nexport type DebugState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n debugLoading: boolean;\n debugStatus: StatusSummary | null;\n debugHealth: HealthSnapshot | null;\n debugModels: unknown[];\n debugHeartbeat: unknown | null;\n debugCallMethod: string;\n debugCallParams: string;\n debugCallResult: string | null;\n debugCallError: string | null;\n};\n\nexport async function loadDebug(state: DebugState) {\n if (!state.client || !state.connected) return;\n if (state.debugLoading) return;\n state.debugLoading = true;\n try {\n const [status, health, models, heartbeat] = await Promise.all([\n state.client.request(\"status\", {}),\n state.client.request(\"health\", {}),\n state.client.request(\"models.list\", {}),\n state.client.request(\"last-heartbeat\", {}),\n ]);\n state.debugStatus = status as StatusSummary;\n state.debugHealth = health as HealthSnapshot;\n const modelPayload = models as { models?: unknown[] } | undefined;\n state.debugModels = Array.isArray(modelPayload?.models)\n ? modelPayload?.models\n : [];\n state.debugHeartbeat = heartbeat as unknown;\n } catch (err) {\n state.debugCallError = String(err);\n } finally {\n state.debugLoading = false;\n }\n}\n\nexport async function callDebugMethod(state: DebugState) {\n if (!state.client || !state.connected) return;\n state.debugCallError = null;\n state.debugCallResult = null;\n try {\n const params = state.debugCallParams.trim()\n ? (JSON.parse(state.debugCallParams) as unknown)\n : {};\n const res = await state.client.request(state.debugCallMethod.trim(), params);\n state.debugCallResult = JSON.stringify(res, null, 2);\n } catch (err) {\n state.debugCallError = String(err);\n }\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { LogEntry, LogLevel } from \"../types\";\n\nexport type LogsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n logsLoading: boolean;\n logsError: string | null;\n logsCursor: number | null;\n logsFile: string | null;\n logsEntries: LogEntry[];\n logsTruncated: boolean;\n logsLastFetchAt: number | null;\n logsLimit: number;\n logsMaxBytes: number;\n};\n\nconst LOG_BUFFER_LIMIT = 2000;\nconst LEVELS = new Set([\n \"trace\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\",\n \"fatal\",\n]);\n\nfunction parseMaybeJsonString(value: unknown) {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"{\") || !trimmed.endsWith(\"}\")) return null;\n try {\n const parsed = JSON.parse(trimmed) as unknown;\n if (!parsed || typeof parsed !== \"object\") return null;\n return parsed as Record;\n } catch {\n return null;\n }\n}\n\nfunction normalizeLevel(value: unknown): LogLevel | null {\n if (typeof value !== \"string\") return null;\n const lowered = value.toLowerCase() as LogLevel;\n return LEVELS.has(lowered) ? lowered : null;\n}\n\nexport function parseLogLine(line: string): LogEntry {\n if (!line.trim()) return { raw: line, message: line };\n try {\n const obj = JSON.parse(line) as Record;\n const meta =\n obj && typeof obj._meta === \"object\" && obj._meta !== null\n ? (obj._meta as Record)\n : null;\n const time =\n typeof obj.time === \"string\"\n ? obj.time\n : typeof meta?.date === \"string\"\n ? meta?.date\n : null;\n const level = normalizeLevel(meta?.logLevelName ?? meta?.level);\n\n const contextCandidate =\n typeof obj[\"0\"] === \"string\"\n ? (obj[\"0\"] as string)\n : typeof meta?.name === \"string\"\n ? (meta?.name as string)\n : null;\n const contextObj = parseMaybeJsonString(contextCandidate);\n let subsystem: string | null = null;\n if (contextObj) {\n if (typeof contextObj.subsystem === \"string\") subsystem = contextObj.subsystem;\n else if (typeof contextObj.module === \"string\") subsystem = contextObj.module;\n }\n if (!subsystem && contextCandidate && contextCandidate.length < 120) {\n subsystem = contextCandidate;\n }\n\n let message: string | null = null;\n if (typeof obj[\"1\"] === \"string\") message = obj[\"1\"] as string;\n else if (!contextObj && typeof obj[\"0\"] === \"string\") message = obj[\"0\"] as string;\n else if (typeof obj.message === \"string\") message = obj.message as string;\n\n return {\n raw: line,\n time,\n level,\n subsystem,\n message: message ?? line,\n meta: meta ?? undefined,\n };\n } catch {\n return { raw: line, message: line };\n }\n}\n\nexport async function loadLogs(\n state: LogsState,\n opts?: { reset?: boolean; quiet?: boolean },\n) {\n if (!state.client || !state.connected) return;\n if (state.logsLoading && !opts?.quiet) return;\n if (!opts?.quiet) state.logsLoading = true;\n state.logsError = null;\n try {\n const res = await state.client.request(\"logs.tail\", {\n cursor: opts?.reset ? undefined : state.logsCursor ?? undefined,\n limit: state.logsLimit,\n maxBytes: state.logsMaxBytes,\n });\n const payload = res as {\n file?: string;\n cursor?: number;\n size?: number;\n lines?: unknown;\n truncated?: boolean;\n reset?: boolean;\n };\n const lines = Array.isArray(payload.lines)\n ? (payload.lines.filter((line) => typeof line === \"string\") as string[])\n : [];\n const entries = lines.map(parseLogLine);\n const shouldReset = Boolean(opts?.reset || payload.reset || state.logsCursor == null);\n state.logsEntries = shouldReset\n ? entries\n : [...state.logsEntries, ...entries].slice(-LOG_BUFFER_LIMIT);\n if (typeof payload.cursor === \"number\") state.logsCursor = payload.cursor;\n if (typeof payload.file === \"string\") state.logsFile = payload.file;\n state.logsTruncated = Boolean(payload.truncated);\n state.logsLastFetchAt = Date.now();\n } catch (err) {\n state.logsError = String(err);\n } finally {\n if (!opts?.quiet) state.logsLoading = false;\n }\n}\n","/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */\n/**\n * 5KB JS implementation of ed25519 EdDSA signatures.\n * Compliant with RFC8032, FIPS 186-5 & ZIP215.\n * @module\n * @example\n * ```js\nimport * as ed from '@noble/ed25519';\n(async () => {\n const secretKey = ed.utils.randomSecretKey();\n const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);\n const pubKey = await ed.getPublicKeyAsync(secretKey); // Sync methods are also present\n const signature = await ed.signAsync(message, secretKey);\n const isValid = await ed.verifyAsync(signature, message, pubKey);\n})();\n```\n */\n/**\n * Curve params. ed25519 is twisted edwards curve. Equation is −x² + y² = -a + dx²y².\n * * P = `2n**255n - 19n` // field over which calculations are done\n * * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points\n * * h = 8 // cofactor\n * * a = `Fp.create(BigInt(-1))` // equation param\n * * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param\n * * Gx, Gy are coordinates of Generator / base point\n */\nconst ed25519_CURVE = {\n p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,\n n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,\n h: 8n,\n a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,\n d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,\n Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,\n Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,\n};\nconst { p: P, n: N, Gx, Gy, a: _a, d: _d, h } = ed25519_CURVE;\nconst L = 32; // field / group byte length\nconst L2 = 64;\n// Helpers and Precomputes sections are reused between libraries\n// ## Helpers\n// ----------\nconst captureTrace = (...args) => {\n if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(...args);\n }\n};\nconst err = (message = '') => {\n const e = new Error(message);\n captureTrace(e, err);\n throw e;\n};\nconst isBig = (n) => typeof n === 'bigint'; // is big integer\nconst isStr = (s) => typeof s === 'string'; // is string\nconst isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n/** Asserts something is Uint8Array. */\nconst abytes = (value, length, title = '') => {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n err(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);\n }\n return value;\n};\n/** create Uint8Array */\nconst u8n = (len) => new Uint8Array(len);\nconst u8fr = (buf) => Uint8Array.from(buf);\nconst padh = (n, pad) => n.toString(16).padStart(pad, '0');\nconst bytesToHex = (b) => Array.from(abytes(b))\n .map((e) => padh(e, 2))\n .join('');\nconst C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters\nconst _ch = (ch) => {\n if (ch >= C._0 && ch <= C._9)\n return ch - C._0; // '2' => 50-48\n if (ch >= C.A && ch <= C.F)\n return ch - (C.A - 10); // 'B' => 66-(65-10)\n if (ch >= C.a && ch <= C.f)\n return ch - (C.a - 10); // 'b' => 98-(97-10)\n return;\n};\nconst hexToBytes = (hex) => {\n const e = 'hex invalid';\n if (!isStr(hex))\n return err(e);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2)\n return err(e);\n const array = u8n(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n // treat each char as ASCII\n const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16\n const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char\n if (n1 === undefined || n2 === undefined)\n return err(e);\n array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9\n }\n return array;\n};\nconst cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments\nconst subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined, consider polyfill');\n// prettier-ignore\nconst concatBytes = (...arrs) => {\n const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); // create u8a of summed length\n let pad = 0; // walk through each array,\n arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type\n return r;\n};\n/** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */\nconst randomBytes = (len = L) => {\n const c = cr();\n return c.getRandomValues(u8n(len));\n};\nconst big = BigInt;\nconst assertRange = (n, min, max, msg = 'bad number: out of range') => (isBig(n) && min <= n && n < max ? n : err(msg));\n/** modular division */\nconst M = (a, b = P) => {\n const r = a % b;\n return r >= 0n ? r : b + r;\n};\nconst modN = (a) => M(a, N);\n/** Modular inversion using euclidean GCD (non-CT). No negative exponent for now. */\n// prettier-ignore\nconst invert = (num, md) => {\n if (num === 0n || md <= 0n)\n err('no inverse n=' + num + ' mod=' + md);\n let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;\n while (a !== 0n) {\n const q = b / a, r = b % a;\n const m = x - u * q, n = y - v * q;\n b = a, a = r, x = u, y = v, u = m, v = n;\n }\n return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point\n};\nconst callHash = (name) => {\n // @ts-ignore\n const fn = hashes[name];\n if (typeof fn !== 'function')\n err('hashes.' + name + ' not set');\n return fn;\n};\nconst hash = (msg) => callHash('sha512')(msg);\nconst apoint = (p) => (p instanceof Point ? p : err('Point expected'));\n// ## End of Helpers\n// -----------------\nconst B256 = 2n ** 256n;\n/** Point in XYZT extended coordinates. */\nclass Point {\n static BASE;\n static ZERO;\n X;\n Y;\n Z;\n T;\n constructor(X, Y, Z, T) {\n const max = B256;\n this.X = assertRange(X, 0n, max);\n this.Y = assertRange(Y, 0n, max);\n this.Z = assertRange(Z, 1n, max);\n this.T = assertRange(T, 0n, max);\n Object.freeze(this);\n }\n static CURVE() {\n return ed25519_CURVE;\n }\n static fromAffine(p) {\n return new Point(p.x, p.y, 1n, M(p.x * p.y));\n }\n /** RFC8032 5.1.3: Uint8Array to Point. */\n static fromBytes(hex, zip215 = false) {\n const d = _d;\n // Copy array to not mess it up.\n const normed = u8fr(abytes(hex, L));\n // adjust first LE byte = last BE byte\n const lastByte = hex[31];\n normed[31] = lastByte & ~0x80;\n const y = bytesToNumLE(normed);\n // zip215=true: 0 <= y < 2^256\n // zip215=false, RFC8032: 0 <= y < 2^255-19\n const max = zip215 ? B256 : P;\n assertRange(y, 0n, max);\n const y2 = M(y * y); // y²\n const u = M(y2 - 1n); // u=y²-1\n const v = M(d * y2 + 1n); // v=dy²+1\n let { isValid, value: x } = uvRatio(u, v); // (uv³)(uv⁷)^(p-5)/8; square root\n if (!isValid)\n err('bad point: y not sqrt'); // not square root: bad point\n const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate\n const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit\n if (!zip215 && x === 0n && isLastByteOdd)\n err('bad point: x==0, isLastByteOdd'); // x=0, x_0=1\n if (isLastByteOdd !== isXOdd)\n x = M(-x);\n return new Point(x, y, 1n, M(x * y)); // Z=1, T=xy\n }\n static fromHex(hex, zip215) {\n return Point.fromBytes(hexToBytes(hex), zip215);\n }\n get x() {\n return this.toAffine().x;\n }\n get y() {\n return this.toAffine().y;\n }\n /** Checks if the point is valid and on-curve. */\n assertValidity() {\n const a = _a;\n const d = _d;\n const p = this;\n if (p.is0())\n return err('bad point: ZERO'); // TODO: optimize, with vars below?\n // Equation in affine coordinates: ax² + y² = 1 + dx²y²\n // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²\n const { X, Y, Z, T } = p;\n const X2 = M(X * X); // X²\n const Y2 = M(Y * Y); // Y²\n const Z2 = M(Z * Z); // Z²\n const Z4 = M(Z2 * Z2); // Z⁴\n const aX2 = M(X2 * a); // aX²\n const left = M(Z2 * M(aX2 + Y2)); // (aX² + Y²)Z²\n const right = M(Z4 + M(d * M(X2 * Y2))); // Z⁴ + dX²Y²\n if (left !== right)\n return err('bad point: equation left != right (1)');\n // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T\n const XY = M(X * Y);\n const ZT = M(Z * T);\n if (XY !== ZT)\n return err('bad point: equation left != right (2)');\n return this;\n }\n /** Equality check: compare points P&Q. */\n equals(other) {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality\n const X1Z2 = M(X1 * Z2);\n const X2Z1 = M(X2 * Z1);\n const Y1Z2 = M(Y1 * Z2);\n const Y2Z1 = M(Y2 * Z1);\n return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;\n }\n is0() {\n return this.equals(I);\n }\n /** Flip point over y coordinate. */\n negate() {\n return new Point(M(-this.X), this.Y, this.Z, M(-this.T));\n }\n /** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */\n double() {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const a = _a;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd\n const A = M(X1 * X1);\n const B = M(Y1 * Y1);\n const C = M(2n * M(Z1 * Z1));\n const D = M(a * A);\n const x1y1 = X1 + Y1;\n const E = M(M(x1y1 * x1y1) - A - B);\n const G = D + B;\n const F = G - C;\n const H = D - B;\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n /** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */\n add(other) {\n const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;\n const { X: X2, Y: Y2, Z: Z2, T: T2 } = apoint(other); // doesn't check if other on-curve\n const a = _a;\n const d = _d;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3\n const A = M(X1 * X2);\n const B = M(Y1 * Y2);\n const C = M(T1 * d * T2);\n const D = M(Z1 * Z2);\n const E = M((X1 + Y1) * (X2 + Y2) - A - B);\n const F = M(D - C);\n const G = M(D + C);\n const H = M(B - a * A);\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n subtract(other) {\n return this.add(apoint(other).negate());\n }\n /**\n * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.\n * Uses {@link wNAF} for base point.\n * Uses fake point to mitigate side-channel leakage.\n * @param n scalar by which point is multiplied\n * @param safe safe mode guards against timing attacks; unsafe mode is faster\n */\n multiply(n, safe = true) {\n if (!safe && (n === 0n || this.is0()))\n return I;\n assertRange(n, 1n, N);\n if (n === 1n)\n return this;\n if (this.equals(G))\n return wNAF(n).p;\n // init result point & fake point\n let p = I;\n let f = G;\n for (let d = this; n > 0n; d = d.double(), n >>= 1n) {\n // if bit is present, add to point\n // if not present, add to fake, for timing safety\n if (n & 1n)\n p = p.add(d);\n else if (safe)\n f = f.add(d);\n }\n return p;\n }\n multiplyUnsafe(scalar) {\n return this.multiply(scalar, false);\n }\n /** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */\n toAffine() {\n const { X, Y, Z } = this;\n // fast-paths for ZERO point OR Z=1\n if (this.equals(I))\n return { x: 0n, y: 1n };\n const iz = invert(Z, P);\n // (Z * Z^-1) must be 1, otherwise bad math\n if (M(Z * iz) !== 1n)\n err('invalid inverse');\n // x = X*Z^-1; y = Y*Z^-1\n const x = M(X * iz);\n const y = M(Y * iz);\n return { x, y };\n }\n toBytes() {\n const { x, y } = this.assertValidity().toAffine();\n const b = numTo32bLE(y);\n // store sign in first LE byte\n b[31] |= x & 1n ? 0x80 : 0;\n return b;\n }\n toHex() {\n return bytesToHex(this.toBytes());\n }\n clearCofactor() {\n return this.multiply(big(h), false);\n }\n isSmallOrder() {\n return this.clearCofactor().is0();\n }\n isTorsionFree() {\n // Multiply by big number N. We can't `mul(N)` because of checks. Instead, we `mul(N/2)*2+1`\n let p = this.multiply(N / 2n, false).double();\n if (N % 2n)\n p = p.add(this);\n return p.is0();\n }\n}\n/** Generator / base point */\nconst G = new Point(Gx, Gy, 1n, M(Gx * Gy));\n/** Identity / zero point */\nconst I = new Point(0n, 1n, 1n, 0n);\n// Static aliases\nPoint.BASE = G;\nPoint.ZERO = I;\nconst numTo32bLE = (num) => hexToBytes(padh(assertRange(num, 0n, B256), L2)).reverse();\nconst bytesToNumLE = (b) => big('0x' + bytesToHex(u8fr(abytes(b)).reverse()));\nconst pow2 = (x, power) => {\n // pow2(x, 4) == x^(2^4)\n let r = x;\n while (power-- > 0n) {\n r *= r;\n r %= P;\n }\n return r;\n};\n// prettier-ignore\nconst pow_2_252_3 = (x) => {\n const x2 = (x * x) % P; // x^2, bits 1\n const b2 = (x2 * x) % P; // x^3, bits 11\n const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111\n const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111\n const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)\n const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)\n const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)\n const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)\n const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)\n const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)\n const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)\n const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.\n return { pow_p_5_8, b2 };\n};\nconst RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; // √-1\n// for sqrt comp\n// prettier-ignore\nconst uvRatio = (u, v) => {\n const v3 = M(v * v * v); // v³\n const v7 = M(v3 * v3 * v); // v⁷\n const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv⁷)^(p-5)/8\n let x = M(u * v3 * pow); // (uv³)(uv⁷)^(p-5)/8\n const vx2 = M(v * x * x); // vx²\n const root1 = x; // First root candidate\n const root2 = M(x * RM1); // Second root candidate; RM1 is √-1\n const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root\n const useRoot2 = vx2 === M(-u); // If vx² = -u, set x <-- x * 2^((p-1)/4)\n const noRoot = vx2 === M(-u * RM1); // There is no valid root, vx² = -u√-1\n if (useRoot1)\n x = root1;\n if (useRoot2 || noRoot)\n x = root2; // We return root2 anyway, for const-time\n if ((M(x) & 1n) === 1n)\n x = M(-x); // edIsNegative\n return { isValid: useRoot1 || useRoot2, value: x };\n};\n// N == L, just weird naming\nconst modL_LE = (hash) => modN(bytesToNumLE(hash)); // modulo L; but little-endian\n/** hashes.sha512 should conform to the interface. */\n// TODO: rename\nconst sha512a = (...m) => hashes.sha512Async(concatBytes(...m)); // Async SHA512\nconst sha512s = (...m) => callHash('sha512')(concatBytes(...m));\n// RFC8032 5.1.5\nconst hash2extK = (hashed) => {\n // slice creates a copy, unlike subarray\n const head = hashed.slice(0, L);\n head[0] &= 248; // Clamp bits: 0b1111_1000\n head[31] &= 127; // 0b0111_1111\n head[31] |= 64; // 0b0100_0000\n const prefix = hashed.slice(L, L2); // secret key \"prefix\"\n const scalar = modL_LE(head); // modular division over curve order\n const point = G.multiply(scalar); // public key point\n const pointBytes = point.toBytes(); // point serialized to Uint8Array\n return { head, prefix, scalar, point, pointBytes };\n};\n// RFC8032 5.1.5; getPublicKey async, sync. Hash priv key and extract point.\nconst getExtendedPublicKeyAsync = (secretKey) => sha512a(abytes(secretKey, L)).then(hash2extK);\nconst getExtendedPublicKey = (secretKey) => hash2extK(sha512s(abytes(secretKey, L)));\n/** Creates 32-byte ed25519 public key from 32-byte secret key. Async. */\nconst getPublicKeyAsync = (secretKey) => getExtendedPublicKeyAsync(secretKey).then((p) => p.pointBytes);\n/** Creates 32-byte ed25519 public key from 32-byte secret key. To use, set `hashes.sha512` first. */\nconst getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;\nconst hashFinishA = (res) => sha512a(res.hashable).then(res.finish);\nconst hashFinishS = (res) => res.finish(sha512s(res.hashable));\n// Code, shared between sync & async sign\nconst _sign = (e, rBytes, msg) => {\n const { pointBytes: P, scalar: s } = e;\n const r = modL_LE(rBytes); // r was created outside, reduce it modulo L\n const R = G.multiply(r).toBytes(); // R = [r]B\n const hashable = concatBytes(R, P, msg); // dom2(F, C) || R || A || PH(M)\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n const S = modN(r + modL_LE(hashed) * s); // S = (r + k * s) mod L; 0 <= s < l\n return abytes(concatBytes(R, numTo32bLE(S)), L2); // 64-byte sig: 32b R.x + 32b LE(S)\n };\n return { hashable, finish };\n};\n/**\n * Signs message using secret key. Async.\n * Follows RFC8032 5.1.6.\n */\nconst signAsync = async (message, secretKey) => {\n const m = abytes(message);\n const e = await getExtendedPublicKeyAsync(secretKey);\n const rBytes = await sha512a(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishA(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\n/**\n * Signs message using secret key. To use, set `hashes.sha512` first.\n * Follows RFC8032 5.1.6.\n */\nconst sign = (message, secretKey) => {\n const m = abytes(message);\n const e = getExtendedPublicKey(secretKey);\n const rBytes = sha512s(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishS(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\nconst defaultVerifyOpts = { zip215: true };\nconst _verify = (sig, msg, pub, opts = defaultVerifyOpts) => {\n sig = abytes(sig, L2); // Signature hex str/Bytes, must be 64 bytes\n msg = abytes(msg); // Message hex str/Bytes\n pub = abytes(pub, L);\n const { zip215 } = opts; // switch between zip215 and rfc8032 verif\n let A;\n let R;\n let s;\n let SB;\n let hashable = Uint8Array.of();\n try {\n A = Point.fromBytes(pub, zip215); // public key A decoded\n R = Point.fromBytes(sig.slice(0, L), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P\n s = bytesToNumLE(sig.slice(L, L2)); // Decode second half as an integer S\n SB = G.multiply(s, false); // in the range 0 <= s < L\n hashable = concatBytes(R.toBytes(), A.toBytes(), msg); // dom2(F, C) || R || A || PH(M)\n }\n catch (error) { }\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n if (SB == null)\n return false; // false if try-catch catched an error\n if (!zip215 && A.isSmallOrder())\n return false; // false for SBS: Strongly Binding Signature\n const k = modL_LE(hashed); // decode in little-endian, modulo L\n const RkA = R.add(A.multiply(k, false)); // [8]R + [8][k]A'\n return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A'\n };\n return { hashable, finish };\n};\n/** Verifies signature on message and public key. Async. Follows RFC8032 5.1.7. */\nconst verifyAsync = async (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishA(_verify(signature, message, publicKey, opts));\n/** Verifies signature on message and public key. To use, set `hashes.sha512` first. Follows RFC8032 5.1.7. */\nconst verify = (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishS(_verify(signature, message, publicKey, opts));\n/** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */\nconst etc = {\n bytesToHex: bytesToHex,\n hexToBytes: hexToBytes,\n concatBytes: concatBytes,\n mod: M,\n invert: invert,\n randomBytes: randomBytes,\n};\nconst hashes = {\n sha512Async: async (message) => {\n const s = subtle();\n const m = concatBytes(message);\n return u8n(await s.digest('SHA-512', m.buffer));\n },\n sha512: undefined,\n};\n// FIPS 186 B.4.1 compliant key generation produces private keys\n// with modulo bias being neglible. takes >N+16 bytes, returns (hash mod n-1)+1\nconst randomSecretKey = (seed = randomBytes(L)) => seed;\nconst keygen = (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = getPublicKey(secretKey);\n return { secretKey, publicKey };\n};\nconst keygenAsync = async (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = await getPublicKeyAsync(secretKey);\n return { secretKey, publicKey };\n};\n/** ed25519-specific key utilities. */\nconst utils = {\n getExtendedPublicKeyAsync: getExtendedPublicKeyAsync,\n getExtendedPublicKey: getExtendedPublicKey,\n randomSecretKey: randomSecretKey,\n};\n// ## Precomputes\n// --------------\nconst W = 8; // W is window size\nconst scalarBits = 256;\nconst pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8, NOT 32 - see wNAF loop\nconst pwindowSize = 2 ** (W - 1); // 128 for W=8\nconst precompute = () => {\n const points = [];\n let p = G;\n let b = p;\n for (let w = 0; w < pwindows; w++) {\n b = p;\n points.push(b);\n for (let i = 1; i < pwindowSize; i++) {\n b = b.add(p);\n points.push(b);\n } // i=1, bc we skip 0\n p = b.double();\n }\n return points;\n};\nlet Gpows = undefined; // precomputes for base point G\n// const-time negate\nconst ctneg = (cnd, p) => {\n const n = p.negate();\n return cnd ? n : p;\n};\n/**\n * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by\n * caching multiples of G (base point). Cache is stored in 32MB of RAM.\n * Any time `G.multiply` is done, precomputes are used.\n * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.\n *\n * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,\n * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.\n *\n * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().\n */\nconst wNAF = (n) => {\n const comp = Gpows || (Gpows = precompute());\n let p = I;\n let f = G; // f must be G, or could become I in the end\n const pow_2_w = 2 ** W; // 256 for W=8\n const maxNum = pow_2_w; // 256 for W=8\n const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111\n const shiftBy = big(W); // 8 for W=8\n for (let w = 0; w < pwindows; w++) {\n let wbits = Number(n & mask); // extract W bits.\n n >>= shiftBy; // shift number by W bits.\n // We use negative indexes to reduce size of precomputed table by 2x.\n // Instead of needing precomputes 0..256, we only calculate them for 0..128.\n // If an index > 128 is found, we do (256-index) - where 256 is next window.\n // Naive: index +127 => 127, +224 => 224\n // Optimized: index +127 => 127, +224 => 256-32\n if (wbits > pwindowSize) {\n wbits -= maxNum;\n n += 1n;\n }\n const off = w * pwindowSize;\n const offF = off; // offsets, evaluate both\n const offP = off + Math.abs(wbits) - 1;\n const isEven = w % 2 !== 0; // conditions, evaluate both\n const isNeg = wbits < 0;\n if (wbits === 0) {\n // off == I: can't add it. Adding random offF instead.\n f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point\n }\n else {\n p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point\n }\n }\n if (n !== 0n)\n err('invalid wnaf');\n return { p, f }; // return both real and fake points for JIT\n};\n// !! Remove the export to easily use in REPL / browser console\nexport { etc, getPublicKey, getPublicKeyAsync, hash, hashes, keygen, keygenAsync, Point, sign, signAsync, utils, verify, verifyAsync, };\n","import { getPublicKeyAsync, signAsync, utils } from \"@noble/ed25519\";\n\ntype StoredIdentity = {\n version: 1;\n deviceId: string;\n publicKey: string;\n privateKey: string;\n createdAtMs: number;\n};\n\nexport type DeviceIdentity = {\n deviceId: string;\n publicKey: string;\n privateKey: string;\n};\n\nconst STORAGE_KEY = \"clawdbot-device-identity-v1\";\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/g, \"\");\n}\n\nfunction base64UrlDecode(input: string): Uint8Array {\n const normalized = input.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\");\n const padded = normalized + \"=\".repeat((4 - (normalized.length % 4)) % 4);\n const binary = atob(padded);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);\n return out;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nasync function fingerprintPublicKey(publicKey: Uint8Array): Promise {\n const hash = await crypto.subtle.digest(\"SHA-256\", publicKey);\n return bytesToHex(new Uint8Array(hash));\n}\n\nasync function generateIdentity(): Promise {\n const privateKey = utils.randomSecretKey();\n const publicKey = await getPublicKeyAsync(privateKey);\n const deviceId = await fingerprintPublicKey(publicKey);\n return {\n deviceId,\n publicKey: base64UrlEncode(publicKey),\n privateKey: base64UrlEncode(privateKey),\n };\n}\n\nexport async function loadOrCreateDeviceIdentity(): Promise {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (raw) {\n const parsed = JSON.parse(raw) as StoredIdentity;\n if (\n parsed?.version === 1 &&\n typeof parsed.deviceId === \"string\" &&\n typeof parsed.publicKey === \"string\" &&\n typeof parsed.privateKey === \"string\"\n ) {\n const derivedId = await fingerprintPublicKey(base64UrlDecode(parsed.publicKey));\n if (derivedId !== parsed.deviceId) {\n const updated: StoredIdentity = {\n ...parsed,\n deviceId: derivedId,\n };\n localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));\n return {\n deviceId: derivedId,\n publicKey: parsed.publicKey,\n privateKey: parsed.privateKey,\n };\n }\n return {\n deviceId: parsed.deviceId,\n publicKey: parsed.publicKey,\n privateKey: parsed.privateKey,\n };\n }\n }\n } catch {\n // fall through to regenerate\n }\n\n const identity = await generateIdentity();\n const stored: StoredIdentity = {\n version: 1,\n deviceId: identity.deviceId,\n publicKey: identity.publicKey,\n privateKey: identity.privateKey,\n createdAtMs: Date.now(),\n };\n localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));\n return identity;\n}\n\nexport async function signDevicePayload(privateKeyBase64Url: string, payload: string) {\n const key = base64UrlDecode(privateKeyBase64Url);\n const data = new TextEncoder().encode(payload);\n const sig = await signAsync(data, key);\n return base64UrlEncode(sig);\n}\n","export type DeviceAuthEntry = {\n token: string;\n role: string;\n scopes: string[];\n updatedAtMs: number;\n};\n\ntype DeviceAuthStore = {\n version: 1;\n deviceId: string;\n tokens: Record;\n};\n\nconst STORAGE_KEY = \"clawdbot.device.auth.v1\";\n\nfunction normalizeRole(role: string): string {\n return role.trim();\n}\n\nfunction normalizeScopes(scopes: string[] | undefined): string[] {\n if (!Array.isArray(scopes)) return [];\n const out = new Set();\n for (const scope of scopes) {\n const trimmed = scope.trim();\n if (trimmed) out.add(trimmed);\n }\n return [...out].sort();\n}\n\nfunction readStore(): DeviceAuthStore | null {\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as DeviceAuthStore;\n if (!parsed || parsed.version !== 1) return null;\n if (!parsed.deviceId || typeof parsed.deviceId !== \"string\") return null;\n if (!parsed.tokens || typeof parsed.tokens !== \"object\") return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction writeStore(store: DeviceAuthStore) {\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(store));\n } catch {\n // best-effort\n }\n}\n\nexport function loadDeviceAuthToken(params: {\n deviceId: string;\n role: string;\n}): DeviceAuthEntry | null {\n const store = readStore();\n if (!store || store.deviceId !== params.deviceId) return null;\n const role = normalizeRole(params.role);\n const entry = store.tokens[role];\n if (!entry || typeof entry.token !== \"string\") return null;\n return entry;\n}\n\nexport function storeDeviceAuthToken(params: {\n deviceId: string;\n role: string;\n token: string;\n scopes?: string[];\n}): DeviceAuthEntry {\n const role = normalizeRole(params.role);\n const next: DeviceAuthStore = {\n version: 1,\n deviceId: params.deviceId,\n tokens: {},\n };\n const existing = readStore();\n if (existing && existing.deviceId === params.deviceId) {\n next.tokens = { ...existing.tokens };\n }\n const entry: DeviceAuthEntry = {\n token: params.token,\n role,\n scopes: normalizeScopes(params.scopes),\n updatedAtMs: Date.now(),\n };\n next.tokens[role] = entry;\n writeStore(next);\n return entry;\n}\n\nexport function clearDeviceAuthToken(params: { deviceId: string; role: string }) {\n const store = readStore();\n if (!store || store.deviceId !== params.deviceId) return;\n const role = normalizeRole(params.role);\n if (!store.tokens[role]) return;\n const next = { ...store, tokens: { ...store.tokens } };\n delete next.tokens[role];\n writeStore(next);\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { loadOrCreateDeviceIdentity } from \"../device-identity\";\nimport { clearDeviceAuthToken, storeDeviceAuthToken } from \"../device-auth\";\n\nexport type DeviceTokenSummary = {\n role: string;\n scopes?: string[];\n createdAtMs?: number;\n rotatedAtMs?: number;\n revokedAtMs?: number;\n lastUsedAtMs?: number;\n};\n\nexport type PendingDevice = {\n requestId: string;\n deviceId: string;\n displayName?: string;\n role?: string;\n remoteIp?: string;\n isRepair?: boolean;\n ts?: number;\n};\n\nexport type PairedDevice = {\n deviceId: string;\n displayName?: string;\n roles?: string[];\n scopes?: string[];\n remoteIp?: string;\n tokens?: DeviceTokenSummary[];\n createdAtMs?: number;\n approvedAtMs?: number;\n};\n\nexport type DevicePairingList = {\n pending: PendingDevice[];\n paired: PairedDevice[];\n};\n\nexport type DevicesState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n devicesLoading: boolean;\n devicesError: string | null;\n devicesList: DevicePairingList | null;\n};\n\nexport async function loadDevices(state: DevicesState, opts?: { quiet?: boolean }) {\n if (!state.client || !state.connected) return;\n if (state.devicesLoading) return;\n state.devicesLoading = true;\n if (!opts?.quiet) state.devicesError = null;\n try {\n const res = (await state.client.request(\"device.pair.list\", {})) as DevicePairingList | null;\n state.devicesList = {\n pending: Array.isArray(res?.pending) ? res!.pending : [],\n paired: Array.isArray(res?.paired) ? res!.paired : [],\n };\n } catch (err) {\n if (!opts?.quiet) state.devicesError = String(err);\n } finally {\n state.devicesLoading = false;\n }\n}\n\nexport async function approveDevicePairing(state: DevicesState, requestId: string) {\n if (!state.client || !state.connected) return;\n try {\n await state.client.request(\"device.pair.approve\", { requestId });\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n\nexport async function rejectDevicePairing(state: DevicesState, requestId: string) {\n if (!state.client || !state.connected) return;\n const confirmed = window.confirm(\"Reject this device pairing request?\");\n if (!confirmed) return;\n try {\n await state.client.request(\"device.pair.reject\", { requestId });\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n\nexport async function rotateDeviceToken(\n state: DevicesState,\n params: { deviceId: string; role: string; scopes?: string[] },\n) {\n if (!state.client || !state.connected) return;\n try {\n const res = (await state.client.request(\"device.token.rotate\", params)) as\n | { token?: string; role?: string; deviceId?: string; scopes?: string[] }\n | undefined;\n if (res?.token) {\n const identity = await loadOrCreateDeviceIdentity();\n const role = res.role ?? params.role;\n if (res.deviceId === identity.deviceId || params.deviceId === identity.deviceId) {\n storeDeviceAuthToken({\n deviceId: identity.deviceId,\n role,\n token: res.token,\n scopes: res.scopes ?? params.scopes ?? [],\n });\n }\n window.prompt(\"New device token (copy and store securely):\", res.token);\n }\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n\nexport async function revokeDeviceToken(\n state: DevicesState,\n params: { deviceId: string; role: string },\n) {\n if (!state.client || !state.connected) return;\n const confirmed = window.confirm(\n `Revoke token for ${params.deviceId} (${params.role})?`,\n );\n if (!confirmed) return;\n try {\n await state.client.request(\"device.token.revoke\", params);\n const identity = await loadOrCreateDeviceIdentity();\n if (params.deviceId === identity.deviceId) {\n clearDeviceAuthToken({ deviceId: identity.deviceId, role: params.role });\n }\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\n\nexport type NodesState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n nodesLoading: boolean;\n nodes: Array>;\n lastError: string | null;\n};\n\nexport async function loadNodes(\n state: NodesState,\n opts?: { quiet?: boolean },\n) {\n if (!state.client || !state.connected) return;\n if (state.nodesLoading) return;\n state.nodesLoading = true;\n if (!opts?.quiet) state.lastError = null;\n try {\n const res = (await state.client.request(\"node.list\", {})) as {\n nodes?: Array>;\n };\n state.nodes = Array.isArray(res.nodes) ? res.nodes : [];\n } catch (err) {\n if (!opts?.quiet) state.lastError = String(err);\n } finally {\n state.nodesLoading = false;\n }\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { cloneConfigObject, removePathValue, setPathValue } from \"./config/form-utils\";\n\nexport type ExecApprovalsDefaults = {\n security?: string;\n ask?: string;\n askFallback?: string;\n autoAllowSkills?: boolean;\n};\n\nexport type ExecApprovalsAllowlistEntry = {\n id?: string;\n pattern: string;\n lastUsedAt?: number;\n lastUsedCommand?: string;\n lastResolvedPath?: string;\n};\n\nexport type ExecApprovalsAgent = ExecApprovalsDefaults & {\n allowlist?: ExecApprovalsAllowlistEntry[];\n};\n\nexport type ExecApprovalsFile = {\n version?: number;\n socket?: { path?: string };\n defaults?: ExecApprovalsDefaults;\n agents?: Record;\n};\n\nexport type ExecApprovalsSnapshot = {\n path: string;\n exists: boolean;\n hash: string;\n file: ExecApprovalsFile;\n};\n\nexport type ExecApprovalsTarget =\n | { kind: \"gateway\" }\n | { kind: \"node\"; nodeId: string };\n\nexport type ExecApprovalsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n execApprovalsLoading: boolean;\n execApprovalsSaving: boolean;\n execApprovalsDirty: boolean;\n execApprovalsSnapshot: ExecApprovalsSnapshot | null;\n execApprovalsForm: ExecApprovalsFile | null;\n execApprovalsSelectedAgent: string | null;\n lastError: string | null;\n};\n\nfunction resolveExecApprovalsRpc(target?: ExecApprovalsTarget | null): {\n method: string;\n params: Record;\n} | null {\n if (!target || target.kind === \"gateway\") {\n return { method: \"exec.approvals.get\", params: {} };\n }\n const nodeId = target.nodeId.trim();\n if (!nodeId) return null;\n return { method: \"exec.approvals.node.get\", params: { nodeId } };\n}\n\nfunction resolveExecApprovalsSaveRpc(\n target: ExecApprovalsTarget | null | undefined,\n params: { file: ExecApprovalsFile; baseHash: string },\n): { method: string; params: Record } | null {\n if (!target || target.kind === \"gateway\") {\n return { method: \"exec.approvals.set\", params };\n }\n const nodeId = target.nodeId.trim();\n if (!nodeId) return null;\n return { method: \"exec.approvals.node.set\", params: { ...params, nodeId } };\n}\n\nexport async function loadExecApprovals(\n state: ExecApprovalsState,\n target?: ExecApprovalsTarget | null,\n) {\n if (!state.client || !state.connected) return;\n if (state.execApprovalsLoading) return;\n state.execApprovalsLoading = true;\n state.lastError = null;\n try {\n const rpc = resolveExecApprovalsRpc(target);\n if (!rpc) {\n state.lastError = \"Select a node before loading exec approvals.\";\n return;\n }\n const res = (await state.client.request(rpc.method, rpc.params)) as ExecApprovalsSnapshot;\n applyExecApprovalsSnapshot(state, res);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.execApprovalsLoading = false;\n }\n}\n\nexport function applyExecApprovalsSnapshot(\n state: ExecApprovalsState,\n snapshot: ExecApprovalsSnapshot,\n) {\n state.execApprovalsSnapshot = snapshot;\n if (!state.execApprovalsDirty) {\n state.execApprovalsForm = cloneConfigObject(snapshot.file ?? {});\n }\n}\n\nexport async function saveExecApprovals(\n state: ExecApprovalsState,\n target?: ExecApprovalsTarget | null,\n) {\n if (!state.client || !state.connected) return;\n state.execApprovalsSaving = true;\n state.lastError = null;\n try {\n const baseHash = state.execApprovalsSnapshot?.hash;\n if (!baseHash) {\n state.lastError = \"Exec approvals hash missing; reload and retry.\";\n return;\n }\n const file =\n state.execApprovalsForm ??\n state.execApprovalsSnapshot?.file ??\n {};\n const rpc = resolveExecApprovalsSaveRpc(target, { file, baseHash });\n if (!rpc) {\n state.lastError = \"Select a node before saving exec approvals.\";\n return;\n }\n await state.client.request(rpc.method, rpc.params);\n state.execApprovalsDirty = false;\n await loadExecApprovals(state, target);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.execApprovalsSaving = false;\n }\n}\n\nexport function updateExecApprovalsFormValue(\n state: ExecApprovalsState,\n path: Array,\n value: unknown,\n) {\n const base = cloneConfigObject(\n state.execApprovalsForm ?? state.execApprovalsSnapshot?.file ?? {},\n );\n setPathValue(base, path, value);\n state.execApprovalsForm = base;\n state.execApprovalsDirty = true;\n}\n\nexport function removeExecApprovalsFormValue(\n state: ExecApprovalsState,\n path: Array,\n) {\n const base = cloneConfigObject(\n state.execApprovalsForm ?? state.execApprovalsSnapshot?.file ?? {},\n );\n removePathValue(base, path);\n state.execApprovalsForm = base;\n state.execApprovalsDirty = true;\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { PresenceEntry } from \"../types\";\n\nexport type PresenceState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n presenceLoading: boolean;\n presenceEntries: PresenceEntry[];\n presenceError: string | null;\n presenceStatus: string | null;\n};\n\nexport async function loadPresence(state: PresenceState) {\n if (!state.client || !state.connected) return;\n if (state.presenceLoading) return;\n state.presenceLoading = true;\n state.presenceError = null;\n state.presenceStatus = null;\n try {\n const res = (await state.client.request(\"system-presence\", {})) as\n | PresenceEntry[]\n | undefined;\n if (Array.isArray(res)) {\n state.presenceEntries = res;\n state.presenceStatus = res.length === 0 ? \"No instances yet.\" : null;\n } else {\n state.presenceEntries = [];\n state.presenceStatus = \"No presence payload.\";\n }\n } catch (err) {\n state.presenceError = String(err);\n } finally {\n state.presenceLoading = false;\n }\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { SkillStatusReport } from \"../types\";\n\nexport type SkillsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n skillsLoading: boolean;\n skillsReport: SkillStatusReport | null;\n skillsError: string | null;\n skillsBusyKey: string | null;\n skillEdits: Record;\n skillMessages: SkillMessageMap;\n};\n\nexport type SkillMessage = {\n kind: \"success\" | \"error\";\n message: string;\n};\n\nexport type SkillMessageMap = Record;\n\ntype LoadSkillsOptions = {\n clearMessages?: boolean;\n};\n\nfunction setSkillMessage(state: SkillsState, key: string, message?: SkillMessage) {\n if (!key.trim()) return;\n const next = { ...state.skillMessages };\n if (message) next[key] = message;\n else delete next[key];\n state.skillMessages = next;\n}\n\nfunction getErrorMessage(err: unknown) {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\nexport async function loadSkills(state: SkillsState, options?: LoadSkillsOptions) {\n if (options?.clearMessages && Object.keys(state.skillMessages).length > 0) {\n state.skillMessages = {};\n }\n if (!state.client || !state.connected) return;\n if (state.skillsLoading) return;\n state.skillsLoading = true;\n state.skillsError = null;\n try {\n const res = (await state.client.request(\"skills.status\", {})) as\n | SkillStatusReport\n | undefined;\n if (res) state.skillsReport = res;\n } catch (err) {\n state.skillsError = getErrorMessage(err);\n } finally {\n state.skillsLoading = false;\n }\n}\n\nexport function updateSkillEdit(\n state: SkillsState,\n skillKey: string,\n value: string,\n) {\n state.skillEdits = { ...state.skillEdits, [skillKey]: value };\n}\n\nexport async function updateSkillEnabled(\n state: SkillsState,\n skillKey: string,\n enabled: boolean,\n) {\n if (!state.client || !state.connected) return;\n state.skillsBusyKey = skillKey;\n state.skillsError = null;\n try {\n await state.client.request(\"skills.update\", { skillKey, enabled });\n await loadSkills(state);\n setSkillMessage(state, skillKey, {\n kind: \"success\",\n message: enabled ? \"Skill enabled\" : \"Skill disabled\",\n });\n } catch (err) {\n const message = getErrorMessage(err);\n state.skillsError = message;\n setSkillMessage(state, skillKey, {\n kind: \"error\",\n message,\n });\n } finally {\n state.skillsBusyKey = null;\n }\n}\n\nexport async function saveSkillApiKey(state: SkillsState, skillKey: string) {\n if (!state.client || !state.connected) return;\n state.skillsBusyKey = skillKey;\n state.skillsError = null;\n try {\n const apiKey = state.skillEdits[skillKey] ?? \"\";\n await state.client.request(\"skills.update\", { skillKey, apiKey });\n await loadSkills(state);\n setSkillMessage(state, skillKey, {\n kind: \"success\",\n message: \"API key saved\",\n });\n } catch (err) {\n const message = getErrorMessage(err);\n state.skillsError = message;\n setSkillMessage(state, skillKey, {\n kind: \"error\",\n message,\n });\n } finally {\n state.skillsBusyKey = null;\n }\n}\n\nexport async function installSkill(\n state: SkillsState,\n skillKey: string,\n name: string,\n installId: string,\n) {\n if (!state.client || !state.connected) return;\n state.skillsBusyKey = skillKey;\n state.skillsError = null;\n try {\n const result = (await state.client.request(\"skills.install\", {\n name,\n installId,\n timeoutMs: 120000,\n })) as { ok?: boolean; message?: string };\n await loadSkills(state);\n setSkillMessage(state, skillKey, {\n kind: \"success\",\n message: result?.message ?? \"Installed\",\n });\n } catch (err) {\n const message = getErrorMessage(err);\n state.skillsError = message;\n setSkillMessage(state, skillKey, {\n kind: \"error\",\n message,\n });\n } finally {\n state.skillsBusyKey = null;\n }\n}\n","export type ThemeMode = \"system\" | \"light\" | \"dark\";\nexport type ResolvedTheme = \"light\" | \"dark\";\n\nexport function getSystemTheme(): ResolvedTheme {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return \"dark\";\n }\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n ? \"dark\"\n : \"light\";\n}\n\nexport function resolveTheme(mode: ThemeMode): ResolvedTheme {\n if (mode === \"system\") return getSystemTheme();\n return mode;\n}\n","import type { ThemeMode } from \"./theme\";\n\nexport type ThemeTransitionContext = {\n element?: HTMLElement | null;\n pointerClientX?: number;\n pointerClientY?: number;\n};\n\nexport type ThemeTransitionOptions = {\n nextTheme: ThemeMode;\n applyTheme: () => void;\n context?: ThemeTransitionContext;\n currentTheme?: ThemeMode | null;\n};\n\ntype DocumentWithViewTransition = Document & {\n startViewTransition?: (callback: () => void) => { finished: Promise };\n};\n\nconst clamp01 = (value: number) => {\n if (Number.isNaN(value)) return 0.5;\n if (value <= 0) return 0;\n if (value >= 1) return 1;\n return value;\n};\n\nconst hasReducedMotionPreference = () => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return false;\n }\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ?? false;\n};\n\nconst cleanupThemeTransition = (root: HTMLElement) => {\n root.classList.remove(\"theme-transition\");\n root.style.removeProperty(\"--theme-switch-x\");\n root.style.removeProperty(\"--theme-switch-y\");\n};\n\nexport const startThemeTransition = ({\n nextTheme,\n applyTheme,\n context,\n currentTheme,\n}: ThemeTransitionOptions) => {\n if (currentTheme === nextTheme) return;\n\n const documentReference = globalThis.document ?? null;\n if (!documentReference) {\n applyTheme();\n return;\n }\n\n const root = documentReference.documentElement;\n const document_ = documentReference as DocumentWithViewTransition;\n const prefersReducedMotion = hasReducedMotionPreference();\n\n const canUseViewTransition =\n Boolean(document_.startViewTransition) && !prefersReducedMotion;\n\n if (canUseViewTransition) {\n let xPercent = 0.5;\n let yPercent = 0.5;\n\n if (\n context?.pointerClientX !== undefined &&\n context?.pointerClientY !== undefined &&\n typeof window !== \"undefined\"\n ) {\n xPercent = clamp01(context.pointerClientX / window.innerWidth);\n yPercent = clamp01(context.pointerClientY / window.innerHeight);\n } else if (context?.element) {\n const rect = context.element.getBoundingClientRect();\n if (\n rect.width > 0 &&\n rect.height > 0 &&\n typeof window !== \"undefined\"\n ) {\n xPercent = clamp01((rect.left + rect.width / 2) / window.innerWidth);\n yPercent = clamp01((rect.top + rect.height / 2) / window.innerHeight);\n }\n }\n\n root.style.setProperty(\"--theme-switch-x\", `${xPercent * 100}%`);\n root.style.setProperty(\"--theme-switch-y\", `${yPercent * 100}%`);\n root.classList.add(\"theme-transition\");\n\n try {\n const transition = document_.startViewTransition?.(() => {\n applyTheme();\n });\n if (transition?.finished) {\n void transition.finished.finally(() => cleanupThemeTransition(root));\n } else {\n cleanupThemeTransition(root);\n }\n } catch {\n cleanupThemeTransition(root);\n applyTheme();\n }\n return;\n }\n\n applyTheme();\n cleanupThemeTransition(root);\n};\n","import { loadLogs } from \"./controllers/logs\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadDebug } from \"./controllers/debug\";\nimport type { ClawdbotApp } from \"./app\";\n\ntype PollingHost = {\n nodesPollInterval: number | null;\n logsPollInterval: number | null;\n debugPollInterval: number | null;\n tab: string;\n};\n\nexport function startNodesPolling(host: PollingHost) {\n if (host.nodesPollInterval != null) return;\n host.nodesPollInterval = window.setInterval(\n () => void loadNodes(host as unknown as ClawdbotApp, { quiet: true }),\n 5000,\n );\n}\n\nexport function stopNodesPolling(host: PollingHost) {\n if (host.nodesPollInterval == null) return;\n clearInterval(host.nodesPollInterval);\n host.nodesPollInterval = null;\n}\n\nexport function startLogsPolling(host: PollingHost) {\n if (host.logsPollInterval != null) return;\n host.logsPollInterval = window.setInterval(() => {\n if (host.tab !== \"logs\") return;\n void loadLogs(host as unknown as ClawdbotApp, { quiet: true });\n }, 2000);\n}\n\nexport function stopLogsPolling(host: PollingHost) {\n if (host.logsPollInterval == null) return;\n clearInterval(host.logsPollInterval);\n host.logsPollInterval = null;\n}\n\nexport function startDebugPolling(host: PollingHost) {\n if (host.debugPollInterval != null) return;\n host.debugPollInterval = window.setInterval(() => {\n if (host.tab !== \"debug\") return;\n void loadDebug(host as unknown as ClawdbotApp);\n }, 3000);\n}\n\nexport function stopDebugPolling(host: PollingHost) {\n if (host.debugPollInterval == null) return;\n clearInterval(host.debugPollInterval);\n host.debugPollInterval = null;\n}\n","import { loadConfig, loadConfigSchema } from \"./controllers/config\";\nimport { loadCronJobs, loadCronStatus } from \"./controllers/cron\";\nimport { loadChannels } from \"./controllers/channels\";\nimport { loadDebug } from \"./controllers/debug\";\nimport { loadLogs } from \"./controllers/logs\";\nimport { loadDevices } from \"./controllers/devices\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadExecApprovals } from \"./controllers/exec-approvals\";\nimport { loadPresence } from \"./controllers/presence\";\nimport { loadSessions } from \"./controllers/sessions\";\nimport { loadSkills } from \"./controllers/skills\";\nimport { inferBasePathFromPathname, normalizeBasePath, normalizePath, pathForTab, tabFromPath, type Tab } from \"./navigation\";\nimport { saveSettings, type UiSettings } from \"./storage\";\nimport { resolveTheme, type ResolvedTheme, type ThemeMode } from \"./theme\";\nimport { startThemeTransition, type ThemeTransitionContext } from \"./theme-transition\";\nimport { scheduleChatScroll, scheduleLogsScroll } from \"./app-scroll\";\nimport { startLogsPolling, stopLogsPolling, startDebugPolling, stopDebugPolling } from \"./app-polling\";\nimport { refreshChat } from \"./app-chat\";\nimport type { ClawdbotApp } from \"./app\";\n\ntype SettingsHost = {\n settings: UiSettings;\n theme: ThemeMode;\n themeResolved: ResolvedTheme;\n applySessionKey: string;\n sessionKey: string;\n tab: Tab;\n connected: boolean;\n chatHasAutoScrolled: boolean;\n logsAtBottom: boolean;\n eventLog: unknown[];\n eventLogBuffer: unknown[];\n basePath: string;\n themeMedia: MediaQueryList | null;\n themeMediaHandler: ((event: MediaQueryListEvent) => void) | null;\n};\n\nexport function applySettings(host: SettingsHost, next: UiSettings) {\n const normalized = {\n ...next,\n lastActiveSessionKey: next.lastActiveSessionKey?.trim() || next.sessionKey.trim() || \"main\",\n };\n host.settings = normalized;\n saveSettings(normalized);\n if (next.theme !== host.theme) {\n host.theme = next.theme;\n applyResolvedTheme(host, resolveTheme(next.theme));\n }\n host.applySessionKey = host.settings.lastActiveSessionKey;\n}\n\nexport function setLastActiveSessionKey(host: SettingsHost, next: string) {\n const trimmed = next.trim();\n if (!trimmed) return;\n if (host.settings.lastActiveSessionKey === trimmed) return;\n applySettings(host, { ...host.settings, lastActiveSessionKey: trimmed });\n}\n\nexport function applySettingsFromUrl(host: SettingsHost) {\n if (!window.location.search) return;\n const params = new URLSearchParams(window.location.search);\n const tokenRaw = params.get(\"token\");\n const passwordRaw = params.get(\"password\");\n const sessionRaw = params.get(\"session\");\n const gatewayUrlRaw = params.get(\"gatewayUrl\");\n let shouldCleanUrl = false;\n\n if (tokenRaw != null) {\n const token = tokenRaw.trim();\n if (token && token !== host.settings.token) {\n applySettings(host, { ...host.settings, token });\n }\n params.delete(\"token\");\n shouldCleanUrl = true;\n }\n\n if (passwordRaw != null) {\n const password = passwordRaw.trim();\n if (password) {\n (host as { password: string }).password = password;\n }\n params.delete(\"password\");\n shouldCleanUrl = true;\n }\n\n if (sessionRaw != null) {\n const session = sessionRaw.trim();\n if (session) {\n host.sessionKey = session;\n applySettings(host, {\n ...host.settings,\n sessionKey: session,\n lastActiveSessionKey: session,\n });\n }\n }\n\n if (gatewayUrlRaw != null) {\n const gatewayUrl = gatewayUrlRaw.trim();\n if (gatewayUrl && gatewayUrl !== host.settings.gatewayUrl) {\n applySettings(host, { ...host.settings, gatewayUrl });\n }\n params.delete(\"gatewayUrl\");\n shouldCleanUrl = true;\n }\n\n if (!shouldCleanUrl) return;\n const url = new URL(window.location.href);\n url.search = params.toString();\n window.history.replaceState({}, \"\", url.toString());\n}\n\nexport function setTab(host: SettingsHost, next: Tab) {\n if (host.tab !== next) host.tab = next;\n if (next === \"chat\") host.chatHasAutoScrolled = false;\n if (next === \"logs\")\n startLogsPolling(host as unknown as Parameters[0]);\n else stopLogsPolling(host as unknown as Parameters[0]);\n if (next === \"debug\")\n startDebugPolling(host as unknown as Parameters[0]);\n else stopDebugPolling(host as unknown as Parameters[0]);\n void refreshActiveTab(host);\n syncUrlWithTab(host, next, false);\n}\n\nexport function setTheme(\n host: SettingsHost,\n next: ThemeMode,\n context?: ThemeTransitionContext,\n) {\n const applyTheme = () => {\n host.theme = next;\n applySettings(host, { ...host.settings, theme: next });\n applyResolvedTheme(host, resolveTheme(next));\n };\n startThemeTransition({\n nextTheme: next,\n applyTheme,\n context,\n currentTheme: host.theme,\n });\n}\n\nexport async function refreshActiveTab(host: SettingsHost) {\n if (host.tab === \"overview\") await loadOverview(host);\n if (host.tab === \"channels\") await loadChannelsTab(host);\n if (host.tab === \"instances\") await loadPresence(host as unknown as ClawdbotApp);\n if (host.tab === \"sessions\") await loadSessions(host as unknown as ClawdbotApp);\n if (host.tab === \"cron\") await loadCron(host);\n if (host.tab === \"skills\") await loadSkills(host as unknown as ClawdbotApp);\n if (host.tab === \"nodes\") {\n await loadNodes(host as unknown as ClawdbotApp);\n await loadDevices(host as unknown as ClawdbotApp);\n await loadConfig(host as unknown as ClawdbotApp);\n await loadExecApprovals(host as unknown as ClawdbotApp);\n }\n if (host.tab === \"chat\") {\n await refreshChat(host as unknown as Parameters[0]);\n scheduleChatScroll(\n host as unknown as Parameters[0],\n !host.chatHasAutoScrolled,\n );\n }\n if (host.tab === \"config\") {\n await loadConfigSchema(host as unknown as ClawdbotApp);\n await loadConfig(host as unknown as ClawdbotApp);\n }\n if (host.tab === \"debug\") {\n await loadDebug(host as unknown as ClawdbotApp);\n host.eventLog = host.eventLogBuffer;\n }\n if (host.tab === \"logs\") {\n host.logsAtBottom = true;\n await loadLogs(host as unknown as ClawdbotApp, { reset: true });\n scheduleLogsScroll(\n host as unknown as Parameters[0],\n true,\n );\n }\n}\n\nexport function inferBasePath() {\n if (typeof window === \"undefined\") return \"\";\n const configured = window.__CLAWDBOT_CONTROL_UI_BASE_PATH__;\n if (typeof configured === \"string\" && configured.trim()) {\n return normalizeBasePath(configured);\n }\n return inferBasePathFromPathname(window.location.pathname);\n}\n\nexport function syncThemeWithSettings(host: SettingsHost) {\n host.theme = host.settings.theme ?? \"system\";\n applyResolvedTheme(host, resolveTheme(host.theme));\n}\n\nexport function applyResolvedTheme(host: SettingsHost, resolved: ResolvedTheme) {\n host.themeResolved = resolved;\n if (typeof document === \"undefined\") return;\n const root = document.documentElement;\n root.dataset.theme = resolved;\n root.style.colorScheme = resolved;\n}\n\nexport function attachThemeListener(host: SettingsHost) {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n host.themeMedia = window.matchMedia(\"(prefers-color-scheme: dark)\");\n host.themeMediaHandler = (event) => {\n if (host.theme !== \"system\") return;\n applyResolvedTheme(host, event.matches ? \"dark\" : \"light\");\n };\n if (typeof host.themeMedia.addEventListener === \"function\") {\n host.themeMedia.addEventListener(\"change\", host.themeMediaHandler);\n return;\n }\n const legacy = host.themeMedia as MediaQueryList & {\n addListener: (cb: (event: MediaQueryListEvent) => void) => void;\n };\n legacy.addListener(host.themeMediaHandler);\n}\n\nexport function detachThemeListener(host: SettingsHost) {\n if (!host.themeMedia || !host.themeMediaHandler) return;\n if (typeof host.themeMedia.removeEventListener === \"function\") {\n host.themeMedia.removeEventListener(\"change\", host.themeMediaHandler);\n return;\n }\n const legacy = host.themeMedia as MediaQueryList & {\n removeListener: (cb: (event: MediaQueryListEvent) => void) => void;\n };\n legacy.removeListener(host.themeMediaHandler);\n host.themeMedia = null;\n host.themeMediaHandler = null;\n}\n\nexport function syncTabWithLocation(host: SettingsHost, replace: boolean) {\n if (typeof window === \"undefined\") return;\n const resolved = tabFromPath(window.location.pathname, host.basePath) ?? \"chat\";\n setTabFromRoute(host, resolved);\n syncUrlWithTab(host, resolved, replace);\n}\n\nexport function onPopState(host: SettingsHost) {\n if (typeof window === \"undefined\") return;\n const resolved = tabFromPath(window.location.pathname, host.basePath);\n if (!resolved) return;\n\n const url = new URL(window.location.href);\n const session = url.searchParams.get(\"session\")?.trim();\n if (session) {\n host.sessionKey = session;\n applySettings(host, {\n ...host.settings,\n sessionKey: session,\n lastActiveSessionKey: session,\n });\n }\n\n setTabFromRoute(host, resolved);\n}\n\nexport function setTabFromRoute(host: SettingsHost, next: Tab) {\n if (host.tab !== next) host.tab = next;\n if (next === \"chat\") host.chatHasAutoScrolled = false;\n if (next === \"logs\")\n startLogsPolling(host as unknown as Parameters[0]);\n else stopLogsPolling(host as unknown as Parameters[0]);\n if (next === \"debug\")\n startDebugPolling(host as unknown as Parameters[0]);\n else stopDebugPolling(host as unknown as Parameters[0]);\n if (host.connected) void refreshActiveTab(host);\n}\n\nexport function syncUrlWithTab(host: SettingsHost, tab: Tab, replace: boolean) {\n if (typeof window === \"undefined\") return;\n const targetPath = normalizePath(pathForTab(tab, host.basePath));\n const currentPath = normalizePath(window.location.pathname);\n const url = new URL(window.location.href);\n\n if (tab === \"chat\" && host.sessionKey) {\n url.searchParams.set(\"session\", host.sessionKey);\n } else {\n url.searchParams.delete(\"session\");\n }\n\n if (currentPath !== targetPath) {\n url.pathname = targetPath;\n }\n\n if (replace) {\n window.history.replaceState({}, \"\", url.toString());\n } else {\n window.history.pushState({}, \"\", url.toString());\n }\n}\n\nexport function syncUrlWithSessionKey(\n host: SettingsHost,\n sessionKey: string,\n replace: boolean,\n) {\n if (typeof window === \"undefined\") return;\n const url = new URL(window.location.href);\n url.searchParams.set(\"session\", sessionKey);\n if (replace) window.history.replaceState({}, \"\", url.toString());\n else window.history.pushState({}, \"\", url.toString());\n}\n\nexport async function loadOverview(host: SettingsHost) {\n await Promise.all([\n loadChannels(host as unknown as ClawdbotApp, false),\n loadPresence(host as unknown as ClawdbotApp),\n loadSessions(host as unknown as ClawdbotApp),\n loadCronStatus(host as unknown as ClawdbotApp),\n loadDebug(host as unknown as ClawdbotApp),\n ]);\n}\n\nexport async function loadChannelsTab(host: SettingsHost) {\n await Promise.all([\n loadChannels(host as unknown as ClawdbotApp, true),\n loadConfigSchema(host as unknown as ClawdbotApp),\n loadConfig(host as unknown as ClawdbotApp),\n ]);\n}\n\nexport async function loadCron(host: SettingsHost) {\n await Promise.all([\n loadChannels(host as unknown as ClawdbotApp, false),\n loadCronStatus(host as unknown as ClawdbotApp),\n loadCronJobs(host as unknown as ClawdbotApp),\n ]);\n}\n","import { abortChatRun, loadChatHistory, sendChatMessage } from \"./controllers/chat\";\nimport { loadSessions } from \"./controllers/sessions\";\nimport { generateUUID } from \"./uuid\";\nimport { resetToolStream } from \"./app-tool-stream\";\nimport { scheduleChatScroll } from \"./app-scroll\";\nimport { setLastActiveSessionKey } from \"./app-settings\";\nimport { normalizeBasePath } from \"./navigation\";\nimport type { GatewayHelloOk } from \"./gateway\";\nimport { parseAgentSessionKey } from \"../../../src/sessions/session-key-utils.js\";\nimport type { ClawdbotApp } from \"./app\";\n\ntype ChatHost = {\n connected: boolean;\n chatMessage: string;\n chatQueue: Array<{ id: string; text: string; createdAt: number }>;\n chatRunId: string | null;\n chatSending: boolean;\n sessionKey: string;\n basePath: string;\n hello: GatewayHelloOk | null;\n chatAvatarUrl: string | null;\n};\n\nexport function isChatBusy(host: ChatHost) {\n return host.chatSending || Boolean(host.chatRunId);\n}\n\nexport function isChatStopCommand(text: string) {\n const trimmed = text.trim();\n if (!trimmed) return false;\n const normalized = trimmed.toLowerCase();\n if (normalized === \"/stop\") return true;\n return (\n normalized === \"stop\" ||\n normalized === \"esc\" ||\n normalized === \"abort\" ||\n normalized === \"wait\" ||\n normalized === \"exit\"\n );\n}\n\nexport async function handleAbortChat(host: ChatHost) {\n if (!host.connected) return;\n host.chatMessage = \"\";\n await abortChatRun(host as unknown as ClawdbotApp);\n}\n\nfunction enqueueChatMessage(host: ChatHost, text: string) {\n const trimmed = text.trim();\n if (!trimmed) return;\n host.chatQueue = [\n ...host.chatQueue,\n {\n id: generateUUID(),\n text: trimmed,\n createdAt: Date.now(),\n },\n ];\n}\n\nasync function sendChatMessageNow(\n host: ChatHost,\n message: string,\n opts?: { previousDraft?: string; restoreDraft?: boolean },\n) {\n resetToolStream(host as unknown as Parameters[0]);\n const ok = await sendChatMessage(host as unknown as ClawdbotApp, message);\n if (!ok && opts?.previousDraft != null) {\n host.chatMessage = opts.previousDraft;\n }\n if (ok) {\n setLastActiveSessionKey(host as unknown as Parameters[0], host.sessionKey);\n }\n if (ok && opts?.restoreDraft && opts.previousDraft?.trim()) {\n host.chatMessage = opts.previousDraft;\n }\n scheduleChatScroll(host as unknown as Parameters[0]);\n if (ok && !host.chatRunId) {\n void flushChatQueue(host);\n }\n return ok;\n}\n\nasync function flushChatQueue(host: ChatHost) {\n if (!host.connected || isChatBusy(host)) return;\n const [next, ...rest] = host.chatQueue;\n if (!next) return;\n host.chatQueue = rest;\n const ok = await sendChatMessageNow(host, next.text);\n if (!ok) {\n host.chatQueue = [next, ...host.chatQueue];\n }\n}\n\nexport function removeQueuedMessage(host: ChatHost, id: string) {\n host.chatQueue = host.chatQueue.filter((item) => item.id !== id);\n}\n\nexport async function handleSendChat(\n host: ChatHost,\n messageOverride?: string,\n opts?: { restoreDraft?: boolean },\n) {\n if (!host.connected) return;\n const previousDraft = host.chatMessage;\n const message = (messageOverride ?? host.chatMessage).trim();\n if (!message) return;\n\n if (isChatStopCommand(message)) {\n await handleAbortChat(host);\n return;\n }\n\n if (messageOverride == null) {\n host.chatMessage = \"\";\n }\n\n if (isChatBusy(host)) {\n enqueueChatMessage(host, message);\n return;\n }\n\n await sendChatMessageNow(host, message, {\n previousDraft: messageOverride == null ? previousDraft : undefined,\n restoreDraft: Boolean(messageOverride && opts?.restoreDraft),\n });\n}\n\nexport async function refreshChat(host: ChatHost) {\n await Promise.all([\n loadChatHistory(host as unknown as ClawdbotApp),\n loadSessions(host as unknown as ClawdbotApp),\n refreshChatAvatar(host),\n ]);\n scheduleChatScroll(host as unknown as Parameters[0], true);\n}\n\nexport const flushChatQueueForEvent = flushChatQueue;\n\ntype SessionDefaultsSnapshot = {\n defaultAgentId?: string;\n};\n\nfunction resolveAgentIdForSession(host: ChatHost): string | null {\n const parsed = parseAgentSessionKey(host.sessionKey);\n if (parsed?.agentId) return parsed.agentId;\n const snapshot = host.hello?.snapshot as { sessionDefaults?: SessionDefaultsSnapshot } | undefined;\n const fallback = snapshot?.sessionDefaults?.defaultAgentId?.trim();\n return fallback || \"main\";\n}\n\nfunction buildAvatarMetaUrl(basePath: string, agentId: string): string {\n const base = normalizeBasePath(basePath);\n const encoded = encodeURIComponent(agentId);\n return base ? `${base}/avatar/${encoded}?meta=1` : `/avatar/${encoded}?meta=1`;\n}\n\nexport async function refreshChatAvatar(host: ChatHost) {\n if (!host.connected) {\n host.chatAvatarUrl = null;\n return;\n }\n const agentId = resolveAgentIdForSession(host);\n if (!agentId) {\n host.chatAvatarUrl = null;\n return;\n }\n host.chatAvatarUrl = null;\n const url = buildAvatarMetaUrl(host.basePath, agentId);\n try {\n const res = await fetch(url, { method: \"GET\" });\n if (!res.ok) {\n host.chatAvatarUrl = null;\n return;\n }\n const data = (await res.json()) as { avatarUrl?: unknown };\n const avatarUrl = typeof data.avatarUrl === \"string\" ? data.avatarUrl.trim() : \"\";\n host.chatAvatarUrl = avatarUrl || null;\n } catch {\n host.chatAvatarUrl = null;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},e=t=>(...e)=>({_$litDirective$:t,values:e});class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}export{i as Directive,t as PartType,e as directive};\n//# sourceMappingURL=directive.js.map\n","import{_$LH as o}from\"./lit-html.js\";\n/**\n * @license\n * Copyright 2020 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const{I:t}=o,i=o=>o,n=o=>null===o||\"object\"!=typeof o&&\"function\"!=typeof o,e={HTML:1,SVG:2,MATHML:3},l=(o,t)=>void 0===t?void 0!==o?._$litType$:o?._$litType$===t,d=o=>null!=o?._$litType$?.h,c=o=>void 0!==o?._$litDirective$,f=o=>o?._$litDirective$,r=o=>void 0===o.strings,s=()=>document.createComment(\"\"),v=(o,n,e)=>{const l=o._$AA.parentNode,d=void 0===n?o._$AB:n._$AA;if(void 0===e){const i=l.insertBefore(s(),d),n=l.insertBefore(s(),d);e=new t(i,n,o,o.options)}else{const t=e._$AB.nextSibling,n=e._$AM,c=n!==o;if(c){let t;e._$AQ?.(o),e._$AM=o,void 0!==e._$AP&&(t=o._$AU)!==n._$AU&&e._$AP(t)}if(t!==d||c){let o=e._$AA;for(;o!==t;){const t=i(o).nextSibling;i(l).insertBefore(o,d),o=t}}}return e},u=(o,t,i=o)=>(o._$AI(t,i),o),m={},p=(o,t=m)=>o._$AH=t,M=o=>o._$AH,h=o=>{o._$AR(),o._$AA.remove()},j=o=>{o._$AR()};export{e as TemplateResultType,j as clearPart,M as getCommittedValue,f as getDirectiveClass,v as insertPart,d as isCompiledTemplateResult,c as isDirectiveResult,n as isPrimitive,r as isSingleExpression,l as isTemplateResult,h as removePart,u as setChildPartValue,p as setCommittedValue};\n//# sourceMappingURL=directive-helpers.js.map\n","import{noChange as e}from\"../lit-html.js\";import{directive as s,Directive as t,PartType as r}from\"../directive.js\";import{getCommittedValue as l,setChildPartValue as o,insertPart as i,removePart as n,setCommittedValue as f}from\"../directive-helpers.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst u=(e,s,t)=>{const r=new Map;for(let l=s;l<=t;l++)r.set(e[l],l);return r},c=s(class extends t{constructor(e){if(super(e),e.type!==r.CHILD)throw Error(\"repeat() can only be used in text expressions\")}dt(e,s,t){let r;void 0===t?t=s:void 0!==s&&(r=s);const l=[],o=[];let i=0;for(const s of e)l[i]=r?r(s,i):i,o[i]=t(s,i),i++;return{values:o,keys:l}}render(e,s,t){return this.dt(e,s,t).values}update(s,[t,r,c]){const d=l(s),{values:p,keys:a}=this.dt(t,r,c);if(!Array.isArray(d))return this.ut=a,p;const h=this.ut??=[],v=[];let m,y,x=0,j=d.length-1,k=0,w=p.length-1;for(;x<=j&&k<=w;)if(null===d[x])x++;else if(null===d[j])j--;else if(h[x]===a[k])v[k]=o(d[x],p[k]),x++,k++;else if(h[j]===a[w])v[w]=o(d[j],p[w]),j--,w--;else if(h[x]===a[w])v[w]=o(d[x],p[w]),i(s,v[w+1],d[x]),x++,w--;else if(h[j]===a[k])v[k]=o(d[j],p[k]),i(s,d[x],d[j]),j--,k++;else if(void 0===m&&(m=u(a,k,w),y=u(h,x,j)),m.has(h[x]))if(m.has(h[j])){const e=y.get(a[k]),t=void 0!==e?d[e]:null;if(null===t){const e=i(s,d[x]);o(e,p[k]),v[k]=e}else v[k]=o(t,p[k]),i(s,d[x],t),d[e]=null;k++}else n(d[j]),j--;else n(d[x]),x++;for(;k<=w;){const e=i(s,v[w+1]);o(e,p[k]),v[k++]=e}for(;x<=j;){const e=d[x++];null!==e&&n(e)}return this.ut=a,f(s,v),e}});export{c as repeat};\n//# sourceMappingURL=repeat.js.map\n","/**\n * Message normalization utilities for chat rendering.\n */\n\nimport type {\n NormalizedMessage,\n MessageContentItem,\n} from \"../types/chat-types\";\n\n/**\n * Normalize a raw message object into a consistent structure.\n */\nexport function normalizeMessage(message: unknown): NormalizedMessage {\n const m = message as Record;\n let role = typeof m.role === \"string\" ? m.role : \"unknown\";\n\n // Detect tool messages by common gateway shapes.\n // Some tool events come through as assistant role with tool_* items in the content array.\n const hasToolId =\n typeof m.toolCallId === \"string\" || typeof m.tool_call_id === \"string\";\n\n const contentRaw = m.content;\n const contentItems = Array.isArray(contentRaw) ? contentRaw : null;\n const hasToolContent =\n Array.isArray(contentItems) &&\n contentItems.some((item) => {\n const x = item as Record;\n const t = String(x.type ?? \"\").toLowerCase();\n return t === \"toolresult\" || t === \"tool_result\";\n });\n\n const hasToolName =\n typeof (m as Record).toolName === \"string\" ||\n typeof (m as Record).tool_name === \"string\";\n\n if (hasToolId || hasToolContent || hasToolName) {\n role = \"toolResult\";\n }\n\n // Extract content\n let content: MessageContentItem[] = [];\n\n if (typeof m.content === \"string\") {\n content = [{ type: \"text\", text: m.content }];\n } else if (Array.isArray(m.content)) {\n content = m.content.map((item: Record) => ({\n type: (item.type as MessageContentItem[\"type\"]) || \"text\",\n text: item.text as string | undefined,\n name: item.name as string | undefined,\n args: item.args || item.arguments,\n }));\n } else if (typeof m.text === \"string\") {\n content = [{ type: \"text\", text: m.text }];\n }\n\n const timestamp = typeof m.timestamp === \"number\" ? m.timestamp : Date.now();\n const id = typeof m.id === \"string\" ? m.id : undefined;\n\n return { role, content, timestamp, id };\n}\n\n/**\n * Normalize role for grouping purposes.\n */\nexport function normalizeRoleForGrouping(role: string): string {\n const lower = role.toLowerCase();\n // Preserve original casing when it's already a core role.\n if (role === \"user\" || role === \"User\") return role;\n if (role === \"assistant\") return \"assistant\";\n if (role === \"system\") return \"system\";\n // Keep tool-related roles distinct so the UI can style/toggle them.\n if (\n lower === \"toolresult\" ||\n lower === \"tool_result\" ||\n lower === \"tool\" ||\n lower === \"function\"\n ) {\n return \"tool\";\n }\n return role;\n}\n\n/**\n * Check if a message is a tool result message based on its role.\n */\nexport function isToolResultMessage(message: unknown): boolean {\n const m = message as Record;\n const role = typeof m.role === \"string\" ? m.role.toLowerCase() : \"\";\n return role === \"toolresult\" || role === \"tool_result\";\n}\n","import{nothing as t,noChange as i}from\"../lit-html.js\";import{directive as r,Directive as s,PartType as n}from\"../directive.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */class e extends s{constructor(i){if(super(i),this.it=t,i.type!==n.CHILD)throw Error(this.constructor.directiveName+\"() can only be used in child bindings\")}render(r){if(r===t||null==r)return this._t=void 0,this.it=r;if(r===i)return r;if(\"string\"!=typeof r)throw Error(this.constructor.directiveName+\"() called with a non-string value\");if(r===this.it)return this._t;this.it=r;const s=[r];return s.raw=s,this._t={_$litType$:this.constructor.resultType,strings:s,values:[]}}}e.directiveName=\"unsafeHTML\",e.resultType=1;const o=r(e);export{e as UnsafeHTMLDirective,o as unsafeHTML};\n//# sourceMappingURL=unsafe-html.js.map\n","/*! @license DOMPurify 3.3.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.3.1/LICENSE */\n\nconst {\n entries,\n setPrototypeOf,\n isFrozen,\n getPrototypeOf,\n getOwnPropertyDescriptor\n} = Object;\nlet {\n freeze,\n seal,\n create\n} = Object; // eslint-disable-line import/no-mutable-exports\nlet {\n apply,\n construct\n} = typeof Reflect !== 'undefined' && Reflect;\nif (!freeze) {\n freeze = function freeze(x) {\n return x;\n };\n}\nif (!seal) {\n seal = function seal(x) {\n return x;\n };\n}\nif (!apply) {\n apply = function apply(func, thisArg) {\n for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n args[_key - 2] = arguments[_key];\n }\n return func.apply(thisArg, args);\n };\n}\nif (!construct) {\n construct = function construct(Func) {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n return new Func(...args);\n };\n}\nconst arrayForEach = unapply(Array.prototype.forEach);\nconst arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);\nconst arrayPop = unapply(Array.prototype.pop);\nconst arrayPush = unapply(Array.prototype.push);\nconst arraySplice = unapply(Array.prototype.splice);\nconst stringToLowerCase = unapply(String.prototype.toLowerCase);\nconst stringToString = unapply(String.prototype.toString);\nconst stringMatch = unapply(String.prototype.match);\nconst stringReplace = unapply(String.prototype.replace);\nconst stringIndexOf = unapply(String.prototype.indexOf);\nconst stringTrim = unapply(String.prototype.trim);\nconst objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);\nconst regExpTest = unapply(RegExp.prototype.test);\nconst typeErrorCreate = unconstruct(TypeError);\n/**\n * Creates a new function that calls the given function with a specified thisArg and arguments.\n *\n * @param func - The function to be wrapped and called.\n * @returns A new function that calls the given function with a specified thisArg and arguments.\n */\nfunction unapply(func) {\n return function (thisArg) {\n if (thisArg instanceof RegExp) {\n thisArg.lastIndex = 0;\n }\n for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {\n args[_key3 - 1] = arguments[_key3];\n }\n return apply(func, thisArg, args);\n };\n}\n/**\n * Creates a new function that constructs an instance of the given constructor function with the provided arguments.\n *\n * @param func - The constructor function to be wrapped and called.\n * @returns A new function that constructs an instance of the given constructor function with the provided arguments.\n */\nfunction unconstruct(Func) {\n return function () {\n for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {\n args[_key4] = arguments[_key4];\n }\n return construct(Func, args);\n };\n}\n/**\n * Add properties to a lookup table\n *\n * @param set - The set to which elements will be added.\n * @param array - The array containing elements to be added to the set.\n * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.\n * @returns The modified set with added elements.\n */\nfunction addToSet(set, array) {\n let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;\n if (setPrototypeOf) {\n // Make 'in' and truthy checks like Boolean(set.constructor)\n // independent of any properties defined on Object.prototype.\n // Prevent prototype setters from intercepting set as a this value.\n setPrototypeOf(set, null);\n }\n let l = array.length;\n while (l--) {\n let element = array[l];\n if (typeof element === 'string') {\n const lcElement = transformCaseFunc(element);\n if (lcElement !== element) {\n // Config presets (e.g. tags.js, attrs.js) are immutable.\n if (!isFrozen(array)) {\n array[l] = lcElement;\n }\n element = lcElement;\n }\n }\n set[element] = true;\n }\n return set;\n}\n/**\n * Clean up an array to harden against CSPP\n *\n * @param array - The array to be cleaned.\n * @returns The cleaned version of the array\n */\nfunction cleanArray(array) {\n for (let index = 0; index < array.length; index++) {\n const isPropertyExist = objectHasOwnProperty(array, index);\n if (!isPropertyExist) {\n array[index] = null;\n }\n }\n return array;\n}\n/**\n * Shallow clone an object\n *\n * @param object - The object to be cloned.\n * @returns A new object that copies the original.\n */\nfunction clone(object) {\n const newObject = create(null);\n for (const [property, value] of entries(object)) {\n const isPropertyExist = objectHasOwnProperty(object, property);\n if (isPropertyExist) {\n if (Array.isArray(value)) {\n newObject[property] = cleanArray(value);\n } else if (value && typeof value === 'object' && value.constructor === Object) {\n newObject[property] = clone(value);\n } else {\n newObject[property] = value;\n }\n }\n }\n return newObject;\n}\n/**\n * This method automatically checks if the prop is function or getter and behaves accordingly.\n *\n * @param object - The object to look up the getter function in its prototype chain.\n * @param prop - The property name for which to find the getter function.\n * @returns The getter function found in the prototype chain or a fallback function.\n */\nfunction lookupGetter(object, prop) {\n while (object !== null) {\n const desc = getOwnPropertyDescriptor(object, prop);\n if (desc) {\n if (desc.get) {\n return unapply(desc.get);\n }\n if (typeof desc.value === 'function') {\n return unapply(desc.value);\n }\n }\n object = getPrototypeOf(object);\n }\n function fallbackValue() {\n return null;\n }\n return fallbackValue;\n}\n\nconst html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);\nconst svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);\nconst svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);\n// List of SVG elements that are disallowed by default.\n// We still need to know them so that we can do namespace\n// checks properly in case one wants to add them to\n// allow-list.\nconst svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);\nconst mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);\n// Similarly to SVG, we want to know all MathML elements,\n// even those that we disallow by default.\nconst mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);\nconst text = freeze(['#text']);\n\nconst html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);\nconst svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);\nconst mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);\nconst xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);\n\n// eslint-disable-next-line unicorn/better-regex\nconst MUSTACHE_EXPR = seal(/\\{\\{[\\w\\W]*|[\\w\\W]*\\}\\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode\nconst ERB_EXPR = seal(/<%[\\w\\W]*|[\\w\\W]*%>/gm);\nconst TMPLIT_EXPR = seal(/\\$\\{[\\w\\W]*/gm); // eslint-disable-line unicorn/better-regex\nconst DATA_ATTR = seal(/^data-[\\-\\w.\\u00B7-\\uFFFF]+$/); // eslint-disable-line no-useless-escape\nconst ARIA_ATTR = seal(/^aria-[\\-\\w]+$/); // eslint-disable-line no-useless-escape\nconst IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i // eslint-disable-line no-useless-escape\n);\nconst IS_SCRIPT_OR_DATA = seal(/^(?:\\w+script|data):/i);\nconst ATTR_WHITESPACE = seal(/[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g // eslint-disable-line no-control-regex\n);\nconst DOCTYPE_NAME = seal(/^html$/i);\nconst CUSTOM_ELEMENT = seal(/^[a-z][.\\w]*(-[.\\w]+)+$/i);\n\nvar EXPRESSIONS = /*#__PURE__*/Object.freeze({\n __proto__: null,\n ARIA_ATTR: ARIA_ATTR,\n ATTR_WHITESPACE: ATTR_WHITESPACE,\n CUSTOM_ELEMENT: CUSTOM_ELEMENT,\n DATA_ATTR: DATA_ATTR,\n DOCTYPE_NAME: DOCTYPE_NAME,\n ERB_EXPR: ERB_EXPR,\n IS_ALLOWED_URI: IS_ALLOWED_URI,\n IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,\n MUSTACHE_EXPR: MUSTACHE_EXPR,\n TMPLIT_EXPR: TMPLIT_EXPR\n});\n\n/* eslint-disable @typescript-eslint/indent */\n// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType\nconst NODE_TYPE = {\n element: 1,\n attribute: 2,\n text: 3,\n cdataSection: 4,\n entityReference: 5,\n // Deprecated\n entityNode: 6,\n // Deprecated\n progressingInstruction: 7,\n comment: 8,\n document: 9,\n documentType: 10,\n documentFragment: 11,\n notation: 12 // Deprecated\n};\nconst getGlobal = function getGlobal() {\n return typeof window === 'undefined' ? null : window;\n};\n/**\n * Creates a no-op policy for internal use only.\n * Don't export this function outside this module!\n * @param trustedTypes The policy factory.\n * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).\n * @return The policy created (or null, if Trusted Types\n * are not supported or creating the policy failed).\n */\nconst _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {\n if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {\n return null;\n }\n // Allow the callers to control the unique policy name\n // by adding a data-tt-policy-suffix to the script element with the DOMPurify.\n // Policy creation with duplicate names throws in Trusted Types.\n let suffix = null;\n const ATTR_NAME = 'data-tt-policy-suffix';\n if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {\n suffix = purifyHostElement.getAttribute(ATTR_NAME);\n }\n const policyName = 'dompurify' + (suffix ? '#' + suffix : '');\n try {\n return trustedTypes.createPolicy(policyName, {\n createHTML(html) {\n return html;\n },\n createScriptURL(scriptUrl) {\n return scriptUrl;\n }\n });\n } catch (_) {\n // Policy creation failed (most likely another DOMPurify script has\n // already run). Skip creating the policy, as this will only cause errors\n // if TT are enforced.\n console.warn('TrustedTypes policy ' + policyName + ' could not be created.');\n return null;\n }\n};\nconst _createHooksMap = function _createHooksMap() {\n return {\n afterSanitizeAttributes: [],\n afterSanitizeElements: [],\n afterSanitizeShadowDOM: [],\n beforeSanitizeAttributes: [],\n beforeSanitizeElements: [],\n beforeSanitizeShadowDOM: [],\n uponSanitizeAttribute: [],\n uponSanitizeElement: [],\n uponSanitizeShadowNode: []\n };\n};\nfunction createDOMPurify() {\n let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();\n const DOMPurify = root => createDOMPurify(root);\n DOMPurify.version = '3.3.1';\n DOMPurify.removed = [];\n if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {\n // Not running in a browser, provide a factory function\n // so that you can pass your own Window\n DOMPurify.isSupported = false;\n return DOMPurify;\n }\n let {\n document\n } = window;\n const originalDocument = document;\n const currentScript = originalDocument.currentScript;\n const {\n DocumentFragment,\n HTMLTemplateElement,\n Node,\n Element,\n NodeFilter,\n NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,\n HTMLFormElement,\n DOMParser,\n trustedTypes\n } = window;\n const ElementPrototype = Element.prototype;\n const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');\n const remove = lookupGetter(ElementPrototype, 'remove');\n const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');\n const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');\n const getParentNode = lookupGetter(ElementPrototype, 'parentNode');\n // As per issue #47, the web-components registry is inherited by a\n // new document created via createHTMLDocument. As per the spec\n // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)\n // a new empty registry is used when creating a template contents owner\n // document, so we use that as our parent document to ensure nothing\n // is inherited.\n if (typeof HTMLTemplateElement === 'function') {\n const template = document.createElement('template');\n if (template.content && template.content.ownerDocument) {\n document = template.content.ownerDocument;\n }\n }\n let trustedTypesPolicy;\n let emptyHTML = '';\n const {\n implementation,\n createNodeIterator,\n createDocumentFragment,\n getElementsByTagName\n } = document;\n const {\n importNode\n } = originalDocument;\n let hooks = _createHooksMap();\n /**\n * Expose whether this browser supports running the full DOMPurify.\n */\n DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;\n const {\n MUSTACHE_EXPR,\n ERB_EXPR,\n TMPLIT_EXPR,\n DATA_ATTR,\n ARIA_ATTR,\n IS_SCRIPT_OR_DATA,\n ATTR_WHITESPACE,\n CUSTOM_ELEMENT\n } = EXPRESSIONS;\n let {\n IS_ALLOWED_URI: IS_ALLOWED_URI$1\n } = EXPRESSIONS;\n /**\n * We consider the elements and attributes below to be safe. Ideally\n * don't add any new ones but feel free to remove unwanted ones.\n */\n /* allowed element names */\n let ALLOWED_TAGS = null;\n const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);\n /* Allowed attribute names */\n let ALLOWED_ATTR = null;\n const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);\n /*\n * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.\n * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)\n * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)\n * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.\n */\n let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {\n tagNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n allowCustomizedBuiltInElements: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: false\n }\n }));\n /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */\n let FORBID_TAGS = null;\n /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */\n let FORBID_ATTR = null;\n /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */\n const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {\n tagCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n }\n }));\n /* Decide if ARIA attributes are okay */\n let ALLOW_ARIA_ATTR = true;\n /* Decide if custom data attributes are okay */\n let ALLOW_DATA_ATTR = true;\n /* Decide if unknown protocols are okay */\n let ALLOW_UNKNOWN_PROTOCOLS = false;\n /* Decide if self-closing tags in attributes are allowed.\n * Usually removed due to a mXSS issue in jQuery 3.0 */\n let ALLOW_SELF_CLOSE_IN_ATTR = true;\n /* Output should be safe for common template engines.\n * This means, DOMPurify removes data attributes, mustaches and ERB\n */\n let SAFE_FOR_TEMPLATES = false;\n /* Output should be safe even for XML used within HTML and alike.\n * This means, DOMPurify removes comments when containing risky content.\n */\n let SAFE_FOR_XML = true;\n /* Decide if document with ... should be returned */\n let WHOLE_DOCUMENT = false;\n /* Track whether config is already set on this instance of DOMPurify. */\n let SET_CONFIG = false;\n /* Decide if all elements (e.g. style, script) must be children of\n * document.body. By default, browsers might move them to document.head */\n let FORCE_BODY = false;\n /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported).\n * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead\n */\n let RETURN_DOM = false;\n /* Decide if a DOM `DocumentFragment` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported) */\n let RETURN_DOM_FRAGMENT = false;\n /* Try to return a Trusted Type object instead of a string, return a string in\n * case Trusted Types are not supported */\n let RETURN_TRUSTED_TYPE = false;\n /* Output should be free from DOM clobbering attacks?\n * This sanitizes markups named with colliding, clobberable built-in DOM APIs.\n */\n let SANITIZE_DOM = true;\n /* Achieve full DOM Clobbering protection by isolating the namespace of named\n * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.\n *\n * HTML/DOM spec rules that enable DOM Clobbering:\n * - Named Access on Window (§7.3.3)\n * - DOM Tree Accessors (§3.1.5)\n * - Form Element Parent-Child Relations (§4.10.3)\n * - Iframe srcdoc / Nested WindowProxies (§4.8.5)\n * - HTMLCollection (§4.2.10.2)\n *\n * Namespace isolation is implemented by prefixing `id` and `name` attributes\n * with a constant string, i.e., `user-content-`\n */\n let SANITIZE_NAMED_PROPS = false;\n const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';\n /* Keep element content when removing element? */\n let KEEP_CONTENT = true;\n /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead\n * of importing it into a new Document and returning a sanitized copy */\n let IN_PLACE = false;\n /* Allow usage of profiles like html, svg and mathMl */\n let USE_PROFILES = {};\n /* Tags to ignore content of when KEEP_CONTENT is true */\n let FORBID_CONTENTS = null;\n const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);\n /* Tags that are safe for data: URIs */\n let DATA_URI_TAGS = null;\n const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);\n /* Attributes safe for values like \"javascript:\" */\n let URI_SAFE_ATTRIBUTES = null;\n const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);\n const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n /* Document namespace */\n let NAMESPACE = HTML_NAMESPACE;\n let IS_EMPTY_INPUT = false;\n /* Allowed XHTML+XML namespaces */\n let ALLOWED_NAMESPACES = null;\n const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);\n let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);\n let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);\n // Certain elements are allowed in both SVG and HTML\n // namespace. We need to specify them explicitly\n // so that they don't get erroneously deleted from\n // HTML namespace.\n const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);\n /* Parsing of strict XHTML documents */\n let PARSER_MEDIA_TYPE = null;\n const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];\n const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';\n let transformCaseFunc = null;\n /* Keep a reference to config to pass to hooks */\n let CONFIG = null;\n /* Ideally, do not touch anything below this line */\n /* ______________________________________________ */\n const formElement = document.createElement('form');\n const isRegexOrFunction = function isRegexOrFunction(testValue) {\n return testValue instanceof RegExp || testValue instanceof Function;\n };\n /**\n * _parseConfig\n *\n * @param cfg optional config literal\n */\n // eslint-disable-next-line complexity\n const _parseConfig = function _parseConfig() {\n let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n if (CONFIG && CONFIG === cfg) {\n return;\n }\n /* Shield configuration object from tampering */\n if (!cfg || typeof cfg !== 'object') {\n cfg = {};\n }\n /* Shield configuration object from prototype pollution */\n cfg = clone(cfg);\n PARSER_MEDIA_TYPE =\n // eslint-disable-next-line unicorn/prefer-includes\n SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;\n // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.\n transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;\n /* Set configuration parameters */\n ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;\n ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;\n ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;\n URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;\n DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;\n FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;\n FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});\n FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});\n USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;\n ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true\n ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true\n ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false\n ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true\n SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false\n SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true\n WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false\n RETURN_DOM = cfg.RETURN_DOM || false; // Default false\n RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false\n RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false\n FORCE_BODY = cfg.FORCE_BODY || false; // Default false\n SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true\n SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false\n KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true\n IN_PLACE = cfg.IN_PLACE || false; // Default false\n IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;\n NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;\n MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;\n HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;\n CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};\n if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {\n CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;\n }\n if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {\n CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;\n }\n if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {\n CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;\n }\n if (SAFE_FOR_TEMPLATES) {\n ALLOW_DATA_ATTR = false;\n }\n if (RETURN_DOM_FRAGMENT) {\n RETURN_DOM = true;\n }\n /* Parse profile info */\n if (USE_PROFILES) {\n ALLOWED_TAGS = addToSet({}, text);\n ALLOWED_ATTR = [];\n if (USE_PROFILES.html === true) {\n addToSet(ALLOWED_TAGS, html$1);\n addToSet(ALLOWED_ATTR, html);\n }\n if (USE_PROFILES.svg === true) {\n addToSet(ALLOWED_TAGS, svg$1);\n addToSet(ALLOWED_ATTR, svg);\n addToSet(ALLOWED_ATTR, xml);\n }\n if (USE_PROFILES.svgFilters === true) {\n addToSet(ALLOWED_TAGS, svgFilters);\n addToSet(ALLOWED_ATTR, svg);\n addToSet(ALLOWED_ATTR, xml);\n }\n if (USE_PROFILES.mathMl === true) {\n addToSet(ALLOWED_TAGS, mathMl$1);\n addToSet(ALLOWED_ATTR, mathMl);\n addToSet(ALLOWED_ATTR, xml);\n }\n }\n /* Merge configuration parameters */\n if (cfg.ADD_TAGS) {\n if (typeof cfg.ADD_TAGS === 'function') {\n EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;\n } else {\n if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {\n ALLOWED_TAGS = clone(ALLOWED_TAGS);\n }\n addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);\n }\n }\n if (cfg.ADD_ATTR) {\n if (typeof cfg.ADD_ATTR === 'function') {\n EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;\n } else {\n if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {\n ALLOWED_ATTR = clone(ALLOWED_ATTR);\n }\n addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);\n }\n }\n if (cfg.ADD_URI_SAFE_ATTR) {\n addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);\n }\n if (cfg.FORBID_CONTENTS) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);\n }\n if (cfg.ADD_FORBID_CONTENTS) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);\n }\n /* Add #text in case KEEP_CONTENT is set to true */\n if (KEEP_CONTENT) {\n ALLOWED_TAGS['#text'] = true;\n }\n /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */\n if (WHOLE_DOCUMENT) {\n addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);\n }\n /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */\n if (ALLOWED_TAGS.table) {\n addToSet(ALLOWED_TAGS, ['tbody']);\n delete FORBID_TAGS.tbody;\n }\n if (cfg.TRUSTED_TYPES_POLICY) {\n if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {\n throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a \"createHTML\" hook.');\n }\n if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {\n throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a \"createScriptURL\" hook.');\n }\n // Overwrite existing TrustedTypes policy.\n trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;\n // Sign local variables required by `sanitize`.\n emptyHTML = trustedTypesPolicy.createHTML('');\n } else {\n // Uninitialized policy, attempt to initialize the internal dompurify policy.\n if (trustedTypesPolicy === undefined) {\n trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);\n }\n // If creating the internal policy succeeded sign internal variables.\n if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {\n emptyHTML = trustedTypesPolicy.createHTML('');\n }\n }\n // Prevent further manipulation of configuration.\n // Not available in IE8, Safari 5, etc.\n if (freeze) {\n freeze(cfg);\n }\n CONFIG = cfg;\n };\n /* Keep track of all possible SVG and MathML tags\n * so that we can perform the namespace checks\n * correctly. */\n const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);\n const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);\n /**\n * @param element a DOM element whose namespace is being checked\n * @returns Return false if the element has a\n * namespace that a spec-compliant parser would never\n * return. Return true otherwise.\n */\n const _checkValidNamespace = function _checkValidNamespace(element) {\n let parent = getParentNode(element);\n // In JSDOM, if we're inside shadow DOM, then parentNode\n // can be null. We just simulate parent in this case.\n if (!parent || !parent.tagName) {\n parent = {\n namespaceURI: NAMESPACE,\n tagName: 'template'\n };\n }\n const tagName = stringToLowerCase(element.tagName);\n const parentTagName = stringToLowerCase(parent.tagName);\n if (!ALLOWED_NAMESPACES[element.namespaceURI]) {\n return false;\n }\n if (element.namespaceURI === SVG_NAMESPACE) {\n // The only way to switch from HTML namespace to SVG\n // is via . If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'svg';\n }\n // The only way to switch from MathML to SVG is via`\n // svg if parent is either or MathML\n // text integration points.\n if (parent.namespaceURI === MATHML_NAMESPACE) {\n return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);\n }\n // We only allow elements that are defined in SVG\n // spec. All others are disallowed in SVG namespace.\n return Boolean(ALL_SVG_TAGS[tagName]);\n }\n if (element.namespaceURI === MATHML_NAMESPACE) {\n // The only way to switch from HTML namespace to MathML\n // is via . If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'math';\n }\n // The only way to switch from SVG to MathML is via\n // and HTML integration points\n if (parent.namespaceURI === SVG_NAMESPACE) {\n return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];\n }\n // We only allow elements that are defined in MathML\n // spec. All others are disallowed in MathML namespace.\n return Boolean(ALL_MATHML_TAGS[tagName]);\n }\n if (element.namespaceURI === HTML_NAMESPACE) {\n // The only way to switch from SVG to HTML is via\n // HTML integration points, and from MathML to HTML\n // is via MathML text integration points\n if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {\n return false;\n }\n if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {\n return false;\n }\n // We disallow tags that are specific for MathML\n // or SVG and should never appear in HTML namespace\n return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);\n }\n // For XHTML and XML documents that support custom namespaces\n if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {\n return true;\n }\n // The code should never reach this place (this means\n // that the element somehow got namespace that is not\n // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).\n // Return false just in case.\n return false;\n };\n /**\n * _forceRemove\n *\n * @param node a DOM node\n */\n const _forceRemove = function _forceRemove(node) {\n arrayPush(DOMPurify.removed, {\n element: node\n });\n try {\n // eslint-disable-next-line unicorn/prefer-dom-node-remove\n getParentNode(node).removeChild(node);\n } catch (_) {\n remove(node);\n }\n };\n /**\n * _removeAttribute\n *\n * @param name an Attribute name\n * @param element a DOM node\n */\n const _removeAttribute = function _removeAttribute(name, element) {\n try {\n arrayPush(DOMPurify.removed, {\n attribute: element.getAttributeNode(name),\n from: element\n });\n } catch (_) {\n arrayPush(DOMPurify.removed, {\n attribute: null,\n from: element\n });\n }\n element.removeAttribute(name);\n // We void attribute values for unremovable \"is\" attributes\n if (name === 'is') {\n if (RETURN_DOM || RETURN_DOM_FRAGMENT) {\n try {\n _forceRemove(element);\n } catch (_) {}\n } else {\n try {\n element.setAttribute(name, '');\n } catch (_) {}\n }\n }\n };\n /**\n * _initDocument\n *\n * @param dirty - a string of dirty markup\n * @return a DOM, filled with the dirty markup\n */\n const _initDocument = function _initDocument(dirty) {\n /* Create a HTML document */\n let doc = null;\n let leadingWhitespace = null;\n if (FORCE_BODY) {\n dirty = '' + dirty;\n } else {\n /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */\n const matches = stringMatch(dirty, /^[\\r\\n\\t ]+/);\n leadingWhitespace = matches && matches[0];\n }\n if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {\n // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)\n dirty = '' + dirty + '';\n }\n const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;\n /*\n * Use the DOMParser API by default, fallback later if needs be\n * DOMParser not work for svg when has multiple root element.\n */\n if (NAMESPACE === HTML_NAMESPACE) {\n try {\n doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);\n } catch (_) {}\n }\n /* Use createHTMLDocument in case DOMParser is not available */\n if (!doc || !doc.documentElement) {\n doc = implementation.createDocument(NAMESPACE, 'template', null);\n try {\n doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;\n } catch (_) {\n // Syntax error if dirtyPayload is invalid xml\n }\n }\n const body = doc.body || doc.documentElement;\n if (dirty && leadingWhitespace) {\n body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);\n }\n /* Work on whole document or just its body */\n if (NAMESPACE === HTML_NAMESPACE) {\n return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];\n }\n return WHOLE_DOCUMENT ? doc.documentElement : body;\n };\n /**\n * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.\n *\n * @param root The root element or node to start traversing on.\n * @return The created NodeIterator\n */\n const _createNodeIterator = function _createNodeIterator(root) {\n return createNodeIterator.call(root.ownerDocument || root, root,\n // eslint-disable-next-line no-bitwise\n NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);\n };\n /**\n * _isClobbered\n *\n * @param element element to check for clobbering attacks\n * @return true if clobbered, false if safe\n */\n const _isClobbered = function _isClobbered(element) {\n return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');\n };\n /**\n * Checks whether the given object is a DOM node.\n *\n * @param value object to check whether it's a DOM node\n * @return true is object is a DOM node\n */\n const _isNode = function _isNode(value) {\n return typeof Node === 'function' && value instanceof Node;\n };\n function _executeHooks(hooks, currentNode, data) {\n arrayForEach(hooks, hook => {\n hook.call(DOMPurify, currentNode, data, CONFIG);\n });\n }\n /**\n * _sanitizeElements\n *\n * @protect nodeName\n * @protect textContent\n * @protect removeChild\n * @param currentNode to check for permission to exist\n * @return true if node was killed, false if left alive\n */\n const _sanitizeElements = function _sanitizeElements(currentNode) {\n let content = null;\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeElements, currentNode, null);\n /* Check if element is clobbered or can clobber */\n if (_isClobbered(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Now let's check the element's type and name */\n const tagName = transformCaseFunc(currentNode.nodeName);\n /* Execute a hook if present */\n _executeHooks(hooks.uponSanitizeElement, currentNode, {\n tagName,\n allowedTags: ALLOWED_TAGS\n });\n /* Detect mXSS attempts abusing namespace confusion */\n if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\\w!]/g, currentNode.textContent)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove any occurrence of processing instructions */\n if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove any kind of possibly harmful comments */\n if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\\w]/g, currentNode.data)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove element if anything forbids its presence */\n if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {\n /* Check if we have a custom element to handle */\n if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {\n if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {\n return false;\n }\n if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {\n return false;\n }\n }\n /* Keep content except for bad-listed elements */\n if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {\n const parentNode = getParentNode(currentNode) || currentNode.parentNode;\n const childNodes = getChildNodes(currentNode) || currentNode.childNodes;\n if (childNodes && parentNode) {\n const childCount = childNodes.length;\n for (let i = childCount - 1; i >= 0; --i) {\n const childClone = cloneNode(childNodes[i], true);\n childClone.__removalCount = (currentNode.__removalCount || 0) + 1;\n parentNode.insertBefore(childClone, getNextSibling(currentNode));\n }\n }\n }\n _forceRemove(currentNode);\n return true;\n }\n /* Check whether element has a valid namespace */\n if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Make sure that older browsers don't get fallback-tag mXSS */\n if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\\/no(script|embed|frames)/i, currentNode.innerHTML)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Sanitize element content to be template-safe */\n if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {\n /* Get the element's text content */\n content = currentNode.textContent;\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n content = stringReplace(content, expr, ' ');\n });\n if (currentNode.textContent !== content) {\n arrayPush(DOMPurify.removed, {\n element: currentNode.cloneNode()\n });\n currentNode.textContent = content;\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeElements, currentNode, null);\n return false;\n };\n /**\n * _isValidAttribute\n *\n * @param lcTag Lowercase tag name of containing element.\n * @param lcName Lowercase attribute name.\n * @param value Attribute value.\n * @return Returns true if `value` is valid, otherwise false.\n */\n // eslint-disable-next-line complexity\n const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {\n /* Make sure attribute cannot clobber */\n if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {\n return false;\n }\n /* Allow valid data-* attributes: At least one character after \"-\"\n (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)\n XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)\n We don't need to check the value; it's always URI safe. */\n if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {\n if (\n // First condition does a very basic check if a) it's basically a valid custom element tagname AND\n // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck\n // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck\n _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName, lcTag)) ||\n // Alternative, second condition checks if it's an `is`-attribute, AND\n // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck\n lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {\n return false;\n }\n /* Check value is safe. First, is attr inert? If so, is safe */\n } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {\n return false;\n } else ;\n return true;\n };\n /**\n * _isBasicCustomElement\n * checks if at least one dash is included in tagName, and it's not the first char\n * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name\n *\n * @param tagName name of the tag of the node to sanitize\n * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.\n */\n const _isBasicCustomElement = function _isBasicCustomElement(tagName) {\n return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);\n };\n /**\n * _sanitizeAttributes\n *\n * @protect attributes\n * @protect nodeName\n * @protect removeAttribute\n * @protect setAttribute\n *\n * @param currentNode to sanitize\n */\n const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);\n const {\n attributes\n } = currentNode;\n /* Check if we have attributes; if not we might have a text node */\n if (!attributes || _isClobbered(currentNode)) {\n return;\n }\n const hookEvent = {\n attrName: '',\n attrValue: '',\n keepAttr: true,\n allowedAttributes: ALLOWED_ATTR,\n forceKeepAttr: undefined\n };\n let l = attributes.length;\n /* Go backwards over all attributes; safely remove bad ones */\n while (l--) {\n const attr = attributes[l];\n const {\n name,\n namespaceURI,\n value: attrValue\n } = attr;\n const lcName = transformCaseFunc(name);\n const initValue = attrValue;\n let value = name === 'value' ? initValue : stringTrim(initValue);\n /* Execute a hook if present */\n hookEvent.attrName = lcName;\n hookEvent.attrValue = value;\n hookEvent.keepAttr = true;\n hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set\n _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);\n value = hookEvent.attrValue;\n /* Full DOM Clobbering protection via namespace isolation,\n * Prefix id and name attributes with `user-content-`\n */\n if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {\n // Remove the attribute with this value\n _removeAttribute(name, currentNode);\n // Prefix the value and later re-create the attribute with the sanitized value\n value = SANITIZE_NAMED_PROPS_PREFIX + value;\n }\n /* Work around a security issue with comments inside attributes */\n if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\\/(style|title|textarea)/i, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Make sure we cannot easily use animated hrefs, even if animations are allowed */\n if (lcName === 'attributename' && stringMatch(value, 'href')) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Did the hooks approve of the attribute? */\n if (hookEvent.forceKeepAttr) {\n continue;\n }\n /* Did the hooks approve of the attribute? */\n if (!hookEvent.keepAttr) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Work around a security issue in jQuery 3.0 */\n if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\\/>/i, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Sanitize attribute content to be template-safe */\n if (SAFE_FOR_TEMPLATES) {\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n value = stringReplace(value, expr, ' ');\n });\n }\n /* Is `value` valid for this attribute? */\n const lcTag = transformCaseFunc(currentNode.nodeName);\n if (!_isValidAttribute(lcTag, lcName, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Handle attributes that require Trusted Types */\n if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {\n if (namespaceURI) ; else {\n switch (trustedTypes.getAttributeType(lcTag, lcName)) {\n case 'TrustedHTML':\n {\n value = trustedTypesPolicy.createHTML(value);\n break;\n }\n case 'TrustedScriptURL':\n {\n value = trustedTypesPolicy.createScriptURL(value);\n break;\n }\n }\n }\n }\n /* Handle invalid data-* attribute set by try-catching it */\n if (value !== initValue) {\n try {\n if (namespaceURI) {\n currentNode.setAttributeNS(namespaceURI, name, value);\n } else {\n /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. \"x-schema\". */\n currentNode.setAttribute(name, value);\n }\n if (_isClobbered(currentNode)) {\n _forceRemove(currentNode);\n } else {\n arrayPop(DOMPurify.removed);\n }\n } catch (_) {\n _removeAttribute(name, currentNode);\n }\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);\n };\n /**\n * _sanitizeShadowDOM\n *\n * @param fragment to iterate over recursively\n */\n const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {\n let shadowNode = null;\n const shadowIterator = _createNodeIterator(fragment);\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);\n while (shadowNode = shadowIterator.nextNode()) {\n /* Execute a hook if present */\n _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);\n /* Sanitize tags and elements */\n _sanitizeElements(shadowNode);\n /* Check attributes next */\n _sanitizeAttributes(shadowNode);\n /* Deep shadow DOM detected */\n if (shadowNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(shadowNode.content);\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);\n };\n // eslint-disable-next-line complexity\n DOMPurify.sanitize = function (dirty) {\n let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n let body = null;\n let importedNode = null;\n let currentNode = null;\n let returnNode = null;\n /* Make sure we have a string to sanitize.\n DO NOT return early, as this will return the wrong type if\n the user has requested a DOM object rather than a string */\n IS_EMPTY_INPUT = !dirty;\n if (IS_EMPTY_INPUT) {\n dirty = '';\n }\n /* Stringify, in case dirty is an object */\n if (typeof dirty !== 'string' && !_isNode(dirty)) {\n if (typeof dirty.toString === 'function') {\n dirty = dirty.toString();\n if (typeof dirty !== 'string') {\n throw typeErrorCreate('dirty is not a string, aborting');\n }\n } else {\n throw typeErrorCreate('toString is not a function');\n }\n }\n /* Return dirty HTML if DOMPurify cannot run */\n if (!DOMPurify.isSupported) {\n return dirty;\n }\n /* Assign config vars */\n if (!SET_CONFIG) {\n _parseConfig(cfg);\n }\n /* Clean up removed elements */\n DOMPurify.removed = [];\n /* Check if dirty is correctly typed for IN_PLACE */\n if (typeof dirty === 'string') {\n IN_PLACE = false;\n }\n if (IN_PLACE) {\n /* Do some early pre-sanitization to avoid unsafe root nodes */\n if (dirty.nodeName) {\n const tagName = transformCaseFunc(dirty.nodeName);\n if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {\n throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');\n }\n }\n } else if (dirty instanceof Node) {\n /* If dirty is a DOM element, append to an empty document to avoid\n elements being stripped by the parser */\n body = _initDocument('');\n importedNode = body.ownerDocument.importNode(dirty, true);\n if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {\n /* Node is already a body, use as is */\n body = importedNode;\n } else if (importedNode.nodeName === 'HTML') {\n body = importedNode;\n } else {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n body.appendChild(importedNode);\n }\n } else {\n /* Exit directly if we have nothing to do */\n if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&\n // eslint-disable-next-line unicorn/prefer-includes\n dirty.indexOf('<') === -1) {\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;\n }\n /* Initialize the document to work on */\n body = _initDocument(dirty);\n /* Check we have a DOM node from the data */\n if (!body) {\n return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';\n }\n }\n /* Remove first element node (ours) if FORCE_BODY is set */\n if (body && FORCE_BODY) {\n _forceRemove(body.firstChild);\n }\n /* Get node iterator */\n const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);\n /* Now start iterating over the created document */\n while (currentNode = nodeIterator.nextNode()) {\n /* Sanitize tags and elements */\n _sanitizeElements(currentNode);\n /* Check attributes next */\n _sanitizeAttributes(currentNode);\n /* Shadow DOM detected, sanitize it */\n if (currentNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(currentNode.content);\n }\n }\n /* If we sanitized `dirty` in-place, return it. */\n if (IN_PLACE) {\n return dirty;\n }\n /* Return sanitized string or DOM */\n if (RETURN_DOM) {\n if (RETURN_DOM_FRAGMENT) {\n returnNode = createDocumentFragment.call(body.ownerDocument);\n while (body.firstChild) {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n returnNode.appendChild(body.firstChild);\n }\n } else {\n returnNode = body;\n }\n if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {\n /*\n AdoptNode() is not used because internal state is not reset\n (e.g. the past names map of a HTMLFormElement), this is safe\n in theory but we would rather not risk another attack vector.\n The state that is cloned by importNode() is explicitly defined\n by the specs.\n */\n returnNode = importNode.call(originalDocument, returnNode, true);\n }\n return returnNode;\n }\n let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;\n /* Serialize doctype if allowed */\n if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {\n serializedHTML = '\\n' + serializedHTML;\n }\n /* Sanitize final string template-safe */\n if (SAFE_FOR_TEMPLATES) {\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n serializedHTML = stringReplace(serializedHTML, expr, ' ');\n });\n }\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;\n };\n DOMPurify.setConfig = function () {\n let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _parseConfig(cfg);\n SET_CONFIG = true;\n };\n DOMPurify.clearConfig = function () {\n CONFIG = null;\n SET_CONFIG = false;\n };\n DOMPurify.isValidAttribute = function (tag, attr, value) {\n /* Initialize shared config vars if necessary. */\n if (!CONFIG) {\n _parseConfig({});\n }\n const lcTag = transformCaseFunc(tag);\n const lcName = transformCaseFunc(attr);\n return _isValidAttribute(lcTag, lcName, value);\n };\n DOMPurify.addHook = function (entryPoint, hookFunction) {\n if (typeof hookFunction !== 'function') {\n return;\n }\n arrayPush(hooks[entryPoint], hookFunction);\n };\n DOMPurify.removeHook = function (entryPoint, hookFunction) {\n if (hookFunction !== undefined) {\n const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);\n return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];\n }\n return arrayPop(hooks[entryPoint]);\n };\n DOMPurify.removeHooks = function (entryPoint) {\n hooks[entryPoint] = [];\n };\n DOMPurify.removeAllHooks = function () {\n hooks = _createHooksMap();\n };\n return DOMPurify;\n}\nvar purify = createDOMPurify();\n\nexport { purify as default };\n//# sourceMappingURL=purify.es.mjs.map\n","/**\n * marked v17.0.1 - a markdown parser\n * Copyright (c) 2018-2025, MarkedJS. (MIT License)\n * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License)\n * https://github.com/markedjs/marked\n */\n\n/**\n * DO NOT EDIT THIS FILE\n * The code in this file is generated from files in ./src/\n */\n\nfunction L(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var T=L();function Z(u){T=u}var C={exec:()=>null};function k(u,e=\"\"){let t=typeof u==\"string\"?u:u.source,n={replace:(r,i)=>{let s=typeof i==\"string\"?i:i.source;return s=s.replace(m.caret,\"$1\"),t=t.replace(r,s),n},getRegex:()=>new RegExp(t,e)};return n}var me=(()=>{try{return!!new RegExp(\"(?<=1)(?/,blockquoteSetextReplace:/\\n {0,3}((?:=+|-+) *)(?=\\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \\t]?/gm,listReplaceTabs:/^\\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\\[[ xX]\\] +\\S/,listReplaceTask:/^\\[[ xX]\\] +/,listTaskCheckbox:/\\[[ xX]\\]/,anyLine:/\\n.*\\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\\||\\| *$/g,tableRowBlankLine:/\\n[ \\t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\\s|>)/i,endPreScriptTag:/^<\\/(pre|code|kbd|script)(\\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'\"]*[^\\s])\\s+(['\"])(.*)\\2/,unicodeAlphaNumeric:/[\\p{L}\\p{N}]/u,escapeTest:/[&<>\"']/,escapeReplace:/[&<>\"']/g,escapeTestNoEncode:/[<>\"']|&(?!(#\\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\\w+);)/,escapeReplaceNoEncode:/[<>\"']|&(?!(#\\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\\w+);)/g,unescapeTest:/&(#(?:\\d+)|(?:#x[0-9A-Fa-f]+)|(?:\\w+));?/ig,caret:/(^|[^\\[])\\^/g,percentDecode:/%25/g,findPipe:/\\|/g,splitPipe:/ \\|/,slashPipe:/\\\\\\|/g,carriageReturn:/\\r\\n|\\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\\S*/,endingNewline:/\\n$/,listItemRegex:u=>new RegExp(`^( {0,3}${u})((?:[\t ][^\\\\n]*)?(?:\\\\n|$))`),nextBulletRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}(?:[*+-]|\\\\d{1,9}[.)])((?:[ \t][^\\\\n]*)?(?:\\\\n|$))`),hrRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\\\* *){3,})(?:\\\\n+|$)`),fencesBeginRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}(?:\\`\\`\\`|~~~)`),headingBeginRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}#`),htmlBeginRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}<(?:[a-z].*>|!--)`,\"i\")},xe=/^(?:[ \\t]*(?:\\n|$))+/,be=/^((?: {4}| {0,3}\\t)[^\\n]+(?:\\n(?:[ \\t]*(?:\\n|$))*)?)+/,Re=/^ {0,3}(`{3,}(?=[^`\\n]*(?:\\n|$))|~{3,})([^\\n]*)(?:\\n|$)(?:|([\\s\\S]*?)(?:\\n|$))(?: {0,3}\\1[~`]* *(?=\\n|$)|$)/,I=/^ {0,3}((?:-[\\t ]*){3,}|(?:_[ \\t]*){3,}|(?:\\*[ \\t]*){3,})(?:\\n+|$)/,Te=/^ {0,3}(#{1,6})(?=\\s|$)(.*)(?:\\n+|$)/,N=/(?:[*+-]|\\d{1,9}[.)])/,re=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\\n(?!\\s*?\\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,se=k(re).replace(/bull/g,N).replace(/blockCode/g,/(?: {4}| {0,3}\\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\\n>]+>\\n/).replace(/\\|table/g,\"\").getRegex(),Oe=k(re).replace(/bull/g,N).replace(/blockCode/g,/(?: {4}| {0,3}\\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\\n>]+>\\n/).replace(/table/g,/ {0,3}\\|?(?:[:\\- ]*\\|)+[\\:\\- ]*\\n/).getRegex(),Q=/^([^\\n]+(?:\\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\\n)[^\\n]+)*)/,we=/^[^\\n]+/,F=/(?!\\s*\\])(?:\\\\[\\s\\S]|[^\\[\\]\\\\])+/,ye=k(/^ {0,3}\\[(label)\\]: *(?:\\n[ \\t]*)?([^<\\s][^\\s]*|<.*?>)(?:(?: +(?:\\n[ \\t]*)?| *\\n[ \\t]*)(title))? *(?:\\n+|$)/).replace(\"label\",F).replace(\"title\",/(?:\"(?:\\\\\"?|[^\"\\\\])*\"|'[^'\\n]*(?:\\n[^'\\n]+)*\\n?'|\\([^()]*\\))/).getRegex(),Pe=k(/^( {0,3}bull)([ \\t][^\\n]+?)?(?:\\n|$)/).replace(/bull/g,N).getRegex(),v=\"address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul\",j=/|$))/,Se=k(\"^ {0,3}(?:<(script|pre|style|textarea)[\\\\s>][\\\\s\\\\S]*?(?:[^\\\\n]*\\\\n+|$)|comment[^\\\\n]*(\\\\n+|$)|<\\\\?[\\\\s\\\\S]*?(?:\\\\?>\\\\n*|$)|\\\\n*|$)|\\\\n*|$)|)[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$)|<(?!script|pre|style|textarea)([a-z][\\\\w-]*)(?:attribute)*? */?>(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$)|(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$))\",\"i\").replace(\"comment\",j).replace(\"tag\",v).replace(\"attribute\",/ +[a-zA-Z:_][\\w.:-]*(?: *= *\"[^\"\\n]*\"| *= *'[^'\\n]*'| *= *[^\\s\"'=<>`]+)?/).getRegex(),ie=k(Q).replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"|lheading\",\"\").replace(\"|table\",\"\").replace(\"blockquote\",\" {0,3}>\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)]) \").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex(),$e=k(/^( {0,3}> ?(paragraph|[^\\n]*)(?:\\n|$))+/).replace(\"paragraph\",ie).getRegex(),U={blockquote:$e,code:be,def:ye,fences:Re,heading:Te,hr:I,html:Se,lheading:se,list:Pe,newline:xe,paragraph:ie,table:C,text:we},te=k(\"^ *([^\\\\n ].*)\\\\n {0,3}((?:\\\\| *)?:?-+:? *(?:\\\\| *:?-+:? *)*(?:\\\\| *)?)(?:\\\\n((?:(?! *\\\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\\\n|$))*)\\\\n*|$)\").replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"blockquote\",\" {0,3}>\").replace(\"code\",\"(?: {4}| {0,3}\t)[^\\\\n]\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)]) \").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex(),_e={...U,lheading:Oe,table:te,paragraph:k(Q).replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"|lheading\",\"\").replace(\"table\",te).replace(\"blockquote\",\" {0,3}>\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)]) \").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex()},Le={...U,html:k(`^ *(?:comment *(?:\\\\n|\\\\s*$)|<(tag)[\\\\s\\\\S]+? *(?:\\\\n{2,}|\\\\s*$)|\\\\s]*)*?/?> *(?:\\\\n{2,}|\\\\s*$))`).replace(\"comment\",j).replace(/tag/g,\"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\\\b)\\\\w+(?!:|[^\\\\w\\\\s@]*@)\\\\b\").getRegex(),def:/^ *\\[([^\\]]+)\\]: *]+)>?(?: +([\"(][^\\n]+[\")]))? *(?:\\n+|$)/,heading:/^(#{1,6})(.*)(?:\\n+|$)/,fences:C,lheading:/^(.+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,paragraph:k(Q).replace(\"hr\",I).replace(\"heading\",` *#{1,6} *[^\n]`).replace(\"lheading\",se).replace(\"|table\",\"\").replace(\"blockquote\",\" {0,3}>\").replace(\"|fences\",\"\").replace(\"|list\",\"\").replace(\"|html\",\"\").replace(\"|tag\",\"\").getRegex()},Me=/^\\\\([!\"#$%&'()*+,\\-./:;<=>?@\\[\\]\\\\^_`{|}~])/,ze=/^(`+)([^`]|[^`][\\s\\S]*?[^`])\\1(?!`)/,oe=/^( {2,}|\\\\)\\n(?!\\s*$)/,Ae=/^(`+|[^`])(?:(?= {2,}\\n)|[\\s\\S]*?(?:(?=[\\\\`+)[^`]+\\k(?!`))*?\\]\\((?:\\\\[\\s\\S]|[^\\\\\\(\\)]|\\((?:\\\\[\\s\\S]|[^\\\\\\(\\)])*\\))*\\)/).replace(\"precode-\",me?\"(?`+)[^`]+\\k(?!`)/).replace(\"html\",/<(?! )[^<>]*?>/).getRegex(),ue=/^(?:\\*+(?:((?!\\*)punct)|[^\\s*]))|^_+(?:((?!_)punct)|([^\\s_]))/,qe=k(ue,\"u\").replace(/punct/g,D).getRegex(),ve=k(ue,\"u\").replace(/punct/g,le).getRegex(),pe=\"^[^_*]*?__[^_*]*?\\\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\\\*)punct(\\\\*+)(?=[\\\\s]|$)|notPunctSpace(\\\\*+)(?!\\\\*)(?=punctSpace|$)|(?!\\\\*)punctSpace(\\\\*+)(?=notPunctSpace)|[\\\\s](\\\\*+)(?!\\\\*)(?=punct)|(?!\\\\*)punct(\\\\*+)(?!\\\\*)(?=punct)|notPunctSpace(\\\\*+)(?=notPunctSpace)\",De=k(pe,\"gu\").replace(/notPunctSpace/g,ae).replace(/punctSpace/g,K).replace(/punct/g,D).getRegex(),He=k(pe,\"gu\").replace(/notPunctSpace/g,Ee).replace(/punctSpace/g,Ie).replace(/punct/g,le).getRegex(),Ze=k(\"^[^_*]*?\\\\*\\\\*[^_*]*?_[^_*]*?(?=\\\\*\\\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)\",\"gu\").replace(/notPunctSpace/g,ae).replace(/punctSpace/g,K).replace(/punct/g,D).getRegex(),Ge=k(/\\\\(punct)/,\"gu\").replace(/punct/g,D).getRegex(),Ne=k(/^<(scheme:[^\\s\\x00-\\x1f<>]*|email)>/).replace(\"scheme\",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace(\"email\",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),Qe=k(j).replace(\"(?:-->|$)\",\"-->\").getRegex(),Fe=k(\"^comment|^|^<[a-zA-Z][\\\\w-]*(?:attribute)*?\\\\s*/?>|^<\\\\?[\\\\s\\\\S]*?\\\\?>|^|^\").replace(\"comment\",Qe).replace(\"attribute\",/\\s+[a-zA-Z:_][\\w.:-]*(?:\\s*=\\s*\"[^\"]*\"|\\s*=\\s*'[^']*'|\\s*=\\s*[^\\s\"'=<>`]+)?/).getRegex(),q=/(?:\\[(?:\\\\[\\s\\S]|[^\\[\\]\\\\])*\\]|\\\\[\\s\\S]|`+[^`]*?`+(?!`)|[^\\[\\]\\\\`])*?/,je=k(/^!?\\[(label)\\]\\(\\s*(href)(?:(?:[ \\t]*(?:\\n[ \\t]*)?)(title))?\\s*\\)/).replace(\"label\",q).replace(\"href\",/<(?:\\\\.|[^\\n<>\\\\])+>|[^ \\t\\n\\x00-\\x1f]*/).replace(\"title\",/\"(?:\\\\\"?|[^\"\\\\])*\"|'(?:\\\\'?|[^'\\\\])*'|\\((?:\\\\\\)?|[^)\\\\])*\\)/).getRegex(),ce=k(/^!?\\[(label)\\]\\[(ref)\\]/).replace(\"label\",q).replace(\"ref\",F).getRegex(),he=k(/^!?\\[(ref)\\](?:\\[\\])?/).replace(\"ref\",F).getRegex(),Ue=k(\"reflink|nolink(?!\\\\()\",\"g\").replace(\"reflink\",ce).replace(\"nolink\",he).getRegex(),ne=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,W={_backpedal:C,anyPunctuation:Ge,autolink:Ne,blockSkip:Be,br:oe,code:ze,del:C,emStrongLDelim:qe,emStrongRDelimAst:De,emStrongRDelimUnd:Ze,escape:Me,link:je,nolink:he,punctuation:Ce,reflink:ce,reflinkSearch:Ue,tag:Fe,text:Ae,url:C},Ke={...W,link:k(/^!?\\[(label)\\]\\((.*?)\\)/).replace(\"label\",q).getRegex(),reflink:k(/^!?\\[(label)\\]\\s*\\[([^\\]]*)\\]/).replace(\"label\",q).getRegex()},G={...W,emStrongRDelimAst:He,emStrongLDelim:ve,url:k(/^((?:protocol):\\/\\/|www\\.)(?:[a-zA-Z0-9\\-]+\\.?)+[^\\s<]*|^email/).replace(\"protocol\",ne).replace(\"email\",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'\"~()&]+|\\([^)]*\\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'\"~)]+(?!$))+/,del:/^(~~?)(?=[^\\s~])((?:\\\\[\\s\\S]|[^\\\\])*?(?:\\\\[\\s\\S]|[^\\s~\\\\]))\\1(?=[^~]|$)/,text:k(/^([`~]+|[^`~])(?:(?= {2,}\\n)|(?=[a-zA-Z0-9.!#$%&'*+\\/=?_`{\\|}~-]+@)|[\\s\\S]*?(?:(?=[\\\\\":\">\",'\"':\""\",\"'\":\"'\"},ke=u=>Xe[u];function w(u,e){if(e){if(m.escapeTest.test(u))return u.replace(m.escapeReplace,ke)}else if(m.escapeTestNoEncode.test(u))return u.replace(m.escapeReplaceNoEncode,ke);return u}function X(u){try{u=encodeURI(u).replace(m.percentDecode,\"%\")}catch{return null}return u}function J(u,e){let t=u.replace(m.findPipe,(i,s,a)=>{let o=!1,l=s;for(;--l>=0&&a[l]===\"\\\\\";)o=!o;return o?\"|\":\" |\"}),n=t.split(m.splitPipe),r=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length0?-2:-1}function ge(u,e,t,n,r){let i=e.href,s=e.title||null,a=u[1].replace(r.other.outputLinkReplace,\"$1\");n.state.inLink=!0;let o={type:u[0].charAt(0)===\"!\"?\"image\":\"link\",raw:t,href:i,title:s,text:a,tokens:n.inlineTokens(a)};return n.state.inLink=!1,o}function Je(u,e,t){let n=u.match(t.other.indentCodeCompensation);if(n===null)return e;let r=n[1];return e.split(`\n`).map(i=>{let s=i.match(t.other.beginningSpace);if(s===null)return i;let[a]=s;return a.length>=r.length?i.slice(r.length):i}).join(`\n`)}var y=class{options;rules;lexer;constructor(e){this.options=e||T}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:\"space\",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=t[0].replace(this.rules.other.codeRemoveIndent,\"\");return{type:\"code\",raw:t[0],codeBlockStyle:\"indented\",text:this.options.pedantic?n:z(n,`\n`)}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],r=Je(n,t[3]||\"\",this.rules);return{type:\"code\",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,\"$1\"):t[2],text:r}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let r=z(n,\"#\");(this.options.pedantic||!r||this.rules.other.endingSpaceChar.test(r))&&(n=r.trim())}return{type:\"heading\",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:\"hr\",raw:z(t[0],`\n`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=z(t[0],`\n`).split(`\n`),r=\"\",i=\"\",s=[];for(;n.length>0;){let a=!1,o=[],l;for(l=0;l1,i={type:\"list\",raw:\"\",ordered:r,start:r?+n.slice(0,-1):\"\",loose:!1,items:[]};n=r?`\\\\d{1,9}\\\\${n.slice(-1)}`:`\\\\${n}`,this.options.pedantic&&(n=r?n:\"[*+-]\");let s=this.rules.other.listItemRegex(n),a=!1;for(;e;){let l=!1,p=\"\",c=\"\";if(!(t=s.exec(e))||this.rules.block.hr.test(e))break;p=t[0],e=e.substring(p.length);let g=t[2].split(`\n`,1)[0].replace(this.rules.other.listReplaceTabs,O=>\" \".repeat(3*O.length)),h=e.split(`\n`,1)[0],R=!g.trim(),f=0;if(this.options.pedantic?(f=2,c=g.trimStart()):R?f=t[1].length+1:(f=t[2].search(this.rules.other.nonSpaceChar),f=f>4?1:f,c=g.slice(f),f+=t[1].length),R&&this.rules.other.blankLine.test(h)&&(p+=h+`\n`,e=e.substring(h.length+1),l=!0),!l){let O=this.rules.other.nextBulletRegex(f),V=this.rules.other.hrRegex(f),Y=this.rules.other.fencesBeginRegex(f),ee=this.rules.other.headingBeginRegex(f),fe=this.rules.other.htmlBeginRegex(f);for(;e;){let H=e.split(`\n`,1)[0],A;if(h=H,this.options.pedantic?(h=h.replace(this.rules.other.listReplaceNesting,\" \"),A=h):A=h.replace(this.rules.other.tabCharGlobal,\" \"),Y.test(h)||ee.test(h)||fe.test(h)||O.test(h)||V.test(h))break;if(A.search(this.rules.other.nonSpaceChar)>=f||!h.trim())c+=`\n`+A.slice(f);else{if(R||g.replace(this.rules.other.tabCharGlobal,\" \").search(this.rules.other.nonSpaceChar)>=4||Y.test(g)||ee.test(g)||V.test(g))break;c+=`\n`+h}!R&&!h.trim()&&(R=!0),p+=H+`\n`,e=e.substring(H.length+1),g=A.slice(f)}}i.loose||(a?i.loose=!0:this.rules.other.doubleBlankLine.test(p)&&(a=!0)),i.items.push({type:\"list_item\",raw:p,task:!!this.options.gfm&&this.rules.other.listIsTask.test(c),loose:!1,text:c,tokens:[]}),i.raw+=p}let o=i.items.at(-1);if(o)o.raw=o.raw.trimEnd(),o.text=o.text.trimEnd();else return;i.raw=i.raw.trimEnd();for(let l of i.items){if(this.lexer.state.top=!1,l.tokens=this.lexer.blockTokens(l.text,[]),l.task){if(l.text=l.text.replace(this.rules.other.listReplaceTask,\"\"),l.tokens[0]?.type===\"text\"||l.tokens[0]?.type===\"paragraph\"){l.tokens[0].raw=l.tokens[0].raw.replace(this.rules.other.listReplaceTask,\"\"),l.tokens[0].text=l.tokens[0].text.replace(this.rules.other.listReplaceTask,\"\");for(let c=this.lexer.inlineQueue.length-1;c>=0;c--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[c].src)){this.lexer.inlineQueue[c].src=this.lexer.inlineQueue[c].src.replace(this.rules.other.listReplaceTask,\"\");break}}let p=this.rules.other.listTaskCheckbox.exec(l.raw);if(p){let c={type:\"checkbox\",raw:p[0]+\" \",checked:p[0]!==\"[ ]\"};l.checked=c.checked,i.loose?l.tokens[0]&&[\"paragraph\",\"text\"].includes(l.tokens[0].type)&&\"tokens\"in l.tokens[0]&&l.tokens[0].tokens?(l.tokens[0].raw=c.raw+l.tokens[0].raw,l.tokens[0].text=c.raw+l.tokens[0].text,l.tokens[0].tokens.unshift(c)):l.tokens.unshift({type:\"paragraph\",raw:c.raw,text:c.raw,tokens:[c]}):l.tokens.unshift(c)}}if(!i.loose){let p=l.tokens.filter(g=>g.type===\"space\"),c=p.length>0&&p.some(g=>this.rules.other.anyLine.test(g.raw));i.loose=c}}if(i.loose)for(let l of i.items){l.loose=!0;for(let p of l.tokens)p.type===\"text\"&&(p.type=\"paragraph\")}return i}}html(e){let t=this.rules.block.html.exec(e);if(t)return{type:\"html\",block:!0,raw:t[0],pre:t[1]===\"pre\"||t[1]===\"script\"||t[1]===\"style\",text:t[0]}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal,\" \"),r=t[2]?t[2].replace(this.rules.other.hrefBrackets,\"$1\").replace(this.rules.inline.anyPunctuation,\"$1\"):\"\",i=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,\"$1\"):t[3];return{type:\"def\",tag:n,raw:t[0],href:r,title:i}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=J(t[1]),r=t[2].replace(this.rules.other.tableAlignChars,\"\").split(\"|\"),i=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,\"\").split(`\n`):[],s={type:\"table\",raw:t[0],header:[],align:[],rows:[]};if(n.length===r.length){for(let a of r)this.rules.other.tableAlignRight.test(a)?s.align.push(\"right\"):this.rules.other.tableAlignCenter.test(a)?s.align.push(\"center\"):this.rules.other.tableAlignLeft.test(a)?s.align.push(\"left\"):s.align.push(null);for(let a=0;a({text:o,tokens:this.lexer.inline(o),header:!1,align:s.align[l]})));return s}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t)return{type:\"heading\",raw:t[0],depth:t[2].charAt(0)===\"=\"?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===`\n`?t[1].slice(0,-1):t[1];return{type:\"paragraph\",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:\"text\",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:\"escape\",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:\"html\",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let s=z(n.slice(0,-1),\"\\\\\");if((n.length-s.length)%2===0)return}else{let s=de(t[2],\"()\");if(s===-2)return;if(s>-1){let o=(t[0].indexOf(\"!\")===0?5:4)+t[1].length+s;t[2]=t[2].substring(0,s),t[0]=t[0].substring(0,o).trim(),t[3]=\"\"}}let r=t[2],i=\"\";if(this.options.pedantic){let s=this.rules.other.pedanticHrefTitle.exec(r);s&&(r=s[1],i=s[3])}else i=t[3]?t[3].slice(1,-1):\"\";return r=r.trim(),this.rules.other.startAngleBracket.test(r)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?r=r.slice(1):r=r.slice(1,-1)),ge(t,{href:r&&r.replace(this.rules.inline.anyPunctuation,\"$1\"),title:i&&i.replace(this.rules.inline.anyPunctuation,\"$1\")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let r=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal,\" \"),i=t[r.toLowerCase()];if(!i){let s=n[0].charAt(0);return{type:\"text\",raw:s,text:s}}return ge(n,i,n[0],this.lexer,this.rules)}}emStrong(e,t,n=\"\"){let r=this.rules.inline.emStrongLDelim.exec(e);if(!r||r[3]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(r[1]||r[2]||\"\")||!n||this.rules.inline.punctuation.exec(n)){let s=[...r[0]].length-1,a,o,l=s,p=0,c=r[0][0]===\"*\"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(c.lastIndex=0,t=t.slice(-1*e.length+s);(r=c.exec(t))!=null;){if(a=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!a)continue;if(o=[...a].length,r[3]||r[4]){l+=o;continue}else if((r[5]||r[6])&&s%3&&!((s+o)%3)){p+=o;continue}if(l-=o,l>0)continue;o=Math.min(o,o+l+p);let g=[...r[0]][0].length,h=e.slice(0,s+r.index+g+o);if(Math.min(s,o)%2){let f=h.slice(1,-1);return{type:\"em\",raw:h,text:f,tokens:this.lexer.inlineTokens(f)}}let R=h.slice(2,-2);return{type:\"strong\",raw:h,text:R,tokens:this.lexer.inlineTokens(R)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal,\" \"),r=this.rules.other.nonSpaceChar.test(n),i=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return r&&i&&(n=n.substring(1,n.length-1)),{type:\"codespan\",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:\"br\",raw:t[0]}}del(e){let t=this.rules.inline.del.exec(e);if(t)return{type:\"del\",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,r;return t[2]===\"@\"?(n=t[1],r=\"mailto:\"+n):(n=t[1],r=n),{type:\"link\",raw:t[0],text:n,href:r,tokens:[{type:\"text\",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,r;if(t[2]===\"@\")n=t[0],r=\"mailto:\"+n;else{let i;do i=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??\"\";while(i!==t[0]);n=t[0],t[1]===\"www.\"?r=\"http://\"+t[0]:r=t[0]}return{type:\"link\",raw:t[0],text:n,href:r,tokens:[{type:\"text\",raw:n,text:n}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:\"text\",raw:t[0],text:t[0],escaped:n}}}};var x=class u{tokens;options;state;inlineQueue;tokenizer;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||T,this.options.tokenizer=this.options.tokenizer||new y,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let t={other:m,block:E.normal,inline:M.normal};this.options.pedantic?(t.block=E.pedantic,t.inline=M.pedantic):this.options.gfm&&(t.block=E.gfm,this.options.breaks?t.inline=M.breaks:t.inline=M.gfm),this.tokenizer.rules=t}static get rules(){return{block:E,inline:M}}static lex(e,t){return new u(t).lex(e)}static lexInline(e,t){return new u(t).inlineTokens(e)}lex(e){e=e.replace(m.carriageReturn,`\n`),this.blockTokens(e,this.tokens);for(let t=0;t(r=s.call({lexer:this},e,t))?(e=e.substring(r.raw.length),t.push(r),!0):!1))continue;if(r=this.tokenizer.space(e)){e=e.substring(r.raw.length);let s=t.at(-1);r.raw.length===1&&s!==void 0?s.raw+=`\n`:t.push(r);continue}if(r=this.tokenizer.code(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type===\"paragraph\"||s?.type===\"text\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.text,this.inlineQueue.at(-1).src=s.text):t.push(r);continue}if(r=this.tokenizer.fences(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.heading(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.hr(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.blockquote(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.list(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.html(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.def(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type===\"paragraph\"||s?.type===\"text\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.raw,this.inlineQueue.at(-1).src=s.text):this.tokens.links[r.tag]||(this.tokens.links[r.tag]={href:r.href,title:r.title},t.push(r));continue}if(r=this.tokenizer.table(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.lheading(e)){e=e.substring(r.raw.length),t.push(r);continue}let i=e;if(this.options.extensions?.startBlock){let s=1/0,a=e.slice(1),o;this.options.extensions.startBlock.forEach(l=>{o=l.call({lexer:this},a),typeof o==\"number\"&&o>=0&&(s=Math.min(s,o))}),s<1/0&&s>=0&&(i=e.substring(0,s+1))}if(this.state.top&&(r=this.tokenizer.paragraph(i))){let s=t.at(-1);n&&s?.type===\"paragraph\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=s.text):t.push(r),n=i.length!==e.length,e=e.substring(r.raw.length);continue}if(r=this.tokenizer.text(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type===\"text\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=s.text):t.push(r);continue}if(e){let s=\"Infinite loop on byte: \"+e.charCodeAt(0);if(this.options.silent){console.error(s);break}else throw new Error(s)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,r=null;if(this.tokens.links){let o=Object.keys(this.tokens.links);if(o.length>0)for(;(r=this.tokenizer.rules.inline.reflinkSearch.exec(n))!=null;)o.includes(r[0].slice(r[0].lastIndexOf(\"[\")+1,-1))&&(n=n.slice(0,r.index)+\"[\"+\"a\".repeat(r[0].length-2)+\"]\"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(r=this.tokenizer.rules.inline.anyPunctuation.exec(n))!=null;)n=n.slice(0,r.index)+\"++\"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let i;for(;(r=this.tokenizer.rules.inline.blockSkip.exec(n))!=null;)i=r[2]?r[2].length:0,n=n.slice(0,r.index+i)+\"[\"+\"a\".repeat(r[0].length-i-2)+\"]\"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);n=this.options.hooks?.emStrongMask?.call({lexer:this},n)??n;let s=!1,a=\"\";for(;e;){s||(a=\"\"),s=!1;let o;if(this.options.extensions?.inline?.some(p=>(o=p.call({lexer:this},e,t))?(e=e.substring(o.raw.length),t.push(o),!0):!1))continue;if(o=this.tokenizer.escape(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.tag(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.link(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(o.raw.length);let p=t.at(-1);o.type===\"text\"&&p?.type===\"text\"?(p.raw+=o.raw,p.text+=o.text):t.push(o);continue}if(o=this.tokenizer.emStrong(e,n,a)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.codespan(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.br(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.del(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.autolink(e)){e=e.substring(o.raw.length),t.push(o);continue}if(!this.state.inLink&&(o=this.tokenizer.url(e))){e=e.substring(o.raw.length),t.push(o);continue}let l=e;if(this.options.extensions?.startInline){let p=1/0,c=e.slice(1),g;this.options.extensions.startInline.forEach(h=>{g=h.call({lexer:this},c),typeof g==\"number\"&&g>=0&&(p=Math.min(p,g))}),p<1/0&&p>=0&&(l=e.substring(0,p+1))}if(o=this.tokenizer.inlineText(l)){e=e.substring(o.raw.length),o.raw.slice(-1)!==\"_\"&&(a=o.raw.slice(-1)),s=!0;let p=t.at(-1);p?.type===\"text\"?(p.raw+=o.raw,p.text+=o.text):t.push(o);continue}if(e){let p=\"Infinite loop on byte: \"+e.charCodeAt(0);if(this.options.silent){console.error(p);break}else throw new Error(p)}}return t}};var P=class{options;parser;constructor(e){this.options=e||T}space(e){return\"\"}code({text:e,lang:t,escaped:n}){let r=(t||\"\").match(m.notSpaceStart)?.[0],i=e.replace(m.endingNewline,\"\")+`\n`;return r?'
    '+(n?i:w(i,!0))+`
    \n`:\"
    \"+(n?i:w(i,!0))+`
    \n`}blockquote({tokens:e}){return`
    \n${this.parser.parse(e)}
    \n`}html({text:e}){return e}def(e){return\"\"}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)}\n`}hr(e){return`
    \n`}list(e){let t=e.ordered,n=e.start,r=\"\";for(let a=0;a\n`+r+\"\n`}listitem(e){return`
  • ${this.parser.parse(e.tokens)}
  • \n`}checkbox({checked:e}){return\" '}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    \n`}table(e){let t=\"\",n=\"\";for(let i=0;i${r}`),`\n\n`+t+`\n`+r+`
    \n`}tablerow({text:e}){return`\n${e}\n`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?\"th\":\"td\";return(e.align?`<${n} align=\"${e.align}\">`:`<${n}>`)+t+`\n`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${w(e,!0)}`}br(e){return\"
    \"}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let r=this.parser.parseInline(n),i=X(e);if(i===null)return r;e=i;let s='
    \"+r+\"\",s}image({href:e,title:t,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let i=X(e);if(i===null)return w(n);e=i;let s=`\"${n}\"`;return\",s}text(e){return\"tokens\"in e&&e.tokens?this.parser.parseInline(e.tokens):\"escaped\"in e&&e.escaped?e.text:w(e.text)}};var $=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return\"\"+e}image({text:e}){return\"\"+e}br(){return\"\"}checkbox({raw:e}){return e}};var b=class u{options;renderer;textRenderer;constructor(e){this.options=e||T,this.options.renderer=this.options.renderer||new P,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new $}static parse(e,t){return new u(t).parse(e)}static parseInline(e,t){return new u(t).parseInline(e)}parse(e){let t=\"\";for(let n=0;n{let a=i[s].flat(1/0);n=n.concat(this.walkTokens(a,t))}):i.tokens&&(n=n.concat(this.walkTokens(i.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let r={...n};if(r.async=this.defaults.async||r.async||!1,n.extensions&&(n.extensions.forEach(i=>{if(!i.name)throw new Error(\"extension name required\");if(\"renderer\"in i){let s=t.renderers[i.name];s?t.renderers[i.name]=function(...a){let o=i.renderer.apply(this,a);return o===!1&&(o=s.apply(this,a)),o}:t.renderers[i.name]=i.renderer}if(\"tokenizer\"in i){if(!i.level||i.level!==\"block\"&&i.level!==\"inline\")throw new Error(\"extension level must be 'block' or 'inline'\");let s=t[i.level];s?s.unshift(i.tokenizer):t[i.level]=[i.tokenizer],i.start&&(i.level===\"block\"?t.startBlock?t.startBlock.push(i.start):t.startBlock=[i.start]:i.level===\"inline\"&&(t.startInline?t.startInline.push(i.start):t.startInline=[i.start]))}\"childTokens\"in i&&i.childTokens&&(t.childTokens[i.name]=i.childTokens)}),r.extensions=t),n.renderer){let i=this.defaults.renderer||new P(this.defaults);for(let s in n.renderer){if(!(s in i))throw new Error(`renderer '${s}' does not exist`);if([\"options\",\"parser\"].includes(s))continue;let a=s,o=n.renderer[a],l=i[a];i[a]=(...p)=>{let c=o.apply(i,p);return c===!1&&(c=l.apply(i,p)),c||\"\"}}r.renderer=i}if(n.tokenizer){let i=this.defaults.tokenizer||new y(this.defaults);for(let s in n.tokenizer){if(!(s in i))throw new Error(`tokenizer '${s}' does not exist`);if([\"options\",\"rules\",\"lexer\"].includes(s))continue;let a=s,o=n.tokenizer[a],l=i[a];i[a]=(...p)=>{let c=o.apply(i,p);return c===!1&&(c=l.apply(i,p)),c}}r.tokenizer=i}if(n.hooks){let i=this.defaults.hooks||new S;for(let s in n.hooks){if(!(s in i))throw new Error(`hook '${s}' does not exist`);if([\"options\",\"block\"].includes(s))continue;let a=s,o=n.hooks[a],l=i[a];S.passThroughHooks.has(s)?i[a]=p=>{if(this.defaults.async&&S.passThroughHooksRespectAsync.has(s))return(async()=>{let g=await o.call(i,p);return l.call(i,g)})();let c=o.call(i,p);return l.call(i,c)}:i[a]=(...p)=>{if(this.defaults.async)return(async()=>{let g=await o.apply(i,p);return g===!1&&(g=await l.apply(i,p)),g})();let c=o.apply(i,p);return c===!1&&(c=l.apply(i,p)),c}}r.hooks=i}if(n.walkTokens){let i=this.defaults.walkTokens,s=n.walkTokens;r.walkTokens=function(a){let o=[];return o.push(s.call(this,a)),i&&(o=o.concat(i.call(this,a))),o}}this.defaults={...this.defaults,...r}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return x.lex(e,t??this.defaults)}parser(e,t){return b.parse(e,t??this.defaults)}parseMarkdown(e){return(n,r)=>{let i={...r},s={...this.defaults,...i},a=this.onError(!!s.silent,!!s.async);if(this.defaults.async===!0&&i.async===!1)return a(new Error(\"marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.\"));if(typeof n>\"u\"||n===null)return a(new Error(\"marked(): input parameter is undefined or null\"));if(typeof n!=\"string\")return a(new Error(\"marked(): input parameter is of type \"+Object.prototype.toString.call(n)+\", string expected\"));if(s.hooks&&(s.hooks.options=s,s.hooks.block=e),s.async)return(async()=>{let o=s.hooks?await s.hooks.preprocess(n):n,p=await(s.hooks?await s.hooks.provideLexer():e?x.lex:x.lexInline)(o,s),c=s.hooks?await s.hooks.processAllTokens(p):p;s.walkTokens&&await Promise.all(this.walkTokens(c,s.walkTokens));let h=await(s.hooks?await s.hooks.provideParser():e?b.parse:b.parseInline)(c,s);return s.hooks?await s.hooks.postprocess(h):h})().catch(a);try{s.hooks&&(n=s.hooks.preprocess(n));let l=(s.hooks?s.hooks.provideLexer():e?x.lex:x.lexInline)(n,s);s.hooks&&(l=s.hooks.processAllTokens(l)),s.walkTokens&&this.walkTokens(l,s.walkTokens);let c=(s.hooks?s.hooks.provideParser():e?b.parse:b.parseInline)(l,s);return s.hooks&&(c=s.hooks.postprocess(c)),c}catch(o){return a(o)}}}onError(e,t){return n=>{if(n.message+=`\nPlease report this to https://github.com/markedjs/marked.`,e){let r=\"

    An error occurred:

    \"+w(n.message+\"\",!0)+\"
    \";return t?Promise.resolve(r):r}if(t)return Promise.reject(n);throw n}}};var _=new B;function d(u,e){return _.parse(u,e)}d.options=d.setOptions=function(u){return _.setOptions(u),d.defaults=_.defaults,Z(d.defaults),d};d.getDefaults=L;d.defaults=T;d.use=function(...u){return _.use(...u),d.defaults=_.defaults,Z(d.defaults),d};d.walkTokens=function(u,e){return _.walkTokens(u,e)};d.parseInline=_.parseInline;d.Parser=b;d.parser=b.parse;d.Renderer=P;d.TextRenderer=$;d.Lexer=x;d.lexer=x.lex;d.Tokenizer=y;d.Hooks=S;d.parse=d;var Dt=d.options,Ht=d.setOptions,Zt=d.use,Gt=d.walkTokens,Nt=d.parseInline,Qt=d,Ft=b.parse,jt=x.lex;export{S as Hooks,x as Lexer,B as Marked,b as Parser,P as Renderer,$ as TextRenderer,y as Tokenizer,T as defaults,L as getDefaults,jt as lexer,d as marked,Dt as options,Qt as parse,Nt as parseInline,Ft as parser,Ht as setOptions,Zt as use,Gt as walkTokens};\n//# sourceMappingURL=marked.esm.js.map\n","import DOMPurify from \"dompurify\";\nimport { marked } from \"marked\";\nimport { truncateText } from \"./format\";\n\nmarked.setOptions({\n gfm: true,\n breaks: true,\n mangle: false,\n});\n\nconst allowedTags = [\n \"a\",\n \"b\",\n \"blockquote\",\n \"br\",\n \"code\",\n \"del\",\n \"em\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"hr\",\n \"i\",\n \"li\",\n \"ol\",\n \"p\",\n \"pre\",\n \"strong\",\n \"table\",\n \"tbody\",\n \"td\",\n \"th\",\n \"thead\",\n \"tr\",\n \"ul\",\n];\n\nconst allowedAttrs = [\"class\", \"href\", \"rel\", \"target\", \"title\", \"start\"];\n\nlet hooksInstalled = false;\nconst MARKDOWN_CHAR_LIMIT = 140_000;\nconst MARKDOWN_PARSE_LIMIT = 40_000;\nconst MARKDOWN_CACHE_LIMIT = 200;\nconst MARKDOWN_CACHE_MAX_CHARS = 50_000;\nconst markdownCache = new Map();\n\nfunction getCachedMarkdown(key: string): string | null {\n const cached = markdownCache.get(key);\n if (cached === undefined) return null;\n markdownCache.delete(key);\n markdownCache.set(key, cached);\n return cached;\n}\n\nfunction setCachedMarkdown(key: string, value: string) {\n markdownCache.set(key, value);\n if (markdownCache.size <= MARKDOWN_CACHE_LIMIT) return;\n const oldest = markdownCache.keys().next().value;\n if (oldest) markdownCache.delete(oldest);\n}\n\nfunction installHooks() {\n if (hooksInstalled) return;\n hooksInstalled = true;\n\n DOMPurify.addHook(\"afterSanitizeAttributes\", (node) => {\n if (!(node instanceof HTMLAnchorElement)) return;\n const href = node.getAttribute(\"href\");\n if (!href) return;\n node.setAttribute(\"rel\", \"noreferrer noopener\");\n node.setAttribute(\"target\", \"_blank\");\n });\n}\n\nexport function toSanitizedMarkdownHtml(markdown: string): string {\n const input = markdown.trim();\n if (!input) return \"\";\n installHooks();\n if (input.length <= MARKDOWN_CACHE_MAX_CHARS) {\n const cached = getCachedMarkdown(input);\n if (cached !== null) return cached;\n }\n const truncated = truncateText(input, MARKDOWN_CHAR_LIMIT);\n const suffix = truncated.truncated\n ? `\\n\\n… truncated (${truncated.total} chars, showing first ${truncated.text.length}).`\n : \"\";\n if (truncated.text.length > MARKDOWN_PARSE_LIMIT) {\n const escaped = escapeHtml(`${truncated.text}${suffix}`);\n const html = `
    ${escaped}
    `;\n const sanitized = DOMPurify.sanitize(html, {\n ALLOWED_TAGS: allowedTags,\n ALLOWED_ATTR: allowedAttrs,\n });\n if (input.length <= MARKDOWN_CACHE_MAX_CHARS) {\n setCachedMarkdown(input, sanitized);\n }\n return sanitized;\n }\n const rendered = marked.parse(`${truncated.text}${suffix}`) as string;\n const sanitized = DOMPurify.sanitize(rendered, {\n ALLOWED_TAGS: allowedTags,\n ALLOWED_ATTR: allowedAttrs,\n });\n if (input.length <= MARKDOWN_CACHE_MAX_CHARS) {\n setCachedMarkdown(input, sanitized);\n }\n return sanitized;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(//g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n","import { html, type TemplateResult } from \"lit\";\n\nexport function renderEmojiIcon(icon: string, className: string): TemplateResult {\n return html`${icon}`;\n}\n\nexport function setEmojiIcon(target: HTMLElement | null, icon: string): void {\n if (!target) return;\n target.textContent = icon;\n}\n","import { html, type TemplateResult } from \"lit\";\nimport { renderEmojiIcon, setEmojiIcon } from \"../icons\";\n\nconst COPIED_FOR_MS = 1500;\nconst ERROR_FOR_MS = 2000;\nconst COPY_LABEL = \"Copy as markdown\";\nconst COPIED_LABEL = \"Copied\";\nconst ERROR_LABEL = \"Copy failed\";\nconst COPY_ICON = \"📋\";\nconst COPIED_ICON = \"✓\";\nconst ERROR_ICON = \"!\";\n\ntype CopyButtonOptions = {\n text: () => string;\n label?: string;\n};\n\nasync function copyTextToClipboard(text: string): Promise {\n if (!text) return false;\n\n try {\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction setButtonLabel(button: HTMLButtonElement, label: string) {\n button.title = label;\n button.setAttribute(\"aria-label\", label);\n}\n\nfunction createCopyButton(options: CopyButtonOptions): TemplateResult {\n const idleLabel = options.label ?? COPY_LABEL;\n return html`\n {\n const btn = e.currentTarget as HTMLButtonElement | null;\n const icon = btn?.querySelector(\n \".chat-copy-btn__icon\",\n ) as HTMLElement | null;\n\n if (!btn || btn.dataset.copying === \"1\") return;\n\n btn.dataset.copying = \"1\";\n btn.setAttribute(\"aria-busy\", \"true\");\n btn.disabled = true;\n\n const copied = await copyTextToClipboard(options.text());\n if (!btn.isConnected) return;\n\n delete btn.dataset.copying;\n btn.removeAttribute(\"aria-busy\");\n btn.disabled = false;\n\n if (!copied) {\n btn.dataset.error = \"1\";\n setButtonLabel(btn, ERROR_LABEL);\n setEmojiIcon(icon, ERROR_ICON);\n\n window.setTimeout(() => {\n if (!btn.isConnected) return;\n delete btn.dataset.error;\n setButtonLabel(btn, idleLabel);\n setEmojiIcon(icon, COPY_ICON);\n }, ERROR_FOR_MS);\n return;\n }\n\n btn.dataset.copied = \"1\";\n setButtonLabel(btn, COPIED_LABEL);\n setEmojiIcon(icon, COPIED_ICON);\n\n window.setTimeout(() => {\n if (!btn.isConnected) return;\n delete btn.dataset.copied;\n setButtonLabel(btn, idleLabel);\n setEmojiIcon(icon, COPY_ICON);\n }, COPIED_FOR_MS);\n }}\n >\n ${renderEmojiIcon(COPY_ICON, \"chat-copy-btn__icon\")}\n \n `;\n}\n\nexport function renderCopyAsMarkdownButton(markdown: string): TemplateResult {\n return createCopyButton({ text: () => markdown, label: COPY_LABEL });\n}\n","import rawConfig from \"./tool-display.json\";\n\ntype ToolDisplayActionSpec = {\n label?: string;\n detailKeys?: string[];\n};\n\ntype ToolDisplaySpec = {\n emoji?: string;\n title?: string;\n label?: string;\n detailKeys?: string[];\n actions?: Record;\n};\n\ntype ToolDisplayConfig = {\n version?: number;\n fallback?: ToolDisplaySpec;\n tools?: Record;\n};\n\nexport type ToolDisplay = {\n name: string;\n emoji: string;\n title: string;\n label: string;\n verb?: string;\n detail?: string;\n};\n\nconst TOOL_DISPLAY_CONFIG = rawConfig as ToolDisplayConfig;\nconst FALLBACK = TOOL_DISPLAY_CONFIG.fallback ?? { emoji: \"🧩\" };\nconst TOOL_MAP = TOOL_DISPLAY_CONFIG.tools ?? {};\n\nfunction normalizeToolName(name?: string): string {\n return (name ?? \"tool\").trim();\n}\n\nfunction defaultTitle(name: string): string {\n const cleaned = name.replace(/_/g, \" \").trim();\n if (!cleaned) return \"Tool\";\n return cleaned\n .split(/\\s+/)\n .map((part) =>\n part.length <= 2 && part.toUpperCase() === part\n ? part\n : `${part.at(0)?.toUpperCase() ?? \"\"}${part.slice(1)}`,\n )\n .join(\" \");\n}\n\nfunction normalizeVerb(value?: string): string | undefined {\n const trimmed = value?.trim();\n if (!trimmed) return undefined;\n return trimmed.replace(/_/g, \" \");\n}\n\nfunction coerceDisplayValue(value: unknown): string | undefined {\n if (value === null || value === undefined) return undefined;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const firstLine = trimmed.split(/\\r?\\n/)[0]?.trim() ?? \"\";\n if (!firstLine) return undefined;\n return firstLine.length > 160 ? `${firstLine.slice(0, 157)}…` : firstLine;\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (Array.isArray(value)) {\n const values = value\n .map((item) => coerceDisplayValue(item))\n .filter((item): item is string => Boolean(item));\n if (values.length === 0) return undefined;\n const preview = values.slice(0, 3).join(\", \");\n return values.length > 3 ? `${preview}…` : preview;\n }\n return undefined;\n}\n\nfunction lookupValueByPath(args: unknown, path: string): unknown {\n if (!args || typeof args !== \"object\") return undefined;\n let current: unknown = args;\n for (const segment of path.split(\".\")) {\n if (!segment) return undefined;\n if (!current || typeof current !== \"object\") return undefined;\n const record = current as Record;\n current = record[segment];\n }\n return current;\n}\n\nfunction resolveDetailFromKeys(args: unknown, keys: string[]): string | undefined {\n for (const key of keys) {\n const value = lookupValueByPath(args, key);\n const display = coerceDisplayValue(value);\n if (display) return display;\n }\n return undefined;\n}\n\nfunction resolveReadDetail(args: unknown): string | undefined {\n if (!args || typeof args !== \"object\") return undefined;\n const record = args as Record;\n const path = typeof record.path === \"string\" ? record.path : undefined;\n if (!path) return undefined;\n const offset = typeof record.offset === \"number\" ? record.offset : undefined;\n const limit = typeof record.limit === \"number\" ? record.limit : undefined;\n if (offset !== undefined && limit !== undefined) {\n return `${path}:${offset}-${offset + limit}`;\n }\n return path;\n}\n\nfunction resolveWriteDetail(args: unknown): string | undefined {\n if (!args || typeof args !== \"object\") return undefined;\n const record = args as Record;\n const path = typeof record.path === \"string\" ? record.path : undefined;\n return path;\n}\n\nfunction resolveActionSpec(\n spec: ToolDisplaySpec | undefined,\n action: string | undefined,\n): ToolDisplayActionSpec | undefined {\n if (!spec || !action) return undefined;\n return spec.actions?.[action] ?? undefined;\n}\n\nexport function resolveToolDisplay(params: {\n name?: string;\n args?: unknown;\n meta?: string;\n}): ToolDisplay {\n const name = normalizeToolName(params.name);\n const key = name.toLowerCase();\n const spec = TOOL_MAP[key];\n const emoji = spec?.emoji ?? FALLBACK.emoji ?? \"🧩\";\n const title = spec?.title ?? defaultTitle(name);\n const label = spec?.label ?? name;\n const actionRaw =\n params.args && typeof params.args === \"object\"\n ? ((params.args as Record).action as string | undefined)\n : undefined;\n const action = typeof actionRaw === \"string\" ? actionRaw.trim() : undefined;\n const actionSpec = resolveActionSpec(spec, action);\n const verb = normalizeVerb(actionSpec?.label ?? action);\n\n let detail: string | undefined;\n if (key === \"read\") detail = resolveReadDetail(params.args);\n if (!detail && (key === \"write\" || key === \"edit\" || key === \"attach\")) {\n detail = resolveWriteDetail(params.args);\n }\n\n const detailKeys =\n actionSpec?.detailKeys ?? spec?.detailKeys ?? FALLBACK.detailKeys ?? [];\n if (!detail && detailKeys.length > 0) {\n detail = resolveDetailFromKeys(params.args, detailKeys);\n }\n\n if (!detail && params.meta) {\n detail = params.meta;\n }\n\n if (detail) {\n detail = shortenHomeInString(detail);\n }\n\n return {\n name,\n emoji,\n title,\n label,\n verb,\n detail,\n };\n}\n\nexport function formatToolDetail(display: ToolDisplay): string | undefined {\n const parts: string[] = [];\n if (display.verb) parts.push(display.verb);\n if (display.detail) parts.push(display.detail);\n if (parts.length === 0) return undefined;\n return parts.join(\" · \");\n}\n\nexport function formatToolSummary(display: ToolDisplay): string {\n const detail = formatToolDetail(display);\n return detail\n ? `${display.emoji} ${display.label}: ${detail}`\n : `${display.emoji} ${display.label}`;\n}\n\nfunction shortenHomeInString(input: string): string {\n if (!input) return input;\n return input\n .replace(/\\/Users\\/[^/]+/g, \"~\")\n .replace(/\\/home\\/[^/]+/g, \"~\");\n}\n","/**\n * Chat-related constants for the UI layer.\n */\n\n/** Character threshold for showing tool output inline vs collapsed */\nexport const TOOL_INLINE_THRESHOLD = 80;\n\n/** Maximum lines to show in collapsed preview */\nexport const PREVIEW_MAX_LINES = 2;\n\n/** Maximum characters to show in collapsed preview */\nexport const PREVIEW_MAX_CHARS = 100;\n","/**\n * Helper functions for tool card rendering.\n */\n\nimport { PREVIEW_MAX_CHARS, PREVIEW_MAX_LINES } from \"./constants\";\n\n/**\n * Format tool output content for display in the sidebar.\n * Detects JSON and wraps it in a code block with formatting.\n */\nexport function formatToolOutputForSidebar(text: string): string {\n const trimmed = text.trim();\n // Try to detect and format JSON\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n const parsed = JSON.parse(trimmed);\n return \"```json\\n\" + JSON.stringify(parsed, null, 2) + \"\\n```\";\n } catch {\n // Not valid JSON, return as-is\n }\n }\n return text;\n}\n\n/**\n * Get a truncated preview of tool output text.\n * Truncates to first N lines or first N characters, whichever is shorter.\n */\nexport function getTruncatedPreview(text: string): string {\n const allLines = text.split(\"\\n\");\n const lines = allLines.slice(0, PREVIEW_MAX_LINES);\n const preview = lines.join(\"\\n\");\n if (preview.length > PREVIEW_MAX_CHARS) {\n return preview.slice(0, PREVIEW_MAX_CHARS) + \"…\";\n }\n return lines.length < allLines.length ? preview + \"…\" : preview;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatToolDetail, resolveToolDisplay } from \"../tool-display\";\nimport type { ToolCard } from \"../types/chat-types\";\nimport { TOOL_INLINE_THRESHOLD } from \"./constants\";\nimport {\n formatToolOutputForSidebar,\n getTruncatedPreview,\n} from \"./tool-helpers\";\nimport { isToolResultMessage } from \"./message-normalizer\";\nimport { extractTextCached } from \"./message-extract\";\n\nexport function extractToolCards(message: unknown): ToolCard[] {\n const m = message as Record;\n const content = normalizeContent(m.content);\n const cards: ToolCard[] = [];\n\n for (const item of content) {\n const kind = String(item.type ?? \"\").toLowerCase();\n const isToolCall =\n [\"toolcall\", \"tool_call\", \"tooluse\", \"tool_use\"].includes(kind) ||\n (typeof item.name === \"string\" && item.arguments != null);\n if (isToolCall) {\n cards.push({\n kind: \"call\",\n name: (item.name as string) ?? \"tool\",\n args: coerceArgs(item.arguments ?? item.args),\n });\n }\n }\n\n for (const item of content) {\n const kind = String(item.type ?? \"\").toLowerCase();\n if (kind !== \"toolresult\" && kind !== \"tool_result\") continue;\n const text = extractToolText(item);\n const name = typeof item.name === \"string\" ? item.name : \"tool\";\n cards.push({ kind: \"result\", name, text });\n }\n\n if (\n isToolResultMessage(message) &&\n !cards.some((card) => card.kind === \"result\")\n ) {\n const name =\n (typeof m.toolName === \"string\" && m.toolName) ||\n (typeof m.tool_name === \"string\" && m.tool_name) ||\n \"tool\";\n const text = extractTextCached(message) ?? undefined;\n cards.push({ kind: \"result\", name, text });\n }\n\n return cards;\n}\n\nexport function renderToolCardSidebar(\n card: ToolCard,\n onOpenSidebar?: (content: string) => void,\n) {\n const display = resolveToolDisplay({ name: card.name, args: card.args });\n const detail = formatToolDetail(display);\n const hasText = Boolean(card.text?.trim());\n\n const canClick = Boolean(onOpenSidebar);\n const handleClick = canClick\n ? () => {\n if (hasText) {\n onOpenSidebar!(formatToolOutputForSidebar(card.text!));\n return;\n }\n const info = `## ${display.label}\\n\\n${\n detail ? `**Command:** \\`${detail}\\`\\n\\n` : \"\"\n }*No output — tool completed successfully.*`;\n onOpenSidebar!(info);\n }\n : undefined;\n\n const isShort = hasText && (card.text?.length ?? 0) <= TOOL_INLINE_THRESHOLD;\n const showCollapsed = hasText && !isShort;\n const showInline = hasText && isShort;\n const isEmpty = !hasText;\n\n return html`\n {\n if (e.key !== \"Enter\" && e.key !== \" \") return;\n e.preventDefault();\n handleClick?.();\n }\n : nothing}\n >\n
    \n
    \n ${display.emoji}\n ${display.label}\n
    \n ${canClick\n ? html`${hasText ? \"View ›\" : \"›\"}`\n : nothing}\n ${isEmpty && !canClick ? html`` : nothing}\n
    \n ${detail\n ? html`
    ${detail}
    `\n : nothing}\n ${isEmpty\n ? html`
    Completed
    `\n : nothing}\n ${showCollapsed\n ? html`
    ${getTruncatedPreview(card.text!)}
    `\n : nothing}\n ${showInline\n ? html`
    ${card.text}
    `\n : nothing}\n \n `;\n}\n\nfunction normalizeContent(content: unknown): Array> {\n if (!Array.isArray(content)) return [];\n return content.filter(Boolean) as Array>;\n}\n\nfunction coerceArgs(value: unknown): unknown {\n if (typeof value !== \"string\") return value;\n const trimmed = value.trim();\n if (!trimmed) return value;\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) return value;\n try {\n return JSON.parse(trimmed);\n } catch {\n return value;\n }\n}\n\nfunction extractToolText(item: Record): string | undefined {\n if (typeof item.text === \"string\") return item.text;\n if (typeof item.content === \"string\") return item.content;\n return undefined;\n}\n","import { html, nothing } from \"lit\";\nimport { unsafeHTML } from \"lit/directives/unsafe-html.js\";\n\nimport type { AssistantIdentity } from \"../assistant-identity\";\nimport { toSanitizedMarkdownHtml } from \"../markdown\";\nimport type { MessageGroup } from \"../types/chat-types\";\nimport { renderCopyAsMarkdownButton } from \"./copy-as-markdown\";\nimport { isToolResultMessage, normalizeRoleForGrouping } from \"./message-normalizer\";\nimport {\n extractTextCached,\n extractThinkingCached,\n formatReasoningMarkdown,\n} from \"./message-extract\";\nimport { extractToolCards, renderToolCardSidebar } from \"./tool-cards\";\n\nexport function renderReadingIndicatorGroup(assistant?: AssistantIdentity) {\n return html`\n
    \n ${renderAvatar(\"assistant\", assistant)}\n
    \n
    \n \n \n \n
    \n
    \n
    \n `;\n}\n\nexport function renderStreamingGroup(\n text: string,\n startedAt: number,\n onOpenSidebar?: (content: string) => void,\n assistant?: AssistantIdentity,\n) {\n const timestamp = new Date(startedAt).toLocaleTimeString([], {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const name = assistant?.name ?? \"Assistant\";\n\n return html`\n
    \n ${renderAvatar(\"assistant\", assistant)}\n
    \n ${renderGroupedMessage(\n {\n role: \"assistant\",\n content: [{ type: \"text\", text }],\n timestamp: startedAt,\n },\n { isStreaming: true, showReasoning: false },\n onOpenSidebar,\n )}\n
    \n ${name}\n ${timestamp}\n
    \n
    \n
    \n `;\n}\n\nexport function renderMessageGroup(\n group: MessageGroup,\n opts: {\n onOpenSidebar?: (content: string) => void;\n showReasoning: boolean;\n assistantName?: string;\n assistantAvatar?: string | null;\n },\n) {\n const normalizedRole = normalizeRoleForGrouping(group.role);\n const assistantName = opts.assistantName ?? \"Assistant\";\n const who =\n normalizedRole === \"user\"\n ? \"You\"\n : normalizedRole === \"assistant\"\n ? assistantName\n : normalizedRole;\n const roleClass =\n normalizedRole === \"user\"\n ? \"user\"\n : normalizedRole === \"assistant\"\n ? \"assistant\"\n : \"other\";\n const timestamp = new Date(group.timestamp).toLocaleTimeString([], {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return html`\n
    \n ${renderAvatar(group.role, {\n name: assistantName,\n avatar: opts.assistantAvatar ?? null,\n })}\n
    \n ${group.messages.map((item, index) =>\n renderGroupedMessage(\n item.message,\n {\n isStreaming:\n group.isStreaming && index === group.messages.length - 1,\n showReasoning: opts.showReasoning,\n },\n opts.onOpenSidebar,\n ),\n )}\n
    \n ${who}\n ${timestamp}\n
    \n
    \n
    \n `;\n}\n\nfunction renderAvatar(\n role: string,\n assistant?: Pick,\n) {\n const normalized = normalizeRoleForGrouping(role);\n const assistantName = assistant?.name?.trim() || \"Assistant\";\n const assistantAvatar = assistant?.avatar?.trim() || \"\";\n const initial =\n normalized === \"user\"\n ? \"U\"\n : normalized === \"assistant\"\n ? assistantName.charAt(0).toUpperCase() || \"A\"\n : normalized === \"tool\"\n ? \"⚙\"\n : \"?\";\n const className =\n normalized === \"user\"\n ? \"user\"\n : normalized === \"assistant\"\n ? \"assistant\"\n : normalized === \"tool\"\n ? \"tool\"\n : \"other\";\n\n if (assistantAvatar && normalized === \"assistant\") {\n if (isAvatarUrl(assistantAvatar)) {\n return html``;\n }\n return html`
    ${assistantAvatar}
    `;\n }\n\n return html`
    ${initial}
    `;\n}\n\nfunction isAvatarUrl(value: string): boolean {\n return (\n /^https?:\\/\\//i.test(value) ||\n /^data:image\\//i.test(value) ||\n /^\\//.test(value) // Relative paths from avatar endpoint\n );\n}\n\nfunction renderGroupedMessage(\n message: unknown,\n opts: { isStreaming: boolean; showReasoning: boolean },\n onOpenSidebar?: (content: string) => void,\n) {\n const m = message as Record;\n const role = typeof m.role === \"string\" ? m.role : \"unknown\";\n const isToolResult =\n isToolResultMessage(message) ||\n role.toLowerCase() === \"toolresult\" ||\n role.toLowerCase() === \"tool_result\" ||\n typeof m.toolCallId === \"string\" ||\n typeof m.tool_call_id === \"string\";\n\n const toolCards = extractToolCards(message);\n const hasToolCards = toolCards.length > 0;\n\n const extractedText = extractTextCached(message);\n const extractedThinking =\n opts.showReasoning && role === \"assistant\"\n ? extractThinkingCached(message)\n : null;\n const markdownBase = extractedText?.trim() ? extractedText : null;\n const reasoningMarkdown = extractedThinking\n ? formatReasoningMarkdown(extractedThinking)\n : null;\n const markdown = markdownBase;\n const canCopyMarkdown = role === \"assistant\" && Boolean(markdown?.trim());\n\n const bubbleClasses = [\n \"chat-bubble\",\n canCopyMarkdown ? \"has-copy\" : \"\",\n opts.isStreaming ? \"streaming\" : \"\",\n \"fade-in\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n if (!markdown && hasToolCards && isToolResult) {\n return html`${toolCards.map((card) =>\n renderToolCardSidebar(card, onOpenSidebar),\n )}`;\n }\n\n if (!markdown && !hasToolCards) return nothing;\n\n return html`\n
    \n ${canCopyMarkdown ? renderCopyAsMarkdownButton(markdown!) : nothing}\n ${reasoningMarkdown\n ? html`
    ${unsafeHTML(\n toSanitizedMarkdownHtml(reasoningMarkdown),\n )}
    `\n : nothing}\n ${markdown\n ? html`
    ${unsafeHTML(toSanitizedMarkdownHtml(markdown))}
    `\n : nothing}\n ${toolCards.map((card) => renderToolCardSidebar(card, onOpenSidebar))}\n
    \n `;\n}\n","import { html, nothing } from \"lit\";\nimport { unsafeHTML } from \"lit/directives/unsafe-html.js\";\n\nimport { toSanitizedMarkdownHtml } from \"../markdown\";\n\nexport type MarkdownSidebarProps = {\n content: string | null;\n error: string | null;\n onClose: () => void;\n onViewRawText: () => void;\n};\n\nexport function renderMarkdownSidebar(props: MarkdownSidebarProps) {\n return html`\n
    \n
    \n
    Tool Output
    \n \n
    \n
    \n ${props.error\n ? html`\n
    ${props.error}
    \n \n `\n : props.content\n ? html`
    ${unsafeHTML(toSanitizedMarkdownHtml(props.content))}
    `\n : html`
    No content available
    `}\n
    \n
    \n `;\n}\n","import { LitElement, html, css } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\n\n/**\n * A draggable divider for resizable split views.\n * Dispatches 'resize' events with { splitRatio: number } detail.\n */\n@customElement(\"resizable-divider\")\nexport class ResizableDivider extends LitElement {\n @property({ type: Number }) splitRatio = 0.6;\n @property({ type: Number }) minRatio = 0.4;\n @property({ type: Number }) maxRatio = 0.7;\n\n private isDragging = false;\n private startX = 0;\n private startRatio = 0;\n\n static styles = css`\n :host {\n width: 4px;\n cursor: col-resize;\n background: var(--border, #333);\n transition: background 150ms ease-out;\n flex-shrink: 0;\n position: relative;\n }\n\n :host::before {\n content: \"\";\n position: absolute;\n top: 0;\n left: -4px;\n right: -4px;\n bottom: 0;\n }\n\n :host(:hover) {\n background: var(--accent, #007bff);\n }\n\n :host(.dragging) {\n background: var(--accent, #007bff);\n }\n `;\n\n render() {\n return html``;\n }\n\n connectedCallback() {\n super.connectedCallback();\n this.addEventListener(\"mousedown\", this.handleMouseDown);\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener(\"mousedown\", this.handleMouseDown);\n document.removeEventListener(\"mousemove\", this.handleMouseMove);\n document.removeEventListener(\"mouseup\", this.handleMouseUp);\n }\n\n private handleMouseDown = (e: MouseEvent) => {\n this.isDragging = true;\n this.startX = e.clientX;\n this.startRatio = this.splitRatio;\n this.classList.add(\"dragging\");\n\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n\n e.preventDefault();\n };\n\n private handleMouseMove = (e: MouseEvent) => {\n if (!this.isDragging) return;\n\n const container = this.parentElement;\n if (!container) return;\n\n const containerWidth = container.getBoundingClientRect().width;\n const deltaX = e.clientX - this.startX;\n const deltaRatio = deltaX / containerWidth;\n\n let newRatio = this.startRatio + deltaRatio;\n newRatio = Math.max(this.minRatio, Math.min(this.maxRatio, newRatio));\n\n this.dispatchEvent(\n new CustomEvent(\"resize\", {\n detail: { splitRatio: newRatio },\n bubbles: true,\n composed: true,\n })\n );\n };\n\n private handleMouseUp = () => {\n this.isDragging = false;\n this.classList.remove(\"dragging\");\n\n document.removeEventListener(\"mousemove\", this.handleMouseMove);\n document.removeEventListener(\"mouseup\", this.handleMouseUp);\n };\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"resizable-divider\": ResizableDivider;\n }\n}\n","import { html, nothing } from \"lit\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport type { SessionsListResult } from \"../types\";\nimport type { ChatQueueItem } from \"../ui-types\";\nimport type { ChatItem, MessageGroup } from \"../types/chat-types\";\nimport {\n normalizeMessage,\n normalizeRoleForGrouping,\n} from \"../chat/message-normalizer\";\nimport {\n renderMessageGroup,\n renderReadingIndicatorGroup,\n renderStreamingGroup,\n} from \"../chat/grouped-render\";\nimport { renderMarkdownSidebar } from \"./markdown-sidebar\";\nimport \"../components/resizable-divider\";\n\nexport type CompactionIndicatorStatus = {\n active: boolean;\n startedAt: number | null;\n completedAt: number | null;\n};\n\nexport type ChatProps = {\n sessionKey: string;\n onSessionKeyChange: (next: string) => void;\n thinkingLevel: string | null;\n showThinking: boolean;\n loading: boolean;\n sending: boolean;\n canAbort?: boolean;\n compactionStatus?: CompactionIndicatorStatus | null;\n messages: unknown[];\n toolMessages: unknown[];\n stream: string | null;\n streamStartedAt: number | null;\n assistantAvatarUrl?: string | null;\n draft: string;\n queue: ChatQueueItem[];\n connected: boolean;\n canSend: boolean;\n disabledReason: string | null;\n error: string | null;\n sessions: SessionsListResult | null;\n // Focus mode\n focusMode: boolean;\n // Sidebar state\n sidebarOpen?: boolean;\n sidebarContent?: string | null;\n sidebarError?: string | null;\n splitRatio?: number;\n assistantName: string;\n assistantAvatar: string | null;\n // Event handlers\n onRefresh: () => void;\n onToggleFocusMode: () => void;\n onDraftChange: (next: string) => void;\n onSend: () => void;\n onAbort?: () => void;\n onQueueRemove: (id: string) => void;\n onNewSession: () => void;\n onOpenSidebar?: (content: string) => void;\n onCloseSidebar?: () => void;\n onSplitRatioChange?: (ratio: number) => void;\n onChatScroll?: (event: Event) => void;\n};\n\nconst COMPACTION_TOAST_DURATION_MS = 5000;\n\nfunction renderCompactionIndicator(status: CompactionIndicatorStatus | null | undefined) {\n if (!status) return nothing;\n \n // Show \"compacting...\" while active\n if (status.active) {\n return html`\n
    \n 🧹 Compacting context...\n
    \n `;\n }\n \n // Show \"compaction complete\" briefly after completion\n if (status.completedAt) {\n const elapsed = Date.now() - status.completedAt;\n if (elapsed < COMPACTION_TOAST_DURATION_MS) {\n return html`\n
    \n 🧹 Context compacted\n
    \n `;\n }\n }\n \n return nothing;\n}\n\nexport function renderChat(props: ChatProps) {\n const canCompose = props.connected;\n const isBusy = props.sending || props.stream !== null;\n const activeSession = props.sessions?.sessions?.find(\n (row) => row.key === props.sessionKey,\n );\n const reasoningLevel = activeSession?.reasoningLevel ?? \"off\";\n const showReasoning = props.showThinking && reasoningLevel !== \"off\";\n const assistantIdentity = {\n name: props.assistantName,\n avatar: props.assistantAvatar ?? props.assistantAvatarUrl ?? null,\n };\n\n const composePlaceholder = props.connected\n ? \"Message (↩ to send, Shift+↩ for line breaks)\"\n : \"Connect to the gateway to start chatting…\";\n\n const splitRatio = props.splitRatio ?? 0.6;\n const sidebarOpen = Boolean(props.sidebarOpen && props.onCloseSidebar);\n const thread = html`\n \n ${props.loading ? html`
    Loading chat…
    ` : nothing}\n ${repeat(buildChatItems(props), (item) => item.key, (item) => {\n if (item.kind === \"reading-indicator\") {\n return renderReadingIndicatorGroup(assistantIdentity);\n }\n\n if (item.kind === \"stream\") {\n return renderStreamingGroup(\n item.text,\n item.startedAt,\n props.onOpenSidebar,\n assistantIdentity,\n );\n }\n\n if (item.kind === \"group\") {\n return renderMessageGroup(item, {\n onOpenSidebar: props.onOpenSidebar,\n showReasoning,\n assistantName: props.assistantName,\n assistantAvatar: assistantIdentity.avatar,\n });\n }\n\n return nothing;\n })}\n \n `;\n\n return html`\n
    \n ${props.disabledReason\n ? html`
    ${props.disabledReason}
    `\n : nothing}\n\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n ${renderCompactionIndicator(props.compactionStatus)}\n\n ${props.focusMode\n ? html`\n \n ✕\n \n `\n : nothing}\n\n \n \n ${thread}\n \n\n ${sidebarOpen\n ? html`\n \n props.onSplitRatioChange?.(e.detail.splitRatio)}\n >\n
    \n ${renderMarkdownSidebar({\n content: props.sidebarContent ?? null,\n error: props.sidebarError ?? null,\n onClose: props.onCloseSidebar!,\n onViewRawText: () => {\n if (!props.sidebarContent || !props.onOpenSidebar) return;\n props.onOpenSidebar(`\\`\\`\\`\\n${props.sidebarContent}\\n\\`\\`\\``);\n },\n })}\n
    \n `\n : nothing}\n \n\n ${props.queue.length\n ? html`\n
    \n
    Queued (${props.queue.length})
    \n
    \n ${props.queue.map(\n (item) => html`\n
    \n
    ${item.text}
    \n props.onQueueRemove(item.id)}\n >\n ✕\n \n
    \n `,\n )}\n
    \n
    \n `\n : nothing}\n\n
    \n \n
    \n \n New session\n \n \n ${isBusy ? \"Queue\" : \"Send\"}\n \n
    \n
    \n
    \n `;\n}\n\nconst CHAT_HISTORY_RENDER_LIMIT = 200;\n\nfunction groupMessages(items: ChatItem[]): Array {\n const result: Array = [];\n let currentGroup: MessageGroup | null = null;\n\n for (const item of items) {\n if (item.kind !== \"message\") {\n if (currentGroup) {\n result.push(currentGroup);\n currentGroup = null;\n }\n result.push(item);\n continue;\n }\n\n const normalized = normalizeMessage(item.message);\n const role = normalizeRoleForGrouping(normalized.role);\n const timestamp = normalized.timestamp || Date.now();\n\n if (!currentGroup || currentGroup.role !== role) {\n if (currentGroup) result.push(currentGroup);\n currentGroup = {\n kind: \"group\",\n key: `group:${role}:${item.key}`,\n role,\n messages: [{ message: item.message, key: item.key }],\n timestamp,\n isStreaming: false,\n };\n } else {\n currentGroup.messages.push({ message: item.message, key: item.key });\n }\n }\n\n if (currentGroup) result.push(currentGroup);\n return result;\n}\n\nfunction buildChatItems(props: ChatProps): Array {\n const items: ChatItem[] = [];\n const history = Array.isArray(props.messages) ? props.messages : [];\n const tools = Array.isArray(props.toolMessages) ? props.toolMessages : [];\n const historyStart = Math.max(0, history.length - CHAT_HISTORY_RENDER_LIMIT);\n if (historyStart > 0) {\n items.push({\n kind: \"message\",\n key: \"chat:history:notice\",\n message: {\n role: \"system\",\n content: `Showing last ${CHAT_HISTORY_RENDER_LIMIT} messages (${historyStart} hidden).`,\n timestamp: Date.now(),\n },\n });\n }\n for (let i = historyStart; i < history.length; i++) {\n const msg = history[i];\n const normalized = normalizeMessage(msg);\n\n if (!props.showThinking && normalized.role.toLowerCase() === \"toolresult\") {\n continue;\n }\n\n items.push({\n kind: \"message\",\n key: messageKey(msg, i),\n message: msg,\n });\n }\n if (props.showThinking) {\n for (let i = 0; i < tools.length; i++) {\n items.push({\n kind: \"message\",\n key: messageKey(tools[i], i + history.length),\n message: tools[i],\n });\n }\n }\n\n if (props.stream !== null) {\n const key = `stream:${props.sessionKey}:${props.streamStartedAt ?? \"live\"}`;\n if (props.stream.trim().length > 0) {\n items.push({\n kind: \"stream\",\n key,\n text: props.stream,\n startedAt: props.streamStartedAt ?? Date.now(),\n });\n } else {\n items.push({ kind: \"reading-indicator\", key });\n }\n }\n\n return groupMessages(items);\n}\n\nfunction messageKey(message: unknown, index: number): string {\n const m = message as Record;\n const toolCallId = typeof m.toolCallId === \"string\" ? m.toolCallId : \"\";\n if (toolCallId) return `tool:${toolCallId}`;\n const id = typeof m.id === \"string\" ? m.id : \"\";\n if (id) return `msg:${id}`;\n const messageId = typeof m.messageId === \"string\" ? m.messageId : \"\";\n if (messageId) return `msg:${messageId}`;\n const timestamp = typeof m.timestamp === \"number\" ? m.timestamp : null;\n const role = typeof m.role === \"string\" ? m.role : \"unknown\";\n if (timestamp != null) return `msg:${role}:${timestamp}:${index}`;\n return `msg:${role}:${index}`;\n}\n","import type { ConfigUiHints } from \"../types\";\n\nexport type JsonSchema = {\n type?: string | string[];\n title?: string;\n description?: string;\n properties?: Record;\n items?: JsonSchema | JsonSchema[];\n additionalProperties?: JsonSchema | boolean;\n enum?: unknown[];\n const?: unknown;\n default?: unknown;\n anyOf?: JsonSchema[];\n oneOf?: JsonSchema[];\n allOf?: JsonSchema[];\n nullable?: boolean;\n};\n\nexport function schemaType(schema: JsonSchema): string | undefined {\n if (!schema) return undefined;\n if (Array.isArray(schema.type)) {\n const filtered = schema.type.filter((t) => t !== \"null\");\n return filtered[0] ?? schema.type[0];\n }\n return schema.type;\n}\n\nexport function defaultValue(schema?: JsonSchema): unknown {\n if (!schema) return \"\";\n if (schema.default !== undefined) return schema.default;\n const type = schemaType(schema);\n switch (type) {\n case \"object\":\n return {};\n case \"array\":\n return [];\n case \"boolean\":\n return false;\n case \"number\":\n case \"integer\":\n return 0;\n case \"string\":\n return \"\";\n default:\n return \"\";\n }\n}\n\nexport function pathKey(path: Array): string {\n return path.filter((segment) => typeof segment === \"string\").join(\".\");\n}\n\nexport function hintForPath(path: Array, hints: ConfigUiHints) {\n const key = pathKey(path);\n const direct = hints[key];\n if (direct) return direct;\n const segments = key.split(\".\");\n for (const [hintKey, hint] of Object.entries(hints)) {\n if (!hintKey.includes(\"*\")) continue;\n const hintSegments = hintKey.split(\".\");\n if (hintSegments.length !== segments.length) continue;\n let match = true;\n for (let i = 0; i < segments.length; i += 1) {\n if (hintSegments[i] !== \"*\" && hintSegments[i] !== segments[i]) {\n match = false;\n break;\n }\n }\n if (match) return hint;\n }\n return undefined;\n}\n\nexport function humanize(raw: string) {\n return raw\n .replace(/_/g, \" \")\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .replace(/\\s+/g, \" \")\n .replace(/^./, (m) => m.toUpperCase());\n}\n\nexport function isSensitivePath(path: Array): boolean {\n const key = pathKey(path).toLowerCase();\n return (\n key.includes(\"token\") ||\n key.includes(\"password\") ||\n key.includes(\"secret\") ||\n key.includes(\"apikey\") ||\n key.endsWith(\"key\")\n );\n}\n\n","import { html, nothing, type TemplateResult } from \"lit\";\nimport type { ConfigUiHints } from \"../types\";\nimport {\n defaultValue,\n hintForPath,\n humanize,\n isSensitivePath,\n pathKey,\n schemaType,\n type JsonSchema,\n} from \"./config-form.shared\";\n\nconst META_KEYS = new Set([\"title\", \"description\", \"default\", \"nullable\"]);\n\nfunction isAnySchema(schema: JsonSchema): boolean {\n const keys = Object.keys(schema ?? {}).filter((key) => !META_KEYS.has(key));\n return keys.length === 0;\n}\n\nfunction jsonValue(value: unknown): string {\n if (value === undefined) return \"\";\n try {\n return JSON.stringify(value, null, 2) ?? \"\";\n } catch {\n return \"\";\n }\n}\n\n// SVG Icons as template literals\nconst icons = {\n chevronDown: html``,\n plus: html``,\n minus: html``,\n trash: html``,\n edit: html``,\n};\n\nexport function renderNode(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult | typeof nothing {\n const { schema, value, path, hints, unsupported, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const type = schemaType(schema);\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const key = pathKey(path);\n\n if (unsupported.has(key)) {\n return html`
    \n
    ${label}
    \n
    Unsupported schema node. Use Raw mode.
    \n
    `;\n }\n\n // Handle anyOf/oneOf unions\n if (schema.anyOf || schema.oneOf) {\n const variants = schema.anyOf ?? schema.oneOf ?? [];\n const nonNull = variants.filter(\n (v) => !(v.type === \"null\" || (Array.isArray(v.type) && v.type.includes(\"null\")))\n );\n\n if (nonNull.length === 1) {\n return renderNode({ ...params, schema: nonNull[0] });\n }\n\n // Check if it's a set of literal values (enum-like)\n const extractLiteral = (v: JsonSchema): unknown | undefined => {\n if (v.const !== undefined) return v.const;\n if (v.enum && v.enum.length === 1) return v.enum[0];\n return undefined;\n };\n const literals = nonNull.map(extractLiteral);\n const allLiterals = literals.every((v) => v !== undefined);\n\n if (allLiterals && literals.length > 0 && literals.length <= 5) {\n // Use segmented control for small sets\n const resolvedValue = value ?? schema.default;\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n ${literals.map((lit, idx) => html`\n onPatch(path, lit)}\n >\n ${String(lit)}\n \n `)}\n
    \n
    \n `;\n }\n\n if (allLiterals && literals.length > 5) {\n // Use dropdown for larger sets\n return renderSelect({ ...params, options: literals, value: value ?? schema.default });\n }\n\n // Handle mixed primitive types\n const primitiveTypes = new Set(\n nonNull.map((variant) => schemaType(variant)).filter(Boolean)\n );\n const normalizedTypes = new Set(\n [...primitiveTypes].map((v) => (v === \"integer\" ? \"number\" : v))\n );\n\n if ([...normalizedTypes].every((v) => [\"string\", \"number\", \"boolean\"].includes(v as string))) {\n const hasString = normalizedTypes.has(\"string\");\n const hasNumber = normalizedTypes.has(\"number\");\n const hasBoolean = normalizedTypes.has(\"boolean\");\n \n if (hasBoolean && normalizedTypes.size === 1) {\n return renderNode({\n ...params,\n schema: { ...schema, type: \"boolean\", anyOf: undefined, oneOf: undefined },\n });\n }\n\n if (hasString || hasNumber) {\n return renderTextInput({\n ...params,\n inputType: hasNumber && !hasString ? \"number\" : \"text\",\n });\n }\n }\n }\n\n // Enum - use segmented for small, dropdown for large\n if (schema.enum) {\n const options = schema.enum;\n if (options.length <= 5) {\n const resolvedValue = value ?? schema.default;\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n ${options.map((opt) => html`\n onPatch(path, opt)}\n >\n ${String(opt)}\n \n `)}\n
    \n
    \n `;\n }\n return renderSelect({ ...params, options, value: value ?? schema.default });\n }\n\n // Object type - collapsible section\n if (type === \"object\") {\n return renderObject(params);\n }\n\n // Array type\n if (type === \"array\") {\n return renderArray(params);\n }\n\n // Boolean - toggle row\n if (type === \"boolean\") {\n const displayValue = typeof value === \"boolean\" ? value : typeof schema.default === \"boolean\" ? schema.default : false;\n return html`\n \n `;\n }\n\n // Number/Integer\n if (type === \"number\" || type === \"integer\") {\n return renderNumberInput(params);\n }\n\n // String\n if (type === \"string\") {\n return renderTextInput({ ...params, inputType: \"text\" });\n }\n\n // Fallback\n return html`\n
    \n
    ${label}
    \n
    Unsupported type: ${type}. Use Raw mode.
    \n
    \n `;\n}\n\nfunction renderTextInput(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n disabled: boolean;\n showLabel?: boolean;\n inputType: \"text\" | \"number\";\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, disabled, onPatch, inputType } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const isSensitive = hint?.sensitive ?? isSensitivePath(path);\n const placeholder =\n hint?.placeholder ??\n (isSensitive ? \"••••\" : schema.default !== undefined ? `Default: ${schema.default}` : \"\");\n const displayValue = value ?? \"\";\n\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n {\n const raw = (e.target as HTMLInputElement).value;\n if (inputType === \"number\") {\n if (raw.trim() === \"\") {\n onPatch(path, undefined);\n return;\n }\n const parsed = Number(raw);\n onPatch(path, Number.isNaN(parsed) ? raw : parsed);\n return;\n }\n onPatch(path, raw);\n }}\n />\n ${schema.default !== undefined ? html`\n onPatch(path, schema.default)}\n >↺\n ` : nothing}\n
    \n
    \n `;\n}\n\nfunction renderNumberInput(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const displayValue = value ?? schema.default ?? \"\";\n const numValue = typeof displayValue === \"number\" ? displayValue : 0;\n\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n onPatch(path, numValue - 1)}\n >−\n {\n const raw = (e.target as HTMLInputElement).value;\n const parsed = raw === \"\" ? undefined : Number(raw);\n onPatch(path, parsed);\n }}\n />\n onPatch(path, numValue + 1)}\n >+\n
    \n
    \n `;\n}\n\nfunction renderSelect(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n disabled: boolean;\n showLabel?: boolean;\n options: unknown[];\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, disabled, options, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const resolvedValue = value ?? schema.default;\n const currentIndex = options.findIndex(\n (opt) => opt === resolvedValue || String(opt) === String(resolvedValue),\n );\n const unset = \"__unset__\";\n\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n = 0 ? String(currentIndex) : unset}\n @change=${(e: Event) => {\n const val = (e.target as HTMLSelectElement).value;\n onPatch(path, val === unset ? undefined : options[Number(val)]);\n }}\n >\n \n ${options.map((opt, idx) => html`\n \n `)}\n \n
    \n `;\n}\n\nfunction renderObject(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, unsupported, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n \n const fallback = value ?? schema.default;\n const obj = fallback && typeof fallback === \"object\" && !Array.isArray(fallback)\n ? (fallback as Record)\n : {};\n const props = schema.properties ?? {};\n const entries = Object.entries(props);\n \n // Sort by hint order\n const sorted = entries.sort((a, b) => {\n const orderA = hintForPath([...path, a[0]], hints)?.order ?? 0;\n const orderB = hintForPath([...path, b[0]], hints)?.order ?? 0;\n if (orderA !== orderB) return orderA - orderB;\n return a[0].localeCompare(b[0]);\n });\n\n const reserved = new Set(Object.keys(props));\n const additional = schema.additionalProperties;\n const allowExtra = Boolean(additional) && typeof additional === \"object\";\n\n // For top-level, don't wrap in collapsible\n if (path.length === 1) {\n return html`\n
    \n ${sorted.map(([propKey, node]) =>\n renderNode({\n schema: node,\n value: obj[propKey],\n path: [...path, propKey],\n hints,\n unsupported,\n disabled,\n onPatch,\n })\n )}\n ${allowExtra ? renderMapField({\n schema: additional as JsonSchema,\n value: obj,\n path,\n hints,\n unsupported,\n disabled,\n reservedKeys: reserved,\n onPatch,\n }) : nothing}\n
    \n `;\n }\n\n // Nested objects get collapsible treatment\n return html`\n
    \n \n ${label}\n ${icons.chevronDown}\n \n ${help ? html`
    ${help}
    ` : nothing}\n
    \n ${sorted.map(([propKey, node]) =>\n renderNode({\n schema: node,\n value: obj[propKey],\n path: [...path, propKey],\n hints,\n unsupported,\n disabled,\n onPatch,\n })\n )}\n ${allowExtra ? renderMapField({\n schema: additional as JsonSchema,\n value: obj,\n path,\n hints,\n unsupported,\n disabled,\n reservedKeys: reserved,\n onPatch,\n }) : nothing}\n
    \n
    \n `;\n}\n\nfunction renderArray(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, unsupported, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n\n const itemsSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;\n if (!itemsSchema) {\n return html`\n
    \n
    ${label}
    \n
    Unsupported array schema. Use Raw mode.
    \n
    \n `;\n }\n\n const arr = Array.isArray(value) ? value : Array.isArray(schema.default) ? schema.default : [];\n\n return html`\n
    \n
    \n ${showLabel ? html`${label}` : nothing}\n ${arr.length} item${arr.length !== 1 ? 's' : ''}\n {\n const next = [...arr, defaultValue(itemsSchema)];\n onPatch(path, next);\n }}\n >\n ${icons.plus}\n Add\n \n
    \n ${help ? html`
    ${help}
    ` : nothing}\n \n ${arr.length === 0 ? html`\n
    \n No items yet. Click \"Add\" to create one.\n
    \n ` : html`\n
    \n ${arr.map((item, idx) => html`\n
    \n
    \n #${idx + 1}\n {\n const next = [...arr];\n next.splice(idx, 1);\n onPatch(path, next);\n }}\n >\n ${icons.trash}\n \n
    \n
    \n ${renderNode({\n schema: itemsSchema,\n value: item,\n path: [...path, idx],\n hints,\n unsupported,\n disabled,\n showLabel: false,\n onPatch,\n })}\n
    \n
    \n `)}\n
    \n `}\n
    \n `;\n}\n\nfunction renderMapField(params: {\n schema: JsonSchema;\n value: Record;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n reservedKeys: Set;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, unsupported, disabled, reservedKeys, onPatch } = params;\n const anySchema = isAnySchema(schema);\n const entries = Object.entries(value ?? {}).filter(([key]) => !reservedKeys.has(key));\n\n return html`\n
    \n
    \n Custom entries\n {\n const next = { ...(value ?? {}) };\n let index = 1;\n let key = `custom-${index}`;\n while (key in next) {\n index += 1;\n key = `custom-${index}`;\n }\n next[key] = anySchema ? {} : defaultValue(schema);\n onPatch(path, next);\n }}\n >\n ${icons.plus}\n Add Entry\n \n
    \n \n ${entries.length === 0 ? html`\n
    No custom entries.
    \n ` : html`\n
    \n ${entries.map(([key, entryValue]) => {\n const valuePath = [...path, key];\n const fallback = jsonValue(entryValue);\n return html`\n
    \n
    \n {\n const nextKey = (e.target as HTMLInputElement).value.trim();\n if (!nextKey || nextKey === key) return;\n const next = { ...(value ?? {}) };\n if (nextKey in next) return;\n next[nextKey] = next[key];\n delete next[key];\n onPatch(path, next);\n }}\n />\n
    \n
    \n ${anySchema\n ? html`\n {\n const target = e.target as HTMLTextAreaElement;\n const raw = target.value.trim();\n if (!raw) {\n onPatch(valuePath, undefined);\n return;\n }\n try {\n onPatch(valuePath, JSON.parse(raw));\n } catch {\n target.value = fallback;\n }\n }}\n >\n `\n : renderNode({\n schema,\n value: entryValue,\n path: valuePath,\n hints,\n unsupported,\n disabled,\n showLabel: false,\n onPatch,\n })}\n
    \n {\n const next = { ...(value ?? {}) };\n delete next[key];\n onPatch(path, next);\n }}\n >\n ${icons.trash}\n \n
    \n `;\n })}\n
    \n `}\n
    \n `;\n}\n","import { html, nothing } from \"lit\";\nimport type { ConfigUiHints } from \"../types\";\nimport {\n hintForPath,\n humanize,\n schemaType,\n type JsonSchema,\n} from \"./config-form.shared\";\nimport { renderNode } from \"./config-form.node\";\n\nexport type ConfigFormProps = {\n schema: JsonSchema | null;\n uiHints: ConfigUiHints;\n value: Record | null;\n disabled?: boolean;\n unsupportedPaths?: string[];\n searchQuery?: string;\n activeSection?: string | null;\n activeSubsection?: string | null;\n onPatch: (path: Array, value: unknown) => void;\n};\n\n// SVG Icons for section cards (Lucide-style)\nconst sectionIcons = {\n env: html``,\n update: html``,\n agents: html``,\n auth: html``,\n channels: html``,\n messages: html``,\n commands: html``,\n hooks: html``,\n skills: html``,\n tools: html``,\n gateway: html``,\n wizard: html``,\n // Additional sections\n meta: html``,\n logging: html``,\n browser: html``,\n ui: html``,\n models: html``,\n bindings: html``,\n broadcast: html``,\n audio: html``,\n session: html``,\n cron: html``,\n web: html``,\n discovery: html``,\n canvasHost: html``,\n talk: html``,\n plugins: html``,\n default: html``,\n};\n\n// Section metadata\nexport const SECTION_META: Record = {\n env: { label: \"Environment Variables\", description: \"Environment variables passed to the gateway process\" },\n update: { label: \"Updates\", description: \"Auto-update settings and release channel\" },\n agents: { label: \"Agents\", description: \"Agent configurations, models, and identities\" },\n auth: { label: \"Authentication\", description: \"API keys and authentication profiles\" },\n channels: { label: \"Channels\", description: \"Messaging channels (Telegram, Discord, Slack, etc.)\" },\n messages: { label: \"Messages\", description: \"Message handling and routing settings\" },\n commands: { label: \"Commands\", description: \"Custom slash commands\" },\n hooks: { label: \"Hooks\", description: \"Webhooks and event hooks\" },\n skills: { label: \"Skills\", description: \"Skill packs and capabilities\" },\n tools: { label: \"Tools\", description: \"Tool configurations (browser, search, etc.)\" },\n gateway: { label: \"Gateway\", description: \"Gateway server settings (port, auth, binding)\" },\n wizard: { label: \"Setup Wizard\", description: \"Setup wizard state and history\" },\n // Additional sections\n meta: { label: \"Metadata\", description: \"Gateway metadata and version information\" },\n logging: { label: \"Logging\", description: \"Log levels and output configuration\" },\n browser: { label: \"Browser\", description: \"Browser automation settings\" },\n ui: { label: \"UI\", description: \"User interface preferences\" },\n models: { label: \"Models\", description: \"AI model configurations and providers\" },\n bindings: { label: \"Bindings\", description: \"Key bindings and shortcuts\" },\n broadcast: { label: \"Broadcast\", description: \"Broadcast and notification settings\" },\n audio: { label: \"Audio\", description: \"Audio input/output settings\" },\n session: { label: \"Session\", description: \"Session management and persistence\" },\n cron: { label: \"Cron\", description: \"Scheduled tasks and automation\" },\n web: { label: \"Web\", description: \"Web server and API settings\" },\n discovery: { label: \"Discovery\", description: \"Service discovery and networking\" },\n canvasHost: { label: \"Canvas Host\", description: \"Canvas rendering and display\" },\n talk: { label: \"Talk\", description: \"Voice and speech settings\" },\n plugins: { label: \"Plugins\", description: \"Plugin management and extensions\" },\n};\n\nfunction getSectionIcon(key: string) {\n return sectionIcons[key as keyof typeof sectionIcons] ?? sectionIcons.default;\n}\n\nfunction matchesSearch(key: string, schema: JsonSchema, query: string): boolean {\n if (!query) return true;\n const q = query.toLowerCase();\n const meta = SECTION_META[key];\n \n // Check key name\n if (key.toLowerCase().includes(q)) return true;\n \n // Check label and description\n if (meta) {\n if (meta.label.toLowerCase().includes(q)) return true;\n if (meta.description.toLowerCase().includes(q)) return true;\n }\n \n return schemaMatches(schema, q);\n}\n\nfunction schemaMatches(schema: JsonSchema, query: string): boolean {\n if (schema.title?.toLowerCase().includes(query)) return true;\n if (schema.description?.toLowerCase().includes(query)) return true;\n if (schema.enum?.some((value) => String(value).toLowerCase().includes(query))) return true;\n\n if (schema.properties) {\n for (const [propKey, propSchema] of Object.entries(schema.properties)) {\n if (propKey.toLowerCase().includes(query)) return true;\n if (schemaMatches(propSchema, query)) return true;\n }\n }\n\n if (schema.items) {\n const items = Array.isArray(schema.items) ? schema.items : [schema.items];\n for (const item of items) {\n if (item && schemaMatches(item, query)) return true;\n }\n }\n\n if (schema.additionalProperties && typeof schema.additionalProperties === \"object\") {\n if (schemaMatches(schema.additionalProperties, query)) return true;\n }\n\n const unions = schema.anyOf ?? schema.oneOf ?? schema.allOf;\n if (unions) {\n for (const entry of unions) {\n if (entry && schemaMatches(entry, query)) return true;\n }\n }\n\n return false;\n}\n\nexport function renderConfigForm(props: ConfigFormProps) {\n if (!props.schema) {\n return html`
    Schema unavailable.
    `;\n }\n const schema = props.schema;\n const value = props.value ?? {};\n if (schemaType(schema) !== \"object\" || !schema.properties) {\n return html`
    Unsupported schema. Use Raw.
    `;\n }\n const unsupported = new Set(props.unsupportedPaths ?? []);\n const properties = schema.properties;\n const searchQuery = props.searchQuery ?? \"\";\n const activeSection = props.activeSection;\n const activeSubsection = props.activeSubsection ?? null;\n\n const entries = Object.entries(properties).sort((a, b) => {\n const orderA = hintForPath([a[0]], props.uiHints)?.order ?? 50;\n const orderB = hintForPath([b[0]], props.uiHints)?.order ?? 50;\n if (orderA !== orderB) return orderA - orderB;\n return a[0].localeCompare(b[0]);\n });\n\n const filteredEntries = entries.filter(([key, node]) => {\n if (activeSection && key !== activeSection) return false;\n if (searchQuery && !matchesSearch(key, node, searchQuery)) return false;\n return true;\n });\n\n let subsectionContext:\n | { sectionKey: string; subsectionKey: string; schema: JsonSchema }\n | null = null;\n if (activeSection && activeSubsection && filteredEntries.length === 1) {\n const sectionSchema = filteredEntries[0]?.[1];\n if (\n sectionSchema &&\n schemaType(sectionSchema) === \"object\" &&\n sectionSchema.properties &&\n sectionSchema.properties[activeSubsection]\n ) {\n subsectionContext = {\n sectionKey: activeSection,\n subsectionKey: activeSubsection,\n schema: sectionSchema.properties[activeSubsection],\n };\n }\n }\n\n if (filteredEntries.length === 0) {\n return html`\n
    \n
    🔍
    \n
    \n ${searchQuery \n ? `No settings match \"${searchQuery}\"` \n : \"No settings in this section\"}\n
    \n
    \n `;\n }\n\n return html`\n
    \n ${subsectionContext\n ? (() => {\n const { sectionKey, subsectionKey, schema: node } = subsectionContext;\n const hint = hintForPath([sectionKey, subsectionKey], props.uiHints);\n const label = hint?.label ?? node.title ?? humanize(subsectionKey);\n const description = hint?.help ?? node.description ?? \"\";\n const sectionValue = (value as Record)[sectionKey];\n const scopedValue =\n sectionValue && typeof sectionValue === \"object\"\n ? (sectionValue as Record)[subsectionKey]\n : undefined;\n const id = `config-section-${sectionKey}-${subsectionKey}`;\n return html`\n
    \n
    \n ${getSectionIcon(sectionKey)}\n
    \n

    ${label}

    \n ${description\n ? html`

    ${description}

    `\n : nothing}\n
    \n
    \n
    \n ${renderNode({\n schema: node,\n value: scopedValue,\n path: [sectionKey, subsectionKey],\n hints: props.uiHints,\n unsupported,\n disabled: props.disabled ?? false,\n showLabel: false,\n onPatch: props.onPatch,\n })}\n
    \n
    \n `;\n })()\n : filteredEntries.map(([key, node]) => {\n const meta = SECTION_META[key] ?? {\n label: key.charAt(0).toUpperCase() + key.slice(1),\n description: node.description ?? \"\",\n };\n\n return html`\n
    \n
    \n ${getSectionIcon(key)}\n
    \n

    ${meta.label}

    \n ${meta.description\n ? html`

    ${meta.description}

    `\n : nothing}\n
    \n
    \n
    \n ${renderNode({\n schema: node,\n value: (value as Record)[key],\n path: [key],\n hints: props.uiHints,\n unsupported,\n disabled: props.disabled ?? false,\n showLabel: false,\n onPatch: props.onPatch,\n })}\n
    \n
    \n `;\n })}\n
    \n `;\n}\n","import { pathKey, schemaType, type JsonSchema } from \"./config-form.shared\";\n\nexport type ConfigSchemaAnalysis = {\n schema: JsonSchema | null;\n unsupportedPaths: string[];\n};\n\nconst META_KEYS = new Set([\"title\", \"description\", \"default\", \"nullable\"]);\n\nfunction isAnySchema(schema: JsonSchema): boolean {\n const keys = Object.keys(schema ?? {}).filter((key) => !META_KEYS.has(key));\n return keys.length === 0;\n}\n\nfunction normalizeEnum(values: unknown[]): { enumValues: unknown[]; nullable: boolean } {\n const filtered = values.filter((value) => value != null);\n const nullable = filtered.length !== values.length;\n const enumValues: unknown[] = [];\n for (const value of filtered) {\n if (!enumValues.some((existing) => Object.is(existing, value))) {\n enumValues.push(value);\n }\n }\n return { enumValues, nullable };\n}\n\nexport function analyzeConfigSchema(raw: unknown): ConfigSchemaAnalysis {\n if (!raw || typeof raw !== \"object\") {\n return { schema: null, unsupportedPaths: [\"\"] };\n }\n return normalizeSchemaNode(raw as JsonSchema, []);\n}\n\nfunction normalizeSchemaNode(\n schema: JsonSchema,\n path: Array,\n): ConfigSchemaAnalysis {\n const unsupported = new Set();\n const normalized: JsonSchema = { ...schema };\n const pathLabel = pathKey(path) || \"\";\n\n if (schema.anyOf || schema.oneOf || schema.allOf) {\n const union = normalizeUnion(schema, path);\n if (union) return union;\n return { schema, unsupportedPaths: [pathLabel] };\n }\n\n const nullable = Array.isArray(schema.type) && schema.type.includes(\"null\");\n const type =\n schemaType(schema) ??\n (schema.properties || schema.additionalProperties ? \"object\" : undefined);\n normalized.type = type ?? schema.type;\n normalized.nullable = nullable || schema.nullable;\n\n if (normalized.enum) {\n const { enumValues, nullable: enumNullable } = normalizeEnum(normalized.enum);\n normalized.enum = enumValues;\n if (enumNullable) normalized.nullable = true;\n if (enumValues.length === 0) unsupported.add(pathLabel);\n }\n\n if (type === \"object\") {\n const properties = schema.properties ?? {};\n const normalizedProps: Record = {};\n for (const [key, value] of Object.entries(properties)) {\n const res = normalizeSchemaNode(value, [...path, key]);\n if (res.schema) normalizedProps[key] = res.schema;\n for (const entry of res.unsupportedPaths) unsupported.add(entry);\n }\n normalized.properties = normalizedProps;\n\n if (schema.additionalProperties === true) {\n unsupported.add(pathLabel);\n } else if (schema.additionalProperties === false) {\n normalized.additionalProperties = false;\n } else if (\n schema.additionalProperties &&\n typeof schema.additionalProperties === \"object\"\n ) {\n if (!isAnySchema(schema.additionalProperties as JsonSchema)) {\n const res = normalizeSchemaNode(\n schema.additionalProperties as JsonSchema,\n [...path, \"*\"],\n );\n normalized.additionalProperties =\n res.schema ?? (schema.additionalProperties as JsonSchema);\n if (res.unsupportedPaths.length > 0) unsupported.add(pathLabel);\n }\n }\n } else if (type === \"array\") {\n const itemsSchema = Array.isArray(schema.items)\n ? schema.items[0]\n : schema.items;\n if (!itemsSchema) {\n unsupported.add(pathLabel);\n } else {\n const res = normalizeSchemaNode(itemsSchema, [...path, \"*\"]);\n normalized.items = res.schema ?? itemsSchema;\n if (res.unsupportedPaths.length > 0) unsupported.add(pathLabel);\n }\n } else if (\n type !== \"string\" &&\n type !== \"number\" &&\n type !== \"integer\" &&\n type !== \"boolean\" &&\n !normalized.enum\n ) {\n unsupported.add(pathLabel);\n }\n\n return {\n schema: normalized,\n unsupportedPaths: Array.from(unsupported),\n };\n}\n\nfunction normalizeUnion(\n schema: JsonSchema,\n path: Array,\n): ConfigSchemaAnalysis | null {\n if (schema.allOf) return null;\n const union = schema.anyOf ?? schema.oneOf;\n if (!union) return null;\n\n const literals: unknown[] = [];\n const remaining: JsonSchema[] = [];\n let nullable = false;\n\n for (const entry of union) {\n if (!entry || typeof entry !== \"object\") return null;\n if (Array.isArray(entry.enum)) {\n const { enumValues, nullable: enumNullable } = normalizeEnum(entry.enum);\n literals.push(...enumValues);\n if (enumNullable) nullable = true;\n continue;\n }\n if (\"const\" in entry) {\n if (entry.const == null) {\n nullable = true;\n continue;\n }\n literals.push(entry.const);\n continue;\n }\n if (schemaType(entry) === \"null\") {\n nullable = true;\n continue;\n }\n remaining.push(entry);\n }\n\n if (literals.length > 0 && remaining.length === 0) {\n const unique: unknown[] = [];\n for (const value of literals) {\n if (!unique.some((existing) => Object.is(existing, value))) {\n unique.push(value);\n }\n }\n return {\n schema: {\n ...schema,\n enum: unique,\n nullable,\n anyOf: undefined,\n oneOf: undefined,\n allOf: undefined,\n },\n unsupportedPaths: [],\n };\n }\n\n if (remaining.length === 1) {\n const res = normalizeSchemaNode(remaining[0], path);\n if (res.schema) {\n res.schema.nullable = nullable || res.schema.nullable;\n }\n return res;\n }\n\n const primitiveTypes = [\"string\", \"number\", \"integer\", \"boolean\"];\n if (\n remaining.length > 0 &&\n literals.length === 0 &&\n remaining.every((entry) => entry.type && primitiveTypes.includes(String(entry.type)))\n ) {\n return {\n schema: {\n ...schema,\n nullable,\n },\n unsupportedPaths: [],\n };\n }\n\n return null;\n}\n","import { html, nothing } from \"lit\";\nimport type { ConfigUiHints } from \"../types\";\nimport { analyzeConfigSchema, renderConfigForm, SECTION_META } from \"./config-form\";\nimport {\n hintForPath,\n humanize,\n schemaType,\n type JsonSchema,\n} from \"./config-form.shared\";\n\nexport type ConfigProps = {\n raw: string;\n valid: boolean | null;\n issues: unknown[];\n loading: boolean;\n saving: boolean;\n applying: boolean;\n updating: boolean;\n connected: boolean;\n schema: unknown | null;\n schemaLoading: boolean;\n uiHints: ConfigUiHints;\n formMode: \"form\" | \"raw\";\n formValue: Record | null;\n originalValue: Record | null;\n searchQuery: string;\n activeSection: string | null;\n activeSubsection: string | null;\n onRawChange: (next: string) => void;\n onFormModeChange: (mode: \"form\" | \"raw\") => void;\n onFormPatch: (path: Array, value: unknown) => void;\n onSearchChange: (query: string) => void;\n onSectionChange: (section: string | null) => void;\n onSubsectionChange: (section: string | null) => void;\n onReload: () => void;\n onSave: () => void;\n onApply: () => void;\n onUpdate: () => void;\n};\n\n// SVG Icons for sidebar (Lucide-style)\nconst sidebarIcons = {\n all: html``,\n env: html``,\n update: html``,\n agents: html``,\n auth: html``,\n channels: html``,\n messages: html``,\n commands: html``,\n hooks: html``,\n skills: html``,\n tools: html``,\n gateway: html``,\n wizard: html``,\n // Additional sections\n meta: html``,\n logging: html``,\n browser: html``,\n ui: html``,\n models: html``,\n bindings: html``,\n broadcast: html``,\n audio: html``,\n session: html``,\n cron: html``,\n web: html``,\n discovery: html``,\n canvasHost: html``,\n talk: html``,\n plugins: html``,\n default: html``,\n};\n\n// Section definitions\nconst SECTIONS: Array<{ key: string; label: string }> = [\n { key: \"env\", label: \"Environment\" },\n { key: \"update\", label: \"Updates\" },\n { key: \"agents\", label: \"Agents\" },\n { key: \"auth\", label: \"Authentication\" },\n { key: \"channels\", label: \"Channels\" },\n { key: \"messages\", label: \"Messages\" },\n { key: \"commands\", label: \"Commands\" },\n { key: \"hooks\", label: \"Hooks\" },\n { key: \"skills\", label: \"Skills\" },\n { key: \"tools\", label: \"Tools\" },\n { key: \"gateway\", label: \"Gateway\" },\n { key: \"wizard\", label: \"Setup Wizard\" },\n];\n\ntype SubsectionEntry = {\n key: string;\n label: string;\n description?: string;\n order: number;\n};\n\nconst ALL_SUBSECTION = \"__all__\";\n\nfunction getSectionIcon(key: string) {\n return sidebarIcons[key as keyof typeof sidebarIcons] ?? sidebarIcons.default;\n}\n\nfunction resolveSectionMeta(key: string, schema?: JsonSchema): {\n label: string;\n description?: string;\n} {\n const meta = SECTION_META[key];\n if (meta) return meta;\n return {\n label: schema?.title ?? humanize(key),\n description: schema?.description ?? \"\",\n };\n}\n\nfunction resolveSubsections(params: {\n key: string;\n schema: JsonSchema | undefined;\n uiHints: ConfigUiHints;\n}): SubsectionEntry[] {\n const { key, schema, uiHints } = params;\n if (!schema || schemaType(schema) !== \"object\" || !schema.properties) return [];\n const entries = Object.entries(schema.properties).map(([subKey, node]) => {\n const hint = hintForPath([key, subKey], uiHints);\n const label = hint?.label ?? node.title ?? humanize(subKey);\n const description = hint?.help ?? node.description ?? \"\";\n const order = hint?.order ?? 50;\n return { key: subKey, label, description, order };\n });\n entries.sort((a, b) => (a.order !== b.order ? a.order - b.order : a.key.localeCompare(b.key)));\n return entries;\n}\n\nfunction computeDiff(\n original: Record | null,\n current: Record | null\n): Array<{ path: string; from: unknown; to: unknown }> {\n if (!original || !current) return [];\n const changes: Array<{ path: string; from: unknown; to: unknown }> = [];\n \n function compare(orig: unknown, curr: unknown, path: string) {\n if (orig === curr) return;\n if (typeof orig !== typeof curr) {\n changes.push({ path, from: orig, to: curr });\n return;\n }\n if (typeof orig !== \"object\" || orig === null || curr === null) {\n if (orig !== curr) {\n changes.push({ path, from: orig, to: curr });\n }\n return;\n }\n if (Array.isArray(orig) && Array.isArray(curr)) {\n if (JSON.stringify(orig) !== JSON.stringify(curr)) {\n changes.push({ path, from: orig, to: curr });\n }\n return;\n }\n const origObj = orig as Record;\n const currObj = curr as Record;\n const allKeys = new Set([...Object.keys(origObj), ...Object.keys(currObj)]);\n for (const key of allKeys) {\n compare(origObj[key], currObj[key], path ? `${path}.${key}` : key);\n }\n }\n \n compare(original, current, \"\");\n return changes;\n}\n\nfunction truncateValue(value: unknown, maxLen = 40): string {\n let str: string;\n try {\n const json = JSON.stringify(value);\n str = json ?? String(value);\n } catch {\n str = String(value);\n }\n if (str.length <= maxLen) return str;\n return str.slice(0, maxLen - 3) + \"...\";\n}\n\nexport function renderConfig(props: ConfigProps) {\n const validity =\n props.valid == null ? \"unknown\" : props.valid ? \"valid\" : \"invalid\";\n const analysis = analyzeConfigSchema(props.schema);\n const formUnsafe = analysis.schema\n ? analysis.unsupportedPaths.length > 0\n : false;\n const canSaveForm =\n Boolean(props.formValue) && !props.loading && !formUnsafe;\n const canSave =\n props.connected &&\n !props.saving &&\n (props.formMode === \"raw\" ? true : canSaveForm);\n const canApply =\n props.connected &&\n !props.applying &&\n !props.updating &&\n (props.formMode === \"raw\" ? true : canSaveForm);\n const canUpdate = props.connected && !props.applying && !props.updating;\n\n // Get available sections from schema\n const schemaProps = analysis.schema?.properties ?? {};\n const availableSections = SECTIONS.filter(s => s.key in schemaProps);\n \n // Add any sections in schema but not in our list\n const knownKeys = new Set(SECTIONS.map(s => s.key));\n const extraSections = Object.keys(schemaProps)\n .filter(k => !knownKeys.has(k))\n .map(k => ({ key: k, label: k.charAt(0).toUpperCase() + k.slice(1) }));\n \n const allSections = [...availableSections, ...extraSections];\n\n const activeSectionSchema =\n props.activeSection && analysis.schema && schemaType(analysis.schema) === \"object\"\n ? (analysis.schema.properties?.[props.activeSection] as JsonSchema | undefined)\n : undefined;\n const activeSectionMeta = props.activeSection\n ? resolveSectionMeta(props.activeSection, activeSectionSchema)\n : null;\n const subsections = props.activeSection\n ? resolveSubsections({\n key: props.activeSection,\n schema: activeSectionSchema,\n uiHints: props.uiHints,\n })\n : [];\n const allowSubnav =\n props.formMode === \"form\" &&\n Boolean(props.activeSection) &&\n subsections.length > 0;\n const isAllSubsection = props.activeSubsection === ALL_SUBSECTION;\n const effectiveSubsection = props.searchQuery\n ? null\n : isAllSubsection\n ? null\n : props.activeSubsection ?? (subsections[0]?.key ?? null);\n \n // Compute diff for showing changes\n const diff = props.formMode === \"form\" \n ? computeDiff(props.originalValue, props.formValue)\n : [];\n const hasChanges = diff.length > 0;\n\n return html`\n
    \n \n \n \n \n
    \n \n
    \n
    \n ${hasChanges ? html`\n ${diff.length} unsaved change${diff.length !== 1 ? \"s\" : \"\"}\n ` : html`\n No changes\n `}\n
    \n
    \n \n \n ${props.saving ? \"Saving…\" : \"Save\"}\n \n \n ${props.applying ? \"Applying…\" : \"Apply\"}\n \n \n ${props.updating ? \"Updating…\" : \"Update\"}\n \n
    \n
    \n \n \n ${hasChanges ? html`\n
    \n \n View ${diff.length} pending change${diff.length !== 1 ? \"s\" : \"\"}\n \n \n \n \n
    \n ${diff.map(change => html`\n
    \n
    ${change.path}
    \n
    \n ${truncateValue(change.from)}\n \n ${truncateValue(change.to)}\n
    \n
    \n `)}\n
    \n
    \n ` : nothing}\n\n ${activeSectionMeta && props.formMode === \"form\"\n ? html`\n
    \n
    ${getSectionIcon(props.activeSection ?? \"\")}
    \n
    \n
    ${activeSectionMeta.label}
    \n ${activeSectionMeta.description\n ? html`
    ${activeSectionMeta.description}
    `\n : nothing}\n
    \n
    \n `\n : nothing}\n\n ${allowSubnav\n ? html`\n
    \n props.onSubsectionChange(ALL_SUBSECTION)}\n >\n All\n \n ${subsections.map(\n (entry) => html`\n props.onSubsectionChange(entry.key)}\n >\n ${entry.label}\n \n `,\n )}\n
    \n `\n : nothing}\n\n \n
    \n ${props.formMode === \"form\"\n ? html`\n ${props.schemaLoading\n ? html`
    \n
    \n Loading schema…\n
    `\n : renderConfigForm({\n schema: analysis.schema,\n uiHints: props.uiHints,\n value: props.formValue,\n disabled: props.loading || !props.formValue,\n unsupportedPaths: analysis.unsupportedPaths,\n onPatch: props.onFormPatch,\n searchQuery: props.searchQuery,\n activeSection: props.activeSection,\n activeSubsection: effectiveSubsection,\n })}\n ${formUnsafe\n ? html`
    \n Form view can't safely edit some fields.\n Use Raw to avoid losing config entries.\n
    `\n : nothing}\n `\n : html`\n \n `}\n
    \n\n ${props.issues.length > 0\n ? html`
    \n
    ${JSON.stringify(props.issues, null, 2)}
    \n
    `\n : nothing}\n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { ChannelAccountSnapshot } from \"../types\";\nimport type { ChannelKey, ChannelsProps } from \"./channels.types\";\n\nexport function formatDuration(ms?: number | null) {\n if (!ms && ms !== 0) return \"n/a\";\n const sec = Math.round(ms / 1000);\n if (sec < 60) return `${sec}s`;\n const min = Math.round(sec / 60);\n if (min < 60) return `${min}m`;\n const hr = Math.round(min / 60);\n return `${hr}h`;\n}\n\nexport function channelEnabled(key: ChannelKey, props: ChannelsProps) {\n const snapshot = props.snapshot;\n const channels = snapshot?.channels as Record | null;\n if (!snapshot || !channels) return false;\n const channelStatus = channels[key] as Record | undefined;\n const configured = typeof channelStatus?.configured === \"boolean\" && channelStatus.configured;\n const running = typeof channelStatus?.running === \"boolean\" && channelStatus.running;\n const connected = typeof channelStatus?.connected === \"boolean\" && channelStatus.connected;\n const accounts = snapshot.channelAccounts?.[key] ?? [];\n const accountActive = accounts.some(\n (account) => account.configured || account.running || account.connected,\n );\n return configured || running || connected || accountActive;\n}\n\nexport function getChannelAccountCount(\n key: ChannelKey,\n channelAccounts?: Record | null,\n): number {\n return channelAccounts?.[key]?.length ?? 0;\n}\n\nexport function renderChannelAccountCount(\n key: ChannelKey,\n channelAccounts?: Record | null,\n) {\n const count = getChannelAccountCount(key, channelAccounts);\n if (count < 2) return nothing;\n return html`
    Accounts (${count})
    `;\n}\n\n","import { html } from \"lit\";\n\nimport type { ConfigUiHints } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport {\n analyzeConfigSchema,\n renderNode,\n schemaType,\n type JsonSchema,\n} from \"./config-form\";\n\ntype ChannelConfigFormProps = {\n channelId: string;\n configValue: Record | null;\n schema: unknown | null;\n uiHints: ConfigUiHints;\n disabled: boolean;\n onPatch: (path: Array, value: unknown) => void;\n};\n\nfunction resolveSchemaNode(\n schema: JsonSchema | null,\n path: Array,\n): JsonSchema | null {\n let current = schema;\n for (const key of path) {\n if (!current) return null;\n const type = schemaType(current);\n if (type === \"object\") {\n const properties = current.properties ?? {};\n if (typeof key === \"string\" && properties[key]) {\n current = properties[key];\n continue;\n }\n const additional = current.additionalProperties;\n if (typeof key === \"string\" && additional && typeof additional === \"object\") {\n current = additional as JsonSchema;\n continue;\n }\n return null;\n }\n if (type === \"array\") {\n if (typeof key !== \"number\") return null;\n const items = Array.isArray(current.items) ? current.items[0] : current.items;\n current = items ?? null;\n continue;\n }\n return null;\n }\n return current;\n}\n\nfunction resolveChannelValue(\n config: Record,\n channelId: string,\n): Record {\n const channels = (config.channels ?? {}) as Record;\n const fromChannels = channels[channelId];\n const fallback = config[channelId];\n const resolved =\n (fromChannels && typeof fromChannels === \"object\"\n ? (fromChannels as Record)\n : null) ??\n (fallback && typeof fallback === \"object\"\n ? (fallback as Record)\n : null);\n return resolved ?? {};\n}\n\nexport function renderChannelConfigForm(props: ChannelConfigFormProps) {\n const analysis = analyzeConfigSchema(props.schema);\n const normalized = analysis.schema;\n if (!normalized) {\n return html`
    Schema unavailable. Use Raw.
    `;\n }\n const node = resolveSchemaNode(normalized, [\"channels\", props.channelId]);\n if (!node) {\n return html`
    Channel config schema unavailable.
    `;\n }\n const configValue = props.configValue ?? {};\n const value = resolveChannelValue(configValue, props.channelId);\n return html`\n
    \n ${renderNode({\n schema: node,\n value,\n path: [\"channels\", props.channelId],\n hints: props.uiHints,\n unsupported: new Set(analysis.unsupportedPaths),\n disabled: props.disabled,\n showLabel: false,\n onPatch: props.onPatch,\n })}\n
    \n `;\n}\n\nexport function renderChannelConfigSection(params: {\n channelId: string;\n props: ChannelsProps;\n}) {\n const { channelId, props } = params;\n const disabled = props.configSaving || props.configSchemaLoading;\n return html`\n
    \n ${props.configSchemaLoading\n ? html`
    Loading config schema…
    `\n : renderChannelConfigForm({\n channelId,\n configValue: props.configForm,\n schema: props.configSchema,\n uiHints: props.configUiHints,\n disabled,\n onPatch: props.onConfigPatch,\n })}\n
    \n props.onConfigSave()}\n >\n ${props.configSaving ? \"Saving…\" : \"Save\"}\n \n props.onConfigReload()}\n >\n Reload\n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { DiscordStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderDiscordCard(params: {\n props: ChannelsProps;\n discord?: DiscordStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, discord, accountCountLabel } = params;\n\n return html`\n
    \n
    Discord
    \n
    Bot status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${discord?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${discord?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Last start\n ${discord?.lastStartAt ? formatAgo(discord.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${discord?.lastProbeAt ? formatAgo(discord.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${discord?.lastError\n ? html`
    \n ${discord.lastError}\n
    `\n : nothing}\n\n ${discord?.probe\n ? html`
    \n Probe ${discord.probe.ok ? \"ok\" : \"failed\"} ·\n ${discord.probe.status ?? \"\"} ${discord.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"discord\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { IMessageStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderIMessageCard(params: {\n props: ChannelsProps;\n imessage?: IMessageStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, imessage, accountCountLabel } = params;\n\n return html`\n
    \n
    iMessage
    \n
    macOS bridge status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${imessage?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${imessage?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Last start\n ${imessage?.lastStartAt ? formatAgo(imessage.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${imessage?.lastProbeAt ? formatAgo(imessage.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${imessage?.lastError\n ? html`
    \n ${imessage.lastError}\n
    `\n : nothing}\n\n ${imessage?.probe\n ? html`
    \n Probe ${imessage.probe.ok ? \"ok\" : \"failed\"} ·\n ${imessage.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"imessage\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","/**\n * Nostr Profile Edit Form\n *\n * Provides UI for editing and publishing Nostr profile (kind:0).\n */\n\nimport { html, nothing, type TemplateResult } from \"lit\";\n\nimport type { NostrProfile as NostrProfileType } from \"../types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface NostrProfileFormState {\n /** Current form values */\n values: NostrProfileType;\n /** Original values for dirty detection */\n original: NostrProfileType;\n /** Whether the form is currently submitting */\n saving: boolean;\n /** Whether import is in progress */\n importing: boolean;\n /** Last error message */\n error: string | null;\n /** Last success message */\n success: string | null;\n /** Validation errors per field */\n fieldErrors: Record;\n /** Whether to show advanced fields */\n showAdvanced: boolean;\n}\n\nexport interface NostrProfileFormCallbacks {\n /** Called when a field value changes */\n onFieldChange: (field: keyof NostrProfileType, value: string) => void;\n /** Called when save is clicked */\n onSave: () => void;\n /** Called when import is clicked */\n onImport: () => void;\n /** Called when cancel is clicked */\n onCancel: () => void;\n /** Called when toggle advanced is clicked */\n onToggleAdvanced: () => void;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction isFormDirty(state: NostrProfileFormState): boolean {\n const { values, original } = state;\n return (\n values.name !== original.name ||\n values.displayName !== original.displayName ||\n values.about !== original.about ||\n values.picture !== original.picture ||\n values.banner !== original.banner ||\n values.website !== original.website ||\n values.nip05 !== original.nip05 ||\n values.lud16 !== original.lud16\n );\n}\n\n// ============================================================================\n// Form Rendering\n// ============================================================================\n\nexport function renderNostrProfileForm(params: {\n state: NostrProfileFormState;\n callbacks: NostrProfileFormCallbacks;\n accountId: string;\n}): TemplateResult {\n const { state, callbacks, accountId } = params;\n const isDirty = isFormDirty(state);\n\n const renderField = (\n field: keyof NostrProfileType,\n label: string,\n opts: {\n type?: \"text\" | \"url\" | \"textarea\";\n placeholder?: string;\n maxLength?: number;\n help?: string;\n } = {}\n ) => {\n const { type = \"text\", placeholder, maxLength, help } = opts;\n const value = state.values[field] ?? \"\";\n const error = state.fieldErrors[field];\n\n const inputId = `nostr-profile-${field}`;\n\n if (type === \"textarea\") {\n return html`\n
    \n \n {\n const target = e.target as HTMLTextAreaElement;\n callbacks.onFieldChange(field, target.value);\n }}\n ?disabled=${state.saving}\n >\n ${help ? html`
    ${help}
    ` : nothing}\n ${error ? html`
    ${error}
    ` : nothing}\n
    \n `;\n }\n\n return html`\n
    \n \n {\n const target = e.target as HTMLInputElement;\n callbacks.onFieldChange(field, target.value);\n }}\n ?disabled=${state.saving}\n />\n ${help ? html`
    ${help}
    ` : nothing}\n ${error ? html`
    ${error}
    ` : nothing}\n
    \n `;\n };\n\n const renderPicturePreview = () => {\n const picture = state.values.picture;\n if (!picture) return nothing;\n\n return html`\n
    \n {\n const img = e.target as HTMLImageElement;\n img.style.display = \"none\";\n }}\n @load=${(e: Event) => {\n const img = e.target as HTMLImageElement;\n img.style.display = \"block\";\n }}\n />\n
    \n `;\n };\n\n return html`\n
    \n
    \n
    Edit Profile
    \n
    Account: ${accountId}
    \n
    \n\n ${state.error\n ? html`
    ${state.error}
    `\n : nothing}\n\n ${state.success\n ? html`
    ${state.success}
    `\n : nothing}\n\n ${renderPicturePreview()}\n\n ${renderField(\"name\", \"Username\", {\n placeholder: \"satoshi\",\n maxLength: 256,\n help: \"Short username (e.g., satoshi)\",\n })}\n\n ${renderField(\"displayName\", \"Display Name\", {\n placeholder: \"Satoshi Nakamoto\",\n maxLength: 256,\n help: \"Your full display name\",\n })}\n\n ${renderField(\"about\", \"Bio\", {\n type: \"textarea\",\n placeholder: \"Tell people about yourself...\",\n maxLength: 2000,\n help: \"A brief bio or description\",\n })}\n\n ${renderField(\"picture\", \"Avatar URL\", {\n type: \"url\",\n placeholder: \"https://example.com/avatar.jpg\",\n help: \"HTTPS URL to your profile picture\",\n })}\n\n ${state.showAdvanced\n ? html`\n
    \n
    Advanced
    \n\n ${renderField(\"banner\", \"Banner URL\", {\n type: \"url\",\n placeholder: \"https://example.com/banner.jpg\",\n help: \"HTTPS URL to a banner image\",\n })}\n\n ${renderField(\"website\", \"Website\", {\n type: \"url\",\n placeholder: \"https://example.com\",\n help: \"Your personal website\",\n })}\n\n ${renderField(\"nip05\", \"NIP-05 Identifier\", {\n placeholder: \"you@example.com\",\n help: \"Verifiable identifier (e.g., you@domain.com)\",\n })}\n\n ${renderField(\"lud16\", \"Lightning Address\", {\n placeholder: \"you@getalby.com\",\n help: \"Lightning address for tips (LUD-16)\",\n })}\n
    \n `\n : nothing}\n\n
    \n \n ${state.saving ? \"Saving...\" : \"Save & Publish\"}\n \n\n \n ${state.importing ? \"Importing...\" : \"Import from Relays\"}\n \n\n \n ${state.showAdvanced ? \"Hide Advanced\" : \"Show Advanced\"}\n \n\n \n Cancel\n \n
    \n\n ${isDirty\n ? html`
    \n You have unsaved changes\n
    `\n : nothing}\n
    \n `;\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create initial form state from existing profile\n */\nexport function createNostrProfileFormState(\n profile: NostrProfileType | undefined\n): NostrProfileFormState {\n const values: NostrProfileType = {\n name: profile?.name ?? \"\",\n displayName: profile?.displayName ?? \"\",\n about: profile?.about ?? \"\",\n picture: profile?.picture ?? \"\",\n banner: profile?.banner ?? \"\",\n website: profile?.website ?? \"\",\n nip05: profile?.nip05 ?? \"\",\n lud16: profile?.lud16 ?? \"\",\n };\n\n return {\n values,\n original: { ...values },\n saving: false,\n importing: false,\n error: null,\n success: null,\n fieldErrors: {},\n showAdvanced: Boolean(\n profile?.banner || profile?.website || profile?.nip05 || profile?.lud16\n ),\n };\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { ChannelAccountSnapshot, NostrStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\nimport {\n renderNostrProfileForm,\n type NostrProfileFormState,\n type NostrProfileFormCallbacks,\n} from \"./channels.nostr-profile-form\";\n\n/**\n * Truncate a pubkey for display (shows first and last 8 chars)\n */\nfunction truncatePubkey(pubkey: string | null | undefined): string {\n if (!pubkey) return \"n/a\";\n if (pubkey.length <= 20) return pubkey;\n return `${pubkey.slice(0, 8)}...${pubkey.slice(-8)}`;\n}\n\nexport function renderNostrCard(params: {\n props: ChannelsProps;\n nostr?: NostrStatus | null;\n nostrAccounts: ChannelAccountSnapshot[];\n accountCountLabel: unknown;\n /** Profile form state (optional - if provided, shows form) */\n profileFormState?: NostrProfileFormState | null;\n /** Profile form callbacks */\n profileFormCallbacks?: NostrProfileFormCallbacks | null;\n /** Called when Edit Profile is clicked */\n onEditProfile?: () => void;\n}) {\n const {\n props,\n nostr,\n nostrAccounts,\n accountCountLabel,\n profileFormState,\n profileFormCallbacks,\n onEditProfile,\n } = params;\n const primaryAccount = nostrAccounts[0];\n const summaryConfigured = nostr?.configured ?? primaryAccount?.configured ?? false;\n const summaryRunning = nostr?.running ?? primaryAccount?.running ?? false;\n const summaryPublicKey =\n nostr?.publicKey ??\n (primaryAccount as { publicKey?: string } | undefined)?.publicKey;\n const summaryLastStartAt = nostr?.lastStartAt ?? primaryAccount?.lastStartAt ?? null;\n const summaryLastError = nostr?.lastError ?? primaryAccount?.lastError ?? null;\n const hasMultipleAccounts = nostrAccounts.length > 1;\n const showingForm = profileFormState !== null && profileFormState !== undefined;\n\n const renderAccountCard = (account: ChannelAccountSnapshot) => {\n const publicKey = (account as { publicKey?: string }).publicKey;\n const profile = (account as { profile?: { name?: string; displayName?: string } }).profile;\n const displayName = profile?.displayName ?? profile?.name ?? account.name ?? account.accountId;\n\n return html`\n
    \n
    \n
    ${displayName}
    \n
    ${account.accountId}
    \n
    \n
    \n
    \n Running\n ${account.running ? \"Yes\" : \"No\"}\n
    \n
    \n Configured\n ${account.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Public Key\n ${truncatePubkey(publicKey)}\n
    \n
    \n Last inbound\n ${account.lastInboundAt ? formatAgo(account.lastInboundAt) : \"n/a\"}\n
    \n ${account.lastError\n ? html`\n
    ${account.lastError}
    \n `\n : nothing}\n
    \n
    \n `;\n };\n\n const renderProfileSection = () => {\n // If showing form, render the form instead of the read-only view\n if (showingForm && profileFormCallbacks) {\n return renderNostrProfileForm({\n state: profileFormState,\n callbacks: profileFormCallbacks,\n accountId: nostrAccounts[0]?.accountId ?? \"default\",\n });\n }\n\n const profile =\n (primaryAccount as\n | {\n profile?: {\n name?: string;\n displayName?: string;\n about?: string;\n picture?: string;\n nip05?: string;\n };\n }\n | undefined)?.profile ?? nostr?.profile;\n const { name, displayName, about, picture, nip05 } = profile ?? {};\n const hasAnyProfileData = name || displayName || about || picture || nip05;\n\n return html`\n
    \n
    \n
    Profile
    \n ${summaryConfigured\n ? html`\n \n Edit Profile\n \n `\n : nothing}\n
    \n ${hasAnyProfileData\n ? html`\n
    \n ${picture\n ? html`\n
    \n {\n (e.target as HTMLImageElement).style.display = \"none\";\n }}\n />\n
    \n `\n : nothing}\n ${name ? html`
    Name${name}
    ` : nothing}\n ${displayName\n ? html`
    Display Name${displayName}
    `\n : nothing}\n ${about\n ? html`
    About${about}
    `\n : nothing}\n ${nip05 ? html`
    NIP-05${nip05}
    ` : nothing}\n
    \n `\n : html`\n
    \n No profile set. Click \"Edit Profile\" to add your name, bio, and avatar.\n
    \n `}\n
    \n `;\n };\n\n return html`\n
    \n
    Nostr
    \n
    Decentralized DMs via Nostr relays (NIP-04).
    \n ${accountCountLabel}\n\n ${hasMultipleAccounts\n ? html`\n
    \n ${nostrAccounts.map((account) => renderAccountCard(account))}\n
    \n `\n : html`\n
    \n
    \n Configured\n ${summaryConfigured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${summaryRunning ? \"Yes\" : \"No\"}\n
    \n
    \n Public Key\n ${truncatePubkey(summaryPublicKey)}\n
    \n
    \n Last start\n ${summaryLastStartAt ? formatAgo(summaryLastStartAt) : \"n/a\"}\n
    \n
    \n `}\n\n ${summaryLastError\n ? html`
    ${summaryLastError}
    `\n : nothing}\n\n ${renderProfileSection()}\n\n ${renderChannelConfigSection({ channelId: \"nostr\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { SignalStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderSignalCard(params: {\n props: ChannelsProps;\n signal?: SignalStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, signal, accountCountLabel } = params;\n\n return html`\n
    \n
    Signal
    \n
    signal-cli status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${signal?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${signal?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Base URL\n ${signal?.baseUrl ?? \"n/a\"}\n
    \n
    \n Last start\n ${signal?.lastStartAt ? formatAgo(signal.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${signal?.lastProbeAt ? formatAgo(signal.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${signal?.lastError\n ? html`
    \n ${signal.lastError}\n
    `\n : nothing}\n\n ${signal?.probe\n ? html`
    \n Probe ${signal.probe.ok ? \"ok\" : \"failed\"} ·\n ${signal.probe.status ?? \"\"} ${signal.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"signal\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { SlackStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderSlackCard(params: {\n props: ChannelsProps;\n slack?: SlackStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, slack, accountCountLabel } = params;\n\n return html`\n
    \n
    Slack
    \n
    Socket mode status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${slack?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${slack?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Last start\n ${slack?.lastStartAt ? formatAgo(slack.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${slack?.lastProbeAt ? formatAgo(slack.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${slack?.lastError\n ? html`
    \n ${slack.lastError}\n
    `\n : nothing}\n\n ${slack?.probe\n ? html`
    \n Probe ${slack.probe.ok ? \"ok\" : \"failed\"} ·\n ${slack.probe.status ?? \"\"} ${slack.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"slack\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { ChannelAccountSnapshot, TelegramStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderTelegramCard(params: {\n props: ChannelsProps;\n telegram?: TelegramStatus;\n telegramAccounts: ChannelAccountSnapshot[];\n accountCountLabel: unknown;\n}) {\n const { props, telegram, telegramAccounts, accountCountLabel } = params;\n const hasMultipleAccounts = telegramAccounts.length > 1;\n\n const renderAccountCard = (account: ChannelAccountSnapshot) => {\n const probe = account.probe as { bot?: { username?: string } } | undefined;\n const botUsername = probe?.bot?.username;\n const label = account.name || account.accountId;\n return html`\n
    \n
    \n
    \n ${botUsername ? `@${botUsername}` : label}\n
    \n
    ${account.accountId}
    \n
    \n
    \n
    \n Running\n ${account.running ? \"Yes\" : \"No\"}\n
    \n
    \n Configured\n ${account.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Last inbound\n ${account.lastInboundAt ? formatAgo(account.lastInboundAt) : \"n/a\"}\n
    \n ${account.lastError\n ? html`\n
    \n ${account.lastError}\n
    \n `\n : nothing}\n
    \n
    \n `;\n };\n\n return html`\n
    \n
    Telegram
    \n
    Bot status and channel configuration.
    \n ${accountCountLabel}\n\n ${hasMultipleAccounts\n ? html`\n
    \n ${telegramAccounts.map((account) => renderAccountCard(account))}\n
    \n `\n : html`\n
    \n
    \n Configured\n ${telegram?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${telegram?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Mode\n ${telegram?.mode ?? \"n/a\"}\n
    \n
    \n Last start\n ${telegram?.lastStartAt ? formatAgo(telegram.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${telegram?.lastProbeAt ? formatAgo(telegram.lastProbeAt) : \"n/a\"}\n
    \n
    \n `}\n\n ${telegram?.lastError\n ? html`
    \n ${telegram.lastError}\n
    `\n : nothing}\n\n ${telegram?.probe\n ? html`
    \n Probe ${telegram.probe.ok ? \"ok\" : \"failed\"} ·\n ${telegram.probe.status ?? \"\"} ${telegram.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"telegram\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { WhatsAppStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\nimport { formatDuration } from \"./channels.shared\";\n\nexport function renderWhatsAppCard(params: {\n props: ChannelsProps;\n whatsapp?: WhatsAppStatus;\n accountCountLabel: unknown;\n}) {\n const { props, whatsapp, accountCountLabel } = params;\n\n return html`\n
    \n
    WhatsApp
    \n
    Link WhatsApp Web and monitor connection health.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${whatsapp?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Linked\n ${whatsapp?.linked ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${whatsapp?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Connected\n ${whatsapp?.connected ? \"Yes\" : \"No\"}\n
    \n
    \n Last connect\n \n ${whatsapp?.lastConnectedAt\n ? formatAgo(whatsapp.lastConnectedAt)\n : \"n/a\"}\n \n
    \n
    \n Last message\n \n ${whatsapp?.lastMessageAt ? formatAgo(whatsapp.lastMessageAt) : \"n/a\"}\n \n
    \n
    \n Auth age\n \n ${whatsapp?.authAgeMs != null\n ? formatDuration(whatsapp.authAgeMs)\n : \"n/a\"}\n \n
    \n
    \n\n ${whatsapp?.lastError\n ? html`
    \n ${whatsapp.lastError}\n
    `\n : nothing}\n\n ${props.whatsappMessage\n ? html`
    \n ${props.whatsappMessage}\n
    `\n : nothing}\n\n ${props.whatsappQrDataUrl\n ? html`
    \n \"WhatsApp\n
    `\n : nothing}\n\n
    \n props.onWhatsAppStart(false)}\n >\n ${props.whatsappBusy ? \"Working…\" : \"Show QR\"}\n \n props.onWhatsAppStart(true)}\n >\n Relink\n \n props.onWhatsAppWait()}\n >\n Wait for scan\n \n props.onWhatsAppLogout()}\n >\n Logout\n \n \n
    \n\n ${renderChannelConfigSection({ channelId: \"whatsapp\", props })}\n
    \n `;\n}\n\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type {\n ChannelAccountSnapshot,\n ChannelUiMetaEntry,\n ChannelsStatusSnapshot,\n DiscordStatus,\n IMessageStatus,\n NostrProfile,\n NostrStatus,\n SignalStatus,\n SlackStatus,\n TelegramStatus,\n WhatsAppStatus,\n} from \"../types\";\nimport type {\n ChannelKey,\n ChannelsChannelData,\n ChannelsProps,\n} from \"./channels.types\";\nimport { channelEnabled, renderChannelAccountCount } from \"./channels.shared\";\nimport { renderChannelConfigSection } from \"./channels.config\";\nimport { renderDiscordCard } from \"./channels.discord\";\nimport { renderIMessageCard } from \"./channels.imessage\";\nimport { renderNostrCard } from \"./channels.nostr\";\nimport { renderSignalCard } from \"./channels.signal\";\nimport { renderSlackCard } from \"./channels.slack\";\nimport { renderTelegramCard } from \"./channels.telegram\";\nimport { renderWhatsAppCard } from \"./channels.whatsapp\";\n\nexport function renderChannels(props: ChannelsProps) {\n const channels = props.snapshot?.channels as Record | null;\n const whatsapp = (channels?.whatsapp ?? undefined) as\n | WhatsAppStatus\n | undefined;\n const telegram = (channels?.telegram ?? undefined) as\n | TelegramStatus\n | undefined;\n const discord = (channels?.discord ?? null) as DiscordStatus | null;\n const slack = (channels?.slack ?? null) as SlackStatus | null;\n const signal = (channels?.signal ?? null) as SignalStatus | null;\n const imessage = (channels?.imessage ?? null) as IMessageStatus | null;\n const nostr = (channels?.nostr ?? null) as NostrStatus | null;\n const channelOrder = resolveChannelOrder(props.snapshot);\n const orderedChannels = channelOrder\n .map((key, index) => ({\n key,\n enabled: channelEnabled(key, props),\n order: index,\n }))\n .sort((a, b) => {\n if (a.enabled !== b.enabled) return a.enabled ? -1 : 1;\n return a.order - b.order;\n });\n\n return html`\n
    \n ${orderedChannels.map((channel) =>\n renderChannel(channel.key, props, {\n whatsapp,\n telegram,\n discord,\n slack,\n signal,\n imessage,\n nostr,\n channelAccounts: props.snapshot?.channelAccounts ?? null,\n }),\n )}\n
    \n\n
    \n
    \n
    \n
    Channel health
    \n
    Channel status snapshots from the gateway.
    \n
    \n
    ${props.lastSuccessAt ? formatAgo(props.lastSuccessAt) : \"n/a\"}
    \n
    \n ${props.lastError\n ? html`
    \n ${props.lastError}\n
    `\n : nothing}\n
    \n${props.snapshot ? JSON.stringify(props.snapshot, null, 2) : \"No snapshot yet.\"}\n      
    \n
    \n `;\n}\n\nfunction resolveChannelOrder(snapshot: ChannelsStatusSnapshot | null): ChannelKey[] {\n if (snapshot?.channelMeta?.length) {\n return snapshot.channelMeta.map((entry) => entry.id) as ChannelKey[];\n }\n if (snapshot?.channelOrder?.length) {\n return snapshot.channelOrder;\n }\n return [\"whatsapp\", \"telegram\", \"discord\", \"slack\", \"signal\", \"imessage\", \"nostr\"];\n}\n\nfunction renderChannel(\n key: ChannelKey,\n props: ChannelsProps,\n data: ChannelsChannelData,\n) {\n const accountCountLabel = renderChannelAccountCount(\n key,\n data.channelAccounts,\n );\n switch (key) {\n case \"whatsapp\":\n return renderWhatsAppCard({\n props,\n whatsapp: data.whatsapp,\n accountCountLabel,\n });\n case \"telegram\":\n return renderTelegramCard({\n props,\n telegram: data.telegram,\n telegramAccounts: data.channelAccounts?.telegram ?? [],\n accountCountLabel,\n });\n case \"discord\":\n return renderDiscordCard({\n props,\n discord: data.discord,\n accountCountLabel,\n });\n case \"slack\":\n return renderSlackCard({\n props,\n slack: data.slack,\n accountCountLabel,\n });\n case \"signal\":\n return renderSignalCard({\n props,\n signal: data.signal,\n accountCountLabel,\n });\n case \"imessage\":\n return renderIMessageCard({\n props,\n imessage: data.imessage,\n accountCountLabel,\n });\n case \"nostr\": {\n const nostrAccounts = data.channelAccounts?.nostr ?? [];\n const primaryAccount = nostrAccounts[0];\n const accountId = primaryAccount?.accountId ?? \"default\";\n const profile =\n (primaryAccount as { profile?: NostrProfile | null } | undefined)?.profile ?? null;\n const showForm =\n props.nostrProfileAccountId === accountId ? props.nostrProfileFormState : null;\n const profileFormCallbacks = showForm\n ? {\n onFieldChange: props.onNostrProfileFieldChange,\n onSave: props.onNostrProfileSave,\n onImport: props.onNostrProfileImport,\n onCancel: props.onNostrProfileCancel,\n onToggleAdvanced: props.onNostrProfileToggleAdvanced,\n }\n : null;\n return renderNostrCard({\n props,\n nostr: data.nostr,\n nostrAccounts,\n accountCountLabel,\n profileFormState: showForm,\n profileFormCallbacks,\n onEditProfile: () => props.onNostrProfileEdit(accountId, profile),\n });\n }\n default:\n return renderGenericChannelCard(key, props, data.channelAccounts ?? {});\n }\n}\n\nfunction renderGenericChannelCard(\n key: ChannelKey,\n props: ChannelsProps,\n channelAccounts: Record,\n) {\n const label = resolveChannelLabel(props.snapshot, key);\n const status = props.snapshot?.channels?.[key] as Record | undefined;\n const configured = typeof status?.configured === \"boolean\" ? status.configured : undefined;\n const running = typeof status?.running === \"boolean\" ? status.running : undefined;\n const connected = typeof status?.connected === \"boolean\" ? status.connected : undefined;\n const lastError = typeof status?.lastError === \"string\" ? status.lastError : undefined;\n const accounts = channelAccounts[key] ?? [];\n const accountCountLabel = renderChannelAccountCount(key, channelAccounts);\n\n return html`\n
    \n
    ${label}
    \n
    Channel status and configuration.
    \n ${accountCountLabel}\n\n ${accounts.length > 0\n ? html`\n
    \n ${accounts.map((account) => renderGenericAccount(account))}\n
    \n `\n : html`\n
    \n
    \n Configured\n ${configured == null ? \"n/a\" : configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${running == null ? \"n/a\" : running ? \"Yes\" : \"No\"}\n
    \n
    \n Connected\n ${connected == null ? \"n/a\" : connected ? \"Yes\" : \"No\"}\n
    \n
    \n `}\n\n ${lastError\n ? html`
    \n ${lastError}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: key, props })}\n
    \n `;\n}\n\nfunction resolveChannelMetaMap(\n snapshot: ChannelsStatusSnapshot | null,\n): Record {\n if (!snapshot?.channelMeta?.length) return {};\n return Object.fromEntries(snapshot.channelMeta.map((entry) => [entry.id, entry]));\n}\n\nfunction resolveChannelLabel(\n snapshot: ChannelsStatusSnapshot | null,\n key: string,\n): string {\n const meta = resolveChannelMetaMap(snapshot)[key];\n return meta?.label ?? snapshot?.channelLabels?.[key] ?? key;\n}\n\nconst RECENT_ACTIVITY_THRESHOLD_MS = 10 * 60 * 1000; // 10 minutes\n\nfunction hasRecentActivity(account: ChannelAccountSnapshot): boolean {\n if (!account.lastInboundAt) return false;\n return Date.now() - account.lastInboundAt < RECENT_ACTIVITY_THRESHOLD_MS;\n}\n\nfunction deriveRunningStatus(account: ChannelAccountSnapshot): \"Yes\" | \"No\" | \"Active\" {\n if (account.running) return \"Yes\";\n // If we have recent inbound activity, the channel is effectively running\n if (hasRecentActivity(account)) return \"Active\";\n return \"No\";\n}\n\nfunction deriveConnectedStatus(account: ChannelAccountSnapshot): \"Yes\" | \"No\" | \"Active\" | \"n/a\" {\n if (account.connected === true) return \"Yes\";\n if (account.connected === false) return \"No\";\n // If connected is null/undefined but we have recent activity, show as active\n if (hasRecentActivity(account)) return \"Active\";\n return \"n/a\";\n}\n\nfunction renderGenericAccount(account: ChannelAccountSnapshot) {\n const runningStatus = deriveRunningStatus(account);\n const connectedStatus = deriveConnectedStatus(account);\n\n return html`\n
    \n
    \n
    ${account.name || account.accountId}
    \n
    ${account.accountId}
    \n
    \n
    \n
    \n Running\n ${runningStatus}\n
    \n
    \n Configured\n ${account.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Connected\n ${connectedStatus}\n
    \n
    \n Last inbound\n ${account.lastInboundAt ? formatAgo(account.lastInboundAt) : \"n/a\"}\n
    \n ${account.lastError\n ? html`\n
    \n ${account.lastError}\n
    \n `\n : nothing}\n
    \n
    \n `;\n}\n","import { formatAgo, formatDurationMs, formatMs } from \"./format\";\nimport type { CronJob, GatewaySessionRow, PresenceEntry } from \"./types\";\n\nexport function formatPresenceSummary(entry: PresenceEntry): string {\n const host = entry.host ?? \"unknown\";\n const ip = entry.ip ? `(${entry.ip})` : \"\";\n const mode = entry.mode ?? \"\";\n const version = entry.version ?? \"\";\n return `${host} ${ip} ${mode} ${version}`.trim();\n}\n\nexport function formatPresenceAge(entry: PresenceEntry): string {\n const ts = entry.ts ?? null;\n return ts ? formatAgo(ts) : \"n/a\";\n}\n\nexport function formatNextRun(ms?: number | null) {\n if (!ms) return \"n/a\";\n return `${formatMs(ms)} (${formatAgo(ms)})`;\n}\n\nexport function formatSessionTokens(row: GatewaySessionRow) {\n if (row.totalTokens == null) return \"n/a\";\n const total = row.totalTokens ?? 0;\n const ctx = row.contextTokens ?? 0;\n return ctx ? `${total} / ${ctx}` : String(total);\n}\n\nexport function formatEventPayload(payload: unknown): string {\n if (payload == null) return \"\";\n try {\n return JSON.stringify(payload, null, 2);\n } catch {\n return String(payload);\n }\n}\n\nexport function formatCronState(job: CronJob) {\n const state = job.state ?? {};\n const next = state.nextRunAtMs ? formatMs(state.nextRunAtMs) : \"n/a\";\n const last = state.lastRunAtMs ? formatMs(state.lastRunAtMs) : \"n/a\";\n const status = state.lastStatus ?? \"n/a\";\n return `${status} · next ${next} · last ${last}`;\n}\n\nexport function formatCronSchedule(job: CronJob) {\n const s = job.schedule;\n if (s.kind === \"at\") return `At ${formatMs(s.atMs)}`;\n if (s.kind === \"every\") return `Every ${formatDurationMs(s.everyMs)}`;\n return `Cron ${s.expr}${s.tz ? ` (${s.tz})` : \"\"}`;\n}\n\nexport function formatCronPayload(job: CronJob) {\n const p = job.payload;\n if (p.kind === \"systemEvent\") return `System: ${p.text}`;\n return `Agent: ${p.message}`;\n}\n\n","import { html, nothing } from \"lit\";\n\nimport { formatMs } from \"../format\";\nimport {\n formatCronPayload,\n formatCronSchedule,\n formatCronState,\n formatNextRun,\n} from \"../presenter\";\nimport type { ChannelUiMetaEntry, CronJob, CronRunLogEntry, CronStatus } from \"../types\";\nimport type { CronFormState } from \"../ui-types\";\n\nexport type CronProps = {\n loading: boolean;\n status: CronStatus | null;\n jobs: CronJob[];\n error: string | null;\n busy: boolean;\n form: CronFormState;\n channels: string[];\n channelLabels?: Record;\n channelMeta?: ChannelUiMetaEntry[];\n runsJobId: string | null;\n runs: CronRunLogEntry[];\n onFormChange: (patch: Partial) => void;\n onRefresh: () => void;\n onAdd: () => void;\n onToggle: (job: CronJob, enabled: boolean) => void;\n onRun: (job: CronJob) => void;\n onRemove: (job: CronJob) => void;\n onLoadRuns: (jobId: string) => void;\n};\n\nfunction buildChannelOptions(props: CronProps): string[] {\n const options = [\"last\", ...props.channels.filter(Boolean)];\n const current = props.form.channel?.trim();\n if (current && !options.includes(current)) {\n options.push(current);\n }\n const seen = new Set();\n return options.filter((value) => {\n if (seen.has(value)) return false;\n seen.add(value);\n return true;\n });\n}\n\nfunction resolveChannelLabel(props: CronProps, channel: string): string {\n if (channel === \"last\") return \"last\";\n const meta = props.channelMeta?.find((entry) => entry.id === channel);\n if (meta?.label) return meta.label;\n return props.channelLabels?.[channel] ?? channel;\n}\n\nexport function renderCron(props: CronProps) {\n const channelOptions = buildChannelOptions(props);\n return html`\n
    \n
    \n
    Scheduler
    \n
    Gateway-owned cron scheduler status.
    \n
    \n
    \n
    Enabled
    \n
    \n ${props.status\n ? props.status.enabled\n ? \"Yes\"\n : \"No\"\n : \"n/a\"}\n
    \n
    \n
    \n
    Jobs
    \n
    ${props.status?.jobs ?? \"n/a\"}
    \n
    \n
    \n
    Next wake
    \n
    ${formatNextRun(props.status?.nextWakeAtMs ?? null)}
    \n
    \n
    \n
    \n \n ${props.error ? html`${props.error}` : nothing}\n
    \n
    \n\n
    \n
    New Job
    \n
    Create a scheduled wakeup or agent run.
    \n
    \n \n \n \n \n \n
    \n ${renderScheduleFields(props)}\n
    \n \n \n \n
    \n \n\t ${props.form.payloadKind === \"agentTurn\"\n\t ? html`\n\t
    \n \n\t \n \n \n ${props.form.sessionTarget === \"isolated\"\n ? html`\n \n `\n : nothing}\n
    \n `\n : nothing}\n
    \n \n
    \n
    \n
    \n\n
    \n
    Jobs
    \n
    All scheduled jobs stored in the gateway.
    \n ${props.jobs.length === 0\n ? html`
    No jobs yet.
    `\n : html`\n
    \n ${props.jobs.map((job) => renderJob(job, props))}\n
    \n `}\n
    \n\n
    \n
    Run history
    \n
    Latest runs for ${props.runsJobId ?? \"(select a job)\"}.
    \n ${props.runsJobId == null\n ? html`\n
    \n Select a job to inspect run history.\n
    \n `\n : props.runs.length === 0\n ? html`
    No runs yet.
    `\n : html`\n
    \n ${props.runs.map((entry) => renderRun(entry))}\n
    \n `}\n
    \n `;\n}\n\nfunction renderScheduleFields(props: CronProps) {\n const form = props.form;\n if (form.scheduleKind === \"at\") {\n return html`\n \n `;\n }\n if (form.scheduleKind === \"every\") {\n return html`\n
    \n \n \n
    \n `;\n }\n return html`\n
    \n \n \n
    \n `;\n}\n\nfunction renderJob(job: CronJob, props: CronProps) {\n const isSelected = props.runsJobId === job.id;\n const itemClass = `list-item list-item-clickable${isSelected ? \" list-item-selected\" : \"\"}`;\n return html`\n
    props.onLoadRuns(job.id)}>\n
    \n
    ${job.name}
    \n
    ${formatCronSchedule(job)}
    \n
    ${formatCronPayload(job)}
    \n ${job.agentId ? html`
    Agent: ${job.agentId}
    ` : nothing}\n
    \n ${job.enabled ? \"enabled\" : \"disabled\"}\n ${job.sessionTarget}\n ${job.wakeMode}\n
    \n
    \n
    \n
    ${formatCronState(job)}
    \n
    \n {\n event.stopPropagation();\n props.onToggle(job, !job.enabled);\n }}\n >\n ${job.enabled ? \"Disable\" : \"Enable\"}\n \n {\n event.stopPropagation();\n props.onRun(job);\n }}\n >\n Run\n \n {\n event.stopPropagation();\n props.onLoadRuns(job.id);\n }}\n >\n Runs\n \n {\n event.stopPropagation();\n props.onRemove(job);\n }}\n >\n Remove\n \n
    \n
    \n
    \n `;\n}\n\nfunction renderRun(entry: CronRunLogEntry) {\n return html`\n
    \n
    \n
    ${entry.status}
    \n
    ${entry.summary ?? \"\"}
    \n
    \n
    \n
    ${formatMs(entry.ts)}
    \n
    ${entry.durationMs ?? 0}ms
    \n ${entry.error ? html`
    ${entry.error}
    ` : nothing}\n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatEventPayload } from \"../presenter\";\nimport type { EventLogEntry } from \"../app-events\";\n\nexport type DebugProps = {\n loading: boolean;\n status: Record | null;\n health: Record | null;\n models: unknown[];\n heartbeat: unknown;\n eventLog: EventLogEntry[];\n callMethod: string;\n callParams: string;\n callResult: string | null;\n callError: string | null;\n onCallMethodChange: (next: string) => void;\n onCallParamsChange: (next: string) => void;\n onRefresh: () => void;\n onCall: () => void;\n};\n\nexport function renderDebug(props: DebugProps) {\n return html`\n
    \n
    \n
    \n
    \n
    Snapshots
    \n
    Status, health, and heartbeat data.
    \n
    \n \n
    \n
    \n
    \n
    Status
    \n
    ${JSON.stringify(props.status ?? {}, null, 2)}
    \n
    \n
    \n
    Health
    \n
    ${JSON.stringify(props.health ?? {}, null, 2)}
    \n
    \n
    \n
    Last heartbeat
    \n
    ${JSON.stringify(props.heartbeat ?? {}, null, 2)}
    \n
    \n
    \n
    \n\n
    \n
    Manual RPC
    \n
    Send a raw gateway method with JSON params.
    \n
    \n \n \n
    \n
    \n \n
    \n ${props.callError\n ? html`
    \n ${props.callError}\n
    `\n : nothing}\n ${props.callResult\n ? html`
    ${props.callResult}
    `\n : nothing}\n
    \n
    \n\n
    \n
    Models
    \n
    Catalog from models.list.
    \n
    ${JSON.stringify(\n        props.models ?? [],\n        null,\n        2,\n      )}
    \n
    \n\n
    \n
    Event Log
    \n
    Latest gateway events.
    \n ${props.eventLog.length === 0\n ? html`
    No events yet.
    `\n : html`\n
    \n ${props.eventLog.map(\n (evt) => html`\n
    \n
    \n
    ${evt.event}
    \n
    ${new Date(evt.ts).toLocaleTimeString()}
    \n
    \n
    \n
    ${formatEventPayload(evt.payload)}
    \n
    \n
    \n `,\n )}\n
    \n `}\n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatPresenceAge, formatPresenceSummary } from \"../presenter\";\nimport type { PresenceEntry } from \"../types\";\n\nexport type InstancesProps = {\n loading: boolean;\n entries: PresenceEntry[];\n lastError: string | null;\n statusMessage: string | null;\n onRefresh: () => void;\n};\n\nexport function renderInstances(props: InstancesProps) {\n return html`\n
    \n
    \n
    \n
    Connected Instances
    \n
    Presence beacons from the gateway and clients.
    \n
    \n \n
    \n ${props.lastError\n ? html`
    \n ${props.lastError}\n
    `\n : nothing}\n ${props.statusMessage\n ? html`
    \n ${props.statusMessage}\n
    `\n : nothing}\n
    \n ${props.entries.length === 0\n ? html`
    No instances reported yet.
    `\n : props.entries.map((entry) => renderEntry(entry))}\n
    \n
    \n `;\n}\n\nfunction renderEntry(entry: PresenceEntry) {\n const lastInput =\n entry.lastInputSeconds != null\n ? `${entry.lastInputSeconds}s ago`\n : \"n/a\";\n const mode = entry.mode ?? \"unknown\";\n const roles = Array.isArray(entry.roles) ? entry.roles.filter(Boolean) : [];\n const scopes = Array.isArray(entry.scopes) ? entry.scopes.filter(Boolean) : [];\n const scopesLabel =\n scopes.length > 0\n ? scopes.length > 3\n ? `${scopes.length} scopes`\n : `scopes: ${scopes.join(\", \")}`\n : null;\n return html`\n
    \n
    \n
    ${entry.host ?? \"unknown host\"}
    \n
    ${formatPresenceSummary(entry)}
    \n
    \n ${mode}\n ${roles.map((role) => html`${role}`)}\n ${scopesLabel ? html`${scopesLabel}` : nothing}\n ${entry.platform ? html`${entry.platform}` : nothing}\n ${entry.deviceFamily\n ? html`${entry.deviceFamily}`\n : nothing}\n ${entry.modelIdentifier\n ? html`${entry.modelIdentifier}`\n : nothing}\n ${entry.version ? html`${entry.version}` : nothing}\n
    \n
    \n
    \n
    ${formatPresenceAge(entry)}
    \n
    Last input ${lastInput}
    \n
    Reason ${entry.reason ?? \"\"}
    \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { LogEntry, LogLevel } from \"../types\";\n\nconst LEVELS: LogLevel[] = [\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"];\n\nexport type LogsProps = {\n loading: boolean;\n error: string | null;\n file: string | null;\n entries: LogEntry[];\n filterText: string;\n levelFilters: Record;\n autoFollow: boolean;\n truncated: boolean;\n onFilterTextChange: (next: string) => void;\n onLevelToggle: (level: LogLevel, enabled: boolean) => void;\n onToggleAutoFollow: (next: boolean) => void;\n onRefresh: () => void;\n onExport: (lines: string[], label: string) => void;\n onScroll: (event: Event) => void;\n};\n\nfunction formatTime(value?: string | null) {\n if (!value) return \"\";\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return value;\n return date.toLocaleTimeString();\n}\n\nfunction matchesFilter(entry: LogEntry, needle: string) {\n if (!needle) return true;\n const haystack = [entry.message, entry.subsystem, entry.raw]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n return haystack.includes(needle);\n}\n\nexport function renderLogs(props: LogsProps) {\n const needle = props.filterText.trim().toLowerCase();\n const levelFiltered = LEVELS.some((level) => !props.levelFilters[level]);\n const filtered = props.entries.filter((entry) => {\n if (entry.level && !props.levelFilters[entry.level]) return false;\n return matchesFilter(entry, needle);\n });\n const exportLabel = needle || levelFiltered ? \"filtered\" : \"visible\";\n\n return html`\n
    \n
    \n
    \n
    Logs
    \n
    Gateway file logs (JSONL).
    \n
    \n
    \n \n props.onExport(filtered.map((entry) => entry.raw), exportLabel)}\n >\n Export ${exportLabel}\n \n
    \n
    \n\n
    \n \n \n
    \n\n
    \n ${LEVELS.map(\n (level) => html`\n \n `,\n )}\n
    \n\n ${props.file\n ? html`
    File: ${props.file}
    `\n : nothing}\n ${props.truncated\n ? html`
    \n Log output truncated; showing latest chunk.\n
    `\n : nothing}\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n
    \n ${filtered.length === 0\n ? html`
    No log entries.
    `\n : filtered.map(\n (entry) => html`\n
    \n
    ${formatTime(entry.time)}
    \n
    ${entry.level ?? \"\"}
    \n
    ${entry.subsystem ?? \"\"}
    \n
    ${entry.message ?? entry.raw}
    \n
    \n `,\n )}\n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { clampText, formatAgo, formatList } from \"../format\";\nimport type {\n ExecApprovalsAllowlistEntry,\n ExecApprovalsFile,\n ExecApprovalsSnapshot,\n} from \"../controllers/exec-approvals\";\nimport type {\n DevicePairingList,\n DeviceTokenSummary,\n PairedDevice,\n PendingDevice,\n} from \"../controllers/devices\";\n\nexport type NodesProps = {\n loading: boolean;\n nodes: Array>;\n devicesLoading: boolean;\n devicesError: string | null;\n devicesList: DevicePairingList | null;\n configForm: Record | null;\n configLoading: boolean;\n configSaving: boolean;\n configDirty: boolean;\n configFormMode: \"form\" | \"raw\";\n execApprovalsLoading: boolean;\n execApprovalsSaving: boolean;\n execApprovalsDirty: boolean;\n execApprovalsSnapshot: ExecApprovalsSnapshot | null;\n execApprovalsForm: ExecApprovalsFile | null;\n execApprovalsSelectedAgent: string | null;\n execApprovalsTarget: \"gateway\" | \"node\";\n execApprovalsTargetNodeId: string | null;\n onRefresh: () => void;\n onDevicesRefresh: () => void;\n onDeviceApprove: (requestId: string) => void;\n onDeviceReject: (requestId: string) => void;\n onDeviceRotate: (deviceId: string, role: string, scopes?: string[]) => void;\n onDeviceRevoke: (deviceId: string, role: string) => void;\n onLoadConfig: () => void;\n onLoadExecApprovals: () => void;\n onBindDefault: (nodeId: string | null) => void;\n onBindAgent: (agentIndex: number, nodeId: string | null) => void;\n onSaveBindings: () => void;\n onExecApprovalsTargetChange: (kind: \"gateway\" | \"node\", nodeId: string | null) => void;\n onExecApprovalsSelectAgent: (agentId: string) => void;\n onExecApprovalsPatch: (path: Array, value: unknown) => void;\n onExecApprovalsRemove: (path: Array) => void;\n onSaveExecApprovals: () => void;\n};\n\nexport function renderNodes(props: NodesProps) {\n const bindingState = resolveBindingsState(props);\n const approvalsState = resolveExecApprovalsState(props);\n return html`\n ${renderExecApprovals(approvalsState)}\n ${renderBindings(bindingState)}\n ${renderDevices(props)}\n
    \n
    \n
    \n
    Nodes
    \n
    Paired devices and live links.
    \n
    \n \n
    \n
    \n ${props.nodes.length === 0\n ? html`
    No nodes found.
    `\n : props.nodes.map((n) => renderNode(n))}\n
    \n
    \n `;\n}\n\nfunction renderDevices(props: NodesProps) {\n const list = props.devicesList ?? { pending: [], paired: [] };\n const pending = Array.isArray(list.pending) ? list.pending : [];\n const paired = Array.isArray(list.paired) ? list.paired : [];\n return html`\n
    \n
    \n
    \n
    Devices
    \n
    Pairing requests + role tokens.
    \n
    \n \n
    \n ${props.devicesError\n ? html`
    ${props.devicesError}
    `\n : nothing}\n
    \n ${pending.length > 0\n ? html`\n
    Pending
    \n ${pending.map((req) => renderPendingDevice(req, props))}\n `\n : nothing}\n ${paired.length > 0\n ? html`\n
    Paired
    \n ${paired.map((device) => renderPairedDevice(device, props))}\n `\n : nothing}\n ${pending.length === 0 && paired.length === 0\n ? html`
    No paired devices.
    `\n : nothing}\n
    \n
    \n `;\n}\n\nfunction renderPendingDevice(req: PendingDevice, props: NodesProps) {\n const name = req.displayName?.trim() || req.deviceId;\n const age = typeof req.ts === \"number\" ? formatAgo(req.ts) : \"n/a\";\n const role = req.role?.trim() ? `role: ${req.role}` : \"role: -\";\n const repair = req.isRepair ? \" · repair\" : \"\";\n const ip = req.remoteIp ? ` · ${req.remoteIp}` : \"\";\n return html`\n
    \n
    \n
    ${name}
    \n
    ${req.deviceId}${ip}
    \n
    \n ${role} · requested ${age}${repair}\n
    \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n `;\n}\n\nfunction renderPairedDevice(device: PairedDevice, props: NodesProps) {\n const name = device.displayName?.trim() || device.deviceId;\n const ip = device.remoteIp ? ` · ${device.remoteIp}` : \"\";\n const roles = `roles: ${formatList(device.roles)}`;\n const scopes = `scopes: ${formatList(device.scopes)}`;\n const tokens = Array.isArray(device.tokens) ? device.tokens : [];\n return html`\n
    \n
    \n
    ${name}
    \n
    ${device.deviceId}${ip}
    \n
    ${roles} · ${scopes}
    \n ${tokens.length === 0\n ? html`
    Tokens: none
    `\n : html`\n
    Tokens
    \n
    \n ${tokens.map((token) => renderTokenRow(device.deviceId, token, props))}\n
    \n `}\n
    \n
    \n `;\n}\n\nfunction renderTokenRow(deviceId: string, token: DeviceTokenSummary, props: NodesProps) {\n const status = token.revokedAtMs ? \"revoked\" : \"active\";\n const scopes = `scopes: ${formatList(token.scopes)}`;\n const when = formatAgo(token.rotatedAtMs ?? token.createdAtMs ?? token.lastUsedAtMs ?? null);\n return html`\n
    \n
    ${token.role} · ${status} · ${scopes} · ${when}
    \n
    \n props.onDeviceRotate(deviceId, token.role, token.scopes)}\n >\n Rotate\n \n ${token.revokedAtMs\n ? nothing\n : html`\n props.onDeviceRevoke(deviceId, token.role)}\n >\n Revoke\n \n `}\n
    \n
    \n `;\n}\n\ntype BindingAgent = {\n id: string;\n name?: string;\n index: number;\n isDefault: boolean;\n binding?: string | null;\n};\n\ntype BindingNode = {\n id: string;\n label: string;\n};\n\ntype BindingState = {\n ready: boolean;\n disabled: boolean;\n configDirty: boolean;\n configLoading: boolean;\n configSaving: boolean;\n defaultBinding?: string | null;\n agents: BindingAgent[];\n nodes: BindingNode[];\n onBindDefault: (nodeId: string | null) => void;\n onBindAgent: (agentIndex: number, nodeId: string | null) => void;\n onSave: () => void;\n onLoadConfig: () => void;\n formMode: \"form\" | \"raw\";\n};\n\ntype ExecSecurity = \"deny\" | \"allowlist\" | \"full\";\ntype ExecAsk = \"off\" | \"on-miss\" | \"always\";\n\ntype ExecApprovalsResolvedDefaults = {\n security: ExecSecurity;\n ask: ExecAsk;\n askFallback: ExecSecurity;\n autoAllowSkills: boolean;\n};\n\ntype ExecApprovalsAgentOption = {\n id: string;\n name?: string;\n isDefault?: boolean;\n};\n\ntype ExecApprovalsTargetNode = {\n id: string;\n label: string;\n};\n\ntype ExecApprovalsState = {\n ready: boolean;\n disabled: boolean;\n dirty: boolean;\n loading: boolean;\n saving: boolean;\n form: ExecApprovalsFile | null;\n defaults: ExecApprovalsResolvedDefaults;\n selectedScope: string;\n selectedAgent: Record | null;\n agents: ExecApprovalsAgentOption[];\n allowlist: ExecApprovalsAllowlistEntry[];\n target: \"gateway\" | \"node\";\n targetNodeId: string | null;\n targetNodes: ExecApprovalsTargetNode[];\n onSelectScope: (agentId: string) => void;\n onSelectTarget: (kind: \"gateway\" | \"node\", nodeId: string | null) => void;\n onPatch: (path: Array, value: unknown) => void;\n onRemove: (path: Array) => void;\n onLoad: () => void;\n onSave: () => void;\n};\n\nconst EXEC_APPROVALS_DEFAULT_SCOPE = \"__defaults__\";\n\nconst SECURITY_OPTIONS: Array<{ value: ExecSecurity; label: string }> = [\n { value: \"deny\", label: \"Deny\" },\n { value: \"allowlist\", label: \"Allowlist\" },\n { value: \"full\", label: \"Full\" },\n];\n\nconst ASK_OPTIONS: Array<{ value: ExecAsk; label: string }> = [\n { value: \"off\", label: \"Off\" },\n { value: \"on-miss\", label: \"On miss\" },\n { value: \"always\", label: \"Always\" },\n];\n\nfunction resolveBindingsState(props: NodesProps): BindingState {\n const config = props.configForm;\n const nodes = resolveExecNodes(props.nodes);\n const { defaultBinding, agents } = resolveAgentBindings(config);\n const ready = Boolean(config);\n const disabled = props.configSaving || props.configFormMode === \"raw\";\n return {\n ready,\n disabled,\n configDirty: props.configDirty,\n configLoading: props.configLoading,\n configSaving: props.configSaving,\n defaultBinding,\n agents,\n nodes,\n onBindDefault: props.onBindDefault,\n onBindAgent: props.onBindAgent,\n onSave: props.onSaveBindings,\n onLoadConfig: props.onLoadConfig,\n formMode: props.configFormMode,\n };\n}\n\nfunction normalizeSecurity(value?: string): ExecSecurity {\n if (value === \"allowlist\" || value === \"full\" || value === \"deny\") return value;\n return \"deny\";\n}\n\nfunction normalizeAsk(value?: string): ExecAsk {\n if (value === \"always\" || value === \"off\" || value === \"on-miss\") return value;\n return \"on-miss\";\n}\n\nfunction resolveExecApprovalsDefaults(\n form: ExecApprovalsFile | null,\n): ExecApprovalsResolvedDefaults {\n const defaults = form?.defaults ?? {};\n return {\n security: normalizeSecurity(defaults.security),\n ask: normalizeAsk(defaults.ask),\n askFallback: normalizeSecurity(defaults.askFallback ?? \"deny\"),\n autoAllowSkills: Boolean(defaults.autoAllowSkills ?? false),\n };\n}\n\nfunction resolveConfigAgents(config: Record | null): ExecApprovalsAgentOption[] {\n const agentsNode = (config?.agents ?? {}) as Record;\n const list = Array.isArray(agentsNode.list) ? agentsNode.list : [];\n const agents: ExecApprovalsAgentOption[] = [];\n list.forEach((entry) => {\n if (!entry || typeof entry !== \"object\") return;\n const record = entry as Record;\n const id = typeof record.id === \"string\" ? record.id.trim() : \"\";\n if (!id) return;\n const name = typeof record.name === \"string\" ? record.name.trim() : undefined;\n const isDefault = record.default === true;\n agents.push({ id, name: name || undefined, isDefault });\n });\n return agents;\n}\n\nfunction resolveExecApprovalsAgents(\n config: Record | null,\n form: ExecApprovalsFile | null,\n): ExecApprovalsAgentOption[] {\n const configAgents = resolveConfigAgents(config);\n const approvalsAgents = Object.keys(form?.agents ?? {});\n const merged = new Map();\n configAgents.forEach((agent) => merged.set(agent.id, agent));\n approvalsAgents.forEach((id) => {\n if (merged.has(id)) return;\n merged.set(id, { id });\n });\n const agents = Array.from(merged.values());\n if (agents.length === 0) {\n agents.push({ id: \"main\", isDefault: true });\n }\n agents.sort((a, b) => {\n if (a.isDefault && !b.isDefault) return -1;\n if (!a.isDefault && b.isDefault) return 1;\n const aLabel = a.name?.trim() ? a.name : a.id;\n const bLabel = b.name?.trim() ? b.name : b.id;\n return aLabel.localeCompare(bLabel);\n });\n return agents;\n}\n\nfunction resolveExecApprovalsScope(\n selected: string | null,\n agents: ExecApprovalsAgentOption[],\n): string {\n if (selected === EXEC_APPROVALS_DEFAULT_SCOPE) return EXEC_APPROVALS_DEFAULT_SCOPE;\n if (selected && agents.some((agent) => agent.id === selected)) return selected;\n return EXEC_APPROVALS_DEFAULT_SCOPE;\n}\n\nfunction resolveExecApprovalsState(props: NodesProps): ExecApprovalsState {\n const form = props.execApprovalsForm ?? props.execApprovalsSnapshot?.file ?? null;\n const ready = Boolean(form);\n const defaults = resolveExecApprovalsDefaults(form);\n const agents = resolveExecApprovalsAgents(props.configForm, form);\n const targetNodes = resolveExecApprovalsNodes(props.nodes);\n const target = props.execApprovalsTarget;\n let targetNodeId =\n target === \"node\" && props.execApprovalsTargetNodeId\n ? props.execApprovalsTargetNodeId\n : null;\n if (target === \"node\" && targetNodeId && !targetNodes.some((node) => node.id === targetNodeId)) {\n targetNodeId = null;\n }\n const selectedScope = resolveExecApprovalsScope(props.execApprovalsSelectedAgent, agents);\n const selectedAgent =\n selectedScope !== EXEC_APPROVALS_DEFAULT_SCOPE\n ? ((form?.agents ?? {})[selectedScope] as Record | undefined) ??\n null\n : null;\n const allowlist = Array.isArray((selectedAgent as { allowlist?: unknown })?.allowlist)\n ? ((selectedAgent as { allowlist?: ExecApprovalsAllowlistEntry[] }).allowlist ??\n [])\n : [];\n return {\n ready,\n disabled: props.execApprovalsSaving || props.execApprovalsLoading,\n dirty: props.execApprovalsDirty,\n loading: props.execApprovalsLoading,\n saving: props.execApprovalsSaving,\n form,\n defaults,\n selectedScope,\n selectedAgent,\n agents,\n allowlist,\n target,\n targetNodeId,\n targetNodes,\n onSelectScope: props.onExecApprovalsSelectAgent,\n onSelectTarget: props.onExecApprovalsTargetChange,\n onPatch: props.onExecApprovalsPatch,\n onRemove: props.onExecApprovalsRemove,\n onLoad: props.onLoadExecApprovals,\n onSave: props.onSaveExecApprovals,\n };\n}\n\nfunction renderBindings(state: BindingState) {\n const supportsBinding = state.nodes.length > 0;\n const defaultValue = state.defaultBinding ?? \"\";\n return html`\n
    \n
    \n
    \n
    Exec node binding
    \n
    \n Pin agents to a specific node when using exec host=node.\n
    \n
    \n \n ${state.configSaving ? \"Saving…\" : \"Save\"}\n \n
    \n\n ${state.formMode === \"raw\"\n ? html`
    \n Switch the Config tab to Form mode to edit bindings here.\n
    `\n : nothing}\n\n ${!state.ready\n ? html`
    \n
    Load config to edit bindings.
    \n \n
    `\n : html`\n
    \n
    \n
    \n
    Default binding
    \n
    Used when agents do not override a node binding.
    \n
    \n
    \n \n ${!supportsBinding\n ? html`
    No nodes with system.run available.
    `\n : nothing}\n
    \n
    \n\n ${state.agents.length === 0\n ? html`
    No agents found.
    `\n : state.agents.map((agent) =>\n renderAgentBinding(agent, state),\n )}\n
    \n `}\n
    \n `;\n}\n\nfunction renderExecApprovals(state: ExecApprovalsState) {\n const ready = state.ready;\n const targetReady = state.target !== \"node\" || Boolean(state.targetNodeId);\n return html`\n
    \n
    \n
    \n
    Exec approvals
    \n
    \n Allowlist and approval policy for exec host=gateway/node.\n
    \n
    \n \n ${state.saving ? \"Saving…\" : \"Save\"}\n \n
    \n\n ${renderExecApprovalsTarget(state)}\n\n ${!ready\n ? html`
    \n
    Load exec approvals to edit allowlists.
    \n \n
    `\n : html`\n ${renderExecApprovalsTabs(state)}\n ${renderExecApprovalsPolicy(state)}\n ${state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE\n ? nothing\n : renderExecApprovalsAllowlist(state)}\n `}\n
    \n `;\n}\n\nfunction renderExecApprovalsTarget(state: ExecApprovalsState) {\n const hasNodes = state.targetNodes.length > 0;\n const nodeValue = state.targetNodeId ?? \"\";\n return html`\n
    \n
    \n
    \n
    Target
    \n
    \n Gateway edits local approvals; node edits the selected node.\n
    \n
    \n
    \n \n ${state.target === \"node\"\n ? html`\n \n `\n : nothing}\n
    \n
    \n ${state.target === \"node\" && !hasNodes\n ? html`
    No nodes advertise exec approvals yet.
    `\n : nothing}\n
    \n `;\n}\n\nfunction renderExecApprovalsTabs(state: ExecApprovalsState) {\n return html`\n
    \n Scope\n
    \n state.onSelectScope(EXEC_APPROVALS_DEFAULT_SCOPE)}\n >\n Defaults\n \n ${state.agents.map((agent) => {\n const label = agent.name?.trim() ? `${agent.name} (${agent.id})` : agent.id;\n return html`\n state.onSelectScope(agent.id)}\n >\n ${label}\n \n `;\n })}\n
    \n
    \n `;\n}\n\nfunction renderExecApprovalsPolicy(state: ExecApprovalsState) {\n const isDefaults = state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE;\n const defaults = state.defaults;\n const agent = state.selectedAgent ?? {};\n const basePath = isDefaults ? [\"defaults\"] : [\"agents\", state.selectedScope];\n const agentSecurity = typeof agent.security === \"string\" ? agent.security : undefined;\n const agentAsk = typeof agent.ask === \"string\" ? agent.ask : undefined;\n const agentAskFallback =\n typeof agent.askFallback === \"string\" ? agent.askFallback : undefined;\n const securityValue = isDefaults ? defaults.security : agentSecurity ?? \"__default__\";\n const askValue = isDefaults ? defaults.ask : agentAsk ?? \"__default__\";\n const askFallbackValue = isDefaults\n ? defaults.askFallback\n : agentAskFallback ?? \"__default__\";\n const autoOverride =\n typeof agent.autoAllowSkills === \"boolean\" ? agent.autoAllowSkills : undefined;\n const autoEffective = autoOverride ?? defaults.autoAllowSkills;\n const autoIsDefault = autoOverride == null;\n\n return html`\n
    \n
    \n
    \n
    Security
    \n
    \n ${isDefaults\n ? \"Default security mode.\"\n : `Default: ${defaults.security}.`}\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    Ask
    \n
    \n ${isDefaults ? \"Default prompt policy.\" : `Default: ${defaults.ask}.`}\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    Ask fallback
    \n
    \n ${isDefaults\n ? \"Applied when the UI prompt is unavailable.\"\n : `Default: ${defaults.askFallback}.`}\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    Auto-allow skill CLIs
    \n
    \n ${isDefaults\n ? \"Allow skill executables listed by the Gateway.\"\n : autoIsDefault\n ? `Using default (${defaults.autoAllowSkills ? \"on\" : \"off\"}).`\n : `Override (${autoEffective ? \"on\" : \"off\"}).`}\n
    \n
    \n
    \n \n ${!isDefaults && !autoIsDefault\n ? html` state.onRemove([...basePath, \"autoAllowSkills\"])}\n >\n Use default\n `\n : nothing}\n
    \n
    \n
    \n `;\n}\n\nfunction renderExecApprovalsAllowlist(state: ExecApprovalsState) {\n const allowlistPath = [\"agents\", state.selectedScope, \"allowlist\"];\n const entries = state.allowlist;\n return html`\n
    \n
    \n
    Allowlist
    \n
    Case-insensitive glob patterns.
    \n
    \n {\n const next = [...entries, { pattern: \"\" }];\n state.onPatch(allowlistPath, next);\n }}\n >\n Add pattern\n \n
    \n
    \n ${entries.length === 0\n ? html`
    No allowlist entries yet.
    `\n : entries.map((entry, index) =>\n renderAllowlistEntry(state, entry, index),\n )}\n
    \n `;\n}\n\nfunction renderAllowlistEntry(\n state: ExecApprovalsState,\n entry: ExecApprovalsAllowlistEntry,\n index: number,\n) {\n const lastUsed = entry.lastUsedAt ? formatAgo(entry.lastUsedAt) : \"never\";\n const lastCommand = entry.lastUsedCommand\n ? clampText(entry.lastUsedCommand, 120)\n : null;\n const lastPath = entry.lastResolvedPath\n ? clampText(entry.lastResolvedPath, 120)\n : null;\n return html`\n
    \n
    \n
    ${entry.pattern?.trim() ? entry.pattern : \"New pattern\"}
    \n
    Last used: ${lastUsed}
    \n ${lastCommand ? html`
    ${lastCommand}
    ` : nothing}\n ${lastPath ? html`
    ${lastPath}
    ` : nothing}\n
    \n
    \n \n {\n if (state.allowlist.length <= 1) {\n state.onRemove([\"agents\", state.selectedScope, \"allowlist\"]);\n return;\n }\n state.onRemove([\"agents\", state.selectedScope, \"allowlist\", index]);\n }}\n >\n Remove\n \n
    \n
    \n `;\n}\n\nfunction renderAgentBinding(agent: BindingAgent, state: BindingState) {\n const bindingValue = agent.binding ?? \"__default__\";\n const label = agent.name?.trim() ? `${agent.name} (${agent.id})` : agent.id;\n const supportsBinding = state.nodes.length > 0;\n return html`\n
    \n
    \n
    ${label}
    \n
    \n ${agent.isDefault ? \"default agent\" : \"agent\"} ·\n ${bindingValue === \"__default__\"\n ? `uses default (${state.defaultBinding ?? \"any\"})`\n : `override: ${agent.binding}`}\n
    \n
    \n
    \n \n
    \n
    \n `;\n}\n\nfunction resolveExecNodes(nodes: Array>): BindingNode[] {\n const list: BindingNode[] = [];\n for (const node of nodes) {\n const commands = Array.isArray(node.commands) ? node.commands : [];\n const supports = commands.some((cmd) => String(cmd) === \"system.run\");\n if (!supports) continue;\n const nodeId = typeof node.nodeId === \"string\" ? node.nodeId.trim() : \"\";\n if (!nodeId) continue;\n const displayName =\n typeof node.displayName === \"string\" && node.displayName.trim()\n ? node.displayName.trim()\n : nodeId;\n list.push({ id: nodeId, label: displayName === nodeId ? nodeId : `${displayName} · ${nodeId}` });\n }\n list.sort((a, b) => a.label.localeCompare(b.label));\n return list;\n}\n\nfunction resolveExecApprovalsNodes(nodes: Array>): ExecApprovalsTargetNode[] {\n const list: ExecApprovalsTargetNode[] = [];\n for (const node of nodes) {\n const commands = Array.isArray(node.commands) ? node.commands : [];\n const supports = commands.some(\n (cmd) => String(cmd) === \"system.execApprovals.get\" || String(cmd) === \"system.execApprovals.set\",\n );\n if (!supports) continue;\n const nodeId = typeof node.nodeId === \"string\" ? node.nodeId.trim() : \"\";\n if (!nodeId) continue;\n const displayName =\n typeof node.displayName === \"string\" && node.displayName.trim()\n ? node.displayName.trim()\n : nodeId;\n list.push({ id: nodeId, label: displayName === nodeId ? nodeId : `${displayName} · ${nodeId}` });\n }\n list.sort((a, b) => a.label.localeCompare(b.label));\n return list;\n}\n\nfunction resolveAgentBindings(config: Record | null): {\n defaultBinding?: string | null;\n agents: BindingAgent[];\n} {\n const fallbackAgent: BindingAgent = {\n id: \"main\",\n name: undefined,\n index: 0,\n isDefault: true,\n binding: null,\n };\n if (!config || typeof config !== \"object\") {\n return { defaultBinding: null, agents: [fallbackAgent] };\n }\n const tools = (config.tools ?? {}) as Record;\n const exec = (tools.exec ?? {}) as Record;\n const defaultBinding =\n typeof exec.node === \"string\" && exec.node.trim() ? exec.node.trim() : null;\n\n const agentsNode = (config.agents ?? {}) as Record;\n const list = Array.isArray(agentsNode.list) ? agentsNode.list : [];\n if (list.length === 0) {\n return { defaultBinding, agents: [fallbackAgent] };\n }\n\n const agents: BindingAgent[] = [];\n list.forEach((entry, index) => {\n if (!entry || typeof entry !== \"object\") return;\n const record = entry as Record;\n const id = typeof record.id === \"string\" ? record.id.trim() : \"\";\n if (!id) return;\n const name = typeof record.name === \"string\" ? record.name.trim() : undefined;\n const isDefault = record.default === true;\n const toolsEntry = (record.tools ?? {}) as Record;\n const execEntry = (toolsEntry.exec ?? {}) as Record;\n const binding =\n typeof execEntry.node === \"string\" && execEntry.node.trim()\n ? execEntry.node.trim()\n : null;\n agents.push({\n id,\n name: name || undefined,\n index,\n isDefault,\n binding,\n });\n });\n\n if (agents.length === 0) {\n agents.push(fallbackAgent);\n }\n\n return { defaultBinding, agents };\n}\n\nfunction renderNode(node: Record) {\n const connected = Boolean(node.connected);\n const paired = Boolean(node.paired);\n const title =\n (typeof node.displayName === \"string\" && node.displayName.trim()) ||\n (typeof node.nodeId === \"string\" ? node.nodeId : \"unknown\");\n const caps = Array.isArray(node.caps) ? (node.caps as unknown[]) : [];\n const commands = Array.isArray(node.commands) ? (node.commands as unknown[]) : [];\n return html`\n
    \n
    \n
    ${title}
    \n
    \n ${typeof node.nodeId === \"string\" ? node.nodeId : \"\"}\n ${typeof node.remoteIp === \"string\" ? ` · ${node.remoteIp}` : \"\"}\n ${typeof node.version === \"string\" ? ` · ${node.version}` : \"\"}\n
    \n
    \n ${paired ? \"paired\" : \"unpaired\"}\n \n ${connected ? \"connected\" : \"offline\"}\n \n ${caps.slice(0, 12).map((c) => html`${String(c)}`)}\n ${commands\n .slice(0, 8)\n .map((c) => html`${String(c)}`)}\n
    \n
    \n
    \n `;\n}\n","import { html } from \"lit\";\n\nimport type { GatewayHelloOk } from \"../gateway\";\nimport { formatAgo, formatDurationMs } from \"../format\";\nimport { formatNextRun } from \"../presenter\";\nimport type { UiSettings } from \"../storage\";\n\nexport type OverviewProps = {\n connected: boolean;\n hello: GatewayHelloOk | null;\n settings: UiSettings;\n password: string;\n lastError: string | null;\n presenceCount: number;\n sessionsCount: number | null;\n cronEnabled: boolean | null;\n cronNext: number | null;\n lastChannelsRefresh: number | null;\n onSettingsChange: (next: UiSettings) => void;\n onPasswordChange: (next: string) => void;\n onSessionKeyChange: (next: string) => void;\n onConnect: () => void;\n onRefresh: () => void;\n};\n\nexport function renderOverview(props: OverviewProps) {\n const snapshot = props.hello?.snapshot as\n | { uptimeMs?: number; policy?: { tickIntervalMs?: number } }\n | undefined;\n const uptime = snapshot?.uptimeMs ? formatDurationMs(snapshot.uptimeMs) : \"n/a\";\n const tick = snapshot?.policy?.tickIntervalMs\n ? `${snapshot.policy.tickIntervalMs}ms`\n : \"n/a\";\n const authHint = (() => {\n if (props.connected || !props.lastError) return null;\n const lower = props.lastError.toLowerCase();\n const authFailed = lower.includes(\"unauthorized\") || lower.includes(\"connect failed\");\n if (!authFailed) return null;\n const hasToken = Boolean(props.settings.token.trim());\n const hasPassword = Boolean(props.password.trim());\n if (!hasToken && !hasPassword) {\n return html`\n
    \n This gateway requires auth. Add a token or password, then click Connect.\n
    \n clawdbot dashboard --no-open → tokenized URL
    \n clawdbot doctor --generate-gateway-token → set token\n
    \n
    \n Docs: Control UI auth\n
    \n
    \n `;\n }\n return html`\n
    \n Auth failed. Re-copy a tokenized URL with\n clawdbot dashboard --no-open, or update the token,\n then click Connect.\n
    \n Docs: Control UI auth\n
    \n
    \n `;\n })();\n const insecureContextHint = (() => {\n if (props.connected || !props.lastError) return null;\n const isSecureContext = typeof window !== \"undefined\" ? window.isSecureContext : true;\n if (isSecureContext !== false) return null;\n const lower = props.lastError.toLowerCase();\n if (!lower.includes(\"secure context\") && !lower.includes(\"device identity required\")) {\n return null;\n }\n return html`\n
    \n This page is HTTP, so the browser blocks device identity. Use HTTPS (Tailscale Serve) or\n open http://127.0.0.1:18789 on the gateway host.\n
    \n If you must stay on HTTP, set\n gateway.controlUi.allowInsecureAuth: true (token-only).\n
    \n
    \n Docs: Tailscale Serve\n · \n Docs: Insecure HTTP\n
    \n
    \n `;\n })();\n\n return html`\n
    \n
    \n
    Gateway Access
    \n
    Where the dashboard connects and how it authenticates.
    \n
    \n \n \n \n \n
    \n
    \n \n \n Click Connect to apply connection changes.\n
    \n
    \n\n
    \n
    Snapshot
    \n
    Latest gateway handshake information.
    \n
    \n
    \n
    Status
    \n
    \n ${props.connected ? \"Connected\" : \"Disconnected\"}\n
    \n
    \n
    \n
    Uptime
    \n
    ${uptime}
    \n
    \n
    \n
    Tick Interval
    \n
    ${tick}
    \n
    \n
    \n
    Last Channels Refresh
    \n
    \n ${props.lastChannelsRefresh\n ? formatAgo(props.lastChannelsRefresh)\n : \"n/a\"}\n
    \n
    \n
    \n ${props.lastError\n ? html`
    \n
    ${props.lastError}
    \n ${authHint ?? \"\"}\n ${insecureContextHint ?? \"\"}\n
    `\n : html`
    \n Use Channels to link WhatsApp, Telegram, Discord, Signal, or iMessage.\n
    `}\n
    \n
    \n\n
    \n
    \n
    Instances
    \n
    ${props.presenceCount}
    \n
    Presence beacons in the last 5 minutes.
    \n
    \n
    \n
    Sessions
    \n
    ${props.sessionsCount ?? \"n/a\"}
    \n
    Recent session keys tracked by the gateway.
    \n
    \n
    \n
    Cron
    \n
    \n ${props.cronEnabled == null\n ? \"n/a\"\n : props.cronEnabled\n ? \"Enabled\"\n : \"Disabled\"}\n
    \n
    Next wake ${formatNextRun(props.cronNext)}
    \n
    \n
    \n\n
    \n
    Notes
    \n
    Quick reminders for remote control setups.
    \n
    \n
    \n
    Tailscale serve
    \n
    \n Prefer serve mode to keep the gateway on loopback with tailnet auth.\n
    \n
    \n
    \n
    Session hygiene
    \n
    Use /new or sessions.patch to reset context.
    \n
    \n
    \n
    Cron reminders
    \n
    Use isolated sessions for recurring runs.
    \n
    \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport { formatSessionTokens } from \"../presenter\";\nimport { pathForTab } from \"../navigation\";\nimport type { GatewaySessionRow, SessionsListResult } from \"../types\";\n\nexport type SessionsProps = {\n loading: boolean;\n result: SessionsListResult | null;\n error: string | null;\n activeMinutes: string;\n limit: string;\n includeGlobal: boolean;\n includeUnknown: boolean;\n basePath: string;\n onFiltersChange: (next: {\n activeMinutes: string;\n limit: string;\n includeGlobal: boolean;\n includeUnknown: boolean;\n }) => void;\n onRefresh: () => void;\n onPatch: (\n key: string,\n patch: {\n label?: string | null;\n thinkingLevel?: string | null;\n verboseLevel?: string | null;\n reasoningLevel?: string | null;\n },\n ) => void;\n onDelete: (key: string) => void;\n};\n\nconst THINK_LEVELS = [\"\", \"off\", \"minimal\", \"low\", \"medium\", \"high\"] as const;\nconst BINARY_THINK_LEVELS = [\"\", \"off\", \"on\"] as const;\nconst VERBOSE_LEVELS = [\n { value: \"\", label: \"inherit\" },\n { value: \"off\", label: \"off (explicit)\" },\n { value: \"on\", label: \"on\" },\n] as const;\nconst REASONING_LEVELS = [\"\", \"off\", \"on\", \"stream\"] as const;\n\nfunction normalizeProviderId(provider?: string | null): string {\n if (!provider) return \"\";\n const normalized = provider.trim().toLowerCase();\n if (normalized === \"z.ai\" || normalized === \"z-ai\") return \"zai\";\n return normalized;\n}\n\nfunction isBinaryThinkingProvider(provider?: string | null): boolean {\n return normalizeProviderId(provider) === \"zai\";\n}\n\nfunction resolveThinkLevelOptions(provider?: string | null): readonly string[] {\n return isBinaryThinkingProvider(provider) ? BINARY_THINK_LEVELS : THINK_LEVELS;\n}\n\nfunction resolveThinkLevelDisplay(value: string, isBinary: boolean): string {\n if (!isBinary) return value;\n if (!value || value === \"off\") return value;\n return \"on\";\n}\n\nfunction resolveThinkLevelPatchValue(value: string, isBinary: boolean): string | null {\n if (!value) return null;\n if (!isBinary) return value;\n if (value === \"on\") return \"low\";\n return value;\n}\n\nexport function renderSessions(props: SessionsProps) {\n const rows = props.result?.sessions ?? [];\n return html`\n
    \n
    \n
    \n
    Sessions
    \n
    Active session keys and per-session overrides.
    \n
    \n \n
    \n\n
    \n \n \n \n \n
    \n\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n
    \n ${props.result ? `Store: ${props.result.path}` : \"\"}\n
    \n\n
    \n
    \n
    Key
    \n
    Label
    \n
    Kind
    \n
    Updated
    \n
    Tokens
    \n
    Thinking
    \n
    Verbose
    \n
    Reasoning
    \n
    Actions
    \n
    \n ${rows.length === 0\n ? html`
    No sessions found.
    `\n : rows.map((row) =>\n renderRow(row, props.basePath, props.onPatch, props.onDelete, props.loading),\n )}\n
    \n
    \n `;\n}\n\nfunction renderRow(\n row: GatewaySessionRow,\n basePath: string,\n onPatch: SessionsProps[\"onPatch\"],\n onDelete: SessionsProps[\"onDelete\"],\n disabled: boolean,\n) {\n const updated = row.updatedAt ? formatAgo(row.updatedAt) : \"n/a\";\n const rawThinking = row.thinkingLevel ?? \"\";\n const isBinaryThinking = isBinaryThinkingProvider(row.modelProvider);\n const thinking = resolveThinkLevelDisplay(rawThinking, isBinaryThinking);\n const thinkLevels = resolveThinkLevelOptions(row.modelProvider);\n const verbose = row.verboseLevel ?? \"\";\n const reasoning = row.reasoningLevel ?? \"\";\n const displayName = row.displayName ?? row.key;\n const canLink = row.kind !== \"global\";\n const chatUrl = canLink\n ? `${pathForTab(\"chat\", basePath)}?session=${encodeURIComponent(row.key)}`\n : null;\n\n return html`\n
    \n
    ${canLink\n ? html`${displayName}`\n : displayName}
    \n
    \n {\n const value = (e.target as HTMLInputElement).value.trim();\n onPatch(row.key, { label: value || null });\n }}\n />\n
    \n
    ${row.kind}
    \n
    ${updated}
    \n
    ${formatSessionTokens(row)}
    \n
    \n {\n const value = (e.target as HTMLSelectElement).value;\n onPatch(row.key, {\n thinkingLevel: resolveThinkLevelPatchValue(value, isBinaryThinking),\n });\n }}\n >\n ${thinkLevels.map((level) =>\n html``,\n )}\n \n
    \n
    \n {\n const value = (e.target as HTMLSelectElement).value;\n onPatch(row.key, { verboseLevel: value || null });\n }}\n >\n ${VERBOSE_LEVELS.map(\n (level) => html``,\n )}\n \n
    \n
    \n {\n const value = (e.target as HTMLSelectElement).value;\n onPatch(row.key, { reasoningLevel: value || null });\n }}\n >\n ${REASONING_LEVELS.map((level) =>\n html``,\n )}\n \n
    \n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { AppViewState } from \"../app-view-state\";\n\nfunction formatRemaining(ms: number): string {\n const remaining = Math.max(0, ms);\n const totalSeconds = Math.floor(remaining / 1000);\n if (totalSeconds < 60) return `${totalSeconds}s`;\n const minutes = Math.floor(totalSeconds / 60);\n if (minutes < 60) return `${minutes}m`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h`;\n}\n\nfunction renderMetaRow(label: string, value?: string | null) {\n if (!value) return nothing;\n return html`
    ${label}${value}
    `;\n}\n\nexport function renderExecApprovalPrompt(state: AppViewState) {\n const active = state.execApprovalQueue[0];\n if (!active) return nothing;\n const request = active.request;\n const remainingMs = active.expiresAtMs - Date.now();\n const remaining = remainingMs > 0 ? `expires in ${formatRemaining(remainingMs)}` : \"expired\";\n const queueCount = state.execApprovalQueue.length;\n return html`\n
    \n
    \n
    \n
    \n
    Exec approval needed
    \n
    ${remaining}
    \n
    \n ${queueCount > 1\n ? html`
    ${queueCount} pending
    `\n : nothing}\n
    \n
    ${request.command}
    \n
    \n ${renderMetaRow(\"Host\", request.host)}\n ${renderMetaRow(\"Agent\", request.agentId)}\n ${renderMetaRow(\"Session\", request.sessionKey)}\n ${renderMetaRow(\"CWD\", request.cwd)}\n ${renderMetaRow(\"Resolved\", request.resolvedPath)}\n ${renderMetaRow(\"Security\", request.security)}\n ${renderMetaRow(\"Ask\", request.ask)}\n
    \n ${state.execApprovalError\n ? html`
    ${state.execApprovalError}
    `\n : nothing}\n
    \n state.handleExecApprovalDecision(\"allow-once\")}\n >\n Allow once\n \n state.handleExecApprovalDecision(\"allow-always\")}\n >\n Always allow\n \n state.handleExecApprovalDecision(\"deny\")}\n >\n Deny\n \n
    \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { clampText } from \"../format\";\nimport type { SkillStatusEntry, SkillStatusReport } from \"../types\";\nimport type { SkillMessageMap } from \"../controllers/skills\";\n\nexport type SkillsProps = {\n loading: boolean;\n report: SkillStatusReport | null;\n error: string | null;\n filter: string;\n edits: Record;\n busyKey: string | null;\n messages: SkillMessageMap;\n onFilterChange: (next: string) => void;\n onRefresh: () => void;\n onToggle: (skillKey: string, enabled: boolean) => void;\n onEdit: (skillKey: string, value: string) => void;\n onSaveKey: (skillKey: string) => void;\n onInstall: (skillKey: string, name: string, installId: string) => void;\n};\n\nexport function renderSkills(props: SkillsProps) {\n const skills = props.report?.skills ?? [];\n const filter = props.filter.trim().toLowerCase();\n const filtered = filter\n ? skills.filter((skill) =>\n [skill.name, skill.description, skill.source]\n .join(\" \")\n .toLowerCase()\n .includes(filter),\n )\n : skills;\n\n return html`\n
    \n
    \n
    \n
    Skills
    \n
    Bundled, managed, and workspace skills.
    \n
    \n \n
    \n\n
    \n \n
    ${filtered.length} shown
    \n
    \n\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n ${filtered.length === 0\n ? html`
    No skills found.
    `\n : html`\n
    \n ${filtered.map((skill) => renderSkill(skill, props))}\n
    \n `}\n
    \n `;\n}\n\nfunction renderSkill(skill: SkillStatusEntry, props: SkillsProps) {\n const busy = props.busyKey === skill.skillKey;\n const apiKey = props.edits[skill.skillKey] ?? \"\";\n const message = props.messages[skill.skillKey] ?? null;\n const canInstall =\n skill.install.length > 0 && skill.missing.bins.length > 0;\n const missing = [\n ...skill.missing.bins.map((b) => `bin:${b}`),\n ...skill.missing.env.map((e) => `env:${e}`),\n ...skill.missing.config.map((c) => `config:${c}`),\n ...skill.missing.os.map((o) => `os:${o}`),\n ];\n const reasons: string[] = [];\n if (skill.disabled) reasons.push(\"disabled\");\n if (skill.blockedByAllowlist) reasons.push(\"blocked by allowlist\");\n return html`\n
    \n
    \n
    \n ${skill.emoji ? `${skill.emoji} ` : \"\"}${skill.name}\n
    \n
    ${clampText(skill.description, 140)}
    \n
    \n ${skill.source}\n \n ${skill.eligible ? \"eligible\" : \"blocked\"}\n \n ${skill.disabled ? html`disabled` : nothing}\n
    \n ${missing.length > 0\n ? html`\n
    \n Missing: ${missing.join(\", \")}\n
    \n `\n : nothing}\n ${reasons.length > 0\n ? html`\n
    \n Reason: ${reasons.join(\", \")}\n
    \n `\n : nothing}\n
    \n
    \n
    \n props.onToggle(skill.skillKey, skill.disabled)}\n >\n ${skill.disabled ? \"Enable\" : \"Disable\"}\n \n ${canInstall\n ? html`\n props.onInstall(skill.skillKey, skill.name, skill.install[0].id)}\n >\n ${busy ? \"Installing…\" : skill.install[0].label}\n `\n : nothing}\n
    \n ${message\n ? html`\n ${message.message}\n
    `\n : nothing}\n ${skill.primaryEnv\n ? html`\n
    \n API key\n \n props.onEdit(skill.skillKey, (e.target as HTMLInputElement).value)}\n />\n
    \n props.onSaveKey(skill.skillKey)}\n >\n Save key\n \n `\n : nothing}\n
    \n \n `;\n}\n","import { html } from \"lit\";\nimport { repeat } from \"lit/directives/repeat.js\";\n\nimport type { AppViewState } from \"./app-view-state\";\nimport { iconForTab, pathForTab, titleForTab, type Tab } from \"./navigation\";\nimport { loadChatHistory } from \"./controllers/chat\";\nimport { syncUrlWithSessionKey } from \"./app-settings\";\nimport type { SessionsListResult } from \"./types\";\nimport type { ThemeMode } from \"./theme\";\nimport type { ThemeTransitionContext } from \"./theme-transition\";\n\nexport function renderTab(state: AppViewState, tab: Tab) {\n const href = pathForTab(tab, state.basePath);\n return html`\n {\n if (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey\n ) {\n return;\n }\n event.preventDefault();\n state.setTab(tab);\n }}\n title=${titleForTab(tab)}\n >\n ${iconForTab(tab)}\n ${titleForTab(tab)}\n \n `;\n}\n\nexport function renderChatControls(state: AppViewState) {\n const sessionOptions = resolveSessionOptions(state.sessionKey, state.sessionsResult);\n const disableThinkingToggle = state.onboarding;\n const disableFocusToggle = state.onboarding;\n const showThinking = state.onboarding ? false : state.settings.chatShowThinking;\n const focusActive = state.onboarding ? true : state.settings.chatFocusMode;\n // Refresh icon\n const refreshIcon = html``;\n const focusIcon = html``;\n return html`\n
    \n \n {\n state.resetToolStream();\n void loadChatHistory(state);\n }}\n title=\"Refresh chat history\"\n >\n ${refreshIcon}\n \n |\n {\n if (disableThinkingToggle) return;\n state.applySettings({\n ...state.settings,\n chatShowThinking: !state.settings.chatShowThinking,\n });\n }}\n aria-pressed=${showThinking}\n title=${disableThinkingToggle\n ? \"Disabled during onboarding\"\n : \"Toggle assistant thinking/working output\"}\n >\n 🧠\n \n {\n if (disableFocusToggle) return;\n state.applySettings({\n ...state.settings,\n chatFocusMode: !state.settings.chatFocusMode,\n });\n }}\n aria-pressed=${focusActive}\n title=${disableFocusToggle\n ? \"Disabled during onboarding\"\n : \"Toggle focus mode (hide sidebar + page header)\"}\n >\n ${focusIcon}\n \n
    \n `;\n}\n\nfunction resolveSessionOptions(sessionKey: string, sessions: SessionsListResult | null) {\n const seen = new Set();\n const options: Array<{ key: string; displayName?: string }> = [];\n\n const resolvedCurrent = sessions?.sessions?.find((s) => s.key === sessionKey);\n\n // Add current session key first\n seen.add(sessionKey);\n options.push({ key: sessionKey, displayName: resolvedCurrent?.displayName });\n\n // Add sessions from the result\n if (sessions?.sessions) {\n for (const s of sessions.sessions) {\n if (!seen.has(s.key)) {\n seen.add(s.key);\n options.push({ key: s.key, displayName: s.displayName });\n }\n }\n }\n\n return options;\n}\n\nconst THEME_ORDER: ThemeMode[] = [\"system\", \"light\", \"dark\"];\n\nexport function renderThemeToggle(state: AppViewState) {\n const index = Math.max(0, THEME_ORDER.indexOf(state.theme));\n const applyTheme = (next: ThemeMode) => (event: MouseEvent) => {\n const element = event.currentTarget as HTMLElement;\n const context: ThemeTransitionContext = { element };\n if (event.clientX || event.clientY) {\n context.pointerClientX = event.clientX;\n context.pointerClientY = event.clientY;\n }\n state.setTheme(next, context);\n };\n\n return html`\n
    \n
    \n \n \n ${renderMonitorIcon()}\n \n \n ${renderSunIcon()}\n \n \n ${renderMoonIcon()}\n \n
    \n
    \n `;\n}\n\nfunction renderSunIcon() {\n return html`\n \n \n \n \n \n \n \n \n \n \n \n `;\n}\n\nfunction renderMoonIcon() {\n return html`\n \n \n \n `;\n}\n\nfunction renderMonitorIcon() {\n return html`\n \n \n \n \n \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { GatewayBrowserClient, GatewayHelloOk } from \"./gateway\";\nimport type { AppViewState } from \"./app-view-state\";\nimport { parseAgentSessionKey } from \"../../../src/routing/session-key.js\";\nimport {\n TAB_GROUPS,\n iconForTab,\n pathForTab,\n subtitleForTab,\n titleForTab,\n type Tab,\n} from \"./navigation\";\nimport type { UiSettings } from \"./storage\";\nimport type { ThemeMode } from \"./theme\";\nimport type { ThemeTransitionContext } from \"./theme-transition\";\nimport type {\n ConfigSnapshot,\n CronJob,\n CronRunLogEntry,\n CronStatus,\n HealthSnapshot,\n LogEntry,\n LogLevel,\n PresenceEntry,\n ChannelsStatusSnapshot,\n SessionsListResult,\n SkillStatusReport,\n StatusSummary,\n} from \"./types\";\nimport type { ChatQueueItem, CronFormState } from \"./ui-types\";\nimport { refreshChatAvatar } from \"./app-chat\";\nimport { renderChat } from \"./views/chat\";\nimport { renderConfig } from \"./views/config\";\nimport { renderChannels } from \"./views/channels\";\nimport { renderCron } from \"./views/cron\";\nimport { renderDebug } from \"./views/debug\";\nimport { renderInstances } from \"./views/instances\";\nimport { renderLogs } from \"./views/logs\";\nimport { renderNodes } from \"./views/nodes\";\nimport { renderOverview } from \"./views/overview\";\nimport { renderSessions } from \"./views/sessions\";\nimport { renderExecApprovalPrompt } from \"./views/exec-approval\";\nimport {\n approveDevicePairing,\n loadDevices,\n rejectDevicePairing,\n revokeDeviceToken,\n rotateDeviceToken,\n} from \"./controllers/devices\";\nimport { renderSkills } from \"./views/skills\";\nimport { renderChatControls, renderTab, renderThemeToggle } from \"./app-render.helpers\";\nimport { loadChannels } from \"./controllers/channels\";\nimport { loadPresence } from \"./controllers/presence\";\nimport { deleteSession, loadSessions, patchSession } from \"./controllers/sessions\";\nimport {\n installSkill,\n loadSkills,\n saveSkillApiKey,\n updateSkillEdit,\n updateSkillEnabled,\n type SkillMessage,\n} from \"./controllers/skills\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadChatHistory } from \"./controllers/chat\";\nimport {\n applyConfig,\n loadConfig,\n runUpdate,\n saveConfig,\n updateConfigFormValue,\n removeConfigFormValue,\n} from \"./controllers/config\";\nimport {\n loadExecApprovals,\n removeExecApprovalsFormValue,\n saveExecApprovals,\n updateExecApprovalsFormValue,\n} from \"./controllers/exec-approvals\";\nimport { loadCronRuns, toggleCronJob, runCronJob, removeCronJob, addCronJob } from \"./controllers/cron\";\nimport { loadDebug, callDebugMethod } from \"./controllers/debug\";\nimport { loadLogs } from \"./controllers/logs\";\n\nconst AVATAR_DATA_RE = /^data:/i;\nconst AVATAR_HTTP_RE = /^https?:\\/\\//i;\n\nfunction resolveAssistantAvatarUrl(state: AppViewState): string | undefined {\n const list = state.agentsList?.agents ?? [];\n const parsed = parseAgentSessionKey(state.sessionKey);\n const agentId =\n parsed?.agentId ??\n state.agentsList?.defaultId ??\n \"main\";\n const agent = list.find((entry) => entry.id === agentId);\n const identity = agent?.identity;\n const candidate = identity?.avatarUrl ?? identity?.avatar;\n if (!candidate) return undefined;\n if (AVATAR_DATA_RE.test(candidate) || AVATAR_HTTP_RE.test(candidate)) return candidate;\n return identity?.avatarUrl;\n}\n\nexport function renderApp(state: AppViewState) {\n const presenceCount = state.presenceEntries.length;\n const sessionsCount = state.sessionsResult?.count ?? null;\n const cronNext = state.cronStatus?.nextWakeAtMs ?? null;\n const chatDisabledReason = state.connected ? null : \"Disconnected from gateway.\";\n const isChat = state.tab === \"chat\";\n const chatFocus = isChat && (state.settings.chatFocusMode || state.onboarding);\n const showThinking = state.onboarding ? false : state.settings.chatShowThinking;\n const assistantAvatarUrl = resolveAssistantAvatarUrl(state);\n const chatAvatarUrl = state.chatAvatarUrl ?? assistantAvatarUrl ?? null;\n\n return html`\n
    \n
    \n
    \n \n state.applySettings({\n ...state.settings,\n navCollapsed: !state.settings.navCollapsed,\n })}\n title=\"${state.settings.navCollapsed ? \"Expand sidebar\" : \"Collapse sidebar\"}\"\n aria-label=\"${state.settings.navCollapsed ? \"Expand sidebar\" : \"Collapse sidebar\"}\"\n >\n \n \n
    \n
    CLAWDBOT
    \n
    Gateway Dashboard
    \n
    \n
    \n
    \n
    \n \n Health\n ${state.connected ? \"OK\" : \"Offline\"}\n
    \n ${renderThemeToggle(state)}\n
    \n
    \n \n
    \n
    \n
    \n
    ${titleForTab(state.tab)}
    \n
    ${subtitleForTab(state.tab)}
    \n
    \n
    \n ${state.lastError\n ? html`
    ${state.lastError}
    `\n : nothing}\n ${isChat ? renderChatControls(state) : nothing}\n
    \n
    \n\n ${state.tab === \"overview\"\n ? renderOverview({\n connected: state.connected,\n hello: state.hello,\n settings: state.settings,\n password: state.password,\n lastError: state.lastError,\n presenceCount,\n sessionsCount,\n cronEnabled: state.cronStatus?.enabled ?? null,\n cronNext,\n lastChannelsRefresh: state.channelsLastSuccess,\n onSettingsChange: (next) => state.applySettings(next),\n onPasswordChange: (next) => (state.password = next),\n onSessionKeyChange: (next) => {\n state.sessionKey = next;\n state.chatMessage = \"\";\n state.resetToolStream();\n state.applySettings({\n ...state.settings,\n sessionKey: next,\n lastActiveSessionKey: next,\n });\n void state.loadAssistantIdentity();\n },\n onConnect: () => state.connect(),\n onRefresh: () => state.loadOverview(),\n })\n : nothing}\n\n ${state.tab === \"channels\"\n ? renderChannels({\n connected: state.connected,\n loading: state.channelsLoading,\n snapshot: state.channelsSnapshot,\n lastError: state.channelsError,\n lastSuccessAt: state.channelsLastSuccess,\n whatsappMessage: state.whatsappLoginMessage,\n whatsappQrDataUrl: state.whatsappLoginQrDataUrl,\n whatsappConnected: state.whatsappLoginConnected,\n whatsappBusy: state.whatsappBusy,\n configSchema: state.configSchema,\n configSchemaLoading: state.configSchemaLoading,\n configForm: state.configForm,\n configUiHints: state.configUiHints,\n configSaving: state.configSaving,\n configFormDirty: state.configFormDirty,\n nostrProfileFormState: state.nostrProfileFormState,\n nostrProfileAccountId: state.nostrProfileAccountId,\n onRefresh: (probe) => loadChannels(state, probe),\n onWhatsAppStart: (force) => state.handleWhatsAppStart(force),\n onWhatsAppWait: () => state.handleWhatsAppWait(),\n onWhatsAppLogout: () => state.handleWhatsAppLogout(),\n onConfigPatch: (path, value) => updateConfigFormValue(state, path, value),\n onConfigSave: () => state.handleChannelConfigSave(),\n onConfigReload: () => state.handleChannelConfigReload(),\n onNostrProfileEdit: (accountId, profile) =>\n state.handleNostrProfileEdit(accountId, profile),\n onNostrProfileCancel: () => state.handleNostrProfileCancel(),\n onNostrProfileFieldChange: (field, value) =>\n state.handleNostrProfileFieldChange(field, value),\n onNostrProfileSave: () => state.handleNostrProfileSave(),\n onNostrProfileImport: () => state.handleNostrProfileImport(),\n onNostrProfileToggleAdvanced: () => state.handleNostrProfileToggleAdvanced(),\n })\n : nothing}\n\n ${state.tab === \"instances\"\n ? renderInstances({\n loading: state.presenceLoading,\n entries: state.presenceEntries,\n lastError: state.presenceError,\n statusMessage: state.presenceStatus,\n onRefresh: () => loadPresence(state),\n })\n : nothing}\n\n ${state.tab === \"sessions\"\n ? renderSessions({\n loading: state.sessionsLoading,\n result: state.sessionsResult,\n error: state.sessionsError,\n activeMinutes: state.sessionsFilterActive,\n limit: state.sessionsFilterLimit,\n includeGlobal: state.sessionsIncludeGlobal,\n includeUnknown: state.sessionsIncludeUnknown,\n basePath: state.basePath,\n onFiltersChange: (next) => {\n state.sessionsFilterActive = next.activeMinutes;\n state.sessionsFilterLimit = next.limit;\n state.sessionsIncludeGlobal = next.includeGlobal;\n state.sessionsIncludeUnknown = next.includeUnknown;\n\t },\n\t onRefresh: () => loadSessions(state),\n\t onPatch: (key, patch) => patchSession(state, key, patch),\n\t onDelete: (key) => deleteSession(state, key),\n\t })\n\t : nothing}\n\n ${state.tab === \"cron\"\n ? renderCron({\n loading: state.cronLoading,\n status: state.cronStatus,\n jobs: state.cronJobs,\n error: state.cronError,\n busy: state.cronBusy,\n form: state.cronForm,\n channels: state.channelsSnapshot?.channelMeta?.length\n ? state.channelsSnapshot.channelMeta.map((entry) => entry.id)\n : state.channelsSnapshot?.channelOrder ?? [],\n channelLabels: state.channelsSnapshot?.channelLabels ?? {},\n channelMeta: state.channelsSnapshot?.channelMeta ?? [],\n runsJobId: state.cronRunsJobId,\n runs: state.cronRuns,\n onFormChange: (patch) => (state.cronForm = { ...state.cronForm, ...patch }),\n onRefresh: () => state.loadCron(),\n onAdd: () => addCronJob(state),\n onToggle: (job, enabled) => toggleCronJob(state, job, enabled),\n onRun: (job) => runCronJob(state, job),\n onRemove: (job) => removeCronJob(state, job),\n onLoadRuns: (jobId) => loadCronRuns(state, jobId),\n })\n : nothing}\n\n ${state.tab === \"skills\"\n ? renderSkills({\n loading: state.skillsLoading,\n report: state.skillsReport,\n error: state.skillsError,\n filter: state.skillsFilter,\n edits: state.skillEdits,\n messages: state.skillMessages,\n busyKey: state.skillsBusyKey,\n onFilterChange: (next) => (state.skillsFilter = next),\n onRefresh: () => loadSkills(state, { clearMessages: true }),\n onToggle: (key, enabled) => updateSkillEnabled(state, key, enabled),\n onEdit: (key, value) => updateSkillEdit(state, key, value),\n onSaveKey: (key) => saveSkillApiKey(state, key),\n onInstall: (skillKey, name, installId) =>\n installSkill(state, skillKey, name, installId),\n })\n : nothing}\n\n ${state.tab === \"nodes\"\n ? renderNodes({\n loading: state.nodesLoading,\n nodes: state.nodes,\n devicesLoading: state.devicesLoading,\n devicesError: state.devicesError,\n devicesList: state.devicesList,\n configForm: state.configForm ?? (state.configSnapshot?.config as Record | null),\n configLoading: state.configLoading,\n configSaving: state.configSaving,\n configDirty: state.configFormDirty,\n configFormMode: state.configFormMode,\n execApprovalsLoading: state.execApprovalsLoading,\n execApprovalsSaving: state.execApprovalsSaving,\n execApprovalsDirty: state.execApprovalsDirty,\n execApprovalsSnapshot: state.execApprovalsSnapshot,\n execApprovalsForm: state.execApprovalsForm,\n execApprovalsSelectedAgent: state.execApprovalsSelectedAgent,\n execApprovalsTarget: state.execApprovalsTarget,\n execApprovalsTargetNodeId: state.execApprovalsTargetNodeId,\n onRefresh: () => loadNodes(state),\n onDevicesRefresh: () => loadDevices(state),\n onDeviceApprove: (requestId) => approveDevicePairing(state, requestId),\n onDeviceReject: (requestId) => rejectDevicePairing(state, requestId),\n onDeviceRotate: (deviceId, role, scopes) =>\n rotateDeviceToken(state, { deviceId, role, scopes }),\n onDeviceRevoke: (deviceId, role) =>\n revokeDeviceToken(state, { deviceId, role }),\n onLoadConfig: () => loadConfig(state),\n onLoadExecApprovals: () => {\n const target =\n state.execApprovalsTarget === \"node\" && state.execApprovalsTargetNodeId\n ? { kind: \"node\" as const, nodeId: state.execApprovalsTargetNodeId }\n : { kind: \"gateway\" as const };\n return loadExecApprovals(state, target);\n },\n onBindDefault: (nodeId) => {\n if (nodeId) {\n updateConfigFormValue(state, [\"tools\", \"exec\", \"node\"], nodeId);\n } else {\n removeConfigFormValue(state, [\"tools\", \"exec\", \"node\"]);\n }\n },\n onBindAgent: (agentIndex, nodeId) => {\n const basePath = [\"agents\", \"list\", agentIndex, \"tools\", \"exec\", \"node\"];\n if (nodeId) {\n updateConfigFormValue(state, basePath, nodeId);\n } else {\n removeConfigFormValue(state, basePath);\n }\n },\n onSaveBindings: () => saveConfig(state),\n onExecApprovalsTargetChange: (kind, nodeId) => {\n state.execApprovalsTarget = kind;\n state.execApprovalsTargetNodeId = nodeId;\n state.execApprovalsSnapshot = null;\n state.execApprovalsForm = null;\n state.execApprovalsDirty = false;\n state.execApprovalsSelectedAgent = null;\n },\n onExecApprovalsSelectAgent: (agentId) => {\n state.execApprovalsSelectedAgent = agentId;\n },\n onExecApprovalsPatch: (path, value) =>\n updateExecApprovalsFormValue(state, path, value),\n onExecApprovalsRemove: (path) =>\n removeExecApprovalsFormValue(state, path),\n onSaveExecApprovals: () => {\n const target =\n state.execApprovalsTarget === \"node\" && state.execApprovalsTargetNodeId\n ? { kind: \"node\" as const, nodeId: state.execApprovalsTargetNodeId }\n : { kind: \"gateway\" as const };\n return saveExecApprovals(state, target);\n },\n })\n : nothing}\n\n ${state.tab === \"chat\"\n ? renderChat({\n sessionKey: state.sessionKey,\n onSessionKeyChange: (next) => {\n state.sessionKey = next;\n state.chatMessage = \"\";\n state.chatStream = null;\n state.chatStreamStartedAt = null;\n state.chatRunId = null;\n state.chatQueue = [];\n state.resetToolStream();\n state.resetChatScroll();\n state.applySettings({\n ...state.settings,\n sessionKey: next,\n lastActiveSessionKey: next,\n });\n void state.loadAssistantIdentity();\n void loadChatHistory(state);\n void refreshChatAvatar(state);\n },\n thinkingLevel: state.chatThinkingLevel,\n showThinking,\n loading: state.chatLoading,\n sending: state.chatSending,\n compactionStatus: state.compactionStatus,\n assistantAvatarUrl: chatAvatarUrl,\n messages: state.chatMessages,\n toolMessages: state.chatToolMessages,\n stream: state.chatStream,\n streamStartedAt: state.chatStreamStartedAt,\n draft: state.chatMessage,\n queue: state.chatQueue,\n connected: state.connected,\n canSend: state.connected,\n disabledReason: chatDisabledReason,\n error: state.lastError,\n sessions: state.sessionsResult,\n focusMode: chatFocus,\n onRefresh: () => {\n state.resetToolStream();\n return Promise.all([loadChatHistory(state), refreshChatAvatar(state)]);\n },\n onToggleFocusMode: () => {\n if (state.onboarding) return;\n state.applySettings({\n ...state.settings,\n chatFocusMode: !state.settings.chatFocusMode,\n });\n },\n onChatScroll: (event) => state.handleChatScroll(event),\n onDraftChange: (next) => (state.chatMessage = next),\n onSend: () => state.handleSendChat(),\n canAbort: Boolean(state.chatRunId),\n onAbort: () => void state.handleAbortChat(),\n onQueueRemove: (id) => state.removeQueuedMessage(id),\n onNewSession: () =>\n state.handleSendChat(\"/new\", { restoreDraft: true }),\n // Sidebar props for tool output viewing\n sidebarOpen: state.sidebarOpen,\n sidebarContent: state.sidebarContent,\n sidebarError: state.sidebarError,\n splitRatio: state.splitRatio,\n onOpenSidebar: (content: string) => state.handleOpenSidebar(content),\n onCloseSidebar: () => state.handleCloseSidebar(),\n onSplitRatioChange: (ratio: number) => state.handleSplitRatioChange(ratio),\n assistantName: state.assistantName,\n assistantAvatar: state.assistantAvatar,\n })\n : nothing}\n\n ${state.tab === \"config\"\n ? renderConfig({\n raw: state.configRaw,\n valid: state.configValid,\n issues: state.configIssues,\n loading: state.configLoading,\n saving: state.configSaving,\n applying: state.configApplying,\n updating: state.updateRunning,\n connected: state.connected,\n schema: state.configSchema,\n schemaLoading: state.configSchemaLoading,\n uiHints: state.configUiHints,\n formMode: state.configFormMode,\n formValue: state.configForm,\n originalValue: state.configFormOriginal,\n searchQuery: state.configSearchQuery,\n activeSection: state.configActiveSection,\n activeSubsection: state.configActiveSubsection,\n onRawChange: (next) => (state.configRaw = next),\n onFormModeChange: (mode) => (state.configFormMode = mode),\n onFormPatch: (path, value) => updateConfigFormValue(state, path, value),\n onSearchChange: (query) => (state.configSearchQuery = query),\n onSectionChange: (section) => {\n state.configActiveSection = section;\n state.configActiveSubsection = null;\n },\n onSubsectionChange: (section) => (state.configActiveSubsection = section),\n onReload: () => loadConfig(state),\n onSave: () => saveConfig(state),\n onApply: () => applyConfig(state),\n onUpdate: () => runUpdate(state),\n })\n : nothing}\n\n ${state.tab === \"debug\"\n ? renderDebug({\n loading: state.debugLoading,\n status: state.debugStatus,\n health: state.debugHealth,\n models: state.debugModels,\n heartbeat: state.debugHeartbeat,\n eventLog: state.eventLog,\n callMethod: state.debugCallMethod,\n callParams: state.debugCallParams,\n callResult: state.debugCallResult,\n callError: state.debugCallError,\n onCallMethodChange: (next) => (state.debugCallMethod = next),\n onCallParamsChange: (next) => (state.debugCallParams = next),\n onRefresh: () => loadDebug(state),\n onCall: () => callDebugMethod(state),\n })\n : nothing}\n\n ${state.tab === \"logs\"\n ? renderLogs({\n loading: state.logsLoading,\n error: state.logsError,\n file: state.logsFile,\n entries: state.logsEntries,\n filterText: state.logsFilterText,\n levelFilters: state.logsLevelFilters,\n autoFollow: state.logsAutoFollow,\n truncated: state.logsTruncated,\n onFilterTextChange: (next) => (state.logsFilterText = next),\n onLevelToggle: (level, enabled) => {\n state.logsLevelFilters = { ...state.logsLevelFilters, [level]: enabled };\n },\n onToggleAutoFollow: (next) => (state.logsAutoFollow = next),\n onRefresh: () => loadLogs(state, { reset: true }),\n onExport: (lines, label) => state.exportLogs(lines, label),\n onScroll: (event) => state.handleLogsScroll(event),\n })\n : nothing}\n
    \n ${renderExecApprovalPrompt(state)}\n
    \n `;\n}\n","import type { LogLevel } from \"./types\";\nimport type { CronFormState } from \"./ui-types\";\n\nexport const DEFAULT_LOG_LEVEL_FILTERS: Record = {\n trace: true,\n debug: true,\n info: true,\n warn: true,\n error: true,\n fatal: true,\n};\n\nexport const DEFAULT_CRON_FORM: CronFormState = {\n name: \"\",\n description: \"\",\n agentId: \"\",\n enabled: true,\n scheduleKind: \"every\",\n scheduleAt: \"\",\n everyAmount: \"30\",\n everyUnit: \"minutes\",\n cronExpr: \"0 7 * * *\",\n cronTz: \"\",\n sessionTarget: \"main\",\n wakeMode: \"next-heartbeat\",\n payloadKind: \"systemEvent\",\n payloadText: \"\",\n deliver: false,\n channel: \"last\",\n to: \"\",\n timeoutSeconds: \"\",\n postToMainPrefix: \"\",\n};\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { AgentsListResult } from \"../types\";\n\nexport type AgentsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n agentsLoading: boolean;\n agentsError: string | null;\n agentsList: AgentsListResult | null;\n};\n\nexport async function loadAgents(state: AgentsState) {\n if (!state.client || !state.connected) return;\n if (state.agentsLoading) return;\n state.agentsLoading = true;\n state.agentsError = null;\n try {\n const res = (await state.client.request(\"agents.list\", {})) as AgentsListResult | undefined;\n if (res) state.agentsList = res;\n } catch (err) {\n state.agentsError = String(err);\n } finally {\n state.agentsLoading = false;\n }\n}\n","export const GATEWAY_CLIENT_IDS = {\n WEBCHAT_UI: \"webchat-ui\",\n CONTROL_UI: \"clawdbot-control-ui\",\n WEBCHAT: \"webchat\",\n CLI: \"cli\",\n GATEWAY_CLIENT: \"gateway-client\",\n MACOS_APP: \"clawdbot-macos\",\n IOS_APP: \"clawdbot-ios\",\n ANDROID_APP: \"clawdbot-android\",\n NODE_HOST: \"node-host\",\n TEST: \"test\",\n FINGERPRINT: \"fingerprint\",\n PROBE: \"clawdbot-probe\",\n} as const;\n\nexport type GatewayClientId = (typeof GATEWAY_CLIENT_IDS)[keyof typeof GATEWAY_CLIENT_IDS];\n\n// Back-compat naming (internal): these values are IDs, not display names.\nexport const GATEWAY_CLIENT_NAMES = GATEWAY_CLIENT_IDS;\nexport type GatewayClientName = GatewayClientId;\n\nexport const GATEWAY_CLIENT_MODES = {\n WEBCHAT: \"webchat\",\n CLI: \"cli\",\n UI: \"ui\",\n BACKEND: \"backend\",\n NODE: \"node\",\n PROBE: \"probe\",\n TEST: \"test\",\n} as const;\n\nexport type GatewayClientMode = (typeof GATEWAY_CLIENT_MODES)[keyof typeof GATEWAY_CLIENT_MODES];\n\nexport type GatewayClientInfo = {\n id: GatewayClientId;\n displayName?: string;\n version: string;\n platform: string;\n deviceFamily?: string;\n modelIdentifier?: string;\n mode: GatewayClientMode;\n instanceId?: string;\n};\n\nconst GATEWAY_CLIENT_ID_SET = new Set(Object.values(GATEWAY_CLIENT_IDS));\nconst GATEWAY_CLIENT_MODE_SET = new Set(Object.values(GATEWAY_CLIENT_MODES));\n\nexport function normalizeGatewayClientId(raw?: string | null): GatewayClientId | undefined {\n const normalized = raw?.trim().toLowerCase();\n if (!normalized) return undefined;\n return GATEWAY_CLIENT_ID_SET.has(normalized as GatewayClientId)\n ? (normalized as GatewayClientId)\n : undefined;\n}\n\nexport function normalizeGatewayClientName(raw?: string | null): GatewayClientName | undefined {\n return normalizeGatewayClientId(raw);\n}\n\nexport function normalizeGatewayClientMode(raw?: string | null): GatewayClientMode | undefined {\n const normalized = raw?.trim().toLowerCase();\n if (!normalized) return undefined;\n return GATEWAY_CLIENT_MODE_SET.has(normalized as GatewayClientMode)\n ? (normalized as GatewayClientMode)\n : undefined;\n}\n","export type DeviceAuthPayloadParams = {\n deviceId: string;\n clientId: string;\n clientMode: string;\n role: string;\n scopes: string[];\n signedAtMs: number;\n token?: string | null;\n nonce?: string | null;\n version?: \"v1\" | \"v2\";\n};\n\nexport function buildDeviceAuthPayload(params: DeviceAuthPayloadParams): string {\n const version = params.version ?? (params.nonce ? \"v2\" : \"v1\");\n const scopes = params.scopes.join(\",\");\n const token = params.token ?? \"\";\n const base = [\n version,\n params.deviceId,\n params.clientId,\n params.clientMode,\n params.role,\n scopes,\n String(params.signedAtMs),\n token,\n ];\n if (version === \"v2\") {\n base.push(params.nonce ?? \"\");\n }\n return base.join(\"|\");\n}\n","import { generateUUID } from \"./uuid\";\nimport {\n GATEWAY_CLIENT_MODES,\n GATEWAY_CLIENT_NAMES,\n type GatewayClientMode,\n type GatewayClientName,\n} from \"../../../src/gateway/protocol/client-info.js\";\nimport { buildDeviceAuthPayload } from \"../../../src/gateway/device-auth.js\";\nimport { loadOrCreateDeviceIdentity, signDevicePayload } from \"./device-identity\";\nimport { clearDeviceAuthToken, loadDeviceAuthToken, storeDeviceAuthToken } from \"./device-auth\";\n\nexport type GatewayEventFrame = {\n type: \"event\";\n event: string;\n payload?: unknown;\n seq?: number;\n stateVersion?: { presence: number; health: number };\n};\n\nexport type GatewayResponseFrame = {\n type: \"res\";\n id: string;\n ok: boolean;\n payload?: unknown;\n error?: { code: string; message: string; details?: unknown };\n};\n\nexport type GatewayHelloOk = {\n type: \"hello-ok\";\n protocol: number;\n features?: { methods?: string[]; events?: string[] };\n snapshot?: unknown;\n auth?: {\n deviceToken?: string;\n role?: string;\n scopes?: string[];\n issuedAtMs?: number;\n };\n policy?: { tickIntervalMs?: number };\n};\n\ntype Pending = {\n resolve: (value: unknown) => void;\n reject: (err: unknown) => void;\n};\n\nexport type GatewayBrowserClientOptions = {\n url: string;\n token?: string;\n password?: string;\n clientName?: GatewayClientName;\n clientVersion?: string;\n platform?: string;\n mode?: GatewayClientMode;\n instanceId?: string;\n onHello?: (hello: GatewayHelloOk) => void;\n onEvent?: (evt: GatewayEventFrame) => void;\n onClose?: (info: { code: number; reason: string }) => void;\n onGap?: (info: { expected: number; received: number }) => void;\n};\n\n// 4008 = application-defined code (browser rejects 1008 \"Policy Violation\")\nconst CONNECT_FAILED_CLOSE_CODE = 4008;\n\nexport class GatewayBrowserClient {\n private ws: WebSocket | null = null;\n private pending = new Map();\n private closed = false;\n private lastSeq: number | null = null;\n private connectNonce: string | null = null;\n private connectSent = false;\n private connectTimer: number | null = null;\n private backoffMs = 800;\n\n constructor(private opts: GatewayBrowserClientOptions) {}\n\n start() {\n this.closed = false;\n this.connect();\n }\n\n stop() {\n this.closed = true;\n this.ws?.close();\n this.ws = null;\n this.flushPending(new Error(\"gateway client stopped\"));\n }\n\n get connected() {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n private connect() {\n if (this.closed) return;\n this.ws = new WebSocket(this.opts.url);\n this.ws.onopen = () => this.queueConnect();\n this.ws.onmessage = (ev) => this.handleMessage(String(ev.data ?? \"\"));\n this.ws.onclose = (ev) => {\n const reason = String(ev.reason ?? \"\");\n this.ws = null;\n this.flushPending(new Error(`gateway closed (${ev.code}): ${reason}`));\n this.opts.onClose?.({ code: ev.code, reason });\n this.scheduleReconnect();\n };\n this.ws.onerror = () => {\n // ignored; close handler will fire\n };\n }\n\n private scheduleReconnect() {\n if (this.closed) return;\n const delay = this.backoffMs;\n this.backoffMs = Math.min(this.backoffMs * 1.7, 15_000);\n window.setTimeout(() => this.connect(), delay);\n }\n\n private flushPending(err: Error) {\n for (const [, p] of this.pending) p.reject(err);\n this.pending.clear();\n }\n\n private async sendConnect() {\n if (this.connectSent) return;\n this.connectSent = true;\n if (this.connectTimer !== null) {\n window.clearTimeout(this.connectTimer);\n this.connectTimer = null;\n }\n\n // crypto.subtle is only available in secure contexts (HTTPS, localhost).\n // Over plain HTTP, we skip device identity and fall back to token-only auth.\n // Gateways may reject this unless gateway.controlUi.allowInsecureAuth is enabled.\n const isSecureContext = typeof crypto !== \"undefined\" && !!crypto.subtle;\n\n const scopes = [\"operator.admin\", \"operator.approvals\", \"operator.pairing\"];\n const role = \"operator\";\n let deviceIdentity: Awaited> | null = null;\n let canFallbackToShared = false;\n let authToken = this.opts.token;\n\n if (isSecureContext) {\n deviceIdentity = await loadOrCreateDeviceIdentity();\n const storedToken = loadDeviceAuthToken({\n deviceId: deviceIdentity.deviceId,\n role,\n })?.token;\n authToken = storedToken ?? this.opts.token;\n canFallbackToShared = Boolean(storedToken && this.opts.token);\n }\n const auth =\n authToken || this.opts.password\n ? {\n token: authToken,\n password: this.opts.password,\n }\n : undefined;\n\n let device:\n | {\n id: string;\n publicKey: string;\n signature: string;\n signedAt: number;\n nonce: string | undefined;\n }\n | undefined;\n\n if (isSecureContext && deviceIdentity) {\n const signedAtMs = Date.now();\n const nonce = this.connectNonce ?? undefined;\n const payload = buildDeviceAuthPayload({\n deviceId: deviceIdentity.deviceId,\n clientId: this.opts.clientName ?? GATEWAY_CLIENT_NAMES.CONTROL_UI,\n clientMode: this.opts.mode ?? GATEWAY_CLIENT_MODES.WEBCHAT,\n role,\n scopes,\n signedAtMs,\n token: authToken ?? null,\n nonce,\n });\n const signature = await signDevicePayload(deviceIdentity.privateKey, payload);\n device = {\n id: deviceIdentity.deviceId,\n publicKey: deviceIdentity.publicKey,\n signature,\n signedAt: signedAtMs,\n nonce,\n };\n }\n const params = {\n minProtocol: 3,\n maxProtocol: 3,\n client: {\n id: this.opts.clientName ?? GATEWAY_CLIENT_NAMES.CONTROL_UI,\n version: this.opts.clientVersion ?? \"dev\",\n platform: this.opts.platform ?? navigator.platform ?? \"web\",\n mode: this.opts.mode ?? GATEWAY_CLIENT_MODES.WEBCHAT,\n instanceId: this.opts.instanceId,\n },\n role,\n scopes,\n device,\n caps: [],\n auth,\n userAgent: navigator.userAgent,\n locale: navigator.language,\n };\n\n void this.request(\"connect\", params)\n .then((hello) => {\n if (hello?.auth?.deviceToken && deviceIdentity) {\n storeDeviceAuthToken({\n deviceId: deviceIdentity.deviceId,\n role: hello.auth.role ?? role,\n token: hello.auth.deviceToken,\n scopes: hello.auth.scopes ?? [],\n });\n }\n this.backoffMs = 800;\n this.opts.onHello?.(hello);\n })\n .catch(() => {\n if (canFallbackToShared && deviceIdentity) {\n clearDeviceAuthToken({ deviceId: deviceIdentity.deviceId, role });\n }\n this.ws?.close(CONNECT_FAILED_CLOSE_CODE, \"connect failed\");\n });\n }\n\n private handleMessage(raw: string) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return;\n }\n\n const frame = parsed as { type?: unknown };\n if (frame.type === \"event\") {\n const evt = parsed as GatewayEventFrame;\n if (evt.event === \"connect.challenge\") {\n const payload = evt.payload as { nonce?: unknown } | undefined;\n const nonce = payload && typeof payload.nonce === \"string\" ? payload.nonce : null;\n if (nonce) {\n this.connectNonce = nonce;\n void this.sendConnect();\n }\n return;\n }\n const seq = typeof evt.seq === \"number\" ? evt.seq : null;\n if (seq !== null) {\n if (this.lastSeq !== null && seq > this.lastSeq + 1) {\n this.opts.onGap?.({ expected: this.lastSeq + 1, received: seq });\n }\n this.lastSeq = seq;\n }\n try {\n this.opts.onEvent?.(evt);\n } catch (err) {\n console.error(\"[gateway] event handler error:\", err);\n }\n return;\n }\n\n if (frame.type === \"res\") {\n const res = parsed as GatewayResponseFrame;\n const pending = this.pending.get(res.id);\n if (!pending) return;\n this.pending.delete(res.id);\n if (res.ok) pending.resolve(res.payload);\n else pending.reject(new Error(res.error?.message ?? \"request failed\"));\n return;\n }\n }\n\n request(method: string, params?: unknown): Promise {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return Promise.reject(new Error(\"gateway not connected\"));\n }\n const id = generateUUID();\n const frame = { type: \"req\", id, method, params };\n const p = new Promise((resolve, reject) => {\n this.pending.set(id, { resolve: (v) => resolve(v as T), reject });\n });\n this.ws.send(JSON.stringify(frame));\n return p;\n }\n\n private queueConnect() {\n this.connectNonce = null;\n this.connectSent = false;\n if (this.connectTimer !== null) window.clearTimeout(this.connectTimer);\n this.connectTimer = window.setTimeout(() => {\n void this.sendConnect();\n }, 750);\n }\n}\n","export type ExecApprovalRequestPayload = {\n command: string;\n cwd?: string | null;\n host?: string | null;\n security?: string | null;\n ask?: string | null;\n agentId?: string | null;\n resolvedPath?: string | null;\n sessionKey?: string | null;\n};\n\nexport type ExecApprovalRequest = {\n id: string;\n request: ExecApprovalRequestPayload;\n createdAtMs: number;\n expiresAtMs: number;\n};\n\nexport type ExecApprovalResolved = {\n id: string;\n decision?: string | null;\n resolvedBy?: string | null;\n ts?: number | null;\n};\n\nfunction isRecord(value: unknown): value is Record {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function parseExecApprovalRequested(payload: unknown): ExecApprovalRequest | null {\n if (!isRecord(payload)) return null;\n const id = typeof payload.id === \"string\" ? payload.id.trim() : \"\";\n const request = payload.request;\n if (!id || !isRecord(request)) return null;\n const command = typeof request.command === \"string\" ? request.command.trim() : \"\";\n if (!command) return null;\n const createdAtMs = typeof payload.createdAtMs === \"number\" ? payload.createdAtMs : 0;\n const expiresAtMs = typeof payload.expiresAtMs === \"number\" ? payload.expiresAtMs : 0;\n if (!createdAtMs || !expiresAtMs) return null;\n return {\n id,\n request: {\n command,\n cwd: typeof request.cwd === \"string\" ? request.cwd : null,\n host: typeof request.host === \"string\" ? request.host : null,\n security: typeof request.security === \"string\" ? request.security : null,\n ask: typeof request.ask === \"string\" ? request.ask : null,\n agentId: typeof request.agentId === \"string\" ? request.agentId : null,\n resolvedPath: typeof request.resolvedPath === \"string\" ? request.resolvedPath : null,\n sessionKey: typeof request.sessionKey === \"string\" ? request.sessionKey : null,\n },\n createdAtMs,\n expiresAtMs,\n };\n}\n\nexport function parseExecApprovalResolved(payload: unknown): ExecApprovalResolved | null {\n if (!isRecord(payload)) return null;\n const id = typeof payload.id === \"string\" ? payload.id.trim() : \"\";\n if (!id) return null;\n return {\n id,\n decision: typeof payload.decision === \"string\" ? payload.decision : null,\n resolvedBy: typeof payload.resolvedBy === \"string\" ? payload.resolvedBy : null,\n ts: typeof payload.ts === \"number\" ? payload.ts : null,\n };\n}\n\nexport function pruneExecApprovalQueue(queue: ExecApprovalRequest[]): ExecApprovalRequest[] {\n const now = Date.now();\n return queue.filter((entry) => entry.expiresAtMs > now);\n}\n\nexport function addExecApproval(\n queue: ExecApprovalRequest[],\n entry: ExecApprovalRequest,\n): ExecApprovalRequest[] {\n const next = pruneExecApprovalQueue(queue).filter((item) => item.id !== entry.id);\n next.push(entry);\n return next;\n}\n\nexport function removeExecApproval(queue: ExecApprovalRequest[], id: string): ExecApprovalRequest[] {\n return pruneExecApprovalQueue(queue).filter((entry) => entry.id !== id);\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport {\n normalizeAssistantIdentity,\n type AssistantIdentity,\n} from \"../assistant-identity\";\n\nexport type AssistantIdentityState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n sessionKey: string;\n assistantName: string;\n assistantAvatar: string | null;\n assistantAgentId: string | null;\n};\n\nexport async function loadAssistantIdentity(\n state: AssistantIdentityState,\n opts?: { sessionKey?: string },\n) {\n if (!state.client || !state.connected) return;\n const sessionKey = opts?.sessionKey?.trim() || state.sessionKey.trim();\n const params = sessionKey ? { sessionKey } : {};\n try {\n const res = (await state.client.request(\"agent.identity.get\", params)) as\n | Partial\n | undefined;\n if (!res) return;\n const normalized = normalizeAssistantIdentity(res);\n state.assistantName = normalized.name;\n state.assistantAvatar = normalized.avatar;\n state.assistantAgentId = normalized.agentId ?? null;\n } catch {\n // Ignore errors; keep last known identity.\n }\n}\n","import { loadChatHistory } from \"./controllers/chat\";\nimport { loadDevices } from \"./controllers/devices\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadAgents } from \"./controllers/agents\";\nimport type { GatewayEventFrame, GatewayHelloOk } from \"./gateway\";\nimport { GatewayBrowserClient } from \"./gateway\";\nimport type { EventLogEntry } from \"./app-events\";\nimport type { AgentsListResult, PresenceEntry, HealthSnapshot, StatusSummary } from \"./types\";\nimport type { Tab } from \"./navigation\";\nimport type { UiSettings } from \"./storage\";\nimport { handleAgentEvent, resetToolStream, type AgentEventPayload } from \"./app-tool-stream\";\nimport { flushChatQueueForEvent } from \"./app-chat\";\nimport {\n applySettings,\n loadCron,\n refreshActiveTab,\n setLastActiveSessionKey,\n} from \"./app-settings\";\nimport { handleChatEvent, type ChatEventPayload } from \"./controllers/chat\";\nimport {\n addExecApproval,\n parseExecApprovalRequested,\n parseExecApprovalResolved,\n removeExecApproval,\n} from \"./controllers/exec-approval\";\nimport type { ClawdbotApp } from \"./app\";\nimport type { ExecApprovalRequest } from \"./controllers/exec-approval\";\nimport { loadAssistantIdentity } from \"./controllers/assistant-identity\";\n\ntype GatewayHost = {\n settings: UiSettings;\n password: string;\n client: GatewayBrowserClient | null;\n connected: boolean;\n hello: GatewayHelloOk | null;\n lastError: string | null;\n onboarding?: boolean;\n eventLogBuffer: EventLogEntry[];\n eventLog: EventLogEntry[];\n tab: Tab;\n presenceEntries: PresenceEntry[];\n presenceError: string | null;\n presenceStatus: StatusSummary | null;\n agentsLoading: boolean;\n agentsList: AgentsListResult | null;\n agentsError: string | null;\n debugHealth: HealthSnapshot | null;\n assistantName: string;\n assistantAvatar: string | null;\n assistantAgentId: string | null;\n sessionKey: string;\n chatRunId: string | null;\n execApprovalQueue: ExecApprovalRequest[];\n execApprovalError: string | null;\n};\n\ntype SessionDefaultsSnapshot = {\n defaultAgentId?: string;\n mainKey?: string;\n mainSessionKey?: string;\n scope?: string;\n};\n\nfunction normalizeSessionKeyForDefaults(\n value: string | undefined,\n defaults: SessionDefaultsSnapshot,\n): string {\n const raw = (value ?? \"\").trim();\n const mainSessionKey = defaults.mainSessionKey?.trim();\n if (!mainSessionKey) return raw;\n if (!raw) return mainSessionKey;\n const mainKey = defaults.mainKey?.trim() || \"main\";\n const defaultAgentId = defaults.defaultAgentId?.trim();\n const isAlias =\n raw === \"main\" ||\n raw === mainKey ||\n (defaultAgentId &&\n (raw === `agent:${defaultAgentId}:main` ||\n raw === `agent:${defaultAgentId}:${mainKey}`));\n return isAlias ? mainSessionKey : raw;\n}\n\nfunction applySessionDefaults(host: GatewayHost, defaults?: SessionDefaultsSnapshot) {\n if (!defaults?.mainSessionKey) return;\n const resolvedSessionKey = normalizeSessionKeyForDefaults(host.sessionKey, defaults);\n const resolvedSettingsSessionKey = normalizeSessionKeyForDefaults(\n host.settings.sessionKey,\n defaults,\n );\n const resolvedLastActiveSessionKey = normalizeSessionKeyForDefaults(\n host.settings.lastActiveSessionKey,\n defaults,\n );\n const nextSessionKey = resolvedSessionKey || resolvedSettingsSessionKey || host.sessionKey;\n const nextSettings = {\n ...host.settings,\n sessionKey: resolvedSettingsSessionKey || nextSessionKey,\n lastActiveSessionKey: resolvedLastActiveSessionKey || nextSessionKey,\n };\n const shouldUpdateSettings =\n nextSettings.sessionKey !== host.settings.sessionKey ||\n nextSettings.lastActiveSessionKey !== host.settings.lastActiveSessionKey;\n if (nextSessionKey !== host.sessionKey) {\n host.sessionKey = nextSessionKey;\n }\n if (shouldUpdateSettings) {\n applySettings(host as unknown as Parameters[0], nextSettings);\n }\n}\n\nexport function connectGateway(host: GatewayHost) {\n host.lastError = null;\n host.hello = null;\n host.connected = false;\n host.execApprovalQueue = [];\n host.execApprovalError = null;\n\n host.client?.stop();\n host.client = new GatewayBrowserClient({\n url: host.settings.gatewayUrl,\n token: host.settings.token.trim() ? host.settings.token : undefined,\n password: host.password.trim() ? host.password : undefined,\n clientName: \"clawdbot-control-ui\",\n mode: \"webchat\",\n onHello: (hello) => {\n host.connected = true;\n host.hello = hello;\n applySnapshot(host, hello);\n void loadAssistantIdentity(host as unknown as ClawdbotApp);\n void loadAgents(host as unknown as ClawdbotApp);\n void loadNodes(host as unknown as ClawdbotApp, { quiet: true });\n void loadDevices(host as unknown as ClawdbotApp, { quiet: true });\n void refreshActiveTab(host as unknown as Parameters[0]);\n },\n onClose: ({ code, reason }) => {\n host.connected = false;\n host.lastError = `disconnected (${code}): ${reason || \"no reason\"}`;\n },\n onEvent: (evt) => handleGatewayEvent(host, evt),\n onGap: ({ expected, received }) => {\n host.lastError = `event gap detected (expected seq ${expected}, got ${received}); refresh recommended`;\n },\n });\n host.client.start();\n}\n\nexport function handleGatewayEvent(host: GatewayHost, evt: GatewayEventFrame) {\n try {\n handleGatewayEventUnsafe(host, evt);\n } catch (err) {\n console.error(\"[gateway] handleGatewayEvent error:\", evt.event, err);\n }\n}\n\nfunction handleGatewayEventUnsafe(host: GatewayHost, evt: GatewayEventFrame) {\n host.eventLogBuffer = [\n { ts: Date.now(), event: evt.event, payload: evt.payload },\n ...host.eventLogBuffer,\n ].slice(0, 250);\n if (host.tab === \"debug\") {\n host.eventLog = host.eventLogBuffer;\n }\n\n if (evt.event === \"agent\") {\n if (host.onboarding) return;\n handleAgentEvent(\n host as unknown as Parameters[0],\n evt.payload as AgentEventPayload | undefined,\n );\n return;\n }\n\n if (evt.event === \"chat\") {\n const payload = evt.payload as ChatEventPayload | undefined;\n if (payload?.sessionKey) {\n setLastActiveSessionKey(\n host as unknown as Parameters[0],\n payload.sessionKey,\n );\n }\n const state = handleChatEvent(host as unknown as ClawdbotApp, payload);\n if (state === \"final\" || state === \"error\" || state === \"aborted\") {\n resetToolStream(host as unknown as Parameters[0]);\n void flushChatQueueForEvent(\n host as unknown as Parameters[0],\n );\n }\n if (state === \"final\") void loadChatHistory(host as unknown as ClawdbotApp);\n return;\n }\n\n if (evt.event === \"presence\") {\n const payload = evt.payload as { presence?: PresenceEntry[] } | undefined;\n if (payload?.presence && Array.isArray(payload.presence)) {\n host.presenceEntries = payload.presence;\n host.presenceError = null;\n host.presenceStatus = null;\n }\n return;\n }\n\n if (evt.event === \"cron\" && host.tab === \"cron\") {\n void loadCron(host as unknown as Parameters[0]);\n }\n\n if (evt.event === \"device.pair.requested\" || evt.event === \"device.pair.resolved\") {\n void loadDevices(host as unknown as ClawdbotApp, { quiet: true });\n }\n\n if (evt.event === \"exec.approval.requested\") {\n const entry = parseExecApprovalRequested(evt.payload);\n if (entry) {\n host.execApprovalQueue = addExecApproval(host.execApprovalQueue, entry);\n host.execApprovalError = null;\n const delay = Math.max(0, entry.expiresAtMs - Date.now() + 500);\n window.setTimeout(() => {\n host.execApprovalQueue = removeExecApproval(host.execApprovalQueue, entry.id);\n }, delay);\n }\n return;\n }\n\n if (evt.event === \"exec.approval.resolved\") {\n const resolved = parseExecApprovalResolved(evt.payload);\n if (resolved) {\n host.execApprovalQueue = removeExecApproval(host.execApprovalQueue, resolved.id);\n }\n }\n}\n\nexport function applySnapshot(host: GatewayHost, hello: GatewayHelloOk) {\n const snapshot = hello.snapshot as\n | {\n presence?: PresenceEntry[];\n health?: HealthSnapshot;\n sessionDefaults?: SessionDefaultsSnapshot;\n }\n | undefined;\n if (snapshot?.presence && Array.isArray(snapshot.presence)) {\n host.presenceEntries = snapshot.presence;\n }\n if (snapshot?.health) {\n host.debugHealth = snapshot.health;\n }\n if (snapshot?.sessionDefaults) {\n applySessionDefaults(host, snapshot.sessionDefaults);\n }\n}\n","import type { Tab } from \"./navigation\";\nimport { connectGateway } from \"./app-gateway\";\nimport {\n applySettingsFromUrl,\n attachThemeListener,\n detachThemeListener,\n inferBasePath,\n syncTabWithLocation,\n syncThemeWithSettings,\n} from \"./app-settings\";\nimport { observeTopbar, scheduleChatScroll, scheduleLogsScroll } from \"./app-scroll\";\nimport {\n startLogsPolling,\n startNodesPolling,\n stopLogsPolling,\n stopNodesPolling,\n startDebugPolling,\n stopDebugPolling,\n} from \"./app-polling\";\n\ntype LifecycleHost = {\n basePath: string;\n tab: Tab;\n chatHasAutoScrolled: boolean;\n chatLoading: boolean;\n chatMessages: unknown[];\n chatToolMessages: unknown[];\n chatStream: string;\n logsAutoFollow: boolean;\n logsAtBottom: boolean;\n logsEntries: unknown[];\n popStateHandler: () => void;\n topbarObserver: ResizeObserver | null;\n};\n\nexport function handleConnected(host: LifecycleHost) {\n host.basePath = inferBasePath();\n syncTabWithLocation(\n host as unknown as Parameters[0],\n true,\n );\n syncThemeWithSettings(\n host as unknown as Parameters[0],\n );\n attachThemeListener(\n host as unknown as Parameters[0],\n );\n window.addEventListener(\"popstate\", host.popStateHandler);\n applySettingsFromUrl(\n host as unknown as Parameters[0],\n );\n connectGateway(host as unknown as Parameters[0]);\n startNodesPolling(host as unknown as Parameters[0]);\n if (host.tab === \"logs\") {\n startLogsPolling(host as unknown as Parameters[0]);\n }\n if (host.tab === \"debug\") {\n startDebugPolling(host as unknown as Parameters[0]);\n }\n}\n\nexport function handleFirstUpdated(host: LifecycleHost) {\n observeTopbar(host as unknown as Parameters[0]);\n}\n\nexport function handleDisconnected(host: LifecycleHost) {\n window.removeEventListener(\"popstate\", host.popStateHandler);\n stopNodesPolling(host as unknown as Parameters[0]);\n stopLogsPolling(host as unknown as Parameters[0]);\n stopDebugPolling(host as unknown as Parameters[0]);\n detachThemeListener(\n host as unknown as Parameters[0],\n );\n host.topbarObserver?.disconnect();\n host.topbarObserver = null;\n}\n\nexport function handleUpdated(\n host: LifecycleHost,\n changed: Map,\n) {\n if (\n host.tab === \"chat\" &&\n (changed.has(\"chatMessages\") ||\n changed.has(\"chatToolMessages\") ||\n changed.has(\"chatStream\") ||\n changed.has(\"chatLoading\") ||\n changed.has(\"tab\"))\n ) {\n const forcedByTab = changed.has(\"tab\");\n const forcedByLoad =\n changed.has(\"chatLoading\") &&\n changed.get(\"chatLoading\") === true &&\n host.chatLoading === false;\n scheduleChatScroll(\n host as unknown as Parameters[0],\n forcedByTab || forcedByLoad || !host.chatHasAutoScrolled,\n );\n }\n if (\n host.tab === \"logs\" &&\n (changed.has(\"logsEntries\") || changed.has(\"logsAutoFollow\") || changed.has(\"tab\"))\n ) {\n if (host.logsAutoFollow && host.logsAtBottom) {\n scheduleLogsScroll(\n host as unknown as Parameters[0],\n changed.has(\"tab\") || changed.has(\"logsAutoFollow\"),\n );\n }\n }\n}\n","import {\n loadChannels,\n logoutWhatsApp,\n startWhatsAppLogin,\n waitWhatsAppLogin,\n} from \"./controllers/channels\";\nimport { loadConfig, saveConfig } from \"./controllers/config\";\nimport type { ClawdbotApp } from \"./app\";\nimport type { NostrProfile } from \"./types\";\nimport { createNostrProfileFormState } from \"./views/channels.nostr-profile-form\";\n\nexport async function handleWhatsAppStart(host: ClawdbotApp, force: boolean) {\n await startWhatsAppLogin(host, force);\n await loadChannels(host, true);\n}\n\nexport async function handleWhatsAppWait(host: ClawdbotApp) {\n await waitWhatsAppLogin(host);\n await loadChannels(host, true);\n}\n\nexport async function handleWhatsAppLogout(host: ClawdbotApp) {\n await logoutWhatsApp(host);\n await loadChannels(host, true);\n}\n\nexport async function handleChannelConfigSave(host: ClawdbotApp) {\n await saveConfig(host);\n await loadConfig(host);\n await loadChannels(host, true);\n}\n\nexport async function handleChannelConfigReload(host: ClawdbotApp) {\n await loadConfig(host);\n await loadChannels(host, true);\n}\n\nfunction parseValidationErrors(details: unknown): Record {\n if (!Array.isArray(details)) return {};\n const errors: Record = {};\n for (const entry of details) {\n if (typeof entry !== \"string\") continue;\n const [rawField, ...rest] = entry.split(\":\");\n if (!rawField || rest.length === 0) continue;\n const field = rawField.trim();\n const message = rest.join(\":\").trim();\n if (field && message) errors[field] = message;\n }\n return errors;\n}\n\nfunction resolveNostrAccountId(host: ClawdbotApp): string {\n const accounts = host.channelsSnapshot?.channelAccounts?.nostr ?? [];\n return accounts[0]?.accountId ?? host.nostrProfileAccountId ?? \"default\";\n}\n\nfunction buildNostrProfileUrl(accountId: string, suffix = \"\"): string {\n return `/api/channels/nostr/${encodeURIComponent(accountId)}/profile${suffix}`;\n}\n\nexport function handleNostrProfileEdit(\n host: ClawdbotApp,\n accountId: string,\n profile: NostrProfile | null,\n) {\n host.nostrProfileAccountId = accountId;\n host.nostrProfileFormState = createNostrProfileFormState(profile ?? undefined);\n}\n\nexport function handleNostrProfileCancel(host: ClawdbotApp) {\n host.nostrProfileFormState = null;\n host.nostrProfileAccountId = null;\n}\n\nexport function handleNostrProfileFieldChange(\n host: ClawdbotApp,\n field: keyof NostrProfile,\n value: string,\n) {\n const state = host.nostrProfileFormState;\n if (!state) return;\n host.nostrProfileFormState = {\n ...state,\n values: {\n ...state.values,\n [field]: value,\n },\n fieldErrors: {\n ...state.fieldErrors,\n [field]: \"\",\n },\n };\n}\n\nexport function handleNostrProfileToggleAdvanced(host: ClawdbotApp) {\n const state = host.nostrProfileFormState;\n if (!state) return;\n host.nostrProfileFormState = {\n ...state,\n showAdvanced: !state.showAdvanced,\n };\n}\n\nexport async function handleNostrProfileSave(host: ClawdbotApp) {\n const state = host.nostrProfileFormState;\n if (!state || state.saving) return;\n const accountId = resolveNostrAccountId(host);\n\n host.nostrProfileFormState = {\n ...state,\n saving: true,\n error: null,\n success: null,\n fieldErrors: {},\n };\n\n try {\n const response = await fetch(buildNostrProfileUrl(accountId), {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(state.values),\n });\n const data = (await response.json().catch(() => null)) as\n | { ok?: boolean; error?: string; details?: unknown; persisted?: boolean }\n | null;\n\n if (!response.ok || data?.ok === false || !data) {\n const errorMessage = data?.error ?? `Profile update failed (${response.status})`;\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: errorMessage,\n success: null,\n fieldErrors: parseValidationErrors(data?.details),\n };\n return;\n }\n\n if (!data.persisted) {\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: \"Profile publish failed on all relays.\",\n success: null,\n };\n return;\n }\n\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: null,\n success: \"Profile published to relays.\",\n fieldErrors: {},\n original: { ...state.values },\n };\n await loadChannels(host, true);\n } catch (err) {\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: `Profile update failed: ${String(err)}`,\n success: null,\n };\n }\n}\n\nexport async function handleNostrProfileImport(host: ClawdbotApp) {\n const state = host.nostrProfileFormState;\n if (!state || state.importing) return;\n const accountId = resolveNostrAccountId(host);\n\n host.nostrProfileFormState = {\n ...state,\n importing: true,\n error: null,\n success: null,\n };\n\n try {\n const response = await fetch(buildNostrProfileUrl(accountId, \"/import\"), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ autoMerge: true }),\n });\n const data = (await response.json().catch(() => null)) as\n | { ok?: boolean; error?: string; imported?: NostrProfile; merged?: NostrProfile; saved?: boolean }\n | null;\n\n if (!response.ok || data?.ok === false || !data) {\n const errorMessage = data?.error ?? `Profile import failed (${response.status})`;\n host.nostrProfileFormState = {\n ...state,\n importing: false,\n error: errorMessage,\n success: null,\n };\n return;\n }\n\n const merged = data.merged ?? data.imported ?? null;\n const nextValues = merged ? { ...state.values, ...merged } : state.values;\n const showAdvanced = Boolean(\n nextValues.banner || nextValues.website || nextValues.nip05 || nextValues.lud16,\n );\n\n host.nostrProfileFormState = {\n ...state,\n importing: false,\n values: nextValues,\n error: null,\n success: data.saved\n ? \"Profile imported from relays. Review and publish.\"\n : \"Profile imported. Review and publish.\",\n showAdvanced,\n };\n\n if (data.saved) {\n await loadChannels(host, true);\n }\n } catch (err) {\n host.nostrProfileFormState = {\n ...state,\n importing: false,\n error: `Profile import failed: ${String(err)}`,\n success: null,\n };\n }\n}\n","import { LitElement, html, nothing } from \"lit\";\nimport { customElement, state } from \"lit/decorators.js\";\n\nimport type { GatewayBrowserClient, GatewayHelloOk } from \"./gateway\";\nimport { resolveInjectedAssistantIdentity } from \"./assistant-identity\";\nimport { loadSettings, type UiSettings } from \"./storage\";\nimport { renderApp } from \"./app-render\";\nimport type { Tab } from \"./navigation\";\nimport type { ResolvedTheme, ThemeMode } from \"./theme\";\nimport type {\n AgentsListResult,\n ConfigSnapshot,\n ConfigUiHints,\n CronJob,\n CronRunLogEntry,\n CronStatus,\n HealthSnapshot,\n LogEntry,\n LogLevel,\n PresenceEntry,\n ChannelsStatusSnapshot,\n SessionsListResult,\n SkillStatusReport,\n StatusSummary,\n NostrProfile,\n} from \"./types\";\nimport { type ChatQueueItem, type CronFormState } from \"./ui-types\";\nimport type { EventLogEntry } from \"./app-events\";\nimport { DEFAULT_CRON_FORM, DEFAULT_LOG_LEVEL_FILTERS } from \"./app-defaults\";\nimport type {\n ExecApprovalsFile,\n ExecApprovalsSnapshot,\n} from \"./controllers/exec-approvals\";\nimport type { DevicePairingList } from \"./controllers/devices\";\nimport type { ExecApprovalRequest } from \"./controllers/exec-approval\";\nimport {\n resetToolStream as resetToolStreamInternal,\n type ToolStreamEntry,\n} from \"./app-tool-stream\";\nimport {\n exportLogs as exportLogsInternal,\n handleChatScroll as handleChatScrollInternal,\n handleLogsScroll as handleLogsScrollInternal,\n resetChatScroll as resetChatScrollInternal,\n} from \"./app-scroll\";\nimport { connectGateway as connectGatewayInternal } from \"./app-gateway\";\nimport {\n handleConnected,\n handleDisconnected,\n handleFirstUpdated,\n handleUpdated,\n} from \"./app-lifecycle\";\nimport {\n applySettings as applySettingsInternal,\n loadCron as loadCronInternal,\n loadOverview as loadOverviewInternal,\n setTab as setTabInternal,\n setTheme as setThemeInternal,\n onPopState as onPopStateInternal,\n} from \"./app-settings\";\nimport {\n handleAbortChat as handleAbortChatInternal,\n handleSendChat as handleSendChatInternal,\n removeQueuedMessage as removeQueuedMessageInternal,\n} from \"./app-chat\";\nimport {\n handleChannelConfigReload as handleChannelConfigReloadInternal,\n handleChannelConfigSave as handleChannelConfigSaveInternal,\n handleNostrProfileCancel as handleNostrProfileCancelInternal,\n handleNostrProfileEdit as handleNostrProfileEditInternal,\n handleNostrProfileFieldChange as handleNostrProfileFieldChangeInternal,\n handleNostrProfileImport as handleNostrProfileImportInternal,\n handleNostrProfileSave as handleNostrProfileSaveInternal,\n handleNostrProfileToggleAdvanced as handleNostrProfileToggleAdvancedInternal,\n handleWhatsAppLogout as handleWhatsAppLogoutInternal,\n handleWhatsAppStart as handleWhatsAppStartInternal,\n handleWhatsAppWait as handleWhatsAppWaitInternal,\n} from \"./app-channels\";\nimport type { NostrProfileFormState } from \"./views/channels.nostr-profile-form\";\nimport { loadAssistantIdentity as loadAssistantIdentityInternal } from \"./controllers/assistant-identity\";\n\ndeclare global {\n interface Window {\n __CLAWDBOT_CONTROL_UI_BASE_PATH__?: string;\n }\n}\n\nconst injectedAssistantIdentity = resolveInjectedAssistantIdentity();\n\nfunction resolveOnboardingMode(): boolean {\n if (!window.location.search) return false;\n const params = new URLSearchParams(window.location.search);\n const raw = params.get(\"onboarding\");\n if (!raw) return false;\n const normalized = raw.trim().toLowerCase();\n return normalized === \"1\" || normalized === \"true\" || normalized === \"yes\" || normalized === \"on\";\n}\n\n@customElement(\"clawdbot-app\")\nexport class ClawdbotApp extends LitElement {\n @state() settings: UiSettings = loadSettings();\n @state() password = \"\";\n @state() tab: Tab = \"chat\";\n @state() onboarding = resolveOnboardingMode();\n @state() connected = false;\n @state() theme: ThemeMode = this.settings.theme ?? \"system\";\n @state() themeResolved: ResolvedTheme = \"dark\";\n @state() hello: GatewayHelloOk | null = null;\n @state() lastError: string | null = null;\n @state() eventLog: EventLogEntry[] = [];\n private eventLogBuffer: EventLogEntry[] = [];\n private toolStreamSyncTimer: number | null = null;\n private sidebarCloseTimer: number | null = null;\n\n @state() assistantName = injectedAssistantIdentity.name;\n @state() assistantAvatar = injectedAssistantIdentity.avatar;\n @state() assistantAgentId = injectedAssistantIdentity.agentId ?? null;\n\n @state() sessionKey = this.settings.sessionKey;\n @state() chatLoading = false;\n @state() chatSending = false;\n @state() chatMessage = \"\";\n @state() chatMessages: unknown[] = [];\n @state() chatToolMessages: unknown[] = [];\n @state() chatStream: string | null = null;\n @state() chatStreamStartedAt: number | null = null;\n @state() chatRunId: string | null = null;\n @state() compactionStatus: import(\"./app-tool-stream\").CompactionStatus | null = null;\n @state() chatAvatarUrl: string | null = null;\n @state() chatThinkingLevel: string | null = null;\n @state() chatQueue: ChatQueueItem[] = [];\n // Sidebar state for tool output viewing\n @state() sidebarOpen = false;\n @state() sidebarContent: string | null = null;\n @state() sidebarError: string | null = null;\n @state() splitRatio = this.settings.splitRatio;\n\n @state() nodesLoading = false;\n @state() nodes: Array> = [];\n @state() devicesLoading = false;\n @state() devicesError: string | null = null;\n @state() devicesList: DevicePairingList | null = null;\n @state() execApprovalsLoading = false;\n @state() execApprovalsSaving = false;\n @state() execApprovalsDirty = false;\n @state() execApprovalsSnapshot: ExecApprovalsSnapshot | null = null;\n @state() execApprovalsForm: ExecApprovalsFile | null = null;\n @state() execApprovalsSelectedAgent: string | null = null;\n @state() execApprovalsTarget: \"gateway\" | \"node\" = \"gateway\";\n @state() execApprovalsTargetNodeId: string | null = null;\n @state() execApprovalQueue: ExecApprovalRequest[] = [];\n @state() execApprovalBusy = false;\n @state() execApprovalError: string | null = null;\n\n @state() configLoading = false;\n @state() configRaw = \"{\\n}\\n\";\n @state() configValid: boolean | null = null;\n @state() configIssues: unknown[] = [];\n @state() configSaving = false;\n @state() configApplying = false;\n @state() updateRunning = false;\n @state() applySessionKey = this.settings.lastActiveSessionKey;\n @state() configSnapshot: ConfigSnapshot | null = null;\n @state() configSchema: unknown | null = null;\n @state() configSchemaVersion: string | null = null;\n @state() configSchemaLoading = false;\n @state() configUiHints: ConfigUiHints = {};\n @state() configForm: Record | null = null;\n @state() configFormOriginal: Record | null = null;\n @state() configFormDirty = false;\n @state() configFormMode: \"form\" | \"raw\" = \"form\";\n @state() configSearchQuery = \"\";\n @state() configActiveSection: string | null = null;\n @state() configActiveSubsection: string | null = null;\n\n @state() channelsLoading = false;\n @state() channelsSnapshot: ChannelsStatusSnapshot | null = null;\n @state() channelsError: string | null = null;\n @state() channelsLastSuccess: number | null = null;\n @state() whatsappLoginMessage: string | null = null;\n @state() whatsappLoginQrDataUrl: string | null = null;\n @state() whatsappLoginConnected: boolean | null = null;\n @state() whatsappBusy = false;\n @state() nostrProfileFormState: NostrProfileFormState | null = null;\n @state() nostrProfileAccountId: string | null = null;\n\n @state() presenceLoading = false;\n @state() presenceEntries: PresenceEntry[] = [];\n @state() presenceError: string | null = null;\n @state() presenceStatus: string | null = null;\n\n @state() agentsLoading = false;\n @state() agentsList: AgentsListResult | null = null;\n @state() agentsError: string | null = null;\n\n @state() sessionsLoading = false;\n @state() sessionsResult: SessionsListResult | null = null;\n @state() sessionsError: string | null = null;\n @state() sessionsFilterActive = \"\";\n @state() sessionsFilterLimit = \"120\";\n @state() sessionsIncludeGlobal = true;\n @state() sessionsIncludeUnknown = false;\n\n @state() cronLoading = false;\n @state() cronJobs: CronJob[] = [];\n @state() cronStatus: CronStatus | null = null;\n @state() cronError: string | null = null;\n @state() cronForm: CronFormState = { ...DEFAULT_CRON_FORM };\n @state() cronRunsJobId: string | null = null;\n @state() cronRuns: CronRunLogEntry[] = [];\n @state() cronBusy = false;\n\n @state() skillsLoading = false;\n @state() skillsReport: SkillStatusReport | null = null;\n @state() skillsError: string | null = null;\n @state() skillsFilter = \"\";\n @state() skillEdits: Record = {};\n @state() skillsBusyKey: string | null = null;\n @state() skillMessages: Record = {};\n\n @state() debugLoading = false;\n @state() debugStatus: StatusSummary | null = null;\n @state() debugHealth: HealthSnapshot | null = null;\n @state() debugModels: unknown[] = [];\n @state() debugHeartbeat: unknown | null = null;\n @state() debugCallMethod = \"\";\n @state() debugCallParams = \"{}\";\n @state() debugCallResult: string | null = null;\n @state() debugCallError: string | null = null;\n\n @state() logsLoading = false;\n @state() logsError: string | null = null;\n @state() logsFile: string | null = null;\n @state() logsEntries: LogEntry[] = [];\n @state() logsFilterText = \"\";\n @state() logsLevelFilters: Record = {\n ...DEFAULT_LOG_LEVEL_FILTERS,\n };\n @state() logsAutoFollow = true;\n @state() logsTruncated = false;\n @state() logsCursor: number | null = null;\n @state() logsLastFetchAt: number | null = null;\n @state() logsLimit = 500;\n @state() logsMaxBytes = 250_000;\n @state() logsAtBottom = true;\n\n client: GatewayBrowserClient | null = null;\n private chatScrollFrame: number | null = null;\n private chatScrollTimeout: number | null = null;\n private chatHasAutoScrolled = false;\n private chatUserNearBottom = true;\n private nodesPollInterval: number | null = null;\n private logsPollInterval: number | null = null;\n private debugPollInterval: number | null = null;\n private logsScrollFrame: number | null = null;\n private toolStreamById = new Map();\n private toolStreamOrder: string[] = [];\n basePath = \"\";\n private popStateHandler = () =>\n onPopStateInternal(\n this as unknown as Parameters[0],\n );\n private themeMedia: MediaQueryList | null = null;\n private themeMediaHandler: ((event: MediaQueryListEvent) => void) | null = null;\n private topbarObserver: ResizeObserver | null = null;\n\n createRenderRoot() {\n return this;\n }\n\n connectedCallback() {\n super.connectedCallback();\n handleConnected(this as unknown as Parameters[0]);\n }\n\n protected firstUpdated() {\n handleFirstUpdated(this as unknown as Parameters[0]);\n }\n\n disconnectedCallback() {\n handleDisconnected(this as unknown as Parameters[0]);\n super.disconnectedCallback();\n }\n\n protected updated(changed: Map) {\n handleUpdated(\n this as unknown as Parameters[0],\n changed,\n );\n }\n\n connect() {\n connectGatewayInternal(\n this as unknown as Parameters[0],\n );\n }\n\n handleChatScroll(event: Event) {\n handleChatScrollInternal(\n this as unknown as Parameters[0],\n event,\n );\n }\n\n handleLogsScroll(event: Event) {\n handleLogsScrollInternal(\n this as unknown as Parameters[0],\n event,\n );\n }\n\n exportLogs(lines: string[], label: string) {\n exportLogsInternal(lines, label);\n }\n\n resetToolStream() {\n resetToolStreamInternal(\n this as unknown as Parameters[0],\n );\n }\n\n resetChatScroll() {\n resetChatScrollInternal(\n this as unknown as Parameters[0],\n );\n }\n\n async loadAssistantIdentity() {\n await loadAssistantIdentityInternal(this);\n }\n\n applySettings(next: UiSettings) {\n applySettingsInternal(\n this as unknown as Parameters[0],\n next,\n );\n }\n\n setTab(next: Tab) {\n setTabInternal(this as unknown as Parameters[0], next);\n }\n\n setTheme(next: ThemeMode, context?: Parameters[2]) {\n setThemeInternal(\n this as unknown as Parameters[0],\n next,\n context,\n );\n }\n\n async loadOverview() {\n await loadOverviewInternal(\n this as unknown as Parameters[0],\n );\n }\n\n async loadCron() {\n await loadCronInternal(\n this as unknown as Parameters[0],\n );\n }\n\n async handleAbortChat() {\n await handleAbortChatInternal(\n this as unknown as Parameters[0],\n );\n }\n\n removeQueuedMessage(id: string) {\n removeQueuedMessageInternal(\n this as unknown as Parameters[0],\n id,\n );\n }\n\n async handleSendChat(\n messageOverride?: string,\n opts?: Parameters[2],\n ) {\n await handleSendChatInternal(\n this as unknown as Parameters[0],\n messageOverride,\n opts,\n );\n }\n\n async handleWhatsAppStart(force: boolean) {\n await handleWhatsAppStartInternal(this, force);\n }\n\n async handleWhatsAppWait() {\n await handleWhatsAppWaitInternal(this);\n }\n\n async handleWhatsAppLogout() {\n await handleWhatsAppLogoutInternal(this);\n }\n\n async handleChannelConfigSave() {\n await handleChannelConfigSaveInternal(this);\n }\n\n async handleChannelConfigReload() {\n await handleChannelConfigReloadInternal(this);\n }\n\n handleNostrProfileEdit(accountId: string, profile: NostrProfile | null) {\n handleNostrProfileEditInternal(this, accountId, profile);\n }\n\n handleNostrProfileCancel() {\n handleNostrProfileCancelInternal(this);\n }\n\n handleNostrProfileFieldChange(field: keyof NostrProfile, value: string) {\n handleNostrProfileFieldChangeInternal(this, field, value);\n }\n\n async handleNostrProfileSave() {\n await handleNostrProfileSaveInternal(this);\n }\n\n async handleNostrProfileImport() {\n await handleNostrProfileImportInternal(this);\n }\n\n handleNostrProfileToggleAdvanced() {\n handleNostrProfileToggleAdvancedInternal(this);\n }\n\n async handleExecApprovalDecision(decision: \"allow-once\" | \"allow-always\" | \"deny\") {\n const active = this.execApprovalQueue[0];\n if (!active || !this.client || this.execApprovalBusy) return;\n this.execApprovalBusy = true;\n this.execApprovalError = null;\n try {\n await this.client.request(\"exec.approval.resolve\", {\n id: active.id,\n decision,\n });\n this.execApprovalQueue = this.execApprovalQueue.filter((entry) => entry.id !== active.id);\n } catch (err) {\n this.execApprovalError = `Exec approval failed: ${String(err)}`;\n } finally {\n this.execApprovalBusy = false;\n }\n }\n\n // Sidebar handlers for tool output viewing\n handleOpenSidebar(content: string) {\n if (this.sidebarCloseTimer != null) {\n window.clearTimeout(this.sidebarCloseTimer);\n this.sidebarCloseTimer = null;\n }\n this.sidebarContent = content;\n this.sidebarError = null;\n this.sidebarOpen = true;\n }\n\n handleCloseSidebar() {\n this.sidebarOpen = false;\n // Clear content after transition\n if (this.sidebarCloseTimer != null) {\n window.clearTimeout(this.sidebarCloseTimer);\n }\n this.sidebarCloseTimer = window.setTimeout(() => {\n if (this.sidebarOpen) return;\n this.sidebarContent = null;\n this.sidebarError = null;\n this.sidebarCloseTimer = null;\n }, 200);\n }\n\n handleSplitRatioChange(ratio: number) {\n const newRatio = Math.max(0.4, Math.min(0.7, ratio));\n this.splitRatio = newRatio;\n this.applySettings({ ...this.settings, splitRatio: newRatio });\n }\n\n render() {\n return renderApp(this);\n }\n}\n"],"names":["t","e","s","o","n$3","r","n","i","S","c","h","a","l","p","d","u","f","b","y$2","y","v","_","m","g","$","x","E","A","C","P","V","N","S$1","I","L","z","H","M","R","k","Z","I$2","Z$1","j","B","D","MAX_ASSISTANT_NAME","MAX_ASSISTANT_AVATAR","DEFAULT_ASSISTANT_NAME","coerceIdentityValue","value","maxLength","trimmed","normalizeAssistantIdentity","input","name","avatar","resolveInjectedAssistantIdentity","KEY","loadSettings","defaults","raw","parsed","saveSettings","next","parseAgentSessionKey","sessionKey","parts","agentId","rest","TAB_GROUPS","TAB_PATHS","PATH_TO_TAB","tab","path","normalizeBasePath","basePath","base","normalizePath","normalized","pathForTab","tabFromPath","pathname","inferBasePathFromPathname","segments","candidate","prefix","iconForTab","titleForTab","subtitleForTab","formatMs","ms","formatAgo","diff","sec","min","hr","formatDurationMs","formatList","values","clampText","max","truncateText","toNumber","fallback","THINKING_TAG_RE","THINKING_OPEN_RE","THINKING_CLOSE_RE","stripThinkingTags","hasOpen","hasClose","result","lastIndex","inThinking","match","idx","ENVELOPE_PREFIX","ENVELOPE_CHANNELS","textCache","thinkingCache","looksLikeEnvelopeHeader","header","label","stripEnvelope","text","extractText","message","role","content","item","joined","extractTextCached","obj","extractThinking","cleaned","rawText","extractRawText","extracted","extractThinkingCached","formatReasoningMarkdown","lines","line","uuidFromBytes","bytes","hex","weakRandomBytes","now","generateUUID","cryptoLike","loadChatHistory","state","res","err","sendChatMessage","msg","runId","error","abortChatRun","handleChatEvent","payload","current","loadSessions","params","activeMinutes","limit","patchSession","key","patch","deleteSession","TOOL_STREAM_LIMIT","TOOL_STREAM_THROTTLE_MS","TOOL_OUTPUT_CHAR_LIMIT","extractToolOutputText","record","entry","part","formatToolOutput","contentText","truncated","buildToolStreamMessage","trimToolStream","host","overflow","removed","id","syncToolStreamMessages","flushToolStreamSync","scheduleToolStreamSync","force","resetToolStream","COMPACTION_TOAST_DURATION_MS","handleCompactionEvent","data","phase","handleAgentEvent","toolCallId","args","output","scheduleChatScroll","pickScrollTarget","container","overflowY","target","distanceFromBottom","retryDelay","latest","latestDistanceFromBottom","scheduleLogsScroll","handleChatScroll","event","handleLogsScroll","resetChatScroll","exportLogs","blob","url","anchor","stamp","observeTopbar","topbar","update","height","cloneConfigObject","serializeConfigForm","form","setPathValue","nextKey","lastKey","removePathValue","loadConfig","applyConfigSnapshot","loadConfigSchema","applyConfigSchema","snapshot","rawFromSnapshot","saveConfig","baseHash","applyConfig","runUpdate","updateConfigFormValue","removeConfigFormValue","loadCronStatus","loadCronJobs","buildCronSchedule","amount","unit","expr","buildCronPayload","timeoutSeconds","addCronJob","schedule","job","toggleCronJob","enabled","runCronJob","loadCronRuns","removeCronJob","jobId","loadChannels","probe","startWhatsAppLogin","waitWhatsAppLogin","logoutWhatsApp","loadDebug","status","health","models","heartbeat","modelPayload","callDebugMethod","LOG_BUFFER_LIMIT","LEVELS","parseMaybeJsonString","normalizeLevel","lowered","parseLogLine","meta","time","level","contextCandidate","contextObj","subsystem","loadLogs","opts","entries","shouldReset","ed25519_CURVE","Gx","Gy","_a","_d","L2","captureTrace","isBig","isStr","isBytes","abytes","length","title","len","needsLen","ofLen","got","u8n","u8fr","buf","padh","pad","bytesToHex","_ch","ch","hexToBytes","hl","al","array","ai","hi","n1","n2","cr","subtle","concatBytes","arrs","sum","randomBytes","big","assertRange","modN","invert","num","md","q","callHash","fn","hashes","apoint","Point","B256","X","Y","T","zip215","normed","lastByte","bytesToNumLE","y2","isValid","uvRatio","isXOdd","isLastByteOdd","X2","Y2","Z2","Z4","aX2","left","right","XY","ZT","other","X1","Y1","Z1","X1Z2","X2Z1","Y1Z2","Y2Z1","x1y1","G","F","X3","Y3","T3","Z3","T1","T2","safe","wNAF","scalar","iz","numTo32bLE","pow2","power","pow_2_252_3","b2","b4","b5","b10","b20","b40","b80","b160","b240","b250","RM1","v3","v7","pow","vx2","root1","root2","useRoot1","useRoot2","noRoot","modL_LE","hash","sha512a","sha512s","hash2extK","hashed","head","point","pointBytes","getExtendedPublicKeyAsync","secretKey","getExtendedPublicKey","getPublicKeyAsync","hashFinishA","_sign","rBytes","signAsync","randomSecretKey","seed","utils","W","scalarBits","pwindows","pwindowSize","precompute","points","w","Gpows","ctneg","cnd","comp","pow_2_w","maxNum","mask","shiftBy","wbits","off","offF","offP","isEven","isNeg","STORAGE_KEY","base64UrlEncode","binary","byte","base64UrlDecode","padded","out","fingerprintPublicKey","publicKey","generateIdentity","privateKey","loadOrCreateDeviceIdentity","derivedId","updated","identity","stored","signDevicePayload","privateKeyBase64Url","sig","normalizeRole","normalizeScopes","scopes","scope","readStore","writeStore","store","loadDeviceAuthToken","storeDeviceAuthToken","existing","clearDeviceAuthToken","loadDevices","approveDevicePairing","requestId","rejectDevicePairing","rotateDeviceToken","revokeDeviceToken","loadNodes","resolveExecApprovalsRpc","nodeId","resolveExecApprovalsSaveRpc","loadExecApprovals","rpc","applyExecApprovalsSnapshot","saveExecApprovals","file","updateExecApprovalsFormValue","removeExecApprovalsFormValue","loadPresence","setSkillMessage","getErrorMessage","loadSkills","options","updateSkillEdit","skillKey","updateSkillEnabled","saveSkillApiKey","apiKey","installSkill","installId","getSystemTheme","resolveTheme","mode","clamp01","hasReducedMotionPreference","cleanupThemeTransition","root","startThemeTransition","nextTheme","applyTheme","context","currentTheme","documentReference","document_","prefersReducedMotion","xPercent","yPercent","rect","transition","startNodesPolling","stopNodesPolling","startLogsPolling","stopLogsPolling","startDebugPolling","stopDebugPolling","applySettings","applyResolvedTheme","setLastActiveSessionKey","applySettingsFromUrl","tokenRaw","passwordRaw","sessionRaw","gatewayUrlRaw","shouldCleanUrl","token","password","session","gatewayUrl","setTab","refreshActiveTab","syncUrlWithTab","setTheme","loadOverview","loadChannelsTab","loadCron","refreshChat","inferBasePath","configured","syncThemeWithSettings","resolved","attachThemeListener","detachThemeListener","syncTabWithLocation","replace","setTabFromRoute","onPopState","targetPath","currentPath","syncUrlWithSessionKey","isChatBusy","isChatStopCommand","handleAbortChat","enqueueChatMessage","sendChatMessageNow","ok","flushChatQueue","removeQueuedMessage","handleSendChat","messageOverride","previousDraft","refreshChatAvatar","flushChatQueueForEvent","resolveAgentIdForSession","buildAvatarMetaUrl","encoded","avatarUrl","i$1","normalizeMessage","hasToolId","contentRaw","contentItems","hasToolContent","hasToolName","timestamp","normalizeRoleForGrouping","lower","isToolResultMessage","setPrototypeOf","isFrozen","getPrototypeOf","getOwnPropertyDescriptor","freeze","seal","create","apply","construct","func","thisArg","_len","_key","Func","_len2","_key2","arrayForEach","unapply","arrayLastIndexOf","arrayPop","arrayPush","arraySplice","stringToLowerCase","stringToString","stringMatch","stringReplace","stringIndexOf","stringTrim","objectHasOwnProperty","regExpTest","typeErrorCreate","unconstruct","_len3","_key3","_len4","_key4","addToSet","set","transformCaseFunc","element","lcElement","cleanArray","index","clone","object","newObject","property","lookupGetter","prop","desc","fallbackValue","html$1","svg$1","svgFilters","svgDisallowed","mathMl$1","mathMlDisallowed","html","svg","mathMl","xml","MUSTACHE_EXPR","ERB_EXPR","TMPLIT_EXPR","DATA_ATTR","ARIA_ATTR","IS_ALLOWED_URI","IS_SCRIPT_OR_DATA","ATTR_WHITESPACE","DOCTYPE_NAME","CUSTOM_ELEMENT","EXPRESSIONS","NODE_TYPE","getGlobal","_createTrustedTypesPolicy","trustedTypes","purifyHostElement","suffix","ATTR_NAME","policyName","scriptUrl","_createHooksMap","createDOMPurify","window","DOMPurify","document","originalDocument","currentScript","DocumentFragment","HTMLTemplateElement","Node","Element","NodeFilter","NamedNodeMap","HTMLFormElement","DOMParser","ElementPrototype","cloneNode","remove","getNextSibling","getChildNodes","getParentNode","template","trustedTypesPolicy","emptyHTML","implementation","createNodeIterator","createDocumentFragment","getElementsByTagName","importNode","hooks","IS_ALLOWED_URI$1","ALLOWED_TAGS","DEFAULT_ALLOWED_TAGS","ALLOWED_ATTR","DEFAULT_ALLOWED_ATTR","CUSTOM_ELEMENT_HANDLING","FORBID_TAGS","FORBID_ATTR","EXTRA_ELEMENT_HANDLING","ALLOW_ARIA_ATTR","ALLOW_DATA_ATTR","ALLOW_UNKNOWN_PROTOCOLS","ALLOW_SELF_CLOSE_IN_ATTR","SAFE_FOR_TEMPLATES","SAFE_FOR_XML","WHOLE_DOCUMENT","SET_CONFIG","FORCE_BODY","RETURN_DOM","RETURN_DOM_FRAGMENT","RETURN_TRUSTED_TYPE","SANITIZE_DOM","SANITIZE_NAMED_PROPS","SANITIZE_NAMED_PROPS_PREFIX","KEEP_CONTENT","IN_PLACE","USE_PROFILES","FORBID_CONTENTS","DEFAULT_FORBID_CONTENTS","DATA_URI_TAGS","DEFAULT_DATA_URI_TAGS","URI_SAFE_ATTRIBUTES","DEFAULT_URI_SAFE_ATTRIBUTES","MATHML_NAMESPACE","SVG_NAMESPACE","HTML_NAMESPACE","NAMESPACE","IS_EMPTY_INPUT","ALLOWED_NAMESPACES","DEFAULT_ALLOWED_NAMESPACES","MATHML_TEXT_INTEGRATION_POINTS","HTML_INTEGRATION_POINTS","COMMON_SVG_AND_HTML_ELEMENTS","PARSER_MEDIA_TYPE","SUPPORTED_PARSER_MEDIA_TYPES","DEFAULT_PARSER_MEDIA_TYPE","CONFIG","formElement","isRegexOrFunction","testValue","_parseConfig","cfg","ALL_SVG_TAGS","ALL_MATHML_TAGS","_checkValidNamespace","parent","tagName","parentTagName","_forceRemove","node","_removeAttribute","_initDocument","dirty","doc","leadingWhitespace","matches","dirtyPayload","body","_createNodeIterator","_isClobbered","_isNode","_executeHooks","currentNode","hook","_sanitizeElements","_isBasicCustomElement","parentNode","childNodes","childCount","childClone","_isValidAttribute","lcTag","lcName","_sanitizeAttributes","attributes","hookEvent","attr","namespaceURI","attrValue","initValue","_sanitizeShadowDOM","fragment","shadowNode","shadowIterator","importedNode","returnNode","nodeIterator","serializedHTML","tag","entryPoint","hookFunction","purify","me","xe","be","Re","Te","re","se","Oe","Q","we","ye","Pe","Se","ie","$e","U","te","_e","Le","Me","ze","oe","Ae","K","ae","Ce","le","Ie","Ee","Be","ue","qe","ve","pe","De","He","Ze","Ge","Ne","Qe","Fe","je","ce","he","Ue","ne","Ke","We","Xe","ke","J","de","ge","Je","O","ee","fe","marked","allowedTags","allowedAttrs","hooksInstalled","MARKDOWN_CHAR_LIMIT","MARKDOWN_PARSE_LIMIT","MARKDOWN_CACHE_LIMIT","MARKDOWN_CACHE_MAX_CHARS","markdownCache","getCachedMarkdown","cached","setCachedMarkdown","oldest","installHooks","toSanitizedMarkdownHtml","markdown","escapeHtml","sanitized","rendered","renderEmojiIcon","icon","className","setEmojiIcon","COPIED_FOR_MS","ERROR_FOR_MS","COPY_LABEL","COPIED_LABEL","ERROR_LABEL","COPY_ICON","COPIED_ICON","ERROR_ICON","copyTextToClipboard","setButtonLabel","button","createCopyButton","idleLabel","btn","copied","renderCopyAsMarkdownButton","TOOL_DISPLAY_CONFIG","rawConfig","FALLBACK","TOOL_MAP","normalizeToolName","defaultTitle","normalizeVerb","coerceDisplayValue","firstLine","preview","lookupValueByPath","segment","resolveDetailFromKeys","keys","display","resolveReadDetail","offset","resolveWriteDetail","resolveActionSpec","spec","action","resolveToolDisplay","emoji","actionRaw","actionSpec","verb","detail","detailKeys","shortenHomeInString","formatToolDetail","TOOL_INLINE_THRESHOLD","PREVIEW_MAX_LINES","PREVIEW_MAX_CHARS","formatToolOutputForSidebar","getTruncatedPreview","allLines","extractToolCards","normalizeContent","cards","kind","coerceArgs","extractToolText","card","renderToolCardSidebar","onOpenSidebar","hasText","canClick","handleClick","info","isShort","showCollapsed","showInline","isEmpty","nothing","renderReadingIndicatorGroup","assistant","renderAvatar","renderStreamingGroup","startedAt","renderGroupedMessage","renderMessageGroup","group","normalizedRole","assistantName","who","roleClass","assistantAvatar","initial","isAvatarUrl","isToolResult","toolCards","hasToolCards","extractedText","extractedThinking","markdownBase","reasoningMarkdown","canCopyMarkdown","bubbleClasses","unsafeHTML","renderMarkdownSidebar","props","ResizableDivider","LitElement","containerWidth","deltaRatio","newRatio","css","__decorateClass","customElement","renderCompactionIndicator","renderChat","canCompose","isBusy","reasoningLevel","row","showReasoning","assistantIdentity","composePlaceholder","splitRatio","sidebarOpen","thread","repeat","buildChatItems","CHAT_HISTORY_RENDER_LIMIT","groupMessages","items","currentGroup","history","tools","historyStart","messageKey","messageId","schemaType","schema","defaultValue","pathKey","hintForPath","hints","direct","hintKey","hint","hintSegments","humanize","isSensitivePath","META_KEYS","isAnySchema","jsonValue","icons","renderNode","unsupported","disabled","onPatch","showLabel","type","help","nonNull","extractLiteral","literals","allLiterals","resolvedValue","lit","renderSelect","primitiveTypes","variant","normalizedTypes","hasString","hasNumber","renderTextInput","opt","renderObject","renderArray","displayValue","renderNumberInput","inputType","isSensitive","placeholder","numValue","currentIndex","unset","val","sorted","orderA","orderB","reserved","additional","allowExtra","propKey","renderMapField","itemsSchema","arr","reservedKeys","anySchema","entryValue","valuePath","sectionIcons","SECTION_META","getSectionIcon","matchesSearch","query","schemaMatches","propSchema","unions","renderConfigForm","properties","searchQuery","activeSection","activeSubsection","filteredEntries","subsectionContext","sectionSchema","sectionKey","subsectionKey","description","sectionValue","scopedValue","normalizeEnum","filtered","nullable","enumValues","analyzeConfigSchema","normalizeSchemaNode","pathLabel","union","normalizeUnion","enumNullable","normalizedProps","remaining","unique","sidebarIcons","SECTIONS","ALL_SUBSECTION","resolveSectionMeta","resolveSubsections","uiHints","subKey","order","computeDiff","original","changes","compare","orig","curr","origObj","currObj","allKeys","truncateValue","maxLen","str","renderConfig","validity","analysis","formUnsafe","canSaveForm","canSave","canApply","canUpdate","schemaProps","availableSections","knownKeys","extraSections","allSections","activeSectionSchema","activeSectionMeta","subsections","allowSubnav","isAllSubsection","effectiveSubsection","hasChanges","section","change","formatDuration","channelEnabled","channels","channelStatus","running","connected","accountActive","account","getChannelAccountCount","channelAccounts","renderChannelAccountCount","count","resolveSchemaNode","resolveChannelValue","config","channelId","fromChannels","renderChannelConfigForm","configValue","renderChannelConfigSection","renderDiscordCard","discord","accountCountLabel","renderIMessageCard","imessage","isFormDirty","renderNostrProfileForm","callbacks","accountId","isDirty","renderField","field","inputId","renderPicturePreview","picture","img","createNostrProfileFormState","profile","truncatePubkey","pubkey","renderNostrCard","nostr","nostrAccounts","profileFormState","profileFormCallbacks","onEditProfile","primaryAccount","summaryConfigured","summaryRunning","summaryPublicKey","summaryLastStartAt","summaryLastError","hasMultipleAccounts","showingForm","renderAccountCard","displayName","renderProfileSection","about","nip05","hasAnyProfileData","renderSignalCard","signal","renderSlackCard","slack","renderTelegramCard","telegram","telegramAccounts","botUsername","renderWhatsAppCard","whatsapp","renderChannels","orderedChannels","resolveChannelOrder","channel","renderChannel","showForm","renderGenericChannelCard","resolveChannelLabel","lastError","accounts","renderGenericAccount","resolveChannelMetaMap","RECENT_ACTIVITY_THRESHOLD_MS","hasRecentActivity","deriveRunningStatus","deriveConnectedStatus","runningStatus","connectedStatus","formatPresenceSummary","ip","version","formatPresenceAge","ts","formatNextRun","formatSessionTokens","total","ctx","formatEventPayload","formatCronState","last","formatCronSchedule","formatCronPayload","buildChannelOptions","seen","renderCron","channelOptions","renderScheduleFields","renderJob","renderRun","itemClass","renderDebug","evt","renderInstances","renderEntry","lastInput","roles","scopesLabel","formatTime","date","matchesFilter","needle","renderLogs","levelFiltered","exportLabel","renderNodes","bindingState","resolveBindingsState","approvalsState","resolveExecApprovalsState","renderExecApprovals","renderBindings","renderDevices","list","pending","paired","req","renderPendingDevice","device","renderPairedDevice","age","repair","tokens","renderTokenRow","deviceId","when","EXEC_APPROVALS_DEFAULT_SCOPE","SECURITY_OPTIONS","ASK_OPTIONS","nodes","resolveExecNodes","defaultBinding","agents","resolveAgentBindings","ready","normalizeSecurity","normalizeAsk","resolveExecApprovalsDefaults","resolveConfigAgents","agentsNode","isDefault","resolveExecApprovalsAgents","configAgents","approvalsAgents","merged","agent","aLabel","bLabel","resolveExecApprovalsScope","selected","targetNodes","resolveExecApprovalsNodes","targetNodeId","selectedScope","selectedAgent","allowlist","supportsBinding","renderAgentBinding","targetReady","renderExecApprovalsTarget","renderExecApprovalsTabs","renderExecApprovalsPolicy","renderExecApprovalsAllowlist","hasNodes","nodeValue","first","isDefaults","agentSecurity","agentAsk","agentAskFallback","securityValue","askValue","askFallbackValue","autoOverride","autoEffective","autoIsDefault","option","allowlistPath","renderAllowlistEntry","lastUsed","lastCommand","lastPath","bindingValue","cmd","fallbackAgent","exec","execEntry","binding","caps","commands","renderOverview","uptime","tick","authHint","hasToken","hasPassword","insecureContextHint","THINK_LEVELS","BINARY_THINK_LEVELS","VERBOSE_LEVELS","REASONING_LEVELS","normalizeProviderId","provider","isBinaryThinkingProvider","resolveThinkLevelOptions","resolveThinkLevelDisplay","isBinary","resolveThinkLevelPatchValue","renderSessions","rows","renderRow","onDelete","rawThinking","isBinaryThinking","thinking","thinkLevels","verbose","reasoning","canLink","chatUrl","formatRemaining","totalSeconds","minutes","renderMetaRow","renderExecApprovalPrompt","active","request","remainingMs","queueCount","renderSkills","skills","filter","skill","renderSkill","busy","canInstall","missing","reasons","renderTab","href","renderChatControls","sessionOptions","resolveSessionOptions","disableThinkingToggle","disableFocusToggle","showThinking","focusActive","refreshIcon","focusIcon","sessions","resolvedCurrent","THEME_ORDER","renderThemeToggle","renderMonitorIcon","renderSunIcon","renderMoonIcon","AVATAR_DATA_RE","AVATAR_HTTP_RE","resolveAssistantAvatarUrl","renderApp","presenceCount","sessionsCount","cronNext","chatDisabledReason","isChat","chatFocus","assistantAvatarUrl","chatAvatarUrl","isGroupCollapsed","hasActiveTab","agentIndex","ratio","DEFAULT_LOG_LEVEL_FILTERS","DEFAULT_CRON_FORM","loadAgents","GATEWAY_CLIENT_IDS","GATEWAY_CLIENT_NAMES","GATEWAY_CLIENT_MODES","buildDeviceAuthPayload","CONNECT_FAILED_CLOSE_CODE","GatewayBrowserClient","ev","reason","delay","isSecureContext","deviceIdentity","canFallbackToShared","authToken","storedToken","auth","signedAtMs","nonce","signature","hello","frame","seq","method","resolve","reject","isRecord","parseExecApprovalRequested","command","createdAtMs","expiresAtMs","parseExecApprovalResolved","pruneExecApprovalQueue","queue","addExecApproval","removeExecApproval","loadAssistantIdentity","normalizeSessionKeyForDefaults","mainSessionKey","mainKey","defaultAgentId","applySessionDefaults","resolvedSessionKey","resolvedSettingsSessionKey","resolvedLastActiveSessionKey","nextSessionKey","nextSettings","shouldUpdateSettings","connectGateway","applySnapshot","code","handleGatewayEvent","expected","received","handleGatewayEventUnsafe","handleConnected","handleFirstUpdated","handleDisconnected","handleUpdated","changed","forcedByTab","forcedByLoad","handleWhatsAppStart","handleWhatsAppWait","handleWhatsAppLogout","handleChannelConfigSave","handleChannelConfigReload","parseValidationErrors","details","errors","rawField","resolveNostrAccountId","buildNostrProfileUrl","handleNostrProfileEdit","handleNostrProfileCancel","handleNostrProfileFieldChange","handleNostrProfileToggleAdvanced","handleNostrProfileSave","response","errorMessage","handleNostrProfileImport","nextValues","showAdvanced","injectedAssistantIdentity","resolveOnboardingMode","ClawdbotApp","onPopStateInternal","connectGatewayInternal","handleChatScrollInternal","handleLogsScrollInternal","exportLogsInternal","resetToolStreamInternal","resetChatScrollInternal","loadAssistantIdentityInternal","applySettingsInternal","setTabInternal","setThemeInternal","loadOverviewInternal","loadCronInternal","handleAbortChatInternal","removeQueuedMessageInternal","handleSendChatInternal","handleWhatsAppStartInternal","handleWhatsAppWaitInternal","handleWhatsAppLogoutInternal","handleChannelConfigSaveInternal","handleChannelConfigReloadInternal","handleNostrProfileEditInternal","handleNostrProfileCancelInternal","handleNostrProfileFieldChangeInternal","handleNostrProfileSaveInternal","handleNostrProfileImportInternal","handleNostrProfileToggleAdvancedInternal","decision"],"mappings":"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","x_google_ignoreList":[0,1,2,3,4,5,6,24,37,38,39,41,42,43]} \ No newline at end of file diff --git a/dist/control-ui/assets/index-bYQnHP3a.js b/dist/control-ui/assets/index-bYQnHP3a.js deleted file mode 100644 index ef15341cf71..00000000000 --- a/dist/control-ui/assets/index-bYQnHP3a.js +++ /dev/null @@ -1,3047 +0,0 @@ -(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))s(i);new MutationObserver(i=>{for(const o of i)if(o.type==="childList")for(const a of o.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&s(a)}).observe(document,{childList:!0,subtree:!0});function n(i){const o={};return i.integrity&&(o.integrity=i.integrity),i.referrerPolicy&&(o.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?o.credentials="include":i.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function s(i){if(i.ep)return;i.ep=!0;const o=n(i);fetch(i.href,o)}})();const zt=globalThis,As=zt.ShadowRoot&&(zt.ShadyCSS===void 0||zt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Ss=Symbol(),Li=new WeakMap;let Ho=class{constructor(t,n,s){if(this._$cssResult$=!0,s!==Ss)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=n}get styleSheet(){let t=this.o;const n=this.t;if(As&&t===void 0){const s=n!==void 0&&n.length===1;s&&(t=Li.get(n)),t===void 0&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),s&&Li.set(n,t))}return t}toString(){return this.cssText}};const Lr=e=>new Ho(typeof e=="string"?e:e+"",void 0,Ss),Rr=(e,...t)=>{const n=e.length===1?e[0]:t.reduce((s,i,o)=>s+(a=>{if(a._$cssResult$===!0)return a.cssText;if(typeof a=="number")return a;throw Error("Value passed to 'css' function must be a 'css' function result: "+a+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[o+1],e[0]);return new Ho(n,e,Ss)},Mr=(e,t)=>{if(As)e.adoptedStyleSheets=t.map(n=>n instanceof CSSStyleSheet?n:n.styleSheet);else for(const n of t){const s=document.createElement("style"),i=zt.litNonce;i!==void 0&&s.setAttribute("nonce",i),s.textContent=n.cssText,e.appendChild(s)}},Ri=As?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let n="";for(const s of t.cssRules)n+=s.cssText;return Lr(n)})(e):e;const{is:Pr,defineProperty:Nr,getOwnPropertyDescriptor:Or,getOwnPropertyNames:Dr,getOwnPropertySymbols:Br,getPrototypeOf:Fr}=Object,en=globalThis,Mi=en.trustedTypes,Ur=Mi?Mi.emptyScript:"",Kr=en.reactiveElementPolyfillSupport,vt=(e,t)=>e,Wt={toAttribute(e,t){switch(t){case Boolean:e=e?Ur:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let n=e;switch(t){case Boolean:n=e!==null;break;case Number:n=e===null?null:Number(e);break;case Object:case Array:try{n=JSON.parse(e)}catch{n=null}}return n}},_s=(e,t)=>!Pr(e,t),Pi={attribute:!0,type:String,converter:Wt,reflect:!1,useDefault:!1,hasChanged:_s};Symbol.metadata??=Symbol("metadata"),en.litPropertyMetadata??=new WeakMap;let Ve=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,n=Pi){if(n.state&&(n.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((n=Object.create(n)).wrapped=!0),this.elementProperties.set(t,n),!n.noAccessor){const s=Symbol(),i=this.getPropertyDescriptor(t,s,n);i!==void 0&&Nr(this.prototype,t,i)}}static getPropertyDescriptor(t,n,s){const{get:i,set:o}=Or(this.prototype,t)??{get(){return this[n]},set(a){this[n]=a}};return{get:i,set(a){const c=i?.call(this);o?.call(this,a),this.requestUpdate(t,c,s)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??Pi}static _$Ei(){if(this.hasOwnProperty(vt("elementProperties")))return;const t=Fr(this);t.finalize(),t.l!==void 0&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(vt("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(vt("properties"))){const n=this.properties,s=[...Dr(n),...Br(n)];for(const i of s)this.createProperty(i,n[i])}const t=this[Symbol.metadata];if(t!==null){const n=litPropertyMetadata.get(t);if(n!==void 0)for(const[s,i]of n)this.elementProperties.set(s,i)}this._$Eh=new Map;for(const[n,s]of this.elementProperties){const i=this._$Eu(n,s);i!==void 0&&this._$Eh.set(i,n)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const n=[];if(Array.isArray(t)){const s=new Set(t.flat(1/0).reverse());for(const i of s)n.unshift(Ri(i))}else t!==void 0&&n.push(Ri(t));return n}static _$Eu(t,n){const s=n.attribute;return s===!1?void 0:typeof s=="string"?s:typeof t=="string"?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),this.renderRoot!==void 0&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,n=this.constructor.elementProperties;for(const s of n.keys())this.hasOwnProperty(s)&&(t.set(s,this[s]),delete this[s]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return Mr(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,n,s){this._$AK(t,s)}_$ET(t,n){const s=this.constructor.elementProperties.get(t),i=this.constructor._$Eu(t,s);if(i!==void 0&&s.reflect===!0){const o=(s.converter?.toAttribute!==void 0?s.converter:Wt).toAttribute(n,s.type);this._$Em=t,o==null?this.removeAttribute(i):this.setAttribute(i,o),this._$Em=null}}_$AK(t,n){const s=this.constructor,i=s._$Eh.get(t);if(i!==void 0&&this._$Em!==i){const o=s.getPropertyOptions(i),a=typeof o.converter=="function"?{fromAttribute:o.converter}:o.converter?.fromAttribute!==void 0?o.converter:Wt;this._$Em=i;const c=a.fromAttribute(n,o.type);this[i]=c??this._$Ej?.get(i)??c,this._$Em=null}}requestUpdate(t,n,s,i=!1,o){if(t!==void 0){const a=this.constructor;if(i===!1&&(o=this[t]),s??=a.getPropertyOptions(t),!((s.hasChanged??_s)(o,n)||s.useDefault&&s.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(a._$Eu(t,s))))return;this.C(t,n,s)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(t,n,{useDefault:s,reflect:i,wrapped:o},a){s&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,a??n??this[t]),o!==!0||a!==void 0)||(this._$AL.has(t)||(this.hasUpdated||s||(n=void 0),this._$AL.set(t,n)),i===!0&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(n){Promise.reject(n)}const t=this.scheduleUpdate();return t!=null&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[i,o]of this._$Ep)this[i]=o;this._$Ep=void 0}const s=this.constructor.elementProperties;if(s.size>0)for(const[i,o]of s){const{wrapped:a}=o,c=this[i];a!==!0||this._$AL.has(i)||c===void 0||this.C(i,void 0,o,c)}}let t=!1;const n=this._$AL;try{t=this.shouldUpdate(n),t?(this.willUpdate(n),this._$EO?.forEach(s=>s.hostUpdate?.()),this.update(n)):this._$EM()}catch(s){throw t=!1,this._$EM(),s}t&&this._$AE(n)}willUpdate(t){}_$AE(t){this._$EO?.forEach(n=>n.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(n=>this._$ET(n,this[n])),this._$EM()}updated(t){}firstUpdated(t){}};Ve.elementStyles=[],Ve.shadowRootOptions={mode:"open"},Ve[vt("elementProperties")]=new Map,Ve[vt("finalized")]=new Map,Kr?.({ReactiveElement:Ve}),(en.reactiveElementVersions??=[]).push("2.1.2");const Ts=globalThis,Ni=e=>e,Vt=Ts.trustedTypes,Oi=Vt?Vt.createPolicy("lit-html",{createHTML:e=>e}):void 0,zo="$lit$",we=`lit$${Math.random().toFixed(9).slice(2)}$`,jo="?"+we,Hr=`<${jo}>`,Pe=document,yt=()=>Pe.createComment(""),wt=e=>e===null||typeof e!="object"&&typeof e!="function",Es=Array.isArray,zr=e=>Es(e)||typeof e?.[Symbol.iterator]=="function",Nn=`[ -\f\r]`,ot=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Di=/-->/g,Bi=/>/g,Ce=RegExp(`>|${Nn}(?:([^\\s"'>=/]+)(${Nn}*=${Nn}*(?:[^ -\f\r"'\`<>=]|("|')|))|$)`,"g"),Fi=/'/g,Ui=/"/g,qo=/^(?:script|style|textarea|title)$/i,jr=e=>(t,...n)=>({_$litType$:e,strings:t,values:n}),d=jr(1),xe=Symbol.for("lit-noChange"),g=Symbol.for("lit-nothing"),Ki=new WeakMap,Me=Pe.createTreeWalker(Pe,129);function Wo(e,t){if(!Es(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return Oi!==void 0?Oi.createHTML(t):t}const qr=(e,t)=>{const n=e.length-1,s=[];let i,o=t===2?"":t===3?"":"",a=ot;for(let c=0;c"?(a=i??ot,u=-1):l[1]===void 0?u=-2:(u=a.lastIndex-l[2].length,p=l[1],a=l[3]===void 0?Ce:l[3]==='"'?Ui:Fi):a===Ui||a===Fi?a=Ce:a===Di||a===Bi?a=ot:(a=Ce,i=void 0);const v=a===Ce&&e[c+1].startsWith("/>")?" ":"";o+=a===ot?r+Hr:u>=0?(s.push(p),r.slice(0,u)+zo+r.slice(u)+we+v):r+we+(u===-2?c:v)}return[Wo(e,o+(e[n]||"")+(t===2?"":t===3?"":"")),s]};let Xn=class Vo{constructor({strings:t,_$litType$:n},s){let i;this.parts=[];let o=0,a=0;const c=t.length-1,r=this.parts,[p,l]=qr(t,n);if(this.el=Vo.createElement(p,s),Me.currentNode=this.el.content,n===2||n===3){const u=this.el.content.firstChild;u.replaceWith(...u.childNodes)}for(;(i=Me.nextNode())!==null&&r.length0){i.textContent=Vt?Vt.emptyScript:"";for(let v=0;v2||s[0]!==""||s[1]!==""?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=g}_$AI(t,n=this,s,i){const o=this.strings;let a=!1;if(o===void 0)t=Qe(this,t,n,0),a=!wt(t)||t!==this._$AH&&t!==xe,a&&(this._$AH=t);else{const c=t;let r,p;for(t=o[0],r=0;r{const s=n?.renderBefore??t;let i=s._$litPart$;if(i===void 0){const o=n?.renderBefore??null;s._$litPart$=i=new tn(t.insertBefore(yt(),o),o,void 0,n??{})}return i._$AI(e),i};const Cs=globalThis;let Ye=class extends Ve{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const n=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=Xr(n,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return xe}};Ye._$litElement$=!0,Ye.finalized=!0,Cs.litElementHydrateSupport?.({LitElement:Ye});const el=Cs.litElementPolyfillSupport;el?.({LitElement:Ye});(Cs.litElementVersions??=[]).push("4.2.2");const Yo=e=>(t,n)=>{n!==void 0?n.addInitializer(()=>{customElements.define(e,t)}):customElements.define(e,t)};const tl={attribute:!0,type:String,converter:Wt,reflect:!1,hasChanged:_s},nl=(e=tl,t,n)=>{const{kind:s,metadata:i}=n;let o=globalThis.litPropertyMetadata.get(i);if(o===void 0&&globalThis.litPropertyMetadata.set(i,o=new Map),s==="setter"&&((e=Object.create(e)).wrapped=!0),o.set(n.name,e),s==="accessor"){const{name:a}=n;return{set(c){const r=t.get.call(this);t.set.call(this,c),this.requestUpdate(a,r,e,!0,c)},init(c){return c!==void 0&&this.C(a,void 0,e,c),c}}}if(s==="setter"){const{name:a}=n;return function(c){const r=this[a];t.call(this,c),this.requestUpdate(a,r,e,!0,c)}}throw Error("Unsupported decorator location: "+s)};function sn(e){return(t,n)=>typeof n=="object"?nl(e,t,n):((s,i,o)=>{const a=i.hasOwnProperty(o);return i.constructor.createProperty(o,s),a?Object.getOwnPropertyDescriptor(i,o):void 0})(e,t,n)}function y(e){return sn({...e,state:!0,attribute:!1})}const sl=50,il=200,ol="Assistant";function Hi(e,t){if(typeof e!="string")return;const n=e.trim();if(n)return n.length<=t?n:n.slice(0,t)}function es(e){const t=Hi(e?.name,sl)??ol,n=Hi(e?.avatar??void 0,il)??null;return{agentId:typeof e?.agentId=="string"&&e.agentId.trim()?e.agentId.trim():null,name:t,avatar:n}}function al(){return es(typeof window>"u"?{}:{name:window.__CLAWDBOT_ASSISTANT_NAME__,avatar:window.__CLAWDBOT_ASSISTANT_AVATAR__})}const Qo="clawdbot.control.settings.v1";function rl(){const t={gatewayUrl:`${location.protocol==="https:"?"wss":"ws"}://${location.host}`,token:"",sessionKey:"main",lastActiveSessionKey:"main",theme:"system",chatFocusMode:!1,chatShowThinking:!0,splitRatio:.6,navCollapsed:!1,navGroupsCollapsed:{}};try{const n=localStorage.getItem(Qo);if(!n)return t;const s=JSON.parse(n);return{gatewayUrl:typeof s.gatewayUrl=="string"&&s.gatewayUrl.trim()?s.gatewayUrl.trim():t.gatewayUrl,token:typeof s.token=="string"?s.token:t.token,sessionKey:typeof s.sessionKey=="string"&&s.sessionKey.trim()?s.sessionKey.trim():t.sessionKey,lastActiveSessionKey:typeof s.lastActiveSessionKey=="string"&&s.lastActiveSessionKey.trim()?s.lastActiveSessionKey.trim():typeof s.sessionKey=="string"&&s.sessionKey.trim()||t.lastActiveSessionKey,theme:s.theme==="light"||s.theme==="dark"||s.theme==="system"?s.theme:t.theme,chatFocusMode:typeof s.chatFocusMode=="boolean"?s.chatFocusMode:t.chatFocusMode,chatShowThinking:typeof s.chatShowThinking=="boolean"?s.chatShowThinking:t.chatShowThinking,splitRatio:typeof s.splitRatio=="number"&&s.splitRatio>=.4&&s.splitRatio<=.7?s.splitRatio:t.splitRatio,navCollapsed:typeof s.navCollapsed=="boolean"?s.navCollapsed:t.navCollapsed,navGroupsCollapsed:typeof s.navGroupsCollapsed=="object"&&s.navGroupsCollapsed!==null?s.navGroupsCollapsed:t.navGroupsCollapsed}}catch{return t}}function ll(e){localStorage.setItem(Qo,JSON.stringify(e))}function Jo(e){const t=(e??"").trim();if(!t)return null;const n=t.split(":").filter(Boolean);if(n.length<3||n[0]!=="agent")return null;const s=n[1]?.trim(),i=n.slice(2).join(":");return!s||!i?null:{agentId:s,rest:i}}const cl=[{label:"Chat",tabs:["chat"]},{label:"Control",tabs:["overview","channels","instances","sessions","cron"]},{label:"Agent",tabs:["skills","nodes"]},{label:"Settings",tabs:["config","debug","logs"]}],Zo={overview:"/overview",channels:"/channels",instances:"/instances",sessions:"/sessions",cron:"/cron",skills:"/skills",nodes:"/nodes",chat:"/chat",config:"/config",debug:"/debug",logs:"/logs"},Xo=new Map(Object.entries(Zo).map(([e,t])=>[t,e]));function on(e){if(!e)return"";let t=e.trim();return t.startsWith("/")||(t=`/${t}`),t==="/"?"":(t.endsWith("/")&&(t=t.slice(0,-1)),t)}function $t(e){if(!e)return"/";let t=e.trim();return t.startsWith("/")||(t=`/${t}`),t.length>1&&t.endsWith("/")&&(t=t.slice(0,-1)),t}function Is(e,t=""){const n=on(t),s=Zo[e];return n?`${n}${s}`:s}function ea(e,t=""){const n=on(t);let s=e||"/";n&&(s===n?s="/":s.startsWith(`${n}/`)&&(s=s.slice(n.length)));let i=$t(s).toLowerCase();return i.endsWith("/index.html")&&(i="/"),i==="/"?"chat":Xo.get(i)??null}function dl(e){let t=$t(e);if(t.endsWith("/index.html")&&(t=$t(t.slice(0,-11))),t==="/")return"";const n=t.split("/").filter(Boolean);if(n.length===0)return"";for(let s=0;s!!(t&&t.trim())).join(", ")}function ss(e,t=120){return e.length<=t?e:`${e.slice(0,Math.max(0,t-1))}…`}function na(e,t){return e.length<=t?{text:e,truncated:!1,total:e.length}:{text:e.slice(0,Math.max(0,t)),truncated:!0,total:e.length}}function Gt(e,t){const n=Number(e);return Number.isFinite(n)?n:t}const On=/<\s*\/?\s*think(?:ing)?\s*>/gi,zi=/<\s*think(?:ing)?\s*>/i,ji=/<\s*\/\s*think(?:ing)?\s*>/i;function Dn(e){if(!e)return e;const t=zi.test(e),n=ji.test(e);if(!t&&!n)return e;if(t!==n)return t?e.replace(zi,"").trimStart():e.replace(ji,"").trimStart();if(!On.test(e))return e;On.lastIndex=0;let s="",i=0,o=!1;for(const a of e.matchAll(On)){const c=a.index??0;o||(s+=e.slice(i,c)),o=!a[0].toLowerCase().includes("/"),i=c+a[0].length}return o||(s+=e.slice(i)),s.trimStart()}const fl=/^\[([^\]]+)\]\s*/,hl=["WebChat","WhatsApp","Telegram","Signal","Slack","Discord","iMessage","Teams","Matrix","Zalo","Zalo Personal","BlueBubbles"];function gl(e){return/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}Z\b/.test(e)||/\d{4}-\d{2}-\d{2} \d{2}:\d{2}\b/.test(e)?!0:hl.some(t=>e.startsWith(`${t} `))}function Bn(e){const t=e.match(fl);if(!t)return e;const n=t[1]??"";return gl(n)?e.slice(t[0].length):e}function an(e){const t=e,n=typeof t.role=="string"?t.role:"",s=t.content;if(typeof s=="string")return n==="assistant"?Dn(s):Bn(s);if(Array.isArray(s)){const i=s.map(o=>{const a=o;return a.type==="text"&&typeof a.text=="string"?a.text:null}).filter(o=>typeof o=="string");if(i.length>0){const o=i.join(` -`);return n==="assistant"?Dn(o):Bn(o)}}return typeof t.text=="string"?n==="assistant"?Dn(t.text):Bn(t.text):null}function vl(e){const n=e.content,s=[];if(Array.isArray(n))for(const c of n){const r=c;if(r.type==="thinking"&&typeof r.thinking=="string"){const p=r.thinking.trim();p&&s.push(p)}}if(s.length>0)return s.join(` -`);const i=ml(e);if(!i)return null;const a=[...i.matchAll(/<\s*think(?:ing)?\s*>([\s\S]*?)<\s*\/\s*think(?:ing)?\s*>/gi)].map(c=>(c[1]??"").trim()).filter(Boolean);return a.length>0?a.join(` -`):null}function ml(e){const t=e,n=t.content;if(typeof n=="string")return n;if(Array.isArray(n)){const s=n.map(i=>{const o=i;return o.type==="text"&&typeof o.text=="string"?o.text:null}).filter(i=>typeof i=="string");if(s.length>0)return s.join(` -`)}return typeof t.text=="string"?t.text:null}function bl(e){const t=e.trim();if(!t)return"";const n=t.split(/\r?\n/).map(s=>s.trim()).filter(Boolean).map(s=>`_${s}_`);return n.length?["_Reasoning:_",...n].join(` -`):""}function qi(e){e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t="";for(let n=0;n>>8&255,e[2]^=t>>>16&255,e[3]^=t>>>24&255,e}function Ls(e=globalThis.crypto){if(e&&typeof e.randomUUID=="function")return e.randomUUID();if(e&&typeof e.getRandomValues=="function"){const t=new Uint8Array(16);return e.getRandomValues(t),qi(t)}return qi(yl())}async function Je(e){if(!(!e.client||!e.connected)){e.chatLoading=!0,e.lastError=null;try{const t=await e.client.request("chat.history",{sessionKey:e.sessionKey,limit:200});e.chatMessages=Array.isArray(t.messages)?t.messages:[],e.chatThinkingLevel=t.thinkingLevel??null}catch(t){e.lastError=String(t)}finally{e.chatLoading=!1}}}async function wl(e,t){if(!e.client||!e.connected)return!1;const n=t.trim();if(!n)return!1;const s=Date.now();e.chatMessages=[...e.chatMessages,{role:"user",content:[{type:"text",text:n}],timestamp:s}],e.chatSending=!0,e.lastError=null;const i=Ls();e.chatRunId=i,e.chatStream="",e.chatStreamStartedAt=s;try{return await e.client.request("chat.send",{sessionKey:e.sessionKey,message:n,deliver:!1,idempotencyKey:i}),!0}catch(o){const a=String(o);return e.chatRunId=null,e.chatStream=null,e.chatStreamStartedAt=null,e.lastError=a,e.chatMessages=[...e.chatMessages,{role:"assistant",content:[{type:"text",text:"Error: "+a}],timestamp:Date.now()}],!1}finally{e.chatSending=!1}}async function $l(e){if(!e.client||!e.connected)return!1;const t=e.chatRunId;try{return await e.client.request("chat.abort",t?{sessionKey:e.sessionKey,runId:t}:{sessionKey:e.sessionKey}),!0}catch(n){return e.lastError=String(n),!1}}function kl(e,t){if(!t||t.sessionKey!==e.sessionKey||t.runId&&e.chatRunId&&t.runId!==e.chatRunId)return null;if(t.state==="delta"){const n=an(t.message);if(typeof n=="string"){const s=e.chatStream??"";(!s||n.length>=s.length)&&(e.chatStream=n)}}else t.state==="final"||t.state==="aborted"?(e.chatStream=null,e.chatRunId=null,e.chatStreamStartedAt=null):t.state==="error"&&(e.chatStream=null,e.chatRunId=null,e.chatStreamStartedAt=null,e.lastError=t.errorMessage??"chat error");return t.state}async function tt(e){if(!(!e.client||!e.connected)&&!e.sessionsLoading){e.sessionsLoading=!0,e.sessionsError=null;try{const t={includeGlobal:e.sessionsIncludeGlobal,includeUnknown:e.sessionsIncludeUnknown},n=Gt(e.sessionsFilterActive,0),s=Gt(e.sessionsFilterLimit,0);n>0&&(t.activeMinutes=n),s>0&&(t.limit=s);const i=await e.client.request("sessions.list",t);i&&(e.sessionsResult=i)}catch(t){e.sessionsError=String(t)}finally{e.sessionsLoading=!1}}}async function xl(e,t,n){if(!e.client||!e.connected)return;const s={key:t};"label"in n&&(s.label=n.label),"thinkingLevel"in n&&(s.thinkingLevel=n.thinkingLevel),"verboseLevel"in n&&(s.verboseLevel=n.verboseLevel),"reasoningLevel"in n&&(s.reasoningLevel=n.reasoningLevel);try{await e.client.request("sessions.patch",s),await tt(e)}catch(i){e.sessionsError=String(i)}}async function Al(e,t){if(!(!e.client||!e.connected||e.sessionsLoading||!window.confirm(`Delete session "${t}"? - -Deletes the session entry and archives its transcript.`))){e.sessionsLoading=!0,e.sessionsError=null;try{await e.client.request("sessions.delete",{key:t,deleteTranscript:!0}),await tt(e)}catch(s){e.sessionsError=String(s)}finally{e.sessionsLoading=!1}}}const Wi=50,Sl=80,_l=12e4;function Tl(e){if(!e||typeof e!="object")return null;const t=e;if(typeof t.text=="string")return t.text;const n=t.content;if(!Array.isArray(n))return null;const s=n.map(i=>{if(!i||typeof i!="object")return null;const o=i;return o.type==="text"&&typeof o.text=="string"?o.text:null}).filter(i=>!!i);return s.length===0?null:s.join(` -`)}function Vi(e){if(e==null)return null;if(typeof e=="number"||typeof e=="boolean")return String(e);const t=Tl(e);let n;if(typeof e=="string")n=e;else if(t)n=t;else try{n=JSON.stringify(e,null,2)}catch{n=String(e)}const s=na(n,_l);return s.truncated?`${s.text} - -… truncated (${s.total} chars, showing first ${s.text.length}).`:s.text}function El(e){const t=[];return t.push({type:"toolcall",name:e.name,arguments:e.args??{}}),e.output&&t.push({type:"toolresult",name:e.name,text:e.output}),{role:"assistant",toolCallId:e.toolCallId,runId:e.runId,content:t,timestamp:e.startedAt}}function Cl(e){if(e.toolStreamOrder.length<=Wi)return;const t=e.toolStreamOrder.length-Wi,n=e.toolStreamOrder.splice(0,t);for(const s of n)e.toolStreamById.delete(s)}function Il(e){e.chatToolMessages=e.toolStreamOrder.map(t=>e.toolStreamById.get(t)?.message).filter(t=>!!t)}function is(e){e.toolStreamSyncTimer!=null&&(clearTimeout(e.toolStreamSyncTimer),e.toolStreamSyncTimer=null),Il(e)}function Ll(e,t=!1){if(t){is(e);return}e.toolStreamSyncTimer==null&&(e.toolStreamSyncTimer=window.setTimeout(()=>is(e),Sl))}function Rs(e){e.toolStreamById.clear(),e.toolStreamOrder=[],e.chatToolMessages=[],is(e)}function Rl(e,t){if(!t||t.stream!=="tool")return;const n=typeof t.sessionKey=="string"?t.sessionKey:void 0;if(n&&n!==e.sessionKey||!n&&e.chatRunId&&t.runId!==e.chatRunId||e.chatRunId&&t.runId!==e.chatRunId||!e.chatRunId)return;const s=t.data??{},i=typeof s.toolCallId=="string"?s.toolCallId:"";if(!i)return;const o=typeof s.name=="string"?s.name:"tool",a=typeof s.phase=="string"?s.phase:"",c=a==="start"?s.args:void 0,r=a==="update"?Vi(s.partialResult):a==="result"?Vi(s.result):void 0,p=Date.now();let l=e.toolStreamById.get(i);l?(l.name=o,c!==void 0&&(l.args=c),r!==void 0&&(l.output=r),l.updatedAt=p):(l={toolCallId:i,runId:t.runId,sessionKey:n,name:o,args:c,output:r,startedAt:typeof t.ts=="number"?t.ts:p,updatedAt:p,message:{}},e.toolStreamById.set(i,l),e.toolStreamOrder.push(i)),l.message=El(l),Cl(e),Ll(e,a==="result")}function rn(e,t=!1){e.chatScrollFrame&&cancelAnimationFrame(e.chatScrollFrame),e.chatScrollTimeout!=null&&(clearTimeout(e.chatScrollTimeout),e.chatScrollTimeout=null);const n=()=>{const s=e.querySelector(".chat-thread");if(s){const i=getComputedStyle(s).overflowY;if(i==="auto"||i==="scroll"||s.scrollHeight-s.clientHeight>1)return s}return document.scrollingElement??document.documentElement};e.updateComplete.then(()=>{e.chatScrollFrame=requestAnimationFrame(()=>{e.chatScrollFrame=null;const s=n();if(!s)return;const i=s.scrollHeight-s.scrollTop-s.clientHeight;if(!(t||e.chatUserNearBottom||i<200))return;t&&(e.chatHasAutoScrolled=!0),s.scrollTop=s.scrollHeight,e.chatUserNearBottom=!0;const a=t?150:120;e.chatScrollTimeout=window.setTimeout(()=>{e.chatScrollTimeout=null;const c=n();if(!c)return;const r=c.scrollHeight-c.scrollTop-c.clientHeight;(t||e.chatUserNearBottom||r<200)&&(c.scrollTop=c.scrollHeight,e.chatUserNearBottom=!0)},a)})})}function sa(e,t=!1){e.logsScrollFrame&&cancelAnimationFrame(e.logsScrollFrame),e.updateComplete.then(()=>{e.logsScrollFrame=requestAnimationFrame(()=>{e.logsScrollFrame=null;const n=e.querySelector(".log-stream");if(!n)return;const s=n.scrollHeight-n.scrollTop-n.clientHeight;(t||s<80)&&(n.scrollTop=n.scrollHeight)})})}function Ml(e,t){const n=t.currentTarget;if(!n)return;const s=n.scrollHeight-n.scrollTop-n.clientHeight;e.chatUserNearBottom=s<200}function Pl(e,t){const n=t.currentTarget;if(!n)return;const s=n.scrollHeight-n.scrollTop-n.clientHeight;e.logsAtBottom=s<80}function Nl(e){e.chatHasAutoScrolled=!1,e.chatUserNearBottom=!0}function Ol(e,t){if(e.length===0)return;const n=new Blob([`${e.join(` -`)} -`],{type:"text/plain"}),s=URL.createObjectURL(n),i=document.createElement("a"),o=new Date().toISOString().slice(0,19).replace(/[:T]/g,"-");i.href=s,i.download=`clawdbot-logs-${t}-${o}.log`,i.click(),URL.revokeObjectURL(s)}function Dl(e){if(typeof ResizeObserver>"u")return;const t=e.querySelector(".topbar");if(!t)return;const n=()=>{const{height:s}=t.getBoundingClientRect();e.style.setProperty("--topbar-height",`${s}px`)};n(),e.topbarObserver=new ResizeObserver(()=>n()),e.topbarObserver.observe(t)}function Ne(e){return typeof structuredClone=="function"?structuredClone(e):JSON.parse(JSON.stringify(e))}function Ze(e){return`${JSON.stringify(e,null,2).trimEnd()} -`}function ia(e,t,n){if(t.length===0)return;let s=e;for(let o=0;o0&&(n.timeoutSeconds=s),n}async function jl(e){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{const t=Hl(e.cronForm),n=zl(e.cronForm),s=e.cronForm.agentId.trim(),i={name:e.cronForm.name.trim(),description:e.cronForm.description.trim()||void 0,agentId:s||void 0,enabled:e.cronForm.enabled,schedule:t,sessionTarget:e.cronForm.sessionTarget,wakeMode:e.cronForm.wakeMode,payload:n,isolation:e.cronForm.postToMainPrefix.trim()&&e.cronForm.sessionTarget==="isolated"?{postToMainPrefix:e.cronForm.postToMainPrefix.trim()}:void 0};if(!i.name)throw new Error("Name required.");await e.client.request("cron.add",i),e.cronForm={...e.cronForm,name:"",description:"",payloadText:""},await ln(e),await St(e)}catch(t){e.cronError=String(t)}finally{e.cronBusy=!1}}}async function ql(e,t,n){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.update",{id:t.id,patch:{enabled:n}}),await ln(e),await St(e)}catch(s){e.cronError=String(s)}finally{e.cronBusy=!1}}}async function Wl(e,t){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.run",{id:t.id,mode:"force"}),await ra(e,t.id)}catch(n){e.cronError=String(n)}finally{e.cronBusy=!1}}}async function Vl(e,t){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.remove",{id:t.id}),e.cronRunsJobId===t.id&&(e.cronRunsJobId=null,e.cronRuns=[]),await ln(e),await St(e)}catch(n){e.cronError=String(n)}finally{e.cronBusy=!1}}}async function ra(e,t){if(!(!e.client||!e.connected))try{const n=await e.client.request("cron.runs",{id:t,limit:50});e.cronRunsJobId=t,e.cronRuns=Array.isArray(n.entries)?n.entries:[]}catch(n){e.cronError=String(n)}}async function oe(e,t){if(!(!e.client||!e.connected)&&!e.channelsLoading){e.channelsLoading=!0,e.channelsError=null;try{const n=await e.client.request("channels.status",{probe:t,timeoutMs:8e3});e.channelsSnapshot=n,e.channelsLastSuccess=Date.now()}catch(n){e.channelsError=String(n)}finally{e.channelsLoading=!1}}}async function Gl(e,t){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{const n=await e.client.request("web.login.start",{force:t,timeoutMs:3e4});e.whatsappLoginMessage=n.message??null,e.whatsappLoginQrDataUrl=n.qrDataUrl??null,e.whatsappLoginConnected=null}catch(n){e.whatsappLoginMessage=String(n),e.whatsappLoginQrDataUrl=null,e.whatsappLoginConnected=null}finally{e.whatsappBusy=!1}}}async function Yl(e){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{const t=await e.client.request("web.login.wait",{timeoutMs:12e4});e.whatsappLoginMessage=t.message??null,e.whatsappLoginConnected=t.connected??null,t.connected&&(e.whatsappLoginQrDataUrl=null)}catch(t){e.whatsappLoginMessage=String(t),e.whatsappLoginConnected=null}finally{e.whatsappBusy=!1}}}async function Ql(e){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{await e.client.request("channels.logout",{channel:"whatsapp"}),e.whatsappLoginMessage="Logged out.",e.whatsappLoginQrDataUrl=null,e.whatsappLoginConnected=null}catch(t){e.whatsappLoginMessage=String(t)}finally{e.whatsappBusy=!1}}}async function cn(e){if(!(!e.client||!e.connected)&&!e.debugLoading){e.debugLoading=!0;try{const[t,n,s,i]=await Promise.all([e.client.request("status",{}),e.client.request("health",{}),e.client.request("models.list",{}),e.client.request("last-heartbeat",{})]);e.debugStatus=t,e.debugHealth=n;const o=s;e.debugModels=Array.isArray(o?.models)?o?.models:[],e.debugHeartbeat=i}catch(t){e.debugCallError=String(t)}finally{e.debugLoading=!1}}}async function Jl(e){if(!(!e.client||!e.connected)){e.debugCallError=null,e.debugCallResult=null;try{const t=e.debugCallParams.trim()?JSON.parse(e.debugCallParams):{},n=await e.client.request(e.debugCallMethod.trim(),t);e.debugCallResult=JSON.stringify(n,null,2)}catch(t){e.debugCallError=String(t)}}}const Zl=2e3,Xl=new Set(["trace","debug","info","warn","error","fatal"]);function ec(e){if(typeof e!="string")return null;const t=e.trim();if(!t.startsWith("{")||!t.endsWith("}"))return null;try{const n=JSON.parse(t);return!n||typeof n!="object"?null:n}catch{return null}}function tc(e){if(typeof e!="string")return null;const t=e.toLowerCase();return Xl.has(t)?t:null}function nc(e){if(!e.trim())return{raw:e,message:e};try{const t=JSON.parse(e),n=t&&typeof t._meta=="object"&&t._meta!==null?t._meta:null,s=typeof t.time=="string"?t.time:typeof n?.date=="string"?n?.date:null,i=tc(n?.logLevelName??n?.level),o=typeof t[0]=="string"?t[0]:typeof n?.name=="string"?n?.name:null,a=ec(o);let c=null;a&&(typeof a.subsystem=="string"?c=a.subsystem:typeof a.module=="string"&&(c=a.module)),!c&&o&&o.length<120&&(c=o);let r=null;return typeof t[1]=="string"?r=t[1]:!a&&typeof t[0]=="string"?r=t[0]:typeof t.message=="string"&&(r=t.message),{raw:e,time:s,level:i,subsystem:c,message:r??e,meta:n??void 0}}catch{return{raw:e,message:e}}}async function Ms(e,t){if(!(!e.client||!e.connected)&&!(e.logsLoading&&!t?.quiet)){t?.quiet||(e.logsLoading=!0),e.logsError=null;try{const s=await e.client.request("logs.tail",{cursor:t?.reset?void 0:e.logsCursor??void 0,limit:e.logsLimit,maxBytes:e.logsMaxBytes}),o=(Array.isArray(s.lines)?s.lines.filter(c=>typeof c=="string"):[]).map(nc),a=!!(t?.reset||s.reset||e.logsCursor==null);e.logsEntries=a?o:[...e.logsEntries,...o].slice(-Zl),typeof s.cursor=="number"&&(e.logsCursor=s.cursor),typeof s.file=="string"&&(e.logsFile=s.file),e.logsTruncated=!!s.truncated,e.logsLastFetchAt=Date.now()}catch(n){e.logsError=String(n)}finally{t?.quiet||(e.logsLoading=!1)}}}const la={p:0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,n:0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,h:8n,a:0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,d:0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,Gx:0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,Gy:0x6666666666666666666666666666666666666666666666666666666666666658n},{p:W,n:jt,Gx:Yi,Gy:Qi,a:Fn,d:Un,h:sc}=la,Oe=32,Ps=64,ic=(...e)=>{"captureStackTrace"in Error&&typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(...e)},H=(e="")=>{const t=new Error(e);throw ic(t,H),t},oc=e=>typeof e=="bigint",ac=e=>typeof e=="string",rc=e=>e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array",Ae=(e,t,n="")=>{const s=rc(e),i=e?.length,o=t!==void 0;if(!s||o&&i!==t){const a=n&&`"${n}" `,c=o?` of length ${t}`:"",r=s?`length=${i}`:`type=${typeof e}`;H(a+"expected Uint8Array"+c+", got "+r)}return e},dn=e=>new Uint8Array(e),ca=e=>Uint8Array.from(e),da=(e,t)=>e.toString(16).padStart(t,"0"),ua=e=>Array.from(Ae(e)).map(t=>da(t,2)).join(""),ge={_0:48,_9:57,A:65,F:70,a:97,f:102},Ji=e=>{if(e>=ge._0&&e<=ge._9)return e-ge._0;if(e>=ge.A&&e<=ge.F)return e-(ge.A-10);if(e>=ge.a&&e<=ge.f)return e-(ge.a-10)},pa=e=>{const t="hex invalid";if(!ac(e))return H(t);const n=e.length,s=n/2;if(n%2)return H(t);const i=dn(s);for(let o=0,a=0;oglobalThis?.crypto,lc=()=>fa()?.subtle??H("crypto.subtle must be defined, consider polyfill"),xt=(...e)=>{const t=dn(e.reduce((s,i)=>s+Ae(i).length,0));let n=0;return e.forEach(s=>{t.set(s,n),n+=s.length}),t},cc=(e=Oe)=>fa().getRandomValues(dn(e)),Yt=BigInt,Re=(e,t,n,s="bad number: out of range")=>oc(e)&&t<=e&&e{const n=e%t;return n>=0n?n:t+n},ha=e=>S(e,jt),dc=(e,t)=>{(e===0n||t<=0n)&&H("no inverse n="+e+" mod="+t);let n=S(e,t),s=t,i=0n,o=1n;for(;n!==0n;){const a=s/n,c=s%n,r=i-o*a;s=n,n=c,i=o,o=r}return s===1n?S(i,t):H("no inverse")},uc=e=>{const t=ba[e];return typeof t!="function"&&H("hashes."+e+" not set"),t},Kn=e=>e instanceof X?e:H("Point expected"),as=2n**256n;class X{static BASE;static ZERO;X;Y;Z;T;constructor(t,n,s,i){const o=as;this.X=Re(t,0n,o),this.Y=Re(n,0n,o),this.Z=Re(s,1n,o),this.T=Re(i,0n,o),Object.freeze(this)}static CURVE(){return la}static fromAffine(t){return new X(t.x,t.y,1n,S(t.x*t.y))}static fromBytes(t,n=!1){const s=Un,i=ca(Ae(t,Oe)),o=t[31];i[31]=o&-129;const a=va(i);Re(a,0n,n?as:W);const r=S(a*a),p=S(r-1n),l=S(s*r+1n);let{isValid:u,value:h}=fc(p,l);u||H("bad point: y not sqrt");const v=(h&1n)===1n,w=(o&128)!==0;return!n&&h===0n&&w&&H("bad point: x==0, isLastByteOdd"),w!==v&&(h=S(-h)),new X(h,a,1n,S(h*a))}static fromHex(t,n){return X.fromBytes(pa(t),n)}get x(){return this.toAffine().x}get y(){return this.toAffine().y}assertValidity(){const t=Fn,n=Un,s=this;if(s.is0())return H("bad point: ZERO");const{X:i,Y:o,Z:a,T:c}=s,r=S(i*i),p=S(o*o),l=S(a*a),u=S(l*l),h=S(r*t),v=S(l*S(h+p)),w=S(u+S(n*S(r*p)));if(v!==w)return H("bad point: equation left != right (1)");const $=S(i*o),x=S(a*c);return $!==x?H("bad point: equation left != right (2)"):this}equals(t){const{X:n,Y:s,Z:i}=this,{X:o,Y:a,Z:c}=Kn(t),r=S(n*c),p=S(o*i),l=S(s*c),u=S(a*i);return r===p&&l===u}is0(){return this.equals(Ge)}negate(){return new X(S(-this.X),this.Y,this.Z,S(-this.T))}double(){const{X:t,Y:n,Z:s}=this,i=Fn,o=S(t*t),a=S(n*n),c=S(2n*S(s*s)),r=S(i*o),p=t+n,l=S(S(p*p)-o-a),u=r+a,h=u-c,v=r-a,w=S(l*h),$=S(u*v),x=S(l*v),E=S(h*u);return new X(w,$,E,x)}add(t){const{X:n,Y:s,Z:i,T:o}=this,{X:a,Y:c,Z:r,T:p}=Kn(t),l=Fn,u=Un,h=S(n*a),v=S(s*c),w=S(o*u*p),$=S(i*r),x=S((n+s)*(a+c)-h-v),E=S($-w),I=S($+w),R=S(v-l*h),C=S(x*E),A=S(I*R),B=S(x*R),ue=S(E*I);return new X(C,A,ue,B)}subtract(t){return this.add(Kn(t).negate())}multiply(t,n=!0){if(!n&&(t===0n||this.is0()))return Ge;if(Re(t,1n,jt),t===1n)return this;if(this.equals(De))return Ac(t).p;let s=Ge,i=De;for(let o=this;t>0n;o=o.double(),t>>=1n)t&1n?s=s.add(o):n&&(i=i.add(o));return s}multiplyUnsafe(t){return this.multiply(t,!1)}toAffine(){const{X:t,Y:n,Z:s}=this;if(this.equals(Ge))return{x:0n,y:1n};const i=dc(s,W);S(s*i)!==1n&&H("invalid inverse");const o=S(t*i),a=S(n*i);return{x:o,y:a}}toBytes(){const{x:t,y:n}=this.assertValidity().toAffine(),s=ga(n);return s[31]|=t&1n?128:0,s}toHex(){return ua(this.toBytes())}clearCofactor(){return this.multiply(Yt(sc),!1)}isSmallOrder(){return this.clearCofactor().is0()}isTorsionFree(){let t=this.multiply(jt/2n,!1).double();return jt%2n&&(t=t.add(this)),t.is0()}}const De=new X(Yi,Qi,1n,S(Yi*Qi)),Ge=new X(0n,1n,1n,0n);X.BASE=De;X.ZERO=Ge;const ga=e=>pa(da(Re(e,0n,as),Ps)).reverse(),va=e=>Yt("0x"+ua(ca(Ae(e)).reverse())),le=(e,t)=>{let n=e;for(;t-- >0n;)n*=n,n%=W;return n},pc=e=>{const n=e*e%W*e%W,s=le(n,2n)*n%W,i=le(s,1n)*e%W,o=le(i,5n)*i%W,a=le(o,10n)*o%W,c=le(a,20n)*a%W,r=le(c,40n)*c%W,p=le(r,80n)*r%W,l=le(p,80n)*r%W,u=le(l,10n)*o%W;return{pow_p_5_8:le(u,2n)*e%W,b2:n}},Zi=0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n,fc=(e,t)=>{const n=S(t*t*t),s=S(n*n*t),i=pc(e*s).pow_p_5_8;let o=S(e*n*i);const a=S(t*o*o),c=o,r=S(o*Zi),p=a===e,l=a===S(-e),u=a===S(-e*Zi);return p&&(o=c),(l||u)&&(o=r),(S(o)&1n)===1n&&(o=S(-o)),{isValid:p||l,value:o}},rs=e=>ha(va(e)),Ns=(...e)=>ba.sha512Async(xt(...e)),hc=(...e)=>uc("sha512")(xt(...e)),ma=e=>{const t=e.slice(0,Oe);t[0]&=248,t[31]&=127,t[31]|=64;const n=e.slice(Oe,Ps),s=rs(t),i=De.multiply(s),o=i.toBytes();return{head:t,prefix:n,scalar:s,point:i,pointBytes:o}},Os=e=>Ns(Ae(e,Oe)).then(ma),gc=e=>ma(hc(Ae(e,Oe))),vc=e=>Os(e).then(t=>t.pointBytes),mc=e=>Ns(e.hashable).then(e.finish),bc=(e,t,n)=>{const{pointBytes:s,scalar:i}=e,o=rs(t),a=De.multiply(o).toBytes();return{hashable:xt(a,s,n),finish:p=>{const l=ha(o+rs(p)*i);return Ae(xt(a,ga(l)),Ps)}}},yc=async(e,t)=>{const n=Ae(e),s=await Os(t),i=await Ns(s.prefix,n);return mc(bc(s,i,n))},ba={sha512Async:async e=>{const t=lc(),n=xt(e);return dn(await t.digest("SHA-512",n.buffer))},sha512:void 0},wc=(e=cc(Oe))=>e,$c={getExtendedPublicKeyAsync:Os,getExtendedPublicKey:gc,randomSecretKey:wc},Qt=8,kc=256,ya=Math.ceil(kc/Qt)+1,ls=2**(Qt-1),xc=()=>{const e=[];let t=De,n=t;for(let s=0;s{const n=t.negate();return e?n:t},Ac=e=>{const t=Xi||(Xi=xc());let n=Ge,s=De;const i=2**Qt,o=i,a=Yt(i-1),c=Yt(Qt);for(let r=0;r>=c,p>ls&&(p-=o,e+=1n);const l=r*ls,u=l,h=l+Math.abs(p)-1,v=r%2!==0,w=p<0;p===0?s=s.add(eo(v,t[u])):n=n.add(eo(w,t[h]))}return e!==0n&&H("invalid wnaf"),{p:n,f:s}},Hn="clawdbot-device-identity-v1";function cs(e){let t="";for(const n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll("+","-").replaceAll("/","_").replace(/=+$/g,"")}function wa(e){const t=e.replaceAll("-","+").replaceAll("_","/"),n=t+"=".repeat((4-t.length%4)%4),s=atob(n),i=new Uint8Array(s.length);for(let o=0;ot.toString(16).padStart(2,"0")).join("")}async function $a(e){const t=await crypto.subtle.digest("SHA-256",e);return Sc(new Uint8Array(t))}async function _c(){const e=$c.randomSecretKey(),t=await vc(e);return{deviceId:await $a(t),publicKey:cs(t),privateKey:cs(e)}}async function Ds(){try{const n=localStorage.getItem(Hn);if(n){const s=JSON.parse(n);if(s?.version===1&&typeof s.deviceId=="string"&&typeof s.publicKey=="string"&&typeof s.privateKey=="string"){const i=await $a(wa(s.publicKey));if(i!==s.deviceId){const o={...s,deviceId:i};return localStorage.setItem(Hn,JSON.stringify(o)),{deviceId:i,publicKey:s.publicKey,privateKey:s.privateKey}}return{deviceId:s.deviceId,publicKey:s.publicKey,privateKey:s.privateKey}}}}catch{}const e=await _c(),t={version:1,deviceId:e.deviceId,publicKey:e.publicKey,privateKey:e.privateKey,createdAtMs:Date.now()};return localStorage.setItem(Hn,JSON.stringify(t)),e}async function Tc(e,t){const n=wa(e),s=new TextEncoder().encode(t),i=await yc(s,n);return cs(i)}const ka="clawdbot.device.auth.v1";function Bs(e){return e.trim()}function Ec(e){if(!Array.isArray(e))return[];const t=new Set;for(const n of e){const s=n.trim();s&&t.add(s)}return[...t].sort()}function Fs(){try{const e=window.localStorage.getItem(ka);if(!e)return null;const t=JSON.parse(e);return!t||t.version!==1||!t.deviceId||typeof t.deviceId!="string"||!t.tokens||typeof t.tokens!="object"?null:t}catch{return null}}function xa(e){try{window.localStorage.setItem(ka,JSON.stringify(e))}catch{}}function Cc(e){const t=Fs();if(!t||t.deviceId!==e.deviceId)return null;const n=Bs(e.role),s=t.tokens[n];return!s||typeof s.token!="string"?null:s}function Aa(e){const t=Bs(e.role),n={version:1,deviceId:e.deviceId,tokens:{}},s=Fs();s&&s.deviceId===e.deviceId&&(n.tokens={...s.tokens});const i={token:e.token,role:t,scopes:Ec(e.scopes),updatedAtMs:Date.now()};return n.tokens[t]=i,xa(n),i}function Sa(e){const t=Fs();if(!t||t.deviceId!==e.deviceId)return;const n=Bs(e.role);if(!t.tokens[n])return;const s={...t,tokens:{...t.tokens}};delete s.tokens[n],xa(s)}async function Se(e,t){if(!(!e.client||!e.connected)&&!e.devicesLoading){e.devicesLoading=!0,t?.quiet||(e.devicesError=null);try{const n=await e.client.request("device.pair.list",{});e.devicesList={pending:Array.isArray(n?.pending)?n.pending:[],paired:Array.isArray(n?.paired)?n.paired:[]}}catch(n){t?.quiet||(e.devicesError=String(n))}finally{e.devicesLoading=!1}}}async function Ic(e,t){if(!(!e.client||!e.connected))try{await e.client.request("device.pair.approve",{requestId:t}),await Se(e)}catch(n){e.devicesError=String(n)}}async function Lc(e,t){if(!(!e.client||!e.connected||!window.confirm("Reject this device pairing request?")))try{await e.client.request("device.pair.reject",{requestId:t}),await Se(e)}catch(s){e.devicesError=String(s)}}async function Rc(e,t){if(!(!e.client||!e.connected))try{const n=await e.client.request("device.token.rotate",t);if(n?.token){const s=await Ds(),i=n.role??t.role;(n.deviceId===s.deviceId||t.deviceId===s.deviceId)&&Aa({deviceId:s.deviceId,role:i,token:n.token,scopes:n.scopes??t.scopes??[]}),window.prompt("New device token (copy and store securely):",n.token)}await Se(e)}catch(n){e.devicesError=String(n)}}async function Mc(e,t){if(!(!e.client||!e.connected||!window.confirm(`Revoke token for ${t.deviceId} (${t.role})?`)))try{await e.client.request("device.token.revoke",t);const s=await Ds();t.deviceId===s.deviceId&&Sa({deviceId:s.deviceId,role:t.role}),await Se(e)}catch(s){e.devicesError=String(s)}}async function un(e,t){if(!(!e.client||!e.connected)&&!e.nodesLoading){e.nodesLoading=!0,t?.quiet||(e.lastError=null);try{const n=await e.client.request("node.list",{});e.nodes=Array.isArray(n.nodes)?n.nodes:[]}catch(n){t?.quiet||(e.lastError=String(n))}finally{e.nodesLoading=!1}}}function Pc(e){if(!e||e.kind==="gateway")return{method:"exec.approvals.get",params:{}};const t=e.nodeId.trim();return t?{method:"exec.approvals.node.get",params:{nodeId:t}}:null}function Nc(e,t){if(!e||e.kind==="gateway")return{method:"exec.approvals.set",params:t};const n=e.nodeId.trim();return n?{method:"exec.approvals.node.set",params:{...t,nodeId:n}}:null}async function Us(e,t){if(!(!e.client||!e.connected)&&!e.execApprovalsLoading){e.execApprovalsLoading=!0,e.lastError=null;try{const n=Pc(t);if(!n){e.lastError="Select a node before loading exec approvals.";return}const s=await e.client.request(n.method,n.params);Oc(e,s)}catch(n){e.lastError=String(n)}finally{e.execApprovalsLoading=!1}}}function Oc(e,t){e.execApprovalsSnapshot=t,e.execApprovalsDirty||(e.execApprovalsForm=Ne(t.file??{}))}async function Dc(e,t){if(!(!e.client||!e.connected)){e.execApprovalsSaving=!0,e.lastError=null;try{const n=e.execApprovalsSnapshot?.hash;if(!n){e.lastError="Exec approvals hash missing; reload and retry.";return}const s=e.execApprovalsForm??e.execApprovalsSnapshot?.file??{},i=Nc(t,{file:s,baseHash:n});if(!i){e.lastError="Select a node before saving exec approvals.";return}await e.client.request(i.method,i.params),e.execApprovalsDirty=!1,await Us(e,t)}catch(n){e.lastError=String(n)}finally{e.execApprovalsSaving=!1}}}function Bc(e,t,n){const s=Ne(e.execApprovalsForm??e.execApprovalsSnapshot?.file??{});ia(s,t,n),e.execApprovalsForm=s,e.execApprovalsDirty=!0}function Fc(e,t){const n=Ne(e.execApprovalsForm??e.execApprovalsSnapshot?.file??{});oa(n,t),e.execApprovalsForm=n,e.execApprovalsDirty=!0}async function Ks(e){if(!(!e.client||!e.connected)&&!e.presenceLoading){e.presenceLoading=!0,e.presenceError=null,e.presenceStatus=null;try{const t=await e.client.request("system-presence",{});Array.isArray(t)?(e.presenceEntries=t,e.presenceStatus=t.length===0?"No instances yet.":null):(e.presenceEntries=[],e.presenceStatus="No presence payload.")}catch(t){e.presenceError=String(t)}finally{e.presenceLoading=!1}}}function Xe(e,t,n){if(!t.trim())return;const s={...e.skillMessages};n?s[t]=n:delete s[t],e.skillMessages=s}function pn(e){return e instanceof Error?e.message:String(e)}async function _t(e,t){if(t?.clearMessages&&Object.keys(e.skillMessages).length>0&&(e.skillMessages={}),!(!e.client||!e.connected)&&!e.skillsLoading){e.skillsLoading=!0,e.skillsError=null;try{const n=await e.client.request("skills.status",{});n&&(e.skillsReport=n)}catch(n){e.skillsError=pn(n)}finally{e.skillsLoading=!1}}}function Uc(e,t,n){e.skillEdits={...e.skillEdits,[t]:n}}async function Kc(e,t,n){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{await e.client.request("skills.update",{skillKey:t,enabled:n}),await _t(e),Xe(e,t,{kind:"success",message:n?"Skill enabled":"Skill disabled"})}catch(s){const i=pn(s);e.skillsError=i,Xe(e,t,{kind:"error",message:i})}finally{e.skillsBusyKey=null}}}async function Hc(e,t){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{const n=e.skillEdits[t]??"";await e.client.request("skills.update",{skillKey:t,apiKey:n}),await _t(e),Xe(e,t,{kind:"success",message:"API key saved"})}catch(n){const s=pn(n);e.skillsError=s,Xe(e,t,{kind:"error",message:s})}finally{e.skillsBusyKey=null}}}async function zc(e,t,n,s){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{const i=await e.client.request("skills.install",{name:n,installId:s,timeoutMs:12e4});await _t(e),Xe(e,t,{kind:"success",message:i?.message??"Installed"})}catch(i){const o=pn(i);e.skillsError=o,Xe(e,t,{kind:"error",message:o})}finally{e.skillsBusyKey=null}}}function jc(){return typeof window>"u"||typeof window.matchMedia!="function"||window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function Hs(e){return e==="system"?jc():e}const Ot=e=>Number.isNaN(e)?.5:e<=0?0:e>=1?1:e,qc=()=>typeof window>"u"||typeof window.matchMedia!="function"?!1:window.matchMedia("(prefers-reduced-motion: reduce)").matches??!1,Dt=e=>{e.classList.remove("theme-transition"),e.style.removeProperty("--theme-switch-x"),e.style.removeProperty("--theme-switch-y")},Wc=({nextTheme:e,applyTheme:t,context:n,currentTheme:s})=>{if(s===e)return;const i=globalThis.document??null;if(!i){t();return}const o=i.documentElement,a=i,c=qc();if(!!a.startViewTransition&&!c){let p=.5,l=.5;if(n?.pointerClientX!==void 0&&n?.pointerClientY!==void 0&&typeof window<"u")p=Ot(n.pointerClientX/window.innerWidth),l=Ot(n.pointerClientY/window.innerHeight);else if(n?.element){const u=n.element.getBoundingClientRect();u.width>0&&u.height>0&&typeof window<"u"&&(p=Ot((u.left+u.width/2)/window.innerWidth),l=Ot((u.top+u.height/2)/window.innerHeight))}o.style.setProperty("--theme-switch-x",`${p*100}%`),o.style.setProperty("--theme-switch-y",`${l*100}%`),o.classList.add("theme-transition");try{const u=a.startViewTransition?.(()=>{t()});u?.finished?u.finished.finally(()=>Dt(o)):Dt(o)}catch{Dt(o),t()}return}t(),Dt(o)};function Vc(e){e.nodesPollInterval==null&&(e.nodesPollInterval=window.setInterval(()=>{un(e,{quiet:!0})},5e3))}function Gc(e){e.nodesPollInterval!=null&&(clearInterval(e.nodesPollInterval),e.nodesPollInterval=null)}function zs(e){e.logsPollInterval==null&&(e.logsPollInterval=window.setInterval(()=>{e.tab==="logs"&&Ms(e,{quiet:!0})},2e3))}function js(e){e.logsPollInterval!=null&&(clearInterval(e.logsPollInterval),e.logsPollInterval=null)}function qs(e){e.debugPollInterval==null&&(e.debugPollInterval=window.setInterval(()=>{e.tab==="debug"&&cn(e)},3e3))}function Ws(e){e.debugPollInterval!=null&&(clearInterval(e.debugPollInterval),e.debugPollInterval=null)}function $e(e,t){const n={...t,lastActiveSessionKey:t.lastActiveSessionKey?.trim()||t.sessionKey.trim()||"main"};e.settings=n,ll(n),t.theme!==e.theme&&(e.theme=t.theme,fn(e,Hs(t.theme))),e.applySessionKey=e.settings.lastActiveSessionKey}function _a(e,t){const n=t.trim();n&&e.settings.lastActiveSessionKey!==n&&$e(e,{...e.settings,lastActiveSessionKey:n})}function Yc(e){if(!window.location.search)return;const t=new URLSearchParams(window.location.search),n=t.get("token"),s=t.get("password"),i=t.get("session"),o=t.get("gatewayUrl");let a=!1;if(n!=null){const r=n.trim();r&&r!==e.settings.token&&$e(e,{...e.settings,token:r}),t.delete("token"),a=!0}if(s!=null){const r=s.trim();r&&(e.password=r),t.delete("password"),a=!0}if(i!=null){const r=i.trim();r&&(e.sessionKey=r,$e(e,{...e.settings,sessionKey:r,lastActiveSessionKey:r}))}if(o!=null){const r=o.trim();r&&r!==e.settings.gatewayUrl&&$e(e,{...e.settings,gatewayUrl:r}),t.delete("gatewayUrl"),a=!0}if(!a)return;const c=new URL(window.location.href);c.search=t.toString(),window.history.replaceState({},"",c.toString())}function Qc(e,t){e.tab!==t&&(e.tab=t),t==="chat"&&(e.chatHasAutoScrolled=!1),t==="logs"?zs(e):js(e),t==="debug"?qs(e):Ws(e),Vs(e),Ea(e,t,!1)}function Jc(e,t,n){Wc({nextTheme:t,applyTheme:()=>{e.theme=t,$e(e,{...e.settings,theme:t}),fn(e,Hs(t))},context:n,currentTheme:e.theme})}async function Vs(e){e.tab==="overview"&&await Ca(e),e.tab==="channels"&&await od(e),e.tab==="instances"&&await Ks(e),e.tab==="sessions"&&await tt(e),e.tab==="cron"&&await Gs(e),e.tab==="skills"&&await _t(e),e.tab==="nodes"&&(await un(e),await Se(e),await me(e),await Us(e)),e.tab==="chat"&&(await dd(e),rn(e,!e.chatHasAutoScrolled)),e.tab==="config"&&(await aa(e),await me(e)),e.tab==="debug"&&(await cn(e),e.eventLog=e.eventLogBuffer),e.tab==="logs"&&(e.logsAtBottom=!0,await Ms(e,{reset:!0}),sa(e,!0))}function Zc(){if(typeof window>"u")return"";const e=window.__CLAWDBOT_CONTROL_UI_BASE_PATH__;return typeof e=="string"&&e.trim()?on(e):dl(window.location.pathname)}function Xc(e){e.theme=e.settings.theme??"system",fn(e,Hs(e.theme))}function fn(e,t){if(e.themeResolved=t,typeof document>"u")return;const n=document.documentElement;n.dataset.theme=t,n.style.colorScheme=t}function ed(e){if(typeof window>"u"||typeof window.matchMedia!="function")return;if(e.themeMedia=window.matchMedia("(prefers-color-scheme: dark)"),e.themeMediaHandler=n=>{e.theme==="system"&&fn(e,n.matches?"dark":"light")},typeof e.themeMedia.addEventListener=="function"){e.themeMedia.addEventListener("change",e.themeMediaHandler);return}e.themeMedia.addListener(e.themeMediaHandler)}function td(e){if(!e.themeMedia||!e.themeMediaHandler)return;if(typeof e.themeMedia.removeEventListener=="function"){e.themeMedia.removeEventListener("change",e.themeMediaHandler);return}e.themeMedia.removeListener(e.themeMediaHandler),e.themeMedia=null,e.themeMediaHandler=null}function nd(e,t){if(typeof window>"u")return;const n=ea(window.location.pathname,e.basePath)??"chat";Ta(e,n),Ea(e,n,t)}function sd(e){if(typeof window>"u")return;const t=ea(window.location.pathname,e.basePath);if(!t)return;const s=new URL(window.location.href).searchParams.get("session")?.trim();s&&(e.sessionKey=s,$e(e,{...e.settings,sessionKey:s,lastActiveSessionKey:s})),Ta(e,t)}function Ta(e,t){e.tab!==t&&(e.tab=t),t==="chat"&&(e.chatHasAutoScrolled=!1),t==="logs"?zs(e):js(e),t==="debug"?qs(e):Ws(e),e.connected&&Vs(e)}function Ea(e,t,n){if(typeof window>"u")return;const s=$t(Is(t,e.basePath)),i=$t(window.location.pathname),o=new URL(window.location.href);t==="chat"&&e.sessionKey?o.searchParams.set("session",e.sessionKey):o.searchParams.delete("session"),i!==s&&(o.pathname=s),n?window.history.replaceState({},"",o.toString()):window.history.pushState({},"",o.toString())}function id(e,t,n){if(typeof window>"u")return;const s=new URL(window.location.href);s.searchParams.set("session",t),window.history.replaceState({},"",s.toString())}async function Ca(e){await Promise.all([oe(e,!1),Ks(e),tt(e),St(e),cn(e)])}async function od(e){await Promise.all([oe(e,!0),aa(e),me(e)])}async function Gs(e){await Promise.all([oe(e,!1),St(e),ln(e)])}function Ia(e){return e.chatSending||!!e.chatRunId}function ad(e){const t=e.trim();if(!t)return!1;const n=t.toLowerCase();return n==="/stop"?!0:n==="stop"||n==="esc"||n==="abort"||n==="wait"||n==="exit"}async function La(e){e.connected&&(e.chatMessage="",await $l(e))}function rd(e,t){const n=t.trim();n&&(e.chatQueue=[...e.chatQueue,{id:Ls(),text:n,createdAt:Date.now()}])}async function Ra(e,t,n){Rs(e);const s=await wl(e,t);return!s&&n?.previousDraft!=null&&(e.chatMessage=n.previousDraft),s&&_a(e,e.sessionKey),s&&n?.restoreDraft&&n.previousDraft?.trim()&&(e.chatMessage=n.previousDraft),rn(e),s&&!e.chatRunId&&Ma(e),s}async function Ma(e){if(!e.connected||Ia(e))return;const[t,...n]=e.chatQueue;if(!t)return;e.chatQueue=n,await Ra(e,t.text)||(e.chatQueue=[t,...e.chatQueue])}function ld(e,t){e.chatQueue=e.chatQueue.filter(n=>n.id!==t)}async function cd(e,t,n){if(!e.connected)return;const s=e.chatMessage,i=(t??e.chatMessage).trim();if(i){if(ad(i)){await La(e);return}if(t==null&&(e.chatMessage=""),Ia(e)){rd(e,i);return}await Ra(e,i,{previousDraft:t==null?s:void 0,restoreDraft:!!(t&&n?.restoreDraft)})}}async function dd(e){await Promise.all([Je(e),tt(e),ds(e)]),rn(e,!0)}const ud=Ma;function pd(e){const t=Jo(e.sessionKey);return t?.agentId?t.agentId:e.hello?.snapshot?.sessionDefaults?.defaultAgentId?.trim()||"main"}function fd(e,t){const n=on(e),s=encodeURIComponent(t);return n?`${n}/avatar/${s}?meta=1`:`/avatar/${s}?meta=1`}async function ds(e){if(!e.connected){e.chatAvatarUrl=null;return}const t=pd(e);if(!t){e.chatAvatarUrl=null;return}e.chatAvatarUrl=null;const n=fd(e.basePath,t);try{const s=await fetch(n,{method:"GET"});if(!s.ok){e.chatAvatarUrl=null;return}const i=await s.json(),o=typeof i.avatarUrl=="string"?i.avatarUrl.trim():"";e.chatAvatarUrl=o||null}catch{e.chatAvatarUrl=null}}const Pa={CHILD:2},Na=e=>(...t)=>({_$litDirective$:e,values:t});let Oa=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,n,s){this._$Ct=t,this._$AM=n,this._$Ci=s}_$AS(t,n){return this.update(t,n)}update(t,n){return this.render(...n)}};const{I:hd}=Jr,to=e=>e,no=()=>document.createComment(""),at=(e,t,n)=>{const s=e._$AA.parentNode,i=t===void 0?e._$AB:t._$AA;if(n===void 0){const o=s.insertBefore(no(),i),a=s.insertBefore(no(),i);n=new hd(o,a,e,e.options)}else{const o=n._$AB.nextSibling,a=n._$AM,c=a!==e;if(c){let r;n._$AQ?.(e),n._$AM=e,n._$AP!==void 0&&(r=e._$AU)!==a._$AU&&n._$AP(r)}if(o!==i||c){let r=n._$AA;for(;r!==o;){const p=to(r).nextSibling;to(s).insertBefore(r,i),r=p}}}return n},Ie=(e,t,n=e)=>(e._$AI(t,n),e),gd={},vd=(e,t=gd)=>e._$AH=t,md=e=>e._$AH,zn=e=>{e._$AR(),e._$AA.remove()};const so=(e,t,n)=>{const s=new Map;for(let i=t;i<=n;i++)s.set(e[i],i);return s},Da=Na(class extends Oa{constructor(e){if(super(e),e.type!==Pa.CHILD)throw Error("repeat() can only be used in text expressions")}dt(e,t,n){let s;n===void 0?n=t:t!==void 0&&(s=t);const i=[],o=[];let a=0;for(const c of e)i[a]=s?s(c,a):a,o[a]=n(c,a),a++;return{values:o,keys:i}}render(e,t,n){return this.dt(e,t,n).values}update(e,[t,n,s]){const i=md(e),{values:o,keys:a}=this.dt(t,n,s);if(!Array.isArray(i))return this.ut=a,o;const c=this.ut??=[],r=[];let p,l,u=0,h=i.length-1,v=0,w=o.length-1;for(;u<=h&&v<=w;)if(i[u]===null)u++;else if(i[h]===null)h--;else if(c[u]===a[v])r[v]=Ie(i[u],o[v]),u++,v++;else if(c[h]===a[w])r[w]=Ie(i[h],o[w]),h--,w--;else if(c[u]===a[w])r[w]=Ie(i[u],o[w]),at(e,r[w+1],i[u]),u++,w--;else if(c[h]===a[v])r[v]=Ie(i[h],o[v]),at(e,i[u],i[h]),h--,v++;else if(p===void 0&&(p=so(a,v,w),l=so(c,u,h)),p.has(c[u]))if(p.has(c[h])){const $=l.get(a[v]),x=$!==void 0?i[$]:null;if(x===null){const E=at(e,i[u]);Ie(E,o[v]),r[v]=E}else r[v]=Ie(x,o[v]),at(e,i[u],x),i[$]=null;v++}else zn(i[h]),h--;else zn(i[u]),u++;for(;v<=w;){const $=at(e,r[w+1]);Ie($,o[v]),r[v++]=$}for(;u<=h;){const $=i[u++];$!==null&&zn($)}return this.ut=a,vd(e,r),xe}});function Ba(e){const t=e;let n=typeof t.role=="string"?t.role:"unknown";const s=typeof t.toolCallId=="string"||typeof t.tool_call_id=="string",i=t.content,o=Array.isArray(i)?i:null,a=Array.isArray(o)&&o.some(u=>{const h=u,v=String(h.type??"").toLowerCase();return v==="toolcall"||v==="tool_call"||v==="tooluse"||v==="tool_use"||v==="toolresult"||v==="tool_result"||v==="tool_call"||v==="tool_result"||typeof h.name=="string"&&h.arguments!=null}),c=typeof t.toolName=="string"||typeof t.tool_name=="string";(s||a||c)&&(n="toolResult");let r=[];typeof t.content=="string"?r=[{type:"text",text:t.content}]:Array.isArray(t.content)?r=t.content.map(u=>({type:u.type||"text",text:u.text,name:u.name,args:u.args||u.arguments})):typeof t.text=="string"&&(r=[{type:"text",text:t.text}]);const p=typeof t.timestamp=="number"?t.timestamp:Date.now(),l=typeof t.id=="string"?t.id:void 0;return{role:n,content:r,timestamp:p,id:l}}function Ys(e){const t=e.toLowerCase();return t==="toolresult"||t==="tool_result"||t==="tool"||t==="function"||t==="toolresult"?"tool":t==="assistant"?"assistant":t==="user"?"user":t==="system"?"system":e}function Fa(e){const t=e,n=typeof t.role=="string"?t.role.toLowerCase():"";return n==="toolresult"||n==="tool_result"}class us extends Oa{constructor(t){if(super(t),this.it=g,t.type!==Pa.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(t){if(t===g||t==null)return this._t=void 0,this.it=t;if(t===xe)return t;if(typeof t!="string")throw Error(this.constructor.directiveName+"() called with a non-string value");if(t===this.it)return this._t;this.it=t;const n=[t];return n.raw=n,this._t={_$litType$:this.constructor.resultType,strings:n,values:[]}}}us.directiveName="unsafeHTML",us.resultType=1;const ps=Na(us);const{entries:Ua,setPrototypeOf:io,isFrozen:bd,getPrototypeOf:yd,getOwnPropertyDescriptor:wd}=Object;let{freeze:Q,seal:te,create:fs}=Object,{apply:hs,construct:gs}=typeof Reflect<"u"&&Reflect;Q||(Q=function(t){return t});te||(te=function(t){return t});hs||(hs=function(t,n){for(var s=arguments.length,i=new Array(s>2?s-2:0),o=2;o1?n-1:0),i=1;i1?n-1:0),i=1;i2&&arguments[2]!==void 0?arguments[2]:qt;io&&io(e,null);let s=t.length;for(;s--;){let i=t[s];if(typeof i=="string"){const o=n(i);o!==i&&(bd(t)||(t[s]=o),i=o)}e[i]=!0}return e}function _d(e){for(let t=0;t/gm),Ld=te(/\$\{[\w\W]*/gm),Rd=te(/^data-[\-\w.\u00B7-\uFFFF]+$/),Md=te(/^aria-[\-\w]+$/),Ka=te(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),Pd=te(/^(?:\w+script|data):/i),Nd=te(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),Ha=te(/^html$/i),Od=te(/^[a-z][.\w]*(-[.\w]+)+$/i);var uo=Object.freeze({__proto__:null,ARIA_ATTR:Md,ATTR_WHITESPACE:Nd,CUSTOM_ELEMENT:Od,DATA_ATTR:Rd,DOCTYPE_NAME:Ha,ERB_EXPR:Id,IS_ALLOWED_URI:Ka,IS_SCRIPT_OR_DATA:Pd,MUSTACHE_EXPR:Cd,TMPLIT_EXPR:Ld});const ut={element:1,text:3,progressingInstruction:7,comment:8,document:9},Dd=function(){return typeof window>"u"?null:window},Bd=function(t,n){if(typeof t!="object"||typeof t.createPolicy!="function")return null;let s=null;const i="data-tt-policy-suffix";n&&n.hasAttribute(i)&&(s=n.getAttribute(i));const o="dompurify"+(s?"#"+s:"");try{return t.createPolicy(o,{createHTML(a){return a},createScriptURL(a){return a}})}catch{return console.warn("TrustedTypes policy "+o+" could not be created."),null}},po=function(){return{afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}};function za(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:Dd();const t=T=>za(T);if(t.version="3.3.1",t.removed=[],!e||!e.document||e.document.nodeType!==ut.document||!e.Element)return t.isSupported=!1,t;let{document:n}=e;const s=n,i=s.currentScript,{DocumentFragment:o,HTMLTemplateElement:a,Node:c,Element:r,NodeFilter:p,NamedNodeMap:l=e.NamedNodeMap||e.MozNamedAttrMap,HTMLFormElement:u,DOMParser:h,trustedTypes:v}=e,w=r.prototype,$=dt(w,"cloneNode"),x=dt(w,"remove"),E=dt(w,"nextSibling"),I=dt(w,"childNodes"),R=dt(w,"parentNode");if(typeof a=="function"){const T=n.createElement("template");T.content&&T.content.ownerDocument&&(n=T.content.ownerDocument)}let C,A="";const{implementation:B,createNodeIterator:ue,createDocumentFragment:bn,getElementsByTagName:yn}=n,{importNode:br}=s;let V=po();t.isSupported=typeof Ua=="function"&&typeof R=="function"&&B&&B.createHTMLDocument!==void 0;const{MUSTACHE_EXPR:wn,ERB_EXPR:$n,TMPLIT_EXPR:kn,DATA_ATTR:yr,ARIA_ATTR:wr,IS_SCRIPT_OR_DATA:$r,ATTR_WHITESPACE:ri,CUSTOM_ELEMENT:kr}=uo;let{IS_ALLOWED_URI:li}=uo,K=null;const ci=L({},[...ao,...Wn,...Vn,...Gn,...ro]);let z=null;const di=L({},[...lo,...Yn,...co,...Ft]);let D=Object.seal(fs(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),nt=null,xn=null;const Ue=Object.seal(fs(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let ui=!0,An=!0,pi=!1,fi=!0,Ke=!1,Et=!0,Te=!1,Sn=!1,_n=!1,He=!1,Ct=!1,It=!1,hi=!0,gi=!1;const xr="user-content-";let Tn=!0,st=!1,ze={},ae=null;const En=L({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let vi=null;const mi=L({},["audio","video","img","source","image","track"]);let Cn=null;const bi=L({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Lt="http://www.w3.org/1998/Math/MathML",Rt="http://www.w3.org/2000/svg",pe="http://www.w3.org/1999/xhtml";let je=pe,In=!1,Ln=null;const Ar=L({},[Lt,Rt,pe],jn);let Mt=L({},["mi","mo","mn","ms","mtext"]),Pt=L({},["annotation-xml"]);const Sr=L({},["title","style","font","a","script"]);let it=null;const _r=["application/xhtml+xml","text/html"],Tr="text/html";let U=null,qe=null;const Er=n.createElement("form"),yi=function(f){return f instanceof RegExp||f instanceof Function},Rn=function(){let f=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(!(qe&&qe===f)){if((!f||typeof f!="object")&&(f={}),f=ce(f),it=_r.indexOf(f.PARSER_MEDIA_TYPE)===-1?Tr:f.PARSER_MEDIA_TYPE,U=it==="application/xhtml+xml"?jn:qt,K=ne(f,"ALLOWED_TAGS")?L({},f.ALLOWED_TAGS,U):ci,z=ne(f,"ALLOWED_ATTR")?L({},f.ALLOWED_ATTR,U):di,Ln=ne(f,"ALLOWED_NAMESPACES")?L({},f.ALLOWED_NAMESPACES,jn):Ar,Cn=ne(f,"ADD_URI_SAFE_ATTR")?L(ce(bi),f.ADD_URI_SAFE_ATTR,U):bi,vi=ne(f,"ADD_DATA_URI_TAGS")?L(ce(mi),f.ADD_DATA_URI_TAGS,U):mi,ae=ne(f,"FORBID_CONTENTS")?L({},f.FORBID_CONTENTS,U):En,nt=ne(f,"FORBID_TAGS")?L({},f.FORBID_TAGS,U):ce({}),xn=ne(f,"FORBID_ATTR")?L({},f.FORBID_ATTR,U):ce({}),ze=ne(f,"USE_PROFILES")?f.USE_PROFILES:!1,ui=f.ALLOW_ARIA_ATTR!==!1,An=f.ALLOW_DATA_ATTR!==!1,pi=f.ALLOW_UNKNOWN_PROTOCOLS||!1,fi=f.ALLOW_SELF_CLOSE_IN_ATTR!==!1,Ke=f.SAFE_FOR_TEMPLATES||!1,Et=f.SAFE_FOR_XML!==!1,Te=f.WHOLE_DOCUMENT||!1,He=f.RETURN_DOM||!1,Ct=f.RETURN_DOM_FRAGMENT||!1,It=f.RETURN_TRUSTED_TYPE||!1,_n=f.FORCE_BODY||!1,hi=f.SANITIZE_DOM!==!1,gi=f.SANITIZE_NAMED_PROPS||!1,Tn=f.KEEP_CONTENT!==!1,st=f.IN_PLACE||!1,li=f.ALLOWED_URI_REGEXP||Ka,je=f.NAMESPACE||pe,Mt=f.MATHML_TEXT_INTEGRATION_POINTS||Mt,Pt=f.HTML_INTEGRATION_POINTS||Pt,D=f.CUSTOM_ELEMENT_HANDLING||{},f.CUSTOM_ELEMENT_HANDLING&&yi(f.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(D.tagNameCheck=f.CUSTOM_ELEMENT_HANDLING.tagNameCheck),f.CUSTOM_ELEMENT_HANDLING&&yi(f.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(D.attributeNameCheck=f.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),f.CUSTOM_ELEMENT_HANDLING&&typeof f.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(D.allowCustomizedBuiltInElements=f.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Ke&&(An=!1),Ct&&(He=!0),ze&&(K=L({},ro),z=[],ze.html===!0&&(L(K,ao),L(z,lo)),ze.svg===!0&&(L(K,Wn),L(z,Yn),L(z,Ft)),ze.svgFilters===!0&&(L(K,Vn),L(z,Yn),L(z,Ft)),ze.mathMl===!0&&(L(K,Gn),L(z,co),L(z,Ft))),f.ADD_TAGS&&(typeof f.ADD_TAGS=="function"?Ue.tagCheck=f.ADD_TAGS:(K===ci&&(K=ce(K)),L(K,f.ADD_TAGS,U))),f.ADD_ATTR&&(typeof f.ADD_ATTR=="function"?Ue.attributeCheck=f.ADD_ATTR:(z===di&&(z=ce(z)),L(z,f.ADD_ATTR,U))),f.ADD_URI_SAFE_ATTR&&L(Cn,f.ADD_URI_SAFE_ATTR,U),f.FORBID_CONTENTS&&(ae===En&&(ae=ce(ae)),L(ae,f.FORBID_CONTENTS,U)),f.ADD_FORBID_CONTENTS&&(ae===En&&(ae=ce(ae)),L(ae,f.ADD_FORBID_CONTENTS,U)),Tn&&(K["#text"]=!0),Te&&L(K,["html","head","body"]),K.table&&(L(K,["tbody"]),delete nt.tbody),f.TRUSTED_TYPES_POLICY){if(typeof f.TRUSTED_TYPES_POLICY.createHTML!="function")throw ct('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if(typeof f.TRUSTED_TYPES_POLICY.createScriptURL!="function")throw ct('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');C=f.TRUSTED_TYPES_POLICY,A=C.createHTML("")}else C===void 0&&(C=Bd(v,i)),C!==null&&typeof A=="string"&&(A=C.createHTML(""));Q&&Q(f),qe=f}},wi=L({},[...Wn,...Vn,...Td]),$i=L({},[...Gn,...Ed]),Cr=function(f){let k=R(f);(!k||!k.tagName)&&(k={namespaceURI:je,tagName:"template"});const _=qt(f.tagName),N=qt(k.tagName);return Ln[f.namespaceURI]?f.namespaceURI===Rt?k.namespaceURI===pe?_==="svg":k.namespaceURI===Lt?_==="svg"&&(N==="annotation-xml"||Mt[N]):!!wi[_]:f.namespaceURI===Lt?k.namespaceURI===pe?_==="math":k.namespaceURI===Rt?_==="math"&&Pt[N]:!!$i[_]:f.namespaceURI===pe?k.namespaceURI===Rt&&!Pt[N]||k.namespaceURI===Lt&&!Mt[N]?!1:!$i[_]&&(Sr[_]||!wi[_]):!!(it==="application/xhtml+xml"&&Ln[f.namespaceURI]):!1},re=function(f){rt(t.removed,{element:f});try{R(f).removeChild(f)}catch{x(f)}},Ee=function(f,k){try{rt(t.removed,{attribute:k.getAttributeNode(f),from:k})}catch{rt(t.removed,{attribute:null,from:k})}if(k.removeAttribute(f),f==="is")if(He||Ct)try{re(k)}catch{}else try{k.setAttribute(f,"")}catch{}},ki=function(f){let k=null,_=null;if(_n)f=""+f;else{const F=qn(f,/^[\r\n\t ]+/);_=F&&F[0]}it==="application/xhtml+xml"&&je===pe&&(f=''+f+"");const N=C?C.createHTML(f):f;if(je===pe)try{k=new h().parseFromString(N,it)}catch{}if(!k||!k.documentElement){k=B.createDocument(je,"template",null);try{k.documentElement.innerHTML=In?A:N}catch{}}const q=k.body||k.documentElement;return f&&_&&q.insertBefore(n.createTextNode(_),q.childNodes[0]||null),je===pe?yn.call(k,Te?"html":"body")[0]:Te?k.documentElement:q},xi=function(f){return ue.call(f.ownerDocument||f,f,p.SHOW_ELEMENT|p.SHOW_COMMENT|p.SHOW_TEXT|p.SHOW_PROCESSING_INSTRUCTION|p.SHOW_CDATA_SECTION,null)},Mn=function(f){return f instanceof u&&(typeof f.nodeName!="string"||typeof f.textContent!="string"||typeof f.removeChild!="function"||!(f.attributes instanceof l)||typeof f.removeAttribute!="function"||typeof f.setAttribute!="function"||typeof f.namespaceURI!="string"||typeof f.insertBefore!="function"||typeof f.hasChildNodes!="function")},Ai=function(f){return typeof c=="function"&&f instanceof c};function fe(T,f,k){Bt(T,_=>{_.call(t,f,k,qe)})}const Si=function(f){let k=null;if(fe(V.beforeSanitizeElements,f,null),Mn(f))return re(f),!0;const _=U(f.nodeName);if(fe(V.uponSanitizeElement,f,{tagName:_,allowedTags:K}),Et&&f.hasChildNodes()&&!Ai(f.firstElementChild)&&G(/<[/\w!]/g,f.innerHTML)&&G(/<[/\w!]/g,f.textContent)||f.nodeType===ut.progressingInstruction||Et&&f.nodeType===ut.comment&&G(/<[/\w]/g,f.data))return re(f),!0;if(!(Ue.tagCheck instanceof Function&&Ue.tagCheck(_))&&(!K[_]||nt[_])){if(!nt[_]&&Ti(_)&&(D.tagNameCheck instanceof RegExp&&G(D.tagNameCheck,_)||D.tagNameCheck instanceof Function&&D.tagNameCheck(_)))return!1;if(Tn&&!ae[_]){const N=R(f)||f.parentNode,q=I(f)||f.childNodes;if(q&&N){const F=q.length;for(let Z=F-1;Z>=0;--Z){const he=$(q[Z],!0);he.__removalCount=(f.__removalCount||0)+1,N.insertBefore(he,E(f))}}}return re(f),!0}return f instanceof r&&!Cr(f)||(_==="noscript"||_==="noembed"||_==="noframes")&&G(/<\/no(script|embed|frames)/i,f.innerHTML)?(re(f),!0):(Ke&&f.nodeType===ut.text&&(k=f.textContent,Bt([wn,$n,kn],N=>{k=lt(k,N," ")}),f.textContent!==k&&(rt(t.removed,{element:f.cloneNode()}),f.textContent=k)),fe(V.afterSanitizeElements,f,null),!1)},_i=function(f,k,_){if(hi&&(k==="id"||k==="name")&&(_ in n||_ in Er))return!1;if(!(An&&!xn[k]&&G(yr,k))){if(!(ui&&G(wr,k))){if(!(Ue.attributeCheck instanceof Function&&Ue.attributeCheck(k,f))){if(!z[k]||xn[k]){if(!(Ti(f)&&(D.tagNameCheck instanceof RegExp&&G(D.tagNameCheck,f)||D.tagNameCheck instanceof Function&&D.tagNameCheck(f))&&(D.attributeNameCheck instanceof RegExp&&G(D.attributeNameCheck,k)||D.attributeNameCheck instanceof Function&&D.attributeNameCheck(k,f))||k==="is"&&D.allowCustomizedBuiltInElements&&(D.tagNameCheck instanceof RegExp&&G(D.tagNameCheck,_)||D.tagNameCheck instanceof Function&&D.tagNameCheck(_))))return!1}else if(!Cn[k]){if(!G(li,lt(_,ri,""))){if(!((k==="src"||k==="xlink:href"||k==="href")&&f!=="script"&&xd(_,"data:")===0&&vi[f])){if(!(pi&&!G($r,lt(_,ri,"")))){if(_)return!1}}}}}}}return!0},Ti=function(f){return f!=="annotation-xml"&&qn(f,kr)},Ei=function(f){fe(V.beforeSanitizeAttributes,f,null);const{attributes:k}=f;if(!k||Mn(f))return;const _={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:z,forceKeepAttr:void 0};let N=k.length;for(;N--;){const q=k[N],{name:F,namespaceURI:Z,value:he}=q,We=U(F),Pn=he;let j=F==="value"?Pn:Ad(Pn);if(_.attrName=We,_.attrValue=j,_.keepAttr=!0,_.forceKeepAttr=void 0,fe(V.uponSanitizeAttribute,f,_),j=_.attrValue,gi&&(We==="id"||We==="name")&&(Ee(F,f),j=xr+j),Et&&G(/((--!?|])>)|<\/(style|title|textarea)/i,j)){Ee(F,f);continue}if(We==="attributename"&&qn(j,"href")){Ee(F,f);continue}if(_.forceKeepAttr)continue;if(!_.keepAttr){Ee(F,f);continue}if(!fi&&G(/\/>/i,j)){Ee(F,f);continue}Ke&&Bt([wn,$n,kn],Ii=>{j=lt(j,Ii," ")});const Ci=U(f.nodeName);if(!_i(Ci,We,j)){Ee(F,f);continue}if(C&&typeof v=="object"&&typeof v.getAttributeType=="function"&&!Z)switch(v.getAttributeType(Ci,We)){case"TrustedHTML":{j=C.createHTML(j);break}case"TrustedScriptURL":{j=C.createScriptURL(j);break}}if(j!==Pn)try{Z?f.setAttributeNS(Z,F,j):f.setAttribute(F,j),Mn(f)?re(f):oo(t.removed)}catch{Ee(F,f)}}fe(V.afterSanitizeAttributes,f,null)},Ir=function T(f){let k=null;const _=xi(f);for(fe(V.beforeSanitizeShadowDOM,f,null);k=_.nextNode();)fe(V.uponSanitizeShadowNode,k,null),Si(k),Ei(k),k.content instanceof o&&T(k.content);fe(V.afterSanitizeShadowDOM,f,null)};return t.sanitize=function(T){let f=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},k=null,_=null,N=null,q=null;if(In=!T,In&&(T=""),typeof T!="string"&&!Ai(T))if(typeof T.toString=="function"){if(T=T.toString(),typeof T!="string")throw ct("dirty is not a string, aborting")}else throw ct("toString is not a function");if(!t.isSupported)return T;if(Sn||Rn(f),t.removed=[],typeof T=="string"&&(st=!1),st){if(T.nodeName){const he=U(T.nodeName);if(!K[he]||nt[he])throw ct("root node is forbidden and cannot be sanitized in-place")}}else if(T instanceof c)k=ki(""),_=k.ownerDocument.importNode(T,!0),_.nodeType===ut.element&&_.nodeName==="BODY"||_.nodeName==="HTML"?k=_:k.appendChild(_);else{if(!He&&!Ke&&!Te&&T.indexOf("<")===-1)return C&&It?C.createHTML(T):T;if(k=ki(T),!k)return He?null:It?A:""}k&&_n&&re(k.firstChild);const F=xi(st?T:k);for(;N=F.nextNode();)Si(N),Ei(N),N.content instanceof o&&Ir(N.content);if(st)return T;if(He){if(Ct)for(q=bn.call(k.ownerDocument);k.firstChild;)q.appendChild(k.firstChild);else q=k;return(z.shadowroot||z.shadowrootmode)&&(q=br.call(s,q,!0)),q}let Z=Te?k.outerHTML:k.innerHTML;return Te&&K["!doctype"]&&k.ownerDocument&&k.ownerDocument.doctype&&k.ownerDocument.doctype.name&&G(Ha,k.ownerDocument.doctype.name)&&(Z=" -`+Z),Ke&&Bt([wn,$n,kn],he=>{Z=lt(Z,he," ")}),C&&It?C.createHTML(Z):Z},t.setConfig=function(){let T=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};Rn(T),Sn=!0},t.clearConfig=function(){qe=null,Sn=!1},t.isValidAttribute=function(T,f,k){qe||Rn({});const _=U(T),N=U(f);return _i(_,N,k)},t.addHook=function(T,f){typeof f=="function"&&rt(V[T],f)},t.removeHook=function(T,f){if(f!==void 0){const k=$d(V[T],f);return k===-1?void 0:kd(V[T],k,1)[0]}return oo(V[T])},t.removeHooks=function(T){V[T]=[]},t.removeAllHooks=function(){V=po()},t}var vs=za();function Qs(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var Fe=Qs();function ja(e){Fe=e}var mt={exec:()=>null};function M(e,t=""){let n=typeof e=="string"?e:e.source,s={replace:(i,o)=>{let a=typeof o=="string"?o:o.source;return a=a.replace(Y.caret,"$1"),n=n.replace(i,a),s},getRegex:()=>new RegExp(n,t)};return s}var Fd=(()=>{try{return!!new RegExp("(?<=1)(?/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] +\S/,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>new RegExp(`^( {0,3}${e})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}#`),htmlBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}<(?:[a-z].*>|!--)`,"i")},Ud=/^(?:[ \t]*(?:\n|$))+/,Kd=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,Hd=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,Tt=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,zd=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,Js=/(?:[*+-]|\d{1,9}[.)])/,qa=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,Wa=M(qa).replace(/bull/g,Js).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),jd=M(qa).replace(/bull/g,Js).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),Zs=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,qd=/^[^\n]+/,Xs=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,Wd=M(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",Xs).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),Vd=M(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,Js).getRegex(),hn="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",ei=/|$))/,Gd=M("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",ei).replace("tag",hn).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Va=M(Zs).replace("hr",Tt).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",hn).getRegex(),Yd=M(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",Va).getRegex(),ti={blockquote:Yd,code:Kd,def:Wd,fences:Hd,heading:zd,hr:Tt,html:Gd,lheading:Wa,list:Vd,newline:Ud,paragraph:Va,table:mt,text:qd},fo=M("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",Tt).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",hn).getRegex(),Qd={...ti,lheading:jd,table:fo,paragraph:M(Zs).replace("hr",Tt).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",fo).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",hn).getRegex()},Jd={...ti,html:M(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",ei).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:mt,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:M(Zs).replace("hr",Tt).replace("heading",` *#{1,6} *[^ -]`).replace("lheading",Wa).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},Zd=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,Xd=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,Ga=/^( {2,}|\\)\n(?!\s*$)/,eu=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",Fd?"(?`+)[^`]+\k(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),Ja=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,ou=M(Ja,"u").replace(/punct/g,gn).getRegex(),au=M(Ja,"u").replace(/punct/g,Qa).getRegex(),Za="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",ru=M(Za,"gu").replace(/notPunctSpace/g,Ya).replace(/punctSpace/g,ni).replace(/punct/g,gn).getRegex(),lu=M(Za,"gu").replace(/notPunctSpace/g,su).replace(/punctSpace/g,nu).replace(/punct/g,Qa).getRegex(),cu=M("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,Ya).replace(/punctSpace/g,ni).replace(/punct/g,gn).getRegex(),du=M(/\\(punct)/,"gu").replace(/punct/g,gn).getRegex(),uu=M(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),pu=M(ei).replace("(?:-->|$)","-->").getRegex(),fu=M("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",pu).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),Jt=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+[^`]*?`+(?!`)|[^\[\]\\`])*?/,hu=M(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]*(?:\n[ \t]*)?)(title))?\s*\)/).replace("label",Jt).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),Xa=M(/^!?\[(label)\]\[(ref)\]/).replace("label",Jt).replace("ref",Xs).getRegex(),er=M(/^!?\[(ref)\](?:\[\])?/).replace("ref",Xs).getRegex(),gu=M("reflink|nolink(?!\\()","g").replace("reflink",Xa).replace("nolink",er).getRegex(),ho=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,si={_backpedal:mt,anyPunctuation:du,autolink:uu,blockSkip:iu,br:Ga,code:Xd,del:mt,emStrongLDelim:ou,emStrongRDelimAst:ru,emStrongRDelimUnd:cu,escape:Zd,link:hu,nolink:er,punctuation:tu,reflink:Xa,reflinkSearch:gu,tag:fu,text:eu,url:mt},vu={...si,link:M(/^!?\[(label)\]\((.*?)\)/).replace("label",Jt).getRegex(),reflink:M(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Jt).getRegex()},ms={...si,emStrongRDelimAst:lu,emStrongLDelim:au,url:M(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",ho).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:M(/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},go=e=>bu[e];function ve(e,t){if(t){if(Y.escapeTest.test(e))return e.replace(Y.escapeReplace,go)}else if(Y.escapeTestNoEncode.test(e))return e.replace(Y.escapeReplaceNoEncode,go);return e}function vo(e){try{e=encodeURI(e).replace(Y.percentDecode,"%")}catch{return null}return e}function mo(e,t){let n=e.replace(Y.findPipe,(o,a,c)=>{let r=!1,p=a;for(;--p>=0&&c[p]==="\\";)r=!r;return r?"|":" |"}),s=n.split(Y.splitPipe),i=0;if(s[0].trim()||s.shift(),s.length>0&&!s.at(-1)?.trim()&&s.pop(),t)if(s.length>t)s.splice(t);else for(;s.length0?-2:-1}function bo(e,t,n,s,i){let o=t.href,a=t.title||null,c=e[1].replace(i.other.outputLinkReplace,"$1");s.state.inLink=!0;let r={type:e[0].charAt(0)==="!"?"image":"link",raw:n,href:o,title:a,text:c,tokens:s.inlineTokens(c)};return s.state.inLink=!1,r}function wu(e,t,n){let s=e.match(n.other.indentCodeCompensation);if(s===null)return t;let i=s[1];return t.split(` -`).map(o=>{let a=o.match(n.other.beginningSpace);if(a===null)return o;let[c]=a;return c.length>=i.length?o.slice(i.length):o}).join(` -`)}var Zt=class{options;rules;lexer;constructor(e){this.options=e||Fe}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?n:ft(n,` -`)}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],s=wu(n,t[3]||"",this.rules);return{type:"code",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:s}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let s=ft(n,"#");(this.options.pedantic||!s||this.rules.other.endingSpaceChar.test(s))&&(n=s.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:ft(t[0],` -`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=ft(t[0],` -`).split(` -`),s="",i="",o=[];for(;n.length>0;){let a=!1,c=[],r;for(r=0;r1,i={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");let o=this.rules.other.listItemRegex(n),a=!1;for(;e;){let r=!1,p="",l="";if(!(t=o.exec(e))||this.rules.block.hr.test(e))break;p=t[0],e=e.substring(p.length);let u=t[2].split(` -`,1)[0].replace(this.rules.other.listReplaceTabs,$=>" ".repeat(3*$.length)),h=e.split(` -`,1)[0],v=!u.trim(),w=0;if(this.options.pedantic?(w=2,l=u.trimStart()):v?w=t[1].length+1:(w=t[2].search(this.rules.other.nonSpaceChar),w=w>4?1:w,l=u.slice(w),w+=t[1].length),v&&this.rules.other.blankLine.test(h)&&(p+=h+` -`,e=e.substring(h.length+1),r=!0),!r){let $=this.rules.other.nextBulletRegex(w),x=this.rules.other.hrRegex(w),E=this.rules.other.fencesBeginRegex(w),I=this.rules.other.headingBeginRegex(w),R=this.rules.other.htmlBeginRegex(w);for(;e;){let C=e.split(` -`,1)[0],A;if(h=C,this.options.pedantic?(h=h.replace(this.rules.other.listReplaceNesting," "),A=h):A=h.replace(this.rules.other.tabCharGlobal," "),E.test(h)||I.test(h)||R.test(h)||$.test(h)||x.test(h))break;if(A.search(this.rules.other.nonSpaceChar)>=w||!h.trim())l+=` -`+A.slice(w);else{if(v||u.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||E.test(u)||I.test(u)||x.test(u))break;l+=` -`+h}!v&&!h.trim()&&(v=!0),p+=C+` -`,e=e.substring(C.length+1),u=A.slice(w)}}i.loose||(a?i.loose=!0:this.rules.other.doubleBlankLine.test(p)&&(a=!0)),i.items.push({type:"list_item",raw:p,task:!!this.options.gfm&&this.rules.other.listIsTask.test(l),loose:!1,text:l,tokens:[]}),i.raw+=p}let c=i.items.at(-1);if(c)c.raw=c.raw.trimEnd(),c.text=c.text.trimEnd();else return;i.raw=i.raw.trimEnd();for(let r of i.items){if(this.lexer.state.top=!1,r.tokens=this.lexer.blockTokens(r.text,[]),r.task){if(r.text=r.text.replace(this.rules.other.listReplaceTask,""),r.tokens[0]?.type==="text"||r.tokens[0]?.type==="paragraph"){r.tokens[0].raw=r.tokens[0].raw.replace(this.rules.other.listReplaceTask,""),r.tokens[0].text=r.tokens[0].text.replace(this.rules.other.listReplaceTask,"");for(let l=this.lexer.inlineQueue.length-1;l>=0;l--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[l].src)){this.lexer.inlineQueue[l].src=this.lexer.inlineQueue[l].src.replace(this.rules.other.listReplaceTask,"");break}}let p=this.rules.other.listTaskCheckbox.exec(r.raw);if(p){let l={type:"checkbox",raw:p[0]+" ",checked:p[0]!=="[ ]"};r.checked=l.checked,i.loose?r.tokens[0]&&["paragraph","text"].includes(r.tokens[0].type)&&"tokens"in r.tokens[0]&&r.tokens[0].tokens?(r.tokens[0].raw=l.raw+r.tokens[0].raw,r.tokens[0].text=l.raw+r.tokens[0].text,r.tokens[0].tokens.unshift(l)):r.tokens.unshift({type:"paragraph",raw:l.raw,text:l.raw,tokens:[l]}):r.tokens.unshift(l)}}if(!i.loose){let p=r.tokens.filter(u=>u.type==="space"),l=p.length>0&&p.some(u=>this.rules.other.anyLine.test(u.raw));i.loose=l}}if(i.loose)for(let r of i.items){r.loose=!0;for(let p of r.tokens)p.type==="text"&&(p.type="paragraph")}return i}}html(e){let t=this.rules.block.html.exec(e);if(t)return{type:"html",block:!0,raw:t[0],pre:t[1]==="pre"||t[1]==="script"||t[1]==="style",text:t[0]}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),s=t[2]?t[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",i=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:n,raw:t[0],href:s,title:i}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=mo(t[1]),s=t[2].replace(this.rules.other.tableAlignChars,"").split("|"),i=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,"").split(` -`):[],o={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(let a of s)this.rules.other.tableAlignRight.test(a)?o.align.push("right"):this.rules.other.tableAlignCenter.test(a)?o.align.push("center"):this.rules.other.tableAlignLeft.test(a)?o.align.push("left"):o.align.push(null);for(let a=0;a({text:c,tokens:this.lexer.inline(c),header:!1,align:o.align[r]})));return o}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:t[2].charAt(0)==="="?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===` -`?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let o=ft(n.slice(0,-1),"\\");if((n.length-o.length)%2===0)return}else{let o=yu(t[2],"()");if(o===-2)return;if(o>-1){let a=(t[0].indexOf("!")===0?5:4)+t[1].length+o;t[2]=t[2].substring(0,o),t[0]=t[0].substring(0,a).trim(),t[3]=""}}let s=t[2],i="";if(this.options.pedantic){let o=this.rules.other.pedanticHrefTitle.exec(s);o&&(s=o[1],i=o[3])}else i=t[3]?t[3].slice(1,-1):"";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?s=s.slice(1):s=s.slice(1,-1)),bo(t,{href:s&&s.replace(this.rules.inline.anyPunctuation,"$1"),title:i&&i.replace(this.rules.inline.anyPunctuation,"$1")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let s=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),i=t[s.toLowerCase()];if(!i){let o=n[0].charAt(0);return{type:"text",raw:o,text:o}}return bo(n,i,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!(!s||s[3]&&n.match(this.rules.other.unicodeAlphaNumeric))&&(!(s[1]||s[2])||!n||this.rules.inline.punctuation.exec(n))){let i=[...s[0]].length-1,o,a,c=i,r=0,p=s[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(p.lastIndex=0,t=t.slice(-1*e.length+i);(s=p.exec(t))!=null;){if(o=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!o)continue;if(a=[...o].length,s[3]||s[4]){c+=a;continue}else if((s[5]||s[6])&&i%3&&!((i+a)%3)){r+=a;continue}if(c-=a,c>0)continue;a=Math.min(a,a+c+r);let l=[...s[0]][0].length,u=e.slice(0,i+s.index+l+a);if(Math.min(i,a)%2){let v=u.slice(1,-1);return{type:"em",raw:u,text:v,tokens:this.lexer.inlineTokens(v)}}let h=u.slice(2,-2);return{type:"strong",raw:u,text:h,tokens:this.lexer.inlineTokens(h)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal," "),s=this.rules.other.nonSpaceChar.test(n),i=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return s&&i&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){let t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,s;return t[2]==="@"?(n=t[1],s="mailto:"+n):(n=t[1],s=n),{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,s;if(t[2]==="@")n=t[0],s="mailto:"+n;else{let i;do i=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??"";while(i!==t[0]);n=t[0],t[1]==="www."?s="http://"+t[0]:s=t[0]}return{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:n}}}},se=class bs{tokens;options;state;inlineQueue;tokenizer;constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||Fe,this.options.tokenizer=this.options.tokenizer||new Zt,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let n={other:Y,block:Ut.normal,inline:pt.normal};this.options.pedantic?(n.block=Ut.pedantic,n.inline=pt.pedantic):this.options.gfm&&(n.block=Ut.gfm,this.options.breaks?n.inline=pt.breaks:n.inline=pt.gfm),this.tokenizer.rules=n}static get rules(){return{block:Ut,inline:pt}}static lex(t,n){return new bs(n).lex(t)}static lexInline(t,n){return new bs(n).inlineTokens(t)}lex(t){t=t.replace(Y.carriageReturn,` -`),this.blockTokens(t,this.tokens);for(let n=0;n(i=a.call({lexer:this},t,n))?(t=t.substring(i.raw.length),n.push(i),!0):!1))continue;if(i=this.tokenizer.space(t)){t=t.substring(i.raw.length);let a=n.at(-1);i.raw.length===1&&a!==void 0?a.raw+=` -`:n.push(i);continue}if(i=this.tokenizer.code(t)){t=t.substring(i.raw.length);let a=n.at(-1);a?.type==="paragraph"||a?.type==="text"?(a.raw+=(a.raw.endsWith(` -`)?"":` -`)+i.raw,a.text+=` -`+i.text,this.inlineQueue.at(-1).src=a.text):n.push(i);continue}if(i=this.tokenizer.fences(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.heading(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.hr(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.blockquote(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.list(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.html(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.def(t)){t=t.substring(i.raw.length);let a=n.at(-1);a?.type==="paragraph"||a?.type==="text"?(a.raw+=(a.raw.endsWith(` -`)?"":` -`)+i.raw,a.text+=` -`+i.raw,this.inlineQueue.at(-1).src=a.text):this.tokens.links[i.tag]||(this.tokens.links[i.tag]={href:i.href,title:i.title},n.push(i));continue}if(i=this.tokenizer.table(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.lheading(t)){t=t.substring(i.raw.length),n.push(i);continue}let o=t;if(this.options.extensions?.startBlock){let a=1/0,c=t.slice(1),r;this.options.extensions.startBlock.forEach(p=>{r=p.call({lexer:this},c),typeof r=="number"&&r>=0&&(a=Math.min(a,r))}),a<1/0&&a>=0&&(o=t.substring(0,a+1))}if(this.state.top&&(i=this.tokenizer.paragraph(o))){let a=n.at(-1);s&&a?.type==="paragraph"?(a.raw+=(a.raw.endsWith(` -`)?"":` -`)+i.raw,a.text+=` -`+i.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=a.text):n.push(i),s=o.length!==t.length,t=t.substring(i.raw.length);continue}if(i=this.tokenizer.text(t)){t=t.substring(i.raw.length);let a=n.at(-1);a?.type==="text"?(a.raw+=(a.raw.endsWith(` -`)?"":` -`)+i.raw,a.text+=` -`+i.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=a.text):n.push(i);continue}if(t){let a="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(a);break}else throw new Error(a)}}return this.state.top=!0,n}inline(t,n=[]){return this.inlineQueue.push({src:t,tokens:n}),n}inlineTokens(t,n=[]){let s=t,i=null;if(this.tokens.links){let r=Object.keys(this.tokens.links);if(r.length>0)for(;(i=this.tokenizer.rules.inline.reflinkSearch.exec(s))!=null;)r.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(s=s.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(i=this.tokenizer.rules.inline.anyPunctuation.exec(s))!=null;)s=s.slice(0,i.index)+"++"+s.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let o;for(;(i=this.tokenizer.rules.inline.blockSkip.exec(s))!=null;)o=i[2]?i[2].length:0,s=s.slice(0,i.index+o)+"["+"a".repeat(i[0].length-o-2)+"]"+s.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);s=this.options.hooks?.emStrongMask?.call({lexer:this},s)??s;let a=!1,c="";for(;t;){a||(c=""),a=!1;let r;if(this.options.extensions?.inline?.some(l=>(r=l.call({lexer:this},t,n))?(t=t.substring(r.raw.length),n.push(r),!0):!1))continue;if(r=this.tokenizer.escape(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.tag(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.link(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.reflink(t,this.tokens.links)){t=t.substring(r.raw.length);let l=n.at(-1);r.type==="text"&&l?.type==="text"?(l.raw+=r.raw,l.text+=r.text):n.push(r);continue}if(r=this.tokenizer.emStrong(t,s,c)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.codespan(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.br(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.del(t)){t=t.substring(r.raw.length),n.push(r);continue}if(r=this.tokenizer.autolink(t)){t=t.substring(r.raw.length),n.push(r);continue}if(!this.state.inLink&&(r=this.tokenizer.url(t))){t=t.substring(r.raw.length),n.push(r);continue}let p=t;if(this.options.extensions?.startInline){let l=1/0,u=t.slice(1),h;this.options.extensions.startInline.forEach(v=>{h=v.call({lexer:this},u),typeof h=="number"&&h>=0&&(l=Math.min(l,h))}),l<1/0&&l>=0&&(p=t.substring(0,l+1))}if(r=this.tokenizer.inlineText(p)){t=t.substring(r.raw.length),r.raw.slice(-1)!=="_"&&(c=r.raw.slice(-1)),a=!0;let l=n.at(-1);l?.type==="text"?(l.raw+=r.raw,l.text+=r.text):n.push(r);continue}if(t){let l="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(l);break}else throw new Error(l)}}return n}},Xt=class{options;parser;constructor(e){this.options=e||Fe}space(e){return""}code({text:e,lang:t,escaped:n}){let s=(t||"").match(Y.notSpaceStart)?.[0],i=e.replace(Y.endingNewline,"")+` -`;return s?'
    '+(n?i:ve(i,!0))+`
    -`:"
    "+(n?i:ve(i,!0))+`
    -`}blockquote({tokens:e}){return`
    -${this.parser.parse(e)}
    -`}html({text:e}){return e}def(e){return""}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)} -`}hr(e){return`
    -`}list(e){let t=e.ordered,n=e.start,s="";for(let a=0;a -`+s+" -`}listitem(e){return`
  • ${this.parser.parse(e.tokens)}
  • -`}checkbox({checked:e}){return" '}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    -`}table(e){let t="",n="";for(let i=0;i${s}`),` - -`+t+` -`+s+`
    -`}tablerow({text:e}){return` -${e} -`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+` -`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${ve(e,!0)}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let s=this.parser.parseInline(n),i=vo(e);if(i===null)return s;e=i;let o='
    ",o}image({href:e,title:t,text:n,tokens:s}){s&&(n=this.parser.parseInline(s,this.parser.textRenderer));let i=vo(e);if(i===null)return ve(n);e=i;let o=`${n}{let a=i[o].flat(1/0);n=n.concat(this.walkTokens(a,t))}):i.tokens&&(n=n.concat(this.walkTokens(i.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let s={...n};if(s.async=this.defaults.async||s.async||!1,n.extensions&&(n.extensions.forEach(i=>{if(!i.name)throw new Error("extension name required");if("renderer"in i){let o=t.renderers[i.name];o?t.renderers[i.name]=function(...a){let c=i.renderer.apply(this,a);return c===!1&&(c=o.apply(this,a)),c}:t.renderers[i.name]=i.renderer}if("tokenizer"in i){if(!i.level||i.level!=="block"&&i.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let o=t[i.level];o?o.unshift(i.tokenizer):t[i.level]=[i.tokenizer],i.start&&(i.level==="block"?t.startBlock?t.startBlock.push(i.start):t.startBlock=[i.start]:i.level==="inline"&&(t.startInline?t.startInline.push(i.start):t.startInline=[i.start]))}"childTokens"in i&&i.childTokens&&(t.childTokens[i.name]=i.childTokens)}),s.extensions=t),n.renderer){let i=this.defaults.renderer||new Xt(this.defaults);for(let o in n.renderer){if(!(o in i))throw new Error(`renderer '${o}' does not exist`);if(["options","parser"].includes(o))continue;let a=o,c=n.renderer[a],r=i[a];i[a]=(...p)=>{let l=c.apply(i,p);return l===!1&&(l=r.apply(i,p)),l||""}}s.renderer=i}if(n.tokenizer){let i=this.defaults.tokenizer||new Zt(this.defaults);for(let o in n.tokenizer){if(!(o in i))throw new Error(`tokenizer '${o}' does not exist`);if(["options","rules","lexer"].includes(o))continue;let a=o,c=n.tokenizer[a],r=i[a];i[a]=(...p)=>{let l=c.apply(i,p);return l===!1&&(l=r.apply(i,p)),l}}s.tokenizer=i}if(n.hooks){let i=this.defaults.hooks||new ht;for(let o in n.hooks){if(!(o in i))throw new Error(`hook '${o}' does not exist`);if(["options","block"].includes(o))continue;let a=o,c=n.hooks[a],r=i[a];ht.passThroughHooks.has(o)?i[a]=p=>{if(this.defaults.async&&ht.passThroughHooksRespectAsync.has(o))return(async()=>{let u=await c.call(i,p);return r.call(i,u)})();let l=c.call(i,p);return r.call(i,l)}:i[a]=(...p)=>{if(this.defaults.async)return(async()=>{let u=await c.apply(i,p);return u===!1&&(u=await r.apply(i,p)),u})();let l=c.apply(i,p);return l===!1&&(l=r.apply(i,p)),l}}s.hooks=i}if(n.walkTokens){let i=this.defaults.walkTokens,o=n.walkTokens;s.walkTokens=function(a){let c=[];return c.push(o.call(this,a)),i&&(c=c.concat(i.call(this,a))),c}}this.defaults={...this.defaults,...s}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return se.lex(e,t??this.defaults)}parser(e,t){return ie.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{let s={...n},i={...this.defaults,...s},o=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&s.async===!1)return o(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof t>"u"||t===null)return o(new Error("marked(): input parameter is undefined or null"));if(typeof t!="string")return o(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));if(i.hooks&&(i.hooks.options=i,i.hooks.block=e),i.async)return(async()=>{let a=i.hooks?await i.hooks.preprocess(t):t,c=await(i.hooks?await i.hooks.provideLexer():e?se.lex:se.lexInline)(a,i),r=i.hooks?await i.hooks.processAllTokens(c):c;i.walkTokens&&await Promise.all(this.walkTokens(r,i.walkTokens));let p=await(i.hooks?await i.hooks.provideParser():e?ie.parse:ie.parseInline)(r,i);return i.hooks?await i.hooks.postprocess(p):p})().catch(o);try{i.hooks&&(t=i.hooks.preprocess(t));let a=(i.hooks?i.hooks.provideLexer():e?se.lex:se.lexInline)(t,i);i.hooks&&(a=i.hooks.processAllTokens(a)),i.walkTokens&&this.walkTokens(a,i.walkTokens);let c=(i.hooks?i.hooks.provideParser():e?ie.parse:ie.parseInline)(a,i);return i.hooks&&(c=i.hooks.postprocess(c)),c}catch(a){return o(a)}}}onError(e,t){return n=>{if(n.message+=` -Please report this to https://github.com/markedjs/marked.`,e){let s="

    An error occurred:

    "+ve(n.message+"",!0)+"
    ";return t?Promise.resolve(s):s}if(t)return Promise.reject(n);throw n}}},Be=new $u;function P(e,t){return Be.parse(e,t)}P.options=P.setOptions=function(e){return Be.setOptions(e),P.defaults=Be.defaults,ja(P.defaults),P};P.getDefaults=Qs;P.defaults=Fe;P.use=function(...e){return Be.use(...e),P.defaults=Be.defaults,ja(P.defaults),P};P.walkTokens=function(e,t){return Be.walkTokens(e,t)};P.parseInline=Be.parseInline;P.Parser=ie;P.parser=ie.parse;P.Renderer=Xt;P.TextRenderer=ii;P.Lexer=se;P.lexer=se.lex;P.Tokenizer=Zt;P.Hooks=ht;P.parse=P;P.options;P.setOptions;P.use;P.walkTokens;P.parseInline;ie.parse;se.lex;P.setOptions({gfm:!0,breaks:!0,mangle:!1});const yo=["a","b","blockquote","br","code","del","em","h1","h2","h3","h4","hr","i","li","ol","p","pre","strong","table","tbody","td","th","thead","tr","ul"],wo=["class","href","rel","target","title","start"];let $o=!1;const ku=14e4,xu=4e4;function Au(){$o||($o=!0,vs.addHook("afterSanitizeAttributes",e=>{!(e instanceof HTMLAnchorElement)||!e.getAttribute("href")||(e.setAttribute("rel","noreferrer noopener"),e.setAttribute("target","_blank"))}))}function ws(e){const t=e.trim();if(!t)return"";Au();const n=na(t,ku),s=n.truncated?` - -… truncated (${n.total} chars, showing first ${n.text.length}).`:"";if(n.text.length>xu){const a=`
    ${Su(`${n.text}${s}`)}
    `;return vs.sanitize(a,{ALLOWED_TAGS:yo,ALLOWED_ATTR:wo})}const i=P.parse(`${n.text}${s}`);return vs.sanitize(i,{ALLOWED_TAGS:yo,ALLOWED_ATTR:wo})}function Su(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function _u(e,t){return d``}function Kt(e,t){e&&(e.textContent=t)}const Tu=1500,Eu=2e3,tr="Copy as markdown",Cu="Copied",Iu="Copy failed",Qn="📋",Lu="✓",Ru="!";async function Mu(e){if(!e)return!1;try{return await navigator.clipboard.writeText(e),!0}catch{return!1}}function Ht(e,t){e.title=t,e.setAttribute("aria-label",t)}function Pu(e){const t=e.label??tr;return d` - - `}function Nu(e){return Pu({text:()=>e,label:tr})}const Ou={emoji:"🧩",detailKeys:["command","path","url","targetUrl","targetId","ref","element","node","nodeId","id","requestId","to","channelId","guildId","userId","name","query","pattern","messageId"]},Du={bash:{emoji:"🛠️",title:"Bash",detailKeys:["command"]},process:{emoji:"🧰",title:"Process",detailKeys:["sessionId"]},read:{emoji:"📖",title:"Read",detailKeys:["path"]},write:{emoji:"✍️",title:"Write",detailKeys:["path"]},edit:{emoji:"📝",title:"Edit",detailKeys:["path"]},attach:{emoji:"📎",title:"Attach",detailKeys:["path","url","fileName"]},browser:{emoji:"🌐",title:"Browser",actions:{status:{label:"status"},start:{label:"start"},stop:{label:"stop"},tabs:{label:"tabs"},open:{label:"open",detailKeys:["targetUrl"]},focus:{label:"focus",detailKeys:["targetId"]},close:{label:"close",detailKeys:["targetId"]},snapshot:{label:"snapshot",detailKeys:["targetUrl","targetId","ref","element","format"]},screenshot:{label:"screenshot",detailKeys:["targetUrl","targetId","ref","element"]},navigate:{label:"navigate",detailKeys:["targetUrl","targetId"]},console:{label:"console",detailKeys:["level","targetId"]},pdf:{label:"pdf",detailKeys:["targetId"]},upload:{label:"upload",detailKeys:["paths","ref","inputRef","element","targetId"]},dialog:{label:"dialog",detailKeys:["accept","promptText","targetId"]},act:{label:"act",detailKeys:["request.kind","request.ref","request.selector","request.text","request.value"]}}},canvas:{emoji:"🖼️",title:"Canvas",actions:{present:{label:"present",detailKeys:["target","node","nodeId"]},hide:{label:"hide",detailKeys:["node","nodeId"]},navigate:{label:"navigate",detailKeys:["url","node","nodeId"]},eval:{label:"eval",detailKeys:["javaScript","node","nodeId"]},snapshot:{label:"snapshot",detailKeys:["format","node","nodeId"]},a2ui_push:{label:"A2UI push",detailKeys:["jsonlPath","node","nodeId"]},a2ui_reset:{label:"A2UI reset",detailKeys:["node","nodeId"]}}},nodes:{emoji:"📱",title:"Nodes",actions:{status:{label:"status"},describe:{label:"describe",detailKeys:["node","nodeId"]},pending:{label:"pending"},approve:{label:"approve",detailKeys:["requestId"]},reject:{label:"reject",detailKeys:["requestId"]},notify:{label:"notify",detailKeys:["node","nodeId","title","body"]},camera_snap:{label:"camera snap",detailKeys:["node","nodeId","facing","deviceId"]},camera_list:{label:"camera list",detailKeys:["node","nodeId"]},camera_clip:{label:"camera clip",detailKeys:["node","nodeId","facing","duration","durationMs"]},screen_record:{label:"screen record",detailKeys:["node","nodeId","duration","durationMs","fps","screenIndex"]}}},cron:{emoji:"⏰",title:"Cron",actions:{status:{label:"status"},list:{label:"list"},add:{label:"add",detailKeys:["job.name","job.id","job.schedule","job.cron"]},update:{label:"update",detailKeys:["id"]},remove:{label:"remove",detailKeys:["id"]},run:{label:"run",detailKeys:["id"]},runs:{label:"runs",detailKeys:["id"]},wake:{label:"wake",detailKeys:["text","mode"]}}},gateway:{emoji:"🔌",title:"Gateway",actions:{restart:{label:"restart",detailKeys:["reason","delayMs"]},"config.get":{label:"config get"},"config.schema":{label:"config schema"},"config.apply":{label:"config apply",detailKeys:["restartDelayMs"]},"update.run":{label:"update run",detailKeys:["restartDelayMs"]}}},whatsapp_login:{emoji:"🟢",title:"WhatsApp Login",actions:{start:{label:"start"},wait:{label:"wait"}}},discord:{emoji:"💬",title:"Discord",actions:{react:{label:"react",detailKeys:["channelId","messageId","emoji"]},reactions:{label:"reactions",detailKeys:["channelId","messageId"]},sticker:{label:"sticker",detailKeys:["to","stickerIds"]},poll:{label:"poll",detailKeys:["question","to"]},permissions:{label:"permissions",detailKeys:["channelId"]},readMessages:{label:"read messages",detailKeys:["channelId","limit"]},sendMessage:{label:"send",detailKeys:["to","content"]},editMessage:{label:"edit",detailKeys:["channelId","messageId"]},deleteMessage:{label:"delete",detailKeys:["channelId","messageId"]},threadCreate:{label:"thread create",detailKeys:["channelId","name"]},threadList:{label:"thread list",detailKeys:["guildId","channelId"]},threadReply:{label:"thread reply",detailKeys:["channelId","content"]},pinMessage:{label:"pin",detailKeys:["channelId","messageId"]},unpinMessage:{label:"unpin",detailKeys:["channelId","messageId"]},listPins:{label:"list pins",detailKeys:["channelId"]},searchMessages:{label:"search",detailKeys:["guildId","content"]},memberInfo:{label:"member",detailKeys:["guildId","userId"]},roleInfo:{label:"roles",detailKeys:["guildId"]},emojiList:{label:"emoji list",detailKeys:["guildId"]},roleAdd:{label:"role add",detailKeys:["guildId","userId","roleId"]},roleRemove:{label:"role remove",detailKeys:["guildId","userId","roleId"]},channelInfo:{label:"channel",detailKeys:["channelId"]},channelList:{label:"channels",detailKeys:["guildId"]},voiceStatus:{label:"voice",detailKeys:["guildId","userId"]},eventList:{label:"events",detailKeys:["guildId"]},eventCreate:{label:"event create",detailKeys:["guildId","name"]},timeout:{label:"timeout",detailKeys:["guildId","userId"]},kick:{label:"kick",detailKeys:["guildId","userId"]},ban:{label:"ban",detailKeys:["guildId","userId"]}}},slack:{emoji:"💬",title:"Slack",actions:{react:{label:"react",detailKeys:["channelId","messageId","emoji"]},reactions:{label:"reactions",detailKeys:["channelId","messageId"]},sendMessage:{label:"send",detailKeys:["to","content"]},editMessage:{label:"edit",detailKeys:["channelId","messageId"]},deleteMessage:{label:"delete",detailKeys:["channelId","messageId"]},readMessages:{label:"read messages",detailKeys:["channelId","limit"]},pinMessage:{label:"pin",detailKeys:["channelId","messageId"]},unpinMessage:{label:"unpin",detailKeys:["channelId","messageId"]},listPins:{label:"list pins",detailKeys:["channelId"]},memberInfo:{label:"member",detailKeys:["userId"]},emojiList:{label:"emoji list"}}}},Bu={fallback:Ou,tools:Du},nr=Bu,ko=nr.fallback??{emoji:"🧩"},Fu=nr.tools??{};function Uu(e){return(e??"tool").trim()}function Ku(e){const t=e.replace(/_/g," ").trim();return t?t.split(/\s+/).map(n=>n.length<=2&&n.toUpperCase()===n?n:`${n.at(0)?.toUpperCase()??""}${n.slice(1)}`).join(" "):"Tool"}function Hu(e){const t=e?.trim();if(t)return t.replace(/_/g," ")}function sr(e){if(e!=null){if(typeof e=="string"){const t=e.trim();if(!t)return;const n=t.split(/\r?\n/)[0]?.trim()??"";return n?n.length>160?`${n.slice(0,157)}…`:n:void 0}if(typeof e=="number"||typeof e=="boolean")return String(e);if(Array.isArray(e)){const t=e.map(s=>sr(s)).filter(s=>!!s);if(t.length===0)return;const n=t.slice(0,3).join(", ");return t.length>3?`${n}…`:n}}}function zu(e,t){if(!e||typeof e!="object")return;let n=e;for(const s of t.split(".")){if(!s||!n||typeof n!="object")return;n=n[s]}return n}function ju(e,t){for(const n of t){const s=zu(e,n),i=sr(s);if(i)return i}}function qu(e){if(!e||typeof e!="object")return;const t=e,n=typeof t.path=="string"?t.path:void 0;if(!n)return;const s=typeof t.offset=="number"?t.offset:void 0,i=typeof t.limit=="number"?t.limit:void 0;return s!==void 0&&i!==void 0?`${n}:${s}-${s+i}`:n}function Wu(e){if(!e||typeof e!="object")return;const t=e;return typeof t.path=="string"?t.path:void 0}function Vu(e,t){if(!(!e||!t))return e.actions?.[t]??void 0}function Gu(e){const t=Uu(e.name),n=t.toLowerCase(),s=Fu[n],i=s?.emoji??ko.emoji??"🧩",o=s?.title??Ku(t),a=s?.label??t,c=e.args&&typeof e.args=="object"?e.args.action:void 0,r=typeof c=="string"?c.trim():void 0,p=Vu(s,r),l=Hu(p?.label??r);let u;n==="read"&&(u=qu(e.args)),!u&&(n==="write"||n==="edit"||n==="attach")&&(u=Wu(e.args));const h=p?.detailKeys??s?.detailKeys??ko.detailKeys??[];return!u&&h.length>0&&(u=ju(e.args,h)),!u&&e.meta&&(u=e.meta),u&&(u=Qu(u)),{name:t,emoji:i,title:o,label:a,verb:l,detail:u}}function Yu(e){const t=[];if(e.verb&&t.push(e.verb),e.detail&&t.push(e.detail),t.length!==0)return t.join(" · ")}function Qu(e){return e&&e.replace(/\/Users\/[^/]+/g,"~").replace(/\/home\/[^/]+/g,"~")}const Ju=80,Zu=2,xo=100;function Xu(e){const t=e.trim();if(t.startsWith("{")||t.startsWith("["))try{const n=JSON.parse(t);return"```json\n"+JSON.stringify(n,null,2)+"\n```"}catch{}return e}function ep(e){const t=e.split(` -`),n=t.slice(0,Zu),s=n.join(` -`);return s.length>xo?s.slice(0,xo)+"…":n.lengthi.kind==="result")){const i=typeof t.toolName=="string"&&t.toolName||typeof t.tool_name=="string"&&t.tool_name||"tool",o=an(e)??void 0;s.push({kind:"result",name:i,text:o})}return s}function Ao(e,t){const n=Gu({name:e.name,args:e.args}),s=Yu(n),i=!!e.text?.trim(),o=!!t,a=o?()=>{if(i){t(Xu(e.text));return}const u=`## ${n.label} - -${s?`**Command:** \`${s}\` - -`:""}*No output — tool completed successfully.*`;t(u)}:void 0,c=i&&(e.text?.length??0)<=Ju,r=i&&!c,p=i&&c,l=!i;return d` -
    {u.key!=="Enter"&&u.key!==" "||(u.preventDefault(),a?.())}:g} - > -
    -
    - ${n.emoji} - ${n.label} -
    - ${o?d`${i?"View ›":"›"}`:g} - ${l&&!o?d``:g} -
    - ${s?d`
    ${s}
    `:g} - ${l?d`
    Completed
    `:g} - ${r?d`
    ${ep(e.text)}
    `:g} - ${p?d`
    ${e.text}
    `:g} -
    - `}function np(e){return Array.isArray(e)?e.filter(Boolean):[]}function sp(e){if(typeof e!="string")return e;const t=e.trim();if(!t||!t.startsWith("{")&&!t.startsWith("["))return e;try{return JSON.parse(t)}catch{return e}}function ip(e){if(typeof e.text=="string")return e.text;if(typeof e.content=="string")return e.content}function op(e){return d` -
    - ${oi("assistant",e)} -
    - -
    -
    - `}function ap(e,t,n,s){const i=new Date(t).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"}),o=s?.name??"Assistant";return d` -
    - ${oi("assistant",s)} -
    - ${ir({role:"assistant",content:[{type:"text",text:e}]},{isStreaming:!0,showReasoning:!1},n)} - -
    -
    - `}function rp(e,t){const n=Ys(e.role),s=t.assistantName??"Assistant",i=n==="user"?"You":n==="assistant"?s:n,o=n==="user"?"user":n==="assistant"?"assistant":"other",a=new Date(e.timestamp).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"});return d` -
    - ${oi(e.role,{name:s,avatar:t.assistantAvatar??null})} -
    - ${e.messages.map((c,r)=>ir(c.message,{isStreaming:e.isStreaming&&r===e.messages.length-1,showReasoning:t.showReasoning},t.onOpenSidebar))} - -
    -
    - `}function oi(e,t){const n=Ys(e),s=t?.name?.trim()||"Assistant",i=t?.avatar?.trim()||"",o=n==="user"?"U":n==="assistant"?s.charAt(0).toUpperCase()||"A":n==="tool"?"⚙":"?",a=n==="user"?"user":n==="assistant"?"assistant":n==="tool"?"tool":"other";return i&&n==="assistant"?lp(i)?d`${s}`:d`
    ${i}
    `:d`
    ${o}
    `}function lp(e){return/^https?:\/\//i.test(e)||/^data:image\//i.test(e)}function ir(e,t,n){const s=e,i=typeof s.role=="string"?s.role:"unknown",o=Fa(e)||i.toLowerCase()==="toolresult"||i.toLowerCase()==="tool_result"||typeof s.toolCallId=="string"||typeof s.tool_call_id=="string",a=tp(e),c=a.length>0,r=an(e),p=t.showReasoning&&i==="assistant"?vl(e):null,l=r?.trim()?r:null,u=p?bl(p):null,h=l,v=i==="assistant"&&!!h?.trim(),w=["chat-bubble",v?"has-copy":"",t.isStreaming?"streaming":"","fade-in"].filter(Boolean).join(" ");return!h&&c&&o?d`${a.map($=>Ao($,n))}`:!h&&!c?g:d` -
    - ${v?Nu(h):g} - ${u?d`
    ${ps(ws(u))}
    `:g} - ${h?d`
    ${ps(ws(h))}
    `:g} - ${a.map($=>Ao($,n))} -
    - `}function cp(e){return d` - - `}var dp=Object.defineProperty,up=Object.getOwnPropertyDescriptor,vn=(e,t,n,s)=>{for(var i=s>1?void 0:s?up(t,n):t,o=e.length-1,a;o>=0;o--)(a=e[o])&&(i=(s?a(t,n,i):a(i))||i);return s&&i&&dp(t,n,i),i};let et=class extends Ye{constructor(){super(...arguments),this.splitRatio=.6,this.minRatio=.4,this.maxRatio=.7,this.isDragging=!1,this.startX=0,this.startRatio=0,this.handleMouseDown=e=>{this.isDragging=!0,this.startX=e.clientX,this.startRatio=this.splitRatio,this.classList.add("dragging"),document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("mouseup",this.handleMouseUp),e.preventDefault()},this.handleMouseMove=e=>{if(!this.isDragging)return;const t=this.parentElement;if(!t)return;const n=t.getBoundingClientRect().width,i=(e.clientX-this.startX)/n;let o=this.startRatio+i;o=Math.max(this.minRatio,Math.min(this.maxRatio,o)),this.dispatchEvent(new CustomEvent("resize",{detail:{splitRatio:o},bubbles:!0,composed:!0}))},this.handleMouseUp=()=>{this.isDragging=!1,this.classList.remove("dragging"),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp)}}render(){return d``}connectedCallback(){super.connectedCallback(),this.addEventListener("mousedown",this.handleMouseDown)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("mousedown",this.handleMouseDown),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp)}};et.styles=Rr` - :host { - width: 4px; - cursor: col-resize; - background: var(--border, #333); - transition: background 150ms ease-out; - flex-shrink: 0; - position: relative; - } - - :host::before { - content: ""; - position: absolute; - top: 0; - left: -4px; - right: -4px; - bottom: 0; - } - - :host(:hover) { - background: var(--accent, #007bff); - } - - :host(.dragging) { - background: var(--accent, #007bff); - } - `;vn([sn({type:Number})],et.prototype,"splitRatio",2);vn([sn({type:Number})],et.prototype,"minRatio",2);vn([sn({type:Number})],et.prototype,"maxRatio",2);et=vn([Yo("resizable-divider")],et);function pp(e){const t=e.connected,n=e.sending||e.stream!==null,i=e.sessions?.sessions?.find(l=>l.key===e.sessionKey)?.reasoningLevel??"off",o=e.showThinking&&i!=="off",a={name:e.assistantName,avatar:e.assistantAvatar??e.assistantAvatarUrl??null},c=e.connected?"Message (↩ to send, Shift+↩ for line breaks)":"Connect to the gateway to start chatting…",r=e.splitRatio??.6,p=!!(e.sidebarOpen&&e.onCloseSidebar);return d` -
    - ${e.disabledReason?d`
    ${e.disabledReason}
    `:g} - - ${e.error?d`
    ${e.error}
    `:g} - - ${e.focusMode?d` - - `:g} - -
    -
    -
    - ${e.loading?d`
    Loading chat…
    `:g} - ${Da(hp(e),l=>l.key,l=>l.kind==="reading-indicator"?op(a):l.kind==="stream"?ap(l.text,l.startedAt,e.onOpenSidebar,a):l.kind==="group"?rp(l,{onOpenSidebar:e.onOpenSidebar,showReasoning:o,assistantName:e.assistantName,assistantAvatar:a.avatar}):g)} -
    -
    - - ${p?d` - e.onSplitRatioChange?.(l.detail.splitRatio)} - > -
    - ${cp({content:e.sidebarContent??null,error:e.sidebarError??null,onClose:e.onCloseSidebar,onViewRawText:()=>{!e.sidebarContent||!e.onOpenSidebar||e.onOpenSidebar(`\`\`\` -${e.sidebarContent} -\`\`\``)}})} -
    - `:g} -
    - - ${e.queue.length?d` -
    -
    Queued (${e.queue.length})
    -
    - ${e.queue.map(l=>d` -
    -
    ${l.text}
    - -
    - `)} -
    -
    - `:g} - -
    - -
    - - -
    -
    -
    - `}const So=200;function fp(e){const t=[];let n=null;for(const s of e){if(s.kind!=="message"){n&&(t.push(n),n=null),t.push(s);continue}const i=Ba(s.message),o=Ys(i.role),a=i.timestamp||Date.now();!n||n.role!==o?(n&&t.push(n),n={kind:"group",key:`group:${o}:${s.key}`,role:o,messages:[{message:s.message,key:s.key}],timestamp:a,isStreaming:!1}):n.messages.push({message:s.message,key:s.key})}return n&&t.push(n),t}function hp(e){const t=[],n=Array.isArray(e.messages)?e.messages:[],s=Array.isArray(e.toolMessages)?e.toolMessages:[],i=Math.max(0,n.length-So);i>0&&t.push({kind:"message",key:"chat:history:notice",message:{role:"system",content:`Showing last ${So} messages (${i} hidden).`,timestamp:Date.now()}});for(let o=i;o0?t.push({kind:"stream",key:o,text:e.stream,startedAt:e.streamStartedAt??Date.now()}):t.push({kind:"reading-indicator",key:o})}return fp(t)}function _o(e,t){const n=e,s=typeof n.toolCallId=="string"?n.toolCallId:"";if(s)return`tool:${s}`;const i=typeof n.id=="string"?n.id:"";if(i)return`msg:${i}`;const o=typeof n.messageId=="string"?n.messageId:"";if(o)return`msg:${o}`;const a=typeof n.timestamp=="number"?n.timestamp:null,c=typeof n.role=="string"?n.role:"unknown",p=an(e)??(typeof n.content=="string"?n.content:null)??gp(e)??String(t),l=vp(p);return a?`msg:${c}:${a}:${l}`:`msg:${c}:${l}`}function gp(e){try{return JSON.stringify(e)}catch{return null}}function vp(e){let t=2166136261;for(let n=0;n>>0).toString(36)}function de(e){if(e)return Array.isArray(e.type)?e.type.filter(n=>n!=="null")[0]??e.type[0]:e.type}function or(e){if(!e)return"";if(e.default!==void 0)return e.default;switch(de(e)){case"object":return{};case"array":return[];case"boolean":return!1;case"number":case"integer":return 0;case"string":return"";default:return""}}function mn(e){return e.filter(t=>typeof t=="string").join(".")}function ee(e,t){const n=mn(e),s=t[n];if(s)return s;const i=n.split(".");for(const[o,a]of Object.entries(t)){if(!o.includes("*"))continue;const c=o.split(".");if(c.length!==i.length)continue;let r=!0;for(let p=0;pt.toUpperCase())}function mp(e){const t=mn(e).toLowerCase();return t.includes("token")||t.includes("password")||t.includes("secret")||t.includes("apikey")||t.endsWith("key")}const bp=new Set(["title","description","default","nullable"]);function yp(e){return Object.keys(e??{}).filter(n=>!bp.has(n)).length===0}function wp(e){if(e===void 0)return"";try{return JSON.stringify(e,null,2)??""}catch{return""}}const At={chevronDown:d``,plus:d``,minus:d``,trash:d``,edit:d``};function be(e){const{schema:t,value:n,path:s,hints:i,unsupported:o,disabled:a,onPatch:c}=e,r=e.showLabel??!0,p=de(t),l=ee(s,i),u=l?.label??t.title??ye(String(s.at(-1))),h=l?.help??t.description,v=mn(s);if(o.has(v))return d`
    -
    ${u}
    -
    Unsupported schema node. Use Raw mode.
    -
    `;if(t.anyOf||t.oneOf){const $=(t.anyOf??t.oneOf??[]).filter(A=>!(A.type==="null"||Array.isArray(A.type)&&A.type.includes("null")));if($.length===1)return be({...e,schema:$[0]});const x=A=>{if(A.const!==void 0)return A.const;if(A.enum&&A.enum.length===1)return A.enum[0]},E=$.map(x),I=E.every(A=>A!==void 0);if(I&&E.length>0&&E.length<=5){const A=n??t.default;return d` -
    - ${r?d``:g} - ${h?d`
    ${h}
    `:g} -
    - ${E.map((B,ue)=>d` - - `)} -
    -
    - `}if(I&&E.length>5)return Eo({...e,options:E,value:n??t.default});const R=new Set($.map(A=>de(A)).filter(Boolean)),C=new Set([...R].map(A=>A==="integer"?"number":A));if([...C].every(A=>["string","number","boolean"].includes(A))){const A=C.has("string"),B=C.has("number");if(C.has("boolean")&&C.size===1)return be({...e,schema:{...t,type:"boolean",anyOf:void 0,oneOf:void 0}});if(A||B)return To({...e,inputType:B&&!A?"number":"text"})}}if(t.enum){const w=t.enum;if(w.length<=5){const $=n??t.default;return d` -
    - ${r?d``:g} - ${h?d`
    ${h}
    `:g} -
    - ${w.map(x=>d` - - `)} -
    -
    - `}return Eo({...e,options:w,value:n??t.default})}if(p==="object")return kp(e);if(p==="array")return xp(e);if(p==="boolean"){const w=typeof n=="boolean"?n:typeof t.default=="boolean"?t.default:!1;return d` - - `}return p==="number"||p==="integer"?$p(e):p==="string"?To({...e,inputType:"text"}):d` -
    -
    ${u}
    -
    Unsupported type: ${p}. Use Raw mode.
    -
    - `}function To(e){const{schema:t,value:n,path:s,hints:i,disabled:o,onPatch:a,inputType:c}=e,r=e.showLabel??!0,p=ee(s,i),l=p?.label??t.title??ye(String(s.at(-1))),u=p?.help??t.description,h=p?.sensitive??mp(s),v=p?.placeholder??(h?"••••":t.default!==void 0?`Default: ${t.default}`:""),w=n??"";return d` -
    - ${r?d``:g} - ${u?d`
    ${u}
    `:g} -
    - {const x=$.target.value;if(c==="number"){if(x.trim()===""){a(s,void 0);return}const E=Number(x);a(s,Number.isNaN(E)?x:E);return}a(s,x)}} - /> - ${t.default!==void 0?d` - - `:g} -
    -
    - `}function $p(e){const{schema:t,value:n,path:s,hints:i,disabled:o,onPatch:a}=e,c=e.showLabel??!0,r=ee(s,i),p=r?.label??t.title??ye(String(s.at(-1))),l=r?.help??t.description,u=n??t.default??"",h=typeof u=="number"?u:0;return d` -
    - ${c?d``:g} - ${l?d`
    ${l}
    `:g} -
    - - {const w=v.target.value,$=w===""?void 0:Number(w);a(s,$)}} - /> - -
    -
    - `}function Eo(e){const{schema:t,value:n,path:s,hints:i,disabled:o,options:a,onPatch:c}=e,r=e.showLabel??!0,p=ee(s,i),l=p?.label??t.title??ye(String(s.at(-1))),u=p?.help??t.description,h=n??t.default,v=a.findIndex($=>$===h||String($)===String(h)),w="__unset__";return d` -
    - ${r?d``:g} - ${u?d`
    ${u}
    `:g} - -
    - `}function kp(e){const{schema:t,value:n,path:s,hints:i,unsupported:o,disabled:a,onPatch:c}=e;e.showLabel;const r=ee(s,i),p=r?.label??t.title??ye(String(s.at(-1))),l=r?.help??t.description,u=n??t.default,h=u&&typeof u=="object"&&!Array.isArray(u)?u:{},v=t.properties??{},$=Object.entries(v).sort((R,C)=>{const A=ee([...s,R[0]],i)?.order??0,B=ee([...s,C[0]],i)?.order??0;return A!==B?A-B:R[0].localeCompare(C[0])}),x=new Set(Object.keys(v)),E=t.additionalProperties,I=!!E&&typeof E=="object";return s.length===1?d` -
    - ${$.map(([R,C])=>be({schema:C,value:h[R],path:[...s,R],hints:i,unsupported:o,disabled:a,onPatch:c}))} - ${I?Co({schema:E,value:h,path:s,hints:i,unsupported:o,disabled:a,reservedKeys:x,onPatch:c}):g} -
    - `:d` -
    - - ${p} - ${At.chevronDown} - - ${l?d`
    ${l}
    `:g} -
    - ${$.map(([R,C])=>be({schema:C,value:h[R],path:[...s,R],hints:i,unsupported:o,disabled:a,onPatch:c}))} - ${I?Co({schema:E,value:h,path:s,hints:i,unsupported:o,disabled:a,reservedKeys:x,onPatch:c}):g} -
    -
    - `}function xp(e){const{schema:t,value:n,path:s,hints:i,unsupported:o,disabled:a,onPatch:c}=e,r=e.showLabel??!0,p=ee(s,i),l=p?.label??t.title??ye(String(s.at(-1))),u=p?.help??t.description,h=Array.isArray(t.items)?t.items[0]:t.items;if(!h)return d` -
    -
    ${l}
    -
    Unsupported array schema. Use Raw mode.
    -
    - `;const v=Array.isArray(n)?n:Array.isArray(t.default)?t.default:[];return d` -
    -
    - ${r?d`${l}`:g} - ${v.length} item${v.length!==1?"s":""} - -
    - ${u?d`
    ${u}
    `:g} - - ${v.length===0?d` -
    - No items yet. Click "Add" to create one. -
    - `:d` -
    - ${v.map((w,$)=>d` -
    -
    - #${$+1} - -
    -
    - ${be({schema:h,value:w,path:[...s,$],hints:i,unsupported:o,disabled:a,showLabel:!1,onPatch:c})} -
    -
    - `)} -
    - `} -
    - `}function Co(e){const{schema:t,value:n,path:s,hints:i,unsupported:o,disabled:a,reservedKeys:c,onPatch:r}=e,p=yp(t),l=Object.entries(n??{}).filter(([u])=>!c.has(u));return d` -
    -
    - Custom entries - -
    - - ${l.length===0?d` -
    No custom entries.
    - `:d` -
    - ${l.map(([u,h])=>{const v=[...s,u],w=wp(h);return d` -
    -
    - {const x=$.target.value.trim();if(!x||x===u)return;const E={...n??{}};x in E||(E[x]=E[u],delete E[u],r(s,E))}} - /> -
    -
    - ${p?d` - - `:be({schema:t,value:h,path:v,hints:i,unsupported:o,disabled:a,showLabel:!1,onPatch:r})} -
    - -
    - `})} -
    - `} -
    - `}const Io={env:d``,update:d``,agents:d``,auth:d``,channels:d``,messages:d``,commands:d``,hooks:d``,skills:d``,tools:d``,gateway:d``,wizard:d``,meta:d``,logging:d``,browser:d``,ui:d``,models:d``,bindings:d``,broadcast:d``,audio:d``,session:d``,cron:d``,web:d``,discovery:d``,canvasHost:d``,talk:d``,plugins:d``,default:d``},ai={env:{label:"Environment Variables",description:"Environment variables passed to the gateway process"},update:{label:"Updates",description:"Auto-update settings and release channel"},agents:{label:"Agents",description:"Agent configurations, models, and identities"},auth:{label:"Authentication",description:"API keys and authentication profiles"},channels:{label:"Channels",description:"Messaging channels (Telegram, Discord, Slack, etc.)"},messages:{label:"Messages",description:"Message handling and routing settings"},commands:{label:"Commands",description:"Custom slash commands"},hooks:{label:"Hooks",description:"Webhooks and event hooks"},skills:{label:"Skills",description:"Skill packs and capabilities"},tools:{label:"Tools",description:"Tool configurations (browser, search, etc.)"},gateway:{label:"Gateway",description:"Gateway server settings (port, auth, binding)"},wizard:{label:"Setup Wizard",description:"Setup wizard state and history"},meta:{label:"Metadata",description:"Gateway metadata and version information"},logging:{label:"Logging",description:"Log levels and output configuration"},browser:{label:"Browser",description:"Browser automation settings"},ui:{label:"UI",description:"User interface preferences"},models:{label:"Models",description:"AI model configurations and providers"},bindings:{label:"Bindings",description:"Key bindings and shortcuts"},broadcast:{label:"Broadcast",description:"Broadcast and notification settings"},audio:{label:"Audio",description:"Audio input/output settings"},session:{label:"Session",description:"Session management and persistence"},cron:{label:"Cron",description:"Scheduled tasks and automation"},web:{label:"Web",description:"Web server and API settings"},discovery:{label:"Discovery",description:"Service discovery and networking"},canvasHost:{label:"Canvas Host",description:"Canvas rendering and display"},talk:{label:"Talk",description:"Voice and speech settings"},plugins:{label:"Plugins",description:"Plugin management and extensions"}};function Lo(e){return Io[e]??Io.default}function Ap(e,t,n){if(!n)return!0;const s=n.toLowerCase(),i=ai[e];return e.toLowerCase().includes(s)||i&&(i.label.toLowerCase().includes(s)||i.description.toLowerCase().includes(s))?!0:gt(t,s)}function gt(e,t){if(e.title?.toLowerCase().includes(t)||e.description?.toLowerCase().includes(t)||e.enum?.some(s=>String(s).toLowerCase().includes(t)))return!0;if(e.properties){for(const[s,i]of Object.entries(e.properties))if(s.toLowerCase().includes(t)||gt(i,t))return!0}if(e.items){const s=Array.isArray(e.items)?e.items:[e.items];for(const i of s)if(i&>(i,t))return!0}if(e.additionalProperties&&typeof e.additionalProperties=="object"&>(e.additionalProperties,t))return!0;const n=e.anyOf??e.oneOf??e.allOf;if(n){for(const s of n)if(s&>(s,t))return!0}return!1}function Sp(e){if(!e.schema)return d`
    Schema unavailable.
    `;const t=e.schema,n=e.value??{};if(de(t)!=="object"||!t.properties)return d`
    Unsupported schema. Use Raw.
    `;const s=new Set(e.unsupportedPaths??[]),i=t.properties,o=e.searchQuery??"",a=e.activeSection,c=e.activeSubsection??null;let r=Object.entries(i);a&&(r=r.filter(([l])=>l===a)),o&&(r=r.filter(([l,u])=>Ap(l,u,o))),r.sort((l,u)=>{const h=ee([l[0]],e.uiHints)?.order??50,v=ee([u[0]],e.uiHints)?.order??50;return h!==v?h-v:l[0].localeCompare(u[0])});let p=null;if(a&&c&&r.length===1){const l=r[0]?.[1];l&&de(l)==="object"&&l.properties&&l.properties[c]&&(p={sectionKey:a,subsectionKey:c,schema:l.properties[c]})}return r.length===0?d` -
    -
    🔍
    -
    - ${o?`No settings match "${o}"`:"No settings in this section"} -
    -
    - `:d` -
    - ${p?(()=>{const{sectionKey:l,subsectionKey:u,schema:h}=p,v=ee([l,u],e.uiHints),w=v?.label??h.title??ye(u),$=v?.help??h.description??"",x=n[l],E=x&&typeof x=="object"?x[u]:void 0,I=`config-section-${l}-${u}`;return d` -
    -
    - ${Lo(l)} -
    -

    ${w}

    - ${$?d`

    ${$}

    `:g} -
    -
    -
    - ${be({schema:h,value:E,path:[l,u],hints:e.uiHints,unsupported:s,disabled:e.disabled??!1,showLabel:!1,onPatch:e.onPatch})} -
    -
    - `})():r.map(([l,u])=>{const h=ai[l]??{label:l.charAt(0).toUpperCase()+l.slice(1),description:u.description??""};return d` -
    -
    - ${Lo(l)} -
    -

    ${h.label}

    - ${h.description?d`

    ${h.description}

    `:g} -
    -
    -
    - ${be({schema:u,value:n[l],path:[l],hints:e.uiHints,unsupported:s,disabled:e.disabled??!1,showLabel:!1,onPatch:e.onPatch})} -
    -
    - `})} -
    - `}const _p=new Set(["title","description","default","nullable"]);function Tp(e){return Object.keys(e??{}).filter(n=>!_p.has(n)).length===0}function ar(e){const t=e.filter(i=>i!=null),n=t.length!==e.length,s=[];for(const i of t)s.some(o=>Object.is(o,i))||s.push(i);return{enumValues:s,nullable:n}}function rr(e){return!e||typeof e!="object"?{schema:null,unsupportedPaths:[""]}:bt(e,[])}function bt(e,t){const n=new Set,s={...e},i=mn(t)||"";if(e.anyOf||e.oneOf||e.allOf){const c=Ep(e,t);return c||{schema:e,unsupportedPaths:[i]}}const o=Array.isArray(e.type)&&e.type.includes("null"),a=de(e)??(e.properties||e.additionalProperties?"object":void 0);if(s.type=a??e.type,s.nullable=o||e.nullable,s.enum){const{enumValues:c,nullable:r}=ar(s.enum);s.enum=c,r&&(s.nullable=!0),c.length===0&&n.add(i)}if(a==="object"){const c=e.properties??{},r={};for(const[p,l]of Object.entries(c)){const u=bt(l,[...t,p]);u.schema&&(r[p]=u.schema);for(const h of u.unsupportedPaths)n.add(h)}if(s.properties=r,e.additionalProperties===!0)n.add(i);else if(e.additionalProperties===!1)s.additionalProperties=!1;else if(e.additionalProperties&&typeof e.additionalProperties=="object"&&!Tp(e.additionalProperties)){const p=bt(e.additionalProperties,[...t,"*"]);s.additionalProperties=p.schema??e.additionalProperties,p.unsupportedPaths.length>0&&n.add(i)}}else if(a==="array"){const c=Array.isArray(e.items)?e.items[0]:e.items;if(!c)n.add(i);else{const r=bt(c,[...t,"*"]);s.items=r.schema??c,r.unsupportedPaths.length>0&&n.add(i)}}else a!=="string"&&a!=="number"&&a!=="integer"&&a!=="boolean"&&!s.enum&&n.add(i);return{schema:s,unsupportedPaths:Array.from(n)}}function Ep(e,t){if(e.allOf)return null;const n=e.anyOf??e.oneOf;if(!n)return null;const s=[],i=[];let o=!1;for(const c of n){if(!c||typeof c!="object")return null;if(Array.isArray(c.enum)){const{enumValues:r,nullable:p}=ar(c.enum);s.push(...r),p&&(o=!0);continue}if("const"in c){if(c.const==null){o=!0;continue}s.push(c.const);continue}if(de(c)==="null"){o=!0;continue}i.push(c)}if(s.length>0&&i.length===0){const c=[];for(const r of s)c.some(p=>Object.is(p,r))||c.push(r);return{schema:{...e,enum:c,nullable:o,anyOf:void 0,oneOf:void 0,allOf:void 0},unsupportedPaths:[]}}if(i.length===1){const c=bt(i[0],t);return c.schema&&(c.schema.nullable=o||c.schema.nullable),c}const a=["string","number","integer","boolean"];return i.length>0&&s.length===0&&i.every(c=>c.type&&a.includes(String(c.type)))?{schema:{...e,nullable:o},unsupportedPaths:[]}:null}const $s={all:d``,env:d``,update:d``,agents:d``,auth:d``,channels:d``,messages:d``,commands:d``,hooks:d``,skills:d``,tools:d``,gateway:d``,wizard:d``,meta:d``,logging:d``,browser:d``,ui:d``,models:d``,bindings:d``,broadcast:d``,audio:d``,session:d``,cron:d``,web:d``,discovery:d``,canvasHost:d``,talk:d``,plugins:d``,default:d``},Ro=[{key:"env",label:"Environment"},{key:"update",label:"Updates"},{key:"agents",label:"Agents"},{key:"auth",label:"Authentication"},{key:"channels",label:"Channels"},{key:"messages",label:"Messages"},{key:"commands",label:"Commands"},{key:"hooks",label:"Hooks"},{key:"skills",label:"Skills"},{key:"tools",label:"Tools"},{key:"gateway",label:"Gateway"},{key:"wizard",label:"Setup Wizard"}],Mo="__all__";function Po(e){return $s[e]??$s.default}function Cp(e,t){const n=ai[e];return n||{label:t?.title??ye(e),description:t?.description??""}}function Ip(e){const{key:t,schema:n,uiHints:s}=e;if(!n||de(n)!=="object"||!n.properties)return[];const i=Object.entries(n.properties).map(([o,a])=>{const c=ee([t,o],s),r=c?.label??a.title??ye(o),p=c?.help??a.description??"",l=c?.order??50;return{key:o,label:r,description:p,order:l}});return i.sort((o,a)=>o.order!==a.order?o.order-a.order:o.key.localeCompare(a.key)),i}function Lp(e,t){if(!e||!t)return[];const n=[];function s(i,o,a){if(i===o)return;if(typeof i!=typeof o){n.push({path:a,from:i,to:o});return}if(typeof i!="object"||i===null||o===null){i!==o&&n.push({path:a,from:i,to:o});return}if(Array.isArray(i)&&Array.isArray(o)){JSON.stringify(i)!==JSON.stringify(o)&&n.push({path:a,from:i,to:o});return}const c=i,r=o,p=new Set([...Object.keys(c),...Object.keys(r)]);for(const l of p)s(c[l],r[l],a?`${a}.${l}`:l)}return s(e,t,""),n}function No(e,t=40){let n;try{n=JSON.stringify(e)??String(e)}catch{n=String(e)}return n.length<=t?n:n.slice(0,t-3)+"..."}function Rp(e){const t=e.valid==null?"unknown":e.valid?"valid":"invalid",n=rr(e.schema),s=n.schema?n.unsupportedPaths.length>0:!1,i=!!e.formValue&&!e.loading&&!s,o=e.connected&&!e.saving&&(e.formMode==="raw"?!0:i),a=e.connected&&!e.applying&&!e.updating&&(e.formMode==="raw"?!0:i),c=e.connected&&!e.applying&&!e.updating,r=n.schema?.properties??{},p=Ro.filter(A=>A.key in r),l=new Set(Ro.map(A=>A.key)),u=Object.keys(r).filter(A=>!l.has(A)).map(A=>({key:A,label:A.charAt(0).toUpperCase()+A.slice(1)})),h=[...p,...u],v=e.activeSection&&n.schema&&de(n.schema)==="object"?n.schema.properties?.[e.activeSection]:void 0,w=e.activeSection?Cp(e.activeSection,v):null,$=e.activeSection?Ip({key:e.activeSection,schema:v,uiHints:e.uiHints}):[],x=e.formMode==="form"&&!!e.activeSection&&$.length>0,E=e.activeSubsection===Mo,I=e.searchQuery||E?null:e.activeSubsection??$[0]?.key??null,R=e.formMode==="form"?Lp(e.originalValue,e.formValue):[],C=R.length>0;return d` -
    - - - - -
    - -
    -
    - ${C?d` - ${R.length} unsaved change${R.length!==1?"s":""} - `:d` - No changes - `} -
    -
    - - - - -
    -
    - - - ${C?d` -
    - - View ${R.length} pending change${R.length!==1?"s":""} - - - - -
    - ${R.map(A=>d` -
    -
    ${A.path}
    -
    - ${No(A.from)} - - ${No(A.to)} -
    -
    - `)} -
    -
    - `:g} - - ${w&&e.formMode==="form"?d` -
    -
    ${Po(e.activeSection??"")}
    -
    -
    ${w.label}
    - ${w.description?d`
    ${w.description}
    `:g} -
    -
    - `:g} - - ${x?d` -
    - - ${$.map(A=>d` - - `)} -
    - `:g} - - -
    - ${e.formMode==="form"?d` - ${e.schemaLoading?d`
    -
    - Loading schema… -
    `:Sp({schema:n.schema,uiHints:e.uiHints,value:e.formValue,disabled:e.loading||!e.formValue,unsupportedPaths:n.unsupportedPaths,onPatch:e.onFormPatch,searchQuery:e.searchQuery,activeSection:e.activeSection,activeSubsection:I})} - ${s?d`
    - Form view can't safely edit some fields. - Use Raw to avoid losing config entries. -
    `:g} - `:d` - - `} -
    - - ${e.issues.length>0?d`
    -
    ${JSON.stringify(e.issues,null,2)}
    -
    `:g} -
    -
    - `}function Mp(e){if(!e&&e!==0)return"n/a";const t=Math.round(e/1e3);if(t<60)return`${t}s`;const n=Math.round(t/60);return n<60?`${n}m`:`${Math.round(n/60)}h`}function Pp(e,t){const n=t.snapshot,s=n?.channels;if(!n||!s)return!1;const i=s[e],o=typeof i?.configured=="boolean"&&i.configured,a=typeof i?.running=="boolean"&&i.running,c=typeof i?.connected=="boolean"&&i.connected,p=(n.channelAccounts?.[e]??[]).some(l=>l.configured||l.running||l.connected);return o||a||c||p}function Np(e,t){return t?.[e]?.length??0}function lr(e,t){const n=Np(e,t);return n<2?g:d``}function Op(e,t){let n=e;for(const s of t){if(!n)return null;const i=de(n);if(i==="object"){const o=n.properties??{};if(typeof s=="string"&&o[s]){n=o[s];continue}const a=n.additionalProperties;if(typeof s=="string"&&a&&typeof a=="object"){n=a;continue}return null}if(i==="array"){if(typeof s!="number")return null;n=(Array.isArray(n.items)?n.items[0]:n.items)??null;continue}return null}return n}function Dp(e,t){const s=(e.channels??{})[t],i=e[t];return(s&&typeof s=="object"?s:null)??(i&&typeof i=="object"?i:null)??{}}function Bp(e){const t=rr(e.schema),n=t.schema;if(!n)return d`
    Schema unavailable. Use Raw.
    `;const s=Op(n,["channels",e.channelId]);if(!s)return d`
    Channel config schema unavailable.
    `;const i=e.configValue??{},o=Dp(i,e.channelId);return d` -
    - ${be({schema:s,value:o,path:["channels",e.channelId],hints:e.uiHints,unsupported:new Set(t.unsupportedPaths),disabled:e.disabled,showLabel:!1,onPatch:e.onPatch})} -
    - `}function _e(e){const{channelId:t,props:n}=e,s=n.configSaving||n.configSchemaLoading;return d` -
    - ${n.configSchemaLoading?d`
    Loading config schema…
    `:Bp({channelId:t,configValue:n.configForm,schema:n.configSchema,uiHints:n.configUiHints,disabled:s,onPatch:n.onConfigPatch})} -
    - - -
    -
    - `}function Fp(e){const{props:t,discord:n,accountCountLabel:s}=e;return d` -
    -
    Discord
    -
    Bot status and channel configuration.
    - ${s} - -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Last start - ${n?.lastStartAt?O(n.lastStartAt):"n/a"} -
    -
    - Last probe - ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} -
    -
    - - ${n?.lastError?d`
    - ${n.lastError} -
    `:g} - - ${n?.probe?d`
    - Probe ${n.probe.ok?"ok":"failed"} · - ${n.probe.status??""} ${n.probe.error??""} -
    `:g} - - ${_e({channelId:"discord",props:t})} - -
    - -
    -
    - `}function Up(e){const{props:t,imessage:n,accountCountLabel:s}=e;return d` -
    -
    iMessage
    -
    macOS bridge status and channel configuration.
    - ${s} - -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Last start - ${n?.lastStartAt?O(n.lastStartAt):"n/a"} -
    -
    - Last probe - ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} -
    -
    - - ${n?.lastError?d`
    - ${n.lastError} -
    `:g} - - ${n?.probe?d`
    - Probe ${n.probe.ok?"ok":"failed"} · - ${n.probe.error??""} -
    `:g} - - ${_e({channelId:"imessage",props:t})} - -
    - -
    -
    - `}function Kp(e){const{values:t,original:n}=e;return t.name!==n.name||t.displayName!==n.displayName||t.about!==n.about||t.picture!==n.picture||t.banner!==n.banner||t.website!==n.website||t.nip05!==n.nip05||t.lud16!==n.lud16}function Hp(e){const{state:t,callbacks:n,accountId:s}=e,i=Kp(t),o=(c,r,p={})=>{const{type:l="text",placeholder:u,maxLength:h,help:v}=p,w=t.values[c]??"",$=t.fieldErrors[c],x=`nostr-profile-${c}`;return l==="textarea"?d` -
    - - - ${v?d`
    ${v}
    `:g} - ${$?d`
    ${$}
    `:g} -
    - `:d` -
    - - {const I=E.target;n.onFieldChange(c,I.value)}} - ?disabled=${t.saving} - /> - ${v?d`
    ${v}
    `:g} - ${$?d`
    ${$}
    `:g} -
    - `},a=()=>{const c=t.values.picture;return c?d` -
    - Profile picture preview{const p=r.target;p.style.display="none"}} - @load=${r=>{const p=r.target;p.style.display="block"}} - /> -
    - `:g};return d` -
    -
    -
    Edit Profile
    -
    Account: ${s}
    -
    - - ${t.error?d`
    ${t.error}
    `:g} - - ${t.success?d`
    ${t.success}
    `:g} - - ${a()} - - ${o("name","Username",{placeholder:"satoshi",maxLength:256,help:"Short username (e.g., satoshi)"})} - - ${o("displayName","Display Name",{placeholder:"Satoshi Nakamoto",maxLength:256,help:"Your full display name"})} - - ${o("about","Bio",{type:"textarea",placeholder:"Tell people about yourself...",maxLength:2e3,help:"A brief bio or description"})} - - ${o("picture","Avatar URL",{type:"url",placeholder:"https://example.com/avatar.jpg",help:"HTTPS URL to your profile picture"})} - - ${t.showAdvanced?d` -
    -
    Advanced
    - - ${o("banner","Banner URL",{type:"url",placeholder:"https://example.com/banner.jpg",help:"HTTPS URL to a banner image"})} - - ${o("website","Website",{type:"url",placeholder:"https://example.com",help:"Your personal website"})} - - ${o("nip05","NIP-05 Identifier",{placeholder:"you@example.com",help:"Verifiable identifier (e.g., you@domain.com)"})} - - ${o("lud16","Lightning Address",{placeholder:"you@getalby.com",help:"Lightning address for tips (LUD-16)"})} -
    - `:g} - -
    - - - - - - - -
    - - ${i?d`
    - You have unsaved changes -
    `:g} -
    - `}function zp(e){const t={name:e?.name??"",displayName:e?.displayName??"",about:e?.about??"",picture:e?.picture??"",banner:e?.banner??"",website:e?.website??"",nip05:e?.nip05??"",lud16:e?.lud16??""};return{values:t,original:{...t},saving:!1,importing:!1,error:null,success:null,fieldErrors:{},showAdvanced:!!(e?.banner||e?.website||e?.nip05||e?.lud16)}}function Oo(e){return e?e.length<=20?e:`${e.slice(0,8)}...${e.slice(-8)}`:"n/a"}function jp(e){const{props:t,nostr:n,nostrAccounts:s,accountCountLabel:i,profileFormState:o,profileFormCallbacks:a,onEditProfile:c}=e,r=s[0],p=n?.configured??r?.configured??!1,l=n?.running??r?.running??!1,u=n?.publicKey??r?.publicKey,h=n?.lastStartAt??r?.lastStartAt??null,v=n?.lastError??r?.lastError??null,w=s.length>1,$=o!=null,x=I=>{const R=I.publicKey,C=I.profile,A=C?.displayName??C?.name??I.name??I.accountId;return d` - - `},E=()=>{if($&&a)return Hp({state:o,callbacks:a,accountId:s[0]?.accountId??"default"});const I=r?.profile??n?.profile,{name:R,displayName:C,about:A,picture:B,nip05:ue}=I??{},bn=R||C||A||B||ue;return d` -
    -
    -
    Profile
    - ${p?d` - - `:g} -
    - ${bn?d` -
    - ${B?d` -
    - Profile picture{yn.target.style.display="none"}} - /> -
    - `:g} - ${R?d`
    Name${R}
    `:g} - ${C?d`
    Display Name${C}
    `:g} - ${A?d`
    About${A}
    `:g} - ${ue?d`
    NIP-05${ue}
    `:g} -
    - `:d` -
    - No profile set. Click "Edit Profile" to add your name, bio, and avatar. -
    - `} -
    - `};return d` -
    -
    Nostr
    -
    Decentralized DMs via Nostr relays (NIP-04).
    - ${i} - - ${w?d` - - `:d` -
    -
    - Configured - ${p?"Yes":"No"} -
    -
    - Running - ${l?"Yes":"No"} -
    -
    - Public Key - ${Oo(u)} -
    -
    - Last start - ${h?O(h):"n/a"} -
    -
    - `} - - ${v?d`
    ${v}
    `:g} - - ${E()} - - ${_e({channelId:"nostr",props:t})} - -
    - -
    -
    - `}function qp(e){const{props:t,signal:n,accountCountLabel:s}=e;return d` -
    -
    Signal
    -
    signal-cli status and channel configuration.
    - ${s} - -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Base URL - ${n?.baseUrl??"n/a"} -
    -
    - Last start - ${n?.lastStartAt?O(n.lastStartAt):"n/a"} -
    -
    - Last probe - ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} -
    -
    - - ${n?.lastError?d`
    - ${n.lastError} -
    `:g} - - ${n?.probe?d`
    - Probe ${n.probe.ok?"ok":"failed"} · - ${n.probe.status??""} ${n.probe.error??""} -
    `:g} - - ${_e({channelId:"signal",props:t})} - -
    - -
    -
    - `}function Wp(e){const{props:t,slack:n,accountCountLabel:s}=e;return d` -
    -
    Slack
    -
    Socket mode status and channel configuration.
    - ${s} - -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Last start - ${n?.lastStartAt?O(n.lastStartAt):"n/a"} -
    -
    - Last probe - ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} -
    -
    - - ${n?.lastError?d`
    - ${n.lastError} -
    `:g} - - ${n?.probe?d`
    - Probe ${n.probe.ok?"ok":"failed"} · - ${n.probe.status??""} ${n.probe.error??""} -
    `:g} - - ${_e({channelId:"slack",props:t})} - -
    - -
    -
    - `}function Vp(e){const{props:t,telegram:n,telegramAccounts:s,accountCountLabel:i}=e,o=s.length>1,a=c=>{const p=c.probe?.bot?.username,l=c.name||c.accountId;return d` - - `};return d` -
    -
    Telegram
    -
    Bot status and channel configuration.
    - ${i} - - ${o?d` - - `:d` -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Mode - ${n?.mode??"n/a"} -
    -
    - Last start - ${n?.lastStartAt?O(n.lastStartAt):"n/a"} -
    -
    - Last probe - ${n?.lastProbeAt?O(n.lastProbeAt):"n/a"} -
    -
    - `} - - ${n?.lastError?d`
    - ${n.lastError} -
    `:g} - - ${n?.probe?d`
    - Probe ${n.probe.ok?"ok":"failed"} · - ${n.probe.status??""} ${n.probe.error??""} -
    `:g} - - ${_e({channelId:"telegram",props:t})} - -
    - -
    -
    - `}function Gp(e){const{props:t,whatsapp:n,accountCountLabel:s}=e;return d` -
    -
    WhatsApp
    -
    Link WhatsApp Web and monitor connection health.
    - ${s} - -
    -
    - Configured - ${n?.configured?"Yes":"No"} -
    -
    - Linked - ${n?.linked?"Yes":"No"} -
    -
    - Running - ${n?.running?"Yes":"No"} -
    -
    - Connected - ${n?.connected?"Yes":"No"} -
    -
    - Last connect - - ${n?.lastConnectedAt?O(n.lastConnectedAt):"n/a"} - -
    -
    - Last message - - ${n?.lastMessageAt?O(n.lastMessageAt):"n/a"} - -
    -
    - Auth age - - ${n?.authAgeMs!=null?Mp(n.authAgeMs):"n/a"} - -
    -
    - - ${n?.lastError?d`
    - ${n.lastError} -
    `:g} - - ${t.whatsappMessage?d`
    - ${t.whatsappMessage} -
    `:g} - - ${t.whatsappQrDataUrl?d`
    - WhatsApp QR -
    `:g} - -
    - - - - - -
    - - ${_e({channelId:"whatsapp",props:t})} -
    - `}function Yp(e){const t=e.snapshot?.channels,n=t?.whatsapp??void 0,s=t?.telegram??void 0,i=t?.discord??null,o=t?.slack??null,a=t?.signal??null,c=t?.imessage??null,r=t?.nostr??null,l=Qp(e.snapshot).map((u,h)=>({key:u,enabled:Pp(u,e),order:h})).sort((u,h)=>u.enabled!==h.enabled?u.enabled?-1:1:u.order-h.order);return d` -
    - ${l.map(u=>Jp(u.key,e,{whatsapp:n,telegram:s,discord:i,slack:o,signal:a,imessage:c,nostr:r,channelAccounts:e.snapshot?.channelAccounts??null}))} -
    - -
    -
    -
    -
    Channel health
    -
    Channel status snapshots from the gateway.
    -
    -
    ${e.lastSuccessAt?O(e.lastSuccessAt):"n/a"}
    -
    - ${e.lastError?d`
    - ${e.lastError} -
    `:g} -
    -${e.snapshot?JSON.stringify(e.snapshot,null,2):"No snapshot yet."}
    -      
    -
    - `}function Qp(e){return e?.channelMeta?.length?e.channelMeta.map(t=>t.id):e?.channelOrder?.length?e.channelOrder:["whatsapp","telegram","discord","slack","signal","imessage","nostr"]}function Jp(e,t,n){const s=lr(e,n.channelAccounts);switch(e){case"whatsapp":return Gp({props:t,whatsapp:n.whatsapp,accountCountLabel:s});case"telegram":return Vp({props:t,telegram:n.telegram,telegramAccounts:n.channelAccounts?.telegram??[],accountCountLabel:s});case"discord":return Fp({props:t,discord:n.discord,accountCountLabel:s});case"slack":return Wp({props:t,slack:n.slack,accountCountLabel:s});case"signal":return qp({props:t,signal:n.signal,accountCountLabel:s});case"imessage":return Up({props:t,imessage:n.imessage,accountCountLabel:s});case"nostr":{const i=n.channelAccounts?.nostr??[],o=i[0],a=o?.accountId??"default",c=o?.profile??null,r=t.nostrProfileAccountId===a?t.nostrProfileFormState:null,p=r?{onFieldChange:t.onNostrProfileFieldChange,onSave:t.onNostrProfileSave,onImport:t.onNostrProfileImport,onCancel:t.onNostrProfileCancel,onToggleAdvanced:t.onNostrProfileToggleAdvanced}:null;return jp({props:t,nostr:n.nostr,nostrAccounts:i,accountCountLabel:s,profileFormState:r,profileFormCallbacks:p,onEditProfile:()=>t.onNostrProfileEdit(a,c)})}default:return Zp(e,t,n.channelAccounts??{})}}function Zp(e,t,n){const s=ef(t.snapshot,e),i=t.snapshot?.channels?.[e],o=typeof i?.configured=="boolean"?i.configured:void 0,a=typeof i?.running=="boolean"?i.running:void 0,c=typeof i?.connected=="boolean"?i.connected:void 0,r=typeof i?.lastError=="string"?i.lastError:void 0,p=n[e]??[],l=lr(e,n);return d` -
    -
    ${s}
    -
    Channel status and configuration.
    - ${l} - - ${p.length>0?d` - - `:d` -
    -
    - Configured - ${o==null?"n/a":o?"Yes":"No"} -
    -
    - Running - ${a==null?"n/a":a?"Yes":"No"} -
    -
    - Connected - ${c==null?"n/a":c?"Yes":"No"} -
    -
    - `} - - ${r?d`
    - ${r} -
    `:g} - - ${_e({channelId:e,props:t})} -
    - `}function Xp(e){return e?.channelMeta?.length?Object.fromEntries(e.channelMeta.map(t=>[t.id,t])):{}}function ef(e,t){return Xp(e)[t]?.label??e?.channelLabels?.[t]??t}const tf=600*1e3;function cr(e){return e.lastInboundAt?Date.now()-e.lastInboundAt
    - `:d` -
    - Auth failed. Re-copy a tokenized URL with - clawdbot dashboard --no-open, or update the token, - then click Connect. - -
    - `})(),o=(()=>{if(e.connected||!e.lastError||(typeof window<"u"?window.isSecureContext:!0)!==!1)return null;const c=e.lastError.toLowerCase();return!c.includes("secure context")&&!c.includes("device identity required")?null:d` -
    - This page is HTTP, so the browser blocks device identity. Use HTTPS (Tailscale Serve) or - open http://127.0.0.1:18789 on the gateway host. -
    - If you must stay on HTTP, set - gateway.controlUi.allowInsecureAuth: true (token-only). -
    - -
    - `})();return d` -
    -
    -
    Gateway Access
    -
    Where the dashboard connects and how it authenticates.
    -
    - - - - -
    -
    - - - Click Connect to apply connection changes. -
    -
    - -
    -
    Snapshot
    -
    Latest gateway handshake information.
    -
    -
    -
    Status
    -
    - ${e.connected?"Connected":"Disconnected"} -
    -
    -
    -
    Uptime
    -
    ${n}
    -
    -
    -
    Tick Interval
    -
    ${s}
    -
    -
    -
    Last Channels Refresh
    -
    - ${e.lastChannelsRefresh?O(e.lastChannelsRefresh):"n/a"} -
    -
    -
    - ${e.lastError?d`
    -
    ${e.lastError}
    - ${i??""} - ${o??""} -
    `:d`
    - Use Channels to link WhatsApp, Telegram, Discord, Signal, or iMessage. -
    `} -
    -
    - -
    -
    -
    Instances
    -
    ${e.presenceCount}
    -
    Presence beacons in the last 5 minutes.
    -
    -
    -
    Sessions
    -
    ${e.sessionsCount??"n/a"}
    -
    Recent session keys tracked by the gateway.
    -
    -
    -
    Cron
    -
    - ${e.cronEnabled==null?"n/a":e.cronEnabled?"Enabled":"Disabled"} -
    -
    Next wake ${dr(e.cronNext)}
    -
    -
    - -
    -
    Notes
    -
    Quick reminders for remote control setups.
    -
    -
    -
    Tailscale serve
    -
    - Prefer serve mode to keep the gateway on loopback with tailnet auth. -
    -
    -
    -
    Session hygiene
    -
    Use /new or sessions.patch to reset context.
    -
    -
    -
    Cron reminders
    -
    Use isolated sessions for recurring runs.
    -
    -
    -
    - `}const Jf=["","off","minimal","low","medium","high"],Zf=["","off","on"],Xf=[{value:"",label:"inherit"},{value:"off",label:"off (explicit)"},{value:"on",label:"on"}],eh=["","off","on","stream"];function th(e){if(!e)return"";const t=e.trim().toLowerCase();return t==="z.ai"||t==="z-ai"?"zai":t}function ur(e){return th(e)==="zai"}function nh(e){return ur(e)?Zf:Jf}function sh(e,t){return!t||!e||e==="off"?e:"on"}function ih(e,t){return e?t&&e==="on"?"low":e:null}function oh(e){const t=e.result?.sessions??[];return d` -
    -
    -
    -
    Sessions
    -
    Active session keys and per-session overrides.
    -
    - -
    - -
    - - - - -
    - - ${e.error?d`
    ${e.error}
    `:g} - -
    - ${e.result?`Store: ${e.result.path}`:""} -
    - -
    -
    -
    Key
    -
    Label
    -
    Kind
    -
    Updated
    -
    Tokens
    -
    Thinking
    -
    Verbose
    -
    Reasoning
    -
    Actions
    -
    - ${t.length===0?d`
    No sessions found.
    `:t.map(n=>ah(n,e.basePath,e.onPatch,e.onDelete,e.loading))} -
    -
    - `}function ah(e,t,n,s,i){const o=e.updatedAt?O(e.updatedAt):"n/a",a=e.thinkingLevel??"",c=ur(e.modelProvider),r=sh(a,c),p=nh(e.modelProvider),l=e.verboseLevel??"",u=e.reasoningLevel??"",h=e.displayName??e.key,v=e.kind!=="global",w=v?`${Is("chat",t)}?session=${encodeURIComponent(e.key)}`:null;return d` -
    -
    ${v?d`${h}`:h}
    -
    - {const x=$.target.value.trim();n(e.key,{label:x||null})}} - /> -
    -
    ${e.kind}
    -
    ${o}
    -
    ${lf(e)}
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - `}function rh(e){const t=Math.max(0,e),n=Math.floor(t/1e3);if(n<60)return`${n}s`;const s=Math.floor(n/60);return s<60?`${s}m`:`${Math.floor(s/60)}h`}function Le(e,t){return t?d`
    ${e}${t}
    `:g}function lh(e){const t=e.execApprovalQueue[0];if(!t)return g;const n=t.request,s=t.expiresAtMs-Date.now(),i=s>0?`expires in ${rh(s)}`:"expired",o=e.execApprovalQueue.length;return d` - - `}function ch(e){const t=e.report?.skills??[],n=e.filter.trim().toLowerCase(),s=n?t.filter(i=>[i.name,i.description,i.source].join(" ").toLowerCase().includes(n)):t;return d` -
    -
    -
    -
    Skills
    -
    Bundled, managed, and workspace skills.
    -
    - -
    - -
    - -
    ${s.length} shown
    -
    - - ${e.error?d`
    ${e.error}
    `:g} - - ${s.length===0?d`
    No skills found.
    `:d` -
    - ${s.map(i=>dh(i,e))} -
    - `} -
    - `}function dh(e,t){const n=t.busyKey===e.skillKey,s=t.edits[e.skillKey]??"",i=t.messages[e.skillKey]??null,o=e.install.length>0&&e.missing.bins.length>0,a=[...e.missing.bins.map(r=>`bin:${r}`),...e.missing.env.map(r=>`env:${r}`),...e.missing.config.map(r=>`config:${r}`),...e.missing.os.map(r=>`os:${r}`)],c=[];return e.disabled&&c.push("disabled"),e.blockedByAllowlist&&c.push("blocked by allowlist"),d` -
    -
    -
    - ${e.emoji?`${e.emoji} `:""}${e.name} -
    -
    ${ss(e.description,140)}
    -
    - ${e.source} - - ${e.eligible?"eligible":"blocked"} - - ${e.disabled?d`disabled`:g} -
    - ${a.length>0?d` -
    - Missing: ${a.join(", ")} -
    - `:g} - ${c.length>0?d` -
    - Reason: ${c.join(", ")} -
    - `:g} -
    -
    -
    - - ${o?d``:g} -
    - ${i?d`
    - ${i.message} -
    `:g} - ${e.primaryEnv?d` -
    - API key - t.onEdit(e.skillKey,r.target.value)} - /> -
    - - `:g} -
    -
    - `}function uh(e,t){const n=Is(t,e.basePath);return d` - {s.defaultPrevented||s.button!==0||s.metaKey||s.ctrlKey||s.shiftKey||s.altKey||(s.preventDefault(),e.setTab(t))}} - title=${ts(t)} - > - - ${ts(t)} - - `}function ph(e){const t=fh(e.sessionKey,e.sessionsResult),n=e.onboarding,s=e.onboarding,i=e.onboarding?!1:e.settings.chatShowThinking,o=e.onboarding?!0:e.settings.chatFocusMode,a=d``,c=d``;return d` -
    - - - | - - -
    - `}function fh(e,t){const n=new Set,s=[],i=t?.sessions?.find(o=>o.key===e);if(n.add(e),s.push({key:e,displayName:i?.displayName}),t?.sessions)for(const o of t.sessions)n.has(o.key)||(n.add(o.key),s.push({key:o.key,displayName:o.displayName}));return s}const hh=["system","light","dark"];function gh(e){const t=Math.max(0,hh.indexOf(e.theme)),n=s=>i=>{const a={element:i.currentTarget};(i.clientX||i.clientY)&&(a.pointerClientX=i.clientX,a.pointerClientY=i.clientY),e.setTheme(s,a)};return d` -
    -
    - - - - -
    -
    - `}function vh(){return d` - - `}function mh(){return d` - - `}function bh(){return d` - - `}const yh=/^data:/i,wh=/^https?:\/\//i;function $h(e){const t=e.agentsList?.agents??[],s=Jo(e.sessionKey)?.agentId??e.agentsList?.defaultId??"main",o=t.find(c=>c.id===s)?.identity,a=o?.avatarUrl??o?.avatar;if(a)return yh.test(a)||wh.test(a)?a:o?.avatarUrl}function kh(e){const t=e.presenceEntries.length,n=e.sessionsResult?.count??null,s=e.cronStatus?.nextWakeAtMs??null,i=e.connected?null:"Disconnected from gateway.",o=e.tab==="chat",a=o&&(e.settings.chatFocusMode||e.onboarding),c=e.onboarding?!1:e.settings.chatShowThinking,r=$h(e),p=e.chatAvatarUrl??r??null;return d` -
    -
    -
    - -
    -
    CLAWDBOT
    -
    Gateway Dashboard
    -
    -
    -
    -
    - - Health - ${e.connected?"OK":"Offline"} -
    - ${gh(e)} -
    -
    - -
    -
    -
    -
    ${ts(e.tab)}
    -
    ${pl(e.tab)}
    -
    -
    - ${e.lastError?d`
    ${e.lastError}
    `:g} - ${o?ph(e):g} -
    -
    - - ${e.tab==="overview"?Qf({connected:e.connected,hello:e.hello,settings:e.settings,password:e.password,lastError:e.lastError,presenceCount:t,sessionsCount:n,cronEnabled:e.cronStatus?.enabled??null,cronNext:s,lastChannelsRefresh:e.channelsLastSuccess,onSettingsChange:l=>e.applySettings(l),onPasswordChange:l=>e.password=l,onSessionKeyChange:l=>{e.sessionKey=l,e.chatMessage="",e.resetToolStream(),e.applySettings({...e.settings,sessionKey:l,lastActiveSessionKey:l}),e.loadAssistantIdentity()},onConnect:()=>e.connect(),onRefresh:()=>e.loadOverview()}):g} - - ${e.tab==="channels"?Yp({connected:e.connected,loading:e.channelsLoading,snapshot:e.channelsSnapshot,lastError:e.channelsError,lastSuccessAt:e.channelsLastSuccess,whatsappMessage:e.whatsappLoginMessage,whatsappQrDataUrl:e.whatsappLoginQrDataUrl,whatsappConnected:e.whatsappLoginConnected,whatsappBusy:e.whatsappBusy,configSchema:e.configSchema,configSchemaLoading:e.configSchemaLoading,configForm:e.configForm,configUiHints:e.configUiHints,configSaving:e.configSaving,configFormDirty:e.configFormDirty,nostrProfileFormState:e.nostrProfileFormState,nostrProfileAccountId:e.nostrProfileAccountId,onRefresh:l=>oe(e,l),onWhatsAppStart:l=>e.handleWhatsAppStart(l),onWhatsAppWait:()=>e.handleWhatsAppWait(),onWhatsAppLogout:()=>e.handleWhatsAppLogout(),onConfigPatch:(l,u)=>Nt(e,l,u),onConfigSave:()=>e.handleChannelConfigSave(),onConfigReload:()=>e.handleChannelConfigReload(),onNostrProfileEdit:(l,u)=>e.handleNostrProfileEdit(l,u),onNostrProfileCancel:()=>e.handleNostrProfileCancel(),onNostrProfileFieldChange:(l,u)=>e.handleNostrProfileFieldChange(l,u),onNostrProfileSave:()=>e.handleNostrProfileSave(),onNostrProfileImport:()=>e.handleNostrProfileImport(),onNostrProfileToggleAdvanced:()=>e.handleNostrProfileToggleAdvanced()}):g} - - ${e.tab==="instances"?wf({loading:e.presenceLoading,entries:e.presenceEntries,lastError:e.presenceError,statusMessage:e.presenceStatus,onRefresh:()=>Ks(e)}):g} - - ${e.tab==="sessions"?oh({loading:e.sessionsLoading,result:e.sessionsResult,error:e.sessionsError,activeMinutes:e.sessionsFilterActive,limit:e.sessionsFilterLimit,includeGlobal:e.sessionsIncludeGlobal,includeUnknown:e.sessionsIncludeUnknown,basePath:e.basePath,onFiltersChange:l=>{e.sessionsFilterActive=l.activeMinutes,e.sessionsFilterLimit=l.limit,e.sessionsIncludeGlobal=l.includeGlobal,e.sessionsIncludeUnknown=l.includeUnknown},onRefresh:()=>tt(e),onPatch:(l,u)=>xl(e,l,u),onDelete:l=>Al(e,l)}):g} - - ${e.tab==="cron"?gf({loading:e.cronLoading,status:e.cronStatus,jobs:e.cronJobs,error:e.cronError,busy:e.cronBusy,form:e.cronForm,channels:e.channelsSnapshot?.channelMeta?.length?e.channelsSnapshot.channelMeta.map(l=>l.id):e.channelsSnapshot?.channelOrder??[],channelLabels:e.channelsSnapshot?.channelLabels??{},channelMeta:e.channelsSnapshot?.channelMeta??[],runsJobId:e.cronRunsJobId,runs:e.cronRuns,onFormChange:l=>e.cronForm={...e.cronForm,...l},onRefresh:()=>e.loadCron(),onAdd:()=>jl(e),onToggle:(l,u)=>ql(e,l,u),onRun:l=>Wl(e,l),onRemove:l=>Vl(e,l),onLoadRuns:l=>ra(e,l)}):g} - - ${e.tab==="skills"?ch({loading:e.skillsLoading,report:e.skillsReport,error:e.skillsError,filter:e.skillsFilter,edits:e.skillEdits,messages:e.skillMessages,busyKey:e.skillsBusyKey,onFilterChange:l=>e.skillsFilter=l,onRefresh:()=>_t(e,{clearMessages:!0}),onToggle:(l,u)=>Kc(e,l,u),onEdit:(l,u)=>Uc(e,l,u),onSaveKey:l=>Hc(e,l),onInstall:(l,u,h)=>zc(e,l,u,h)}):g} - - ${e.tab==="nodes"?Sf({loading:e.nodesLoading,nodes:e.nodes,devicesLoading:e.devicesLoading,devicesError:e.devicesError,devicesList:e.devicesList,configForm:e.configForm??e.configSnapshot?.config,configLoading:e.configLoading,configSaving:e.configSaving,configDirty:e.configFormDirty,configFormMode:e.configFormMode,execApprovalsLoading:e.execApprovalsLoading,execApprovalsSaving:e.execApprovalsSaving,execApprovalsDirty:e.execApprovalsDirty,execApprovalsSnapshot:e.execApprovalsSnapshot,execApprovalsForm:e.execApprovalsForm,execApprovalsSelectedAgent:e.execApprovalsSelectedAgent,execApprovalsTarget:e.execApprovalsTarget,execApprovalsTargetNodeId:e.execApprovalsTargetNodeId,onRefresh:()=>un(e),onDevicesRefresh:()=>Se(e),onDeviceApprove:l=>Ic(e,l),onDeviceReject:l=>Lc(e,l),onDeviceRotate:(l,u,h)=>Rc(e,{deviceId:l,role:u,scopes:h}),onDeviceRevoke:(l,u)=>Mc(e,{deviceId:l,role:u}),onLoadConfig:()=>me(e),onLoadExecApprovals:()=>{const l=e.execApprovalsTarget==="node"&&e.execApprovalsTargetNodeId?{kind:"node",nodeId:e.execApprovalsTargetNodeId}:{kind:"gateway"};return Us(e,l)},onBindDefault:l=>{l?Nt(e,["tools","exec","node"],l):Gi(e,["tools","exec","node"])},onBindAgent:(l,u)=>{const h=["agents","list",l,"tools","exec","node"];u?Nt(e,h,u):Gi(e,h)},onSaveBindings:()=>os(e),onExecApprovalsTargetChange:(l,u)=>{e.execApprovalsTarget=l,e.execApprovalsTargetNodeId=u,e.execApprovalsSnapshot=null,e.execApprovalsForm=null,e.execApprovalsDirty=!1,e.execApprovalsSelectedAgent=null},onExecApprovalsSelectAgent:l=>{e.execApprovalsSelectedAgent=l},onExecApprovalsPatch:(l,u)=>Bc(e,l,u),onExecApprovalsRemove:l=>Fc(e,l),onSaveExecApprovals:()=>{const l=e.execApprovalsTarget==="node"&&e.execApprovalsTargetNodeId?{kind:"node",nodeId:e.execApprovalsTargetNodeId}:{kind:"gateway"};return Dc(e,l)}}):g} - - ${e.tab==="chat"?pp({sessionKey:e.sessionKey,onSessionKeyChange:l=>{e.sessionKey=l,e.chatMessage="",e.chatStream=null,e.chatStreamStartedAt=null,e.chatRunId=null,e.chatQueue=[],e.resetToolStream(),e.resetChatScroll(),e.applySettings({...e.settings,sessionKey:l,lastActiveSessionKey:l}),e.loadAssistantIdentity(),Je(e),ds(e)},thinkingLevel:e.chatThinkingLevel,showThinking:c,loading:e.chatLoading,sending:e.chatSending,assistantAvatarUrl:p,messages:e.chatMessages,toolMessages:e.chatToolMessages,stream:e.chatStream,streamStartedAt:e.chatStreamStartedAt,draft:e.chatMessage,queue:e.chatQueue,connected:e.connected,canSend:e.connected,disabledReason:i,error:e.lastError,sessions:e.sessionsResult,focusMode:a,onRefresh:()=>(e.resetToolStream(),Promise.all([Je(e),ds(e)])),onToggleFocusMode:()=>{e.onboarding||e.applySettings({...e.settings,chatFocusMode:!e.settings.chatFocusMode})},onChatScroll:l=>e.handleChatScroll(l),onDraftChange:l=>e.chatMessage=l,onSend:()=>e.handleSendChat(),canAbort:!!e.chatRunId,onAbort:()=>{e.handleAbortChat()},onQueueRemove:l=>e.removeQueuedMessage(l),onNewSession:()=>e.handleSendChat("/new",{restoreDraft:!0}),sidebarOpen:e.sidebarOpen,sidebarContent:e.sidebarContent,sidebarError:e.sidebarError,splitRatio:e.splitRatio,onOpenSidebar:l=>e.handleOpenSidebar(l),onCloseSidebar:()=>e.handleCloseSidebar(),onSplitRatioChange:l=>e.handleSplitRatioChange(l),assistantName:e.assistantName,assistantAvatar:e.assistantAvatar}):g} - - ${e.tab==="config"?Rp({raw:e.configRaw,valid:e.configValid,issues:e.configIssues,loading:e.configLoading,saving:e.configSaving,applying:e.configApplying,updating:e.updateRunning,connected:e.connected,schema:e.configSchema,schemaLoading:e.configSchemaLoading,uiHints:e.configUiHints,formMode:e.configFormMode,formValue:e.configForm,originalValue:e.configFormOriginal,searchQuery:e.configSearchQuery,activeSection:e.configActiveSection,activeSubsection:e.configActiveSubsection,onRawChange:l=>e.configRaw=l,onFormModeChange:l=>e.configFormMode=l,onFormPatch:(l,u)=>Nt(e,l,u),onSearchChange:l=>e.configSearchQuery=l,onSectionChange:l=>{e.configActiveSection=l,e.configActiveSubsection=null},onSubsectionChange:l=>e.configActiveSubsection=l,onReload:()=>me(e),onSave:()=>os(e),onApply:()=>Ul(e),onUpdate:()=>Kl(e)}):g} - - ${e.tab==="debug"?yf({loading:e.debugLoading,status:e.debugStatus,health:e.debugHealth,models:e.debugModels,heartbeat:e.debugHeartbeat,eventLog:e.eventLog,callMethod:e.debugCallMethod,callParams:e.debugCallParams,callResult:e.debugCallResult,callError:e.debugCallError,onCallMethodChange:l=>e.debugCallMethod=l,onCallParamsChange:l=>e.debugCallParams=l,onRefresh:()=>cn(e),onCall:()=>Jl(e)}):g} - - ${e.tab==="logs"?Af({loading:e.logsLoading,error:e.logsError,file:e.logsFile,entries:e.logsEntries,filterText:e.logsFilterText,levelFilters:e.logsLevelFilters,autoFollow:e.logsAutoFollow,truncated:e.logsTruncated,onFilterTextChange:l=>e.logsFilterText=l,onLevelToggle:(l,u)=>{e.logsLevelFilters={...e.logsLevelFilters,[l]:u}},onToggleAutoFollow:l=>e.logsAutoFollow=l,onRefresh:()=>Ms(e,{reset:!0}),onExport:(l,u)=>e.exportLogs(l,u),onScroll:l=>e.handleLogsScroll(l)}):g} -
    - ${lh(e)} -
    - `}const xh={trace:!0,debug:!0,info:!0,warn:!0,error:!0,fatal:!0},Ah={name:"",description:"",agentId:"",enabled:!0,scheduleKind:"every",scheduleAt:"",everyAmount:"30",everyUnit:"minutes",cronExpr:"0 7 * * *",cronTz:"",sessionTarget:"main",wakeMode:"next-heartbeat",payloadKind:"systemEvent",payloadText:"",deliver:!1,channel:"last",to:"",timeoutSeconds:"",postToMainPrefix:""};async function Sh(e){if(!(!e.client||!e.connected)&&!e.agentsLoading){e.agentsLoading=!0,e.agentsError=null;try{const t=await e.client.request("agents.list",{});t&&(e.agentsList=t)}catch(t){e.agentsError=String(t)}finally{e.agentsLoading=!1}}}const pr={WEBCHAT_UI:"webchat-ui",CONTROL_UI:"clawdbot-control-ui",WEBCHAT:"webchat",CLI:"cli",GATEWAY_CLIENT:"gateway-client",MACOS_APP:"clawdbot-macos",IOS_APP:"clawdbot-ios",ANDROID_APP:"clawdbot-android",NODE_HOST:"node-host",TEST:"test",FINGERPRINT:"fingerprint",PROBE:"clawdbot-probe"},Uo=pr,ks={WEBCHAT:"webchat",CLI:"cli",UI:"ui",BACKEND:"backend",NODE:"node",PROBE:"probe",TEST:"test"};new Set(Object.values(pr));new Set(Object.values(ks));function _h(e){const t=e.version??(e.nonce?"v2":"v1"),n=e.scopes.join(","),s=e.token??"",i=[t,e.deviceId,e.clientId,e.clientMode,e.role,n,String(e.signedAtMs),s];return t==="v2"&&i.push(e.nonce??""),i.join("|")}const Th=4008;class Eh{constructor(t){this.opts=t,this.ws=null,this.pending=new Map,this.closed=!1,this.lastSeq=null,this.connectNonce=null,this.connectSent=!1,this.connectTimer=null,this.backoffMs=800}start(){this.closed=!1,this.connect()}stop(){this.closed=!0,this.ws?.close(),this.ws=null,this.flushPending(new Error("gateway client stopped"))}get connected(){return this.ws?.readyState===WebSocket.OPEN}connect(){this.closed||(this.ws=new WebSocket(this.opts.url),this.ws.onopen=()=>this.queueConnect(),this.ws.onmessage=t=>this.handleMessage(String(t.data??"")),this.ws.onclose=t=>{const n=String(t.reason??"");this.ws=null,this.flushPending(new Error(`gateway closed (${t.code}): ${n}`)),this.opts.onClose?.({code:t.code,reason:n}),this.scheduleReconnect()},this.ws.onerror=()=>{})}scheduleReconnect(){if(this.closed)return;const t=this.backoffMs;this.backoffMs=Math.min(this.backoffMs*1.7,15e3),window.setTimeout(()=>this.connect(),t)}flushPending(t){for(const[,n]of this.pending)n.reject(t);this.pending.clear()}async sendConnect(){if(this.connectSent)return;this.connectSent=!0,this.connectTimer!==null&&(window.clearTimeout(this.connectTimer),this.connectTimer=null);const t=typeof crypto<"u"&&!!crypto.subtle,n=["operator.admin","operator.approvals","operator.pairing"],s="operator";let i=null,o=!1,a=this.opts.token;if(t){i=await Ds();const l=Cc({deviceId:i.deviceId,role:s})?.token;a=l??this.opts.token,o=!!(l&&this.opts.token)}const c=a||this.opts.password?{token:a,password:this.opts.password}:void 0;let r;if(t&&i){const l=Date.now(),u=this.connectNonce??void 0,h=_h({deviceId:i.deviceId,clientId:this.opts.clientName??Uo.CONTROL_UI,clientMode:this.opts.mode??ks.WEBCHAT,role:s,scopes:n,signedAtMs:l,token:a??null,nonce:u}),v=await Tc(i.privateKey,h);r={id:i.deviceId,publicKey:i.publicKey,signature:v,signedAt:l,nonce:u}}const p={minProtocol:3,maxProtocol:3,client:{id:this.opts.clientName??Uo.CONTROL_UI,version:this.opts.clientVersion??"dev",platform:this.opts.platform??navigator.platform??"web",mode:this.opts.mode??ks.WEBCHAT,instanceId:this.opts.instanceId},role:s,scopes:n,device:r,caps:[],auth:c,userAgent:navigator.userAgent,locale:navigator.language};this.request("connect",p).then(l=>{l?.auth?.deviceToken&&i&&Aa({deviceId:i.deviceId,role:l.auth.role??s,token:l.auth.deviceToken,scopes:l.auth.scopes??[]}),this.backoffMs=800,this.opts.onHello?.(l)}).catch(()=>{o&&i&&Sa({deviceId:i.deviceId,role:s}),this.ws?.close(Th,"connect failed")})}handleMessage(t){let n;try{n=JSON.parse(t)}catch{return}const s=n;if(s.type==="event"){const i=n;if(i.event==="connect.challenge"){const a=i.payload,c=a&&typeof a.nonce=="string"?a.nonce:null;c&&(this.connectNonce=c,this.sendConnect());return}const o=typeof i.seq=="number"?i.seq:null;o!==null&&(this.lastSeq!==null&&o>this.lastSeq+1&&this.opts.onGap?.({expected:this.lastSeq+1,received:o}),this.lastSeq=o),this.opts.onEvent?.(i);return}if(s.type==="res"){const i=n,o=this.pending.get(i.id);if(!o)return;this.pending.delete(i.id),i.ok?o.resolve(i.payload):o.reject(new Error(i.error?.message??"request failed"));return}}request(t,n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return Promise.reject(new Error("gateway not connected"));const s=Ls(),i={type:"req",id:s,method:t,params:n},o=new Promise((a,c)=>{this.pending.set(s,{resolve:r=>a(r),reject:c})});return this.ws.send(JSON.stringify(i)),o}queueConnect(){this.connectNonce=null,this.connectSent=!1,this.connectTimer!==null&&window.clearTimeout(this.connectTimer),this.connectTimer=window.setTimeout(()=>{this.sendConnect()},750)}}function xs(e){return typeof e=="object"&&e!==null}function Ch(e){if(!xs(e))return null;const t=typeof e.id=="string"?e.id.trim():"",n=e.request;if(!t||!xs(n))return null;const s=typeof n.command=="string"?n.command.trim():"";if(!s)return null;const i=typeof e.createdAtMs=="number"?e.createdAtMs:0,o=typeof e.expiresAtMs=="number"?e.expiresAtMs:0;return!i||!o?null:{id:t,request:{command:s,cwd:typeof n.cwd=="string"?n.cwd:null,host:typeof n.host=="string"?n.host:null,security:typeof n.security=="string"?n.security:null,ask:typeof n.ask=="string"?n.ask:null,agentId:typeof n.agentId=="string"?n.agentId:null,resolvedPath:typeof n.resolvedPath=="string"?n.resolvedPath:null,sessionKey:typeof n.sessionKey=="string"?n.sessionKey:null},createdAtMs:i,expiresAtMs:o}}function Ih(e){if(!xs(e))return null;const t=typeof e.id=="string"?e.id.trim():"";return t?{id:t,decision:typeof e.decision=="string"?e.decision:null,resolvedBy:typeof e.resolvedBy=="string"?e.resolvedBy:null,ts:typeof e.ts=="number"?e.ts:null}:null}function fr(e){const t=Date.now();return e.filter(n=>n.expiresAtMs>t)}function Lh(e,t){const n=fr(e).filter(s=>s.id!==t.id);return n.push(t),n}function Ko(e,t){return fr(e).filter(n=>n.id!==t)}async function hr(e,t){if(!e.client||!e.connected)return;const n=e.sessionKey.trim(),s=n?{sessionKey:n}:{};try{const i=await e.client.request("agent.identity.get",s);if(!i)return;const o=es(i);e.assistantName=o.name,e.assistantAvatar=o.avatar,e.assistantAgentId=o.agentId??null}catch{}}function Jn(e,t){const n=(e??"").trim(),s=t.mainSessionKey?.trim();if(!s)return n;if(!n)return s;const i=t.mainKey?.trim()||"main",o=t.defaultAgentId?.trim();return n==="main"||n===i||o&&(n===`agent:${o}:main`||n===`agent:${o}:${i}`)?s:n}function Rh(e,t){if(!t?.mainSessionKey)return;const n=Jn(e.sessionKey,t),s=Jn(e.settings.sessionKey,t),i=Jn(e.settings.lastActiveSessionKey,t),o=n||s||e.sessionKey,a={...e.settings,sessionKey:s||o,lastActiveSessionKey:i||o},c=a.sessionKey!==e.settings.sessionKey||a.lastActiveSessionKey!==e.settings.lastActiveSessionKey;o!==e.sessionKey&&(e.sessionKey=o),c&&$e(e,a)}function gr(e){e.lastError=null,e.hello=null,e.connected=!1,e.execApprovalQueue=[],e.execApprovalError=null,e.client?.stop(),e.client=new Eh({url:e.settings.gatewayUrl,token:e.settings.token.trim()?e.settings.token:void 0,password:e.password.trim()?e.password:void 0,clientName:"clawdbot-control-ui",mode:"webchat",onHello:t=>{e.connected=!0,e.hello=t,Ph(e,t),hr(e),Sh(e),un(e,{quiet:!0}),Se(e,{quiet:!0}),Vs(e)},onClose:({code:t,reason:n})=>{e.connected=!1,e.lastError=`disconnected (${t}): ${n||"no reason"}`},onEvent:t=>Mh(e,t),onGap:({expected:t,received:n})=>{e.lastError=`event gap detected (expected seq ${t}, got ${n}); refresh recommended`}}),e.client.start()}function Mh(e,t){if(e.eventLogBuffer=[{ts:Date.now(),event:t.event,payload:t.payload},...e.eventLogBuffer].slice(0,250),e.tab==="debug"&&(e.eventLog=e.eventLogBuffer),t.event==="agent"){if(e.onboarding)return;Rl(e,t.payload);return}if(t.event==="chat"){const n=t.payload;n?.sessionKey&&_a(e,n.sessionKey);const s=kl(e,n);(s==="final"||s==="error"||s==="aborted")&&(Rs(e),ud(e)),s==="final"&&Je(e);return}if(t.event==="presence"){const n=t.payload;n?.presence&&Array.isArray(n.presence)&&(e.presenceEntries=n.presence,e.presenceError=null,e.presenceStatus=null);return}if(t.event==="cron"&&e.tab==="cron"&&Gs(e),(t.event==="device.pair.requested"||t.event==="device.pair.resolved")&&Se(e,{quiet:!0}),t.event==="exec.approval.requested"){const n=Ch(t.payload);if(n){e.execApprovalQueue=Lh(e.execApprovalQueue,n),e.execApprovalError=null;const s=Math.max(0,n.expiresAtMs-Date.now()+500);window.setTimeout(()=>{e.execApprovalQueue=Ko(e.execApprovalQueue,n.id)},s)}return}if(t.event==="exec.approval.resolved"){const n=Ih(t.payload);n&&(e.execApprovalQueue=Ko(e.execApprovalQueue,n.id))}}function Ph(e,t){const n=t.snapshot;n?.presence&&Array.isArray(n.presence)&&(e.presenceEntries=n.presence),n?.health&&(e.debugHealth=n.health),n?.sessionDefaults&&Rh(e,n.sessionDefaults)}function Nh(e){e.basePath=Zc(),nd(e,!0),Xc(e),ed(e),window.addEventListener("popstate",e.popStateHandler),Yc(e),gr(e),Vc(e),e.tab==="logs"&&zs(e),e.tab==="debug"&&qs(e)}function Oh(e){Dl(e)}function Dh(e){window.removeEventListener("popstate",e.popStateHandler),Gc(e),js(e),Ws(e),td(e),e.topbarObserver?.disconnect(),e.topbarObserver=null}function Bh(e,t){if(e.tab==="chat"&&(t.has("chatMessages")||t.has("chatToolMessages")||t.has("chatStream")||t.has("chatLoading")||t.has("tab"))){const n=t.has("tab"),s=t.has("chatLoading")&&t.get("chatLoading")===!0&&e.chatLoading===!1;rn(e,n||s||!e.chatHasAutoScrolled)}e.tab==="logs"&&(t.has("logsEntries")||t.has("logsAutoFollow")||t.has("tab"))&&e.logsAutoFollow&&e.logsAtBottom&&sa(e,t.has("tab")||t.has("logsAutoFollow"))}async function Fh(e,t){await Gl(e,t),await oe(e,!0)}async function Uh(e){await Yl(e),await oe(e,!0)}async function Kh(e){await Ql(e),await oe(e,!0)}async function Hh(e){await os(e),await me(e),await oe(e,!0)}async function zh(e){await me(e),await oe(e,!0)}function jh(e){if(!Array.isArray(e))return{};const t={};for(const n of e){if(typeof n!="string")continue;const[s,...i]=n.split(":");if(!s||i.length===0)continue;const o=s.trim(),a=i.join(":").trim();o&&a&&(t[o]=a)}return t}function vr(e){return(e.channelsSnapshot?.channelAccounts?.nostr??[])[0]?.accountId??e.nostrProfileAccountId??"default"}function mr(e,t=""){return`/api/channels/nostr/${encodeURIComponent(e)}/profile${t}`}function qh(e,t,n){e.nostrProfileAccountId=t,e.nostrProfileFormState=zp(n??void 0)}function Wh(e){e.nostrProfileFormState=null,e.nostrProfileAccountId=null}function Vh(e,t,n){const s=e.nostrProfileFormState;s&&(e.nostrProfileFormState={...s,values:{...s.values,[t]:n},fieldErrors:{...s.fieldErrors,[t]:""}})}function Gh(e){const t=e.nostrProfileFormState;t&&(e.nostrProfileFormState={...t,showAdvanced:!t.showAdvanced})}async function Yh(e){const t=e.nostrProfileFormState;if(!t||t.saving)return;const n=vr(e);e.nostrProfileFormState={...t,saving:!0,error:null,success:null,fieldErrors:{}};try{const s=await fetch(mr(n),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(t.values)}),i=await s.json().catch(()=>null);if(!s.ok||i?.ok===!1||!i){const o=i?.error??`Profile update failed (${s.status})`;e.nostrProfileFormState={...t,saving:!1,error:o,success:null,fieldErrors:jh(i?.details)};return}if(!i.persisted){e.nostrProfileFormState={...t,saving:!1,error:"Profile publish failed on all relays.",success:null};return}e.nostrProfileFormState={...t,saving:!1,error:null,success:"Profile published to relays.",fieldErrors:{},original:{...t.values}},await oe(e,!0)}catch(s){e.nostrProfileFormState={...t,saving:!1,error:`Profile update failed: ${String(s)}`,success:null}}}async function Qh(e){const t=e.nostrProfileFormState;if(!t||t.importing)return;const n=vr(e);e.nostrProfileFormState={...t,importing:!0,error:null,success:null};try{const s=await fetch(mr(n,"/import"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({autoMerge:!0})}),i=await s.json().catch(()=>null);if(!s.ok||i?.ok===!1||!i){const r=i?.error??`Profile import failed (${s.status})`;e.nostrProfileFormState={...t,importing:!1,error:r,success:null};return}const o=i.merged??i.imported??null,a=o?{...t.values,...o}:t.values,c=!!(a.banner||a.website||a.nip05||a.lud16);e.nostrProfileFormState={...t,importing:!1,values:a,error:null,success:i.saved?"Profile imported from relays. Review and publish.":"Profile imported. Review and publish.",showAdvanced:c},i.saved&&await oe(e,!0)}catch(s){e.nostrProfileFormState={...t,importing:!1,error:`Profile import failed: ${String(s)}`,success:null}}}var Jh=Object.defineProperty,Zh=Object.getOwnPropertyDescriptor,b=(e,t,n,s)=>{for(var i=s>1?void 0:s?Zh(t,n):t,o=e.length-1,a;o>=0;o--)(a=e[o])&&(i=(s?a(t,n,i):a(i))||i);return s&&i&&Jh(t,n,i),i};const Zn=al();function Xh(){if(!window.location.search)return!1;const t=new URLSearchParams(window.location.search).get("onboarding");if(!t)return!1;const n=t.trim().toLowerCase();return n==="1"||n==="true"||n==="yes"||n==="on"}let m=class extends Ye{constructor(){super(...arguments),this.settings=rl(),this.password="",this.tab="chat",this.onboarding=Xh(),this.connected=!1,this.theme=this.settings.theme??"system",this.themeResolved="dark",this.hello=null,this.lastError=null,this.eventLog=[],this.eventLogBuffer=[],this.toolStreamSyncTimer=null,this.sidebarCloseTimer=null,this.assistantName=Zn.name,this.assistantAvatar=Zn.avatar,this.assistantAgentId=Zn.agentId??null,this.sessionKey=this.settings.sessionKey,this.chatLoading=!1,this.chatSending=!1,this.chatMessage="",this.chatMessages=[],this.chatToolMessages=[],this.chatStream=null,this.chatStreamStartedAt=null,this.chatRunId=null,this.chatAvatarUrl=null,this.chatThinkingLevel=null,this.chatQueue=[],this.sidebarOpen=!1,this.sidebarContent=null,this.sidebarError=null,this.splitRatio=this.settings.splitRatio,this.nodesLoading=!1,this.nodes=[],this.devicesLoading=!1,this.devicesError=null,this.devicesList=null,this.execApprovalsLoading=!1,this.execApprovalsSaving=!1,this.execApprovalsDirty=!1,this.execApprovalsSnapshot=null,this.execApprovalsForm=null,this.execApprovalsSelectedAgent=null,this.execApprovalsTarget="gateway",this.execApprovalsTargetNodeId=null,this.execApprovalQueue=[],this.execApprovalBusy=!1,this.execApprovalError=null,this.configLoading=!1,this.configRaw=`{ -} -`,this.configValid=null,this.configIssues=[],this.configSaving=!1,this.configApplying=!1,this.updateRunning=!1,this.applySessionKey=this.settings.lastActiveSessionKey,this.configSnapshot=null,this.configSchema=null,this.configSchemaVersion=null,this.configSchemaLoading=!1,this.configUiHints={},this.configForm=null,this.configFormOriginal=null,this.configFormDirty=!1,this.configFormMode="form",this.configSearchQuery="",this.configActiveSection=null,this.configActiveSubsection=null,this.channelsLoading=!1,this.channelsSnapshot=null,this.channelsError=null,this.channelsLastSuccess=null,this.whatsappLoginMessage=null,this.whatsappLoginQrDataUrl=null,this.whatsappLoginConnected=null,this.whatsappBusy=!1,this.nostrProfileFormState=null,this.nostrProfileAccountId=null,this.presenceLoading=!1,this.presenceEntries=[],this.presenceError=null,this.presenceStatus=null,this.agentsLoading=!1,this.agentsList=null,this.agentsError=null,this.sessionsLoading=!1,this.sessionsResult=null,this.sessionsError=null,this.sessionsFilterActive="",this.sessionsFilterLimit="120",this.sessionsIncludeGlobal=!0,this.sessionsIncludeUnknown=!1,this.cronLoading=!1,this.cronJobs=[],this.cronStatus=null,this.cronError=null,this.cronForm={...Ah},this.cronRunsJobId=null,this.cronRuns=[],this.cronBusy=!1,this.skillsLoading=!1,this.skillsReport=null,this.skillsError=null,this.skillsFilter="",this.skillEdits={},this.skillsBusyKey=null,this.skillMessages={},this.debugLoading=!1,this.debugStatus=null,this.debugHealth=null,this.debugModels=[],this.debugHeartbeat=null,this.debugCallMethod="",this.debugCallParams="{}",this.debugCallResult=null,this.debugCallError=null,this.logsLoading=!1,this.logsError=null,this.logsFile=null,this.logsEntries=[],this.logsFilterText="",this.logsLevelFilters={...xh},this.logsAutoFollow=!0,this.logsTruncated=!1,this.logsCursor=null,this.logsLastFetchAt=null,this.logsLimit=500,this.logsMaxBytes=25e4,this.logsAtBottom=!0,this.client=null,this.chatScrollFrame=null,this.chatScrollTimeout=null,this.chatHasAutoScrolled=!1,this.chatUserNearBottom=!0,this.nodesPollInterval=null,this.logsPollInterval=null,this.debugPollInterval=null,this.logsScrollFrame=null,this.toolStreamById=new Map,this.toolStreamOrder=[],this.basePath="",this.popStateHandler=()=>sd(this),this.themeMedia=null,this.themeMediaHandler=null,this.topbarObserver=null}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),Nh(this)}firstUpdated(){Oh(this)}disconnectedCallback(){Dh(this),super.disconnectedCallback()}updated(e){Bh(this,e)}connect(){gr(this)}handleChatScroll(e){Ml(this,e)}handleLogsScroll(e){Pl(this,e)}exportLogs(e,t){Ol(e,t)}resetToolStream(){Rs(this)}resetChatScroll(){Nl(this)}async loadAssistantIdentity(){await hr(this)}applySettings(e){$e(this,e)}setTab(e){Qc(this,e)}setTheme(e,t){Jc(this,e,t)}async loadOverview(){await Ca(this)}async loadCron(){await Gs(this)}async handleAbortChat(){await La(this)}removeQueuedMessage(e){ld(this,e)}async handleSendChat(e,t){await cd(this,e,t)}async handleWhatsAppStart(e){await Fh(this,e)}async handleWhatsAppWait(){await Uh(this)}async handleWhatsAppLogout(){await Kh(this)}async handleChannelConfigSave(){await Hh(this)}async handleChannelConfigReload(){await zh(this)}handleNostrProfileEdit(e,t){qh(this,e,t)}handleNostrProfileCancel(){Wh(this)}handleNostrProfileFieldChange(e,t){Vh(this,e,t)}async handleNostrProfileSave(){await Yh(this)}async handleNostrProfileImport(){await Qh(this)}handleNostrProfileToggleAdvanced(){Gh(this)}async handleExecApprovalDecision(e){const t=this.execApprovalQueue[0];if(!(!t||!this.client||this.execApprovalBusy)){this.execApprovalBusy=!0,this.execApprovalError=null;try{await this.client.request("exec.approval.resolve",{id:t.id,decision:e}),this.execApprovalQueue=this.execApprovalQueue.filter(n=>n.id!==t.id)}catch(n){this.execApprovalError=`Exec approval failed: ${String(n)}`}finally{this.execApprovalBusy=!1}}}handleOpenSidebar(e){this.sidebarCloseTimer!=null&&(window.clearTimeout(this.sidebarCloseTimer),this.sidebarCloseTimer=null),this.sidebarContent=e,this.sidebarError=null,this.sidebarOpen=!0}handleCloseSidebar(){this.sidebarOpen=!1,this.sidebarCloseTimer!=null&&window.clearTimeout(this.sidebarCloseTimer),this.sidebarCloseTimer=window.setTimeout(()=>{this.sidebarOpen||(this.sidebarContent=null,this.sidebarError=null,this.sidebarCloseTimer=null)},200)}handleSplitRatioChange(e){const t=Math.max(.4,Math.min(.7,e));this.splitRatio=t,this.applySettings({...this.settings,splitRatio:t})}render(){return kh(this)}};b([y()],m.prototype,"settings",2);b([y()],m.prototype,"password",2);b([y()],m.prototype,"tab",2);b([y()],m.prototype,"onboarding",2);b([y()],m.prototype,"connected",2);b([y()],m.prototype,"theme",2);b([y()],m.prototype,"themeResolved",2);b([y()],m.prototype,"hello",2);b([y()],m.prototype,"lastError",2);b([y()],m.prototype,"eventLog",2);b([y()],m.prototype,"assistantName",2);b([y()],m.prototype,"assistantAvatar",2);b([y()],m.prototype,"assistantAgentId",2);b([y()],m.prototype,"sessionKey",2);b([y()],m.prototype,"chatLoading",2);b([y()],m.prototype,"chatSending",2);b([y()],m.prototype,"chatMessage",2);b([y()],m.prototype,"chatMessages",2);b([y()],m.prototype,"chatToolMessages",2);b([y()],m.prototype,"chatStream",2);b([y()],m.prototype,"chatStreamStartedAt",2);b([y()],m.prototype,"chatRunId",2);b([y()],m.prototype,"chatAvatarUrl",2);b([y()],m.prototype,"chatThinkingLevel",2);b([y()],m.prototype,"chatQueue",2);b([y()],m.prototype,"sidebarOpen",2);b([y()],m.prototype,"sidebarContent",2);b([y()],m.prototype,"sidebarError",2);b([y()],m.prototype,"splitRatio",2);b([y()],m.prototype,"nodesLoading",2);b([y()],m.prototype,"nodes",2);b([y()],m.prototype,"devicesLoading",2);b([y()],m.prototype,"devicesError",2);b([y()],m.prototype,"devicesList",2);b([y()],m.prototype,"execApprovalsLoading",2);b([y()],m.prototype,"execApprovalsSaving",2);b([y()],m.prototype,"execApprovalsDirty",2);b([y()],m.prototype,"execApprovalsSnapshot",2);b([y()],m.prototype,"execApprovalsForm",2);b([y()],m.prototype,"execApprovalsSelectedAgent",2);b([y()],m.prototype,"execApprovalsTarget",2);b([y()],m.prototype,"execApprovalsTargetNodeId",2);b([y()],m.prototype,"execApprovalQueue",2);b([y()],m.prototype,"execApprovalBusy",2);b([y()],m.prototype,"execApprovalError",2);b([y()],m.prototype,"configLoading",2);b([y()],m.prototype,"configRaw",2);b([y()],m.prototype,"configValid",2);b([y()],m.prototype,"configIssues",2);b([y()],m.prototype,"configSaving",2);b([y()],m.prototype,"configApplying",2);b([y()],m.prototype,"updateRunning",2);b([y()],m.prototype,"applySessionKey",2);b([y()],m.prototype,"configSnapshot",2);b([y()],m.prototype,"configSchema",2);b([y()],m.prototype,"configSchemaVersion",2);b([y()],m.prototype,"configSchemaLoading",2);b([y()],m.prototype,"configUiHints",2);b([y()],m.prototype,"configForm",2);b([y()],m.prototype,"configFormOriginal",2);b([y()],m.prototype,"configFormDirty",2);b([y()],m.prototype,"configFormMode",2);b([y()],m.prototype,"configSearchQuery",2);b([y()],m.prototype,"configActiveSection",2);b([y()],m.prototype,"configActiveSubsection",2);b([y()],m.prototype,"channelsLoading",2);b([y()],m.prototype,"channelsSnapshot",2);b([y()],m.prototype,"channelsError",2);b([y()],m.prototype,"channelsLastSuccess",2);b([y()],m.prototype,"whatsappLoginMessage",2);b([y()],m.prototype,"whatsappLoginQrDataUrl",2);b([y()],m.prototype,"whatsappLoginConnected",2);b([y()],m.prototype,"whatsappBusy",2);b([y()],m.prototype,"nostrProfileFormState",2);b([y()],m.prototype,"nostrProfileAccountId",2);b([y()],m.prototype,"presenceLoading",2);b([y()],m.prototype,"presenceEntries",2);b([y()],m.prototype,"presenceError",2);b([y()],m.prototype,"presenceStatus",2);b([y()],m.prototype,"agentsLoading",2);b([y()],m.prototype,"agentsList",2);b([y()],m.prototype,"agentsError",2);b([y()],m.prototype,"sessionsLoading",2);b([y()],m.prototype,"sessionsResult",2);b([y()],m.prototype,"sessionsError",2);b([y()],m.prototype,"sessionsFilterActive",2);b([y()],m.prototype,"sessionsFilterLimit",2);b([y()],m.prototype,"sessionsIncludeGlobal",2);b([y()],m.prototype,"sessionsIncludeUnknown",2);b([y()],m.prototype,"cronLoading",2);b([y()],m.prototype,"cronJobs",2);b([y()],m.prototype,"cronStatus",2);b([y()],m.prototype,"cronError",2);b([y()],m.prototype,"cronForm",2);b([y()],m.prototype,"cronRunsJobId",2);b([y()],m.prototype,"cronRuns",2);b([y()],m.prototype,"cronBusy",2);b([y()],m.prototype,"skillsLoading",2);b([y()],m.prototype,"skillsReport",2);b([y()],m.prototype,"skillsError",2);b([y()],m.prototype,"skillsFilter",2);b([y()],m.prototype,"skillEdits",2);b([y()],m.prototype,"skillsBusyKey",2);b([y()],m.prototype,"skillMessages",2);b([y()],m.prototype,"debugLoading",2);b([y()],m.prototype,"debugStatus",2);b([y()],m.prototype,"debugHealth",2);b([y()],m.prototype,"debugModels",2);b([y()],m.prototype,"debugHeartbeat",2);b([y()],m.prototype,"debugCallMethod",2);b([y()],m.prototype,"debugCallParams",2);b([y()],m.prototype,"debugCallResult",2);b([y()],m.prototype,"debugCallError",2);b([y()],m.prototype,"logsLoading",2);b([y()],m.prototype,"logsError",2);b([y()],m.prototype,"logsFile",2);b([y()],m.prototype,"logsEntries",2);b([y()],m.prototype,"logsFilterText",2);b([y()],m.prototype,"logsLevelFilters",2);b([y()],m.prototype,"logsAutoFollow",2);b([y()],m.prototype,"logsTruncated",2);b([y()],m.prototype,"logsCursor",2);b([y()],m.prototype,"logsLastFetchAt",2);b([y()],m.prototype,"logsLimit",2);b([y()],m.prototype,"logsMaxBytes",2);b([y()],m.prototype,"logsAtBottom",2);m=b([Yo("clawdbot-app")],m); -//# sourceMappingURL=index-bYQnHP3a.js.map diff --git a/dist/control-ui/assets/index-bYQnHP3a.js.map b/dist/control-ui/assets/index-bYQnHP3a.js.map deleted file mode 100644 index 1df80badef9..00000000000 --- a/dist/control-ui/assets/index-bYQnHP3a.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index-bYQnHP3a.js","sources":["../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/css-tag.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/reactive-element.js","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/lit-html.js","../../../node_modules/.pnpm/lit-element@4.2.2/node_modules/lit-element/lit-element.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/custom-element.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/property.js","../../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/state.js","../../../ui/src/ui/assistant-identity.ts","../../../ui/src/ui/storage.ts","../../../src/sessions/session-key-utils.ts","../../../ui/src/ui/navigation.ts","../../../ui/src/ui/format.ts","../../../ui/src/ui/chat/message-extract.ts","../../../ui/src/ui/uuid.ts","../../../ui/src/ui/controllers/chat.ts","../../../ui/src/ui/controllers/sessions.ts","../../../ui/src/ui/app-tool-stream.ts","../../../ui/src/ui/app-scroll.ts","../../../ui/src/ui/controllers/config/form-utils.ts","../../../ui/src/ui/controllers/config.ts","../../../ui/src/ui/controllers/cron.ts","../../../ui/src/ui/controllers/channels.ts","../../../ui/src/ui/controllers/debug.ts","../../../ui/src/ui/controllers/logs.ts","../../../node_modules/.pnpm/@noble+ed25519@3.0.0/node_modules/@noble/ed25519/index.js","../../../ui/src/ui/device-identity.ts","../../../ui/src/ui/device-auth.ts","../../../ui/src/ui/controllers/devices.ts","../../../ui/src/ui/controllers/nodes.ts","../../../ui/src/ui/controllers/exec-approvals.ts","../../../ui/src/ui/controllers/presence.ts","../../../ui/src/ui/controllers/skills.ts","../../../ui/src/ui/theme.ts","../../../ui/src/ui/theme-transition.ts","../../../ui/src/ui/app-polling.ts","../../../ui/src/ui/app-settings.ts","../../../ui/src/ui/app-chat.ts","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directive.js","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directive-helpers.js","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directives/repeat.js","../../../ui/src/ui/chat/message-normalizer.ts","../../../node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directives/unsafe-html.js","../../../node_modules/.pnpm/dompurify@3.3.1/node_modules/dompurify/dist/purify.es.mjs","../../../node_modules/.pnpm/marked@17.0.1/node_modules/marked/lib/marked.esm.js","../../../ui/src/ui/markdown.ts","../../../ui/src/ui/icons.ts","../../../ui/src/ui/chat/copy-as-markdown.ts","../../../ui/src/ui/tool-display.ts","../../../ui/src/ui/chat/constants.ts","../../../ui/src/ui/chat/tool-helpers.ts","../../../ui/src/ui/chat/tool-cards.ts","../../../ui/src/ui/chat/grouped-render.ts","../../../ui/src/ui/views/markdown-sidebar.ts","../../../ui/src/ui/components/resizable-divider.ts","../../../ui/src/ui/views/chat.ts","../../../ui/src/ui/views/config-form.shared.ts","../../../ui/src/ui/views/config-form.node.ts","../../../ui/src/ui/views/config-form.render.ts","../../../ui/src/ui/views/config-form.analyze.ts","../../../ui/src/ui/views/config.ts","../../../ui/src/ui/views/channels.shared.ts","../../../ui/src/ui/views/channels.config.ts","../../../ui/src/ui/views/channels.discord.ts","../../../ui/src/ui/views/channels.imessage.ts","../../../ui/src/ui/views/channels.nostr-profile-form.ts","../../../ui/src/ui/views/channels.nostr.ts","../../../ui/src/ui/views/channels.signal.ts","../../../ui/src/ui/views/channels.slack.ts","../../../ui/src/ui/views/channels.telegram.ts","../../../ui/src/ui/views/channels.whatsapp.ts","../../../ui/src/ui/views/channels.ts","../../../ui/src/ui/presenter.ts","../../../ui/src/ui/views/cron.ts","../../../ui/src/ui/views/debug.ts","../../../ui/src/ui/views/instances.ts","../../../ui/src/ui/views/logs.ts","../../../ui/src/ui/views/nodes.ts","../../../ui/src/ui/views/overview.ts","../../../ui/src/ui/views/sessions.ts","../../../ui/src/ui/views/exec-approval.ts","../../../ui/src/ui/views/skills.ts","../../../ui/src/ui/app-render.helpers.ts","../../../ui/src/ui/app-render.ts","../../../ui/src/ui/app-defaults.ts","../../../ui/src/ui/controllers/agents.ts","../../../src/gateway/protocol/client-info.ts","../../../src/gateway/device-auth.ts","../../../ui/src/ui/gateway.ts","../../../ui/src/ui/controllers/exec-approval.ts","../../../ui/src/ui/controllers/assistant-identity.ts","../../../ui/src/ui/app-gateway.ts","../../../ui/src/ui/app-lifecycle.ts","../../../ui/src/ui/app-channels.ts","../../../ui/src/ui/app.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2019 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&\"adoptedStyleSheets\"in Document.prototype&&\"replace\"in CSSStyleSheet.prototype,s=Symbol(),o=new WeakMap;class n{constructor(t,e,o){if(this._$cssResult$=!0,o!==s)throw Error(\"CSSResult is not constructable. Use `unsafeCSS` or `css` instead.\");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const s=this.t;if(e&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=o.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&o.set(s,t))}return t}toString(){return this.cssText}}const r=t=>new n(\"string\"==typeof t?t:t+\"\",void 0,s),i=(t,...e)=>{const o=1===t.length?t[0]:e.reduce((e,s,o)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if(\"number\"==typeof t)return t;throw Error(\"Value passed to 'css' function must be a 'css' function result: \"+t+\". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.\")})(s)+t[o+1],t[0]);return new n(o,t,s)},S=(s,o)=>{if(e)s.adoptedStyleSheets=o.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of o){const o=document.createElement(\"style\"),n=t.litNonce;void 0!==n&&o.setAttribute(\"nonce\",n),o.textContent=e.cssText,s.appendChild(o)}},c=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e=\"\";for(const s of t.cssRules)e+=s.cssText;return r(e)})(t):t;export{n as CSSResult,S as adoptStyles,i as css,c as getCompatibleStyle,e as supportsAdoptingStyleSheets,r as unsafeCSS};\n//# sourceMappingURL=css-tag.js.map\n","import{getCompatibleStyle as t,adoptStyles as s}from\"./css-tag.js\";export{CSSResult,css,supportsAdoptingStyleSheets,unsafeCSS}from\"./css-tag.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const{is:i,defineProperty:e,getOwnPropertyDescriptor:h,getOwnPropertyNames:r,getOwnPropertySymbols:o,getPrototypeOf:n}=Object,a=globalThis,c=a.trustedTypes,l=c?c.emptyScript:\"\",p=a.reactiveElementPolyfillSupport,d=(t,s)=>t,u={toAttribute(t,s){switch(s){case Boolean:t=t?l:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,s){let i=t;switch(s){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},f=(t,s)=>!i(t,s),b={attribute:!0,type:String,converter:u,reflect:!1,useDefault:!1,hasChanged:f};Symbol.metadata??=Symbol(\"metadata\"),a.litPropertyMetadata??=new WeakMap;class y extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,s=b){if(s.state&&(s.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((s=Object.create(s)).wrapped=!0),this.elementProperties.set(t,s),!s.noAccessor){const i=Symbol(),h=this.getPropertyDescriptor(t,i,s);void 0!==h&&e(this.prototype,t,h)}}static getPropertyDescriptor(t,s,i){const{get:e,set:r}=h(this.prototype,t)??{get(){return this[s]},set(t){this[s]=t}};return{get:e,set(s){const h=e?.call(this);r?.call(this,s),this.requestUpdate(t,h,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(d(\"elementProperties\")))return;const t=n(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(d(\"finalized\")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(d(\"properties\"))){const t=this.properties,s=[...r(t),...o(t)];for(const i of s)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const s=litPropertyMetadata.get(t);if(void 0!==s)for(const[t,i]of s)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,s]of this.elementProperties){const i=this._$Eu(t,s);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(s){const i=[];if(Array.isArray(s)){const e=new Set(s.flat(1/0).reverse());for(const s of e)i.unshift(t(s))}else void 0!==s&&i.push(t(s));return i}static _$Eu(t,s){const i=s.attribute;return!1===i?void 0:\"string\"==typeof i?i:\"string\"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,s=this.constructor.elementProperties;for(const i of s.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return s(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,s,i){this._$AK(t,i)}_$ET(t,s){const i=this.constructor.elementProperties.get(t),e=this.constructor._$Eu(t,i);if(void 0!==e&&!0===i.reflect){const h=(void 0!==i.converter?.toAttribute?i.converter:u).toAttribute(s,i.type);this._$Em=t,null==h?this.removeAttribute(e):this.setAttribute(e,h),this._$Em=null}}_$AK(t,s){const i=this.constructor,e=i._$Eh.get(t);if(void 0!==e&&this._$Em!==e){const t=i.getPropertyOptions(e),h=\"function\"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:u;this._$Em=e;const r=h.fromAttribute(s,t.type);this[e]=r??this._$Ej?.get(e)??r,this._$Em=null}}requestUpdate(t,s,i,e=!1,h){if(void 0!==t){const r=this.constructor;if(!1===e&&(h=this[t]),i??=r.getPropertyOptions(t),!((i.hasChanged??f)(h,s)||i.useDefault&&i.reflect&&h===this._$Ej?.get(t)&&!this.hasAttribute(r._$Eu(t,i))))return;this.C(t,s,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,s,{useDefault:i,reflect:e,wrapped:h},r){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,r??s??this[t]),!0!==h||void 0!==r)||(this._$AL.has(t)||(this.hasUpdated||i||(s=void 0),this._$AL.set(t,s)),!0===e&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,s]of this._$Ep)this[t]=s;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[s,i]of t){const{wrapped:t}=i,e=this[s];!0!==t||this._$AL.has(s)||void 0===e||this.C(s,void 0,i,e)}}let t=!1;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(s)):this._$EM()}catch(s){throw t=!1,this._$EM(),s}t&&this._$AE(s)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}}y.elementStyles=[],y.shadowRootOptions={mode:\"open\"},y[d(\"elementProperties\")]=new Map,y[d(\"finalized\")]=new Map,p?.({ReactiveElement:y}),(a.reactiveElementVersions??=[]).push(\"2.1.2\");export{y as ReactiveElement,s as adoptStyles,u as defaultConverter,t as getCompatibleStyle,f as notEqual};\n//# sourceMappingURL=reactive-element.js.map\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=globalThis,i=t=>t,s=t.trustedTypes,e=s?s.createPolicy(\"lit-html\",{createHTML:t=>t}):void 0,h=\"$lit$\",o=`lit$${Math.random().toFixed(9).slice(2)}$`,n=\"?\"+o,r=`<${n}>`,l=document,c=()=>l.createComment(\"\"),a=t=>null===t||\"object\"!=typeof t&&\"function\"!=typeof t,u=Array.isArray,d=t=>u(t)||\"function\"==typeof t?.[Symbol.iterator],f=\"[ \\t\\n\\f\\r]\",v=/<(?:(!--|\\/[^a-zA-Z])|(\\/?[a-zA-Z][^>\\s]*)|(\\/?$))/g,_=/-->/g,m=/>/g,p=RegExp(`>|${f}(?:([^\\\\s\"'>=/]+)(${f}*=${f}*(?:[^ \\t\\n\\f\\r\"'\\`<>=]|(\"|')|))|$)`,\"g\"),g=/'/g,$=/\"/g,y=/^(?:script|style|textarea|title)$/i,x=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),b=x(1),w=x(2),T=x(3),E=Symbol.for(\"lit-noChange\"),A=Symbol.for(\"lit-nothing\"),C=new WeakMap,P=l.createTreeWalker(l,129);function V(t,i){if(!u(t)||!t.hasOwnProperty(\"raw\"))throw Error(\"invalid template strings array\");return void 0!==e?e.createHTML(i):i}const N=(t,i)=>{const s=t.length-1,e=[];let n,l=2===i?\"\":3===i?\"\":\"\",c=v;for(let i=0;i\"===u[0]?(c=n??v,d=-1):void 0===u[1]?d=-2:(d=c.lastIndex-u[2].length,a=u[1],c=void 0===u[3]?p:'\"'===u[3]?$:g):c===$||c===g?c=p:c===_||c===m?c=v:(c=p,n=void 0);const x=c===p&&t[i+1].startsWith(\"/>\")?\" \":\"\";l+=c===v?s+r:d>=0?(e.push(a),s.slice(0,d)+h+s.slice(d)+o+x):s+o+(-2===d?i:x)}return[V(t,l+(t[s]||\"\")+(2===i?\"\":3===i?\"\":\"\")),e]};class S{constructor({strings:t,_$litType$:i},e){let r;this.parts=[];let l=0,a=0;const u=t.length-1,d=this.parts,[f,v]=N(t,i);if(this.el=S.createElement(f,e),P.currentNode=this.el.content,2===i||3===i){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(r=P.nextNode())&&d.length0){r.textContent=s?s.emptyScript:\"\";for(let s=0;s2||\"\"!==s[0]||\"\"!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=A}_$AI(t,i=this,s,e){const h=this.strings;let o=!1;if(void 0===h)t=M(this,t,i,0),o=!a(t)||t!==this._$AH&&t!==E,o&&(this._$AH=t);else{const e=t;let n,r;for(t=h[0],n=0;n{const e=s?.renderBefore??i;let h=e._$litPart$;if(void 0===h){const t=s?.renderBefore??null;e._$litPart$=h=new k(i.insertBefore(c(),t),t,void 0,s??{})}return h._$AI(t),h};export{j as _$LH,b as html,T as mathml,E as noChange,A as nothing,D as render,w as svg};\n//# sourceMappingURL=lit-html.js.map\n","import{ReactiveElement as t}from\"@lit/reactive-element\";export*from\"@lit/reactive-element\";import{render as e,noChange as r}from\"lit-html\";export*from\"lit-html\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const s=globalThis;class i extends t{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const r=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=e(r,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return r}}i._$litElement$=!0,i[\"finalized\"]=!0,s.litElementHydrateSupport?.({LitElement:i});const o=s.litElementPolyfillSupport;o?.({LitElement:i});const n={_$AK:(t,e,r)=>{t._$AK(e,r)},_$AL:t=>t._$AL};(s.litElementVersions??=[]).push(\"4.2.2\");export{i as LitElement,n as _$LE};\n//# sourceMappingURL=lit-element.js.map\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=t=>(e,o)=>{void 0!==o?o.addInitializer(()=>{customElements.define(t,e)}):customElements.define(t,e)};export{t as customElement};\n//# sourceMappingURL=custom-element.js.map\n","import{notEqual as t,defaultConverter as e}from\"../reactive-element.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const o={attribute:!0,type:String,converter:e,reflect:!1,hasChanged:t},r=(t=o,e,r)=>{const{kind:n,metadata:i}=r;let s=globalThis.litPropertyMetadata.get(i);if(void 0===s&&globalThis.litPropertyMetadata.set(i,s=new Map),\"setter\"===n&&((t=Object.create(t)).wrapped=!0),s.set(r.name,t),\"accessor\"===n){const{name:o}=r;return{set(r){const n=e.get.call(this);e.set.call(this,r),this.requestUpdate(o,n,t,!0,r)},init(e){return void 0!==e&&this.C(o,void 0,t,e),e}}}if(\"setter\"===n){const{name:o}=r;return function(r){const n=this[o];e.call(this,r),this.requestUpdate(o,n,t,!0,r)}}throw Error(\"Unsupported decorator location: \"+n)};function n(t){return(e,o)=>\"object\"==typeof o?r(t,e,o):((t,e,o)=>{const r=e.hasOwnProperty(o);return e.constructor.createProperty(o,t),r?Object.getOwnPropertyDescriptor(e,o):void 0})(t,e,o)}export{n as property,r as standardProperty};\n//# sourceMappingURL=property.js.map\n","import{property as t}from\"./property.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */function r(r){return t({...r,state:!0,attribute:!1})}export{r as state};\n//# sourceMappingURL=state.js.map\n","const MAX_ASSISTANT_NAME = 50;\nconst MAX_ASSISTANT_AVATAR = 200;\n\nexport const DEFAULT_ASSISTANT_NAME = \"Assistant\";\nexport const DEFAULT_ASSISTANT_AVATAR = \"A\";\n\nexport type AssistantIdentity = {\n agentId?: string | null;\n name: string;\n avatar: string | null;\n};\n\ndeclare global {\n interface Window {\n __CLAWDBOT_ASSISTANT_NAME__?: string;\n __CLAWDBOT_ASSISTANT_AVATAR__?: string;\n }\n}\n\nfunction coerceIdentityValue(value: string | undefined, maxLength: number): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n if (trimmed.length <= maxLength) return trimmed;\n return trimmed.slice(0, maxLength);\n}\n\nexport function normalizeAssistantIdentity(\n input?: Partial | null,\n): AssistantIdentity {\n const name =\n coerceIdentityValue(input?.name, MAX_ASSISTANT_NAME) ?? DEFAULT_ASSISTANT_NAME;\n const avatar = coerceIdentityValue(input?.avatar ?? undefined, MAX_ASSISTANT_AVATAR) ?? null;\n const agentId =\n typeof input?.agentId === \"string\" && input.agentId.trim()\n ? input.agentId.trim()\n : null;\n return { agentId, name, avatar };\n}\n\nexport function resolveInjectedAssistantIdentity(): AssistantIdentity {\n if (typeof window === \"undefined\") {\n return normalizeAssistantIdentity({});\n }\n return normalizeAssistantIdentity({\n name: window.__CLAWDBOT_ASSISTANT_NAME__,\n avatar: window.__CLAWDBOT_ASSISTANT_AVATAR__,\n });\n}\n","const KEY = \"clawdbot.control.settings.v1\";\n\nimport type { ThemeMode } from \"./theme\";\n\nexport type UiSettings = {\n gatewayUrl: string;\n token: string;\n sessionKey: string;\n lastActiveSessionKey: string;\n theme: ThemeMode;\n chatFocusMode: boolean;\n chatShowThinking: boolean;\n splitRatio: number; // Sidebar split ratio (0.4 to 0.7, default 0.6)\n navCollapsed: boolean; // Collapsible sidebar state\n navGroupsCollapsed: Record; // Which nav groups are collapsed\n};\n\nexport function loadSettings(): UiSettings {\n const defaultUrl = (() => {\n const proto = location.protocol === \"https:\" ? \"wss\" : \"ws\";\n return `${proto}://${location.host}`;\n })();\n\n const defaults: UiSettings = {\n gatewayUrl: defaultUrl,\n token: \"\",\n sessionKey: \"main\",\n lastActiveSessionKey: \"main\",\n theme: \"system\",\n chatFocusMode: false,\n chatShowThinking: true,\n splitRatio: 0.6,\n navCollapsed: false,\n navGroupsCollapsed: {},\n };\n\n try {\n const raw = localStorage.getItem(KEY);\n if (!raw) return defaults;\n const parsed = JSON.parse(raw) as Partial;\n return {\n gatewayUrl:\n typeof parsed.gatewayUrl === \"string\" && parsed.gatewayUrl.trim()\n ? parsed.gatewayUrl.trim()\n : defaults.gatewayUrl,\n token: typeof parsed.token === \"string\" ? parsed.token : defaults.token,\n sessionKey:\n typeof parsed.sessionKey === \"string\" && parsed.sessionKey.trim()\n ? parsed.sessionKey.trim()\n : defaults.sessionKey,\n lastActiveSessionKey:\n typeof parsed.lastActiveSessionKey === \"string\" &&\n parsed.lastActiveSessionKey.trim()\n ? parsed.lastActiveSessionKey.trim()\n : (typeof parsed.sessionKey === \"string\" &&\n parsed.sessionKey.trim()) ||\n defaults.lastActiveSessionKey,\n theme:\n parsed.theme === \"light\" ||\n parsed.theme === \"dark\" ||\n parsed.theme === \"system\"\n ? parsed.theme\n : defaults.theme,\n chatFocusMode:\n typeof parsed.chatFocusMode === \"boolean\"\n ? parsed.chatFocusMode\n : defaults.chatFocusMode,\n chatShowThinking:\n typeof parsed.chatShowThinking === \"boolean\"\n ? parsed.chatShowThinking\n : defaults.chatShowThinking,\n splitRatio:\n typeof parsed.splitRatio === \"number\" &&\n parsed.splitRatio >= 0.4 &&\n parsed.splitRatio <= 0.7\n ? parsed.splitRatio\n : defaults.splitRatio,\n navCollapsed:\n typeof parsed.navCollapsed === \"boolean\"\n ? parsed.navCollapsed\n : defaults.navCollapsed,\n navGroupsCollapsed:\n typeof parsed.navGroupsCollapsed === \"object\" &&\n parsed.navGroupsCollapsed !== null\n ? parsed.navGroupsCollapsed\n : defaults.navGroupsCollapsed,\n };\n } catch {\n return defaults;\n }\n}\n\nexport function saveSettings(next: UiSettings) {\n localStorage.setItem(KEY, JSON.stringify(next));\n}\n","export type ParsedAgentSessionKey = {\n agentId: string;\n rest: string;\n};\n\nexport function parseAgentSessionKey(\n sessionKey: string | undefined | null,\n): ParsedAgentSessionKey | null {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return null;\n const parts = raw.split(\":\").filter(Boolean);\n if (parts.length < 3) return null;\n if (parts[0] !== \"agent\") return null;\n const agentId = parts[1]?.trim();\n const rest = parts.slice(2).join(\":\");\n if (!agentId || !rest) return null;\n return { agentId, rest };\n}\n\nexport function isSubagentSessionKey(sessionKey: string | undefined | null): boolean {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return false;\n if (raw.toLowerCase().startsWith(\"subagent:\")) return true;\n const parsed = parseAgentSessionKey(raw);\n return Boolean((parsed?.rest ?? \"\").toLowerCase().startsWith(\"subagent:\"));\n}\n\nexport function isAcpSessionKey(sessionKey: string | undefined | null): boolean {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return false;\n const normalized = raw.toLowerCase();\n if (normalized.startsWith(\"acp:\")) return true;\n const parsed = parseAgentSessionKey(raw);\n return Boolean((parsed?.rest ?? \"\").toLowerCase().startsWith(\"acp:\"));\n}\n\nconst THREAD_SESSION_MARKERS = [\":thread:\", \":topic:\"];\n\nexport function resolveThreadParentSessionKey(\n sessionKey: string | undefined | null,\n): string | null {\n const raw = (sessionKey ?? \"\").trim();\n if (!raw) return null;\n const normalized = raw.toLowerCase();\n let idx = -1;\n for (const marker of THREAD_SESSION_MARKERS) {\n const candidate = normalized.lastIndexOf(marker);\n if (candidate > idx) idx = candidate;\n }\n if (idx <= 0) return null;\n const parent = raw.slice(0, idx).trim();\n return parent ? parent : null;\n}\n","export const TAB_GROUPS = [\n { label: \"Chat\", tabs: [\"chat\"] },\n {\n label: \"Control\",\n tabs: [\"overview\", \"channels\", \"instances\", \"sessions\", \"cron\"],\n },\n { label: \"Agent\", tabs: [\"skills\", \"nodes\"] },\n { label: \"Settings\", tabs: [\"config\", \"debug\", \"logs\"] },\n] as const;\n\nexport type Tab =\n | \"overview\"\n | \"channels\"\n | \"instances\"\n | \"sessions\"\n | \"cron\"\n | \"skills\"\n | \"nodes\"\n | \"chat\"\n | \"config\"\n | \"debug\"\n | \"logs\";\n\nconst TAB_PATHS: Record = {\n overview: \"/overview\",\n channels: \"/channels\",\n instances: \"/instances\",\n sessions: \"/sessions\",\n cron: \"/cron\",\n skills: \"/skills\",\n nodes: \"/nodes\",\n chat: \"/chat\",\n config: \"/config\",\n debug: \"/debug\",\n logs: \"/logs\",\n};\n\nconst PATH_TO_TAB = new Map(\n Object.entries(TAB_PATHS).map(([tab, path]) => [path, tab as Tab]),\n);\n\nexport function normalizeBasePath(basePath: string): string {\n if (!basePath) return \"\";\n let base = basePath.trim();\n if (!base.startsWith(\"/\")) base = `/${base}`;\n if (base === \"/\") return \"\";\n if (base.endsWith(\"/\")) base = base.slice(0, -1);\n return base;\n}\n\nexport function normalizePath(path: string): string {\n if (!path) return \"/\";\n let normalized = path.trim();\n if (!normalized.startsWith(\"/\")) normalized = `/${normalized}`;\n if (normalized.length > 1 && normalized.endsWith(\"/\")) {\n normalized = normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function pathForTab(tab: Tab, basePath = \"\"): string {\n const base = normalizeBasePath(basePath);\n const path = TAB_PATHS[tab];\n return base ? `${base}${path}` : path;\n}\n\nexport function tabFromPath(pathname: string, basePath = \"\"): Tab | null {\n const base = normalizeBasePath(basePath);\n let path = pathname || \"/\";\n if (base) {\n if (path === base) {\n path = \"/\";\n } else if (path.startsWith(`${base}/`)) {\n path = path.slice(base.length);\n }\n }\n let normalized = normalizePath(path).toLowerCase();\n if (normalized.endsWith(\"/index.html\")) normalized = \"/\";\n if (normalized === \"/\") return \"chat\";\n return PATH_TO_TAB.get(normalized) ?? null;\n}\n\nexport function inferBasePathFromPathname(pathname: string): string {\n let normalized = normalizePath(pathname);\n if (normalized.endsWith(\"/index.html\")) {\n normalized = normalizePath(normalized.slice(0, -\"/index.html\".length));\n }\n if (normalized === \"/\") return \"\";\n const segments = normalized.split(\"/\").filter(Boolean);\n if (segments.length === 0) return \"\";\n for (let i = 0; i < segments.length; i++) {\n const candidate = `/${segments.slice(i).join(\"/\")}`.toLowerCase();\n if (PATH_TO_TAB.has(candidate)) {\n const prefix = segments.slice(0, i);\n return prefix.length ? `/${prefix.join(\"/\")}` : \"\";\n }\n }\n return `/${segments.join(\"/\")}`;\n}\n\nexport function iconForTab(tab: Tab): string {\n switch (tab) {\n case \"chat\":\n return \"💬\";\n case \"overview\":\n return \"📊\";\n case \"channels\":\n return \"🔗\";\n case \"instances\":\n return \"📡\";\n case \"sessions\":\n return \"📄\";\n case \"cron\":\n return \"⏰\";\n case \"skills\":\n return \"⚡️\";\n case \"nodes\":\n return \"🖥️\";\n case \"config\":\n return \"⚙️\";\n case \"debug\":\n return \"🐞\";\n case \"logs\":\n return \"🧾\";\n default:\n return \"📁\";\n }\n}\n\nexport function titleForTab(tab: Tab) {\n switch (tab) {\n case \"overview\":\n return \"Overview\";\n case \"channels\":\n return \"Channels\";\n case \"instances\":\n return \"Instances\";\n case \"sessions\":\n return \"Sessions\";\n case \"cron\":\n return \"Cron Jobs\";\n case \"skills\":\n return \"Skills\";\n case \"nodes\":\n return \"Nodes\";\n case \"chat\":\n return \"Chat\";\n case \"config\":\n return \"Config\";\n case \"debug\":\n return \"Debug\";\n case \"logs\":\n return \"Logs\";\n default:\n return \"Control\";\n }\n}\n\nexport function subtitleForTab(tab: Tab) {\n switch (tab) {\n case \"overview\":\n return \"Gateway status, entry points, and a fast health read.\";\n case \"channels\":\n return \"Manage channels and settings.\";\n case \"instances\":\n return \"Presence beacons from connected clients and nodes.\";\n case \"sessions\":\n return \"Inspect active sessions and adjust per-session defaults.\";\n case \"cron\":\n return \"Schedule wakeups and recurring agent runs.\";\n case \"skills\":\n return \"Manage skill availability and API key injection.\";\n case \"nodes\":\n return \"Paired devices, capabilities, and command exposure.\";\n case \"chat\":\n return \"Direct gateway chat session for quick interventions.\";\n case \"config\":\n return \"Edit ~/.clawdbot/clawdbot.json safely.\";\n case \"debug\":\n return \"Gateway snapshots, events, and manual RPC calls.\";\n case \"logs\":\n return \"Live tail of the gateway file logs.\";\n default:\n return \"\";\n }\n}\n","export function formatMs(ms?: number | null): string {\n if (!ms && ms !== 0) return \"n/a\";\n return new Date(ms).toLocaleString();\n}\n\nexport function formatAgo(ms?: number | null): string {\n if (!ms && ms !== 0) return \"n/a\";\n const diff = Date.now() - ms;\n if (diff < 0) return \"just now\";\n const sec = Math.round(diff / 1000);\n if (sec < 60) return `${sec}s ago`;\n const min = Math.round(sec / 60);\n if (min < 60) return `${min}m ago`;\n const hr = Math.round(min / 60);\n if (hr < 48) return `${hr}h ago`;\n const day = Math.round(hr / 24);\n return `${day}d ago`;\n}\n\nexport function formatDurationMs(ms?: number | null): string {\n if (!ms && ms !== 0) return \"n/a\";\n if (ms < 1000) return `${ms}ms`;\n const sec = Math.round(ms / 1000);\n if (sec < 60) return `${sec}s`;\n const min = Math.round(sec / 60);\n if (min < 60) return `${min}m`;\n const hr = Math.round(min / 60);\n if (hr < 48) return `${hr}h`;\n const day = Math.round(hr / 24);\n return `${day}d`;\n}\n\nexport function formatList(values?: Array): string {\n if (!values || values.length === 0) return \"none\";\n return values.filter((v): v is string => Boolean(v && v.trim())).join(\", \");\n}\n\nexport function clampText(value: string, max = 120): string {\n if (value.length <= max) return value;\n return `${value.slice(0, Math.max(0, max - 1))}…`;\n}\n\nexport function truncateText(value: string, max: number): {\n text: string;\n truncated: boolean;\n total: number;\n} {\n if (value.length <= max) {\n return { text: value, truncated: false, total: value.length };\n }\n return {\n text: value.slice(0, Math.max(0, max)),\n truncated: true,\n total: value.length,\n };\n}\n\nexport function toNumber(value: string, fallback: number): number {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\nexport function parseList(input: string): string[] {\n return input\n .split(/[,\\n]/)\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n}\n\nconst THINKING_TAG_RE = /<\\s*\\/?\\s*think(?:ing)?\\s*>/gi;\nconst THINKING_OPEN_RE = /<\\s*think(?:ing)?\\s*>/i;\nconst THINKING_CLOSE_RE = /<\\s*\\/\\s*think(?:ing)?\\s*>/i;\n\nexport function stripThinkingTags(value: string): string {\n if (!value) return value;\n const hasOpen = THINKING_OPEN_RE.test(value);\n const hasClose = THINKING_CLOSE_RE.test(value);\n if (!hasOpen && !hasClose) return value;\n // If we don't have a balanced pair, avoid dropping trailing content.\n if (hasOpen !== hasClose) {\n if (!hasOpen) return value.replace(THINKING_CLOSE_RE, \"\").trimStart();\n return value.replace(THINKING_OPEN_RE, \"\").trimStart();\n }\n\n if (!THINKING_TAG_RE.test(value)) return value;\n THINKING_TAG_RE.lastIndex = 0;\n\n let result = \"\";\n let lastIndex = 0;\n let inThinking = false;\n for (const match of value.matchAll(THINKING_TAG_RE)) {\n const idx = match.index ?? 0;\n if (!inThinking) {\n result += value.slice(lastIndex, idx);\n }\n const tag = match[0].toLowerCase();\n inThinking = !tag.includes(\"/\");\n lastIndex = idx + match[0].length;\n }\n if (!inThinking) {\n result += value.slice(lastIndex);\n }\n return result.trimStart();\n}\n","import { stripThinkingTags } from \"../format\";\n\nconst ENVELOPE_PREFIX = /^\\[([^\\]]+)\\]\\s*/;\nconst ENVELOPE_CHANNELS = [\n \"WebChat\",\n \"WhatsApp\",\n \"Telegram\",\n \"Signal\",\n \"Slack\",\n \"Discord\",\n \"iMessage\",\n \"Teams\",\n \"Matrix\",\n \"Zalo\",\n \"Zalo Personal\",\n \"BlueBubbles\",\n];\n\nfunction looksLikeEnvelopeHeader(header: string): boolean {\n if (/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}Z\\b/.test(header)) return true;\n if (/\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}\\b/.test(header)) return true;\n return ENVELOPE_CHANNELS.some((label) => header.startsWith(`${label} `));\n}\n\nexport function stripEnvelope(text: string): string {\n const match = text.match(ENVELOPE_PREFIX);\n if (!match) return text;\n const header = match[1] ?? \"\";\n if (!looksLikeEnvelopeHeader(header)) return text;\n return text.slice(match[0].length);\n}\n\nexport function extractText(message: unknown): string | null {\n const m = message as Record;\n const role = typeof m.role === \"string\" ? m.role : \"\";\n const content = m.content;\n if (typeof content === \"string\") {\n const processed = role === \"assistant\" ? stripThinkingTags(content) : stripEnvelope(content);\n return processed;\n }\n if (Array.isArray(content)) {\n const parts = content\n .map((p) => {\n const item = p as Record;\n if (item.type === \"text\" && typeof item.text === \"string\") return item.text;\n return null;\n })\n .filter((v): v is string => typeof v === \"string\");\n if (parts.length > 0) {\n const joined = parts.join(\"\\n\");\n const processed = role === \"assistant\" ? stripThinkingTags(joined) : stripEnvelope(joined);\n return processed;\n }\n }\n if (typeof m.text === \"string\") {\n const processed = role === \"assistant\" ? stripThinkingTags(m.text) : stripEnvelope(m.text);\n return processed;\n }\n return null;\n}\n\nexport function extractThinking(message: unknown): string | null {\n const m = message as Record;\n const content = m.content;\n const parts: string[] = [];\n if (Array.isArray(content)) {\n for (const p of content) {\n const item = p as Record;\n if (item.type === \"thinking\" && typeof item.thinking === \"string\") {\n const cleaned = item.thinking.trim();\n if (cleaned) parts.push(cleaned);\n }\n }\n }\n if (parts.length > 0) return parts.join(\"\\n\");\n\n // Back-compat: older logs may still have tags inside text blocks.\n const rawText = extractRawText(message);\n if (!rawText) return null;\n const matches = [\n ...rawText.matchAll(\n /<\\s*think(?:ing)?\\s*>([\\s\\S]*?)<\\s*\\/\\s*think(?:ing)?\\s*>/gi,\n ),\n ];\n const extracted = matches\n .map((m) => (m[1] ?? \"\").trim())\n .filter(Boolean);\n return extracted.length > 0 ? extracted.join(\"\\n\") : null;\n}\n\nexport function extractRawText(message: unknown): string | null {\n const m = message as Record;\n const content = m.content;\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n const parts = content\n .map((p) => {\n const item = p as Record;\n if (item.type === \"text\" && typeof item.text === \"string\") return item.text;\n return null;\n })\n .filter((v): v is string => typeof v === \"string\");\n if (parts.length > 0) return parts.join(\"\\n\");\n }\n if (typeof m.text === \"string\") return m.text;\n return null;\n}\n\nexport function formatReasoningMarkdown(text: string): string {\n const trimmed = text.trim();\n if (!trimmed) return \"\";\n const lines = trimmed\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => `_${line}_`);\n return lines.length ? [\"_Reasoning:_\", ...lines].join(\"\\n\") : \"\";\n}\n","export type CryptoLike = {\n randomUUID?: (() => string) | undefined;\n getRandomValues?: ((array: Uint8Array) => Uint8Array) | undefined;\n};\n\nfunction uuidFromBytes(bytes: Uint8Array): string {\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1\n\n let hex = \"\";\n for (let i = 0; i < bytes.length; i++) {\n hex += bytes[i]!.toString(16).padStart(2, \"0\");\n }\n\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(\n 16,\n 20,\n )}-${hex.slice(20)}`;\n}\n\nfunction weakRandomBytes(): Uint8Array {\n const bytes = new Uint8Array(16);\n const now = Date.now();\n for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);\n bytes[0] ^= now & 0xff;\n bytes[1] ^= (now >>> 8) & 0xff;\n bytes[2] ^= (now >>> 16) & 0xff;\n bytes[3] ^= (now >>> 24) & 0xff;\n return bytes;\n}\n\nexport function generateUUID(cryptoLike: CryptoLike | null = globalThis.crypto): string {\n if (cryptoLike && typeof cryptoLike.randomUUID === \"function\") return cryptoLike.randomUUID();\n\n if (cryptoLike && typeof cryptoLike.getRandomValues === \"function\") {\n const bytes = new Uint8Array(16);\n cryptoLike.getRandomValues(bytes);\n return uuidFromBytes(bytes);\n }\n\n return uuidFromBytes(weakRandomBytes());\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { extractText } from \"../chat/message-extract\";\nimport { generateUUID } from \"../uuid\";\n\nexport type ChatState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n sessionKey: string;\n chatLoading: boolean;\n chatMessages: unknown[];\n chatThinkingLevel: string | null;\n chatSending: boolean;\n chatMessage: string;\n chatRunId: string | null;\n chatStream: string | null;\n chatStreamStartedAt: number | null;\n lastError: string | null;\n};\n\nexport type ChatEventPayload = {\n runId: string;\n sessionKey: string;\n state: \"delta\" | \"final\" | \"aborted\" | \"error\";\n message?: unknown;\n errorMessage?: string;\n};\n\nexport async function loadChatHistory(state: ChatState) {\n if (!state.client || !state.connected) return;\n state.chatLoading = true;\n state.lastError = null;\n try {\n const res = (await state.client.request(\"chat.history\", {\n sessionKey: state.sessionKey,\n limit: 200,\n })) as { messages?: unknown[]; thinkingLevel?: string | null };\n state.chatMessages = Array.isArray(res.messages) ? res.messages : [];\n state.chatThinkingLevel = res.thinkingLevel ?? null;\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.chatLoading = false;\n }\n}\n\nexport async function sendChatMessage(state: ChatState, message: string): Promise {\n if (!state.client || !state.connected) return false;\n const msg = message.trim();\n if (!msg) return false;\n\n const now = Date.now();\n state.chatMessages = [\n ...state.chatMessages,\n {\n role: \"user\",\n content: [{ type: \"text\", text: msg }],\n timestamp: now,\n },\n ];\n\n state.chatSending = true;\n state.lastError = null;\n const runId = generateUUID();\n state.chatRunId = runId;\n state.chatStream = \"\";\n state.chatStreamStartedAt = now;\n try {\n await state.client.request(\"chat.send\", {\n sessionKey: state.sessionKey,\n message: msg,\n deliver: false,\n idempotencyKey: runId,\n });\n return true;\n } catch (err) {\n const error = String(err);\n state.chatRunId = null;\n state.chatStream = null;\n state.chatStreamStartedAt = null;\n state.lastError = error;\n state.chatMessages = [\n ...state.chatMessages,\n {\n role: \"assistant\",\n content: [{ type: \"text\", text: \"Error: \" + error }],\n timestamp: Date.now(),\n },\n ];\n return false;\n } finally {\n state.chatSending = false;\n }\n}\n\nexport async function abortChatRun(state: ChatState): Promise {\n if (!state.client || !state.connected) return false;\n const runId = state.chatRunId;\n try {\n await state.client.request(\n \"chat.abort\",\n runId\n ? { sessionKey: state.sessionKey, runId }\n : { sessionKey: state.sessionKey },\n );\n return true;\n } catch (err) {\n state.lastError = String(err);\n return false;\n }\n}\n\nexport function handleChatEvent(\n state: ChatState,\n payload?: ChatEventPayload,\n) {\n if (!payload) return null;\n if (payload.sessionKey !== state.sessionKey) return null;\n if (payload.runId && state.chatRunId && payload.runId !== state.chatRunId)\n return null;\n\n if (payload.state === \"delta\") {\n const next = extractText(payload.message);\n if (typeof next === \"string\") {\n const current = state.chatStream ?? \"\";\n if (!current || next.length >= current.length) {\n state.chatStream = next;\n }\n }\n } else if (payload.state === \"final\") {\n state.chatStream = null;\n state.chatRunId = null;\n state.chatStreamStartedAt = null;\n } else if (payload.state === \"aborted\") {\n state.chatStream = null;\n state.chatRunId = null;\n state.chatStreamStartedAt = null;\n } else if (payload.state === \"error\") {\n state.chatStream = null;\n state.chatRunId = null;\n state.chatStreamStartedAt = null;\n state.lastError = payload.errorMessage ?? \"chat error\";\n }\n return payload.state;\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { toNumber } from \"../format\";\nimport type { SessionsListResult } from \"../types\";\n\nexport type SessionsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n sessionsLoading: boolean;\n sessionsResult: SessionsListResult | null;\n sessionsError: string | null;\n sessionsFilterActive: string;\n sessionsFilterLimit: string;\n sessionsIncludeGlobal: boolean;\n sessionsIncludeUnknown: boolean;\n};\n\nexport async function loadSessions(state: SessionsState) {\n if (!state.client || !state.connected) return;\n if (state.sessionsLoading) return;\n state.sessionsLoading = true;\n state.sessionsError = null;\n try {\n const params: Record = {\n includeGlobal: state.sessionsIncludeGlobal,\n includeUnknown: state.sessionsIncludeUnknown,\n };\n const activeMinutes = toNumber(state.sessionsFilterActive, 0);\n const limit = toNumber(state.sessionsFilterLimit, 0);\n if (activeMinutes > 0) params.activeMinutes = activeMinutes;\n if (limit > 0) params.limit = limit;\n const res = (await state.client.request(\"sessions.list\", params)) as\n | SessionsListResult\n | undefined;\n if (res) state.sessionsResult = res;\n } catch (err) {\n state.sessionsError = String(err);\n } finally {\n state.sessionsLoading = false;\n }\n}\n\nexport async function patchSession(\n state: SessionsState,\n key: string,\n patch: {\n label?: string | null;\n thinkingLevel?: string | null;\n verboseLevel?: string | null;\n reasoningLevel?: string | null;\n },\n) {\n if (!state.client || !state.connected) return;\n const params: Record = { key };\n if (\"label\" in patch) params.label = patch.label;\n if (\"thinkingLevel\" in patch) params.thinkingLevel = patch.thinkingLevel;\n if (\"verboseLevel\" in patch) params.verboseLevel = patch.verboseLevel;\n if (\"reasoningLevel\" in patch) params.reasoningLevel = patch.reasoningLevel;\n try {\n await state.client.request(\"sessions.patch\", params);\n await loadSessions(state);\n } catch (err) {\n state.sessionsError = String(err);\n }\n}\n\nexport async function deleteSession(state: SessionsState, key: string) {\n if (!state.client || !state.connected) return;\n if (state.sessionsLoading) return;\n const confirmed = window.confirm(\n `Delete session \"${key}\"?\\n\\nDeletes the session entry and archives its transcript.`,\n );\n if (!confirmed) return;\n state.sessionsLoading = true;\n state.sessionsError = null;\n try {\n await state.client.request(\"sessions.delete\", { key, deleteTranscript: true });\n await loadSessions(state);\n } catch (err) {\n state.sessionsError = String(err);\n } finally {\n state.sessionsLoading = false;\n }\n}\n","import { truncateText } from \"./format\";\n\nconst TOOL_STREAM_LIMIT = 50;\nconst TOOL_STREAM_THROTTLE_MS = 80;\nconst TOOL_OUTPUT_CHAR_LIMIT = 120_000;\n\nexport type AgentEventPayload = {\n runId: string;\n seq: number;\n stream: string;\n ts: number;\n sessionKey?: string;\n data: Record;\n};\n\nexport type ToolStreamEntry = {\n toolCallId: string;\n runId: string;\n sessionKey?: string;\n name: string;\n args?: unknown;\n output?: string;\n startedAt: number;\n updatedAt: number;\n message: Record;\n};\n\ntype ToolStreamHost = {\n sessionKey: string;\n chatRunId: string | null;\n toolStreamById: Map;\n toolStreamOrder: string[];\n chatToolMessages: Record[];\n toolStreamSyncTimer: number | null;\n};\n\nfunction extractToolOutputText(value: unknown): string | null {\n if (!value || typeof value !== \"object\") return null;\n const record = value as Record;\n if (typeof record.text === \"string\") return record.text;\n const content = record.content;\n if (!Array.isArray(content)) return null;\n const parts = content\n .map((item) => {\n if (!item || typeof item !== \"object\") return null;\n const entry = item as Record;\n if (entry.type === \"text\" && typeof entry.text === \"string\") return entry.text;\n return null;\n })\n .filter((part): part is string => Boolean(part));\n if (parts.length === 0) return null;\n return parts.join(\"\\n\");\n}\n\nfunction formatToolOutput(value: unknown): string | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n const contentText = extractToolOutputText(value);\n let text: string;\n if (typeof value === \"string\") {\n text = value;\n } else if (contentText) {\n text = contentText;\n } else {\n try {\n text = JSON.stringify(value, null, 2);\n } catch {\n text = String(value);\n }\n }\n const truncated = truncateText(text, TOOL_OUTPUT_CHAR_LIMIT);\n if (!truncated.truncated) return truncated.text;\n return `${truncated.text}\\n\\n… truncated (${truncated.total} chars, showing first ${truncated.text.length}).`;\n}\n\nfunction buildToolStreamMessage(entry: ToolStreamEntry): Record {\n const content: Array> = [];\n content.push({\n type: \"toolcall\",\n name: entry.name,\n arguments: entry.args ?? {},\n });\n if (entry.output) {\n content.push({\n type: \"toolresult\",\n name: entry.name,\n text: entry.output,\n });\n }\n return {\n role: \"assistant\",\n toolCallId: entry.toolCallId,\n runId: entry.runId,\n content,\n timestamp: entry.startedAt,\n };\n}\n\nfunction trimToolStream(host: ToolStreamHost) {\n if (host.toolStreamOrder.length <= TOOL_STREAM_LIMIT) return;\n const overflow = host.toolStreamOrder.length - TOOL_STREAM_LIMIT;\n const removed = host.toolStreamOrder.splice(0, overflow);\n for (const id of removed) host.toolStreamById.delete(id);\n}\n\nfunction syncToolStreamMessages(host: ToolStreamHost) {\n host.chatToolMessages = host.toolStreamOrder\n .map((id) => host.toolStreamById.get(id)?.message)\n .filter((msg): msg is Record => Boolean(msg));\n}\n\nexport function flushToolStreamSync(host: ToolStreamHost) {\n if (host.toolStreamSyncTimer != null) {\n clearTimeout(host.toolStreamSyncTimer);\n host.toolStreamSyncTimer = null;\n }\n syncToolStreamMessages(host);\n}\n\nexport function scheduleToolStreamSync(host: ToolStreamHost, force = false) {\n if (force) {\n flushToolStreamSync(host);\n return;\n }\n if (host.toolStreamSyncTimer != null) return;\n host.toolStreamSyncTimer = window.setTimeout(\n () => flushToolStreamSync(host),\n TOOL_STREAM_THROTTLE_MS,\n );\n}\n\nexport function resetToolStream(host: ToolStreamHost) {\n host.toolStreamById.clear();\n host.toolStreamOrder = [];\n host.chatToolMessages = [];\n flushToolStreamSync(host);\n}\n\nexport function handleAgentEvent(host: ToolStreamHost, payload?: AgentEventPayload) {\n if (!payload || payload.stream !== \"tool\") return;\n const sessionKey =\n typeof payload.sessionKey === \"string\" ? payload.sessionKey : undefined;\n if (sessionKey && sessionKey !== host.sessionKey) return;\n // Fallback: only accept session-less events for the active run.\n if (!sessionKey && host.chatRunId && payload.runId !== host.chatRunId) return;\n if (host.chatRunId && payload.runId !== host.chatRunId) return;\n if (!host.chatRunId) return;\n\n const data = payload.data ?? {};\n const toolCallId = typeof data.toolCallId === \"string\" ? data.toolCallId : \"\";\n if (!toolCallId) return;\n const name = typeof data.name === \"string\" ? data.name : \"tool\";\n const phase = typeof data.phase === \"string\" ? data.phase : \"\";\n const args = phase === \"start\" ? data.args : undefined;\n const output =\n phase === \"update\"\n ? formatToolOutput(data.partialResult)\n : phase === \"result\"\n ? formatToolOutput(data.result)\n : undefined;\n\n const now = Date.now();\n let entry = host.toolStreamById.get(toolCallId);\n if (!entry) {\n entry = {\n toolCallId,\n runId: payload.runId,\n sessionKey,\n name,\n args,\n output,\n startedAt: typeof payload.ts === \"number\" ? payload.ts : now,\n updatedAt: now,\n message: {},\n };\n host.toolStreamById.set(toolCallId, entry);\n host.toolStreamOrder.push(toolCallId);\n } else {\n entry.name = name;\n if (args !== undefined) entry.args = args;\n if (output !== undefined) entry.output = output;\n entry.updatedAt = now;\n }\n\n entry.message = buildToolStreamMessage(entry);\n trimToolStream(host);\n scheduleToolStreamSync(host, phase === \"result\");\n}\n","type ScrollHost = {\n updateComplete: Promise;\n querySelector: (selectors: string) => Element | null;\n style: CSSStyleDeclaration;\n chatScrollFrame: number | null;\n chatScrollTimeout: number | null;\n chatHasAutoScrolled: boolean;\n chatUserNearBottom: boolean;\n logsScrollFrame: number | null;\n logsAtBottom: boolean;\n topbarObserver: ResizeObserver | null;\n};\n\nexport function scheduleChatScroll(host: ScrollHost, force = false) {\n if (host.chatScrollFrame) cancelAnimationFrame(host.chatScrollFrame);\n if (host.chatScrollTimeout != null) {\n clearTimeout(host.chatScrollTimeout);\n host.chatScrollTimeout = null;\n }\n const pickScrollTarget = () => {\n const container = host.querySelector(\".chat-thread\") as HTMLElement | null;\n if (container) {\n const overflowY = getComputedStyle(container).overflowY;\n const canScroll =\n overflowY === \"auto\" ||\n overflowY === \"scroll\" ||\n container.scrollHeight - container.clientHeight > 1;\n if (canScroll) return container;\n }\n return (document.scrollingElement ?? document.documentElement) as HTMLElement | null;\n };\n // Wait for Lit render to complete, then scroll\n void host.updateComplete.then(() => {\n host.chatScrollFrame = requestAnimationFrame(() => {\n host.chatScrollFrame = null;\n const target = pickScrollTarget();\n if (!target) return;\n const distanceFromBottom =\n target.scrollHeight - target.scrollTop - target.clientHeight;\n const shouldStick = force || host.chatUserNearBottom || distanceFromBottom < 200;\n if (!shouldStick) return;\n if (force) host.chatHasAutoScrolled = true;\n target.scrollTop = target.scrollHeight;\n host.chatUserNearBottom = true;\n const retryDelay = force ? 150 : 120;\n host.chatScrollTimeout = window.setTimeout(() => {\n host.chatScrollTimeout = null;\n const latest = pickScrollTarget();\n if (!latest) return;\n const latestDistanceFromBottom =\n latest.scrollHeight - latest.scrollTop - latest.clientHeight;\n const shouldStickRetry =\n force || host.chatUserNearBottom || latestDistanceFromBottom < 200;\n if (!shouldStickRetry) return;\n latest.scrollTop = latest.scrollHeight;\n host.chatUserNearBottom = true;\n }, retryDelay);\n });\n });\n}\n\nexport function scheduleLogsScroll(host: ScrollHost, force = false) {\n if (host.logsScrollFrame) cancelAnimationFrame(host.logsScrollFrame);\n void host.updateComplete.then(() => {\n host.logsScrollFrame = requestAnimationFrame(() => {\n host.logsScrollFrame = null;\n const container = host.querySelector(\".log-stream\") as HTMLElement | null;\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n const shouldStick = force || distanceFromBottom < 80;\n if (!shouldStick) return;\n container.scrollTop = container.scrollHeight;\n });\n });\n}\n\nexport function handleChatScroll(host: ScrollHost, event: Event) {\n const container = event.currentTarget as HTMLElement | null;\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n host.chatUserNearBottom = distanceFromBottom < 200;\n}\n\nexport function handleLogsScroll(host: ScrollHost, event: Event) {\n const container = event.currentTarget as HTMLElement | null;\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n host.logsAtBottom = distanceFromBottom < 80;\n}\n\nexport function resetChatScroll(host: ScrollHost) {\n host.chatHasAutoScrolled = false;\n host.chatUserNearBottom = true;\n}\n\nexport function exportLogs(lines: string[], label: string) {\n if (lines.length === 0) return;\n const blob = new Blob([`${lines.join(\"\\n\")}\\n`], { type: \"text/plain\" });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n const stamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, \"-\");\n anchor.href = url;\n anchor.download = `clawdbot-logs-${label}-${stamp}.log`;\n anchor.click();\n URL.revokeObjectURL(url);\n}\n\nexport function observeTopbar(host: ScrollHost) {\n if (typeof ResizeObserver === \"undefined\") return;\n const topbar = host.querySelector(\".topbar\");\n if (!topbar) return;\n const update = () => {\n const { height } = topbar.getBoundingClientRect();\n host.style.setProperty(\"--topbar-height\", `${height}px`);\n };\n update();\n host.topbarObserver = new ResizeObserver(() => update());\n host.topbarObserver.observe(topbar);\n}\n","export function cloneConfigObject(value: T): T {\n if (typeof structuredClone === \"function\") {\n return structuredClone(value);\n }\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nexport function serializeConfigForm(form: Record): string {\n return `${JSON.stringify(form, null, 2).trimEnd()}\\n`;\n}\n\nexport function setPathValue(\n obj: Record | unknown[],\n path: Array,\n value: unknown,\n) {\n if (path.length === 0) return;\n let current: Record | unknown[] = obj;\n for (let i = 0; i < path.length - 1; i += 1) {\n const key = path[i];\n const nextKey = path[i + 1];\n if (typeof key === \"number\") {\n if (!Array.isArray(current)) return;\n if (current[key] == null) {\n current[key] =\n typeof nextKey === \"number\" ? [] : ({} as Record);\n }\n current = current[key] as Record | unknown[];\n } else {\n if (typeof current !== \"object\" || current == null) return;\n const record = current as Record;\n if (record[key] == null) {\n record[key] =\n typeof nextKey === \"number\" ? [] : ({} as Record);\n }\n current = record[key] as Record | unknown[];\n }\n }\n const lastKey = path[path.length - 1];\n if (typeof lastKey === \"number\") {\n if (Array.isArray(current)) current[lastKey] = value;\n return;\n }\n if (typeof current === \"object\" && current != null) {\n (current as Record)[lastKey] = value;\n }\n}\n\nexport function removePathValue(\n obj: Record | unknown[],\n path: Array,\n) {\n if (path.length === 0) return;\n let current: Record | unknown[] = obj;\n for (let i = 0; i < path.length - 1; i += 1) {\n const key = path[i];\n if (typeof key === \"number\") {\n if (!Array.isArray(current)) return;\n current = current[key] as Record | unknown[];\n } else {\n if (typeof current !== \"object\" || current == null) return;\n current = (current as Record)[key] as\n | Record\n | unknown[];\n }\n if (current == null) return;\n }\n const lastKey = path[path.length - 1];\n if (typeof lastKey === \"number\") {\n if (Array.isArray(current)) current.splice(lastKey, 1);\n return;\n }\n if (typeof current === \"object\" && current != null) {\n delete (current as Record)[lastKey];\n }\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type {\n ConfigSchemaResponse,\n ConfigSnapshot,\n ConfigUiHints,\n} from \"../types\";\nimport {\n cloneConfigObject,\n removePathValue,\n serializeConfigForm,\n setPathValue,\n} from \"./config/form-utils\";\n\nexport type ConfigState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n applySessionKey: string;\n configLoading: boolean;\n configRaw: string;\n configValid: boolean | null;\n configIssues: unknown[];\n configSaving: boolean;\n configApplying: boolean;\n updateRunning: boolean;\n configSnapshot: ConfigSnapshot | null;\n configSchema: unknown | null;\n configSchemaVersion: string | null;\n configSchemaLoading: boolean;\n configUiHints: ConfigUiHints;\n configForm: Record | null;\n configFormOriginal: Record | null;\n configFormDirty: boolean;\n configFormMode: \"form\" | \"raw\";\n configSearchQuery: string;\n configActiveSection: string | null;\n configActiveSubsection: string | null;\n lastError: string | null;\n};\n\nexport async function loadConfig(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.configLoading = true;\n state.lastError = null;\n try {\n const res = (await state.client.request(\"config.get\", {})) as ConfigSnapshot;\n applyConfigSnapshot(state, res);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configLoading = false;\n }\n}\n\nexport async function loadConfigSchema(state: ConfigState) {\n if (!state.client || !state.connected) return;\n if (state.configSchemaLoading) return;\n state.configSchemaLoading = true;\n try {\n const res = (await state.client.request(\n \"config.schema\",\n {},\n )) as ConfigSchemaResponse;\n applyConfigSchema(state, res);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configSchemaLoading = false;\n }\n}\n\nexport function applyConfigSchema(\n state: ConfigState,\n res: ConfigSchemaResponse,\n) {\n state.configSchema = res.schema ?? null;\n state.configUiHints = res.uiHints ?? {};\n state.configSchemaVersion = res.version ?? null;\n}\n\nexport function applyConfigSnapshot(state: ConfigState, snapshot: ConfigSnapshot) {\n state.configSnapshot = snapshot;\n const rawFromSnapshot =\n typeof snapshot.raw === \"string\"\n ? snapshot.raw\n : snapshot.config && typeof snapshot.config === \"object\"\n ? serializeConfigForm(snapshot.config as Record)\n : state.configRaw;\n if (!state.configFormDirty || state.configFormMode === \"raw\") {\n state.configRaw = rawFromSnapshot;\n } else if (state.configForm) {\n state.configRaw = serializeConfigForm(state.configForm);\n } else {\n state.configRaw = rawFromSnapshot;\n }\n state.configValid = typeof snapshot.valid === \"boolean\" ? snapshot.valid : null;\n state.configIssues = Array.isArray(snapshot.issues) ? snapshot.issues : [];\n\n if (!state.configFormDirty) {\n state.configForm = cloneConfigObject(snapshot.config ?? {});\n state.configFormOriginal = cloneConfigObject(snapshot.config ?? {});\n }\n}\n\nexport async function saveConfig(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.configSaving = true;\n state.lastError = null;\n try {\n const raw =\n state.configFormMode === \"form\" && state.configForm\n ? serializeConfigForm(state.configForm)\n : state.configRaw;\n const baseHash = state.configSnapshot?.hash;\n if (!baseHash) {\n state.lastError = \"Config hash missing; reload and retry.\";\n return;\n }\n await state.client.request(\"config.set\", { raw, baseHash });\n state.configFormDirty = false;\n await loadConfig(state);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configSaving = false;\n }\n}\n\nexport async function applyConfig(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.configApplying = true;\n state.lastError = null;\n try {\n const raw =\n state.configFormMode === \"form\" && state.configForm\n ? serializeConfigForm(state.configForm)\n : state.configRaw;\n const baseHash = state.configSnapshot?.hash;\n if (!baseHash) {\n state.lastError = \"Config hash missing; reload and retry.\";\n return;\n }\n await state.client.request(\"config.apply\", {\n raw,\n baseHash,\n sessionKey: state.applySessionKey,\n });\n state.configFormDirty = false;\n await loadConfig(state);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.configApplying = false;\n }\n}\n\nexport async function runUpdate(state: ConfigState) {\n if (!state.client || !state.connected) return;\n state.updateRunning = true;\n state.lastError = null;\n try {\n await state.client.request(\"update.run\", {\n sessionKey: state.applySessionKey,\n });\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.updateRunning = false;\n }\n}\n\nexport function updateConfigFormValue(\n state: ConfigState,\n path: Array,\n value: unknown,\n) {\n const base = cloneConfigObject(\n state.configForm ?? state.configSnapshot?.config ?? {},\n );\n setPathValue(base, path, value);\n state.configForm = base;\n state.configFormDirty = true;\n if (state.configFormMode === \"form\") {\n state.configRaw = serializeConfigForm(base);\n }\n}\n\nexport function removeConfigFormValue(\n state: ConfigState,\n path: Array,\n) {\n const base = cloneConfigObject(\n state.configForm ?? state.configSnapshot?.config ?? {},\n );\n removePathValue(base, path);\n state.configForm = base;\n state.configFormDirty = true;\n if (state.configFormMode === \"form\") {\n state.configRaw = serializeConfigForm(base);\n }\n}\n","import { toNumber } from \"../format\";\nimport type { GatewayBrowserClient } from \"../gateway\";\nimport type { CronJob, CronRunLogEntry, CronStatus } from \"../types\";\nimport type { CronFormState } from \"../ui-types\";\n\nexport type CronState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n cronLoading: boolean;\n cronJobs: CronJob[];\n cronStatus: CronStatus | null;\n cronError: string | null;\n cronForm: CronFormState;\n cronRunsJobId: string | null;\n cronRuns: CronRunLogEntry[];\n cronBusy: boolean;\n};\n\nexport async function loadCronStatus(state: CronState) {\n if (!state.client || !state.connected) return;\n try {\n const res = (await state.client.request(\"cron.status\", {})) as CronStatus;\n state.cronStatus = res;\n } catch (err) {\n state.cronError = String(err);\n }\n}\n\nexport async function loadCronJobs(state: CronState) {\n if (!state.client || !state.connected) return;\n if (state.cronLoading) return;\n state.cronLoading = true;\n state.cronError = null;\n try {\n const res = (await state.client.request(\"cron.list\", {\n includeDisabled: true,\n })) as { jobs?: CronJob[] };\n state.cronJobs = Array.isArray(res.jobs) ? res.jobs : [];\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronLoading = false;\n }\n}\n\nexport function buildCronSchedule(form: CronFormState) {\n if (form.scheduleKind === \"at\") {\n const ms = Date.parse(form.scheduleAt);\n if (!Number.isFinite(ms)) throw new Error(\"Invalid run time.\");\n return { kind: \"at\" as const, atMs: ms };\n }\n if (form.scheduleKind === \"every\") {\n const amount = toNumber(form.everyAmount, 0);\n if (amount <= 0) throw new Error(\"Invalid interval amount.\");\n const unit = form.everyUnit;\n const mult = unit === \"minutes\" ? 60_000 : unit === \"hours\" ? 3_600_000 : 86_400_000;\n return { kind: \"every\" as const, everyMs: amount * mult };\n }\n const expr = form.cronExpr.trim();\n if (!expr) throw new Error(\"Cron expression required.\");\n return { kind: \"cron\" as const, expr, tz: form.cronTz.trim() || undefined };\n}\n\nexport function buildCronPayload(form: CronFormState) {\n if (form.payloadKind === \"systemEvent\") {\n const text = form.payloadText.trim();\n if (!text) throw new Error(\"System event text required.\");\n return { kind: \"systemEvent\" as const, text };\n }\n const message = form.payloadText.trim();\n if (!message) throw new Error(\"Agent message required.\");\n const payload: {\n kind: \"agentTurn\";\n message: string;\n deliver?: boolean;\n channel?: string;\n to?: string;\n timeoutSeconds?: number;\n } = { kind: \"agentTurn\", message };\n if (form.deliver) payload.deliver = true;\n if (form.channel) payload.channel = form.channel;\n if (form.to.trim()) payload.to = form.to.trim();\n const timeoutSeconds = toNumber(form.timeoutSeconds, 0);\n if (timeoutSeconds > 0) payload.timeoutSeconds = timeoutSeconds;\n return payload;\n}\n\nexport async function addCronJob(state: CronState) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n const schedule = buildCronSchedule(state.cronForm);\n const payload = buildCronPayload(state.cronForm);\n const agentId = state.cronForm.agentId.trim();\n const job = {\n name: state.cronForm.name.trim(),\n description: state.cronForm.description.trim() || undefined,\n agentId: agentId || undefined,\n enabled: state.cronForm.enabled,\n schedule,\n sessionTarget: state.cronForm.sessionTarget,\n wakeMode: state.cronForm.wakeMode,\n payload,\n isolation:\n state.cronForm.postToMainPrefix.trim() &&\n state.cronForm.sessionTarget === \"isolated\"\n ? { postToMainPrefix: state.cronForm.postToMainPrefix.trim() }\n : undefined,\n };\n if (!job.name) throw new Error(\"Name required.\");\n await state.client.request(\"cron.add\", job);\n state.cronForm = {\n ...state.cronForm,\n name: \"\",\n description: \"\",\n payloadText: \"\",\n };\n await loadCronJobs(state);\n await loadCronStatus(state);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function toggleCronJob(\n state: CronState,\n job: CronJob,\n enabled: boolean,\n) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n await state.client.request(\"cron.update\", { id: job.id, patch: { enabled } });\n await loadCronJobs(state);\n await loadCronStatus(state);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function runCronJob(state: CronState, job: CronJob) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n await state.client.request(\"cron.run\", { id: job.id, mode: \"force\" });\n await loadCronRuns(state, job.id);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function removeCronJob(state: CronState, job: CronJob) {\n if (!state.client || !state.connected || state.cronBusy) return;\n state.cronBusy = true;\n state.cronError = null;\n try {\n await state.client.request(\"cron.remove\", { id: job.id });\n if (state.cronRunsJobId === job.id) {\n state.cronRunsJobId = null;\n state.cronRuns = [];\n }\n await loadCronJobs(state);\n await loadCronStatus(state);\n } catch (err) {\n state.cronError = String(err);\n } finally {\n state.cronBusy = false;\n }\n}\n\nexport async function loadCronRuns(state: CronState, jobId: string) {\n if (!state.client || !state.connected) return;\n try {\n const res = (await state.client.request(\"cron.runs\", {\n id: jobId,\n limit: 50,\n })) as { entries?: CronRunLogEntry[] };\n state.cronRunsJobId = jobId;\n state.cronRuns = Array.isArray(res.entries) ? res.entries : [];\n } catch (err) {\n state.cronError = String(err);\n }\n}\n","import type { ChannelsStatusSnapshot } from \"../types\";\nimport type { ChannelsState } from \"./channels.types\";\n\nexport type { ChannelsState };\n\nexport async function loadChannels(state: ChannelsState, probe: boolean) {\n if (!state.client || !state.connected) return;\n if (state.channelsLoading) return;\n state.channelsLoading = true;\n state.channelsError = null;\n try {\n const res = (await state.client.request(\"channels.status\", {\n probe,\n timeoutMs: 8000,\n })) as ChannelsStatusSnapshot;\n state.channelsSnapshot = res;\n state.channelsLastSuccess = Date.now();\n } catch (err) {\n state.channelsError = String(err);\n } finally {\n state.channelsLoading = false;\n }\n}\n\nexport async function startWhatsAppLogin(state: ChannelsState, force: boolean) {\n if (!state.client || !state.connected || state.whatsappBusy) return;\n state.whatsappBusy = true;\n try {\n const res = (await state.client.request(\"web.login.start\", {\n force,\n timeoutMs: 30000,\n })) as { message?: string; qrDataUrl?: string };\n state.whatsappLoginMessage = res.message ?? null;\n state.whatsappLoginQrDataUrl = res.qrDataUrl ?? null;\n state.whatsappLoginConnected = null;\n } catch (err) {\n state.whatsappLoginMessage = String(err);\n state.whatsappLoginQrDataUrl = null;\n state.whatsappLoginConnected = null;\n } finally {\n state.whatsappBusy = false;\n }\n}\n\nexport async function waitWhatsAppLogin(state: ChannelsState) {\n if (!state.client || !state.connected || state.whatsappBusy) return;\n state.whatsappBusy = true;\n try {\n const res = (await state.client.request(\"web.login.wait\", {\n timeoutMs: 120000,\n })) as { connected?: boolean; message?: string };\n state.whatsappLoginMessage = res.message ?? null;\n state.whatsappLoginConnected = res.connected ?? null;\n if (res.connected) state.whatsappLoginQrDataUrl = null;\n } catch (err) {\n state.whatsappLoginMessage = String(err);\n state.whatsappLoginConnected = null;\n } finally {\n state.whatsappBusy = false;\n }\n}\n\nexport async function logoutWhatsApp(state: ChannelsState) {\n if (!state.client || !state.connected || state.whatsappBusy) return;\n state.whatsappBusy = true;\n try {\n await state.client.request(\"channels.logout\", { channel: \"whatsapp\" });\n state.whatsappLoginMessage = \"Logged out.\";\n state.whatsappLoginQrDataUrl = null;\n state.whatsappLoginConnected = null;\n } catch (err) {\n state.whatsappLoginMessage = String(err);\n } finally {\n state.whatsappBusy = false;\n }\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { HealthSnapshot, StatusSummary } from \"../types\";\n\nexport type DebugState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n debugLoading: boolean;\n debugStatus: StatusSummary | null;\n debugHealth: HealthSnapshot | null;\n debugModels: unknown[];\n debugHeartbeat: unknown | null;\n debugCallMethod: string;\n debugCallParams: string;\n debugCallResult: string | null;\n debugCallError: string | null;\n};\n\nexport async function loadDebug(state: DebugState) {\n if (!state.client || !state.connected) return;\n if (state.debugLoading) return;\n state.debugLoading = true;\n try {\n const [status, health, models, heartbeat] = await Promise.all([\n state.client.request(\"status\", {}),\n state.client.request(\"health\", {}),\n state.client.request(\"models.list\", {}),\n state.client.request(\"last-heartbeat\", {}),\n ]);\n state.debugStatus = status as StatusSummary;\n state.debugHealth = health as HealthSnapshot;\n const modelPayload = models as { models?: unknown[] } | undefined;\n state.debugModels = Array.isArray(modelPayload?.models)\n ? modelPayload?.models\n : [];\n state.debugHeartbeat = heartbeat as unknown;\n } catch (err) {\n state.debugCallError = String(err);\n } finally {\n state.debugLoading = false;\n }\n}\n\nexport async function callDebugMethod(state: DebugState) {\n if (!state.client || !state.connected) return;\n state.debugCallError = null;\n state.debugCallResult = null;\n try {\n const params = state.debugCallParams.trim()\n ? (JSON.parse(state.debugCallParams) as unknown)\n : {};\n const res = await state.client.request(state.debugCallMethod.trim(), params);\n state.debugCallResult = JSON.stringify(res, null, 2);\n } catch (err) {\n state.debugCallError = String(err);\n }\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { LogEntry, LogLevel } from \"../types\";\n\nexport type LogsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n logsLoading: boolean;\n logsError: string | null;\n logsCursor: number | null;\n logsFile: string | null;\n logsEntries: LogEntry[];\n logsTruncated: boolean;\n logsLastFetchAt: number | null;\n logsLimit: number;\n logsMaxBytes: number;\n};\n\nconst LOG_BUFFER_LIMIT = 2000;\nconst LEVELS = new Set([\n \"trace\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\",\n \"fatal\",\n]);\n\nfunction parseMaybeJsonString(value: unknown) {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"{\") || !trimmed.endsWith(\"}\")) return null;\n try {\n const parsed = JSON.parse(trimmed) as unknown;\n if (!parsed || typeof parsed !== \"object\") return null;\n return parsed as Record;\n } catch {\n return null;\n }\n}\n\nfunction normalizeLevel(value: unknown): LogLevel | null {\n if (typeof value !== \"string\") return null;\n const lowered = value.toLowerCase() as LogLevel;\n return LEVELS.has(lowered) ? lowered : null;\n}\n\nexport function parseLogLine(line: string): LogEntry {\n if (!line.trim()) return { raw: line, message: line };\n try {\n const obj = JSON.parse(line) as Record;\n const meta =\n obj && typeof obj._meta === \"object\" && obj._meta !== null\n ? (obj._meta as Record)\n : null;\n const time =\n typeof obj.time === \"string\"\n ? obj.time\n : typeof meta?.date === \"string\"\n ? meta?.date\n : null;\n const level = normalizeLevel(meta?.logLevelName ?? meta?.level);\n\n const contextCandidate =\n typeof obj[\"0\"] === \"string\"\n ? (obj[\"0\"] as string)\n : typeof meta?.name === \"string\"\n ? (meta?.name as string)\n : null;\n const contextObj = parseMaybeJsonString(contextCandidate);\n let subsystem: string | null = null;\n if (contextObj) {\n if (typeof contextObj.subsystem === \"string\") subsystem = contextObj.subsystem;\n else if (typeof contextObj.module === \"string\") subsystem = contextObj.module;\n }\n if (!subsystem && contextCandidate && contextCandidate.length < 120) {\n subsystem = contextCandidate;\n }\n\n let message: string | null = null;\n if (typeof obj[\"1\"] === \"string\") message = obj[\"1\"] as string;\n else if (!contextObj && typeof obj[\"0\"] === \"string\") message = obj[\"0\"] as string;\n else if (typeof obj.message === \"string\") message = obj.message as string;\n\n return {\n raw: line,\n time,\n level,\n subsystem,\n message: message ?? line,\n meta: meta ?? undefined,\n };\n } catch {\n return { raw: line, message: line };\n }\n}\n\nexport async function loadLogs(\n state: LogsState,\n opts?: { reset?: boolean; quiet?: boolean },\n) {\n if (!state.client || !state.connected) return;\n if (state.logsLoading && !opts?.quiet) return;\n if (!opts?.quiet) state.logsLoading = true;\n state.logsError = null;\n try {\n const res = await state.client.request(\"logs.tail\", {\n cursor: opts?.reset ? undefined : state.logsCursor ?? undefined,\n limit: state.logsLimit,\n maxBytes: state.logsMaxBytes,\n });\n const payload = res as {\n file?: string;\n cursor?: number;\n size?: number;\n lines?: unknown;\n truncated?: boolean;\n reset?: boolean;\n };\n const lines = Array.isArray(payload.lines)\n ? (payload.lines.filter((line) => typeof line === \"string\") as string[])\n : [];\n const entries = lines.map(parseLogLine);\n const shouldReset = Boolean(opts?.reset || payload.reset || state.logsCursor == null);\n state.logsEntries = shouldReset\n ? entries\n : [...state.logsEntries, ...entries].slice(-LOG_BUFFER_LIMIT);\n if (typeof payload.cursor === \"number\") state.logsCursor = payload.cursor;\n if (typeof payload.file === \"string\") state.logsFile = payload.file;\n state.logsTruncated = Boolean(payload.truncated);\n state.logsLastFetchAt = Date.now();\n } catch (err) {\n state.logsError = String(err);\n } finally {\n if (!opts?.quiet) state.logsLoading = false;\n }\n}\n","/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */\n/**\n * 5KB JS implementation of ed25519 EdDSA signatures.\n * Compliant with RFC8032, FIPS 186-5 & ZIP215.\n * @module\n * @example\n * ```js\nimport * as ed from '@noble/ed25519';\n(async () => {\n const secretKey = ed.utils.randomSecretKey();\n const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);\n const pubKey = await ed.getPublicKeyAsync(secretKey); // Sync methods are also present\n const signature = await ed.signAsync(message, secretKey);\n const isValid = await ed.verifyAsync(signature, message, pubKey);\n})();\n```\n */\n/**\n * Curve params. ed25519 is twisted edwards curve. Equation is −x² + y² = -a + dx²y².\n * * P = `2n**255n - 19n` // field over which calculations are done\n * * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points\n * * h = 8 // cofactor\n * * a = `Fp.create(BigInt(-1))` // equation param\n * * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param\n * * Gx, Gy are coordinates of Generator / base point\n */\nconst ed25519_CURVE = {\n p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,\n n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,\n h: 8n,\n a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,\n d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,\n Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,\n Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,\n};\nconst { p: P, n: N, Gx, Gy, a: _a, d: _d, h } = ed25519_CURVE;\nconst L = 32; // field / group byte length\nconst L2 = 64;\n// Helpers and Precomputes sections are reused between libraries\n// ## Helpers\n// ----------\nconst captureTrace = (...args) => {\n if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(...args);\n }\n};\nconst err = (message = '') => {\n const e = new Error(message);\n captureTrace(e, err);\n throw e;\n};\nconst isBig = (n) => typeof n === 'bigint'; // is big integer\nconst isStr = (s) => typeof s === 'string'; // is string\nconst isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n/** Asserts something is Uint8Array. */\nconst abytes = (value, length, title = '') => {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n err(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);\n }\n return value;\n};\n/** create Uint8Array */\nconst u8n = (len) => new Uint8Array(len);\nconst u8fr = (buf) => Uint8Array.from(buf);\nconst padh = (n, pad) => n.toString(16).padStart(pad, '0');\nconst bytesToHex = (b) => Array.from(abytes(b))\n .map((e) => padh(e, 2))\n .join('');\nconst C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters\nconst _ch = (ch) => {\n if (ch >= C._0 && ch <= C._9)\n return ch - C._0; // '2' => 50-48\n if (ch >= C.A && ch <= C.F)\n return ch - (C.A - 10); // 'B' => 66-(65-10)\n if (ch >= C.a && ch <= C.f)\n return ch - (C.a - 10); // 'b' => 98-(97-10)\n return;\n};\nconst hexToBytes = (hex) => {\n const e = 'hex invalid';\n if (!isStr(hex))\n return err(e);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2)\n return err(e);\n const array = u8n(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n // treat each char as ASCII\n const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16\n const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char\n if (n1 === undefined || n2 === undefined)\n return err(e);\n array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9\n }\n return array;\n};\nconst cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments\nconst subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined, consider polyfill');\n// prettier-ignore\nconst concatBytes = (...arrs) => {\n const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); // create u8a of summed length\n let pad = 0; // walk through each array,\n arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type\n return r;\n};\n/** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */\nconst randomBytes = (len = L) => {\n const c = cr();\n return c.getRandomValues(u8n(len));\n};\nconst big = BigInt;\nconst assertRange = (n, min, max, msg = 'bad number: out of range') => (isBig(n) && min <= n && n < max ? n : err(msg));\n/** modular division */\nconst M = (a, b = P) => {\n const r = a % b;\n return r >= 0n ? r : b + r;\n};\nconst modN = (a) => M(a, N);\n/** Modular inversion using euclidean GCD (non-CT). No negative exponent for now. */\n// prettier-ignore\nconst invert = (num, md) => {\n if (num === 0n || md <= 0n)\n err('no inverse n=' + num + ' mod=' + md);\n let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;\n while (a !== 0n) {\n const q = b / a, r = b % a;\n const m = x - u * q, n = y - v * q;\n b = a, a = r, x = u, y = v, u = m, v = n;\n }\n return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point\n};\nconst callHash = (name) => {\n // @ts-ignore\n const fn = hashes[name];\n if (typeof fn !== 'function')\n err('hashes.' + name + ' not set');\n return fn;\n};\nconst hash = (msg) => callHash('sha512')(msg);\nconst apoint = (p) => (p instanceof Point ? p : err('Point expected'));\n// ## End of Helpers\n// -----------------\nconst B256 = 2n ** 256n;\n/** Point in XYZT extended coordinates. */\nclass Point {\n static BASE;\n static ZERO;\n X;\n Y;\n Z;\n T;\n constructor(X, Y, Z, T) {\n const max = B256;\n this.X = assertRange(X, 0n, max);\n this.Y = assertRange(Y, 0n, max);\n this.Z = assertRange(Z, 1n, max);\n this.T = assertRange(T, 0n, max);\n Object.freeze(this);\n }\n static CURVE() {\n return ed25519_CURVE;\n }\n static fromAffine(p) {\n return new Point(p.x, p.y, 1n, M(p.x * p.y));\n }\n /** RFC8032 5.1.3: Uint8Array to Point. */\n static fromBytes(hex, zip215 = false) {\n const d = _d;\n // Copy array to not mess it up.\n const normed = u8fr(abytes(hex, L));\n // adjust first LE byte = last BE byte\n const lastByte = hex[31];\n normed[31] = lastByte & ~0x80;\n const y = bytesToNumLE(normed);\n // zip215=true: 0 <= y < 2^256\n // zip215=false, RFC8032: 0 <= y < 2^255-19\n const max = zip215 ? B256 : P;\n assertRange(y, 0n, max);\n const y2 = M(y * y); // y²\n const u = M(y2 - 1n); // u=y²-1\n const v = M(d * y2 + 1n); // v=dy²+1\n let { isValid, value: x } = uvRatio(u, v); // (uv³)(uv⁷)^(p-5)/8; square root\n if (!isValid)\n err('bad point: y not sqrt'); // not square root: bad point\n const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate\n const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit\n if (!zip215 && x === 0n && isLastByteOdd)\n err('bad point: x==0, isLastByteOdd'); // x=0, x_0=1\n if (isLastByteOdd !== isXOdd)\n x = M(-x);\n return new Point(x, y, 1n, M(x * y)); // Z=1, T=xy\n }\n static fromHex(hex, zip215) {\n return Point.fromBytes(hexToBytes(hex), zip215);\n }\n get x() {\n return this.toAffine().x;\n }\n get y() {\n return this.toAffine().y;\n }\n /** Checks if the point is valid and on-curve. */\n assertValidity() {\n const a = _a;\n const d = _d;\n const p = this;\n if (p.is0())\n return err('bad point: ZERO'); // TODO: optimize, with vars below?\n // Equation in affine coordinates: ax² + y² = 1 + dx²y²\n // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²\n const { X, Y, Z, T } = p;\n const X2 = M(X * X); // X²\n const Y2 = M(Y * Y); // Y²\n const Z2 = M(Z * Z); // Z²\n const Z4 = M(Z2 * Z2); // Z⁴\n const aX2 = M(X2 * a); // aX²\n const left = M(Z2 * M(aX2 + Y2)); // (aX² + Y²)Z²\n const right = M(Z4 + M(d * M(X2 * Y2))); // Z⁴ + dX²Y²\n if (left !== right)\n return err('bad point: equation left != right (1)');\n // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T\n const XY = M(X * Y);\n const ZT = M(Z * T);\n if (XY !== ZT)\n return err('bad point: equation left != right (2)');\n return this;\n }\n /** Equality check: compare points P&Q. */\n equals(other) {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality\n const X1Z2 = M(X1 * Z2);\n const X2Z1 = M(X2 * Z1);\n const Y1Z2 = M(Y1 * Z2);\n const Y2Z1 = M(Y2 * Z1);\n return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;\n }\n is0() {\n return this.equals(I);\n }\n /** Flip point over y coordinate. */\n negate() {\n return new Point(M(-this.X), this.Y, this.Z, M(-this.T));\n }\n /** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */\n double() {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const a = _a;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd\n const A = M(X1 * X1);\n const B = M(Y1 * Y1);\n const C = M(2n * M(Z1 * Z1));\n const D = M(a * A);\n const x1y1 = X1 + Y1;\n const E = M(M(x1y1 * x1y1) - A - B);\n const G = D + B;\n const F = G - C;\n const H = D - B;\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n /** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */\n add(other) {\n const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;\n const { X: X2, Y: Y2, Z: Z2, T: T2 } = apoint(other); // doesn't check if other on-curve\n const a = _a;\n const d = _d;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3\n const A = M(X1 * X2);\n const B = M(Y1 * Y2);\n const C = M(T1 * d * T2);\n const D = M(Z1 * Z2);\n const E = M((X1 + Y1) * (X2 + Y2) - A - B);\n const F = M(D - C);\n const G = M(D + C);\n const H = M(B - a * A);\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n subtract(other) {\n return this.add(apoint(other).negate());\n }\n /**\n * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.\n * Uses {@link wNAF} for base point.\n * Uses fake point to mitigate side-channel leakage.\n * @param n scalar by which point is multiplied\n * @param safe safe mode guards against timing attacks; unsafe mode is faster\n */\n multiply(n, safe = true) {\n if (!safe && (n === 0n || this.is0()))\n return I;\n assertRange(n, 1n, N);\n if (n === 1n)\n return this;\n if (this.equals(G))\n return wNAF(n).p;\n // init result point & fake point\n let p = I;\n let f = G;\n for (let d = this; n > 0n; d = d.double(), n >>= 1n) {\n // if bit is present, add to point\n // if not present, add to fake, for timing safety\n if (n & 1n)\n p = p.add(d);\n else if (safe)\n f = f.add(d);\n }\n return p;\n }\n multiplyUnsafe(scalar) {\n return this.multiply(scalar, false);\n }\n /** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */\n toAffine() {\n const { X, Y, Z } = this;\n // fast-paths for ZERO point OR Z=1\n if (this.equals(I))\n return { x: 0n, y: 1n };\n const iz = invert(Z, P);\n // (Z * Z^-1) must be 1, otherwise bad math\n if (M(Z * iz) !== 1n)\n err('invalid inverse');\n // x = X*Z^-1; y = Y*Z^-1\n const x = M(X * iz);\n const y = M(Y * iz);\n return { x, y };\n }\n toBytes() {\n const { x, y } = this.assertValidity().toAffine();\n const b = numTo32bLE(y);\n // store sign in first LE byte\n b[31] |= x & 1n ? 0x80 : 0;\n return b;\n }\n toHex() {\n return bytesToHex(this.toBytes());\n }\n clearCofactor() {\n return this.multiply(big(h), false);\n }\n isSmallOrder() {\n return this.clearCofactor().is0();\n }\n isTorsionFree() {\n // Multiply by big number N. We can't `mul(N)` because of checks. Instead, we `mul(N/2)*2+1`\n let p = this.multiply(N / 2n, false).double();\n if (N % 2n)\n p = p.add(this);\n return p.is0();\n }\n}\n/** Generator / base point */\nconst G = new Point(Gx, Gy, 1n, M(Gx * Gy));\n/** Identity / zero point */\nconst I = new Point(0n, 1n, 1n, 0n);\n// Static aliases\nPoint.BASE = G;\nPoint.ZERO = I;\nconst numTo32bLE = (num) => hexToBytes(padh(assertRange(num, 0n, B256), L2)).reverse();\nconst bytesToNumLE = (b) => big('0x' + bytesToHex(u8fr(abytes(b)).reverse()));\nconst pow2 = (x, power) => {\n // pow2(x, 4) == x^(2^4)\n let r = x;\n while (power-- > 0n) {\n r *= r;\n r %= P;\n }\n return r;\n};\n// prettier-ignore\nconst pow_2_252_3 = (x) => {\n const x2 = (x * x) % P; // x^2, bits 1\n const b2 = (x2 * x) % P; // x^3, bits 11\n const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111\n const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111\n const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)\n const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)\n const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)\n const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)\n const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)\n const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)\n const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)\n const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.\n return { pow_p_5_8, b2 };\n};\nconst RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; // √-1\n// for sqrt comp\n// prettier-ignore\nconst uvRatio = (u, v) => {\n const v3 = M(v * v * v); // v³\n const v7 = M(v3 * v3 * v); // v⁷\n const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv⁷)^(p-5)/8\n let x = M(u * v3 * pow); // (uv³)(uv⁷)^(p-5)/8\n const vx2 = M(v * x * x); // vx²\n const root1 = x; // First root candidate\n const root2 = M(x * RM1); // Second root candidate; RM1 is √-1\n const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root\n const useRoot2 = vx2 === M(-u); // If vx² = -u, set x <-- x * 2^((p-1)/4)\n const noRoot = vx2 === M(-u * RM1); // There is no valid root, vx² = -u√-1\n if (useRoot1)\n x = root1;\n if (useRoot2 || noRoot)\n x = root2; // We return root2 anyway, for const-time\n if ((M(x) & 1n) === 1n)\n x = M(-x); // edIsNegative\n return { isValid: useRoot1 || useRoot2, value: x };\n};\n// N == L, just weird naming\nconst modL_LE = (hash) => modN(bytesToNumLE(hash)); // modulo L; but little-endian\n/** hashes.sha512 should conform to the interface. */\n// TODO: rename\nconst sha512a = (...m) => hashes.sha512Async(concatBytes(...m)); // Async SHA512\nconst sha512s = (...m) => callHash('sha512')(concatBytes(...m));\n// RFC8032 5.1.5\nconst hash2extK = (hashed) => {\n // slice creates a copy, unlike subarray\n const head = hashed.slice(0, L);\n head[0] &= 248; // Clamp bits: 0b1111_1000\n head[31] &= 127; // 0b0111_1111\n head[31] |= 64; // 0b0100_0000\n const prefix = hashed.slice(L, L2); // secret key \"prefix\"\n const scalar = modL_LE(head); // modular division over curve order\n const point = G.multiply(scalar); // public key point\n const pointBytes = point.toBytes(); // point serialized to Uint8Array\n return { head, prefix, scalar, point, pointBytes };\n};\n// RFC8032 5.1.5; getPublicKey async, sync. Hash priv key and extract point.\nconst getExtendedPublicKeyAsync = (secretKey) => sha512a(abytes(secretKey, L)).then(hash2extK);\nconst getExtendedPublicKey = (secretKey) => hash2extK(sha512s(abytes(secretKey, L)));\n/** Creates 32-byte ed25519 public key from 32-byte secret key. Async. */\nconst getPublicKeyAsync = (secretKey) => getExtendedPublicKeyAsync(secretKey).then((p) => p.pointBytes);\n/** Creates 32-byte ed25519 public key from 32-byte secret key. To use, set `hashes.sha512` first. */\nconst getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;\nconst hashFinishA = (res) => sha512a(res.hashable).then(res.finish);\nconst hashFinishS = (res) => res.finish(sha512s(res.hashable));\n// Code, shared between sync & async sign\nconst _sign = (e, rBytes, msg) => {\n const { pointBytes: P, scalar: s } = e;\n const r = modL_LE(rBytes); // r was created outside, reduce it modulo L\n const R = G.multiply(r).toBytes(); // R = [r]B\n const hashable = concatBytes(R, P, msg); // dom2(F, C) || R || A || PH(M)\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n const S = modN(r + modL_LE(hashed) * s); // S = (r + k * s) mod L; 0 <= s < l\n return abytes(concatBytes(R, numTo32bLE(S)), L2); // 64-byte sig: 32b R.x + 32b LE(S)\n };\n return { hashable, finish };\n};\n/**\n * Signs message using secret key. Async.\n * Follows RFC8032 5.1.6.\n */\nconst signAsync = async (message, secretKey) => {\n const m = abytes(message);\n const e = await getExtendedPublicKeyAsync(secretKey);\n const rBytes = await sha512a(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishA(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\n/**\n * Signs message using secret key. To use, set `hashes.sha512` first.\n * Follows RFC8032 5.1.6.\n */\nconst sign = (message, secretKey) => {\n const m = abytes(message);\n const e = getExtendedPublicKey(secretKey);\n const rBytes = sha512s(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishS(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\nconst defaultVerifyOpts = { zip215: true };\nconst _verify = (sig, msg, pub, opts = defaultVerifyOpts) => {\n sig = abytes(sig, L2); // Signature hex str/Bytes, must be 64 bytes\n msg = abytes(msg); // Message hex str/Bytes\n pub = abytes(pub, L);\n const { zip215 } = opts; // switch between zip215 and rfc8032 verif\n let A;\n let R;\n let s;\n let SB;\n let hashable = Uint8Array.of();\n try {\n A = Point.fromBytes(pub, zip215); // public key A decoded\n R = Point.fromBytes(sig.slice(0, L), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P\n s = bytesToNumLE(sig.slice(L, L2)); // Decode second half as an integer S\n SB = G.multiply(s, false); // in the range 0 <= s < L\n hashable = concatBytes(R.toBytes(), A.toBytes(), msg); // dom2(F, C) || R || A || PH(M)\n }\n catch (error) { }\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n if (SB == null)\n return false; // false if try-catch catched an error\n if (!zip215 && A.isSmallOrder())\n return false; // false for SBS: Strongly Binding Signature\n const k = modL_LE(hashed); // decode in little-endian, modulo L\n const RkA = R.add(A.multiply(k, false)); // [8]R + [8][k]A'\n return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A'\n };\n return { hashable, finish };\n};\n/** Verifies signature on message and public key. Async. Follows RFC8032 5.1.7. */\nconst verifyAsync = async (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishA(_verify(signature, message, publicKey, opts));\n/** Verifies signature on message and public key. To use, set `hashes.sha512` first. Follows RFC8032 5.1.7. */\nconst verify = (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishS(_verify(signature, message, publicKey, opts));\n/** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */\nconst etc = {\n bytesToHex: bytesToHex,\n hexToBytes: hexToBytes,\n concatBytes: concatBytes,\n mod: M,\n invert: invert,\n randomBytes: randomBytes,\n};\nconst hashes = {\n sha512Async: async (message) => {\n const s = subtle();\n const m = concatBytes(message);\n return u8n(await s.digest('SHA-512', m.buffer));\n },\n sha512: undefined,\n};\n// FIPS 186 B.4.1 compliant key generation produces private keys\n// with modulo bias being neglible. takes >N+16 bytes, returns (hash mod n-1)+1\nconst randomSecretKey = (seed = randomBytes(L)) => seed;\nconst keygen = (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = getPublicKey(secretKey);\n return { secretKey, publicKey };\n};\nconst keygenAsync = async (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = await getPublicKeyAsync(secretKey);\n return { secretKey, publicKey };\n};\n/** ed25519-specific key utilities. */\nconst utils = {\n getExtendedPublicKeyAsync: getExtendedPublicKeyAsync,\n getExtendedPublicKey: getExtendedPublicKey,\n randomSecretKey: randomSecretKey,\n};\n// ## Precomputes\n// --------------\nconst W = 8; // W is window size\nconst scalarBits = 256;\nconst pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8, NOT 32 - see wNAF loop\nconst pwindowSize = 2 ** (W - 1); // 128 for W=8\nconst precompute = () => {\n const points = [];\n let p = G;\n let b = p;\n for (let w = 0; w < pwindows; w++) {\n b = p;\n points.push(b);\n for (let i = 1; i < pwindowSize; i++) {\n b = b.add(p);\n points.push(b);\n } // i=1, bc we skip 0\n p = b.double();\n }\n return points;\n};\nlet Gpows = undefined; // precomputes for base point G\n// const-time negate\nconst ctneg = (cnd, p) => {\n const n = p.negate();\n return cnd ? n : p;\n};\n/**\n * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by\n * caching multiples of G (base point). Cache is stored in 32MB of RAM.\n * Any time `G.multiply` is done, precomputes are used.\n * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.\n *\n * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,\n * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.\n *\n * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().\n */\nconst wNAF = (n) => {\n const comp = Gpows || (Gpows = precompute());\n let p = I;\n let f = G; // f must be G, or could become I in the end\n const pow_2_w = 2 ** W; // 256 for W=8\n const maxNum = pow_2_w; // 256 for W=8\n const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111\n const shiftBy = big(W); // 8 for W=8\n for (let w = 0; w < pwindows; w++) {\n let wbits = Number(n & mask); // extract W bits.\n n >>= shiftBy; // shift number by W bits.\n // We use negative indexes to reduce size of precomputed table by 2x.\n // Instead of needing precomputes 0..256, we only calculate them for 0..128.\n // If an index > 128 is found, we do (256-index) - where 256 is next window.\n // Naive: index +127 => 127, +224 => 224\n // Optimized: index +127 => 127, +224 => 256-32\n if (wbits > pwindowSize) {\n wbits -= maxNum;\n n += 1n;\n }\n const off = w * pwindowSize;\n const offF = off; // offsets, evaluate both\n const offP = off + Math.abs(wbits) - 1;\n const isEven = w % 2 !== 0; // conditions, evaluate both\n const isNeg = wbits < 0;\n if (wbits === 0) {\n // off == I: can't add it. Adding random offF instead.\n f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point\n }\n else {\n p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point\n }\n }\n if (n !== 0n)\n err('invalid wnaf');\n return { p, f }; // return both real and fake points for JIT\n};\n// !! Remove the export to easily use in REPL / browser console\nexport { etc, getPublicKey, getPublicKeyAsync, hash, hashes, keygen, keygenAsync, Point, sign, signAsync, utils, verify, verifyAsync, };\n","import { getPublicKeyAsync, signAsync, utils } from \"@noble/ed25519\";\n\ntype StoredIdentity = {\n version: 1;\n deviceId: string;\n publicKey: string;\n privateKey: string;\n createdAtMs: number;\n};\n\nexport type DeviceIdentity = {\n deviceId: string;\n publicKey: string;\n privateKey: string;\n};\n\nconst STORAGE_KEY = \"clawdbot-device-identity-v1\";\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/g, \"\");\n}\n\nfunction base64UrlDecode(input: string): Uint8Array {\n const normalized = input.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\");\n const padded = normalized + \"=\".repeat((4 - (normalized.length % 4)) % 4);\n const binary = atob(padded);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);\n return out;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nasync function fingerprintPublicKey(publicKey: Uint8Array): Promise {\n const hash = await crypto.subtle.digest(\"SHA-256\", publicKey);\n return bytesToHex(new Uint8Array(hash));\n}\n\nasync function generateIdentity(): Promise {\n const privateKey = utils.randomSecretKey();\n const publicKey = await getPublicKeyAsync(privateKey);\n const deviceId = await fingerprintPublicKey(publicKey);\n return {\n deviceId,\n publicKey: base64UrlEncode(publicKey),\n privateKey: base64UrlEncode(privateKey),\n };\n}\n\nexport async function loadOrCreateDeviceIdentity(): Promise {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (raw) {\n const parsed = JSON.parse(raw) as StoredIdentity;\n if (\n parsed?.version === 1 &&\n typeof parsed.deviceId === \"string\" &&\n typeof parsed.publicKey === \"string\" &&\n typeof parsed.privateKey === \"string\"\n ) {\n const derivedId = await fingerprintPublicKey(base64UrlDecode(parsed.publicKey));\n if (derivedId !== parsed.deviceId) {\n const updated: StoredIdentity = {\n ...parsed,\n deviceId: derivedId,\n };\n localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));\n return {\n deviceId: derivedId,\n publicKey: parsed.publicKey,\n privateKey: parsed.privateKey,\n };\n }\n return {\n deviceId: parsed.deviceId,\n publicKey: parsed.publicKey,\n privateKey: parsed.privateKey,\n };\n }\n }\n } catch {\n // fall through to regenerate\n }\n\n const identity = await generateIdentity();\n const stored: StoredIdentity = {\n version: 1,\n deviceId: identity.deviceId,\n publicKey: identity.publicKey,\n privateKey: identity.privateKey,\n createdAtMs: Date.now(),\n };\n localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));\n return identity;\n}\n\nexport async function signDevicePayload(privateKeyBase64Url: string, payload: string) {\n const key = base64UrlDecode(privateKeyBase64Url);\n const data = new TextEncoder().encode(payload);\n const sig = await signAsync(data, key);\n return base64UrlEncode(sig);\n}\n","export type DeviceAuthEntry = {\n token: string;\n role: string;\n scopes: string[];\n updatedAtMs: number;\n};\n\ntype DeviceAuthStore = {\n version: 1;\n deviceId: string;\n tokens: Record;\n};\n\nconst STORAGE_KEY = \"clawdbot.device.auth.v1\";\n\nfunction normalizeRole(role: string): string {\n return role.trim();\n}\n\nfunction normalizeScopes(scopes: string[] | undefined): string[] {\n if (!Array.isArray(scopes)) return [];\n const out = new Set();\n for (const scope of scopes) {\n const trimmed = scope.trim();\n if (trimmed) out.add(trimmed);\n }\n return [...out].sort();\n}\n\nfunction readStore(): DeviceAuthStore | null {\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as DeviceAuthStore;\n if (!parsed || parsed.version !== 1) return null;\n if (!parsed.deviceId || typeof parsed.deviceId !== \"string\") return null;\n if (!parsed.tokens || typeof parsed.tokens !== \"object\") return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction writeStore(store: DeviceAuthStore) {\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(store));\n } catch {\n // best-effort\n }\n}\n\nexport function loadDeviceAuthToken(params: {\n deviceId: string;\n role: string;\n}): DeviceAuthEntry | null {\n const store = readStore();\n if (!store || store.deviceId !== params.deviceId) return null;\n const role = normalizeRole(params.role);\n const entry = store.tokens[role];\n if (!entry || typeof entry.token !== \"string\") return null;\n return entry;\n}\n\nexport function storeDeviceAuthToken(params: {\n deviceId: string;\n role: string;\n token: string;\n scopes?: string[];\n}): DeviceAuthEntry {\n const role = normalizeRole(params.role);\n const next: DeviceAuthStore = {\n version: 1,\n deviceId: params.deviceId,\n tokens: {},\n };\n const existing = readStore();\n if (existing && existing.deviceId === params.deviceId) {\n next.tokens = { ...existing.tokens };\n }\n const entry: DeviceAuthEntry = {\n token: params.token,\n role,\n scopes: normalizeScopes(params.scopes),\n updatedAtMs: Date.now(),\n };\n next.tokens[role] = entry;\n writeStore(next);\n return entry;\n}\n\nexport function clearDeviceAuthToken(params: { deviceId: string; role: string }) {\n const store = readStore();\n if (!store || store.deviceId !== params.deviceId) return;\n const role = normalizeRole(params.role);\n if (!store.tokens[role]) return;\n const next = { ...store, tokens: { ...store.tokens } };\n delete next.tokens[role];\n writeStore(next);\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { loadOrCreateDeviceIdentity } from \"../device-identity\";\nimport { clearDeviceAuthToken, storeDeviceAuthToken } from \"../device-auth\";\n\nexport type DeviceTokenSummary = {\n role: string;\n scopes?: string[];\n createdAtMs?: number;\n rotatedAtMs?: number;\n revokedAtMs?: number;\n lastUsedAtMs?: number;\n};\n\nexport type PendingDevice = {\n requestId: string;\n deviceId: string;\n displayName?: string;\n role?: string;\n remoteIp?: string;\n isRepair?: boolean;\n ts?: number;\n};\n\nexport type PairedDevice = {\n deviceId: string;\n displayName?: string;\n roles?: string[];\n scopes?: string[];\n remoteIp?: string;\n tokens?: DeviceTokenSummary[];\n createdAtMs?: number;\n approvedAtMs?: number;\n};\n\nexport type DevicePairingList = {\n pending: PendingDevice[];\n paired: PairedDevice[];\n};\n\nexport type DevicesState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n devicesLoading: boolean;\n devicesError: string | null;\n devicesList: DevicePairingList | null;\n};\n\nexport async function loadDevices(state: DevicesState, opts?: { quiet?: boolean }) {\n if (!state.client || !state.connected) return;\n if (state.devicesLoading) return;\n state.devicesLoading = true;\n if (!opts?.quiet) state.devicesError = null;\n try {\n const res = (await state.client.request(\"device.pair.list\", {})) as DevicePairingList | null;\n state.devicesList = {\n pending: Array.isArray(res?.pending) ? res!.pending : [],\n paired: Array.isArray(res?.paired) ? res!.paired : [],\n };\n } catch (err) {\n if (!opts?.quiet) state.devicesError = String(err);\n } finally {\n state.devicesLoading = false;\n }\n}\n\nexport async function approveDevicePairing(state: DevicesState, requestId: string) {\n if (!state.client || !state.connected) return;\n try {\n await state.client.request(\"device.pair.approve\", { requestId });\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n\nexport async function rejectDevicePairing(state: DevicesState, requestId: string) {\n if (!state.client || !state.connected) return;\n const confirmed = window.confirm(\"Reject this device pairing request?\");\n if (!confirmed) return;\n try {\n await state.client.request(\"device.pair.reject\", { requestId });\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n\nexport async function rotateDeviceToken(\n state: DevicesState,\n params: { deviceId: string; role: string; scopes?: string[] },\n) {\n if (!state.client || !state.connected) return;\n try {\n const res = (await state.client.request(\"device.token.rotate\", params)) as\n | { token?: string; role?: string; deviceId?: string; scopes?: string[] }\n | undefined;\n if (res?.token) {\n const identity = await loadOrCreateDeviceIdentity();\n const role = res.role ?? params.role;\n if (res.deviceId === identity.deviceId || params.deviceId === identity.deviceId) {\n storeDeviceAuthToken({\n deviceId: identity.deviceId,\n role,\n token: res.token,\n scopes: res.scopes ?? params.scopes ?? [],\n });\n }\n window.prompt(\"New device token (copy and store securely):\", res.token);\n }\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n\nexport async function revokeDeviceToken(\n state: DevicesState,\n params: { deviceId: string; role: string },\n) {\n if (!state.client || !state.connected) return;\n const confirmed = window.confirm(\n `Revoke token for ${params.deviceId} (${params.role})?`,\n );\n if (!confirmed) return;\n try {\n await state.client.request(\"device.token.revoke\", params);\n const identity = await loadOrCreateDeviceIdentity();\n if (params.deviceId === identity.deviceId) {\n clearDeviceAuthToken({ deviceId: identity.deviceId, role: params.role });\n }\n await loadDevices(state);\n } catch (err) {\n state.devicesError = String(err);\n }\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\n\nexport type NodesState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n nodesLoading: boolean;\n nodes: Array>;\n lastError: string | null;\n};\n\nexport async function loadNodes(\n state: NodesState,\n opts?: { quiet?: boolean },\n) {\n if (!state.client || !state.connected) return;\n if (state.nodesLoading) return;\n state.nodesLoading = true;\n if (!opts?.quiet) state.lastError = null;\n try {\n const res = (await state.client.request(\"node.list\", {})) as {\n nodes?: Array>;\n };\n state.nodes = Array.isArray(res.nodes) ? res.nodes : [];\n } catch (err) {\n if (!opts?.quiet) state.lastError = String(err);\n } finally {\n state.nodesLoading = false;\n }\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport { cloneConfigObject, removePathValue, setPathValue } from \"./config/form-utils\";\n\nexport type ExecApprovalsDefaults = {\n security?: string;\n ask?: string;\n askFallback?: string;\n autoAllowSkills?: boolean;\n};\n\nexport type ExecApprovalsAllowlistEntry = {\n pattern: string;\n lastUsedAt?: number;\n lastUsedCommand?: string;\n lastResolvedPath?: string;\n};\n\nexport type ExecApprovalsAgent = ExecApprovalsDefaults & {\n allowlist?: ExecApprovalsAllowlistEntry[];\n};\n\nexport type ExecApprovalsFile = {\n version?: number;\n socket?: { path?: string };\n defaults?: ExecApprovalsDefaults;\n agents?: Record;\n};\n\nexport type ExecApprovalsSnapshot = {\n path: string;\n exists: boolean;\n hash: string;\n file: ExecApprovalsFile;\n};\n\nexport type ExecApprovalsTarget =\n | { kind: \"gateway\" }\n | { kind: \"node\"; nodeId: string };\n\nexport type ExecApprovalsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n execApprovalsLoading: boolean;\n execApprovalsSaving: boolean;\n execApprovalsDirty: boolean;\n execApprovalsSnapshot: ExecApprovalsSnapshot | null;\n execApprovalsForm: ExecApprovalsFile | null;\n execApprovalsSelectedAgent: string | null;\n lastError: string | null;\n};\n\nfunction resolveExecApprovalsRpc(target?: ExecApprovalsTarget | null): {\n method: string;\n params: Record;\n} | null {\n if (!target || target.kind === \"gateway\") {\n return { method: \"exec.approvals.get\", params: {} };\n }\n const nodeId = target.nodeId.trim();\n if (!nodeId) return null;\n return { method: \"exec.approvals.node.get\", params: { nodeId } };\n}\n\nfunction resolveExecApprovalsSaveRpc(\n target: ExecApprovalsTarget | null | undefined,\n params: { file: ExecApprovalsFile; baseHash: string },\n): { method: string; params: Record } | null {\n if (!target || target.kind === \"gateway\") {\n return { method: \"exec.approvals.set\", params };\n }\n const nodeId = target.nodeId.trim();\n if (!nodeId) return null;\n return { method: \"exec.approvals.node.set\", params: { ...params, nodeId } };\n}\n\nexport async function loadExecApprovals(\n state: ExecApprovalsState,\n target?: ExecApprovalsTarget | null,\n) {\n if (!state.client || !state.connected) return;\n if (state.execApprovalsLoading) return;\n state.execApprovalsLoading = true;\n state.lastError = null;\n try {\n const rpc = resolveExecApprovalsRpc(target);\n if (!rpc) {\n state.lastError = \"Select a node before loading exec approvals.\";\n return;\n }\n const res = (await state.client.request(rpc.method, rpc.params)) as ExecApprovalsSnapshot;\n applyExecApprovalsSnapshot(state, res);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.execApprovalsLoading = false;\n }\n}\n\nexport function applyExecApprovalsSnapshot(\n state: ExecApprovalsState,\n snapshot: ExecApprovalsSnapshot,\n) {\n state.execApprovalsSnapshot = snapshot;\n if (!state.execApprovalsDirty) {\n state.execApprovalsForm = cloneConfigObject(snapshot.file ?? {});\n }\n}\n\nexport async function saveExecApprovals(\n state: ExecApprovalsState,\n target?: ExecApprovalsTarget | null,\n) {\n if (!state.client || !state.connected) return;\n state.execApprovalsSaving = true;\n state.lastError = null;\n try {\n const baseHash = state.execApprovalsSnapshot?.hash;\n if (!baseHash) {\n state.lastError = \"Exec approvals hash missing; reload and retry.\";\n return;\n }\n const file =\n state.execApprovalsForm ??\n state.execApprovalsSnapshot?.file ??\n {};\n const rpc = resolveExecApprovalsSaveRpc(target, { file, baseHash });\n if (!rpc) {\n state.lastError = \"Select a node before saving exec approvals.\";\n return;\n }\n await state.client.request(rpc.method, rpc.params);\n state.execApprovalsDirty = false;\n await loadExecApprovals(state, target);\n } catch (err) {\n state.lastError = String(err);\n } finally {\n state.execApprovalsSaving = false;\n }\n}\n\nexport function updateExecApprovalsFormValue(\n state: ExecApprovalsState,\n path: Array,\n value: unknown,\n) {\n const base = cloneConfigObject(\n state.execApprovalsForm ?? state.execApprovalsSnapshot?.file ?? {},\n );\n setPathValue(base, path, value);\n state.execApprovalsForm = base;\n state.execApprovalsDirty = true;\n}\n\nexport function removeExecApprovalsFormValue(\n state: ExecApprovalsState,\n path: Array,\n) {\n const base = cloneConfigObject(\n state.execApprovalsForm ?? state.execApprovalsSnapshot?.file ?? {},\n );\n removePathValue(base, path);\n state.execApprovalsForm = base;\n state.execApprovalsDirty = true;\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { PresenceEntry } from \"../types\";\n\nexport type PresenceState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n presenceLoading: boolean;\n presenceEntries: PresenceEntry[];\n presenceError: string | null;\n presenceStatus: string | null;\n};\n\nexport async function loadPresence(state: PresenceState) {\n if (!state.client || !state.connected) return;\n if (state.presenceLoading) return;\n state.presenceLoading = true;\n state.presenceError = null;\n state.presenceStatus = null;\n try {\n const res = (await state.client.request(\"system-presence\", {})) as\n | PresenceEntry[]\n | undefined;\n if (Array.isArray(res)) {\n state.presenceEntries = res;\n state.presenceStatus = res.length === 0 ? \"No instances yet.\" : null;\n } else {\n state.presenceEntries = [];\n state.presenceStatus = \"No presence payload.\";\n }\n } catch (err) {\n state.presenceError = String(err);\n } finally {\n state.presenceLoading = false;\n }\n}\n\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { SkillStatusReport } from \"../types\";\n\nexport type SkillsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n skillsLoading: boolean;\n skillsReport: SkillStatusReport | null;\n skillsError: string | null;\n skillsBusyKey: string | null;\n skillEdits: Record;\n skillMessages: SkillMessageMap;\n};\n\nexport type SkillMessage = {\n kind: \"success\" | \"error\";\n message: string;\n};\n\nexport type SkillMessageMap = Record;\n\ntype LoadSkillsOptions = {\n clearMessages?: boolean;\n};\n\nfunction setSkillMessage(state: SkillsState, key: string, message?: SkillMessage) {\n if (!key.trim()) return;\n const next = { ...state.skillMessages };\n if (message) next[key] = message;\n else delete next[key];\n state.skillMessages = next;\n}\n\nfunction getErrorMessage(err: unknown) {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\nexport async function loadSkills(state: SkillsState, options?: LoadSkillsOptions) {\n if (options?.clearMessages && Object.keys(state.skillMessages).length > 0) {\n state.skillMessages = {};\n }\n if (!state.client || !state.connected) return;\n if (state.skillsLoading) return;\n state.skillsLoading = true;\n state.skillsError = null;\n try {\n const res = (await state.client.request(\"skills.status\", {})) as\n | SkillStatusReport\n | undefined;\n if (res) state.skillsReport = res;\n } catch (err) {\n state.skillsError = getErrorMessage(err);\n } finally {\n state.skillsLoading = false;\n }\n}\n\nexport function updateSkillEdit(\n state: SkillsState,\n skillKey: string,\n value: string,\n) {\n state.skillEdits = { ...state.skillEdits, [skillKey]: value };\n}\n\nexport async function updateSkillEnabled(\n state: SkillsState,\n skillKey: string,\n enabled: boolean,\n) {\n if (!state.client || !state.connected) return;\n state.skillsBusyKey = skillKey;\n state.skillsError = null;\n try {\n await state.client.request(\"skills.update\", { skillKey, enabled });\n await loadSkills(state);\n setSkillMessage(state, skillKey, {\n kind: \"success\",\n message: enabled ? \"Skill enabled\" : \"Skill disabled\",\n });\n } catch (err) {\n const message = getErrorMessage(err);\n state.skillsError = message;\n setSkillMessage(state, skillKey, {\n kind: \"error\",\n message,\n });\n } finally {\n state.skillsBusyKey = null;\n }\n}\n\nexport async function saveSkillApiKey(state: SkillsState, skillKey: string) {\n if (!state.client || !state.connected) return;\n state.skillsBusyKey = skillKey;\n state.skillsError = null;\n try {\n const apiKey = state.skillEdits[skillKey] ?? \"\";\n await state.client.request(\"skills.update\", { skillKey, apiKey });\n await loadSkills(state);\n setSkillMessage(state, skillKey, {\n kind: \"success\",\n message: \"API key saved\",\n });\n } catch (err) {\n const message = getErrorMessage(err);\n state.skillsError = message;\n setSkillMessage(state, skillKey, {\n kind: \"error\",\n message,\n });\n } finally {\n state.skillsBusyKey = null;\n }\n}\n\nexport async function installSkill(\n state: SkillsState,\n skillKey: string,\n name: string,\n installId: string,\n) {\n if (!state.client || !state.connected) return;\n state.skillsBusyKey = skillKey;\n state.skillsError = null;\n try {\n const result = (await state.client.request(\"skills.install\", {\n name,\n installId,\n timeoutMs: 120000,\n })) as { ok?: boolean; message?: string };\n await loadSkills(state);\n setSkillMessage(state, skillKey, {\n kind: \"success\",\n message: result?.message ?? \"Installed\",\n });\n } catch (err) {\n const message = getErrorMessage(err);\n state.skillsError = message;\n setSkillMessage(state, skillKey, {\n kind: \"error\",\n message,\n });\n } finally {\n state.skillsBusyKey = null;\n }\n}\n","export type ThemeMode = \"system\" | \"light\" | \"dark\";\nexport type ResolvedTheme = \"light\" | \"dark\";\n\nexport function getSystemTheme(): ResolvedTheme {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return \"dark\";\n }\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n ? \"dark\"\n : \"light\";\n}\n\nexport function resolveTheme(mode: ThemeMode): ResolvedTheme {\n if (mode === \"system\") return getSystemTheme();\n return mode;\n}\n","import type { ThemeMode } from \"./theme\";\n\nexport type ThemeTransitionContext = {\n element?: HTMLElement | null;\n pointerClientX?: number;\n pointerClientY?: number;\n};\n\nexport type ThemeTransitionOptions = {\n nextTheme: ThemeMode;\n applyTheme: () => void;\n context?: ThemeTransitionContext;\n currentTheme?: ThemeMode | null;\n};\n\ntype DocumentWithViewTransition = Document & {\n startViewTransition?: (callback: () => void) => { finished: Promise };\n};\n\nconst clamp01 = (value: number) => {\n if (Number.isNaN(value)) return 0.5;\n if (value <= 0) return 0;\n if (value >= 1) return 1;\n return value;\n};\n\nconst hasReducedMotionPreference = () => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return false;\n }\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ?? false;\n};\n\nconst cleanupThemeTransition = (root: HTMLElement) => {\n root.classList.remove(\"theme-transition\");\n root.style.removeProperty(\"--theme-switch-x\");\n root.style.removeProperty(\"--theme-switch-y\");\n};\n\nexport const startThemeTransition = ({\n nextTheme,\n applyTheme,\n context,\n currentTheme,\n}: ThemeTransitionOptions) => {\n if (currentTheme === nextTheme) return;\n\n const documentReference = globalThis.document ?? null;\n if (!documentReference) {\n applyTheme();\n return;\n }\n\n const root = documentReference.documentElement;\n const document_ = documentReference as DocumentWithViewTransition;\n const prefersReducedMotion = hasReducedMotionPreference();\n\n const canUseViewTransition =\n Boolean(document_.startViewTransition) && !prefersReducedMotion;\n\n if (canUseViewTransition) {\n let xPercent = 0.5;\n let yPercent = 0.5;\n\n if (\n context?.pointerClientX !== undefined &&\n context?.pointerClientY !== undefined &&\n typeof window !== \"undefined\"\n ) {\n xPercent = clamp01(context.pointerClientX / window.innerWidth);\n yPercent = clamp01(context.pointerClientY / window.innerHeight);\n } else if (context?.element) {\n const rect = context.element.getBoundingClientRect();\n if (\n rect.width > 0 &&\n rect.height > 0 &&\n typeof window !== \"undefined\"\n ) {\n xPercent = clamp01((rect.left + rect.width / 2) / window.innerWidth);\n yPercent = clamp01((rect.top + rect.height / 2) / window.innerHeight);\n }\n }\n\n root.style.setProperty(\"--theme-switch-x\", `${xPercent * 100}%`);\n root.style.setProperty(\"--theme-switch-y\", `${yPercent * 100}%`);\n root.classList.add(\"theme-transition\");\n\n try {\n const transition = document_.startViewTransition?.(() => {\n applyTheme();\n });\n if (transition?.finished) {\n void transition.finished.finally(() => cleanupThemeTransition(root));\n } else {\n cleanupThemeTransition(root);\n }\n } catch {\n cleanupThemeTransition(root);\n applyTheme();\n }\n return;\n }\n\n applyTheme();\n cleanupThemeTransition(root);\n};\n","import { loadLogs } from \"./controllers/logs\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadDebug } from \"./controllers/debug\";\nimport type { ClawdbotApp } from \"./app\";\n\ntype PollingHost = {\n nodesPollInterval: number | null;\n logsPollInterval: number | null;\n debugPollInterval: number | null;\n tab: string;\n};\n\nexport function startNodesPolling(host: PollingHost) {\n if (host.nodesPollInterval != null) return;\n host.nodesPollInterval = window.setInterval(\n () => void loadNodes(host as unknown as ClawdbotApp, { quiet: true }),\n 5000,\n );\n}\n\nexport function stopNodesPolling(host: PollingHost) {\n if (host.nodesPollInterval == null) return;\n clearInterval(host.nodesPollInterval);\n host.nodesPollInterval = null;\n}\n\nexport function startLogsPolling(host: PollingHost) {\n if (host.logsPollInterval != null) return;\n host.logsPollInterval = window.setInterval(() => {\n if (host.tab !== \"logs\") return;\n void loadLogs(host as unknown as ClawdbotApp, { quiet: true });\n }, 2000);\n}\n\nexport function stopLogsPolling(host: PollingHost) {\n if (host.logsPollInterval == null) return;\n clearInterval(host.logsPollInterval);\n host.logsPollInterval = null;\n}\n\nexport function startDebugPolling(host: PollingHost) {\n if (host.debugPollInterval != null) return;\n host.debugPollInterval = window.setInterval(() => {\n if (host.tab !== \"debug\") return;\n void loadDebug(host as unknown as ClawdbotApp);\n }, 3000);\n}\n\nexport function stopDebugPolling(host: PollingHost) {\n if (host.debugPollInterval == null) return;\n clearInterval(host.debugPollInterval);\n host.debugPollInterval = null;\n}\n","import { loadConfig, loadConfigSchema } from \"./controllers/config\";\nimport { loadCronJobs, loadCronStatus } from \"./controllers/cron\";\nimport { loadChannels } from \"./controllers/channels\";\nimport { loadDebug } from \"./controllers/debug\";\nimport { loadLogs } from \"./controllers/logs\";\nimport { loadDevices } from \"./controllers/devices\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadExecApprovals } from \"./controllers/exec-approvals\";\nimport { loadPresence } from \"./controllers/presence\";\nimport { loadSessions } from \"./controllers/sessions\";\nimport { loadSkills } from \"./controllers/skills\";\nimport { inferBasePathFromPathname, normalizeBasePath, normalizePath, pathForTab, tabFromPath, type Tab } from \"./navigation\";\nimport { saveSettings, type UiSettings } from \"./storage\";\nimport { resolveTheme, type ResolvedTheme, type ThemeMode } from \"./theme\";\nimport { startThemeTransition, type ThemeTransitionContext } from \"./theme-transition\";\nimport { scheduleChatScroll, scheduleLogsScroll } from \"./app-scroll\";\nimport { startLogsPolling, stopLogsPolling, startDebugPolling, stopDebugPolling } from \"./app-polling\";\nimport { refreshChat } from \"./app-chat\";\nimport type { ClawdbotApp } from \"./app\";\n\ntype SettingsHost = {\n settings: UiSettings;\n theme: ThemeMode;\n themeResolved: ResolvedTheme;\n applySessionKey: string;\n sessionKey: string;\n tab: Tab;\n connected: boolean;\n chatHasAutoScrolled: boolean;\n logsAtBottom: boolean;\n eventLog: unknown[];\n eventLogBuffer: unknown[];\n basePath: string;\n themeMedia: MediaQueryList | null;\n themeMediaHandler: ((event: MediaQueryListEvent) => void) | null;\n};\n\nexport function applySettings(host: SettingsHost, next: UiSettings) {\n const normalized = {\n ...next,\n lastActiveSessionKey: next.lastActiveSessionKey?.trim() || next.sessionKey.trim() || \"main\",\n };\n host.settings = normalized;\n saveSettings(normalized);\n if (next.theme !== host.theme) {\n host.theme = next.theme;\n applyResolvedTheme(host, resolveTheme(next.theme));\n }\n host.applySessionKey = host.settings.lastActiveSessionKey;\n}\n\nexport function setLastActiveSessionKey(host: SettingsHost, next: string) {\n const trimmed = next.trim();\n if (!trimmed) return;\n if (host.settings.lastActiveSessionKey === trimmed) return;\n applySettings(host, { ...host.settings, lastActiveSessionKey: trimmed });\n}\n\nexport function applySettingsFromUrl(host: SettingsHost) {\n if (!window.location.search) return;\n const params = new URLSearchParams(window.location.search);\n const tokenRaw = params.get(\"token\");\n const passwordRaw = params.get(\"password\");\n const sessionRaw = params.get(\"session\");\n const gatewayUrlRaw = params.get(\"gatewayUrl\");\n let shouldCleanUrl = false;\n\n if (tokenRaw != null) {\n const token = tokenRaw.trim();\n if (token && token !== host.settings.token) {\n applySettings(host, { ...host.settings, token });\n }\n params.delete(\"token\");\n shouldCleanUrl = true;\n }\n\n if (passwordRaw != null) {\n const password = passwordRaw.trim();\n if (password) {\n (host as { password: string }).password = password;\n }\n params.delete(\"password\");\n shouldCleanUrl = true;\n }\n\n if (sessionRaw != null) {\n const session = sessionRaw.trim();\n if (session) {\n host.sessionKey = session;\n applySettings(host, {\n ...host.settings,\n sessionKey: session,\n lastActiveSessionKey: session,\n });\n }\n }\n\n if (gatewayUrlRaw != null) {\n const gatewayUrl = gatewayUrlRaw.trim();\n if (gatewayUrl && gatewayUrl !== host.settings.gatewayUrl) {\n applySettings(host, { ...host.settings, gatewayUrl });\n }\n params.delete(\"gatewayUrl\");\n shouldCleanUrl = true;\n }\n\n if (!shouldCleanUrl) return;\n const url = new URL(window.location.href);\n url.search = params.toString();\n window.history.replaceState({}, \"\", url.toString());\n}\n\nexport function setTab(host: SettingsHost, next: Tab) {\n if (host.tab !== next) host.tab = next;\n if (next === \"chat\") host.chatHasAutoScrolled = false;\n if (next === \"logs\")\n startLogsPolling(host as unknown as Parameters[0]);\n else stopLogsPolling(host as unknown as Parameters[0]);\n if (next === \"debug\")\n startDebugPolling(host as unknown as Parameters[0]);\n else stopDebugPolling(host as unknown as Parameters[0]);\n void refreshActiveTab(host);\n syncUrlWithTab(host, next, false);\n}\n\nexport function setTheme(\n host: SettingsHost,\n next: ThemeMode,\n context?: ThemeTransitionContext,\n) {\n const applyTheme = () => {\n host.theme = next;\n applySettings(host, { ...host.settings, theme: next });\n applyResolvedTheme(host, resolveTheme(next));\n };\n startThemeTransition({\n nextTheme: next,\n applyTheme,\n context,\n currentTheme: host.theme,\n });\n}\n\nexport async function refreshActiveTab(host: SettingsHost) {\n if (host.tab === \"overview\") await loadOverview(host);\n if (host.tab === \"channels\") await loadChannelsTab(host);\n if (host.tab === \"instances\") await loadPresence(host as unknown as ClawdbotApp);\n if (host.tab === \"sessions\") await loadSessions(host as unknown as ClawdbotApp);\n if (host.tab === \"cron\") await loadCron(host);\n if (host.tab === \"skills\") await loadSkills(host as unknown as ClawdbotApp);\n if (host.tab === \"nodes\") {\n await loadNodes(host as unknown as ClawdbotApp);\n await loadDevices(host as unknown as ClawdbotApp);\n await loadConfig(host as unknown as ClawdbotApp);\n await loadExecApprovals(host as unknown as ClawdbotApp);\n }\n if (host.tab === \"chat\") {\n await refreshChat(host as unknown as Parameters[0]);\n scheduleChatScroll(\n host as unknown as Parameters[0],\n !host.chatHasAutoScrolled,\n );\n }\n if (host.tab === \"config\") {\n await loadConfigSchema(host as unknown as ClawdbotApp);\n await loadConfig(host as unknown as ClawdbotApp);\n }\n if (host.tab === \"debug\") {\n await loadDebug(host as unknown as ClawdbotApp);\n host.eventLog = host.eventLogBuffer;\n }\n if (host.tab === \"logs\") {\n host.logsAtBottom = true;\n await loadLogs(host as unknown as ClawdbotApp, { reset: true });\n scheduleLogsScroll(\n host as unknown as Parameters[0],\n true,\n );\n }\n}\n\nexport function inferBasePath() {\n if (typeof window === \"undefined\") return \"\";\n const configured = window.__CLAWDBOT_CONTROL_UI_BASE_PATH__;\n if (typeof configured === \"string\" && configured.trim()) {\n return normalizeBasePath(configured);\n }\n return inferBasePathFromPathname(window.location.pathname);\n}\n\nexport function syncThemeWithSettings(host: SettingsHost) {\n host.theme = host.settings.theme ?? \"system\";\n applyResolvedTheme(host, resolveTheme(host.theme));\n}\n\nexport function applyResolvedTheme(host: SettingsHost, resolved: ResolvedTheme) {\n host.themeResolved = resolved;\n if (typeof document === \"undefined\") return;\n const root = document.documentElement;\n root.dataset.theme = resolved;\n root.style.colorScheme = resolved;\n}\n\nexport function attachThemeListener(host: SettingsHost) {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n host.themeMedia = window.matchMedia(\"(prefers-color-scheme: dark)\");\n host.themeMediaHandler = (event) => {\n if (host.theme !== \"system\") return;\n applyResolvedTheme(host, event.matches ? \"dark\" : \"light\");\n };\n if (typeof host.themeMedia.addEventListener === \"function\") {\n host.themeMedia.addEventListener(\"change\", host.themeMediaHandler);\n return;\n }\n const legacy = host.themeMedia as MediaQueryList & {\n addListener: (cb: (event: MediaQueryListEvent) => void) => void;\n };\n legacy.addListener(host.themeMediaHandler);\n}\n\nexport function detachThemeListener(host: SettingsHost) {\n if (!host.themeMedia || !host.themeMediaHandler) return;\n if (typeof host.themeMedia.removeEventListener === \"function\") {\n host.themeMedia.removeEventListener(\"change\", host.themeMediaHandler);\n return;\n }\n const legacy = host.themeMedia as MediaQueryList & {\n removeListener: (cb: (event: MediaQueryListEvent) => void) => void;\n };\n legacy.removeListener(host.themeMediaHandler);\n host.themeMedia = null;\n host.themeMediaHandler = null;\n}\n\nexport function syncTabWithLocation(host: SettingsHost, replace: boolean) {\n if (typeof window === \"undefined\") return;\n const resolved = tabFromPath(window.location.pathname, host.basePath) ?? \"chat\";\n setTabFromRoute(host, resolved);\n syncUrlWithTab(host, resolved, replace);\n}\n\nexport function onPopState(host: SettingsHost) {\n if (typeof window === \"undefined\") return;\n const resolved = tabFromPath(window.location.pathname, host.basePath);\n if (!resolved) return;\n\n const url = new URL(window.location.href);\n const session = url.searchParams.get(\"session\")?.trim();\n if (session) {\n host.sessionKey = session;\n applySettings(host, {\n ...host.settings,\n sessionKey: session,\n lastActiveSessionKey: session,\n });\n }\n\n setTabFromRoute(host, resolved);\n}\n\nexport function setTabFromRoute(host: SettingsHost, next: Tab) {\n if (host.tab !== next) host.tab = next;\n if (next === \"chat\") host.chatHasAutoScrolled = false;\n if (next === \"logs\")\n startLogsPolling(host as unknown as Parameters[0]);\n else stopLogsPolling(host as unknown as Parameters[0]);\n if (next === \"debug\")\n startDebugPolling(host as unknown as Parameters[0]);\n else stopDebugPolling(host as unknown as Parameters[0]);\n if (host.connected) void refreshActiveTab(host);\n}\n\nexport function syncUrlWithTab(host: SettingsHost, tab: Tab, replace: boolean) {\n if (typeof window === \"undefined\") return;\n const targetPath = normalizePath(pathForTab(tab, host.basePath));\n const currentPath = normalizePath(window.location.pathname);\n const url = new URL(window.location.href);\n\n if (tab === \"chat\" && host.sessionKey) {\n url.searchParams.set(\"session\", host.sessionKey);\n } else {\n url.searchParams.delete(\"session\");\n }\n\n if (currentPath !== targetPath) {\n url.pathname = targetPath;\n }\n\n if (replace) {\n window.history.replaceState({}, \"\", url.toString());\n } else {\n window.history.pushState({}, \"\", url.toString());\n }\n}\n\nexport function syncUrlWithSessionKey(\n host: SettingsHost,\n sessionKey: string,\n replace: boolean,\n) {\n if (typeof window === \"undefined\") return;\n const url = new URL(window.location.href);\n url.searchParams.set(\"session\", sessionKey);\n if (replace) window.history.replaceState({}, \"\", url.toString());\n else window.history.pushState({}, \"\", url.toString());\n}\n\nexport async function loadOverview(host: SettingsHost) {\n await Promise.all([\n loadChannels(host as unknown as ClawdbotApp, false),\n loadPresence(host as unknown as ClawdbotApp),\n loadSessions(host as unknown as ClawdbotApp),\n loadCronStatus(host as unknown as ClawdbotApp),\n loadDebug(host as unknown as ClawdbotApp),\n ]);\n}\n\nexport async function loadChannelsTab(host: SettingsHost) {\n await Promise.all([\n loadChannels(host as unknown as ClawdbotApp, true),\n loadConfigSchema(host as unknown as ClawdbotApp),\n loadConfig(host as unknown as ClawdbotApp),\n ]);\n}\n\nexport async function loadCron(host: SettingsHost) {\n await Promise.all([\n loadChannels(host as unknown as ClawdbotApp, false),\n loadCronStatus(host as unknown as ClawdbotApp),\n loadCronJobs(host as unknown as ClawdbotApp),\n ]);\n}\n","import { abortChatRun, loadChatHistory, sendChatMessage } from \"./controllers/chat\";\nimport { loadSessions } from \"./controllers/sessions\";\nimport { generateUUID } from \"./uuid\";\nimport { resetToolStream } from \"./app-tool-stream\";\nimport { scheduleChatScroll } from \"./app-scroll\";\nimport { setLastActiveSessionKey } from \"./app-settings\";\nimport { normalizeBasePath } from \"./navigation\";\nimport type { GatewayHelloOk } from \"./gateway\";\nimport { parseAgentSessionKey } from \"../../../src/sessions/session-key-utils.js\";\nimport type { ClawdbotApp } from \"./app\";\n\ntype ChatHost = {\n connected: boolean;\n chatMessage: string;\n chatQueue: Array<{ id: string; text: string; createdAt: number }>;\n chatRunId: string | null;\n chatSending: boolean;\n sessionKey: string;\n basePath: string;\n hello: GatewayHelloOk | null;\n chatAvatarUrl: string | null;\n};\n\nexport function isChatBusy(host: ChatHost) {\n return host.chatSending || Boolean(host.chatRunId);\n}\n\nexport function isChatStopCommand(text: string) {\n const trimmed = text.trim();\n if (!trimmed) return false;\n const normalized = trimmed.toLowerCase();\n if (normalized === \"/stop\") return true;\n return (\n normalized === \"stop\" ||\n normalized === \"esc\" ||\n normalized === \"abort\" ||\n normalized === \"wait\" ||\n normalized === \"exit\"\n );\n}\n\nexport async function handleAbortChat(host: ChatHost) {\n if (!host.connected) return;\n host.chatMessage = \"\";\n await abortChatRun(host as unknown as ClawdbotApp);\n}\n\nfunction enqueueChatMessage(host: ChatHost, text: string) {\n const trimmed = text.trim();\n if (!trimmed) return;\n host.chatQueue = [\n ...host.chatQueue,\n {\n id: generateUUID(),\n text: trimmed,\n createdAt: Date.now(),\n },\n ];\n}\n\nasync function sendChatMessageNow(\n host: ChatHost,\n message: string,\n opts?: { previousDraft?: string; restoreDraft?: boolean },\n) {\n resetToolStream(host as unknown as Parameters[0]);\n const ok = await sendChatMessage(host as unknown as ClawdbotApp, message);\n if (!ok && opts?.previousDraft != null) {\n host.chatMessage = opts.previousDraft;\n }\n if (ok) {\n setLastActiveSessionKey(host as unknown as Parameters[0], host.sessionKey);\n }\n if (ok && opts?.restoreDraft && opts.previousDraft?.trim()) {\n host.chatMessage = opts.previousDraft;\n }\n scheduleChatScroll(host as unknown as Parameters[0]);\n if (ok && !host.chatRunId) {\n void flushChatQueue(host);\n }\n return ok;\n}\n\nasync function flushChatQueue(host: ChatHost) {\n if (!host.connected || isChatBusy(host)) return;\n const [next, ...rest] = host.chatQueue;\n if (!next) return;\n host.chatQueue = rest;\n const ok = await sendChatMessageNow(host, next.text);\n if (!ok) {\n host.chatQueue = [next, ...host.chatQueue];\n }\n}\n\nexport function removeQueuedMessage(host: ChatHost, id: string) {\n host.chatQueue = host.chatQueue.filter((item) => item.id !== id);\n}\n\nexport async function handleSendChat(\n host: ChatHost,\n messageOverride?: string,\n opts?: { restoreDraft?: boolean },\n) {\n if (!host.connected) return;\n const previousDraft = host.chatMessage;\n const message = (messageOverride ?? host.chatMessage).trim();\n if (!message) return;\n\n if (isChatStopCommand(message)) {\n await handleAbortChat(host);\n return;\n }\n\n if (messageOverride == null) {\n host.chatMessage = \"\";\n }\n\n if (isChatBusy(host)) {\n enqueueChatMessage(host, message);\n return;\n }\n\n await sendChatMessageNow(host, message, {\n previousDraft: messageOverride == null ? previousDraft : undefined,\n restoreDraft: Boolean(messageOverride && opts?.restoreDraft),\n });\n}\n\nexport async function refreshChat(host: ChatHost) {\n await Promise.all([\n loadChatHistory(host as unknown as ClawdbotApp),\n loadSessions(host as unknown as ClawdbotApp),\n refreshChatAvatar(host),\n ]);\n scheduleChatScroll(host as unknown as Parameters[0], true);\n}\n\nexport const flushChatQueueForEvent = flushChatQueue;\n\ntype SessionDefaultsSnapshot = {\n defaultAgentId?: string;\n};\n\nfunction resolveAgentIdForSession(host: ChatHost): string | null {\n const parsed = parseAgentSessionKey(host.sessionKey);\n if (parsed?.agentId) return parsed.agentId;\n const snapshot = host.hello?.snapshot as { sessionDefaults?: SessionDefaultsSnapshot } | undefined;\n const fallback = snapshot?.sessionDefaults?.defaultAgentId?.trim();\n return fallback || \"main\";\n}\n\nfunction buildAvatarMetaUrl(basePath: string, agentId: string): string {\n const base = normalizeBasePath(basePath);\n const encoded = encodeURIComponent(agentId);\n return base ? `${base}/avatar/${encoded}?meta=1` : `/avatar/${encoded}?meta=1`;\n}\n\nexport async function refreshChatAvatar(host: ChatHost) {\n if (!host.connected) {\n host.chatAvatarUrl = null;\n return;\n }\n const agentId = resolveAgentIdForSession(host);\n if (!agentId) {\n host.chatAvatarUrl = null;\n return;\n }\n host.chatAvatarUrl = null;\n const url = buildAvatarMetaUrl(host.basePath, agentId);\n try {\n const res = await fetch(url, { method: \"GET\" });\n if (!res.ok) {\n host.chatAvatarUrl = null;\n return;\n }\n const data = (await res.json()) as { avatarUrl?: unknown };\n const avatarUrl = typeof data.avatarUrl === \"string\" ? data.avatarUrl.trim() : \"\";\n host.chatAvatarUrl = avatarUrl || null;\n } catch {\n host.chatAvatarUrl = null;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},e=t=>(...e)=>({_$litDirective$:t,values:e});class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}export{i as Directive,t as PartType,e as directive};\n//# sourceMappingURL=directive.js.map\n","import{_$LH as o}from\"./lit-html.js\";\n/**\n * @license\n * Copyright 2020 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const{I:t}=o,i=o=>o,n=o=>null===o||\"object\"!=typeof o&&\"function\"!=typeof o,e={HTML:1,SVG:2,MATHML:3},l=(o,t)=>void 0===t?void 0!==o?._$litType$:o?._$litType$===t,d=o=>null!=o?._$litType$?.h,c=o=>void 0!==o?._$litDirective$,f=o=>o?._$litDirective$,r=o=>void 0===o.strings,s=()=>document.createComment(\"\"),v=(o,n,e)=>{const l=o._$AA.parentNode,d=void 0===n?o._$AB:n._$AA;if(void 0===e){const i=l.insertBefore(s(),d),n=l.insertBefore(s(),d);e=new t(i,n,o,o.options)}else{const t=e._$AB.nextSibling,n=e._$AM,c=n!==o;if(c){let t;e._$AQ?.(o),e._$AM=o,void 0!==e._$AP&&(t=o._$AU)!==n._$AU&&e._$AP(t)}if(t!==d||c){let o=e._$AA;for(;o!==t;){const t=i(o).nextSibling;i(l).insertBefore(o,d),o=t}}}return e},u=(o,t,i=o)=>(o._$AI(t,i),o),m={},p=(o,t=m)=>o._$AH=t,M=o=>o._$AH,h=o=>{o._$AR(),o._$AA.remove()},j=o=>{o._$AR()};export{e as TemplateResultType,j as clearPart,M as getCommittedValue,f as getDirectiveClass,v as insertPart,d as isCompiledTemplateResult,c as isDirectiveResult,n as isPrimitive,r as isSingleExpression,l as isTemplateResult,h as removePart,u as setChildPartValue,p as setCommittedValue};\n//# sourceMappingURL=directive-helpers.js.map\n","import{noChange as e}from\"../lit-html.js\";import{directive as s,Directive as t,PartType as r}from\"../directive.js\";import{getCommittedValue as l,setChildPartValue as o,insertPart as i,removePart as n,setCommittedValue as f}from\"../directive-helpers.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst u=(e,s,t)=>{const r=new Map;for(let l=s;l<=t;l++)r.set(e[l],l);return r},c=s(class extends t{constructor(e){if(super(e),e.type!==r.CHILD)throw Error(\"repeat() can only be used in text expressions\")}dt(e,s,t){let r;void 0===t?t=s:void 0!==s&&(r=s);const l=[],o=[];let i=0;for(const s of e)l[i]=r?r(s,i):i,o[i]=t(s,i),i++;return{values:o,keys:l}}render(e,s,t){return this.dt(e,s,t).values}update(s,[t,r,c]){const d=l(s),{values:p,keys:a}=this.dt(t,r,c);if(!Array.isArray(d))return this.ut=a,p;const h=this.ut??=[],v=[];let m,y,x=0,j=d.length-1,k=0,w=p.length-1;for(;x<=j&&k<=w;)if(null===d[x])x++;else if(null===d[j])j--;else if(h[x]===a[k])v[k]=o(d[x],p[k]),x++,k++;else if(h[j]===a[w])v[w]=o(d[j],p[w]),j--,w--;else if(h[x]===a[w])v[w]=o(d[x],p[w]),i(s,v[w+1],d[x]),x++,w--;else if(h[j]===a[k])v[k]=o(d[j],p[k]),i(s,d[x],d[j]),j--,k++;else if(void 0===m&&(m=u(a,k,w),y=u(h,x,j)),m.has(h[x]))if(m.has(h[j])){const e=y.get(a[k]),t=void 0!==e?d[e]:null;if(null===t){const e=i(s,d[x]);o(e,p[k]),v[k]=e}else v[k]=o(t,p[k]),i(s,d[x],t),d[e]=null;k++}else n(d[j]),j--;else n(d[x]),x++;for(;k<=w;){const e=i(s,v[w+1]);o(e,p[k]),v[k++]=e}for(;x<=j;){const e=d[x++];null!==e&&n(e)}return this.ut=a,f(s,v),e}});export{c as repeat};\n//# sourceMappingURL=repeat.js.map\n","/**\n * Message normalization utilities for chat rendering.\n */\n\nimport type {\n NormalizedMessage,\n MessageContentItem,\n} from \"../types/chat-types\";\n\n/**\n * Normalize a raw message object into a consistent structure.\n */\nexport function normalizeMessage(message: unknown): NormalizedMessage {\n const m = message as Record;\n let role = typeof m.role === \"string\" ? m.role : \"unknown\";\n\n // Detect tool messages by common gateway shapes.\n // Some tool events come through as assistant role with tool_* items in the content array.\n const hasToolId =\n typeof m.toolCallId === \"string\" || typeof m.tool_call_id === \"string\";\n\n const contentRaw = m.content;\n const contentItems = Array.isArray(contentRaw) ? contentRaw : null;\n const hasToolContent =\n Array.isArray(contentItems) &&\n contentItems.some((item) => {\n const x = item as Record;\n const t = String(x.type ?? \"\").toLowerCase();\n return (\n t === \"toolcall\" ||\n t === \"tool_call\" ||\n t === \"tooluse\" ||\n t === \"tool_use\" ||\n t === \"toolresult\" ||\n t === \"tool_result\" ||\n t === \"tool_call\" ||\n t === \"tool_result\" ||\n (typeof x.name === \"string\" && x.arguments != null)\n );\n });\n\n const hasToolName =\n typeof (m as Record).toolName === \"string\" ||\n typeof (m as Record).tool_name === \"string\";\n\n if (hasToolId || hasToolContent || hasToolName) {\n role = \"toolResult\";\n }\n\n // Extract content\n let content: MessageContentItem[] = [];\n\n if (typeof m.content === \"string\") {\n content = [{ type: \"text\", text: m.content }];\n } else if (Array.isArray(m.content)) {\n content = m.content.map((item: Record) => ({\n type: (item.type as MessageContentItem[\"type\"]) || \"text\",\n text: item.text as string | undefined,\n name: item.name as string | undefined,\n args: item.args || item.arguments,\n }));\n } else if (typeof m.text === \"string\") {\n content = [{ type: \"text\", text: m.text }];\n }\n\n const timestamp = typeof m.timestamp === \"number\" ? m.timestamp : Date.now();\n const id = typeof m.id === \"string\" ? m.id : undefined;\n\n return { role, content, timestamp, id };\n}\n\n/**\n * Normalize role for grouping purposes.\n */\nexport function normalizeRoleForGrouping(role: string): string {\n const lower = role.toLowerCase();\n // Keep tool-related roles distinct so the UI can style/toggle them.\n if (\n lower === \"toolresult\" ||\n lower === \"tool_result\" ||\n lower === \"tool\" ||\n lower === \"function\" ||\n lower === \"toolresult\"\n ) {\n return \"tool\";\n }\n if (lower === \"assistant\") return \"assistant\";\n if (lower === \"user\") return \"user\";\n if (lower === \"system\") return \"system\";\n return role;\n}\n\n/**\n * Check if a message is a tool result message based on its role.\n */\nexport function isToolResultMessage(message: unknown): boolean {\n const m = message as Record;\n const role = typeof m.role === \"string\" ? m.role.toLowerCase() : \"\";\n return role === \"toolresult\" || role === \"tool_result\";\n}\n","import{nothing as t,noChange as i}from\"../lit-html.js\";import{directive as r,Directive as s,PartType as n}from\"../directive.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */class e extends s{constructor(i){if(super(i),this.it=t,i.type!==n.CHILD)throw Error(this.constructor.directiveName+\"() can only be used in child bindings\")}render(r){if(r===t||null==r)return this._t=void 0,this.it=r;if(r===i)return r;if(\"string\"!=typeof r)throw Error(this.constructor.directiveName+\"() called with a non-string value\");if(r===this.it)return this._t;this.it=r;const s=[r];return s.raw=s,this._t={_$litType$:this.constructor.resultType,strings:s,values:[]}}}e.directiveName=\"unsafeHTML\",e.resultType=1;const o=r(e);export{e as UnsafeHTMLDirective,o as unsafeHTML};\n//# sourceMappingURL=unsafe-html.js.map\n","/*! @license DOMPurify 3.3.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.3.1/LICENSE */\n\nconst {\n entries,\n setPrototypeOf,\n isFrozen,\n getPrototypeOf,\n getOwnPropertyDescriptor\n} = Object;\nlet {\n freeze,\n seal,\n create\n} = Object; // eslint-disable-line import/no-mutable-exports\nlet {\n apply,\n construct\n} = typeof Reflect !== 'undefined' && Reflect;\nif (!freeze) {\n freeze = function freeze(x) {\n return x;\n };\n}\nif (!seal) {\n seal = function seal(x) {\n return x;\n };\n}\nif (!apply) {\n apply = function apply(func, thisArg) {\n for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n args[_key - 2] = arguments[_key];\n }\n return func.apply(thisArg, args);\n };\n}\nif (!construct) {\n construct = function construct(Func) {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n return new Func(...args);\n };\n}\nconst arrayForEach = unapply(Array.prototype.forEach);\nconst arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);\nconst arrayPop = unapply(Array.prototype.pop);\nconst arrayPush = unapply(Array.prototype.push);\nconst arraySplice = unapply(Array.prototype.splice);\nconst stringToLowerCase = unapply(String.prototype.toLowerCase);\nconst stringToString = unapply(String.prototype.toString);\nconst stringMatch = unapply(String.prototype.match);\nconst stringReplace = unapply(String.prototype.replace);\nconst stringIndexOf = unapply(String.prototype.indexOf);\nconst stringTrim = unapply(String.prototype.trim);\nconst objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);\nconst regExpTest = unapply(RegExp.prototype.test);\nconst typeErrorCreate = unconstruct(TypeError);\n/**\n * Creates a new function that calls the given function with a specified thisArg and arguments.\n *\n * @param func - The function to be wrapped and called.\n * @returns A new function that calls the given function with a specified thisArg and arguments.\n */\nfunction unapply(func) {\n return function (thisArg) {\n if (thisArg instanceof RegExp) {\n thisArg.lastIndex = 0;\n }\n for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {\n args[_key3 - 1] = arguments[_key3];\n }\n return apply(func, thisArg, args);\n };\n}\n/**\n * Creates a new function that constructs an instance of the given constructor function with the provided arguments.\n *\n * @param func - The constructor function to be wrapped and called.\n * @returns A new function that constructs an instance of the given constructor function with the provided arguments.\n */\nfunction unconstruct(Func) {\n return function () {\n for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {\n args[_key4] = arguments[_key4];\n }\n return construct(Func, args);\n };\n}\n/**\n * Add properties to a lookup table\n *\n * @param set - The set to which elements will be added.\n * @param array - The array containing elements to be added to the set.\n * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.\n * @returns The modified set with added elements.\n */\nfunction addToSet(set, array) {\n let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;\n if (setPrototypeOf) {\n // Make 'in' and truthy checks like Boolean(set.constructor)\n // independent of any properties defined on Object.prototype.\n // Prevent prototype setters from intercepting set as a this value.\n setPrototypeOf(set, null);\n }\n let l = array.length;\n while (l--) {\n let element = array[l];\n if (typeof element === 'string') {\n const lcElement = transformCaseFunc(element);\n if (lcElement !== element) {\n // Config presets (e.g. tags.js, attrs.js) are immutable.\n if (!isFrozen(array)) {\n array[l] = lcElement;\n }\n element = lcElement;\n }\n }\n set[element] = true;\n }\n return set;\n}\n/**\n * Clean up an array to harden against CSPP\n *\n * @param array - The array to be cleaned.\n * @returns The cleaned version of the array\n */\nfunction cleanArray(array) {\n for (let index = 0; index < array.length; index++) {\n const isPropertyExist = objectHasOwnProperty(array, index);\n if (!isPropertyExist) {\n array[index] = null;\n }\n }\n return array;\n}\n/**\n * Shallow clone an object\n *\n * @param object - The object to be cloned.\n * @returns A new object that copies the original.\n */\nfunction clone(object) {\n const newObject = create(null);\n for (const [property, value] of entries(object)) {\n const isPropertyExist = objectHasOwnProperty(object, property);\n if (isPropertyExist) {\n if (Array.isArray(value)) {\n newObject[property] = cleanArray(value);\n } else if (value && typeof value === 'object' && value.constructor === Object) {\n newObject[property] = clone(value);\n } else {\n newObject[property] = value;\n }\n }\n }\n return newObject;\n}\n/**\n * This method automatically checks if the prop is function or getter and behaves accordingly.\n *\n * @param object - The object to look up the getter function in its prototype chain.\n * @param prop - The property name for which to find the getter function.\n * @returns The getter function found in the prototype chain or a fallback function.\n */\nfunction lookupGetter(object, prop) {\n while (object !== null) {\n const desc = getOwnPropertyDescriptor(object, prop);\n if (desc) {\n if (desc.get) {\n return unapply(desc.get);\n }\n if (typeof desc.value === 'function') {\n return unapply(desc.value);\n }\n }\n object = getPrototypeOf(object);\n }\n function fallbackValue() {\n return null;\n }\n return fallbackValue;\n}\n\nconst html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);\nconst svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);\nconst svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);\n// List of SVG elements that are disallowed by default.\n// We still need to know them so that we can do namespace\n// checks properly in case one wants to add them to\n// allow-list.\nconst svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);\nconst mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);\n// Similarly to SVG, we want to know all MathML elements,\n// even those that we disallow by default.\nconst mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);\nconst text = freeze(['#text']);\n\nconst html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);\nconst svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);\nconst mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);\nconst xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);\n\n// eslint-disable-next-line unicorn/better-regex\nconst MUSTACHE_EXPR = seal(/\\{\\{[\\w\\W]*|[\\w\\W]*\\}\\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode\nconst ERB_EXPR = seal(/<%[\\w\\W]*|[\\w\\W]*%>/gm);\nconst TMPLIT_EXPR = seal(/\\$\\{[\\w\\W]*/gm); // eslint-disable-line unicorn/better-regex\nconst DATA_ATTR = seal(/^data-[\\-\\w.\\u00B7-\\uFFFF]+$/); // eslint-disable-line no-useless-escape\nconst ARIA_ATTR = seal(/^aria-[\\-\\w]+$/); // eslint-disable-line no-useless-escape\nconst IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i // eslint-disable-line no-useless-escape\n);\nconst IS_SCRIPT_OR_DATA = seal(/^(?:\\w+script|data):/i);\nconst ATTR_WHITESPACE = seal(/[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g // eslint-disable-line no-control-regex\n);\nconst DOCTYPE_NAME = seal(/^html$/i);\nconst CUSTOM_ELEMENT = seal(/^[a-z][.\\w]*(-[.\\w]+)+$/i);\n\nvar EXPRESSIONS = /*#__PURE__*/Object.freeze({\n __proto__: null,\n ARIA_ATTR: ARIA_ATTR,\n ATTR_WHITESPACE: ATTR_WHITESPACE,\n CUSTOM_ELEMENT: CUSTOM_ELEMENT,\n DATA_ATTR: DATA_ATTR,\n DOCTYPE_NAME: DOCTYPE_NAME,\n ERB_EXPR: ERB_EXPR,\n IS_ALLOWED_URI: IS_ALLOWED_URI,\n IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,\n MUSTACHE_EXPR: MUSTACHE_EXPR,\n TMPLIT_EXPR: TMPLIT_EXPR\n});\n\n/* eslint-disable @typescript-eslint/indent */\n// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType\nconst NODE_TYPE = {\n element: 1,\n attribute: 2,\n text: 3,\n cdataSection: 4,\n entityReference: 5,\n // Deprecated\n entityNode: 6,\n // Deprecated\n progressingInstruction: 7,\n comment: 8,\n document: 9,\n documentType: 10,\n documentFragment: 11,\n notation: 12 // Deprecated\n};\nconst getGlobal = function getGlobal() {\n return typeof window === 'undefined' ? null : window;\n};\n/**\n * Creates a no-op policy for internal use only.\n * Don't export this function outside this module!\n * @param trustedTypes The policy factory.\n * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).\n * @return The policy created (or null, if Trusted Types\n * are not supported or creating the policy failed).\n */\nconst _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {\n if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {\n return null;\n }\n // Allow the callers to control the unique policy name\n // by adding a data-tt-policy-suffix to the script element with the DOMPurify.\n // Policy creation with duplicate names throws in Trusted Types.\n let suffix = null;\n const ATTR_NAME = 'data-tt-policy-suffix';\n if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {\n suffix = purifyHostElement.getAttribute(ATTR_NAME);\n }\n const policyName = 'dompurify' + (suffix ? '#' + suffix : '');\n try {\n return trustedTypes.createPolicy(policyName, {\n createHTML(html) {\n return html;\n },\n createScriptURL(scriptUrl) {\n return scriptUrl;\n }\n });\n } catch (_) {\n // Policy creation failed (most likely another DOMPurify script has\n // already run). Skip creating the policy, as this will only cause errors\n // if TT are enforced.\n console.warn('TrustedTypes policy ' + policyName + ' could not be created.');\n return null;\n }\n};\nconst _createHooksMap = function _createHooksMap() {\n return {\n afterSanitizeAttributes: [],\n afterSanitizeElements: [],\n afterSanitizeShadowDOM: [],\n beforeSanitizeAttributes: [],\n beforeSanitizeElements: [],\n beforeSanitizeShadowDOM: [],\n uponSanitizeAttribute: [],\n uponSanitizeElement: [],\n uponSanitizeShadowNode: []\n };\n};\nfunction createDOMPurify() {\n let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();\n const DOMPurify = root => createDOMPurify(root);\n DOMPurify.version = '3.3.1';\n DOMPurify.removed = [];\n if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {\n // Not running in a browser, provide a factory function\n // so that you can pass your own Window\n DOMPurify.isSupported = false;\n return DOMPurify;\n }\n let {\n document\n } = window;\n const originalDocument = document;\n const currentScript = originalDocument.currentScript;\n const {\n DocumentFragment,\n HTMLTemplateElement,\n Node,\n Element,\n NodeFilter,\n NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,\n HTMLFormElement,\n DOMParser,\n trustedTypes\n } = window;\n const ElementPrototype = Element.prototype;\n const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');\n const remove = lookupGetter(ElementPrototype, 'remove');\n const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');\n const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');\n const getParentNode = lookupGetter(ElementPrototype, 'parentNode');\n // As per issue #47, the web-components registry is inherited by a\n // new document created via createHTMLDocument. As per the spec\n // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)\n // a new empty registry is used when creating a template contents owner\n // document, so we use that as our parent document to ensure nothing\n // is inherited.\n if (typeof HTMLTemplateElement === 'function') {\n const template = document.createElement('template');\n if (template.content && template.content.ownerDocument) {\n document = template.content.ownerDocument;\n }\n }\n let trustedTypesPolicy;\n let emptyHTML = '';\n const {\n implementation,\n createNodeIterator,\n createDocumentFragment,\n getElementsByTagName\n } = document;\n const {\n importNode\n } = originalDocument;\n let hooks = _createHooksMap();\n /**\n * Expose whether this browser supports running the full DOMPurify.\n */\n DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;\n const {\n MUSTACHE_EXPR,\n ERB_EXPR,\n TMPLIT_EXPR,\n DATA_ATTR,\n ARIA_ATTR,\n IS_SCRIPT_OR_DATA,\n ATTR_WHITESPACE,\n CUSTOM_ELEMENT\n } = EXPRESSIONS;\n let {\n IS_ALLOWED_URI: IS_ALLOWED_URI$1\n } = EXPRESSIONS;\n /**\n * We consider the elements and attributes below to be safe. Ideally\n * don't add any new ones but feel free to remove unwanted ones.\n */\n /* allowed element names */\n let ALLOWED_TAGS = null;\n const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);\n /* Allowed attribute names */\n let ALLOWED_ATTR = null;\n const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);\n /*\n * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.\n * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)\n * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)\n * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.\n */\n let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {\n tagNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n allowCustomizedBuiltInElements: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: false\n }\n }));\n /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */\n let FORBID_TAGS = null;\n /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */\n let FORBID_ATTR = null;\n /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */\n const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {\n tagCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n }\n }));\n /* Decide if ARIA attributes are okay */\n let ALLOW_ARIA_ATTR = true;\n /* Decide if custom data attributes are okay */\n let ALLOW_DATA_ATTR = true;\n /* Decide if unknown protocols are okay */\n let ALLOW_UNKNOWN_PROTOCOLS = false;\n /* Decide if self-closing tags in attributes are allowed.\n * Usually removed due to a mXSS issue in jQuery 3.0 */\n let ALLOW_SELF_CLOSE_IN_ATTR = true;\n /* Output should be safe for common template engines.\n * This means, DOMPurify removes data attributes, mustaches and ERB\n */\n let SAFE_FOR_TEMPLATES = false;\n /* Output should be safe even for XML used within HTML and alike.\n * This means, DOMPurify removes comments when containing risky content.\n */\n let SAFE_FOR_XML = true;\n /* Decide if document with ... should be returned */\n let WHOLE_DOCUMENT = false;\n /* Track whether config is already set on this instance of DOMPurify. */\n let SET_CONFIG = false;\n /* Decide if all elements (e.g. style, script) must be children of\n * document.body. By default, browsers might move them to document.head */\n let FORCE_BODY = false;\n /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported).\n * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead\n */\n let RETURN_DOM = false;\n /* Decide if a DOM `DocumentFragment` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported) */\n let RETURN_DOM_FRAGMENT = false;\n /* Try to return a Trusted Type object instead of a string, return a string in\n * case Trusted Types are not supported */\n let RETURN_TRUSTED_TYPE = false;\n /* Output should be free from DOM clobbering attacks?\n * This sanitizes markups named with colliding, clobberable built-in DOM APIs.\n */\n let SANITIZE_DOM = true;\n /* Achieve full DOM Clobbering protection by isolating the namespace of named\n * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.\n *\n * HTML/DOM spec rules that enable DOM Clobbering:\n * - Named Access on Window (§7.3.3)\n * - DOM Tree Accessors (§3.1.5)\n * - Form Element Parent-Child Relations (§4.10.3)\n * - Iframe srcdoc / Nested WindowProxies (§4.8.5)\n * - HTMLCollection (§4.2.10.2)\n *\n * Namespace isolation is implemented by prefixing `id` and `name` attributes\n * with a constant string, i.e., `user-content-`\n */\n let SANITIZE_NAMED_PROPS = false;\n const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';\n /* Keep element content when removing element? */\n let KEEP_CONTENT = true;\n /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead\n * of importing it into a new Document and returning a sanitized copy */\n let IN_PLACE = false;\n /* Allow usage of profiles like html, svg and mathMl */\n let USE_PROFILES = {};\n /* Tags to ignore content of when KEEP_CONTENT is true */\n let FORBID_CONTENTS = null;\n const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);\n /* Tags that are safe for data: URIs */\n let DATA_URI_TAGS = null;\n const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);\n /* Attributes safe for values like \"javascript:\" */\n let URI_SAFE_ATTRIBUTES = null;\n const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);\n const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n /* Document namespace */\n let NAMESPACE = HTML_NAMESPACE;\n let IS_EMPTY_INPUT = false;\n /* Allowed XHTML+XML namespaces */\n let ALLOWED_NAMESPACES = null;\n const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);\n let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);\n let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);\n // Certain elements are allowed in both SVG and HTML\n // namespace. We need to specify them explicitly\n // so that they don't get erroneously deleted from\n // HTML namespace.\n const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);\n /* Parsing of strict XHTML documents */\n let PARSER_MEDIA_TYPE = null;\n const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];\n const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';\n let transformCaseFunc = null;\n /* Keep a reference to config to pass to hooks */\n let CONFIG = null;\n /* Ideally, do not touch anything below this line */\n /* ______________________________________________ */\n const formElement = document.createElement('form');\n const isRegexOrFunction = function isRegexOrFunction(testValue) {\n return testValue instanceof RegExp || testValue instanceof Function;\n };\n /**\n * _parseConfig\n *\n * @param cfg optional config literal\n */\n // eslint-disable-next-line complexity\n const _parseConfig = function _parseConfig() {\n let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n if (CONFIG && CONFIG === cfg) {\n return;\n }\n /* Shield configuration object from tampering */\n if (!cfg || typeof cfg !== 'object') {\n cfg = {};\n }\n /* Shield configuration object from prototype pollution */\n cfg = clone(cfg);\n PARSER_MEDIA_TYPE =\n // eslint-disable-next-line unicorn/prefer-includes\n SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;\n // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.\n transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;\n /* Set configuration parameters */\n ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;\n ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;\n ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;\n URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;\n DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;\n FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;\n FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});\n FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});\n USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;\n ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true\n ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true\n ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false\n ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true\n SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false\n SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true\n WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false\n RETURN_DOM = cfg.RETURN_DOM || false; // Default false\n RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false\n RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false\n FORCE_BODY = cfg.FORCE_BODY || false; // Default false\n SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true\n SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false\n KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true\n IN_PLACE = cfg.IN_PLACE || false; // Default false\n IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;\n NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;\n MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;\n HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;\n CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};\n if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {\n CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;\n }\n if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {\n CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;\n }\n if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {\n CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;\n }\n if (SAFE_FOR_TEMPLATES) {\n ALLOW_DATA_ATTR = false;\n }\n if (RETURN_DOM_FRAGMENT) {\n RETURN_DOM = true;\n }\n /* Parse profile info */\n if (USE_PROFILES) {\n ALLOWED_TAGS = addToSet({}, text);\n ALLOWED_ATTR = [];\n if (USE_PROFILES.html === true) {\n addToSet(ALLOWED_TAGS, html$1);\n addToSet(ALLOWED_ATTR, html);\n }\n if (USE_PROFILES.svg === true) {\n addToSet(ALLOWED_TAGS, svg$1);\n addToSet(ALLOWED_ATTR, svg);\n addToSet(ALLOWED_ATTR, xml);\n }\n if (USE_PROFILES.svgFilters === true) {\n addToSet(ALLOWED_TAGS, svgFilters);\n addToSet(ALLOWED_ATTR, svg);\n addToSet(ALLOWED_ATTR, xml);\n }\n if (USE_PROFILES.mathMl === true) {\n addToSet(ALLOWED_TAGS, mathMl$1);\n addToSet(ALLOWED_ATTR, mathMl);\n addToSet(ALLOWED_ATTR, xml);\n }\n }\n /* Merge configuration parameters */\n if (cfg.ADD_TAGS) {\n if (typeof cfg.ADD_TAGS === 'function') {\n EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;\n } else {\n if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {\n ALLOWED_TAGS = clone(ALLOWED_TAGS);\n }\n addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);\n }\n }\n if (cfg.ADD_ATTR) {\n if (typeof cfg.ADD_ATTR === 'function') {\n EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;\n } else {\n if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {\n ALLOWED_ATTR = clone(ALLOWED_ATTR);\n }\n addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);\n }\n }\n if (cfg.ADD_URI_SAFE_ATTR) {\n addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);\n }\n if (cfg.FORBID_CONTENTS) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);\n }\n if (cfg.ADD_FORBID_CONTENTS) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);\n }\n /* Add #text in case KEEP_CONTENT is set to true */\n if (KEEP_CONTENT) {\n ALLOWED_TAGS['#text'] = true;\n }\n /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */\n if (WHOLE_DOCUMENT) {\n addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);\n }\n /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */\n if (ALLOWED_TAGS.table) {\n addToSet(ALLOWED_TAGS, ['tbody']);\n delete FORBID_TAGS.tbody;\n }\n if (cfg.TRUSTED_TYPES_POLICY) {\n if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {\n throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a \"createHTML\" hook.');\n }\n if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {\n throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a \"createScriptURL\" hook.');\n }\n // Overwrite existing TrustedTypes policy.\n trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;\n // Sign local variables required by `sanitize`.\n emptyHTML = trustedTypesPolicy.createHTML('');\n } else {\n // Uninitialized policy, attempt to initialize the internal dompurify policy.\n if (trustedTypesPolicy === undefined) {\n trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);\n }\n // If creating the internal policy succeeded sign internal variables.\n if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {\n emptyHTML = trustedTypesPolicy.createHTML('');\n }\n }\n // Prevent further manipulation of configuration.\n // Not available in IE8, Safari 5, etc.\n if (freeze) {\n freeze(cfg);\n }\n CONFIG = cfg;\n };\n /* Keep track of all possible SVG and MathML tags\n * so that we can perform the namespace checks\n * correctly. */\n const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);\n const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);\n /**\n * @param element a DOM element whose namespace is being checked\n * @returns Return false if the element has a\n * namespace that a spec-compliant parser would never\n * return. Return true otherwise.\n */\n const _checkValidNamespace = function _checkValidNamespace(element) {\n let parent = getParentNode(element);\n // In JSDOM, if we're inside shadow DOM, then parentNode\n // can be null. We just simulate parent in this case.\n if (!parent || !parent.tagName) {\n parent = {\n namespaceURI: NAMESPACE,\n tagName: 'template'\n };\n }\n const tagName = stringToLowerCase(element.tagName);\n const parentTagName = stringToLowerCase(parent.tagName);\n if (!ALLOWED_NAMESPACES[element.namespaceURI]) {\n return false;\n }\n if (element.namespaceURI === SVG_NAMESPACE) {\n // The only way to switch from HTML namespace to SVG\n // is via . If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'svg';\n }\n // The only way to switch from MathML to SVG is via`\n // svg if parent is either or MathML\n // text integration points.\n if (parent.namespaceURI === MATHML_NAMESPACE) {\n return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);\n }\n // We only allow elements that are defined in SVG\n // spec. All others are disallowed in SVG namespace.\n return Boolean(ALL_SVG_TAGS[tagName]);\n }\n if (element.namespaceURI === MATHML_NAMESPACE) {\n // The only way to switch from HTML namespace to MathML\n // is via . If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'math';\n }\n // The only way to switch from SVG to MathML is via\n // and HTML integration points\n if (parent.namespaceURI === SVG_NAMESPACE) {\n return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];\n }\n // We only allow elements that are defined in MathML\n // spec. All others are disallowed in MathML namespace.\n return Boolean(ALL_MATHML_TAGS[tagName]);\n }\n if (element.namespaceURI === HTML_NAMESPACE) {\n // The only way to switch from SVG to HTML is via\n // HTML integration points, and from MathML to HTML\n // is via MathML text integration points\n if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {\n return false;\n }\n if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {\n return false;\n }\n // We disallow tags that are specific for MathML\n // or SVG and should never appear in HTML namespace\n return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);\n }\n // For XHTML and XML documents that support custom namespaces\n if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {\n return true;\n }\n // The code should never reach this place (this means\n // that the element somehow got namespace that is not\n // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).\n // Return false just in case.\n return false;\n };\n /**\n * _forceRemove\n *\n * @param node a DOM node\n */\n const _forceRemove = function _forceRemove(node) {\n arrayPush(DOMPurify.removed, {\n element: node\n });\n try {\n // eslint-disable-next-line unicorn/prefer-dom-node-remove\n getParentNode(node).removeChild(node);\n } catch (_) {\n remove(node);\n }\n };\n /**\n * _removeAttribute\n *\n * @param name an Attribute name\n * @param element a DOM node\n */\n const _removeAttribute = function _removeAttribute(name, element) {\n try {\n arrayPush(DOMPurify.removed, {\n attribute: element.getAttributeNode(name),\n from: element\n });\n } catch (_) {\n arrayPush(DOMPurify.removed, {\n attribute: null,\n from: element\n });\n }\n element.removeAttribute(name);\n // We void attribute values for unremovable \"is\" attributes\n if (name === 'is') {\n if (RETURN_DOM || RETURN_DOM_FRAGMENT) {\n try {\n _forceRemove(element);\n } catch (_) {}\n } else {\n try {\n element.setAttribute(name, '');\n } catch (_) {}\n }\n }\n };\n /**\n * _initDocument\n *\n * @param dirty - a string of dirty markup\n * @return a DOM, filled with the dirty markup\n */\n const _initDocument = function _initDocument(dirty) {\n /* Create a HTML document */\n let doc = null;\n let leadingWhitespace = null;\n if (FORCE_BODY) {\n dirty = '' + dirty;\n } else {\n /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */\n const matches = stringMatch(dirty, /^[\\r\\n\\t ]+/);\n leadingWhitespace = matches && matches[0];\n }\n if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {\n // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)\n dirty = '' + dirty + '';\n }\n const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;\n /*\n * Use the DOMParser API by default, fallback later if needs be\n * DOMParser not work for svg when has multiple root element.\n */\n if (NAMESPACE === HTML_NAMESPACE) {\n try {\n doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);\n } catch (_) {}\n }\n /* Use createHTMLDocument in case DOMParser is not available */\n if (!doc || !doc.documentElement) {\n doc = implementation.createDocument(NAMESPACE, 'template', null);\n try {\n doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;\n } catch (_) {\n // Syntax error if dirtyPayload is invalid xml\n }\n }\n const body = doc.body || doc.documentElement;\n if (dirty && leadingWhitespace) {\n body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);\n }\n /* Work on whole document or just its body */\n if (NAMESPACE === HTML_NAMESPACE) {\n return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];\n }\n return WHOLE_DOCUMENT ? doc.documentElement : body;\n };\n /**\n * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.\n *\n * @param root The root element or node to start traversing on.\n * @return The created NodeIterator\n */\n const _createNodeIterator = function _createNodeIterator(root) {\n return createNodeIterator.call(root.ownerDocument || root, root,\n // eslint-disable-next-line no-bitwise\n NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);\n };\n /**\n * _isClobbered\n *\n * @param element element to check for clobbering attacks\n * @return true if clobbered, false if safe\n */\n const _isClobbered = function _isClobbered(element) {\n return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');\n };\n /**\n * Checks whether the given object is a DOM node.\n *\n * @param value object to check whether it's a DOM node\n * @return true is object is a DOM node\n */\n const _isNode = function _isNode(value) {\n return typeof Node === 'function' && value instanceof Node;\n };\n function _executeHooks(hooks, currentNode, data) {\n arrayForEach(hooks, hook => {\n hook.call(DOMPurify, currentNode, data, CONFIG);\n });\n }\n /**\n * _sanitizeElements\n *\n * @protect nodeName\n * @protect textContent\n * @protect removeChild\n * @param currentNode to check for permission to exist\n * @return true if node was killed, false if left alive\n */\n const _sanitizeElements = function _sanitizeElements(currentNode) {\n let content = null;\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeElements, currentNode, null);\n /* Check if element is clobbered or can clobber */\n if (_isClobbered(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Now let's check the element's type and name */\n const tagName = transformCaseFunc(currentNode.nodeName);\n /* Execute a hook if present */\n _executeHooks(hooks.uponSanitizeElement, currentNode, {\n tagName,\n allowedTags: ALLOWED_TAGS\n });\n /* Detect mXSS attempts abusing namespace confusion */\n if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\\w!]/g, currentNode.textContent)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove any occurrence of processing instructions */\n if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove any kind of possibly harmful comments */\n if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\\w]/g, currentNode.data)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove element if anything forbids its presence */\n if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {\n /* Check if we have a custom element to handle */\n if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {\n if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {\n return false;\n }\n if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {\n return false;\n }\n }\n /* Keep content except for bad-listed elements */\n if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {\n const parentNode = getParentNode(currentNode) || currentNode.parentNode;\n const childNodes = getChildNodes(currentNode) || currentNode.childNodes;\n if (childNodes && parentNode) {\n const childCount = childNodes.length;\n for (let i = childCount - 1; i >= 0; --i) {\n const childClone = cloneNode(childNodes[i], true);\n childClone.__removalCount = (currentNode.__removalCount || 0) + 1;\n parentNode.insertBefore(childClone, getNextSibling(currentNode));\n }\n }\n }\n _forceRemove(currentNode);\n return true;\n }\n /* Check whether element has a valid namespace */\n if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Make sure that older browsers don't get fallback-tag mXSS */\n if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\\/no(script|embed|frames)/i, currentNode.innerHTML)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Sanitize element content to be template-safe */\n if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {\n /* Get the element's text content */\n content = currentNode.textContent;\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n content = stringReplace(content, expr, ' ');\n });\n if (currentNode.textContent !== content) {\n arrayPush(DOMPurify.removed, {\n element: currentNode.cloneNode()\n });\n currentNode.textContent = content;\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeElements, currentNode, null);\n return false;\n };\n /**\n * _isValidAttribute\n *\n * @param lcTag Lowercase tag name of containing element.\n * @param lcName Lowercase attribute name.\n * @param value Attribute value.\n * @return Returns true if `value` is valid, otherwise false.\n */\n // eslint-disable-next-line complexity\n const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {\n /* Make sure attribute cannot clobber */\n if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {\n return false;\n }\n /* Allow valid data-* attributes: At least one character after \"-\"\n (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)\n XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)\n We don't need to check the value; it's always URI safe. */\n if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {\n if (\n // First condition does a very basic check if a) it's basically a valid custom element tagname AND\n // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck\n // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck\n _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName, lcTag)) ||\n // Alternative, second condition checks if it's an `is`-attribute, AND\n // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck\n lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {\n return false;\n }\n /* Check value is safe. First, is attr inert? If so, is safe */\n } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {\n return false;\n } else ;\n return true;\n };\n /**\n * _isBasicCustomElement\n * checks if at least one dash is included in tagName, and it's not the first char\n * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name\n *\n * @param tagName name of the tag of the node to sanitize\n * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.\n */\n const _isBasicCustomElement = function _isBasicCustomElement(tagName) {\n return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);\n };\n /**\n * _sanitizeAttributes\n *\n * @protect attributes\n * @protect nodeName\n * @protect removeAttribute\n * @protect setAttribute\n *\n * @param currentNode to sanitize\n */\n const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);\n const {\n attributes\n } = currentNode;\n /* Check if we have attributes; if not we might have a text node */\n if (!attributes || _isClobbered(currentNode)) {\n return;\n }\n const hookEvent = {\n attrName: '',\n attrValue: '',\n keepAttr: true,\n allowedAttributes: ALLOWED_ATTR,\n forceKeepAttr: undefined\n };\n let l = attributes.length;\n /* Go backwards over all attributes; safely remove bad ones */\n while (l--) {\n const attr = attributes[l];\n const {\n name,\n namespaceURI,\n value: attrValue\n } = attr;\n const lcName = transformCaseFunc(name);\n const initValue = attrValue;\n let value = name === 'value' ? initValue : stringTrim(initValue);\n /* Execute a hook if present */\n hookEvent.attrName = lcName;\n hookEvent.attrValue = value;\n hookEvent.keepAttr = true;\n hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set\n _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);\n value = hookEvent.attrValue;\n /* Full DOM Clobbering protection via namespace isolation,\n * Prefix id and name attributes with `user-content-`\n */\n if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {\n // Remove the attribute with this value\n _removeAttribute(name, currentNode);\n // Prefix the value and later re-create the attribute with the sanitized value\n value = SANITIZE_NAMED_PROPS_PREFIX + value;\n }\n /* Work around a security issue with comments inside attributes */\n if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\\/(style|title|textarea)/i, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Make sure we cannot easily use animated hrefs, even if animations are allowed */\n if (lcName === 'attributename' && stringMatch(value, 'href')) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Did the hooks approve of the attribute? */\n if (hookEvent.forceKeepAttr) {\n continue;\n }\n /* Did the hooks approve of the attribute? */\n if (!hookEvent.keepAttr) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Work around a security issue in jQuery 3.0 */\n if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\\/>/i, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Sanitize attribute content to be template-safe */\n if (SAFE_FOR_TEMPLATES) {\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n value = stringReplace(value, expr, ' ');\n });\n }\n /* Is `value` valid for this attribute? */\n const lcTag = transformCaseFunc(currentNode.nodeName);\n if (!_isValidAttribute(lcTag, lcName, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Handle attributes that require Trusted Types */\n if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {\n if (namespaceURI) ; else {\n switch (trustedTypes.getAttributeType(lcTag, lcName)) {\n case 'TrustedHTML':\n {\n value = trustedTypesPolicy.createHTML(value);\n break;\n }\n case 'TrustedScriptURL':\n {\n value = trustedTypesPolicy.createScriptURL(value);\n break;\n }\n }\n }\n }\n /* Handle invalid data-* attribute set by try-catching it */\n if (value !== initValue) {\n try {\n if (namespaceURI) {\n currentNode.setAttributeNS(namespaceURI, name, value);\n } else {\n /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. \"x-schema\". */\n currentNode.setAttribute(name, value);\n }\n if (_isClobbered(currentNode)) {\n _forceRemove(currentNode);\n } else {\n arrayPop(DOMPurify.removed);\n }\n } catch (_) {\n _removeAttribute(name, currentNode);\n }\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);\n };\n /**\n * _sanitizeShadowDOM\n *\n * @param fragment to iterate over recursively\n */\n const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {\n let shadowNode = null;\n const shadowIterator = _createNodeIterator(fragment);\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);\n while (shadowNode = shadowIterator.nextNode()) {\n /* Execute a hook if present */\n _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);\n /* Sanitize tags and elements */\n _sanitizeElements(shadowNode);\n /* Check attributes next */\n _sanitizeAttributes(shadowNode);\n /* Deep shadow DOM detected */\n if (shadowNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(shadowNode.content);\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);\n };\n // eslint-disable-next-line complexity\n DOMPurify.sanitize = function (dirty) {\n let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n let body = null;\n let importedNode = null;\n let currentNode = null;\n let returnNode = null;\n /* Make sure we have a string to sanitize.\n DO NOT return early, as this will return the wrong type if\n the user has requested a DOM object rather than a string */\n IS_EMPTY_INPUT = !dirty;\n if (IS_EMPTY_INPUT) {\n dirty = '';\n }\n /* Stringify, in case dirty is an object */\n if (typeof dirty !== 'string' && !_isNode(dirty)) {\n if (typeof dirty.toString === 'function') {\n dirty = dirty.toString();\n if (typeof dirty !== 'string') {\n throw typeErrorCreate('dirty is not a string, aborting');\n }\n } else {\n throw typeErrorCreate('toString is not a function');\n }\n }\n /* Return dirty HTML if DOMPurify cannot run */\n if (!DOMPurify.isSupported) {\n return dirty;\n }\n /* Assign config vars */\n if (!SET_CONFIG) {\n _parseConfig(cfg);\n }\n /* Clean up removed elements */\n DOMPurify.removed = [];\n /* Check if dirty is correctly typed for IN_PLACE */\n if (typeof dirty === 'string') {\n IN_PLACE = false;\n }\n if (IN_PLACE) {\n /* Do some early pre-sanitization to avoid unsafe root nodes */\n if (dirty.nodeName) {\n const tagName = transformCaseFunc(dirty.nodeName);\n if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {\n throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');\n }\n }\n } else if (dirty instanceof Node) {\n /* If dirty is a DOM element, append to an empty document to avoid\n elements being stripped by the parser */\n body = _initDocument('');\n importedNode = body.ownerDocument.importNode(dirty, true);\n if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {\n /* Node is already a body, use as is */\n body = importedNode;\n } else if (importedNode.nodeName === 'HTML') {\n body = importedNode;\n } else {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n body.appendChild(importedNode);\n }\n } else {\n /* Exit directly if we have nothing to do */\n if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&\n // eslint-disable-next-line unicorn/prefer-includes\n dirty.indexOf('<') === -1) {\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;\n }\n /* Initialize the document to work on */\n body = _initDocument(dirty);\n /* Check we have a DOM node from the data */\n if (!body) {\n return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';\n }\n }\n /* Remove first element node (ours) if FORCE_BODY is set */\n if (body && FORCE_BODY) {\n _forceRemove(body.firstChild);\n }\n /* Get node iterator */\n const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);\n /* Now start iterating over the created document */\n while (currentNode = nodeIterator.nextNode()) {\n /* Sanitize tags and elements */\n _sanitizeElements(currentNode);\n /* Check attributes next */\n _sanitizeAttributes(currentNode);\n /* Shadow DOM detected, sanitize it */\n if (currentNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(currentNode.content);\n }\n }\n /* If we sanitized `dirty` in-place, return it. */\n if (IN_PLACE) {\n return dirty;\n }\n /* Return sanitized string or DOM */\n if (RETURN_DOM) {\n if (RETURN_DOM_FRAGMENT) {\n returnNode = createDocumentFragment.call(body.ownerDocument);\n while (body.firstChild) {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n returnNode.appendChild(body.firstChild);\n }\n } else {\n returnNode = body;\n }\n if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {\n /*\n AdoptNode() is not used because internal state is not reset\n (e.g. the past names map of a HTMLFormElement), this is safe\n in theory but we would rather not risk another attack vector.\n The state that is cloned by importNode() is explicitly defined\n by the specs.\n */\n returnNode = importNode.call(originalDocument, returnNode, true);\n }\n return returnNode;\n }\n let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;\n /* Serialize doctype if allowed */\n if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {\n serializedHTML = '\\n' + serializedHTML;\n }\n /* Sanitize final string template-safe */\n if (SAFE_FOR_TEMPLATES) {\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n serializedHTML = stringReplace(serializedHTML, expr, ' ');\n });\n }\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;\n };\n DOMPurify.setConfig = function () {\n let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _parseConfig(cfg);\n SET_CONFIG = true;\n };\n DOMPurify.clearConfig = function () {\n CONFIG = null;\n SET_CONFIG = false;\n };\n DOMPurify.isValidAttribute = function (tag, attr, value) {\n /* Initialize shared config vars if necessary. */\n if (!CONFIG) {\n _parseConfig({});\n }\n const lcTag = transformCaseFunc(tag);\n const lcName = transformCaseFunc(attr);\n return _isValidAttribute(lcTag, lcName, value);\n };\n DOMPurify.addHook = function (entryPoint, hookFunction) {\n if (typeof hookFunction !== 'function') {\n return;\n }\n arrayPush(hooks[entryPoint], hookFunction);\n };\n DOMPurify.removeHook = function (entryPoint, hookFunction) {\n if (hookFunction !== undefined) {\n const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);\n return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];\n }\n return arrayPop(hooks[entryPoint]);\n };\n DOMPurify.removeHooks = function (entryPoint) {\n hooks[entryPoint] = [];\n };\n DOMPurify.removeAllHooks = function () {\n hooks = _createHooksMap();\n };\n return DOMPurify;\n}\nvar purify = createDOMPurify();\n\nexport { purify as default };\n//# sourceMappingURL=purify.es.mjs.map\n","/**\n * marked v17.0.1 - a markdown parser\n * Copyright (c) 2018-2025, MarkedJS. (MIT License)\n * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License)\n * https://github.com/markedjs/marked\n */\n\n/**\n * DO NOT EDIT THIS FILE\n * The code in this file is generated from files in ./src/\n */\n\nfunction L(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var T=L();function Z(u){T=u}var C={exec:()=>null};function k(u,e=\"\"){let t=typeof u==\"string\"?u:u.source,n={replace:(r,i)=>{let s=typeof i==\"string\"?i:i.source;return s=s.replace(m.caret,\"$1\"),t=t.replace(r,s),n},getRegex:()=>new RegExp(t,e)};return n}var me=(()=>{try{return!!new RegExp(\"(?<=1)(?/,blockquoteSetextReplace:/\\n {0,3}((?:=+|-+) *)(?=\\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \\t]?/gm,listReplaceTabs:/^\\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\\[[ xX]\\] +\\S/,listReplaceTask:/^\\[[ xX]\\] +/,listTaskCheckbox:/\\[[ xX]\\]/,anyLine:/\\n.*\\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\\||\\| *$/g,tableRowBlankLine:/\\n[ \\t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\\s|>)/i,endPreScriptTag:/^<\\/(pre|code|kbd|script)(\\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'\"]*[^\\s])\\s+(['\"])(.*)\\2/,unicodeAlphaNumeric:/[\\p{L}\\p{N}]/u,escapeTest:/[&<>\"']/,escapeReplace:/[&<>\"']/g,escapeTestNoEncode:/[<>\"']|&(?!(#\\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\\w+);)/,escapeReplaceNoEncode:/[<>\"']|&(?!(#\\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\\w+);)/g,unescapeTest:/&(#(?:\\d+)|(?:#x[0-9A-Fa-f]+)|(?:\\w+));?/ig,caret:/(^|[^\\[])\\^/g,percentDecode:/%25/g,findPipe:/\\|/g,splitPipe:/ \\|/,slashPipe:/\\\\\\|/g,carriageReturn:/\\r\\n|\\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\\S*/,endingNewline:/\\n$/,listItemRegex:u=>new RegExp(`^( {0,3}${u})((?:[\t ][^\\\\n]*)?(?:\\\\n|$))`),nextBulletRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}(?:[*+-]|\\\\d{1,9}[.)])((?:[ \t][^\\\\n]*)?(?:\\\\n|$))`),hrRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\\\* *){3,})(?:\\\\n+|$)`),fencesBeginRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}(?:\\`\\`\\`|~~~)`),headingBeginRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}#`),htmlBeginRegex:u=>new RegExp(`^ {0,${Math.min(3,u-1)}}<(?:[a-z].*>|!--)`,\"i\")},xe=/^(?:[ \\t]*(?:\\n|$))+/,be=/^((?: {4}| {0,3}\\t)[^\\n]+(?:\\n(?:[ \\t]*(?:\\n|$))*)?)+/,Re=/^ {0,3}(`{3,}(?=[^`\\n]*(?:\\n|$))|~{3,})([^\\n]*)(?:\\n|$)(?:|([\\s\\S]*?)(?:\\n|$))(?: {0,3}\\1[~`]* *(?=\\n|$)|$)/,I=/^ {0,3}((?:-[\\t ]*){3,}|(?:_[ \\t]*){3,}|(?:\\*[ \\t]*){3,})(?:\\n+|$)/,Te=/^ {0,3}(#{1,6})(?=\\s|$)(.*)(?:\\n+|$)/,N=/(?:[*+-]|\\d{1,9}[.)])/,re=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\\n(?!\\s*?\\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,se=k(re).replace(/bull/g,N).replace(/blockCode/g,/(?: {4}| {0,3}\\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\\n>]+>\\n/).replace(/\\|table/g,\"\").getRegex(),Oe=k(re).replace(/bull/g,N).replace(/blockCode/g,/(?: {4}| {0,3}\\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\\n>]+>\\n/).replace(/table/g,/ {0,3}\\|?(?:[:\\- ]*\\|)+[\\:\\- ]*\\n/).getRegex(),Q=/^([^\\n]+(?:\\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\\n)[^\\n]+)*)/,we=/^[^\\n]+/,F=/(?!\\s*\\])(?:\\\\[\\s\\S]|[^\\[\\]\\\\])+/,ye=k(/^ {0,3}\\[(label)\\]: *(?:\\n[ \\t]*)?([^<\\s][^\\s]*|<.*?>)(?:(?: +(?:\\n[ \\t]*)?| *\\n[ \\t]*)(title))? *(?:\\n+|$)/).replace(\"label\",F).replace(\"title\",/(?:\"(?:\\\\\"?|[^\"\\\\])*\"|'[^'\\n]*(?:\\n[^'\\n]+)*\\n?'|\\([^()]*\\))/).getRegex(),Pe=k(/^( {0,3}bull)([ \\t][^\\n]+?)?(?:\\n|$)/).replace(/bull/g,N).getRegex(),v=\"address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul\",j=/|$))/,Se=k(\"^ {0,3}(?:<(script|pre|style|textarea)[\\\\s>][\\\\s\\\\S]*?(?:[^\\\\n]*\\\\n+|$)|comment[^\\\\n]*(\\\\n+|$)|<\\\\?[\\\\s\\\\S]*?(?:\\\\?>\\\\n*|$)|\\\\n*|$)|\\\\n*|$)|)[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$)|<(?!script|pre|style|textarea)([a-z][\\\\w-]*)(?:attribute)*? */?>(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$)|(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$))\",\"i\").replace(\"comment\",j).replace(\"tag\",v).replace(\"attribute\",/ +[a-zA-Z:_][\\w.:-]*(?: *= *\"[^\"\\n]*\"| *= *'[^'\\n]*'| *= *[^\\s\"'=<>`]+)?/).getRegex(),ie=k(Q).replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"|lheading\",\"\").replace(\"|table\",\"\").replace(\"blockquote\",\" {0,3}>\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)]) \").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex(),$e=k(/^( {0,3}> ?(paragraph|[^\\n]*)(?:\\n|$))+/).replace(\"paragraph\",ie).getRegex(),U={blockquote:$e,code:be,def:ye,fences:Re,heading:Te,hr:I,html:Se,lheading:se,list:Pe,newline:xe,paragraph:ie,table:C,text:we},te=k(\"^ *([^\\\\n ].*)\\\\n {0,3}((?:\\\\| *)?:?-+:? *(?:\\\\| *:?-+:? *)*(?:\\\\| *)?)(?:\\\\n((?:(?! *\\\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\\\n|$))*)\\\\n*|$)\").replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"blockquote\",\" {0,3}>\").replace(\"code\",\"(?: {4}| {0,3}\t)[^\\\\n]\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)]) \").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex(),_e={...U,lheading:Oe,table:te,paragraph:k(Q).replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"|lheading\",\"\").replace(\"table\",te).replace(\"blockquote\",\" {0,3}>\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)]) \").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex()},Le={...U,html:k(`^ *(?:comment *(?:\\\\n|\\\\s*$)|<(tag)[\\\\s\\\\S]+? *(?:\\\\n{2,}|\\\\s*$)|\\\\s]*)*?/?> *(?:\\\\n{2,}|\\\\s*$))`).replace(\"comment\",j).replace(/tag/g,\"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\\\b)\\\\w+(?!:|[^\\\\w\\\\s@]*@)\\\\b\").getRegex(),def:/^ *\\[([^\\]]+)\\]: *]+)>?(?: +([\"(][^\\n]+[\")]))? *(?:\\n+|$)/,heading:/^(#{1,6})(.*)(?:\\n+|$)/,fences:C,lheading:/^(.+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,paragraph:k(Q).replace(\"hr\",I).replace(\"heading\",` *#{1,6} *[^\n]`).replace(\"lheading\",se).replace(\"|table\",\"\").replace(\"blockquote\",\" {0,3}>\").replace(\"|fences\",\"\").replace(\"|list\",\"\").replace(\"|html\",\"\").replace(\"|tag\",\"\").getRegex()},Me=/^\\\\([!\"#$%&'()*+,\\-./:;<=>?@\\[\\]\\\\^_`{|}~])/,ze=/^(`+)([^`]|[^`][\\s\\S]*?[^`])\\1(?!`)/,oe=/^( {2,}|\\\\)\\n(?!\\s*$)/,Ae=/^(`+|[^`])(?:(?= {2,}\\n)|[\\s\\S]*?(?:(?=[\\\\`+)[^`]+\\k(?!`))*?\\]\\((?:\\\\[\\s\\S]|[^\\\\\\(\\)]|\\((?:\\\\[\\s\\S]|[^\\\\\\(\\)])*\\))*\\)/).replace(\"precode-\",me?\"(?`+)[^`]+\\k(?!`)/).replace(\"html\",/<(?! )[^<>]*?>/).getRegex(),ue=/^(?:\\*+(?:((?!\\*)punct)|[^\\s*]))|^_+(?:((?!_)punct)|([^\\s_]))/,qe=k(ue,\"u\").replace(/punct/g,D).getRegex(),ve=k(ue,\"u\").replace(/punct/g,le).getRegex(),pe=\"^[^_*]*?__[^_*]*?\\\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\\\*)punct(\\\\*+)(?=[\\\\s]|$)|notPunctSpace(\\\\*+)(?!\\\\*)(?=punctSpace|$)|(?!\\\\*)punctSpace(\\\\*+)(?=notPunctSpace)|[\\\\s](\\\\*+)(?!\\\\*)(?=punct)|(?!\\\\*)punct(\\\\*+)(?!\\\\*)(?=punct)|notPunctSpace(\\\\*+)(?=notPunctSpace)\",De=k(pe,\"gu\").replace(/notPunctSpace/g,ae).replace(/punctSpace/g,K).replace(/punct/g,D).getRegex(),He=k(pe,\"gu\").replace(/notPunctSpace/g,Ee).replace(/punctSpace/g,Ie).replace(/punct/g,le).getRegex(),Ze=k(\"^[^_*]*?\\\\*\\\\*[^_*]*?_[^_*]*?(?=\\\\*\\\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)\",\"gu\").replace(/notPunctSpace/g,ae).replace(/punctSpace/g,K).replace(/punct/g,D).getRegex(),Ge=k(/\\\\(punct)/,\"gu\").replace(/punct/g,D).getRegex(),Ne=k(/^<(scheme:[^\\s\\x00-\\x1f<>]*|email)>/).replace(\"scheme\",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace(\"email\",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),Qe=k(j).replace(\"(?:-->|$)\",\"-->\").getRegex(),Fe=k(\"^comment|^|^<[a-zA-Z][\\\\w-]*(?:attribute)*?\\\\s*/?>|^<\\\\?[\\\\s\\\\S]*?\\\\?>|^|^\").replace(\"comment\",Qe).replace(\"attribute\",/\\s+[a-zA-Z:_][\\w.:-]*(?:\\s*=\\s*\"[^\"]*\"|\\s*=\\s*'[^']*'|\\s*=\\s*[^\\s\"'=<>`]+)?/).getRegex(),q=/(?:\\[(?:\\\\[\\s\\S]|[^\\[\\]\\\\])*\\]|\\\\[\\s\\S]|`+[^`]*?`+(?!`)|[^\\[\\]\\\\`])*?/,je=k(/^!?\\[(label)\\]\\(\\s*(href)(?:(?:[ \\t]*(?:\\n[ \\t]*)?)(title))?\\s*\\)/).replace(\"label\",q).replace(\"href\",/<(?:\\\\.|[^\\n<>\\\\])+>|[^ \\t\\n\\x00-\\x1f]*/).replace(\"title\",/\"(?:\\\\\"?|[^\"\\\\])*\"|'(?:\\\\'?|[^'\\\\])*'|\\((?:\\\\\\)?|[^)\\\\])*\\)/).getRegex(),ce=k(/^!?\\[(label)\\]\\[(ref)\\]/).replace(\"label\",q).replace(\"ref\",F).getRegex(),he=k(/^!?\\[(ref)\\](?:\\[\\])?/).replace(\"ref\",F).getRegex(),Ue=k(\"reflink|nolink(?!\\\\()\",\"g\").replace(\"reflink\",ce).replace(\"nolink\",he).getRegex(),ne=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,W={_backpedal:C,anyPunctuation:Ge,autolink:Ne,blockSkip:Be,br:oe,code:ze,del:C,emStrongLDelim:qe,emStrongRDelimAst:De,emStrongRDelimUnd:Ze,escape:Me,link:je,nolink:he,punctuation:Ce,reflink:ce,reflinkSearch:Ue,tag:Fe,text:Ae,url:C},Ke={...W,link:k(/^!?\\[(label)\\]\\((.*?)\\)/).replace(\"label\",q).getRegex(),reflink:k(/^!?\\[(label)\\]\\s*\\[([^\\]]*)\\]/).replace(\"label\",q).getRegex()},G={...W,emStrongRDelimAst:He,emStrongLDelim:ve,url:k(/^((?:protocol):\\/\\/|www\\.)(?:[a-zA-Z0-9\\-]+\\.?)+[^\\s<]*|^email/).replace(\"protocol\",ne).replace(\"email\",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'\"~()&]+|\\([^)]*\\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'\"~)]+(?!$))+/,del:/^(~~?)(?=[^\\s~])((?:\\\\[\\s\\S]|[^\\\\])*?(?:\\\\[\\s\\S]|[^\\s~\\\\]))\\1(?=[^~]|$)/,text:k(/^([`~]+|[^`~])(?:(?= {2,}\\n)|(?=[a-zA-Z0-9.!#$%&'*+\\/=?_`{\\|}~-]+@)|[\\s\\S]*?(?:(?=[\\\\\":\">\",'\"':\""\",\"'\":\"'\"},ke=u=>Xe[u];function w(u,e){if(e){if(m.escapeTest.test(u))return u.replace(m.escapeReplace,ke)}else if(m.escapeTestNoEncode.test(u))return u.replace(m.escapeReplaceNoEncode,ke);return u}function X(u){try{u=encodeURI(u).replace(m.percentDecode,\"%\")}catch{return null}return u}function J(u,e){let t=u.replace(m.findPipe,(i,s,a)=>{let o=!1,l=s;for(;--l>=0&&a[l]===\"\\\\\";)o=!o;return o?\"|\":\" |\"}),n=t.split(m.splitPipe),r=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length0?-2:-1}function ge(u,e,t,n,r){let i=e.href,s=e.title||null,a=u[1].replace(r.other.outputLinkReplace,\"$1\");n.state.inLink=!0;let o={type:u[0].charAt(0)===\"!\"?\"image\":\"link\",raw:t,href:i,title:s,text:a,tokens:n.inlineTokens(a)};return n.state.inLink=!1,o}function Je(u,e,t){let n=u.match(t.other.indentCodeCompensation);if(n===null)return e;let r=n[1];return e.split(`\n`).map(i=>{let s=i.match(t.other.beginningSpace);if(s===null)return i;let[a]=s;return a.length>=r.length?i.slice(r.length):i}).join(`\n`)}var y=class{options;rules;lexer;constructor(e){this.options=e||T}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:\"space\",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=t[0].replace(this.rules.other.codeRemoveIndent,\"\");return{type:\"code\",raw:t[0],codeBlockStyle:\"indented\",text:this.options.pedantic?n:z(n,`\n`)}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],r=Je(n,t[3]||\"\",this.rules);return{type:\"code\",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,\"$1\"):t[2],text:r}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let r=z(n,\"#\");(this.options.pedantic||!r||this.rules.other.endingSpaceChar.test(r))&&(n=r.trim())}return{type:\"heading\",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:\"hr\",raw:z(t[0],`\n`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=z(t[0],`\n`).split(`\n`),r=\"\",i=\"\",s=[];for(;n.length>0;){let a=!1,o=[],l;for(l=0;l1,i={type:\"list\",raw:\"\",ordered:r,start:r?+n.slice(0,-1):\"\",loose:!1,items:[]};n=r?`\\\\d{1,9}\\\\${n.slice(-1)}`:`\\\\${n}`,this.options.pedantic&&(n=r?n:\"[*+-]\");let s=this.rules.other.listItemRegex(n),a=!1;for(;e;){let l=!1,p=\"\",c=\"\";if(!(t=s.exec(e))||this.rules.block.hr.test(e))break;p=t[0],e=e.substring(p.length);let g=t[2].split(`\n`,1)[0].replace(this.rules.other.listReplaceTabs,O=>\" \".repeat(3*O.length)),h=e.split(`\n`,1)[0],R=!g.trim(),f=0;if(this.options.pedantic?(f=2,c=g.trimStart()):R?f=t[1].length+1:(f=t[2].search(this.rules.other.nonSpaceChar),f=f>4?1:f,c=g.slice(f),f+=t[1].length),R&&this.rules.other.blankLine.test(h)&&(p+=h+`\n`,e=e.substring(h.length+1),l=!0),!l){let O=this.rules.other.nextBulletRegex(f),V=this.rules.other.hrRegex(f),Y=this.rules.other.fencesBeginRegex(f),ee=this.rules.other.headingBeginRegex(f),fe=this.rules.other.htmlBeginRegex(f);for(;e;){let H=e.split(`\n`,1)[0],A;if(h=H,this.options.pedantic?(h=h.replace(this.rules.other.listReplaceNesting,\" \"),A=h):A=h.replace(this.rules.other.tabCharGlobal,\" \"),Y.test(h)||ee.test(h)||fe.test(h)||O.test(h)||V.test(h))break;if(A.search(this.rules.other.nonSpaceChar)>=f||!h.trim())c+=`\n`+A.slice(f);else{if(R||g.replace(this.rules.other.tabCharGlobal,\" \").search(this.rules.other.nonSpaceChar)>=4||Y.test(g)||ee.test(g)||V.test(g))break;c+=`\n`+h}!R&&!h.trim()&&(R=!0),p+=H+`\n`,e=e.substring(H.length+1),g=A.slice(f)}}i.loose||(a?i.loose=!0:this.rules.other.doubleBlankLine.test(p)&&(a=!0)),i.items.push({type:\"list_item\",raw:p,task:!!this.options.gfm&&this.rules.other.listIsTask.test(c),loose:!1,text:c,tokens:[]}),i.raw+=p}let o=i.items.at(-1);if(o)o.raw=o.raw.trimEnd(),o.text=o.text.trimEnd();else return;i.raw=i.raw.trimEnd();for(let l of i.items){if(this.lexer.state.top=!1,l.tokens=this.lexer.blockTokens(l.text,[]),l.task){if(l.text=l.text.replace(this.rules.other.listReplaceTask,\"\"),l.tokens[0]?.type===\"text\"||l.tokens[0]?.type===\"paragraph\"){l.tokens[0].raw=l.tokens[0].raw.replace(this.rules.other.listReplaceTask,\"\"),l.tokens[0].text=l.tokens[0].text.replace(this.rules.other.listReplaceTask,\"\");for(let c=this.lexer.inlineQueue.length-1;c>=0;c--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[c].src)){this.lexer.inlineQueue[c].src=this.lexer.inlineQueue[c].src.replace(this.rules.other.listReplaceTask,\"\");break}}let p=this.rules.other.listTaskCheckbox.exec(l.raw);if(p){let c={type:\"checkbox\",raw:p[0]+\" \",checked:p[0]!==\"[ ]\"};l.checked=c.checked,i.loose?l.tokens[0]&&[\"paragraph\",\"text\"].includes(l.tokens[0].type)&&\"tokens\"in l.tokens[0]&&l.tokens[0].tokens?(l.tokens[0].raw=c.raw+l.tokens[0].raw,l.tokens[0].text=c.raw+l.tokens[0].text,l.tokens[0].tokens.unshift(c)):l.tokens.unshift({type:\"paragraph\",raw:c.raw,text:c.raw,tokens:[c]}):l.tokens.unshift(c)}}if(!i.loose){let p=l.tokens.filter(g=>g.type===\"space\"),c=p.length>0&&p.some(g=>this.rules.other.anyLine.test(g.raw));i.loose=c}}if(i.loose)for(let l of i.items){l.loose=!0;for(let p of l.tokens)p.type===\"text\"&&(p.type=\"paragraph\")}return i}}html(e){let t=this.rules.block.html.exec(e);if(t)return{type:\"html\",block:!0,raw:t[0],pre:t[1]===\"pre\"||t[1]===\"script\"||t[1]===\"style\",text:t[0]}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal,\" \"),r=t[2]?t[2].replace(this.rules.other.hrefBrackets,\"$1\").replace(this.rules.inline.anyPunctuation,\"$1\"):\"\",i=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,\"$1\"):t[3];return{type:\"def\",tag:n,raw:t[0],href:r,title:i}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=J(t[1]),r=t[2].replace(this.rules.other.tableAlignChars,\"\").split(\"|\"),i=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,\"\").split(`\n`):[],s={type:\"table\",raw:t[0],header:[],align:[],rows:[]};if(n.length===r.length){for(let a of r)this.rules.other.tableAlignRight.test(a)?s.align.push(\"right\"):this.rules.other.tableAlignCenter.test(a)?s.align.push(\"center\"):this.rules.other.tableAlignLeft.test(a)?s.align.push(\"left\"):s.align.push(null);for(let a=0;a({text:o,tokens:this.lexer.inline(o),header:!1,align:s.align[l]})));return s}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t)return{type:\"heading\",raw:t[0],depth:t[2].charAt(0)===\"=\"?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===`\n`?t[1].slice(0,-1):t[1];return{type:\"paragraph\",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:\"text\",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:\"escape\",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:\"html\",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let s=z(n.slice(0,-1),\"\\\\\");if((n.length-s.length)%2===0)return}else{let s=de(t[2],\"()\");if(s===-2)return;if(s>-1){let o=(t[0].indexOf(\"!\")===0?5:4)+t[1].length+s;t[2]=t[2].substring(0,s),t[0]=t[0].substring(0,o).trim(),t[3]=\"\"}}let r=t[2],i=\"\";if(this.options.pedantic){let s=this.rules.other.pedanticHrefTitle.exec(r);s&&(r=s[1],i=s[3])}else i=t[3]?t[3].slice(1,-1):\"\";return r=r.trim(),this.rules.other.startAngleBracket.test(r)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?r=r.slice(1):r=r.slice(1,-1)),ge(t,{href:r&&r.replace(this.rules.inline.anyPunctuation,\"$1\"),title:i&&i.replace(this.rules.inline.anyPunctuation,\"$1\")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let r=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal,\" \"),i=t[r.toLowerCase()];if(!i){let s=n[0].charAt(0);return{type:\"text\",raw:s,text:s}}return ge(n,i,n[0],this.lexer,this.rules)}}emStrong(e,t,n=\"\"){let r=this.rules.inline.emStrongLDelim.exec(e);if(!r||r[3]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(r[1]||r[2]||\"\")||!n||this.rules.inline.punctuation.exec(n)){let s=[...r[0]].length-1,a,o,l=s,p=0,c=r[0][0]===\"*\"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(c.lastIndex=0,t=t.slice(-1*e.length+s);(r=c.exec(t))!=null;){if(a=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!a)continue;if(o=[...a].length,r[3]||r[4]){l+=o;continue}else if((r[5]||r[6])&&s%3&&!((s+o)%3)){p+=o;continue}if(l-=o,l>0)continue;o=Math.min(o,o+l+p);let g=[...r[0]][0].length,h=e.slice(0,s+r.index+g+o);if(Math.min(s,o)%2){let f=h.slice(1,-1);return{type:\"em\",raw:h,text:f,tokens:this.lexer.inlineTokens(f)}}let R=h.slice(2,-2);return{type:\"strong\",raw:h,text:R,tokens:this.lexer.inlineTokens(R)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal,\" \"),r=this.rules.other.nonSpaceChar.test(n),i=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return r&&i&&(n=n.substring(1,n.length-1)),{type:\"codespan\",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:\"br\",raw:t[0]}}del(e){let t=this.rules.inline.del.exec(e);if(t)return{type:\"del\",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,r;return t[2]===\"@\"?(n=t[1],r=\"mailto:\"+n):(n=t[1],r=n),{type:\"link\",raw:t[0],text:n,href:r,tokens:[{type:\"text\",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,r;if(t[2]===\"@\")n=t[0],r=\"mailto:\"+n;else{let i;do i=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??\"\";while(i!==t[0]);n=t[0],t[1]===\"www.\"?r=\"http://\"+t[0]:r=t[0]}return{type:\"link\",raw:t[0],text:n,href:r,tokens:[{type:\"text\",raw:n,text:n}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:\"text\",raw:t[0],text:t[0],escaped:n}}}};var x=class u{tokens;options;state;inlineQueue;tokenizer;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||T,this.options.tokenizer=this.options.tokenizer||new y,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let t={other:m,block:E.normal,inline:M.normal};this.options.pedantic?(t.block=E.pedantic,t.inline=M.pedantic):this.options.gfm&&(t.block=E.gfm,this.options.breaks?t.inline=M.breaks:t.inline=M.gfm),this.tokenizer.rules=t}static get rules(){return{block:E,inline:M}}static lex(e,t){return new u(t).lex(e)}static lexInline(e,t){return new u(t).inlineTokens(e)}lex(e){e=e.replace(m.carriageReturn,`\n`),this.blockTokens(e,this.tokens);for(let t=0;t(r=s.call({lexer:this},e,t))?(e=e.substring(r.raw.length),t.push(r),!0):!1))continue;if(r=this.tokenizer.space(e)){e=e.substring(r.raw.length);let s=t.at(-1);r.raw.length===1&&s!==void 0?s.raw+=`\n`:t.push(r);continue}if(r=this.tokenizer.code(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type===\"paragraph\"||s?.type===\"text\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.text,this.inlineQueue.at(-1).src=s.text):t.push(r);continue}if(r=this.tokenizer.fences(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.heading(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.hr(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.blockquote(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.list(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.html(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.def(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type===\"paragraph\"||s?.type===\"text\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.raw,this.inlineQueue.at(-1).src=s.text):this.tokens.links[r.tag]||(this.tokens.links[r.tag]={href:r.href,title:r.title},t.push(r));continue}if(r=this.tokenizer.table(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.lheading(e)){e=e.substring(r.raw.length),t.push(r);continue}let i=e;if(this.options.extensions?.startBlock){let s=1/0,a=e.slice(1),o;this.options.extensions.startBlock.forEach(l=>{o=l.call({lexer:this},a),typeof o==\"number\"&&o>=0&&(s=Math.min(s,o))}),s<1/0&&s>=0&&(i=e.substring(0,s+1))}if(this.state.top&&(r=this.tokenizer.paragraph(i))){let s=t.at(-1);n&&s?.type===\"paragraph\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=s.text):t.push(r),n=i.length!==e.length,e=e.substring(r.raw.length);continue}if(r=this.tokenizer.text(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type===\"text\"?(s.raw+=(s.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,s.text+=`\n`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=s.text):t.push(r);continue}if(e){let s=\"Infinite loop on byte: \"+e.charCodeAt(0);if(this.options.silent){console.error(s);break}else throw new Error(s)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,r=null;if(this.tokens.links){let o=Object.keys(this.tokens.links);if(o.length>0)for(;(r=this.tokenizer.rules.inline.reflinkSearch.exec(n))!=null;)o.includes(r[0].slice(r[0].lastIndexOf(\"[\")+1,-1))&&(n=n.slice(0,r.index)+\"[\"+\"a\".repeat(r[0].length-2)+\"]\"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(r=this.tokenizer.rules.inline.anyPunctuation.exec(n))!=null;)n=n.slice(0,r.index)+\"++\"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let i;for(;(r=this.tokenizer.rules.inline.blockSkip.exec(n))!=null;)i=r[2]?r[2].length:0,n=n.slice(0,r.index+i)+\"[\"+\"a\".repeat(r[0].length-i-2)+\"]\"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);n=this.options.hooks?.emStrongMask?.call({lexer:this},n)??n;let s=!1,a=\"\";for(;e;){s||(a=\"\"),s=!1;let o;if(this.options.extensions?.inline?.some(p=>(o=p.call({lexer:this},e,t))?(e=e.substring(o.raw.length),t.push(o),!0):!1))continue;if(o=this.tokenizer.escape(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.tag(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.link(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(o.raw.length);let p=t.at(-1);o.type===\"text\"&&p?.type===\"text\"?(p.raw+=o.raw,p.text+=o.text):t.push(o);continue}if(o=this.tokenizer.emStrong(e,n,a)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.codespan(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.br(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.del(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.autolink(e)){e=e.substring(o.raw.length),t.push(o);continue}if(!this.state.inLink&&(o=this.tokenizer.url(e))){e=e.substring(o.raw.length),t.push(o);continue}let l=e;if(this.options.extensions?.startInline){let p=1/0,c=e.slice(1),g;this.options.extensions.startInline.forEach(h=>{g=h.call({lexer:this},c),typeof g==\"number\"&&g>=0&&(p=Math.min(p,g))}),p<1/0&&p>=0&&(l=e.substring(0,p+1))}if(o=this.tokenizer.inlineText(l)){e=e.substring(o.raw.length),o.raw.slice(-1)!==\"_\"&&(a=o.raw.slice(-1)),s=!0;let p=t.at(-1);p?.type===\"text\"?(p.raw+=o.raw,p.text+=o.text):t.push(o);continue}if(e){let p=\"Infinite loop on byte: \"+e.charCodeAt(0);if(this.options.silent){console.error(p);break}else throw new Error(p)}}return t}};var P=class{options;parser;constructor(e){this.options=e||T}space(e){return\"\"}code({text:e,lang:t,escaped:n}){let r=(t||\"\").match(m.notSpaceStart)?.[0],i=e.replace(m.endingNewline,\"\")+`\n`;return r?'
    '+(n?i:w(i,!0))+`
    \n`:\"
    \"+(n?i:w(i,!0))+`
    \n`}blockquote({tokens:e}){return`
    \n${this.parser.parse(e)}
    \n`}html({text:e}){return e}def(e){return\"\"}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)}\n`}hr(e){return`
    \n`}list(e){let t=e.ordered,n=e.start,r=\"\";for(let a=0;a\n`+r+\"\n`}listitem(e){return`
  • ${this.parser.parse(e.tokens)}
  • \n`}checkbox({checked:e}){return\" '}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    \n`}table(e){let t=\"\",n=\"\";for(let i=0;i${r}`),`\n\n`+t+`\n`+r+`
    \n`}tablerow({text:e}){return`\n${e}\n`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?\"th\":\"td\";return(e.align?`<${n} align=\"${e.align}\">`:`<${n}>`)+t+`\n`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${w(e,!0)}`}br(e){return\"
    \"}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let r=this.parser.parseInline(n),i=X(e);if(i===null)return r;e=i;let s='
    \"+r+\"\",s}image({href:e,title:t,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let i=X(e);if(i===null)return w(n);e=i;let s=`\"${n}\"`;return\",s}text(e){return\"tokens\"in e&&e.tokens?this.parser.parseInline(e.tokens):\"escaped\"in e&&e.escaped?e.text:w(e.text)}};var $=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return\"\"+e}image({text:e}){return\"\"+e}br(){return\"\"}checkbox({raw:e}){return e}};var b=class u{options;renderer;textRenderer;constructor(e){this.options=e||T,this.options.renderer=this.options.renderer||new P,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new $}static parse(e,t){return new u(t).parse(e)}static parseInline(e,t){return new u(t).parseInline(e)}parse(e){let t=\"\";for(let n=0;n{let a=i[s].flat(1/0);n=n.concat(this.walkTokens(a,t))}):i.tokens&&(n=n.concat(this.walkTokens(i.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let r={...n};if(r.async=this.defaults.async||r.async||!1,n.extensions&&(n.extensions.forEach(i=>{if(!i.name)throw new Error(\"extension name required\");if(\"renderer\"in i){let s=t.renderers[i.name];s?t.renderers[i.name]=function(...a){let o=i.renderer.apply(this,a);return o===!1&&(o=s.apply(this,a)),o}:t.renderers[i.name]=i.renderer}if(\"tokenizer\"in i){if(!i.level||i.level!==\"block\"&&i.level!==\"inline\")throw new Error(\"extension level must be 'block' or 'inline'\");let s=t[i.level];s?s.unshift(i.tokenizer):t[i.level]=[i.tokenizer],i.start&&(i.level===\"block\"?t.startBlock?t.startBlock.push(i.start):t.startBlock=[i.start]:i.level===\"inline\"&&(t.startInline?t.startInline.push(i.start):t.startInline=[i.start]))}\"childTokens\"in i&&i.childTokens&&(t.childTokens[i.name]=i.childTokens)}),r.extensions=t),n.renderer){let i=this.defaults.renderer||new P(this.defaults);for(let s in n.renderer){if(!(s in i))throw new Error(`renderer '${s}' does not exist`);if([\"options\",\"parser\"].includes(s))continue;let a=s,o=n.renderer[a],l=i[a];i[a]=(...p)=>{let c=o.apply(i,p);return c===!1&&(c=l.apply(i,p)),c||\"\"}}r.renderer=i}if(n.tokenizer){let i=this.defaults.tokenizer||new y(this.defaults);for(let s in n.tokenizer){if(!(s in i))throw new Error(`tokenizer '${s}' does not exist`);if([\"options\",\"rules\",\"lexer\"].includes(s))continue;let a=s,o=n.tokenizer[a],l=i[a];i[a]=(...p)=>{let c=o.apply(i,p);return c===!1&&(c=l.apply(i,p)),c}}r.tokenizer=i}if(n.hooks){let i=this.defaults.hooks||new S;for(let s in n.hooks){if(!(s in i))throw new Error(`hook '${s}' does not exist`);if([\"options\",\"block\"].includes(s))continue;let a=s,o=n.hooks[a],l=i[a];S.passThroughHooks.has(s)?i[a]=p=>{if(this.defaults.async&&S.passThroughHooksRespectAsync.has(s))return(async()=>{let g=await o.call(i,p);return l.call(i,g)})();let c=o.call(i,p);return l.call(i,c)}:i[a]=(...p)=>{if(this.defaults.async)return(async()=>{let g=await o.apply(i,p);return g===!1&&(g=await l.apply(i,p)),g})();let c=o.apply(i,p);return c===!1&&(c=l.apply(i,p)),c}}r.hooks=i}if(n.walkTokens){let i=this.defaults.walkTokens,s=n.walkTokens;r.walkTokens=function(a){let o=[];return o.push(s.call(this,a)),i&&(o=o.concat(i.call(this,a))),o}}this.defaults={...this.defaults,...r}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return x.lex(e,t??this.defaults)}parser(e,t){return b.parse(e,t??this.defaults)}parseMarkdown(e){return(n,r)=>{let i={...r},s={...this.defaults,...i},a=this.onError(!!s.silent,!!s.async);if(this.defaults.async===!0&&i.async===!1)return a(new Error(\"marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.\"));if(typeof n>\"u\"||n===null)return a(new Error(\"marked(): input parameter is undefined or null\"));if(typeof n!=\"string\")return a(new Error(\"marked(): input parameter is of type \"+Object.prototype.toString.call(n)+\", string expected\"));if(s.hooks&&(s.hooks.options=s,s.hooks.block=e),s.async)return(async()=>{let o=s.hooks?await s.hooks.preprocess(n):n,p=await(s.hooks?await s.hooks.provideLexer():e?x.lex:x.lexInline)(o,s),c=s.hooks?await s.hooks.processAllTokens(p):p;s.walkTokens&&await Promise.all(this.walkTokens(c,s.walkTokens));let h=await(s.hooks?await s.hooks.provideParser():e?b.parse:b.parseInline)(c,s);return s.hooks?await s.hooks.postprocess(h):h})().catch(a);try{s.hooks&&(n=s.hooks.preprocess(n));let l=(s.hooks?s.hooks.provideLexer():e?x.lex:x.lexInline)(n,s);s.hooks&&(l=s.hooks.processAllTokens(l)),s.walkTokens&&this.walkTokens(l,s.walkTokens);let c=(s.hooks?s.hooks.provideParser():e?b.parse:b.parseInline)(l,s);return s.hooks&&(c=s.hooks.postprocess(c)),c}catch(o){return a(o)}}}onError(e,t){return n=>{if(n.message+=`\nPlease report this to https://github.com/markedjs/marked.`,e){let r=\"

    An error occurred:

    \"+w(n.message+\"\",!0)+\"
    \";return t?Promise.resolve(r):r}if(t)return Promise.reject(n);throw n}}};var _=new B;function d(u,e){return _.parse(u,e)}d.options=d.setOptions=function(u){return _.setOptions(u),d.defaults=_.defaults,Z(d.defaults),d};d.getDefaults=L;d.defaults=T;d.use=function(...u){return _.use(...u),d.defaults=_.defaults,Z(d.defaults),d};d.walkTokens=function(u,e){return _.walkTokens(u,e)};d.parseInline=_.parseInline;d.Parser=b;d.parser=b.parse;d.Renderer=P;d.TextRenderer=$;d.Lexer=x;d.lexer=x.lex;d.Tokenizer=y;d.Hooks=S;d.parse=d;var Dt=d.options,Ht=d.setOptions,Zt=d.use,Gt=d.walkTokens,Nt=d.parseInline,Qt=d,Ft=b.parse,jt=x.lex;export{S as Hooks,x as Lexer,B as Marked,b as Parser,P as Renderer,$ as TextRenderer,y as Tokenizer,T as defaults,L as getDefaults,jt as lexer,d as marked,Dt as options,Qt as parse,Nt as parseInline,Ft as parser,Ht as setOptions,Zt as use,Gt as walkTokens};\n//# sourceMappingURL=marked.esm.js.map\n","import DOMPurify from \"dompurify\";\nimport { marked } from \"marked\";\nimport { truncateText } from \"./format\";\n\nmarked.setOptions({\n gfm: true,\n breaks: true,\n mangle: false,\n});\n\nconst allowedTags = [\n \"a\",\n \"b\",\n \"blockquote\",\n \"br\",\n \"code\",\n \"del\",\n \"em\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"hr\",\n \"i\",\n \"li\",\n \"ol\",\n \"p\",\n \"pre\",\n \"strong\",\n \"table\",\n \"tbody\",\n \"td\",\n \"th\",\n \"thead\",\n \"tr\",\n \"ul\",\n];\n\nconst allowedAttrs = [\"class\", \"href\", \"rel\", \"target\", \"title\", \"start\"];\n\nlet hooksInstalled = false;\nconst MARKDOWN_CHAR_LIMIT = 140_000;\nconst MARKDOWN_PARSE_LIMIT = 40_000;\n\nfunction installHooks() {\n if (hooksInstalled) return;\n hooksInstalled = true;\n\n DOMPurify.addHook(\"afterSanitizeAttributes\", (node) => {\n if (!(node instanceof HTMLAnchorElement)) return;\n const href = node.getAttribute(\"href\");\n if (!href) return;\n node.setAttribute(\"rel\", \"noreferrer noopener\");\n node.setAttribute(\"target\", \"_blank\");\n });\n}\n\nexport function toSanitizedMarkdownHtml(markdown: string): string {\n const input = markdown.trim();\n if (!input) return \"\";\n installHooks();\n const truncated = truncateText(input, MARKDOWN_CHAR_LIMIT);\n const suffix = truncated.truncated\n ? `\\n\\n… truncated (${truncated.total} chars, showing first ${truncated.text.length}).`\n : \"\";\n if (truncated.text.length > MARKDOWN_PARSE_LIMIT) {\n const escaped = escapeHtml(`${truncated.text}${suffix}`);\n const html = `
    ${escaped}
    `;\n return DOMPurify.sanitize(html, {\n ALLOWED_TAGS: allowedTags,\n ALLOWED_ATTR: allowedAttrs,\n });\n }\n const rendered = marked.parse(`${truncated.text}${suffix}`) as string;\n return DOMPurify.sanitize(rendered, {\n ALLOWED_TAGS: allowedTags,\n ALLOWED_ATTR: allowedAttrs,\n });\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(//g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n","import { html, type TemplateResult } from \"lit\";\n\nexport function renderEmojiIcon(icon: string, className: string): TemplateResult {\n return html`${icon}`;\n}\n\nexport function setEmojiIcon(target: HTMLElement | null, icon: string): void {\n if (!target) return;\n target.textContent = icon;\n}\n","import { html, type TemplateResult } from \"lit\";\nimport { renderEmojiIcon, setEmojiIcon } from \"../icons\";\n\nconst COPIED_FOR_MS = 1500;\nconst ERROR_FOR_MS = 2000;\nconst COPY_LABEL = \"Copy as markdown\";\nconst COPIED_LABEL = \"Copied\";\nconst ERROR_LABEL = \"Copy failed\";\nconst COPY_ICON = \"📋\";\nconst COPIED_ICON = \"✓\";\nconst ERROR_ICON = \"!\";\n\ntype CopyButtonOptions = {\n text: () => string;\n label?: string;\n};\n\nasync function copyTextToClipboard(text: string): Promise {\n if (!text) return false;\n\n try {\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction setButtonLabel(button: HTMLButtonElement, label: string) {\n button.title = label;\n button.setAttribute(\"aria-label\", label);\n}\n\nfunction createCopyButton(options: CopyButtonOptions): TemplateResult {\n const idleLabel = options.label ?? COPY_LABEL;\n return html`\n {\n const btn = e.currentTarget as HTMLButtonElement | null;\n const icon = btn?.querySelector(\n \".chat-copy-btn__icon\",\n ) as HTMLElement | null;\n\n if (!btn || btn.dataset.copying === \"1\") return;\n\n btn.dataset.copying = \"1\";\n btn.setAttribute(\"aria-busy\", \"true\");\n btn.disabled = true;\n\n const copied = await copyTextToClipboard(options.text());\n if (!btn.isConnected) return;\n\n delete btn.dataset.copying;\n btn.removeAttribute(\"aria-busy\");\n btn.disabled = false;\n\n if (!copied) {\n btn.dataset.error = \"1\";\n setButtonLabel(btn, ERROR_LABEL);\n setEmojiIcon(icon, ERROR_ICON);\n\n window.setTimeout(() => {\n if (!btn.isConnected) return;\n delete btn.dataset.error;\n setButtonLabel(btn, idleLabel);\n setEmojiIcon(icon, COPY_ICON);\n }, ERROR_FOR_MS);\n return;\n }\n\n btn.dataset.copied = \"1\";\n setButtonLabel(btn, COPIED_LABEL);\n setEmojiIcon(icon, COPIED_ICON);\n\n window.setTimeout(() => {\n if (!btn.isConnected) return;\n delete btn.dataset.copied;\n setButtonLabel(btn, idleLabel);\n setEmojiIcon(icon, COPY_ICON);\n }, COPIED_FOR_MS);\n }}\n >\n ${renderEmojiIcon(COPY_ICON, \"chat-copy-btn__icon\")}\n \n `;\n}\n\nexport function renderCopyAsMarkdownButton(markdown: string): TemplateResult {\n return createCopyButton({ text: () => markdown, label: COPY_LABEL });\n}\n","import rawConfig from \"./tool-display.json\";\n\ntype ToolDisplayActionSpec = {\n label?: string;\n detailKeys?: string[];\n};\n\ntype ToolDisplaySpec = {\n emoji?: string;\n title?: string;\n label?: string;\n detailKeys?: string[];\n actions?: Record;\n};\n\ntype ToolDisplayConfig = {\n version?: number;\n fallback?: ToolDisplaySpec;\n tools?: Record;\n};\n\nexport type ToolDisplay = {\n name: string;\n emoji: string;\n title: string;\n label: string;\n verb?: string;\n detail?: string;\n};\n\nconst TOOL_DISPLAY_CONFIG = rawConfig as ToolDisplayConfig;\nconst FALLBACK = TOOL_DISPLAY_CONFIG.fallback ?? { emoji: \"🧩\" };\nconst TOOL_MAP = TOOL_DISPLAY_CONFIG.tools ?? {};\n\nfunction normalizeToolName(name?: string): string {\n return (name ?? \"tool\").trim();\n}\n\nfunction defaultTitle(name: string): string {\n const cleaned = name.replace(/_/g, \" \").trim();\n if (!cleaned) return \"Tool\";\n return cleaned\n .split(/\\s+/)\n .map((part) =>\n part.length <= 2 && part.toUpperCase() === part\n ? part\n : `${part.at(0)?.toUpperCase() ?? \"\"}${part.slice(1)}`,\n )\n .join(\" \");\n}\n\nfunction normalizeVerb(value?: string): string | undefined {\n const trimmed = value?.trim();\n if (!trimmed) return undefined;\n return trimmed.replace(/_/g, \" \");\n}\n\nfunction coerceDisplayValue(value: unknown): string | undefined {\n if (value === null || value === undefined) return undefined;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const firstLine = trimmed.split(/\\r?\\n/)[0]?.trim() ?? \"\";\n if (!firstLine) return undefined;\n return firstLine.length > 160 ? `${firstLine.slice(0, 157)}…` : firstLine;\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (Array.isArray(value)) {\n const values = value\n .map((item) => coerceDisplayValue(item))\n .filter((item): item is string => Boolean(item));\n if (values.length === 0) return undefined;\n const preview = values.slice(0, 3).join(\", \");\n return values.length > 3 ? `${preview}…` : preview;\n }\n return undefined;\n}\n\nfunction lookupValueByPath(args: unknown, path: string): unknown {\n if (!args || typeof args !== \"object\") return undefined;\n let current: unknown = args;\n for (const segment of path.split(\".\")) {\n if (!segment) return undefined;\n if (!current || typeof current !== \"object\") return undefined;\n const record = current as Record;\n current = record[segment];\n }\n return current;\n}\n\nfunction resolveDetailFromKeys(args: unknown, keys: string[]): string | undefined {\n for (const key of keys) {\n const value = lookupValueByPath(args, key);\n const display = coerceDisplayValue(value);\n if (display) return display;\n }\n return undefined;\n}\n\nfunction resolveReadDetail(args: unknown): string | undefined {\n if (!args || typeof args !== \"object\") return undefined;\n const record = args as Record;\n const path = typeof record.path === \"string\" ? record.path : undefined;\n if (!path) return undefined;\n const offset = typeof record.offset === \"number\" ? record.offset : undefined;\n const limit = typeof record.limit === \"number\" ? record.limit : undefined;\n if (offset !== undefined && limit !== undefined) {\n return `${path}:${offset}-${offset + limit}`;\n }\n return path;\n}\n\nfunction resolveWriteDetail(args: unknown): string | undefined {\n if (!args || typeof args !== \"object\") return undefined;\n const record = args as Record;\n const path = typeof record.path === \"string\" ? record.path : undefined;\n return path;\n}\n\nfunction resolveActionSpec(\n spec: ToolDisplaySpec | undefined,\n action: string | undefined,\n): ToolDisplayActionSpec | undefined {\n if (!spec || !action) return undefined;\n return spec.actions?.[action] ?? undefined;\n}\n\nexport function resolveToolDisplay(params: {\n name?: string;\n args?: unknown;\n meta?: string;\n}): ToolDisplay {\n const name = normalizeToolName(params.name);\n const key = name.toLowerCase();\n const spec = TOOL_MAP[key];\n const emoji = spec?.emoji ?? FALLBACK.emoji ?? \"🧩\";\n const title = spec?.title ?? defaultTitle(name);\n const label = spec?.label ?? name;\n const actionRaw =\n params.args && typeof params.args === \"object\"\n ? ((params.args as Record).action as string | undefined)\n : undefined;\n const action = typeof actionRaw === \"string\" ? actionRaw.trim() : undefined;\n const actionSpec = resolveActionSpec(spec, action);\n const verb = normalizeVerb(actionSpec?.label ?? action);\n\n let detail: string | undefined;\n if (key === \"read\") detail = resolveReadDetail(params.args);\n if (!detail && (key === \"write\" || key === \"edit\" || key === \"attach\")) {\n detail = resolveWriteDetail(params.args);\n }\n\n const detailKeys =\n actionSpec?.detailKeys ?? spec?.detailKeys ?? FALLBACK.detailKeys ?? [];\n if (!detail && detailKeys.length > 0) {\n detail = resolveDetailFromKeys(params.args, detailKeys);\n }\n\n if (!detail && params.meta) {\n detail = params.meta;\n }\n\n if (detail) {\n detail = shortenHomeInString(detail);\n }\n\n return {\n name,\n emoji,\n title,\n label,\n verb,\n detail,\n };\n}\n\nexport function formatToolDetail(display: ToolDisplay): string | undefined {\n const parts: string[] = [];\n if (display.verb) parts.push(display.verb);\n if (display.detail) parts.push(display.detail);\n if (parts.length === 0) return undefined;\n return parts.join(\" · \");\n}\n\nexport function formatToolSummary(display: ToolDisplay): string {\n const detail = formatToolDetail(display);\n return detail\n ? `${display.emoji} ${display.label}: ${detail}`\n : `${display.emoji} ${display.label}`;\n}\n\nfunction shortenHomeInString(input: string): string {\n if (!input) return input;\n return input\n .replace(/\\/Users\\/[^/]+/g, \"~\")\n .replace(/\\/home\\/[^/]+/g, \"~\");\n}\n","/**\n * Chat-related constants for the UI layer.\n */\n\n/** Character threshold for showing tool output inline vs collapsed */\nexport const TOOL_INLINE_THRESHOLD = 80;\n\n/** Maximum lines to show in collapsed preview */\nexport const PREVIEW_MAX_LINES = 2;\n\n/** Maximum characters to show in collapsed preview */\nexport const PREVIEW_MAX_CHARS = 100;\n","/**\n * Helper functions for tool card rendering.\n */\n\nimport { PREVIEW_MAX_CHARS, PREVIEW_MAX_LINES } from \"./constants\";\n\n/**\n * Format tool output content for display in the sidebar.\n * Detects JSON and wraps it in a code block with formatting.\n */\nexport function formatToolOutputForSidebar(text: string): string {\n const trimmed = text.trim();\n // Try to detect and format JSON\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n const parsed = JSON.parse(trimmed);\n return \"```json\\n\" + JSON.stringify(parsed, null, 2) + \"\\n```\";\n } catch {\n // Not valid JSON, return as-is\n }\n }\n return text;\n}\n\n/**\n * Get a truncated preview of tool output text.\n * Truncates to first N lines or first N characters, whichever is shorter.\n */\nexport function getTruncatedPreview(text: string): string {\n const allLines = text.split(\"\\n\");\n const lines = allLines.slice(0, PREVIEW_MAX_LINES);\n const preview = lines.join(\"\\n\");\n if (preview.length > PREVIEW_MAX_CHARS) {\n return preview.slice(0, PREVIEW_MAX_CHARS) + \"…\";\n }\n return lines.length < allLines.length ? preview + \"…\" : preview;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatToolDetail, resolveToolDisplay } from \"../tool-display\";\nimport type { ToolCard } from \"../types/chat-types\";\nimport { TOOL_INLINE_THRESHOLD } from \"./constants\";\nimport {\n formatToolOutputForSidebar,\n getTruncatedPreview,\n} from \"./tool-helpers\";\nimport { isToolResultMessage } from \"./message-normalizer\";\nimport { extractText } from \"./message-extract\";\n\nexport function extractToolCards(message: unknown): ToolCard[] {\n const m = message as Record;\n const content = normalizeContent(m.content);\n const cards: ToolCard[] = [];\n\n for (const item of content) {\n const kind = String(item.type ?? \"\").toLowerCase();\n const isToolCall =\n [\"toolcall\", \"tool_call\", \"tooluse\", \"tool_use\"].includes(kind) ||\n (typeof item.name === \"string\" && item.arguments != null);\n if (isToolCall) {\n cards.push({\n kind: \"call\",\n name: (item.name as string) ?? \"tool\",\n args: coerceArgs(item.arguments ?? item.args),\n });\n }\n }\n\n for (const item of content) {\n const kind = String(item.type ?? \"\").toLowerCase();\n if (kind !== \"toolresult\" && kind !== \"tool_result\") continue;\n const text = extractToolText(item);\n const name = typeof item.name === \"string\" ? item.name : \"tool\";\n cards.push({ kind: \"result\", name, text });\n }\n\n if (\n isToolResultMessage(message) &&\n !cards.some((card) => card.kind === \"result\")\n ) {\n const name =\n (typeof m.toolName === \"string\" && m.toolName) ||\n (typeof m.tool_name === \"string\" && m.tool_name) ||\n \"tool\";\n const text = extractText(message) ?? undefined;\n cards.push({ kind: \"result\", name, text });\n }\n\n return cards;\n}\n\nexport function renderToolCardSidebar(\n card: ToolCard,\n onOpenSidebar?: (content: string) => void,\n) {\n const display = resolveToolDisplay({ name: card.name, args: card.args });\n const detail = formatToolDetail(display);\n const hasText = Boolean(card.text?.trim());\n\n const canClick = Boolean(onOpenSidebar);\n const handleClick = canClick\n ? () => {\n if (hasText) {\n onOpenSidebar!(formatToolOutputForSidebar(card.text!));\n return;\n }\n const info = `## ${display.label}\\n\\n${\n detail ? `**Command:** \\`${detail}\\`\\n\\n` : \"\"\n }*No output — tool completed successfully.*`;\n onOpenSidebar!(info);\n }\n : undefined;\n\n const isShort = hasText && (card.text?.length ?? 0) <= TOOL_INLINE_THRESHOLD;\n const showCollapsed = hasText && !isShort;\n const showInline = hasText && isShort;\n const isEmpty = !hasText;\n\n return html`\n {\n if (e.key !== \"Enter\" && e.key !== \" \") return;\n e.preventDefault();\n handleClick?.();\n }\n : nothing}\n >\n
    \n
    \n ${display.emoji}\n ${display.label}\n
    \n ${canClick\n ? html`${hasText ? \"View ›\" : \"›\"}`\n : nothing}\n ${isEmpty && !canClick ? html`` : nothing}\n
    \n ${detail\n ? html`
    ${detail}
    `\n : nothing}\n ${isEmpty\n ? html`
    Completed
    `\n : nothing}\n ${showCollapsed\n ? html`
    ${getTruncatedPreview(card.text!)}
    `\n : nothing}\n ${showInline\n ? html`
    ${card.text}
    `\n : nothing}\n \n `;\n}\n\nfunction normalizeContent(content: unknown): Array> {\n if (!Array.isArray(content)) return [];\n return content.filter(Boolean) as Array>;\n}\n\nfunction coerceArgs(value: unknown): unknown {\n if (typeof value !== \"string\") return value;\n const trimmed = value.trim();\n if (!trimmed) return value;\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) return value;\n try {\n return JSON.parse(trimmed);\n } catch {\n return value;\n }\n}\n\nfunction extractToolText(item: Record): string | undefined {\n if (typeof item.text === \"string\") return item.text;\n if (typeof item.content === \"string\") return item.content;\n return undefined;\n}\n","import { html, nothing } from \"lit\";\nimport { unsafeHTML } from \"lit/directives/unsafe-html.js\";\n\nimport type { AssistantIdentity } from \"../assistant-identity\";\nimport { toSanitizedMarkdownHtml } from \"../markdown\";\nimport type { MessageGroup } from \"../types/chat-types\";\nimport { renderCopyAsMarkdownButton } from \"./copy-as-markdown\";\nimport { isToolResultMessage, normalizeRoleForGrouping } from \"./message-normalizer\";\nimport {\n extractText,\n extractThinking,\n formatReasoningMarkdown,\n} from \"./message-extract\";\nimport { extractToolCards, renderToolCardSidebar } from \"./tool-cards\";\n\nexport function renderReadingIndicatorGroup(assistant?: AssistantIdentity) {\n return html`\n
    \n ${renderAvatar(\"assistant\", assistant)}\n
    \n
    \n \n \n \n
    \n
    \n
    \n `;\n}\n\nexport function renderStreamingGroup(\n text: string,\n startedAt: number,\n onOpenSidebar?: (content: string) => void,\n assistant?: AssistantIdentity,\n) {\n const timestamp = new Date(startedAt).toLocaleTimeString([], {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const name = assistant?.name ?? \"Assistant\";\n\n return html`\n
    \n ${renderAvatar(\"assistant\", assistant)}\n
    \n ${renderGroupedMessage(\n {\n role: \"assistant\",\n content: [{ type: \"text\", text }],\n timestamp: startedAt,\n },\n { isStreaming: true, showReasoning: false },\n onOpenSidebar,\n )}\n
    \n ${name}\n ${timestamp}\n
    \n
    \n
    \n `;\n}\n\nexport function renderMessageGroup(\n group: MessageGroup,\n opts: {\n onOpenSidebar?: (content: string) => void;\n showReasoning: boolean;\n assistantName?: string;\n assistantAvatar?: string | null;\n },\n) {\n const normalizedRole = normalizeRoleForGrouping(group.role);\n const assistantName = opts.assistantName ?? \"Assistant\";\n const who =\n normalizedRole === \"user\"\n ? \"You\"\n : normalizedRole === \"assistant\"\n ? assistantName\n : normalizedRole;\n const roleClass =\n normalizedRole === \"user\"\n ? \"user\"\n : normalizedRole === \"assistant\"\n ? \"assistant\"\n : \"other\";\n const timestamp = new Date(group.timestamp).toLocaleTimeString([], {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return html`\n
    \n ${renderAvatar(group.role, {\n name: assistantName,\n avatar: opts.assistantAvatar ?? null,\n })}\n
    \n ${group.messages.map((item, index) =>\n renderGroupedMessage(\n item.message,\n {\n isStreaming:\n group.isStreaming && index === group.messages.length - 1,\n showReasoning: opts.showReasoning,\n },\n opts.onOpenSidebar,\n ),\n )}\n
    \n ${who}\n ${timestamp}\n
    \n
    \n
    \n `;\n}\n\nfunction renderAvatar(\n role: string,\n assistant?: Pick,\n) {\n const normalized = normalizeRoleForGrouping(role);\n const assistantName = assistant?.name?.trim() || \"Assistant\";\n const assistantAvatar = assistant?.avatar?.trim() || \"\";\n const initial =\n normalized === \"user\"\n ? \"U\"\n : normalized === \"assistant\"\n ? assistantName.charAt(0).toUpperCase() || \"A\"\n : normalized === \"tool\"\n ? \"⚙\"\n : \"?\";\n const className =\n normalized === \"user\"\n ? \"user\"\n : normalized === \"assistant\"\n ? \"assistant\"\n : normalized === \"tool\"\n ? \"tool\"\n : \"other\";\n\n if (assistantAvatar && normalized === \"assistant\") {\n if (isAvatarUrl(assistantAvatar)) {\n return html``;\n }\n return html`
    ${assistantAvatar}
    `;\n }\n\n return html`
    ${initial}
    `;\n}\n\nfunction isAvatarUrl(value: string): boolean {\n return (\n /^https?:\\/\\//i.test(value) ||\n /^data:image\\//i.test(value)\n );\n}\n\nfunction renderGroupedMessage(\n message: unknown,\n opts: { isStreaming: boolean; showReasoning: boolean },\n onOpenSidebar?: (content: string) => void,\n) {\n const m = message as Record;\n const role = typeof m.role === \"string\" ? m.role : \"unknown\";\n const isToolResult =\n isToolResultMessage(message) ||\n role.toLowerCase() === \"toolresult\" ||\n role.toLowerCase() === \"tool_result\" ||\n typeof m.toolCallId === \"string\" ||\n typeof m.tool_call_id === \"string\";\n\n const toolCards = extractToolCards(message);\n const hasToolCards = toolCards.length > 0;\n\n const extractedText = extractText(message);\n const extractedThinking =\n opts.showReasoning && role === \"assistant\" ? extractThinking(message) : null;\n const markdownBase = extractedText?.trim() ? extractedText : null;\n const reasoningMarkdown = extractedThinking\n ? formatReasoningMarkdown(extractedThinking)\n : null;\n const markdown = markdownBase;\n const canCopyMarkdown = role === \"assistant\" && Boolean(markdown?.trim());\n\n const bubbleClasses = [\n \"chat-bubble\",\n canCopyMarkdown ? \"has-copy\" : \"\",\n opts.isStreaming ? \"streaming\" : \"\",\n \"fade-in\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n if (!markdown && hasToolCards && isToolResult) {\n return html`${toolCards.map((card) =>\n renderToolCardSidebar(card, onOpenSidebar),\n )}`;\n }\n\n if (!markdown && !hasToolCards) return nothing;\n\n return html`\n
    \n ${canCopyMarkdown ? renderCopyAsMarkdownButton(markdown!) : nothing}\n ${reasoningMarkdown\n ? html`
    ${unsafeHTML(\n toSanitizedMarkdownHtml(reasoningMarkdown),\n )}
    `\n : nothing}\n ${markdown\n ? html`
    ${unsafeHTML(toSanitizedMarkdownHtml(markdown))}
    `\n : nothing}\n ${toolCards.map((card) => renderToolCardSidebar(card, onOpenSidebar))}\n
    \n `;\n}\n","import { html, nothing } from \"lit\";\nimport { unsafeHTML } from \"lit/directives/unsafe-html.js\";\n\nimport { toSanitizedMarkdownHtml } from \"../markdown\";\n\nexport type MarkdownSidebarProps = {\n content: string | null;\n error: string | null;\n onClose: () => void;\n onViewRawText: () => void;\n};\n\nexport function renderMarkdownSidebar(props: MarkdownSidebarProps) {\n return html`\n
    \n
    \n
    Tool Output
    \n \n
    \n
    \n ${props.error\n ? html`\n
    ${props.error}
    \n \n `\n : props.content\n ? html`
    ${unsafeHTML(toSanitizedMarkdownHtml(props.content))}
    `\n : html`
    No content available
    `}\n
    \n
    \n `;\n}\n","import { LitElement, html, css } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\n\n/**\n * A draggable divider for resizable split views.\n * Dispatches 'resize' events with { splitRatio: number } detail.\n */\n@customElement(\"resizable-divider\")\nexport class ResizableDivider extends LitElement {\n @property({ type: Number }) splitRatio = 0.6;\n @property({ type: Number }) minRatio = 0.4;\n @property({ type: Number }) maxRatio = 0.7;\n\n private isDragging = false;\n private startX = 0;\n private startRatio = 0;\n\n static styles = css`\n :host {\n width: 4px;\n cursor: col-resize;\n background: var(--border, #333);\n transition: background 150ms ease-out;\n flex-shrink: 0;\n position: relative;\n }\n\n :host::before {\n content: \"\";\n position: absolute;\n top: 0;\n left: -4px;\n right: -4px;\n bottom: 0;\n }\n\n :host(:hover) {\n background: var(--accent, #007bff);\n }\n\n :host(.dragging) {\n background: var(--accent, #007bff);\n }\n `;\n\n render() {\n return html``;\n }\n\n connectedCallback() {\n super.connectedCallback();\n this.addEventListener(\"mousedown\", this.handleMouseDown);\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener(\"mousedown\", this.handleMouseDown);\n document.removeEventListener(\"mousemove\", this.handleMouseMove);\n document.removeEventListener(\"mouseup\", this.handleMouseUp);\n }\n\n private handleMouseDown = (e: MouseEvent) => {\n this.isDragging = true;\n this.startX = e.clientX;\n this.startRatio = this.splitRatio;\n this.classList.add(\"dragging\");\n\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n\n e.preventDefault();\n };\n\n private handleMouseMove = (e: MouseEvent) => {\n if (!this.isDragging) return;\n\n const container = this.parentElement;\n if (!container) return;\n\n const containerWidth = container.getBoundingClientRect().width;\n const deltaX = e.clientX - this.startX;\n const deltaRatio = deltaX / containerWidth;\n\n let newRatio = this.startRatio + deltaRatio;\n newRatio = Math.max(this.minRatio, Math.min(this.maxRatio, newRatio));\n\n this.dispatchEvent(\n new CustomEvent(\"resize\", {\n detail: { splitRatio: newRatio },\n bubbles: true,\n composed: true,\n })\n );\n };\n\n private handleMouseUp = () => {\n this.isDragging = false;\n this.classList.remove(\"dragging\");\n\n document.removeEventListener(\"mousemove\", this.handleMouseMove);\n document.removeEventListener(\"mouseup\", this.handleMouseUp);\n };\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"resizable-divider\": ResizableDivider;\n }\n}\n","import { html, nothing } from \"lit\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport type { SessionsListResult } from \"../types\";\nimport type { ChatQueueItem } from \"../ui-types\";\nimport type { ChatItem, MessageGroup } from \"../types/chat-types\";\nimport {\n normalizeMessage,\n normalizeRoleForGrouping,\n} from \"../chat/message-normalizer\";\nimport { extractText } from \"../chat/message-extract\";\nimport {\n renderMessageGroup,\n renderReadingIndicatorGroup,\n renderStreamingGroup,\n} from \"../chat/grouped-render\";\nimport { renderMarkdownSidebar } from \"./markdown-sidebar\";\nimport \"../components/resizable-divider\";\n\nexport type ChatProps = {\n sessionKey: string;\n onSessionKeyChange: (next: string) => void;\n thinkingLevel: string | null;\n showThinking: boolean;\n loading: boolean;\n sending: boolean;\n canAbort?: boolean;\n messages: unknown[];\n toolMessages: unknown[];\n stream: string | null;\n streamStartedAt: number | null;\n assistantAvatarUrl?: string | null;\n draft: string;\n queue: ChatQueueItem[];\n connected: boolean;\n canSend: boolean;\n disabledReason: string | null;\n error: string | null;\n sessions: SessionsListResult | null;\n // Focus mode\n focusMode: boolean;\n // Sidebar state\n sidebarOpen?: boolean;\n sidebarContent?: string | null;\n sidebarError?: string | null;\n splitRatio?: number;\n assistantName: string;\n assistantAvatar: string | null;\n // Event handlers\n onRefresh: () => void;\n onToggleFocusMode: () => void;\n onDraftChange: (next: string) => void;\n onSend: () => void;\n onAbort?: () => void;\n onQueueRemove: (id: string) => void;\n onNewSession: () => void;\n onOpenSidebar?: (content: string) => void;\n onCloseSidebar?: () => void;\n onSplitRatioChange?: (ratio: number) => void;\n onChatScroll?: (event: Event) => void;\n};\n\nexport function renderChat(props: ChatProps) {\n const canCompose = props.connected;\n const isBusy = props.sending || props.stream !== null;\n const activeSession = props.sessions?.sessions?.find(\n (row) => row.key === props.sessionKey,\n );\n const reasoningLevel = activeSession?.reasoningLevel ?? \"off\";\n const showReasoning = props.showThinking && reasoningLevel !== \"off\";\n const assistantIdentity = {\n name: props.assistantName,\n avatar: props.assistantAvatar ?? props.assistantAvatarUrl ?? null,\n };\n\n const composePlaceholder = props.connected\n ? \"Message (↩ to send, Shift+↩ for line breaks)\"\n : \"Connect to the gateway to start chatting…\";\n\n const splitRatio = props.splitRatio ?? 0.6;\n const sidebarOpen = Boolean(props.sidebarOpen && props.onCloseSidebar);\n\n return html`\n
    \n ${props.disabledReason\n ? html`
    ${props.disabledReason}
    `\n : nothing}\n\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n ${props.focusMode\n ? html`\n \n ✕\n \n `\n : nothing}\n\n \n \n \n ${props.loading\n ? html`
    Loading chat…
    `\n : nothing}\n ${repeat(buildChatItems(props), (item) => item.key, (item) => {\n if (item.kind === \"reading-indicator\") {\n return renderReadingIndicatorGroup(assistantIdentity);\n }\n\n if (item.kind === \"stream\") {\n return renderStreamingGroup(\n item.text,\n item.startedAt,\n props.onOpenSidebar,\n assistantIdentity,\n );\n }\n\n if (item.kind === \"group\") {\n return renderMessageGroup(item, {\n onOpenSidebar: props.onOpenSidebar,\n showReasoning,\n assistantName: props.assistantName,\n assistantAvatar: assistantIdentity.avatar,\n });\n }\n\n return nothing;\n })}\n \n \n\n ${sidebarOpen\n ? html`\n \n props.onSplitRatioChange?.(e.detail.splitRatio)}\n >\n
    \n ${renderMarkdownSidebar({\n content: props.sidebarContent ?? null,\n error: props.sidebarError ?? null,\n onClose: props.onCloseSidebar!,\n onViewRawText: () => {\n if (!props.sidebarContent || !props.onOpenSidebar) return;\n props.onOpenSidebar(`\\`\\`\\`\\n${props.sidebarContent}\\n\\`\\`\\``);\n },\n })}\n
    \n `\n : nothing}\n \n\n ${props.queue.length\n ? html`\n
    \n
    Queued (${props.queue.length})
    \n
    \n ${props.queue.map(\n (item) => html`\n
    \n
    ${item.text}
    \n props.onQueueRemove(item.id)}\n >\n ✕\n \n
    \n `,\n )}\n
    \n
    \n `\n : nothing}\n\n
    \n \n
    \n \n New session\n \n \n ${isBusy ? \"Queue\" : \"Send\"}\n \n
    \n
    \n
    \n `;\n}\n\nconst CHAT_HISTORY_RENDER_LIMIT = 200;\n\nfunction groupMessages(items: ChatItem[]): Array {\n const result: Array = [];\n let currentGroup: MessageGroup | null = null;\n\n for (const item of items) {\n if (item.kind !== \"message\") {\n if (currentGroup) {\n result.push(currentGroup);\n currentGroup = null;\n }\n result.push(item);\n continue;\n }\n\n const normalized = normalizeMessage(item.message);\n const role = normalizeRoleForGrouping(normalized.role);\n const timestamp = normalized.timestamp || Date.now();\n\n if (!currentGroup || currentGroup.role !== role) {\n if (currentGroup) result.push(currentGroup);\n currentGroup = {\n kind: \"group\",\n key: `group:${role}:${item.key}`,\n role,\n messages: [{ message: item.message, key: item.key }],\n timestamp,\n isStreaming: false,\n };\n } else {\n currentGroup.messages.push({ message: item.message, key: item.key });\n }\n }\n\n if (currentGroup) result.push(currentGroup);\n return result;\n}\n\nfunction buildChatItems(props: ChatProps): Array {\n const items: ChatItem[] = [];\n const history = Array.isArray(props.messages) ? props.messages : [];\n const tools = Array.isArray(props.toolMessages) ? props.toolMessages : [];\n const historyStart = Math.max(0, history.length - CHAT_HISTORY_RENDER_LIMIT);\n if (historyStart > 0) {\n items.push({\n kind: \"message\",\n key: \"chat:history:notice\",\n message: {\n role: \"system\",\n content: `Showing last ${CHAT_HISTORY_RENDER_LIMIT} messages (${historyStart} hidden).`,\n timestamp: Date.now(),\n },\n });\n }\n for (let i = historyStart; i < history.length; i++) {\n const msg = history[i];\n const normalized = normalizeMessage(msg);\n\n if (!props.showThinking && normalized.role.toLowerCase() === \"toolresult\") {\n continue;\n }\n\n items.push({\n kind: \"message\",\n key: messageKey(msg, i),\n message: msg,\n });\n }\n if (props.showThinking) {\n for (let i = 0; i < tools.length; i++) {\n items.push({\n kind: \"message\",\n key: messageKey(tools[i], i + history.length),\n message: tools[i],\n });\n }\n }\n\n if (props.stream !== null) {\n const key = `stream:${props.sessionKey}:${props.streamStartedAt ?? \"live\"}`;\n if (props.stream.trim().length > 0) {\n items.push({\n kind: \"stream\",\n key,\n text: props.stream,\n startedAt: props.streamStartedAt ?? Date.now(),\n });\n } else {\n items.push({ kind: \"reading-indicator\", key });\n }\n }\n\n return groupMessages(items);\n}\n\nfunction messageKey(message: unknown, index: number): string {\n const m = message as Record;\n const toolCallId = typeof m.toolCallId === \"string\" ? m.toolCallId : \"\";\n if (toolCallId) return `tool:${toolCallId}`;\n const id = typeof m.id === \"string\" ? m.id : \"\";\n if (id) return `msg:${id}`;\n const messageId = typeof m.messageId === \"string\" ? m.messageId : \"\";\n if (messageId) return `msg:${messageId}`;\n const timestamp = typeof m.timestamp === \"number\" ? m.timestamp : null;\n const role = typeof m.role === \"string\" ? m.role : \"unknown\";\n const fingerprint =\n extractText(message) ?? (typeof m.content === \"string\" ? m.content : null);\n const seed = fingerprint ?? safeJson(message) ?? String(index);\n const hash = fnv1a(seed);\n return timestamp ? `msg:${role}:${timestamp}:${hash}` : `msg:${role}:${hash}`;\n}\n\nfunction safeJson(value: unknown): string | null {\n try {\n return JSON.stringify(value);\n } catch {\n return null;\n }\n}\n\nfunction fnv1a(input: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(36);\n}\n","import type { ConfigUiHints } from \"../types\";\n\nexport type JsonSchema = {\n type?: string | string[];\n title?: string;\n description?: string;\n properties?: Record;\n items?: JsonSchema | JsonSchema[];\n additionalProperties?: JsonSchema | boolean;\n enum?: unknown[];\n const?: unknown;\n default?: unknown;\n anyOf?: JsonSchema[];\n oneOf?: JsonSchema[];\n allOf?: JsonSchema[];\n nullable?: boolean;\n};\n\nexport function schemaType(schema: JsonSchema): string | undefined {\n if (!schema) return undefined;\n if (Array.isArray(schema.type)) {\n const filtered = schema.type.filter((t) => t !== \"null\");\n return filtered[0] ?? schema.type[0];\n }\n return schema.type;\n}\n\nexport function defaultValue(schema?: JsonSchema): unknown {\n if (!schema) return \"\";\n if (schema.default !== undefined) return schema.default;\n const type = schemaType(schema);\n switch (type) {\n case \"object\":\n return {};\n case \"array\":\n return [];\n case \"boolean\":\n return false;\n case \"number\":\n case \"integer\":\n return 0;\n case \"string\":\n return \"\";\n default:\n return \"\";\n }\n}\n\nexport function pathKey(path: Array): string {\n return path.filter((segment) => typeof segment === \"string\").join(\".\");\n}\n\nexport function hintForPath(path: Array, hints: ConfigUiHints) {\n const key = pathKey(path);\n const direct = hints[key];\n if (direct) return direct;\n const segments = key.split(\".\");\n for (const [hintKey, hint] of Object.entries(hints)) {\n if (!hintKey.includes(\"*\")) continue;\n const hintSegments = hintKey.split(\".\");\n if (hintSegments.length !== segments.length) continue;\n let match = true;\n for (let i = 0; i < segments.length; i += 1) {\n if (hintSegments[i] !== \"*\" && hintSegments[i] !== segments[i]) {\n match = false;\n break;\n }\n }\n if (match) return hint;\n }\n return undefined;\n}\n\nexport function humanize(raw: string) {\n return raw\n .replace(/_/g, \" \")\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .replace(/\\s+/g, \" \")\n .replace(/^./, (m) => m.toUpperCase());\n}\n\nexport function isSensitivePath(path: Array): boolean {\n const key = pathKey(path).toLowerCase();\n return (\n key.includes(\"token\") ||\n key.includes(\"password\") ||\n key.includes(\"secret\") ||\n key.includes(\"apikey\") ||\n key.endsWith(\"key\")\n );\n}\n\n","import { html, nothing, type TemplateResult } from \"lit\";\nimport type { ConfigUiHints } from \"../types\";\nimport {\n defaultValue,\n hintForPath,\n humanize,\n isSensitivePath,\n pathKey,\n schemaType,\n type JsonSchema,\n} from \"./config-form.shared\";\n\nconst META_KEYS = new Set([\"title\", \"description\", \"default\", \"nullable\"]);\n\nfunction isAnySchema(schema: JsonSchema): boolean {\n const keys = Object.keys(schema ?? {}).filter((key) => !META_KEYS.has(key));\n return keys.length === 0;\n}\n\nfunction jsonValue(value: unknown): string {\n if (value === undefined) return \"\";\n try {\n return JSON.stringify(value, null, 2) ?? \"\";\n } catch {\n return \"\";\n }\n}\n\n// SVG Icons as template literals\nconst icons = {\n chevronDown: html``,\n plus: html``,\n minus: html``,\n trash: html``,\n edit: html``,\n};\n\nexport function renderNode(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult | typeof nothing {\n const { schema, value, path, hints, unsupported, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const type = schemaType(schema);\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const key = pathKey(path);\n\n if (unsupported.has(key)) {\n return html`
    \n
    ${label}
    \n
    Unsupported schema node. Use Raw mode.
    \n
    `;\n }\n\n // Handle anyOf/oneOf unions\n if (schema.anyOf || schema.oneOf) {\n const variants = schema.anyOf ?? schema.oneOf ?? [];\n const nonNull = variants.filter(\n (v) => !(v.type === \"null\" || (Array.isArray(v.type) && v.type.includes(\"null\")))\n );\n\n if (nonNull.length === 1) {\n return renderNode({ ...params, schema: nonNull[0] });\n }\n\n // Check if it's a set of literal values (enum-like)\n const extractLiteral = (v: JsonSchema): unknown | undefined => {\n if (v.const !== undefined) return v.const;\n if (v.enum && v.enum.length === 1) return v.enum[0];\n return undefined;\n };\n const literals = nonNull.map(extractLiteral);\n const allLiterals = literals.every((v) => v !== undefined);\n\n if (allLiterals && literals.length > 0 && literals.length <= 5) {\n // Use segmented control for small sets\n const resolvedValue = value ?? schema.default;\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n ${literals.map((lit, idx) => html`\n onPatch(path, lit)}\n >\n ${String(lit)}\n \n `)}\n
    \n
    \n `;\n }\n\n if (allLiterals && literals.length > 5) {\n // Use dropdown for larger sets\n return renderSelect({ ...params, options: literals, value: value ?? schema.default });\n }\n\n // Handle mixed primitive types\n const primitiveTypes = new Set(\n nonNull.map((variant) => schemaType(variant)).filter(Boolean)\n );\n const normalizedTypes = new Set(\n [...primitiveTypes].map((v) => (v === \"integer\" ? \"number\" : v))\n );\n\n if ([...normalizedTypes].every((v) => [\"string\", \"number\", \"boolean\"].includes(v as string))) {\n const hasString = normalizedTypes.has(\"string\");\n const hasNumber = normalizedTypes.has(\"number\");\n const hasBoolean = normalizedTypes.has(\"boolean\");\n \n if (hasBoolean && normalizedTypes.size === 1) {\n return renderNode({\n ...params,\n schema: { ...schema, type: \"boolean\", anyOf: undefined, oneOf: undefined },\n });\n }\n\n if (hasString || hasNumber) {\n return renderTextInput({\n ...params,\n inputType: hasNumber && !hasString ? \"number\" : \"text\",\n });\n }\n }\n }\n\n // Enum - use segmented for small, dropdown for large\n if (schema.enum) {\n const options = schema.enum;\n if (options.length <= 5) {\n const resolvedValue = value ?? schema.default;\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n ${options.map((opt) => html`\n onPatch(path, opt)}\n >\n ${String(opt)}\n \n `)}\n
    \n
    \n `;\n }\n return renderSelect({ ...params, options, value: value ?? schema.default });\n }\n\n // Object type - collapsible section\n if (type === \"object\") {\n return renderObject(params);\n }\n\n // Array type\n if (type === \"array\") {\n return renderArray(params);\n }\n\n // Boolean - toggle row\n if (type === \"boolean\") {\n const displayValue = typeof value === \"boolean\" ? value : typeof schema.default === \"boolean\" ? schema.default : false;\n return html`\n \n `;\n }\n\n // Number/Integer\n if (type === \"number\" || type === \"integer\") {\n return renderNumberInput(params);\n }\n\n // String\n if (type === \"string\") {\n return renderTextInput({ ...params, inputType: \"text\" });\n }\n\n // Fallback\n return html`\n
    \n
    ${label}
    \n
    Unsupported type: ${type}. Use Raw mode.
    \n
    \n `;\n}\n\nfunction renderTextInput(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n disabled: boolean;\n showLabel?: boolean;\n inputType: \"text\" | \"number\";\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, disabled, onPatch, inputType } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const isSensitive = hint?.sensitive ?? isSensitivePath(path);\n const placeholder =\n hint?.placeholder ??\n (isSensitive ? \"••••\" : schema.default !== undefined ? `Default: ${schema.default}` : \"\");\n const displayValue = value ?? \"\";\n\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n {\n const raw = (e.target as HTMLInputElement).value;\n if (inputType === \"number\") {\n if (raw.trim() === \"\") {\n onPatch(path, undefined);\n return;\n }\n const parsed = Number(raw);\n onPatch(path, Number.isNaN(parsed) ? raw : parsed);\n return;\n }\n onPatch(path, raw);\n }}\n />\n ${schema.default !== undefined ? html`\n onPatch(path, schema.default)}\n >↺\n ` : nothing}\n
    \n
    \n `;\n}\n\nfunction renderNumberInput(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const displayValue = value ?? schema.default ?? \"\";\n const numValue = typeof displayValue === \"number\" ? displayValue : 0;\n\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n
    \n onPatch(path, numValue - 1)}\n >−\n {\n const raw = (e.target as HTMLInputElement).value;\n const parsed = raw === \"\" ? undefined : Number(raw);\n onPatch(path, parsed);\n }}\n />\n onPatch(path, numValue + 1)}\n >+\n
    \n
    \n `;\n}\n\nfunction renderSelect(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n disabled: boolean;\n showLabel?: boolean;\n options: unknown[];\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, disabled, options, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n const resolvedValue = value ?? schema.default;\n const currentIndex = options.findIndex(\n (opt) => opt === resolvedValue || String(opt) === String(resolvedValue),\n );\n const unset = \"__unset__\";\n\n return html`\n
    \n ${showLabel ? html`` : nothing}\n ${help ? html`
    ${help}
    ` : nothing}\n = 0 ? String(currentIndex) : unset}\n @change=${(e: Event) => {\n const val = (e.target as HTMLSelectElement).value;\n onPatch(path, val === unset ? undefined : options[Number(val)]);\n }}\n >\n \n ${options.map((opt, idx) => html`\n \n `)}\n \n
    \n `;\n}\n\nfunction renderObject(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, unsupported, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n \n const fallback = value ?? schema.default;\n const obj = fallback && typeof fallback === \"object\" && !Array.isArray(fallback)\n ? (fallback as Record)\n : {};\n const props = schema.properties ?? {};\n const entries = Object.entries(props);\n \n // Sort by hint order\n const sorted = entries.sort((a, b) => {\n const orderA = hintForPath([...path, a[0]], hints)?.order ?? 0;\n const orderB = hintForPath([...path, b[0]], hints)?.order ?? 0;\n if (orderA !== orderB) return orderA - orderB;\n return a[0].localeCompare(b[0]);\n });\n\n const reserved = new Set(Object.keys(props));\n const additional = schema.additionalProperties;\n const allowExtra = Boolean(additional) && typeof additional === \"object\";\n\n // For top-level, don't wrap in collapsible\n if (path.length === 1) {\n return html`\n
    \n ${sorted.map(([propKey, node]) =>\n renderNode({\n schema: node,\n value: obj[propKey],\n path: [...path, propKey],\n hints,\n unsupported,\n disabled,\n onPatch,\n })\n )}\n ${allowExtra ? renderMapField({\n schema: additional as JsonSchema,\n value: obj,\n path,\n hints,\n unsupported,\n disabled,\n reservedKeys: reserved,\n onPatch,\n }) : nothing}\n
    \n `;\n }\n\n // Nested objects get collapsible treatment\n return html`\n
    \n \n ${label}\n ${icons.chevronDown}\n \n ${help ? html`
    ${help}
    ` : nothing}\n
    \n ${sorted.map(([propKey, node]) =>\n renderNode({\n schema: node,\n value: obj[propKey],\n path: [...path, propKey],\n hints,\n unsupported,\n disabled,\n onPatch,\n })\n )}\n ${allowExtra ? renderMapField({\n schema: additional as JsonSchema,\n value: obj,\n path,\n hints,\n unsupported,\n disabled,\n reservedKeys: reserved,\n onPatch,\n }) : nothing}\n
    \n
    \n `;\n}\n\nfunction renderArray(params: {\n schema: JsonSchema;\n value: unknown;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n showLabel?: boolean;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, unsupported, disabled, onPatch } = params;\n const showLabel = params.showLabel ?? true;\n const hint = hintForPath(path, hints);\n const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));\n const help = hint?.help ?? schema.description;\n\n const itemsSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;\n if (!itemsSchema) {\n return html`\n
    \n
    ${label}
    \n
    Unsupported array schema. Use Raw mode.
    \n
    \n `;\n }\n\n const arr = Array.isArray(value) ? value : Array.isArray(schema.default) ? schema.default : [];\n\n return html`\n
    \n
    \n ${showLabel ? html`${label}` : nothing}\n ${arr.length} item${arr.length !== 1 ? 's' : ''}\n {\n const next = [...arr, defaultValue(itemsSchema)];\n onPatch(path, next);\n }}\n >\n ${icons.plus}\n Add\n \n
    \n ${help ? html`
    ${help}
    ` : nothing}\n \n ${arr.length === 0 ? html`\n
    \n No items yet. Click \"Add\" to create one.\n
    \n ` : html`\n
    \n ${arr.map((item, idx) => html`\n
    \n
    \n #${idx + 1}\n {\n const next = [...arr];\n next.splice(idx, 1);\n onPatch(path, next);\n }}\n >\n ${icons.trash}\n \n
    \n
    \n ${renderNode({\n schema: itemsSchema,\n value: item,\n path: [...path, idx],\n hints,\n unsupported,\n disabled,\n showLabel: false,\n onPatch,\n })}\n
    \n
    \n `)}\n
    \n `}\n
    \n `;\n}\n\nfunction renderMapField(params: {\n schema: JsonSchema;\n value: Record;\n path: Array;\n hints: ConfigUiHints;\n unsupported: Set;\n disabled: boolean;\n reservedKeys: Set;\n onPatch: (path: Array, value: unknown) => void;\n}): TemplateResult {\n const { schema, value, path, hints, unsupported, disabled, reservedKeys, onPatch } = params;\n const anySchema = isAnySchema(schema);\n const entries = Object.entries(value ?? {}).filter(([key]) => !reservedKeys.has(key));\n\n return html`\n
    \n
    \n Custom entries\n {\n const next = { ...(value ?? {}) };\n let index = 1;\n let key = `custom-${index}`;\n while (key in next) {\n index += 1;\n key = `custom-${index}`;\n }\n next[key] = anySchema ? {} : defaultValue(schema);\n onPatch(path, next);\n }}\n >\n ${icons.plus}\n Add Entry\n \n
    \n \n ${entries.length === 0 ? html`\n
    No custom entries.
    \n ` : html`\n
    \n ${entries.map(([key, entryValue]) => {\n const valuePath = [...path, key];\n const fallback = jsonValue(entryValue);\n return html`\n
    \n
    \n {\n const nextKey = (e.target as HTMLInputElement).value.trim();\n if (!nextKey || nextKey === key) return;\n const next = { ...(value ?? {}) };\n if (nextKey in next) return;\n next[nextKey] = next[key];\n delete next[key];\n onPatch(path, next);\n }}\n />\n
    \n
    \n ${anySchema\n ? html`\n {\n const target = e.target as HTMLTextAreaElement;\n const raw = target.value.trim();\n if (!raw) {\n onPatch(valuePath, undefined);\n return;\n }\n try {\n onPatch(valuePath, JSON.parse(raw));\n } catch {\n target.value = fallback;\n }\n }}\n >\n `\n : renderNode({\n schema,\n value: entryValue,\n path: valuePath,\n hints,\n unsupported,\n disabled,\n showLabel: false,\n onPatch,\n })}\n
    \n {\n const next = { ...(value ?? {}) };\n delete next[key];\n onPatch(path, next);\n }}\n >\n ${icons.trash}\n \n
    \n `;\n })}\n
    \n `}\n
    \n `;\n}\n","import { html, nothing } from \"lit\";\nimport type { ConfigUiHints } from \"../types\";\nimport {\n hintForPath,\n humanize,\n schemaType,\n type JsonSchema,\n} from \"./config-form.shared\";\nimport { renderNode } from \"./config-form.node\";\n\nexport type ConfigFormProps = {\n schema: JsonSchema | null;\n uiHints: ConfigUiHints;\n value: Record | null;\n disabled?: boolean;\n unsupportedPaths?: string[];\n searchQuery?: string;\n activeSection?: string | null;\n activeSubsection?: string | null;\n onPatch: (path: Array, value: unknown) => void;\n};\n\n// SVG Icons for section cards (Lucide-style)\nconst sectionIcons = {\n env: html``,\n update: html``,\n agents: html``,\n auth: html``,\n channels: html``,\n messages: html``,\n commands: html``,\n hooks: html``,\n skills: html``,\n tools: html``,\n gateway: html``,\n wizard: html``,\n // Additional sections\n meta: html``,\n logging: html``,\n browser: html``,\n ui: html``,\n models: html``,\n bindings: html``,\n broadcast: html``,\n audio: html``,\n session: html``,\n cron: html``,\n web: html``,\n discovery: html``,\n canvasHost: html``,\n talk: html``,\n plugins: html``,\n default: html``,\n};\n\n// Section metadata\nexport const SECTION_META: Record = {\n env: { label: \"Environment Variables\", description: \"Environment variables passed to the gateway process\" },\n update: { label: \"Updates\", description: \"Auto-update settings and release channel\" },\n agents: { label: \"Agents\", description: \"Agent configurations, models, and identities\" },\n auth: { label: \"Authentication\", description: \"API keys and authentication profiles\" },\n channels: { label: \"Channels\", description: \"Messaging channels (Telegram, Discord, Slack, etc.)\" },\n messages: { label: \"Messages\", description: \"Message handling and routing settings\" },\n commands: { label: \"Commands\", description: \"Custom slash commands\" },\n hooks: { label: \"Hooks\", description: \"Webhooks and event hooks\" },\n skills: { label: \"Skills\", description: \"Skill packs and capabilities\" },\n tools: { label: \"Tools\", description: \"Tool configurations (browser, search, etc.)\" },\n gateway: { label: \"Gateway\", description: \"Gateway server settings (port, auth, binding)\" },\n wizard: { label: \"Setup Wizard\", description: \"Setup wizard state and history\" },\n // Additional sections\n meta: { label: \"Metadata\", description: \"Gateway metadata and version information\" },\n logging: { label: \"Logging\", description: \"Log levels and output configuration\" },\n browser: { label: \"Browser\", description: \"Browser automation settings\" },\n ui: { label: \"UI\", description: \"User interface preferences\" },\n models: { label: \"Models\", description: \"AI model configurations and providers\" },\n bindings: { label: \"Bindings\", description: \"Key bindings and shortcuts\" },\n broadcast: { label: \"Broadcast\", description: \"Broadcast and notification settings\" },\n audio: { label: \"Audio\", description: \"Audio input/output settings\" },\n session: { label: \"Session\", description: \"Session management and persistence\" },\n cron: { label: \"Cron\", description: \"Scheduled tasks and automation\" },\n web: { label: \"Web\", description: \"Web server and API settings\" },\n discovery: { label: \"Discovery\", description: \"Service discovery and networking\" },\n canvasHost: { label: \"Canvas Host\", description: \"Canvas rendering and display\" },\n talk: { label: \"Talk\", description: \"Voice and speech settings\" },\n plugins: { label: \"Plugins\", description: \"Plugin management and extensions\" },\n};\n\nfunction getSectionIcon(key: string) {\n return sectionIcons[key as keyof typeof sectionIcons] ?? sectionIcons.default;\n}\n\nfunction matchesSearch(key: string, schema: JsonSchema, query: string): boolean {\n if (!query) return true;\n const q = query.toLowerCase();\n const meta = SECTION_META[key];\n \n // Check key name\n if (key.toLowerCase().includes(q)) return true;\n \n // Check label and description\n if (meta) {\n if (meta.label.toLowerCase().includes(q)) return true;\n if (meta.description.toLowerCase().includes(q)) return true;\n }\n \n return schemaMatches(schema, q);\n}\n\nfunction schemaMatches(schema: JsonSchema, query: string): boolean {\n if (schema.title?.toLowerCase().includes(query)) return true;\n if (schema.description?.toLowerCase().includes(query)) return true;\n if (schema.enum?.some((value) => String(value).toLowerCase().includes(query))) return true;\n\n if (schema.properties) {\n for (const [propKey, propSchema] of Object.entries(schema.properties)) {\n if (propKey.toLowerCase().includes(query)) return true;\n if (schemaMatches(propSchema, query)) return true;\n }\n }\n\n if (schema.items) {\n const items = Array.isArray(schema.items) ? schema.items : [schema.items];\n for (const item of items) {\n if (item && schemaMatches(item, query)) return true;\n }\n }\n\n if (schema.additionalProperties && typeof schema.additionalProperties === \"object\") {\n if (schemaMatches(schema.additionalProperties, query)) return true;\n }\n\n const unions = schema.anyOf ?? schema.oneOf ?? schema.allOf;\n if (unions) {\n for (const entry of unions) {\n if (entry && schemaMatches(entry, query)) return true;\n }\n }\n\n return false;\n}\n\nexport function renderConfigForm(props: ConfigFormProps) {\n if (!props.schema) {\n return html`
    Schema unavailable.
    `;\n }\n const schema = props.schema;\n const value = props.value ?? {};\n if (schemaType(schema) !== \"object\" || !schema.properties) {\n return html`
    Unsupported schema. Use Raw.
    `;\n }\n const unsupported = new Set(props.unsupportedPaths ?? []);\n const properties = schema.properties;\n const searchQuery = props.searchQuery ?? \"\";\n const activeSection = props.activeSection;\n const activeSubsection = props.activeSubsection ?? null;\n\n // Filter and sort entries\n let entries = Object.entries(properties);\n \n // Filter by active section\n if (activeSection) {\n entries = entries.filter(([key]) => key === activeSection);\n }\n \n // Filter by search\n if (searchQuery) {\n entries = entries.filter(([key, node]) => matchesSearch(key, node, searchQuery));\n }\n \n // Sort by hint order, then alphabetically\n entries.sort((a, b) => {\n const orderA = hintForPath([a[0]], props.uiHints)?.order ?? 50;\n const orderB = hintForPath([b[0]], props.uiHints)?.order ?? 50;\n if (orderA !== orderB) return orderA - orderB;\n return a[0].localeCompare(b[0]);\n });\n\n let subsectionContext:\n | { sectionKey: string; subsectionKey: string; schema: JsonSchema }\n | null = null;\n if (activeSection && activeSubsection && entries.length === 1) {\n const sectionSchema = entries[0]?.[1];\n if (\n sectionSchema &&\n schemaType(sectionSchema) === \"object\" &&\n sectionSchema.properties &&\n sectionSchema.properties[activeSubsection]\n ) {\n subsectionContext = {\n sectionKey: activeSection,\n subsectionKey: activeSubsection,\n schema: sectionSchema.properties[activeSubsection],\n };\n }\n }\n\n if (entries.length === 0) {\n return html`\n
    \n
    🔍
    \n
    \n ${searchQuery \n ? `No settings match \"${searchQuery}\"` \n : \"No settings in this section\"}\n
    \n
    \n `;\n }\n\n return html`\n
    \n ${subsectionContext\n ? (() => {\n const { sectionKey, subsectionKey, schema: node } = subsectionContext;\n const hint = hintForPath([sectionKey, subsectionKey], props.uiHints);\n const label = hint?.label ?? node.title ?? humanize(subsectionKey);\n const description = hint?.help ?? node.description ?? \"\";\n const sectionValue = (value as Record)[sectionKey];\n const scopedValue =\n sectionValue && typeof sectionValue === \"object\"\n ? (sectionValue as Record)[subsectionKey]\n : undefined;\n const id = `config-section-${sectionKey}-${subsectionKey}`;\n return html`\n
    \n
    \n ${getSectionIcon(sectionKey)}\n
    \n

    ${label}

    \n ${description\n ? html`

    ${description}

    `\n : nothing}\n
    \n
    \n
    \n ${renderNode({\n schema: node,\n value: scopedValue,\n path: [sectionKey, subsectionKey],\n hints: props.uiHints,\n unsupported,\n disabled: props.disabled ?? false,\n showLabel: false,\n onPatch: props.onPatch,\n })}\n
    \n
    \n `;\n })()\n : entries.map(([key, node]) => {\n const meta = SECTION_META[key] ?? {\n label: key.charAt(0).toUpperCase() + key.slice(1),\n description: node.description ?? \"\",\n };\n\n return html`\n
    \n
    \n ${getSectionIcon(key)}\n
    \n

    ${meta.label}

    \n ${meta.description\n ? html`

    ${meta.description}

    `\n : nothing}\n
    \n
    \n
    \n ${renderNode({\n schema: node,\n value: (value as Record)[key],\n path: [key],\n hints: props.uiHints,\n unsupported,\n disabled: props.disabled ?? false,\n showLabel: false,\n onPatch: props.onPatch,\n })}\n
    \n
    \n `;\n })}\n
    \n `;\n}\n","import { pathKey, schemaType, type JsonSchema } from \"./config-form.shared\";\n\nexport type ConfigSchemaAnalysis = {\n schema: JsonSchema | null;\n unsupportedPaths: string[];\n};\n\nconst META_KEYS = new Set([\"title\", \"description\", \"default\", \"nullable\"]);\n\nfunction isAnySchema(schema: JsonSchema): boolean {\n const keys = Object.keys(schema ?? {}).filter((key) => !META_KEYS.has(key));\n return keys.length === 0;\n}\n\nfunction normalizeEnum(values: unknown[]): { enumValues: unknown[]; nullable: boolean } {\n const filtered = values.filter((value) => value != null);\n const nullable = filtered.length !== values.length;\n const enumValues: unknown[] = [];\n for (const value of filtered) {\n if (!enumValues.some((existing) => Object.is(existing, value))) {\n enumValues.push(value);\n }\n }\n return { enumValues, nullable };\n}\n\nexport function analyzeConfigSchema(raw: unknown): ConfigSchemaAnalysis {\n if (!raw || typeof raw !== \"object\") {\n return { schema: null, unsupportedPaths: [\"\"] };\n }\n return normalizeSchemaNode(raw as JsonSchema, []);\n}\n\nfunction normalizeSchemaNode(\n schema: JsonSchema,\n path: Array,\n): ConfigSchemaAnalysis {\n const unsupported = new Set();\n const normalized: JsonSchema = { ...schema };\n const pathLabel = pathKey(path) || \"\";\n\n if (schema.anyOf || schema.oneOf || schema.allOf) {\n const union = normalizeUnion(schema, path);\n if (union) return union;\n return { schema, unsupportedPaths: [pathLabel] };\n }\n\n const nullable = Array.isArray(schema.type) && schema.type.includes(\"null\");\n const type =\n schemaType(schema) ??\n (schema.properties || schema.additionalProperties ? \"object\" : undefined);\n normalized.type = type ?? schema.type;\n normalized.nullable = nullable || schema.nullable;\n\n if (normalized.enum) {\n const { enumValues, nullable: enumNullable } = normalizeEnum(normalized.enum);\n normalized.enum = enumValues;\n if (enumNullable) normalized.nullable = true;\n if (enumValues.length === 0) unsupported.add(pathLabel);\n }\n\n if (type === \"object\") {\n const properties = schema.properties ?? {};\n const normalizedProps: Record = {};\n for (const [key, value] of Object.entries(properties)) {\n const res = normalizeSchemaNode(value, [...path, key]);\n if (res.schema) normalizedProps[key] = res.schema;\n for (const entry of res.unsupportedPaths) unsupported.add(entry);\n }\n normalized.properties = normalizedProps;\n\n if (schema.additionalProperties === true) {\n unsupported.add(pathLabel);\n } else if (schema.additionalProperties === false) {\n normalized.additionalProperties = false;\n } else if (\n schema.additionalProperties &&\n typeof schema.additionalProperties === \"object\"\n ) {\n if (!isAnySchema(schema.additionalProperties as JsonSchema)) {\n const res = normalizeSchemaNode(\n schema.additionalProperties as JsonSchema,\n [...path, \"*\"],\n );\n normalized.additionalProperties =\n res.schema ?? (schema.additionalProperties as JsonSchema);\n if (res.unsupportedPaths.length > 0) unsupported.add(pathLabel);\n }\n }\n } else if (type === \"array\") {\n const itemsSchema = Array.isArray(schema.items)\n ? schema.items[0]\n : schema.items;\n if (!itemsSchema) {\n unsupported.add(pathLabel);\n } else {\n const res = normalizeSchemaNode(itemsSchema, [...path, \"*\"]);\n normalized.items = res.schema ?? itemsSchema;\n if (res.unsupportedPaths.length > 0) unsupported.add(pathLabel);\n }\n } else if (\n type !== \"string\" &&\n type !== \"number\" &&\n type !== \"integer\" &&\n type !== \"boolean\" &&\n !normalized.enum\n ) {\n unsupported.add(pathLabel);\n }\n\n return {\n schema: normalized,\n unsupportedPaths: Array.from(unsupported),\n };\n}\n\nfunction normalizeUnion(\n schema: JsonSchema,\n path: Array,\n): ConfigSchemaAnalysis | null {\n if (schema.allOf) return null;\n const union = schema.anyOf ?? schema.oneOf;\n if (!union) return null;\n\n const literals: unknown[] = [];\n const remaining: JsonSchema[] = [];\n let nullable = false;\n\n for (const entry of union) {\n if (!entry || typeof entry !== \"object\") return null;\n if (Array.isArray(entry.enum)) {\n const { enumValues, nullable: enumNullable } = normalizeEnum(entry.enum);\n literals.push(...enumValues);\n if (enumNullable) nullable = true;\n continue;\n }\n if (\"const\" in entry) {\n if (entry.const == null) {\n nullable = true;\n continue;\n }\n literals.push(entry.const);\n continue;\n }\n if (schemaType(entry) === \"null\") {\n nullable = true;\n continue;\n }\n remaining.push(entry);\n }\n\n if (literals.length > 0 && remaining.length === 0) {\n const unique: unknown[] = [];\n for (const value of literals) {\n if (!unique.some((existing) => Object.is(existing, value))) {\n unique.push(value);\n }\n }\n return {\n schema: {\n ...schema,\n enum: unique,\n nullable,\n anyOf: undefined,\n oneOf: undefined,\n allOf: undefined,\n },\n unsupportedPaths: [],\n };\n }\n\n if (remaining.length === 1) {\n const res = normalizeSchemaNode(remaining[0], path);\n if (res.schema) {\n res.schema.nullable = nullable || res.schema.nullable;\n }\n return res;\n }\n\n const primitiveTypes = [\"string\", \"number\", \"integer\", \"boolean\"];\n if (\n remaining.length > 0 &&\n literals.length === 0 &&\n remaining.every((entry) => entry.type && primitiveTypes.includes(String(entry.type)))\n ) {\n return {\n schema: {\n ...schema,\n nullable,\n },\n unsupportedPaths: [],\n };\n }\n\n return null;\n}\n","import { html, nothing } from \"lit\";\nimport type { ConfigUiHints } from \"../types\";\nimport { analyzeConfigSchema, renderConfigForm, SECTION_META } from \"./config-form\";\nimport {\n hintForPath,\n humanize,\n schemaType,\n type JsonSchema,\n} from \"./config-form.shared\";\n\nexport type ConfigProps = {\n raw: string;\n valid: boolean | null;\n issues: unknown[];\n loading: boolean;\n saving: boolean;\n applying: boolean;\n updating: boolean;\n connected: boolean;\n schema: unknown | null;\n schemaLoading: boolean;\n uiHints: ConfigUiHints;\n formMode: \"form\" | \"raw\";\n formValue: Record | null;\n originalValue: Record | null;\n searchQuery: string;\n activeSection: string | null;\n activeSubsection: string | null;\n onRawChange: (next: string) => void;\n onFormModeChange: (mode: \"form\" | \"raw\") => void;\n onFormPatch: (path: Array, value: unknown) => void;\n onSearchChange: (query: string) => void;\n onSectionChange: (section: string | null) => void;\n onSubsectionChange: (section: string | null) => void;\n onReload: () => void;\n onSave: () => void;\n onApply: () => void;\n onUpdate: () => void;\n};\n\n// SVG Icons for sidebar (Lucide-style)\nconst sidebarIcons = {\n all: html``,\n env: html``,\n update: html``,\n agents: html``,\n auth: html``,\n channels: html``,\n messages: html``,\n commands: html``,\n hooks: html``,\n skills: html``,\n tools: html``,\n gateway: html``,\n wizard: html``,\n // Additional sections\n meta: html``,\n logging: html``,\n browser: html``,\n ui: html``,\n models: html``,\n bindings: html``,\n broadcast: html``,\n audio: html``,\n session: html``,\n cron: html``,\n web: html``,\n discovery: html``,\n canvasHost: html``,\n talk: html``,\n plugins: html``,\n default: html``,\n};\n\n// Section definitions\nconst SECTIONS: Array<{ key: string; label: string }> = [\n { key: \"env\", label: \"Environment\" },\n { key: \"update\", label: \"Updates\" },\n { key: \"agents\", label: \"Agents\" },\n { key: \"auth\", label: \"Authentication\" },\n { key: \"channels\", label: \"Channels\" },\n { key: \"messages\", label: \"Messages\" },\n { key: \"commands\", label: \"Commands\" },\n { key: \"hooks\", label: \"Hooks\" },\n { key: \"skills\", label: \"Skills\" },\n { key: \"tools\", label: \"Tools\" },\n { key: \"gateway\", label: \"Gateway\" },\n { key: \"wizard\", label: \"Setup Wizard\" },\n];\n\ntype SubsectionEntry = {\n key: string;\n label: string;\n description?: string;\n order: number;\n};\n\nconst ALL_SUBSECTION = \"__all__\";\n\nfunction getSectionIcon(key: string) {\n return sidebarIcons[key as keyof typeof sidebarIcons] ?? sidebarIcons.default;\n}\n\nfunction resolveSectionMeta(key: string, schema?: JsonSchema): {\n label: string;\n description?: string;\n} {\n const meta = SECTION_META[key];\n if (meta) return meta;\n return {\n label: schema?.title ?? humanize(key),\n description: schema?.description ?? \"\",\n };\n}\n\nfunction resolveSubsections(params: {\n key: string;\n schema: JsonSchema | undefined;\n uiHints: ConfigUiHints;\n}): SubsectionEntry[] {\n const { key, schema, uiHints } = params;\n if (!schema || schemaType(schema) !== \"object\" || !schema.properties) return [];\n const entries = Object.entries(schema.properties).map(([subKey, node]) => {\n const hint = hintForPath([key, subKey], uiHints);\n const label = hint?.label ?? node.title ?? humanize(subKey);\n const description = hint?.help ?? node.description ?? \"\";\n const order = hint?.order ?? 50;\n return { key: subKey, label, description, order };\n });\n entries.sort((a, b) => (a.order !== b.order ? a.order - b.order : a.key.localeCompare(b.key)));\n return entries;\n}\n\nfunction computeDiff(\n original: Record | null,\n current: Record | null\n): Array<{ path: string; from: unknown; to: unknown }> {\n if (!original || !current) return [];\n const changes: Array<{ path: string; from: unknown; to: unknown }> = [];\n \n function compare(orig: unknown, curr: unknown, path: string) {\n if (orig === curr) return;\n if (typeof orig !== typeof curr) {\n changes.push({ path, from: orig, to: curr });\n return;\n }\n if (typeof orig !== \"object\" || orig === null || curr === null) {\n if (orig !== curr) {\n changes.push({ path, from: orig, to: curr });\n }\n return;\n }\n if (Array.isArray(orig) && Array.isArray(curr)) {\n if (JSON.stringify(orig) !== JSON.stringify(curr)) {\n changes.push({ path, from: orig, to: curr });\n }\n return;\n }\n const origObj = orig as Record;\n const currObj = curr as Record;\n const allKeys = new Set([...Object.keys(origObj), ...Object.keys(currObj)]);\n for (const key of allKeys) {\n compare(origObj[key], currObj[key], path ? `${path}.${key}` : key);\n }\n }\n \n compare(original, current, \"\");\n return changes;\n}\n\nfunction truncateValue(value: unknown, maxLen = 40): string {\n let str: string;\n try {\n const json = JSON.stringify(value);\n str = json ?? String(value);\n } catch {\n str = String(value);\n }\n if (str.length <= maxLen) return str;\n return str.slice(0, maxLen - 3) + \"...\";\n}\n\nexport function renderConfig(props: ConfigProps) {\n const validity =\n props.valid == null ? \"unknown\" : props.valid ? \"valid\" : \"invalid\";\n const analysis = analyzeConfigSchema(props.schema);\n const formUnsafe = analysis.schema\n ? analysis.unsupportedPaths.length > 0\n : false;\n const canSaveForm =\n Boolean(props.formValue) && !props.loading && !formUnsafe;\n const canSave =\n props.connected &&\n !props.saving &&\n (props.formMode === \"raw\" ? true : canSaveForm);\n const canApply =\n props.connected &&\n !props.applying &&\n !props.updating &&\n (props.formMode === \"raw\" ? true : canSaveForm);\n const canUpdate = props.connected && !props.applying && !props.updating;\n\n // Get available sections from schema\n const schemaProps = analysis.schema?.properties ?? {};\n const availableSections = SECTIONS.filter(s => s.key in schemaProps);\n \n // Add any sections in schema but not in our list\n const knownKeys = new Set(SECTIONS.map(s => s.key));\n const extraSections = Object.keys(schemaProps)\n .filter(k => !knownKeys.has(k))\n .map(k => ({ key: k, label: k.charAt(0).toUpperCase() + k.slice(1) }));\n \n const allSections = [...availableSections, ...extraSections];\n\n const activeSectionSchema =\n props.activeSection && analysis.schema && schemaType(analysis.schema) === \"object\"\n ? (analysis.schema.properties?.[props.activeSection] as JsonSchema | undefined)\n : undefined;\n const activeSectionMeta = props.activeSection\n ? resolveSectionMeta(props.activeSection, activeSectionSchema)\n : null;\n const subsections = props.activeSection\n ? resolveSubsections({\n key: props.activeSection,\n schema: activeSectionSchema,\n uiHints: props.uiHints,\n })\n : [];\n const allowSubnav =\n props.formMode === \"form\" &&\n Boolean(props.activeSection) &&\n subsections.length > 0;\n const isAllSubsection = props.activeSubsection === ALL_SUBSECTION;\n const effectiveSubsection = props.searchQuery\n ? null\n : isAllSubsection\n ? null\n : props.activeSubsection ?? (subsections[0]?.key ?? null);\n \n // Compute diff for showing changes\n const diff = props.formMode === \"form\" \n ? computeDiff(props.originalValue, props.formValue)\n : [];\n const hasChanges = diff.length > 0;\n\n return html`\n
    \n \n \n \n \n
    \n \n
    \n
    \n ${hasChanges ? html`\n ${diff.length} unsaved change${diff.length !== 1 ? \"s\" : \"\"}\n ` : html`\n No changes\n `}\n
    \n
    \n \n \n ${props.saving ? \"Saving…\" : \"Save\"}\n \n \n ${props.applying ? \"Applying…\" : \"Apply\"}\n \n \n ${props.updating ? \"Updating…\" : \"Update\"}\n \n
    \n
    \n \n \n ${hasChanges ? html`\n
    \n \n View ${diff.length} pending change${diff.length !== 1 ? \"s\" : \"\"}\n \n \n \n \n
    \n ${diff.map(change => html`\n
    \n
    ${change.path}
    \n
    \n ${truncateValue(change.from)}\n \n ${truncateValue(change.to)}\n
    \n
    \n `)}\n
    \n
    \n ` : nothing}\n\n ${activeSectionMeta && props.formMode === \"form\"\n ? html`\n
    \n
    ${getSectionIcon(props.activeSection ?? \"\")}
    \n
    \n
    ${activeSectionMeta.label}
    \n ${activeSectionMeta.description\n ? html`
    ${activeSectionMeta.description}
    `\n : nothing}\n
    \n
    \n `\n : nothing}\n\n ${allowSubnav\n ? html`\n
    \n props.onSubsectionChange(ALL_SUBSECTION)}\n >\n All\n \n ${subsections.map(\n (entry) => html`\n props.onSubsectionChange(entry.key)}\n >\n ${entry.label}\n \n `,\n )}\n
    \n `\n : nothing}\n\n \n
    \n ${props.formMode === \"form\"\n ? html`\n ${props.schemaLoading\n ? html`
    \n
    \n Loading schema…\n
    `\n : renderConfigForm({\n schema: analysis.schema,\n uiHints: props.uiHints,\n value: props.formValue,\n disabled: props.loading || !props.formValue,\n unsupportedPaths: analysis.unsupportedPaths,\n onPatch: props.onFormPatch,\n searchQuery: props.searchQuery,\n activeSection: props.activeSection,\n activeSubsection: effectiveSubsection,\n })}\n ${formUnsafe\n ? html`
    \n Form view can't safely edit some fields.\n Use Raw to avoid losing config entries.\n
    `\n : nothing}\n `\n : html`\n \n `}\n
    \n\n ${props.issues.length > 0\n ? html`
    \n
    ${JSON.stringify(props.issues, null, 2)}
    \n
    `\n : nothing}\n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { ChannelAccountSnapshot } from \"../types\";\nimport type { ChannelKey, ChannelsProps } from \"./channels.types\";\n\nexport function formatDuration(ms?: number | null) {\n if (!ms && ms !== 0) return \"n/a\";\n const sec = Math.round(ms / 1000);\n if (sec < 60) return `${sec}s`;\n const min = Math.round(sec / 60);\n if (min < 60) return `${min}m`;\n const hr = Math.round(min / 60);\n return `${hr}h`;\n}\n\nexport function channelEnabled(key: ChannelKey, props: ChannelsProps) {\n const snapshot = props.snapshot;\n const channels = snapshot?.channels as Record | null;\n if (!snapshot || !channels) return false;\n const channelStatus = channels[key] as Record | undefined;\n const configured = typeof channelStatus?.configured === \"boolean\" && channelStatus.configured;\n const running = typeof channelStatus?.running === \"boolean\" && channelStatus.running;\n const connected = typeof channelStatus?.connected === \"boolean\" && channelStatus.connected;\n const accounts = snapshot.channelAccounts?.[key] ?? [];\n const accountActive = accounts.some(\n (account) => account.configured || account.running || account.connected,\n );\n return configured || running || connected || accountActive;\n}\n\nexport function getChannelAccountCount(\n key: ChannelKey,\n channelAccounts?: Record | null,\n): number {\n return channelAccounts?.[key]?.length ?? 0;\n}\n\nexport function renderChannelAccountCount(\n key: ChannelKey,\n channelAccounts?: Record | null,\n) {\n const count = getChannelAccountCount(key, channelAccounts);\n if (count < 2) return nothing;\n return html`
    Accounts (${count})
    `;\n}\n\n","import { html } from \"lit\";\n\nimport type { ConfigUiHints } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport {\n analyzeConfigSchema,\n renderNode,\n schemaType,\n type JsonSchema,\n} from \"./config-form\";\n\ntype ChannelConfigFormProps = {\n channelId: string;\n configValue: Record | null;\n schema: unknown | null;\n uiHints: ConfigUiHints;\n disabled: boolean;\n onPatch: (path: Array, value: unknown) => void;\n};\n\nfunction resolveSchemaNode(\n schema: JsonSchema | null,\n path: Array,\n): JsonSchema | null {\n let current = schema;\n for (const key of path) {\n if (!current) return null;\n const type = schemaType(current);\n if (type === \"object\") {\n const properties = current.properties ?? {};\n if (typeof key === \"string\" && properties[key]) {\n current = properties[key];\n continue;\n }\n const additional = current.additionalProperties;\n if (typeof key === \"string\" && additional && typeof additional === \"object\") {\n current = additional as JsonSchema;\n continue;\n }\n return null;\n }\n if (type === \"array\") {\n if (typeof key !== \"number\") return null;\n const items = Array.isArray(current.items) ? current.items[0] : current.items;\n current = items ?? null;\n continue;\n }\n return null;\n }\n return current;\n}\n\nfunction resolveChannelValue(\n config: Record,\n channelId: string,\n): Record {\n const channels = (config.channels ?? {}) as Record;\n const fromChannels = channels[channelId];\n const fallback = config[channelId];\n const resolved =\n (fromChannels && typeof fromChannels === \"object\"\n ? (fromChannels as Record)\n : null) ??\n (fallback && typeof fallback === \"object\"\n ? (fallback as Record)\n : null);\n return resolved ?? {};\n}\n\nexport function renderChannelConfigForm(props: ChannelConfigFormProps) {\n const analysis = analyzeConfigSchema(props.schema);\n const normalized = analysis.schema;\n if (!normalized) {\n return html`
    Schema unavailable. Use Raw.
    `;\n }\n const node = resolveSchemaNode(normalized, [\"channels\", props.channelId]);\n if (!node) {\n return html`
    Channel config schema unavailable.
    `;\n }\n const configValue = props.configValue ?? {};\n const value = resolveChannelValue(configValue, props.channelId);\n return html`\n
    \n ${renderNode({\n schema: node,\n value,\n path: [\"channels\", props.channelId],\n hints: props.uiHints,\n unsupported: new Set(analysis.unsupportedPaths),\n disabled: props.disabled,\n showLabel: false,\n onPatch: props.onPatch,\n })}\n
    \n `;\n}\n\nexport function renderChannelConfigSection(params: {\n channelId: string;\n props: ChannelsProps;\n}) {\n const { channelId, props } = params;\n const disabled = props.configSaving || props.configSchemaLoading;\n return html`\n
    \n ${props.configSchemaLoading\n ? html`
    Loading config schema…
    `\n : renderChannelConfigForm({\n channelId,\n configValue: props.configForm,\n schema: props.configSchema,\n uiHints: props.configUiHints,\n disabled,\n onPatch: props.onConfigPatch,\n })}\n
    \n props.onConfigSave()}\n >\n ${props.configSaving ? \"Saving…\" : \"Save\"}\n \n props.onConfigReload()}\n >\n Reload\n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { DiscordStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderDiscordCard(params: {\n props: ChannelsProps;\n discord?: DiscordStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, discord, accountCountLabel } = params;\n\n return html`\n
    \n
    Discord
    \n
    Bot status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${discord?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${discord?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Last start\n ${discord?.lastStartAt ? formatAgo(discord.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${discord?.lastProbeAt ? formatAgo(discord.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${discord?.lastError\n ? html`
    \n ${discord.lastError}\n
    `\n : nothing}\n\n ${discord?.probe\n ? html`
    \n Probe ${discord.probe.ok ? \"ok\" : \"failed\"} ·\n ${discord.probe.status ?? \"\"} ${discord.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"discord\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { IMessageStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderIMessageCard(params: {\n props: ChannelsProps;\n imessage?: IMessageStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, imessage, accountCountLabel } = params;\n\n return html`\n
    \n
    iMessage
    \n
    macOS bridge status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${imessage?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${imessage?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Last start\n ${imessage?.lastStartAt ? formatAgo(imessage.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${imessage?.lastProbeAt ? formatAgo(imessage.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${imessage?.lastError\n ? html`
    \n ${imessage.lastError}\n
    `\n : nothing}\n\n ${imessage?.probe\n ? html`
    \n Probe ${imessage.probe.ok ? \"ok\" : \"failed\"} ·\n ${imessage.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"imessage\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","/**\n * Nostr Profile Edit Form\n *\n * Provides UI for editing and publishing Nostr profile (kind:0).\n */\n\nimport { html, nothing, type TemplateResult } from \"lit\";\n\nimport type { NostrProfile as NostrProfileType } from \"../types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface NostrProfileFormState {\n /** Current form values */\n values: NostrProfileType;\n /** Original values for dirty detection */\n original: NostrProfileType;\n /** Whether the form is currently submitting */\n saving: boolean;\n /** Whether import is in progress */\n importing: boolean;\n /** Last error message */\n error: string | null;\n /** Last success message */\n success: string | null;\n /** Validation errors per field */\n fieldErrors: Record;\n /** Whether to show advanced fields */\n showAdvanced: boolean;\n}\n\nexport interface NostrProfileFormCallbacks {\n /** Called when a field value changes */\n onFieldChange: (field: keyof NostrProfileType, value: string) => void;\n /** Called when save is clicked */\n onSave: () => void;\n /** Called when import is clicked */\n onImport: () => void;\n /** Called when cancel is clicked */\n onCancel: () => void;\n /** Called when toggle advanced is clicked */\n onToggleAdvanced: () => void;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction isFormDirty(state: NostrProfileFormState): boolean {\n const { values, original } = state;\n return (\n values.name !== original.name ||\n values.displayName !== original.displayName ||\n values.about !== original.about ||\n values.picture !== original.picture ||\n values.banner !== original.banner ||\n values.website !== original.website ||\n values.nip05 !== original.nip05 ||\n values.lud16 !== original.lud16\n );\n}\n\n// ============================================================================\n// Form Rendering\n// ============================================================================\n\nexport function renderNostrProfileForm(params: {\n state: NostrProfileFormState;\n callbacks: NostrProfileFormCallbacks;\n accountId: string;\n}): TemplateResult {\n const { state, callbacks, accountId } = params;\n const isDirty = isFormDirty(state);\n\n const renderField = (\n field: keyof NostrProfileType,\n label: string,\n opts: {\n type?: \"text\" | \"url\" | \"textarea\";\n placeholder?: string;\n maxLength?: number;\n help?: string;\n } = {}\n ) => {\n const { type = \"text\", placeholder, maxLength, help } = opts;\n const value = state.values[field] ?? \"\";\n const error = state.fieldErrors[field];\n\n const inputId = `nostr-profile-${field}`;\n\n if (type === \"textarea\") {\n return html`\n
    \n \n {\n const target = e.target as HTMLTextAreaElement;\n callbacks.onFieldChange(field, target.value);\n }}\n ?disabled=${state.saving}\n >\n ${help ? html`
    ${help}
    ` : nothing}\n ${error ? html`
    ${error}
    ` : nothing}\n
    \n `;\n }\n\n return html`\n
    \n \n {\n const target = e.target as HTMLInputElement;\n callbacks.onFieldChange(field, target.value);\n }}\n ?disabled=${state.saving}\n />\n ${help ? html`
    ${help}
    ` : nothing}\n ${error ? html`
    ${error}
    ` : nothing}\n
    \n `;\n };\n\n const renderPicturePreview = () => {\n const picture = state.values.picture;\n if (!picture) return nothing;\n\n return html`\n
    \n {\n const img = e.target as HTMLImageElement;\n img.style.display = \"none\";\n }}\n @load=${(e: Event) => {\n const img = e.target as HTMLImageElement;\n img.style.display = \"block\";\n }}\n />\n
    \n `;\n };\n\n return html`\n
    \n
    \n
    Edit Profile
    \n
    Account: ${accountId}
    \n
    \n\n ${state.error\n ? html`
    ${state.error}
    `\n : nothing}\n\n ${state.success\n ? html`
    ${state.success}
    `\n : nothing}\n\n ${renderPicturePreview()}\n\n ${renderField(\"name\", \"Username\", {\n placeholder: \"satoshi\",\n maxLength: 256,\n help: \"Short username (e.g., satoshi)\",\n })}\n\n ${renderField(\"displayName\", \"Display Name\", {\n placeholder: \"Satoshi Nakamoto\",\n maxLength: 256,\n help: \"Your full display name\",\n })}\n\n ${renderField(\"about\", \"Bio\", {\n type: \"textarea\",\n placeholder: \"Tell people about yourself...\",\n maxLength: 2000,\n help: \"A brief bio or description\",\n })}\n\n ${renderField(\"picture\", \"Avatar URL\", {\n type: \"url\",\n placeholder: \"https://example.com/avatar.jpg\",\n help: \"HTTPS URL to your profile picture\",\n })}\n\n ${state.showAdvanced\n ? html`\n
    \n
    Advanced
    \n\n ${renderField(\"banner\", \"Banner URL\", {\n type: \"url\",\n placeholder: \"https://example.com/banner.jpg\",\n help: \"HTTPS URL to a banner image\",\n })}\n\n ${renderField(\"website\", \"Website\", {\n type: \"url\",\n placeholder: \"https://example.com\",\n help: \"Your personal website\",\n })}\n\n ${renderField(\"nip05\", \"NIP-05 Identifier\", {\n placeholder: \"you@example.com\",\n help: \"Verifiable identifier (e.g., you@domain.com)\",\n })}\n\n ${renderField(\"lud16\", \"Lightning Address\", {\n placeholder: \"you@getalby.com\",\n help: \"Lightning address for tips (LUD-16)\",\n })}\n
    \n `\n : nothing}\n\n
    \n \n ${state.saving ? \"Saving...\" : \"Save & Publish\"}\n \n\n \n ${state.importing ? \"Importing...\" : \"Import from Relays\"}\n \n\n \n ${state.showAdvanced ? \"Hide Advanced\" : \"Show Advanced\"}\n \n\n \n Cancel\n \n
    \n\n ${isDirty\n ? html`
    \n You have unsaved changes\n
    `\n : nothing}\n
    \n `;\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create initial form state from existing profile\n */\nexport function createNostrProfileFormState(\n profile: NostrProfileType | undefined\n): NostrProfileFormState {\n const values: NostrProfileType = {\n name: profile?.name ?? \"\",\n displayName: profile?.displayName ?? \"\",\n about: profile?.about ?? \"\",\n picture: profile?.picture ?? \"\",\n banner: profile?.banner ?? \"\",\n website: profile?.website ?? \"\",\n nip05: profile?.nip05 ?? \"\",\n lud16: profile?.lud16 ?? \"\",\n };\n\n return {\n values,\n original: { ...values },\n saving: false,\n importing: false,\n error: null,\n success: null,\n fieldErrors: {},\n showAdvanced: Boolean(\n profile?.banner || profile?.website || profile?.nip05 || profile?.lud16\n ),\n };\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { ChannelAccountSnapshot, NostrStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\nimport {\n renderNostrProfileForm,\n type NostrProfileFormState,\n type NostrProfileFormCallbacks,\n} from \"./channels.nostr-profile-form\";\n\n/**\n * Truncate a pubkey for display (shows first and last 8 chars)\n */\nfunction truncatePubkey(pubkey: string | null | undefined): string {\n if (!pubkey) return \"n/a\";\n if (pubkey.length <= 20) return pubkey;\n return `${pubkey.slice(0, 8)}...${pubkey.slice(-8)}`;\n}\n\nexport function renderNostrCard(params: {\n props: ChannelsProps;\n nostr?: NostrStatus | null;\n nostrAccounts: ChannelAccountSnapshot[];\n accountCountLabel: unknown;\n /** Profile form state (optional - if provided, shows form) */\n profileFormState?: NostrProfileFormState | null;\n /** Profile form callbacks */\n profileFormCallbacks?: NostrProfileFormCallbacks | null;\n /** Called when Edit Profile is clicked */\n onEditProfile?: () => void;\n}) {\n const {\n props,\n nostr,\n nostrAccounts,\n accountCountLabel,\n profileFormState,\n profileFormCallbacks,\n onEditProfile,\n } = params;\n const primaryAccount = nostrAccounts[0];\n const summaryConfigured = nostr?.configured ?? primaryAccount?.configured ?? false;\n const summaryRunning = nostr?.running ?? primaryAccount?.running ?? false;\n const summaryPublicKey =\n nostr?.publicKey ??\n (primaryAccount as { publicKey?: string } | undefined)?.publicKey;\n const summaryLastStartAt = nostr?.lastStartAt ?? primaryAccount?.lastStartAt ?? null;\n const summaryLastError = nostr?.lastError ?? primaryAccount?.lastError ?? null;\n const hasMultipleAccounts = nostrAccounts.length > 1;\n const showingForm = profileFormState !== null && profileFormState !== undefined;\n\n const renderAccountCard = (account: ChannelAccountSnapshot) => {\n const publicKey = (account as { publicKey?: string }).publicKey;\n const profile = (account as { profile?: { name?: string; displayName?: string } }).profile;\n const displayName = profile?.displayName ?? profile?.name ?? account.name ?? account.accountId;\n\n return html`\n
    \n
    \n
    ${displayName}
    \n
    ${account.accountId}
    \n
    \n
    \n
    \n Running\n ${account.running ? \"Yes\" : \"No\"}\n
    \n
    \n Configured\n ${account.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Public Key\n ${truncatePubkey(publicKey)}\n
    \n
    \n Last inbound\n ${account.lastInboundAt ? formatAgo(account.lastInboundAt) : \"n/a\"}\n
    \n ${account.lastError\n ? html`\n
    ${account.lastError}
    \n `\n : nothing}\n
    \n
    \n `;\n };\n\n const renderProfileSection = () => {\n // If showing form, render the form instead of the read-only view\n if (showingForm && profileFormCallbacks) {\n return renderNostrProfileForm({\n state: profileFormState,\n callbacks: profileFormCallbacks,\n accountId: nostrAccounts[0]?.accountId ?? \"default\",\n });\n }\n\n const profile =\n (primaryAccount as\n | {\n profile?: {\n name?: string;\n displayName?: string;\n about?: string;\n picture?: string;\n nip05?: string;\n };\n }\n | undefined)?.profile ?? nostr?.profile;\n const { name, displayName, about, picture, nip05 } = profile ?? {};\n const hasAnyProfileData = name || displayName || about || picture || nip05;\n\n return html`\n
    \n
    \n
    Profile
    \n ${summaryConfigured\n ? html`\n \n Edit Profile\n \n `\n : nothing}\n
    \n ${hasAnyProfileData\n ? html`\n
    \n ${picture\n ? html`\n
    \n {\n (e.target as HTMLImageElement).style.display = \"none\";\n }}\n />\n
    \n `\n : nothing}\n ${name ? html`
    Name${name}
    ` : nothing}\n ${displayName\n ? html`
    Display Name${displayName}
    `\n : nothing}\n ${about\n ? html`
    About${about}
    `\n : nothing}\n ${nip05 ? html`
    NIP-05${nip05}
    ` : nothing}\n
    \n `\n : html`\n
    \n No profile set. Click \"Edit Profile\" to add your name, bio, and avatar.\n
    \n `}\n
    \n `;\n };\n\n return html`\n
    \n
    Nostr
    \n
    Decentralized DMs via Nostr relays (NIP-04).
    \n ${accountCountLabel}\n\n ${hasMultipleAccounts\n ? html`\n
    \n ${nostrAccounts.map((account) => renderAccountCard(account))}\n
    \n `\n : html`\n
    \n
    \n Configured\n ${summaryConfigured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${summaryRunning ? \"Yes\" : \"No\"}\n
    \n
    \n Public Key\n ${truncatePubkey(summaryPublicKey)}\n
    \n
    \n Last start\n ${summaryLastStartAt ? formatAgo(summaryLastStartAt) : \"n/a\"}\n
    \n
    \n `}\n\n ${summaryLastError\n ? html`
    ${summaryLastError}
    `\n : nothing}\n\n ${renderProfileSection()}\n\n ${renderChannelConfigSection({ channelId: \"nostr\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { SignalStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderSignalCard(params: {\n props: ChannelsProps;\n signal?: SignalStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, signal, accountCountLabel } = params;\n\n return html`\n
    \n
    Signal
    \n
    signal-cli status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${signal?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${signal?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Base URL\n ${signal?.baseUrl ?? \"n/a\"}\n
    \n
    \n Last start\n ${signal?.lastStartAt ? formatAgo(signal.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${signal?.lastProbeAt ? formatAgo(signal.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${signal?.lastError\n ? html`
    \n ${signal.lastError}\n
    `\n : nothing}\n\n ${signal?.probe\n ? html`
    \n Probe ${signal.probe.ok ? \"ok\" : \"failed\"} ·\n ${signal.probe.status ?? \"\"} ${signal.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"signal\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { SlackStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderSlackCard(params: {\n props: ChannelsProps;\n slack?: SlackStatus | null;\n accountCountLabel: unknown;\n}) {\n const { props, slack, accountCountLabel } = params;\n\n return html`\n
    \n
    Slack
    \n
    Socket mode status and channel configuration.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${slack?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${slack?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Last start\n ${slack?.lastStartAt ? formatAgo(slack.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${slack?.lastProbeAt ? formatAgo(slack.lastProbeAt) : \"n/a\"}\n
    \n
    \n\n ${slack?.lastError\n ? html`
    \n ${slack.lastError}\n
    `\n : nothing}\n\n ${slack?.probe\n ? html`
    \n Probe ${slack.probe.ok ? \"ok\" : \"failed\"} ·\n ${slack.probe.status ?? \"\"} ${slack.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"slack\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { ChannelAccountSnapshot, TelegramStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\n\nexport function renderTelegramCard(params: {\n props: ChannelsProps;\n telegram?: TelegramStatus;\n telegramAccounts: ChannelAccountSnapshot[];\n accountCountLabel: unknown;\n}) {\n const { props, telegram, telegramAccounts, accountCountLabel } = params;\n const hasMultipleAccounts = telegramAccounts.length > 1;\n\n const renderAccountCard = (account: ChannelAccountSnapshot) => {\n const probe = account.probe as { bot?: { username?: string } } | undefined;\n const botUsername = probe?.bot?.username;\n const label = account.name || account.accountId;\n return html`\n
    \n
    \n
    \n ${botUsername ? `@${botUsername}` : label}\n
    \n
    ${account.accountId}
    \n
    \n
    \n
    \n Running\n ${account.running ? \"Yes\" : \"No\"}\n
    \n
    \n Configured\n ${account.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Last inbound\n ${account.lastInboundAt ? formatAgo(account.lastInboundAt) : \"n/a\"}\n
    \n ${account.lastError\n ? html`\n
    \n ${account.lastError}\n
    \n `\n : nothing}\n
    \n
    \n `;\n };\n\n return html`\n
    \n
    Telegram
    \n
    Bot status and channel configuration.
    \n ${accountCountLabel}\n\n ${hasMultipleAccounts\n ? html`\n
    \n ${telegramAccounts.map((account) => renderAccountCard(account))}\n
    \n `\n : html`\n
    \n
    \n Configured\n ${telegram?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${telegram?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Mode\n ${telegram?.mode ?? \"n/a\"}\n
    \n
    \n Last start\n ${telegram?.lastStartAt ? formatAgo(telegram.lastStartAt) : \"n/a\"}\n
    \n
    \n Last probe\n ${telegram?.lastProbeAt ? formatAgo(telegram.lastProbeAt) : \"n/a\"}\n
    \n
    \n `}\n\n ${telegram?.lastError\n ? html`
    \n ${telegram.lastError}\n
    `\n : nothing}\n\n ${telegram?.probe\n ? html`
    \n Probe ${telegram.probe.ok ? \"ok\" : \"failed\"} ·\n ${telegram.probe.status ?? \"\"} ${telegram.probe.error ?? \"\"}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: \"telegram\", props })}\n\n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type { WhatsAppStatus } from \"../types\";\nimport type { ChannelsProps } from \"./channels.types\";\nimport { renderChannelConfigSection } from \"./channels.config\";\nimport { formatDuration } from \"./channels.shared\";\n\nexport function renderWhatsAppCard(params: {\n props: ChannelsProps;\n whatsapp?: WhatsAppStatus;\n accountCountLabel: unknown;\n}) {\n const { props, whatsapp, accountCountLabel } = params;\n\n return html`\n
    \n
    WhatsApp
    \n
    Link WhatsApp Web and monitor connection health.
    \n ${accountCountLabel}\n\n
    \n
    \n Configured\n ${whatsapp?.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Linked\n ${whatsapp?.linked ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${whatsapp?.running ? \"Yes\" : \"No\"}\n
    \n
    \n Connected\n ${whatsapp?.connected ? \"Yes\" : \"No\"}\n
    \n
    \n Last connect\n \n ${whatsapp?.lastConnectedAt\n ? formatAgo(whatsapp.lastConnectedAt)\n : \"n/a\"}\n \n
    \n
    \n Last message\n \n ${whatsapp?.lastMessageAt ? formatAgo(whatsapp.lastMessageAt) : \"n/a\"}\n \n
    \n
    \n Auth age\n \n ${whatsapp?.authAgeMs != null\n ? formatDuration(whatsapp.authAgeMs)\n : \"n/a\"}\n \n
    \n
    \n\n ${whatsapp?.lastError\n ? html`
    \n ${whatsapp.lastError}\n
    `\n : nothing}\n\n ${props.whatsappMessage\n ? html`
    \n ${props.whatsappMessage}\n
    `\n : nothing}\n\n ${props.whatsappQrDataUrl\n ? html`
    \n \"WhatsApp\n
    `\n : nothing}\n\n
    \n props.onWhatsAppStart(false)}\n >\n ${props.whatsappBusy ? \"Working…\" : \"Show QR\"}\n \n props.onWhatsAppStart(true)}\n >\n Relink\n \n props.onWhatsAppWait()}\n >\n Wait for scan\n \n props.onWhatsAppLogout()}\n >\n Logout\n \n \n
    \n\n ${renderChannelConfigSection({ channelId: \"whatsapp\", props })}\n
    \n `;\n}\n\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport type {\n ChannelAccountSnapshot,\n ChannelUiMetaEntry,\n ChannelsStatusSnapshot,\n DiscordStatus,\n IMessageStatus,\n NostrProfile,\n NostrStatus,\n SignalStatus,\n SlackStatus,\n TelegramStatus,\n WhatsAppStatus,\n} from \"../types\";\nimport type {\n ChannelKey,\n ChannelsChannelData,\n ChannelsProps,\n} from \"./channels.types\";\nimport { channelEnabled, renderChannelAccountCount } from \"./channels.shared\";\nimport { renderChannelConfigSection } from \"./channels.config\";\nimport { renderDiscordCard } from \"./channels.discord\";\nimport { renderIMessageCard } from \"./channels.imessage\";\nimport { renderNostrCard } from \"./channels.nostr\";\nimport { renderSignalCard } from \"./channels.signal\";\nimport { renderSlackCard } from \"./channels.slack\";\nimport { renderTelegramCard } from \"./channels.telegram\";\nimport { renderWhatsAppCard } from \"./channels.whatsapp\";\n\nexport function renderChannels(props: ChannelsProps) {\n const channels = props.snapshot?.channels as Record | null;\n const whatsapp = (channels?.whatsapp ?? undefined) as\n | WhatsAppStatus\n | undefined;\n const telegram = (channels?.telegram ?? undefined) as\n | TelegramStatus\n | undefined;\n const discord = (channels?.discord ?? null) as DiscordStatus | null;\n const slack = (channels?.slack ?? null) as SlackStatus | null;\n const signal = (channels?.signal ?? null) as SignalStatus | null;\n const imessage = (channels?.imessage ?? null) as IMessageStatus | null;\n const nostr = (channels?.nostr ?? null) as NostrStatus | null;\n const channelOrder = resolveChannelOrder(props.snapshot);\n const orderedChannels = channelOrder\n .map((key, index) => ({\n key,\n enabled: channelEnabled(key, props),\n order: index,\n }))\n .sort((a, b) => {\n if (a.enabled !== b.enabled) return a.enabled ? -1 : 1;\n return a.order - b.order;\n });\n\n return html`\n
    \n ${orderedChannels.map((channel) =>\n renderChannel(channel.key, props, {\n whatsapp,\n telegram,\n discord,\n slack,\n signal,\n imessage,\n nostr,\n channelAccounts: props.snapshot?.channelAccounts ?? null,\n }),\n )}\n
    \n\n
    \n
    \n
    \n
    Channel health
    \n
    Channel status snapshots from the gateway.
    \n
    \n
    ${props.lastSuccessAt ? formatAgo(props.lastSuccessAt) : \"n/a\"}
    \n
    \n ${props.lastError\n ? html`
    \n ${props.lastError}\n
    `\n : nothing}\n
    \n${props.snapshot ? JSON.stringify(props.snapshot, null, 2) : \"No snapshot yet.\"}\n      
    \n
    \n `;\n}\n\nfunction resolveChannelOrder(snapshot: ChannelsStatusSnapshot | null): ChannelKey[] {\n if (snapshot?.channelMeta?.length) {\n return snapshot.channelMeta.map((entry) => entry.id) as ChannelKey[];\n }\n if (snapshot?.channelOrder?.length) {\n return snapshot.channelOrder;\n }\n return [\"whatsapp\", \"telegram\", \"discord\", \"slack\", \"signal\", \"imessage\", \"nostr\"];\n}\n\nfunction renderChannel(\n key: ChannelKey,\n props: ChannelsProps,\n data: ChannelsChannelData,\n) {\n const accountCountLabel = renderChannelAccountCount(\n key,\n data.channelAccounts,\n );\n switch (key) {\n case \"whatsapp\":\n return renderWhatsAppCard({\n props,\n whatsapp: data.whatsapp,\n accountCountLabel,\n });\n case \"telegram\":\n return renderTelegramCard({\n props,\n telegram: data.telegram,\n telegramAccounts: data.channelAccounts?.telegram ?? [],\n accountCountLabel,\n });\n case \"discord\":\n return renderDiscordCard({\n props,\n discord: data.discord,\n accountCountLabel,\n });\n case \"slack\":\n return renderSlackCard({\n props,\n slack: data.slack,\n accountCountLabel,\n });\n case \"signal\":\n return renderSignalCard({\n props,\n signal: data.signal,\n accountCountLabel,\n });\n case \"imessage\":\n return renderIMessageCard({\n props,\n imessage: data.imessage,\n accountCountLabel,\n });\n case \"nostr\": {\n const nostrAccounts = data.channelAccounts?.nostr ?? [];\n const primaryAccount = nostrAccounts[0];\n const accountId = primaryAccount?.accountId ?? \"default\";\n const profile =\n (primaryAccount as { profile?: NostrProfile | null } | undefined)?.profile ?? null;\n const showForm =\n props.nostrProfileAccountId === accountId ? props.nostrProfileFormState : null;\n const profileFormCallbacks = showForm\n ? {\n onFieldChange: props.onNostrProfileFieldChange,\n onSave: props.onNostrProfileSave,\n onImport: props.onNostrProfileImport,\n onCancel: props.onNostrProfileCancel,\n onToggleAdvanced: props.onNostrProfileToggleAdvanced,\n }\n : null;\n return renderNostrCard({\n props,\n nostr: data.nostr,\n nostrAccounts,\n accountCountLabel,\n profileFormState: showForm,\n profileFormCallbacks,\n onEditProfile: () => props.onNostrProfileEdit(accountId, profile),\n });\n }\n default:\n return renderGenericChannelCard(key, props, data.channelAccounts ?? {});\n }\n}\n\nfunction renderGenericChannelCard(\n key: ChannelKey,\n props: ChannelsProps,\n channelAccounts: Record,\n) {\n const label = resolveChannelLabel(props.snapshot, key);\n const status = props.snapshot?.channels?.[key] as Record | undefined;\n const configured = typeof status?.configured === \"boolean\" ? status.configured : undefined;\n const running = typeof status?.running === \"boolean\" ? status.running : undefined;\n const connected = typeof status?.connected === \"boolean\" ? status.connected : undefined;\n const lastError = typeof status?.lastError === \"string\" ? status.lastError : undefined;\n const accounts = channelAccounts[key] ?? [];\n const accountCountLabel = renderChannelAccountCount(key, channelAccounts);\n\n return html`\n
    \n
    ${label}
    \n
    Channel status and configuration.
    \n ${accountCountLabel}\n\n ${accounts.length > 0\n ? html`\n
    \n ${accounts.map((account) => renderGenericAccount(account))}\n
    \n `\n : html`\n
    \n
    \n Configured\n ${configured == null ? \"n/a\" : configured ? \"Yes\" : \"No\"}\n
    \n
    \n Running\n ${running == null ? \"n/a\" : running ? \"Yes\" : \"No\"}\n
    \n
    \n Connected\n ${connected == null ? \"n/a\" : connected ? \"Yes\" : \"No\"}\n
    \n
    \n `}\n\n ${lastError\n ? html`
    \n ${lastError}\n
    `\n : nothing}\n\n ${renderChannelConfigSection({ channelId: key, props })}\n
    \n `;\n}\n\nfunction resolveChannelMetaMap(\n snapshot: ChannelsStatusSnapshot | null,\n): Record {\n if (!snapshot?.channelMeta?.length) return {};\n return Object.fromEntries(snapshot.channelMeta.map((entry) => [entry.id, entry]));\n}\n\nfunction resolveChannelLabel(\n snapshot: ChannelsStatusSnapshot | null,\n key: string,\n): string {\n const meta = resolveChannelMetaMap(snapshot)[key];\n return meta?.label ?? snapshot?.channelLabels?.[key] ?? key;\n}\n\nconst RECENT_ACTIVITY_THRESHOLD_MS = 10 * 60 * 1000; // 10 minutes\n\nfunction hasRecentActivity(account: ChannelAccountSnapshot): boolean {\n if (!account.lastInboundAt) return false;\n return Date.now() - account.lastInboundAt < RECENT_ACTIVITY_THRESHOLD_MS;\n}\n\nfunction deriveRunningStatus(account: ChannelAccountSnapshot): \"Yes\" | \"No\" | \"Active\" {\n if (account.running) return \"Yes\";\n // If we have recent inbound activity, the channel is effectively running\n if (hasRecentActivity(account)) return \"Active\";\n return \"No\";\n}\n\nfunction deriveConnectedStatus(account: ChannelAccountSnapshot): \"Yes\" | \"No\" | \"Active\" | \"n/a\" {\n if (account.connected === true) return \"Yes\";\n if (account.connected === false) return \"No\";\n // If connected is null/undefined but we have recent activity, show as active\n if (hasRecentActivity(account)) return \"Active\";\n return \"n/a\";\n}\n\nfunction renderGenericAccount(account: ChannelAccountSnapshot) {\n const runningStatus = deriveRunningStatus(account);\n const connectedStatus = deriveConnectedStatus(account);\n\n return html`\n
    \n
    \n
    ${account.name || account.accountId}
    \n
    ${account.accountId}
    \n
    \n
    \n
    \n Running\n ${runningStatus}\n
    \n
    \n Configured\n ${account.configured ? \"Yes\" : \"No\"}\n
    \n
    \n Connected\n ${connectedStatus}\n
    \n
    \n Last inbound\n ${account.lastInboundAt ? formatAgo(account.lastInboundAt) : \"n/a\"}\n
    \n ${account.lastError\n ? html`\n
    \n ${account.lastError}\n
    \n `\n : nothing}\n
    \n
    \n `;\n}\n","import { formatAgo, formatDurationMs, formatMs } from \"./format\";\nimport type { CronJob, GatewaySessionRow, PresenceEntry } from \"./types\";\n\nexport function formatPresenceSummary(entry: PresenceEntry): string {\n const host = entry.host ?? \"unknown\";\n const ip = entry.ip ? `(${entry.ip})` : \"\";\n const mode = entry.mode ?? \"\";\n const version = entry.version ?? \"\";\n return `${host} ${ip} ${mode} ${version}`.trim();\n}\n\nexport function formatPresenceAge(entry: PresenceEntry): string {\n const ts = entry.ts ?? null;\n return ts ? formatAgo(ts) : \"n/a\";\n}\n\nexport function formatNextRun(ms?: number | null) {\n if (!ms) return \"n/a\";\n return `${formatMs(ms)} (${formatAgo(ms)})`;\n}\n\nexport function formatSessionTokens(row: GatewaySessionRow) {\n if (row.totalTokens == null) return \"n/a\";\n const total = row.totalTokens ?? 0;\n const ctx = row.contextTokens ?? 0;\n return ctx ? `${total} / ${ctx}` : String(total);\n}\n\nexport function formatEventPayload(payload: unknown): string {\n if (payload == null) return \"\";\n try {\n return JSON.stringify(payload, null, 2);\n } catch {\n return String(payload);\n }\n}\n\nexport function formatCronState(job: CronJob) {\n const state = job.state ?? {};\n const next = state.nextRunAtMs ? formatMs(state.nextRunAtMs) : \"n/a\";\n const last = state.lastRunAtMs ? formatMs(state.lastRunAtMs) : \"n/a\";\n const status = state.lastStatus ?? \"n/a\";\n return `${status} · next ${next} · last ${last}`;\n}\n\nexport function formatCronSchedule(job: CronJob) {\n const s = job.schedule;\n if (s.kind === \"at\") return `At ${formatMs(s.atMs)}`;\n if (s.kind === \"every\") return `Every ${formatDurationMs(s.everyMs)}`;\n return `Cron ${s.expr}${s.tz ? ` (${s.tz})` : \"\"}`;\n}\n\nexport function formatCronPayload(job: CronJob) {\n const p = job.payload;\n if (p.kind === \"systemEvent\") return `System: ${p.text}`;\n return `Agent: ${p.message}`;\n}\n\n","import { html, nothing } from \"lit\";\n\nimport { formatMs } from \"../format\";\nimport {\n formatCronPayload,\n formatCronSchedule,\n formatCronState,\n formatNextRun,\n} from \"../presenter\";\nimport type { ChannelUiMetaEntry, CronJob, CronRunLogEntry, CronStatus } from \"../types\";\nimport type { CronFormState } from \"../ui-types\";\n\nexport type CronProps = {\n loading: boolean;\n status: CronStatus | null;\n jobs: CronJob[];\n error: string | null;\n busy: boolean;\n form: CronFormState;\n channels: string[];\n channelLabels?: Record;\n channelMeta?: ChannelUiMetaEntry[];\n runsJobId: string | null;\n runs: CronRunLogEntry[];\n onFormChange: (patch: Partial) => void;\n onRefresh: () => void;\n onAdd: () => void;\n onToggle: (job: CronJob, enabled: boolean) => void;\n onRun: (job: CronJob) => void;\n onRemove: (job: CronJob) => void;\n onLoadRuns: (jobId: string) => void;\n};\n\nfunction buildChannelOptions(props: CronProps): string[] {\n const options = [\"last\", ...props.channels.filter(Boolean)];\n const current = props.form.channel?.trim();\n if (current && !options.includes(current)) {\n options.push(current);\n }\n const seen = new Set();\n return options.filter((value) => {\n if (seen.has(value)) return false;\n seen.add(value);\n return true;\n });\n}\n\nfunction resolveChannelLabel(props: CronProps, channel: string): string {\n if (channel === \"last\") return \"last\";\n const meta = props.channelMeta?.find((entry) => entry.id === channel);\n if (meta?.label) return meta.label;\n return props.channelLabels?.[channel] ?? channel;\n}\n\nexport function renderCron(props: CronProps) {\n const channelOptions = buildChannelOptions(props);\n return html`\n
    \n
    \n
    Scheduler
    \n
    Gateway-owned cron scheduler status.
    \n
    \n
    \n
    Enabled
    \n
    \n ${props.status\n ? props.status.enabled\n ? \"Yes\"\n : \"No\"\n : \"n/a\"}\n
    \n
    \n
    \n
    Jobs
    \n
    ${props.status?.jobs ?? \"n/a\"}
    \n
    \n
    \n
    Next wake
    \n
    ${formatNextRun(props.status?.nextWakeAtMs ?? null)}
    \n
    \n
    \n
    \n \n ${props.error ? html`${props.error}` : nothing}\n
    \n
    \n\n
    \n
    New Job
    \n
    Create a scheduled wakeup or agent run.
    \n
    \n \n \n \n \n \n
    \n ${renderScheduleFields(props)}\n
    \n \n \n \n
    \n \n\t ${props.form.payloadKind === \"agentTurn\"\n\t ? html`\n\t
    \n \n\t \n \n \n ${props.form.sessionTarget === \"isolated\"\n ? html`\n \n `\n : nothing}\n
    \n `\n : nothing}\n
    \n \n
    \n
    \n
    \n\n
    \n
    Jobs
    \n
    All scheduled jobs stored in the gateway.
    \n ${props.jobs.length === 0\n ? html`
    No jobs yet.
    `\n : html`\n
    \n ${props.jobs.map((job) => renderJob(job, props))}\n
    \n `}\n
    \n\n
    \n
    Run history
    \n
    Latest runs for ${props.runsJobId ?? \"(select a job)\"}.
    \n ${props.runsJobId == null\n ? html`\n
    \n Select a job to inspect run history.\n
    \n `\n : props.runs.length === 0\n ? html`
    No runs yet.
    `\n : html`\n
    \n ${props.runs.map((entry) => renderRun(entry))}\n
    \n `}\n
    \n `;\n}\n\nfunction renderScheduleFields(props: CronProps) {\n const form = props.form;\n if (form.scheduleKind === \"at\") {\n return html`\n \n `;\n }\n if (form.scheduleKind === \"every\") {\n return html`\n
    \n \n \n
    \n `;\n }\n return html`\n
    \n \n \n
    \n `;\n}\n\nfunction renderJob(job: CronJob, props: CronProps) {\n const isSelected = props.runsJobId === job.id;\n const itemClass = `list-item list-item-clickable${isSelected ? \" list-item-selected\" : \"\"}`;\n return html`\n
    props.onLoadRuns(job.id)}>\n
    \n
    ${job.name}
    \n
    ${formatCronSchedule(job)}
    \n
    ${formatCronPayload(job)}
    \n ${job.agentId ? html`
    Agent: ${job.agentId}
    ` : nothing}\n
    \n ${job.enabled ? \"enabled\" : \"disabled\"}\n ${job.sessionTarget}\n ${job.wakeMode}\n
    \n
    \n
    \n
    ${formatCronState(job)}
    \n
    \n {\n event.stopPropagation();\n props.onToggle(job, !job.enabled);\n }}\n >\n ${job.enabled ? \"Disable\" : \"Enable\"}\n \n {\n event.stopPropagation();\n props.onRun(job);\n }}\n >\n Run\n \n {\n event.stopPropagation();\n props.onLoadRuns(job.id);\n }}\n >\n Runs\n \n {\n event.stopPropagation();\n props.onRemove(job);\n }}\n >\n Remove\n \n
    \n
    \n
    \n `;\n}\n\nfunction renderRun(entry: CronRunLogEntry) {\n return html`\n
    \n
    \n
    ${entry.status}
    \n
    ${entry.summary ?? \"\"}
    \n
    \n
    \n
    ${formatMs(entry.ts)}
    \n
    ${entry.durationMs ?? 0}ms
    \n ${entry.error ? html`
    ${entry.error}
    ` : nothing}\n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatEventPayload } from \"../presenter\";\nimport type { EventLogEntry } from \"../app-events\";\n\nexport type DebugProps = {\n loading: boolean;\n status: Record | null;\n health: Record | null;\n models: unknown[];\n heartbeat: unknown;\n eventLog: EventLogEntry[];\n callMethod: string;\n callParams: string;\n callResult: string | null;\n callError: string | null;\n onCallMethodChange: (next: string) => void;\n onCallParamsChange: (next: string) => void;\n onRefresh: () => void;\n onCall: () => void;\n};\n\nexport function renderDebug(props: DebugProps) {\n return html`\n
    \n
    \n
    \n
    \n
    Snapshots
    \n
    Status, health, and heartbeat data.
    \n
    \n \n
    \n
    \n
    \n
    Status
    \n
    ${JSON.stringify(props.status ?? {}, null, 2)}
    \n
    \n
    \n
    Health
    \n
    ${JSON.stringify(props.health ?? {}, null, 2)}
    \n
    \n
    \n
    Last heartbeat
    \n
    ${JSON.stringify(props.heartbeat ?? {}, null, 2)}
    \n
    \n
    \n
    \n\n
    \n
    Manual RPC
    \n
    Send a raw gateway method with JSON params.
    \n
    \n \n \n
    \n
    \n \n
    \n ${props.callError\n ? html`
    \n ${props.callError}\n
    `\n : nothing}\n ${props.callResult\n ? html`
    ${props.callResult}
    `\n : nothing}\n
    \n
    \n\n
    \n
    Models
    \n
    Catalog from models.list.
    \n
    ${JSON.stringify(\n        props.models ?? [],\n        null,\n        2,\n      )}
    \n
    \n\n
    \n
    Event Log
    \n
    Latest gateway events.
    \n ${props.eventLog.length === 0\n ? html`
    No events yet.
    `\n : html`\n
    \n ${props.eventLog.map(\n (evt) => html`\n
    \n
    \n
    ${evt.event}
    \n
    ${new Date(evt.ts).toLocaleTimeString()}
    \n
    \n
    \n
    ${formatEventPayload(evt.payload)}
    \n
    \n
    \n `,\n )}\n
    \n `}\n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatPresenceAge, formatPresenceSummary } from \"../presenter\";\nimport type { PresenceEntry } from \"../types\";\n\nexport type InstancesProps = {\n loading: boolean;\n entries: PresenceEntry[];\n lastError: string | null;\n statusMessage: string | null;\n onRefresh: () => void;\n};\n\nexport function renderInstances(props: InstancesProps) {\n return html`\n
    \n
    \n
    \n
    Connected Instances
    \n
    Presence beacons from the gateway and clients.
    \n
    \n \n
    \n ${props.lastError\n ? html`
    \n ${props.lastError}\n
    `\n : nothing}\n ${props.statusMessage\n ? html`
    \n ${props.statusMessage}\n
    `\n : nothing}\n
    \n ${props.entries.length === 0\n ? html`
    No instances reported yet.
    `\n : props.entries.map((entry) => renderEntry(entry))}\n
    \n
    \n `;\n}\n\nfunction renderEntry(entry: PresenceEntry) {\n const lastInput =\n entry.lastInputSeconds != null\n ? `${entry.lastInputSeconds}s ago`\n : \"n/a\";\n const mode = entry.mode ?? \"unknown\";\n const roles = Array.isArray(entry.roles) ? entry.roles.filter(Boolean) : [];\n const scopes = Array.isArray(entry.scopes) ? entry.scopes.filter(Boolean) : [];\n const scopesLabel =\n scopes.length > 0\n ? scopes.length > 3\n ? `${scopes.length} scopes`\n : `scopes: ${scopes.join(\", \")}`\n : null;\n return html`\n
    \n
    \n
    ${entry.host ?? \"unknown host\"}
    \n
    ${formatPresenceSummary(entry)}
    \n
    \n ${mode}\n ${roles.map((role) => html`${role}`)}\n ${scopesLabel ? html`${scopesLabel}` : nothing}\n ${entry.platform ? html`${entry.platform}` : nothing}\n ${entry.deviceFamily\n ? html`${entry.deviceFamily}`\n : nothing}\n ${entry.modelIdentifier\n ? html`${entry.modelIdentifier}`\n : nothing}\n ${entry.version ? html`${entry.version}` : nothing}\n
    \n
    \n
    \n
    ${formatPresenceAge(entry)}
    \n
    Last input ${lastInput}
    \n
    Reason ${entry.reason ?? \"\"}
    \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { LogEntry, LogLevel } from \"../types\";\n\nconst LEVELS: LogLevel[] = [\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"];\n\nexport type LogsProps = {\n loading: boolean;\n error: string | null;\n file: string | null;\n entries: LogEntry[];\n filterText: string;\n levelFilters: Record;\n autoFollow: boolean;\n truncated: boolean;\n onFilterTextChange: (next: string) => void;\n onLevelToggle: (level: LogLevel, enabled: boolean) => void;\n onToggleAutoFollow: (next: boolean) => void;\n onRefresh: () => void;\n onExport: (lines: string[], label: string) => void;\n onScroll: (event: Event) => void;\n};\n\nfunction formatTime(value?: string | null) {\n if (!value) return \"\";\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return value;\n return date.toLocaleTimeString();\n}\n\nfunction matchesFilter(entry: LogEntry, needle: string) {\n if (!needle) return true;\n const haystack = [entry.message, entry.subsystem, entry.raw]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n return haystack.includes(needle);\n}\n\nexport function renderLogs(props: LogsProps) {\n const needle = props.filterText.trim().toLowerCase();\n const levelFiltered = LEVELS.some((level) => !props.levelFilters[level]);\n const filtered = props.entries.filter((entry) => {\n if (entry.level && !props.levelFilters[entry.level]) return false;\n return matchesFilter(entry, needle);\n });\n const exportLabel = needle || levelFiltered ? \"filtered\" : \"visible\";\n\n return html`\n
    \n
    \n
    \n
    Logs
    \n
    Gateway file logs (JSONL).
    \n
    \n
    \n \n props.onExport(filtered.map((entry) => entry.raw), exportLabel)}\n >\n Export ${exportLabel}\n \n
    \n
    \n\n
    \n \n \n
    \n\n
    \n ${LEVELS.map(\n (level) => html`\n \n `,\n )}\n
    \n\n ${props.file\n ? html`
    File: ${props.file}
    `\n : nothing}\n ${props.truncated\n ? html`
    \n Log output truncated; showing latest chunk.\n
    `\n : nothing}\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n
    \n ${filtered.length === 0\n ? html`
    No log entries.
    `\n : filtered.map(\n (entry) => html`\n
    \n
    ${formatTime(entry.time)}
    \n
    ${entry.level ?? \"\"}
    \n
    ${entry.subsystem ?? \"\"}
    \n
    ${entry.message ?? entry.raw}
    \n
    \n `,\n )}\n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { clampText, formatAgo, formatList } from \"../format\";\nimport type {\n ExecApprovalsAllowlistEntry,\n ExecApprovalsFile,\n ExecApprovalsSnapshot,\n} from \"../controllers/exec-approvals\";\nimport type {\n DevicePairingList,\n DeviceTokenSummary,\n PairedDevice,\n PendingDevice,\n} from \"../controllers/devices\";\n\nexport type NodesProps = {\n loading: boolean;\n nodes: Array>;\n devicesLoading: boolean;\n devicesError: string | null;\n devicesList: DevicePairingList | null;\n configForm: Record | null;\n configLoading: boolean;\n configSaving: boolean;\n configDirty: boolean;\n configFormMode: \"form\" | \"raw\";\n execApprovalsLoading: boolean;\n execApprovalsSaving: boolean;\n execApprovalsDirty: boolean;\n execApprovalsSnapshot: ExecApprovalsSnapshot | null;\n execApprovalsForm: ExecApprovalsFile | null;\n execApprovalsSelectedAgent: string | null;\n execApprovalsTarget: \"gateway\" | \"node\";\n execApprovalsTargetNodeId: string | null;\n onRefresh: () => void;\n onDevicesRefresh: () => void;\n onDeviceApprove: (requestId: string) => void;\n onDeviceReject: (requestId: string) => void;\n onDeviceRotate: (deviceId: string, role: string, scopes?: string[]) => void;\n onDeviceRevoke: (deviceId: string, role: string) => void;\n onLoadConfig: () => void;\n onLoadExecApprovals: () => void;\n onBindDefault: (nodeId: string | null) => void;\n onBindAgent: (agentIndex: number, nodeId: string | null) => void;\n onSaveBindings: () => void;\n onExecApprovalsTargetChange: (kind: \"gateway\" | \"node\", nodeId: string | null) => void;\n onExecApprovalsSelectAgent: (agentId: string) => void;\n onExecApprovalsPatch: (path: Array, value: unknown) => void;\n onExecApprovalsRemove: (path: Array) => void;\n onSaveExecApprovals: () => void;\n};\n\nexport function renderNodes(props: NodesProps) {\n const bindingState = resolveBindingsState(props);\n const approvalsState = resolveExecApprovalsState(props);\n return html`\n ${renderExecApprovals(approvalsState)}\n ${renderBindings(bindingState)}\n ${renderDevices(props)}\n
    \n
    \n
    \n
    Nodes
    \n
    Paired devices and live links.
    \n
    \n \n
    \n
    \n ${props.nodes.length === 0\n ? html`
    No nodes found.
    `\n : props.nodes.map((n) => renderNode(n))}\n
    \n
    \n `;\n}\n\nfunction renderDevices(props: NodesProps) {\n const list = props.devicesList ?? { pending: [], paired: [] };\n const pending = Array.isArray(list.pending) ? list.pending : [];\n const paired = Array.isArray(list.paired) ? list.paired : [];\n return html`\n
    \n
    \n
    \n
    Devices
    \n
    Pairing requests + role tokens.
    \n
    \n \n
    \n ${props.devicesError\n ? html`
    ${props.devicesError}
    `\n : nothing}\n
    \n ${pending.length > 0\n ? html`\n
    Pending
    \n ${pending.map((req) => renderPendingDevice(req, props))}\n `\n : nothing}\n ${paired.length > 0\n ? html`\n
    Paired
    \n ${paired.map((device) => renderPairedDevice(device, props))}\n `\n : nothing}\n ${pending.length === 0 && paired.length === 0\n ? html`
    No paired devices.
    `\n : nothing}\n
    \n
    \n `;\n}\n\nfunction renderPendingDevice(req: PendingDevice, props: NodesProps) {\n const name = req.displayName?.trim() || req.deviceId;\n const age = typeof req.ts === \"number\" ? formatAgo(req.ts) : \"n/a\";\n const role = req.role?.trim() ? `role: ${req.role}` : \"role: -\";\n const repair = req.isRepair ? \" · repair\" : \"\";\n const ip = req.remoteIp ? ` · ${req.remoteIp}` : \"\";\n return html`\n
    \n
    \n
    ${name}
    \n
    ${req.deviceId}${ip}
    \n
    \n ${role} · requested ${age}${repair}\n
    \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n `;\n}\n\nfunction renderPairedDevice(device: PairedDevice, props: NodesProps) {\n const name = device.displayName?.trim() || device.deviceId;\n const ip = device.remoteIp ? ` · ${device.remoteIp}` : \"\";\n const roles = `roles: ${formatList(device.roles)}`;\n const scopes = `scopes: ${formatList(device.scopes)}`;\n const tokens = Array.isArray(device.tokens) ? device.tokens : [];\n return html`\n
    \n
    \n
    ${name}
    \n
    ${device.deviceId}${ip}
    \n
    ${roles} · ${scopes}
    \n ${tokens.length === 0\n ? html`
    Tokens: none
    `\n : html`\n
    Tokens
    \n
    \n ${tokens.map((token) => renderTokenRow(device.deviceId, token, props))}\n
    \n `}\n
    \n
    \n `;\n}\n\nfunction renderTokenRow(deviceId: string, token: DeviceTokenSummary, props: NodesProps) {\n const status = token.revokedAtMs ? \"revoked\" : \"active\";\n const scopes = `scopes: ${formatList(token.scopes)}`;\n const when = formatAgo(token.rotatedAtMs ?? token.createdAtMs ?? token.lastUsedAtMs ?? null);\n return html`\n
    \n
    ${token.role} · ${status} · ${scopes} · ${when}
    \n
    \n props.onDeviceRotate(deviceId, token.role, token.scopes)}\n >\n Rotate\n \n ${token.revokedAtMs\n ? nothing\n : html`\n props.onDeviceRevoke(deviceId, token.role)}\n >\n Revoke\n \n `}\n
    \n
    \n `;\n}\n\ntype BindingAgent = {\n id: string;\n name?: string;\n index: number;\n isDefault: boolean;\n binding?: string | null;\n};\n\ntype BindingNode = {\n id: string;\n label: string;\n};\n\ntype BindingState = {\n ready: boolean;\n disabled: boolean;\n configDirty: boolean;\n configLoading: boolean;\n configSaving: boolean;\n defaultBinding?: string | null;\n agents: BindingAgent[];\n nodes: BindingNode[];\n onBindDefault: (nodeId: string | null) => void;\n onBindAgent: (agentIndex: number, nodeId: string | null) => void;\n onSave: () => void;\n onLoadConfig: () => void;\n formMode: \"form\" | \"raw\";\n};\n\ntype ExecSecurity = \"deny\" | \"allowlist\" | \"full\";\ntype ExecAsk = \"off\" | \"on-miss\" | \"always\";\n\ntype ExecApprovalsResolvedDefaults = {\n security: ExecSecurity;\n ask: ExecAsk;\n askFallback: ExecSecurity;\n autoAllowSkills: boolean;\n};\n\ntype ExecApprovalsAgentOption = {\n id: string;\n name?: string;\n isDefault?: boolean;\n};\n\ntype ExecApprovalsTargetNode = {\n id: string;\n label: string;\n};\n\ntype ExecApprovalsState = {\n ready: boolean;\n disabled: boolean;\n dirty: boolean;\n loading: boolean;\n saving: boolean;\n form: ExecApprovalsFile | null;\n defaults: ExecApprovalsResolvedDefaults;\n selectedScope: string;\n selectedAgent: Record | null;\n agents: ExecApprovalsAgentOption[];\n allowlist: ExecApprovalsAllowlistEntry[];\n target: \"gateway\" | \"node\";\n targetNodeId: string | null;\n targetNodes: ExecApprovalsTargetNode[];\n onSelectScope: (agentId: string) => void;\n onSelectTarget: (kind: \"gateway\" | \"node\", nodeId: string | null) => void;\n onPatch: (path: Array, value: unknown) => void;\n onRemove: (path: Array) => void;\n onLoad: () => void;\n onSave: () => void;\n};\n\nconst EXEC_APPROVALS_DEFAULT_SCOPE = \"__defaults__\";\n\nconst SECURITY_OPTIONS: Array<{ value: ExecSecurity; label: string }> = [\n { value: \"deny\", label: \"Deny\" },\n { value: \"allowlist\", label: \"Allowlist\" },\n { value: \"full\", label: \"Full\" },\n];\n\nconst ASK_OPTIONS: Array<{ value: ExecAsk; label: string }> = [\n { value: \"off\", label: \"Off\" },\n { value: \"on-miss\", label: \"On miss\" },\n { value: \"always\", label: \"Always\" },\n];\n\nfunction resolveBindingsState(props: NodesProps): BindingState {\n const config = props.configForm;\n const nodes = resolveExecNodes(props.nodes);\n const { defaultBinding, agents } = resolveAgentBindings(config);\n const ready = Boolean(config);\n const disabled = props.configSaving || props.configFormMode === \"raw\";\n return {\n ready,\n disabled,\n configDirty: props.configDirty,\n configLoading: props.configLoading,\n configSaving: props.configSaving,\n defaultBinding,\n agents,\n nodes,\n onBindDefault: props.onBindDefault,\n onBindAgent: props.onBindAgent,\n onSave: props.onSaveBindings,\n onLoadConfig: props.onLoadConfig,\n formMode: props.configFormMode,\n };\n}\n\nfunction normalizeSecurity(value?: string): ExecSecurity {\n if (value === \"allowlist\" || value === \"full\" || value === \"deny\") return value;\n return \"deny\";\n}\n\nfunction normalizeAsk(value?: string): ExecAsk {\n if (value === \"always\" || value === \"off\" || value === \"on-miss\") return value;\n return \"on-miss\";\n}\n\nfunction resolveExecApprovalsDefaults(\n form: ExecApprovalsFile | null,\n): ExecApprovalsResolvedDefaults {\n const defaults = form?.defaults ?? {};\n return {\n security: normalizeSecurity(defaults.security),\n ask: normalizeAsk(defaults.ask),\n askFallback: normalizeSecurity(defaults.askFallback ?? \"deny\"),\n autoAllowSkills: Boolean(defaults.autoAllowSkills ?? false),\n };\n}\n\nfunction resolveConfigAgents(config: Record | null): ExecApprovalsAgentOption[] {\n const agentsNode = (config?.agents ?? {}) as Record;\n const list = Array.isArray(agentsNode.list) ? agentsNode.list : [];\n const agents: ExecApprovalsAgentOption[] = [];\n list.forEach((entry) => {\n if (!entry || typeof entry !== \"object\") return;\n const record = entry as Record;\n const id = typeof record.id === \"string\" ? record.id.trim() : \"\";\n if (!id) return;\n const name = typeof record.name === \"string\" ? record.name.trim() : undefined;\n const isDefault = record.default === true;\n agents.push({ id, name: name || undefined, isDefault });\n });\n return agents;\n}\n\nfunction resolveExecApprovalsAgents(\n config: Record | null,\n form: ExecApprovalsFile | null,\n): ExecApprovalsAgentOption[] {\n const configAgents = resolveConfigAgents(config);\n const approvalsAgents = Object.keys(form?.agents ?? {});\n const merged = new Map();\n configAgents.forEach((agent) => merged.set(agent.id, agent));\n approvalsAgents.forEach((id) => {\n if (merged.has(id)) return;\n merged.set(id, { id });\n });\n const agents = Array.from(merged.values());\n if (agents.length === 0) {\n agents.push({ id: \"main\", isDefault: true });\n }\n agents.sort((a, b) => {\n if (a.isDefault && !b.isDefault) return -1;\n if (!a.isDefault && b.isDefault) return 1;\n const aLabel = a.name?.trim() ? a.name : a.id;\n const bLabel = b.name?.trim() ? b.name : b.id;\n return aLabel.localeCompare(bLabel);\n });\n return agents;\n}\n\nfunction resolveExecApprovalsScope(\n selected: string | null,\n agents: ExecApprovalsAgentOption[],\n): string {\n if (selected === EXEC_APPROVALS_DEFAULT_SCOPE) return EXEC_APPROVALS_DEFAULT_SCOPE;\n if (selected && agents.some((agent) => agent.id === selected)) return selected;\n return EXEC_APPROVALS_DEFAULT_SCOPE;\n}\n\nfunction resolveExecApprovalsState(props: NodesProps): ExecApprovalsState {\n const form = props.execApprovalsForm ?? props.execApprovalsSnapshot?.file ?? null;\n const ready = Boolean(form);\n const defaults = resolveExecApprovalsDefaults(form);\n const agents = resolveExecApprovalsAgents(props.configForm, form);\n const targetNodes = resolveExecApprovalsNodes(props.nodes);\n const target = props.execApprovalsTarget;\n let targetNodeId =\n target === \"node\" && props.execApprovalsTargetNodeId\n ? props.execApprovalsTargetNodeId\n : null;\n if (target === \"node\" && targetNodeId && !targetNodes.some((node) => node.id === targetNodeId)) {\n targetNodeId = null;\n }\n const selectedScope = resolveExecApprovalsScope(props.execApprovalsSelectedAgent, agents);\n const selectedAgent =\n selectedScope !== EXEC_APPROVALS_DEFAULT_SCOPE\n ? ((form?.agents ?? {})[selectedScope] as Record | undefined) ??\n null\n : null;\n const allowlist = Array.isArray((selectedAgent as { allowlist?: unknown })?.allowlist)\n ? ((selectedAgent as { allowlist?: ExecApprovalsAllowlistEntry[] }).allowlist ??\n [])\n : [];\n return {\n ready,\n disabled: props.execApprovalsSaving || props.execApprovalsLoading,\n dirty: props.execApprovalsDirty,\n loading: props.execApprovalsLoading,\n saving: props.execApprovalsSaving,\n form,\n defaults,\n selectedScope,\n selectedAgent,\n agents,\n allowlist,\n target,\n targetNodeId,\n targetNodes,\n onSelectScope: props.onExecApprovalsSelectAgent,\n onSelectTarget: props.onExecApprovalsTargetChange,\n onPatch: props.onExecApprovalsPatch,\n onRemove: props.onExecApprovalsRemove,\n onLoad: props.onLoadExecApprovals,\n onSave: props.onSaveExecApprovals,\n };\n}\n\nfunction renderBindings(state: BindingState) {\n const supportsBinding = state.nodes.length > 0;\n const defaultValue = state.defaultBinding ?? \"\";\n return html`\n
    \n
    \n
    \n
    Exec node binding
    \n
    \n Pin agents to a specific node when using exec host=node.\n
    \n
    \n \n ${state.configSaving ? \"Saving…\" : \"Save\"}\n \n
    \n\n ${state.formMode === \"raw\"\n ? html`
    \n Switch the Config tab to Form mode to edit bindings here.\n
    `\n : nothing}\n\n ${!state.ready\n ? html`
    \n
    Load config to edit bindings.
    \n \n
    `\n : html`\n
    \n
    \n
    \n
    Default binding
    \n
    Used when agents do not override a node binding.
    \n
    \n
    \n \n ${!supportsBinding\n ? html`
    No nodes with system.run available.
    `\n : nothing}\n
    \n
    \n\n ${state.agents.length === 0\n ? html`
    No agents found.
    `\n : state.agents.map((agent) =>\n renderAgentBinding(agent, state),\n )}\n
    \n `}\n
    \n `;\n}\n\nfunction renderExecApprovals(state: ExecApprovalsState) {\n const ready = state.ready;\n const targetReady = state.target !== \"node\" || Boolean(state.targetNodeId);\n return html`\n
    \n
    \n
    \n
    Exec approvals
    \n
    \n Allowlist and approval policy for exec host=gateway/node.\n
    \n
    \n \n ${state.saving ? \"Saving…\" : \"Save\"}\n \n
    \n\n ${renderExecApprovalsTarget(state)}\n\n ${!ready\n ? html`
    \n
    Load exec approvals to edit allowlists.
    \n \n
    `\n : html`\n ${renderExecApprovalsTabs(state)}\n ${renderExecApprovalsPolicy(state)}\n ${state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE\n ? nothing\n : renderExecApprovalsAllowlist(state)}\n `}\n
    \n `;\n}\n\nfunction renderExecApprovalsTarget(state: ExecApprovalsState) {\n const hasNodes = state.targetNodes.length > 0;\n const nodeValue = state.targetNodeId ?? \"\";\n return html`\n
    \n
    \n
    \n
    Target
    \n
    \n Gateway edits local approvals; node edits the selected node.\n
    \n
    \n
    \n \n ${state.target === \"node\"\n ? html`\n \n `\n : nothing}\n
    \n
    \n ${state.target === \"node\" && !hasNodes\n ? html`
    No nodes advertise exec approvals yet.
    `\n : nothing}\n
    \n `;\n}\n\nfunction renderExecApprovalsTabs(state: ExecApprovalsState) {\n return html`\n
    \n Scope\n
    \n state.onSelectScope(EXEC_APPROVALS_DEFAULT_SCOPE)}\n >\n Defaults\n \n ${state.agents.map((agent) => {\n const label = agent.name?.trim() ? `${agent.name} (${agent.id})` : agent.id;\n return html`\n state.onSelectScope(agent.id)}\n >\n ${label}\n \n `;\n })}\n
    \n
    \n `;\n}\n\nfunction renderExecApprovalsPolicy(state: ExecApprovalsState) {\n const isDefaults = state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE;\n const defaults = state.defaults;\n const agent = state.selectedAgent ?? {};\n const basePath = isDefaults ? [\"defaults\"] : [\"agents\", state.selectedScope];\n const agentSecurity = typeof agent.security === \"string\" ? agent.security : undefined;\n const agentAsk = typeof agent.ask === \"string\" ? agent.ask : undefined;\n const agentAskFallback =\n typeof agent.askFallback === \"string\" ? agent.askFallback : undefined;\n const securityValue = isDefaults ? defaults.security : agentSecurity ?? \"__default__\";\n const askValue = isDefaults ? defaults.ask : agentAsk ?? \"__default__\";\n const askFallbackValue = isDefaults\n ? defaults.askFallback\n : agentAskFallback ?? \"__default__\";\n const autoOverride =\n typeof agent.autoAllowSkills === \"boolean\" ? agent.autoAllowSkills : undefined;\n const autoEffective = autoOverride ?? defaults.autoAllowSkills;\n const autoIsDefault = autoOverride == null;\n\n return html`\n
    \n
    \n
    \n
    Security
    \n
    \n ${isDefaults\n ? \"Default security mode.\"\n : `Default: ${defaults.security}.`}\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    Ask
    \n
    \n ${isDefaults ? \"Default prompt policy.\" : `Default: ${defaults.ask}.`}\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    Ask fallback
    \n
    \n ${isDefaults\n ? \"Applied when the UI prompt is unavailable.\"\n : `Default: ${defaults.askFallback}.`}\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    Auto-allow skill CLIs
    \n
    \n ${isDefaults\n ? \"Allow skill executables listed by the Gateway.\"\n : autoIsDefault\n ? `Using default (${defaults.autoAllowSkills ? \"on\" : \"off\"}).`\n : `Override (${autoEffective ? \"on\" : \"off\"}).`}\n
    \n
    \n
    \n \n ${!isDefaults && !autoIsDefault\n ? html` state.onRemove([...basePath, \"autoAllowSkills\"])}\n >\n Use default\n `\n : nothing}\n
    \n
    \n
    \n `;\n}\n\nfunction renderExecApprovalsAllowlist(state: ExecApprovalsState) {\n const allowlistPath = [\"agents\", state.selectedScope, \"allowlist\"];\n const entries = state.allowlist;\n return html`\n
    \n
    \n
    Allowlist
    \n
    Case-insensitive glob patterns.
    \n
    \n {\n const next = [...entries, { pattern: \"\" }];\n state.onPatch(allowlistPath, next);\n }}\n >\n Add pattern\n \n
    \n
    \n ${entries.length === 0\n ? html`
    No allowlist entries yet.
    `\n : entries.map((entry, index) =>\n renderAllowlistEntry(state, entry, index),\n )}\n
    \n `;\n}\n\nfunction renderAllowlistEntry(\n state: ExecApprovalsState,\n entry: ExecApprovalsAllowlistEntry,\n index: number,\n) {\n const lastUsed = entry.lastUsedAt ? formatAgo(entry.lastUsedAt) : \"never\";\n const lastCommand = entry.lastUsedCommand\n ? clampText(entry.lastUsedCommand, 120)\n : null;\n const lastPath = entry.lastResolvedPath\n ? clampText(entry.lastResolvedPath, 120)\n : null;\n return html`\n
    \n
    \n
    ${entry.pattern?.trim() ? entry.pattern : \"New pattern\"}
    \n
    Last used: ${lastUsed}
    \n ${lastCommand ? html`
    ${lastCommand}
    ` : nothing}\n ${lastPath ? html`
    ${lastPath}
    ` : nothing}\n
    \n
    \n \n {\n if (state.allowlist.length <= 1) {\n state.onRemove([\"agents\", state.selectedScope, \"allowlist\"]);\n return;\n }\n state.onRemove([\"agents\", state.selectedScope, \"allowlist\", index]);\n }}\n >\n Remove\n \n
    \n
    \n `;\n}\n\nfunction renderAgentBinding(agent: BindingAgent, state: BindingState) {\n const bindingValue = agent.binding ?? \"__default__\";\n const label = agent.name?.trim() ? `${agent.name} (${agent.id})` : agent.id;\n const supportsBinding = state.nodes.length > 0;\n return html`\n
    \n
    \n
    ${label}
    \n
    \n ${agent.isDefault ? \"default agent\" : \"agent\"} ·\n ${bindingValue === \"__default__\"\n ? `uses default (${state.defaultBinding ?? \"any\"})`\n : `override: ${agent.binding}`}\n
    \n
    \n
    \n \n
    \n
    \n `;\n}\n\nfunction resolveExecNodes(nodes: Array>): BindingNode[] {\n const list: BindingNode[] = [];\n for (const node of nodes) {\n const commands = Array.isArray(node.commands) ? node.commands : [];\n const supports = commands.some((cmd) => String(cmd) === \"system.run\");\n if (!supports) continue;\n const nodeId = typeof node.nodeId === \"string\" ? node.nodeId.trim() : \"\";\n if (!nodeId) continue;\n const displayName =\n typeof node.displayName === \"string\" && node.displayName.trim()\n ? node.displayName.trim()\n : nodeId;\n list.push({ id: nodeId, label: displayName === nodeId ? nodeId : `${displayName} · ${nodeId}` });\n }\n list.sort((a, b) => a.label.localeCompare(b.label));\n return list;\n}\n\nfunction resolveExecApprovalsNodes(nodes: Array>): ExecApprovalsTargetNode[] {\n const list: ExecApprovalsTargetNode[] = [];\n for (const node of nodes) {\n const commands = Array.isArray(node.commands) ? node.commands : [];\n const supports = commands.some(\n (cmd) => String(cmd) === \"system.execApprovals.get\" || String(cmd) === \"system.execApprovals.set\",\n );\n if (!supports) continue;\n const nodeId = typeof node.nodeId === \"string\" ? node.nodeId.trim() : \"\";\n if (!nodeId) continue;\n const displayName =\n typeof node.displayName === \"string\" && node.displayName.trim()\n ? node.displayName.trim()\n : nodeId;\n list.push({ id: nodeId, label: displayName === nodeId ? nodeId : `${displayName} · ${nodeId}` });\n }\n list.sort((a, b) => a.label.localeCompare(b.label));\n return list;\n}\n\nfunction resolveAgentBindings(config: Record | null): {\n defaultBinding?: string | null;\n agents: BindingAgent[];\n} {\n const fallbackAgent: BindingAgent = {\n id: \"main\",\n name: undefined,\n index: 0,\n isDefault: true,\n binding: null,\n };\n if (!config || typeof config !== \"object\") {\n return { defaultBinding: null, agents: [fallbackAgent] };\n }\n const tools = (config.tools ?? {}) as Record;\n const exec = (tools.exec ?? {}) as Record;\n const defaultBinding =\n typeof exec.node === \"string\" && exec.node.trim() ? exec.node.trim() : null;\n\n const agentsNode = (config.agents ?? {}) as Record;\n const list = Array.isArray(agentsNode.list) ? agentsNode.list : [];\n if (list.length === 0) {\n return { defaultBinding, agents: [fallbackAgent] };\n }\n\n const agents: BindingAgent[] = [];\n list.forEach((entry, index) => {\n if (!entry || typeof entry !== \"object\") return;\n const record = entry as Record;\n const id = typeof record.id === \"string\" ? record.id.trim() : \"\";\n if (!id) return;\n const name = typeof record.name === \"string\" ? record.name.trim() : undefined;\n const isDefault = record.default === true;\n const toolsEntry = (record.tools ?? {}) as Record;\n const execEntry = (toolsEntry.exec ?? {}) as Record;\n const binding =\n typeof execEntry.node === \"string\" && execEntry.node.trim()\n ? execEntry.node.trim()\n : null;\n agents.push({\n id,\n name: name || undefined,\n index,\n isDefault,\n binding,\n });\n });\n\n if (agents.length === 0) {\n agents.push(fallbackAgent);\n }\n\n return { defaultBinding, agents };\n}\n\nfunction renderNode(node: Record) {\n const connected = Boolean(node.connected);\n const paired = Boolean(node.paired);\n const title =\n (typeof node.displayName === \"string\" && node.displayName.trim()) ||\n (typeof node.nodeId === \"string\" ? node.nodeId : \"unknown\");\n const caps = Array.isArray(node.caps) ? (node.caps as unknown[]) : [];\n const commands = Array.isArray(node.commands) ? (node.commands as unknown[]) : [];\n return html`\n
    \n
    \n
    ${title}
    \n
    \n ${typeof node.nodeId === \"string\" ? node.nodeId : \"\"}\n ${typeof node.remoteIp === \"string\" ? ` · ${node.remoteIp}` : \"\"}\n ${typeof node.version === \"string\" ? ` · ${node.version}` : \"\"}\n
    \n
    \n ${paired ? \"paired\" : \"unpaired\"}\n \n ${connected ? \"connected\" : \"offline\"}\n \n ${caps.slice(0, 12).map((c) => html`${String(c)}`)}\n ${commands\n .slice(0, 8)\n .map((c) => html`${String(c)}`)}\n
    \n
    \n
    \n `;\n}\n","import { html } from \"lit\";\n\nimport type { GatewayHelloOk } from \"../gateway\";\nimport { formatAgo, formatDurationMs } from \"../format\";\nimport { formatNextRun } from \"../presenter\";\nimport type { UiSettings } from \"../storage\";\n\nexport type OverviewProps = {\n connected: boolean;\n hello: GatewayHelloOk | null;\n settings: UiSettings;\n password: string;\n lastError: string | null;\n presenceCount: number;\n sessionsCount: number | null;\n cronEnabled: boolean | null;\n cronNext: number | null;\n lastChannelsRefresh: number | null;\n onSettingsChange: (next: UiSettings) => void;\n onPasswordChange: (next: string) => void;\n onSessionKeyChange: (next: string) => void;\n onConnect: () => void;\n onRefresh: () => void;\n};\n\nexport function renderOverview(props: OverviewProps) {\n const snapshot = props.hello?.snapshot as\n | { uptimeMs?: number; policy?: { tickIntervalMs?: number } }\n | undefined;\n const uptime = snapshot?.uptimeMs ? formatDurationMs(snapshot.uptimeMs) : \"n/a\";\n const tick = snapshot?.policy?.tickIntervalMs\n ? `${snapshot.policy.tickIntervalMs}ms`\n : \"n/a\";\n const authHint = (() => {\n if (props.connected || !props.lastError) return null;\n const lower = props.lastError.toLowerCase();\n const authFailed = lower.includes(\"unauthorized\") || lower.includes(\"connect failed\");\n if (!authFailed) return null;\n const hasToken = Boolean(props.settings.token.trim());\n const hasPassword = Boolean(props.password.trim());\n if (!hasToken && !hasPassword) {\n return html`\n
    \n This gateway requires auth. Add a token or password, then click Connect.\n
    \n clawdbot dashboard --no-open → tokenized URL
    \n clawdbot doctor --generate-gateway-token → set token\n
    \n
    \n Docs: Control UI auth\n
    \n
    \n `;\n }\n return html`\n
    \n Auth failed. Re-copy a tokenized URL with\n clawdbot dashboard --no-open, or update the token,\n then click Connect.\n
    \n Docs: Control UI auth\n
    \n
    \n `;\n })();\n const insecureContextHint = (() => {\n if (props.connected || !props.lastError) return null;\n const isSecureContext = typeof window !== \"undefined\" ? window.isSecureContext : true;\n if (isSecureContext !== false) return null;\n const lower = props.lastError.toLowerCase();\n if (!lower.includes(\"secure context\") && !lower.includes(\"device identity required\")) {\n return null;\n }\n return html`\n
    \n This page is HTTP, so the browser blocks device identity. Use HTTPS (Tailscale Serve) or\n open http://127.0.0.1:18789 on the gateway host.\n
    \n If you must stay on HTTP, set\n gateway.controlUi.allowInsecureAuth: true (token-only).\n
    \n
    \n Docs: Tailscale Serve\n · \n Docs: Insecure HTTP\n
    \n
    \n `;\n })();\n\n return html`\n
    \n
    \n
    Gateway Access
    \n
    Where the dashboard connects and how it authenticates.
    \n
    \n \n \n \n \n
    \n
    \n \n \n Click Connect to apply connection changes.\n
    \n
    \n\n
    \n
    Snapshot
    \n
    Latest gateway handshake information.
    \n
    \n
    \n
    Status
    \n
    \n ${props.connected ? \"Connected\" : \"Disconnected\"}\n
    \n
    \n
    \n
    Uptime
    \n
    ${uptime}
    \n
    \n
    \n
    Tick Interval
    \n
    ${tick}
    \n
    \n
    \n
    Last Channels Refresh
    \n
    \n ${props.lastChannelsRefresh\n ? formatAgo(props.lastChannelsRefresh)\n : \"n/a\"}\n
    \n
    \n
    \n ${props.lastError\n ? html`
    \n
    ${props.lastError}
    \n ${authHint ?? \"\"}\n ${insecureContextHint ?? \"\"}\n
    `\n : html`
    \n Use Channels to link WhatsApp, Telegram, Discord, Signal, or iMessage.\n
    `}\n
    \n
    \n\n
    \n
    \n
    Instances
    \n
    ${props.presenceCount}
    \n
    Presence beacons in the last 5 minutes.
    \n
    \n
    \n
    Sessions
    \n
    ${props.sessionsCount ?? \"n/a\"}
    \n
    Recent session keys tracked by the gateway.
    \n
    \n
    \n
    Cron
    \n
    \n ${props.cronEnabled == null\n ? \"n/a\"\n : props.cronEnabled\n ? \"Enabled\"\n : \"Disabled\"}\n
    \n
    Next wake ${formatNextRun(props.cronNext)}
    \n
    \n
    \n\n
    \n
    Notes
    \n
    Quick reminders for remote control setups.
    \n
    \n
    \n
    Tailscale serve
    \n
    \n Prefer serve mode to keep the gateway on loopback with tailnet auth.\n
    \n
    \n
    \n
    Session hygiene
    \n
    Use /new or sessions.patch to reset context.
    \n
    \n
    \n
    Cron reminders
    \n
    Use isolated sessions for recurring runs.
    \n
    \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { formatAgo } from \"../format\";\nimport { formatSessionTokens } from \"../presenter\";\nimport { pathForTab } from \"../navigation\";\nimport type { GatewaySessionRow, SessionsListResult } from \"../types\";\n\nexport type SessionsProps = {\n loading: boolean;\n result: SessionsListResult | null;\n error: string | null;\n activeMinutes: string;\n limit: string;\n includeGlobal: boolean;\n includeUnknown: boolean;\n basePath: string;\n onFiltersChange: (next: {\n activeMinutes: string;\n limit: string;\n includeGlobal: boolean;\n includeUnknown: boolean;\n }) => void;\n onRefresh: () => void;\n onPatch: (\n key: string,\n patch: {\n label?: string | null;\n thinkingLevel?: string | null;\n verboseLevel?: string | null;\n reasoningLevel?: string | null;\n },\n ) => void;\n onDelete: (key: string) => void;\n};\n\nconst THINK_LEVELS = [\"\", \"off\", \"minimal\", \"low\", \"medium\", \"high\"] as const;\nconst BINARY_THINK_LEVELS = [\"\", \"off\", \"on\"] as const;\nconst VERBOSE_LEVELS = [\n { value: \"\", label: \"inherit\" },\n { value: \"off\", label: \"off (explicit)\" },\n { value: \"on\", label: \"on\" },\n] as const;\nconst REASONING_LEVELS = [\"\", \"off\", \"on\", \"stream\"] as const;\n\nfunction normalizeProviderId(provider?: string | null): string {\n if (!provider) return \"\";\n const normalized = provider.trim().toLowerCase();\n if (normalized === \"z.ai\" || normalized === \"z-ai\") return \"zai\";\n return normalized;\n}\n\nfunction isBinaryThinkingProvider(provider?: string | null): boolean {\n return normalizeProviderId(provider) === \"zai\";\n}\n\nfunction resolveThinkLevelOptions(provider?: string | null): readonly string[] {\n return isBinaryThinkingProvider(provider) ? BINARY_THINK_LEVELS : THINK_LEVELS;\n}\n\nfunction resolveThinkLevelDisplay(value: string, isBinary: boolean): string {\n if (!isBinary) return value;\n if (!value || value === \"off\") return value;\n return \"on\";\n}\n\nfunction resolveThinkLevelPatchValue(value: string, isBinary: boolean): string | null {\n if (!value) return null;\n if (!isBinary) return value;\n if (value === \"on\") return \"low\";\n return value;\n}\n\nexport function renderSessions(props: SessionsProps) {\n const rows = props.result?.sessions ?? [];\n return html`\n
    \n
    \n
    \n
    Sessions
    \n
    Active session keys and per-session overrides.
    \n
    \n \n
    \n\n
    \n \n \n \n \n
    \n\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n
    \n ${props.result ? `Store: ${props.result.path}` : \"\"}\n
    \n\n
    \n
    \n
    Key
    \n
    Label
    \n
    Kind
    \n
    Updated
    \n
    Tokens
    \n
    Thinking
    \n
    Verbose
    \n
    Reasoning
    \n
    Actions
    \n
    \n ${rows.length === 0\n ? html`
    No sessions found.
    `\n : rows.map((row) =>\n renderRow(row, props.basePath, props.onPatch, props.onDelete, props.loading),\n )}\n
    \n
    \n `;\n}\n\nfunction renderRow(\n row: GatewaySessionRow,\n basePath: string,\n onPatch: SessionsProps[\"onPatch\"],\n onDelete: SessionsProps[\"onDelete\"],\n disabled: boolean,\n) {\n const updated = row.updatedAt ? formatAgo(row.updatedAt) : \"n/a\";\n const rawThinking = row.thinkingLevel ?? \"\";\n const isBinaryThinking = isBinaryThinkingProvider(row.modelProvider);\n const thinking = resolveThinkLevelDisplay(rawThinking, isBinaryThinking);\n const thinkLevels = resolveThinkLevelOptions(row.modelProvider);\n const verbose = row.verboseLevel ?? \"\";\n const reasoning = row.reasoningLevel ?? \"\";\n const displayName = row.displayName ?? row.key;\n const canLink = row.kind !== \"global\";\n const chatUrl = canLink\n ? `${pathForTab(\"chat\", basePath)}?session=${encodeURIComponent(row.key)}`\n : null;\n\n return html`\n
    \n
    ${canLink\n ? html`${displayName}`\n : displayName}
    \n
    \n {\n const value = (e.target as HTMLInputElement).value.trim();\n onPatch(row.key, { label: value || null });\n }}\n />\n
    \n
    ${row.kind}
    \n
    ${updated}
    \n
    ${formatSessionTokens(row)}
    \n
    \n {\n const value = (e.target as HTMLSelectElement).value;\n onPatch(row.key, {\n thinkingLevel: resolveThinkLevelPatchValue(value, isBinaryThinking),\n });\n }}\n >\n ${thinkLevels.map((level) =>\n html``,\n )}\n \n
    \n
    \n {\n const value = (e.target as HTMLSelectElement).value;\n onPatch(row.key, { verboseLevel: value || null });\n }}\n >\n ${VERBOSE_LEVELS.map(\n (level) => html``,\n )}\n \n
    \n
    \n {\n const value = (e.target as HTMLSelectElement).value;\n onPatch(row.key, { reasoningLevel: value || null });\n }}\n >\n ${REASONING_LEVELS.map((level) =>\n html``,\n )}\n \n
    \n
    \n \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { AppViewState } from \"../app-view-state\";\n\nfunction formatRemaining(ms: number): string {\n const remaining = Math.max(0, ms);\n const totalSeconds = Math.floor(remaining / 1000);\n if (totalSeconds < 60) return `${totalSeconds}s`;\n const minutes = Math.floor(totalSeconds / 60);\n if (minutes < 60) return `${minutes}m`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h`;\n}\n\nfunction renderMetaRow(label: string, value?: string | null) {\n if (!value) return nothing;\n return html`
    ${label}${value}
    `;\n}\n\nexport function renderExecApprovalPrompt(state: AppViewState) {\n const active = state.execApprovalQueue[0];\n if (!active) return nothing;\n const request = active.request;\n const remainingMs = active.expiresAtMs - Date.now();\n const remaining = remainingMs > 0 ? `expires in ${formatRemaining(remainingMs)}` : \"expired\";\n const queueCount = state.execApprovalQueue.length;\n return html`\n
    \n
    \n
    \n
    \n
    Exec approval needed
    \n
    ${remaining}
    \n
    \n ${queueCount > 1\n ? html`
    ${queueCount} pending
    `\n : nothing}\n
    \n
    ${request.command}
    \n
    \n ${renderMetaRow(\"Host\", request.host)}\n ${renderMetaRow(\"Agent\", request.agentId)}\n ${renderMetaRow(\"Session\", request.sessionKey)}\n ${renderMetaRow(\"CWD\", request.cwd)}\n ${renderMetaRow(\"Resolved\", request.resolvedPath)}\n ${renderMetaRow(\"Security\", request.security)}\n ${renderMetaRow(\"Ask\", request.ask)}\n
    \n ${state.execApprovalError\n ? html`
    ${state.execApprovalError}
    `\n : nothing}\n
    \n state.handleExecApprovalDecision(\"allow-once\")}\n >\n Allow once\n \n state.handleExecApprovalDecision(\"allow-always\")}\n >\n Always allow\n \n state.handleExecApprovalDecision(\"deny\")}\n >\n Deny\n \n
    \n
    \n
    \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport { clampText } from \"../format\";\nimport type { SkillStatusEntry, SkillStatusReport } from \"../types\";\nimport type { SkillMessageMap } from \"../controllers/skills\";\n\nexport type SkillsProps = {\n loading: boolean;\n report: SkillStatusReport | null;\n error: string | null;\n filter: string;\n edits: Record;\n busyKey: string | null;\n messages: SkillMessageMap;\n onFilterChange: (next: string) => void;\n onRefresh: () => void;\n onToggle: (skillKey: string, enabled: boolean) => void;\n onEdit: (skillKey: string, value: string) => void;\n onSaveKey: (skillKey: string) => void;\n onInstall: (skillKey: string, name: string, installId: string) => void;\n};\n\nexport function renderSkills(props: SkillsProps) {\n const skills = props.report?.skills ?? [];\n const filter = props.filter.trim().toLowerCase();\n const filtered = filter\n ? skills.filter((skill) =>\n [skill.name, skill.description, skill.source]\n .join(\" \")\n .toLowerCase()\n .includes(filter),\n )\n : skills;\n\n return html`\n
    \n
    \n
    \n
    Skills
    \n
    Bundled, managed, and workspace skills.
    \n
    \n \n
    \n\n
    \n \n
    ${filtered.length} shown
    \n
    \n\n ${props.error\n ? html`
    ${props.error}
    `\n : nothing}\n\n ${filtered.length === 0\n ? html`
    No skills found.
    `\n : html`\n
    \n ${filtered.map((skill) => renderSkill(skill, props))}\n
    \n `}\n
    \n `;\n}\n\nfunction renderSkill(skill: SkillStatusEntry, props: SkillsProps) {\n const busy = props.busyKey === skill.skillKey;\n const apiKey = props.edits[skill.skillKey] ?? \"\";\n const message = props.messages[skill.skillKey] ?? null;\n const canInstall =\n skill.install.length > 0 && skill.missing.bins.length > 0;\n const missing = [\n ...skill.missing.bins.map((b) => `bin:${b}`),\n ...skill.missing.env.map((e) => `env:${e}`),\n ...skill.missing.config.map((c) => `config:${c}`),\n ...skill.missing.os.map((o) => `os:${o}`),\n ];\n const reasons: string[] = [];\n if (skill.disabled) reasons.push(\"disabled\");\n if (skill.blockedByAllowlist) reasons.push(\"blocked by allowlist\");\n return html`\n
    \n
    \n
    \n ${skill.emoji ? `${skill.emoji} ` : \"\"}${skill.name}\n
    \n
    ${clampText(skill.description, 140)}
    \n
    \n ${skill.source}\n \n ${skill.eligible ? \"eligible\" : \"blocked\"}\n \n ${skill.disabled ? html`disabled` : nothing}\n
    \n ${missing.length > 0\n ? html`\n
    \n Missing: ${missing.join(\", \")}\n
    \n `\n : nothing}\n ${reasons.length > 0\n ? html`\n
    \n Reason: ${reasons.join(\", \")}\n
    \n `\n : nothing}\n
    \n
    \n
    \n props.onToggle(skill.skillKey, skill.disabled)}\n >\n ${skill.disabled ? \"Enable\" : \"Disable\"}\n \n ${canInstall\n ? html`\n props.onInstall(skill.skillKey, skill.name, skill.install[0].id)}\n >\n ${busy ? \"Installing…\" : skill.install[0].label}\n `\n : nothing}\n
    \n ${message\n ? html`\n ${message.message}\n
    `\n : nothing}\n ${skill.primaryEnv\n ? html`\n
    \n API key\n \n props.onEdit(skill.skillKey, (e.target as HTMLInputElement).value)}\n />\n
    \n props.onSaveKey(skill.skillKey)}\n >\n Save key\n \n `\n : nothing}\n
    \n \n `;\n}\n","import { html } from \"lit\";\nimport { repeat } from \"lit/directives/repeat.js\";\n\nimport type { AppViewState } from \"./app-view-state\";\nimport { iconForTab, pathForTab, titleForTab, type Tab } from \"./navigation\";\nimport { loadChatHistory } from \"./controllers/chat\";\nimport { syncUrlWithSessionKey } from \"./app-settings\";\nimport type { SessionsListResult } from \"./types\";\nimport type { ThemeMode } from \"./theme\";\nimport type { ThemeTransitionContext } from \"./theme-transition\";\n\nexport function renderTab(state: AppViewState, tab: Tab) {\n const href = pathForTab(tab, state.basePath);\n return html`\n {\n if (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey\n ) {\n return;\n }\n event.preventDefault();\n state.setTab(tab);\n }}\n title=${titleForTab(tab)}\n >\n ${iconForTab(tab)}\n ${titleForTab(tab)}\n \n `;\n}\n\nexport function renderChatControls(state: AppViewState) {\n const sessionOptions = resolveSessionOptions(state.sessionKey, state.sessionsResult);\n const disableThinkingToggle = state.onboarding;\n const disableFocusToggle = state.onboarding;\n const showThinking = state.onboarding ? false : state.settings.chatShowThinking;\n const focusActive = state.onboarding ? true : state.settings.chatFocusMode;\n // Refresh icon\n const refreshIcon = html``;\n const focusIcon = html``;\n return html`\n
    \n \n {\n state.resetToolStream();\n void loadChatHistory(state);\n }}\n title=\"Refresh chat history\"\n >\n ${refreshIcon}\n \n |\n {\n if (disableThinkingToggle) return;\n state.applySettings({\n ...state.settings,\n chatShowThinking: !state.settings.chatShowThinking,\n });\n }}\n aria-pressed=${showThinking}\n title=${disableThinkingToggle\n ? \"Disabled during onboarding\"\n : \"Toggle assistant thinking/working output\"}\n >\n 🧠\n \n {\n if (disableFocusToggle) return;\n state.applySettings({\n ...state.settings,\n chatFocusMode: !state.settings.chatFocusMode,\n });\n }}\n aria-pressed=${focusActive}\n title=${disableFocusToggle\n ? \"Disabled during onboarding\"\n : \"Toggle focus mode (hide sidebar + page header)\"}\n >\n ${focusIcon}\n \n
    \n `;\n}\n\nfunction resolveSessionOptions(sessionKey: string, sessions: SessionsListResult | null) {\n const seen = new Set();\n const options: Array<{ key: string; displayName?: string }> = [];\n\n const resolvedCurrent = sessions?.sessions?.find((s) => s.key === sessionKey);\n\n // Add current session key first\n seen.add(sessionKey);\n options.push({ key: sessionKey, displayName: resolvedCurrent?.displayName });\n\n // Add sessions from the result\n if (sessions?.sessions) {\n for (const s of sessions.sessions) {\n if (!seen.has(s.key)) {\n seen.add(s.key);\n options.push({ key: s.key, displayName: s.displayName });\n }\n }\n }\n\n return options;\n}\n\nconst THEME_ORDER: ThemeMode[] = [\"system\", \"light\", \"dark\"];\n\nexport function renderThemeToggle(state: AppViewState) {\n const index = Math.max(0, THEME_ORDER.indexOf(state.theme));\n const applyTheme = (next: ThemeMode) => (event: MouseEvent) => {\n const element = event.currentTarget as HTMLElement;\n const context: ThemeTransitionContext = { element };\n if (event.clientX || event.clientY) {\n context.pointerClientX = event.clientX;\n context.pointerClientY = event.clientY;\n }\n state.setTheme(next, context);\n };\n\n return html`\n
    \n
    \n \n \n ${renderMonitorIcon()}\n \n \n ${renderSunIcon()}\n \n \n ${renderMoonIcon()}\n \n
    \n
    \n `;\n}\n\nfunction renderSunIcon() {\n return html`\n \n \n \n \n \n \n \n \n \n \n \n `;\n}\n\nfunction renderMoonIcon() {\n return html`\n \n \n \n `;\n}\n\nfunction renderMonitorIcon() {\n return html`\n \n \n \n \n \n `;\n}\n","import { html, nothing } from \"lit\";\n\nimport type { GatewayBrowserClient, GatewayHelloOk } from \"./gateway\";\nimport type { AppViewState } from \"./app-view-state\";\nimport { parseAgentSessionKey } from \"../../../src/routing/session-key.js\";\nimport {\n TAB_GROUPS,\n iconForTab,\n pathForTab,\n subtitleForTab,\n titleForTab,\n type Tab,\n} from \"./navigation\";\nimport type { UiSettings } from \"./storage\";\nimport type { ThemeMode } from \"./theme\";\nimport type { ThemeTransitionContext } from \"./theme-transition\";\nimport type {\n ConfigSnapshot,\n CronJob,\n CronRunLogEntry,\n CronStatus,\n HealthSnapshot,\n LogEntry,\n LogLevel,\n PresenceEntry,\n ChannelsStatusSnapshot,\n SessionsListResult,\n SkillStatusReport,\n StatusSummary,\n} from \"./types\";\nimport type { ChatQueueItem, CronFormState } from \"./ui-types\";\nimport { refreshChatAvatar } from \"./app-chat\";\nimport { renderChat } from \"./views/chat\";\nimport { renderConfig } from \"./views/config\";\nimport { renderChannels } from \"./views/channels\";\nimport { renderCron } from \"./views/cron\";\nimport { renderDebug } from \"./views/debug\";\nimport { renderInstances } from \"./views/instances\";\nimport { renderLogs } from \"./views/logs\";\nimport { renderNodes } from \"./views/nodes\";\nimport { renderOverview } from \"./views/overview\";\nimport { renderSessions } from \"./views/sessions\";\nimport { renderExecApprovalPrompt } from \"./views/exec-approval\";\nimport {\n approveDevicePairing,\n loadDevices,\n rejectDevicePairing,\n revokeDeviceToken,\n rotateDeviceToken,\n} from \"./controllers/devices\";\nimport { renderSkills } from \"./views/skills\";\nimport { renderChatControls, renderTab, renderThemeToggle } from \"./app-render.helpers\";\nimport { loadChannels } from \"./controllers/channels\";\nimport { loadPresence } from \"./controllers/presence\";\nimport { deleteSession, loadSessions, patchSession } from \"./controllers/sessions\";\nimport {\n installSkill,\n loadSkills,\n saveSkillApiKey,\n updateSkillEdit,\n updateSkillEnabled,\n type SkillMessage,\n} from \"./controllers/skills\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadChatHistory } from \"./controllers/chat\";\nimport {\n applyConfig,\n loadConfig,\n runUpdate,\n saveConfig,\n updateConfigFormValue,\n removeConfigFormValue,\n} from \"./controllers/config\";\nimport {\n loadExecApprovals,\n removeExecApprovalsFormValue,\n saveExecApprovals,\n updateExecApprovalsFormValue,\n} from \"./controllers/exec-approvals\";\nimport { loadCronRuns, toggleCronJob, runCronJob, removeCronJob, addCronJob } from \"./controllers/cron\";\nimport { loadDebug, callDebugMethod } from \"./controllers/debug\";\nimport { loadLogs } from \"./controllers/logs\";\n\nconst AVATAR_DATA_RE = /^data:/i;\nconst AVATAR_HTTP_RE = /^https?:\\/\\//i;\n\nfunction resolveAssistantAvatarUrl(state: AppViewState): string | undefined {\n const list = state.agentsList?.agents ?? [];\n const parsed = parseAgentSessionKey(state.sessionKey);\n const agentId =\n parsed?.agentId ??\n state.agentsList?.defaultId ??\n \"main\";\n const agent = list.find((entry) => entry.id === agentId);\n const identity = agent?.identity;\n const candidate = identity?.avatarUrl ?? identity?.avatar;\n if (!candidate) return undefined;\n if (AVATAR_DATA_RE.test(candidate) || AVATAR_HTTP_RE.test(candidate)) return candidate;\n return identity?.avatarUrl;\n}\n\nexport function renderApp(state: AppViewState) {\n const presenceCount = state.presenceEntries.length;\n const sessionsCount = state.sessionsResult?.count ?? null;\n const cronNext = state.cronStatus?.nextWakeAtMs ?? null;\n const chatDisabledReason = state.connected ? null : \"Disconnected from gateway.\";\n const isChat = state.tab === \"chat\";\n const chatFocus = isChat && (state.settings.chatFocusMode || state.onboarding);\n const showThinking = state.onboarding ? false : state.settings.chatShowThinking;\n const assistantAvatarUrl = resolveAssistantAvatarUrl(state);\n const chatAvatarUrl = state.chatAvatarUrl ?? assistantAvatarUrl ?? null;\n\n return html`\n
    \n
    \n
    \n \n state.applySettings({\n ...state.settings,\n navCollapsed: !state.settings.navCollapsed,\n })}\n title=\"${state.settings.navCollapsed ? \"Expand sidebar\" : \"Collapse sidebar\"}\"\n aria-label=\"${state.settings.navCollapsed ? \"Expand sidebar\" : \"Collapse sidebar\"}\"\n >\n \n \n
    \n
    CLAWDBOT
    \n
    Gateway Dashboard
    \n
    \n
    \n
    \n
    \n \n Health\n ${state.connected ? \"OK\" : \"Offline\"}\n
    \n ${renderThemeToggle(state)}\n
    \n
    \n \n
    \n
    \n
    \n
    ${titleForTab(state.tab)}
    \n
    ${subtitleForTab(state.tab)}
    \n
    \n
    \n ${state.lastError\n ? html`
    ${state.lastError}
    `\n : nothing}\n ${isChat ? renderChatControls(state) : nothing}\n
    \n
    \n\n ${state.tab === \"overview\"\n ? renderOverview({\n connected: state.connected,\n hello: state.hello,\n settings: state.settings,\n password: state.password,\n lastError: state.lastError,\n presenceCount,\n sessionsCount,\n cronEnabled: state.cronStatus?.enabled ?? null,\n cronNext,\n lastChannelsRefresh: state.channelsLastSuccess,\n onSettingsChange: (next) => state.applySettings(next),\n onPasswordChange: (next) => (state.password = next),\n onSessionKeyChange: (next) => {\n state.sessionKey = next;\n state.chatMessage = \"\";\n state.resetToolStream();\n state.applySettings({\n ...state.settings,\n sessionKey: next,\n lastActiveSessionKey: next,\n });\n void state.loadAssistantIdentity();\n },\n onConnect: () => state.connect(),\n onRefresh: () => state.loadOverview(),\n })\n : nothing}\n\n ${state.tab === \"channels\"\n ? renderChannels({\n connected: state.connected,\n loading: state.channelsLoading,\n snapshot: state.channelsSnapshot,\n lastError: state.channelsError,\n lastSuccessAt: state.channelsLastSuccess,\n whatsappMessage: state.whatsappLoginMessage,\n whatsappQrDataUrl: state.whatsappLoginQrDataUrl,\n whatsappConnected: state.whatsappLoginConnected,\n whatsappBusy: state.whatsappBusy,\n configSchema: state.configSchema,\n configSchemaLoading: state.configSchemaLoading,\n configForm: state.configForm,\n configUiHints: state.configUiHints,\n configSaving: state.configSaving,\n configFormDirty: state.configFormDirty,\n nostrProfileFormState: state.nostrProfileFormState,\n nostrProfileAccountId: state.nostrProfileAccountId,\n onRefresh: (probe) => loadChannels(state, probe),\n onWhatsAppStart: (force) => state.handleWhatsAppStart(force),\n onWhatsAppWait: () => state.handleWhatsAppWait(),\n onWhatsAppLogout: () => state.handleWhatsAppLogout(),\n onConfigPatch: (path, value) => updateConfigFormValue(state, path, value),\n onConfigSave: () => state.handleChannelConfigSave(),\n onConfigReload: () => state.handleChannelConfigReload(),\n onNostrProfileEdit: (accountId, profile) =>\n state.handleNostrProfileEdit(accountId, profile),\n onNostrProfileCancel: () => state.handleNostrProfileCancel(),\n onNostrProfileFieldChange: (field, value) =>\n state.handleNostrProfileFieldChange(field, value),\n onNostrProfileSave: () => state.handleNostrProfileSave(),\n onNostrProfileImport: () => state.handleNostrProfileImport(),\n onNostrProfileToggleAdvanced: () => state.handleNostrProfileToggleAdvanced(),\n })\n : nothing}\n\n ${state.tab === \"instances\"\n ? renderInstances({\n loading: state.presenceLoading,\n entries: state.presenceEntries,\n lastError: state.presenceError,\n statusMessage: state.presenceStatus,\n onRefresh: () => loadPresence(state),\n })\n : nothing}\n\n ${state.tab === \"sessions\"\n ? renderSessions({\n loading: state.sessionsLoading,\n result: state.sessionsResult,\n error: state.sessionsError,\n activeMinutes: state.sessionsFilterActive,\n limit: state.sessionsFilterLimit,\n includeGlobal: state.sessionsIncludeGlobal,\n includeUnknown: state.sessionsIncludeUnknown,\n basePath: state.basePath,\n onFiltersChange: (next) => {\n state.sessionsFilterActive = next.activeMinutes;\n state.sessionsFilterLimit = next.limit;\n state.sessionsIncludeGlobal = next.includeGlobal;\n state.sessionsIncludeUnknown = next.includeUnknown;\n\t },\n\t onRefresh: () => loadSessions(state),\n\t onPatch: (key, patch) => patchSession(state, key, patch),\n\t onDelete: (key) => deleteSession(state, key),\n\t })\n\t : nothing}\n\n ${state.tab === \"cron\"\n ? renderCron({\n loading: state.cronLoading,\n status: state.cronStatus,\n jobs: state.cronJobs,\n error: state.cronError,\n busy: state.cronBusy,\n form: state.cronForm,\n channels: state.channelsSnapshot?.channelMeta?.length\n ? state.channelsSnapshot.channelMeta.map((entry) => entry.id)\n : state.channelsSnapshot?.channelOrder ?? [],\n channelLabels: state.channelsSnapshot?.channelLabels ?? {},\n channelMeta: state.channelsSnapshot?.channelMeta ?? [],\n runsJobId: state.cronRunsJobId,\n runs: state.cronRuns,\n onFormChange: (patch) => (state.cronForm = { ...state.cronForm, ...patch }),\n onRefresh: () => state.loadCron(),\n onAdd: () => addCronJob(state),\n onToggle: (job, enabled) => toggleCronJob(state, job, enabled),\n onRun: (job) => runCronJob(state, job),\n onRemove: (job) => removeCronJob(state, job),\n onLoadRuns: (jobId) => loadCronRuns(state, jobId),\n })\n : nothing}\n\n ${state.tab === \"skills\"\n ? renderSkills({\n loading: state.skillsLoading,\n report: state.skillsReport,\n error: state.skillsError,\n filter: state.skillsFilter,\n edits: state.skillEdits,\n messages: state.skillMessages,\n busyKey: state.skillsBusyKey,\n onFilterChange: (next) => (state.skillsFilter = next),\n onRefresh: () => loadSkills(state, { clearMessages: true }),\n onToggle: (key, enabled) => updateSkillEnabled(state, key, enabled),\n onEdit: (key, value) => updateSkillEdit(state, key, value),\n onSaveKey: (key) => saveSkillApiKey(state, key),\n onInstall: (skillKey, name, installId) =>\n installSkill(state, skillKey, name, installId),\n })\n : nothing}\n\n ${state.tab === \"nodes\"\n ? renderNodes({\n loading: state.nodesLoading,\n nodes: state.nodes,\n devicesLoading: state.devicesLoading,\n devicesError: state.devicesError,\n devicesList: state.devicesList,\n configForm: state.configForm ?? (state.configSnapshot?.config as Record | null),\n configLoading: state.configLoading,\n configSaving: state.configSaving,\n configDirty: state.configFormDirty,\n configFormMode: state.configFormMode,\n execApprovalsLoading: state.execApprovalsLoading,\n execApprovalsSaving: state.execApprovalsSaving,\n execApprovalsDirty: state.execApprovalsDirty,\n execApprovalsSnapshot: state.execApprovalsSnapshot,\n execApprovalsForm: state.execApprovalsForm,\n execApprovalsSelectedAgent: state.execApprovalsSelectedAgent,\n execApprovalsTarget: state.execApprovalsTarget,\n execApprovalsTargetNodeId: state.execApprovalsTargetNodeId,\n onRefresh: () => loadNodes(state),\n onDevicesRefresh: () => loadDevices(state),\n onDeviceApprove: (requestId) => approveDevicePairing(state, requestId),\n onDeviceReject: (requestId) => rejectDevicePairing(state, requestId),\n onDeviceRotate: (deviceId, role, scopes) =>\n rotateDeviceToken(state, { deviceId, role, scopes }),\n onDeviceRevoke: (deviceId, role) =>\n revokeDeviceToken(state, { deviceId, role }),\n onLoadConfig: () => loadConfig(state),\n onLoadExecApprovals: () => {\n const target =\n state.execApprovalsTarget === \"node\" && state.execApprovalsTargetNodeId\n ? { kind: \"node\" as const, nodeId: state.execApprovalsTargetNodeId }\n : { kind: \"gateway\" as const };\n return loadExecApprovals(state, target);\n },\n onBindDefault: (nodeId) => {\n if (nodeId) {\n updateConfigFormValue(state, [\"tools\", \"exec\", \"node\"], nodeId);\n } else {\n removeConfigFormValue(state, [\"tools\", \"exec\", \"node\"]);\n }\n },\n onBindAgent: (agentIndex, nodeId) => {\n const basePath = [\"agents\", \"list\", agentIndex, \"tools\", \"exec\", \"node\"];\n if (nodeId) {\n updateConfigFormValue(state, basePath, nodeId);\n } else {\n removeConfigFormValue(state, basePath);\n }\n },\n onSaveBindings: () => saveConfig(state),\n onExecApprovalsTargetChange: (kind, nodeId) => {\n state.execApprovalsTarget = kind;\n state.execApprovalsTargetNodeId = nodeId;\n state.execApprovalsSnapshot = null;\n state.execApprovalsForm = null;\n state.execApprovalsDirty = false;\n state.execApprovalsSelectedAgent = null;\n },\n onExecApprovalsSelectAgent: (agentId) => {\n state.execApprovalsSelectedAgent = agentId;\n },\n onExecApprovalsPatch: (path, value) =>\n updateExecApprovalsFormValue(state, path, value),\n onExecApprovalsRemove: (path) =>\n removeExecApprovalsFormValue(state, path),\n onSaveExecApprovals: () => {\n const target =\n state.execApprovalsTarget === \"node\" && state.execApprovalsTargetNodeId\n ? { kind: \"node\" as const, nodeId: state.execApprovalsTargetNodeId }\n : { kind: \"gateway\" as const };\n return saveExecApprovals(state, target);\n },\n })\n : nothing}\n\n ${state.tab === \"chat\"\n ? renderChat({\n sessionKey: state.sessionKey,\n onSessionKeyChange: (next) => {\n state.sessionKey = next;\n state.chatMessage = \"\";\n state.chatStream = null;\n state.chatStreamStartedAt = null;\n state.chatRunId = null;\n state.chatQueue = [];\n state.resetToolStream();\n state.resetChatScroll();\n state.applySettings({\n ...state.settings,\n sessionKey: next,\n lastActiveSessionKey: next,\n });\n void state.loadAssistantIdentity();\n void loadChatHistory(state);\n void refreshChatAvatar(state);\n },\n thinkingLevel: state.chatThinkingLevel,\n showThinking,\n loading: state.chatLoading,\n sending: state.chatSending,\n assistantAvatarUrl: chatAvatarUrl,\n messages: state.chatMessages,\n toolMessages: state.chatToolMessages,\n stream: state.chatStream,\n streamStartedAt: state.chatStreamStartedAt,\n draft: state.chatMessage,\n queue: state.chatQueue,\n connected: state.connected,\n canSend: state.connected,\n disabledReason: chatDisabledReason,\n error: state.lastError,\n sessions: state.sessionsResult,\n focusMode: chatFocus,\n onRefresh: () => {\n state.resetToolStream();\n return Promise.all([loadChatHistory(state), refreshChatAvatar(state)]);\n },\n onToggleFocusMode: () => {\n if (state.onboarding) return;\n state.applySettings({\n ...state.settings,\n chatFocusMode: !state.settings.chatFocusMode,\n });\n },\n onChatScroll: (event) => state.handleChatScroll(event),\n onDraftChange: (next) => (state.chatMessage = next),\n onSend: () => state.handleSendChat(),\n canAbort: Boolean(state.chatRunId),\n onAbort: () => void state.handleAbortChat(),\n onQueueRemove: (id) => state.removeQueuedMessage(id),\n onNewSession: () =>\n state.handleSendChat(\"/new\", { restoreDraft: true }),\n // Sidebar props for tool output viewing\n sidebarOpen: state.sidebarOpen,\n sidebarContent: state.sidebarContent,\n sidebarError: state.sidebarError,\n splitRatio: state.splitRatio,\n onOpenSidebar: (content: string) => state.handleOpenSidebar(content),\n onCloseSidebar: () => state.handleCloseSidebar(),\n onSplitRatioChange: (ratio: number) => state.handleSplitRatioChange(ratio),\n assistantName: state.assistantName,\n assistantAvatar: state.assistantAvatar,\n })\n : nothing}\n\n ${state.tab === \"config\"\n ? renderConfig({\n raw: state.configRaw,\n valid: state.configValid,\n issues: state.configIssues,\n loading: state.configLoading,\n saving: state.configSaving,\n applying: state.configApplying,\n updating: state.updateRunning,\n connected: state.connected,\n schema: state.configSchema,\n schemaLoading: state.configSchemaLoading,\n uiHints: state.configUiHints,\n formMode: state.configFormMode,\n formValue: state.configForm,\n originalValue: state.configFormOriginal,\n searchQuery: state.configSearchQuery,\n activeSection: state.configActiveSection,\n activeSubsection: state.configActiveSubsection,\n onRawChange: (next) => (state.configRaw = next),\n onFormModeChange: (mode) => (state.configFormMode = mode),\n onFormPatch: (path, value) => updateConfigFormValue(state, path, value),\n onSearchChange: (query) => (state.configSearchQuery = query),\n onSectionChange: (section) => {\n state.configActiveSection = section;\n state.configActiveSubsection = null;\n },\n onSubsectionChange: (section) => (state.configActiveSubsection = section),\n onReload: () => loadConfig(state),\n onSave: () => saveConfig(state),\n onApply: () => applyConfig(state),\n onUpdate: () => runUpdate(state),\n })\n : nothing}\n\n ${state.tab === \"debug\"\n ? renderDebug({\n loading: state.debugLoading,\n status: state.debugStatus,\n health: state.debugHealth,\n models: state.debugModels,\n heartbeat: state.debugHeartbeat,\n eventLog: state.eventLog,\n callMethod: state.debugCallMethod,\n callParams: state.debugCallParams,\n callResult: state.debugCallResult,\n callError: state.debugCallError,\n onCallMethodChange: (next) => (state.debugCallMethod = next),\n onCallParamsChange: (next) => (state.debugCallParams = next),\n onRefresh: () => loadDebug(state),\n onCall: () => callDebugMethod(state),\n })\n : nothing}\n\n ${state.tab === \"logs\"\n ? renderLogs({\n loading: state.logsLoading,\n error: state.logsError,\n file: state.logsFile,\n entries: state.logsEntries,\n filterText: state.logsFilterText,\n levelFilters: state.logsLevelFilters,\n autoFollow: state.logsAutoFollow,\n truncated: state.logsTruncated,\n onFilterTextChange: (next) => (state.logsFilterText = next),\n onLevelToggle: (level, enabled) => {\n state.logsLevelFilters = { ...state.logsLevelFilters, [level]: enabled };\n },\n onToggleAutoFollow: (next) => (state.logsAutoFollow = next),\n onRefresh: () => loadLogs(state, { reset: true }),\n onExport: (lines, label) => state.exportLogs(lines, label),\n onScroll: (event) => state.handleLogsScroll(event),\n })\n : nothing}\n
    \n ${renderExecApprovalPrompt(state)}\n
    \n `;\n}\n","import type { LogLevel } from \"./types\";\nimport type { CronFormState } from \"./ui-types\";\n\nexport const DEFAULT_LOG_LEVEL_FILTERS: Record = {\n trace: true,\n debug: true,\n info: true,\n warn: true,\n error: true,\n fatal: true,\n};\n\nexport const DEFAULT_CRON_FORM: CronFormState = {\n name: \"\",\n description: \"\",\n agentId: \"\",\n enabled: true,\n scheduleKind: \"every\",\n scheduleAt: \"\",\n everyAmount: \"30\",\n everyUnit: \"minutes\",\n cronExpr: \"0 7 * * *\",\n cronTz: \"\",\n sessionTarget: \"main\",\n wakeMode: \"next-heartbeat\",\n payloadKind: \"systemEvent\",\n payloadText: \"\",\n deliver: false,\n channel: \"last\",\n to: \"\",\n timeoutSeconds: \"\",\n postToMainPrefix: \"\",\n};\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport type { AgentsListResult } from \"../types\";\n\nexport type AgentsState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n agentsLoading: boolean;\n agentsError: string | null;\n agentsList: AgentsListResult | null;\n};\n\nexport async function loadAgents(state: AgentsState) {\n if (!state.client || !state.connected) return;\n if (state.agentsLoading) return;\n state.agentsLoading = true;\n state.agentsError = null;\n try {\n const res = (await state.client.request(\"agents.list\", {})) as AgentsListResult | undefined;\n if (res) state.agentsList = res;\n } catch (err) {\n state.agentsError = String(err);\n } finally {\n state.agentsLoading = false;\n }\n}\n","export const GATEWAY_CLIENT_IDS = {\n WEBCHAT_UI: \"webchat-ui\",\n CONTROL_UI: \"clawdbot-control-ui\",\n WEBCHAT: \"webchat\",\n CLI: \"cli\",\n GATEWAY_CLIENT: \"gateway-client\",\n MACOS_APP: \"clawdbot-macos\",\n IOS_APP: \"clawdbot-ios\",\n ANDROID_APP: \"clawdbot-android\",\n NODE_HOST: \"node-host\",\n TEST: \"test\",\n FINGERPRINT: \"fingerprint\",\n PROBE: \"clawdbot-probe\",\n} as const;\n\nexport type GatewayClientId = (typeof GATEWAY_CLIENT_IDS)[keyof typeof GATEWAY_CLIENT_IDS];\n\n// Back-compat naming (internal): these values are IDs, not display names.\nexport const GATEWAY_CLIENT_NAMES = GATEWAY_CLIENT_IDS;\nexport type GatewayClientName = GatewayClientId;\n\nexport const GATEWAY_CLIENT_MODES = {\n WEBCHAT: \"webchat\",\n CLI: \"cli\",\n UI: \"ui\",\n BACKEND: \"backend\",\n NODE: \"node\",\n PROBE: \"probe\",\n TEST: \"test\",\n} as const;\n\nexport type GatewayClientMode = (typeof GATEWAY_CLIENT_MODES)[keyof typeof GATEWAY_CLIENT_MODES];\n\nexport type GatewayClientInfo = {\n id: GatewayClientId;\n displayName?: string;\n version: string;\n platform: string;\n deviceFamily?: string;\n modelIdentifier?: string;\n mode: GatewayClientMode;\n instanceId?: string;\n};\n\nconst GATEWAY_CLIENT_ID_SET = new Set(Object.values(GATEWAY_CLIENT_IDS));\nconst GATEWAY_CLIENT_MODE_SET = new Set(Object.values(GATEWAY_CLIENT_MODES));\n\nexport function normalizeGatewayClientId(raw?: string | null): GatewayClientId | undefined {\n const normalized = raw?.trim().toLowerCase();\n if (!normalized) return undefined;\n return GATEWAY_CLIENT_ID_SET.has(normalized as GatewayClientId)\n ? (normalized as GatewayClientId)\n : undefined;\n}\n\nexport function normalizeGatewayClientName(raw?: string | null): GatewayClientName | undefined {\n return normalizeGatewayClientId(raw);\n}\n\nexport function normalizeGatewayClientMode(raw?: string | null): GatewayClientMode | undefined {\n const normalized = raw?.trim().toLowerCase();\n if (!normalized) return undefined;\n return GATEWAY_CLIENT_MODE_SET.has(normalized as GatewayClientMode)\n ? (normalized as GatewayClientMode)\n : undefined;\n}\n","export type DeviceAuthPayloadParams = {\n deviceId: string;\n clientId: string;\n clientMode: string;\n role: string;\n scopes: string[];\n signedAtMs: number;\n token?: string | null;\n nonce?: string | null;\n version?: \"v1\" | \"v2\";\n};\n\nexport function buildDeviceAuthPayload(params: DeviceAuthPayloadParams): string {\n const version = params.version ?? (params.nonce ? \"v2\" : \"v1\");\n const scopes = params.scopes.join(\",\");\n const token = params.token ?? \"\";\n const base = [\n version,\n params.deviceId,\n params.clientId,\n params.clientMode,\n params.role,\n scopes,\n String(params.signedAtMs),\n token,\n ];\n if (version === \"v2\") {\n base.push(params.nonce ?? \"\");\n }\n return base.join(\"|\");\n}\n","import { generateUUID } from \"./uuid\";\nimport {\n GATEWAY_CLIENT_MODES,\n GATEWAY_CLIENT_NAMES,\n type GatewayClientMode,\n type GatewayClientName,\n} from \"../../../src/gateway/protocol/client-info.js\";\nimport { buildDeviceAuthPayload } from \"../../../src/gateway/device-auth.js\";\nimport { loadOrCreateDeviceIdentity, signDevicePayload } from \"./device-identity\";\nimport { clearDeviceAuthToken, loadDeviceAuthToken, storeDeviceAuthToken } from \"./device-auth\";\n\nexport type GatewayEventFrame = {\n type: \"event\";\n event: string;\n payload?: unknown;\n seq?: number;\n stateVersion?: { presence: number; health: number };\n};\n\nexport type GatewayResponseFrame = {\n type: \"res\";\n id: string;\n ok: boolean;\n payload?: unknown;\n error?: { code: string; message: string; details?: unknown };\n};\n\nexport type GatewayHelloOk = {\n type: \"hello-ok\";\n protocol: number;\n features?: { methods?: string[]; events?: string[] };\n snapshot?: unknown;\n auth?: {\n deviceToken?: string;\n role?: string;\n scopes?: string[];\n issuedAtMs?: number;\n };\n policy?: { tickIntervalMs?: number };\n};\n\ntype Pending = {\n resolve: (value: unknown) => void;\n reject: (err: unknown) => void;\n};\n\nexport type GatewayBrowserClientOptions = {\n url: string;\n token?: string;\n password?: string;\n clientName?: GatewayClientName;\n clientVersion?: string;\n platform?: string;\n mode?: GatewayClientMode;\n instanceId?: string;\n onHello?: (hello: GatewayHelloOk) => void;\n onEvent?: (evt: GatewayEventFrame) => void;\n onClose?: (info: { code: number; reason: string }) => void;\n onGap?: (info: { expected: number; received: number }) => void;\n};\n\n// 4008 = application-defined code (browser rejects 1008 \"Policy Violation\")\nconst CONNECT_FAILED_CLOSE_CODE = 4008;\n\nexport class GatewayBrowserClient {\n private ws: WebSocket | null = null;\n private pending = new Map();\n private closed = false;\n private lastSeq: number | null = null;\n private connectNonce: string | null = null;\n private connectSent = false;\n private connectTimer: number | null = null;\n private backoffMs = 800;\n\n constructor(private opts: GatewayBrowserClientOptions) {}\n\n start() {\n this.closed = false;\n this.connect();\n }\n\n stop() {\n this.closed = true;\n this.ws?.close();\n this.ws = null;\n this.flushPending(new Error(\"gateway client stopped\"));\n }\n\n get connected() {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n private connect() {\n if (this.closed) return;\n this.ws = new WebSocket(this.opts.url);\n this.ws.onopen = () => this.queueConnect();\n this.ws.onmessage = (ev) => this.handleMessage(String(ev.data ?? \"\"));\n this.ws.onclose = (ev) => {\n const reason = String(ev.reason ?? \"\");\n this.ws = null;\n this.flushPending(new Error(`gateway closed (${ev.code}): ${reason}`));\n this.opts.onClose?.({ code: ev.code, reason });\n this.scheduleReconnect();\n };\n this.ws.onerror = () => {\n // ignored; close handler will fire\n };\n }\n\n private scheduleReconnect() {\n if (this.closed) return;\n const delay = this.backoffMs;\n this.backoffMs = Math.min(this.backoffMs * 1.7, 15_000);\n window.setTimeout(() => this.connect(), delay);\n }\n\n private flushPending(err: Error) {\n for (const [, p] of this.pending) p.reject(err);\n this.pending.clear();\n }\n\n private async sendConnect() {\n if (this.connectSent) return;\n this.connectSent = true;\n if (this.connectTimer !== null) {\n window.clearTimeout(this.connectTimer);\n this.connectTimer = null;\n }\n\n // crypto.subtle is only available in secure contexts (HTTPS, localhost).\n // Over plain HTTP, we skip device identity and fall back to token-only auth.\n // Gateways may reject this unless gateway.controlUi.allowInsecureAuth is enabled.\n const isSecureContext = typeof crypto !== \"undefined\" && !!crypto.subtle;\n\n const scopes = [\"operator.admin\", \"operator.approvals\", \"operator.pairing\"];\n const role = \"operator\";\n let deviceIdentity: Awaited> | null = null;\n let canFallbackToShared = false;\n let authToken = this.opts.token;\n\n if (isSecureContext) {\n deviceIdentity = await loadOrCreateDeviceIdentity();\n const storedToken = loadDeviceAuthToken({\n deviceId: deviceIdentity.deviceId,\n role,\n })?.token;\n authToken = storedToken ?? this.opts.token;\n canFallbackToShared = Boolean(storedToken && this.opts.token);\n }\n const auth =\n authToken || this.opts.password\n ? {\n token: authToken,\n password: this.opts.password,\n }\n : undefined;\n\n let device:\n | {\n id: string;\n publicKey: string;\n signature: string;\n signedAt: number;\n nonce: string | undefined;\n }\n | undefined;\n\n if (isSecureContext && deviceIdentity) {\n const signedAtMs = Date.now();\n const nonce = this.connectNonce ?? undefined;\n const payload = buildDeviceAuthPayload({\n deviceId: deviceIdentity.deviceId,\n clientId: this.opts.clientName ?? GATEWAY_CLIENT_NAMES.CONTROL_UI,\n clientMode: this.opts.mode ?? GATEWAY_CLIENT_MODES.WEBCHAT,\n role,\n scopes,\n signedAtMs,\n token: authToken ?? null,\n nonce,\n });\n const signature = await signDevicePayload(deviceIdentity.privateKey, payload);\n device = {\n id: deviceIdentity.deviceId,\n publicKey: deviceIdentity.publicKey,\n signature,\n signedAt: signedAtMs,\n nonce,\n };\n }\n const params = {\n minProtocol: 3,\n maxProtocol: 3,\n client: {\n id: this.opts.clientName ?? GATEWAY_CLIENT_NAMES.CONTROL_UI,\n version: this.opts.clientVersion ?? \"dev\",\n platform: this.opts.platform ?? navigator.platform ?? \"web\",\n mode: this.opts.mode ?? GATEWAY_CLIENT_MODES.WEBCHAT,\n instanceId: this.opts.instanceId,\n },\n role,\n scopes,\n device,\n caps: [],\n auth,\n userAgent: navigator.userAgent,\n locale: navigator.language,\n };\n\n void this.request(\"connect\", params)\n .then((hello) => {\n if (hello?.auth?.deviceToken && deviceIdentity) {\n storeDeviceAuthToken({\n deviceId: deviceIdentity.deviceId,\n role: hello.auth.role ?? role,\n token: hello.auth.deviceToken,\n scopes: hello.auth.scopes ?? [],\n });\n }\n this.backoffMs = 800;\n this.opts.onHello?.(hello);\n })\n .catch(() => {\n if (canFallbackToShared && deviceIdentity) {\n clearDeviceAuthToken({ deviceId: deviceIdentity.deviceId, role });\n }\n this.ws?.close(CONNECT_FAILED_CLOSE_CODE, \"connect failed\");\n });\n }\n\n private handleMessage(raw: string) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return;\n }\n\n const frame = parsed as { type?: unknown };\n if (frame.type === \"event\") {\n const evt = parsed as GatewayEventFrame;\n if (evt.event === \"connect.challenge\") {\n const payload = evt.payload as { nonce?: unknown } | undefined;\n const nonce = payload && typeof payload.nonce === \"string\" ? payload.nonce : null;\n if (nonce) {\n this.connectNonce = nonce;\n void this.sendConnect();\n }\n return;\n }\n const seq = typeof evt.seq === \"number\" ? evt.seq : null;\n if (seq !== null) {\n if (this.lastSeq !== null && seq > this.lastSeq + 1) {\n this.opts.onGap?.({ expected: this.lastSeq + 1, received: seq });\n }\n this.lastSeq = seq;\n }\n this.opts.onEvent?.(evt);\n return;\n }\n\n if (frame.type === \"res\") {\n const res = parsed as GatewayResponseFrame;\n const pending = this.pending.get(res.id);\n if (!pending) return;\n this.pending.delete(res.id);\n if (res.ok) pending.resolve(res.payload);\n else pending.reject(new Error(res.error?.message ?? \"request failed\"));\n return;\n }\n }\n\n request(method: string, params?: unknown): Promise {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return Promise.reject(new Error(\"gateway not connected\"));\n }\n const id = generateUUID();\n const frame = { type: \"req\", id, method, params };\n const p = new Promise((resolve, reject) => {\n this.pending.set(id, { resolve: (v) => resolve(v as T), reject });\n });\n this.ws.send(JSON.stringify(frame));\n return p;\n }\n\n private queueConnect() {\n this.connectNonce = null;\n this.connectSent = false;\n if (this.connectTimer !== null) window.clearTimeout(this.connectTimer);\n this.connectTimer = window.setTimeout(() => {\n void this.sendConnect();\n }, 750);\n }\n}\n","export type ExecApprovalRequestPayload = {\n command: string;\n cwd?: string | null;\n host?: string | null;\n security?: string | null;\n ask?: string | null;\n agentId?: string | null;\n resolvedPath?: string | null;\n sessionKey?: string | null;\n};\n\nexport type ExecApprovalRequest = {\n id: string;\n request: ExecApprovalRequestPayload;\n createdAtMs: number;\n expiresAtMs: number;\n};\n\nexport type ExecApprovalResolved = {\n id: string;\n decision?: string | null;\n resolvedBy?: string | null;\n ts?: number | null;\n};\n\nfunction isRecord(value: unknown): value is Record {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function parseExecApprovalRequested(payload: unknown): ExecApprovalRequest | null {\n if (!isRecord(payload)) return null;\n const id = typeof payload.id === \"string\" ? payload.id.trim() : \"\";\n const request = payload.request;\n if (!id || !isRecord(request)) return null;\n const command = typeof request.command === \"string\" ? request.command.trim() : \"\";\n if (!command) return null;\n const createdAtMs = typeof payload.createdAtMs === \"number\" ? payload.createdAtMs : 0;\n const expiresAtMs = typeof payload.expiresAtMs === \"number\" ? payload.expiresAtMs : 0;\n if (!createdAtMs || !expiresAtMs) return null;\n return {\n id,\n request: {\n command,\n cwd: typeof request.cwd === \"string\" ? request.cwd : null,\n host: typeof request.host === \"string\" ? request.host : null,\n security: typeof request.security === \"string\" ? request.security : null,\n ask: typeof request.ask === \"string\" ? request.ask : null,\n agentId: typeof request.agentId === \"string\" ? request.agentId : null,\n resolvedPath: typeof request.resolvedPath === \"string\" ? request.resolvedPath : null,\n sessionKey: typeof request.sessionKey === \"string\" ? request.sessionKey : null,\n },\n createdAtMs,\n expiresAtMs,\n };\n}\n\nexport function parseExecApprovalResolved(payload: unknown): ExecApprovalResolved | null {\n if (!isRecord(payload)) return null;\n const id = typeof payload.id === \"string\" ? payload.id.trim() : \"\";\n if (!id) return null;\n return {\n id,\n decision: typeof payload.decision === \"string\" ? payload.decision : null,\n resolvedBy: typeof payload.resolvedBy === \"string\" ? payload.resolvedBy : null,\n ts: typeof payload.ts === \"number\" ? payload.ts : null,\n };\n}\n\nexport function pruneExecApprovalQueue(queue: ExecApprovalRequest[]): ExecApprovalRequest[] {\n const now = Date.now();\n return queue.filter((entry) => entry.expiresAtMs > now);\n}\n\nexport function addExecApproval(\n queue: ExecApprovalRequest[],\n entry: ExecApprovalRequest,\n): ExecApprovalRequest[] {\n const next = pruneExecApprovalQueue(queue).filter((item) => item.id !== entry.id);\n next.push(entry);\n return next;\n}\n\nexport function removeExecApproval(queue: ExecApprovalRequest[], id: string): ExecApprovalRequest[] {\n return pruneExecApprovalQueue(queue).filter((entry) => entry.id !== id);\n}\n","import type { GatewayBrowserClient } from \"../gateway\";\nimport {\n normalizeAssistantIdentity,\n type AssistantIdentity,\n} from \"../assistant-identity\";\n\nexport type AssistantIdentityState = {\n client: GatewayBrowserClient | null;\n connected: boolean;\n sessionKey: string;\n assistantName: string;\n assistantAvatar: string | null;\n assistantAgentId: string | null;\n};\n\nexport async function loadAssistantIdentity(\n state: AssistantIdentityState,\n opts?: { sessionKey?: string },\n) {\n if (!state.client || !state.connected) return;\n const sessionKey = opts?.sessionKey?.trim() || state.sessionKey.trim();\n const params = sessionKey ? { sessionKey } : {};\n try {\n const res = (await state.client.request(\"agent.identity.get\", params)) as\n | Partial\n | undefined;\n if (!res) return;\n const normalized = normalizeAssistantIdentity(res);\n state.assistantName = normalized.name;\n state.assistantAvatar = normalized.avatar;\n state.assistantAgentId = normalized.agentId ?? null;\n } catch {\n // Ignore errors; keep last known identity.\n }\n}\n","import { loadChatHistory } from \"./controllers/chat\";\nimport { loadDevices } from \"./controllers/devices\";\nimport { loadNodes } from \"./controllers/nodes\";\nimport { loadAgents } from \"./controllers/agents\";\nimport type { GatewayEventFrame, GatewayHelloOk } from \"./gateway\";\nimport { GatewayBrowserClient } from \"./gateway\";\nimport type { EventLogEntry } from \"./app-events\";\nimport type { AgentsListResult, PresenceEntry, HealthSnapshot, StatusSummary } from \"./types\";\nimport type { Tab } from \"./navigation\";\nimport type { UiSettings } from \"./storage\";\nimport { handleAgentEvent, resetToolStream, type AgentEventPayload } from \"./app-tool-stream\";\nimport { flushChatQueueForEvent } from \"./app-chat\";\nimport {\n applySettings,\n loadCron,\n refreshActiveTab,\n setLastActiveSessionKey,\n} from \"./app-settings\";\nimport { handleChatEvent, type ChatEventPayload } from \"./controllers/chat\";\nimport {\n addExecApproval,\n parseExecApprovalRequested,\n parseExecApprovalResolved,\n removeExecApproval,\n} from \"./controllers/exec-approval\";\nimport type { ClawdbotApp } from \"./app\";\nimport type { ExecApprovalRequest } from \"./controllers/exec-approval\";\nimport { loadAssistantIdentity } from \"./controllers/assistant-identity\";\n\ntype GatewayHost = {\n settings: UiSettings;\n password: string;\n client: GatewayBrowserClient | null;\n connected: boolean;\n hello: GatewayHelloOk | null;\n lastError: string | null;\n onboarding?: boolean;\n eventLogBuffer: EventLogEntry[];\n eventLog: EventLogEntry[];\n tab: Tab;\n presenceEntries: PresenceEntry[];\n presenceError: string | null;\n presenceStatus: StatusSummary | null;\n agentsLoading: boolean;\n agentsList: AgentsListResult | null;\n agentsError: string | null;\n debugHealth: HealthSnapshot | null;\n assistantName: string;\n assistantAvatar: string | null;\n assistantAgentId: string | null;\n sessionKey: string;\n chatRunId: string | null;\n execApprovalQueue: ExecApprovalRequest[];\n execApprovalError: string | null;\n};\n\ntype SessionDefaultsSnapshot = {\n defaultAgentId?: string;\n mainKey?: string;\n mainSessionKey?: string;\n scope?: string;\n};\n\nfunction normalizeSessionKeyForDefaults(\n value: string | undefined,\n defaults: SessionDefaultsSnapshot,\n): string {\n const raw = (value ?? \"\").trim();\n const mainSessionKey = defaults.mainSessionKey?.trim();\n if (!mainSessionKey) return raw;\n if (!raw) return mainSessionKey;\n const mainKey = defaults.mainKey?.trim() || \"main\";\n const defaultAgentId = defaults.defaultAgentId?.trim();\n const isAlias =\n raw === \"main\" ||\n raw === mainKey ||\n (defaultAgentId &&\n (raw === `agent:${defaultAgentId}:main` ||\n raw === `agent:${defaultAgentId}:${mainKey}`));\n return isAlias ? mainSessionKey : raw;\n}\n\nfunction applySessionDefaults(host: GatewayHost, defaults?: SessionDefaultsSnapshot) {\n if (!defaults?.mainSessionKey) return;\n const resolvedSessionKey = normalizeSessionKeyForDefaults(host.sessionKey, defaults);\n const resolvedSettingsSessionKey = normalizeSessionKeyForDefaults(\n host.settings.sessionKey,\n defaults,\n );\n const resolvedLastActiveSessionKey = normalizeSessionKeyForDefaults(\n host.settings.lastActiveSessionKey,\n defaults,\n );\n const nextSessionKey = resolvedSessionKey || resolvedSettingsSessionKey || host.sessionKey;\n const nextSettings = {\n ...host.settings,\n sessionKey: resolvedSettingsSessionKey || nextSessionKey,\n lastActiveSessionKey: resolvedLastActiveSessionKey || nextSessionKey,\n };\n const shouldUpdateSettings =\n nextSettings.sessionKey !== host.settings.sessionKey ||\n nextSettings.lastActiveSessionKey !== host.settings.lastActiveSessionKey;\n if (nextSessionKey !== host.sessionKey) {\n host.sessionKey = nextSessionKey;\n }\n if (shouldUpdateSettings) {\n applySettings(host as unknown as Parameters[0], nextSettings);\n }\n}\n\nexport function connectGateway(host: GatewayHost) {\n host.lastError = null;\n host.hello = null;\n host.connected = false;\n host.execApprovalQueue = [];\n host.execApprovalError = null;\n\n host.client?.stop();\n host.client = new GatewayBrowserClient({\n url: host.settings.gatewayUrl,\n token: host.settings.token.trim() ? host.settings.token : undefined,\n password: host.password.trim() ? host.password : undefined,\n clientName: \"clawdbot-control-ui\",\n mode: \"webchat\",\n onHello: (hello) => {\n host.connected = true;\n host.hello = hello;\n applySnapshot(host, hello);\n void loadAssistantIdentity(host as unknown as ClawdbotApp);\n void loadAgents(host as unknown as ClawdbotApp);\n void loadNodes(host as unknown as ClawdbotApp, { quiet: true });\n void loadDevices(host as unknown as ClawdbotApp, { quiet: true });\n void refreshActiveTab(host as unknown as Parameters[0]);\n },\n onClose: ({ code, reason }) => {\n host.connected = false;\n host.lastError = `disconnected (${code}): ${reason || \"no reason\"}`;\n },\n onEvent: (evt) => handleGatewayEvent(host, evt),\n onGap: ({ expected, received }) => {\n host.lastError = `event gap detected (expected seq ${expected}, got ${received}); refresh recommended`;\n },\n });\n host.client.start();\n}\n\nexport function handleGatewayEvent(host: GatewayHost, evt: GatewayEventFrame) {\n host.eventLogBuffer = [\n { ts: Date.now(), event: evt.event, payload: evt.payload },\n ...host.eventLogBuffer,\n ].slice(0, 250);\n if (host.tab === \"debug\") {\n host.eventLog = host.eventLogBuffer;\n }\n\n if (evt.event === \"agent\") {\n if (host.onboarding) return;\n handleAgentEvent(\n host as unknown as Parameters[0],\n evt.payload as AgentEventPayload | undefined,\n );\n return;\n }\n\n if (evt.event === \"chat\") {\n const payload = evt.payload as ChatEventPayload | undefined;\n if (payload?.sessionKey) {\n setLastActiveSessionKey(\n host as unknown as Parameters[0],\n payload.sessionKey,\n );\n }\n const state = handleChatEvent(host as unknown as ClawdbotApp, payload);\n if (state === \"final\" || state === \"error\" || state === \"aborted\") {\n resetToolStream(host as unknown as Parameters[0]);\n void flushChatQueueForEvent(\n host as unknown as Parameters[0],\n );\n }\n if (state === \"final\") void loadChatHistory(host as unknown as ClawdbotApp);\n return;\n }\n\n if (evt.event === \"presence\") {\n const payload = evt.payload as { presence?: PresenceEntry[] } | undefined;\n if (payload?.presence && Array.isArray(payload.presence)) {\n host.presenceEntries = payload.presence;\n host.presenceError = null;\n host.presenceStatus = null;\n }\n return;\n }\n\n if (evt.event === \"cron\" && host.tab === \"cron\") {\n void loadCron(host as unknown as Parameters[0]);\n }\n\n if (evt.event === \"device.pair.requested\" || evt.event === \"device.pair.resolved\") {\n void loadDevices(host as unknown as ClawdbotApp, { quiet: true });\n }\n\n if (evt.event === \"exec.approval.requested\") {\n const entry = parseExecApprovalRequested(evt.payload);\n if (entry) {\n host.execApprovalQueue = addExecApproval(host.execApprovalQueue, entry);\n host.execApprovalError = null;\n const delay = Math.max(0, entry.expiresAtMs - Date.now() + 500);\n window.setTimeout(() => {\n host.execApprovalQueue = removeExecApproval(host.execApprovalQueue, entry.id);\n }, delay);\n }\n return;\n }\n\n if (evt.event === \"exec.approval.resolved\") {\n const resolved = parseExecApprovalResolved(evt.payload);\n if (resolved) {\n host.execApprovalQueue = removeExecApproval(host.execApprovalQueue, resolved.id);\n }\n }\n}\n\nexport function applySnapshot(host: GatewayHost, hello: GatewayHelloOk) {\n const snapshot = hello.snapshot as\n | {\n presence?: PresenceEntry[];\n health?: HealthSnapshot;\n sessionDefaults?: SessionDefaultsSnapshot;\n }\n | undefined;\n if (snapshot?.presence && Array.isArray(snapshot.presence)) {\n host.presenceEntries = snapshot.presence;\n }\n if (snapshot?.health) {\n host.debugHealth = snapshot.health;\n }\n if (snapshot?.sessionDefaults) {\n applySessionDefaults(host, snapshot.sessionDefaults);\n }\n}\n","import type { Tab } from \"./navigation\";\nimport { connectGateway } from \"./app-gateway\";\nimport {\n applySettingsFromUrl,\n attachThemeListener,\n detachThemeListener,\n inferBasePath,\n syncTabWithLocation,\n syncThemeWithSettings,\n} from \"./app-settings\";\nimport { observeTopbar, scheduleChatScroll, scheduleLogsScroll } from \"./app-scroll\";\nimport {\n startLogsPolling,\n startNodesPolling,\n stopLogsPolling,\n stopNodesPolling,\n startDebugPolling,\n stopDebugPolling,\n} from \"./app-polling\";\n\ntype LifecycleHost = {\n basePath: string;\n tab: Tab;\n chatHasAutoScrolled: boolean;\n chatLoading: boolean;\n chatMessages: unknown[];\n chatToolMessages: unknown[];\n chatStream: string;\n logsAutoFollow: boolean;\n logsAtBottom: boolean;\n logsEntries: unknown[];\n popStateHandler: () => void;\n topbarObserver: ResizeObserver | null;\n};\n\nexport function handleConnected(host: LifecycleHost) {\n host.basePath = inferBasePath();\n syncTabWithLocation(\n host as unknown as Parameters[0],\n true,\n );\n syncThemeWithSettings(\n host as unknown as Parameters[0],\n );\n attachThemeListener(\n host as unknown as Parameters[0],\n );\n window.addEventListener(\"popstate\", host.popStateHandler);\n applySettingsFromUrl(\n host as unknown as Parameters[0],\n );\n connectGateway(host as unknown as Parameters[0]);\n startNodesPolling(host as unknown as Parameters[0]);\n if (host.tab === \"logs\") {\n startLogsPolling(host as unknown as Parameters[0]);\n }\n if (host.tab === \"debug\") {\n startDebugPolling(host as unknown as Parameters[0]);\n }\n}\n\nexport function handleFirstUpdated(host: LifecycleHost) {\n observeTopbar(host as unknown as Parameters[0]);\n}\n\nexport function handleDisconnected(host: LifecycleHost) {\n window.removeEventListener(\"popstate\", host.popStateHandler);\n stopNodesPolling(host as unknown as Parameters[0]);\n stopLogsPolling(host as unknown as Parameters[0]);\n stopDebugPolling(host as unknown as Parameters[0]);\n detachThemeListener(\n host as unknown as Parameters[0],\n );\n host.topbarObserver?.disconnect();\n host.topbarObserver = null;\n}\n\nexport function handleUpdated(\n host: LifecycleHost,\n changed: Map,\n) {\n if (\n host.tab === \"chat\" &&\n (changed.has(\"chatMessages\") ||\n changed.has(\"chatToolMessages\") ||\n changed.has(\"chatStream\") ||\n changed.has(\"chatLoading\") ||\n changed.has(\"tab\"))\n ) {\n const forcedByTab = changed.has(\"tab\");\n const forcedByLoad =\n changed.has(\"chatLoading\") &&\n changed.get(\"chatLoading\") === true &&\n host.chatLoading === false;\n scheduleChatScroll(\n host as unknown as Parameters[0],\n forcedByTab || forcedByLoad || !host.chatHasAutoScrolled,\n );\n }\n if (\n host.tab === \"logs\" &&\n (changed.has(\"logsEntries\") || changed.has(\"logsAutoFollow\") || changed.has(\"tab\"))\n ) {\n if (host.logsAutoFollow && host.logsAtBottom) {\n scheduleLogsScroll(\n host as unknown as Parameters[0],\n changed.has(\"tab\") || changed.has(\"logsAutoFollow\"),\n );\n }\n }\n}\n","import {\n loadChannels,\n logoutWhatsApp,\n startWhatsAppLogin,\n waitWhatsAppLogin,\n} from \"./controllers/channels\";\nimport { loadConfig, saveConfig } from \"./controllers/config\";\nimport type { ClawdbotApp } from \"./app\";\nimport type { NostrProfile } from \"./types\";\nimport { createNostrProfileFormState } from \"./views/channels.nostr-profile-form\";\n\nexport async function handleWhatsAppStart(host: ClawdbotApp, force: boolean) {\n await startWhatsAppLogin(host, force);\n await loadChannels(host, true);\n}\n\nexport async function handleWhatsAppWait(host: ClawdbotApp) {\n await waitWhatsAppLogin(host);\n await loadChannels(host, true);\n}\n\nexport async function handleWhatsAppLogout(host: ClawdbotApp) {\n await logoutWhatsApp(host);\n await loadChannels(host, true);\n}\n\nexport async function handleChannelConfigSave(host: ClawdbotApp) {\n await saveConfig(host);\n await loadConfig(host);\n await loadChannels(host, true);\n}\n\nexport async function handleChannelConfigReload(host: ClawdbotApp) {\n await loadConfig(host);\n await loadChannels(host, true);\n}\n\nfunction parseValidationErrors(details: unknown): Record {\n if (!Array.isArray(details)) return {};\n const errors: Record = {};\n for (const entry of details) {\n if (typeof entry !== \"string\") continue;\n const [rawField, ...rest] = entry.split(\":\");\n if (!rawField || rest.length === 0) continue;\n const field = rawField.trim();\n const message = rest.join(\":\").trim();\n if (field && message) errors[field] = message;\n }\n return errors;\n}\n\nfunction resolveNostrAccountId(host: ClawdbotApp): string {\n const accounts = host.channelsSnapshot?.channelAccounts?.nostr ?? [];\n return accounts[0]?.accountId ?? host.nostrProfileAccountId ?? \"default\";\n}\n\nfunction buildNostrProfileUrl(accountId: string, suffix = \"\"): string {\n return `/api/channels/nostr/${encodeURIComponent(accountId)}/profile${suffix}`;\n}\n\nexport function handleNostrProfileEdit(\n host: ClawdbotApp,\n accountId: string,\n profile: NostrProfile | null,\n) {\n host.nostrProfileAccountId = accountId;\n host.nostrProfileFormState = createNostrProfileFormState(profile ?? undefined);\n}\n\nexport function handleNostrProfileCancel(host: ClawdbotApp) {\n host.nostrProfileFormState = null;\n host.nostrProfileAccountId = null;\n}\n\nexport function handleNostrProfileFieldChange(\n host: ClawdbotApp,\n field: keyof NostrProfile,\n value: string,\n) {\n const state = host.nostrProfileFormState;\n if (!state) return;\n host.nostrProfileFormState = {\n ...state,\n values: {\n ...state.values,\n [field]: value,\n },\n fieldErrors: {\n ...state.fieldErrors,\n [field]: \"\",\n },\n };\n}\n\nexport function handleNostrProfileToggleAdvanced(host: ClawdbotApp) {\n const state = host.nostrProfileFormState;\n if (!state) return;\n host.nostrProfileFormState = {\n ...state,\n showAdvanced: !state.showAdvanced,\n };\n}\n\nexport async function handleNostrProfileSave(host: ClawdbotApp) {\n const state = host.nostrProfileFormState;\n if (!state || state.saving) return;\n const accountId = resolveNostrAccountId(host);\n\n host.nostrProfileFormState = {\n ...state,\n saving: true,\n error: null,\n success: null,\n fieldErrors: {},\n };\n\n try {\n const response = await fetch(buildNostrProfileUrl(accountId), {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(state.values),\n });\n const data = (await response.json().catch(() => null)) as\n | { ok?: boolean; error?: string; details?: unknown; persisted?: boolean }\n | null;\n\n if (!response.ok || data?.ok === false || !data) {\n const errorMessage = data?.error ?? `Profile update failed (${response.status})`;\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: errorMessage,\n success: null,\n fieldErrors: parseValidationErrors(data?.details),\n };\n return;\n }\n\n if (!data.persisted) {\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: \"Profile publish failed on all relays.\",\n success: null,\n };\n return;\n }\n\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: null,\n success: \"Profile published to relays.\",\n fieldErrors: {},\n original: { ...state.values },\n };\n await loadChannels(host, true);\n } catch (err) {\n host.nostrProfileFormState = {\n ...state,\n saving: false,\n error: `Profile update failed: ${String(err)}`,\n success: null,\n };\n }\n}\n\nexport async function handleNostrProfileImport(host: ClawdbotApp) {\n const state = host.nostrProfileFormState;\n if (!state || state.importing) return;\n const accountId = resolveNostrAccountId(host);\n\n host.nostrProfileFormState = {\n ...state,\n importing: true,\n error: null,\n success: null,\n };\n\n try {\n const response = await fetch(buildNostrProfileUrl(accountId, \"/import\"), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ autoMerge: true }),\n });\n const data = (await response.json().catch(() => null)) as\n | { ok?: boolean; error?: string; imported?: NostrProfile; merged?: NostrProfile; saved?: boolean }\n | null;\n\n if (!response.ok || data?.ok === false || !data) {\n const errorMessage = data?.error ?? `Profile import failed (${response.status})`;\n host.nostrProfileFormState = {\n ...state,\n importing: false,\n error: errorMessage,\n success: null,\n };\n return;\n }\n\n const merged = data.merged ?? data.imported ?? null;\n const nextValues = merged ? { ...state.values, ...merged } : state.values;\n const showAdvanced = Boolean(\n nextValues.banner || nextValues.website || nextValues.nip05 || nextValues.lud16,\n );\n\n host.nostrProfileFormState = {\n ...state,\n importing: false,\n values: nextValues,\n error: null,\n success: data.saved\n ? \"Profile imported from relays. Review and publish.\"\n : \"Profile imported. Review and publish.\",\n showAdvanced,\n };\n\n if (data.saved) {\n await loadChannels(host, true);\n }\n } catch (err) {\n host.nostrProfileFormState = {\n ...state,\n importing: false,\n error: `Profile import failed: ${String(err)}`,\n success: null,\n };\n }\n}\n","import { LitElement, html, nothing } from \"lit\";\nimport { customElement, state } from \"lit/decorators.js\";\n\nimport type { GatewayBrowserClient, GatewayHelloOk } from \"./gateway\";\nimport { resolveInjectedAssistantIdentity } from \"./assistant-identity\";\nimport { loadSettings, type UiSettings } from \"./storage\";\nimport { renderApp } from \"./app-render\";\nimport type { Tab } from \"./navigation\";\nimport type { ResolvedTheme, ThemeMode } from \"./theme\";\nimport type {\n AgentsListResult,\n ConfigSnapshot,\n ConfigUiHints,\n CronJob,\n CronRunLogEntry,\n CronStatus,\n HealthSnapshot,\n LogEntry,\n LogLevel,\n PresenceEntry,\n ChannelsStatusSnapshot,\n SessionsListResult,\n SkillStatusReport,\n StatusSummary,\n NostrProfile,\n} from \"./types\";\nimport { type ChatQueueItem, type CronFormState } from \"./ui-types\";\nimport type { EventLogEntry } from \"./app-events\";\nimport { DEFAULT_CRON_FORM, DEFAULT_LOG_LEVEL_FILTERS } from \"./app-defaults\";\nimport type {\n ExecApprovalsFile,\n ExecApprovalsSnapshot,\n} from \"./controllers/exec-approvals\";\nimport type { DevicePairingList } from \"./controllers/devices\";\nimport type { ExecApprovalRequest } from \"./controllers/exec-approval\";\nimport {\n resetToolStream as resetToolStreamInternal,\n type ToolStreamEntry,\n} from \"./app-tool-stream\";\nimport {\n exportLogs as exportLogsInternal,\n handleChatScroll as handleChatScrollInternal,\n handleLogsScroll as handleLogsScrollInternal,\n resetChatScroll as resetChatScrollInternal,\n} from \"./app-scroll\";\nimport { connectGateway as connectGatewayInternal } from \"./app-gateway\";\nimport {\n handleConnected,\n handleDisconnected,\n handleFirstUpdated,\n handleUpdated,\n} from \"./app-lifecycle\";\nimport {\n applySettings as applySettingsInternal,\n loadCron as loadCronInternal,\n loadOverview as loadOverviewInternal,\n setTab as setTabInternal,\n setTheme as setThemeInternal,\n onPopState as onPopStateInternal,\n} from \"./app-settings\";\nimport {\n handleAbortChat as handleAbortChatInternal,\n handleSendChat as handleSendChatInternal,\n removeQueuedMessage as removeQueuedMessageInternal,\n} from \"./app-chat\";\nimport {\n handleChannelConfigReload as handleChannelConfigReloadInternal,\n handleChannelConfigSave as handleChannelConfigSaveInternal,\n handleNostrProfileCancel as handleNostrProfileCancelInternal,\n handleNostrProfileEdit as handleNostrProfileEditInternal,\n handleNostrProfileFieldChange as handleNostrProfileFieldChangeInternal,\n handleNostrProfileImport as handleNostrProfileImportInternal,\n handleNostrProfileSave as handleNostrProfileSaveInternal,\n handleNostrProfileToggleAdvanced as handleNostrProfileToggleAdvancedInternal,\n handleWhatsAppLogout as handleWhatsAppLogoutInternal,\n handleWhatsAppStart as handleWhatsAppStartInternal,\n handleWhatsAppWait as handleWhatsAppWaitInternal,\n} from \"./app-channels\";\nimport type { NostrProfileFormState } from \"./views/channels.nostr-profile-form\";\nimport { loadAssistantIdentity as loadAssistantIdentityInternal } from \"./controllers/assistant-identity\";\n\ndeclare global {\n interface Window {\n __CLAWDBOT_CONTROL_UI_BASE_PATH__?: string;\n }\n}\n\nconst injectedAssistantIdentity = resolveInjectedAssistantIdentity();\n\nfunction resolveOnboardingMode(): boolean {\n if (!window.location.search) return false;\n const params = new URLSearchParams(window.location.search);\n const raw = params.get(\"onboarding\");\n if (!raw) return false;\n const normalized = raw.trim().toLowerCase();\n return normalized === \"1\" || normalized === \"true\" || normalized === \"yes\" || normalized === \"on\";\n}\n\n@customElement(\"clawdbot-app\")\nexport class ClawdbotApp extends LitElement {\n @state() settings: UiSettings = loadSettings();\n @state() password = \"\";\n @state() tab: Tab = \"chat\";\n @state() onboarding = resolveOnboardingMode();\n @state() connected = false;\n @state() theme: ThemeMode = this.settings.theme ?? \"system\";\n @state() themeResolved: ResolvedTheme = \"dark\";\n @state() hello: GatewayHelloOk | null = null;\n @state() lastError: string | null = null;\n @state() eventLog: EventLogEntry[] = [];\n private eventLogBuffer: EventLogEntry[] = [];\n private toolStreamSyncTimer: number | null = null;\n private sidebarCloseTimer: number | null = null;\n\n @state() assistantName = injectedAssistantIdentity.name;\n @state() assistantAvatar = injectedAssistantIdentity.avatar;\n @state() assistantAgentId = injectedAssistantIdentity.agentId ?? null;\n\n @state() sessionKey = this.settings.sessionKey;\n @state() chatLoading = false;\n @state() chatSending = false;\n @state() chatMessage = \"\";\n @state() chatMessages: unknown[] = [];\n @state() chatToolMessages: unknown[] = [];\n @state() chatStream: string | null = null;\n @state() chatStreamStartedAt: number | null = null;\n @state() chatRunId: string | null = null;\n @state() chatAvatarUrl: string | null = null;\n @state() chatThinkingLevel: string | null = null;\n @state() chatQueue: ChatQueueItem[] = [];\n // Sidebar state for tool output viewing\n @state() sidebarOpen = false;\n @state() sidebarContent: string | null = null;\n @state() sidebarError: string | null = null;\n @state() splitRatio = this.settings.splitRatio;\n\n @state() nodesLoading = false;\n @state() nodes: Array> = [];\n @state() devicesLoading = false;\n @state() devicesError: string | null = null;\n @state() devicesList: DevicePairingList | null = null;\n @state() execApprovalsLoading = false;\n @state() execApprovalsSaving = false;\n @state() execApprovalsDirty = false;\n @state() execApprovalsSnapshot: ExecApprovalsSnapshot | null = null;\n @state() execApprovalsForm: ExecApprovalsFile | null = null;\n @state() execApprovalsSelectedAgent: string | null = null;\n @state() execApprovalsTarget: \"gateway\" | \"node\" = \"gateway\";\n @state() execApprovalsTargetNodeId: string | null = null;\n @state() execApprovalQueue: ExecApprovalRequest[] = [];\n @state() execApprovalBusy = false;\n @state() execApprovalError: string | null = null;\n\n @state() configLoading = false;\n @state() configRaw = \"{\\n}\\n\";\n @state() configValid: boolean | null = null;\n @state() configIssues: unknown[] = [];\n @state() configSaving = false;\n @state() configApplying = false;\n @state() updateRunning = false;\n @state() applySessionKey = this.settings.lastActiveSessionKey;\n @state() configSnapshot: ConfigSnapshot | null = null;\n @state() configSchema: unknown | null = null;\n @state() configSchemaVersion: string | null = null;\n @state() configSchemaLoading = false;\n @state() configUiHints: ConfigUiHints = {};\n @state() configForm: Record | null = null;\n @state() configFormOriginal: Record | null = null;\n @state() configFormDirty = false;\n @state() configFormMode: \"form\" | \"raw\" = \"form\";\n @state() configSearchQuery = \"\";\n @state() configActiveSection: string | null = null;\n @state() configActiveSubsection: string | null = null;\n\n @state() channelsLoading = false;\n @state() channelsSnapshot: ChannelsStatusSnapshot | null = null;\n @state() channelsError: string | null = null;\n @state() channelsLastSuccess: number | null = null;\n @state() whatsappLoginMessage: string | null = null;\n @state() whatsappLoginQrDataUrl: string | null = null;\n @state() whatsappLoginConnected: boolean | null = null;\n @state() whatsappBusy = false;\n @state() nostrProfileFormState: NostrProfileFormState | null = null;\n @state() nostrProfileAccountId: string | null = null;\n\n @state() presenceLoading = false;\n @state() presenceEntries: PresenceEntry[] = [];\n @state() presenceError: string | null = null;\n @state() presenceStatus: string | null = null;\n\n @state() agentsLoading = false;\n @state() agentsList: AgentsListResult | null = null;\n @state() agentsError: string | null = null;\n\n @state() sessionsLoading = false;\n @state() sessionsResult: SessionsListResult | null = null;\n @state() sessionsError: string | null = null;\n @state() sessionsFilterActive = \"\";\n @state() sessionsFilterLimit = \"120\";\n @state() sessionsIncludeGlobal = true;\n @state() sessionsIncludeUnknown = false;\n\n @state() cronLoading = false;\n @state() cronJobs: CronJob[] = [];\n @state() cronStatus: CronStatus | null = null;\n @state() cronError: string | null = null;\n @state() cronForm: CronFormState = { ...DEFAULT_CRON_FORM };\n @state() cronRunsJobId: string | null = null;\n @state() cronRuns: CronRunLogEntry[] = [];\n @state() cronBusy = false;\n\n @state() skillsLoading = false;\n @state() skillsReport: SkillStatusReport | null = null;\n @state() skillsError: string | null = null;\n @state() skillsFilter = \"\";\n @state() skillEdits: Record = {};\n @state() skillsBusyKey: string | null = null;\n @state() skillMessages: Record = {};\n\n @state() debugLoading = false;\n @state() debugStatus: StatusSummary | null = null;\n @state() debugHealth: HealthSnapshot | null = null;\n @state() debugModels: unknown[] = [];\n @state() debugHeartbeat: unknown | null = null;\n @state() debugCallMethod = \"\";\n @state() debugCallParams = \"{}\";\n @state() debugCallResult: string | null = null;\n @state() debugCallError: string | null = null;\n\n @state() logsLoading = false;\n @state() logsError: string | null = null;\n @state() logsFile: string | null = null;\n @state() logsEntries: LogEntry[] = [];\n @state() logsFilterText = \"\";\n @state() logsLevelFilters: Record = {\n ...DEFAULT_LOG_LEVEL_FILTERS,\n };\n @state() logsAutoFollow = true;\n @state() logsTruncated = false;\n @state() logsCursor: number | null = null;\n @state() logsLastFetchAt: number | null = null;\n @state() logsLimit = 500;\n @state() logsMaxBytes = 250_000;\n @state() logsAtBottom = true;\n\n client: GatewayBrowserClient | null = null;\n private chatScrollFrame: number | null = null;\n private chatScrollTimeout: number | null = null;\n private chatHasAutoScrolled = false;\n private chatUserNearBottom = true;\n private nodesPollInterval: number | null = null;\n private logsPollInterval: number | null = null;\n private debugPollInterval: number | null = null;\n private logsScrollFrame: number | null = null;\n private toolStreamById = new Map();\n private toolStreamOrder: string[] = [];\n basePath = \"\";\n private popStateHandler = () =>\n onPopStateInternal(\n this as unknown as Parameters[0],\n );\n private themeMedia: MediaQueryList | null = null;\n private themeMediaHandler: ((event: MediaQueryListEvent) => void) | null = null;\n private topbarObserver: ResizeObserver | null = null;\n\n createRenderRoot() {\n return this;\n }\n\n connectedCallback() {\n super.connectedCallback();\n handleConnected(this as unknown as Parameters[0]);\n }\n\n protected firstUpdated() {\n handleFirstUpdated(this as unknown as Parameters[0]);\n }\n\n disconnectedCallback() {\n handleDisconnected(this as unknown as Parameters[0]);\n super.disconnectedCallback();\n }\n\n protected updated(changed: Map) {\n handleUpdated(\n this as unknown as Parameters[0],\n changed,\n );\n }\n\n connect() {\n connectGatewayInternal(\n this as unknown as Parameters[0],\n );\n }\n\n handleChatScroll(event: Event) {\n handleChatScrollInternal(\n this as unknown as Parameters[0],\n event,\n );\n }\n\n handleLogsScroll(event: Event) {\n handleLogsScrollInternal(\n this as unknown as Parameters[0],\n event,\n );\n }\n\n exportLogs(lines: string[], label: string) {\n exportLogsInternal(lines, label);\n }\n\n resetToolStream() {\n resetToolStreamInternal(\n this as unknown as Parameters[0],\n );\n }\n\n resetChatScroll() {\n resetChatScrollInternal(\n this as unknown as Parameters[0],\n );\n }\n\n async loadAssistantIdentity() {\n await loadAssistantIdentityInternal(this);\n }\n\n applySettings(next: UiSettings) {\n applySettingsInternal(\n this as unknown as Parameters[0],\n next,\n );\n }\n\n setTab(next: Tab) {\n setTabInternal(this as unknown as Parameters[0], next);\n }\n\n setTheme(next: ThemeMode, context?: Parameters[2]) {\n setThemeInternal(\n this as unknown as Parameters[0],\n next,\n context,\n );\n }\n\n async loadOverview() {\n await loadOverviewInternal(\n this as unknown as Parameters[0],\n );\n }\n\n async loadCron() {\n await loadCronInternal(\n this as unknown as Parameters[0],\n );\n }\n\n async handleAbortChat() {\n await handleAbortChatInternal(\n this as unknown as Parameters[0],\n );\n }\n\n removeQueuedMessage(id: string) {\n removeQueuedMessageInternal(\n this as unknown as Parameters[0],\n id,\n );\n }\n\n async handleSendChat(\n messageOverride?: string,\n opts?: Parameters[2],\n ) {\n await handleSendChatInternal(\n this as unknown as Parameters[0],\n messageOverride,\n opts,\n );\n }\n\n async handleWhatsAppStart(force: boolean) {\n await handleWhatsAppStartInternal(this, force);\n }\n\n async handleWhatsAppWait() {\n await handleWhatsAppWaitInternal(this);\n }\n\n async handleWhatsAppLogout() {\n await handleWhatsAppLogoutInternal(this);\n }\n\n async handleChannelConfigSave() {\n await handleChannelConfigSaveInternal(this);\n }\n\n async handleChannelConfigReload() {\n await handleChannelConfigReloadInternal(this);\n }\n\n handleNostrProfileEdit(accountId: string, profile: NostrProfile | null) {\n handleNostrProfileEditInternal(this, accountId, profile);\n }\n\n handleNostrProfileCancel() {\n handleNostrProfileCancelInternal(this);\n }\n\n handleNostrProfileFieldChange(field: keyof NostrProfile, value: string) {\n handleNostrProfileFieldChangeInternal(this, field, value);\n }\n\n async handleNostrProfileSave() {\n await handleNostrProfileSaveInternal(this);\n }\n\n async handleNostrProfileImport() {\n await handleNostrProfileImportInternal(this);\n }\n\n handleNostrProfileToggleAdvanced() {\n handleNostrProfileToggleAdvancedInternal(this);\n }\n\n async handleExecApprovalDecision(decision: \"allow-once\" | \"allow-always\" | \"deny\") {\n const active = this.execApprovalQueue[0];\n if (!active || !this.client || this.execApprovalBusy) return;\n this.execApprovalBusy = true;\n this.execApprovalError = null;\n try {\n await this.client.request(\"exec.approval.resolve\", {\n id: active.id,\n decision,\n });\n this.execApprovalQueue = this.execApprovalQueue.filter((entry) => entry.id !== active.id);\n } catch (err) {\n this.execApprovalError = `Exec approval failed: ${String(err)}`;\n } finally {\n this.execApprovalBusy = false;\n }\n }\n\n // Sidebar handlers for tool output viewing\n handleOpenSidebar(content: string) {\n if (this.sidebarCloseTimer != null) {\n window.clearTimeout(this.sidebarCloseTimer);\n this.sidebarCloseTimer = null;\n }\n this.sidebarContent = content;\n this.sidebarError = null;\n this.sidebarOpen = true;\n }\n\n handleCloseSidebar() {\n this.sidebarOpen = false;\n // Clear content after transition\n if (this.sidebarCloseTimer != null) {\n window.clearTimeout(this.sidebarCloseTimer);\n }\n this.sidebarCloseTimer = window.setTimeout(() => {\n if (this.sidebarOpen) return;\n this.sidebarContent = null;\n this.sidebarError = null;\n this.sidebarCloseTimer = null;\n }, 200);\n }\n\n handleSplitRatioChange(ratio: number) {\n const newRatio = Math.max(0.4, Math.min(0.7, ratio));\n this.splitRatio = newRatio;\n this.applySettings({ ...this.settings, splitRatio: newRatio });\n }\n\n render() {\n return renderApp(this);\n }\n}\n"],"names":["t","e","s","o","n$3","r","n","i","S","c","h","a","l","p","d","u","f","b","y$2","y","v","_","m","g","$","x","E","A","C","P","V","N","S$1","I","L","z","H","M","R","k","Z","I$2","Z$1","j","B","D","MAX_ASSISTANT_NAME","MAX_ASSISTANT_AVATAR","DEFAULT_ASSISTANT_NAME","coerceIdentityValue","value","maxLength","trimmed","normalizeAssistantIdentity","input","name","avatar","resolveInjectedAssistantIdentity","KEY","loadSettings","defaults","raw","parsed","saveSettings","next","parseAgentSessionKey","sessionKey","parts","agentId","rest","TAB_GROUPS","TAB_PATHS","PATH_TO_TAB","tab","path","normalizeBasePath","basePath","base","normalizePath","normalized","pathForTab","tabFromPath","pathname","inferBasePathFromPathname","segments","candidate","prefix","iconForTab","titleForTab","subtitleForTab","formatMs","ms","formatAgo","diff","sec","min","hr","formatDurationMs","formatList","values","clampText","max","truncateText","toNumber","fallback","THINKING_TAG_RE","THINKING_OPEN_RE","THINKING_CLOSE_RE","stripThinkingTags","hasOpen","hasClose","result","lastIndex","inThinking","match","idx","ENVELOPE_PREFIX","ENVELOPE_CHANNELS","looksLikeEnvelopeHeader","header","label","stripEnvelope","text","extractText","message","role","content","item","joined","extractThinking","cleaned","rawText","extractRawText","extracted","formatReasoningMarkdown","lines","line","uuidFromBytes","bytes","hex","weakRandomBytes","now","generateUUID","cryptoLike","loadChatHistory","state","res","err","sendChatMessage","msg","runId","error","abortChatRun","handleChatEvent","payload","current","loadSessions","params","activeMinutes","limit","patchSession","key","patch","deleteSession","TOOL_STREAM_LIMIT","TOOL_STREAM_THROTTLE_MS","TOOL_OUTPUT_CHAR_LIMIT","extractToolOutputText","record","entry","part","formatToolOutput","contentText","truncated","buildToolStreamMessage","trimToolStream","host","overflow","removed","id","syncToolStreamMessages","flushToolStreamSync","scheduleToolStreamSync","force","resetToolStream","handleAgentEvent","data","toolCallId","phase","args","output","scheduleChatScroll","pickScrollTarget","container","overflowY","target","distanceFromBottom","retryDelay","latest","latestDistanceFromBottom","scheduleLogsScroll","handleChatScroll","event","handleLogsScroll","resetChatScroll","exportLogs","blob","url","anchor","stamp","observeTopbar","topbar","update","height","cloneConfigObject","serializeConfigForm","form","setPathValue","obj","nextKey","lastKey","removePathValue","loadConfig","applyConfigSnapshot","loadConfigSchema","applyConfigSchema","snapshot","rawFromSnapshot","saveConfig","baseHash","applyConfig","runUpdate","updateConfigFormValue","removeConfigFormValue","loadCronStatus","loadCronJobs","buildCronSchedule","amount","unit","expr","buildCronPayload","timeoutSeconds","addCronJob","schedule","job","toggleCronJob","enabled","runCronJob","loadCronRuns","removeCronJob","jobId","loadChannels","probe","startWhatsAppLogin","waitWhatsAppLogin","logoutWhatsApp","loadDebug","status","health","models","heartbeat","modelPayload","callDebugMethod","LOG_BUFFER_LIMIT","LEVELS","parseMaybeJsonString","normalizeLevel","lowered","parseLogLine","meta","time","level","contextCandidate","contextObj","subsystem","loadLogs","opts","entries","shouldReset","ed25519_CURVE","Gx","Gy","_a","_d","L2","captureTrace","isBig","isStr","isBytes","abytes","length","title","len","needsLen","ofLen","got","u8n","u8fr","buf","padh","pad","bytesToHex","_ch","ch","hexToBytes","hl","al","array","ai","hi","n1","n2","cr","subtle","concatBytes","arrs","sum","randomBytes","big","assertRange","modN","invert","num","md","q","callHash","fn","hashes","apoint","Point","B256","X","Y","T","zip215","normed","lastByte","bytesToNumLE","y2","isValid","uvRatio","isXOdd","isLastByteOdd","X2","Y2","Z2","Z4","aX2","left","right","XY","ZT","other","X1","Y1","Z1","X1Z2","X2Z1","Y1Z2","Y2Z1","x1y1","G","F","X3","Y3","T3","Z3","T1","T2","safe","wNAF","scalar","iz","numTo32bLE","pow2","power","pow_2_252_3","b2","b4","b5","b10","b20","b40","b80","b160","b240","b250","RM1","v3","v7","pow","vx2","root1","root2","useRoot1","useRoot2","noRoot","modL_LE","hash","sha512a","sha512s","hash2extK","hashed","head","point","pointBytes","getExtendedPublicKeyAsync","secretKey","getExtendedPublicKey","getPublicKeyAsync","hashFinishA","_sign","rBytes","signAsync","randomSecretKey","seed","utils","W","scalarBits","pwindows","pwindowSize","precompute","points","w","Gpows","ctneg","cnd","comp","pow_2_w","maxNum","mask","shiftBy","wbits","off","offF","offP","isEven","isNeg","STORAGE_KEY","base64UrlEncode","binary","byte","base64UrlDecode","padded","out","fingerprintPublicKey","publicKey","generateIdentity","privateKey","loadOrCreateDeviceIdentity","derivedId","updated","identity","stored","signDevicePayload","privateKeyBase64Url","sig","normalizeRole","normalizeScopes","scopes","scope","readStore","writeStore","store","loadDeviceAuthToken","storeDeviceAuthToken","existing","clearDeviceAuthToken","loadDevices","approveDevicePairing","requestId","rejectDevicePairing","rotateDeviceToken","revokeDeviceToken","loadNodes","resolveExecApprovalsRpc","nodeId","resolveExecApprovalsSaveRpc","loadExecApprovals","rpc","applyExecApprovalsSnapshot","saveExecApprovals","file","updateExecApprovalsFormValue","removeExecApprovalsFormValue","loadPresence","setSkillMessage","getErrorMessage","loadSkills","options","updateSkillEdit","skillKey","updateSkillEnabled","saveSkillApiKey","apiKey","installSkill","installId","getSystemTheme","resolveTheme","mode","clamp01","hasReducedMotionPreference","cleanupThemeTransition","root","startThemeTransition","nextTheme","applyTheme","context","currentTheme","documentReference","document_","prefersReducedMotion","xPercent","yPercent","rect","transition","startNodesPolling","stopNodesPolling","startLogsPolling","stopLogsPolling","startDebugPolling","stopDebugPolling","applySettings","applyResolvedTheme","setLastActiveSessionKey","applySettingsFromUrl","tokenRaw","passwordRaw","sessionRaw","gatewayUrlRaw","shouldCleanUrl","token","password","session","gatewayUrl","setTab","refreshActiveTab","syncUrlWithTab","setTheme","loadOverview","loadChannelsTab","loadCron","refreshChat","inferBasePath","configured","syncThemeWithSettings","resolved","attachThemeListener","detachThemeListener","syncTabWithLocation","replace","setTabFromRoute","onPopState","targetPath","currentPath","syncUrlWithSessionKey","isChatBusy","isChatStopCommand","handleAbortChat","enqueueChatMessage","sendChatMessageNow","ok","flushChatQueue","removeQueuedMessage","handleSendChat","messageOverride","previousDraft","refreshChatAvatar","flushChatQueueForEvent","resolveAgentIdForSession","buildAvatarMetaUrl","encoded","avatarUrl","i$1","normalizeMessage","hasToolId","contentRaw","contentItems","hasToolContent","hasToolName","timestamp","normalizeRoleForGrouping","lower","isToolResultMessage","setPrototypeOf","isFrozen","getPrototypeOf","getOwnPropertyDescriptor","freeze","seal","create","apply","construct","func","thisArg","_len","_key","Func","_len2","_key2","arrayForEach","unapply","arrayLastIndexOf","arrayPop","arrayPush","arraySplice","stringToLowerCase","stringToString","stringMatch","stringReplace","stringIndexOf","stringTrim","objectHasOwnProperty","regExpTest","typeErrorCreate","unconstruct","_len3","_key3","_len4","_key4","addToSet","set","transformCaseFunc","element","lcElement","cleanArray","index","clone","object","newObject","property","lookupGetter","prop","desc","fallbackValue","html$1","svg$1","svgFilters","svgDisallowed","mathMl$1","mathMlDisallowed","html","svg","mathMl","xml","MUSTACHE_EXPR","ERB_EXPR","TMPLIT_EXPR","DATA_ATTR","ARIA_ATTR","IS_ALLOWED_URI","IS_SCRIPT_OR_DATA","ATTR_WHITESPACE","DOCTYPE_NAME","CUSTOM_ELEMENT","EXPRESSIONS","NODE_TYPE","getGlobal","_createTrustedTypesPolicy","trustedTypes","purifyHostElement","suffix","ATTR_NAME","policyName","scriptUrl","_createHooksMap","createDOMPurify","window","DOMPurify","document","originalDocument","currentScript","DocumentFragment","HTMLTemplateElement","Node","Element","NodeFilter","NamedNodeMap","HTMLFormElement","DOMParser","ElementPrototype","cloneNode","remove","getNextSibling","getChildNodes","getParentNode","template","trustedTypesPolicy","emptyHTML","implementation","createNodeIterator","createDocumentFragment","getElementsByTagName","importNode","hooks","IS_ALLOWED_URI$1","ALLOWED_TAGS","DEFAULT_ALLOWED_TAGS","ALLOWED_ATTR","DEFAULT_ALLOWED_ATTR","CUSTOM_ELEMENT_HANDLING","FORBID_TAGS","FORBID_ATTR","EXTRA_ELEMENT_HANDLING","ALLOW_ARIA_ATTR","ALLOW_DATA_ATTR","ALLOW_UNKNOWN_PROTOCOLS","ALLOW_SELF_CLOSE_IN_ATTR","SAFE_FOR_TEMPLATES","SAFE_FOR_XML","WHOLE_DOCUMENT","SET_CONFIG","FORCE_BODY","RETURN_DOM","RETURN_DOM_FRAGMENT","RETURN_TRUSTED_TYPE","SANITIZE_DOM","SANITIZE_NAMED_PROPS","SANITIZE_NAMED_PROPS_PREFIX","KEEP_CONTENT","IN_PLACE","USE_PROFILES","FORBID_CONTENTS","DEFAULT_FORBID_CONTENTS","DATA_URI_TAGS","DEFAULT_DATA_URI_TAGS","URI_SAFE_ATTRIBUTES","DEFAULT_URI_SAFE_ATTRIBUTES","MATHML_NAMESPACE","SVG_NAMESPACE","HTML_NAMESPACE","NAMESPACE","IS_EMPTY_INPUT","ALLOWED_NAMESPACES","DEFAULT_ALLOWED_NAMESPACES","MATHML_TEXT_INTEGRATION_POINTS","HTML_INTEGRATION_POINTS","COMMON_SVG_AND_HTML_ELEMENTS","PARSER_MEDIA_TYPE","SUPPORTED_PARSER_MEDIA_TYPES","DEFAULT_PARSER_MEDIA_TYPE","CONFIG","formElement","isRegexOrFunction","testValue","_parseConfig","cfg","ALL_SVG_TAGS","ALL_MATHML_TAGS","_checkValidNamespace","parent","tagName","parentTagName","_forceRemove","node","_removeAttribute","_initDocument","dirty","doc","leadingWhitespace","matches","dirtyPayload","body","_createNodeIterator","_isClobbered","_isNode","_executeHooks","currentNode","hook","_sanitizeElements","_isBasicCustomElement","parentNode","childNodes","childCount","childClone","_isValidAttribute","lcTag","lcName","_sanitizeAttributes","attributes","hookEvent","attr","namespaceURI","attrValue","initValue","_sanitizeShadowDOM","fragment","shadowNode","shadowIterator","importedNode","returnNode","nodeIterator","serializedHTML","tag","entryPoint","hookFunction","purify","me","xe","be","Re","Te","re","se","Oe","Q","we","ye","Pe","Se","ie","$e","U","te","_e","Le","Me","ze","oe","Ae","K","ae","Ce","le","Ie","Ee","Be","ue","qe","ve","pe","De","He","Ze","Ge","Ne","Qe","Fe","je","ce","he","Ue","ne","Ke","We","Xe","ke","J","de","ge","Je","O","ee","fe","marked","allowedTags","allowedAttrs","hooksInstalled","MARKDOWN_CHAR_LIMIT","MARKDOWN_PARSE_LIMIT","installHooks","toSanitizedMarkdownHtml","markdown","escapeHtml","rendered","renderEmojiIcon","icon","className","setEmojiIcon","COPIED_FOR_MS","ERROR_FOR_MS","COPY_LABEL","COPIED_LABEL","ERROR_LABEL","COPY_ICON","COPIED_ICON","ERROR_ICON","copyTextToClipboard","setButtonLabel","button","createCopyButton","idleLabel","btn","copied","renderCopyAsMarkdownButton","TOOL_DISPLAY_CONFIG","rawConfig","FALLBACK","TOOL_MAP","normalizeToolName","defaultTitle","normalizeVerb","coerceDisplayValue","firstLine","preview","lookupValueByPath","segment","resolveDetailFromKeys","keys","display","resolveReadDetail","offset","resolveWriteDetail","resolveActionSpec","spec","action","resolveToolDisplay","emoji","actionRaw","actionSpec","verb","detail","detailKeys","shortenHomeInString","formatToolDetail","TOOL_INLINE_THRESHOLD","PREVIEW_MAX_LINES","PREVIEW_MAX_CHARS","formatToolOutputForSidebar","getTruncatedPreview","allLines","extractToolCards","normalizeContent","cards","kind","coerceArgs","extractToolText","card","renderToolCardSidebar","onOpenSidebar","hasText","canClick","handleClick","info","isShort","showCollapsed","showInline","isEmpty","nothing","renderReadingIndicatorGroup","assistant","renderAvatar","renderStreamingGroup","startedAt","renderGroupedMessage","renderMessageGroup","group","normalizedRole","assistantName","who","roleClass","assistantAvatar","initial","isAvatarUrl","isToolResult","toolCards","hasToolCards","extractedText","extractedThinking","markdownBase","reasoningMarkdown","canCopyMarkdown","bubbleClasses","unsafeHTML","renderMarkdownSidebar","props","ResizableDivider","LitElement","containerWidth","deltaRatio","newRatio","css","__decorateClass","customElement","renderChat","canCompose","isBusy","reasoningLevel","row","showReasoning","assistantIdentity","composePlaceholder","splitRatio","sidebarOpen","repeat","buildChatItems","CHAT_HISTORY_RENDER_LIMIT","groupMessages","items","currentGroup","history","tools","historyStart","messageKey","messageId","safeJson","fnv1a","schemaType","schema","defaultValue","pathKey","hintForPath","hints","direct","hintKey","hint","hintSegments","humanize","isSensitivePath","META_KEYS","isAnySchema","jsonValue","icons","renderNode","unsupported","disabled","onPatch","showLabel","type","help","nonNull","extractLiteral","literals","allLiterals","resolvedValue","lit","renderSelect","primitiveTypes","variant","normalizedTypes","hasString","hasNumber","renderTextInput","opt","renderObject","renderArray","displayValue","renderNumberInput","inputType","isSensitive","placeholder","numValue","currentIndex","unset","val","sorted","orderA","orderB","reserved","additional","allowExtra","propKey","renderMapField","itemsSchema","arr","reservedKeys","anySchema","entryValue","valuePath","sectionIcons","SECTION_META","getSectionIcon","matchesSearch","query","schemaMatches","propSchema","unions","renderConfigForm","properties","searchQuery","activeSection","activeSubsection","subsectionContext","sectionSchema","sectionKey","subsectionKey","description","sectionValue","scopedValue","normalizeEnum","filtered","nullable","enumValues","analyzeConfigSchema","normalizeSchemaNode","pathLabel","union","normalizeUnion","enumNullable","normalizedProps","remaining","unique","sidebarIcons","SECTIONS","ALL_SUBSECTION","resolveSectionMeta","resolveSubsections","uiHints","subKey","order","computeDiff","original","changes","compare","orig","curr","origObj","currObj","allKeys","truncateValue","maxLen","str","renderConfig","validity","analysis","formUnsafe","canSaveForm","canSave","canApply","canUpdate","schemaProps","availableSections","knownKeys","extraSections","allSections","activeSectionSchema","activeSectionMeta","subsections","allowSubnav","isAllSubsection","effectiveSubsection","hasChanges","section","change","formatDuration","channelEnabled","channels","channelStatus","running","connected","accountActive","account","getChannelAccountCount","channelAccounts","renderChannelAccountCount","count","resolveSchemaNode","resolveChannelValue","config","channelId","fromChannels","renderChannelConfigForm","configValue","renderChannelConfigSection","renderDiscordCard","discord","accountCountLabel","renderIMessageCard","imessage","isFormDirty","renderNostrProfileForm","callbacks","accountId","isDirty","renderField","field","inputId","renderPicturePreview","picture","img","createNostrProfileFormState","profile","truncatePubkey","pubkey","renderNostrCard","nostr","nostrAccounts","profileFormState","profileFormCallbacks","onEditProfile","primaryAccount","summaryConfigured","summaryRunning","summaryPublicKey","summaryLastStartAt","summaryLastError","hasMultipleAccounts","showingForm","renderAccountCard","displayName","renderProfileSection","about","nip05","hasAnyProfileData","renderSignalCard","signal","renderSlackCard","slack","renderTelegramCard","telegram","telegramAccounts","botUsername","renderWhatsAppCard","whatsapp","renderChannels","orderedChannels","resolveChannelOrder","channel","renderChannel","showForm","renderGenericChannelCard","resolveChannelLabel","lastError","accounts","renderGenericAccount","resolveChannelMetaMap","RECENT_ACTIVITY_THRESHOLD_MS","hasRecentActivity","deriveRunningStatus","deriveConnectedStatus","runningStatus","connectedStatus","formatPresenceSummary","ip","version","formatPresenceAge","ts","formatNextRun","formatSessionTokens","total","ctx","formatEventPayload","formatCronState","last","formatCronSchedule","formatCronPayload","buildChannelOptions","seen","renderCron","channelOptions","renderScheduleFields","renderJob","renderRun","itemClass","renderDebug","evt","renderInstances","renderEntry","lastInput","roles","scopesLabel","formatTime","date","matchesFilter","needle","renderLogs","levelFiltered","exportLabel","renderNodes","bindingState","resolveBindingsState","approvalsState","resolveExecApprovalsState","renderExecApprovals","renderBindings","renderDevices","list","pending","paired","req","renderPendingDevice","device","renderPairedDevice","age","repair","tokens","renderTokenRow","deviceId","when","EXEC_APPROVALS_DEFAULT_SCOPE","SECURITY_OPTIONS","ASK_OPTIONS","nodes","resolveExecNodes","defaultBinding","agents","resolveAgentBindings","ready","normalizeSecurity","normalizeAsk","resolveExecApprovalsDefaults","resolveConfigAgents","agentsNode","isDefault","resolveExecApprovalsAgents","configAgents","approvalsAgents","merged","agent","aLabel","bLabel","resolveExecApprovalsScope","selected","targetNodes","resolveExecApprovalsNodes","targetNodeId","selectedScope","selectedAgent","allowlist","supportsBinding","renderAgentBinding","targetReady","renderExecApprovalsTarget","renderExecApprovalsTabs","renderExecApprovalsPolicy","renderExecApprovalsAllowlist","hasNodes","nodeValue","first","isDefaults","agentSecurity","agentAsk","agentAskFallback","securityValue","askValue","askFallbackValue","autoOverride","autoEffective","autoIsDefault","option","allowlistPath","renderAllowlistEntry","lastUsed","lastCommand","lastPath","bindingValue","cmd","fallbackAgent","exec","execEntry","binding","caps","commands","renderOverview","uptime","tick","authHint","hasToken","hasPassword","insecureContextHint","THINK_LEVELS","BINARY_THINK_LEVELS","VERBOSE_LEVELS","REASONING_LEVELS","normalizeProviderId","provider","isBinaryThinkingProvider","resolveThinkLevelOptions","resolveThinkLevelDisplay","isBinary","resolveThinkLevelPatchValue","renderSessions","rows","renderRow","onDelete","rawThinking","isBinaryThinking","thinking","thinkLevels","verbose","reasoning","canLink","chatUrl","formatRemaining","totalSeconds","minutes","renderMetaRow","renderExecApprovalPrompt","active","request","remainingMs","queueCount","renderSkills","skills","filter","skill","renderSkill","busy","canInstall","missing","reasons","renderTab","href","renderChatControls","sessionOptions","resolveSessionOptions","disableThinkingToggle","disableFocusToggle","showThinking","focusActive","refreshIcon","focusIcon","sessions","resolvedCurrent","THEME_ORDER","renderThemeToggle","renderMonitorIcon","renderSunIcon","renderMoonIcon","AVATAR_DATA_RE","AVATAR_HTTP_RE","resolveAssistantAvatarUrl","renderApp","presenceCount","sessionsCount","cronNext","chatDisabledReason","isChat","chatFocus","assistantAvatarUrl","chatAvatarUrl","isGroupCollapsed","hasActiveTab","agentIndex","ratio","DEFAULT_LOG_LEVEL_FILTERS","DEFAULT_CRON_FORM","loadAgents","GATEWAY_CLIENT_IDS","GATEWAY_CLIENT_NAMES","GATEWAY_CLIENT_MODES","buildDeviceAuthPayload","CONNECT_FAILED_CLOSE_CODE","GatewayBrowserClient","ev","reason","delay","isSecureContext","deviceIdentity","canFallbackToShared","authToken","storedToken","auth","signedAtMs","nonce","signature","hello","frame","seq","method","resolve","reject","isRecord","parseExecApprovalRequested","command","createdAtMs","expiresAtMs","parseExecApprovalResolved","pruneExecApprovalQueue","queue","addExecApproval","removeExecApproval","loadAssistantIdentity","normalizeSessionKeyForDefaults","mainSessionKey","mainKey","defaultAgentId","applySessionDefaults","resolvedSessionKey","resolvedSettingsSessionKey","resolvedLastActiveSessionKey","nextSessionKey","nextSettings","shouldUpdateSettings","connectGateway","applySnapshot","code","handleGatewayEvent","expected","received","handleConnected","handleFirstUpdated","handleDisconnected","handleUpdated","changed","forcedByTab","forcedByLoad","handleWhatsAppStart","handleWhatsAppWait","handleWhatsAppLogout","handleChannelConfigSave","handleChannelConfigReload","parseValidationErrors","details","errors","rawField","resolveNostrAccountId","buildNostrProfileUrl","handleNostrProfileEdit","handleNostrProfileCancel","handleNostrProfileFieldChange","handleNostrProfileToggleAdvanced","handleNostrProfileSave","response","errorMessage","handleNostrProfileImport","nextValues","showAdvanced","injectedAssistantIdentity","resolveOnboardingMode","ClawdbotApp","onPopStateInternal","connectGatewayInternal","handleChatScrollInternal","handleLogsScrollInternal","exportLogsInternal","resetToolStreamInternal","resetChatScrollInternal","loadAssistantIdentityInternal","applySettingsInternal","setTabInternal","setThemeInternal","loadOverviewInternal","loadCronInternal","handleAbortChatInternal","removeQueuedMessageInternal","handleSendChatInternal","handleWhatsAppStartInternal","handleWhatsAppWaitInternal","handleWhatsAppLogoutInternal","handleChannelConfigSaveInternal","handleChannelConfigReloadInternal","handleNostrProfileEditInternal","handleNostrProfileCancelInternal","handleNostrProfileFieldChangeInternal","handleNostrProfileSaveInternal","handleNostrProfileImportInternal","handleNostrProfileToggleAdvancedInternal","decision"],"mappings":"ssBAKA,MAAMA,GAAE,WAAWC,GAAED,GAAE,aAAsBA,GAAE,WAAX,QAAqBA,GAAE,SAAS,eAAe,uBAAuB,SAAS,WAAW,YAAY,cAAc,UAAUE,GAAE,OAAM,EAAGC,GAAE,IAAI,QAAO,IAAAC,GAAC,KAAO,CAAC,YAAY,EAAEH,EAAEE,EAAE,CAAC,GAAG,KAAK,aAAa,GAAGA,IAAID,GAAE,MAAM,MAAM,mEAAmE,EAAE,KAAK,QAAQ,EAAE,KAAK,EAAED,CAAC,CAAC,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,MAAMC,EAAE,KAAK,EAAE,GAAGD,IAAY,IAAT,OAAW,CAAC,MAAMA,EAAWC,IAAT,QAAgBA,EAAE,SAAN,EAAaD,IAAI,EAAEE,GAAE,IAAID,CAAC,GAAY,IAAT,UAAc,KAAK,EAAE,EAAE,IAAI,eAAe,YAAY,KAAK,OAAO,EAAED,GAAGE,GAAE,IAAID,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,OAAO,KAAK,OAAO,CAAC,EAAC,MAAMG,GAAEL,GAAG,IAAIM,GAAY,OAAON,GAAjB,SAAmBA,EAAEA,EAAE,GAAG,OAAOE,EAAC,EAAEK,GAAE,CAACP,KAAKC,IAAI,CAAC,MAAME,EAAMH,EAAE,SAAN,EAAaA,EAAE,CAAC,EAAEC,EAAE,OAAO,CAACA,EAAEC,EAAE,IAAID,GAAGD,GAAG,CAAC,GAAQA,EAAE,eAAP,GAAoB,OAAOA,EAAE,QAAQ,GAAa,OAAOA,GAAjB,SAAmB,OAAOA,EAAE,MAAM,MAAM,mEAAmEA,EAAE,sFAAsF,CAAC,GAAGE,CAAC,EAAEF,EAAE,EAAE,CAAC,EAAEA,EAAE,CAAC,CAAC,EAAE,OAAO,IAAIM,GAAEH,EAAEH,EAAEE,EAAC,CAAC,EAAEM,GAAE,CAACN,EAAEC,IAAI,CAAC,GAAGF,GAAEC,EAAE,mBAAmBC,EAAE,IAAIH,GAAGA,aAAa,cAAcA,EAAEA,EAAE,UAAU,MAAO,WAAUC,KAAKE,EAAE,CAAC,MAAMA,EAAE,SAAS,cAAc,OAAO,EAAEG,EAAEN,GAAE,SAAkBM,IAAT,QAAYH,EAAE,aAAa,QAAQG,CAAC,EAAEH,EAAE,YAAYF,EAAE,QAAQC,EAAE,YAAYC,CAAC,CAAC,CAAC,EAAEM,GAAER,GAAED,GAAGA,EAAEA,GAAGA,aAAa,eAAe,GAAG,CAAC,IAAIC,EAAE,GAAG,UAAU,KAAK,EAAE,SAASA,GAAG,EAAE,QAAQ,OAAOI,GAAEJ,CAAC,CAAC,GAAGD,CAAC,EAAEA,ECApzC,KAAK,CAAC,GAAGO,GAAE,eAAeN,GAAE,yBAAyBS,GAAE,oBAAoBL,GAAE,sBAAsBF,GAAE,eAAeG,EAAC,EAAE,OAAOK,GAAE,WAAWF,GAAEE,GAAE,aAAaC,GAAEH,GAAEA,GAAE,YAAY,GAAGI,GAAEF,GAAE,+BAA+BG,GAAE,CAACd,EAAEE,IAAIF,EAAEe,GAAE,CAAC,YAAYf,EAAEE,EAAE,CAAC,OAAOA,EAAC,CAAE,KAAK,QAAQF,EAAEA,EAAEY,GAAE,KAAK,MAAM,KAAK,OAAO,KAAK,MAAMZ,EAAQA,GAAN,KAAQA,EAAE,KAAK,UAAUA,CAAC,CAAC,CAAC,OAAOA,CAAC,EAAE,cAAcA,EAAEE,EAAE,CAAC,IAAIK,EAAEP,EAAE,OAAOE,EAAC,CAAE,KAAK,QAAQK,EAASP,IAAP,KAAS,MAAM,KAAK,OAAOO,EAASP,IAAP,KAAS,KAAK,OAAOA,CAAC,EAAE,MAAM,KAAK,OAAO,KAAK,MAAM,GAAG,CAACO,EAAE,KAAK,MAAMP,CAAC,CAAC,MAAS,CAACO,EAAE,IAAI,CAAC,CAAC,OAAOA,CAAC,CAAC,EAAES,GAAE,CAAChB,EAAEE,IAAI,CAACK,GAAEP,EAAEE,CAAC,EAAEe,GAAE,CAAC,UAAU,GAAG,KAAK,OAAO,UAAUF,GAAE,QAAQ,GAAG,WAAW,GAAG,WAAWC,EAAC,EAAE,OAAO,WAAW,OAAO,UAAU,EAAEL,GAAE,sBAAsB,IAAI,QAAO,IAAAO,GAAC,cAAgB,WAAW,CAAC,OAAO,eAAe,EAAE,CAAC,KAAK,KAAI,GAAI,KAAK,IAAI,CAAA,GAAI,KAAK,CAAC,CAAC,CAAC,WAAW,oBAAoB,CAAC,OAAO,KAAK,SAAQ,EAAG,KAAK,MAAM,CAAC,GAAG,KAAK,KAAK,KAAI,CAAE,CAAC,CAAC,OAAO,eAAe,EAAEhB,EAAEe,GAAE,CAAC,GAAGf,EAAE,QAAQA,EAAE,UAAU,IAAI,KAAK,KAAI,EAAG,KAAK,UAAU,eAAe,CAAC,KAAKA,EAAE,OAAO,OAAOA,CAAC,GAAG,QAAQ,IAAI,KAAK,kBAAkB,IAAI,EAAEA,CAAC,EAAE,CAACA,EAAE,WAAW,CAAC,MAAMK,EAAE,OAAM,EAAGG,EAAE,KAAK,sBAAsB,EAAEH,EAAEL,CAAC,EAAWQ,IAAT,QAAYT,GAAE,KAAK,UAAU,EAAES,CAAC,CAAC,CAAC,CAAC,OAAO,sBAAsB,EAAER,EAAEK,EAAE,CAAC,KAAK,CAAC,IAAIN,EAAE,IAAII,CAAC,EAAEK,GAAE,KAAK,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,KAAKR,CAAC,CAAC,EAAE,IAAIF,EAAE,CAAC,KAAKE,CAAC,EAAEF,CAAC,CAAC,EAAE,MAAM,CAAC,IAAIC,EAAE,IAAIC,EAAE,CAAC,MAAMQ,EAAET,GAAG,KAAK,IAAI,EAAEI,GAAG,KAAK,KAAKH,CAAC,EAAE,KAAK,cAAc,EAAEQ,EAAEH,CAAC,CAAC,EAAE,aAAa,GAAG,WAAW,EAAE,CAAC,CAAC,OAAO,mBAAmB,EAAE,CAAC,OAAO,KAAK,kBAAkB,IAAI,CAAC,GAAGU,EAAC,CAAC,OAAO,MAAM,CAAC,GAAG,KAAK,eAAeH,GAAE,mBAAmB,CAAC,EAAE,OAAO,MAAM,EAAER,GAAE,IAAI,EAAE,EAAE,SAAQ,EAAY,EAAE,IAAX,SAAe,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,KAAK,kBAAkB,IAAI,IAAI,EAAE,iBAAiB,CAAC,CAAC,OAAO,UAAU,CAAC,GAAG,KAAK,eAAeQ,GAAE,WAAW,CAAC,EAAE,OAAO,GAAG,KAAK,UAAU,GAAG,KAAK,KAAI,EAAG,KAAK,eAAeA,GAAE,YAAY,CAAC,EAAE,CAAC,MAAMd,EAAE,KAAK,WAAW,EAAE,CAAC,GAAGK,GAAEL,CAAC,EAAE,GAAGG,GAAEH,CAAC,CAAC,EAAE,UAAU,KAAK,EAAE,KAAK,eAAe,EAAEA,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,OAAO,QAAQ,EAAE,GAAU,IAAP,KAAS,CAAC,MAAME,EAAE,oBAAoB,IAAI,CAAC,EAAE,GAAYA,IAAT,OAAW,SAAS,CAACF,EAAE,CAAC,IAAIE,EAAE,KAAK,kBAAkB,IAAIF,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,SAAS,CAACA,EAAE,CAAC,IAAI,KAAK,kBAAkB,CAAC,MAAM,EAAE,KAAK,KAAKA,EAAE,CAAC,EAAW,IAAT,QAAY,KAAK,KAAK,IAAI,EAAEA,CAAC,CAAC,CAAC,KAAK,cAAc,KAAK,eAAe,KAAK,MAAM,CAAC,CAAC,OAAO,eAAeE,EAAE,CAAC,MAAMK,EAAE,CAAA,EAAG,GAAG,MAAM,QAAQL,CAAC,EAAE,CAAC,MAAMD,EAAE,IAAI,IAAIC,EAAE,KAAK,GAAG,EAAE,QAAO,CAAE,EAAE,UAAUA,KAAKD,EAAEM,EAAE,QAAQP,GAAEE,CAAC,CAAC,CAAC,MAAeA,IAAT,QAAYK,EAAE,KAAKP,GAAEE,CAAC,CAAC,EAAE,OAAOK,CAAC,CAAC,OAAO,KAAK,EAAEL,EAAE,CAAC,MAAMK,EAAEL,EAAE,UAAU,OAAWK,IAAL,GAAO,OAAiB,OAAOA,GAAjB,SAAmBA,EAAY,OAAO,GAAjB,SAAmB,EAAE,YAAW,EAAG,MAAM,CAAC,aAAa,CAAC,MAAK,EAAG,KAAK,KAAK,OAAO,KAAK,gBAAgB,GAAG,KAAK,WAAW,GAAG,KAAK,KAAK,KAAK,KAAK,KAAI,CAAE,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,QAAQ,GAAG,KAAK,eAAe,CAAC,EAAE,KAAK,KAAK,IAAI,IAAI,KAAK,KAAI,EAAG,KAAK,cAAa,EAAG,KAAK,YAAY,GAAG,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,KAAK,OAAO,IAAI,KAAK,IAAI,CAAC,EAAW,KAAK,aAAd,QAA0B,KAAK,aAAa,EAAE,gBAAa,CAAI,CAAC,iBAAiB,EAAE,CAAC,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,IAAIL,EAAE,KAAK,YAAY,kBAAkB,UAAUK,KAAKL,EAAE,KAAI,EAAG,KAAK,eAAeK,CAAC,IAAI,EAAE,IAAIA,EAAE,KAAKA,CAAC,CAAC,EAAE,OAAO,KAAKA,CAAC,GAAG,EAAE,KAAK,IAAI,KAAK,KAAK,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,KAAK,YAAY,KAAK,aAAa,KAAK,YAAY,iBAAiB,EAAE,OAAOL,GAAE,EAAE,KAAK,YAAY,aAAa,EAAE,CAAC,CAAC,mBAAmB,CAAC,KAAK,aAAa,KAAK,iBAAgB,EAAG,KAAK,eAAe,EAAE,EAAE,KAAK,MAAM,QAAQ,GAAG,EAAE,gBAAa,CAAI,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,sBAAsB,CAAC,KAAK,MAAM,QAAQ,GAAG,EAAE,mBAAgB,CAAI,CAAC,CAAC,yBAAyB,EAAEA,EAAEK,EAAE,CAAC,KAAK,KAAK,EAAEA,CAAC,CAAC,CAAC,KAAK,EAAEL,EAAE,CAAC,MAAMK,EAAE,KAAK,YAAY,kBAAkB,IAAI,CAAC,EAAEN,EAAE,KAAK,YAAY,KAAK,EAAEM,CAAC,EAAE,GAAYN,IAAT,QAAiBM,EAAE,UAAP,GAAe,CAAC,MAAMG,GAAYH,EAAE,WAAW,cAAtB,OAAkCA,EAAE,UAAUQ,IAAG,YAAYb,EAAEK,EAAE,IAAI,EAAE,KAAK,KAAK,EAAQG,GAAN,KAAQ,KAAK,gBAAgBT,CAAC,EAAE,KAAK,aAAaA,EAAES,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC,KAAK,EAAER,EAAE,CAAC,MAAMK,EAAE,KAAK,YAAYN,EAAEM,EAAE,KAAK,IAAI,CAAC,EAAE,GAAYN,IAAT,QAAY,KAAK,OAAOA,EAAE,CAAC,MAAMD,EAAEO,EAAE,mBAAmBN,CAAC,EAAES,EAAc,OAAOV,EAAE,WAArB,WAA+B,CAAC,cAAcA,EAAE,SAAS,EAAWA,EAAE,WAAW,gBAAtB,OAAoCA,EAAE,UAAUe,GAAE,KAAK,KAAKd,EAAE,MAAMI,EAAEK,EAAE,cAAcR,EAAEF,EAAE,IAAI,EAAE,KAAKC,CAAC,EAAEI,GAAG,KAAK,MAAM,IAAIJ,CAAC,GAAGI,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC,cAAc,EAAEH,EAAEK,EAAEN,EAAE,GAAGS,EAAE,CAAC,GAAY,IAAT,OAAW,CAAC,MAAML,EAAE,KAAK,YAAY,GAAQJ,IAAL,KAASS,EAAE,KAAK,CAAC,GAAGH,IAAIF,EAAE,mBAAmB,CAAC,EAAE,GAAGE,EAAE,YAAYS,IAAGN,EAAER,CAAC,GAAGK,EAAE,YAAYA,EAAE,SAASG,IAAI,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,aAAaL,EAAE,KAAK,EAAEE,CAAC,CAAC,GAAG,OAAO,KAAK,EAAE,EAAEL,EAAEK,CAAC,CAAC,CAAM,KAAK,kBAAV,KAA4B,KAAK,KAAK,KAAK,KAAI,EAAG,CAAC,EAAE,EAAEL,EAAE,CAAC,WAAWK,EAAE,QAAQN,EAAE,QAAQS,CAAC,EAAEL,EAAE,CAACE,GAAG,EAAE,KAAK,OAAO,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,EAAEF,GAAGH,GAAG,KAAK,CAAC,CAAC,EAAOQ,IAAL,IAAiBL,IAAT,UAAc,KAAK,KAAK,IAAI,CAAC,IAAI,KAAK,YAAYE,IAAIL,EAAE,QAAQ,KAAK,KAAK,IAAI,EAAEA,CAAC,GAAQD,IAAL,IAAQ,KAAK,OAAO,IAAI,KAAK,OAAO,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,KAAK,gBAAgB,GAAG,GAAG,CAAC,MAAM,KAAK,IAAI,OAAOD,EAAE,CAAC,QAAQ,OAAOA,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,eAAc,EAAG,OAAa,GAAN,MAAS,MAAM,EAAE,CAAC,KAAK,eAAe,CAAC,gBAAgB,CAAC,OAAO,KAAK,cAAa,CAAE,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,gBAAgB,OAAO,GAAG,CAAC,KAAK,WAAW,CAAC,GAAG,KAAK,aAAa,KAAK,iBAAgB,EAAG,KAAK,KAAK,CAAC,SAAS,CAACA,EAAEE,CAAC,IAAI,KAAK,KAAK,KAAKF,CAAC,EAAEE,EAAE,KAAK,KAAK,MAAM,CAAC,MAAMF,EAAE,KAAK,YAAY,kBAAkB,GAAGA,EAAE,KAAK,EAAE,SAAS,CAACE,EAAEK,CAAC,IAAIP,EAAE,CAAC,KAAK,CAAC,QAAQA,CAAC,EAAEO,EAAEN,EAAE,KAAKC,CAAC,EAAOF,IAAL,IAAQ,KAAK,KAAK,IAAIE,CAAC,GAAYD,IAAT,QAAY,KAAK,EAAEC,EAAE,OAAOK,EAAEN,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,MAAMC,EAAE,KAAK,KAAK,GAAG,CAAC,EAAE,KAAK,aAAaA,CAAC,EAAE,GAAG,KAAK,WAAWA,CAAC,EAAE,KAAK,MAAM,QAAQF,GAAGA,EAAE,cAAc,EAAE,KAAK,OAAOE,CAAC,GAAG,KAAK,KAAI,CAAE,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,KAAI,EAAG,CAAC,CAAC,GAAG,KAAK,KAAKA,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,QAAQF,GAAGA,EAAE,cAAW,CAAI,EAAE,KAAK,aAAa,KAAK,WAAW,GAAG,KAAK,aAAa,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC,IAAI,gBAAgB,CAAC,OAAO,KAAK,kBAAiB,CAAE,CAAC,mBAAmB,CAAC,OAAO,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,KAAK,KAAK,QAAQA,GAAG,KAAK,KAAKA,EAAE,KAAKA,CAAC,CAAC,CAAC,EAAE,KAAK,KAAI,CAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,EAACmB,GAAE,cAAc,CAAA,EAAGA,GAAE,kBAAkB,CAAC,KAAK,MAAM,EAAEA,GAAEL,GAAE,mBAAmB,CAAC,EAAE,IAAI,IAAIK,GAAEL,GAAE,WAAW,CAAC,EAAE,IAAI,IAAID,KAAI,CAAC,gBAAgBM,EAAC,CAAC,GAAGR,GAAE,0BAA0B,CAAA,GAAI,KAAK,OAAO,ECA3xL,MAACX,GAAE,WAAWO,GAAEP,GAAGA,EAAEE,GAAEF,GAAE,aAAaC,GAAEC,GAAEA,GAAE,aAAa,WAAW,CAAC,WAAWF,GAAGA,CAAC,CAAC,EAAE,OAAOU,GAAE,QAAQP,GAAE,OAAO,KAAK,OAAM,EAAG,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC,IAAIG,GAAE,IAAIH,GAAEE,GAAE,IAAIC,EAAC,IAAIM,GAAE,SAASH,GAAE,IAAIG,GAAE,cAAc,EAAE,EAAED,GAAEX,GAAUA,IAAP,MAAoB,OAAOA,GAAjB,UAAgC,OAAOA,GAAnB,WAAqBe,GAAE,MAAM,QAAQD,GAAEd,GAAGe,GAAEf,CAAC,GAAe,OAAOA,IAAI,OAAO,QAAQ,GAAtC,WAAwCgB,GAAE;AAAA,OAAcI,GAAE,sDAAsDC,GAAE,OAAOC,GAAE,KAAKT,GAAE,OAAO,KAAKG,EAAC,qBAAqBA,EAAC,KAAKA,EAAC;AAAA,0BAAsC,GAAG,EAAEO,GAAE,KAAKC,GAAE,KAAKL,GAAE,qCAAqCM,GAAEzB,GAAG,CAACO,KAAKL,KAAK,CAAC,WAAWF,EAAE,QAAQO,EAAE,OAAOL,CAAC,GAAGe,EAAEQ,GAAE,CAAC,EAAgBC,GAAE,OAAO,IAAI,cAAc,EAAEC,EAAE,OAAO,IAAI,aAAa,EAAEC,GAAE,IAAI,QAAQC,GAAEjB,GAAE,iBAAiBA,GAAE,GAAG,EAAE,SAASkB,GAAE9B,EAAEO,EAAE,CAAC,GAAG,CAACQ,GAAEf,CAAC,GAAG,CAACA,EAAE,eAAe,KAAK,EAAE,MAAM,MAAM,gCAAgC,EAAE,OAAgBC,KAAT,OAAWA,GAAE,WAAWM,CAAC,EAAEA,CAAC,CAAC,MAAMwB,GAAE,CAAC/B,EAAEO,IAAI,CAAC,MAAML,EAAEF,EAAE,OAAO,EAAEC,EAAE,CAAA,EAAG,IAAIK,EAAEM,EAAML,IAAJ,EAAM,QAAYA,IAAJ,EAAM,SAAS,GAAGE,EAAEW,GAAE,QAAQb,EAAE,EAAEA,EAAEL,EAAEK,IAAI,CAAC,MAAML,EAAEF,EAAEO,CAAC,EAAE,IAAII,EAAEI,EAAED,EAAE,GAAGE,EAAE,EAAE,KAAKA,EAAEd,EAAE,SAASO,EAAE,UAAUO,EAAED,EAAEN,EAAE,KAAKP,CAAC,EAASa,IAAP,OAAWC,EAAEP,EAAE,UAAUA,IAAIW,GAAUL,EAAE,CAAC,IAAX,MAAaN,EAAEY,GAAWN,EAAE,CAAC,IAAZ,OAAcN,EAAEa,GAAWP,EAAE,CAAC,IAAZ,QAAeI,GAAE,KAAKJ,EAAE,CAAC,CAAC,IAAIT,EAAE,OAAO,KAAKS,EAAE,CAAC,EAAE,GAAG,GAAGN,EAAEI,IAAYE,EAAE,CAAC,IAAZ,SAAgBN,EAAEI,IAAGJ,IAAII,GAAQE,EAAE,CAAC,IAAT,KAAYN,EAAEH,GAAGc,GAAEN,EAAE,IAAaC,EAAE,CAAC,IAAZ,OAAcD,EAAE,IAAIA,EAAEL,EAAE,UAAUM,EAAE,CAAC,EAAE,OAAOJ,EAAEI,EAAE,CAAC,EAAEN,EAAWM,EAAE,CAAC,IAAZ,OAAcF,GAAQE,EAAE,CAAC,IAAT,IAAWS,GAAED,IAAGd,IAAIe,IAAGf,IAAIc,GAAEd,EAAEI,GAAEJ,IAAIY,IAAGZ,IAAIa,GAAEb,EAAEW,IAAGX,EAAEI,GAAEP,EAAE,QAAQ,MAAMmB,EAAEhB,IAAII,IAAGb,EAAEO,EAAE,CAAC,EAAE,WAAW,IAAI,EAAE,IAAI,GAAGK,GAAGH,IAAIW,GAAElB,EAAEG,GAAES,GAAG,GAAGb,EAAE,KAAKU,CAAC,EAAET,EAAE,MAAM,EAAEY,CAAC,EAAEJ,GAAER,EAAE,MAAMY,CAAC,EAAEX,GAAEsB,GAAGvB,EAAEC,IAAQW,IAAL,GAAOP,EAAEkB,EAAE,CAAC,MAAM,CAACK,GAAE9B,EAAEY,GAAGZ,EAAEE,CAAC,GAAG,QAAYK,IAAJ,EAAM,SAAaA,IAAJ,EAAM,UAAU,GAAG,EAAEN,CAAC,CAAC,EAAC,IAAA+B,GAAC,MAAMxB,EAAC,CAAC,YAAY,CAAC,QAAQ,EAAE,WAAWD,CAAC,EAAEN,EAAE,CAAC,IAAII,EAAE,KAAK,MAAM,CAAA,EAAG,IAAIO,EAAE,EAAE,EAAE,EAAE,MAAMG,EAAE,EAAE,OAAO,EAAED,EAAE,KAAK,MAAM,CAACE,EAAEI,CAAC,EAAEW,GAAE,EAAExB,CAAC,EAAE,GAAG,KAAK,GAAGC,GAAE,cAAcQ,EAAEf,CAAC,EAAE4B,GAAE,YAAY,KAAK,GAAG,QAAYtB,IAAJ,GAAWA,IAAJ,EAAM,CAAC,MAAMP,EAAE,KAAK,GAAG,QAAQ,WAAWA,EAAE,YAAY,GAAGA,EAAE,UAAU,CAAC,CAAC,MAAaK,EAAEwB,GAAE,SAAQ,KAApB,MAAyBf,EAAE,OAAOC,GAAG,CAAC,GAAOV,EAAE,WAAN,EAAe,CAAC,GAAGA,EAAE,gBAAgB,UAAUL,KAAKK,EAAE,kBAAiB,EAAG,GAAGL,EAAE,SAASU,EAAC,EAAE,CAAC,MAAMH,EAAEa,EAAE,GAAG,EAAElB,EAAEG,EAAE,aAAaL,CAAC,EAAE,MAAMG,EAAC,EAAEF,EAAE,eAAe,KAAKM,CAAC,EAAEO,EAAE,KAAK,CAAC,KAAK,EAAE,MAAMF,EAAE,KAAKX,EAAE,CAAC,EAAE,QAAQC,EAAE,KAAWD,EAAE,CAAC,IAAT,IAAWgC,GAAQhC,EAAE,CAAC,IAAT,IAAWiC,GAAQjC,EAAE,CAAC,IAAT,IAAWkC,GAAEC,EAAC,CAAC,EAAE/B,EAAE,gBAAgBL,CAAC,CAAC,MAAMA,EAAE,WAAWG,EAAC,IAAIW,EAAE,KAAK,CAAC,KAAK,EAAE,MAAMF,CAAC,CAAC,EAAEP,EAAE,gBAAgBL,CAAC,GAAG,GAAGmB,GAAE,KAAKd,EAAE,OAAO,EAAE,CAAC,MAAML,EAAEK,EAAE,YAAY,MAAMF,EAAC,EAAEI,EAAEP,EAAE,OAAO,EAAE,GAAGO,EAAE,EAAE,CAACF,EAAE,YAAYH,GAAEA,GAAE,YAAY,GAAG,QAAQA,EAAE,EAAEA,EAAEK,EAAEL,IAAIG,EAAE,OAAOL,EAAEE,CAAC,EAAEO,GAAC,CAAE,EAAEoB,GAAE,SAAQ,EAAGf,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAEF,CAAC,CAAC,EAAEP,EAAE,OAAOL,EAAEO,CAAC,EAAEE,GAAC,CAAE,CAAC,CAAC,CAAC,SAAaJ,EAAE,WAAN,EAAe,GAAGA,EAAE,OAAOC,GAAEQ,EAAE,KAAK,CAAC,KAAK,EAAE,MAAMF,CAAC,CAAC,MAAM,CAAC,IAAIZ,EAAE,GAAG,MAAWA,EAAEK,EAAE,KAAK,QAAQF,GAAEH,EAAE,CAAC,KAA5B,IAAgCc,EAAE,KAAK,CAAC,KAAK,EAAE,MAAMF,CAAC,CAAC,EAAEZ,GAAGG,GAAE,OAAO,CAAC,CAACS,GAAG,CAAC,CAAC,OAAO,cAAc,EAAEL,EAAE,CAAC,MAAM,EAAEK,GAAE,cAAc,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,EAAC,SAASyB,GAAErC,EAAEO,EAAEL,EAAEF,EAAEC,EAAE,CAAC,GAAGM,IAAImB,GAAE,OAAOnB,EAAE,IAAIG,EAAWT,IAAT,OAAWC,EAAE,OAAOD,CAAC,EAAEC,EAAE,KAAK,MAAM,EAAES,GAAEJ,CAAC,EAAE,OAAOA,EAAE,gBAAgB,OAAOG,GAAG,cAAc,IAAIA,GAAG,OAAO,EAAE,EAAW,IAAT,OAAWA,EAAE,QAAQA,EAAE,IAAI,EAAEV,CAAC,EAAEU,EAAE,KAAKV,EAAEE,EAAED,CAAC,GAAYA,IAAT,QAAYC,EAAE,OAAO,CAAA,GAAID,CAAC,EAAES,EAAER,EAAE,KAAKQ,GAAYA,IAAT,SAAaH,EAAE8B,GAAErC,EAAEU,EAAE,KAAKV,EAAEO,EAAE,MAAM,EAAEG,EAAET,CAAC,GAAGM,CAAC,CAAC,MAAM+B,EAAC,CAAC,YAAY,EAAE/B,EAAE,CAAC,KAAK,KAAK,CAAA,EAAG,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,KAAK,KAAKA,CAAC,CAAC,IAAI,YAAY,CAAC,OAAO,KAAK,KAAK,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQA,CAAC,EAAE,MAAM,CAAC,EAAE,KAAK,KAAKN,GAAG,GAAG,eAAeW,IAAG,WAAWL,EAAE,EAAE,EAAEsB,GAAE,YAAY5B,EAAE,IAAIS,EAAEmB,GAAE,WAAW1B,EAAE,EAAEG,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,KAAc,IAAT,QAAY,CAAC,GAAGH,IAAI,EAAE,MAAM,CAAC,IAAII,EAAM,EAAE,OAAN,EAAWA,EAAE,IAAIgC,GAAE7B,EAAEA,EAAE,YAAY,KAAK,CAAC,EAAM,EAAE,OAAN,EAAWH,EAAE,IAAI,EAAE,KAAKG,EAAE,EAAE,KAAK,EAAE,QAAQ,KAAK,CAAC,EAAM,EAAE,OAAN,IAAaH,EAAE,IAAIiC,GAAE9B,EAAE,KAAK,CAAC,GAAG,KAAK,KAAK,KAAKH,CAAC,EAAE,EAAE,EAAE,EAAED,CAAC,CAAC,CAACH,IAAI,GAAG,QAAQO,EAAEmB,GAAE,SAAQ,EAAG1B,IAAI,CAAC,OAAO0B,GAAE,YAAYjB,GAAEX,CAAC,CAAC,EAAE,EAAE,CAAC,IAAIM,EAAE,EAAE,UAAU,KAAK,KAAK,KAAc,IAAT,SAAsB,EAAE,UAAX,QAAoB,EAAE,KAAK,EAAE,EAAEA,CAAC,EAAEA,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,KAAK,EAAEA,CAAC,CAAC,GAAGA,GAAG,CAAC,QAAC,MAAMgC,EAAC,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC,YAAY,EAAEhC,EAAE,EAAEN,EAAE,CAAC,KAAK,KAAK,EAAE,KAAK,KAAK0B,EAAE,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,KAAK,KAAKpB,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQN,EAAE,KAAK,KAAKA,GAAG,aAAa,EAAE,CAAC,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,KAAK,WAAW,MAAMM,EAAE,KAAK,KAAK,OAAgBA,IAAT,QAAiB,GAAG,WAAR,KAAmB,EAAEA,EAAE,YAAY,CAAC,CAAC,IAAI,WAAW,CAAC,OAAO,KAAK,IAAI,CAAC,IAAI,SAAS,CAAC,OAAO,KAAK,IAAI,CAAC,KAAK,EAAEA,EAAE,KAAK,CAAC,EAAE8B,GAAE,KAAK,EAAE9B,CAAC,EAAEI,GAAE,CAAC,EAAE,IAAIgB,GAAS,GAAN,MAAc,IAAL,IAAQ,KAAK,OAAOA,GAAG,KAAK,KAAI,EAAG,KAAK,KAAKA,GAAG,IAAI,KAAK,MAAM,IAAID,IAAG,KAAK,EAAE,CAAC,EAAW,EAAE,aAAX,OAAsB,KAAK,EAAE,CAAC,EAAW,EAAE,WAAX,OAAoB,KAAK,EAAE,CAAC,EAAEZ,GAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,WAAW,aAAa,EAAE,KAAK,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,OAAO,IAAI,KAAK,KAAI,EAAG,KAAK,KAAK,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,OAAOa,GAAGhB,GAAE,KAAK,IAAI,EAAE,KAAK,KAAK,YAAY,KAAK,EAAE,KAAK,EAAEC,GAAE,eAAe,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAOL,EAAE,WAAW,CAAC,EAAE,EAAEN,EAAY,OAAO,GAAjB,SAAmB,KAAK,KAAK,CAAC,GAAY,EAAE,KAAX,SAAgB,EAAE,GAAGO,GAAE,cAAcsB,GAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,OAAO,GAAG,GAAG,GAAG,KAAK,MAAM,OAAO7B,EAAE,KAAK,KAAK,EAAEM,CAAC,MAAM,CAAC,MAAMP,EAAE,IAAIsC,GAAErC,EAAE,IAAI,EAAEC,EAAEF,EAAE,EAAE,KAAK,OAAO,EAAEA,EAAE,EAAEO,CAAC,EAAE,KAAK,EAAEL,CAAC,EAAE,KAAK,KAAKF,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAIO,EAAEqB,GAAE,IAAI,EAAE,OAAO,EAAE,OAAgBrB,IAAT,QAAYqB,GAAE,IAAI,EAAE,QAAQrB,EAAE,IAAIC,GAAE,CAAC,CAAC,EAAED,CAAC,CAAC,EAAE,EAAE,CAACQ,GAAE,KAAK,IAAI,IAAI,KAAK,KAAK,CAAA,EAAG,KAAK,QAAQ,MAAMR,EAAE,KAAK,KAAK,IAAI,EAAEN,EAAE,EAAE,UAAUS,KAAK,EAAET,IAAIM,EAAE,OAAOA,EAAE,KAAK,EAAE,IAAIgC,GAAE,KAAK,EAAE9B,GAAC,CAAE,EAAE,KAAK,EAAEA,IAAG,EAAE,KAAK,KAAK,OAAO,CAAC,EAAE,EAAEF,EAAEN,CAAC,EAAE,EAAE,KAAKS,CAAC,EAAET,IAAIA,EAAEM,EAAE,SAAS,KAAK,KAAK,GAAG,EAAE,KAAK,YAAYN,CAAC,EAAEM,EAAE,OAAON,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,YAAYC,EAAE,CAAC,IAAI,KAAK,OAAO,GAAG,GAAGA,CAAC,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,EAAEK,GAAE,CAAC,EAAE,YAAYA,GAAE,CAAC,EAAE,OAAM,EAAG,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAU,KAAK,OAAd,SAAqB,KAAK,KAAK,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC,EAAC,MAAM6B,EAAC,CAAC,IAAI,SAAS,CAAC,OAAO,KAAK,QAAQ,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,YAAY,EAAE7B,EAAE,EAAEN,EAAES,EAAE,CAAC,KAAK,KAAK,EAAE,KAAK,KAAKiB,EAAE,KAAK,KAAK,OAAO,KAAK,QAAQ,EAAE,KAAK,KAAKpB,EAAE,KAAK,KAAKN,EAAE,KAAK,QAAQS,EAAE,EAAE,OAAO,GAAQ,EAAE,CAAC,IAAR,IAAgB,EAAE,CAAC,IAAR,IAAW,KAAK,KAAK,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,IAAI,MAAM,EAAE,KAAK,QAAQ,GAAG,KAAK,KAAKiB,CAAC,CAAC,KAAK,EAAEpB,EAAE,KAAK,EAAEN,EAAE,CAAC,MAAMS,EAAE,KAAK,QAAQ,IAAIP,EAAE,GAAG,GAAYO,IAAT,OAAW,EAAE2B,GAAE,KAAK,EAAE9B,EAAE,CAAC,EAAEJ,EAAE,CAACQ,GAAE,CAAC,GAAG,IAAI,KAAK,MAAM,IAAIe,GAAEvB,IAAI,KAAK,KAAK,OAAO,CAAC,MAAMF,EAAE,EAAE,IAAIK,EAAED,EAAE,IAAI,EAAEK,EAAE,CAAC,EAAEJ,EAAE,EAAEA,EAAEI,EAAE,OAAO,EAAEJ,IAAID,EAAEgC,GAAE,KAAKpC,EAAE,EAAEK,CAAC,EAAEC,EAAED,CAAC,EAAED,IAAIqB,KAAIrB,EAAE,KAAK,KAAKC,CAAC,GAAGH,IAAI,CAACQ,GAAEN,CAAC,GAAGA,IAAI,KAAK,KAAKC,CAAC,EAAED,IAAIsB,EAAE,EAAEA,EAAE,IAAIA,IAAI,IAAItB,GAAG,IAAIK,EAAEJ,EAAE,CAAC,GAAG,KAAK,KAAKA,CAAC,EAAED,CAAC,CAACF,GAAG,CAACF,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI0B,EAAE,KAAK,QAAQ,gBAAgB,KAAK,IAAI,EAAE,KAAK,QAAQ,aAAa,KAAK,KAAK,GAAG,EAAE,CAAC,CAAC,CAAA,IAAAc,GAAC,cAAgBL,EAAC,CAAC,aAAa,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,QAAQ,KAAK,IAAI,EAAE,IAAIT,EAAE,OAAO,CAAC,CAAC,KAAC,cAAgBS,EAAC,CAAC,aAAa,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,QAAQ,gBAAgB,KAAK,KAAK,CAAC,CAAC,GAAG,IAAIT,CAAC,CAAC,CAAC,KAAC,cAAgBS,EAAC,CAAC,YAAY,EAAE7B,EAAE,EAAEN,EAAES,EAAE,CAAC,MAAM,EAAEH,EAAE,EAAEN,EAAES,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,KAAK,EAAEH,EAAE,KAAK,CAAC,IAAI,EAAE8B,GAAE,KAAK,EAAE9B,EAAE,CAAC,GAAGoB,KAAKD,GAAE,OAAO,MAAM,EAAE,KAAK,KAAKzB,EAAE,IAAI0B,GAAG,IAAIA,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQjB,EAAE,IAAIiB,IAAI,IAAIA,GAAG1B,GAAGA,GAAG,KAAK,QAAQ,oBAAoB,KAAK,KAAK,KAAK,CAAC,EAAES,GAAG,KAAK,QAAQ,iBAAiB,KAAK,KAAK,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,YAAY,EAAE,CAAa,OAAO,KAAK,MAAxB,WAA6B,KAAK,KAAK,KAAK,KAAK,SAAS,MAAM,KAAK,QAAQ,CAAC,EAAE,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,EAAAgC,GAAC,KAAO,CAAC,YAAY,EAAEnC,EAAE,EAAE,CAAC,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK,OAAO,KAAK,KAAKA,EAAE,KAAK,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC8B,GAAE,KAAK,CAAC,CAAC,CAAC,EAAC,MAAMM,GAAE,CAA+B,EAAEJ,EAAmB,EAAEK,GAAE5C,GAAE,uBAAuB4C,KAAIpC,GAAE+B,EAAC,GAAGvC,GAAE,kBAAkB,CAAA,GAAI,KAAK,OAAO,EAAE,MAAM6C,GAAE,CAAC7C,EAAEO,EAAEL,IAAI,CAAC,MAAMD,EAAEC,GAAG,cAAcK,EAAE,IAAIG,EAAET,EAAE,WAAW,GAAYS,IAAT,OAAW,CAAC,MAAMV,EAAEE,GAAG,cAAc,KAAKD,EAAE,WAAWS,EAAE,IAAI6B,GAAEhC,EAAE,aAAaE,GAAC,EAAGT,CAAC,EAAEA,EAAE,OAAOE,GAAG,CAAA,CAAE,CAAC,CAAC,OAAOQ,EAAE,KAAKV,CAAC,EAAEU,CAAC,ECAh7N,MAAMR,GAAE,kBAAW,cAAgBF,EAAC,CAAC,aAAa,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,cAAc,CAAC,KAAK,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,iBAAgB,EAAG,OAAO,KAAK,cAAc,eAAe,EAAE,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,MAAMK,EAAE,KAAK,OAAM,EAAG,KAAK,aAAa,KAAK,cAAc,YAAY,KAAK,aAAa,MAAM,OAAO,CAAC,EAAE,KAAK,KAAKJ,GAAEI,EAAE,KAAK,WAAW,KAAK,aAAa,CAAC,CAAC,mBAAmB,CAAC,MAAM,kBAAiB,EAAG,KAAK,MAAM,aAAa,EAAE,CAAC,CAAC,sBAAsB,CAAC,MAAM,qBAAoB,EAAG,KAAK,MAAM,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAOA,EAAC,CAAC,EAACE,GAAE,cAAc,GAAGA,GAAE,UAAa,GAAGL,GAAE,2BAA2B,CAAC,WAAWK,EAAC,CAAC,EAAE,MAAMJ,GAAED,GAAE,0BAA0BC,KAAI,CAAC,WAAWI,EAAC,CAAC,GAAwDL,GAAE,qBAAqB,IAAI,KAAK,OAAO,ECA/xB,MAAMF,GAAEA,GAAG,CAACC,EAAEE,IAAI,CAAUA,WAAEA,EAAE,eAAe,IAAI,CAAC,eAAe,OAAOH,EAAEC,CAAC,CAAC,CAAC,EAAE,eAAe,OAAOD,EAAEC,CAAC,CAAC,ECAxG,MAAME,GAAE,CAAC,UAAU,GAAG,KAAK,OAAO,UAAUF,GAAE,QAAQ,GAAG,WAAWD,EAAC,EAAEK,GAAE,CAACL,EAAEG,GAAEF,EAAEI,IAAI,CAAC,KAAK,CAAC,KAAKC,EAAE,SAAS,CAAC,EAAED,EAAE,IAAIH,EAAE,WAAW,oBAAoB,IAAI,CAAC,EAAE,GAAYA,IAAT,QAAY,WAAW,oBAAoB,IAAI,EAAEA,EAAE,IAAI,GAAG,EAAaI,IAAX,YAAgBN,EAAE,OAAO,OAAOA,CAAC,GAAG,QAAQ,IAAIE,EAAE,IAAIG,EAAE,KAAKL,CAAC,EAAeM,IAAb,WAAe,CAAC,KAAK,CAAC,KAAKH,CAAC,EAAEE,EAAE,MAAM,CAAC,IAAIA,EAAE,CAAC,MAAMC,EAAEL,EAAE,IAAI,KAAK,IAAI,EAAEA,EAAE,IAAI,KAAK,KAAKI,CAAC,EAAE,KAAK,cAAcF,EAAEG,EAAEN,EAAE,GAAGK,CAAC,CAAC,EAAE,KAAKJ,EAAE,CAAC,OAAgBA,IAAT,QAAY,KAAK,EAAEE,EAAE,OAAOH,EAAEC,CAAC,EAAEA,CAAC,CAAC,CAAC,CAAC,GAAcK,IAAX,SAAa,CAAC,KAAK,CAAC,KAAKH,CAAC,EAAEE,EAAE,OAAO,SAASA,EAAE,CAAC,MAAMC,EAAE,KAAKH,CAAC,EAAEF,EAAE,KAAK,KAAKI,CAAC,EAAE,KAAK,cAAcF,EAAEG,EAAEN,EAAE,GAAGK,CAAC,CAAC,CAAC,CAAC,MAAM,MAAM,mCAAmCC,CAAC,CAAC,EAAE,SAASA,GAAEN,EAAE,CAAC,MAAM,CAACC,EAAEE,IAAc,OAAOA,GAAjB,SAAmBE,GAAEL,EAAEC,EAAEE,CAAC,GAAG,CAACH,EAAEC,EAAE,IAAI,CAAC,MAAMI,EAAEJ,EAAE,eAAe,CAAC,EAAE,OAAOA,EAAE,YAAY,eAAe,EAAED,CAAC,EAAEK,EAAE,OAAO,yBAAyBJ,EAAE,CAAC,EAAE,MAAM,GAAGD,EAAEC,EAAEE,CAAC,CAAC,CCA5yB,SAASE,EAAEA,EAAE,CAAC,OAAOL,GAAE,CAAC,GAAGK,EAAE,MAAM,GAAG,UAAU,EAAE,CAAC,CAAC,CCLvD,MAAMyC,GAAqB,GACrBC,GAAuB,IAEhBC,GAAyB,YAgBtC,SAASC,GAAoBC,EAA2BC,EAAuC,CAC7F,GAAI,OAAOD,GAAU,SAAU,OAC/B,MAAME,EAAUF,EAAM,KAAA,EACtB,GAAKE,EACL,OAAIA,EAAQ,QAAUD,EAAkBC,EACjCA,EAAQ,MAAM,EAAGD,CAAS,CACnC,CAEO,SAASE,GACdC,EACmB,CACnB,MAAMC,EACJN,GAAoBK,GAAO,KAAMR,EAAkB,GAAKE,GACpDQ,EAASP,GAAoBK,GAAO,QAAU,OAAWP,EAAoB,GAAK,KAKxF,MAAO,CAAE,QAHP,OAAOO,GAAO,SAAY,UAAYA,EAAM,QAAQ,KAAA,EAChDA,EAAM,QAAQ,KAAA,EACd,KACY,KAAAC,EAAM,OAAAC,CAAA,CAC1B,CAEO,SAASC,IAAsD,CACpE,OACSJ,GADL,OAAO,OAAW,IACc,CAAA,EAEF,CAChC,KAAM,OAAO,4BACb,OAAQ,OAAO,6BAAA,CAJqB,CAMxC,CChDA,MAAMK,GAAM,+BAiBL,SAASC,IAA2B,CAMzC,MAAMC,EAAuB,CAC3B,WAJO,GADO,SAAS,WAAa,SAAW,MAAQ,IACxC,MAAM,SAAS,IAAI,GAKlC,MAAO,GACP,WAAY,OACZ,qBAAsB,OACtB,MAAO,SACP,cAAe,GACf,iBAAkB,GAClB,WAAY,GACZ,aAAc,GACd,mBAAoB,CAAA,CAAC,EAGvB,GAAI,CACF,MAAMC,EAAM,aAAa,QAAQH,EAAG,EACpC,GAAI,CAACG,EAAK,OAAOD,EACjB,MAAME,EAAS,KAAK,MAAMD,CAAG,EAC7B,MAAO,CACL,WACE,OAAOC,EAAO,YAAe,UAAYA,EAAO,WAAW,KAAA,EACvDA,EAAO,WAAW,KAAA,EAClBF,EAAS,WACf,MAAO,OAAOE,EAAO,OAAU,SAAWA,EAAO,MAAQF,EAAS,MAClE,WACE,OAAOE,EAAO,YAAe,UAAYA,EAAO,WAAW,KAAA,EACvDA,EAAO,WAAW,KAAA,EAClBF,EAAS,WACf,qBACE,OAAOE,EAAO,sBAAyB,UACvCA,EAAO,qBAAqB,OACxBA,EAAO,qBAAqB,OAC3B,OAAOA,EAAO,YAAe,UAC5BA,EAAO,WAAW,QACpBF,EAAS,qBACf,MACEE,EAAO,QAAU,SACjBA,EAAO,QAAU,QACjBA,EAAO,QAAU,SACbA,EAAO,MACPF,EAAS,MACf,cACE,OAAOE,EAAO,eAAkB,UAC5BA,EAAO,cACPF,EAAS,cACf,iBACE,OAAOE,EAAO,kBAAqB,UAC/BA,EAAO,iBACPF,EAAS,iBACf,WACE,OAAOE,EAAO,YAAe,UAC7BA,EAAO,YAAc,IACrBA,EAAO,YAAc,GACjBA,EAAO,WACPF,EAAS,WACf,aACE,OAAOE,EAAO,cAAiB,UAC3BA,EAAO,aACPF,EAAS,aACf,mBACE,OAAOE,EAAO,oBAAuB,UACrCA,EAAO,qBAAuB,KAC1BA,EAAO,mBACPF,EAAS,kBAAA,CAEnB,MAAQ,CACN,OAAOA,CACT,CACF,CAEO,SAASG,GAAaC,EAAkB,CAC7C,aAAa,QAAQN,GAAK,KAAK,UAAUM,CAAI,CAAC,CAChD,CCzFO,SAASC,GACdC,EAC8B,CAC9B,MAAML,GAAOK,GAAc,IAAI,KAAA,EAC/B,GAAI,CAACL,EAAK,OAAO,KACjB,MAAMM,EAAQN,EAAI,MAAM,GAAG,EAAE,OAAO,OAAO,EAE3C,GADIM,EAAM,OAAS,GACfA,EAAM,CAAC,IAAM,QAAS,OAAO,KACjC,MAAMC,EAAUD,EAAM,CAAC,GAAG,KAAA,EACpBE,EAAOF,EAAM,MAAM,CAAC,EAAE,KAAK,GAAG,EACpC,MAAI,CAACC,GAAW,CAACC,EAAa,KACvB,CAAE,QAAAD,EAAS,KAAAC,CAAA,CACpB,CCjBO,MAAMC,GAAa,CACxB,CAAE,MAAO,OAAQ,KAAM,CAAC,MAAM,CAAA,EAC9B,CACE,MAAO,UACP,KAAM,CAAC,WAAY,WAAY,YAAa,WAAY,MAAM,CAAA,EAEhE,CAAE,MAAO,QAAS,KAAM,CAAC,SAAU,OAAO,CAAA,EAC1C,CAAE,MAAO,WAAY,KAAM,CAAC,SAAU,QAAS,MAAM,CAAA,CACvD,EAeMC,GAAiC,CACrC,SAAU,YACV,SAAU,YACV,UAAW,aACX,SAAU,YACV,KAAM,QACN,OAAQ,UACR,MAAO,SACP,KAAM,QACN,OAAQ,UACR,MAAO,SACP,KAAM,OACR,EAEMC,GAAc,IAAI,IACtB,OAAO,QAAQD,EAAS,EAAE,IAAI,CAAC,CAACE,EAAKC,CAAI,IAAM,CAACA,EAAMD,CAAU,CAAC,CACnE,EAEO,SAASE,GAAkBC,EAA0B,CAC1D,GAAI,CAACA,EAAU,MAAO,GACtB,IAAIC,EAAOD,EAAS,KAAA,EAEpB,OADKC,EAAK,WAAW,GAAG,IAAGA,EAAO,IAAIA,CAAI,IACtCA,IAAS,IAAY,IACrBA,EAAK,SAAS,GAAG,MAAUA,EAAK,MAAM,EAAG,EAAE,GACxCA,EACT,CAEO,SAASC,GAAcJ,EAAsB,CAClD,GAAI,CAACA,EAAM,MAAO,IAClB,IAAIK,EAAaL,EAAK,KAAA,EACtB,OAAKK,EAAW,WAAW,GAAG,IAAGA,EAAa,IAAIA,CAAU,IACxDA,EAAW,OAAS,GAAKA,EAAW,SAAS,GAAG,IAClDA,EAAaA,EAAW,MAAM,EAAG,EAAE,GAE9BA,CACT,CAEO,SAASC,GAAWP,EAAUG,EAAW,GAAY,CAC1D,MAAMC,EAAOF,GAAkBC,CAAQ,EACjCF,EAAOH,GAAUE,CAAG,EAC1B,OAAOI,EAAO,GAAGA,CAAI,GAAGH,CAAI,GAAKA,CACnC,CAEO,SAASO,GAAYC,EAAkBN,EAAW,GAAgB,CACvE,MAAMC,EAAOF,GAAkBC,CAAQ,EACvC,IAAIF,EAAOQ,GAAY,IACnBL,IACEH,IAASG,EACXH,EAAO,IACEA,EAAK,WAAW,GAAGG,CAAI,GAAG,IACnCH,EAAOA,EAAK,MAAMG,EAAK,MAAM,IAGjC,IAAIE,EAAaD,GAAcJ,CAAI,EAAE,YAAA,EAErC,OADIK,EAAW,SAAS,aAAa,IAAGA,EAAa,KACjDA,IAAe,IAAY,OACxBP,GAAY,IAAIO,CAAU,GAAK,IACxC,CAEO,SAASI,GAA0BD,EAA0B,CAClE,IAAIH,EAAaD,GAAcI,CAAQ,EAIvC,GAHIH,EAAW,SAAS,aAAa,IACnCA,EAAaD,GAAcC,EAAW,MAAM,EAAG,GAAqB,CAAC,GAEnEA,IAAe,IAAK,MAAO,GAC/B,MAAMK,EAAWL,EAAW,MAAM,GAAG,EAAE,OAAO,OAAO,EACrD,GAAIK,EAAS,SAAW,EAAG,MAAO,GAClC,QAAS7E,EAAI,EAAGA,EAAI6E,EAAS,OAAQ7E,IAAK,CACxC,MAAM8E,EAAY,IAAID,EAAS,MAAM7E,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,YAAA,EACpD,GAAIiE,GAAY,IAAIa,CAAS,EAAG,CAC9B,MAAMC,EAASF,EAAS,MAAM,EAAG7E,CAAC,EAClC,OAAO+E,EAAO,OAAS,IAAIA,EAAO,KAAK,GAAG,CAAC,GAAK,EAClD,CACF,CACA,MAAO,IAAIF,EAAS,KAAK,GAAG,CAAC,EAC/B,CAEO,SAASG,GAAWd,EAAkB,CAC3C,OAAQA,EAAA,CACN,IAAK,OACH,MAAO,KACT,IAAK,WACH,MAAO,KACT,IAAK,WACH,MAAO,KACT,IAAK,YACH,MAAO,KACT,IAAK,WACH,MAAO,KACT,IAAK,OACH,MAAO,IACT,IAAK,SACH,MAAO,KACT,IAAK,QACH,MAAO,MACT,IAAK,SACH,MAAO,KACT,IAAK,QACH,MAAO,KACT,IAAK,OACH,MAAO,KACT,QACE,MAAO,IAAA,CAEb,CAEO,SAASe,GAAYf,EAAU,CACpC,OAAQA,EAAA,CACN,IAAK,WACH,MAAO,WACT,IAAK,WACH,MAAO,WACT,IAAK,YACH,MAAO,YACT,IAAK,WACH,MAAO,WACT,IAAK,OACH,MAAO,YACT,IAAK,SACH,MAAO,SACT,IAAK,QACH,MAAO,QACT,IAAK,OACH,MAAO,OACT,IAAK,SACH,MAAO,SACT,IAAK,QACH,MAAO,QACT,IAAK,OACH,MAAO,OACT,QACE,MAAO,SAAA,CAEb,CAEO,SAASgB,GAAehB,EAAU,CACvC,OAAQA,EAAA,CACN,IAAK,WACH,MAAO,wDACT,IAAK,WACH,MAAO,gCACT,IAAK,YACH,MAAO,qDACT,IAAK,WACH,MAAO,2DACT,IAAK,OACH,MAAO,6CACT,IAAK,SACH,MAAO,mDACT,IAAK,QACH,MAAO,sDACT,IAAK,OACH,MAAO,uDACT,IAAK,SACH,MAAO,yCACT,IAAK,QACH,MAAO,mDACT,IAAK,OACH,MAAO,sCACT,QACE,MAAO,EAAA,CAEb,CCzLO,SAASiB,GAASC,EAA4B,CACnD,MAAI,CAACA,GAAMA,IAAO,EAAU,MACrB,IAAI,KAAKA,CAAE,EAAE,eAAA,CACtB,CAEO,SAASC,EAAUD,EAA4B,CACpD,GAAI,CAACA,GAAMA,IAAO,EAAG,MAAO,MAC5B,MAAME,EAAO,KAAK,IAAA,EAAQF,EAC1B,GAAIE,EAAO,EAAG,MAAO,WACrB,MAAMC,EAAM,KAAK,MAAMD,EAAO,GAAI,EAClC,GAAIC,EAAM,GAAI,MAAO,GAAGA,CAAG,QAC3B,MAAMC,EAAM,KAAK,MAAMD,EAAM,EAAE,EAC/B,GAAIC,EAAM,GAAI,MAAO,GAAGA,CAAG,QAC3B,MAAMC,EAAK,KAAK,MAAMD,EAAM,EAAE,EAC9B,OAAIC,EAAK,GAAW,GAAGA,CAAE,QAElB,GADK,KAAK,MAAMA,EAAK,EAAE,CACjB,OACf,CAEO,SAASC,GAAiBN,EAA4B,CAC3D,GAAI,CAACA,GAAMA,IAAO,EAAG,MAAO,MAC5B,GAAIA,EAAK,IAAM,MAAO,GAAGA,CAAE,KAC3B,MAAMG,EAAM,KAAK,MAAMH,EAAK,GAAI,EAChC,GAAIG,EAAM,GAAI,MAAO,GAAGA,CAAG,IAC3B,MAAMC,EAAM,KAAK,MAAMD,EAAM,EAAE,EAC/B,GAAIC,EAAM,GAAI,MAAO,GAAGA,CAAG,IAC3B,MAAMC,EAAK,KAAK,MAAMD,EAAM,EAAE,EAC9B,OAAIC,EAAK,GAAW,GAAGA,CAAE,IAElB,GADK,KAAK,MAAMA,EAAK,EAAE,CACjB,GACf,CAEO,SAASE,GAAWC,EAAmD,CAC5E,MAAI,CAACA,GAAUA,EAAO,SAAW,EAAU,OACpCA,EAAO,OAAQ/E,GAAmB,GAAQA,GAAKA,EAAE,KAAA,EAAO,EAAE,KAAK,IAAI,CAC5E,CAEO,SAASgF,GAAUlD,EAAemD,EAAM,IAAa,CAC1D,OAAInD,EAAM,QAAUmD,EAAYnD,EACzB,GAAGA,EAAM,MAAM,EAAG,KAAK,IAAI,EAAGmD,EAAM,CAAC,CAAC,CAAC,GAChD,CAEO,SAASC,GAAapD,EAAemD,EAI1C,CACA,OAAInD,EAAM,QAAUmD,EACX,CAAE,KAAMnD,EAAO,UAAW,GAAO,MAAOA,EAAM,MAAA,EAEhD,CACL,KAAMA,EAAM,MAAM,EAAG,KAAK,IAAI,EAAGmD,CAAG,CAAC,EACrC,UAAW,GACX,MAAOnD,EAAM,MAAA,CAEjB,CAEO,SAASqD,GAASrD,EAAesD,EAA0B,CAChE,MAAM,EAAI,OAAOtD,CAAK,EACtB,OAAO,OAAO,SAAS,CAAC,EAAI,EAAIsD,CAClC,CASA,MAAMC,GAAkB,gCAClBC,GAAmB,yBACnBC,GAAoB,8BAEnB,SAASC,GAAkB1D,EAAuB,CACvD,GAAI,CAACA,EAAO,OAAOA,EACnB,MAAM2D,EAAUH,GAAiB,KAAKxD,CAAK,EACrC4D,EAAWH,GAAkB,KAAKzD,CAAK,EAC7C,GAAI,CAAC2D,GAAW,CAACC,EAAU,OAAO5D,EAElC,GAAI2D,IAAYC,EACd,OAAKD,EACE3D,EAAM,QAAQwD,GAAkB,EAAE,EAAE,UAAA,EADtBxD,EAAM,QAAQyD,GAAmB,EAAE,EAAE,UAAA,EAI5D,GAAI,CAACF,GAAgB,KAAKvD,CAAK,EAAG,OAAOA,EACzCuD,GAAgB,UAAY,EAE5B,IAAIM,EAAS,GACTC,EAAY,EACZC,EAAa,GACjB,UAAWC,KAAShE,EAAM,SAASuD,EAAe,EAAG,CACnD,MAAMU,EAAMD,EAAM,OAAS,EACtBD,IACHF,GAAU7D,EAAM,MAAM8D,EAAWG,CAAG,GAGtCF,EAAa,CADDC,EAAM,CAAC,EAAE,YAAA,EACH,SAAS,GAAG,EAC9BF,EAAYG,EAAMD,EAAM,CAAC,EAAE,MAC7B,CACA,OAAKD,IACHF,GAAU7D,EAAM,MAAM8D,CAAS,GAE1BD,EAAO,UAAA,CAChB,CCrGA,MAAMK,GAAkB,mBAClBC,GAAoB,CACxB,UACA,WACA,WACA,SACA,QACA,UACA,WACA,QACA,SACA,OACA,gBACA,aACF,EAEA,SAASC,GAAwBC,EAAyB,CAExD,MADI,mCAAmC,KAAKA,CAAM,GAC9C,kCAAkC,KAAKA,CAAM,EAAU,GACpDF,GAAkB,KAAMG,GAAUD,EAAO,WAAW,GAAGC,CAAK,GAAG,CAAC,CACzE,CAEO,SAASC,GAAcC,EAAsB,CAClD,MAAMR,EAAQQ,EAAK,MAAMN,EAAe,EACxC,GAAI,CAACF,EAAO,OAAOQ,EACnB,MAAMH,EAASL,EAAM,CAAC,GAAK,GAC3B,OAAKI,GAAwBC,CAAM,EAC5BG,EAAK,MAAMR,EAAM,CAAC,EAAE,MAAM,EADYQ,CAE/C,CAEO,SAASC,GAAYC,EAAiC,CAC3D,MAAMtG,EAAIsG,EACJC,EAAO,OAAOvG,EAAE,MAAS,SAAWA,EAAE,KAAO,GAC7CwG,EAAUxG,EAAE,QAClB,GAAI,OAAOwG,GAAY,SAErB,OADkBD,IAAS,YAAcjB,GAAkBkB,CAAO,EAAIL,GAAcK,CAAO,EAG7F,GAAI,MAAM,QAAQA,CAAO,EAAG,CAC1B,MAAM3D,EAAQ2D,EACX,IAAKjH,GAAM,CACV,MAAMkH,EAAOlH,EACb,OAAIkH,EAAK,OAAS,QAAU,OAAOA,EAAK,MAAS,SAAiBA,EAAK,KAChE,IACT,CAAC,EACA,OAAQ3G,GAAmB,OAAOA,GAAM,QAAQ,EACnD,GAAI+C,EAAM,OAAS,EAAG,CACpB,MAAM6D,EAAS7D,EAAM,KAAK;AAAA,CAAI,EAE9B,OADkB0D,IAAS,YAAcjB,GAAkBoB,CAAM,EAAIP,GAAcO,CAAM,CAE3F,CACF,CACA,OAAI,OAAO1G,EAAE,MAAS,SACFuG,IAAS,YAAcjB,GAAkBtF,EAAE,IAAI,EAAImG,GAAcnG,EAAE,IAAI,EAGpF,IACT,CAEO,SAAS2G,GAAgBL,EAAiC,CAE/D,MAAME,EADIF,EACQ,QACZzD,EAAkB,CAAA,EACxB,GAAI,MAAM,QAAQ2D,CAAO,EACvB,UAAWjH,KAAKiH,EAAS,CACvB,MAAMC,EAAOlH,EACb,GAAIkH,EAAK,OAAS,YAAc,OAAOA,EAAK,UAAa,SAAU,CACjE,MAAMG,EAAUH,EAAK,SAAS,KAAA,EAC1BG,GAAS/D,EAAM,KAAK+D,CAAO,CACjC,CACF,CAEF,GAAI/D,EAAM,OAAS,EAAG,OAAOA,EAAM,KAAK;AAAA,CAAI,EAG5C,MAAMgE,EAAUC,GAAeR,CAAO,EACtC,GAAI,CAACO,EAAS,OAAO,KAMrB,MAAME,EALU,CACd,GAAGF,EAAQ,SACT,6DAAA,CACF,EAGC,IAAK7G,IAAOA,EAAE,CAAC,GAAK,IAAI,KAAA,CAAM,EAC9B,OAAO,OAAO,EACjB,OAAO+G,EAAU,OAAS,EAAIA,EAAU,KAAK;AAAA,CAAI,EAAI,IACvD,CAEO,SAASD,GAAeR,EAAiC,CAC9D,MAAMtG,EAAIsG,EACJE,EAAUxG,EAAE,QAClB,GAAI,OAAOwG,GAAY,SAAU,OAAOA,EACxC,GAAI,MAAM,QAAQA,CAAO,EAAG,CAC1B,MAAM3D,EAAQ2D,EACX,IAAKjH,GAAM,CACV,MAAMkH,EAAOlH,EACb,OAAIkH,EAAK,OAAS,QAAU,OAAOA,EAAK,MAAS,SAAiBA,EAAK,KAChE,IACT,CAAC,EACA,OAAQ3G,GAAmB,OAAOA,GAAM,QAAQ,EACnD,GAAI+C,EAAM,OAAS,EAAG,OAAOA,EAAM,KAAK;AAAA,CAAI,CAC9C,CACA,OAAI,OAAO7C,EAAE,MAAS,SAAiBA,EAAE,KAClC,IACT,CAEO,SAASgH,GAAwBZ,EAAsB,CAC5D,MAAMtE,EAAUsE,EAAK,KAAA,EACrB,GAAI,CAACtE,EAAS,MAAO,GACrB,MAAMmF,EAAQnF,EACX,MAAM,OAAO,EACb,IAAKoF,GAASA,EAAK,KAAA,CAAM,EACzB,OAAO,OAAO,EACd,IAAKA,GAAS,IAAIA,CAAI,GAAG,EAC5B,OAAOD,EAAM,OAAS,CAAC,eAAgB,GAAGA,CAAK,EAAE,KAAK;AAAA,CAAI,EAAI,EAChE,CChHA,SAASE,GAAcC,EAA2B,CAChDA,EAAM,CAAC,EAAKA,EAAM,CAAC,EAAI,GAAQ,GAC/BA,EAAM,CAAC,EAAKA,EAAM,CAAC,EAAI,GAAQ,IAE/B,IAAIC,EAAM,GACV,QAASpI,EAAI,EAAGA,EAAImI,EAAM,OAAQnI,IAChCoI,GAAOD,EAAMnI,CAAC,EAAG,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,EAG/C,MAAO,GAAGoI,EAAI,MAAM,EAAG,CAAC,CAAC,IAAIA,EAAI,MAAM,EAAG,EAAE,CAAC,IAAIA,EAAI,MAAM,GAAI,EAAE,CAAC,IAAIA,EAAI,MACxE,GACA,EAAA,CACD,IAAIA,EAAI,MAAM,EAAE,CAAC,EACpB,CAEA,SAASC,IAA8B,CACrC,MAAMF,EAAQ,IAAI,WAAW,EAAE,EACzBG,EAAM,KAAK,IAAA,EACjB,QAAStI,EAAI,EAAGA,EAAImI,EAAM,OAAQnI,IAAKmI,EAAMnI,CAAC,EAAI,KAAK,MAAM,KAAK,OAAA,EAAW,GAAG,EAChF,OAAAmI,EAAM,CAAC,GAAKG,EAAM,IAClBH,EAAM,CAAC,GAAMG,IAAQ,EAAK,IAC1BH,EAAM,CAAC,GAAMG,IAAQ,GAAM,IAC3BH,EAAM,CAAC,GAAMG,IAAQ,GAAM,IACpBH,CACT,CAEO,SAASI,GAAaC,EAAgC,WAAW,OAAgB,CACtF,GAAIA,GAAc,OAAOA,EAAW,YAAe,WAAY,OAAOA,EAAW,WAAA,EAEjF,GAAIA,GAAc,OAAOA,EAAW,iBAAoB,WAAY,CAClE,MAAML,EAAQ,IAAI,WAAW,EAAE,EAC/B,OAAAK,EAAW,gBAAgBL,CAAK,EACzBD,GAAcC,CAAK,CAC5B,CAEA,OAAOD,GAAcG,IAAiB,CACxC,CCdA,eAAsBI,GAAgBC,EAAkB,CACtD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,YAAc,GACpBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,eAAgB,CACtD,WAAYA,EAAM,WAClB,MAAO,GAAA,CACR,EACDA,EAAM,aAAe,MAAM,QAAQC,EAAI,QAAQ,EAAIA,EAAI,SAAW,CAAA,EAClED,EAAM,kBAAoBC,EAAI,eAAiB,IACjD,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,YAAc,EACtB,EACF,CAEA,eAAsBG,GAAgBH,EAAkBrB,EAAmC,CACzF,GAAI,CAACqB,EAAM,QAAU,CAACA,EAAM,UAAW,MAAO,GAC9C,MAAMI,EAAMzB,EAAQ,KAAA,EACpB,GAAI,CAACyB,EAAK,MAAO,GAEjB,MAAMR,EAAM,KAAK,IAAA,EACjBI,EAAM,aAAe,CACnB,GAAGA,EAAM,aACT,CACE,KAAM,OACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAMI,EAAK,EACrC,UAAWR,CAAA,CACb,EAGFI,EAAM,YAAc,GACpBA,EAAM,UAAY,KAClB,MAAMK,EAAQR,GAAA,EACdG,EAAM,UAAYK,EAClBL,EAAM,WAAa,GACnBA,EAAM,oBAAsBJ,EAC5B,GAAI,CACF,aAAMI,EAAM,OAAO,QAAQ,YAAa,CACtC,WAAYA,EAAM,WAClB,QAASI,EACT,QAAS,GACT,eAAgBC,CAAA,CACjB,EACM,EACT,OAASH,EAAK,CACZ,MAAMI,EAAQ,OAAOJ,CAAG,EACxB,OAAAF,EAAM,UAAY,KAClBA,EAAM,WAAa,KACnBA,EAAM,oBAAsB,KAC5BA,EAAM,UAAYM,EAClBN,EAAM,aAAe,CACnB,GAAGA,EAAM,aACT,CACE,KAAM,YACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,UAAYM,EAAO,EACnD,UAAW,KAAK,IAAA,CAAI,CACtB,EAEK,EACT,QAAA,CACEN,EAAM,YAAc,EACtB,CACF,CAEA,eAAsBO,GAAaP,EAAoC,CACrE,GAAI,CAACA,EAAM,QAAU,CAACA,EAAM,UAAW,MAAO,GAC9C,MAAMK,EAAQL,EAAM,UACpB,GAAI,CACF,aAAMA,EAAM,OAAO,QACjB,aACAK,EACI,CAAE,WAAYL,EAAM,WAAY,MAAAK,GAChC,CAAE,WAAYL,EAAM,UAAA,CAAW,EAE9B,EACT,OAASE,EAAK,CACZ,OAAAF,EAAM,UAAY,OAAOE,CAAG,EACrB,EACT,CACF,CAEO,SAASM,GACdR,EACAS,EACA,CAGA,GAFI,CAACA,GACDA,EAAQ,aAAeT,EAAM,YAC7BS,EAAQ,OAAST,EAAM,WAAaS,EAAQ,QAAUT,EAAM,UAC9D,OAAO,KAET,GAAIS,EAAQ,QAAU,QAAS,CAC7B,MAAM1F,EAAO2D,GAAY+B,EAAQ,OAAO,EACxC,GAAI,OAAO1F,GAAS,SAAU,CAC5B,MAAM2F,EAAUV,EAAM,YAAc,IAChC,CAACU,GAAW3F,EAAK,QAAU2F,EAAQ,UACrCV,EAAM,WAAajF,EAEvB,CACF,MAAW0F,EAAQ,QAAU,SAIlBA,EAAQ,QAAU,WAH3BT,EAAM,WAAa,KACnBA,EAAM,UAAY,KAClBA,EAAM,oBAAsB,MAKnBS,EAAQ,QAAU,UAC3BT,EAAM,WAAa,KACnBA,EAAM,UAAY,KAClBA,EAAM,oBAAsB,KAC5BA,EAAM,UAAYS,EAAQ,cAAgB,cAE5C,OAAOA,EAAQ,KACjB,CC/HA,eAAsBE,GAAaX,EAAsB,CACvD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,gBACV,CAAAA,EAAM,gBAAkB,GACxBA,EAAM,cAAgB,KACtB,GAAI,CACF,MAAMY,EAAkC,CACtC,cAAeZ,EAAM,sBACrB,eAAgBA,EAAM,sBAAA,EAElBa,EAAgBvD,GAAS0C,EAAM,qBAAsB,CAAC,EACtDc,EAAQxD,GAAS0C,EAAM,oBAAqB,CAAC,EAC/Ca,EAAgB,IAAGD,EAAO,cAAgBC,GAC1CC,EAAQ,IAAGF,EAAO,MAAQE,GAC9B,MAAMb,EAAO,MAAMD,EAAM,OAAO,QAAQ,gBAAiBY,CAAM,EAG3DX,MAAW,eAAiBA,EAClC,OAASC,EAAK,CACZF,EAAM,cAAgB,OAAOE,CAAG,CAClC,QAAA,CACEF,EAAM,gBAAkB,EAC1B,EACF,CAEA,eAAsBe,GACpBf,EACAgB,EACAC,EAMA,CACA,GAAI,CAACjB,EAAM,QAAU,CAACA,EAAM,UAAW,OACvC,MAAMY,EAAkC,CAAE,IAAAI,CAAA,EACtC,UAAWC,IAAOL,EAAO,MAAQK,EAAM,OACvC,kBAAmBA,IAAOL,EAAO,cAAgBK,EAAM,eACvD,iBAAkBA,IAAOL,EAAO,aAAeK,EAAM,cACrD,mBAAoBA,IAAOL,EAAO,eAAiBK,EAAM,gBAC7D,GAAI,CACF,MAAMjB,EAAM,OAAO,QAAQ,iBAAkBY,CAAM,EACnD,MAAMD,GAAaX,CAAK,CAC1B,OAASE,EAAK,CACZF,EAAM,cAAgB,OAAOE,CAAG,CAClC,CACF,CAEA,eAAsBgB,GAAclB,EAAsBgB,EAAa,CAMrE,GALI,GAAChB,EAAM,QAAU,CAACA,EAAM,WACxBA,EAAM,iBAIN,CAHc,OAAO,QACvB,mBAAmBgB,CAAG;AAAA;AAAA,uDAAA,GAGxB,CAAAhB,EAAM,gBAAkB,GACxBA,EAAM,cAAgB,KACtB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,kBAAmB,CAAE,IAAAgB,EAAK,iBAAkB,GAAM,EAC7E,MAAML,GAAaX,CAAK,CAC1B,OAASE,EAAK,CACZF,EAAM,cAAgB,OAAOE,CAAG,CAClC,QAAA,CACEF,EAAM,gBAAkB,EAC1B,EACF,CChFA,MAAMmB,GAAoB,GACpBC,GAA0B,GAC1BC,GAAyB,KAgC/B,SAASC,GAAsBrH,EAA+B,CAC5D,GAAI,CAACA,GAAS,OAAOA,GAAU,SAAU,OAAO,KAChD,MAAMsH,EAAStH,EACf,GAAI,OAAOsH,EAAO,MAAS,gBAAiBA,EAAO,KACnD,MAAM1C,EAAU0C,EAAO,QACvB,GAAI,CAAC,MAAM,QAAQ1C,CAAO,EAAG,OAAO,KACpC,MAAM3D,EAAQ2D,EACX,IAAKC,GAAS,CACb,GAAI,CAACA,GAAQ,OAAOA,GAAS,SAAU,OAAO,KAC9C,MAAM0C,EAAQ1C,EACd,OAAI0C,EAAM,OAAS,QAAU,OAAOA,EAAM,MAAS,SAAiBA,EAAM,KACnE,IACT,CAAC,EACA,OAAQC,GAAyB,EAAQA,CAAK,EACjD,OAAIvG,EAAM,SAAW,EAAU,KACxBA,EAAM,KAAK;AAAA,CAAI,CACxB,CAEA,SAASwG,GAAiBzH,EAA+B,CACvD,GAAIA,GAAU,KAA6B,OAAO,KAClD,GAAI,OAAOA,GAAU,UAAY,OAAOA,GAAU,UAChD,OAAO,OAAOA,CAAK,EAErB,MAAM0H,EAAcL,GAAsBrH,CAAK,EAC/C,IAAIwE,EACJ,GAAI,OAAOxE,GAAU,SACnBwE,EAAOxE,UACE0H,EACTlD,EAAOkD,MAEP,IAAI,CACFlD,EAAO,KAAK,UAAUxE,EAAO,KAAM,CAAC,CACtC,MAAQ,CACNwE,EAAO,OAAOxE,CAAK,CACrB,CAEF,MAAM2H,EAAYvE,GAAaoB,EAAM4C,EAAsB,EAC3D,OAAKO,EAAU,UACR,GAAGA,EAAU,IAAI;AAAA;AAAA,eAAoBA,EAAU,KAAK,yBAAyBA,EAAU,KAAK,MAAM,KADxEA,EAAU,IAE7C,CAEA,SAASC,GAAuBL,EAAiD,CAC/E,MAAM3C,EAA0C,CAAA,EAChD,OAAAA,EAAQ,KAAK,CACX,KAAM,WACN,KAAM2C,EAAM,KACZ,UAAWA,EAAM,MAAQ,CAAA,CAAC,CAC3B,EACGA,EAAM,QACR3C,EAAQ,KAAK,CACX,KAAM,aACN,KAAM2C,EAAM,KACZ,KAAMA,EAAM,MAAA,CACb,EAEI,CACL,KAAM,YACN,WAAYA,EAAM,WAClB,MAAOA,EAAM,MACb,QAAA3C,EACA,UAAW2C,EAAM,SAAA,CAErB,CAEA,SAASM,GAAeC,EAAsB,CAC5C,GAAIA,EAAK,gBAAgB,QAAUZ,GAAmB,OACtD,MAAMa,EAAWD,EAAK,gBAAgB,OAASZ,GACzCc,EAAUF,EAAK,gBAAgB,OAAO,EAAGC,CAAQ,EACvD,UAAWE,KAAMD,EAASF,EAAK,eAAe,OAAOG,CAAE,CACzD,CAEA,SAASC,GAAuBJ,EAAsB,CACpDA,EAAK,iBAAmBA,EAAK,gBAC1B,IAAKG,GAAOH,EAAK,eAAe,IAAIG,CAAE,GAAG,OAAO,EAChD,OAAQ9B,GAAwC,EAAQA,CAAI,CACjE,CAEO,SAASgC,GAAoBL,EAAsB,CACpDA,EAAK,qBAAuB,OAC9B,aAAaA,EAAK,mBAAmB,EACrCA,EAAK,oBAAsB,MAE7BI,GAAuBJ,CAAI,CAC7B,CAEO,SAASM,GAAuBN,EAAsBO,EAAQ,GAAO,CAC1E,GAAIA,EAAO,CACTF,GAAoBL,CAAI,EACxB,MACF,CACIA,EAAK,qBAAuB,OAChCA,EAAK,oBAAsB,OAAO,WAChC,IAAMK,GAAoBL,CAAI,EAC9BX,EAAA,EAEJ,CAEO,SAASmB,GAAgBR,EAAsB,CACpDA,EAAK,eAAe,MAAA,EACpBA,EAAK,gBAAkB,CAAA,EACvBA,EAAK,iBAAmB,CAAA,EACxBK,GAAoBL,CAAI,CAC1B,CAEO,SAASS,GAAiBT,EAAsBtB,EAA6B,CAClF,GAAI,CAACA,GAAWA,EAAQ,SAAW,OAAQ,OAC3C,MAAMxF,EACJ,OAAOwF,EAAQ,YAAe,SAAWA,EAAQ,WAAa,OAKhE,GAJIxF,GAAcA,IAAe8G,EAAK,YAElC,CAAC9G,GAAc8G,EAAK,WAAatB,EAAQ,QAAUsB,EAAK,WACxDA,EAAK,WAAatB,EAAQ,QAAUsB,EAAK,WACzC,CAACA,EAAK,UAAW,OAErB,MAAMU,EAAOhC,EAAQ,MAAQ,CAAA,EACvBiC,EAAa,OAAOD,EAAK,YAAe,SAAWA,EAAK,WAAa,GAC3E,GAAI,CAACC,EAAY,OACjB,MAAMpI,EAAO,OAAOmI,EAAK,MAAS,SAAWA,EAAK,KAAO,OACnDE,EAAQ,OAAOF,EAAK,OAAU,SAAWA,EAAK,MAAQ,GACtDG,EAAOD,IAAU,QAAUF,EAAK,KAAO,OACvCI,EACJF,IAAU,SACNjB,GAAiBe,EAAK,aAAa,EACnCE,IAAU,SACRjB,GAAiBe,EAAK,MAAM,EAC5B,OAEF7C,EAAM,KAAK,IAAA,EACjB,IAAI4B,EAAQO,EAAK,eAAe,IAAIW,CAAU,EACzClB,GAeHA,EAAM,KAAOlH,EACTsI,IAAS,SAAWpB,EAAM,KAAOoB,GACjCC,IAAW,SAAWrB,EAAM,OAASqB,GACzCrB,EAAM,UAAY5B,IAjBlB4B,EAAQ,CACN,WAAAkB,EACA,MAAOjC,EAAQ,MACf,WAAAxF,EACA,KAAAX,EACA,KAAAsI,EACA,OAAAC,EACA,UAAW,OAAOpC,EAAQ,IAAO,SAAWA,EAAQ,GAAKb,EACzD,UAAWA,EACX,QAAS,CAAA,CAAC,EAEZmC,EAAK,eAAe,IAAIW,EAAYlB,CAAK,EACzCO,EAAK,gBAAgB,KAAKW,CAAU,GAQtClB,EAAM,QAAUK,GAAuBL,CAAK,EAC5CM,GAAeC,CAAI,EACnBM,GAAuBN,EAAMY,IAAU,QAAQ,CACjD,CChLO,SAASG,GAAmBf,EAAkBO,EAAQ,GAAO,CAC9DP,EAAK,iBAAiB,qBAAqBA,EAAK,eAAe,EAC/DA,EAAK,mBAAqB,OAC5B,aAAaA,EAAK,iBAAiB,EACnCA,EAAK,kBAAoB,MAE3B,MAAMgB,EAAmB,IAAM,CAC7B,MAAMC,EAAYjB,EAAK,cAAc,cAAc,EACnD,GAAIiB,EAAW,CACb,MAAMC,EAAY,iBAAiBD,CAAS,EAAE,UAK9C,GAHEC,IAAc,QACdA,IAAc,UACdD,EAAU,aAAeA,EAAU,aAAe,EACrC,OAAOA,CACxB,CACA,OAAQ,SAAS,kBAAoB,SAAS,eAChD,EAEKjB,EAAK,eAAe,KAAK,IAAM,CAClCA,EAAK,gBAAkB,sBAAsB,IAAM,CACjDA,EAAK,gBAAkB,KACvB,MAAMmB,EAASH,EAAA,EACf,GAAI,CAACG,EAAQ,OACb,MAAMC,EACJD,EAAO,aAAeA,EAAO,UAAYA,EAAO,aAElD,GAAI,EADgBZ,GAASP,EAAK,oBAAsBoB,EAAqB,KAC3D,OACdb,MAAY,oBAAsB,IACtCY,EAAO,UAAYA,EAAO,aAC1BnB,EAAK,mBAAqB,GAC1B,MAAMqB,EAAad,EAAQ,IAAM,IACjCP,EAAK,kBAAoB,OAAO,WAAW,IAAM,CAC/CA,EAAK,kBAAoB,KACzB,MAAMsB,EAASN,EAAA,EACf,GAAI,CAACM,EAAQ,OACb,MAAMC,EACJD,EAAO,aAAeA,EAAO,UAAYA,EAAO,cAEhDf,GAASP,EAAK,oBAAsBuB,EAA2B,OAEjED,EAAO,UAAYA,EAAO,aAC1BtB,EAAK,mBAAqB,GAC5B,EAAGqB,CAAU,CACf,CAAC,CACH,CAAC,CACH,CAEO,SAASG,GAAmBxB,EAAkBO,EAAQ,GAAO,CAC9DP,EAAK,iBAAiB,qBAAqBA,EAAK,eAAe,EAC9DA,EAAK,eAAe,KAAK,IAAM,CAClCA,EAAK,gBAAkB,sBAAsB,IAAM,CACjDA,EAAK,gBAAkB,KACvB,MAAMiB,EAAYjB,EAAK,cAAc,aAAa,EAClD,GAAI,CAACiB,EAAW,OAChB,MAAMG,EACJH,EAAU,aAAeA,EAAU,UAAYA,EAAU,cACvCV,GAASa,EAAqB,MAElDH,EAAU,UAAYA,EAAU,aAClC,CAAC,CACH,CAAC,CACH,CAEO,SAASQ,GAAiBzB,EAAkB0B,EAAc,CAC/D,MAAMT,EAAYS,EAAM,cACxB,GAAI,CAACT,EAAW,OAChB,MAAMG,EACJH,EAAU,aAAeA,EAAU,UAAYA,EAAU,aAC3DjB,EAAK,mBAAqBoB,EAAqB,GACjD,CAEO,SAASO,GAAiB3B,EAAkB0B,EAAc,CAC/D,MAAMT,EAAYS,EAAM,cACxB,GAAI,CAACT,EAAW,OAChB,MAAMG,EACJH,EAAU,aAAeA,EAAU,UAAYA,EAAU,aAC3DjB,EAAK,aAAeoB,EAAqB,EAC3C,CAEO,SAASQ,GAAgB5B,EAAkB,CAChDA,EAAK,oBAAsB,GAC3BA,EAAK,mBAAqB,EAC5B,CAEO,SAAS6B,GAAWtE,EAAiBf,EAAe,CACzD,GAAIe,EAAM,SAAW,EAAG,OACxB,MAAMuE,EAAO,IAAI,KAAK,CAAC,GAAGvE,EAAM,KAAK;AAAA,CAAI,CAAC;AAAA,CAAI,EAAG,CAAE,KAAM,aAAc,EACjEwE,EAAM,IAAI,gBAAgBD,CAAI,EAC9BE,EAAS,SAAS,cAAc,GAAG,EACnCC,EAAQ,IAAI,KAAA,EAAO,YAAA,EAAc,MAAM,EAAG,EAAE,EAAE,QAAQ,QAAS,GAAG,EACxED,EAAO,KAAOD,EACdC,EAAO,SAAW,iBAAiBxF,CAAK,IAAIyF,CAAK,OACjDD,EAAO,MAAA,EACP,IAAI,gBAAgBD,CAAG,CACzB,CAEO,SAASG,GAAclC,EAAkB,CAC9C,GAAI,OAAO,eAAmB,IAAa,OAC3C,MAAMmC,EAASnC,EAAK,cAAc,SAAS,EAC3C,GAAI,CAACmC,EAAQ,OACb,MAAMC,EAAS,IAAM,CACnB,KAAM,CAAE,OAAAC,CAAA,EAAWF,EAAO,sBAAA,EAC1BnC,EAAK,MAAM,YAAY,kBAAmB,GAAGqC,CAAM,IAAI,CACzD,EACAD,EAAA,EACApC,EAAK,eAAiB,IAAI,eAAe,IAAMoC,GAAQ,EACvDpC,EAAK,eAAe,QAAQmC,CAAM,CACpC,CCzHO,SAASG,GAAqBpK,EAAa,CAChD,OAAI,OAAO,iBAAoB,WACtB,gBAAgBA,CAAK,EAEvB,KAAK,MAAM,KAAK,UAAUA,CAAK,CAAC,CACzC,CAEO,SAASqK,GAAoBC,EAAuC,CACzE,MAAO,GAAG,KAAK,UAAUA,EAAM,KAAM,CAAC,EAAE,SAAS;AAAA,CACnD,CAEO,SAASC,GACdC,EACAhJ,EACAxB,EACA,CACA,GAAIwB,EAAK,SAAW,EAAG,OACvB,IAAIiF,EAA+C+D,EACnD,QAASnN,EAAI,EAAGA,EAAImE,EAAK,OAAS,EAAGnE,GAAK,EAAG,CAC3C,MAAM0J,EAAMvF,EAAKnE,CAAC,EACZoN,EAAUjJ,EAAKnE,EAAI,CAAC,EAC1B,GAAI,OAAO0J,GAAQ,SAAU,CAC3B,GAAI,CAAC,MAAM,QAAQN,CAAO,EAAG,OACzBA,EAAQM,CAAG,GAAK,OAClBN,EAAQM,CAAG,EACT,OAAO0D,GAAY,SAAW,CAAA,EAAM,CAAA,GAExChE,EAAUA,EAAQM,CAAG,CACvB,KAAO,CACL,GAAI,OAAON,GAAY,UAAYA,GAAW,KAAM,OACpD,MAAMa,EAASb,EACXa,EAAOP,CAAG,GAAK,OACjBO,EAAOP,CAAG,EACR,OAAO0D,GAAY,SAAW,CAAA,EAAM,CAAA,GAExChE,EAAUa,EAAOP,CAAG,CACtB,CACF,CACA,MAAM2D,EAAUlJ,EAAKA,EAAK,OAAS,CAAC,EACpC,GAAI,OAAOkJ,GAAY,SAAU,CAC3B,MAAM,QAAQjE,CAAO,IAAGA,EAAQiE,CAAO,EAAI1K,GAC/C,MACF,CACI,OAAOyG,GAAY,UAAYA,GAAW,OAC3CA,EAAoCiE,CAAO,EAAI1K,EAEpD,CAEO,SAAS2K,GACdH,EACAhJ,EACA,CACA,GAAIA,EAAK,SAAW,EAAG,OACvB,IAAIiF,EAA+C+D,EACnD,QAAS,EAAI,EAAG,EAAIhJ,EAAK,OAAS,EAAG,GAAK,EAAG,CAC3C,MAAMuF,EAAMvF,EAAK,CAAC,EAClB,GAAI,OAAOuF,GAAQ,SAAU,CAC3B,GAAI,CAAC,MAAM,QAAQN,CAAO,EAAG,OAC7BA,EAAUA,EAAQM,CAAG,CACvB,KAAO,CACL,GAAI,OAAON,GAAY,UAAYA,GAAW,KAAM,OACpDA,EAAWA,EAAoCM,CAAG,CAGpD,CACA,GAAIN,GAAW,KAAM,MACvB,CACA,MAAMiE,EAAUlJ,EAAKA,EAAK,OAAS,CAAC,EACpC,GAAI,OAAOkJ,GAAY,SAAU,CAC3B,MAAM,QAAQjE,CAAO,GAAGA,EAAQ,OAAOiE,EAAS,CAAC,EACrD,MACF,CACI,OAAOjE,GAAY,UAAYA,GAAW,MAC5C,OAAQA,EAAoCiE,CAAO,CAEvD,CCpCA,eAAsBE,GAAW7E,EAAoB,CACnD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,cAAgB,GACtBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,aAAc,EAAE,EACxD8E,GAAoB9E,EAAOC,CAAG,CAChC,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,cAAgB,EACxB,EACF,CAEA,eAAsB+E,GAAiB/E,EAAoB,CACzD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,oBACV,CAAAA,EAAM,oBAAsB,GAC5B,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAC9B,gBACA,CAAA,CAAC,EAEHgF,GAAkBhF,EAAOC,CAAG,CAC9B,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,oBAAsB,EAC9B,EACF,CAEO,SAASgF,GACdhF,EACAC,EACA,CACAD,EAAM,aAAeC,EAAI,QAAU,KACnCD,EAAM,cAAgBC,EAAI,SAAW,CAAA,EACrCD,EAAM,oBAAsBC,EAAI,SAAW,IAC7C,CAEO,SAAS6E,GAAoB9E,EAAoBiF,EAA0B,CAChFjF,EAAM,eAAiBiF,EACvB,MAAMC,EACJ,OAAOD,EAAS,KAAQ,SACpBA,EAAS,IACTA,EAAS,QAAU,OAAOA,EAAS,QAAW,SAC5CX,GAAoBW,EAAS,MAAiC,EAC9DjF,EAAM,UACV,CAACA,EAAM,iBAAmBA,EAAM,iBAAmB,MACrDA,EAAM,UAAYkF,EACTlF,EAAM,WACfA,EAAM,UAAYsE,GAAoBtE,EAAM,UAAU,EAEtDA,EAAM,UAAYkF,EAEpBlF,EAAM,YAAc,OAAOiF,EAAS,OAAU,UAAYA,EAAS,MAAQ,KAC3EjF,EAAM,aAAe,MAAM,QAAQiF,EAAS,MAAM,EAAIA,EAAS,OAAS,CAAA,EAEnEjF,EAAM,kBACTA,EAAM,WAAaqE,GAAkBY,EAAS,QAAU,CAAA,CAAE,EAC1DjF,EAAM,mBAAqBqE,GAAkBY,EAAS,QAAU,CAAA,CAAE,EAEtE,CAEA,eAAsBE,GAAWnF,EAAoB,CACnD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,aAAe,GACrBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMpF,EACJoF,EAAM,iBAAmB,QAAUA,EAAM,WACrCsE,GAAoBtE,EAAM,UAAU,EACpCA,EAAM,UACNoF,EAAWpF,EAAM,gBAAgB,KACvC,GAAI,CAACoF,EAAU,CACbpF,EAAM,UAAY,yCAClB,MACF,CACA,MAAMA,EAAM,OAAO,QAAQ,aAAc,CAAE,IAAApF,EAAK,SAAAwK,EAAU,EAC1DpF,EAAM,gBAAkB,GACxB,MAAM6E,GAAW7E,CAAK,CACxB,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,aAAe,EACvB,EACF,CAEA,eAAsBqF,GAAYrF,EAAoB,CACpD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,eAAiB,GACvBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMpF,EACJoF,EAAM,iBAAmB,QAAUA,EAAM,WACrCsE,GAAoBtE,EAAM,UAAU,EACpCA,EAAM,UACNoF,EAAWpF,EAAM,gBAAgB,KACvC,GAAI,CAACoF,EAAU,CACbpF,EAAM,UAAY,yCAClB,MACF,CACA,MAAMA,EAAM,OAAO,QAAQ,eAAgB,CACzC,IAAApF,EACA,SAAAwK,EACA,WAAYpF,EAAM,eAAA,CACnB,EACDA,EAAM,gBAAkB,GACxB,MAAM6E,GAAW7E,CAAK,CACxB,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,eAAiB,EACzB,EACF,CAEA,eAAsBsF,GAAUtF,EAAoB,CAClD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,cAAgB,GACtBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,aAAc,CACvC,WAAYA,EAAM,eAAA,CACnB,CACH,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,cAAgB,EACxB,EACF,CAEO,SAASuF,GACdvF,EACAvE,EACAxB,EACA,CACA,MAAM2B,EAAOyI,GACXrE,EAAM,YAAcA,EAAM,gBAAgB,QAAU,CAAA,CAAC,EAEvDwE,GAAa5I,EAAMH,EAAMxB,CAAK,EAC9B+F,EAAM,WAAapE,EACnBoE,EAAM,gBAAkB,GACpBA,EAAM,iBAAmB,SAC3BA,EAAM,UAAYsE,GAAoB1I,CAAI,EAE9C,CAEO,SAAS4J,GACdxF,EACAvE,EACA,CACA,MAAMG,EAAOyI,GACXrE,EAAM,YAAcA,EAAM,gBAAgB,QAAU,CAAA,CAAC,EAEvD4E,GAAgBhJ,EAAMH,CAAI,EAC1BuE,EAAM,WAAapE,EACnBoE,EAAM,gBAAkB,GACpBA,EAAM,iBAAmB,SAC3BA,EAAM,UAAYsE,GAAoB1I,CAAI,EAE9C,CCrLA,eAAsB6J,GAAezF,EAAkB,CACrD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,cAAe,EAAE,EACzDA,EAAM,WAAaC,CACrB,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,CACF,CAEA,eAAsBwF,GAAa1F,EAAkB,CACnD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,YACV,CAAAA,EAAM,YAAc,GACpBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,YAAa,CACnD,gBAAiB,EAAA,CAClB,EACDA,EAAM,SAAW,MAAM,QAAQC,EAAI,IAAI,EAAIA,EAAI,KAAO,CAAA,CACxD,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,YAAc,EACtB,EACF,CAEO,SAAS2F,GAAkBpB,EAAqB,CACrD,GAAIA,EAAK,eAAiB,KAAM,CAC9B,MAAM7H,EAAK,KAAK,MAAM6H,EAAK,UAAU,EACrC,GAAI,CAAC,OAAO,SAAS7H,CAAE,EAAG,MAAM,IAAI,MAAM,mBAAmB,EAC7D,MAAO,CAAE,KAAM,KAAe,KAAMA,CAAA,CACtC,CACA,GAAI6H,EAAK,eAAiB,QAAS,CACjC,MAAMqB,EAAStI,GAASiH,EAAK,YAAa,CAAC,EAC3C,GAAIqB,GAAU,EAAG,MAAM,IAAI,MAAM,0BAA0B,EAC3D,MAAMC,EAAOtB,EAAK,UAElB,MAAO,CAAE,KAAM,QAAkB,QAASqB,GAD7BC,IAAS,UAAY,IAASA,IAAS,QAAU,KAAY,MACvB,CACrD,CACA,MAAMC,EAAOvB,EAAK,SAAS,KAAA,EAC3B,GAAI,CAACuB,EAAM,MAAM,IAAI,MAAM,2BAA2B,EACtD,MAAO,CAAE,KAAM,OAAiB,KAAAA,EAAM,GAAIvB,EAAK,OAAO,KAAA,GAAU,MAAA,CAClE,CAEO,SAASwB,GAAiBxB,EAAqB,CACpD,GAAIA,EAAK,cAAgB,cAAe,CACtC,MAAM9F,EAAO8F,EAAK,YAAY,KAAA,EAC9B,GAAI,CAAC9F,EAAM,MAAM,IAAI,MAAM,6BAA6B,EACxD,MAAO,CAAE,KAAM,cAAwB,KAAAA,CAAA,CACzC,CACA,MAAME,EAAU4F,EAAK,YAAY,KAAA,EACjC,GAAI,CAAC5F,EAAS,MAAM,IAAI,MAAM,yBAAyB,EACvD,MAAM8B,EAOF,CAAE,KAAM,YAAa,QAAA9B,CAAA,EACrB4F,EAAK,UAAS9D,EAAQ,QAAU,IAChC8D,EAAK,UAAS9D,EAAQ,QAAU8D,EAAK,SACrCA,EAAK,GAAG,KAAA,MAAgB,GAAKA,EAAK,GAAG,KAAA,GACzC,MAAMyB,EAAiB1I,GAASiH,EAAK,eAAgB,CAAC,EACtD,OAAIyB,EAAiB,IAAGvF,EAAQ,eAAiBuF,GAC1CvF,CACT,CAEA,eAAsBwF,GAAWjG,EAAkB,CACjD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,UAC/C,CAAAA,EAAM,SAAW,GACjBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMkG,EAAWP,GAAkB3F,EAAM,QAAQ,EAC3CS,EAAUsF,GAAiB/F,EAAM,QAAQ,EACzC7E,EAAU6E,EAAM,SAAS,QAAQ,KAAA,EACjCmG,EAAM,CACV,KAAMnG,EAAM,SAAS,KAAK,KAAA,EAC1B,YAAaA,EAAM,SAAS,YAAY,QAAU,OAClD,QAAS7E,GAAW,OACpB,QAAS6E,EAAM,SAAS,QACxB,SAAAkG,EACA,cAAelG,EAAM,SAAS,cAC9B,SAAUA,EAAM,SAAS,SACzB,QAAAS,EACA,UACET,EAAM,SAAS,iBAAiB,KAAA,GAChCA,EAAM,SAAS,gBAAkB,WAC7B,CAAE,iBAAkBA,EAAM,SAAS,iBAAiB,KAAA,GACpD,MAAA,EAER,GAAI,CAACmG,EAAI,KAAM,MAAM,IAAI,MAAM,gBAAgB,EAC/C,MAAMnG,EAAM,OAAO,QAAQ,WAAYmG,CAAG,EAC1CnG,EAAM,SAAW,CACf,GAAGA,EAAM,SACT,KAAM,GACN,YAAa,GACb,YAAa,EAAA,EAEf,MAAM0F,GAAa1F,CAAK,EACxB,MAAMyF,GAAezF,CAAK,CAC5B,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,SAAW,EACnB,EACF,CAEA,eAAsBoG,GACpBpG,EACAmG,EACAE,EACA,CACA,GAAI,GAACrG,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,UAC/C,CAAAA,EAAM,SAAW,GACjBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,cAAe,CAAE,GAAImG,EAAI,GAAI,MAAO,CAAE,QAAAE,CAAA,CAAQ,CAAG,EAC5E,MAAMX,GAAa1F,CAAK,EACxB,MAAMyF,GAAezF,CAAK,CAC5B,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,SAAW,EACnB,EACF,CAEA,eAAsBsG,GAAWtG,EAAkBmG,EAAc,CAC/D,GAAI,GAACnG,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,UAC/C,CAAAA,EAAM,SAAW,GACjBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,WAAY,CAAE,GAAImG,EAAI,GAAI,KAAM,QAAS,EACpE,MAAMI,GAAavG,EAAOmG,EAAI,EAAE,CAClC,OAASjG,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,SAAW,EACnB,EACF,CAEA,eAAsBwG,GAAcxG,EAAkBmG,EAAc,CAClE,GAAI,GAACnG,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,UAC/C,CAAAA,EAAM,SAAW,GACjBA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,cAAe,CAAE,GAAImG,EAAI,GAAI,EACpDnG,EAAM,gBAAkBmG,EAAI,KAC9BnG,EAAM,cAAgB,KACtBA,EAAM,SAAW,CAAA,GAEnB,MAAM0F,GAAa1F,CAAK,EACxB,MAAMyF,GAAezF,CAAK,CAC5B,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,SAAW,EACnB,EACF,CAEA,eAAsBuG,GAAavG,EAAkByG,EAAe,CAClE,GAAI,GAACzG,EAAM,QAAU,CAACA,EAAM,WAC5B,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,YAAa,CACnD,GAAIyG,EACJ,MAAO,EAAA,CACR,EACDzG,EAAM,cAAgByG,EACtBzG,EAAM,SAAW,MAAM,QAAQC,EAAI,OAAO,EAAIA,EAAI,QAAU,CAAA,CAC9D,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,CACF,CC1LA,eAAsBwG,GAAa1G,EAAsB2G,EAAgB,CACvE,GAAI,GAAC3G,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,gBACV,CAAAA,EAAM,gBAAkB,GACxBA,EAAM,cAAgB,KACtB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,kBAAmB,CACzD,MAAA2G,EACA,UAAW,GAAA,CACZ,EACD3G,EAAM,iBAAmBC,EACzBD,EAAM,oBAAsB,KAAK,IAAA,CACnC,OAASE,EAAK,CACZF,EAAM,cAAgB,OAAOE,CAAG,CAClC,QAAA,CACEF,EAAM,gBAAkB,EAC1B,EACF,CAEA,eAAsB4G,GAAmB5G,EAAsBsC,EAAgB,CAC7E,GAAI,GAACtC,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,cAC/C,CAAAA,EAAM,aAAe,GACrB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,kBAAmB,CACzD,MAAAsC,EACA,UAAW,GAAA,CACZ,EACDtC,EAAM,qBAAuBC,EAAI,SAAW,KAC5CD,EAAM,uBAAyBC,EAAI,WAAa,KAChDD,EAAM,uBAAyB,IACjC,OAASE,EAAK,CACZF,EAAM,qBAAuB,OAAOE,CAAG,EACvCF,EAAM,uBAAyB,KAC/BA,EAAM,uBAAyB,IACjC,QAAA,CACEA,EAAM,aAAe,EACvB,EACF,CAEA,eAAsB6G,GAAkB7G,EAAsB,CAC5D,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,cAC/C,CAAAA,EAAM,aAAe,GACrB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,iBAAkB,CACxD,UAAW,IAAA,CACZ,EACDA,EAAM,qBAAuBC,EAAI,SAAW,KAC5CD,EAAM,uBAAyBC,EAAI,WAAa,KAC5CA,EAAI,YAAWD,EAAM,uBAAyB,KACpD,OAASE,EAAK,CACZF,EAAM,qBAAuB,OAAOE,CAAG,EACvCF,EAAM,uBAAyB,IACjC,QAAA,CACEA,EAAM,aAAe,EACvB,EACF,CAEA,eAAsB8G,GAAe9G,EAAsB,CACzD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAAaA,EAAM,cAC/C,CAAAA,EAAM,aAAe,GACrB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,kBAAmB,CAAE,QAAS,WAAY,EACrEA,EAAM,qBAAuB,cAC7BA,EAAM,uBAAyB,KAC/BA,EAAM,uBAAyB,IACjC,OAASE,EAAK,CACZF,EAAM,qBAAuB,OAAOE,CAAG,CACzC,QAAA,CACEF,EAAM,aAAe,EACvB,EACF,CC1DA,eAAsB+G,GAAU/G,EAAmB,CACjD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,aACV,CAAAA,EAAM,aAAe,GACrB,GAAI,CACF,KAAM,CAACgH,EAAQC,EAAQC,EAAQC,CAAS,EAAI,MAAM,QAAQ,IAAI,CAC5DnH,EAAM,OAAO,QAAQ,SAAU,CAAA,CAAE,EACjCA,EAAM,OAAO,QAAQ,SAAU,CAAA,CAAE,EACjCA,EAAM,OAAO,QAAQ,cAAe,CAAA,CAAE,EACtCA,EAAM,OAAO,QAAQ,iBAAkB,CAAA,CAAE,CAAA,CAC1C,EACDA,EAAM,YAAcgH,EACpBhH,EAAM,YAAciH,EACpB,MAAMG,EAAeF,EACrBlH,EAAM,YAAc,MAAM,QAAQoH,GAAc,MAAM,EAClDA,GAAc,OACd,CAAA,EACJpH,EAAM,eAAiBmH,CACzB,OAASjH,EAAK,CACZF,EAAM,eAAiB,OAAOE,CAAG,CACnC,QAAA,CACEF,EAAM,aAAe,EACvB,EACF,CAEA,eAAsBqH,GAAgBrH,EAAmB,CACvD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,eAAiB,KACvBA,EAAM,gBAAkB,KACxB,GAAI,CACF,MAAMY,EAASZ,EAAM,gBAAgB,KAAA,EAChC,KAAK,MAAMA,EAAM,eAAe,EACjC,CAAA,EACEC,EAAM,MAAMD,EAAM,OAAO,QAAQA,EAAM,gBAAgB,KAAA,EAAQY,CAAM,EAC3EZ,EAAM,gBAAkB,KAAK,UAAUC,EAAK,KAAM,CAAC,CACrD,OAASC,EAAK,CACZF,EAAM,eAAiB,OAAOE,CAAG,CACnC,EACF,CCtCA,MAAMoH,GAAmB,IACnBC,OAAa,IAAc,CAC/B,QACA,QACA,OACA,OACA,QACA,OACF,CAAC,EAED,SAASC,GAAqBvN,EAAgB,CAC5C,GAAI,OAAOA,GAAU,SAAU,OAAO,KACtC,MAAME,EAAUF,EAAM,KAAA,EACtB,GAAI,CAACE,EAAQ,WAAW,GAAG,GAAK,CAACA,EAAQ,SAAS,GAAG,EAAG,OAAO,KAC/D,GAAI,CACF,MAAMU,EAAS,KAAK,MAAMV,CAAO,EACjC,MAAI,CAACU,GAAU,OAAOA,GAAW,SAAiB,KAC3CA,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAS4M,GAAexN,EAAiC,CACvD,GAAI,OAAOA,GAAU,SAAU,OAAO,KACtC,MAAMyN,EAAUzN,EAAM,YAAA,EACtB,OAAOsN,GAAO,IAAIG,CAAO,EAAIA,EAAU,IACzC,CAEO,SAASC,GAAapI,EAAwB,CACnD,GAAI,CAACA,EAAK,aAAe,CAAE,IAAKA,EAAM,QAASA,CAAA,EAC/C,GAAI,CACF,MAAMkF,EAAM,KAAK,MAAMlF,CAAI,EACrBqI,EACJnD,GAAO,OAAOA,EAAI,OAAU,UAAYA,EAAI,QAAU,KACjDA,EAAI,MACL,KACAoD,EACJ,OAAOpD,EAAI,MAAS,SAChBA,EAAI,KACJ,OAAOmD,GAAM,MAAS,SACpBA,GAAM,KACN,KACFE,EAAQL,GAAeG,GAAM,cAAgBA,GAAM,KAAK,EAExDG,EACJ,OAAOtD,EAAI,CAAG,GAAM,SACfA,EAAI,CAAG,EACR,OAAOmD,GAAM,MAAS,SACnBA,GAAM,KACP,KACFI,EAAaR,GAAqBO,CAAgB,EACxD,IAAIE,EAA2B,KAC3BD,IACE,OAAOA,EAAW,WAAc,WAAsBA,EAAW,UAC5D,OAAOA,EAAW,QAAW,aAAsBA,EAAW,SAErE,CAACC,GAAaF,GAAoBA,EAAiB,OAAS,MAC9DE,EAAYF,GAGd,IAAIpJ,EAAyB,KAC7B,OAAI,OAAO8F,EAAI,CAAG,GAAM,SAAU9F,EAAU8F,EAAI,CAAG,EAC1C,CAACuD,GAAc,OAAOvD,EAAI,CAAG,GAAM,SAAU9F,EAAU8F,EAAI,CAAG,EAC9D,OAAOA,EAAI,SAAY,aAAoBA,EAAI,SAEjD,CACL,IAAKlF,EACL,KAAAsI,EACA,MAAAC,EACA,UAAAG,EACA,QAAStJ,GAAWY,EACpB,KAAMqI,GAAQ,MAAA,CAElB,MAAQ,CACN,MAAO,CAAE,IAAKrI,EAAM,QAASA,CAAA,CAC/B,CACF,CAEA,eAAsB2I,GACpBlI,EACAmI,EACA,CACA,GAAI,GAACnI,EAAM,QAAU,CAACA,EAAM,YACxB,EAAAA,EAAM,aAAe,CAACmI,GAAM,OAChC,CAAKA,GAAM,QAAOnI,EAAM,YAAc,IACtCA,EAAM,UAAY,KAClB,GAAI,CAMF,MAAMS,EALM,MAAMT,EAAM,OAAO,QAAQ,YAAa,CAClD,OAAQmI,GAAM,MAAQ,OAAYnI,EAAM,YAAc,OACtD,MAAOA,EAAM,UACb,SAAUA,EAAM,YAAA,CACjB,EAYKoI,GAHQ,MAAM,QAAQ3H,EAAQ,KAAK,EACpCA,EAAQ,MAAM,OAAQlB,GAAS,OAAOA,GAAS,QAAQ,EACxD,CAAA,GACkB,IAAIoI,EAAY,EAChCU,EAAc,GAAQF,GAAM,OAAS1H,EAAQ,OAAST,EAAM,YAAc,MAChFA,EAAM,YAAcqI,EAChBD,EACA,CAAC,GAAGpI,EAAM,YAAa,GAAGoI,CAAO,EAAE,MAAM,CAACd,EAAgB,EAC1D,OAAO7G,EAAQ,QAAW,WAAUT,EAAM,WAAaS,EAAQ,QAC/D,OAAOA,EAAQ,MAAS,WAAUT,EAAM,SAAWS,EAAQ,MAC/DT,EAAM,cAAgB,EAAQS,EAAQ,UACtCT,EAAM,gBAAkB,KAAK,IAAA,CAC/B,OAASE,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACOiI,GAAM,QAAOnI,EAAM,YAAc,GACxC,EACF,CC7GA,MAAMsI,GAAgB,CAClB,EAAG,oEACH,EAAG,oEACH,EAAG,GACH,EAAG,oEACH,EAAG,oEACH,GAAI,oEACJ,GAAI,mEACR,EACM,CAAE,EAAG1P,EAAG,EAAGE,GAAG,GAAAyP,GAAI,GAAAC,GAAI,EAAGC,GAAI,EAAGC,GAAE,EAAEjR,EAAC,EAAK6Q,GAC1CrP,GAAI,GACJ0P,GAAK,GAILC,GAAe,IAAIhG,IAAS,CAC1B,sBAAuB,OAAS,OAAO,MAAM,mBAAsB,YACnE,MAAM,kBAAkB,GAAGA,CAAI,CAEvC,EACM1C,EAAM,CAACvB,EAAU,KAAO,CAC1B,MAAM3H,EAAI,IAAI,MAAM2H,CAAO,EAC3B,MAAAiK,GAAa5R,EAAGkJ,CAAG,EACblJ,CACV,EACM6R,GAASxR,GAAM,OAAOA,GAAM,SAC5ByR,GAAS7R,GAAM,OAAOA,GAAM,SAC5B8R,GAAWrR,GAAMA,aAAa,YAAe,YAAY,OAAOA,CAAC,GAAKA,EAAE,YAAY,OAAS,aAE7FsR,GAAS,CAAC/O,EAAOgP,EAAQC,EAAQ,KAAO,CAC1C,MAAMzJ,EAAQsJ,GAAQ9O,CAAK,EACrBkP,EAAMlP,GAAO,OACbmP,EAAWH,IAAW,OAC5B,GAAI,CAACxJ,GAAU2J,GAAYD,IAAQF,EAAS,CACxC,MAAM5M,EAAS6M,GAAS,IAAIA,CAAK,KAC3BG,EAAQD,EAAW,cAAcH,CAAM,GAAK,GAC5CK,EAAM7J,EAAQ,UAAU0J,CAAG,GAAK,QAAQ,OAAOlP,CAAK,GAC1DiG,EAAI7D,EAAS,sBAAwBgN,EAAQ,SAAWC,CAAG,CAC/D,CACA,OAAOrP,CACX,EAEMsP,GAAOJ,GAAQ,IAAI,WAAWA,CAAG,EACjCK,GAAQC,GAAQ,WAAW,KAAKA,CAAG,EACnCC,GAAO,CAACrS,EAAGsS,IAAQtS,EAAE,SAAS,EAAE,EAAE,SAASsS,EAAK,GAAG,EACnDC,GAAc5R,GAAM,MAAM,KAAKgR,GAAOhR,CAAC,CAAC,EACzC,IAAKhB,GAAM0S,GAAK1S,EAAG,CAAC,CAAC,EACrB,KAAK,EAAE,EACN2B,GAAI,CAAE,GAAI,GAAI,GAAI,GAAI,EAAG,GAAI,EAAG,GAAI,EAAG,GAAI,EAAG,GAAG,EACjDkR,GAAOC,GAAO,CAChB,GAAIA,GAAMnR,GAAE,IAAMmR,GAAMnR,GAAE,GACtB,OAAOmR,EAAKnR,GAAE,GAClB,GAAImR,GAAMnR,GAAE,GAAKmR,GAAMnR,GAAE,EACrB,OAAOmR,GAAMnR,GAAE,EAAI,IACvB,GAAImR,GAAMnR,GAAE,GAAKmR,GAAMnR,GAAE,EACrB,OAAOmR,GAAMnR,GAAE,EAAI,GAE3B,EACMoR,GAAcrK,GAAQ,CACxB,MAAM1I,EAAI,cACV,GAAI,CAAC8R,GAAMpJ,CAAG,EACV,OAAOQ,EAAIlJ,CAAC,EAChB,MAAMgT,EAAKtK,EAAI,OACTuK,EAAKD,EAAK,EAChB,GAAIA,EAAK,EACL,OAAO9J,EAAIlJ,CAAC,EAChB,MAAMkT,EAAQX,GAAIU,CAAE,EACpB,QAASE,EAAK,EAAGC,EAAK,EAAGD,EAAKF,EAAIE,IAAMC,GAAM,EAAG,CAE7C,MAAMC,EAAKR,GAAInK,EAAI,WAAW0K,CAAE,CAAC,EAC3BE,EAAKT,GAAInK,EAAI,WAAW0K,EAAK,CAAC,CAAC,EACrC,GAAIC,IAAO,QAAaC,IAAO,OAC3B,OAAOpK,EAAIlJ,CAAC,EAChBkT,EAAMC,CAAE,EAAIE,EAAK,GAAKC,CAC1B,CACA,OAAOJ,CACX,EACMK,GAAK,IAAM,YAAY,OACvBC,GAAS,IAAMD,GAAE,GAAI,QAAUrK,EAAI,kDAAkD,EAErFuK,GAAc,IAAIC,IAAS,CAC7B,MAAMtT,EAAImS,GAAImB,EAAK,OAAO,CAACC,EAAKjT,IAAMiT,EAAM3B,GAAOtR,CAAC,EAAE,OAAQ,CAAC,CAAC,EAChE,IAAIiS,EAAM,EACV,OAAAe,EAAK,QAAQhT,GAAK,CAAEN,EAAE,IAAIM,EAAGiS,CAAG,EAAGA,GAAOjS,EAAE,MAAQ,CAAC,EAC9CN,CACX,EAEMwT,GAAc,CAACzB,EAAMlQ,KACbsR,GAAE,EACH,gBAAgBhB,GAAIJ,CAAG,CAAC,EAE/B0B,GAAM,OACNC,GAAc,CAACzT,EAAGyF,EAAKM,EAAKgD,EAAM,6BAAgCyI,GAAMxR,CAAC,GAAKyF,GAAOzF,GAAKA,EAAI+F,EAAM/F,EAAI6I,EAAIE,CAAG,EAE/GhH,EAAI,CAAC1B,EAAGM,EAAIY,IAAM,CACpB,MAAMxB,EAAIM,EAAIM,EACd,OAAOZ,GAAK,GAAKA,EAAIY,EAAIZ,CAC7B,EACM2T,GAAQrT,GAAM0B,EAAE1B,EAAGoB,EAAC,EAGpBkS,GAAS,CAACC,EAAKC,IAAO,EACpBD,IAAQ,IAAMC,GAAM,KACpBhL,EAAI,gBAAkB+K,EAAM,QAAUC,CAAE,EACzC,IAACxT,EAAI0B,EAAE6R,EAAKC,CAAE,EAAGlT,EAAIkT,EAAI1S,EAAI,GAAYV,EAAI,GAChD,KAAOJ,IAAM,IAAI,CACb,MAAMyT,EAAInT,EAAIN,EAAGN,EAAIY,EAAIN,EACnBW,EAAIG,EAAIV,EAAIqT,EAClBnT,EAAIN,EAAGA,EAAIN,EAAGoB,EAAIV,EAAUA,EAAIO,CACpC,CACA,OAAOL,IAAM,GAAKoB,EAAEZ,EAAG0S,CAAE,EAAIhL,EAAI,YAAY,CACjD,EACMkL,GAAY9Q,GAAS,CAEvB,MAAM+Q,EAAKC,GAAOhR,CAAI,EACtB,OAAI,OAAO+Q,GAAO,YACdnL,EAAI,UAAY5F,EAAO,UAAU,EAC9B+Q,CACX,EAEME,GAAU3T,GAAOA,aAAa4T,EAAQ5T,EAAIsI,EAAI,gBAAgB,EAG9DuL,GAAO,IAAM,KAEnB,MAAMD,CAAM,CACR,OAAO,KACP,OAAO,KACP,EACA,EACA,EACA,EACA,YAAYE,EAAGC,EAAGpS,EAAGqS,EAAG,CACpB,MAAMxO,EAAMqO,GACZ,KAAK,EAAIX,GAAYY,EAAG,GAAItO,CAAG,EAC/B,KAAK,EAAI0N,GAAYa,EAAG,GAAIvO,CAAG,EAC/B,KAAK,EAAI0N,GAAYvR,EAAG,GAAI6D,CAAG,EAC/B,KAAK,EAAI0N,GAAYc,EAAG,GAAIxO,CAAG,EAC/B,OAAO,OAAO,IAAI,CACtB,CACA,OAAO,OAAQ,CACX,OAAOkL,EACX,CACA,OAAO,WAAW1Q,EAAG,CACjB,OAAO,IAAI4T,EAAM5T,EAAE,EAAGA,EAAE,EAAG,GAAIwB,EAAExB,EAAE,EAAIA,EAAE,CAAC,CAAC,CAC/C,CAEA,OAAO,UAAU8H,EAAKmM,EAAS,GAAO,CAClC,MAAMhU,EAAI6Q,GAEJoD,EAAStC,GAAKR,GAAOtJ,EAAKzG,EAAC,CAAC,EAE5B8S,EAAWrM,EAAI,EAAE,EACvBoM,EAAO,EAAE,EAAIC,EAAW,KACxB,MAAM7T,EAAI8T,GAAaF,CAAM,EAI7BhB,GAAY5S,EAAG,GADH2T,EAASJ,GAAO7S,CACN,EACtB,MAAMqT,EAAK7S,EAAElB,EAAIA,CAAC,EACZJ,EAAIsB,EAAE6S,EAAK,EAAE,EACb9T,EAAIiB,EAAEvB,EAAIoU,EAAK,EAAE,EACvB,GAAI,CAAE,QAAAC,EAAS,MAAO1T,CAAC,EAAK2T,GAAQrU,EAAGK,CAAC,EACnC+T,GACDhM,EAAI,uBAAuB,EAC/B,MAAMkM,GAAU5T,EAAI,MAAQ,GACtB6T,GAAiBN,EAAW,OAAU,EAC5C,MAAI,CAACF,GAAUrT,IAAM,IAAM6T,GACvBnM,EAAI,gCAAgC,EACpCmM,IAAkBD,IAClB5T,EAAIY,EAAE,CAACZ,CAAC,GACL,IAAIgT,EAAMhT,EAAGN,EAAG,GAAIkB,EAAEZ,EAAIN,CAAC,CAAC,CACvC,CACA,OAAO,QAAQwH,EAAKmM,EAAQ,CACxB,OAAOL,EAAM,UAAUzB,GAAWrK,CAAG,EAAGmM,CAAM,CAClD,CACA,IAAI,GAAI,CACJ,OAAO,KAAK,SAAQ,EAAG,CAC3B,CACA,IAAI,GAAI,CACJ,OAAO,KAAK,SAAQ,EAAG,CAC3B,CAEA,gBAAiB,CACb,MAAMnU,EAAI+Q,GACJ5Q,EAAI6Q,GACJ9Q,EAAI,KACV,GAAIA,EAAE,IAAG,EACL,OAAOsI,EAAI,iBAAiB,EAGhC,KAAM,CAAE,EAAAwL,EAAG,EAAAC,EAAG,EAAApS,EAAG,EAAAqS,CAAC,EAAKhU,EACjB0U,EAAKlT,EAAEsS,EAAIA,CAAC,EACZa,EAAKnT,EAAEuS,EAAIA,CAAC,EACZa,EAAKpT,EAAEG,EAAIA,CAAC,EACZkT,EAAKrT,EAAEoT,EAAKA,CAAE,EACdE,EAAMtT,EAAEkT,EAAK5U,CAAC,EACdiV,EAAOvT,EAAEoT,EAAKpT,EAAEsT,EAAMH,CAAE,CAAC,EACzBK,EAAQxT,EAAEqT,EAAKrT,EAAEvB,EAAIuB,EAAEkT,EAAKC,CAAE,CAAC,CAAC,EACtC,GAAII,IAASC,EACT,OAAO1M,EAAI,uCAAuC,EAEtD,MAAM2M,EAAKzT,EAAEsS,EAAIC,CAAC,EACZmB,EAAK1T,EAAEG,EAAIqS,CAAC,EAClB,OAAIiB,IAAOC,EACA5M,EAAI,uCAAuC,EAC/C,IACX,CAEA,OAAO6M,EAAO,CACV,KAAM,CAAE,EAAGC,EAAI,EAAGC,EAAI,EAAGC,CAAE,EAAK,KAC1B,CAAE,EAAGZ,EAAI,EAAGC,EAAI,EAAGC,CAAE,EAAKjB,GAAOwB,CAAK,EACtCI,EAAO/T,EAAE4T,EAAKR,CAAE,EAChBY,EAAOhU,EAAEkT,EAAKY,CAAE,EAChBG,EAAOjU,EAAE6T,EAAKT,CAAE,EAChBc,EAAOlU,EAAEmT,EAAKW,CAAE,EACtB,OAAOC,IAASC,GAAQC,IAASC,CACrC,CACA,KAAM,CACF,OAAO,KAAK,OAAOtU,EAAC,CACxB,CAEA,QAAS,CACL,OAAO,IAAIwS,EAAMpS,EAAE,CAAC,KAAK,CAAC,EAAG,KAAK,EAAG,KAAK,EAAGA,EAAE,CAAC,KAAK,CAAC,CAAC,CAC3D,CAEA,QAAS,CACL,KAAM,CAAE,EAAG4T,EAAI,EAAGC,EAAI,EAAGC,CAAE,EAAK,KAC1BxV,EAAI+Q,GAEJ/P,EAAIU,EAAE4T,EAAKA,CAAE,EACbrT,EAAIP,EAAE6T,EAAKA,CAAE,EACbtU,EAAIS,EAAE,GAAKA,EAAE8T,EAAKA,CAAE,CAAC,EACrBtT,EAAIR,EAAE1B,EAAIgB,CAAC,EACX6U,EAAOP,EAAKC,EACZxU,EAAIW,EAAEA,EAAEmU,EAAOA,CAAI,EAAI7U,EAAIiB,CAAC,EAC5B6T,EAAI5T,EAAID,EACR8T,EAAID,EAAI7U,EACRQ,EAAIS,EAAID,EACR+T,EAAKtU,EAAEX,EAAIgV,CAAC,EACZE,EAAKvU,EAAEoU,EAAIrU,CAAC,EACZyU,EAAKxU,EAAEX,EAAIU,CAAC,EACZ0U,EAAKzU,EAAEqU,EAAID,CAAC,EAClB,OAAO,IAAIhC,EAAMkC,EAAIC,EAAIE,EAAID,CAAE,CACnC,CAEA,IAAIb,EAAO,CACP,KAAM,CAAE,EAAGC,EAAI,EAAGC,EAAI,EAAGC,EAAI,EAAGY,CAAE,EAAK,KACjC,CAAE,EAAGxB,EAAI,EAAGC,EAAI,EAAGC,EAAI,EAAGuB,CAAE,EAAKxC,GAAOwB,CAAK,EAC7CrV,EAAI+Q,GACJ5Q,EAAI6Q,GAEJhQ,EAAIU,EAAE4T,EAAKV,CAAE,EACb3S,EAAIP,EAAE6T,EAAKV,CAAE,EACb5T,EAAIS,EAAE0U,EAAKjW,EAAIkW,CAAE,EACjBnU,EAAIR,EAAE8T,EAAKV,CAAE,EACb/T,EAAIW,GAAG4T,EAAKC,IAAOX,EAAKC,GAAM7T,EAAIiB,CAAC,EACnC8T,EAAIrU,EAAEQ,EAAIjB,CAAC,EACX6U,EAAIpU,EAAEQ,EAAIjB,CAAC,EACXQ,EAAIC,EAAEO,EAAIjC,EAAIgB,CAAC,EACfgV,EAAKtU,EAAEX,EAAIgV,CAAC,EACZE,EAAKvU,EAAEoU,EAAIrU,CAAC,EACZyU,EAAKxU,EAAEX,EAAIU,CAAC,EACZ0U,GAAKzU,EAAEqU,EAAID,CAAC,EAClB,OAAO,IAAIhC,EAAMkC,EAAIC,EAAIE,GAAID,CAAE,CACnC,CACA,SAASb,EAAO,CACZ,OAAO,KAAK,IAAIxB,GAAOwB,CAAK,EAAE,OAAM,CAAE,CAC1C,CAQA,SAAS1V,EAAG2W,EAAO,GAAM,CACrB,GAAI,CAACA,IAAS3W,IAAM,IAAM,KAAK,IAAG,GAC9B,OAAO2B,GAEX,GADA8R,GAAYzT,EAAG,GAAIyB,EAAC,EAChBzB,IAAM,GACN,OAAO,KACX,GAAI,KAAK,OAAOmW,EAAC,EACb,OAAOS,GAAK5W,CAAC,EAAE,EAEnB,IAAIO,EAAIoB,GACJjB,EAAIyV,GACR,QAAS3V,EAAI,KAAMR,EAAI,GAAIQ,EAAIA,EAAE,OAAM,EAAIR,IAAM,GAGzCA,EAAI,GACJO,EAAIA,EAAE,IAAIC,CAAC,EACNmW,IACLjW,EAAIA,EAAE,IAAIF,CAAC,GAEnB,OAAOD,CACX,CACA,eAAesW,EAAQ,CACnB,OAAO,KAAK,SAASA,EAAQ,EAAK,CACtC,CAEA,UAAW,CACP,KAAM,CAAE,EAAAxC,EAAG,EAAAC,EAAG,EAAApS,CAAC,EAAK,KAEpB,GAAI,KAAK,OAAOP,EAAC,EACb,MAAO,CAAE,EAAG,GAAI,EAAG,EAAE,EACzB,MAAMmV,EAAKnD,GAAOzR,EAAGX,CAAC,EAElBQ,EAAEG,EAAI4U,CAAE,IAAM,IACdjO,EAAI,iBAAiB,EAEzB,MAAM1H,EAAIY,EAAEsS,EAAIyC,CAAE,EACZjW,EAAIkB,EAAEuS,EAAIwC,CAAE,EAClB,MAAO,CAAE,EAAA3V,EAAG,EAAAN,CAAC,CACjB,CACA,SAAU,CACN,KAAM,CAAE,EAAAM,EAAG,EAAAN,CAAC,EAAK,KAAK,eAAc,EAAG,SAAQ,EACzCF,EAAIoW,GAAWlW,CAAC,EAEtB,OAAAF,EAAE,EAAE,GAAKQ,EAAI,GAAK,IAAO,EAClBR,CACX,CACA,OAAQ,CACJ,OAAO4R,GAAW,KAAK,SAAS,CACpC,CACA,eAAgB,CACZ,OAAO,KAAK,SAASiB,GAAIpT,EAAC,EAAG,EAAK,CACtC,CACA,cAAe,CACX,OAAO,KAAK,cAAa,EAAG,IAAG,CACnC,CACA,eAAgB,CAEZ,IAAIG,EAAI,KAAK,SAASkB,GAAI,GAAI,EAAK,EAAE,OAAM,EAC3C,OAAIA,GAAI,KACJlB,EAAIA,EAAE,IAAI,IAAI,GACXA,EAAE,IAAG,CAChB,CACJ,CAEA,MAAM4V,GAAI,IAAIhC,EAAMjD,GAAIC,GAAI,GAAIpP,EAAEmP,GAAKC,EAAE,CAAC,EAEpCxP,GAAI,IAAIwS,EAAM,GAAI,GAAI,GAAI,EAAE,EAElCA,EAAM,KAAOgC,GACbhC,EAAM,KAAOxS,GACb,MAAMoV,GAAcnD,GAAQlB,GAAWL,GAAKoB,GAAYG,EAAK,GAAIQ,EAAI,EAAG9C,EAAE,CAAC,EAAE,QAAO,EAC9EqD,GAAgBhU,GAAM6S,GAAI,KAAOjB,GAAWJ,GAAKR,GAAOhR,CAAC,CAAC,EAAE,QAAO,CAAE,CAAC,EACtEqW,GAAO,CAAC7V,EAAG8V,IAAU,CAEvB,IAAIlX,EAAIoB,EACR,KAAO8V,KAAU,IACblX,GAAKA,EACLA,GAAKwB,EAET,OAAOxB,CACX,EAEMmX,GAAe/V,GAAM,CAEvB,MAAMgW,EADMhW,EAAIA,EAAKI,EACJJ,EAAKI,EAChB6V,EAAMJ,GAAKG,EAAI,EAAE,EAAIA,EAAM5V,EAC3B8V,EAAML,GAAKI,EAAI,EAAE,EAAIjW,EAAKI,EAC1B+V,EAAON,GAAKK,EAAI,EAAE,EAAIA,EAAM9V,EAC5BgW,EAAOP,GAAKM,EAAK,GAAG,EAAIA,EAAO/V,EAC/BiW,EAAOR,GAAKO,EAAK,GAAG,EAAIA,EAAOhW,EAC/BkW,EAAOT,GAAKQ,EAAK,GAAG,EAAIA,EAAOjW,EAC/BmW,EAAQV,GAAKS,EAAK,GAAG,EAAIA,EAAOlW,EAChCoW,EAAQX,GAAKU,EAAM,GAAG,EAAID,EAAOlW,EACjCqW,EAAQZ,GAAKW,EAAM,GAAG,EAAIL,EAAO/V,EAEvC,MAAO,CAAE,UADUyV,GAAKY,EAAM,EAAE,EAAIzW,EAAKI,EACrB,GAAA4V,CAAE,CAC1B,EACMU,GAAM,oEAGN/C,GAAU,CAACrU,EAAGK,IAAM,CACtB,MAAMgX,EAAK/V,EAAEjB,EAAIA,EAAIA,CAAC,EAChBiX,EAAKhW,EAAE+V,EAAKA,EAAKhX,CAAC,EAClBkX,EAAMd,GAAYzW,EAAIsX,CAAE,EAAE,UAChC,IAAI5W,EAAIY,EAAEtB,EAAIqX,EAAKE,CAAG,EACtB,MAAMC,EAAMlW,EAAEjB,EAAIK,EAAIA,CAAC,EACjB+W,EAAQ/W,EACRgX,EAAQpW,EAAEZ,EAAI0W,EAAG,EACjBO,EAAWH,IAAQxX,EACnB4X,EAAWJ,IAAQlW,EAAE,CAACtB,CAAC,EACvB6X,EAASL,IAAQlW,EAAE,CAACtB,EAAIoX,EAAG,EACjC,OAAIO,IACAjX,EAAI+W,IACJG,GAAYC,KACZnX,EAAIgX,IACHpW,EAAEZ,CAAC,EAAI,MAAQ,KAChBA,EAAIY,EAAE,CAACZ,CAAC,GACL,CAAE,QAASiX,GAAYC,EAAU,MAAOlX,CAAC,CACpD,EAEMoX,GAAWC,GAAS9E,GAAKiB,GAAa6D,CAAI,CAAC,EAG3CC,GAAU,IAAIzX,IAAMiT,GAAO,YAAYb,GAAY,GAAGpS,CAAC,CAAC,EACxD0X,GAAU,IAAI1X,IAAM+S,GAAS,QAAQ,EAAEX,GAAY,GAAGpS,CAAC,CAAC,EAExD2X,GAAaC,GAAW,CAE1B,MAAMC,EAAOD,EAAO,MAAM,EAAGhX,EAAC,EAC9BiX,EAAK,CAAC,GAAK,IACXA,EAAK,EAAE,GAAK,IACZA,EAAK,EAAE,GAAK,GACZ,MAAM7T,EAAS4T,EAAO,MAAMhX,GAAG0P,EAAE,EAC3BuF,EAAS0B,GAAQM,CAAI,EACrBC,EAAQ3C,GAAE,SAASU,CAAM,EACzBkC,EAAaD,EAAM,UACzB,MAAO,CAAE,KAAAD,EAAM,OAAA7T,EAAQ,OAAA6R,EAAQ,MAAAiC,EAAO,WAAAC,CAAU,CACpD,EAEMC,GAA6BC,GAAcR,GAAQ9G,GAAOsH,EAAWrX,EAAC,CAAC,EAAE,KAAK+W,EAAS,EACvFO,GAAwBD,GAAcN,GAAUD,GAAQ/G,GAAOsH,EAAWrX,EAAC,CAAC,CAAC,EAE7EuX,GAAqBF,GAAcD,GAA0BC,CAAS,EAAE,KAAM1Y,GAAMA,EAAE,UAAU,EAGhG6Y,GAAexQ,GAAQ6P,GAAQ7P,EAAI,QAAQ,EAAE,KAAKA,EAAI,MAAM,EAG5DyQ,GAAQ,CAAC,EAAGC,EAAQvQ,IAAQ,CAC9B,KAAM,CAAE,WAAYxH,EAAG,OAAQ3B,CAAC,EAAK,EAC/BG,EAAIwY,GAAQe,CAAM,EAClBtX,EAAImU,GAAE,SAASpW,CAAC,EAAE,QAAO,EAO/B,MAAO,CAAE,SANQqT,GAAYpR,EAAGT,EAAGwH,CAAG,EAMnB,OALH6P,GAAW,CAEvB,MAAM1Y,EAAIwT,GAAK3T,EAAIwY,GAAQK,CAAM,EAAIhZ,CAAC,EACtC,OAAO+R,GAAOyB,GAAYpR,EAAG+U,GAAW7W,CAAC,CAAC,EAAGoR,EAAE,CACnD,CACyB,CAC7B,EAKMiI,GAAY,MAAOjS,EAAS2R,IAAc,CAC5C,MAAMjY,EAAI2Q,GAAOrK,CAAO,EAClB3H,EAAI,MAAMqZ,GAA0BC,CAAS,EAC7CK,EAAS,MAAMb,GAAQ9Y,EAAE,OAAQqB,CAAC,EACxC,OAAOoY,GAAYC,GAAM1Z,EAAG2Z,EAAQtY,CAAC,CAAC,CAC1C,EAuDMiT,GAAS,CACX,YAAa,MAAO3M,GAAY,CAC5B,MAAM1H,EAAIuT,GAAM,EACVnS,EAAIoS,GAAY9L,CAAO,EAC7B,OAAO4K,GAAI,MAAMtS,EAAE,OAAO,UAAWoB,EAAE,MAAM,CAAC,CAClD,EACA,OAAQ,MACZ,EAGMwY,GAAkB,CAACC,EAAOlG,GAAY3R,EAAC,IAAM6X,EAY7CC,GAAQ,CACV,0BAA2BV,GAC3B,qBAAsBE,GACtB,gBAAiBM,EACrB,EAGMG,GAAI,EACJC,GAAa,IACbC,GAAW,KAAK,KAAKD,GAAaD,EAAC,EAAI,EACvCG,GAAc,IAAMH,GAAI,GACxBI,GAAa,IAAM,CACrB,MAAMC,EAAS,CAAA,EACf,IAAIzZ,EAAI4V,GACJxV,EAAIJ,EACR,QAAS0Z,EAAI,EAAGA,EAAIJ,GAAUI,IAAK,CAC/BtZ,EAAIJ,EACJyZ,EAAO,KAAKrZ,CAAC,EACb,QAAS,EAAI,EAAG,EAAImZ,GAAa,IAC7BnZ,EAAIA,EAAE,IAAIJ,CAAC,EACXyZ,EAAO,KAAKrZ,CAAC,EAEjBJ,EAAII,EAAE,OAAM,CAChB,CACA,OAAOqZ,CACX,EACA,IAAIE,GAEJ,MAAMC,GAAQ,CAACC,EAAK7Z,IAAM,CACtB,MAAM,EAAIA,EAAE,OAAM,EAClB,OAAO6Z,EAAM,EAAI7Z,CACrB,EAYMqW,GAAQ5W,GAAM,CAChB,MAAMqa,EAAOH,KAAUA,GAAQH,GAAU,GACzC,IAAIxZ,EAAIoB,GACJjB,EAAIyV,GACR,MAAMmE,EAAU,GAAKX,GACfY,EAASD,EACTE,EAAOhH,GAAI8G,EAAU,CAAC,EACtBG,EAAUjH,GAAImG,EAAC,EACrB,QAASM,EAAI,EAAGA,EAAIJ,GAAUI,IAAK,CAC/B,IAAIS,EAAQ,OAAO1a,EAAIwa,CAAI,EAC3Bxa,IAAMya,EAMFC,EAAQZ,KACRY,GAASH,EACTva,GAAK,IAET,MAAM2a,EAAMV,EAAIH,GACVc,EAAOD,EACPE,EAAOF,EAAM,KAAK,IAAID,CAAK,EAAI,EAC/BI,EAASb,EAAI,IAAM,EACnBc,EAAQL,EAAQ,EAClBA,IAAU,EAEVha,EAAIA,EAAE,IAAIyZ,GAAMW,EAAQT,EAAKO,CAAI,CAAC,CAAC,EAGnCra,EAAIA,EAAE,IAAI4Z,GAAMY,EAAOV,EAAKQ,CAAI,CAAC,CAAC,CAE1C,CACA,OAAI7a,IAAM,IACN6I,EAAI,cAAc,EACf,CAAE,EAAAtI,EAAG,EAAAG,EAChB,ECnmBMsa,GAAc,8BAEpB,SAASC,GAAgB7S,EAA2B,CAClD,IAAI8S,EAAS,GACb,UAAWC,KAAQ/S,EAAO8S,GAAU,OAAO,aAAaC,CAAI,EAC5D,OAAO,KAAKD,CAAM,EAAE,WAAW,IAAK,GAAG,EAAE,WAAW,IAAK,GAAG,EAAE,QAAQ,OAAQ,EAAE,CAClF,CAEA,SAASE,GAAgBpY,EAA2B,CAClD,MAAMyB,EAAazB,EAAM,WAAW,IAAK,GAAG,EAAE,WAAW,IAAK,GAAG,EAC3DqY,EAAS5W,EAAa,IAAI,QAAQ,EAAKA,EAAW,OAAS,GAAM,CAAC,EAClEyW,EAAS,KAAKG,CAAM,EACpBC,EAAM,IAAI,WAAWJ,EAAO,MAAM,EACxC,QAASjb,EAAI,EAAGA,EAAIib,EAAO,OAAQjb,GAAK,EAAGqb,EAAIrb,CAAC,EAAIib,EAAO,WAAWjb,CAAC,EACvE,OAAOqb,CACT,CAEA,SAAS/I,GAAWnK,EAA2B,CAC7C,OAAO,MAAM,KAAKA,CAAK,EACpB,IAAKzH,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAC1C,KAAK,EAAE,CACZ,CAEA,eAAe4a,GAAqBC,EAAwC,CAC1E,MAAMhD,EAAO,MAAM,OAAO,OAAO,OAAO,UAAWgD,CAAS,EAC5D,OAAOjJ,GAAW,IAAI,WAAWiG,CAAI,CAAC,CACxC,CAEA,eAAeiD,IAA4C,CACzD,MAAMC,EAAahC,GAAM,gBAAA,EACnB8B,EAAY,MAAMrC,GAAkBuC,CAAU,EAEpD,MAAO,CACL,SAFe,MAAMH,GAAqBC,CAAS,EAGnD,UAAWP,GAAgBO,CAAS,EACpC,WAAYP,GAAgBS,CAAU,CAAA,CAE1C,CAEA,eAAsBC,IAAsD,CAC1E,GAAI,CACF,MAAMpY,EAAM,aAAa,QAAQyX,EAAW,EAC5C,GAAIzX,EAAK,CACP,MAAMC,EAAS,KAAK,MAAMD,CAAG,EAC7B,GACEC,GAAQ,UAAY,GACpB,OAAOA,EAAO,UAAa,UAC3B,OAAOA,EAAO,WAAc,UAC5B,OAAOA,EAAO,YAAe,SAC7B,CACA,MAAMoY,EAAY,MAAML,GAAqBH,GAAgB5X,EAAO,SAAS,CAAC,EAC9E,GAAIoY,IAAcpY,EAAO,SAAU,CACjC,MAAMqY,EAA0B,CAC9B,GAAGrY,EACH,SAAUoY,CAAA,EAEZ,oBAAa,QAAQZ,GAAa,KAAK,UAAUa,CAAO,CAAC,EAClD,CACL,SAAUD,EACV,UAAWpY,EAAO,UAClB,WAAYA,EAAO,UAAA,CAEvB,CACA,MAAO,CACL,SAAUA,EAAO,SACjB,UAAWA,EAAO,UAClB,WAAYA,EAAO,UAAA,CAEvB,CACF,CACF,MAAQ,CAER,CAEA,MAAMsY,EAAW,MAAML,GAAA,EACjBM,EAAyB,CAC7B,QAAS,EACT,SAAUD,EAAS,SACnB,UAAWA,EAAS,UACpB,WAAYA,EAAS,WACrB,YAAa,KAAK,IAAA,CAAI,EAExB,oBAAa,QAAQd,GAAa,KAAK,UAAUe,CAAM,CAAC,EACjDD,CACT,CAEA,eAAsBE,GAAkBC,EAA6B7S,EAAiB,CACpF,MAAMO,EAAMyR,GAAgBa,CAAmB,EACzC7Q,EAAO,IAAI,cAAc,OAAOhC,CAAO,EACvC8S,EAAM,MAAM3C,GAAUnO,EAAMzB,CAAG,EACrC,OAAOsR,GAAgBiB,CAAG,CAC5B,CC9FA,MAAMlB,GAAc,0BAEpB,SAASmB,GAAc5U,EAAsB,CAC3C,OAAOA,EAAK,KAAA,CACd,CAEA,SAAS6U,GAAgBC,EAAwC,CAC/D,GAAI,CAAC,MAAM,QAAQA,CAAM,QAAU,CAAA,EACnC,MAAMf,MAAU,IAChB,UAAWgB,KAASD,EAAQ,CAC1B,MAAMvZ,EAAUwZ,EAAM,KAAA,EAClBxZ,GAASwY,EAAI,IAAIxY,CAAO,CAC9B,CACA,MAAO,CAAC,GAAGwY,CAAG,EAAE,KAAA,CAClB,CAEA,SAASiB,IAAoC,CAC3C,GAAI,CACF,MAAMhZ,EAAM,OAAO,aAAa,QAAQyX,EAAW,EACnD,GAAI,CAACzX,EAAK,OAAO,KACjB,MAAMC,EAAS,KAAK,MAAMD,CAAG,EAG7B,MAFI,CAACC,GAAUA,EAAO,UAAY,GAC9B,CAACA,EAAO,UAAY,OAAOA,EAAO,UAAa,UAC/C,CAACA,EAAO,QAAU,OAAOA,EAAO,QAAW,SAAiB,KACzDA,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAASgZ,GAAWC,EAAwB,CAC1C,GAAI,CACF,OAAO,aAAa,QAAQzB,GAAa,KAAK,UAAUyB,CAAK,CAAC,CAChE,MAAQ,CAER,CACF,CAEO,SAASC,GAAoBnT,EAGT,CACzB,MAAMkT,EAAQF,GAAA,EACd,GAAI,CAACE,GAASA,EAAM,WAAalT,EAAO,SAAU,OAAO,KACzD,MAAMhC,EAAO4U,GAAc5S,EAAO,IAAI,EAChCY,EAAQsS,EAAM,OAAOlV,CAAI,EAC/B,MAAI,CAAC4C,GAAS,OAAOA,EAAM,OAAU,SAAiB,KAC/CA,CACT,CAEO,SAASwS,GAAqBpT,EAKjB,CAClB,MAAMhC,EAAO4U,GAAc5S,EAAO,IAAI,EAChC7F,EAAwB,CAC5B,QAAS,EACT,SAAU6F,EAAO,SACjB,OAAQ,CAAA,CAAC,EAELqT,EAAWL,GAAA,EACbK,GAAYA,EAAS,WAAarT,EAAO,WAC3C7F,EAAK,OAAS,CAAE,GAAGkZ,EAAS,MAAA,GAE9B,MAAMzS,EAAyB,CAC7B,MAAOZ,EAAO,MACd,KAAAhC,EACA,OAAQ6U,GAAgB7S,EAAO,MAAM,EACrC,YAAa,KAAK,IAAA,CAAI,EAExB,OAAA7F,EAAK,OAAO6D,CAAI,EAAI4C,EACpBqS,GAAW9Y,CAAI,EACRyG,CACT,CAEO,SAAS0S,GAAqBtT,EAA4C,CAC/E,MAAMkT,EAAQF,GAAA,EACd,GAAI,CAACE,GAASA,EAAM,WAAalT,EAAO,SAAU,OAClD,MAAMhC,EAAO4U,GAAc5S,EAAO,IAAI,EACtC,GAAI,CAACkT,EAAM,OAAOlV,CAAI,EAAG,OACzB,MAAM7D,EAAO,CAAE,GAAG+Y,EAAO,OAAQ,CAAE,GAAGA,EAAM,OAAO,EACnD,OAAO/Y,EAAK,OAAO6D,CAAI,EACvBiV,GAAW9Y,CAAI,CACjB,CCnDA,eAAsBoZ,GAAYnU,EAAqBmI,EAA4B,CACjF,GAAI,GAACnI,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,eACV,CAAAA,EAAM,eAAiB,GAClBmI,GAAM,QAAOnI,EAAM,aAAe,MACvC,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,mBAAoB,EAAE,EAC9DA,EAAM,YAAc,CAClB,QAAS,MAAM,QAAQC,GAAK,OAAO,EAAIA,EAAK,QAAU,CAAA,EACtD,OAAQ,MAAM,QAAQA,GAAK,MAAM,EAAIA,EAAK,OAAS,CAAA,CAAC,CAExD,OAASC,EAAK,CACPiI,GAAM,QAAOnI,EAAM,aAAe,OAAOE,CAAG,EACnD,QAAA,CACEF,EAAM,eAAiB,EACzB,EACF,CAEA,eAAsBoU,GAAqBpU,EAAqBqU,EAAmB,CACjF,GAAI,GAACrU,EAAM,QAAU,CAACA,EAAM,WAC5B,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,sBAAuB,CAAE,UAAAqU,EAAW,EAC/D,MAAMF,GAAYnU,CAAK,CACzB,OAASE,EAAK,CACZF,EAAM,aAAe,OAAOE,CAAG,CACjC,CACF,CAEA,eAAsBoU,GAAoBtU,EAAqBqU,EAAmB,CAGhF,GAFI,GAACrU,EAAM,QAAU,CAACA,EAAM,WAExB,CADc,OAAO,QAAQ,qCAAqC,GAEtE,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,qBAAsB,CAAE,UAAAqU,EAAW,EAC9D,MAAMF,GAAYnU,CAAK,CACzB,OAASE,EAAK,CACZF,EAAM,aAAe,OAAOE,CAAG,CACjC,CACF,CAEA,eAAsBqU,GACpBvU,EACAY,EACA,CACA,GAAI,GAACZ,EAAM,QAAU,CAACA,EAAM,WAC5B,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,sBAAuBY,CAAM,EAGrE,GAAIX,GAAK,MAAO,CACd,MAAMkT,EAAW,MAAMH,GAAA,EACjBpU,EAAOqB,EAAI,MAAQW,EAAO,MAC5BX,EAAI,WAAakT,EAAS,UAAYvS,EAAO,WAAauS,EAAS,WACrEa,GAAqB,CACnB,SAAUb,EAAS,SACnB,KAAAvU,EACA,MAAOqB,EAAI,MACX,OAAQA,EAAI,QAAUW,EAAO,QAAU,CAAA,CAAC,CACzC,EAEH,OAAO,OAAO,8CAA+CX,EAAI,KAAK,CACxE,CACA,MAAMkU,GAAYnU,CAAK,CACzB,OAASE,EAAK,CACZF,EAAM,aAAe,OAAOE,CAAG,CACjC,CACF,CAEA,eAAsBsU,GACpBxU,EACAY,EACA,CAKA,GAJI,GAACZ,EAAM,QAAU,CAACA,EAAM,WAIxB,CAHc,OAAO,QACvB,oBAAoBY,EAAO,QAAQ,KAAKA,EAAO,IAAI,IAAA,GAGrD,GAAI,CACF,MAAMZ,EAAM,OAAO,QAAQ,sBAAuBY,CAAM,EACxD,MAAMuS,EAAW,MAAMH,GAAA,EACnBpS,EAAO,WAAauS,EAAS,UAC/Be,GAAqB,CAAE,SAAUf,EAAS,SAAU,KAAMvS,EAAO,KAAM,EAEzE,MAAMuT,GAAYnU,CAAK,CACzB,OAASE,EAAK,CACZF,EAAM,aAAe,OAAOE,CAAG,CACjC,CACF,CC5HA,eAAsBuU,GACpBzU,EACAmI,EACA,CACA,GAAI,GAACnI,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,aACV,CAAAA,EAAM,aAAe,GAChBmI,GAAM,QAAOnI,EAAM,UAAY,MACpC,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,YAAa,EAAE,EAGvDA,EAAM,MAAQ,MAAM,QAAQC,EAAI,KAAK,EAAIA,EAAI,MAAQ,CAAA,CACvD,OAASC,EAAK,CACPiI,GAAM,QAAOnI,EAAM,UAAY,OAAOE,CAAG,EAChD,QAAA,CACEF,EAAM,aAAe,EACvB,EACF,CCuBA,SAAS0U,GAAwBxR,EAGxB,CACP,GAAI,CAACA,GAAUA,EAAO,OAAS,UAC7B,MAAO,CAAE,OAAQ,qBAAsB,OAAQ,CAAA,CAAC,EAElD,MAAMyR,EAASzR,EAAO,OAAO,KAAA,EAC7B,OAAKyR,EACE,CAAE,OAAQ,0BAA2B,OAAQ,CAAE,OAAAA,EAAO,EADzC,IAEtB,CAEA,SAASC,GACP1R,EACAtC,EAC4D,CAC5D,GAAI,CAACsC,GAAUA,EAAO,OAAS,UAC7B,MAAO,CAAE,OAAQ,qBAAsB,OAAAtC,CAAA,EAEzC,MAAM+T,EAASzR,EAAO,OAAO,KAAA,EAC7B,OAAKyR,EACE,CAAE,OAAQ,0BAA2B,OAAQ,CAAE,GAAG/T,EAAQ,OAAA+T,EAAO,EADpD,IAEtB,CAEA,eAAsBE,GACpB7U,EACAkD,EACA,CACA,GAAI,GAAClD,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,qBACV,CAAAA,EAAM,qBAAuB,GAC7BA,EAAM,UAAY,KAClB,GAAI,CACF,MAAM8U,EAAMJ,GAAwBxR,CAAM,EAC1C,GAAI,CAAC4R,EAAK,CACR9U,EAAM,UAAY,+CAClB,MACF,CACA,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ8U,EAAI,OAAQA,EAAI,MAAM,EAC9DC,GAA2B/U,EAAOC,CAAG,CACvC,OAASC,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,qBAAuB,EAC/B,EACF,CAEO,SAAS+U,GACd/U,EACAiF,EACA,CACAjF,EAAM,sBAAwBiF,EACzBjF,EAAM,qBACTA,EAAM,kBAAoBqE,GAAkBY,EAAS,MAAQ,CAAA,CAAE,EAEnE,CAEA,eAAsB+P,GACpBhV,EACAkD,EACA,CACA,GAAI,GAAClD,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,oBAAsB,GAC5BA,EAAM,UAAY,KAClB,GAAI,CACF,MAAMoF,EAAWpF,EAAM,uBAAuB,KAC9C,GAAI,CAACoF,EAAU,CACbpF,EAAM,UAAY,iDAClB,MACF,CACA,MAAMiV,EACJjV,EAAM,mBACNA,EAAM,uBAAuB,MAC7B,CAAA,EACI8U,EAAMF,GAA4B1R,EAAQ,CAAE,KAAA+R,EAAM,SAAA7P,EAAU,EAClE,GAAI,CAAC0P,EAAK,CACR9U,EAAM,UAAY,8CAClB,MACF,CACA,MAAMA,EAAM,OAAO,QAAQ8U,EAAI,OAAQA,EAAI,MAAM,EACjD9U,EAAM,mBAAqB,GAC3B,MAAM6U,GAAkB7U,EAAOkD,CAAM,CACvC,OAAShD,EAAK,CACZF,EAAM,UAAY,OAAOE,CAAG,CAC9B,QAAA,CACEF,EAAM,oBAAsB,EAC9B,EACF,CAEO,SAASkV,GACdlV,EACAvE,EACAxB,EACA,CACA,MAAM2B,EAAOyI,GACXrE,EAAM,mBAAqBA,EAAM,uBAAuB,MAAQ,CAAA,CAAC,EAEnEwE,GAAa5I,EAAMH,EAAMxB,CAAK,EAC9B+F,EAAM,kBAAoBpE,EAC1BoE,EAAM,mBAAqB,EAC7B,CAEO,SAASmV,GACdnV,EACAvE,EACA,CACA,MAAMG,EAAOyI,GACXrE,EAAM,mBAAqBA,EAAM,uBAAuB,MAAQ,CAAA,CAAC,EAEnE4E,GAAgBhJ,EAAMH,CAAI,EAC1BuE,EAAM,kBAAoBpE,EAC1BoE,EAAM,mBAAqB,EAC7B,CCvJA,eAAsBoV,GAAapV,EAAsB,CACvD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,gBACV,CAAAA,EAAM,gBAAkB,GACxBA,EAAM,cAAgB,KACtBA,EAAM,eAAiB,KACvB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,kBAAmB,EAAE,EAGzD,MAAM,QAAQC,CAAG,GACnBD,EAAM,gBAAkBC,EACxBD,EAAM,eAAiBC,EAAI,SAAW,EAAI,oBAAsB,OAEhED,EAAM,gBAAkB,CAAA,EACxBA,EAAM,eAAiB,uBAE3B,OAASE,EAAK,CACZF,EAAM,cAAgB,OAAOE,CAAG,CAClC,QAAA,CACEF,EAAM,gBAAkB,EAC1B,EACF,CCTA,SAASqV,GAAgBrV,EAAoBgB,EAAarC,EAAwB,CAChF,GAAI,CAACqC,EAAI,OAAQ,OACjB,MAAMjG,EAAO,CAAE,GAAGiF,EAAM,aAAA,EACpBrB,EAAS5D,EAAKiG,CAAG,EAAIrC,EACpB,OAAO5D,EAAKiG,CAAG,EACpBhB,EAAM,cAAgBjF,CACxB,CAEA,SAASua,GAAgBpV,EAAc,CACrC,OAAIA,aAAe,MAAcA,EAAI,QAC9B,OAAOA,CAAG,CACnB,CAEA,eAAsBqV,GAAWvV,EAAoBwV,EAA6B,CAIhF,GAHIA,GAAS,eAAiB,OAAO,KAAKxV,EAAM,aAAa,EAAE,OAAS,IACtEA,EAAM,cAAgB,CAAA,GAEpB,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,cACV,CAAAA,EAAM,cAAgB,GACtBA,EAAM,YAAc,KACpB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,gBAAiB,EAAE,EAGvDC,MAAW,aAAeA,EAChC,OAASC,EAAK,CACZF,EAAM,YAAcsV,GAAgBpV,CAAG,CACzC,QAAA,CACEF,EAAM,cAAgB,EACxB,EACF,CAEO,SAASyV,GACdzV,EACA0V,EACAzb,EACA,CACA+F,EAAM,WAAa,CAAE,GAAGA,EAAM,WAAY,CAAC0V,CAAQ,EAAGzb,CAAA,CACxD,CAEA,eAAsB0b,GACpB3V,EACA0V,EACArP,EACA,CACA,GAAI,GAACrG,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,cAAgB0V,EACtB1V,EAAM,YAAc,KACpB,GAAI,CACF,MAAMA,EAAM,OAAO,QAAQ,gBAAiB,CAAE,SAAA0V,EAAU,QAAArP,EAAS,EACjE,MAAMkP,GAAWvV,CAAK,EACtBqV,GAAgBrV,EAAO0V,EAAU,CAC/B,KAAM,UACN,QAASrP,EAAU,gBAAkB,gBAAA,CACtC,CACH,OAASnG,EAAK,CACZ,MAAMvB,EAAU2W,GAAgBpV,CAAG,EACnCF,EAAM,YAAcrB,EACpB0W,GAAgBrV,EAAO0V,EAAU,CAC/B,KAAM,QACN,QAAA/W,CAAA,CACD,CACH,QAAA,CACEqB,EAAM,cAAgB,IACxB,EACF,CAEA,eAAsB4V,GAAgB5V,EAAoB0V,EAAkB,CAC1E,GAAI,GAAC1V,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,cAAgB0V,EACtB1V,EAAM,YAAc,KACpB,GAAI,CACF,MAAM6V,EAAS7V,EAAM,WAAW0V,CAAQ,GAAK,GAC7C,MAAM1V,EAAM,OAAO,QAAQ,gBAAiB,CAAE,SAAA0V,EAAU,OAAAG,EAAQ,EAChE,MAAMN,GAAWvV,CAAK,EACtBqV,GAAgBrV,EAAO0V,EAAU,CAC/B,KAAM,UACN,QAAS,eAAA,CACV,CACH,OAASxV,EAAK,CACZ,MAAMvB,EAAU2W,GAAgBpV,CAAG,EACnCF,EAAM,YAAcrB,EACpB0W,GAAgBrV,EAAO0V,EAAU,CAC/B,KAAM,QACN,QAAA/W,CAAA,CACD,CACH,QAAA,CACEqB,EAAM,cAAgB,IACxB,EACF,CAEA,eAAsB8V,GACpB9V,EACA0V,EACApb,EACAyb,EACA,CACA,GAAI,GAAC/V,EAAM,QAAU,CAACA,EAAM,WAC5B,CAAAA,EAAM,cAAgB0V,EACtB1V,EAAM,YAAc,KACpB,GAAI,CACF,MAAMlC,EAAU,MAAMkC,EAAM,OAAO,QAAQ,iBAAkB,CAC3D,KAAA1F,EACA,UAAAyb,EACA,UAAW,IAAA,CACZ,EACD,MAAMR,GAAWvV,CAAK,EACtBqV,GAAgBrV,EAAO0V,EAAU,CAC/B,KAAM,UACN,QAAS5X,GAAQ,SAAW,WAAA,CAC7B,CACH,OAASoC,EAAK,CACZ,MAAMvB,EAAU2W,GAAgBpV,CAAG,EACnCF,EAAM,YAAcrB,EACpB0W,GAAgBrV,EAAO0V,EAAU,CAC/B,KAAM,QACN,QAAA/W,CAAA,CACD,CACH,QAAA,CACEqB,EAAM,cAAgB,IACxB,EACF,CChJO,SAASgW,IAAgC,CAC9C,OAAI,OAAO,OAAW,KAAe,OAAO,OAAO,YAAe,YAG3D,OAAO,WAAW,8BAA8B,EAAE,QAFhD,OAIL,OACN,CAEO,SAASC,GAAaC,EAAgC,CAC3D,OAAIA,IAAS,SAAiBF,GAAA,EACvBE,CACT,CCIA,MAAMC,GAAWlc,GACX,OAAO,MAAMA,CAAK,EAAU,GAC5BA,GAAS,EAAU,EACnBA,GAAS,EAAU,EAChBA,EAGHmc,GAA6B,IAC7B,OAAO,OAAW,KAAe,OAAO,OAAO,YAAe,WACzD,GAEF,OAAO,WAAW,kCAAkC,EAAE,SAAW,GAGpEC,GAA0BC,GAAsB,CACpDA,EAAK,UAAU,OAAO,kBAAkB,EACxCA,EAAK,MAAM,eAAe,kBAAkB,EAC5CA,EAAK,MAAM,eAAe,kBAAkB,CAC9C,EAEaC,GAAuB,CAAC,CACnC,UAAAC,EACA,WAAAC,EACA,QAAAC,EACA,aAAAC,CACF,IAA8B,CAC5B,GAAIA,IAAiBH,EAAW,OAEhC,MAAMI,EAAoB,WAAW,UAAY,KACjD,GAAI,CAACA,EAAmB,CACtBH,EAAA,EACA,MACF,CAEA,MAAMH,EAAOM,EAAkB,gBACzBC,EAAYD,EACZE,EAAuBV,GAAA,EAK7B,GAFE,EAAQS,EAAU,qBAAwB,CAACC,EAEnB,CACxB,IAAIC,EAAW,GACXC,EAAW,GAEf,GACEN,GAAS,iBAAmB,QAC5BA,GAAS,iBAAmB,QAC5B,OAAO,OAAW,IAElBK,EAAWZ,GAAQO,EAAQ,eAAiB,OAAO,UAAU,EAC7DM,EAAWb,GAAQO,EAAQ,eAAiB,OAAO,WAAW,UACrDA,GAAS,QAAS,CAC3B,MAAMO,EAAOP,EAAQ,QAAQ,sBAAA,EAE3BO,EAAK,MAAQ,GACbA,EAAK,OAAS,GACd,OAAO,OAAW,MAElBF,EAAWZ,IAASc,EAAK,KAAOA,EAAK,MAAQ,GAAK,OAAO,UAAU,EACnED,EAAWb,IAASc,EAAK,IAAMA,EAAK,OAAS,GAAK,OAAO,WAAW,EAExE,CAEAX,EAAK,MAAM,YAAY,mBAAoB,GAAGS,EAAW,GAAG,GAAG,EAC/DT,EAAK,MAAM,YAAY,mBAAoB,GAAGU,EAAW,GAAG,GAAG,EAC/DV,EAAK,UAAU,IAAI,kBAAkB,EAErC,GAAI,CACF,MAAMY,EAAaL,EAAU,sBAAsB,IAAM,CACvDJ,EAAA,CACF,CAAC,EACGS,GAAY,SACTA,EAAW,SAAS,QAAQ,IAAMb,GAAuBC,CAAI,CAAC,EAEnED,GAAuBC,CAAI,CAE/B,MAAQ,CACND,GAAuBC,CAAI,EAC3BG,EAAA,CACF,CACA,MACF,CAEAA,EAAA,EACAJ,GAAuBC,CAAI,CAC7B,EC7FO,SAASa,GAAkBpV,EAAmB,CAC/CA,EAAK,mBAAqB,OAC9BA,EAAK,kBAAoB,OAAO,YAC9B,IAAA,CAAW0S,GAAU1S,EAAgC,CAAE,MAAO,GAAM,GACpE,GAAA,EAEJ,CAEO,SAASqV,GAAiBrV,EAAmB,CAC9CA,EAAK,mBAAqB,OAC9B,cAAcA,EAAK,iBAAiB,EACpCA,EAAK,kBAAoB,KAC3B,CAEO,SAASsV,GAAiBtV,EAAmB,CAC9CA,EAAK,kBAAoB,OAC7BA,EAAK,iBAAmB,OAAO,YAAY,IAAM,CAC3CA,EAAK,MAAQ,QACZmG,GAASnG,EAAgC,CAAE,MAAO,GAAM,CAC/D,EAAG,GAAI,EACT,CAEO,SAASuV,GAAgBvV,EAAmB,CAC7CA,EAAK,kBAAoB,OAC7B,cAAcA,EAAK,gBAAgB,EACnCA,EAAK,iBAAmB,KAC1B,CAEO,SAASwV,GAAkBxV,EAAmB,CAC/CA,EAAK,mBAAqB,OAC9BA,EAAK,kBAAoB,OAAO,YAAY,IAAM,CAC5CA,EAAK,MAAQ,SACZgF,GAAUhF,CAA8B,CAC/C,EAAG,GAAI,EACT,CAEO,SAASyV,GAAiBzV,EAAmB,CAC9CA,EAAK,mBAAqB,OAC9B,cAAcA,EAAK,iBAAiB,EACpCA,EAAK,kBAAoB,KAC3B,CCfO,SAAS0V,GAAc1V,EAAoBhH,EAAkB,CAClE,MAAMe,EAAa,CACjB,GAAGf,EACH,qBAAsBA,EAAK,sBAAsB,KAAA,GAAUA,EAAK,WAAW,QAAU,MAAA,EAEvFgH,EAAK,SAAWjG,EAChBhB,GAAagB,CAAU,EACnBf,EAAK,QAAUgH,EAAK,QACtBA,EAAK,MAAQhH,EAAK,MAClB2c,GAAmB3V,EAAMkU,GAAalb,EAAK,KAAK,CAAC,GAEnDgH,EAAK,gBAAkBA,EAAK,SAAS,oBACvC,CAEO,SAAS4V,GAAwB5V,EAAoBhH,EAAc,CACxE,MAAMZ,EAAUY,EAAK,KAAA,EAChBZ,GACD4H,EAAK,SAAS,uBAAyB5H,GAC3Csd,GAAc1V,EAAM,CAAE,GAAGA,EAAK,SAAU,qBAAsB5H,EAAS,CACzE,CAEO,SAASyd,GAAqB7V,EAAoB,CACvD,GAAI,CAAC,OAAO,SAAS,OAAQ,OAC7B,MAAMnB,EAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM,EACnDiX,EAAWjX,EAAO,IAAI,OAAO,EAC7BkX,EAAclX,EAAO,IAAI,UAAU,EACnCmX,EAAanX,EAAO,IAAI,SAAS,EACjCoX,EAAgBpX,EAAO,IAAI,YAAY,EAC7C,IAAIqX,EAAiB,GAErB,GAAIJ,GAAY,KAAM,CACpB,MAAMK,EAAQL,EAAS,KAAA,EACnBK,GAASA,IAAUnW,EAAK,SAAS,OACnC0V,GAAc1V,EAAM,CAAE,GAAGA,EAAK,SAAU,MAAAmW,EAAO,EAEjDtX,EAAO,OAAO,OAAO,EACrBqX,EAAiB,EACnB,CAEA,GAAIH,GAAe,KAAM,CACvB,MAAMK,EAAWL,EAAY,KAAA,EACzBK,IACDpW,EAA8B,SAAWoW,GAE5CvX,EAAO,OAAO,UAAU,EACxBqX,EAAiB,EACnB,CAEA,GAAIF,GAAc,KAAM,CACtB,MAAMK,EAAUL,EAAW,KAAA,EACvBK,IACFrW,EAAK,WAAaqW,EAClBX,GAAc1V,EAAM,CAClB,GAAGA,EAAK,SACR,WAAYqW,EACZ,qBAAsBA,CAAA,CACvB,EAEL,CAEA,GAAIJ,GAAiB,KAAM,CACzB,MAAMK,EAAaL,EAAc,KAAA,EAC7BK,GAAcA,IAAetW,EAAK,SAAS,YAC7C0V,GAAc1V,EAAM,CAAE,GAAGA,EAAK,SAAU,WAAAsW,EAAY,EAEtDzX,EAAO,OAAO,YAAY,EAC1BqX,EAAiB,EACnB,CAEA,GAAI,CAACA,EAAgB,OACrB,MAAMnU,EAAM,IAAI,IAAI,OAAO,SAAS,IAAI,EACxCA,EAAI,OAASlD,EAAO,SAAA,EACpB,OAAO,QAAQ,aAAa,CAAA,EAAI,GAAIkD,EAAI,UAAU,CACpD,CAEO,SAASwU,GAAOvW,EAAoBhH,EAAW,CAChDgH,EAAK,MAAQhH,IAAMgH,EAAK,IAAMhH,GAC9BA,IAAS,SAAQgH,EAAK,oBAAsB,IAC5ChH,IAAS,OACXsc,GAAiBtV,CAAyD,KACvDA,CAAwD,EACzEhH,IAAS,QACXwc,GAAkBxV,CAA0D,KACxDA,CAAyD,EAC1EwW,GAAiBxW,CAAI,EAC1ByW,GAAezW,EAAMhH,EAAM,EAAK,CAClC,CAEO,SAAS0d,GACd1W,EACAhH,EACA2b,EACA,CAMAH,GAAqB,CACnB,UAAWxb,EACX,WAPiB,IAAM,CACvBgH,EAAK,MAAQhH,EACb0c,GAAc1V,EAAM,CAAE,GAAGA,EAAK,SAAU,MAAOhH,EAAM,EACrD2c,GAAmB3V,EAAMkU,GAAalb,CAAI,CAAC,CAC7C,EAIE,QAAA2b,EACA,aAAc3U,EAAK,KAAA,CACpB,CACH,CAEA,eAAsBwW,GAAiBxW,EAAoB,CACrDA,EAAK,MAAQ,YAAY,MAAM2W,GAAa3W,CAAI,EAChDA,EAAK,MAAQ,YAAY,MAAM4W,GAAgB5W,CAAI,EACnDA,EAAK,MAAQ,aAAa,MAAMqT,GAAarT,CAA8B,EAC3EA,EAAK,MAAQ,YAAY,MAAMpB,GAAaoB,CAA8B,EAC1EA,EAAK,MAAQ,QAAQ,MAAM6W,GAAS7W,CAAI,EACxCA,EAAK,MAAQ,UAAU,MAAMwT,GAAWxT,CAA8B,EACtEA,EAAK,MAAQ,UACf,MAAM0S,GAAU1S,CAA8B,EAC9C,MAAMoS,GAAYpS,CAA8B,EAChD,MAAM8C,GAAW9C,CAA8B,EAC/C,MAAM8S,GAAkB9S,CAA8B,GAEpDA,EAAK,MAAQ,SACf,MAAM8W,GAAY9W,CAAoD,EACtEe,GACEf,EACA,CAACA,EAAK,mBAAA,GAGNA,EAAK,MAAQ,WACf,MAAMgD,GAAiBhD,CAA8B,EACrD,MAAM8C,GAAW9C,CAA8B,GAE7CA,EAAK,MAAQ,UACf,MAAMgF,GAAUhF,CAA8B,EAC9CA,EAAK,SAAWA,EAAK,gBAEnBA,EAAK,MAAQ,SACfA,EAAK,aAAe,GACpB,MAAMmG,GAASnG,EAAgC,CAAE,MAAO,GAAM,EAC9DwB,GACExB,EACA,EAAA,EAGN,CAEO,SAAS+W,IAAgB,CAC9B,GAAI,OAAO,OAAW,IAAa,MAAO,GAC1C,MAAMC,EAAa,OAAO,kCAC1B,OAAI,OAAOA,GAAe,UAAYA,EAAW,OACxCrd,GAAkBqd,CAAU,EAE9B7c,GAA0B,OAAO,SAAS,QAAQ,CAC3D,CAEO,SAAS8c,GAAsBjX,EAAoB,CACxDA,EAAK,MAAQA,EAAK,SAAS,OAAS,SACpC2V,GAAmB3V,EAAMkU,GAAalU,EAAK,KAAK,CAAC,CACnD,CAEO,SAAS2V,GAAmB3V,EAAoBkX,EAAyB,CAE9E,GADAlX,EAAK,cAAgBkX,EACjB,OAAO,SAAa,IAAa,OACrC,MAAM3C,EAAO,SAAS,gBACtBA,EAAK,QAAQ,MAAQ2C,EACrB3C,EAAK,MAAM,YAAc2C,CAC3B,CAEO,SAASC,GAAoBnX,EAAoB,CACtD,GAAI,OAAO,OAAW,KAAe,OAAO,OAAO,YAAe,WAAY,OAM9E,GALAA,EAAK,WAAa,OAAO,WAAW,8BAA8B,EAClEA,EAAK,kBAAqB0B,GAAU,CAC9B1B,EAAK,QAAU,UACnB2V,GAAmB3V,EAAM0B,EAAM,QAAU,OAAS,OAAO,CAC3D,EACI,OAAO1B,EAAK,WAAW,kBAAqB,WAAY,CAC1DA,EAAK,WAAW,iBAAiB,SAAUA,EAAK,iBAAiB,EACjE,MACF,CACeA,EAAK,WAGb,YAAYA,EAAK,iBAAiB,CAC3C,CAEO,SAASoX,GAAoBpX,EAAoB,CACtD,GAAI,CAACA,EAAK,YAAc,CAACA,EAAK,kBAAmB,OACjD,GAAI,OAAOA,EAAK,WAAW,qBAAwB,WAAY,CAC7DA,EAAK,WAAW,oBAAoB,SAAUA,EAAK,iBAAiB,EACpE,MACF,CACeA,EAAK,WAGb,eAAeA,EAAK,iBAAiB,EAC5CA,EAAK,WAAa,KAClBA,EAAK,kBAAoB,IAC3B,CAEO,SAASqX,GAAoBrX,EAAoBsX,EAAkB,CACxE,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMJ,EAAWjd,GAAY,OAAO,SAAS,SAAU+F,EAAK,QAAQ,GAAK,OACzEuX,GAAgBvX,EAAMkX,CAAQ,EAC9BT,GAAezW,EAAMkX,EAAUI,CAAO,CACxC,CAEO,SAASE,GAAWxX,EAAoB,CAC7C,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMkX,EAAWjd,GAAY,OAAO,SAAS,SAAU+F,EAAK,QAAQ,EACpE,GAAI,CAACkX,EAAU,OAGf,MAAMb,EADM,IAAI,IAAI,OAAO,SAAS,IAAI,EACpB,aAAa,IAAI,SAAS,GAAG,KAAA,EAC7CA,IACFrW,EAAK,WAAaqW,EAClBX,GAAc1V,EAAM,CAClB,GAAGA,EAAK,SACR,WAAYqW,EACZ,qBAAsBA,CAAA,CACvB,GAGHkB,GAAgBvX,EAAMkX,CAAQ,CAChC,CAEO,SAASK,GAAgBvX,EAAoBhH,EAAW,CACzDgH,EAAK,MAAQhH,IAAMgH,EAAK,IAAMhH,GAC9BA,IAAS,SAAQgH,EAAK,oBAAsB,IAC5ChH,IAAS,OACXsc,GAAiBtV,CAAyD,KACvDA,CAAwD,EACzEhH,IAAS,QACXwc,GAAkBxV,CAA0D,KACxDA,CAAyD,EAC3EA,EAAK,WAAgBwW,GAAiBxW,CAAI,CAChD,CAEO,SAASyW,GAAezW,EAAoBvG,EAAU6d,EAAkB,CAC7E,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMG,EAAa3d,GAAcE,GAAWP,EAAKuG,EAAK,QAAQ,CAAC,EACzD0X,EAAc5d,GAAc,OAAO,SAAS,QAAQ,EACpDiI,EAAM,IAAI,IAAI,OAAO,SAAS,IAAI,EAEpCtI,IAAQ,QAAUuG,EAAK,WACzB+B,EAAI,aAAa,IAAI,UAAW/B,EAAK,UAAU,EAE/C+B,EAAI,aAAa,OAAO,SAAS,EAG/B2V,IAAgBD,IAClB1V,EAAI,SAAW0V,GAGbH,EACF,OAAO,QAAQ,aAAa,CAAA,EAAI,GAAIvV,EAAI,UAAU,EAElD,OAAO,QAAQ,UAAU,CAAA,EAAI,GAAIA,EAAI,UAAU,CAEnD,CAEO,SAAS4V,GACd3X,EACA9G,EACAoe,EACA,CACA,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMvV,EAAM,IAAI,IAAI,OAAO,SAAS,IAAI,EACxCA,EAAI,aAAa,IAAI,UAAW7I,CAAU,SACtB,QAAQ,aAAa,CAAA,EAAI,GAAI6I,EAAI,UAAU,CAEjE,CAEA,eAAsB4U,GAAa3W,EAAoB,CACrD,MAAM,QAAQ,IAAI,CAChB2E,GAAa3E,EAAgC,EAAK,EAClDqT,GAAarT,CAA8B,EAC3CpB,GAAaoB,CAA8B,EAC3C0D,GAAe1D,CAA8B,EAC7CgF,GAAUhF,CAA8B,CAAA,CACzC,CACH,CAEA,eAAsB4W,GAAgB5W,EAAoB,CACxD,MAAM,QAAQ,IAAI,CAChB2E,GAAa3E,EAAgC,EAAI,EACjDgD,GAAiBhD,CAA8B,EAC/C8C,GAAW9C,CAA8B,CAAA,CAC1C,CACH,CAEA,eAAsB6W,GAAS7W,EAAoB,CACjD,MAAM,QAAQ,IAAI,CAChB2E,GAAa3E,EAAgC,EAAK,EAClD0D,GAAe1D,CAA8B,EAC7C2D,GAAa3D,CAA8B,CAAA,CAC5C,CACH,CCpTO,SAAS4X,GAAW5X,EAAgB,CACzC,OAAOA,EAAK,aAAe,EAAQA,EAAK,SAC1C,CAEO,SAAS6X,GAAkBnb,EAAc,CAC9C,MAAMtE,EAAUsE,EAAK,KAAA,EACrB,GAAI,CAACtE,EAAS,MAAO,GACrB,MAAM2B,EAAa3B,EAAQ,YAAA,EAC3B,OAAI2B,IAAe,QAAgB,GAEjCA,IAAe,QACfA,IAAe,OACfA,IAAe,SACfA,IAAe,QACfA,IAAe,MAEnB,CAEA,eAAsB+d,GAAgB9X,EAAgB,CAC/CA,EAAK,YACVA,EAAK,YAAc,GACnB,MAAMxB,GAAawB,CAA8B,EACnD,CAEA,SAAS+X,GAAmB/X,EAAgBtD,EAAc,CACxD,MAAMtE,EAAUsE,EAAK,KAAA,EAChBtE,IACL4H,EAAK,UAAY,CACf,GAAGA,EAAK,UACR,CACE,GAAIlC,GAAA,EACJ,KAAM1F,EACN,UAAW,KAAK,IAAA,CAAI,CACtB,EAEJ,CAEA,eAAe4f,GACbhY,EACApD,EACAwJ,EACA,CACA5F,GAAgBR,CAAwD,EACxE,MAAMiY,EAAK,MAAM7Z,GAAgB4B,EAAgCpD,CAAO,EACxE,MAAI,CAACqb,GAAM7R,GAAM,eAAiB,OAChCpG,EAAK,YAAcoG,EAAK,eAEtB6R,GACFrC,GAAwB5V,EAAkEA,EAAK,UAAU,EAEvGiY,GAAM7R,GAAM,cAAgBA,EAAK,eAAe,SAClDpG,EAAK,YAAcoG,EAAK,eAE1BrF,GAAmBf,CAA2D,EAC1EiY,GAAM,CAACjY,EAAK,WACTkY,GAAelY,CAAI,EAEnBiY,CACT,CAEA,eAAeC,GAAelY,EAAgB,CAC5C,GAAI,CAACA,EAAK,WAAa4X,GAAW5X,CAAI,EAAG,OACzC,KAAM,CAAChH,EAAM,GAAGK,CAAI,EAAI2G,EAAK,UAC7B,GAAI,CAAChH,EAAM,OACXgH,EAAK,UAAY3G,EACN,MAAM2e,GAAmBhY,EAAMhH,EAAK,IAAI,IAEjDgH,EAAK,UAAY,CAAChH,EAAM,GAAGgH,EAAK,SAAS,EAE7C,CAEO,SAASmY,GAAoBnY,EAAgBG,EAAY,CAC9DH,EAAK,UAAYA,EAAK,UAAU,OAAQjD,GAASA,EAAK,KAAOoD,CAAE,CACjE,CAEA,eAAsBiY,GACpBpY,EACAqY,EACAjS,EACA,CACA,GAAI,CAACpG,EAAK,UAAW,OACrB,MAAMsY,EAAgBtY,EAAK,YACrBpD,GAAWyb,GAAmBrY,EAAK,aAAa,KAAA,EACtD,GAAKpD,EAEL,IAAIib,GAAkBjb,CAAO,EAAG,CAC9B,MAAMkb,GAAgB9X,CAAI,EAC1B,MACF,CAMA,GAJIqY,GAAmB,OACrBrY,EAAK,YAAc,IAGjB4X,GAAW5X,CAAI,EAAG,CACpB+X,GAAmB/X,EAAMpD,CAAO,EAChC,MACF,CAEA,MAAMob,GAAmBhY,EAAMpD,EAAS,CACtC,cAAeyb,GAAmB,KAAOC,EAAgB,OACzD,aAAc,GAAQD,GAAmBjS,GAAM,aAAY,CAC5D,EACH,CAEA,eAAsB0Q,GAAY9W,EAAgB,CAChD,MAAM,QAAQ,IAAI,CAChBhC,GAAgBgC,CAA8B,EAC9CpB,GAAaoB,CAA8B,EAC3CuY,GAAkBvY,CAAI,CAAA,CACvB,EACDe,GAAmBf,EAA6D,EAAI,CACtF,CAEO,MAAMwY,GAAyBN,GAMtC,SAASO,GAAyBzY,EAA+B,CAC/D,MAAMlH,EAASG,GAAqB+G,EAAK,UAAU,EACnD,OAAIlH,GAAQ,QAAgBA,EAAO,QAClBkH,EAAK,OAAO,UACF,iBAAiB,gBAAgB,KAAA,GACzC,MACrB,CAEA,SAAS0Y,GAAmB9e,EAAkBR,EAAyB,CACrE,MAAMS,EAAOF,GAAkBC,CAAQ,EACjC+e,EAAU,mBAAmBvf,CAAO,EAC1C,OAAOS,EAAO,GAAGA,CAAI,WAAW8e,CAAO,UAAY,WAAWA,CAAO,SACvE,CAEA,eAAsBJ,GAAkBvY,EAAgB,CACtD,GAAI,CAACA,EAAK,UAAW,CACnBA,EAAK,cAAgB,KACrB,MACF,CACA,MAAM5G,EAAUqf,GAAyBzY,CAAI,EAC7C,GAAI,CAAC5G,EAAS,CACZ4G,EAAK,cAAgB,KACrB,MACF,CACAA,EAAK,cAAgB,KACrB,MAAM+B,EAAM2W,GAAmB1Y,EAAK,SAAU5G,CAAO,EACrD,GAAI,CACF,MAAM8E,EAAM,MAAM,MAAM6D,EAAK,CAAE,OAAQ,MAAO,EAC9C,GAAI,CAAC7D,EAAI,GAAI,CACX8B,EAAK,cAAgB,KACrB,MACF,CACA,MAAMU,EAAQ,MAAMxC,EAAI,KAAA,EAClB0a,EAAY,OAAOlY,EAAK,WAAc,SAAWA,EAAK,UAAU,OAAS,GAC/EV,EAAK,cAAgB4Y,GAAa,IACpC,MAAQ,CACN5Y,EAAK,cAAgB,IACvB,CACF,CChLA,MAAMhL,GAAE,CAAa,MAAM,CAAkD,EAAEC,GAAED,GAAG,IAAIC,KAAK,CAAC,gBAAgBD,EAAE,OAAOC,CAAC,GAAE,IAAA4jB,GAAC,KAAO,CAAC,YAAY,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE5jB,EAAEM,EAAE,CAAC,KAAK,KAAK,EAAE,KAAK,KAAKN,EAAE,KAAK,KAAKM,CAAC,CAAC,KAAK,EAAEN,EAAE,CAAC,OAAO,KAAK,OAAO,EAAEA,CAAC,CAAC,CAAC,OAAO,EAAEA,EAAE,CAAC,OAAO,KAAK,OAAO,GAAGA,CAAC,CAAC,CAAC,ECApS,KAAC,CAAC,EAAED,EAAC,EAAEG,GAAEI,GAAEJ,GAAGA,EAA8PD,GAAE,IAAI,SAAS,cAAc,EAAE,EAAEkB,GAAE,CAACjB,EAAEG,EAAEL,IAAI,CAAC,MAAMW,EAAET,EAAE,KAAK,WAAWW,EAAWR,IAAT,OAAWH,EAAE,KAAKG,EAAE,KAAK,GAAYL,IAAT,OAAW,CAAC,MAAMM,EAAEK,EAAE,aAAaV,GAAC,EAAGY,CAAC,EAAER,EAAEM,EAAE,aAAaV,GAAC,EAAGY,CAAC,EAAEb,EAAE,IAAID,GAAEO,EAAED,EAAEH,EAAEA,EAAE,OAAO,CAAC,KAAK,CAAC,MAAMH,EAAEC,EAAE,KAAK,YAAYK,EAAEL,EAAE,KAAK,EAAEK,IAAIH,EAAE,GAAG,EAAE,CAAC,IAAIH,EAAEC,EAAE,OAAOE,CAAC,EAAEF,EAAE,KAAKE,EAAWF,EAAE,OAAX,SAAkBD,EAAEG,EAAE,QAAQG,EAAE,MAAML,EAAE,KAAKD,CAAC,CAAC,CAAC,GAAGA,IAAIc,GAAG,EAAE,CAAC,IAAIX,EAAEF,EAAE,KAAK,KAAKE,IAAIH,GAAG,CAAC,MAAMA,EAAEO,GAAEJ,CAAC,EAAE,YAAYI,GAAEK,CAAC,EAAE,aAAaT,EAAEW,CAAC,EAAEX,EAAEH,CAAC,CAAC,CAAC,CAAC,OAAOC,CAAC,EAAEc,GAAE,CAACZ,EAAE,EAAEI,EAAEJ,KAAKA,EAAE,KAAK,EAAEI,CAAC,EAAEJ,GAAGmB,GAAE,CAAA,EAAGT,GAAE,CAACV,EAAE,EAAEmB,KAAInB,EAAE,KAAK,EAAEkC,GAAElC,GAAGA,EAAE,KAAKO,GAAEP,GAAG,CAACA,EAAE,KAAI,EAAGA,EAAE,KAAK,QAAQ,ECC5xB,MAAMY,GAAE,CAAC,EAAEb,EAAEF,IAAI,CAAC,MAAMK,EAAE,IAAI,IAAI,QAAQO,EAAEV,EAAEU,GAAGZ,EAAEY,IAAIP,EAAE,IAAI,EAAEO,CAAC,EAAEA,CAAC,EAAE,OAAOP,CAAC,EAAEI,GAAEP,GAAE,cAAcF,EAAC,CAAC,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,EAAE,OAAOK,GAAE,MAAM,MAAM,MAAM,+CAA+C,CAAC,CAAC,GAAG,EAAEH,EAAEF,EAAE,CAAC,IAAIK,EAAWL,IAAT,OAAWA,EAAEE,EAAWA,IAAT,SAAaG,EAAEH,GAAG,MAAMU,EAAE,CAAA,EAAG,EAAE,GAAG,IAAIL,EAAE,EAAE,UAAUL,KAAK,EAAEU,EAAEL,CAAC,EAAEF,EAAEA,EAAEH,EAAEK,CAAC,EAAEA,EAAE,EAAEA,CAAC,EAAEP,EAAEE,EAAEK,CAAC,EAAEA,IAAI,MAAM,CAAC,OAAO,EAAE,KAAKK,CAAC,CAAC,CAAC,OAAO,EAAEV,EAAEF,EAAE,CAAC,OAAO,KAAK,GAAG,EAAEE,EAAEF,CAAC,EAAE,MAAM,CAAC,OAAOE,EAAE,CAAC,EAAEG,EAAEI,CAAC,EAAE,CAAC,MAAMK,EAAEF,GAAEV,CAAC,EAAE,CAAC,OAAOW,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,EAAER,EAAEI,CAAC,EAAE,GAAG,CAAC,MAAM,QAAQK,CAAC,EAAE,OAAO,KAAK,GAAG,EAAED,EAAE,MAAMH,EAAE,KAAK,KAAK,CAAA,EAAGU,EAAE,GAAG,IAAIE,EAAEH,EAAEM,EAAE,EAAEkB,EAAE7B,EAAE,OAAO,EAAEyB,EAAE,EAAE,EAAE1B,EAAE,OAAO,EAAE,KAAKY,GAAGkB,GAAGJ,GAAG,GAAG,GAAUzB,EAAEW,CAAC,IAAV,KAAYA,YAAmBX,EAAE6B,CAAC,IAAV,KAAYA,YAAYjC,EAAEe,CAAC,IAAI,EAAEc,CAAC,EAAEnB,EAAEmB,CAAC,EAAEpC,GAAEW,EAAEW,CAAC,EAAEZ,EAAE0B,CAAC,CAAC,EAAEd,IAAIc,YAAY7B,EAAEiC,CAAC,IAAI,EAAE,CAAC,EAAEvB,EAAE,CAAC,EAAEjB,GAAEW,EAAE6B,CAAC,EAAE9B,EAAE,CAAC,CAAC,EAAE8B,IAAI,YAAYjC,EAAEe,CAAC,IAAI,EAAE,CAAC,EAAEL,EAAE,CAAC,EAAEjB,GAAEW,EAAEW,CAAC,EAAEZ,EAAE,CAAC,CAAC,EAAEN,GAAEL,EAAEkB,EAAE,EAAE,CAAC,EAAEN,EAAEW,CAAC,CAAC,EAAEA,IAAI,YAAYf,EAAEiC,CAAC,IAAI,EAAEJ,CAAC,EAAEnB,EAAEmB,CAAC,EAAEpC,GAAEW,EAAE6B,CAAC,EAAE9B,EAAE0B,CAAC,CAAC,EAAEhC,GAAEL,EAAEY,EAAEW,CAAC,EAAEX,EAAE6B,CAAC,CAAC,EAAEA,IAAIJ,YAAqBjB,IAAT,SAAaA,EAAEP,GAAE,EAAEwB,EAAE,CAAC,EAAEpB,EAAEJ,GAAEL,EAAEe,EAAEkB,CAAC,GAAGrB,EAAE,IAAIZ,EAAEe,CAAC,CAAC,EAAE,GAAGH,EAAE,IAAIZ,EAAEiC,CAAC,CAAC,EAAE,CAAC,MAAM1C,EAAEkB,EAAE,IAAI,EAAEoB,CAAC,CAAC,EAAEvC,EAAWC,IAAT,OAAWa,EAAEb,CAAC,EAAE,KAAK,GAAUD,IAAP,KAAS,CAAC,MAAMC,EAAEM,GAAEL,EAAEY,EAAEW,CAAC,CAAC,EAAEtB,GAAEF,EAAEY,EAAE0B,CAAC,CAAC,EAAEnB,EAAEmB,CAAC,EAAEtC,CAAC,MAAMmB,EAAEmB,CAAC,EAAEpC,GAAEH,EAAEa,EAAE0B,CAAC,CAAC,EAAEhC,GAAEL,EAAEY,EAAEW,CAAC,EAAEzB,CAAC,EAAEc,EAAEb,CAAC,EAAE,KAAKsC,GAAG,MAAMjC,GAAEQ,EAAE6B,CAAC,CAAC,EAAEA,SAASrC,GAAEQ,EAAEW,CAAC,CAAC,EAAEA,IAAI,KAAKc,GAAG,GAAG,CAAC,MAAMtC,EAAEM,GAAEL,EAAEkB,EAAE,EAAE,CAAC,CAAC,EAAEjB,GAAEF,EAAEY,EAAE0B,CAAC,CAAC,EAAEnB,EAAEmB,GAAG,EAAEtC,CAAC,CAAC,KAAKwB,GAAGkB,GAAG,CAAC,MAAM1C,EAAEa,EAAEW,GAAG,EAASxB,IAAP,MAAUK,GAAEL,CAAC,CAAC,CAAC,OAAO,KAAK,GAAG,EAAEe,GAAEd,EAAEkB,CAAC,EAAEnB,EAAC,CAAC,CAAC,ECM7qC,SAAS6jB,GAAiBlc,EAAqC,CACpE,MAAMtG,EAAIsG,EACV,IAAIC,EAAO,OAAOvG,EAAE,MAAS,SAAWA,EAAE,KAAO,UAIjD,MAAMyiB,EACJ,OAAOziB,EAAE,YAAe,UAAY,OAAOA,EAAE,cAAiB,SAE1D0iB,EAAa1iB,EAAE,QACf2iB,EAAe,MAAM,QAAQD,CAAU,EAAIA,EAAa,KACxDE,EACJ,MAAM,QAAQD,CAAY,GAC1BA,EAAa,KAAMlc,GAAS,CAC1B,MAAMtG,EAAIsG,EACJ/H,EAAI,OAAOyB,EAAE,MAAQ,EAAE,EAAE,YAAA,EAC/B,OACEzB,IAAM,YACNA,IAAM,aACNA,IAAM,WACNA,IAAM,YACNA,IAAM,cACNA,IAAM,eACNA,IAAM,aACNA,IAAM,eACL,OAAOyB,EAAE,MAAS,UAAYA,EAAE,WAAa,IAElD,CAAC,EAEG0iB,EACJ,OAAQ7iB,EAA8B,UAAa,UACnD,OAAQA,EAA8B,WAAc,UAElDyiB,GAAaG,GAAkBC,KACjCtc,EAAO,cAIT,IAAIC,EAAgC,CAAA,EAEhC,OAAOxG,EAAE,SAAY,SACvBwG,EAAU,CAAC,CAAE,KAAM,OAAQ,KAAMxG,EAAE,QAAS,EACnC,MAAM,QAAQA,EAAE,OAAO,EAChCwG,EAAUxG,EAAE,QAAQ,IAAKyG,IAAmC,CAC1D,KAAOA,EAAK,MAAuC,OACnD,KAAMA,EAAK,KACX,KAAMA,EAAK,KACX,KAAMA,EAAK,MAAQA,EAAK,SAAA,EACxB,EACO,OAAOzG,EAAE,MAAS,WAC3BwG,EAAU,CAAC,CAAE,KAAM,OAAQ,KAAMxG,EAAE,KAAM,GAG3C,MAAM8iB,EAAY,OAAO9iB,EAAE,WAAc,SAAWA,EAAE,UAAY,KAAK,IAAA,EACjE6J,EAAK,OAAO7J,EAAE,IAAO,SAAWA,EAAE,GAAK,OAE7C,MAAO,CAAE,KAAAuG,EAAM,QAAAC,EAAS,UAAAsc,EAAW,GAAAjZ,CAAA,CACrC,CAKO,SAASkZ,GAAyBxc,EAAsB,CAC7D,MAAMyc,EAAQzc,EAAK,YAAA,EAEnB,OACEyc,IAAU,cACVA,IAAU,eACVA,IAAU,QACVA,IAAU,YACVA,IAAU,aAEH,OAELA,IAAU,YAAoB,YAC9BA,IAAU,OAAe,OACzBA,IAAU,SAAiB,SACxBzc,CACT,CAKO,SAAS0c,GAAoB3c,EAA2B,CAC7D,MAAMtG,EAAIsG,EACJC,EAAO,OAAOvG,EAAE,MAAS,SAAWA,EAAE,KAAK,cAAgB,GACjE,OAAOuG,IAAS,cAAgBA,IAAS,aAC3C,CC9FG,MAAM5H,WAAUC,EAAC,CAAC,YAAYK,EAAE,CAAC,GAAG,MAAMA,CAAC,EAAE,KAAK,GAAGP,EAAEO,EAAE,OAAOD,GAAE,MAAM,MAAM,MAAM,KAAK,YAAY,cAAc,uCAAuC,CAAC,CAAC,OAAOD,EAAE,CAAC,GAAGA,IAAIL,GAASK,GAAN,KAAQ,OAAO,KAAK,GAAG,OAAO,KAAK,GAAGA,EAAE,GAAGA,IAAIE,GAAE,OAAOF,EAAE,GAAa,OAAOA,GAAjB,SAAmB,MAAM,MAAM,KAAK,YAAY,cAAc,mCAAmC,EAAE,GAAGA,IAAI,KAAK,GAAG,OAAO,KAAK,GAAG,KAAK,GAAGA,EAAE,MAAMH,EAAE,CAACG,CAAC,EAAE,OAAOH,EAAE,IAAIA,EAAE,KAAK,GAAG,CAAC,WAAW,KAAK,YAAY,WAAW,QAAQA,EAAE,OAAO,CAAA,CAAE,CAAC,CAAC,CAACD,GAAE,cAAc,aAAaA,GAAE,WAAW,EAAE,MAAME,GAAEE,GAAEJ,EAAC,ECHnhB,KAAM,CACJ,QAAAoR,GACA,eAAAmT,GACA,SAAAC,GACA,eAAAC,GACA,yBAAAC,EACF,EAAI,OACJ,GAAI,CACF,OAAAC,EACA,KAAAC,GACA,OAAAC,EACF,EAAI,OACA,CACF,MAAAC,GACA,UAAAC,EACF,EAAI,OAAO,QAAY,KAAe,QACjCJ,IACHA,EAAS,SAAgBnjB,EAAG,CAC1B,OAAOA,CACT,GAEGojB,KACHA,GAAO,SAAcpjB,EAAG,CACtB,OAAOA,CACT,GAEGsjB,KACHA,GAAQ,SAAeE,EAAMC,EAAS,CACpC,QAASC,EAAO,UAAU,OAAQtZ,EAAO,IAAI,MAAMsZ,EAAO,EAAIA,EAAO,EAAI,CAAC,EAAGC,EAAO,EAAGA,EAAOD,EAAMC,IAClGvZ,EAAKuZ,EAAO,CAAC,EAAI,UAAUA,CAAI,EAEjC,OAAOH,EAAK,MAAMC,EAASrZ,CAAI,CACjC,GAEGmZ,KACHA,GAAY,SAAmBK,EAAM,CACnC,QAASC,EAAQ,UAAU,OAAQzZ,EAAO,IAAI,MAAMyZ,EAAQ,EAAIA,EAAQ,EAAI,CAAC,EAAGC,EAAQ,EAAGA,EAAQD,EAAOC,IACxG1Z,EAAK0Z,EAAQ,CAAC,EAAI,UAAUA,CAAK,EAEnC,OAAO,IAAIF,EAAK,GAAGxZ,CAAI,CACzB,GAEF,MAAM2Z,GAAeC,EAAQ,MAAM,UAAU,OAAO,EAC9CC,GAAmBD,EAAQ,MAAM,UAAU,WAAW,EACtDE,GAAWF,EAAQ,MAAM,UAAU,GAAG,EACtCG,GAAYH,EAAQ,MAAM,UAAU,IAAI,EACxCI,GAAcJ,EAAQ,MAAM,UAAU,MAAM,EAC5CK,GAAoBL,EAAQ,OAAO,UAAU,WAAW,EACxDM,GAAiBN,EAAQ,OAAO,UAAU,QAAQ,EAClDO,GAAcP,EAAQ,OAAO,UAAU,KAAK,EAC5CQ,GAAgBR,EAAQ,OAAO,UAAU,OAAO,EAChDS,GAAgBT,EAAQ,OAAO,UAAU,OAAO,EAChDU,GAAaV,EAAQ,OAAO,UAAU,IAAI,EAC1CW,GAAuBX,EAAQ,OAAO,UAAU,cAAc,EAC9DY,EAAaZ,EAAQ,OAAO,UAAU,IAAI,EAC1Ca,GAAkBC,GAAY,SAAS,EAO7C,SAASd,EAAQR,EAAM,CACrB,OAAO,SAAUC,EAAS,CACpBA,aAAmB,SACrBA,EAAQ,UAAY,GAEtB,QAASsB,EAAQ,UAAU,OAAQ3a,EAAO,IAAI,MAAM2a,EAAQ,EAAIA,EAAQ,EAAI,CAAC,EAAGC,EAAQ,EAAGA,EAAQD,EAAOC,IACxG5a,EAAK4a,EAAQ,CAAC,EAAI,UAAUA,CAAK,EAEnC,OAAO1B,GAAME,EAAMC,EAASrZ,CAAI,CAClC,CACF,CAOA,SAAS0a,GAAYlB,EAAM,CACzB,OAAO,UAAY,CACjB,QAASqB,EAAQ,UAAU,OAAQ7a,EAAO,IAAI,MAAM6a,CAAK,EAAGC,EAAQ,EAAGA,EAAQD,EAAOC,IACpF9a,EAAK8a,CAAK,EAAI,UAAUA,CAAK,EAE/B,OAAO3B,GAAUK,EAAMxZ,CAAI,CAC7B,CACF,CASA,SAAS+a,EAASC,EAAK1T,EAAO,CAC5B,IAAI2T,EAAoB,UAAU,OAAS,GAAK,UAAU,CAAC,IAAM,OAAY,UAAU,CAAC,EAAIhB,GACxFtB,IAIFA,GAAeqC,EAAK,IAAI,EAE1B,IAAIjmB,EAAIuS,EAAM,OACd,KAAOvS,KAAK,CACV,IAAImmB,EAAU5T,EAAMvS,CAAC,EACrB,GAAI,OAAOmmB,GAAY,SAAU,CAC/B,MAAMC,EAAYF,EAAkBC,CAAO,EACvCC,IAAcD,IAEXtC,GAAStR,CAAK,IACjBA,EAAMvS,CAAC,EAAIomB,GAEbD,EAAUC,EAEd,CACAH,EAAIE,CAAO,EAAI,EACjB,CACA,OAAOF,CACT,CAOA,SAASI,GAAW9T,EAAO,CACzB,QAAS+T,EAAQ,EAAGA,EAAQ/T,EAAM,OAAQ+T,IAChBd,GAAqBjT,EAAO+T,CAAK,IAEvD/T,EAAM+T,CAAK,EAAI,MAGnB,OAAO/T,CACT,CAOA,SAASgU,GAAMC,EAAQ,CACrB,MAAMC,EAAYvC,GAAO,IAAI,EAC7B,SAAW,CAACwC,EAAUpkB,CAAK,IAAKmO,GAAQ+V,CAAM,EACpBhB,GAAqBgB,EAAQE,CAAQ,IAEvD,MAAM,QAAQpkB,CAAK,EACrBmkB,EAAUC,CAAQ,EAAIL,GAAW/jB,CAAK,EAC7BA,GAAS,OAAOA,GAAU,UAAYA,EAAM,cAAgB,OACrEmkB,EAAUC,CAAQ,EAAIH,GAAMjkB,CAAK,EAEjCmkB,EAAUC,CAAQ,EAAIpkB,GAI5B,OAAOmkB,CACT,CAQA,SAASE,GAAaH,EAAQI,EAAM,CAClC,KAAOJ,IAAW,MAAM,CACtB,MAAMK,EAAO9C,GAAyByC,EAAQI,CAAI,EAClD,GAAIC,EAAM,CACR,GAAIA,EAAK,IACP,OAAOhC,EAAQgC,EAAK,GAAG,EAEzB,GAAI,OAAOA,EAAK,OAAU,WACxB,OAAOhC,EAAQgC,EAAK,KAAK,CAE7B,CACAL,EAAS1C,GAAe0C,CAAM,CAChC,CACA,SAASM,GAAgB,CACvB,OAAO,IACT,CACA,OAAOA,CACT,CAEA,MAAMC,GAAS/C,EAAO,CAAC,IAAK,OAAQ,UAAW,UAAW,OAAQ,UAAW,QAAS,QAAS,IAAK,MAAO,MAAO,MAAO,QAAS,aAAc,OAAQ,KAAM,SAAU,SAAU,UAAW,SAAU,OAAQ,OAAQ,MAAO,WAAY,UAAW,OAAQ,WAAY,KAAM,YAAa,MAAO,UAAW,MAAO,SAAU,MAAO,MAAO,KAAM,KAAM,UAAW,KAAM,WAAY,aAAc,SAAU,OAAQ,SAAU,OAAQ,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,OAAQ,SAAU,SAAU,KAAM,OAAQ,IAAK,MAAO,QAAS,MAAO,MAAO,QAAS,SAAU,KAAM,OAAQ,MAAO,OAAQ,UAAW,OAAQ,WAAY,QAAS,MAAO,OAAQ,KAAM,WAAY,SAAU,SAAU,IAAK,UAAW,MAAO,WAAY,IAAK,KAAM,KAAM,OAAQ,IAAK,OAAQ,SAAU,UAAW,SAAU,SAAU,OAAQ,QAAS,SAAU,SAAU,OAAQ,SAAU,SAAU,QAAS,MAAO,UAAW,MAAO,QAAS,QAAS,KAAM,WAAY,WAAY,QAAS,KAAM,QAAS,OAAQ,KAAM,QAAS,KAAM,IAAK,KAAM,MAAO,QAAS,KAAK,CAAC,EAC3/BgD,GAAQhD,EAAO,CAAC,MAAO,IAAK,WAAY,cAAe,eAAgB,eAAgB,gBAAiB,mBAAoB,SAAU,WAAY,OAAQ,OAAQ,UAAW,eAAgB,cAAe,SAAU,OAAQ,IAAK,QAAS,WAAY,QAAS,QAAS,YAAa,OAAQ,iBAAkB,SAAU,OAAQ,WAAY,QAAS,OAAQ,OAAQ,UAAW,UAAW,WAAY,iBAAkB,OAAQ,OAAQ,QAAS,SAAU,SAAU,OAAQ,WAAY,QAAS,OAAQ,QAAS,OAAQ,OAAO,CAAC,EACvgBiD,GAAajD,EAAO,CAAC,UAAW,gBAAiB,sBAAuB,cAAe,mBAAoB,oBAAqB,oBAAqB,iBAAkB,eAAgB,UAAW,UAAW,UAAW,UAAW,UAAW,iBAAkB,UAAW,UAAW,cAAe,eAAgB,WAAY,eAAgB,qBAAsB,cAAe,SAAU,cAAc,CAAC,EAK/YkD,GAAgBlD,EAAO,CAAC,UAAW,gBAAiB,SAAU,UAAW,YAAa,mBAAoB,iBAAkB,gBAAiB,gBAAiB,gBAAiB,QAAS,YAAa,OAAQ,eAAgB,YAAa,UAAW,gBAAiB,SAAU,MAAO,aAAc,UAAW,KAAK,CAAC,EACtTmD,GAAWnD,EAAO,CAAC,OAAQ,WAAY,SAAU,UAAW,QAAS,SAAU,KAAM,aAAc,gBAAiB,KAAM,KAAM,QAAS,UAAW,WAAY,QAAS,OAAQ,KAAM,SAAU,QAAS,SAAU,OAAQ,OAAQ,UAAW,SAAU,MAAO,QAAS,MAAO,SAAU,aAAc,aAAa,CAAC,EAGtToD,GAAmBpD,EAAO,CAAC,UAAW,cAAe,aAAc,WAAY,YAAa,UAAW,UAAW,SAAU,SAAU,QAAS,YAAa,aAAc,iBAAkB,cAAe,MAAM,CAAC,EAClNld,GAAOkd,EAAO,CAAC,OAAO,CAAC,EAEvBqD,GAAOrD,EAAO,CAAC,SAAU,SAAU,QAAS,MAAO,iBAAkB,eAAgB,uBAAwB,WAAY,aAAc,UAAW,SAAU,UAAW,cAAe,cAAe,UAAW,OAAQ,QAAS,QAAS,QAAS,OAAQ,UAAW,WAAY,eAAgB,SAAU,cAAe,WAAY,WAAY,UAAW,MAAO,WAAY,0BAA2B,wBAAyB,WAAY,YAAa,UAAW,eAAgB,cAAe,OAAQ,MAAO,UAAW,SAAU,SAAU,OAAQ,OAAQ,WAAY,KAAM,QAAS,YAAa,YAAa,QAAS,OAAQ,QAAS,OAAQ,OAAQ,UAAW,OAAQ,MAAO,MAAO,YAAa,QAAS,SAAU,MAAO,YAAa,WAAY,QAAS,OAAQ,QAAS,UAAW,aAAc,SAAU,OAAQ,UAAW,OAAQ,UAAW,cAAe,cAAe,UAAW,gBAAiB,sBAAuB,SAAU,UAAW,UAAW,aAAc,WAAY,MAAO,WAAY,MAAO,WAAY,OAAQ,OAAQ,UAAW,aAAc,QAAS,WAAY,QAAS,OAAQ,QAAS,OAAQ,OAAQ,UAAW,QAAS,MAAO,SAAU,OAAQ,QAAS,UAAW,WAAY,QAAS,YAAa,OAAQ,SAAU,SAAU,QAAS,QAAS,OAAQ,QAAS,MAAM,CAAC,EAC3wCsD,GAAMtD,EAAO,CAAC,gBAAiB,aAAc,WAAY,qBAAsB,YAAa,SAAU,gBAAiB,gBAAiB,UAAW,gBAAiB,iBAAkB,QAAS,OAAQ,KAAM,QAAS,OAAQ,gBAAiB,YAAa,YAAa,QAAS,sBAAuB,8BAA+B,gBAAiB,kBAAmB,KAAM,KAAM,IAAK,KAAM,KAAM,kBAAmB,YAAa,UAAW,UAAW,MAAO,WAAY,YAAa,MAAO,WAAY,OAAQ,eAAgB,YAAa,SAAU,cAAe,cAAe,gBAAiB,cAAe,YAAa,mBAAoB,eAAgB,aAAc,eAAgB,cAAe,KAAM,KAAM,KAAM,KAAM,aAAc,WAAY,gBAAiB,oBAAqB,SAAU,OAAQ,KAAM,kBAAmB,KAAM,MAAO,YAAa,IAAK,KAAM,KAAM,KAAM,KAAM,UAAW,YAAa,aAAc,WAAY,OAAQ,eAAgB,iBAAkB,eAAgB,mBAAoB,iBAAkB,QAAS,aAAc,aAAc,eAAgB,eAAgB,cAAe,cAAe,mBAAoB,YAAa,MAAO,OAAQ,YAAa,QAAS,SAAU,OAAQ,MAAO,OAAQ,aAAc,SAAU,WAAY,UAAW,QAAS,SAAU,cAAe,SAAU,WAAY,cAAe,OAAQ,aAAc,sBAAuB,mBAAoB,eAAgB,SAAU,gBAAiB,sBAAuB,iBAAkB,IAAK,KAAM,KAAM,SAAU,OAAQ,OAAQ,cAAe,YAAa,UAAW,SAAU,SAAU,QAAS,OAAQ,kBAAmB,QAAS,mBAAoB,mBAAoB,eAAgB,cAAe,eAAgB,cAAe,aAAc,eAAgB,mBAAoB,oBAAqB,iBAAkB,kBAAmB,oBAAqB,iBAAkB,SAAU,eAAgB,QAAS,eAAgB,iBAAkB,WAAY,cAAe,UAAW,UAAW,YAAa,mBAAoB,cAAe,kBAAmB,iBAAkB,aAAc,OAAQ,KAAM,KAAM,UAAW,SAAU,UAAW,aAAc,UAAW,aAAc,gBAAiB,gBAAiB,QAAS,eAAgB,OAAQ,eAAgB,mBAAoB,mBAAoB,IAAK,KAAM,KAAM,QAAS,IAAK,KAAM,KAAM,IAAK,YAAY,CAAC,EACt1EuD,GAASvD,EAAO,CAAC,SAAU,cAAe,QAAS,WAAY,QAAS,eAAgB,cAAe,aAAc,aAAc,QAAS,MAAO,UAAW,eAAgB,WAAY,QAAS,QAAS,SAAU,OAAQ,KAAM,UAAW,SAAU,gBAAiB,SAAU,SAAU,iBAAkB,YAAa,WAAY,cAAe,UAAW,UAAW,gBAAiB,WAAY,WAAY,OAAQ,WAAY,WAAY,aAAc,UAAW,SAAU,SAAU,cAAe,gBAAiB,uBAAwB,YAAa,YAAa,aAAc,WAAY,iBAAkB,iBAAkB,YAAa,UAAW,QAAS,OAAO,CAAC,EAC7pBwD,GAAMxD,EAAO,CAAC,aAAc,SAAU,cAAe,YAAa,aAAa,CAAC,EAGhFyD,GAAgBxD,GAAK,2BAA2B,EAChDyD,GAAWzD,GAAK,uBAAuB,EACvC0D,GAAc1D,GAAK,eAAe,EAClC2D,GAAY3D,GAAK,8BAA8B,EAC/C4D,GAAY5D,GAAK,gBAAgB,EACjC6D,GAAiB7D,GAAK,kGAC5B,EACM8D,GAAoB9D,GAAK,uBAAuB,EAChD+D,GAAkB/D,GAAK,6DAC7B,EACMgE,GAAehE,GAAK,SAAS,EAC7BiE,GAAiBjE,GAAK,0BAA0B,EAEtD,IAAIkE,GAA2B,OAAO,OAAO,CAC3C,UAAW,KACX,UAAWN,GACX,gBAAiBG,GACjB,eAAgBE,GAChB,UAAWN,GACX,aAAcK,GACd,SAAUP,GACV,eAAgBI,GAChB,kBAAmBC,GACnB,cAAeN,GACf,YAAaE,EACf,CAAC,EAID,MAAMS,GAAY,CAChB,QAAS,EAET,KAAM,EAMN,uBAAwB,EACxB,QAAS,EACT,SAAU,CAIZ,EACMC,GAAY,UAAqB,CACrC,OAAO,OAAO,OAAW,IAAc,KAAO,MAChD,EASMC,GAA4B,SAAmCC,EAAcC,EAAmB,CACpG,GAAI,OAAOD,GAAiB,UAAY,OAAOA,EAAa,cAAiB,WAC3E,OAAO,KAKT,IAAIE,EAAS,KACb,MAAMC,EAAY,wBACdF,GAAqBA,EAAkB,aAAaE,CAAS,IAC/DD,EAASD,EAAkB,aAAaE,CAAS,GAEnD,MAAMC,EAAa,aAAeF,EAAS,IAAMA,EAAS,IAC1D,GAAI,CACF,OAAOF,EAAa,aAAaI,EAAY,CAC3C,WAAWtB,EAAM,CACf,OAAOA,CACT,EACA,gBAAgBuB,EAAW,CACzB,OAAOA,CACT,CACN,CAAK,CACH,MAAY,CAIV,eAAQ,KAAK,uBAAyBD,EAAa,wBAAwB,EACpE,IACT,CACF,EACME,GAAkB,UAA2B,CACjD,MAAO,CACL,wBAAyB,CAAA,EACzB,sBAAuB,CAAA,EACvB,uBAAwB,CAAA,EACxB,yBAA0B,CAAA,EAC1B,uBAAwB,CAAA,EACxB,wBAAyB,CAAA,EACzB,sBAAuB,CAAA,EACvB,oBAAqB,CAAA,EACrB,uBAAwB,CAAA,CAC5B,CACA,EACA,SAASC,IAAkB,CACzB,IAAIC,EAAS,UAAU,OAAS,GAAK,UAAU,CAAC,IAAM,OAAY,UAAU,CAAC,EAAIV,GAAS,EAC1F,MAAMW,EAAYrK,GAAQmK,GAAgBnK,CAAI,EAG9C,GAFAqK,EAAU,QAAU,QACpBA,EAAU,QAAU,CAAA,EAChB,CAACD,GAAU,CAACA,EAAO,UAAYA,EAAO,SAAS,WAAaX,GAAU,UAAY,CAACW,EAAO,QAG5F,OAAAC,EAAU,YAAc,GACjBA,EAET,GAAI,CACF,SAAAC,CACJ,EAAMF,EACJ,MAAMG,EAAmBD,EACnBE,EAAgBD,EAAiB,cACjC,CACJ,iBAAAE,EACA,oBAAAC,EACA,KAAAC,EACA,QAAAC,EACA,WAAAC,EACA,aAAAC,EAAeV,EAAO,cAAgBA,EAAO,gBAC7C,gBAAAW,EACA,UAAAC,EACA,aAAApB,CACJ,EAAMQ,EACEa,EAAmBL,EAAQ,UAC3BM,EAAYlD,GAAaiD,EAAkB,WAAW,EACtDE,EAASnD,GAAaiD,EAAkB,QAAQ,EAChDG,EAAiBpD,GAAaiD,EAAkB,aAAa,EAC7DI,EAAgBrD,GAAaiD,EAAkB,YAAY,EAC3DK,EAAgBtD,GAAaiD,EAAkB,YAAY,EAOjE,GAAI,OAAOP,GAAwB,WAAY,CAC7C,MAAMa,EAAWjB,EAAS,cAAc,UAAU,EAC9CiB,EAAS,SAAWA,EAAS,QAAQ,gBACvCjB,EAAWiB,EAAS,QAAQ,cAEhC,CACA,IAAIC,EACAC,EAAY,GAChB,KAAM,CACJ,eAAAC,EACA,mBAAAC,GACA,uBAAAC,GACA,qBAAAC,EACJ,EAAMvB,EACE,CACJ,WAAAwB,EACJ,EAAMvB,EACJ,IAAIwB,EAAQ7B,GAAe,EAI3BG,EAAU,YAAc,OAAOvY,IAAY,YAAc,OAAOwZ,GAAkB,YAAcI,GAAkBA,EAAe,qBAAuB,OACxJ,KAAM,CACJ,cAAA5C,GACA,SAAAC,GACA,YAAAC,GACA,UAAAC,GACA,UAAAC,GACA,kBAAAE,GACA,gBAAAC,GACA,eAAAE,EACJ,EAAMC,GACJ,GAAI,CACF,eAAgBwC,EACpB,EAAMxC,GAMAyC,EAAe,KACnB,MAAMC,GAAuB7E,EAAS,CAAA,EAAI,CAAC,GAAGe,GAAQ,GAAGC,GAAO,GAAGC,GAAY,GAAGE,GAAU,GAAGrgB,EAAI,CAAC,EAEpG,IAAIgkB,EAAe,KACnB,MAAMC,GAAuB/E,EAAS,CAAA,EAAI,CAAC,GAAGqB,GAAM,GAAGC,GAAK,GAAGC,GAAQ,GAAGC,EAAG,CAAC,EAO9E,IAAIwD,EAA0B,OAAO,KAAK9G,GAAO,KAAM,CACrD,aAAc,CACZ,SAAU,GACV,aAAc,GACd,WAAY,GACZ,MAAO,IACb,EACI,mBAAoB,CAClB,SAAU,GACV,aAAc,GACd,WAAY,GACZ,MAAO,IACb,EACI,+BAAgC,CAC9B,SAAU,GACV,aAAc,GACd,WAAY,GACZ,MAAO,EACb,CACA,CAAG,CAAC,EAEE+G,GAAc,KAEdC,GAAc,KAElB,MAAMC,GAAyB,OAAO,KAAKjH,GAAO,KAAM,CACtD,SAAU,CACR,SAAU,GACV,aAAc,GACd,WAAY,GACZ,MAAO,IACb,EACI,eAAgB,CACd,SAAU,GACV,aAAc,GACd,WAAY,GACZ,MAAO,IACb,CACA,CAAG,CAAC,EAEF,IAAIkH,GAAkB,GAElBC,GAAkB,GAElBC,GAA0B,GAG1BC,GAA2B,GAI3BC,GAAqB,GAIrBC,GAAe,GAEfC,GAAiB,GAEjBC,GAAa,GAGbC,GAAa,GAKbC,GAAa,GAGbC,GAAsB,GAGtBC,GAAsB,GAItBC,GAAe,GAcfC,GAAuB,GAC3B,MAAMC,GAA8B,gBAEpC,IAAIC,GAAe,GAGfC,GAAW,GAEXC,GAAe,CAAA,EAEfC,GAAkB,KACtB,MAAMC,GAA0BvG,EAAS,CAAA,EAAI,CAAC,iBAAkB,QAAS,WAAY,OAAQ,gBAAiB,OAAQ,SAAU,OAAQ,KAAM,KAAM,KAAM,KAAM,QAAS,UAAW,WAAY,WAAY,YAAa,SAAU,QAAS,MAAO,WAAY,QAAS,QAAS,QAAS,KAAK,CAAC,EAEhS,IAAIwG,GAAgB,KACpB,MAAMC,GAAwBzG,EAAS,CAAA,EAAI,CAAC,QAAS,QAAS,MAAO,SAAU,QAAS,OAAO,CAAC,EAEhG,IAAI0G,GAAsB,KAC1B,MAAMC,GAA8B3G,EAAS,GAAI,CAAC,MAAO,QAAS,MAAO,KAAM,QAAS,OAAQ,UAAW,cAAe,OAAQ,UAAW,QAAS,QAAS,QAAS,OAAO,CAAC,EAC1K4G,GAAmB,qCACnBC,GAAgB,6BAChBC,GAAiB,+BAEvB,IAAIC,GAAYD,GACZE,GAAiB,GAEjBC,GAAqB,KACzB,MAAMC,GAA6BlH,EAAS,GAAI,CAAC4G,GAAkBC,GAAeC,EAAc,EAAG3H,EAAc,EACjH,IAAIgI,GAAiCnH,EAAS,CAAA,EAAI,CAAC,KAAM,KAAM,KAAM,KAAM,OAAO,CAAC,EAC/EoH,GAA0BpH,EAAS,GAAI,CAAC,gBAAgB,CAAC,EAK7D,MAAMqH,GAA+BrH,EAAS,CAAA,EAAI,CAAC,QAAS,QAAS,OAAQ,IAAK,QAAQ,CAAC,EAE3F,IAAIsH,GAAoB,KACxB,MAAMC,GAA+B,CAAC,wBAAyB,WAAW,EACpEC,GAA4B,YAClC,IAAItH,EAAoB,KAEpBuH,GAAS,KAGb,MAAMC,GAAczE,EAAS,cAAc,MAAM,EAC3C0E,GAAoB,SAA2BC,EAAW,CAC9D,OAAOA,aAAqB,QAAUA,aAAqB,QAC7D,EAOMC,GAAe,UAAwB,CAC3C,IAAIC,EAAM,UAAU,OAAS,GAAK,UAAU,CAAC,IAAM,OAAY,UAAU,CAAC,EAAI,CAAA,EAC9E,GAAI,EAAAL,IAAUA,KAAWK,GAoIzB,KAhII,CAACA,GAAO,OAAOA,GAAQ,YACzBA,EAAM,CAAA,GAGRA,EAAMvH,GAAMuH,CAAG,EACfR,GAEAC,GAA6B,QAAQO,EAAI,iBAAiB,IAAM,GAAKN,GAA4BM,EAAI,kBAErG5H,EAAoBoH,KAAsB,wBAA0BnI,GAAiBD,GAErF0F,EAAepF,GAAqBsI,EAAK,cAAc,EAAI9H,EAAS,CAAA,EAAI8H,EAAI,aAAc5H,CAAiB,EAAI2E,GAC/GC,EAAetF,GAAqBsI,EAAK,cAAc,EAAI9H,EAAS,CAAA,EAAI8H,EAAI,aAAc5H,CAAiB,EAAI6E,GAC/GkC,GAAqBzH,GAAqBsI,EAAK,oBAAoB,EAAI9H,EAAS,CAAA,EAAI8H,EAAI,mBAAoB3I,EAAc,EAAI+H,GAC9HR,GAAsBlH,GAAqBsI,EAAK,mBAAmB,EAAI9H,EAASO,GAAMoG,EAA2B,EAAGmB,EAAI,kBAAmB5H,CAAiB,EAAIyG,GAChKH,GAAgBhH,GAAqBsI,EAAK,mBAAmB,EAAI9H,EAASO,GAAMkG,EAAqB,EAAGqB,EAAI,kBAAmB5H,CAAiB,EAAIuG,GACpJH,GAAkB9G,GAAqBsI,EAAK,iBAAiB,EAAI9H,EAAS,CAAA,EAAI8H,EAAI,gBAAiB5H,CAAiB,EAAIqG,GACxHtB,GAAczF,GAAqBsI,EAAK,aAAa,EAAI9H,EAAS,GAAI8H,EAAI,YAAa5H,CAAiB,EAAIK,GAAM,CAAA,CAAE,EACpH2E,GAAc1F,GAAqBsI,EAAK,aAAa,EAAI9H,EAAS,GAAI8H,EAAI,YAAa5H,CAAiB,EAAIK,GAAM,CAAA,CAAE,EACpH8F,GAAe7G,GAAqBsI,EAAK,cAAc,EAAIA,EAAI,aAAe,GAC9E1C,GAAkB0C,EAAI,kBAAoB,GAC1CzC,GAAkByC,EAAI,kBAAoB,GAC1CxC,GAA0BwC,EAAI,yBAA2B,GACzDvC,GAA2BuC,EAAI,2BAA6B,GAC5DtC,GAAqBsC,EAAI,oBAAsB,GAC/CrC,GAAeqC,EAAI,eAAiB,GACpCpC,GAAiBoC,EAAI,gBAAkB,GACvCjC,GAAaiC,EAAI,YAAc,GAC/BhC,GAAsBgC,EAAI,qBAAuB,GACjD/B,GAAsB+B,EAAI,qBAAuB,GACjDlC,GAAakC,EAAI,YAAc,GAC/B9B,GAAe8B,EAAI,eAAiB,GACpC7B,GAAuB6B,EAAI,sBAAwB,GACnD3B,GAAe2B,EAAI,eAAiB,GACpC1B,GAAW0B,EAAI,UAAY,GAC3BnD,GAAmBmD,EAAI,oBAAsBhG,GAC7CiF,GAAYe,EAAI,WAAahB,GAC7BK,GAAiCW,EAAI,gCAAkCX,GACvEC,GAA0BU,EAAI,yBAA2BV,GACzDpC,EAA0B8C,EAAI,yBAA2B,CAAA,EACrDA,EAAI,yBAA2BH,GAAkBG,EAAI,wBAAwB,YAAY,IAC3F9C,EAAwB,aAAe8C,EAAI,wBAAwB,cAEjEA,EAAI,yBAA2BH,GAAkBG,EAAI,wBAAwB,kBAAkB,IACjG9C,EAAwB,mBAAqB8C,EAAI,wBAAwB,oBAEvEA,EAAI,yBAA2B,OAAOA,EAAI,wBAAwB,gCAAmC,YACvG9C,EAAwB,+BAAiC8C,EAAI,wBAAwB,gCAEnFtC,KACFH,GAAkB,IAEhBS,KACFD,GAAa,IAGXQ,KACFzB,EAAe5E,EAAS,CAAA,EAAIlf,EAAI,EAChCgkB,EAAe,CAAA,EACXuB,GAAa,OAAS,KACxBrG,EAAS4E,EAAc7D,EAAM,EAC7Bf,EAAS8E,EAAczD,EAAI,GAEzBgF,GAAa,MAAQ,KACvBrG,EAAS4E,EAAc5D,EAAK,EAC5BhB,EAAS8E,EAAcxD,EAAG,EAC1BtB,EAAS8E,EAActD,EAAG,GAExB6E,GAAa,aAAe,KAC9BrG,EAAS4E,EAAc3D,EAAU,EACjCjB,EAAS8E,EAAcxD,EAAG,EAC1BtB,EAAS8E,EAActD,EAAG,GAExB6E,GAAa,SAAW,KAC1BrG,EAAS4E,EAAczD,EAAQ,EAC/BnB,EAAS8E,EAAcvD,EAAM,EAC7BvB,EAAS8E,EAActD,EAAG,IAI1BsG,EAAI,WACF,OAAOA,EAAI,UAAa,WAC1B3C,GAAuB,SAAW2C,EAAI,UAElClD,IAAiBC,KACnBD,EAAerE,GAAMqE,CAAY,GAEnC5E,EAAS4E,EAAckD,EAAI,SAAU5H,CAAiB,IAGtD4H,EAAI,WACF,OAAOA,EAAI,UAAa,WAC1B3C,GAAuB,eAAiB2C,EAAI,UAExChD,IAAiBC,KACnBD,EAAevE,GAAMuE,CAAY,GAEnC9E,EAAS8E,EAAcgD,EAAI,SAAU5H,CAAiB,IAGtD4H,EAAI,mBACN9H,EAAS0G,GAAqBoB,EAAI,kBAAmB5H,CAAiB,EAEpE4H,EAAI,kBACFxB,KAAoBC,KACtBD,GAAkB/F,GAAM+F,EAAe,GAEzCtG,EAASsG,GAAiBwB,EAAI,gBAAiB5H,CAAiB,GAE9D4H,EAAI,sBACFxB,KAAoBC,KACtBD,GAAkB/F,GAAM+F,EAAe,GAEzCtG,EAASsG,GAAiBwB,EAAI,oBAAqB5H,CAAiB,GAGlEiG,KACFvB,EAAa,OAAO,EAAI,IAGtBc,IACF1F,EAAS4E,EAAc,CAAC,OAAQ,OAAQ,MAAM,CAAC,EAG7CA,EAAa,QACf5E,EAAS4E,EAAc,CAAC,OAAO,CAAC,EAChC,OAAOK,GAAY,OAEjB6C,EAAI,qBAAsB,CAC5B,GAAI,OAAOA,EAAI,qBAAqB,YAAe,WACjD,MAAMpI,GAAgB,6EAA6E,EAErG,GAAI,OAAOoI,EAAI,qBAAqB,iBAAoB,WACtD,MAAMpI,GAAgB,kFAAkF,EAG1GyE,EAAqB2D,EAAI,qBAEzB1D,EAAYD,EAAmB,WAAW,EAAE,CAC9C,MAEMA,IAAuB,SACzBA,EAAqB7B,GAA0BC,EAAcY,CAAa,GAGxEgB,IAAuB,MAAQ,OAAOC,GAAc,WACtDA,EAAYD,EAAmB,WAAW,EAAE,GAK5CnG,GACFA,EAAO8J,CAAG,EAEZL,GAASK,EACX,EAIMC,GAAe/H,EAAS,GAAI,CAAC,GAAGgB,GAAO,GAAGC,GAAY,GAAGC,EAAa,CAAC,EACvE8G,GAAkBhI,EAAS,CAAA,EAAI,CAAC,GAAGmB,GAAU,GAAGC,EAAgB,CAAC,EAOjE6G,GAAuB,SAA8B9H,EAAS,CAClE,IAAI+H,EAASjE,EAAc9D,CAAO,GAG9B,CAAC+H,GAAU,CAACA,EAAO,WACrBA,EAAS,CACP,aAAcnB,GACd,QAAS,UACjB,GAEI,MAAMoB,EAAUjJ,GAAkBiB,EAAQ,OAAO,EAC3CiI,EAAgBlJ,GAAkBgJ,EAAO,OAAO,EACtD,OAAKjB,GAAmB9G,EAAQ,YAAY,EAGxCA,EAAQ,eAAiB0G,GAIvBqB,EAAO,eAAiBpB,GACnBqB,IAAY,MAKjBD,EAAO,eAAiBtB,GACnBuB,IAAY,QAAUC,IAAkB,kBAAoBjB,GAA+BiB,CAAa,GAI1G,EAAQL,GAAaI,CAAO,EAEjChI,EAAQ,eAAiByG,GAIvBsB,EAAO,eAAiBpB,GACnBqB,IAAY,OAIjBD,EAAO,eAAiBrB,GACnBsB,IAAY,QAAUf,GAAwBgB,CAAa,EAI7D,EAAQJ,GAAgBG,CAAO,EAEpChI,EAAQ,eAAiB2G,GAIvBoB,EAAO,eAAiBrB,IAAiB,CAACO,GAAwBgB,CAAa,GAG/EF,EAAO,eAAiBtB,IAAoB,CAACO,GAA+BiB,CAAa,EACpF,GAIF,CAACJ,GAAgBG,CAAO,IAAMd,GAA6Bc,CAAO,GAAK,CAACJ,GAAaI,CAAO,GAGjG,GAAAb,KAAsB,yBAA2BL,GAAmB9G,EAAQ,YAAY,GAlDnF,EA0DX,EAMMkI,GAAe,SAAsBC,EAAM,CAC/CtJ,GAAUgE,EAAU,QAAS,CAC3B,QAASsF,CACf,CAAK,EACD,GAAI,CAEFrE,EAAcqE,CAAI,EAAE,YAAYA,CAAI,CACtC,MAAY,CACVxE,EAAOwE,CAAI,CACb,CACF,EAOMC,GAAmB,SAA0B5rB,EAAMwjB,EAAS,CAChE,GAAI,CACFnB,GAAUgE,EAAU,QAAS,CAC3B,UAAW7C,EAAQ,iBAAiBxjB,CAAI,EACxC,KAAMwjB,CACd,CAAO,CACH,MAAY,CACVnB,GAAUgE,EAAU,QAAS,CAC3B,UAAW,KACX,KAAM7C,CACd,CAAO,CACH,CAGA,GAFAA,EAAQ,gBAAgBxjB,CAAI,EAExBA,IAAS,KACX,GAAIkpB,IAAcC,GAChB,GAAI,CACFuC,GAAalI,CAAO,CACtB,MAAY,CAAC,KAEb,IAAI,CACFA,EAAQ,aAAaxjB,EAAM,EAAE,CAC/B,MAAY,CAAC,CAGnB,EAOM6rB,GAAgB,SAAuBC,EAAO,CAElD,IAAIC,EAAM,KACNC,EAAoB,KACxB,GAAI/C,GACF6C,EAAQ,oBAAsBA,MACzB,CAEL,MAAMG,EAAUxJ,GAAYqJ,EAAO,aAAa,EAChDE,EAAoBC,GAAWA,EAAQ,CAAC,CAC1C,CACItB,KAAsB,yBAA2BP,KAAcD,KAEjE2B,EAAQ,iEAAmEA,EAAQ,kBAErF,MAAMI,EAAe1E,EAAqBA,EAAmB,WAAWsE,CAAK,EAAIA,EAKjF,GAAI1B,KAAcD,GAChB,GAAI,CACF4B,EAAM,IAAI/E,EAAS,EAAG,gBAAgBkF,EAAcvB,EAAiB,CACvE,MAAY,CAAC,CAGf,GAAI,CAACoB,GAAO,CAACA,EAAI,gBAAiB,CAChCA,EAAMrE,EAAe,eAAe0C,GAAW,WAAY,IAAI,EAC/D,GAAI,CACF2B,EAAI,gBAAgB,UAAY1B,GAAiB5C,EAAYyE,CAC/D,MAAY,CAEZ,CACF,CACA,MAAMC,EAAOJ,EAAI,MAAQA,EAAI,gBAK7B,OAJID,GAASE,GACXG,EAAK,aAAa7F,EAAS,eAAe0F,CAAiB,EAAGG,EAAK,WAAW,CAAC,GAAK,IAAI,EAGtF/B,KAAcD,GACTtC,GAAqB,KAAKkE,EAAKhD,GAAiB,OAAS,MAAM,EAAE,CAAC,EAEpEA,GAAiBgD,EAAI,gBAAkBI,CAChD,EAOMC,GAAsB,SAA6BpQ,EAAM,CAC7D,OAAO2L,GAAmB,KAAK3L,EAAK,eAAiBA,EAAMA,EAE3D6K,EAAW,aAAeA,EAAW,aAAeA,EAAW,UAAYA,EAAW,4BAA8BA,EAAW,mBAAoB,IAAI,CACzJ,EAOMwF,GAAe,SAAsB7I,EAAS,CAClD,OAAOA,aAAmBuD,IAAoB,OAAOvD,EAAQ,UAAa,UAAY,OAAOA,EAAQ,aAAgB,UAAY,OAAOA,EAAQ,aAAgB,YAAc,EAAEA,EAAQ,sBAAsBsD,IAAiB,OAAOtD,EAAQ,iBAAoB,YAAc,OAAOA,EAAQ,cAAiB,YAAc,OAAOA,EAAQ,cAAiB,UAAY,OAAOA,EAAQ,cAAiB,YAAc,OAAOA,EAAQ,eAAkB,WAC3b,EAOM8I,GAAU,SAAiB3sB,EAAO,CACtC,OAAO,OAAOgnB,GAAS,YAAchnB,aAAiBgnB,CACxD,EACA,SAAS4F,GAAcxE,EAAOyE,EAAarkB,EAAM,CAC/C8Z,GAAa8F,EAAO0E,GAAQ,CAC1BA,EAAK,KAAKpG,EAAWmG,EAAarkB,EAAM2iB,EAAM,CAChD,CAAC,CACH,CAUA,MAAM4B,GAAoB,SAA2BF,EAAa,CAChE,IAAIjoB,EAAU,KAId,GAFAgoB,GAAcxE,EAAM,uBAAwByE,EAAa,IAAI,EAEzDH,GAAaG,CAAW,EAC1B,OAAAd,GAAac,CAAW,EACjB,GAGT,MAAMhB,EAAUjI,EAAkBiJ,EAAY,QAAQ,EAiBtD,GAfAD,GAAcxE,EAAM,oBAAqByE,EAAa,CACpD,QAAAhB,EACA,YAAavD,CACnB,CAAK,EAEGa,IAAgB0D,EAAY,cAAa,GAAM,CAACF,GAAQE,EAAY,iBAAiB,GAAK1J,EAAW,WAAY0J,EAAY,SAAS,GAAK1J,EAAW,WAAY0J,EAAY,WAAW,GAKzLA,EAAY,WAAa/G,GAAU,wBAKnCqD,IAAgB0D,EAAY,WAAa/G,GAAU,SAAW3C,EAAW,UAAW0J,EAAY,IAAI,EACtG,OAAAd,GAAac,CAAW,EACjB,GAGT,GAAI,EAAEhE,GAAuB,oBAAoB,UAAYA,GAAuB,SAASgD,CAAO,KAAO,CAACvD,EAAauD,CAAO,GAAKlD,GAAYkD,CAAO,GAAI,CAE1J,GAAI,CAAClD,GAAYkD,CAAO,GAAKmB,GAAsBnB,CAAO,IACpDnD,EAAwB,wBAAwB,QAAUvF,EAAWuF,EAAwB,aAAcmD,CAAO,GAGlHnD,EAAwB,wBAAwB,UAAYA,EAAwB,aAAamD,CAAO,GAC1G,MAAO,GAIX,GAAIhC,IAAgB,CAACG,GAAgB6B,CAAO,EAAG,CAC7C,MAAMoB,EAAatF,EAAckF,CAAW,GAAKA,EAAY,WACvDK,EAAaxF,EAAcmF,CAAW,GAAKA,EAAY,WAC7D,GAAIK,GAAcD,EAAY,CAC5B,MAAME,EAAaD,EAAW,OAC9B,QAAS7vB,EAAI8vB,EAAa,EAAG9vB,GAAK,EAAG,EAAEA,EAAG,CACxC,MAAM+vB,GAAa7F,EAAU2F,EAAW7vB,CAAC,EAAG,EAAI,EAChD+vB,GAAW,gBAAkBP,EAAY,gBAAkB,GAAK,EAChEI,EAAW,aAAaG,GAAY3F,EAAeoF,CAAW,CAAC,CACjE,CACF,CACF,CACA,OAAAd,GAAac,CAAW,EACjB,EACT,CAOA,OALIA,aAAuB5F,GAAW,CAAC0E,GAAqBkB,CAAW,IAKlEhB,IAAY,YAAcA,IAAY,WAAaA,IAAY,aAAe1I,EAAW,8BAA+B0J,EAAY,SAAS,GAChJd,GAAac,CAAW,EACjB,KAGL3D,IAAsB2D,EAAY,WAAa/G,GAAU,OAE3DlhB,EAAUioB,EAAY,YACtBvK,GAAa,CAAC6C,GAAeC,GAAUC,EAAW,EAAGxZ,GAAQ,CAC3DjH,EAAUme,GAAcne,EAASiH,EAAM,GAAG,CAC5C,CAAC,EACGghB,EAAY,cAAgBjoB,IAC9B8d,GAAUgE,EAAU,QAAS,CAC3B,QAASmG,EAAY,UAAS,CACxC,CAAS,EACDA,EAAY,YAAcjoB,IAI9BgoB,GAAcxE,EAAM,sBAAuByE,EAAa,IAAI,EACrD,GACT,EAUMQ,GAAoB,SAA2BC,EAAOC,EAAQvtB,EAAO,CAEzE,GAAI0pB,KAAiB6D,IAAW,MAAQA,IAAW,UAAYvtB,KAAS2mB,GAAY3mB,KAASorB,IAC3F,MAAO,GAMT,GAAI,EAAArC,IAAmB,CAACH,GAAY2E,CAAM,GAAKpK,EAAWmC,GAAWiI,CAAM,IAAU,GAAI,EAAAzE,IAAmB3F,EAAWoC,GAAWgI,CAAM,IAAU,GAAI,EAAA1E,GAAuB,0BAA0B,UAAYA,GAAuB,eAAe0E,EAAQD,CAAK,IAAU,GAAI,CAAC9E,EAAa+E,CAAM,GAAK3E,GAAY2E,CAAM,GAC7T,GAIA,EAAAP,GAAsBM,CAAK,IAAM5E,EAAwB,wBAAwB,QAAUvF,EAAWuF,EAAwB,aAAc4E,CAAK,GAAK5E,EAAwB,wBAAwB,UAAYA,EAAwB,aAAa4E,CAAK,KAAO5E,EAAwB,8BAA8B,QAAUvF,EAAWuF,EAAwB,mBAAoB6E,CAAM,GAAK7E,EAAwB,8BAA8B,UAAYA,EAAwB,mBAAmB6E,EAAQD,CAAK,IAG/fC,IAAW,MAAQ7E,EAAwB,iCAAmCA,EAAwB,wBAAwB,QAAUvF,EAAWuF,EAAwB,aAAc1oB,CAAK,GAAK0oB,EAAwB,wBAAwB,UAAYA,EAAwB,aAAa1oB,CAAK,IACvS,MAAO,WAGA,CAAAoqB,GAAoBmD,CAAM,GAAU,GAAI,CAAApK,EAAWkF,GAAkBtF,GAAc/iB,EAAO0lB,GAAiB,EAAE,CAAC,GAAU,GAAK,GAAA6H,IAAW,OAASA,IAAW,cAAgBA,IAAW,SAAWD,IAAU,UAAYtK,GAAchjB,EAAO,OAAO,IAAM,GAAKkqB,GAAcoD,CAAK,IAAU,GAAI,EAAAtE,IAA2B,CAAC7F,EAAWsC,GAAmB1C,GAAc/iB,EAAO0lB,GAAiB,EAAE,CAAC,IAAU,GAAI1lB,EAC1Z,MAAO,SAET,MAAO,EACT,EASMgtB,GAAwB,SAA+BnB,EAAS,CACpE,OAAOA,IAAY,kBAAoB/I,GAAY+I,EAASjG,EAAc,CAC5E,EAWM4H,GAAsB,SAA6BX,EAAa,CAEpED,GAAcxE,EAAM,yBAA0ByE,EAAa,IAAI,EAC/D,KAAM,CACJ,WAAAY,CACN,EAAQZ,EAEJ,GAAI,CAACY,GAAcf,GAAaG,CAAW,EACzC,OAEF,MAAMa,EAAY,CAChB,SAAU,GACV,UAAW,GACX,SAAU,GACV,kBAAmBlF,EACnB,cAAe,MACrB,EACI,IAAI9qB,EAAI+vB,EAAW,OAEnB,KAAO/vB,KAAK,CACV,MAAMiwB,EAAOF,EAAW/vB,CAAC,EACnB,CACJ,KAAA2C,EACA,aAAAutB,EACA,MAAOC,EACf,EAAUF,EACEJ,GAAS3J,EAAkBvjB,CAAI,EAC/BytB,GAAYD,GAClB,IAAI7tB,EAAQK,IAAS,QAAUytB,GAAY7K,GAAW6K,EAAS,EAkB/D,GAhBAJ,EAAU,SAAWH,GACrBG,EAAU,UAAY1tB,EACtB0tB,EAAU,SAAW,GACrBA,EAAU,cAAgB,OAC1Bd,GAAcxE,EAAM,sBAAuByE,EAAaa,CAAS,EACjE1tB,EAAQ0tB,EAAU,UAId/D,KAAyB4D,KAAW,MAAQA,KAAW,UAEzDtB,GAAiB5rB,EAAMwsB,CAAW,EAElC7sB,EAAQ4pB,GAA8B5pB,GAGpCmpB,IAAgBhG,EAAW,yCAA0CnjB,CAAK,EAAG,CAC/EisB,GAAiB5rB,EAAMwsB,CAAW,EAClC,QACF,CAEA,GAAIU,KAAW,iBAAmBzK,GAAY9iB,EAAO,MAAM,EAAG,CAC5DisB,GAAiB5rB,EAAMwsB,CAAW,EAClC,QACF,CAEA,GAAIa,EAAU,cACZ,SAGF,GAAI,CAACA,EAAU,SAAU,CACvBzB,GAAiB5rB,EAAMwsB,CAAW,EAClC,QACF,CAEA,GAAI,CAAC5D,IAA4B9F,EAAW,OAAQnjB,CAAK,EAAG,CAC1DisB,GAAiB5rB,EAAMwsB,CAAW,EAClC,QACF,CAEI3D,IACF5G,GAAa,CAAC6C,GAAeC,GAAUC,EAAW,EAAGxZ,IAAQ,CAC3D7L,EAAQ+iB,GAAc/iB,EAAO6L,GAAM,GAAG,CACxC,CAAC,EAGH,MAAMyhB,GAAQ1J,EAAkBiJ,EAAY,QAAQ,EACpD,GAAI,CAACQ,GAAkBC,GAAOC,GAAQvtB,CAAK,EAAG,CAC5CisB,GAAiB5rB,EAAMwsB,CAAW,EAClC,QACF,CAEA,GAAIhF,GAAsB,OAAO5B,GAAiB,UAAY,OAAOA,EAAa,kBAAqB,YACjG,CAAA2H,EACF,OAAQ3H,EAAa,iBAAiBqH,GAAOC,EAAM,EAAC,CAClD,IAAK,cACH,CACEvtB,EAAQ6nB,EAAmB,WAAW7nB,CAAK,EAC3C,KACF,CACF,IAAK,mBACH,CACEA,EAAQ6nB,EAAmB,gBAAgB7nB,CAAK,EAChD,KACF,CACd,CAIM,GAAIA,IAAU8tB,GACZ,GAAI,CACEF,EACFf,EAAY,eAAee,EAAcvtB,EAAML,CAAK,EAGpD6sB,EAAY,aAAaxsB,EAAML,CAAK,EAElC0sB,GAAaG,CAAW,EAC1Bd,GAAac,CAAW,EAExBpK,GAASiE,EAAU,OAAO,CAE9B,MAAY,CACVuF,GAAiB5rB,EAAMwsB,CAAW,CACpC,CAEJ,CAEAD,GAAcxE,EAAM,wBAAyByE,EAAa,IAAI,CAChE,EAMMkB,GAAqB,SAASA,EAAmBC,EAAU,CAC/D,IAAIC,EAAa,KACjB,MAAMC,EAAiBzB,GAAoBuB,CAAQ,EAGnD,IADApB,GAAcxE,EAAM,wBAAyB4F,EAAU,IAAI,EACpDC,EAAaC,EAAe,YAEjCtB,GAAcxE,EAAM,uBAAwB6F,EAAY,IAAI,EAE5DlB,GAAkBkB,CAAU,EAE5BT,GAAoBS,CAAU,EAE1BA,EAAW,mBAAmBnH,GAChCiH,EAAmBE,EAAW,OAAO,EAIzCrB,GAAcxE,EAAM,uBAAwB4F,EAAU,IAAI,CAC5D,EAEA,OAAAtH,EAAU,SAAW,SAAUyF,EAAO,CACpC,IAAIX,EAAM,UAAU,OAAS,GAAK,UAAU,CAAC,IAAM,OAAY,UAAU,CAAC,EAAI,CAAA,EAC1EgB,EAAO,KACP2B,EAAe,KACftB,EAAc,KACduB,EAAa,KASjB,GALA1D,GAAiB,CAACyB,EACdzB,KACFyB,EAAQ,SAGN,OAAOA,GAAU,UAAY,CAACQ,GAAQR,CAAK,EAC7C,GAAI,OAAOA,EAAM,UAAa,YAE5B,GADAA,EAAQA,EAAM,SAAQ,EAClB,OAAOA,GAAU,SACnB,MAAM/I,GAAgB,iCAAiC,MAGzD,OAAMA,GAAgB,4BAA4B,EAItD,GAAI,CAACsD,EAAU,YACb,OAAOyF,EAYT,GATK9C,IACHkC,GAAaC,CAAG,EAGlB9E,EAAU,QAAU,CAAA,EAEhB,OAAOyF,GAAU,WACnBrC,GAAW,IAETA,IAEF,GAAIqC,EAAM,SAAU,CAClB,MAAMN,GAAUjI,EAAkBuI,EAAM,QAAQ,EAChD,GAAI,CAAC7D,EAAauD,EAAO,GAAKlD,GAAYkD,EAAO,EAC/C,MAAMzI,GAAgB,yDAAyD,CAEnF,UACS+I,aAAiBnF,EAG1BwF,EAAON,GAAc,SAAS,EAC9BiC,EAAe3B,EAAK,cAAc,WAAWL,EAAO,EAAI,EACpDgC,EAAa,WAAarI,GAAU,SAAWqI,EAAa,WAAa,QAGlEA,EAAa,WAAa,OADnC3B,EAAO2B,EAKP3B,EAAK,YAAY2B,CAAY,MAE1B,CAEL,GAAI,CAAC5E,IAAc,CAACL,IAAsB,CAACE,IAE3C+C,EAAM,QAAQ,GAAG,IAAM,GACrB,OAAOtE,GAAsB4B,GAAsB5B,EAAmB,WAAWsE,CAAK,EAAIA,EAK5F,GAFAK,EAAON,GAAcC,CAAK,EAEtB,CAACK,EACH,OAAOjD,GAAa,KAAOE,GAAsB3B,EAAY,EAEjE,CAEI0E,GAAQlD,IACVyC,GAAaS,EAAK,UAAU,EAG9B,MAAM6B,EAAe5B,GAAoB3C,GAAWqC,EAAQK,CAAI,EAEhE,KAAOK,EAAcwB,EAAa,YAEhCtB,GAAkBF,CAAW,EAE7BW,GAAoBX,CAAW,EAE3BA,EAAY,mBAAmB/F,GACjCiH,GAAmBlB,EAAY,OAAO,EAI1C,GAAI/C,GACF,OAAOqC,EAGT,GAAI5C,GAAY,CACd,GAAIC,GAEF,IADA4E,EAAanG,GAAuB,KAAKuE,EAAK,aAAa,EACpDA,EAAK,YAEV4B,EAAW,YAAY5B,EAAK,UAAU,OAGxC4B,EAAa5B,EAEf,OAAIhE,EAAa,YAAcA,EAAa,kBAQ1C4F,EAAajG,GAAW,KAAKvB,EAAkBwH,EAAY,EAAI,GAE1DA,CACT,CACA,IAAIE,EAAiBlF,GAAiBoD,EAAK,UAAYA,EAAK,UAE5D,OAAIpD,IAAkBd,EAAa,UAAU,GAAKkE,EAAK,eAAiBA,EAAK,cAAc,SAAWA,EAAK,cAAc,QAAQ,MAAQrJ,EAAWwC,GAAc6G,EAAK,cAAc,QAAQ,IAAI,IAC/L8B,EAAiB,aAAe9B,EAAK,cAAc,QAAQ,KAAO;AAAA,EAAQ8B,GAGxEpF,IACF5G,GAAa,CAAC6C,GAAeC,GAAUC,EAAW,EAAGxZ,IAAQ,CAC3DyiB,EAAiBvL,GAAcuL,EAAgBziB,GAAM,GAAG,CAC1D,CAAC,EAEIgc,GAAsB4B,GAAsB5B,EAAmB,WAAWyG,CAAc,EAAIA,CACrG,EACA5H,EAAU,UAAY,UAAY,CAChC,IAAI8E,EAAM,UAAU,OAAS,GAAK,UAAU,CAAC,IAAM,OAAY,UAAU,CAAC,EAAI,CAAA,EAC9ED,GAAaC,CAAG,EAChBnC,GAAa,EACf,EACA3C,EAAU,YAAc,UAAY,CAClCyE,GAAS,KACT9B,GAAa,EACf,EACA3C,EAAU,iBAAmB,SAAU6H,EAAKZ,EAAM3tB,EAAO,CAElDmrB,IACHI,GAAa,CAAA,CAAE,EAEjB,MAAM+B,EAAQ1J,EAAkB2K,CAAG,EAC7BhB,EAAS3J,EAAkB+J,CAAI,EACrC,OAAON,GAAkBC,EAAOC,EAAQvtB,CAAK,CAC/C,EACA0mB,EAAU,QAAU,SAAU8H,EAAYC,EAAc,CAClD,OAAOA,GAAiB,YAG5B/L,GAAU0F,EAAMoG,CAAU,EAAGC,CAAY,CAC3C,EACA/H,EAAU,WAAa,SAAU8H,EAAYC,EAAc,CACzD,GAAIA,IAAiB,OAAW,CAC9B,MAAMzK,EAAQxB,GAAiB4F,EAAMoG,CAAU,EAAGC,CAAY,EAC9D,OAAOzK,IAAU,GAAK,OAAYrB,GAAYyF,EAAMoG,CAAU,EAAGxK,EAAO,CAAC,EAAE,CAAC,CAC9E,CACA,OAAOvB,GAAS2F,EAAMoG,CAAU,CAAC,CACnC,EACA9H,EAAU,YAAc,SAAU8H,EAAY,CAC5CpG,EAAMoG,CAAU,EAAI,CAAA,CACtB,EACA9H,EAAU,eAAiB,UAAY,CACrC0B,EAAQ7B,GAAe,CACzB,EACOG,CACT,CACA,IAAIgI,GAASlI,GAAe,EC11C5B,SAASxnB,IAAG,CAAC,MAAM,CAAC,MAAM,GAAG,OAAO,GAAG,WAAW,KAAK,IAAI,GAAG,MAAM,KAAK,SAAS,GAAG,SAAS,KAAK,OAAO,GAAG,UAAU,KAAK,WAAW,IAAI,CAAC,CAAC,IAAI2S,GAAE3S,GAAC,EAAG,SAASM,GAAEzB,EAAE,CAAC8T,GAAE9T,CAAC,CAAC,IAAIa,GAAE,CAAC,KAAK,IAAI,IAAI,EAAE,SAASW,EAAExB,EAAEd,EAAE,GAAG,CAAC,IAAID,EAAE,OAAOe,GAAG,SAASA,EAAEA,EAAE,OAAOT,EAAE,CAAC,QAAQ,CAACD,EAAEE,IAAI,CAAC,IAAIL,EAAE,OAAOK,GAAG,SAASA,EAAEA,EAAE,OAAO,OAAOL,EAAEA,EAAE,QAAQoB,EAAE,MAAM,IAAI,EAAEtB,EAAEA,EAAE,QAAQK,EAAEH,CAAC,EAAEI,CAAC,EAAE,SAAS,IAAI,IAAI,OAAON,EAAEC,CAAC,CAAC,EAAE,OAAOK,CAAC,CAAC,IAAIuxB,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,OAAO,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAC,EAAIvwB,EAAE,CAAC,iBAAiB,yBAAyB,kBAAkB,cAAc,uBAAuB,gBAAgB,eAAe,OAAO,WAAW,KAAK,kBAAkB,KAAK,gBAAgB,KAAK,aAAa,OAAO,kBAAkB,MAAM,cAAc,MAAM,oBAAoB,OAAO,UAAU,WAAW,gBAAgB,oBAAoB,gBAAgB,WAAW,wBAAwB,iCAAiC,yBAAyB,mBAAmB,gBAAgB,OAAO,mBAAmB,0BAA0B,WAAW,iBAAiB,gBAAgB,eAAe,iBAAiB,YAAY,QAAQ,SAAS,aAAa,WAAW,eAAe,OAAO,gBAAgB,aAAa,kBAAkB,YAAY,gBAAgB,YAAY,iBAAiB,aAAa,eAAe,YAAY,UAAU,QAAQ,QAAQ,UAAU,kBAAkB,iCAAiC,gBAAgB,mCAAmC,kBAAkB,KAAK,gBAAgB,KAAK,kBAAkB,gCAAgC,oBAAoB,gBAAgB,WAAW,UAAU,cAAc,WAAW,mBAAmB,oDAAoD,sBAAsB,qDAAqD,aAAa,6CAA6C,MAAM,eAAe,cAAc,OAAO,SAAS,MAAM,UAAU,MAAM,UAAU,QAAQ,eAAe,WAAW,UAAU,SAAS,cAAc,OAAO,cAAc,MAAM,cAAcP,GAAG,IAAI,OAAO,WAAWA,CAAC,8BAA8B,EAAE,gBAAgBA,GAAG,IAAI,OAAO,QAAQ,KAAK,IAAI,EAAEA,EAAE,CAAC,CAAC,oDAAoD,EAAE,QAAQA,GAAG,IAAI,OAAO,QAAQ,KAAK,IAAI,EAAEA,EAAE,CAAC,CAAC,oDAAoD,EAAE,iBAAiBA,GAAG,IAAI,OAAO,QAAQ,KAAK,IAAI,EAAEA,EAAE,CAAC,CAAC,iBAAiB,EAAE,kBAAkBA,GAAG,IAAI,OAAO,QAAQ,KAAK,IAAI,EAAEA,EAAE,CAAC,CAAC,IAAI,EAAE,eAAeA,GAAG,IAAI,OAAO,QAAQ,KAAK,IAAI,EAAEA,EAAE,CAAC,CAAC,qBAAqB,GAAG,CAAC,EAAE+wB,GAAG,uBAAuBC,GAAG,wDAAwDC,GAAG,8GAA8G/vB,GAAE,qEAAqEgwB,GAAG,uCAAuClwB,GAAE,wBAAwBmwB,GAAG,iKAAiKC,GAAG5vB,EAAE2vB,EAAE,EAAE,QAAQ,QAAQnwB,EAAC,EAAE,QAAQ,aAAa,mBAAmB,EAAE,QAAQ,UAAU,uBAAuB,EAAE,QAAQ,cAAc,SAAS,EAAE,QAAQ,WAAW,cAAc,EAAE,QAAQ,QAAQ,mBAAmB,EAAE,QAAQ,WAAW,EAAE,EAAE,SAAQ,EAAGqwB,GAAG7vB,EAAE2vB,EAAE,EAAE,QAAQ,QAAQnwB,EAAC,EAAE,QAAQ,aAAa,mBAAmB,EAAE,QAAQ,UAAU,uBAAuB,EAAE,QAAQ,cAAc,SAAS,EAAE,QAAQ,WAAW,cAAc,EAAE,QAAQ,QAAQ,mBAAmB,EAAE,QAAQ,SAAS,mCAAmC,EAAE,SAAQ,EAAGswB,GAAE,uFAAuFC,GAAG,UAAU5b,GAAE,mCAAmC6b,GAAGhwB,EAAE,6GAA6G,EAAE,QAAQ,QAAQmU,EAAC,EAAE,QAAQ,QAAQ,8DAA8D,EAAE,SAAQ,EAAG8b,GAAGjwB,EAAE,sCAAsC,EAAE,QAAQ,QAAQR,EAAC,EAAE,SAAQ,EAAGX,GAAE,gWAAgWuB,GAAE,gCAAgC8vB,GAAGlwB,EAAE,4dAA4d,GAAG,EAAE,QAAQ,UAAUI,EAAC,EAAE,QAAQ,MAAMvB,EAAC,EAAE,QAAQ,YAAY,0EAA0E,EAAE,SAAQ,EAAGsxB,GAAGnwB,EAAE8vB,EAAC,EAAE,QAAQ,KAAKpwB,EAAC,EAAE,QAAQ,UAAU,uBAAuB,EAAE,QAAQ,YAAY,EAAE,EAAE,QAAQ,SAAS,EAAE,EAAE,QAAQ,aAAa,SAAS,EAAE,QAAQ,SAAS,gDAAgD,EAAE,QAAQ,OAAO,wBAAwB,EAAE,QAAQ,OAAO,6DAA6D,EAAE,QAAQ,MAAMb,EAAC,EAAE,SAAQ,EAAGuxB,GAAGpwB,EAAE,yCAAyC,EAAE,QAAQ,YAAYmwB,EAAE,EAAE,SAAQ,EAAGE,GAAE,CAAC,WAAWD,GAAG,KAAKZ,GAAG,IAAIQ,GAAG,OAAOP,GAAG,QAAQC,GAAG,GAAGhwB,GAAE,KAAKwwB,GAAG,SAASN,GAAG,KAAKK,GAAG,QAAQV,GAAG,UAAUY,GAAG,MAAM9wB,GAAE,KAAK0wB,EAAE,EAAEO,GAAGtwB,EAAE,6JAA6J,EAAE,QAAQ,KAAKN,EAAC,EAAE,QAAQ,UAAU,uBAAuB,EAAE,QAAQ,aAAa,SAAS,EAAE,QAAQ,OAAO,wBAAwB,EAAE,QAAQ,SAAS,gDAAgD,EAAE,QAAQ,OAAO,wBAAwB,EAAE,QAAQ,OAAO,6DAA6D,EAAE,QAAQ,MAAMb,EAAC,EAAE,SAAQ,EAAG0xB,GAAG,CAAC,GAAGF,GAAE,SAASR,GAAG,MAAMS,GAAG,UAAUtwB,EAAE8vB,EAAC,EAAE,QAAQ,KAAKpwB,EAAC,EAAE,QAAQ,UAAU,uBAAuB,EAAE,QAAQ,YAAY,EAAE,EAAE,QAAQ,QAAQ4wB,EAAE,EAAE,QAAQ,aAAa,SAAS,EAAE,QAAQ,SAAS,gDAAgD,EAAE,QAAQ,OAAO,wBAAwB,EAAE,QAAQ,OAAO,6DAA6D,EAAE,QAAQ,MAAMzxB,EAAC,EAAE,SAAQ,CAAE,EAAE2xB,GAAG,CAAC,GAAGH,GAAE,KAAKrwB,EAAE,wIAAwI,EAAE,QAAQ,UAAUI,EAAC,EAAE,QAAQ,OAAO,mKAAmK,EAAE,SAAQ,EAAG,IAAI,oEAAoE,QAAQ,yBAAyB,OAAOf,GAAE,SAAS,mCAAmC,UAAUW,EAAE8vB,EAAC,EAAE,QAAQ,KAAKpwB,EAAC,EAAE,QAAQ,UAAU;AAAA,EACn3N,EAAE,QAAQ,WAAWkwB,EAAE,EAAE,QAAQ,SAAS,EAAE,EAAE,QAAQ,aAAa,SAAS,EAAE,QAAQ,UAAU,EAAE,EAAE,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE,EAAE,QAAQ,OAAO,EAAE,EAAE,SAAQ,CAAE,EAAEa,GAAG,8CAA8CC,GAAG,sCAAsCC,GAAG,wBAAwBC,GAAG,8EAA8EtwB,GAAE,gBAAgBuwB,GAAE,kBAAkBC,GAAG,mBAAmBC,GAAG/wB,EAAE,wBAAwB,GAAG,EAAE,QAAQ,cAAc6wB,EAAC,EAAE,SAAQ,EAAGG,GAAG,qBAAqBC,GAAG,uBAAuBC,GAAG,yBAAyBC,GAAGnxB,EAAE,yBAAyB,GAAG,EAAE,QAAQ,OAAO,mGAAmG,EAAE,QAAQ,WAAWsvB,GAAG,WAAW,WAAW,EAAE,QAAQ,OAAO,yBAAyB,EAAE,QAAQ,OAAO,gBAAgB,EAAE,WAAW8B,GAAG,gEAAgEC,GAAGrxB,EAAEoxB,GAAG,GAAG,EAAE,QAAQ,SAAS9wB,EAAC,EAAE,SAAQ,EAAGgxB,GAAGtxB,EAAEoxB,GAAG,GAAG,EAAE,QAAQ,SAASJ,EAAE,EAAE,SAAQ,EAAGO,GAAG,wQAAwQC,GAAGxxB,EAAEuxB,GAAG,IAAI,EAAE,QAAQ,iBAAiBT,EAAE,EAAE,QAAQ,cAAcD,EAAC,EAAE,QAAQ,SAASvwB,EAAC,EAAE,SAAQ,EAAGmxB,GAAGzxB,EAAEuxB,GAAG,IAAI,EAAE,QAAQ,iBAAiBL,EAAE,EAAE,QAAQ,cAAcD,EAAE,EAAE,QAAQ,SAASD,EAAE,EAAE,SAAQ,EAAGU,GAAG1xB,EAAE,mNAAmN,IAAI,EAAE,QAAQ,iBAAiB8wB,EAAE,EAAE,QAAQ,cAAcD,EAAC,EAAE,QAAQ,SAASvwB,EAAC,EAAE,SAAQ,EAAGqxB,GAAG3xB,EAAE,YAAY,IAAI,EAAE,QAAQ,SAASM,EAAC,EAAE,SAAQ,EAAGsxB,GAAG5xB,EAAE,qCAAqC,EAAE,QAAQ,SAAS,8BAA8B,EAAE,QAAQ,QAAQ,8IAA8I,EAAE,SAAQ,EAAG6xB,GAAG7xB,EAAEI,EAAC,EAAE,QAAQ,YAAY,KAAK,EAAE,SAAQ,EAAG0xB,GAAG9xB,EAAE,0JAA0J,EAAE,QAAQ,UAAU6xB,EAAE,EAAE,QAAQ,YAAY,6EAA6E,EAAE,SAAQ,EAAGhgB,GAAE,wEAAwEkgB,GAAG/xB,EAAE,mEAAmE,EAAE,QAAQ,QAAQ6R,EAAC,EAAE,QAAQ,OAAO,yCAAyC,EAAE,QAAQ,QAAQ,6DAA6D,EAAE,WAAWmgB,GAAGhyB,EAAE,yBAAyB,EAAE,QAAQ,QAAQ6R,EAAC,EAAE,QAAQ,MAAMsC,EAAC,EAAE,SAAQ,EAAG8d,GAAGjyB,EAAE,uBAAuB,EAAE,QAAQ,MAAMmU,EAAC,EAAE,WAAW+d,GAAGlyB,EAAE,wBAAwB,GAAG,EAAE,QAAQ,UAAUgyB,EAAE,EAAE,QAAQ,SAASC,EAAE,EAAE,SAAQ,EAAGE,GAAG,qCAAqCza,GAAE,CAAC,WAAWrY,GAAE,eAAesyB,GAAG,SAASC,GAAG,UAAUT,GAAG,GAAGR,GAAG,KAAKD,GAAG,IAAIrxB,GAAE,eAAegyB,GAAG,kBAAkBG,GAAG,kBAAkBE,GAAG,OAAOjB,GAAG,KAAKsB,GAAG,OAAOE,GAAG,YAAYlB,GAAG,QAAQiB,GAAG,cAAcE,GAAG,IAAIJ,GAAG,KAAKlB,GAAG,IAAIvxB,EAAC,EAAE+yB,GAAG,CAAC,GAAG1a,GAAE,KAAK1X,EAAE,yBAAyB,EAAE,QAAQ,QAAQ6R,EAAC,EAAE,SAAQ,EAAG,QAAQ7R,EAAE,+BAA+B,EAAE,QAAQ,QAAQ6R,EAAC,EAAE,SAAQ,CAAE,EAAEqC,GAAE,CAAC,GAAGwD,GAAE,kBAAkB+Z,GAAG,eAAeH,GAAG,IAAItxB,EAAE,gEAAgE,EAAE,QAAQ,WAAWmyB,EAAE,EAAE,QAAQ,QAAQ,2EAA2E,EAAE,SAAQ,EAAG,WAAW,6EAA6E,IAAI,0EAA0E,KAAKnyB,EAAE,qNAAqN,EAAE,QAAQ,WAAWmyB,EAAE,EAAE,SAAQ,CAAE,EAAEE,GAAG,CAAC,GAAGne,GAAE,GAAGlU,EAAE2wB,EAAE,EAAE,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK3wB,EAAEkU,GAAE,IAAI,EAAE,QAAQ,OAAO,eAAe,EAAE,QAAQ,UAAU,GAAG,EAAE,SAAQ,CAAE,EAAE/U,GAAE,CAAC,OAAOkxB,GAAE,IAAIE,GAAG,SAASC,EAAE,EAAE1wB,GAAE,CAAC,OAAO4X,GAAE,IAAIxD,GAAE,OAAOme,GAAG,SAASD,EAAE,EAAME,GAAG,CAAC,IAAI,QAAQ,IAAI,OAAO,IAAI,OAAO,IAAI,SAAS,IAAI,OAAO,EAAEC,GAAG/zB,GAAG8zB,GAAG9zB,CAAC,EAAE,SAASwZ,GAAExZ,EAAEd,EAAE,CAAC,GAAGA,GAAG,GAAGqB,EAAE,WAAW,KAAKP,CAAC,EAAE,OAAOA,EAAE,QAAQO,EAAE,cAAcwzB,EAAE,UAAUxzB,EAAE,mBAAmB,KAAKP,CAAC,EAAE,OAAOA,EAAE,QAAQO,EAAE,sBAAsBwzB,EAAE,EAAE,OAAO/zB,CAAC,CAAC,SAAS4T,GAAE5T,EAAE,CAAC,GAAG,CAACA,EAAE,UAAUA,CAAC,EAAE,QAAQO,EAAE,cAAc,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,OAAOP,CAAC,CAAC,SAASg0B,GAAEh0B,EAAEd,EAAE,CAAC,IAAID,EAAEe,EAAE,QAAQO,EAAE,SAAS,CAACf,EAAEL,EAAES,IAAI,CAAC,IAAIR,EAAE,GAAGS,EAAEV,EAAE,KAAK,EAAEU,GAAG,GAAGD,EAAEC,CAAC,IAAI,MAAMT,EAAE,CAACA,EAAE,OAAOA,EAAE,IAAI,IAAI,CAAC,EAAEG,EAAEN,EAAE,MAAMsB,EAAE,SAAS,EAAEjB,EAAE,EAAE,GAAGC,EAAE,CAAC,EAAE,KAAI,GAAIA,EAAE,MAAK,EAAGA,EAAE,OAAO,GAAG,CAACA,EAAE,GAAG,EAAE,GAAG,KAAI,GAAIA,EAAE,IAAG,EAAGL,EAAE,GAAGK,EAAE,OAAOL,EAAEK,EAAE,OAAOL,CAAC,MAAO,MAAKK,EAAE,OAAOL,GAAGK,EAAE,KAAK,EAAE,EAAE,KAAKD,EAAEC,EAAE,OAAOD,IAAIC,EAAED,CAAC,EAAEC,EAAED,CAAC,EAAE,OAAO,QAAQiB,EAAE,UAAU,GAAG,EAAE,OAAOhB,CAAC,CAAC,SAAS6B,GAAEpB,EAAEd,EAAED,EAAE,CAAC,IAAIM,EAAES,EAAE,OAAO,GAAGT,IAAI,EAAE,MAAM,GAAG,IAAID,EAAE,EAAE,KAAKA,EAAEC,GAAUS,EAAE,OAAOT,EAAED,EAAE,CAAC,IAASJ,GAAMI,IAAoC,OAAOU,EAAE,MAAM,EAAET,EAAED,CAAC,CAAC,CAAC,SAAS20B,GAAGj0B,EAAEd,EAAE,CAAC,GAAGc,EAAE,QAAQd,EAAE,CAAC,CAAC,IAAI,GAAG,MAAM,GAAG,IAAID,EAAE,EAAE,QAAQM,EAAE,EAAEA,EAAES,EAAE,OAAOT,IAAI,GAAGS,EAAET,CAAC,IAAI,KAAKA,YAAYS,EAAET,CAAC,IAAIL,EAAE,CAAC,EAAED,YAAYe,EAAET,CAAC,IAAIL,EAAE,CAAC,IAAID,IAAIA,EAAE,GAAG,OAAOM,EAAE,OAAON,EAAE,EAAE,GAAG,EAAE,CAAC,SAASi1B,GAAGl0B,EAAEd,EAAED,EAAEM,EAAED,EAAE,CAAC,IAAIE,EAAEN,EAAE,KAAKC,EAAED,EAAE,OAAO,KAAKU,EAAEI,EAAE,CAAC,EAAE,QAAQV,EAAE,MAAM,kBAAkB,IAAI,EAAEC,EAAE,MAAM,OAAO,GAAG,IAAIH,EAAE,CAAC,KAAKY,EAAE,CAAC,EAAE,OAAO,CAAC,IAAI,IAAI,QAAQ,OAAO,IAAIf,EAAE,KAAKO,EAAE,MAAML,EAAE,KAAKS,EAAE,OAAOL,EAAE,aAAaK,CAAC,CAAC,EAAE,OAAOL,EAAE,MAAM,OAAO,GAAGH,CAAC,CAAC,SAAS+0B,GAAGn0B,EAAEd,EAAED,EAAE,CAAC,IAAIM,EAAES,EAAE,MAAMf,EAAE,MAAM,sBAAsB,EAAE,GAAGM,IAAI,KAAK,OAAOL,EAAE,IAAII,EAAEC,EAAE,CAAC,EAAE,OAAOL,EAAE,MAAM;AAAA,CACtiL,EAAE,IAAIM,GAAG,CAAC,IAAIL,EAAEK,EAAE,MAAMP,EAAE,MAAM,cAAc,EAAE,GAAGE,IAAI,KAAK,OAAOK,EAAE,GAAG,CAACI,CAAC,EAAET,EAAE,OAAOS,EAAE,QAAQN,EAAE,OAAOE,EAAE,MAAMF,EAAE,MAAM,EAAEE,CAAC,CAAC,EAAE,KAAK;AAAA,CACnI,CAAC,CAAC,IAAIY,GAAE,KAAK,CAAC,QAAQ,MAAM,MAAM,YAAY,EAAE,CAAC,KAAK,QAAQ,GAAG0T,EAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,QAAQ,KAAK,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,iBAAiB,EAAE,EAAE,MAAM,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,eAAe,WAAW,KAAK,KAAK,QAAQ,SAAS,EAAE1S,GAAE,EAAE;AAAA,CACvW,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,OAAO,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE9B,EAAE60B,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,KAAK,EAAE,MAAM,CAAC,KAAK,OAAO,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAI,EAAG,QAAQ,KAAK,MAAM,OAAO,eAAe,IAAI,EAAE,EAAE,CAAC,EAAE,KAAK70B,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,QAAQ,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAI,EAAG,GAAG,KAAK,MAAM,MAAM,WAAW,KAAK,CAAC,EAAE,CAAC,IAAIA,EAAE8B,GAAE,EAAE,GAAG,GAAG,KAAK,QAAQ,UAAU,CAAC9B,GAAG,KAAK,MAAM,MAAM,gBAAgB,KAAKA,CAAC,KAAK,EAAEA,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,UAAU,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI8B,GAAE,EAAE,CAAC,EAAE;AAAA,CACjkB,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,WAAW,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAEA,GAAE,EAAE,CAAC,EAAE;AAAA,CAC9E,EAAE,MAAM;AAAA,CACR,EAAE9B,EAAE,GAAG,EAAE,GAAGH,EAAE,GAAG,KAAK,EAAE,OAAO,GAAG,CAAC,IAAI,EAAE,GAAGC,EAAE,CAAA,EAAGS,EAAE,IAAIA,EAAE,EAAEA,EAAE,EAAE,OAAOA,IAAI,GAAG,KAAK,MAAM,MAAM,gBAAgB,KAAK,EAAEA,CAAC,CAAC,EAAET,EAAE,KAAK,EAAES,CAAC,CAAC,EAAE,EAAE,WAAW,CAAC,EAAET,EAAE,KAAK,EAAES,CAAC,CAAC,MAAO,OAAM,EAAE,EAAE,MAAMA,CAAC,EAAE,IAAI,EAAET,EAAE,KAAK;AAAA,CACxM,EAAEM,EAAE,EAAE,QAAQ,KAAK,MAAM,MAAM,wBAAwB;AAAA,OACjD,EAAE,QAAQ,KAAK,MAAM,MAAM,yBAAyB,EAAE,EAAEJ,EAAEA,EAAE,GAAGA,CAAC;AAAA,EACrE,CAAC,GAAG,EAAE,EAAE,EAAE,GAAG,CAAC;AAAA,EACdI,CAAC,GAAGA,EAAE,IAAIc,EAAE,KAAK,MAAM,MAAM,IAAI,GAAG,KAAK,MAAM,MAAM,IAAI,GAAG,KAAK,MAAM,YAAYd,EAAEP,EAAE,EAAE,EAAE,KAAK,MAAM,MAAM,IAAIqB,EAAE,EAAE,SAAS,EAAE,MAAM,IAAI,EAAErB,EAAE,GAAG,EAAE,EAAE,GAAG,GAAG,OAAO,OAAO,MAAM,GAAG,GAAG,OAAO,aAAa,CAAC,IAAIoC,EAAE,EAAEtB,EAAEsB,EAAE,IAAI;AAAA,EACzN,EAAE,KAAK;AAAA,CACR,EAAE6yB,EAAE,KAAK,WAAWn0B,CAAC,EAAEd,EAAEA,EAAE,OAAO,CAAC,EAAEi1B,EAAE90B,EAAEA,EAAE,UAAU,EAAEA,EAAE,OAAOiC,EAAE,IAAI,MAAM,EAAE6yB,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,OAAO7yB,EAAE,KAAK,MAAM,EAAE6yB,EAAE,KAAK,KAAK,SAAS,GAAG,OAAO,OAAO,CAAC,IAAI7yB,EAAE,EAAEtB,EAAEsB,EAAE,IAAI;AAAA,EAClL,EAAE,KAAK;AAAA,CACR,EAAE6yB,EAAE,KAAK,KAAKn0B,CAAC,EAAEd,EAAEA,EAAE,OAAO,CAAC,EAAEi1B,EAAE90B,EAAEA,EAAE,UAAU,EAAEA,EAAE,OAAO,EAAE,IAAI,MAAM,EAAE80B,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,OAAO7yB,EAAE,IAAI,MAAM,EAAE6yB,EAAE,IAAI,EAAEn0B,EAAE,UAAUd,EAAE,GAAG,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM;AAAA,CACpK,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,aAAa,IAAIG,EAAE,OAAOH,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAI,EAAGG,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,IAAI,GAAG,QAAQA,EAAE,MAAMA,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,GAAG,MAAM,CAAA,CAAE,EAAE,EAAEA,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,QAAQ,WAAW,EAAEA,EAAE,EAAE,SAAS,IAAIH,EAAE,KAAK,MAAM,MAAM,cAAc,CAAC,EAAE,EAAE,GAAG,KAAK,GAAG,CAAC,IAAIU,EAAE,GAAG,EAAE,GAAGH,EAAE,GAAG,GAAG,EAAE,EAAEP,EAAE,KAAK,CAAC,IAAI,KAAK,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,IAAIqB,EAAE,EAAE,CAAC,EAAE,MAAM;AAAA,EACvd,CAAC,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,gBAAgB4zB,GAAG,IAAI,OAAO,EAAEA,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM;AAAA,EACpF,CAAC,EAAE,CAAC,EAAE7yB,EAAE,CAACf,EAAE,KAAI,EAAGP,EAAE,EAAE,GAAG,KAAK,QAAQ,UAAUA,EAAE,EAAEP,EAAEc,EAAE,UAAS,GAAIe,EAAEtB,EAAE,EAAE,CAAC,EAAE,OAAO,GAAGA,EAAE,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,MAAM,YAAY,EAAEA,EAAEA,EAAE,EAAE,EAAEA,EAAEP,EAAEc,EAAE,MAAMP,CAAC,EAAEA,GAAG,EAAE,CAAC,EAAE,QAAQsB,GAAG,KAAK,MAAM,MAAM,UAAU,KAAK,CAAC,IAAI,GAAG,EAAE;AAAA,EACzN,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE1B,EAAE,IAAI,CAACA,EAAE,CAAC,IAAIu0B,EAAE,KAAK,MAAM,MAAM,gBAAgBn0B,CAAC,EAAEc,EAAE,KAAK,MAAM,MAAM,QAAQd,CAAC,EAAE4T,EAAE,KAAK,MAAM,MAAM,iBAAiB5T,CAAC,EAAEo0B,EAAG,KAAK,MAAM,MAAM,kBAAkBp0B,CAAC,EAAEq0B,EAAG,KAAK,MAAM,MAAM,eAAer0B,CAAC,EAAE,KAAK,GAAG,CAAC,IAAIoB,EAAE,EAAE,MAAM;AAAA,EACzP,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAEA,EAAE,KAAK,QAAQ,UAAU,EAAE,EAAE,QAAQ,KAAK,MAAM,MAAM,mBAAmB,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,QAAQ,KAAK,MAAM,MAAM,cAAc,MAAM,EAAEwS,EAAE,KAAK,CAAC,GAAGwgB,EAAG,KAAK,CAAC,GAAGC,EAAG,KAAK,CAAC,GAAGF,EAAE,KAAK,CAAC,GAAGrzB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,EAAE,OAAO,KAAK,MAAM,MAAM,YAAY,GAAGd,GAAG,CAAC,EAAE,KAAI,EAAGP,GAAG;AAAA,EAC9Q,EAAE,MAAMO,CAAC,MAAM,CAAC,GAAGsB,GAAGf,EAAE,QAAQ,KAAK,MAAM,MAAM,cAAc,MAAM,EAAE,OAAO,KAAK,MAAM,MAAM,YAAY,GAAG,GAAGqT,EAAE,KAAKrT,CAAC,GAAG6zB,EAAG,KAAK7zB,CAAC,GAAGO,EAAE,KAAKP,CAAC,EAAE,MAAMd,GAAG;AAAA,EAC3J,CAAC,CAAC,CAAC6B,GAAG,CAAC,EAAE,SAASA,EAAE,IAAI,GAAGF,EAAE;AAAA,EAC7B,EAAE,EAAE,UAAUA,EAAE,OAAO,CAAC,EAAEb,EAAE,EAAE,MAAMP,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,GAAG,KAAK,MAAM,MAAM,gBAAgB,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,KAAK,YAAY,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,QAAQ,KAAK,KAAK,MAAM,MAAM,WAAW,KAAKP,CAAC,EAAE,MAAM,GAAG,KAAKA,EAAE,OAAO,CAAA,CAAE,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,IAAIN,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,GAAGA,EAAEA,EAAE,IAAIA,EAAE,IAAI,QAAO,EAAGA,EAAE,KAAKA,EAAE,KAAK,QAAO,MAAQ,QAAO,EAAE,IAAI,EAAE,IAAI,QAAO,EAAG,QAAQS,KAAK,EAAE,MAAM,CAAC,GAAG,KAAK,MAAM,MAAM,IAAI,GAAGA,EAAE,OAAO,KAAK,MAAM,YAAYA,EAAE,KAAK,CAAA,CAAE,EAAEA,EAAE,KAAK,CAAC,GAAGA,EAAE,KAAKA,EAAE,KAAK,QAAQ,KAAK,MAAM,MAAM,gBAAgB,EAAE,EAAEA,EAAE,OAAO,CAAC,GAAG,OAAO,QAAQA,EAAE,OAAO,CAAC,GAAG,OAAO,YAAY,CAACA,EAAE,OAAO,CAAC,EAAE,IAAIA,EAAE,OAAO,CAAC,EAAE,IAAI,QAAQ,KAAK,MAAM,MAAM,gBAAgB,EAAE,EAAEA,EAAE,OAAO,CAAC,EAAE,KAAKA,EAAE,OAAO,CAAC,EAAE,KAAK,QAAQ,KAAK,MAAM,MAAM,gBAAgB,EAAE,EAAE,QAAQH,EAAE,KAAK,MAAM,YAAY,OAAO,EAAEA,GAAG,EAAEA,IAAI,GAAG,KAAK,MAAM,MAAM,WAAW,KAAK,KAAK,MAAM,YAAYA,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,MAAM,YAAYA,CAAC,EAAE,IAAI,KAAK,MAAM,YAAYA,CAAC,EAAE,IAAI,QAAQ,KAAK,MAAM,MAAM,gBAAgB,EAAE,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,iBAAiB,KAAKG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAIH,EAAE,CAAC,KAAK,WAAW,IAAI,EAAE,CAAC,EAAE,IAAI,QAAQ,EAAE,CAAC,IAAI,KAAK,EAAEG,EAAE,QAAQH,EAAE,QAAQ,EAAE,MAAMG,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,EAAE,SAASA,EAAE,OAAO,CAAC,EAAE,IAAI,GAAG,WAAWA,EAAE,OAAO,CAAC,GAAGA,EAAE,OAAO,CAAC,EAAE,QAAQA,EAAE,OAAO,CAAC,EAAE,IAAIH,EAAE,IAAIG,EAAE,OAAO,CAAC,EAAE,IAAIA,EAAE,OAAO,CAAC,EAAE,KAAKH,EAAE,IAAIG,EAAE,OAAO,CAAC,EAAE,KAAKA,EAAE,OAAO,CAAC,EAAE,OAAO,QAAQH,CAAC,GAAGG,EAAE,OAAO,QAAQ,CAAC,KAAK,YAAY,IAAIH,EAAE,IAAI,KAAKA,EAAE,IAAI,OAAO,CAACA,CAAC,CAAC,CAAC,EAAEG,EAAE,OAAO,QAAQH,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,EAAEG,EAAE,OAAO,OAAOW,GAAGA,EAAE,OAAO,OAAO,EAAEd,EAAE,EAAE,OAAO,GAAG,EAAE,KAAKc,GAAG,KAAK,MAAM,MAAM,QAAQ,KAAKA,EAAE,GAAG,CAAC,EAAE,EAAE,MAAMd,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,QAAQG,KAAK,EAAE,MAAM,CAACA,EAAE,MAAM,GAAG,QAAQ,KAAKA,EAAE,OAAO,EAAE,OAAO,SAAS,EAAE,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,OAAO,MAAM,GAAG,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,OAAO,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,IAAI,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,YAAW,EAAG,QAAQ,KAAK,MAAM,MAAM,oBAAoB,GAAG,EAAEP,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,aAAa,IAAI,EAAE,QAAQ,KAAK,MAAM,OAAO,eAAe,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,QAAQ,KAAK,MAAM,OAAO,eAAe,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,KAAKA,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,MAAM,KAAK,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,MAAM,MAAM,eAAe,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE00B,GAAE,EAAE,CAAC,CAAC,EAAE10B,EAAE,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,gBAAgB,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,EAAE,CAAC,GAAG,KAAI,EAAG,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,kBAAkB,EAAE,EAAE,MAAM;AAAA,CAC53E,EAAE,CAAA,EAAGH,EAAE,CAAC,KAAK,QAAQ,IAAI,EAAE,CAAC,EAAE,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,KAAK,CAAA,CAAE,EAAE,GAAG,EAAE,SAASG,EAAE,OAAO,CAAC,QAAQ,KAAKA,EAAE,KAAK,MAAM,MAAM,gBAAgB,KAAK,CAAC,EAAEH,EAAE,MAAM,KAAK,OAAO,EAAE,KAAK,MAAM,MAAM,iBAAiB,KAAK,CAAC,EAAEA,EAAE,MAAM,KAAK,QAAQ,EAAE,KAAK,MAAM,MAAM,eAAe,KAAK,CAAC,EAAEA,EAAE,MAAM,KAAK,MAAM,EAAEA,EAAE,MAAM,KAAK,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,OAAO,IAAIA,EAAE,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,OAAO,EAAE,CAAC,CAAC,EAAE,OAAO,GAAG,MAAMA,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,EAAEA,EAAE,KAAK,KAAK60B,GAAE,EAAE70B,EAAE,OAAO,MAAM,EAAE,IAAI,CAACC,EAAES,KAAK,CAAC,KAAKT,EAAE,OAAO,KAAK,MAAM,OAAOA,CAAC,EAAE,OAAO,GAAG,MAAMD,EAAE,MAAMU,CAAC,CAAC,EAAE,CAAC,EAAE,OAAOV,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,SAAS,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,UAAU,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,UAAU,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,IAAI;AAAA,EACzyB,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,YAAY,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,OAAO,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,SAAS,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,MAAM,MAAM,QAAQ,KAAK,MAAM,MAAM,UAAU,KAAK,EAAE,CAAC,CAAC,EAAE,KAAK,MAAM,MAAM,OAAO,GAAG,KAAK,MAAM,MAAM,QAAQ,KAAK,MAAM,MAAM,QAAQ,KAAK,EAAE,CAAC,CAAC,IAAI,KAAK,MAAM,MAAM,OAAO,IAAI,CAAC,KAAK,MAAM,MAAM,YAAY,KAAK,MAAM,MAAM,kBAAkB,KAAK,EAAE,CAAC,CAAC,EAAE,KAAK,MAAM,MAAM,WAAW,GAAG,KAAK,MAAM,MAAM,YAAY,KAAK,MAAM,MAAM,gBAAgB,KAAK,EAAE,CAAC,CAAC,IAAI,KAAK,MAAM,MAAM,WAAW,IAAI,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,MAAM,OAAO,WAAW,KAAK,MAAM,MAAM,WAAW,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAI,EAAG,GAAG,CAAC,KAAK,QAAQ,UAAU,KAAK,MAAM,MAAM,kBAAkB,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,MAAM,MAAM,gBAAgB,KAAK,CAAC,EAAE,OAAO,IAAIA,EAAEiC,GAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAOjC,EAAE,QAAQ,IAAI,EAAE,MAAM,KAAK,CAAC,IAAIA,EAAE80B,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG90B,IAAI,GAAG,OAAO,GAAGA,EAAE,GAAG,CAAC,IAAIC,GAAG,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,OAAOD,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,UAAU,EAAEA,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,UAAU,EAAEC,CAAC,EAAE,KAAI,EAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAIE,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,KAAK,QAAQ,SAAS,CAAC,IAAIH,EAAE,KAAK,MAAM,MAAM,kBAAkB,KAAKG,CAAC,EAAEH,IAAIG,EAAEH,EAAE,CAAC,EAAE,EAAEA,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAOG,EAAEA,EAAE,KAAI,EAAG,KAAK,MAAM,MAAM,kBAAkB,KAAKA,CAAC,IAAI,KAAK,QAAQ,UAAU,CAAC,KAAK,MAAM,MAAM,gBAAgB,KAAK,CAAC,EAAEA,EAAEA,EAAE,MAAM,CAAC,EAAEA,EAAEA,EAAE,MAAM,EAAE,EAAE,GAAG40B,GAAG,EAAE,CAAC,KAAK50B,GAAGA,EAAE,QAAQ,KAAK,MAAM,OAAO,eAAe,IAAI,EAAE,MAAM,GAAG,EAAE,QAAQ,KAAK,MAAM,OAAO,eAAe,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,MAAM,OAAO,QAAQ,KAAK,CAAC,KAAK,EAAE,KAAK,MAAM,OAAO,OAAO,KAAK,CAAC,GAAG,CAAC,IAAIA,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,QAAQ,KAAK,MAAM,MAAM,oBAAoB,GAAG,EAAE,EAAE,EAAEA,EAAE,YAAW,CAAE,EAAE,GAAG,CAAC,EAAE,CAAC,IAAIH,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,KAAK,OAAO,IAAIA,EAAE,KAAKA,CAAC,CAAC,CAAC,OAAO+0B,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,EAAE,GAAG,CAAC,IAAI50B,EAAE,KAAK,MAAM,OAAO,eAAe,KAAK,CAAC,EAAE,GAAG,GAACA,GAAGA,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,MAAM,mBAAmB,KAAY,EAAEA,EAAE,CAAC,GAAGA,EAAE,CAAC,IAAQ,CAAC,GAAG,KAAK,MAAM,OAAO,YAAY,KAAK,CAAC,GAAE,CAAC,IAAIH,EAAE,CAAC,GAAGG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAEM,EAAER,EAAES,EAAEV,EAAEW,EAAE,EAAEJ,EAAEJ,EAAE,CAAC,EAAE,CAAC,IAAI,IAAI,KAAK,MAAM,OAAO,kBAAkB,KAAK,MAAM,OAAO,kBAAkB,IAAII,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,EAAE,OAAOP,CAAC,GAAGG,EAAEI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,GAAGE,EAAEN,EAAE,CAAC,GAAGA,EAAE,CAAC,GAAGA,EAAE,CAAC,GAAGA,EAAE,CAAC,GAAGA,EAAE,CAAC,GAAGA,EAAE,CAAC,EAAE,CAACM,EAAE,SAAS,GAAGR,EAAE,CAAC,GAAGQ,CAAC,EAAE,OAAON,EAAE,CAAC,GAAGA,EAAE,CAAC,EAAE,CAACO,GAAGT,EAAE,QAAQ,UAAUE,EAAE,CAAC,GAAGA,EAAE,CAAC,IAAIH,EAAE,GAAG,GAAGA,EAAEC,GAAG,GAAG,CAACU,GAAGV,EAAE,QAAQ,CAAC,GAAGS,GAAGT,EAAES,EAAE,EAAE,SAAST,EAAE,KAAK,IAAIA,EAAEA,EAAES,EAAEC,CAAC,EAAE,IAAIU,EAAE,CAAC,GAAGlB,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,OAAOK,EAAE,EAAE,MAAM,EAAER,EAAEG,EAAE,MAAMkB,EAAEpB,CAAC,EAAE,GAAG,KAAK,IAAID,EAAEC,CAAC,EAAE,EAAE,CAAC,IAAIa,EAAEN,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,KAAK,IAAIA,EAAE,KAAKM,EAAE,OAAO,KAAK,MAAM,aAAaA,CAAC,CAAC,CAAC,CAAC,IAAIsB,EAAE5B,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,SAAS,IAAIA,EAAE,KAAK4B,EAAE,OAAO,KAAK,MAAM,aAAaA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,MAAM,kBAAkB,GAAG,EAAEjC,EAAE,KAAK,MAAM,MAAM,aAAa,KAAK,CAAC,EAAE,EAAE,KAAK,MAAM,MAAM,kBAAkB,KAAK,CAAC,GAAG,KAAK,MAAM,MAAM,gBAAgB,KAAK,CAAC,EAAE,OAAOA,GAAG,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,WAAW,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,MAAM,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,KAAK,MAAM,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,SAAS,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAEA,EAAE,OAAO,EAAE,CAAC,IAAI,KAAK,EAAE,EAAE,CAAC,EAAEA,EAAE,UAAU,IAAI,EAAE,EAAE,CAAC,EAAEA,EAAE,GAAG,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAKA,EAAE,OAAO,CAAC,CAAC,KAAK,OAAO,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,EAAE,CAAC,IAAI,EAAEA,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,EAAEA,EAAE,UAAU,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,MAAM,OAAO,WAAW,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,OAAOA,EAAE,UAAU,EAAE,CAAC,EAAEA,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAKA,EAAE,OAAO,CAAC,CAAC,KAAK,OAAO,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,KAAK,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,MAAM,WAAW,MAAM,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAMoB,GAAE,MAAMV,EAAC,CAAC,OAAO,QAAQ,MAAM,YAAY,UAAU,YAAYd,EAAE,CAAC,KAAK,OAAO,CAAA,EAAG,KAAK,OAAO,MAAM,OAAO,OAAO,IAAI,EAAE,KAAK,QAAQA,GAAG4U,GAAE,KAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW,IAAI1T,GAAE,KAAK,UAAU,KAAK,QAAQ,UAAU,KAAK,UAAU,QAAQ,KAAK,QAAQ,KAAK,UAAU,MAAM,KAAK,KAAK,YAAY,CAAA,EAAG,KAAK,MAAM,CAAC,OAAO,GAAG,WAAW,GAAG,IAAI,EAAE,EAAE,IAAInB,EAAE,CAAC,MAAMsB,EAAE,MAAMI,GAAE,OAAO,OAAOW,GAAE,MAAM,EAAE,KAAK,QAAQ,UAAUrC,EAAE,MAAM0B,GAAE,SAAS1B,EAAE,OAAOqC,GAAE,UAAU,KAAK,QAAQ,MAAMrC,EAAE,MAAM0B,GAAE,IAAI,KAAK,QAAQ,OAAO1B,EAAE,OAAOqC,GAAE,OAAOrC,EAAE,OAAOqC,GAAE,KAAK,KAAK,UAAU,MAAMrC,CAAC,CAAC,WAAW,OAAO,CAAC,MAAM,CAAC,MAAM0B,GAAE,OAAOW,EAAC,CAAC,CAAC,OAAO,IAAIpC,EAAED,EAAE,CAAC,OAAO,IAAIe,GAAEf,CAAC,EAAE,IAAIC,CAAC,CAAC,CAAC,OAAO,UAAUA,EAAED,EAAE,CAAC,OAAO,IAAIe,GAAEf,CAAC,EAAE,aAAaC,CAAC,CAAC,CAAC,IAAIA,EAAE,CAACA,EAAEA,EAAE,QAAQqB,EAAE,eAAe;AAAA,CACvqJ,EAAE,KAAK,YAAYrB,EAAE,KAAK,MAAM,EAAE,QAAQD,EAAE,EAAEA,EAAE,KAAK,YAAY,OAAOA,IAAI,CAAC,IAAIM,EAAE,KAAK,YAAYN,CAAC,EAAE,KAAK,aAAaM,EAAE,IAAIA,EAAE,MAAM,CAAC,CAAC,OAAO,KAAK,YAAY,CAAA,EAAG,KAAK,MAAM,CAAC,YAAYL,EAAED,EAAE,CAAA,EAAGM,EAAE,GAAG,CAAC,IAAI,KAAK,QAAQ,WAAWL,EAAEA,EAAE,QAAQqB,EAAE,cAAc,MAAM,EAAE,QAAQA,EAAE,UAAU,EAAE,GAAGrB,GAAG,CAAC,IAAII,EAAE,GAAG,KAAK,QAAQ,YAAY,OAAO,KAAKH,IAAIG,EAAEH,EAAE,KAAK,CAAC,MAAM,IAAI,EAAED,EAAED,CAAC,IAAIC,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,GAAGA,EAAE,KAAK,UAAU,MAAMJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAE,IAAIH,EAAEF,EAAE,GAAG,EAAE,EAAEK,EAAE,IAAI,SAAS,GAAGH,IAAI,OAAOA,EAAE,KAAK;AAAA,EACxhBF,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,KAAKJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAE,IAAIH,EAAEF,EAAE,GAAG,EAAE,EAAEE,GAAG,OAAO,aAAaA,GAAG,OAAO,QAAQA,EAAE,MAAMA,EAAE,IAAI,SAAS;AAAA,CAC5J,EAAE,GAAG;AAAA,GACHG,EAAE,IAAIH,EAAE,MAAM;AAAA,EACfG,EAAE,KAAK,KAAK,YAAY,GAAG,EAAE,EAAE,IAAIH,EAAE,MAAMF,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,OAAOJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,QAAQJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,GAAGJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,WAAWJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,KAAKJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,KAAKJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,IAAIJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAE,IAAIH,EAAEF,EAAE,GAAG,EAAE,EAAEE,GAAG,OAAO,aAAaA,GAAG,OAAO,QAAQA,EAAE,MAAMA,EAAE,IAAI,SAAS;AAAA,CACvpB,EAAE,GAAG;AAAA,GACHG,EAAE,IAAIH,EAAE,MAAM;AAAA,EACfG,EAAE,IAAI,KAAK,YAAY,GAAG,EAAE,EAAE,IAAIH,EAAE,MAAM,KAAK,OAAO,MAAMG,EAAE,GAAG,IAAI,KAAK,OAAO,MAAMA,EAAE,GAAG,EAAE,CAAC,KAAKA,EAAE,KAAK,MAAMA,EAAE,KAAK,EAAEL,EAAE,KAAKK,CAAC,GAAG,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,MAAMJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,SAASJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAEL,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,IAAIE,EAAEN,EAAE,GAAG,KAAK,QAAQ,YAAY,WAAW,CAAC,IAAIC,EAAE,IAAIS,EAAEV,EAAE,MAAM,CAAC,EAAEE,EAAE,KAAK,QAAQ,WAAW,WAAW,QAAQS,GAAG,CAACT,EAAES,EAAE,KAAK,CAAC,MAAM,IAAI,EAAED,CAAC,EAAE,OAAOR,GAAG,UAAUA,GAAG,IAAID,EAAE,KAAK,IAAIA,EAAEC,CAAC,EAAE,CAAC,EAAED,EAAE,KAAKA,GAAG,IAAIK,EAAEN,EAAE,UAAU,EAAEC,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,MAAM,MAAMG,EAAE,KAAK,UAAU,UAAUE,CAAC,GAAG,CAAC,IAAIL,EAAEF,EAAE,GAAG,EAAE,EAAEM,GAAGJ,GAAG,OAAO,aAAaA,EAAE,MAAMA,EAAE,IAAI,SAAS;AAAA,CACnoB,EAAE,GAAG;AAAA,GACHG,EAAE,IAAIH,EAAE,MAAM;AAAA,EACfG,EAAE,KAAK,KAAK,YAAY,IAAG,EAAG,KAAK,YAAY,GAAG,EAAE,EAAE,IAAIH,EAAE,MAAMF,EAAE,KAAKK,CAAC,EAAEC,EAAEC,EAAE,SAASN,EAAE,OAAOA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,KAAKJ,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUI,EAAE,IAAI,MAAM,EAAE,IAAIH,EAAEF,EAAE,GAAG,EAAE,EAAEE,GAAG,OAAO,QAAQA,EAAE,MAAMA,EAAE,IAAI,SAAS;AAAA,CACzP,EAAE,GAAG;AAAA,GACHG,EAAE,IAAIH,EAAE,MAAM;AAAA,EACfG,EAAE,KAAK,KAAK,YAAY,IAAG,EAAG,KAAK,YAAY,GAAG,EAAE,EAAE,IAAIH,EAAE,MAAMF,EAAE,KAAKK,CAAC,EAAE,QAAQ,CAAC,GAAGJ,EAAE,CAAC,IAAIC,EAAE,0BAA0BD,EAAE,WAAW,CAAC,EAAE,GAAG,KAAK,QAAQ,OAAO,CAAC,QAAQ,MAAMC,CAAC,EAAE,KAAK,KAAM,OAAM,IAAI,MAAMA,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,GAAGF,CAAC,CAAC,OAAOC,EAAED,EAAE,CAAA,EAAG,CAAC,OAAO,KAAK,YAAY,KAAK,CAAC,IAAIC,EAAE,OAAOD,CAAC,CAAC,EAAEA,CAAC,CAAC,aAAaC,EAAED,EAAE,CAAA,EAAG,CAAC,IAAIM,EAAEL,EAAEI,EAAE,KAAK,GAAG,KAAK,OAAO,MAAM,CAAC,IAAIF,EAAE,OAAO,KAAK,KAAK,OAAO,KAAK,EAAE,GAAGA,EAAE,OAAO,EAAE,MAAME,EAAE,KAAK,UAAU,MAAM,OAAO,cAAc,KAAKC,CAAC,IAAI,MAAMH,EAAE,SAASE,EAAE,CAAC,EAAE,MAAMA,EAAE,CAAC,EAAE,YAAY,GAAG,EAAE,EAAE,EAAE,CAAC,IAAIC,EAAEA,EAAE,MAAM,EAAED,EAAE,KAAK,EAAE,IAAI,IAAI,OAAOA,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,IAAIC,EAAE,MAAM,KAAK,UAAU,MAAM,OAAO,cAAc,SAAS,EAAE,CAAC,MAAMD,EAAE,KAAK,UAAU,MAAM,OAAO,eAAe,KAAKC,CAAC,IAAI,MAAMA,EAAEA,EAAE,MAAM,EAAED,EAAE,KAAK,EAAE,KAAKC,EAAE,MAAM,KAAK,UAAU,MAAM,OAAO,eAAe,SAAS,EAAE,IAAIC,EAAE,MAAMF,EAAE,KAAK,UAAU,MAAM,OAAO,UAAU,KAAKC,CAAC,IAAI,MAAMC,EAAEF,EAAE,CAAC,EAAEA,EAAE,CAAC,EAAE,OAAO,EAAEC,EAAEA,EAAE,MAAM,EAAED,EAAE,MAAME,CAAC,EAAE,IAAI,IAAI,OAAOF,EAAE,CAAC,EAAE,OAAOE,EAAE,CAAC,EAAE,IAAID,EAAE,MAAM,KAAK,UAAU,MAAM,OAAO,UAAU,SAAS,EAAEA,EAAE,KAAK,QAAQ,OAAO,cAAc,KAAK,CAAC,MAAM,IAAI,EAAEA,CAAC,GAAGA,EAAE,IAAIJ,EAAE,GAAGS,EAAE,GAAG,KAAKV,GAAG,CAACC,IAAIS,EAAE,IAAIT,EAAE,GAAG,IAAIC,EAAE,GAAG,KAAK,QAAQ,YAAY,QAAQ,KAAKU,IAAIV,EAAEU,EAAE,KAAK,CAAC,MAAM,IAAI,EAAEZ,EAAED,CAAC,IAAIC,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,GAAGA,EAAE,KAAK,UAAU,OAAOF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,IAAIF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,KAAKF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,QAAQF,EAAE,KAAK,OAAO,KAAK,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAE,IAAIU,EAAEb,EAAE,GAAG,EAAE,EAAEG,EAAE,OAAO,QAAQU,GAAG,OAAO,QAAQA,EAAE,KAAKV,EAAE,IAAIU,EAAE,MAAMV,EAAE,MAAMH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,SAASF,EAAEK,EAAEK,CAAC,EAAE,CAACV,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,SAASF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,GAAGF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,IAAIF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGA,EAAE,KAAK,UAAU,SAASF,CAAC,EAAE,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,MAAM,SAASA,EAAE,KAAK,UAAU,IAAIF,CAAC,GAAG,CAACA,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,IAAIS,EAAEX,EAAE,GAAG,KAAK,QAAQ,YAAY,YAAY,CAAC,IAAIY,EAAE,IAAIJ,EAAER,EAAE,MAAM,CAAC,EAAEsB,EAAE,KAAK,QAAQ,WAAW,YAAY,QAAQb,GAAG,CAACa,EAAEb,EAAE,KAAK,CAAC,MAAM,IAAI,EAAED,CAAC,EAAE,OAAOc,GAAG,UAAUA,GAAG,IAAIV,EAAE,KAAK,IAAIA,EAAEU,CAAC,EAAE,CAAC,EAAEV,EAAE,KAAKA,GAAG,IAAID,EAAEX,EAAE,UAAU,EAAEY,EAAE,CAAC,EAAE,CAAC,GAAGV,EAAE,KAAK,UAAU,WAAWS,CAAC,EAAE,CAACX,EAAEA,EAAE,UAAUE,EAAE,IAAI,MAAM,EAAEA,EAAE,IAAI,MAAM,EAAE,IAAI,MAAMQ,EAAER,EAAE,IAAI,MAAM,EAAE,GAAGD,EAAE,GAAG,IAAIW,EAAEb,EAAE,GAAG,EAAE,EAAEa,GAAG,OAAO,QAAQA,EAAE,KAAKV,EAAE,IAAIU,EAAE,MAAMV,EAAE,MAAMH,EAAE,KAAKG,CAAC,EAAE,QAAQ,CAAC,GAAGF,EAAE,CAAC,IAAIY,EAAE,0BAA0BZ,EAAE,WAAW,CAAC,EAAE,GAAG,KAAK,QAAQ,OAAO,CAAC,QAAQ,MAAMY,CAAC,EAAE,KAAK,KAAM,OAAM,IAAI,MAAMA,CAAC,CAAC,CAAC,CAAC,OAAOb,CAAC,CAAC,EAAM6B,GAAE,KAAK,CAAC,QAAQ,OAAO,YAAY,EAAE,CAAC,KAAK,QAAQ,GAAGgT,EAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,IAAIxU,GAAG,GAAG,IAAI,MAAMiB,EAAE,aAAa,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQA,EAAE,cAAc,EAAE,EAAE;AAAA,EAC7zF,OAAOjB,EAAE,8BAA8Bka,GAAEla,CAAC,EAAE,MAAM,EAAE,EAAEka,GAAE,EAAE,EAAE,GAAG;AAAA,EAC/D,eAAe,EAAE,EAAEA,GAAE,EAAE,EAAE,GAAG;AAAA,CAC7B,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM;AAAA,EAC7B,KAAK,OAAO,MAAM,CAAC,CAAC;AAAA,CACrB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC;AAAA,CACtH,CAAC,GAAG,EAAE,CAAC,MAAM;AAAA,CACb,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAMla,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAE,EAAE,MAAM,OAAO,IAAI,CAAC,IAAIF,EAAE,EAAE,MAAM,CAAC,EAAEE,GAAG,KAAK,SAASF,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,KAAKD,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,IAAI,GAAG,MAAM,IAAI,EAAEA,EAAE;AAAA,EAC7KG,EAAE,KAAK,EAAE;AAAA,CACV,CAAC,SAAS,EAAE,CAAC,MAAM,OAAO,KAAK,OAAO,MAAM,EAAE,MAAM,CAAC;AAAA,CACrD,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,WAAW,EAAE,cAAc,IAAI,+BAA+B,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,MAAM,KAAK,OAAO,YAAY,CAAC,CAAC;AAAA,CACxJ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAE,EAAE,OAAO,OAAO,IAAI,GAAG,KAAK,UAAU,EAAE,OAAO,CAAC,CAAC,EAAE,GAAG,KAAK,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,IAAIA,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,OAAO,IAAI,CAAC,IAAIH,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAEA,EAAE,OAAO,IAAI,GAAG,KAAK,UAAUA,EAAE,CAAC,CAAC,EAAEG,GAAG,KAAK,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAOA,IAAIA,EAAE,UAAUA,CAAC,YAAY;AAAA;AAAA,EAEpS,EAAE;AAAA,EACFA,EAAE;AAAA,CACH,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM;AAAA,EACzB,CAAC;AAAA,CACF,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,OAAO,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,KAAK,KAAK,OAAO,EAAE,MAAM,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC;AAAA,CACxI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,WAAW,KAAK,OAAO,YAAY,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,OAAO,KAAK,OAAO,YAAY,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,SAASka,GAAE,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,QAAQ,KAAK,OAAO,YAAY,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,IAAIla,EAAE,KAAK,OAAO,YAAY,CAAC,EAAE,EAAEsU,GAAE,CAAC,EAAE,GAAG,IAAI,KAAK,OAAOtU,EAAE,EAAE,EAAE,IAAIH,EAAE,YAAY,EAAE,IAAI,OAAO,IAAIA,GAAG,WAAWqa,GAAE,CAAC,EAAE,KAAKra,GAAG,IAAIG,EAAE,OAAOH,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAOG,CAAC,EAAE,CAACA,IAAI,EAAE,KAAK,OAAO,YAAYA,EAAE,KAAK,OAAO,YAAY,GAAG,IAAI,EAAEsU,GAAE,CAAC,EAAE,GAAG,IAAI,KAAK,OAAO4F,GAAE,CAAC,EAAE,EAAE,EAAE,IAAIra,EAAE,aAAa,CAAC,UAAU,CAAC,IAAI,OAAO,IAAIA,GAAG,WAAWqa,GAAE,CAAC,CAAC,KAAKra,GAAG,IAAIA,CAAC,CAAC,KAAK,EAAE,CAAC,MAAM,WAAW,GAAG,EAAE,OAAO,KAAK,OAAO,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,EAAE,QAAQ,EAAE,KAAKqa,GAAE,EAAE,IAAI,CAAC,CAAC,EAAM/Y,GAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,EAAMP,GAAE,MAAMF,EAAC,CAAC,QAAQ,SAAS,aAAa,YAAYd,EAAE,CAAC,KAAK,QAAQA,GAAG4U,GAAE,KAAK,QAAQ,SAAS,KAAK,QAAQ,UAAU,IAAIhT,GAAE,KAAK,SAAS,KAAK,QAAQ,SAAS,KAAK,SAAS,QAAQ,KAAK,QAAQ,KAAK,SAAS,OAAO,KAAK,KAAK,aAAa,IAAIL,EAAC,CAAC,OAAO,MAAMvB,EAAED,EAAE,CAAC,OAAO,IAAIe,GAAEf,CAAC,EAAE,MAAMC,CAAC,CAAC,CAAC,OAAO,YAAYA,EAAED,EAAE,CAAC,OAAO,IAAIe,GAAEf,CAAC,EAAE,YAAYC,CAAC,CAAC,CAAC,MAAMA,EAAE,CAAC,IAAID,EAAE,GAAG,QAAQM,EAAE,EAAEA,EAAEL,EAAE,OAAOK,IAAI,CAAC,IAAID,EAAEJ,EAAEK,CAAC,EAAE,GAAG,KAAK,QAAQ,YAAY,YAAYD,EAAE,IAAI,EAAE,CAAC,IAAIH,EAAEG,EAAEM,EAAE,KAAK,QAAQ,WAAW,UAAUT,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,IAAI,EAAEA,CAAC,EAAE,GAAGS,IAAI,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,OAAO,QAAQ,aAAa,OAAO,OAAO,MAAM,YAAY,MAAM,EAAE,SAAST,EAAE,IAAI,EAAE,CAACF,GAAGW,GAAG,GAAG,QAAQ,CAAC,CAAC,IAAIJ,EAAEF,EAAE,OAAOE,EAAE,MAAM,IAAI,QAAQ,CAACP,GAAG,KAAK,SAAS,MAAMO,CAAC,EAAE,KAAK,CAAC,IAAI,KAAK,CAACP,GAAG,KAAK,SAAS,GAAGO,CAAC,EAAE,KAAK,CAAC,IAAI,UAAU,CAACP,GAAG,KAAK,SAAS,QAAQO,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACP,GAAG,KAAK,SAAS,KAAKO,CAAC,EAAE,KAAK,CAAC,IAAI,QAAQ,CAACP,GAAG,KAAK,SAAS,MAAMO,CAAC,EAAE,KAAK,CAAC,IAAI,aAAa,CAACP,GAAG,KAAK,SAAS,WAAWO,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACP,GAAG,KAAK,SAAS,KAAKO,CAAC,EAAE,KAAK,CAAC,IAAI,WAAW,CAACP,GAAG,KAAK,SAAS,SAASO,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACP,GAAG,KAAK,SAAS,KAAKO,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,CAACP,GAAG,KAAK,SAAS,IAAIO,CAAC,EAAE,KAAK,CAAC,IAAI,YAAY,CAACP,GAAG,KAAK,SAAS,UAAUO,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACP,GAAG,KAAK,SAAS,KAAKO,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAIL,EAAE,eAAeK,EAAE,KAAK,wBAAwB,GAAG,KAAK,QAAQ,OAAO,OAAO,QAAQ,MAAML,CAAC,EAAE,GAAG,MAAM,IAAI,MAAMA,CAAC,CAAC,CAAC,CAAC,CAAC,OAAOF,CAAC,CAAC,YAAYC,EAAED,EAAE,KAAK,SAAS,CAAC,IAAIM,EAAE,GAAG,QAAQD,EAAE,EAAEA,EAAEJ,EAAE,OAAOI,IAAI,CAAC,IAAIE,EAAEN,EAAEI,CAAC,EAAE,GAAG,KAAK,QAAQ,YAAY,YAAYE,EAAE,IAAI,EAAE,CAAC,IAAII,EAAE,KAAK,QAAQ,WAAW,UAAUJ,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,IAAI,EAAEA,CAAC,EAAE,GAAGI,IAAI,IAAI,CAAC,CAAC,SAAS,OAAO,OAAO,QAAQ,SAAS,KAAK,WAAW,KAAK,MAAM,MAAM,EAAE,SAASJ,EAAE,IAAI,EAAE,CAACD,GAAGK,GAAG,GAAG,QAAQ,CAAC,CAAC,IAAIT,EAAEK,EAAE,OAAOL,EAAE,KAAI,CAAE,IAAI,SAAS,CAACI,GAAGN,EAAE,KAAKE,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACI,GAAGN,EAAE,KAAKE,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACI,GAAGN,EAAE,KAAKE,CAAC,EAAE,KAAK,CAAC,IAAI,QAAQ,CAACI,GAAGN,EAAE,MAAME,CAAC,EAAE,KAAK,CAAC,IAAI,WAAW,CAACI,GAAGN,EAAE,SAASE,CAAC,EAAE,KAAK,CAAC,IAAI,SAAS,CAACI,GAAGN,EAAE,OAAOE,CAAC,EAAE,KAAK,CAAC,IAAI,KAAK,CAACI,GAAGN,EAAE,GAAGE,CAAC,EAAE,KAAK,CAAC,IAAI,WAAW,CAACI,GAAGN,EAAE,SAASE,CAAC,EAAE,KAAK,CAAC,IAAI,KAAK,CAACI,GAAGN,EAAE,GAAGE,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,CAACI,GAAGN,EAAE,IAAIE,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAACI,GAAGN,EAAE,KAAKE,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAIS,EAAE,eAAeT,EAAE,KAAK,wBAAwB,GAAG,KAAK,QAAQ,OAAO,OAAO,QAAQ,MAAMS,CAAC,EAAE,GAAG,MAAM,IAAI,MAAMA,CAAC,CAAC,CAAC,CAAC,CAAC,OAAOL,CAAC,CAAC,EAAME,GAAE,KAAK,CAAC,QAAQ,MAAM,YAAY,EAAE,CAAC,KAAK,QAAQ,GAAGqU,EAAC,CAAC,OAAO,iBAAiB,IAAI,IAAI,CAAC,aAAa,cAAc,mBAAmB,cAAc,CAAC,EAAE,OAAO,6BAA6B,IAAI,IAAI,CAAC,aAAa,cAAc,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,iBAAiB,EAAE,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,KAAK,MAAMpT,GAAE,IAAIA,GAAE,SAAS,CAAC,eAAe,CAAC,OAAO,KAAK,MAAMR,GAAE,MAAMA,GAAE,WAAW,CAAC,EAAM2B,GAAE,KAAK,CAAC,SAASV,GAAC,EAAG,QAAQ,KAAK,WAAW,MAAM,KAAK,cAAc,EAAE,EAAE,YAAY,KAAK,cAAc,EAAE,EAAE,OAAOjB,GAAE,SAASY,GAAE,aAAaL,GAAE,MAAMC,GAAE,UAAUN,GAAE,MAAMX,GAAE,eAAe,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,QAAQH,KAAK,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,KAAKA,CAAC,CAAC,EAAEA,EAAE,MAAM,IAAI,QAAQ,CAAC,IAAI,EAAEA,EAAE,QAAQH,KAAK,EAAE,OAAO,EAAE,EAAE,OAAO,KAAK,WAAWA,EAAE,OAAO,CAAC,CAAC,EAAE,QAAQA,KAAK,EAAE,KAAK,QAAQ,KAAKA,EAAE,EAAE,EAAE,OAAO,KAAK,WAAW,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,EAAEG,EAAE,EAAE,EAAE,OAAO,KAAK,WAAW,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAEA,EAAE,KAAK,SAAS,YAAY,cAAc,EAAE,IAAI,EAAE,KAAK,SAAS,WAAW,YAAY,EAAE,IAAI,EAAE,QAAQH,GAAG,CAAC,IAAI,EAAE,EAAEA,CAAC,EAAE,KAAK,GAAG,EAAE,EAAE,EAAE,OAAO,KAAK,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,OAAO,KAAK,WAAW,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,SAAS,YAAY,CAAC,UAAU,CAAA,EAAG,YAAY,CAAA,CAAE,EAAE,OAAO,EAAE,QAAQ,GAAG,CAAC,IAAIG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAGA,EAAE,MAAM,KAAK,SAAS,OAAOA,EAAE,OAAO,GAAG,EAAE,aAAa,EAAE,WAAW,QAAQ,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM,IAAI,MAAM,yBAAyB,EAAE,GAAG,aAAa,EAAE,CAAC,IAAIH,EAAE,EAAE,UAAU,EAAE,IAAI,EAAEA,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,IAAIC,EAAE,EAAE,SAAS,MAAM,KAAK,CAAC,EAAE,OAAOA,IAAI,KAAKA,EAAED,EAAE,MAAM,KAAK,CAAC,GAAGC,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC,GAAG,cAAc,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,QAAQ,SAAS,EAAE,QAAQ,SAAS,MAAM,IAAI,MAAM,6CAA6C,EAAE,IAAID,EAAE,EAAE,EAAE,KAAK,EAAEA,EAAEA,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,QAAQ,QAAQ,EAAE,WAAW,EAAE,WAAW,KAAK,EAAE,KAAK,EAAE,EAAE,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,WAAW,EAAE,YAAY,EAAE,YAAY,KAAK,EAAE,KAAK,EAAE,EAAE,YAAY,CAAC,EAAE,KAAK,GAAG,CAAC,gBAAgB,GAAG,EAAE,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,YAAY,CAAC,EAAEG,EAAE,WAAW,GAAG,EAAE,SAAS,CAAC,IAAI,EAAE,KAAK,SAAS,UAAU,IAAIwB,GAAE,KAAK,QAAQ,EAAE,QAAQ3B,KAAK,EAAE,SAAS,CAAC,GAAG,EAAEA,KAAK,GAAG,MAAM,IAAI,MAAM,aAAaA,CAAC,kBAAkB,EAAE,GAAG,CAAC,UAAU,QAAQ,EAAE,SAASA,CAAC,EAAE,SAAS,IAAI,EAAEA,EAAEC,EAAE,EAAE,SAAS,CAAC,EAAES,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,IAAI,CAAC,IAAIH,EAAEN,EAAE,MAAM,EAAE,CAAC,EAAE,OAAOM,IAAI,KAAKA,EAAEG,EAAE,MAAM,EAAE,CAAC,GAAGH,GAAG,EAAE,CAAC,CAACJ,EAAE,SAAS,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,SAAS,WAAW,IAAIc,GAAE,KAAK,QAAQ,EAAE,QAAQjB,KAAK,EAAE,UAAU,CAAC,GAAG,EAAEA,KAAK,GAAG,MAAM,IAAI,MAAM,cAAcA,CAAC,kBAAkB,EAAE,GAAG,CAAC,UAAU,QAAQ,OAAO,EAAE,SAASA,CAAC,EAAE,SAAS,IAAI,EAAEA,EAAEC,EAAE,EAAE,UAAU,CAAC,EAAES,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,IAAI,CAAC,IAAIH,EAAEN,EAAE,MAAM,EAAE,CAAC,EAAE,OAAOM,IAAI,KAAKA,EAAEG,EAAE,MAAM,EAAE,CAAC,GAAGH,CAAC,CAAC,CAACJ,EAAE,UAAU,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,SAAS,OAAO,IAAIG,GAAE,QAAQN,KAAK,EAAE,MAAM,CAAC,GAAG,EAAEA,KAAK,GAAG,MAAM,IAAI,MAAM,SAASA,CAAC,kBAAkB,EAAE,GAAG,CAAC,UAAU,OAAO,EAAE,SAASA,CAAC,EAAE,SAAS,IAAI,EAAEA,EAAEC,EAAE,EAAE,MAAM,CAAC,EAAES,EAAE,EAAE,CAAC,EAAEJ,GAAE,iBAAiB,IAAIN,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,SAAS,OAAOM,GAAE,6BAA6B,IAAIN,CAAC,EAAE,OAAO,SAAS,CAAC,IAAIqB,EAAE,MAAMpB,EAAE,KAAK,EAAE,CAAC,EAAE,OAAOS,EAAE,KAAK,EAAEW,CAAC,CAAC,GAAC,EAAI,IAAId,EAAEN,EAAE,KAAK,EAAE,CAAC,EAAE,OAAOS,EAAE,KAAK,EAAEH,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,MAAM,OAAO,SAAS,CAAC,IAAIc,EAAE,MAAMpB,EAAE,MAAM,EAAE,CAAC,EAAE,OAAOoB,IAAI,KAAKA,EAAE,MAAMX,EAAE,MAAM,EAAE,CAAC,GAAGW,CAAC,GAAC,EAAI,IAAId,EAAEN,EAAE,MAAM,EAAE,CAAC,EAAE,OAAOM,IAAI,KAAKA,EAAEG,EAAE,MAAM,EAAE,CAAC,GAAGH,CAAC,CAAC,CAACJ,EAAE,MAAM,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,EAAE,KAAK,SAAS,WAAWH,EAAE,EAAE,WAAWG,EAAE,WAAW,SAAS,EAAE,CAAC,IAAIF,EAAE,CAAA,EAAG,OAAOA,EAAE,KAAKD,EAAE,KAAK,KAAK,CAAC,CAAC,EAAE,IAAIC,EAAEA,EAAE,OAAO,EAAE,KAAK,KAAK,CAAC,CAAC,GAAGA,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,GAAG,KAAK,SAAS,GAAGE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,KAAK,SAAS,CAAC,GAAG,KAAK,SAAS,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,OAAOoB,GAAE,IAAI,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAOR,GAAE,MAAM,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC,cAAc,EAAE,CAAC,MAAM,CAACX,EAAED,IAAI,CAAC,IAAIE,EAAE,CAAC,GAAGF,CAAC,EAAEH,EAAE,CAAC,GAAG,KAAK,SAAS,GAAGK,CAAC,EAAEI,EAAE,KAAK,QAAQ,CAAC,CAACT,EAAE,OAAO,CAAC,CAACA,EAAE,KAAK,EAAE,GAAG,KAAK,SAAS,QAAQ,IAAIK,EAAE,QAAQ,GAAG,OAAOI,EAAE,IAAI,MAAM,oIAAoI,CAAC,EAAE,GAAG,OAAOL,EAAE,KAAKA,IAAI,KAAK,OAAOK,EAAE,IAAI,MAAM,gDAAgD,CAAC,EAAE,GAAG,OAAOL,GAAG,SAAS,OAAOK,EAAE,IAAI,MAAM,wCAAwC,OAAO,UAAU,SAAS,KAAKL,CAAC,EAAE,mBAAmB,CAAC,EAAE,GAAGJ,EAAE,QAAQA,EAAE,MAAM,QAAQA,EAAEA,EAAE,MAAM,MAAM,GAAGA,EAAE,MAAM,OAAO,SAAS,CAAC,IAAIC,EAAED,EAAE,MAAM,MAAMA,EAAE,MAAM,WAAWI,CAAC,EAAEA,EAAEO,EAAE,MAAMX,EAAE,MAAM,MAAMA,EAAE,MAAM,aAAY,EAAG,EAAEuB,GAAE,IAAIA,GAAE,WAAWtB,EAAED,CAAC,EAAEO,EAAEP,EAAE,MAAM,MAAMA,EAAE,MAAM,iBAAiBW,CAAC,EAAEA,EAAEX,EAAE,YAAY,MAAM,QAAQ,IAAI,KAAK,WAAWO,EAAEP,EAAE,UAAU,CAAC,EAAE,IAAIQ,EAAE,MAAMR,EAAE,MAAM,MAAMA,EAAE,MAAM,gBAAgB,EAAEe,GAAE,MAAMA,GAAE,aAAaR,EAAEP,CAAC,EAAE,OAAOA,EAAE,MAAM,MAAMA,EAAE,MAAM,YAAYQ,CAAC,EAAEA,CAAC,KAAK,MAAMC,CAAC,EAAE,GAAG,CAACT,EAAE,QAAQI,EAAEJ,EAAE,MAAM,WAAWI,CAAC,GAAG,IAAIM,GAAGV,EAAE,MAAMA,EAAE,MAAM,eAAe,EAAEuB,GAAE,IAAIA,GAAE,WAAWnB,EAAEJ,CAAC,EAAEA,EAAE,QAAQU,EAAEV,EAAE,MAAM,iBAAiBU,CAAC,GAAGV,EAAE,YAAY,KAAK,WAAWU,EAAEV,EAAE,UAAU,EAAE,IAAI,GAAGA,EAAE,MAAMA,EAAE,MAAM,cAAa,EAAG,EAAEe,GAAE,MAAMA,GAAE,aAAaL,EAAEV,CAAC,EAAE,OAAOA,EAAE,QAAQ,EAAEA,EAAE,MAAM,YAAY,CAAC,GAAG,CAAC,OAAOC,EAAE,CAAC,OAAOQ,EAAER,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,GAAG,CAAC,GAAG,EAAE,SAAS;AAAA,2DAC5iQ,EAAE,CAAC,IAAIE,EAAE,iCAAiCka,GAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,SAAS,OAAO,EAAE,QAAQ,QAAQla,CAAC,EAAEA,CAAC,CAAC,GAAG,EAAE,OAAO,QAAQ,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAMgB,GAAE,IAAIuB,GAAE,SAAS9B,EAAEC,EAAEd,EAAE,CAAC,OAAOoB,GAAE,MAAMN,EAAEd,CAAC,CAAC,CAACa,EAAE,QAAQA,EAAE,WAAW,SAASC,EAAE,CAAC,OAAOM,GAAE,WAAWN,CAAC,EAAED,EAAE,SAASO,GAAE,SAASmB,GAAE1B,EAAE,QAAQ,EAAEA,CAAC,EAAEA,EAAE,YAAYoB,GAAEpB,EAAE,SAAS+T,GAAE/T,EAAE,IAAI,YAAYC,EAAE,CAAC,OAAOM,GAAE,IAAI,GAAGN,CAAC,EAAED,EAAE,SAASO,GAAE,SAASmB,GAAE1B,EAAE,QAAQ,EAAEA,CAAC,EAAEA,EAAE,WAAW,SAASC,EAAEd,EAAE,CAAC,OAAOoB,GAAE,WAAWN,EAAEd,CAAC,CAAC,EAAEa,EAAE,YAAYO,GAAE,YAAYP,EAAE,OAAOG,GAAEH,EAAE,OAAOG,GAAE,MAAMH,EAAE,SAASe,GAAEf,EAAE,aAAaU,GAAEV,EAAE,MAAMW,GAAEX,EAAE,MAAMW,GAAE,IAAIX,EAAE,UAAUK,GAAEL,EAAE,MAAMN,GAAEM,EAAE,MAAMA,EAASA,EAAE,QAAWA,EAAE,WAAcA,EAAE,IAAOA,EAAE,WAAcA,EAAE,YAAoBG,GAAE,MAASQ,GAAE,IClE1uB6zB,EAAO,WAAW,CAChB,IAAK,GACL,OAAQ,GACR,OAAQ,EACV,CAAC,EAED,MAAMC,GAAc,CAClB,IACA,IACA,aACA,KACA,OACA,MACA,KACA,KACA,KACA,KACA,KACA,KACA,IACA,KACA,KACA,IACA,MACA,SACA,QACA,QACA,KACA,KACA,QACA,KACA,IACF,EAEMC,GAAe,CAAC,QAAS,OAAQ,MAAO,SAAU,QAAS,OAAO,EAExE,IAAIC,GAAiB,GACrB,MAAMC,GAAsB,KACtBC,GAAuB,IAE7B,SAASC,IAAe,CAClBH,KACJA,GAAiB,GAEjB7L,GAAU,QAAQ,0BAA4BsF,GAAS,CACjD,EAAEA,aAAgB,oBAElB,CADSA,EAAK,aAAa,MAAM,IAErCA,EAAK,aAAa,MAAO,qBAAqB,EAC9CA,EAAK,aAAa,SAAU,QAAQ,EACtC,CAAC,EACH,CAEO,SAAS2G,GAAwBC,EAA0B,CAChE,MAAMxyB,EAAQwyB,EAAS,KAAA,EACvB,GAAI,CAACxyB,EAAO,MAAO,GACnBsyB,GAAA,EACA,MAAM/qB,EAAYvE,GAAahD,EAAOoyB,EAAmB,EACnDrM,EAASxe,EAAU,UACrB;AAAA;AAAA,eAAoBA,EAAU,KAAK,yBAAyBA,EAAU,KAAK,MAAM,KACjF,GACJ,GAAIA,EAAU,KAAK,OAAS8qB,GAAsB,CAEhD,MAAM1N,EAAO,2BADG8N,GAAW,GAAGlrB,EAAU,IAAI,GAAGwe,CAAM,EAAE,CACR,SAC/C,OAAOO,GAAU,SAAS3B,EAAM,CAC9B,aAAcsN,GACd,aAAcC,EAAA,CACf,CACH,CACA,MAAMQ,EAAWV,EAAO,MAAM,GAAGzqB,EAAU,IAAI,GAAGwe,CAAM,EAAE,EAC1D,OAAOO,GAAU,SAASoM,EAAU,CAClC,aAAcT,GACd,aAAcC,EAAA,CACf,CACH,CAEA,SAASO,GAAW7yB,EAAuB,CACzC,OAAOA,EACJ,QAAQ,KAAM,OAAO,EACrB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,QAAQ,EACtB,QAAQ,KAAM,OAAO,CAC1B,CCrFO,SAAS+yB,GAAgBC,EAAcC,EAAmC,CAC/E,OAAOlO,gBAAmBkO,CAAS,uBAAuBD,CAAI,SAChE,CAEO,SAASE,GAAajqB,EAA4B+pB,EAAoB,CACtE/pB,IACLA,EAAO,YAAc+pB,EACvB,CCNA,MAAMG,GAAgB,KAChBC,GAAe,IACfC,GAAa,mBACbC,GAAe,SACfC,GAAc,cACdC,GAAY,KACZC,GAAc,IACdC,GAAa,IAOnB,eAAeC,GAAoBnvB,EAAgC,CACjE,GAAI,CAACA,EAAM,MAAO,GAElB,GAAI,CACF,aAAM,UAAU,UAAU,UAAUA,CAAI,EACjC,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAEA,SAASovB,GAAeC,EAA2BvvB,EAAe,CAChEuvB,EAAO,MAAQvvB,EACfuvB,EAAO,aAAa,aAAcvvB,CAAK,CACzC,CAEA,SAASwvB,GAAiBvY,EAA4C,CACpE,MAAMwY,EAAYxY,EAAQ,OAAS8X,GACnC,OAAOtO;AAAAA;AAAAA;AAAAA;AAAAA,cAIKgP,CAAS;AAAA,mBACJA,CAAS;AAAA,eACb,MAAOh3B,GAAa,CAC3B,MAAMi3B,EAAMj3B,EAAE,cACRi2B,EAAOgB,GAAK,cAChB,sBAAA,EAGF,GAAI,CAACA,GAAOA,EAAI,QAAQ,UAAY,IAAK,OAEzCA,EAAI,QAAQ,QAAU,IACtBA,EAAI,aAAa,YAAa,MAAM,EACpCA,EAAI,SAAW,GAEf,MAAMC,EAAS,MAAMN,GAAoBpY,EAAQ,MAAM,EACvD,GAAKyY,EAAI,YAMT,IAJA,OAAOA,EAAI,QAAQ,QACnBA,EAAI,gBAAgB,WAAW,EAC/BA,EAAI,SAAW,GAEX,CAACC,EAAQ,CACXD,EAAI,QAAQ,MAAQ,IACpBJ,GAAeI,EAAKT,EAAW,EAC/BL,GAAaF,EAAMU,EAAU,EAE7B,OAAO,WAAW,IAAM,CACjBM,EAAI,cACT,OAAOA,EAAI,QAAQ,MACnBJ,GAAeI,EAAKD,CAAS,EAC7Bb,GAAaF,EAAMQ,EAAS,EAC9B,EAAGJ,EAAY,EACf,MACF,CAEAY,EAAI,QAAQ,OAAS,IACrBJ,GAAeI,EAAKV,EAAY,EAChCJ,GAAaF,EAAMS,EAAW,EAE9B,OAAO,WAAW,IAAM,CACjBO,EAAI,cACT,OAAOA,EAAI,QAAQ,OACnBJ,GAAeI,EAAKD,CAAS,EAC7Bb,GAAaF,EAAMQ,EAAS,EAC9B,EAAGL,EAAa,EAClB,CAAC;AAAA;AAAA,QAECJ,GAAgBS,GAAW,qBAAqB,CAAC;AAAA;AAAA,GAGzD,CAEO,SAASU,GAA2BtB,EAAkC,CAC3E,OAAOkB,GAAiB,CAAE,KAAM,IAAMlB,EAAU,MAAOS,GAAY,CACrE,4vLC/DMc,GAAsBC,GACtBC,GAAWF,GAAoB,UAAY,CAAE,MAAO,IAAA,EACpDG,GAAWH,GAAoB,OAAS,CAAA,EAE9C,SAASI,GAAkBl0B,EAAuB,CAChD,OAAQA,GAAQ,QAAQ,KAAA,CAC1B,CAEA,SAASm0B,GAAan0B,EAAsB,CAC1C,MAAM2E,EAAU3E,EAAK,QAAQ,KAAM,GAAG,EAAE,KAAA,EACxC,OAAK2E,EACEA,EACJ,MAAM,KAAK,EACX,IAAKwC,GACJA,EAAK,QAAU,GAAKA,EAAK,YAAA,IAAkBA,EACvCA,EACA,GAAGA,EAAK,GAAG,CAAC,GAAG,YAAA,GAAiB,EAAE,GAAGA,EAAK,MAAM,CAAC,CAAC,EAAA,EAEvD,KAAK,GAAG,EARU,MASvB,CAEA,SAASitB,GAAcz0B,EAAoC,CACzD,MAAME,EAAUF,GAAO,KAAA,EACvB,GAAKE,EACL,OAAOA,EAAQ,QAAQ,KAAM,GAAG,CAClC,CAEA,SAASw0B,GAAmB10B,EAAoC,CAC9D,GAAIA,GAAU,KACd,IAAI,OAAOA,GAAU,SAAU,CAC7B,MAAME,EAAUF,EAAM,KAAA,EACtB,GAAI,CAACE,EAAS,OACd,MAAMy0B,EAAYz0B,EAAQ,MAAM,OAAO,EAAE,CAAC,GAAG,QAAU,GACvD,OAAKy0B,EACEA,EAAU,OAAS,IAAM,GAAGA,EAAU,MAAM,EAAG,GAAG,CAAC,IAAMA,EADhD,MAElB,CACA,GAAI,OAAO30B,GAAU,UAAY,OAAOA,GAAU,UAChD,OAAO,OAAOA,CAAK,EAErB,GAAI,MAAM,QAAQA,CAAK,EAAG,CACxB,MAAMiD,EAASjD,EACZ,IAAK6E,GAAS6vB,GAAmB7vB,CAAI,CAAC,EACtC,OAAQA,GAAyB,EAAQA,CAAK,EACjD,GAAI5B,EAAO,SAAW,EAAG,OACzB,MAAM2xB,EAAU3xB,EAAO,MAAM,EAAG,CAAC,EAAE,KAAK,IAAI,EAC5C,OAAOA,EAAO,OAAS,EAAI,GAAG2xB,CAAO,IAAMA,CAC7C,EAEF,CAEA,SAASC,GAAkBlsB,EAAenH,EAAuB,CAC/D,GAAI,CAACmH,GAAQ,OAAOA,GAAS,SAAU,OACvC,IAAIlC,EAAmBkC,EACvB,UAAWmsB,KAAWtzB,EAAK,MAAM,GAAG,EAAG,CAErC,GADI,CAACszB,GACD,CAACruB,GAAW,OAAOA,GAAY,SAAU,OAE7CA,EADeA,EACEquB,CAAO,CAC1B,CACA,OAAOruB,CACT,CAEA,SAASsuB,GAAsBpsB,EAAeqsB,EAAoC,CAChF,UAAWjuB,KAAOiuB,EAAM,CACtB,MAAMh1B,EAAQ60B,GAAkBlsB,EAAM5B,CAAG,EACnCkuB,EAAUP,GAAmB10B,CAAK,EACxC,GAAIi1B,EAAS,OAAOA,CACtB,CAEF,CAEA,SAASC,GAAkBvsB,EAAmC,CAC5D,GAAI,CAACA,GAAQ,OAAOA,GAAS,SAAU,OACvC,MAAMrB,EAASqB,EACTnH,EAAO,OAAO8F,EAAO,MAAS,SAAWA,EAAO,KAAO,OAC7D,GAAI,CAAC9F,EAAM,OACX,MAAM2zB,EAAS,OAAO7tB,EAAO,QAAW,SAAWA,EAAO,OAAS,OAC7DT,EAAQ,OAAOS,EAAO,OAAU,SAAWA,EAAO,MAAQ,OAChE,OAAI6tB,IAAW,QAAatuB,IAAU,OAC7B,GAAGrF,CAAI,IAAI2zB,CAAM,IAAIA,EAAStuB,CAAK,GAErCrF,CACT,CAEA,SAAS4zB,GAAmBzsB,EAAmC,CAC7D,GAAI,CAACA,GAAQ,OAAOA,GAAS,SAAU,OACvC,MAAMrB,EAASqB,EAEf,OADa,OAAOrB,EAAO,MAAS,SAAWA,EAAO,KAAO,MAE/D,CAEA,SAAS+tB,GACPC,EACAC,EACmC,CACnC,GAAI,GAACD,GAAQ,CAACC,GACd,OAAOD,EAAK,UAAUC,CAAM,GAAK,MACnC,CAEO,SAASC,GAAmB7uB,EAInB,CACd,MAAMtG,EAAOk0B,GAAkB5tB,EAAO,IAAI,EACpCI,EAAM1G,EAAK,YAAA,EACXi1B,EAAOhB,GAASvtB,CAAG,EACnB0uB,EAAQH,GAAM,OAASjB,GAAS,OAAS,KACzCplB,EAAQqmB,GAAM,OAASd,GAAan0B,CAAI,EACxCiE,EAAQgxB,GAAM,OAASj1B,EACvBq1B,EACJ/uB,EAAO,MAAQ,OAAOA,EAAO,MAAS,SAChCA,EAAO,KAAiC,OAC1C,OACA4uB,EAAS,OAAOG,GAAc,SAAWA,EAAU,OAAS,OAC5DC,EAAaN,GAAkBC,EAAMC,CAAM,EAC3CK,EAAOnB,GAAckB,GAAY,OAASJ,CAAM,EAEtD,IAAIM,EACA9uB,IAAQ,SAAQ8uB,EAASX,GAAkBvuB,EAAO,IAAI,GACtD,CAACkvB,IAAW9uB,IAAQ,SAAWA,IAAQ,QAAUA,IAAQ,YAC3D8uB,EAAST,GAAmBzuB,EAAO,IAAI,GAGzC,MAAMmvB,EACJH,GAAY,YAAcL,GAAM,YAAcjB,GAAS,YAAc,CAAA,EACvE,MAAI,CAACwB,GAAUC,EAAW,OAAS,IACjCD,EAASd,GAAsBpuB,EAAO,KAAMmvB,CAAU,GAGpD,CAACD,GAAUlvB,EAAO,OACpBkvB,EAASlvB,EAAO,MAGdkvB,IACFA,EAASE,GAAoBF,CAAM,GAG9B,CACL,KAAAx1B,EACA,MAAAo1B,EACA,MAAAxmB,EACA,MAAA3K,EACA,KAAAsxB,EACA,OAAAC,CAAA,CAEJ,CAEO,SAASG,GAAiBf,EAA0C,CACzE,MAAMh0B,EAAkB,CAAA,EAGxB,GAFIg0B,EAAQ,MAAMh0B,EAAM,KAAKg0B,EAAQ,IAAI,EACrCA,EAAQ,QAAQh0B,EAAM,KAAKg0B,EAAQ,MAAM,EACzCh0B,EAAM,SAAW,EACrB,OAAOA,EAAM,KAAK,KAAK,CACzB,CASA,SAAS80B,GAAoB31B,EAAuB,CAClD,OAAKA,GACEA,EACJ,QAAQ,kBAAmB,GAAG,EAC9B,QAAQ,iBAAkB,GAAG,CAClC,CCjMO,MAAM61B,GAAwB,GAGxBC,GAAoB,EAGpBC,GAAoB,ICD1B,SAASC,GAA2B5xB,EAAsB,CAC/D,MAAMtE,EAAUsE,EAAK,KAAA,EAErB,GAAItE,EAAQ,WAAW,GAAG,GAAKA,EAAQ,WAAW,GAAG,EACnD,GAAI,CACF,MAAMU,EAAS,KAAK,MAAMV,CAAO,EACjC,MAAO,YAAc,KAAK,UAAUU,EAAQ,KAAM,CAAC,EAAI,OACzD,MAAQ,CAER,CAEF,OAAO4D,CACT,CAMO,SAAS6xB,GAAoB7xB,EAAsB,CACxD,MAAM8xB,EAAW9xB,EAAK,MAAM;AAAA,CAAI,EAC1Ba,EAAQixB,EAAS,MAAM,EAAGJ,EAAiB,EAC3CtB,EAAUvvB,EAAM,KAAK;AAAA,CAAI,EAC/B,OAAIuvB,EAAQ,OAASuB,GACZvB,EAAQ,MAAM,EAAGuB,EAAiB,EAAI,IAExC9wB,EAAM,OAASixB,EAAS,OAAS1B,EAAU,IAAMA,CAC1D,CCxBO,SAAS2B,GAAiB7xB,EAA8B,CAC7D,MAAMtG,EAAIsG,EACJE,EAAU4xB,GAAiBp4B,EAAE,OAAO,EACpCq4B,EAAoB,CAAA,EAE1B,UAAW5xB,KAAQD,EAAS,CAC1B,MAAM8xB,EAAO,OAAO7xB,EAAK,MAAQ,EAAE,EAAE,YAAA,GAEnC,CAAC,WAAY,YAAa,UAAW,UAAU,EAAE,SAAS6xB,CAAI,GAC7D,OAAO7xB,EAAK,MAAS,UAAYA,EAAK,WAAa,OAEpD4xB,EAAM,KAAK,CACT,KAAM,OACN,KAAO5xB,EAAK,MAAmB,OAC/B,KAAM8xB,GAAW9xB,EAAK,WAAaA,EAAK,IAAI,CAAA,CAC7C,CAEL,CAEA,UAAWA,KAAQD,EAAS,CAC1B,MAAM8xB,EAAO,OAAO7xB,EAAK,MAAQ,EAAE,EAAE,YAAA,EACrC,GAAI6xB,IAAS,cAAgBA,IAAS,cAAe,SACrD,MAAMlyB,EAAOoyB,GAAgB/xB,CAAI,EAC3BxE,EAAO,OAAOwE,EAAK,MAAS,SAAWA,EAAK,KAAO,OACzD4xB,EAAM,KAAK,CAAE,KAAM,SAAU,KAAAp2B,EAAM,KAAAmE,EAAM,CAC3C,CAEA,GACE6c,GAAoB3c,CAAO,GAC3B,CAAC+xB,EAAM,KAAMI,GAASA,EAAK,OAAS,QAAQ,EAC5C,CACA,MAAMx2B,EACH,OAAOjC,EAAE,UAAa,UAAYA,EAAE,UACpC,OAAOA,EAAE,WAAc,UAAYA,EAAE,WACtC,OACIoG,EAAOC,GAAYC,CAAO,GAAK,OACrC+xB,EAAM,KAAK,CAAE,KAAM,SAAU,KAAAp2B,EAAM,KAAAmE,EAAM,CAC3C,CAEA,OAAOiyB,CACT,CAEO,SAASK,GACdD,EACAE,EACA,CACA,MAAM9B,EAAUO,GAAmB,CAAE,KAAMqB,EAAK,KAAM,KAAMA,EAAK,KAAM,EACjEhB,EAASG,GAAiBf,CAAO,EACjC+B,EAAU,EAAQH,EAAK,MAAM,OAE7BI,EAAW,EAAQF,EACnBG,EAAcD,EAChB,IAAM,CACJ,GAAID,EAAS,CACXD,EAAeX,GAA2BS,EAAK,IAAK,CAAC,EACrD,MACF,CACA,MAAMM,EAAO,MAAMlC,EAAQ,KAAK;AAAA;AAAA,EAC9BY,EAAS,kBAAkBA,CAAM;AAAA;AAAA,EAAW,EAC9C,6CACAkB,EAAeI,CAAI,CACrB,EACA,OAEEC,EAAUJ,IAAYH,EAAK,MAAM,QAAU,IAAMZ,GACjDoB,EAAgBL,GAAW,CAACI,EAC5BE,EAAaN,GAAWI,EACxBG,EAAU,CAACP,EAEjB,OAAOjS;AAAAA;AAAAA,8BAEqBkS,EAAW,4BAA8B,EAAE;AAAA,eAC1DC,CAAW;AAAA,aACbD,EAAW,SAAWO,CAAO;AAAA,iBACzBP,EAAW,IAAMO,CAAO;AAAA,iBACxBP,EACNl6B,GAAqB,CAChBA,EAAE,MAAQ,SAAWA,EAAE,MAAQ,MACnCA,EAAE,eAAA,EACFm6B,IAAA,EACF,EACAM,CAAO;AAAA;AAAA;AAAA;AAAA,+CAI8BvC,EAAQ,KAAK;AAAA,kBAC1CA,EAAQ,KAAK;AAAA;AAAA,UAErBgC,EACElS,yCAA4CiS,EAAU,SAAW,GAAG,UACpEQ,CAAO;AAAA,UACTD,GAAW,CAACN,EAAWlS,iDAAsDyS,CAAO;AAAA;AAAA,QAEtF3B,EACE9Q,wCAA2C8Q,CAAM,SACjD2B,CAAO;AAAA,QACTD,EACExS,kEACAyS,CAAO;AAAA,QACTH,EACEtS,8CAAiDsR,GAAoBQ,EAAK,IAAK,CAAC,SAChFW,CAAO;AAAA,QACTF,EACEvS,6CAAgD8R,EAAK,IAAI,SACzDW,CAAO;AAAA;AAAA,GAGjB,CAEA,SAAShB,GAAiB5xB,EAAkD,CAC1E,OAAK,MAAM,QAAQA,CAAO,EACnBA,EAAQ,OAAO,OAAO,EADO,CAAA,CAEtC,CAEA,SAAS+xB,GAAW32B,EAAyB,CAC3C,GAAI,OAAOA,GAAU,SAAU,OAAOA,EACtC,MAAME,EAAUF,EAAM,KAAA,EAEtB,GADI,CAACE,GACD,CAACA,EAAQ,WAAW,GAAG,GAAK,CAACA,EAAQ,WAAW,GAAG,EAAG,OAAOF,EACjE,GAAI,CACF,OAAO,KAAK,MAAME,CAAO,CAC3B,MAAQ,CACN,OAAOF,CACT,CACF,CAEA,SAAS42B,GAAgB/xB,EAAmD,CAC1E,GAAI,OAAOA,EAAK,MAAS,gBAAiBA,EAAK,KAC/C,GAAI,OAAOA,EAAK,SAAY,gBAAiBA,EAAK,OAEpD,CC/HO,SAAS4yB,GAA4BC,EAA+B,CACzE,OAAO3S;AAAAA;AAAAA,QAED4S,GAAa,YAAaD,CAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAU5C,CAEO,SAASE,GACdpzB,EACAqzB,EACAd,EACAW,EACA,CACA,MAAMxW,EAAY,IAAI,KAAK2W,CAAS,EAAE,mBAAmB,CAAA,EAAI,CAC3D,KAAM,UACN,OAAQ,SAAA,CACT,EACKx3B,EAAOq3B,GAAW,MAAQ,YAEhC,OAAO3S;AAAAA;AAAAA,QAED4S,GAAa,YAAaD,CAAS,CAAC;AAAA;AAAA,UAElCI,GACA,CACE,KAAM,YACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAAtzB,EAAM,CAElC,EACA,CAAE,YAAa,GAAM,cAAe,EAAA,EACpCuyB,CAAA,CACD;AAAA;AAAA,2CAEkC12B,CAAI;AAAA,+CACA6gB,CAAS;AAAA;AAAA;AAAA;AAAA,GAKxD,CAEO,SAAS6W,GACdC,EACA9pB,EAMA,CACA,MAAM+pB,EAAiB9W,GAAyB6W,EAAM,IAAI,EACpDE,EAAgBhqB,EAAK,eAAiB,YACtCiqB,EACJF,IAAmB,OACf,MACAA,IAAmB,YACjBC,EACAD,EACFG,EACJH,IAAmB,OACf,OACAA,IAAmB,YACjB,YACA,QACF/W,EAAY,IAAI,KAAK8W,EAAM,SAAS,EAAE,mBAAmB,GAAI,CACjE,KAAM,UACN,OAAQ,SAAA,CACT,EAED,OAAOjT;AAAAA,6BACoBqT,CAAS;AAAA,QAC9BT,GAAaK,EAAM,KAAM,CACzB,KAAME,EACN,OAAQhqB,EAAK,iBAAmB,IAAA,CACjC,CAAC;AAAA;AAAA,UAEE8pB,EAAM,SAAS,IAAI,CAACnzB,EAAMmf,IAC1B8T,GACEjzB,EAAK,QACL,CACE,YACEmzB,EAAM,aAAehU,IAAUgU,EAAM,SAAS,OAAS,EACzD,cAAe9pB,EAAK,aAAA,EAEtBA,EAAK,aAAA,CACP,CACD;AAAA;AAAA,2CAEkCiqB,CAAG;AAAA,+CACCjX,CAAS;AAAA;AAAA;AAAA;AAAA,GAKxD,CAEA,SAASyW,GACPhzB,EACA+yB,EACA,CACA,MAAM71B,EAAasf,GAAyBxc,CAAI,EAC1CuzB,EAAgBR,GAAW,MAAM,KAAA,GAAU,YAC3CW,EAAkBX,GAAW,QAAQ,KAAA,GAAU,GAC/CY,EACJz2B,IAAe,OACX,IACAA,IAAe,YACbq2B,EAAc,OAAO,CAAC,EAAE,eAAiB,IACzCr2B,IAAe,OACb,IACA,IACJoxB,EACJpxB,IAAe,OACX,OACAA,IAAe,YACb,YACFA,IAAe,OACX,OACA,QAEV,OAAIw2B,GAAmBx2B,IAAe,YAChC02B,GAAYF,CAAe,EACtBtT;AAAAA,6BACgBkO,CAAS;AAAA,eACvBoF,CAAe;AAAA,eACfH,CAAa;AAAA,UAGjBnT,4BAA+BkO,CAAS,KAAKoF,CAAe,SAG9DtT,4BAA+BkO,CAAS,KAAKqF,CAAO,QAC7D,CAEA,SAASC,GAAYv4B,EAAwB,CAC3C,MACE,gBAAgB,KAAKA,CAAK,GAC1B,iBAAiB,KAAKA,CAAK,CAE/B,CAEA,SAAS83B,GACPpzB,EACAwJ,EACA6oB,EACA,CACA,MAAM34B,EAAIsG,EACJC,EAAO,OAAOvG,EAAE,MAAS,SAAWA,EAAE,KAAO,UAC7Co6B,EACJnX,GAAoB3c,CAAO,GAC3BC,EAAK,YAAA,IAAkB,cACvBA,EAAK,YAAA,IAAkB,eACvB,OAAOvG,EAAE,YAAe,UACxB,OAAOA,EAAE,cAAiB,SAEtBq6B,EAAYlC,GAAiB7xB,CAAO,EACpCg0B,EAAeD,EAAU,OAAS,EAElCE,EAAgBl0B,GAAYC,CAAO,EACnCk0B,EACJ1qB,EAAK,eAAiBvJ,IAAS,YAAcI,GAAgBL,CAAO,EAAI,KACpEm0B,EAAeF,GAAe,KAAA,EAASA,EAAgB,KACvDG,EAAoBF,EACtBxzB,GAAwBwzB,CAAiB,EACzC,KACEhG,EAAWiG,EACXE,EAAkBp0B,IAAS,aAAe,EAAQiuB,GAAU,OAE5DoG,EAAgB,CACpB,cACAD,EAAkB,WAAa,GAC/B7qB,EAAK,YAAc,YAAc,GACjC,SAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG,EAEX,MAAI,CAAC0kB,GAAY8F,GAAgBF,EACxBzT,IAAO0T,EAAU,IAAK5B,GAC3BC,GAAsBD,EAAME,CAAa,CAAA,CAC1C,GAGC,CAACnE,GAAY,CAAC8F,EAAqBlB,EAEhCzS;AAAAA,kBACSiU,CAAa;AAAA,QACvBD,EAAkB7E,GAA2BtB,CAAS,EAAI4E,CAAO;AAAA,QACjEsB,EACE/T,+BAAkCkU,GAChCtG,GAAwBmG,CAAiB,CAAA,CAC1C,SACDtB,CAAO;AAAA,QACT5E,EACE7N,2BAA8BkU,GAAWtG,GAAwBC,CAAQ,CAAC,CAAC,SAC3E4E,CAAO;AAAA,QACTiB,EAAU,IAAK5B,GAASC,GAAsBD,EAAME,CAAa,CAAC,CAAC;AAAA;AAAA,GAG3E,CClNO,SAASmC,GAAsBC,EAA6B,CACjE,OAAOpU;AAAAA;AAAAA;AAAAA;AAAAA,yBAIgBoU,EAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,UAK5BA,EAAM,MACJpU;AAAAA,4CACgCoU,EAAM,KAAK;AAAA,+BACxBA,EAAM,aAAa;AAAA;AAAA;AAAA,cAItCA,EAAM,QACJpU,kCAAqCkU,GAAWtG,GAAwBwG,EAAM,OAAO,CAAC,CAAC,SACvFpU,gDAAmD;AAAA;AAAA;AAAA,GAIjE,sMC3BO,IAAMqU,GAAN,cAA+BC,EAAW,CAA1C,aAAA,CAAA,MAAA,GAAA,SAAA,EACuB,KAAA,WAAa,GACb,KAAA,SAAW,GACX,KAAA,SAAW,GAEvC,KAAQ,WAAa,GACrB,KAAQ,OAAS,EACjB,KAAQ,WAAa,EA8CrB,KAAQ,gBAAmB,GAAkB,CAC3C,KAAK,WAAa,GAClB,KAAK,OAAS,EAAE,QAChB,KAAK,WAAa,KAAK,WACvB,KAAK,UAAU,IAAI,UAAU,EAE7B,SAAS,iBAAiB,YAAa,KAAK,eAAe,EAC3D,SAAS,iBAAiB,UAAW,KAAK,aAAa,EAEvD,EAAE,eAAA,CACJ,EAEA,KAAQ,gBAAmB,GAAkB,CAC3C,GAAI,CAAC,KAAK,WAAY,OAEtB,MAAMtwB,EAAY,KAAK,cACvB,GAAI,CAACA,EAAW,OAEhB,MAAMuwB,EAAiBvwB,EAAU,sBAAA,EAAwB,MAEnDwwB,GADS,EAAE,QAAU,KAAK,QACJD,EAE5B,IAAIE,EAAW,KAAK,WAAaD,EACjCC,EAAW,KAAK,IAAI,KAAK,SAAU,KAAK,IAAI,KAAK,SAAUA,CAAQ,CAAC,EAEpE,KAAK,cACH,IAAI,YAAY,SAAU,CACxB,OAAQ,CAAE,WAAYA,CAAA,EACtB,QAAS,GACT,SAAU,EAAA,CACX,CAAA,CAEL,EAEA,KAAQ,cAAgB,IAAM,CAC5B,KAAK,WAAa,GAClB,KAAK,UAAU,OAAO,UAAU,EAEhC,SAAS,oBAAoB,YAAa,KAAK,eAAe,EAC9D,SAAS,oBAAoB,UAAW,KAAK,aAAa,CAC5D,CAAA,CAxDA,QAAS,CACP,OAAOzU,GACT,CAEA,mBAAoB,CAClB,MAAM,kBAAA,EACN,KAAK,iBAAiB,YAAa,KAAK,eAAe,CACzD,CAEA,sBAAuB,CACrB,MAAM,qBAAA,EACN,KAAK,oBAAoB,YAAa,KAAK,eAAe,EAC1D,SAAS,oBAAoB,YAAa,KAAK,eAAe,EAC9D,SAAS,oBAAoB,UAAW,KAAK,aAAa,CAC5D,CA2CF,EA9FaqU,GASJ,OAASK;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,IARYC,GAAA,CAA3BtV,GAAS,CAAE,KAAM,MAAA,CAAQ,CAAA,EADfgV,GACiB,UAAA,aAAA,CAAA,EACAM,GAAA,CAA3BtV,GAAS,CAAE,KAAM,MAAA,CAAQ,CAAA,EAFfgV,GAEiB,UAAA,WAAA,CAAA,EACAM,GAAA,CAA3BtV,GAAS,CAAE,KAAM,MAAA,CAAQ,CAAA,EAHfgV,GAGiB,UAAA,WAAA,CAAA,EAHjBA,GAANM,GAAA,CADNC,GAAc,mBAAmB,CAAA,EACrBP,EAAA,ECqDN,SAASQ,GAAWT,EAAkB,CAC3C,MAAMU,EAAaV,EAAM,UACnBW,EAASX,EAAM,SAAWA,EAAM,SAAW,KAI3CY,EAHgBZ,EAAM,UAAU,UAAU,KAC7Ca,GAAQA,EAAI,MAAQb,EAAM,UAAA,GAES,gBAAkB,MAClDc,EAAgBd,EAAM,cAAgBY,IAAmB,MACzDG,EAAoB,CACxB,KAAMf,EAAM,cACZ,OAAQA,EAAM,iBAAmBA,EAAM,oBAAsB,IAAA,EAGzDgB,EAAqBhB,EAAM,UAC7B,+CACA,4CAEEiB,EAAajB,EAAM,YAAc,GACjCkB,EAAc,GAAQlB,EAAM,aAAeA,EAAM,gBAEvD,OAAOpU;AAAAA;AAAAA,QAEDoU,EAAM,eACJpU,yBAA4BoU,EAAM,cAAc,SAChD3B,CAAO;AAAA;AAAA,QAET2B,EAAM,MACJpU,gCAAmCoU,EAAM,KAAK,SAC9C3B,CAAO;AAAA;AAAA,QAET2B,EAAM,UACJpU;AAAAA;AAAAA;AAAAA;AAAAA,uBAIaoU,EAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAOpC3B,CAAO;AAAA;AAAA;AAAA,sCAGqB6C,EAAc,6BAA+B,EAAE;AAAA;AAAA;AAAA;AAAA,yBAI5DA,EAAc,OAAOD,EAAa,GAAG,IAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAMxDjB,EAAM,YAAY;AAAA;AAAA,cAE1BA,EAAM,QACJpU,0CACAyS,CAAO;AAAA,cACT8C,GAAOC,GAAepB,CAAK,EAAIt0B,GAASA,EAAK,IAAMA,GAC/CA,EAAK,OAAS,oBACT4yB,GAA4ByC,CAAiB,EAGlDr1B,EAAK,OAAS,SACT+yB,GACL/yB,EAAK,KACLA,EAAK,UACLs0B,EAAM,cACNe,CAAA,EAIAr1B,EAAK,OAAS,QACTkzB,GAAmBlzB,EAAM,CAC9B,cAAes0B,EAAM,cACrB,cAAAc,EACA,cAAed,EAAM,cACrB,gBAAiBe,EAAkB,MAAA,CACpC,EAGI1C,CACR,CAAC;AAAA;AAAA;AAAA;AAAA,UAIJ6C,EACEtV;AAAAA;AAAAA,8BAEkBqV,CAAU;AAAA,0BACbr9B,GACTo8B,EAAM,qBAAqBp8B,EAAE,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA,kBAG/Cm8B,GAAsB,CACtB,QAASC,EAAM,gBAAkB,KACjC,MAAOA,EAAM,cAAgB,KAC7B,QAASA,EAAM,eACf,cAAe,IAAM,CACf,CAACA,EAAM,gBAAkB,CAACA,EAAM,eACpCA,EAAM,cAAc;AAAA,EAAWA,EAAM,cAAc;AAAA,OAAU,CAC/D,CAAA,CACD,CAAC;AAAA;AAAA,cAGN3B,CAAO;AAAA;AAAA;AAAA,QAGX2B,EAAM,MAAM,OACVpU;AAAAA;AAAAA,uDAE6CoU,EAAM,MAAM,MAAM;AAAA;AAAA,kBAEvDA,EAAM,MAAM,IACXt0B,GAASkgB;AAAAA;AAAAA,sDAE0BlgB,EAAK,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,iCAK9B,IAAMs0B,EAAM,cAAct0B,EAAK,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAA,CAMlD;AAAA;AAAA;AAAA,YAIP2yB,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAMI2B,EAAM,KAAK;AAAA,wBACR,CAACA,EAAM,SAAS;AAAA,uBAChBp8B,GAAqB,CAC3BA,EAAE,MAAQ,UACVA,EAAE,aAAeA,EAAE,UAAY,KAC/BA,EAAE,UACDo8B,EAAM,YACXp8B,EAAE,eAAA,EACE88B,KAAkB,OAAA,GACxB,CAAC;AAAA,qBACS98B,GACRo8B,EAAM,cAAep8B,EAAE,OAA+B,KAAK,CAAC;AAAA,0BAChDo9B,CAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMpB,CAAChB,EAAM,WAAaA,EAAM,OAAO;AAAA,qBACpCA,EAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMf,CAACA,EAAM,SAAS;AAAA,qBACnBA,EAAM,MAAM;AAAA;AAAA,cAEnBW,EAAS,QAAU,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,GAMvC,CAEA,MAAMU,GAA4B,IAElC,SAASC,GAAcC,EAAmD,CACxE,MAAM72B,EAAyC,CAAA,EAC/C,IAAI82B,EAAoC,KAExC,UAAW91B,KAAQ61B,EAAO,CACxB,GAAI71B,EAAK,OAAS,UAAW,CACvB81B,IACF92B,EAAO,KAAK82B,CAAY,EACxBA,EAAe,MAEjB92B,EAAO,KAAKgB,CAAI,EAChB,QACF,CAEA,MAAMhD,EAAa+e,GAAiB/b,EAAK,OAAO,EAC1CF,EAAOwc,GAAyBtf,EAAW,IAAI,EAC/Cqf,EAAYrf,EAAW,WAAa,KAAK,IAAA,EAE3C,CAAC84B,GAAgBA,EAAa,OAASh2B,GACrCg2B,GAAc92B,EAAO,KAAK82B,CAAY,EAC1CA,EAAe,CACb,KAAM,QACN,IAAK,SAASh2B,CAAI,IAAIE,EAAK,GAAG,GAC9B,KAAAF,EACA,SAAU,CAAC,CAAE,QAASE,EAAK,QAAS,IAAKA,EAAK,IAAK,EACnD,UAAAqc,EACA,YAAa,EAAA,GAGfyZ,EAAa,SAAS,KAAK,CAAE,QAAS91B,EAAK,QAAS,IAAKA,EAAK,IAAK,CAEvE,CAEA,OAAI81B,GAAc92B,EAAO,KAAK82B,CAAY,EACnC92B,CACT,CAEA,SAAS02B,GAAepB,EAAkD,CACxE,MAAMuB,EAAoB,CAAA,EACpBE,EAAU,MAAM,QAAQzB,EAAM,QAAQ,EAAIA,EAAM,SAAW,CAAA,EAC3D0B,EAAQ,MAAM,QAAQ1B,EAAM,YAAY,EAAIA,EAAM,aAAe,CAAA,EACjE2B,EAAe,KAAK,IAAI,EAAGF,EAAQ,OAASJ,EAAyB,EACvEM,EAAe,GACjBJ,EAAM,KAAK,CACT,KAAM,UACN,IAAK,sBACL,QAAS,CACP,KAAM,SACN,QAAS,gBAAgBF,EAAyB,cAAcM,CAAY,YAC5E,UAAW,KAAK,IAAA,CAAI,CACtB,CACD,EAEH,QAASz9B,EAAIy9B,EAAcz9B,EAAIu9B,EAAQ,OAAQv9B,IAAK,CAClD,MAAM8I,EAAMy0B,EAAQv9B,CAAC,EACfwE,EAAa+e,GAAiBza,CAAG,EAEnC,CAACgzB,EAAM,cAAgBt3B,EAAW,KAAK,YAAA,IAAkB,cAI7D64B,EAAM,KAAK,CACT,KAAM,UACN,IAAKK,GAAW50B,EAAK9I,CAAC,EACtB,QAAS8I,CAAA,CACV,CACH,CACA,GAAIgzB,EAAM,aACR,QAAS97B,EAAI,EAAGA,EAAIw9B,EAAM,OAAQx9B,IAChCq9B,EAAM,KAAK,CACT,KAAM,UACN,IAAKK,GAAWF,EAAMx9B,CAAC,EAAGA,EAAIu9B,EAAQ,MAAM,EAC5C,QAASC,EAAMx9B,CAAC,CAAA,CACjB,EAIL,GAAI87B,EAAM,SAAW,KAAM,CACzB,MAAMpyB,EAAM,UAAUoyB,EAAM,UAAU,IAAIA,EAAM,iBAAmB,MAAM,GACrEA,EAAM,OAAO,KAAA,EAAO,OAAS,EAC/BuB,EAAM,KAAK,CACT,KAAM,SACN,IAAA3zB,EACA,KAAMoyB,EAAM,OACZ,UAAWA,EAAM,iBAAmB,KAAK,IAAA,CAAI,CAC9C,EAEDuB,EAAM,KAAK,CAAE,KAAM,oBAAqB,IAAA3zB,EAAK,CAEjD,CAEA,OAAO0zB,GAAcC,CAAK,CAC5B,CAEA,SAASK,GAAWr2B,EAAkBsf,EAAuB,CAC3D,MAAM5lB,EAAIsG,EACJ+D,EAAa,OAAOrK,EAAE,YAAe,SAAWA,EAAE,WAAa,GACrE,GAAIqK,EAAY,MAAO,QAAQA,CAAU,GACzC,MAAMR,EAAK,OAAO7J,EAAE,IAAO,SAAWA,EAAE,GAAK,GAC7C,GAAI6J,EAAI,MAAO,OAAOA,CAAE,GACxB,MAAM+yB,EAAY,OAAO58B,EAAE,WAAc,SAAWA,EAAE,UAAY,GAClE,GAAI48B,EAAW,MAAO,OAAOA,CAAS,GACtC,MAAM9Z,EAAY,OAAO9iB,EAAE,WAAc,SAAWA,EAAE,UAAY,KAC5DuG,EAAO,OAAOvG,EAAE,MAAS,SAAWA,EAAE,KAAO,UAG7CyY,EADJpS,GAAYC,CAAO,IAAM,OAAOtG,EAAE,SAAY,SAAWA,EAAE,QAAU,OAC3C68B,GAASv2B,CAAO,GAAK,OAAOsf,CAAK,EACvDpO,EAAOslB,GAAMrkB,CAAI,EACvB,OAAOqK,EAAY,OAAOvc,CAAI,IAAIuc,CAAS,IAAItL,CAAI,GAAK,OAAOjR,CAAI,IAAIiR,CAAI,EAC7E,CAEA,SAASqlB,GAASj7B,EAA+B,CAC/C,GAAI,CACF,OAAO,KAAK,UAAUA,CAAK,CAC7B,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAASk7B,GAAM96B,EAAuB,CACpC,IAAIwV,EAAO,WACX,QAASvY,EAAI,EAAGA,EAAI+C,EAAM,OAAQ/C,IAChCuY,GAAQxV,EAAM,WAAW/C,CAAC,EAC1BuY,EAAO,KAAK,KAAKA,EAAM,QAAU,EAEnC,OAAQA,IAAS,GAAG,SAAS,EAAE,CACjC,CC1VO,SAASulB,GAAWC,EAAwC,CACjE,GAAKA,EACL,OAAI,MAAM,QAAQA,EAAO,IAAI,EACVA,EAAO,KAAK,OAAQt+B,GAAMA,IAAM,MAAM,EACvC,CAAC,GAAKs+B,EAAO,KAAK,CAAC,EAE9BA,EAAO,IAChB,CAEO,SAASC,GAAaD,EAA8B,CACzD,GAAI,CAACA,EAAQ,MAAO,GACpB,GAAIA,EAAO,UAAY,OAAW,OAAOA,EAAO,QAEhD,OADaD,GAAWC,CAAM,EACtB,CACN,IAAK,SACH,MAAO,CAAA,EACT,IAAK,QACH,MAAO,CAAA,EACT,IAAK,UACH,MAAO,GACT,IAAK,SACL,IAAK,UACH,MAAO,GACT,IAAK,SACH,MAAO,GACT,QACE,MAAO,EAAA,CAEb,CAEO,SAASE,GAAQ95B,EAAsC,CAC5D,OAAOA,EAAK,OAAQszB,GAAY,OAAOA,GAAY,QAAQ,EAAE,KAAK,GAAG,CACvE,CAEO,SAASyG,GAAY/5B,EAA8Bg6B,EAAsB,CAC9E,MAAMz0B,EAAMu0B,GAAQ95B,CAAI,EAClBi6B,EAASD,EAAMz0B,CAAG,EACxB,GAAI00B,EAAQ,OAAOA,EACnB,MAAMv5B,EAAW6E,EAAI,MAAM,GAAG,EAC9B,SAAW,CAAC20B,EAASC,CAAI,IAAK,OAAO,QAAQH,CAAK,EAAG,CACnD,GAAI,CAACE,EAAQ,SAAS,GAAG,EAAG,SAC5B,MAAME,EAAeF,EAAQ,MAAM,GAAG,EACtC,GAAIE,EAAa,SAAW15B,EAAS,OAAQ,SAC7C,IAAI8B,EAAQ,GACZ,QAAS3G,EAAI,EAAGA,EAAI6E,EAAS,OAAQ7E,GAAK,EACxC,GAAIu+B,EAAav+B,CAAC,IAAM,KAAOu+B,EAAav+B,CAAC,IAAM6E,EAAS7E,CAAC,EAAG,CAC9D2G,EAAQ,GACR,KACF,CAEF,GAAIA,EAAO,OAAO23B,CACpB,CAEF,CAEO,SAASE,GAASl7B,EAAa,CACpC,OAAOA,EACJ,QAAQ,KAAM,GAAG,EACjB,QAAQ,qBAAsB,OAAO,EACrC,QAAQ,OAAQ,GAAG,EACnB,QAAQ,KAAOvC,GAAMA,EAAE,aAAa,CACzC,CAEO,SAAS09B,GAAgBt6B,EAAuC,CACrE,MAAMuF,EAAMu0B,GAAQ95B,CAAI,EAAE,YAAA,EAC1B,OACEuF,EAAI,SAAS,OAAO,GACpBA,EAAI,SAAS,UAAU,GACvBA,EAAI,SAAS,QAAQ,GACrBA,EAAI,SAAS,QAAQ,GACrBA,EAAI,SAAS,KAAK,CAEtB,CC9EA,MAAMg1B,OAAgB,IAAI,CAAC,QAAS,cAAe,UAAW,UAAU,CAAC,EAEzE,SAASC,GAAYZ,EAA6B,CAEhD,OADa,OAAO,KAAKA,GAAU,CAAA,CAAE,EAAE,OAAQr0B,GAAQ,CAACg1B,GAAU,IAAIh1B,CAAG,CAAC,EAC9D,SAAW,CACzB,CAEA,SAASk1B,GAAUj8B,EAAwB,CACzC,GAAIA,IAAU,OAAW,MAAO,GAChC,GAAI,CACF,OAAO,KAAK,UAAUA,EAAO,KAAM,CAAC,GAAK,EAC3C,MAAQ,CACN,MAAO,EACT,CACF,CAGA,MAAMk8B,GAAQ,CACZ,YAAanX,kLACb,KAAMA,6NACN,MAAOA,iLACP,MAAOA,gRACP,KAAMA,yRACR,EAEO,SAASoX,GAAWx1B,EASS,CAClC,KAAM,CAAE,OAAAy0B,EAAQ,MAAAp7B,EAAO,KAAAwB,EAAM,MAAAg6B,EAAO,YAAAY,EAAa,SAAAC,EAAU,QAAAC,GAAY31B,EACjE41B,EAAY51B,EAAO,WAAa,GAChC61B,EAAOrB,GAAWC,CAAM,EACxBO,EAAOJ,GAAY/5B,EAAMg6B,CAAK,EAC9Bl3B,EAAQq3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOr6B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnEi7B,EAAOd,GAAM,MAAQP,EAAO,YAC5Br0B,EAAMu0B,GAAQ95B,CAAI,EAExB,GAAI46B,EAAY,IAAIr1B,CAAG,EACrB,OAAOge;AAAAA,sCAC2BzgB,CAAK;AAAA;AAAA,YAMzC,GAAI82B,EAAO,OAASA,EAAO,MAAO,CAEhC,MAAMsB,GADWtB,EAAO,OAASA,EAAO,OAAS,CAAA,GACxB,OACtBl9B,GAAM,EAAEA,EAAE,OAAS,QAAW,MAAM,QAAQA,EAAE,IAAI,GAAKA,EAAE,KAAK,SAAS,MAAM,EAAA,EAGhF,GAAIw+B,EAAQ,SAAW,EACrB,OAAOP,GAAW,CAAE,GAAGx1B,EAAQ,OAAQ+1B,EAAQ,CAAC,EAAG,EAIrD,MAAMC,EAAkBz+B,GAAuC,CAC7D,GAAIA,EAAE,QAAU,OAAW,OAAOA,EAAE,MACpC,GAAIA,EAAE,MAAQA,EAAE,KAAK,SAAW,EAAG,OAAOA,EAAE,KAAK,CAAC,CAEpD,EACM0+B,EAAWF,EAAQ,IAAIC,CAAc,EACrCE,EAAcD,EAAS,MAAO1+B,GAAMA,IAAM,MAAS,EAEzD,GAAI2+B,GAAeD,EAAS,OAAS,GAAKA,EAAS,QAAU,EAAG,CAE9D,MAAME,EAAgB98B,GAASo7B,EAAO,QACtC,OAAOrW;AAAAA;AAAAA,YAEDwX,EAAYxX,oCAAuCzgB,CAAK,WAAakzB,CAAO;AAAA,YAC5EiF,EAAO1X,iCAAoC0X,CAAI,SAAWjF,CAAO;AAAA;AAAA,cAE/DoF,EAAS,IAAI,CAACG,EAAK94B,KAAQ8gB;AAAAA;AAAAA;AAAAA,4CAGGgY,IAAQD,GAAiB,OAAOC,CAAG,IAAM,OAAOD,CAAa,EAAI,SAAW,EAAE;AAAA,4BAC9FT,CAAQ;AAAA,yBACX,IAAMC,EAAQ96B,EAAMu7B,CAAG,CAAC;AAAA;AAAA,kBAE/B,OAAOA,CAAG,CAAC;AAAA;AAAA,aAEhB,CAAC;AAAA;AAAA;AAAA,OAIV,CAEA,GAAIF,GAAeD,EAAS,OAAS,EAEnC,OAAOI,GAAa,CAAE,GAAGr2B,EAAQ,QAASi2B,EAAU,MAAO58B,GAASo7B,EAAO,QAAS,EAItF,MAAM6B,EAAiB,IAAI,IACzBP,EAAQ,IAAKQ,GAAY/B,GAAW+B,CAAO,CAAC,EAAE,OAAO,OAAO,CAAA,EAExDC,EAAkB,IAAI,IAC1B,CAAC,GAAGF,CAAc,EAAE,IAAK/+B,GAAOA,IAAM,UAAY,SAAWA,CAAE,CAAA,EAGjE,GAAI,CAAC,GAAGi/B,CAAe,EAAE,MAAOj/B,GAAM,CAAC,SAAU,SAAU,SAAS,EAAE,SAASA,CAAW,CAAC,EAAG,CAC5F,MAAMk/B,EAAYD,EAAgB,IAAI,QAAQ,EACxCE,EAAYF,EAAgB,IAAI,QAAQ,EAG9C,GAFmBA,EAAgB,IAAI,SAAS,GAE9BA,EAAgB,OAAS,EACzC,OAAOhB,GAAW,CAChB,GAAGx1B,EACH,OAAQ,CAAE,GAAGy0B,EAAQ,KAAM,UAAW,MAAO,OAAW,MAAO,MAAA,CAAU,CAC1E,EAGH,GAAIgC,GAAaC,EACf,OAAOC,GAAgB,CACrB,GAAG32B,EACH,UAAW02B,GAAa,CAACD,EAAY,SAAW,MAAA,CACjD,CAEL,CACF,CAGA,GAAIhC,EAAO,KAAM,CACf,MAAM7f,EAAU6f,EAAO,KACvB,GAAI7f,EAAQ,QAAU,EAAG,CACvB,MAAMuhB,EAAgB98B,GAASo7B,EAAO,QACtC,OAAOrW;AAAAA;AAAAA,YAEDwX,EAAYxX,oCAAuCzgB,CAAK,WAAakzB,CAAO;AAAA,YAC5EiF,EAAO1X,iCAAoC0X,CAAI,SAAWjF,CAAO;AAAA;AAAA,cAE/Djc,EAAQ,IAAKgiB,GAAQxY;AAAAA;AAAAA;AAAAA,4CAGSwY,IAAQT,GAAiB,OAAOS,CAAG,IAAM,OAAOT,CAAa,EAAI,SAAW,EAAE;AAAA,4BAC9FT,CAAQ;AAAA,yBACX,IAAMC,EAAQ96B,EAAM+7B,CAAG,CAAC;AAAA;AAAA,kBAE/B,OAAOA,CAAG,CAAC;AAAA;AAAA,aAEhB,CAAC;AAAA;AAAA;AAAA,OAIV,CACA,OAAOP,GAAa,CAAE,GAAGr2B,EAAQ,QAAA4U,EAAS,MAAOvb,GAASo7B,EAAO,QAAS,CAC5E,CAGA,GAAIoB,IAAS,SACX,OAAOgB,GAAa72B,CAAM,EAI5B,GAAI61B,IAAS,QACX,OAAOiB,GAAY92B,CAAM,EAI3B,GAAI61B,IAAS,UAAW,CACtB,MAAMkB,EAAe,OAAO19B,GAAU,UAAYA,EAAQ,OAAOo7B,EAAO,SAAY,UAAYA,EAAO,QAAU,GACjH,OAAOrW;AAAAA,qCAC0BsX,EAAW,WAAa,EAAE;AAAA;AAAA,gDAEf/3B,CAAK;AAAA,YACzCm4B,EAAO1X,uCAA0C0X,CAAI,UAAYjF,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA,uBAK7DkG,CAAY;AAAA,wBACXrB,CAAQ;AAAA,sBACTt/B,GAAau/B,EAAQ96B,EAAOzE,EAAE,OAA4B,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,KAMvF,CAGA,OAAIy/B,IAAS,UAAYA,IAAS,UACzBmB,GAAkBh3B,CAAM,EAI7B61B,IAAS,SACJc,GAAgB,CAAE,GAAG32B,EAAQ,UAAW,OAAQ,EAIlDoe;AAAAA;AAAAA,sCAE6BzgB,CAAK;AAAA,wDACak4B,CAAI;AAAA;AAAA,GAG5D,CAEA,SAASc,GAAgB32B,EASN,CACjB,KAAM,CAAE,OAAAy0B,EAAQ,MAAAp7B,EAAO,KAAAwB,EAAM,MAAAg6B,EAAO,SAAAa,EAAU,QAAAC,EAAS,UAAAsB,GAAcj3B,EAC/D41B,EAAY51B,EAAO,WAAa,GAChCg1B,EAAOJ,GAAY/5B,EAAMg6B,CAAK,EAC9Bl3B,EAAQq3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOr6B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnEi7B,EAAOd,GAAM,MAAQP,EAAO,YAC5ByC,EAAclC,GAAM,WAAaG,GAAgBt6B,CAAI,EACrDs8B,EACJnC,GAAM,cACLkC,EAAc,OAASzC,EAAO,UAAY,OAAY,YAAYA,EAAO,OAAO,GAAK,IAClFsC,EAAe19B,GAAS,GAE9B,OAAO+kB;AAAAA;AAAAA,QAEDwX,EAAYxX,oCAAuCzgB,CAAK,WAAakzB,CAAO;AAAA,QAC5EiF,EAAO1X,iCAAoC0X,CAAI,SAAWjF,CAAO;AAAA;AAAA;AAAA,iBAGxDqG,EAAc,WAAaD,CAAS;AAAA;AAAA,wBAE7BE,CAAW;AAAA,mBAChBJ,GAAgB,KAAO,GAAK,OAAOA,CAAY,CAAC;AAAA,sBAC7CrB,CAAQ;AAAA,mBACVt/B,GAAa,CACrB,MAAM4D,EAAO5D,EAAE,OAA4B,MAC3C,GAAI6gC,IAAc,SAAU,CAC1B,GAAIj9B,EAAI,KAAA,IAAW,GAAI,CACrB27B,EAAQ96B,EAAM,MAAS,EACvB,MACF,CACA,MAAMZ,EAAS,OAAOD,CAAG,EACzB27B,EAAQ96B,EAAM,OAAO,MAAMZ,CAAM,EAAID,EAAMC,CAAM,EACjD,MACF,CACA07B,EAAQ96B,EAAMb,CAAG,CACnB,CAAC;AAAA;AAAA,UAEDy6B,EAAO,UAAY,OAAYrW;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,wBAKjBsX,CAAQ;AAAA,qBACX,IAAMC,EAAQ96B,EAAM45B,EAAO,OAAO,CAAC;AAAA;AAAA,UAE5C5D,CAAO;AAAA;AAAA;AAAA,GAInB,CAEA,SAASmG,GAAkBh3B,EAQR,CACjB,KAAM,CAAE,OAAAy0B,EAAQ,MAAAp7B,EAAO,KAAAwB,EAAM,MAAAg6B,EAAO,SAAAa,EAAU,QAAAC,GAAY31B,EACpD41B,EAAY51B,EAAO,WAAa,GAChCg1B,EAAOJ,GAAY/5B,EAAMg6B,CAAK,EAC9Bl3B,EAAQq3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOr6B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnEi7B,EAAOd,GAAM,MAAQP,EAAO,YAC5BsC,EAAe19B,GAASo7B,EAAO,SAAW,GAC1C2C,EAAW,OAAOL,GAAiB,SAAWA,EAAe,EAEnE,OAAO3Y;AAAAA;AAAAA,QAEDwX,EAAYxX,oCAAuCzgB,CAAK,WAAakzB,CAAO;AAAA,QAC5EiF,EAAO1X,iCAAoC0X,CAAI,SAAWjF,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKnD6E,CAAQ;AAAA,mBACX,IAAMC,EAAQ96B,EAAMu8B,EAAW,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKjCL,GAAgB,KAAO,GAAK,OAAOA,CAAY,CAAC;AAAA,sBAC7CrB,CAAQ;AAAA,mBACVt/B,GAAa,CACrB,MAAM4D,EAAO5D,EAAE,OAA4B,MACrC6D,EAASD,IAAQ,GAAK,OAAY,OAAOA,CAAG,EAClD27B,EAAQ96B,EAAMZ,CAAM,CACtB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKWy7B,CAAQ;AAAA,mBACX,IAAMC,EAAQ96B,EAAMu8B,EAAW,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,GAKpD,CAEA,SAASf,GAAar2B,EASH,CACjB,KAAM,CAAE,OAAAy0B,EAAQ,MAAAp7B,EAAO,KAAAwB,EAAM,MAAAg6B,EAAO,SAAAa,EAAU,QAAA9gB,EAAS,QAAA+gB,GAAY31B,EAC7D41B,EAAY51B,EAAO,WAAa,GAChCg1B,EAAOJ,GAAY/5B,EAAMg6B,CAAK,EAC9Bl3B,EAAQq3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOr6B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnEi7B,EAAOd,GAAM,MAAQP,EAAO,YAC5B0B,EAAgB98B,GAASo7B,EAAO,QAChC4C,EAAeziB,EAAQ,UAC1BgiB,GAAQA,IAAQT,GAAiB,OAAOS,CAAG,IAAM,OAAOT,CAAa,CAAA,EAElEmB,EAAQ,YAEd,OAAOlZ;AAAAA;AAAAA,QAEDwX,EAAYxX,oCAAuCzgB,CAAK,WAAakzB,CAAO;AAAA,QAC5EiF,EAAO1X,iCAAoC0X,CAAI,SAAWjF,CAAO;AAAA;AAAA;AAAA,oBAGrD6E,CAAQ;AAAA,iBACX2B,GAAgB,EAAI,OAAOA,CAAY,EAAIC,CAAK;AAAA,kBAC9ClhC,GAAa,CACtB,MAAMmhC,EAAOnhC,EAAE,OAA6B,MAC5Cu/B,EAAQ96B,EAAM08B,IAAQD,EAAQ,OAAY1iB,EAAQ,OAAO2iB,CAAG,CAAC,CAAC,CAChE,CAAC;AAAA;AAAA,wBAEeD,CAAK;AAAA,UACnB1iB,EAAQ,IAAI,CAACgiB,EAAKt5B,IAAQ8gB;AAAAA,0BACV,OAAO9gB,CAAG,CAAC,IAAI,OAAOs5B,CAAG,CAAC;AAAA,SAC3C,CAAC;AAAA;AAAA;AAAA,GAIV,CAEA,SAASC,GAAa72B,EASH,CACjB,KAAM,CAAE,OAAAy0B,EAAQ,MAAAp7B,EAAO,KAAAwB,EAAM,MAAAg6B,EAAO,YAAAY,EAAa,SAAAC,EAAU,QAAAC,GAAY31B,EACrDA,EAAO,UACzB,MAAMg1B,EAAOJ,GAAY/5B,EAAMg6B,CAAK,EAC9Bl3B,EAAQq3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOr6B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnEi7B,EAAOd,GAAM,MAAQP,EAAO,YAE5B93B,EAAWtD,GAASo7B,EAAO,QAC3B5wB,EAAMlH,GAAY,OAAOA,GAAa,UAAY,CAAC,MAAM,QAAQA,CAAQ,EAC1EA,EACD,CAAA,EACE61B,EAAQiC,EAAO,YAAc,CAAA,EAI7B+C,EAHU,OAAO,QAAQhF,CAAK,EAGb,KAAK,CAAC17B,EAAGM,IAAM,CACpC,MAAMqgC,EAAS7C,GAAY,CAAC,GAAG/5B,EAAM/D,EAAE,CAAC,CAAC,EAAG+9B,CAAK,GAAG,OAAS,EACvD6C,EAAS9C,GAAY,CAAC,GAAG/5B,EAAMzD,EAAE,CAAC,CAAC,EAAGy9B,CAAK,GAAG,OAAS,EAC7D,OAAI4C,IAAWC,EAAeD,EAASC,EAChC5gC,EAAE,CAAC,EAAE,cAAcM,EAAE,CAAC,CAAC,CAChC,CAAC,EAEKugC,EAAW,IAAI,IAAI,OAAO,KAAKnF,CAAK,CAAC,EACrCoF,EAAanD,EAAO,qBACpBoD,EAAa,EAAQD,GAAe,OAAOA,GAAe,SAGhE,OAAI/8B,EAAK,SAAW,EACXujB;AAAAA;AAAAA,UAEDoZ,EAAO,IAAI,CAAC,CAACM,EAASzS,CAAI,IAC1BmQ,GAAW,CACT,OAAQnQ,EACR,MAAOxhB,EAAIi0B,CAAO,EAClB,KAAM,CAAC,GAAGj9B,EAAMi9B,CAAO,EACvB,MAAAjD,EACA,YAAAY,EACA,SAAAC,EACA,QAAAC,CAAA,CACD,CAAA,CACF;AAAA,UACCkC,EAAaE,GAAe,CAC5B,OAAQH,EACR,MAAO/zB,EACP,KAAAhJ,EACA,MAAAg6B,EACA,YAAAY,EACA,SAAAC,EACA,aAAciC,EACd,QAAAhC,CAAA,CACD,EAAI9E,CAAO;AAAA;AAAA,MAMXzS;AAAAA;AAAAA;AAAAA,0CAGiCzgB,CAAK;AAAA,4CACH43B,GAAM,WAAW;AAAA;AAAA,QAErDO,EAAO1X,kCAAqC0X,CAAI,SAAWjF,CAAO;AAAA;AAAA,UAEhE2G,EAAO,IAAI,CAAC,CAACM,EAASzS,CAAI,IAC1BmQ,GAAW,CACT,OAAQnQ,EACR,MAAOxhB,EAAIi0B,CAAO,EAClB,KAAM,CAAC,GAAGj9B,EAAMi9B,CAAO,EACvB,MAAAjD,EACA,YAAAY,EACA,SAAAC,EACA,QAAAC,CAAA,CACD,CAAA,CACF;AAAA,UACCkC,EAAaE,GAAe,CAC5B,OAAQH,EACR,MAAO/zB,EACP,KAAAhJ,EACA,MAAAg6B,EACA,YAAAY,EACA,SAAAC,EACA,aAAciC,EACd,QAAAhC,CAAA,CACD,EAAI9E,CAAO;AAAA;AAAA;AAAA,GAIpB,CAEA,SAASiG,GAAY92B,EASF,CACjB,KAAM,CAAE,OAAAy0B,EAAQ,MAAAp7B,EAAO,KAAAwB,EAAM,MAAAg6B,EAAO,YAAAY,EAAa,SAAAC,EAAU,QAAAC,GAAY31B,EACjE41B,EAAY51B,EAAO,WAAa,GAChCg1B,EAAOJ,GAAY/5B,EAAMg6B,CAAK,EAC9Bl3B,EAAQq3B,GAAM,OAASP,EAAO,OAASS,GAAS,OAAOr6B,EAAK,GAAG,EAAE,CAAC,CAAC,EACnEi7B,EAAOd,GAAM,MAAQP,EAAO,YAE5BuD,EAAc,MAAM,QAAQvD,EAAO,KAAK,EAAIA,EAAO,MAAM,CAAC,EAAIA,EAAO,MAC3E,GAAI,CAACuD,EACH,OAAO5Z;AAAAA;AAAAA,wCAE6BzgB,CAAK;AAAA;AAAA;AAAA,MAM3C,MAAMs6B,EAAM,MAAM,QAAQ5+B,CAAK,EAAIA,EAAQ,MAAM,QAAQo7B,EAAO,OAAO,EAAIA,EAAO,QAAU,CAAA,EAE5F,OAAOrW;AAAAA;AAAAA;AAAAA,UAGCwX,EAAYxX,mCAAsCzgB,CAAK,UAAYkzB,CAAO;AAAA,yCAC3CoH,EAAI,MAAM,QAAQA,EAAI,SAAW,EAAI,IAAM,EAAE;AAAA;AAAA;AAAA;AAAA,sBAIhEvC,CAAQ;AAAA,mBACX,IAAM,CACb,MAAMv7B,EAAO,CAAC,GAAG89B,EAAKvD,GAAasD,CAAW,CAAC,EAC/CrC,EAAQ96B,EAAMV,CAAI,CACpB,CAAC;AAAA;AAAA,8CAEmCo7B,GAAM,IAAI;AAAA;AAAA;AAAA;AAAA,QAIhDO,EAAO1X,iCAAoC0X,CAAI,SAAWjF,CAAO;AAAA;AAAA,QAEjEoH,EAAI,SAAW,EAAI7Z;AAAAA;AAAAA;AAAAA;AAAAA,QAIjBA;AAAAA;AAAAA,YAEE6Z,EAAI,IAAI,CAAC/5B,EAAMZ,IAAQ8gB;AAAAA;AAAAA;AAAAA,uDAGoB9gB,EAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,8BAKhCo4B,CAAQ;AAAA,2BACX,IAAM,CACb,MAAMv7B,EAAO,CAAC,GAAG89B,CAAG,EACpB99B,EAAK,OAAOmD,EAAK,CAAC,EAClBq4B,EAAQ96B,EAAMV,CAAI,CACpB,CAAC;AAAA;AAAA,oBAECo7B,GAAM,KAAK;AAAA;AAAA;AAAA;AAAA,kBAIbC,GAAW,CACX,OAAQwC,EACR,MAAO95B,EACP,KAAM,CAAC,GAAGrD,EAAMyC,CAAG,EACnB,MAAAu3B,EACA,YAAAY,EACA,SAAAC,EACA,UAAW,GACX,QAAAC,CAAA,CACD,CAAC;AAAA;AAAA;AAAA,WAGP,CAAC;AAAA;AAAA,OAEL;AAAA;AAAA,GAGP,CAEA,SAASoC,GAAe/3B,EASL,CACjB,KAAM,CAAE,OAAAy0B,EAAQ,MAAAp7B,EAAO,KAAAwB,EAAM,MAAAg6B,EAAO,YAAAY,EAAa,SAAAC,EAAU,aAAAwC,EAAc,QAAAvC,CAAA,EAAY31B,EAC/Em4B,EAAY9C,GAAYZ,CAAM,EAC9BjtB,EAAU,OAAO,QAAQnO,GAAS,CAAA,CAAE,EAAE,OAAO,CAAC,CAAC+G,CAAG,IAAM,CAAC83B,EAAa,IAAI93B,CAAG,CAAC,EAEpF,OAAOge;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,sBAOasX,CAAQ;AAAA,mBACX,IAAM,CACb,MAAMv7B,EAAO,CAAE,GAAId,GAAS,EAAC,EAC7B,IAAIgkB,EAAQ,EACRjd,EAAM,UAAUid,CAAK,GACzB,KAAOjd,KAAOjG,GACZkjB,GAAS,EACTjd,EAAM,UAAUid,CAAK,GAEvBljB,EAAKiG,CAAG,EAAI+3B,EAAY,CAAA,EAAKzD,GAAaD,CAAM,EAChDkB,EAAQ96B,EAAMV,CAAI,CACpB,CAAC;AAAA;AAAA,4CAEiCo7B,GAAM,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,QAK9C/tB,EAAQ,SAAW,EAAI4W;AAAAA;AAAAA,QAErBA;AAAAA;AAAAA,YAEE5W,EAAQ,IAAI,CAAC,CAACpH,EAAKg4B,CAAU,IAAM,CACnC,MAAMC,EAAY,CAAC,GAAGx9B,EAAMuF,CAAG,EACzBzD,EAAW24B,GAAU8C,CAAU,EACrC,OAAOha;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,6BAOUhe,CAAG;AAAA,gCACAs1B,CAAQ;AAAA,8BACTt/B,GAAa,CACtB,MAAM0N,EAAW1N,EAAE,OAA4B,MAAM,KAAA,EACrD,GAAI,CAAC0N,GAAWA,IAAY1D,EAAK,OACjC,MAAMjG,EAAO,CAAE,GAAId,GAAS,EAAC,EACzByK,KAAW3J,IACfA,EAAK2J,CAAO,EAAI3J,EAAKiG,CAAG,EACxB,OAAOjG,EAAKiG,CAAG,EACfu1B,EAAQ96B,EAAMV,CAAI,EACpB,CAAC;AAAA;AAAA;AAAA;AAAA,oBAIDg+B,EACE/Z;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,mCAKazhB,CAAQ;AAAA,sCACL+4B,CAAQ;AAAA,oCACTt/B,GAAa,CACtB,MAAMkM,EAASlM,EAAE,OACX4D,EAAMsI,EAAO,MAAM,KAAA,EACzB,GAAI,CAACtI,EAAK,CACR27B,EAAQ0C,EAAW,MAAS,EAC5B,MACF,CACA,GAAI,CACF1C,EAAQ0C,EAAW,KAAK,MAAMr+B,CAAG,CAAC,CACpC,MAAQ,CACNsI,EAAO,MAAQ3F,CACjB,CACF,CAAC;AAAA;AAAA,wBAGL64B,GAAW,CACT,OAAAf,EACA,MAAO2D,EACP,KAAMC,EACN,MAAAxD,EACA,YAAAY,EACA,SAAAC,EACA,UAAW,GACX,QAAAC,CAAA,CACD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAMMD,CAAQ;AAAA,2BACX,IAAM,CACb,MAAMv7B,EAAO,CAAE,GAAId,GAAS,EAAC,EAC7B,OAAOc,EAAKiG,CAAG,EACfu1B,EAAQ96B,EAAMV,CAAI,CACpB,CAAC;AAAA;AAAA,oBAECo7B,GAAM,KAAK;AAAA;AAAA;AAAA,aAIrB,CAAC,CAAC;AAAA;AAAA,OAEL;AAAA;AAAA,GAGP,CCnpBA,MAAM+C,GAAe,CACnB,IAAKla,+2BACL,OAAQA,8OACR,OAAQA,mZACR,KAAMA,iMACN,SAAUA,uKACV,SAAUA,kOACV,SAAUA,kLACV,MAAOA,mPACP,OAAQA,mNACR,MAAOA,kQACP,QAASA,wRACT,OAAQA,mVAER,KAAMA,gLACN,QAASA,oVACT,QAASA,8TACT,GAAIA,0OACJ,OAAQA,+UACR,SAAUA,6SACV,UAAWA,oUACX,MAAOA,sMACP,QAASA,+QACT,KAAMA,+KACN,IAAKA,wRACL,UAAWA,kLACX,WAAYA,gPACZ,KAAMA,mSACN,QAASA,8VACT,QAASA,gNACX,EAGama,GAAuE,CAClF,IAAK,CAAE,MAAO,wBAAyB,YAAa,qDAAA,EACpD,OAAQ,CAAE,MAAO,UAAW,YAAa,0CAAA,EACzC,OAAQ,CAAE,MAAO,SAAU,YAAa,8CAAA,EACxC,KAAM,CAAE,MAAO,iBAAkB,YAAa,sCAAA,EAC9C,SAAU,CAAE,MAAO,WAAY,YAAa,qDAAA,EAC5C,SAAU,CAAE,MAAO,WAAY,YAAa,uCAAA,EAC5C,SAAU,CAAE,MAAO,WAAY,YAAa,uBAAA,EAC5C,MAAO,CAAE,MAAO,QAAS,YAAa,0BAAA,EACtC,OAAQ,CAAE,MAAO,SAAU,YAAa,8BAAA,EACxC,MAAO,CAAE,MAAO,QAAS,YAAa,6CAAA,EACtC,QAAS,CAAE,MAAO,UAAW,YAAa,+CAAA,EAC1C,OAAQ,CAAE,MAAO,eAAgB,YAAa,gCAAA,EAE9C,KAAM,CAAE,MAAO,WAAY,YAAa,0CAAA,EACxC,QAAS,CAAE,MAAO,UAAW,YAAa,qCAAA,EAC1C,QAAS,CAAE,MAAO,UAAW,YAAa,6BAAA,EAC1C,GAAI,CAAE,MAAO,KAAM,YAAa,4BAAA,EAChC,OAAQ,CAAE,MAAO,SAAU,YAAa,uCAAA,EACxC,SAAU,CAAE,MAAO,WAAY,YAAa,4BAAA,EAC5C,UAAW,CAAE,MAAO,YAAa,YAAa,qCAAA,EAC9C,MAAO,CAAE,MAAO,QAAS,YAAa,6BAAA,EACtC,QAAS,CAAE,MAAO,UAAW,YAAa,oCAAA,EAC1C,KAAM,CAAE,MAAO,OAAQ,YAAa,gCAAA,EACpC,IAAK,CAAE,MAAO,MAAO,YAAa,6BAAA,EAClC,UAAW,CAAE,MAAO,YAAa,YAAa,kCAAA,EAC9C,WAAY,CAAE,MAAO,cAAe,YAAa,8BAAA,EACjD,KAAM,CAAE,MAAO,OAAQ,YAAa,2BAAA,EACpC,QAAS,CAAE,MAAO,UAAW,YAAa,kCAAA,CAC5C,EAEA,SAASC,GAAep4B,EAAa,CACnC,OAAOk4B,GAAal4B,CAAgC,GAAKk4B,GAAa,OACxE,CAEA,SAASG,GAAcr4B,EAAaq0B,EAAoBiE,EAAwB,CAC9E,GAAI,CAACA,EAAO,MAAO,GACnB,MAAMnuB,EAAImuB,EAAM,YAAA,EACV1xB,EAAOuxB,GAAan4B,CAAG,EAM7B,OAHIA,EAAI,YAAA,EAAc,SAASmK,CAAC,GAG5BvD,IACEA,EAAK,MAAM,YAAA,EAAc,SAASuD,CAAC,GACnCvD,EAAK,YAAY,YAAA,EAAc,SAASuD,CAAC,GAAU,GAGlDouB,GAAclE,EAAQlqB,CAAC,CAChC,CAEA,SAASouB,GAAclE,EAAoBiE,EAAwB,CAGjE,GAFIjE,EAAO,OAAO,YAAA,EAAc,SAASiE,CAAK,GAC1CjE,EAAO,aAAa,YAAA,EAAc,SAASiE,CAAK,GAChDjE,EAAO,MAAM,KAAMp7B,GAAU,OAAOA,CAAK,EAAE,YAAA,EAAc,SAASq/B,CAAK,CAAC,EAAG,MAAO,GAEtF,GAAIjE,EAAO,YACT,SAAW,CAACqD,EAASc,CAAU,IAAK,OAAO,QAAQnE,EAAO,UAAU,EAElE,GADIqD,EAAQ,YAAA,EAAc,SAASY,CAAK,GACpCC,GAAcC,EAAYF,CAAK,EAAG,MAAO,GAIjD,GAAIjE,EAAO,MAAO,CAChB,MAAMV,EAAQ,MAAM,QAAQU,EAAO,KAAK,EAAIA,EAAO,MAAQ,CAACA,EAAO,KAAK,EACxE,UAAWv2B,KAAQ61B,EACjB,GAAI71B,GAAQy6B,GAAcz6B,EAAMw6B,CAAK,EAAG,MAAO,EAEnD,CAEA,GAAIjE,EAAO,sBAAwB,OAAOA,EAAO,sBAAyB,UACpEkE,GAAclE,EAAO,qBAAsBiE,CAAK,EAAG,MAAO,GAGhE,MAAMG,EAASpE,EAAO,OAASA,EAAO,OAASA,EAAO,MACtD,GAAIoE,GACF,UAAWj4B,KAASi4B,EAClB,GAAIj4B,GAAS+3B,GAAc/3B,EAAO83B,CAAK,EAAG,MAAO,GAIrD,MAAO,EACT,CAEO,SAASI,GAAiBtG,EAAwB,CACvD,GAAI,CAACA,EAAM,OACT,OAAOpU,gDAET,MAAMqW,EAASjC,EAAM,OACfn5B,EAAQm5B,EAAM,OAAS,CAAA,EAC7B,GAAIgC,GAAWC,CAAM,IAAM,UAAY,CAACA,EAAO,WAC7C,OAAOrW,kEAET,MAAMqX,EAAc,IAAI,IAAIjD,EAAM,kBAAoB,CAAA,CAAE,EAClDuG,EAAatE,EAAO,WACpBuE,EAAcxG,EAAM,aAAe,GACnCyG,EAAgBzG,EAAM,cACtB0G,EAAmB1G,EAAM,kBAAoB,KAGnD,IAAIhrB,EAAU,OAAO,QAAQuxB,CAAU,EAGnCE,IACFzxB,EAAUA,EAAQ,OAAO,CAAC,CAACpH,CAAG,IAAMA,IAAQ64B,CAAa,GAIvDD,IACFxxB,EAAUA,EAAQ,OAAO,CAAC,CAACpH,EAAKilB,CAAI,IAAMoT,GAAcr4B,EAAKilB,EAAM2T,CAAW,CAAC,GAIjFxxB,EAAQ,KAAK,CAAC1Q,EAAGM,IAAM,CACrB,MAAMqgC,EAAS7C,GAAY,CAAC99B,EAAE,CAAC,CAAC,EAAG07B,EAAM,OAAO,GAAG,OAAS,GACtDkF,EAAS9C,GAAY,CAACx9B,EAAE,CAAC,CAAC,EAAGo7B,EAAM,OAAO,GAAG,OAAS,GAC5D,OAAIiF,IAAWC,EAAeD,EAASC,EAChC5gC,EAAE,CAAC,EAAE,cAAcM,EAAE,CAAC,CAAC,CAChC,CAAC,EAED,IAAI+hC,EAEO,KACX,GAAIF,GAAiBC,GAAoB1xB,EAAQ,SAAW,EAAG,CAC7D,MAAM4xB,EAAgB5xB,EAAQ,CAAC,IAAI,CAAC,EAElC4xB,GACA5E,GAAW4E,CAAa,IAAM,UAC9BA,EAAc,YACdA,EAAc,WAAWF,CAAgB,IAEzCC,EAAoB,CAClB,WAAYF,EACZ,cAAeC,EACf,OAAQE,EAAc,WAAWF,CAAgB,CAAA,EAGvD,CAEA,OAAI1xB,EAAQ,SAAW,EACd4W;AAAAA;AAAAA;AAAAA;AAAAA,YAIC4a,EACE,sBAAsBA,CAAW,IACjC,6BAA6B;AAAA;AAAA;AAAA,MAMlC5a;AAAAA;AAAAA,QAED+a,GACG,IAAM,CACL,KAAM,CAAE,WAAAE,EAAY,cAAAC,EAAe,OAAQjU,GAAS8T,EAC9CnE,EAAOJ,GAAY,CAACyE,EAAYC,CAAa,EAAG9G,EAAM,OAAO,EAC7D70B,EAAQq3B,GAAM,OAAS3P,EAAK,OAAS6P,GAASoE,CAAa,EAC3DC,EAAcvE,GAAM,MAAQ3P,EAAK,aAAe,GAChDmU,EAAgBngC,EAAkCggC,CAAU,EAC5DI,EACJD,GAAgB,OAAOA,GAAiB,SACnCA,EAAyCF,CAAa,EACvD,OACAh4B,EAAK,kBAAkB+3B,CAAU,IAAIC,CAAa,GACxD,OAAOlb;AAAAA,wDACqC9c,CAAE;AAAA;AAAA,4DAEEk3B,GAAea,CAAU,CAAC;AAAA;AAAA,6DAEzB17B,CAAK;AAAA,sBAC5C47B,EACEnb,yCAA4Cmb,CAAW,OACvD1I,CAAO;AAAA;AAAA;AAAA;AAAA,oBAIX2E,GAAW,CACX,OAAQnQ,EACR,MAAOoU,EACP,KAAM,CAACJ,EAAYC,CAAa,EAChC,MAAO9G,EAAM,QACb,YAAAiD,EACA,SAAUjD,EAAM,UAAY,GAC5B,UAAW,GACX,QAASA,EAAM,OAAA,CAChB,CAAC;AAAA;AAAA;AAAA,aAIV,GAAA,EACAhrB,EAAQ,IAAI,CAAC,CAACpH,EAAKilB,CAAI,IAAM,CAC3B,MAAMre,EAAOuxB,GAAan4B,CAAG,GAAK,CAChC,MAAOA,EAAI,OAAO,CAAC,EAAE,cAAgBA,EAAI,MAAM,CAAC,EAChD,YAAailB,EAAK,aAAe,EAAA,EAGnC,OAAOjH;AAAAA,wEACqDhe,CAAG;AAAA;AAAA,4DAEfo4B,GAAep4B,CAAG,CAAC;AAAA;AAAA,6DAElB4G,EAAK,KAAK;AAAA,sBACjDA,EAAK,YACHoX,yCAA4CpX,EAAK,WAAW,OAC5D6pB,CAAO;AAAA;AAAA;AAAA;AAAA,oBAIX2E,GAAW,CACX,OAAQnQ,EACR,MAAQhsB,EAAkC+G,CAAG,EAC7C,KAAM,CAACA,CAAG,EACV,MAAOoyB,EAAM,QACb,YAAAiD,EACA,SAAUjD,EAAM,UAAY,GAC5B,UAAW,GACX,QAASA,EAAM,OAAA,CAChB,CAAC;AAAA;AAAA;AAAA,aAIV,CAAC,CAAC;AAAA;AAAA,GAGZ,CCpRA,MAAM4C,OAAgB,IAAI,CAAC,QAAS,cAAe,UAAW,UAAU,CAAC,EAEzE,SAASC,GAAYZ,EAA6B,CAEhD,OADa,OAAO,KAAKA,GAAU,CAAA,CAAE,EAAE,OAAQr0B,GAAQ,CAACg1B,GAAU,IAAIh1B,CAAG,CAAC,EAC9D,SAAW,CACzB,CAEA,SAASs5B,GAAcp9B,EAAiE,CACtF,MAAMq9B,EAAWr9B,EAAO,OAAQjD,GAAUA,GAAS,IAAI,EACjDugC,EAAWD,EAAS,SAAWr9B,EAAO,OACtCu9B,EAAwB,CAAA,EAC9B,UAAWxgC,KAASsgC,EACbE,EAAW,KAAMxmB,GAAa,OAAO,GAAGA,EAAUha,CAAK,CAAC,GAC3DwgC,EAAW,KAAKxgC,CAAK,EAGzB,MAAO,CAAE,WAAAwgC,EAAY,SAAAD,CAAA,CACvB,CAEO,SAASE,GAAoB9/B,EAAoC,CACtE,MAAI,CAACA,GAAO,OAAOA,GAAQ,SAClB,CAAE,OAAQ,KAAM,iBAAkB,CAAC,QAAQ,CAAA,EAE7C+/B,GAAoB//B,EAAmB,EAAE,CAClD,CAEA,SAAS+/B,GACPtF,EACA55B,EACsB,CACtB,MAAM46B,MAAkB,IAClBv6B,EAAyB,CAAE,GAAGu5B,CAAA,EAC9BuF,EAAYrF,GAAQ95B,CAAI,GAAK,SAEnC,GAAI45B,EAAO,OAASA,EAAO,OAASA,EAAO,MAAO,CAChD,MAAMwF,EAAQC,GAAezF,EAAQ55B,CAAI,EACzC,OAAIo/B,GACG,CAAE,OAAAxF,EAAQ,iBAAkB,CAACuF,CAAS,CAAA,CAC/C,CAEA,MAAMJ,EAAW,MAAM,QAAQnF,EAAO,IAAI,GAAKA,EAAO,KAAK,SAAS,MAAM,EACpEoB,EACJrB,GAAWC,CAAM,IAChBA,EAAO,YAAcA,EAAO,qBAAuB,SAAW,QAIjE,GAHAv5B,EAAW,KAAO26B,GAAQpB,EAAO,KACjCv5B,EAAW,SAAW0+B,GAAYnF,EAAO,SAErCv5B,EAAW,KAAM,CACnB,KAAM,CAAE,WAAA2+B,EAAY,SAAUM,GAAiBT,GAAcx+B,EAAW,IAAI,EAC5EA,EAAW,KAAO2+B,EACdM,MAAyB,SAAW,IACpCN,EAAW,SAAW,GAAGpE,EAAY,IAAIuE,CAAS,CACxD,CAEA,GAAInE,IAAS,SAAU,CACrB,MAAMkD,EAAatE,EAAO,YAAc,CAAA,EAClC2F,EAA8C,CAAA,EACpD,SAAW,CAACh6B,EAAK/G,CAAK,IAAK,OAAO,QAAQ0/B,CAAU,EAAG,CACrD,MAAM15B,EAAM06B,GAAoB1gC,EAAO,CAAC,GAAGwB,EAAMuF,CAAG,CAAC,EACjDf,EAAI,SAAQ+6B,EAAgBh6B,CAAG,EAAIf,EAAI,QAC3C,UAAWuB,KAASvB,EAAI,iBAAkBo2B,EAAY,IAAI70B,CAAK,CACjE,CAGA,GAFA1F,EAAW,WAAak/B,EAEpB3F,EAAO,uBAAyB,GAClCgB,EAAY,IAAIuE,CAAS,UAChBvF,EAAO,uBAAyB,GACzCv5B,EAAW,qBAAuB,WAElCu5B,EAAO,sBACP,OAAOA,EAAO,sBAAyB,UAEnC,CAACY,GAAYZ,EAAO,oBAAkC,EAAG,CAC3D,MAAMp1B,EAAM06B,GACVtF,EAAO,qBACP,CAAC,GAAG55B,EAAM,GAAG,CAAA,EAEfK,EAAW,qBACTmE,EAAI,QAAWo1B,EAAO,qBACpBp1B,EAAI,iBAAiB,OAAS,GAAGo2B,EAAY,IAAIuE,CAAS,CAChE,CAEJ,SAAWnE,IAAS,QAAS,CAC3B,MAAMmC,EAAc,MAAM,QAAQvD,EAAO,KAAK,EAC1CA,EAAO,MAAM,CAAC,EACdA,EAAO,MACX,GAAI,CAACuD,EACHvC,EAAY,IAAIuE,CAAS,MACpB,CACL,MAAM36B,EAAM06B,GAAoB/B,EAAa,CAAC,GAAGn9B,EAAM,GAAG,CAAC,EAC3DK,EAAW,MAAQmE,EAAI,QAAU24B,EAC7B34B,EAAI,iBAAiB,OAAS,GAAGo2B,EAAY,IAAIuE,CAAS,CAChE,CACF,MACEnE,IAAS,UACTA,IAAS,UACTA,IAAS,WACTA,IAAS,WACT,CAAC36B,EAAW,MAEZu6B,EAAY,IAAIuE,CAAS,EAG3B,MAAO,CACL,OAAQ9+B,EACR,iBAAkB,MAAM,KAAKu6B,CAAW,CAAA,CAE5C,CAEA,SAASyE,GACPzF,EACA55B,EAC6B,CAC7B,GAAI45B,EAAO,MAAO,OAAO,KACzB,MAAMwF,EAAQxF,EAAO,OAASA,EAAO,MACrC,GAAI,CAACwF,EAAO,OAAO,KAEnB,MAAMhE,EAAsB,CAAA,EACtBoE,EAA0B,CAAA,EAChC,IAAIT,EAAW,GAEf,UAAWh5B,KAASq5B,EAAO,CACzB,GAAI,CAACr5B,GAAS,OAAOA,GAAU,SAAU,OAAO,KAChD,GAAI,MAAM,QAAQA,EAAM,IAAI,EAAG,CAC7B,KAAM,CAAE,WAAAi5B,EAAY,SAAUM,GAAiBT,GAAc94B,EAAM,IAAI,EACvEq1B,EAAS,KAAK,GAAG4D,CAAU,EACvBM,IAAcP,EAAW,IAC7B,QACF,CACA,GAAI,UAAWh5B,EAAO,CACpB,GAAIA,EAAM,OAAS,KAAM,CACvBg5B,EAAW,GACX,QACF,CACA3D,EAAS,KAAKr1B,EAAM,KAAK,EACzB,QACF,CACA,GAAI4zB,GAAW5zB,CAAK,IAAM,OAAQ,CAChCg5B,EAAW,GACX,QACF,CACAS,EAAU,KAAKz5B,CAAK,CACtB,CAEA,GAAIq1B,EAAS,OAAS,GAAKoE,EAAU,SAAW,EAAG,CACjD,MAAMC,EAAoB,CAAA,EAC1B,UAAWjhC,KAAS48B,EACbqE,EAAO,KAAMjnB,GAAa,OAAO,GAAGA,EAAUha,CAAK,CAAC,GACvDihC,EAAO,KAAKjhC,CAAK,EAGrB,MAAO,CACL,OAAQ,CACN,GAAGo7B,EACH,KAAM6F,EACN,SAAAV,EACA,MAAO,OACP,MAAO,OACP,MAAO,MAAA,EAET,iBAAkB,CAAA,CAAC,CAEvB,CAEA,GAAIS,EAAU,SAAW,EAAG,CAC1B,MAAMh7B,EAAM06B,GAAoBM,EAAU,CAAC,EAAGx/B,CAAI,EAClD,OAAIwE,EAAI,SACNA,EAAI,OAAO,SAAWu6B,GAAYv6B,EAAI,OAAO,UAExCA,CACT,CAEA,MAAMi3B,EAAiB,CAAC,SAAU,SAAU,UAAW,SAAS,EAChE,OACE+D,EAAU,OAAS,GACnBpE,EAAS,SAAW,GACpBoE,EAAU,MAAOz5B,GAAUA,EAAM,MAAQ01B,EAAe,SAAS,OAAO11B,EAAM,IAAI,CAAC,CAAC,EAE7E,CACL,OAAQ,CACN,GAAG6zB,EACH,SAAAmF,CAAA,EAEF,iBAAkB,CAAA,CAAC,EAIhB,IACT,CC1JA,MAAMW,GAAe,CACnB,IAAKnc,kRACL,IAAKA,62BACL,OAAQA,4OACR,OAAQA,iZACR,KAAMA,+LACN,SAAUA,qKACV,SAAUA,gOACV,SAAUA,gLACV,MAAOA,iPACP,OAAQA,iNACR,MAAOA,gQACP,QAASA,sRACT,OAAQA,iVAER,KAAMA,8KACN,QAASA,kVACT,QAASA,4TACT,GAAIA,wOACJ,OAAQA,6UACR,SAAUA,2SACV,UAAWA,kUACX,MAAOA,oMACP,QAASA,6QACT,KAAMA,6KACN,IAAKA,sRACL,UAAWA,gLACX,WAAYA,8OACZ,KAAMA,iSACN,QAASA,4VACT,QAASA,8MACX,EAGMoc,GAAkD,CACtD,CAAE,IAAK,MAAO,MAAO,aAAA,EACrB,CAAE,IAAK,SAAU,MAAO,SAAA,EACxB,CAAE,IAAK,SAAU,MAAO,QAAA,EACxB,CAAE,IAAK,OAAQ,MAAO,gBAAA,EACtB,CAAE,IAAK,WAAY,MAAO,UAAA,EAC1B,CAAE,IAAK,WAAY,MAAO,UAAA,EAC1B,CAAE,IAAK,WAAY,MAAO,UAAA,EAC1B,CAAE,IAAK,QAAS,MAAO,OAAA,EACvB,CAAE,IAAK,SAAU,MAAO,QAAA,EACxB,CAAE,IAAK,QAAS,MAAO,OAAA,EACvB,CAAE,IAAK,UAAW,MAAO,SAAA,EACzB,CAAE,IAAK,SAAU,MAAO,cAAA,CAC1B,EASMC,GAAiB,UAEvB,SAASjC,GAAep4B,EAAa,CACnC,OAAOm6B,GAAan6B,CAAgC,GAAKm6B,GAAa,OACxE,CAEA,SAASG,GAAmBt6B,EAAaq0B,EAGvC,CACA,MAAMztB,EAAOuxB,GAAan4B,CAAG,EAC7B,OAAI4G,GACG,CACL,MAAOytB,GAAQ,OAASS,GAAS90B,CAAG,EACpC,YAAaq0B,GAAQ,aAAe,EAAA,CAExC,CAEA,SAASkG,GAAmB36B,EAIN,CACpB,KAAM,CAAE,IAAAI,EAAK,OAAAq0B,EAAQ,QAAAmG,CAAA,EAAY56B,EACjC,GAAI,CAACy0B,GAAUD,GAAWC,CAAM,IAAM,UAAY,CAACA,EAAO,WAAY,MAAO,CAAA,EAC7E,MAAMjtB,EAAU,OAAO,QAAQitB,EAAO,UAAU,EAAE,IAAI,CAAC,CAACoG,EAAQxV,CAAI,IAAM,CACxE,MAAM2P,EAAOJ,GAAY,CAACx0B,EAAKy6B,CAAM,EAAGD,CAAO,EACzCj9B,EAAQq3B,GAAM,OAAS3P,EAAK,OAAS6P,GAAS2F,CAAM,EACpDtB,EAAcvE,GAAM,MAAQ3P,EAAK,aAAe,GAChDyV,EAAQ9F,GAAM,OAAS,GAC7B,MAAO,CAAE,IAAK6F,EAAQ,MAAAl9B,EAAO,YAAA47B,EAAa,MAAAuB,CAAA,CAC5C,CAAC,EACD,OAAAtzB,EAAQ,KAAK,CAAC1Q,EAAGM,IAAON,EAAE,QAAUM,EAAE,MAAQN,EAAE,MAAQM,EAAE,MAAQN,EAAE,IAAI,cAAcM,EAAE,GAAG,CAAE,EACtFoQ,CACT,CAEA,SAASuzB,GACPC,EACAl7B,EACqD,CACrD,GAAI,CAACk7B,GAAY,CAACl7B,QAAgB,CAAA,EAClC,MAAMm7B,EAA+D,CAAA,EAErE,SAASC,EAAQC,EAAeC,EAAevgC,EAAc,CAC3D,GAAIsgC,IAASC,EAAM,OACnB,GAAI,OAAOD,GAAS,OAAOC,EAAM,CAC/BH,EAAQ,KAAK,CAAE,KAAApgC,EAAM,KAAMsgC,EAAM,GAAIC,EAAM,EAC3C,MACF,CACA,GAAI,OAAOD,GAAS,UAAYA,IAAS,MAAQC,IAAS,KAAM,CAC1DD,IAASC,GACXH,EAAQ,KAAK,CAAE,KAAApgC,EAAM,KAAMsgC,EAAM,GAAIC,EAAM,EAE7C,MACF,CACA,GAAI,MAAM,QAAQD,CAAI,GAAK,MAAM,QAAQC,CAAI,EAAG,CAC1C,KAAK,UAAUD,CAAI,IAAM,KAAK,UAAUC,CAAI,GAC9CH,EAAQ,KAAK,CAAE,KAAApgC,EAAM,KAAMsgC,EAAM,GAAIC,EAAM,EAE7C,MACF,CACA,MAAMC,EAAUF,EACVG,EAAUF,EACVG,EAAU,IAAI,IAAI,CAAC,GAAG,OAAO,KAAKF,CAAO,EAAG,GAAG,OAAO,KAAKC,CAAO,CAAC,CAAC,EAC1E,UAAWl7B,KAAOm7B,EAChBL,EAAQG,EAAQj7B,CAAG,EAAGk7B,EAAQl7B,CAAG,EAAGvF,EAAO,GAAGA,CAAI,IAAIuF,CAAG,GAAKA,CAAG,CAErE,CAEA,OAAA86B,EAAQF,EAAUl7B,EAAS,EAAE,EACtBm7B,CACT,CAEA,SAASO,GAAcniC,EAAgBoiC,EAAS,GAAY,CAC1D,IAAIC,EACJ,GAAI,CAEFA,EADa,KAAK,UAAUriC,CAAK,GACnB,OAAOA,CAAK,CAC5B,MAAQ,CACNqiC,EAAM,OAAOriC,CAAK,CACpB,CACA,OAAIqiC,EAAI,QAAUD,EAAeC,EAC1BA,EAAI,MAAM,EAAGD,EAAS,CAAC,EAAI,KACpC,CAEO,SAASE,GAAanJ,EAAoB,CAC/C,MAAMoJ,EACJpJ,EAAM,OAAS,KAAO,UAAYA,EAAM,MAAQ,QAAU,UACtDqJ,EAAW/B,GAAoBtH,EAAM,MAAM,EAC3CsJ,EAAaD,EAAS,OACxBA,EAAS,iBAAiB,OAAS,EACnC,GACEE,EACJ,EAAQvJ,EAAM,WAAc,CAACA,EAAM,SAAW,CAACsJ,EAC3CE,EACJxJ,EAAM,WACN,CAACA,EAAM,SACNA,EAAM,WAAa,MAAQ,GAAOuJ,GAC/BE,EACJzJ,EAAM,WACN,CAACA,EAAM,UACP,CAACA,EAAM,WACNA,EAAM,WAAa,MAAQ,GAAOuJ,GAC/BG,EAAY1J,EAAM,WAAa,CAACA,EAAM,UAAY,CAACA,EAAM,SAGzD2J,EAAcN,EAAS,QAAQ,YAAc,CAAA,EAC7CO,EAAoB5B,GAAS,OAAOnkC,GAAKA,EAAE,OAAO8lC,CAAW,EAG7DE,EAAY,IAAI,IAAI7B,GAAS,IAAInkC,GAAKA,EAAE,GAAG,CAAC,EAC5CimC,EAAgB,OAAO,KAAKH,CAAW,EAC1C,OAAOzjC,GAAK,CAAC2jC,EAAU,IAAI3jC,CAAC,CAAC,EAC7B,IAAIA,IAAM,CAAE,IAAKA,EAAG,MAAOA,EAAE,OAAO,CAAC,EAAE,YAAA,EAAgBA,EAAE,MAAM,CAAC,GAAI,EAEjE6jC,EAAc,CAAC,GAAGH,EAAmB,GAAGE,CAAa,EAErDE,EACJhK,EAAM,eAAiBqJ,EAAS,QAAUrH,GAAWqH,EAAS,MAAM,IAAM,SACrEA,EAAS,OAAO,aAAarJ,EAAM,aAAa,EACjD,OACAiK,EAAoBjK,EAAM,cAC5BkI,GAAmBlI,EAAM,cAAegK,CAAmB,EAC3D,KACEE,EAAclK,EAAM,cACtBmI,GAAmB,CACjB,IAAKnI,EAAM,cACX,OAAQgK,EACR,QAAShK,EAAM,OAAA,CAChB,EACD,CAAA,EACEmK,EACJnK,EAAM,WAAa,QACnB,EAAQA,EAAM,eACdkK,EAAY,OAAS,EACjBE,EAAkBpK,EAAM,mBAAqBiI,GAC7CoC,EAAsBrK,EAAM,aAE9BoK,EADA,KAGEpK,EAAM,kBAAqBkK,EAAY,CAAC,GAAG,KAAO,KAGlD1gC,EAAOw2B,EAAM,WAAa,OAC5BuI,GAAYvI,EAAM,cAAeA,EAAM,SAAS,EAChD,CAAA,EACEsK,EAAa9gC,EAAK,OAAS,EAEjC,OAAOoiB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,uCAM8Bwd,IAAa,QAAU,WAAaA,IAAa,UAAY,eAAiB,EAAE,KAAKA,CAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAa/GpJ,EAAM,WAAW;AAAA,qBAChBp8B,GAAao8B,EAAM,eAAgBp8B,EAAE,OAA4B,KAAK,CAAC;AAAA;AAAA,YAEjFo8B,EAAM,YAAcpU;AAAAA;AAAAA;AAAAA,uBAGT,IAAMoU,EAAM,eAAe,EAAE,CAAC;AAAA;AAAA,YAEvC3B,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAMiB2B,EAAM,gBAAkB,KAAO,SAAW,EAAE;AAAA,qBAC7D,IAAMA,EAAM,gBAAgB,IAAI,CAAC;AAAA;AAAA,6CAET+H,GAAa,GAAG;AAAA;AAAA;AAAA,YAGjDgC,EAAY,IAAIQ,GAAW3e;AAAAA;AAAAA,wCAECoU,EAAM,gBAAkBuK,EAAQ,IAAM,SAAW,EAAE;AAAA,uBACpE,IAAMvK,EAAM,gBAAgBuK,EAAQ,GAAG,CAAC;AAAA;AAAA,+CAEhBvE,GAAeuE,EAAQ,GAAG,CAAC;AAAA,gDAC1BA,EAAQ,KAAK;AAAA;AAAA,WAElD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+CAOmCvK,EAAM,WAAa,OAAS,SAAW,EAAE;AAAA,0BAC9DA,EAAM,eAAiB,CAACA,EAAM,MAAM;AAAA,uBACvC,IAAMA,EAAM,iBAAiB,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,+CAKZA,EAAM,WAAa,MAAQ,SAAW,EAAE;AAAA,uBAChE,IAAMA,EAAM,iBAAiB,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAa5CsK,EAAa1e;AAAAA,mDACwBpiB,EAAK,MAAM,kBAAkBA,EAAK,SAAW,EAAI,IAAM,EAAE;AAAA,cAC5FoiB;AAAAA;AAAAA,aAEH;AAAA;AAAA;AAAA,oDAGuCoU,EAAM,OAAO,WAAWA,EAAM,QAAQ;AAAA,gBAC1EA,EAAM,QAAU,WAAa,QAAQ;AAAA;AAAA;AAAA;AAAA,0BAI3B,CAACwJ,CAAO;AAAA,uBACXxJ,EAAM,MAAM;AAAA;AAAA,gBAEnBA,EAAM,OAAS,UAAY,MAAM;AAAA;AAAA;AAAA;AAAA,0BAIvB,CAACyJ,CAAQ;AAAA,uBACZzJ,EAAM,OAAO;AAAA;AAAA,gBAEpBA,EAAM,SAAW,YAAc,OAAO;AAAA;AAAA;AAAA;AAAA,0BAI5B,CAAC0J,CAAS;AAAA,uBACb1J,EAAM,QAAQ;AAAA;AAAA,gBAErBA,EAAM,SAAW,YAAc,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAM7CsK,EAAa1e;AAAAA;AAAAA;AAAAA,2BAGIpiB,EAAK,MAAM,kBAAkBA,EAAK,SAAW,EAAI,IAAM,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAMpEA,EAAK,IAAIghC,GAAU5e;AAAAA;AAAAA,mDAEgB4e,EAAO,IAAI;AAAA;AAAA,sDAERxB,GAAcwB,EAAO,IAAI,CAAC;AAAA;AAAA,oDAE5BxB,GAAcwB,EAAO,EAAE,CAAC;AAAA;AAAA;AAAA,eAG7D,CAAC;AAAA;AAAA;AAAA,UAGJnM,CAAO;AAAA;AAAA,UAET4L,GAAqBjK,EAAM,WAAa,OACtCpU;AAAAA;AAAAA,yDAE6Coa,GAAehG,EAAM,eAAiB,EAAE,CAAC;AAAA;AAAA,4DAEtCiK,EAAkB,KAAK;AAAA,oBAC/DA,EAAkB,YAChBre,2CAA8Cqe,EAAkB,WAAW,SAC3E5L,CAAO;AAAA;AAAA;AAAA,cAIjBA,CAAO;AAAA;AAAA,UAET8L,EACEve;AAAAA;AAAAA;AAAAA,+CAGmCye,IAAwB,KAAO,SAAW,EAAE;AAAA,2BAChE,IAAMrK,EAAM,mBAAmBiI,EAAc,CAAC;AAAA;AAAA;AAAA;AAAA,kBAIvDiC,EAAY,IACX97B,GAAUwd;AAAAA;AAAAA,mDAGLye,IAAwBj8B,EAAM,IAAM,SAAW,EACjD;AAAA,8BACQA,EAAM,aAAeA,EAAM,KAAK;AAAA,+BAC/B,IAAM4xB,EAAM,mBAAmB5xB,EAAM,GAAG,CAAC;AAAA;AAAA,wBAEhDA,EAAM,KAAK;AAAA;AAAA,mBAAA,CAGlB;AAAA;AAAA,cAGLiwB,CAAO;AAAA;AAAA;AAAA;AAAA,YAIP2B,EAAM,WAAa,OACjBpU;AAAAA,kBACIoU,EAAM,cACJpU;AAAAA;AAAAA;AAAAA,4BAIA0a,GAAiB,CACf,OAAQ+C,EAAS,OACjB,QAASrJ,EAAM,QACf,MAAOA,EAAM,UACb,SAAUA,EAAM,SAAW,CAACA,EAAM,UAClC,iBAAkBqJ,EAAS,iBAC3B,QAASrJ,EAAM,YACf,YAAaA,EAAM,YACnB,cAAeA,EAAM,cACrB,iBAAkBqK,CAAA,CACnB,CAAC;AAAA,kBACJf,EACE1d;AAAAA;AAAAA;AAAAA,4BAIAyS,CAAO;AAAA,gBAEbzS;AAAAA;AAAAA;AAAAA;AAAAA,6BAIeoU,EAAM,GAAG;AAAA,6BACRp8B,GACRo8B,EAAM,YAAap8B,EAAE,OAA+B,KAAK,CAAC;AAAA;AAAA;AAAA,eAGjE;AAAA;AAAA;AAAA,UAGLo8B,EAAM,OAAO,OAAS,EACpBpU;AAAAA,wCAC4B,KAAK,UAAUoU,EAAM,OAAQ,KAAM,CAAC,CAAC;AAAA,oBAEjE3B,CAAO;AAAA;AAAA;AAAA,GAInB,CC5cO,SAASoM,GAAenhC,EAAoB,CACjD,GAAI,CAACA,GAAMA,IAAO,EAAG,MAAO,MAC5B,MAAMG,EAAM,KAAK,MAAMH,EAAK,GAAI,EAChC,GAAIG,EAAM,GAAI,MAAO,GAAGA,CAAG,IAC3B,MAAMC,EAAM,KAAK,MAAMD,EAAM,EAAE,EAC/B,OAAIC,EAAM,GAAW,GAAGA,CAAG,IAEpB,GADI,KAAK,MAAMA,EAAM,EAAE,CAClB,GACd,CAEO,SAASghC,GAAe98B,EAAiBoyB,EAAsB,CACpE,MAAMnuB,EAAWmuB,EAAM,SACjB2K,EAAW94B,GAAU,SAC3B,GAAI,CAACA,GAAY,CAAC84B,EAAU,MAAO,GACnC,MAAMC,EAAgBD,EAAS/8B,CAAG,EAC5B+X,EAAa,OAAOilB,GAAe,YAAe,WAAaA,EAAc,WAC7EC,EAAU,OAAOD,GAAe,SAAY,WAAaA,EAAc,QACvEE,EAAY,OAAOF,GAAe,WAAc,WAAaA,EAAc,UAE3EG,GADWl5B,EAAS,kBAAkBjE,CAAG,GAAK,CAAA,GACrB,KAC5Bo9B,GAAYA,EAAQ,YAAcA,EAAQ,SAAWA,EAAQ,SAAA,EAEhE,OAAOrlB,GAAcklB,GAAWC,GAAaC,CAC/C,CAEO,SAASE,GACdr9B,EACAs9B,EACQ,CACR,OAAOA,IAAkBt9B,CAAG,GAAG,QAAU,CAC3C,CAEO,SAASu9B,GACdv9B,EACAs9B,EACA,CACA,MAAME,EAAQH,GAAuBr9B,EAAKs9B,CAAe,EACzD,OAAIE,EAAQ,EAAU/M,EACfzS,yCAA4Cwf,CAAK,SAC1D,CCxBA,SAASC,GACPpJ,EACA55B,EACmB,CACnB,IAAIiF,EAAU20B,EACd,UAAWr0B,KAAOvF,EAAM,CACtB,GAAI,CAACiF,EAAS,OAAO,KACrB,MAAM+1B,EAAOrB,GAAW10B,CAAO,EAC/B,GAAI+1B,IAAS,SAAU,CACrB,MAAMkD,EAAaj5B,EAAQ,YAAc,CAAA,EACzC,GAAI,OAAOM,GAAQ,UAAY24B,EAAW34B,CAAG,EAAG,CAC9CN,EAAUi5B,EAAW34B,CAAG,EACxB,QACF,CACA,MAAMw3B,EAAa93B,EAAQ,qBAC3B,GAAI,OAAOM,GAAQ,UAAYw3B,GAAc,OAAOA,GAAe,SAAU,CAC3E93B,EAAU83B,EACV,QACF,CACA,OAAO,IACT,CACA,GAAI/B,IAAS,QAAS,CACpB,GAAI,OAAOz1B,GAAQ,SAAU,OAAO,KAEpCN,GADc,MAAM,QAAQA,EAAQ,KAAK,EAAIA,EAAQ,MAAM,CAAC,EAAIA,EAAQ,QACrD,KACnB,QACF,CACA,OAAO,IACT,CACA,OAAOA,CACT,CAEA,SAASg+B,GACPC,EACAC,EACyB,CAEzB,MAAMC,GADYF,EAAO,UAAY,CAAA,GACPC,CAAS,EACjCrhC,EAAWohC,EAAOC,CAAS,EAQjC,OANGC,GAAgB,OAAOA,GAAiB,SACpCA,EACD,QACHthC,GAAY,OAAOA,GAAa,SAC5BA,EACD,OACa,CAAA,CACrB,CAEO,SAASuhC,GAAwB1L,EAA+B,CACrE,MAAMqJ,EAAW/B,GAAoBtH,EAAM,MAAM,EAC3Ct3B,EAAa2gC,EAAS,OAC5B,GAAI,CAAC3gC,EACH,OAAOkjB,kEAET,MAAMiH,EAAOwY,GAAkB3iC,EAAY,CAAC,WAAYs3B,EAAM,SAAS,CAAC,EACxE,GAAI,CAACnN,EACH,OAAOjH,wEAET,MAAM+f,EAAc3L,EAAM,aAAe,CAAA,EACnCn5B,EAAQykC,GAAoBK,EAAa3L,EAAM,SAAS,EAC9D,OAAOpU;AAAAA;AAAAA,QAEDoX,GAAW,CACX,OAAQnQ,EACR,MAAAhsB,EACA,KAAM,CAAC,WAAYm5B,EAAM,SAAS,EAClC,MAAOA,EAAM,QACb,YAAa,IAAI,IAAIqJ,EAAS,gBAAgB,EAC9C,SAAUrJ,EAAM,SAChB,UAAW,GACX,QAASA,EAAM,OAAA,CAChB,CAAC;AAAA;AAAA,GAGR,CAEO,SAAS4L,GAA2Bp+B,EAGxC,CACD,KAAM,CAAE,UAAAg+B,EAAW,MAAAxL,CAAA,EAAUxyB,EACvB01B,EAAWlD,EAAM,cAAgBA,EAAM,oBAC7C,OAAOpU;AAAAA;AAAAA,QAEDoU,EAAM,oBACJpU,mDACA8f,GAAwB,CACtB,UAAAF,EACA,YAAaxL,EAAM,WACnB,OAAQA,EAAM,aACd,QAASA,EAAM,cACf,SAAAkD,EACA,QAASlD,EAAM,aAAA,CAChB,CAAC;AAAA;AAAA;AAAA;AAAA,sBAIUkD,GAAY,CAAClD,EAAM,eAAe;AAAA,mBACrC,IAAMA,EAAM,aAAA,CAAc;AAAA;AAAA,YAEjCA,EAAM,aAAe,UAAY,MAAM;AAAA;AAAA;AAAA;AAAA,sBAI7BkD,CAAQ;AAAA,mBACX,IAAMlD,EAAM,eAAA,CAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAO/C,CC9HO,SAAS6L,GAAkBr+B,EAI/B,CACD,KAAM,CAAE,MAAAwyB,EAAO,QAAA8L,EAAS,kBAAAC,CAAA,EAAsBv+B,EAE9C,OAAOoe;AAAAA;AAAAA;AAAAA;AAAAA,QAIDmgB,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKPD,GAAS,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIlCA,GAAS,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAI/BA,GAAS,YAAcviC,EAAUuiC,EAAQ,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,kBAI7DA,GAAS,YAAcviC,EAAUuiC,EAAQ,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,QAIvEA,GAAS,UACPlgB;AAAAA,cACIkgB,EAAQ,SAAS;AAAA,kBAErBzN,CAAO;AAAA;AAAA,QAETyN,GAAS,MACPlgB;AAAAA,oBACUkgB,EAAQ,MAAM,GAAK,KAAO,QAAQ;AAAA,cACxCA,EAAQ,MAAM,QAAU,EAAE,IAAIA,EAAQ,MAAM,OAAS,EAAE;AAAA,kBAE3DzN,CAAO;AAAA;AAAA,QAETuN,GAA2B,CAAE,UAAW,UAAW,MAAA5L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG9B,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CCtDO,SAASgM,GAAmBx+B,EAIhC,CACD,KAAM,CAAE,MAAAwyB,EAAO,SAAAiM,EAAU,kBAAAF,CAAA,EAAsBv+B,EAE/C,OAAOoe;AAAAA;AAAAA;AAAAA;AAAAA,QAIDmgB,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKPE,GAAU,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAInCA,GAAU,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIhCA,GAAU,YAAc1iC,EAAU0iC,EAAS,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,kBAI/DA,GAAU,YAAc1iC,EAAU0iC,EAAS,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,QAIzEA,GAAU,UACRrgB;AAAAA,cACIqgB,EAAS,SAAS;AAAA,kBAEtB5N,CAAO;AAAA;AAAA,QAET4N,GAAU,MACRrgB;AAAAA,oBACUqgB,EAAS,MAAM,GAAK,KAAO,QAAQ;AAAA,cACzCA,EAAS,MAAM,OAAS,EAAE;AAAA,kBAE9B5N,CAAO;AAAA;AAAA,QAETuN,GAA2B,CAAE,UAAW,WAAY,MAAA5L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG/B,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CCXA,SAASkM,GAAYt/B,EAAuC,CAC1D,KAAM,CAAE,OAAA9C,EAAQ,SAAA0+B,CAAA,EAAa57B,EAC7B,OACE9C,EAAO,OAAS0+B,EAAS,MACzB1+B,EAAO,cAAgB0+B,EAAS,aAChC1+B,EAAO,QAAU0+B,EAAS,OAC1B1+B,EAAO,UAAY0+B,EAAS,SAC5B1+B,EAAO,SAAW0+B,EAAS,QAC3B1+B,EAAO,UAAY0+B,EAAS,SAC5B1+B,EAAO,QAAU0+B,EAAS,OAC1B1+B,EAAO,QAAU0+B,EAAS,KAE9B,CAMO,SAAS2D,GAAuB3+B,EAIpB,CACjB,KAAM,CAAE,MAAAZ,EAAO,UAAAw/B,EAAW,UAAAC,CAAA,EAAc7+B,EAClC8+B,EAAUJ,GAAYt/B,CAAK,EAE3B2/B,EAAc,CAClBC,EACArhC,EACA4J,EAKI,CAAA,IACD,CACH,KAAM,CAAE,KAAAsuB,EAAO,OAAQ,YAAAsB,EAAa,UAAA79B,EAAW,KAAAw8B,GAASvuB,EAClDlO,EAAQ+F,EAAM,OAAO4/B,CAAK,GAAK,GAC/Bt/B,EAAQN,EAAM,YAAY4/B,CAAK,EAE/BC,EAAU,iBAAiBD,CAAK,GAEtC,OAAInJ,IAAS,WACJzX;AAAAA;AAAAA,wBAEW6gB,CAAO;AAAA,cACjBthC,CAAK;AAAA;AAAA;AAAA,kBAGDshC,CAAO;AAAA,qBACJ5lC,CAAK;AAAA,0BACA89B,GAAe,EAAE;AAAA,wBACnB79B,GAAa,GAAI;AAAA;AAAA;AAAA,qBAGnBlD,GAAkB,CAC1B,MAAMkM,EAASlM,EAAE,OACjBwoC,EAAU,cAAcI,EAAO18B,EAAO,KAAK,CAC7C,CAAC;AAAA,wBACWlD,EAAM,MAAM;AAAA;AAAA,YAExB02B,EAAO1X,6EAAgF0X,CAAI,SAAWjF,CAAO;AAAA,YAC7GnxB,EAAQ0e,+EAAkF1e,CAAK,SAAWmxB,CAAO;AAAA;AAAA,QAKlHzS;AAAAA;AAAAA,sBAEW6gB,CAAO;AAAA,YACjBthC,CAAK;AAAA;AAAA;AAAA,gBAGDshC,CAAO;AAAA,iBACNpJ,CAAI;AAAA,mBACFx8B,CAAK;AAAA,wBACA89B,GAAe,EAAE;AAAA,sBACnB79B,GAAa,GAAG;AAAA;AAAA,mBAElBlD,GAAkB,CAC1B,MAAMkM,EAASlM,EAAE,OACjBwoC,EAAU,cAAcI,EAAO18B,EAAO,KAAK,CAC7C,CAAC;AAAA,sBACWlD,EAAM,MAAM;AAAA;AAAA,UAExB02B,EAAO1X,6EAAgF0X,CAAI,SAAWjF,CAAO;AAAA,UAC7GnxB,EAAQ0e,+EAAkF1e,CAAK,SAAWmxB,CAAO;AAAA;AAAA,KAGzH,EAEMqO,EAAuB,IAAM,CACjC,MAAMC,EAAU//B,EAAM,OAAO,QAC7B,OAAK+/B,EAEE/gB;AAAAA;AAAAA;AAAAA,gBAGK+gB,CAAO;AAAA;AAAA;AAAA,mBAGH/oC,GAAa,CACrB,MAAMgpC,EAAMhpC,EAAE,OACdgpC,EAAI,MAAM,QAAU,MACtB,CAAC;AAAA,kBACQhpC,GAAa,CACpB,MAAMgpC,EAAMhpC,EAAE,OACdgpC,EAAI,MAAM,QAAU,OACtB,CAAC;AAAA;AAAA;AAAA,MAfcvO,CAmBvB,EAEA,OAAOzS;AAAAA;AAAAA;AAAAA;AAAAA,2EAIkEygB,CAAS;AAAA;AAAA;AAAA,QAG5Ez/B,EAAM,MACJgf,6DAAgEhf,EAAM,KAAK,SAC3EyxB,CAAO;AAAA;AAAA,QAETzxB,EAAM,QACJgf,8DAAiEhf,EAAM,OAAO,SAC9EyxB,CAAO;AAAA;AAAA,QAETqO,GAAsB;AAAA;AAAA,QAEtBH,EAAY,OAAQ,WAAY,CAChC,YAAa,UACb,UAAW,IACX,KAAM,gCAAA,CACP,CAAC;AAAA;AAAA,QAEAA,EAAY,cAAe,eAAgB,CAC3C,YAAa,mBACb,UAAW,IACX,KAAM,wBAAA,CACP,CAAC;AAAA;AAAA,QAEAA,EAAY,QAAS,MAAO,CAC5B,KAAM,WACN,YAAa,gCACb,UAAW,IACX,KAAM,4BAAA,CACP,CAAC;AAAA;AAAA,QAEAA,EAAY,UAAW,aAAc,CACrC,KAAM,MACN,YAAa,iCACb,KAAM,mCAAA,CACP,CAAC;AAAA;AAAA,QAEA3/B,EAAM,aACJgf;AAAAA;AAAAA;AAAAA;AAAAA,gBAIM2gB,EAAY,SAAU,aAAc,CACpC,KAAM,MACN,YAAa,iCACb,KAAM,6BAAA,CACP,CAAC;AAAA;AAAA,gBAEAA,EAAY,UAAW,UAAW,CAClC,KAAM,MACN,YAAa,sBACb,KAAM,uBAAA,CACP,CAAC;AAAA;AAAA,gBAEAA,EAAY,QAAS,oBAAqB,CAC1C,YAAa,kBACb,KAAM,8CAAA,CACP,CAAC;AAAA;AAAA,gBAEAA,EAAY,QAAS,oBAAqB,CAC1C,YAAa,kBACb,KAAM,qCAAA,CACP,CAAC;AAAA;AAAA,YAGNlO,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKE+N,EAAU,MAAM;AAAA,sBACbx/B,EAAM,QAAU,CAAC0/B,CAAO;AAAA;AAAA,YAElC1/B,EAAM,OAAS,YAAc,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKtCw/B,EAAU,QAAQ;AAAA,sBACfx/B,EAAM,WAAaA,EAAM,MAAM;AAAA;AAAA,YAEzCA,EAAM,UAAY,eAAiB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKhDw/B,EAAU,gBAAgB;AAAA;AAAA,YAEjCx/B,EAAM,aAAe,gBAAkB,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,mBAK/Cw/B,EAAU,QAAQ;AAAA,sBACfx/B,EAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAM1B0/B,EACE1gB;AAAAA;AAAAA,kBAGAyS,CAAO;AAAA;AAAA,GAGjB,CASO,SAASwO,GACdC,EACuB,CACvB,MAAMhjC,EAA2B,CAC/B,KAAMgjC,GAAS,MAAQ,GACvB,YAAaA,GAAS,aAAe,GACrC,MAAOA,GAAS,OAAS,GACzB,QAASA,GAAS,SAAW,GAC7B,OAAQA,GAAS,QAAU,GAC3B,QAASA,GAAS,SAAW,GAC7B,MAAOA,GAAS,OAAS,GACzB,MAAOA,GAAS,OAAS,EAAA,EAG3B,MAAO,CACL,OAAAhjC,EACA,SAAU,CAAE,GAAGA,CAAA,EACf,OAAQ,GACR,UAAW,GACX,MAAO,KACP,QAAS,KACT,YAAa,CAAA,EACb,aAAc,GACZgjC,GAAS,QAAUA,GAAS,SAAWA,GAAS,OAASA,GAAS,MACpE,CAEJ,CCxSA,SAASC,GAAeC,EAA2C,CACjE,OAAKA,EACDA,EAAO,QAAU,GAAWA,EACzB,GAAGA,EAAO,MAAM,EAAG,CAAC,CAAC,MAAMA,EAAO,MAAM,EAAE,CAAC,GAF9B,KAGtB,CAEO,SAASC,GAAgBz/B,EAW7B,CACD,KAAM,CACJ,MAAAwyB,EACA,MAAAkN,EACA,cAAAC,EACA,kBAAApB,EACA,iBAAAqB,EACA,qBAAAC,EACA,cAAAC,CAAA,EACE9/B,EACE+/B,EAAiBJ,EAAc,CAAC,EAChCK,EAAoBN,GAAO,YAAcK,GAAgB,YAAc,GACvEE,EAAiBP,GAAO,SAAWK,GAAgB,SAAW,GAC9DG,EACJR,GAAO,WACNK,GAAuD,UACpDI,EAAqBT,GAAO,aAAeK,GAAgB,aAAe,KAC1EK,EAAmBV,GAAO,WAAaK,GAAgB,WAAa,KACpEM,EAAsBV,EAAc,OAAS,EAC7CW,EAAcV,GAAqB,KAEnCW,EAAqB/C,GAAoC,CAC7D,MAAMvrB,EAAaurB,EAAmC,UAChD8B,EAAW9B,EAAkE,QAC7EgD,EAAclB,GAAS,aAAeA,GAAS,MAAQ9B,EAAQ,MAAQA,EAAQ,UAErF,OAAOpf;AAAAA;AAAAA;AAAAA,4CAGiCoiB,CAAW;AAAA,yCACdhD,EAAQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,oBAKtCA,EAAQ,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,oBAI9BA,EAAQ,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,6CAIRvrB,GAAa,EAAE,KAAKstB,GAAettB,CAAS,CAAC;AAAA;AAAA;AAAA;AAAA,oBAItEurB,EAAQ,cAAgBzhC,EAAUyhC,EAAQ,aAAa,EAAI,KAAK;AAAA;AAAA,YAExEA,EAAQ,UACNpf;AAAAA,kDACoCof,EAAQ,SAAS;AAAA,gBAErD3M,CAAO;AAAA;AAAA;AAAA,KAInB,EAEM4P,EAAuB,IAAM,CAEjC,GAAIH,GAAeT,EACjB,OAAOlB,GAAuB,CAC5B,MAAOiB,EACP,UAAWC,EACX,UAAWF,EAAc,CAAC,GAAG,WAAa,SAAA,CAC3C,EAGH,MAAML,EACHS,GAUe,SAAWL,GAAO,QAC9B,CAAE,KAAAhmC,EAAM,YAAA8mC,EAAa,MAAAE,EAAO,QAAAvB,EAAS,MAAAwB,EAAA,EAAUrB,GAAW,CAAA,EAC1DsB,GAAoBlnC,GAAQ8mC,GAAeE,GAASvB,GAAWwB,GAErE,OAAOviB;AAAAA;AAAAA;AAAAA;AAAAA,YAIC4hB,EACE5hB;AAAAA;AAAAA;AAAAA,2BAGa0hB,CAAa;AAAA;AAAA;AAAA;AAAA;AAAA,gBAM1BjP,CAAO;AAAA;AAAA,UAEX+P,GACExiB;AAAAA;AAAAA,kBAEM+gB,EACE/gB;AAAAA;AAAAA;AAAAA,gCAGY+gB,CAAO;AAAA;AAAA;AAAA,mCAGH/oC,IAAa,CACpBA,GAAE,OAA4B,MAAM,QAAU,MACjD,CAAC;AAAA;AAAA;AAAA,sBAIPy6B,CAAO;AAAA,kBACTn3B,EAAO0kB,8CAAiD1kB,CAAI,gBAAkBm3B,CAAO;AAAA,kBACrF2P,EACEpiB,sDAAyDoiB,CAAW,gBACpE3P,CAAO;AAAA,kBACT6P,EACEtiB,oHAAuHsiB,CAAK,gBAC5H7P,CAAO;AAAA,kBACT8P,GAAQviB,gDAAmDuiB,EAAK,gBAAkB9P,CAAO;AAAA;AAAA,cAG/FzS;AAAAA;AAAAA;AAAAA;AAAAA,aAIC;AAAA;AAAA,KAGX,EAEA,OAAOA;AAAAA;AAAAA;AAAAA;AAAAA,QAIDmgB,CAAiB;AAAA;AAAA,QAEjB8B,EACEjiB;AAAAA;AAAAA,gBAEMuhB,EAAc,IAAKnC,GAAY+C,EAAkB/C,CAAO,CAAC,CAAC;AAAA;AAAA,YAGhEpf;AAAAA;AAAAA;AAAAA;AAAAA,wBAIc4hB,EAAoB,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,wBAIhCC,EAAiB,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,iDAIJC,GAAoB,EAAE;AAAA,qBAClDX,GAAeW,CAAgB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,wBAK7BC,EAAqBpkC,EAAUokC,CAAkB,EAAI,KAAK;AAAA;AAAA;AAAA,WAGvE;AAAA;AAAA,QAEHC,EACEhiB,0DAA6DgiB,CAAgB,SAC7EvP,CAAO;AAAA;AAAA,QAET4P,GAAsB;AAAA;AAAA,QAEtBrC,GAA2B,CAAE,UAAW,QAAS,MAAA5L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG5B,IAAMA,EAAM,UAAU,EAAK,CAAC;AAAA;AAAA;AAAA,GAIjE,CCjNO,SAASqO,GAAiB7gC,EAI9B,CACD,KAAM,CAAE,MAAAwyB,EAAO,OAAAsO,EAAQ,kBAAAvC,CAAA,EAAsBv+B,EAE7C,OAAOoe;AAAAA;AAAAA;AAAAA;AAAAA,QAIDmgB,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKPuC,GAAQ,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIjCA,GAAQ,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAI9BA,GAAQ,SAAW,KAAK;AAAA;AAAA;AAAA;AAAA,kBAIxBA,GAAQ,YAAc/kC,EAAU+kC,EAAO,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,kBAI3DA,GAAQ,YAAc/kC,EAAU+kC,EAAO,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,QAIrEA,GAAQ,UACN1iB;AAAAA,cACI0iB,EAAO,SAAS;AAAA,kBAEpBjQ,CAAO;AAAA;AAAA,QAETiQ,GAAQ,MACN1iB;AAAAA,oBACU0iB,EAAO,MAAM,GAAK,KAAO,QAAQ;AAAA,cACvCA,EAAO,MAAM,QAAU,EAAE,IAAIA,EAAO,MAAM,OAAS,EAAE;AAAA,kBAEzDjQ,CAAO;AAAA;AAAA,QAETuN,GAA2B,CAAE,UAAW,SAAU,MAAA5L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG7B,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CC1DO,SAASuO,GAAgB/gC,EAI7B,CACD,KAAM,CAAE,MAAAwyB,EAAO,MAAAwO,EAAO,kBAAAzC,CAAA,EAAsBv+B,EAE5C,OAAOoe;AAAAA;AAAAA;AAAAA;AAAAA,QAIDmgB,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKPyC,GAAO,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIhCA,GAAO,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAI7BA,GAAO,YAAcjlC,EAAUilC,EAAM,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,kBAIzDA,GAAO,YAAcjlC,EAAUilC,EAAM,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,QAInEA,GAAO,UACL5iB;AAAAA,cACI4iB,EAAM,SAAS;AAAA,kBAEnBnQ,CAAO;AAAA;AAAA,QAETmQ,GAAO,MACL5iB;AAAAA,oBACU4iB,EAAM,MAAM,GAAK,KAAO,QAAQ;AAAA,cACtCA,EAAM,MAAM,QAAU,EAAE,IAAIA,EAAM,MAAM,OAAS,EAAE;AAAA,kBAEvDnQ,CAAO;AAAA;AAAA,QAETuN,GAA2B,CAAE,UAAW,QAAS,MAAA5L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG5B,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CCtDO,SAASyO,GAAmBjhC,EAKhC,CACD,KAAM,CAAE,MAAAwyB,EAAO,SAAA0O,EAAU,iBAAAC,EAAkB,kBAAA5C,GAAsBv+B,EAC3DqgC,EAAsBc,EAAiB,OAAS,EAEhDZ,EAAqB/C,GAAoC,CAE7D,MAAM4D,EADQ5D,EAAQ,OACK,KAAK,SAC1B7/B,EAAQ6/B,EAAQ,MAAQA,EAAQ,UACtC,OAAOpf;AAAAA;AAAAA;AAAAA;AAAAA,cAIGgjB,EAAc,IAAIA,CAAW,GAAKzjC,CAAK;AAAA;AAAA,yCAEZ6/B,EAAQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,oBAKtCA,EAAQ,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,oBAI9BA,EAAQ,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,oBAIjCA,EAAQ,cAAgBzhC,EAAUyhC,EAAQ,aAAa,EAAI,KAAK;AAAA;AAAA,YAExEA,EAAQ,UACNpf;AAAAA;AAAAA,oBAEMof,EAAQ,SAAS;AAAA;AAAA,gBAGvB3M,CAAO;AAAA;AAAA;AAAA,KAInB,EAEA,OAAOzS;AAAAA;AAAAA;AAAAA;AAAAA,QAIDmgB,CAAiB;AAAA;AAAA,QAEjB8B,EACEjiB;AAAAA;AAAAA,gBAEM+iB,EAAiB,IAAK3D,GAAY+C,EAAkB/C,CAAO,CAAC,CAAC;AAAA;AAAA,YAGnEpf;AAAAA;AAAAA;AAAAA;AAAAA,wBAIc8iB,GAAU,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,wBAInCA,GAAU,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,wBAIhCA,GAAU,MAAQ,KAAK;AAAA;AAAA;AAAA;AAAA,wBAIvBA,GAAU,YAAcnlC,EAAUmlC,EAAS,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA,wBAI/DA,GAAU,YAAcnlC,EAAUmlC,EAAS,WAAW,EAAI,KAAK;AAAA;AAAA;AAAA,WAG5E;AAAA;AAAA,QAEHA,GAAU,UACR9iB;AAAAA,cACI8iB,EAAS,SAAS;AAAA,kBAEtBrQ,CAAO;AAAA;AAAA,QAETqQ,GAAU,MACR9iB;AAAAA,oBACU8iB,EAAS,MAAM,GAAK,KAAO,QAAQ;AAAA,cACzCA,EAAS,MAAM,QAAU,EAAE,IAAIA,EAAS,MAAM,OAAS,EAAE;AAAA,kBAE7DrQ,CAAO;AAAA;AAAA,QAETuN,GAA2B,CAAE,UAAW,WAAY,MAAA5L,CAAA,CAAO,CAAC;AAAA;AAAA;AAAA,qCAG/B,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMhE,CCxGO,SAAS6O,GAAmBrhC,EAIhC,CACD,KAAM,CAAE,MAAAwyB,EAAO,SAAA8O,EAAU,kBAAA/C,CAAA,EAAsBv+B,EAE/C,OAAOoe;AAAAA;AAAAA;AAAAA;AAAAA,QAIDmgB,CAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKP+C,GAAU,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAInCA,GAAU,OAAS,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAI/BA,GAAU,QAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIhCA,GAAU,UAAY,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,cAKtCA,GAAU,gBACRvlC,EAAUulC,EAAS,eAAe,EAClC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAMPA,GAAU,cAAgBvlC,EAAUulC,EAAS,aAAa,EAAI,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAMnEA,GAAU,WAAa,KACrBrE,GAAeqE,EAAS,SAAS,EACjC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKbA,GAAU,UACRljB;AAAAA,cACIkjB,EAAS,SAAS;AAAA,kBAEtBzQ,CAAO;AAAA;AAAA,QAET2B,EAAM,gBACJpU;AAAAA,cACIoU,EAAM,eAAe;AAAA,kBAEzB3B,CAAO;AAAA;AAAA,QAET2B,EAAM,kBACJpU;AAAAA,uBACaoU,EAAM,iBAAiB;AAAA,kBAEpC3B,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKK2B,EAAM,YAAY;AAAA,mBACrB,IAAMA,EAAM,gBAAgB,EAAK,CAAC;AAAA;AAAA,YAEzCA,EAAM,aAAe,WAAa,SAAS;AAAA;AAAA;AAAA;AAAA,sBAIjCA,EAAM,YAAY;AAAA,mBACrB,IAAMA,EAAM,gBAAgB,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAM9BA,EAAM,YAAY;AAAA,mBACrB,IAAMA,EAAM,eAAA,CAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAMzBA,EAAM,YAAY;AAAA,mBACrB,IAAMA,EAAM,iBAAA,CAAkB;AAAA;AAAA;AAAA;AAAA,qCAIZ,IAAMA,EAAM,UAAU,EAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,QAKxD4L,GAA2B,CAAE,UAAW,WAAY,MAAA5L,CAAA,CAAO,CAAC;AAAA;AAAA,GAGpE,CCtFO,SAAS+O,GAAe/O,EAAsB,CACnD,MAAM2K,EAAW3K,EAAM,UAAU,SAC3B8O,EAAYnE,GAAU,UAAY,OAGlC+D,EAAY/D,GAAU,UAAY,OAGlCmB,EAAWnB,GAAU,SAAW,KAChC6D,EAAS7D,GAAU,OAAS,KAC5B2D,EAAU3D,GAAU,QAAU,KAC9BsB,EAAYtB,GAAU,UAAY,KAClCuC,EAASvC,GAAU,OAAS,KAE5BqE,EADeC,GAAoBjP,EAAM,QAAQ,EAEpD,IAAI,CAACpyB,EAAKid,KAAW,CACpB,IAAAjd,EACA,QAAS88B,GAAe98B,EAAKoyB,CAAK,EAClC,MAAOnV,CAAA,EACP,EACD,KAAK,CAACvmB,EAAGM,IACJN,EAAE,UAAYM,EAAE,QAAgBN,EAAE,QAAU,GAAK,EAC9CA,EAAE,MAAQM,EAAE,KACpB,EAEH,OAAOgnB;AAAAA;AAAAA,QAEDojB,EAAgB,IAAKE,GACrBC,GAAcD,EAAQ,IAAKlP,EAAO,CAChC,SAAA8O,EACA,SAAAJ,EACA,QAAA5C,EACA,MAAA0C,EACA,OAAAF,EACA,SAAArC,EACA,MAAAiB,EACA,gBAAiBlN,EAAM,UAAU,iBAAmB,IAAA,CACrD,CAAA,CACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BASsBA,EAAM,cAAgBz2B,EAAUy2B,EAAM,aAAa,EAAI,KAAK;AAAA;AAAA,QAEjFA,EAAM,UACJpU;AAAAA,cACIoU,EAAM,SAAS;AAAA,kBAEnB3B,CAAO;AAAA;AAAA,EAEf2B,EAAM,SAAW,KAAK,UAAUA,EAAM,SAAU,KAAM,CAAC,EAAI,kBAAkB;AAAA;AAAA;AAAA,GAI/E,CAEA,SAASiP,GAAoBp9B,EAAuD,CAClF,OAAIA,GAAU,aAAa,OAClBA,EAAS,YAAY,IAAKzD,GAAUA,EAAM,EAAE,EAEjDyD,GAAU,cAAc,OACnBA,EAAS,aAEX,CAAC,WAAY,WAAY,UAAW,QAAS,SAAU,WAAY,OAAO,CACnF,CAEA,SAASs9B,GACPvhC,EACAoyB,EACA3wB,EACA,CACA,MAAM08B,EAAoBZ,GACxBv9B,EACAyB,EAAK,eAAA,EAEP,OAAQzB,EAAA,CACN,IAAK,WACH,OAAOihC,GAAmB,CACxB,MAAA7O,EACA,SAAU3wB,EAAK,SACf,kBAAA08B,CAAA,CACD,EACH,IAAK,WACH,OAAO0C,GAAmB,CACxB,MAAAzO,EACA,SAAU3wB,EAAK,SACf,iBAAkBA,EAAK,iBAAiB,UAAY,CAAA,EACpD,kBAAA08B,CAAA,CACD,EACH,IAAK,UACH,OAAOF,GAAkB,CACvB,MAAA7L,EACA,QAAS3wB,EAAK,QACd,kBAAA08B,CAAA,CACD,EACH,IAAK,QACH,OAAOwC,GAAgB,CACrB,MAAAvO,EACA,MAAO3wB,EAAK,MACZ,kBAAA08B,CAAA,CACD,EACH,IAAK,SACH,OAAOsC,GAAiB,CACtB,MAAArO,EACA,OAAQ3wB,EAAK,OACb,kBAAA08B,CAAA,CACD,EACH,IAAK,WACH,OAAOC,GAAmB,CACxB,MAAAhM,EACA,SAAU3wB,EAAK,SACf,kBAAA08B,CAAA,CACD,EACH,IAAK,QAAS,CACZ,MAAMoB,EAAgB99B,EAAK,iBAAiB,OAAS,CAAA,EAC/Ck+B,EAAiBJ,EAAc,CAAC,EAChCd,EAAYkB,GAAgB,WAAa,UACzCT,EACHS,GAAkE,SAAW,KAC1E6B,EACJpP,EAAM,wBAA0BqM,EAAYrM,EAAM,sBAAwB,KACtEqN,EAAuB+B,EACzB,CACE,cAAepP,EAAM,0BACrB,OAAQA,EAAM,mBACd,SAAUA,EAAM,qBAChB,SAAUA,EAAM,qBAChB,iBAAkBA,EAAM,4BAAA,EAE1B,KACJ,OAAOiN,GAAgB,CACrB,MAAAjN,EACA,MAAO3wB,EAAK,MACZ,cAAA89B,EACA,kBAAApB,EACA,iBAAkBqD,EAClB,qBAAA/B,EACA,cAAe,IAAMrN,EAAM,mBAAmBqM,EAAWS,CAAO,CAAA,CACjE,CACH,CACA,QACE,OAAOuC,GAAyBzhC,EAAKoyB,EAAO3wB,EAAK,iBAAmB,CAAA,CAAE,CAAA,CAE5E,CAEA,SAASggC,GACPzhC,EACAoyB,EACAkL,EACA,CACA,MAAM//B,EAAQmkC,GAAoBtP,EAAM,SAAUpyB,CAAG,EAC/CgG,EAASosB,EAAM,UAAU,WAAWpyB,CAAG,EACvC+X,EAAa,OAAO/R,GAAQ,YAAe,UAAYA,EAAO,WAAa,OAC3Ei3B,EAAU,OAAOj3B,GAAQ,SAAY,UAAYA,EAAO,QAAU,OAClEk3B,EAAY,OAAOl3B,GAAQ,WAAc,UAAYA,EAAO,UAAY,OACxE27B,EAAY,OAAO37B,GAAQ,WAAc,SAAWA,EAAO,UAAY,OACvE47B,EAAWtE,EAAgBt9B,CAAG,GAAK,CAAA,EACnCm+B,EAAoBZ,GAA0Bv9B,EAAKs9B,CAAe,EAExE,OAAOtf;AAAAA;AAAAA,gCAEuBzgB,CAAK;AAAA;AAAA,QAE7B4gC,CAAiB;AAAA;AAAA,QAEjByD,EAAS,OAAS,EAChB5jB;AAAAA;AAAAA,gBAEM4jB,EAAS,IAAKxE,GAAYyE,GAAqBzE,CAAO,CAAC,CAAC;AAAA;AAAA,YAG9Dpf;AAAAA;AAAAA;AAAAA;AAAAA,wBAIcjG,GAAc,KAAO,MAAQA,EAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,wBAItDklB,GAAW,KAAO,MAAQA,EAAU,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,wBAIhDC,GAAa,KAAO,MAAQA,EAAY,MAAQ,IAAI;AAAA;AAAA;AAAA,WAGjE;AAAA;AAAA,QAEHyE,EACE3jB;AAAAA,cACI2jB,CAAS;AAAA,kBAEblR,CAAO;AAAA;AAAA,QAETuN,GAA2B,CAAE,UAAWh+B,EAAK,MAAAoyB,CAAA,CAAO,CAAC;AAAA;AAAA,GAG7D,CAEA,SAAS0P,GACP79B,EACoC,CACpC,OAAKA,GAAU,aAAa,OACrB,OAAO,YAAYA,EAAS,YAAY,IAAKzD,GAAU,CAACA,EAAM,GAAIA,CAAK,CAAC,CAAC,EADrC,CAAA,CAE7C,CAEA,SAASkhC,GACPz9B,EACAjE,EACQ,CAER,OADa8hC,GAAsB79B,CAAQ,EAAEjE,CAAG,GACnC,OAASiE,GAAU,gBAAgBjE,CAAG,GAAKA,CAC1D,CAEA,MAAM+hC,GAA+B,IAAU,IAE/C,SAASC,GAAkB5E,EAA0C,CACnE,OAAKA,EAAQ,cACN,KAAK,IAAA,EAAQA,EAAQ,cAAgB2E,GADT,EAErC,CAEA,SAASE,GAAoB7E,EAA0D,CACrF,OAAIA,EAAQ,QAAgB,MAExB4E,GAAkB5E,CAAO,EAAU,SAChC,IACT,CAEA,SAAS8E,GAAsB9E,EAAkE,CAC/F,OAAIA,EAAQ,YAAc,GAAa,MACnCA,EAAQ,YAAc,GAAc,KAEpC4E,GAAkB5E,CAAO,EAAU,SAChC,KACT,CAEA,SAASyE,GAAqBzE,EAAiC,CAC7D,MAAM+E,EAAgBF,GAAoB7E,CAAO,EAC3CgF,EAAkBF,GAAsB9E,CAAO,EAErD,OAAOpf;AAAAA;AAAAA;AAAAA,0CAGiCof,EAAQ,MAAQA,EAAQ,SAAS;AAAA,uCACpCA,EAAQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKtC+E,CAAa;AAAA;AAAA;AAAA;AAAA,kBAIb/E,EAAQ,WAAa,MAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,kBAIjCgF,CAAe;AAAA;AAAA;AAAA;AAAA,kBAIfhF,EAAQ,cAAgBzhC,EAAUyhC,EAAQ,aAAa,EAAI,KAAK;AAAA;AAAA,UAExEA,EAAQ,UACNpf;AAAAA;AAAAA,kBAEMof,EAAQ,SAAS;AAAA;AAAA,cAGvB3M,CAAO;AAAA;AAAA;AAAA,GAInB,CClTO,SAAS4R,GAAsB7hC,EAA8B,CAClE,MAAMO,EAAOP,EAAM,MAAQ,UACrB8hC,EAAK9hC,EAAM,GAAK,IAAIA,EAAM,EAAE,IAAM,GAClC0U,EAAO1U,EAAM,MAAQ,GACrB+hC,EAAU/hC,EAAM,SAAW,GACjC,MAAO,GAAGO,CAAI,IAAIuhC,CAAE,IAAIptB,CAAI,IAAIqtB,CAAO,GAAG,KAAA,CAC5C,CAEO,SAASC,GAAkBhiC,EAA8B,CAC9D,MAAMiiC,EAAKjiC,EAAM,IAAM,KACvB,OAAOiiC,EAAK9mC,EAAU8mC,CAAE,EAAI,KAC9B,CAEO,SAASC,GAAchnC,EAAoB,CAChD,OAAKA,EACE,GAAGD,GAASC,CAAE,CAAC,KAAKC,EAAUD,CAAE,CAAC,IADxB,KAElB,CAEO,SAASinC,GAAoB1P,EAAwB,CAC1D,GAAIA,EAAI,aAAe,KAAM,MAAO,MACpC,MAAM2P,EAAQ3P,EAAI,aAAe,EAC3B4P,EAAM5P,EAAI,eAAiB,EACjC,OAAO4P,EAAM,GAAGD,CAAK,MAAMC,CAAG,GAAK,OAAOD,CAAK,CACjD,CAEO,SAASE,GAAmBrjC,EAA0B,CAC3D,GAAIA,GAAW,KAAM,MAAO,GAC5B,GAAI,CACF,OAAO,KAAK,UAAUA,EAAS,KAAM,CAAC,CACxC,MAAQ,CACN,OAAO,OAAOA,CAAO,CACvB,CACF,CAEO,SAASsjC,GAAgB59B,EAAc,CAC5C,MAAMnG,EAAQmG,EAAI,OAAS,CAAA,EACrBpL,EAAOiF,EAAM,YAAcvD,GAASuD,EAAM,WAAW,EAAI,MACzDgkC,EAAOhkC,EAAM,YAAcvD,GAASuD,EAAM,WAAW,EAAI,MAE/D,MAAO,GADQA,EAAM,YAAc,KACnB,WAAWjF,CAAI,WAAWipC,CAAI,EAChD,CAEO,SAASC,GAAmB99B,EAAc,CAC/C,MAAMlP,EAAIkP,EAAI,SACd,OAAIlP,EAAE,OAAS,KAAa,MAAMwF,GAASxF,EAAE,IAAI,CAAC,GAC9CA,EAAE,OAAS,QAAgB,SAAS+F,GAAiB/F,EAAE,OAAO,CAAC,GAC5D,QAAQA,EAAE,IAAI,GAAGA,EAAE,GAAK,KAAKA,EAAE,EAAE,IAAM,EAAE,EAClD,CAEO,SAASitC,GAAkB/9B,EAAc,CAC9C,MAAMvO,EAAIuO,EAAI,QACd,OAAIvO,EAAE,OAAS,cAAsB,WAAWA,EAAE,IAAI,GAC/C,UAAUA,EAAE,OAAO,EAC5B,CCvBA,SAASusC,GAAoB/Q,EAA4B,CACvD,MAAM5d,EAAU,CAAC,OAAQ,GAAG4d,EAAM,SAAS,OAAO,OAAO,CAAC,EACpD1yB,EAAU0yB,EAAM,KAAK,SAAS,KAAA,EAChC1yB,GAAW,CAAC8U,EAAQ,SAAS9U,CAAO,GACtC8U,EAAQ,KAAK9U,CAAO,EAEtB,MAAM0jC,MAAW,IACjB,OAAO5uB,EAAQ,OAAQvb,GACjBmqC,EAAK,IAAInqC,CAAK,EAAU,IAC5BmqC,EAAK,IAAInqC,CAAK,EACP,GACR,CACH,CAEA,SAASyoC,GAAoBtP,EAAkBkP,EAAyB,CACtE,GAAIA,IAAY,OAAQ,MAAO,OAC/B,MAAM16B,EAAOwrB,EAAM,aAAa,KAAM5xB,GAAUA,EAAM,KAAO8gC,CAAO,EACpE,OAAI16B,GAAM,MAAcA,EAAK,MACtBwrB,EAAM,gBAAgBkP,CAAO,GAAKA,CAC3C,CAEO,SAAS+B,GAAWjR,EAAkB,CAC3C,MAAMkR,EAAiBH,GAAoB/Q,CAAK,EAChD,OAAOpU;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,gBASOoU,EAAM,OACJA,EAAM,OAAO,QACX,MACA,KACF,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,sCAKeA,EAAM,QAAQ,MAAQ,KAAK;AAAA;AAAA;AAAA;AAAA,sCAI3BsQ,GAActQ,EAAM,QAAQ,cAAgB,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,0CAI7CA,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,cACnEA,EAAM,QAAU,cAAgB,SAAS;AAAA;AAAA,YAE3CA,EAAM,MAAQpU,wBAA2BoU,EAAM,KAAK,UAAY3B,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAW5D2B,EAAM,KAAK,IAAI;AAAA,uBACdp8B,GACRo8B,EAAM,aAAa,CAAE,KAAOp8B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAM3Do8B,EAAM,KAAK,WAAW;AAAA,uBACrBp8B,GACRo8B,EAAM,aAAa,CAAE,YAAcp8B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAMlEo8B,EAAM,KAAK,OAAO;AAAA,uBACjBp8B,GACRo8B,EAAM,aAAa,CAAE,QAAUp8B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAQ5Do8B,EAAM,KAAK,OAAO;AAAA,wBAClBp8B,GACTo8B,EAAM,aAAa,CAAE,QAAUp8B,EAAE,OAA4B,QAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAMhEo8B,EAAM,KAAK,YAAY;AAAA,wBACrBp8B,GACTo8B,EAAM,aAAa,CACjB,aAAep8B,EAAE,OAA6B,KAAA,CAC/C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAQRutC,GAAqBnR,CAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,uBAKdA,EAAM,KAAK,aAAa;AAAA,wBACtBp8B,GACTo8B,EAAM,aAAa,CACjB,cAAgBp8B,EAAE,OAA6B,KAAA,CAChD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBASKo8B,EAAM,KAAK,QAAQ;AAAA,wBACjBp8B,GACTo8B,EAAM,aAAa,CACjB,SAAWp8B,EAAE,OAA6B,KAAA,CAC3C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBASKo8B,EAAM,KAAK,WAAW;AAAA,wBACpBp8B,GACTo8B,EAAM,aAAa,CACjB,YAAcp8B,EAAE,OAA6B,KAAA,CAC9C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAQAo8B,EAAM,KAAK,cAAgB,cAAgB,cAAgB,eAAe;AAAA;AAAA,qBAEvEA,EAAM,KAAK,WAAW;AAAA,qBACrBp8B,GACRo8B,EAAM,aAAa,CACjB,YAAcp8B,EAAE,OAA+B,KAAA,CAChD,CAAC;AAAA;AAAA;AAAA;AAAA,aAIHo8B,EAAM,KAAK,cAAgB,YAC3BpU;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,+BAMkBoU,EAAM,KAAK,OAAO;AAAA,8BAClBp8B,GACTo8B,EAAM,aAAa,CACjB,QAAUp8B,EAAE,OAA4B,OAAA,CACzC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAMMo8B,EAAM,KAAK,SAAW,MAAM;AAAA,+BAC1Bp8B,GACTo8B,EAAM,aAAa,CACjB,QAAUp8B,EAAE,OAA6B,KAAA,CAC1C,CAAC;AAAA;AAAA,uBAEFstC,EAAe,IACbhC,GACCtjB,kBAAqBsjB,CAAO;AAAA,8BACxBI,GAAoBtP,EAAOkP,CAAO,CAAC;AAAA,oCAAA,CAE1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAMMlP,EAAM,KAAK,EAAE;AAAA,6BACZp8B,GACRo8B,EAAM,aAAa,CAAE,GAAKp8B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAOzDo8B,EAAM,KAAK,cAAc;AAAA,6BACxBp8B,GACRo8B,EAAM,aAAa,CACjB,eAAiBp8B,EAAE,OAA4B,KAAA,CAChD,CAAC;AAAA;AAAA;AAAA,kBAGNo8B,EAAM,KAAK,gBAAkB,WAC3BpU;AAAAA;AAAAA;AAAAA;AAAAA,mCAIeoU,EAAM,KAAK,gBAAgB;AAAA,mCAC1Bp8B,GACRo8B,EAAM,aAAa,CACjB,iBAAmBp8B,EAAE,OAA4B,KAAA,CAClD,CAAC;AAAA;AAAA;AAAA,sBAIVy6B,CAAO;AAAA;AAAA,cAGfA,CAAO;AAAA;AAAA,kDAE+B2B,EAAM,IAAI,WAAWA,EAAM,KAAK;AAAA,cACpEA,EAAM,KAAO,UAAY,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QASxCA,EAAM,KAAK,SAAW,EACpBpU,mEACAA;AAAAA;AAAAA,gBAEMoU,EAAM,KAAK,IAAKjtB,GAAQq+B,GAAUr+B,EAAKitB,CAAK,CAAC,CAAC;AAAA;AAAA,WAEnD;AAAA;AAAA;AAAA;AAAA;AAAA,8CAKmCA,EAAM,WAAa,gBAAgB;AAAA,QACzEA,EAAM,WAAa,KACjBpU;AAAAA;AAAAA;AAAAA;AAAAA,YAKAoU,EAAM,KAAK,SAAW,EACpBpU,mEACAA;AAAAA;AAAAA,kBAEMoU,EAAM,KAAK,IAAK5xB,GAAUijC,GAAUjjC,CAAK,CAAC,CAAC;AAAA;AAAA,aAEhD;AAAA;AAAA,GAGb,CAEA,SAAS+iC,GAAqBnR,EAAkB,CAC9C,MAAM7uB,EAAO6uB,EAAM,KACnB,OAAI7uB,EAAK,eAAiB,KACjBya;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,mBAKQza,EAAK,UAAU;AAAA,mBACdvN,GACRo8B,EAAM,aAAa,CACjB,WAAap8B,EAAE,OAA4B,KAAA,CAC5C,CAAC;AAAA;AAAA;AAAA,MAKRuN,EAAK,eAAiB,QACjBya;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,qBAKUza,EAAK,WAAW;AAAA,qBACfvN,GACRo8B,EAAM,aAAa,CACjB,YAAcp8B,EAAE,OAA4B,KAAA,CAC7C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAMKuN,EAAK,SAAS;AAAA,sBACZvN,GACTo8B,EAAM,aAAa,CACjB,UAAYp8B,EAAE,OAA6B,KAAA,CAC5C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAUPgoB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,mBAKUza,EAAK,QAAQ;AAAA,mBACZvN,GACRo8B,EAAM,aAAa,CAAE,SAAWp8B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAM/DuN,EAAK,MAAM;AAAA,mBACVvN,GACRo8B,EAAM,aAAa,CAAE,OAASp8B,EAAE,OAA4B,MAAO,CAAC;AAAA;AAAA;AAAA;AAAA,GAKhF,CAEA,SAASwtC,GAAUr+B,EAAcitB,EAAkB,CAEjD,MAAMsR,EAAY,gCADCtR,EAAM,YAAcjtB,EAAI,GACoB,sBAAwB,EAAE,GACzF,OAAO6Y;AAAAA,iBACQ0lB,CAAS,WAAW,IAAMtR,EAAM,WAAWjtB,EAAI,EAAE,CAAC;AAAA;AAAA,kCAEjCA,EAAI,IAAI;AAAA,gCACV89B,GAAmB99B,CAAG,CAAC;AAAA,6BAC1B+9B,GAAkB/9B,CAAG,CAAC;AAAA,UACzCA,EAAI,QAAU6Y,8BAAiC7Y,EAAI,OAAO,SAAWsrB,CAAO;AAAA;AAAA,+BAEvDtrB,EAAI,QAAU,UAAY,UAAU;AAAA,+BACpCA,EAAI,aAAa;AAAA,+BACjBA,EAAI,QAAQ;AAAA;AAAA;AAAA;AAAA,eAI5B49B,GAAgB59B,CAAG,CAAC;AAAA;AAAA;AAAA;AAAA,wBAIXitB,EAAM,IAAI;AAAA,qBACZ3vB,GAAiB,CACzBA,EAAM,gBAAA,EACN2vB,EAAM,SAASjtB,EAAK,CAACA,EAAI,OAAO,CAClC,CAAC;AAAA;AAAA,cAECA,EAAI,QAAU,UAAY,QAAQ;AAAA;AAAA;AAAA;AAAA,wBAIxBitB,EAAM,IAAI;AAAA,qBACZ3vB,GAAiB,CACzBA,EAAM,gBAAA,EACN2vB,EAAM,MAAMjtB,CAAG,CACjB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMWitB,EAAM,IAAI;AAAA,qBACZ3vB,GAAiB,CACzBA,EAAM,gBAAA,EACN2vB,EAAM,WAAWjtB,EAAI,EAAE,CACzB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMWitB,EAAM,IAAI;AAAA,qBACZ3vB,GAAiB,CACzBA,EAAM,gBAAA,EACN2vB,EAAM,SAASjtB,CAAG,CACpB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQb,CAEA,SAASs+B,GAAUjjC,EAAwB,CACzC,OAAOwd;AAAAA;AAAAA;AAAAA,kCAGyBxd,EAAM,MAAM;AAAA,gCACdA,EAAM,SAAW,EAAE;AAAA;AAAA;AAAA,eAGpC/E,GAAS+E,EAAM,EAAE,CAAC;AAAA,6BACJA,EAAM,YAAc,CAAC;AAAA,UACxCA,EAAM,MAAQwd,uBAA0Bxd,EAAM,KAAK,SAAWiwB,CAAO;AAAA;AAAA;AAAA,GAI/E,CC5aO,SAASkT,GAAYvR,EAAmB,CAC7C,OAAOpU;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,0CAQiCoU,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,cACnEA,EAAM,QAAU,cAAgB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAMjB,KAAK,UAAUA,EAAM,QAAU,GAAI,KAAM,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,sCAI3C,KAAK,UAAUA,EAAM,QAAU,GAAI,KAAM,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,sCAI3C,KAAK,UAAUA,EAAM,WAAa,GAAI,KAAM,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAY7DA,EAAM,UAAU;AAAA,uBACfp8B,GACRo8B,EAAM,mBAAoBp8B,EAAE,OAA4B,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAOvDo8B,EAAM,UAAU;AAAA,uBACfp8B,GACRo8B,EAAM,mBAAoBp8B,EAAE,OAA+B,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+CAMlCo8B,EAAM,MAAM;AAAA;AAAA,UAEjDA,EAAM,UACJpU;AAAAA,gBACIoU,EAAM,SAAS;AAAA,oBAEnB3B,CAAO;AAAA,UACT2B,EAAM,WACJpU,sDAAyDoU,EAAM,UAAU,SACzE3B,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0DAOuC,KAAK,UACvD2B,EAAM,QAAU,CAAA,EAChB,KACA,CAAA,CACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMCA,EAAM,SAAS,SAAW,EACxBpU,qEACAA;AAAAA;AAAAA,gBAEMoU,EAAM,SAAS,IACdwR,GAAQ5lB;AAAAA;AAAAA;AAAAA,gDAGuB4lB,EAAI,KAAK;AAAA,8CACX,IAAI,KAAKA,EAAI,EAAE,EAAE,oBAAoB;AAAA;AAAA;AAAA,gDAGnCd,GAAmBc,EAAI,OAAO,CAAC;AAAA;AAAA;AAAA,iBAAA,CAIhE;AAAA;AAAA,WAEJ;AAAA;AAAA,GAGX,CC7GO,SAASC,GAAgBzR,EAAuB,CACrD,OAAOpU;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,wCAO+BoU,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,YACnEA,EAAM,QAAU,WAAa,SAAS;AAAA;AAAA;AAAA,QAG1CA,EAAM,UACJpU;AAAAA,cACIoU,EAAM,SAAS;AAAA,kBAEnB3B,CAAO;AAAA,QACT2B,EAAM,cACJpU;AAAAA,cACIoU,EAAM,aAAa;AAAA,kBAEvB3B,CAAO;AAAA;AAAA,UAEP2B,EAAM,QAAQ,SAAW,EACvBpU,uDACAoU,EAAM,QAAQ,IAAK5xB,GAAUsjC,GAAYtjC,CAAK,CAAC,CAAC;AAAA;AAAA;AAAA,GAI5D,CAEA,SAASsjC,GAAYtjC,EAAsB,CACzC,MAAMujC,EACJvjC,EAAM,kBAAoB,KACtB,GAAGA,EAAM,gBAAgB,QACzB,MACA0U,EAAO1U,EAAM,MAAQ,UACrBwjC,EAAQ,MAAM,QAAQxjC,EAAM,KAAK,EAAIA,EAAM,MAAM,OAAO,OAAO,EAAI,CAAA,EACnEkS,EAAS,MAAM,QAAQlS,EAAM,MAAM,EAAIA,EAAM,OAAO,OAAO,OAAO,EAAI,CAAA,EACtEyjC,EACJvxB,EAAO,OAAS,EACZA,EAAO,OAAS,EACd,GAAGA,EAAO,MAAM,UAChB,WAAWA,EAAO,KAAK,IAAI,CAAC,GAC9B,KACN,OAAOsL;AAAAA;AAAAA;AAAAA,kCAGyBxd,EAAM,MAAQ,cAAc;AAAA,gCAC9B6hC,GAAsB7hC,CAAK,CAAC;AAAA;AAAA,+BAE7B0U,CAAI;AAAA,YACvB8uB,EAAM,IAAKpmC,GAASogB,uBAA0BpgB,CAAI,SAAS,CAAC;AAAA,YAC5DqmC,EAAcjmB,uBAA0BimB,CAAW,UAAYxT,CAAO;AAAA,YACtEjwB,EAAM,SAAWwd,uBAA0Bxd,EAAM,QAAQ,UAAYiwB,CAAO;AAAA,YAC5EjwB,EAAM,aACJwd,uBAA0Bxd,EAAM,YAAY,UAC5CiwB,CAAO;AAAA,YACTjwB,EAAM,gBACJwd,uBAA0Bxd,EAAM,eAAe,UAC/CiwB,CAAO;AAAA,YACTjwB,EAAM,QAAUwd,uBAA0Bxd,EAAM,OAAO,UAAYiwB,CAAO;AAAA;AAAA;AAAA;AAAA,eAIvE+R,GAAkBhiC,CAAK,CAAC;AAAA,wCACCujC,CAAS;AAAA,oCACbvjC,EAAM,QAAU,EAAE;AAAA;AAAA;AAAA,GAItD,CChFA,MAAM+F,GAAqB,CAAC,QAAS,QAAS,OAAQ,OAAQ,QAAS,OAAO,EAmB9E,SAAS29B,GAAWjrC,EAAuB,CACzC,GAAI,CAACA,EAAO,MAAO,GACnB,MAAMkrC,EAAO,IAAI,KAAKlrC,CAAK,EAC3B,OAAI,OAAO,MAAMkrC,EAAK,QAAA,CAAS,EAAUlrC,EAClCkrC,EAAK,mBAAA,CACd,CAEA,SAASC,GAAc5jC,EAAiB6jC,EAAgB,CACtD,OAAKA,EACY,CAAC7jC,EAAM,QAASA,EAAM,UAAWA,EAAM,GAAG,EACxD,OAAO,OAAO,EACd,KAAK,GAAG,EACR,YAAA,EACa,SAAS6jC,CAAM,EALX,EAMtB,CAEO,SAASC,GAAWlS,EAAkB,CAC3C,MAAMiS,EAASjS,EAAM,WAAW,KAAA,EAAO,YAAA,EACjCmS,EAAgBh+B,GAAO,KAAMO,GAAU,CAACsrB,EAAM,aAAatrB,CAAK,CAAC,EACjEyyB,EAAWnH,EAAM,QAAQ,OAAQ5xB,GACjCA,EAAM,OAAS,CAAC4xB,EAAM,aAAa5xB,EAAM,KAAK,EAAU,GACrD4jC,GAAc5jC,EAAO6jC,CAAM,CACnC,EACKG,EAAcH,GAAUE,EAAgB,WAAa,UAE3D,OAAOvmB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,0CAQiCoU,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,cACnEA,EAAM,QAAU,WAAa,SAAS;AAAA;AAAA;AAAA;AAAA,wBAI5BmH,EAAS,SAAW,CAAC;AAAA,qBACxB,IAAMnH,EAAM,SAASmH,EAAS,IAAK/4B,GAAUA,EAAM,GAAG,EAAGgkC,CAAW,CAAC;AAAA;AAAA,qBAErEA,CAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBASXpS,EAAM,UAAU;AAAA,qBACfp8B,GACRo8B,EAAM,mBAAoBp8B,EAAE,OAA4B,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAQrDo8B,EAAM,UAAU;AAAA,sBAChBp8B,GACTo8B,EAAM,mBAAoBp8B,EAAE,OAA4B,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMpEuQ,GAAO,IACNO,GAAUkX;AAAAA,0CACqBlX,CAAK;AAAA;AAAA;AAAA,2BAGpBsrB,EAAM,aAAatrB,CAAK,CAAC;AAAA,0BACzB9Q,GACTo8B,EAAM,cAActrB,EAAQ9Q,EAAE,OAA4B,OAAO,CAAC;AAAA;AAAA,sBAE9D8Q,CAAK;AAAA;AAAA,WAAA,CAGlB;AAAA;AAAA;AAAA,QAGDsrB,EAAM,KACJpU,uDAA0DoU,EAAM,IAAI,SACpE3B,CAAO;AAAA,QACT2B,EAAM,UACJpU;AAAAA;AAAAA,kBAGAyS,CAAO;AAAA,QACT2B,EAAM,MACJpU,0DAA6DoU,EAAM,KAAK,SACxE3B,CAAO;AAAA;AAAA,kEAEiD2B,EAAM,QAAQ;AAAA,UACtEmH,EAAS,SAAW,EAClBvb,mEACAub,EAAS,IACN/4B,GAAUwd;AAAAA;AAAAA,+CAEsBkmB,GAAW1jC,EAAM,IAAI,CAAC;AAAA,0CAC3BA,EAAM,OAAS,EAAE,KAAKA,EAAM,OAAS,EAAE;AAAA,oDAC7BA,EAAM,WAAa,EAAE;AAAA,kDACvBA,EAAM,SAAWA,EAAM,GAAG;AAAA;AAAA,eAAA,CAG/D;AAAA;AAAA;AAAA,GAIb,CClFO,SAASikC,GAAYrS,EAAmB,CAC7C,MAAMsS,EAAeC,GAAqBvS,CAAK,EACzCwS,EAAiBC,GAA0BzS,CAAK,EACtD,OAAOpU;AAAAA,MACH8mB,GAAoBF,CAAc,CAAC;AAAA,MACnCG,GAAeL,CAAY,CAAC;AAAA,MAC5BM,GAAc5S,CAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAOcA,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,YACnEA,EAAM,QAAU,WAAa,SAAS;AAAA;AAAA;AAAA;AAAA,UAIxCA,EAAM,MAAM,SAAW,EACrBpU,4CACAoU,EAAM,MAAM,IAAK/7B,GAAM++B,GAAW/+B,CAAC,CAAC,CAAC;AAAA;AAAA;AAAA,GAIjD,CAEA,SAAS2uC,GAAc5S,EAAmB,CACxC,MAAM6S,EAAO7S,EAAM,aAAe,CAAE,QAAS,CAAA,EAAI,OAAQ,EAAC,EACpD8S,EAAU,MAAM,QAAQD,EAAK,OAAO,EAAIA,EAAK,QAAU,CAAA,EACvDE,EAAS,MAAM,QAAQF,EAAK,MAAM,EAAIA,EAAK,OAAS,CAAA,EAC1D,OAAOjnB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,wCAO+BoU,EAAM,cAAc,WAAWA,EAAM,gBAAgB;AAAA,YACjFA,EAAM,eAAiB,WAAa,SAAS;AAAA;AAAA;AAAA,QAGjDA,EAAM,aACJpU,0DAA6DoU,EAAM,YAAY,SAC/E3B,CAAO;AAAA;AAAA,UAEPyU,EAAQ,OAAS,EACflnB;AAAAA;AAAAA,gBAEIknB,EAAQ,IAAKE,GAAQC,GAAoBD,EAAKhT,CAAK,CAAC,CAAC;AAAA,cAEzD3B,CAAO;AAAA,UACT0U,EAAO,OAAS,EACdnnB;AAAAA;AAAAA,gBAEImnB,EAAO,IAAKG,GAAWC,GAAmBD,EAAQlT,CAAK,CAAC,CAAC;AAAA,cAE7D3B,CAAO;AAAA,UACTyU,EAAQ,SAAW,GAAKC,EAAO,SAAW,EACxCnnB,+CACAyS,CAAO;AAAA;AAAA;AAAA,GAInB,CAEA,SAAS4U,GAAoBD,EAAoBhT,EAAmB,CAClE,MAAM94B,EAAO8rC,EAAI,aAAa,KAAA,GAAUA,EAAI,SACtCI,EAAM,OAAOJ,EAAI,IAAO,SAAWzpC,EAAUypC,EAAI,EAAE,EAAI,MACvDxnC,EAAOwnC,EAAI,MAAM,KAAA,EAAS,SAASA,EAAI,IAAI,GAAK,UAChDK,EAASL,EAAI,SAAW,YAAc,GACtC9C,EAAK8C,EAAI,SAAW,MAAMA,EAAI,QAAQ,GAAK,GACjD,OAAOpnB;AAAAA;AAAAA;AAAAA,kCAGyB1kB,CAAI;AAAA,gCACN8rC,EAAI,QAAQ,GAAG9C,CAAE;AAAA;AAAA,YAErC1kC,CAAI,gBAAgB4nC,CAAG,GAAGC,CAAM;AAAA;AAAA;AAAA;AAAA;AAAA,uDAKW,IAAMrT,EAAM,gBAAgBgT,EAAI,SAAS,CAAC;AAAA;AAAA;AAAA,+CAGlD,IAAMhT,EAAM,eAAegT,EAAI,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOxF,CAEA,SAASG,GAAmBD,EAAsBlT,EAAmB,CACnE,MAAM94B,EAAOgsC,EAAO,aAAa,KAAA,GAAUA,EAAO,SAC5ChD,EAAKgD,EAAO,SAAW,MAAMA,EAAO,QAAQ,GAAK,GACjDtB,EAAQ,UAAU/nC,GAAWqpC,EAAO,KAAK,CAAC,GAC1C5yB,EAAS,WAAWzW,GAAWqpC,EAAO,MAAM,CAAC,GAC7CI,EAAS,MAAM,QAAQJ,EAAO,MAAM,EAAIA,EAAO,OAAS,CAAA,EAC9D,OAAOtnB;AAAAA;AAAAA;AAAAA,kCAGyB1kB,CAAI;AAAA,gCACNgsC,EAAO,QAAQ,GAAGhD,CAAE;AAAA,sDACE0B,CAAK,MAAMtxB,CAAM;AAAA,UAC7DgzB,EAAO,SAAW,EAChB1nB,kEACAA;AAAAA;AAAAA;AAAAA,kBAGM0nB,EAAO,IAAKxuB,GAAUyuB,GAAeL,EAAO,SAAUpuB,EAAOkb,CAAK,CAAC,CAAC;AAAA;AAAA,aAEzE;AAAA;AAAA;AAAA,GAIb,CAEA,SAASuT,GAAeC,EAAkB1uB,EAA2Bkb,EAAmB,CACtF,MAAMpsB,EAASkR,EAAM,YAAc,UAAY,SACzCxE,EAAS,WAAWzW,GAAWib,EAAM,MAAM,CAAC,GAC5C2uB,EAAOlqC,EAAUub,EAAM,aAAeA,EAAM,aAAeA,EAAM,cAAgB,IAAI,EAC3F,OAAO8G;AAAAA;AAAAA,8BAEqB9G,EAAM,IAAI,MAAMlR,CAAM,MAAM0M,CAAM,MAAMmzB,CAAI;AAAA;AAAA;AAAA;AAAA,mBAIvD,IAAMzT,EAAM,eAAewT,EAAU1uB,EAAM,KAAMA,EAAM,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA,UAIvEA,EAAM,YACJuZ,EACAzS;AAAAA;AAAAA;AAAAA,yBAGa,IAAMoU,EAAM,eAAewT,EAAU1uB,EAAM,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,aAI5D;AAAA;AAAA;AAAA,GAIb,CA2EA,MAAM4uB,GAA+B,eAE/BC,GAAkE,CACtE,CAAE,MAAO,OAAQ,MAAO,MAAA,EACxB,CAAE,MAAO,YAAa,MAAO,WAAA,EAC7B,CAAE,MAAO,OAAQ,MAAO,MAAA,CAC1B,EAEMC,GAAwD,CAC5D,CAAE,MAAO,MAAO,MAAO,KAAA,EACvB,CAAE,MAAO,UAAW,MAAO,SAAA,EAC3B,CAAE,MAAO,SAAU,MAAO,QAAA,CAC5B,EAEA,SAASrB,GAAqBvS,EAAiC,CAC7D,MAAMuL,EAASvL,EAAM,WACf6T,EAAQC,GAAiB9T,EAAM,KAAK,EACpC,CAAE,eAAA+T,EAAgB,OAAAC,GAAWC,GAAqB1I,CAAM,EACxD2I,EAAQ,EAAQ3I,EAChBrI,EAAWlD,EAAM,cAAgBA,EAAM,iBAAmB,MAChE,MAAO,CACL,MAAAkU,EACA,SAAAhR,EACA,YAAalD,EAAM,YACnB,cAAeA,EAAM,cACrB,aAAcA,EAAM,aACpB,eAAA+T,EACA,OAAAC,EACA,MAAAH,EACA,cAAe7T,EAAM,cACrB,YAAaA,EAAM,YACnB,OAAQA,EAAM,eACd,aAAcA,EAAM,aACpB,SAAUA,EAAM,cAAA,CAEpB,CAEA,SAASmU,GAAkBttC,EAA8B,CACvD,OAAIA,IAAU,aAAeA,IAAU,QAAUA,IAAU,OAAeA,EACnE,MACT,CAEA,SAASutC,GAAavtC,EAAyB,CAC7C,OAAIA,IAAU,UAAYA,IAAU,OAASA,IAAU,UAAkBA,EAClE,SACT,CAEA,SAASwtC,GACPljC,EAC+B,CAC/B,MAAM5J,EAAW4J,GAAM,UAAY,CAAA,EACnC,MAAO,CACL,SAAUgjC,GAAkB5sC,EAAS,QAAQ,EAC7C,IAAK6sC,GAAa7sC,EAAS,GAAG,EAC9B,YAAa4sC,GAAkB5sC,EAAS,aAAe,MAAM,EAC7D,gBAAiB,GAAQA,EAAS,iBAAmB,GAAK,CAE9D,CAEA,SAAS+sC,GAAoB/I,EAAoE,CAC/F,MAAMgJ,EAAchJ,GAAQ,QAAU,CAAA,EAChCsH,EAAO,MAAM,QAAQ0B,EAAW,IAAI,EAAIA,EAAW,KAAO,CAAA,EAC1DP,EAAqC,CAAA,EAC3C,OAAAnB,EAAK,QAASzkC,GAAU,CACtB,GAAI,CAACA,GAAS,OAAOA,GAAU,SAAU,OACzC,MAAMD,EAASC,EACTU,EAAK,OAAOX,EAAO,IAAO,SAAWA,EAAO,GAAG,OAAS,GAC9D,GAAI,CAACW,EAAI,OACT,MAAM5H,EAAO,OAAOiH,EAAO,MAAS,SAAWA,EAAO,KAAK,OAAS,OAC9DqmC,EAAYrmC,EAAO,UAAY,GACrC6lC,EAAO,KAAK,CAAE,GAAAllC,EAAI,KAAM5H,GAAQ,OAAW,UAAAstC,EAAW,CACxD,CAAC,EACMR,CACT,CAEA,SAASS,GACPlJ,EACAp6B,EAC4B,CAC5B,MAAMujC,EAAeJ,GAAoB/I,CAAM,EACzCoJ,EAAkB,OAAO,KAAKxjC,GAAM,QAAU,CAAA,CAAE,EAChDyjC,MAAa,IACnBF,EAAa,QAASG,GAAUD,EAAO,IAAIC,EAAM,GAAIA,CAAK,CAAC,EAC3DF,EAAgB,QAAS7lC,GAAO,CAC1B8lC,EAAO,IAAI9lC,CAAE,GACjB8lC,EAAO,IAAI9lC,EAAI,CAAE,GAAAA,CAAA,CAAI,CACvB,CAAC,EACD,MAAMklC,EAAS,MAAM,KAAKY,EAAO,QAAQ,EACzC,OAAIZ,EAAO,SAAW,GACpBA,EAAO,KAAK,CAAE,GAAI,OAAQ,UAAW,GAAM,EAE7CA,EAAO,KAAK,CAAC,EAAGpvC,IAAM,CACpB,GAAI,EAAE,WAAa,CAACA,EAAE,UAAW,MAAO,GACxC,GAAI,CAAC,EAAE,WAAaA,EAAE,UAAW,MAAO,GACxC,MAAMkwC,EAAS,EAAE,MAAM,OAAS,EAAE,KAAO,EAAE,GACrCC,EAASnwC,EAAE,MAAM,OAASA,EAAE,KAAOA,EAAE,GAC3C,OAAOkwC,EAAO,cAAcC,CAAM,CACpC,CAAC,EACMf,CACT,CAEA,SAASgB,GACPC,EACAjB,EACQ,CACR,OAAIiB,IAAavB,GAAqCA,GAClDuB,GAAYjB,EAAO,KAAMa,GAAUA,EAAM,KAAOI,CAAQ,EAAUA,EAC/DvB,EACT,CAEA,SAASjB,GAA0BzS,EAAuC,CACxE,MAAM7uB,EAAO6uB,EAAM,mBAAqBA,EAAM,uBAAuB,MAAQ,KACvEkU,EAAQ,EAAQ/iC,EAChB5J,EAAW8sC,GAA6BljC,CAAI,EAC5C6iC,EAASS,GAA2BzU,EAAM,WAAY7uB,CAAI,EAC1D+jC,EAAcC,GAA0BnV,EAAM,KAAK,EACnDlwB,EAASkwB,EAAM,oBACrB,IAAIoV,EACFtlC,IAAW,QAAUkwB,EAAM,0BACvBA,EAAM,0BACN,KACFlwB,IAAW,QAAUslC,GAAgB,CAACF,EAAY,KAAMriB,GAASA,EAAK,KAAOuiB,CAAY,IAC3FA,EAAe,MAEjB,MAAMC,EAAgBL,GAA0BhV,EAAM,2BAA4BgU,CAAM,EAClFsB,EACJD,IAAkB3B,IACZviC,GAAM,QAAU,IAAIkkC,CAAa,GACnC,KACA,KACAE,EAAY,MAAM,QAASD,GAA2C,SAAS,EAC/EA,EAAgE,WAChE,CAAA,EACF,CAAA,EACJ,MAAO,CACL,MAAApB,EACA,SAAUlU,EAAM,qBAAuBA,EAAM,qBAC7C,MAAOA,EAAM,mBACb,QAASA,EAAM,qBACf,OAAQA,EAAM,oBACd,KAAA7uB,EACA,SAAA5J,EACA,cAAA8tC,EACA,cAAAC,EACA,OAAAtB,EACA,UAAAuB,EACA,OAAAzlC,EACA,aAAAslC,EACA,YAAAF,EACA,cAAelV,EAAM,2BACrB,eAAgBA,EAAM,4BACtB,QAASA,EAAM,qBACf,SAAUA,EAAM,sBAChB,OAAQA,EAAM,oBACd,OAAQA,EAAM,mBAAA,CAElB,CAEA,SAAS2S,GAAe/lC,EAAqB,CAC3C,MAAM4oC,EAAkB5oC,EAAM,MAAM,OAAS,EACvCs1B,EAAet1B,EAAM,gBAAkB,GAC7C,OAAOgf;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,sBAWahf,EAAM,UAAY,CAACA,EAAM,WAAW;AAAA,mBACvCA,EAAM,MAAM;AAAA;AAAA,YAEnBA,EAAM,aAAe,UAAY,MAAM;AAAA;AAAA;AAAA;AAAA,QAI3CA,EAAM,WAAa,MACjBgf;AAAAA;AAAAA,kBAGAyS,CAAO;AAAA;AAAA,QAERzxB,EAAM,MAOLgf;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,kCAWwBhf,EAAM,UAAY,CAAC4oC,CAAe;AAAA,gCACnCnlC,GAAiB,CAE1B,MAAMxJ,EADSwJ,EAAM,OACA,MAAM,KAAA,EAC3BzD,EAAM,cAAc/F,GAAgB,IAAI,CAC1C,CAAC;AAAA;AAAA,mDAE4Bq7B,IAAiB,EAAE;AAAA,wBAC9Ct1B,EAAM,MAAM,IACXimB,GACCjH;AAAAA,oCACUiH,EAAK,EAAE;AAAA,wCACHqP,IAAiBrP,EAAK,EAAE;AAAA;AAAA,8BAElCA,EAAK,KAAK;AAAA,oCAAA,CAEjB;AAAA;AAAA;AAAA,oBAGF2iB,EAECnX,EADAzS,+DACO;AAAA;AAAA;AAAA;AAAA,gBAIbhf,EAAM,OAAO,SAAW,EACtBgf,6CACAhf,EAAM,OAAO,IAAKioC,GAChBY,GAAmBZ,EAAOjoC,CAAK,CAAA,CAChC;AAAA;AAAA,YA9CTgf;AAAAA;AAAAA,4CAEkChf,EAAM,aAAa,WAAWA,EAAM,YAAY;AAAA,gBAC5EA,EAAM,cAAgB,WAAa,aAAa;AAAA;AAAA,iBA6CrD;AAAA;AAAA,GAGX,CAEA,SAAS8lC,GAAoB9lC,EAA2B,CACtD,MAAMsnC,EAAQtnC,EAAM,MACd8oC,EAAc9oC,EAAM,SAAW,QAAU,EAAQA,EAAM,aAC7D,OAAOgf;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,sBAWahf,EAAM,UAAY,CAACA,EAAM,OAAS,CAAC8oC,CAAW;AAAA,mBACjD9oC,EAAM,MAAM;AAAA;AAAA,YAEnBA,EAAM,OAAS,UAAY,MAAM;AAAA;AAAA;AAAA;AAAA,QAIrC+oC,GAA0B/oC,CAAK,CAAC;AAAA;AAAA,QAE/BsnC,EAOCtoB;AAAAA,cACIgqB,GAAwBhpC,CAAK,CAAC;AAAA,cAC9BipC,GAA0BjpC,CAAK,CAAC;AAAA,cAChCA,EAAM,gBAAkB8mC,GACtBrV,EACAyX,GAA6BlpC,CAAK,CAAC;AAAA,YAXzCgf;AAAAA;AAAAA,4CAEkChf,EAAM,SAAW,CAAC8oC,CAAW,WAAW9oC,EAAM,MAAM;AAAA,gBAChFA,EAAM,QAAU,WAAa,gBAAgB;AAAA;AAAA,iBASlD;AAAA;AAAA,GAGX,CAEA,SAAS+oC,GAA0B/oC,EAA2B,CAC5D,MAAMmpC,EAAWnpC,EAAM,YAAY,OAAS,EACtCopC,EAAYppC,EAAM,cAAgB,GACxC,OAAOgf;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,0BAaiBhf,EAAM,QAAQ;AAAA,wBACfyD,GAAiB,CAG1B,GAFeA,EAAM,OACA,QACP,OAAQ,CACpB,MAAM4lC,EAAQrpC,EAAM,YAAY,CAAC,GAAG,IAAM,KAC1CA,EAAM,eAAe,OAAQopC,GAAaC,CAAK,CACjD,MACErpC,EAAM,eAAe,UAAW,IAAI,CAExC,CAAC;AAAA;AAAA,kDAEmCA,EAAM,SAAW,SAAS;AAAA,+CAC7BA,EAAM,SAAW,MAAM;AAAA;AAAA;AAAA,YAG1DA,EAAM,SAAW,OACfgf;AAAAA;AAAAA;AAAAA;AAAAA,gCAIkBhf,EAAM,UAAY,CAACmpC,CAAQ;AAAA,8BAC5B1lC,GAAiB,CAE1B,MAAMxJ,EADSwJ,EAAM,OACA,MAAM,KAAA,EAC3BzD,EAAM,eAAe,OAAQ/F,GAAgB,IAAI,CACnD,CAAC;AAAA;AAAA,iDAE4BmvC,IAAc,EAAE;AAAA,sBAC3CppC,EAAM,YAAY,IACjBimB,GACCjH;AAAAA,kCACUiH,EAAK,EAAE;AAAA,sCACHmjB,IAAcnjB,EAAK,EAAE;AAAA;AAAA,4BAE/BA,EAAK,KAAK;AAAA,kCAAA,CAEjB;AAAA;AAAA;AAAA,gBAIPwL,CAAO;AAAA;AAAA;AAAA,QAGbzxB,EAAM,SAAW,QAAU,CAACmpC,EAC1BnqB,mEACAyS,CAAO;AAAA;AAAA,GAGjB,CAEA,SAASuX,GAAwBhpC,EAA2B,CAC1D,OAAOgf;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,+BAKsBhf,EAAM,gBAAkB8mC,GAA+B,SAAW,EAAE;AAAA,mBAChF,IAAM9mC,EAAM,cAAc8mC,EAA4B,CAAC;AAAA;AAAA;AAAA;AAAA,UAIhE9mC,EAAM,OAAO,IAAKioC,GAAU,CAC5B,MAAM1pC,EAAQ0pC,EAAM,MAAM,KAAA,EAAS,GAAGA,EAAM,IAAI,KAAKA,EAAM,EAAE,IAAMA,EAAM,GACzE,OAAOjpB;AAAAA;AAAAA,mCAEkBhf,EAAM,gBAAkBioC,EAAM,GAAK,SAAW,EAAE;AAAA,uBAC5D,IAAMjoC,EAAM,cAAcioC,EAAM,EAAE,CAAC;AAAA;AAAA,gBAE1C1pC,CAAK;AAAA;AAAA,WAGb,CAAC,CAAC;AAAA;AAAA;AAAA,GAIV,CAEA,SAAS0qC,GAA0BjpC,EAA2B,CAC5D,MAAMspC,EAAatpC,EAAM,gBAAkB8mC,GACrCnsC,EAAWqF,EAAM,SACjBioC,EAAQjoC,EAAM,eAAiB,CAAA,EAC/BrE,EAAW2tC,EAAa,CAAC,UAAU,EAAI,CAAC,SAAUtpC,EAAM,aAAa,EACrEupC,EAAgB,OAAOtB,EAAM,UAAa,SAAWA,EAAM,SAAW,OACtEuB,EAAW,OAAOvB,EAAM,KAAQ,SAAWA,EAAM,IAAM,OACvDwB,EACJ,OAAOxB,EAAM,aAAgB,SAAWA,EAAM,YAAc,OACxDyB,EAAgBJ,EAAa3uC,EAAS,SAAW4uC,GAAiB,cAClEI,EAAWL,EAAa3uC,EAAS,IAAM6uC,GAAY,cACnDI,EAAmBN,EACrB3uC,EAAS,YACT8uC,GAAoB,cAClBI,EACJ,OAAO5B,EAAM,iBAAoB,UAAYA,EAAM,gBAAkB,OACjE6B,EAAgBD,GAAgBlvC,EAAS,gBACzCovC,EAAgBF,GAAgB,KAEtC,OAAO7qB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,cAMKsqB,EACE,yBACA,YAAY3uC,EAAS,QAAQ,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAOtBqF,EAAM,QAAQ;AAAA,wBACfyD,GAAiB,CAE1B,MAAMxJ,EADSwJ,EAAM,OACA,MACjB,CAAC6lC,GAAcrvC,IAAU,cAC3B+F,EAAM,SAAS,CAAC,GAAGrE,EAAU,UAAU,CAAC,EAExCqE,EAAM,QAAQ,CAAC,GAAGrE,EAAU,UAAU,EAAG1B,CAAK,CAElD,CAAC;AAAA;AAAA,gBAEEqvC,EAIC7X,EAHAzS,0CAA6C0qB,IAAkB,aAAa;AAAA,mCAC3D/uC,EAAS,QAAQ;AAAA,4BAE3B;AAAA,gBACTosC,GAAiB,IAChBiD,GACChrB;AAAAA,4BACUgrB,EAAO,KAAK;AAAA,gCACRN,IAAkBM,EAAO,KAAK;AAAA;AAAA,sBAExCA,EAAO,KAAK;AAAA,4BAAA,CAEnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAUDV,EAAa,yBAA2B,YAAY3uC,EAAS,GAAG,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAOvDqF,EAAM,QAAQ;AAAA,wBACfyD,GAAiB,CAE1B,MAAMxJ,EADSwJ,EAAM,OACA,MACjB,CAAC6lC,GAAcrvC,IAAU,cAC3B+F,EAAM,SAAS,CAAC,GAAGrE,EAAU,KAAK,CAAC,EAEnCqE,EAAM,QAAQ,CAAC,GAAGrE,EAAU,KAAK,EAAG1B,CAAK,CAE7C,CAAC;AAAA;AAAA,gBAEEqvC,EAIC7X,EAHAzS,0CAA6C2qB,IAAa,aAAa;AAAA,mCACtDhvC,EAAS,GAAG;AAAA,4BAEtB;AAAA,gBACTqsC,GAAY,IACXgD,GACChrB;AAAAA,4BACUgrB,EAAO,KAAK;AAAA,gCACRL,IAAaK,EAAO,KAAK;AAAA;AAAA,sBAEnCA,EAAO,KAAK;AAAA,4BAAA,CAEnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAUDV,EACE,6CACA,YAAY3uC,EAAS,WAAW,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAOzBqF,EAAM,QAAQ;AAAA,wBACfyD,GAAiB,CAE1B,MAAMxJ,EADSwJ,EAAM,OACA,MACjB,CAAC6lC,GAAcrvC,IAAU,cAC3B+F,EAAM,SAAS,CAAC,GAAGrE,EAAU,aAAa,CAAC,EAE3CqE,EAAM,QAAQ,CAAC,GAAGrE,EAAU,aAAa,EAAG1B,CAAK,CAErD,CAAC;AAAA;AAAA,gBAEEqvC,EAIC7X,EAHAzS,0CAA6C4qB,IAAqB,aAAa;AAAA,mCAC9DjvC,EAAS,WAAW;AAAA,4BAE9B;AAAA,gBACTosC,GAAiB,IAChBiD,GACChrB;AAAAA,4BACUgrB,EAAO,KAAK;AAAA,gCACRJ,IAAqBI,EAAO,KAAK;AAAA;AAAA,sBAE3CA,EAAO,KAAK;AAAA,4BAAA,CAEnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAUDV,EACE,iDACAS,EACE,kBAAkBpvC,EAAS,gBAAkB,KAAO,KAAK,KACzD,aAAamvC,EAAgB,KAAO,KAAK,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAQrC9pC,EAAM,QAAQ;AAAA,yBACf8pC,CAAa;AAAA,wBACbrmC,GAAiB,CAC1B,MAAMP,EAASO,EAAM,OACrBzD,EAAM,QAAQ,CAAC,GAAGrE,EAAU,iBAAiB,EAAGuH,EAAO,OAAO,CAChE,CAAC;AAAA;AAAA;AAAA,YAGH,CAAComC,GAAc,CAACS,EACd/qB;AAAAA;AAAAA,4BAEchf,EAAM,QAAQ;AAAA,yBACjB,IAAMA,EAAM,SAAS,CAAC,GAAGrE,EAAU,iBAAiB,CAAC,CAAC;AAAA;AAAA;AAAA,yBAIjE81B,CAAO;AAAA;AAAA;AAAA;AAAA,GAKrB,CAEA,SAASyX,GAA6BlpC,EAA2B,CAC/D,MAAMiqC,EAAgB,CAAC,SAAUjqC,EAAM,cAAe,WAAW,EAC3DoI,EAAUpI,EAAM,UACtB,OAAOgf;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,oBAQWhf,EAAM,QAAQ;AAAA,iBACjB,IAAM,CACb,MAAMjF,EAAO,CAAC,GAAGqN,EAAS,CAAE,QAAS,GAAI,EACzCpI,EAAM,QAAQiqC,EAAelvC,CAAI,CACnC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMDqN,EAAQ,SAAW,EACjB4W,sDACA5W,EAAQ,IAAI,CAAC5G,EAAOyc,IAClBisB,GAAqBlqC,EAAOwB,EAAOyc,CAAK,CAAA,CACzC;AAAA;AAAA,GAGX,CAEA,SAASisB,GACPlqC,EACAwB,EACAyc,EACA,CACA,MAAMksB,EAAW3oC,EAAM,WAAa7E,EAAU6E,EAAM,UAAU,EAAI,QAC5D4oC,EAAc5oC,EAAM,gBACtBrE,GAAUqE,EAAM,gBAAiB,GAAG,EACpC,KACE6oC,EAAW7oC,EAAM,iBACnBrE,GAAUqE,EAAM,iBAAkB,GAAG,EACrC,KACJ,OAAOwd;AAAAA;AAAAA;AAAAA,kCAGyBxd,EAAM,SAAS,KAAA,EAASA,EAAM,QAAU,aAAa;AAAA,2CAC5C2oC,CAAQ;AAAA,UACzCC,EAAcprB,+BAAkCorB,CAAW,SAAW3Y,CAAO;AAAA,UAC7E4Y,EAAWrrB,+BAAkCqrB,CAAQ,SAAW5Y,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAO5DjwB,EAAM,SAAW,EAAE;AAAA,wBAChBxB,EAAM,QAAQ;AAAA,qBAChByD,GAAiB,CACzB,MAAMP,EAASO,EAAM,OACrBzD,EAAM,QACJ,CAAC,SAAUA,EAAM,cAAe,YAAaie,EAAO,SAAS,EAC7D/a,EAAO,KAAA,CAEX,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKSlD,EAAM,QAAQ;AAAA,mBACjB,IAAM,CACb,GAAIA,EAAM,UAAU,QAAU,EAAG,CAC/BA,EAAM,SAAS,CAAC,SAAUA,EAAM,cAAe,WAAW,CAAC,EAC3D,MACF,CACAA,EAAM,SAAS,CAAC,SAAUA,EAAM,cAAe,YAAaie,CAAK,CAAC,CACpE,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOX,CAEA,SAAS4qB,GAAmBZ,EAAqBjoC,EAAqB,CACpE,MAAMsqC,EAAerC,EAAM,SAAW,cAChC1pC,EAAQ0pC,EAAM,MAAM,KAAA,EAAS,GAAGA,EAAM,IAAI,KAAKA,EAAM,EAAE,IAAMA,EAAM,GACnEW,EAAkB5oC,EAAM,MAAM,OAAS,EAC7C,OAAOgf;AAAAA;AAAAA;AAAAA,kCAGyBzgB,CAAK;AAAA;AAAA,YAE3B0pC,EAAM,UAAY,gBAAkB,OAAO;AAAA,YAC3CqC,IAAiB,cACf,iBAAiBtqC,EAAM,gBAAkB,KAAK,IAC9C,aAAaioC,EAAM,OAAO,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAOlBjoC,EAAM,UAAY,CAAC4oC,CAAe;AAAA,sBACnCnlC,GAAiB,CAE1B,MAAMxJ,EADSwJ,EAAM,OACA,MAAM,KAAA,EAC3BzD,EAAM,YAAYioC,EAAM,MAAOhuC,IAAU,cAAgB,KAAOA,CAAK,CACvE,CAAC;AAAA;AAAA,oDAEuCqwC,IAAiB,aAAa;AAAA;AAAA;AAAA,cAGpEtqC,EAAM,MAAM,IACXimB,GACCjH;AAAAA,0BACUiH,EAAK,EAAE;AAAA,8BACHqkB,IAAiBrkB,EAAK,EAAE;AAAA;AAAA,oBAElCA,EAAK,KAAK;AAAA,0BAAA,CAEjB;AAAA;AAAA;AAAA;AAAA;AAAA,GAMb,CAEA,SAASihB,GAAiBD,EAAsD,CAC9E,MAAMhB,EAAsB,CAAA,EAC5B,UAAWhgB,KAAQghB,EAAO,CAGxB,GAAI,EAFa,MAAM,QAAQhhB,EAAK,QAAQ,EAAIA,EAAK,SAAW,CAAA,GACtC,KAAMskB,GAAQ,OAAOA,CAAG,IAAM,YAAY,EACrD,SACf,MAAM51B,EAAS,OAAOsR,EAAK,QAAW,SAAWA,EAAK,OAAO,OAAS,GACtE,GAAI,CAACtR,EAAQ,SACb,MAAMysB,EACJ,OAAOnb,EAAK,aAAgB,UAAYA,EAAK,YAAY,KAAA,EACrDA,EAAK,YAAY,KAAA,EACjBtR,EACNsxB,EAAK,KAAK,CAAE,GAAItxB,EAAQ,MAAOysB,IAAgBzsB,EAASA,EAAS,GAAGysB,CAAW,MAAMzsB,CAAM,GAAI,CACjG,CACA,OAAAsxB,EAAK,KAAK,CAACvuC,EAAGM,IAAMN,EAAE,MAAM,cAAcM,EAAE,KAAK,CAAC,EAC3CiuC,CACT,CAEA,SAASsC,GAA0BtB,EAAkE,CACnG,MAAMhB,EAAkC,CAAA,EACxC,UAAWhgB,KAAQghB,EAAO,CAKxB,GAAI,EAJa,MAAM,QAAQhhB,EAAK,QAAQ,EAAIA,EAAK,SAAW,CAAA,GACtC,KACvBskB,GAAQ,OAAOA,CAAG,IAAM,4BAA8B,OAAOA,CAAG,IAAM,0BAAA,EAE1D,SACf,MAAM51B,EAAS,OAAOsR,EAAK,QAAW,SAAWA,EAAK,OAAO,OAAS,GACtE,GAAI,CAACtR,EAAQ,SACb,MAAMysB,EACJ,OAAOnb,EAAK,aAAgB,UAAYA,EAAK,YAAY,KAAA,EACrDA,EAAK,YAAY,KAAA,EACjBtR,EACNsxB,EAAK,KAAK,CAAE,GAAItxB,EAAQ,MAAOysB,IAAgBzsB,EAASA,EAAS,GAAGysB,CAAW,MAAMzsB,CAAM,GAAI,CACjG,CACA,OAAAsxB,EAAK,KAAK,CAACvuC,EAAGM,IAAMN,EAAE,MAAM,cAAcM,EAAE,KAAK,CAAC,EAC3CiuC,CACT,CAEA,SAASoB,GAAqB1I,EAG5B,CACA,MAAM6L,EAA8B,CAClC,GAAI,OACJ,KAAM,OACN,MAAO,EACP,UAAW,GACX,QAAS,IAAA,EAEX,GAAI,CAAC7L,GAAU,OAAOA,GAAW,SAC/B,MAAO,CAAE,eAAgB,KAAM,OAAQ,CAAC6L,CAAa,CAAA,EAGvD,MAAMC,GADS9L,EAAO,OAAS,CAAA,GACX,MAAQ,CAAA,EACtBwI,EACJ,OAAOsD,EAAK,MAAS,UAAYA,EAAK,KAAK,KAAA,EAASA,EAAK,KAAK,KAAA,EAAS,KAEnE9C,EAAchJ,EAAO,QAAU,CAAA,EAC/BsH,EAAO,MAAM,QAAQ0B,EAAW,IAAI,EAAIA,EAAW,KAAO,CAAA,EAChE,GAAI1B,EAAK,SAAW,EAClB,MAAO,CAAE,eAAAkB,EAAgB,OAAQ,CAACqD,CAAa,CAAA,EAGjD,MAAMpD,EAAyB,CAAA,EAC/B,OAAAnB,EAAK,QAAQ,CAACzkC,EAAOyc,IAAU,CAC7B,GAAI,CAACzc,GAAS,OAAOA,GAAU,SAAU,OACzC,MAAMD,EAASC,EACTU,EAAK,OAAOX,EAAO,IAAO,SAAWA,EAAO,GAAG,OAAS,GAC9D,GAAI,CAACW,EAAI,OACT,MAAM5H,EAAO,OAAOiH,EAAO,MAAS,SAAWA,EAAO,KAAK,OAAS,OAC9DqmC,EAAYrmC,EAAO,UAAY,GAE/BmpC,GADcnpC,EAAO,OAAS,CAAA,GACN,MAAQ,CAAA,EAChCopC,EACJ,OAAOD,EAAU,MAAS,UAAYA,EAAU,KAAK,KAAA,EACjDA,EAAU,KAAK,KAAA,EACf,KACNtD,EAAO,KAAK,CACV,GAAAllC,EACA,KAAM5H,GAAQ,OACd,MAAA2jB,EACA,UAAA2pB,EACA,QAAA+C,CAAA,CACD,CACH,CAAC,EAEGvD,EAAO,SAAW,GACpBA,EAAO,KAAKoD,CAAa,EAGpB,CAAE,eAAArD,EAAgB,OAAAC,CAAA,CAC3B,CAEA,SAAShR,GAAWnQ,EAA+B,CACjD,MAAMiY,EAAY,EAAQjY,EAAK,UACzBkgB,EAAS,EAAQlgB,EAAK,OACtB/c,EACH,OAAO+c,EAAK,aAAgB,UAAYA,EAAK,YAAY,KAAA,IACzD,OAAOA,EAAK,QAAW,SAAWA,EAAK,OAAS,WAC7C2kB,EAAO,MAAM,QAAQ3kB,EAAK,IAAI,EAAKA,EAAK,KAAqB,CAAA,EAC7D4kB,EAAW,MAAM,QAAQ5kB,EAAK,QAAQ,EAAKA,EAAK,SAAyB,CAAA,EAC/E,OAAOjH;AAAAA;AAAAA;AAAAA,kCAGyB9V,CAAK;AAAA;AAAA,YAE3B,OAAO+c,EAAK,QAAW,SAAWA,EAAK,OAAS,EAAE;AAAA,YAClD,OAAOA,EAAK,UAAa,SAAW,MAAMA,EAAK,QAAQ,GAAK,EAAE;AAAA,YAC9D,OAAOA,EAAK,SAAY,SAAW,MAAMA,EAAK,OAAO,GAAK,EAAE;AAAA;AAAA;AAAA,+BAGzCkgB,EAAS,SAAW,UAAU;AAAA,8BAC/BjI,EAAY,UAAY,WAAW;AAAA,cACnDA,EAAY,YAAc,SAAS;AAAA;AAAA,YAErC0M,EAAK,MAAM,EAAG,EAAE,EAAE,IAAKpzC,GAAMwnB,uBAA0B,OAAOxnB,CAAC,CAAC,SAAS,CAAC;AAAA,YAC1EqzC,EACC,MAAM,EAAG,CAAC,EACV,IAAKrzC,GAAMwnB,uBAA0B,OAAOxnB,CAAC,CAAC,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA,GAKrE,CCriCO,SAASszC,GAAe1X,EAAsB,CACnD,MAAMnuB,EAAWmuB,EAAM,OAAO,SAGxB2X,EAAS9lC,GAAU,SAAWjI,GAAiBiI,EAAS,QAAQ,EAAI,MACpE+lC,EAAO/lC,GAAU,QAAQ,eAC3B,GAAGA,EAAS,OAAO,cAAc,KACjC,MACEgmC,GAAY,IAAM,CACtB,GAAI7X,EAAM,WAAa,CAACA,EAAM,UAAW,OAAO,KAChD,MAAM/X,EAAQ+X,EAAM,UAAU,YAAA,EAE9B,GAAI,EADe/X,EAAM,SAAS,cAAc,GAAKA,EAAM,SAAS,gBAAgB,GACnE,OAAO,KACxB,MAAM6vB,EAAW,EAAQ9X,EAAM,SAAS,MAAM,OACxC+X,EAAc,EAAQ/X,EAAM,SAAS,OAC3C,MAAI,CAAC8X,GAAY,CAACC,EACTnsB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,QAoBFA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,KAiBT,GAAA,EACMosB,GAAuB,IAAM,CAGjC,GAFIhY,EAAM,WAAa,CAACA,EAAM,YACN,OAAO,OAAW,IAAc,OAAO,gBAAkB,MACzD,GAAO,OAAO,KACtC,MAAM/X,EAAQ+X,EAAM,UAAU,YAAA,EAC9B,MAAI,CAAC/X,EAAM,SAAS,gBAAgB,GAAK,CAACA,EAAM,SAAS,0BAA0B,EAC1E,KAEF2D;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,KA6BT,GAAA,EAEA,OAAOA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,uBAScoU,EAAM,SAAS,UAAU;AAAA,uBACxBp8B,GAAa,CACrB,MAAMmB,EAAKnB,EAAE,OAA4B,MACzCo8B,EAAM,iBAAiB,CAAE,GAAGA,EAAM,SAAU,WAAYj7B,EAAG,CAC7D,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAOQi7B,EAAM,SAAS,KAAK;AAAA,uBACnBp8B,GAAa,CACrB,MAAMmB,EAAKnB,EAAE,OAA4B,MACzCo8B,EAAM,iBAAiB,CAAE,GAAGA,EAAM,SAAU,MAAOj7B,EAAG,CACxD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAQQi7B,EAAM,QAAQ;AAAA,uBACbp8B,GAAa,CACrB,MAAMmB,EAAKnB,EAAE,OAA4B,MACzCo8B,EAAM,iBAAiBj7B,CAAC,CAC1B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAOQi7B,EAAM,SAAS,UAAU;AAAA,uBACxBp8B,GAAa,CACrB,MAAMmB,EAAKnB,EAAE,OAA4B,MACzCo8B,EAAM,mBAAmBj7B,CAAC,CAC5B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,uCAKwB,IAAMi7B,EAAM,WAAW;AAAA,uCACvB,IAAMA,EAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCAWzBA,EAAM,UAAY,KAAO,MAAM;AAAA,gBACpDA,EAAM,UAAY,YAAc,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,sCAKxB2X,CAAM;AAAA;AAAA;AAAA;AAAA,sCAINC,CAAI;AAAA;AAAA;AAAA;AAAA;AAAA,gBAK1B5X,EAAM,oBACJz2B,EAAUy2B,EAAM,mBAAmB,EACnC,KAAK;AAAA;AAAA;AAAA;AAAA,UAIbA,EAAM,UACJpU;AAAAA,qBACSoU,EAAM,SAAS;AAAA,gBACpB6X,GAAY,EAAE;AAAA,gBACdG,GAAuB,EAAE;AAAA,oBAE7BpsB;AAAAA;AAAAA,mBAEO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kCAOeoU,EAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,kCAKnBA,EAAM,eAAiB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMlDA,EAAM,aAAe,KACnB,MACAA,EAAM,YACJ,UACA,UAAU;AAAA;AAAA,uCAEasQ,GAActQ,EAAM,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAyBpE,CCjOA,MAAMiY,GAAe,CAAC,GAAI,MAAO,UAAW,MAAO,SAAU,MAAM,EAC7DC,GAAsB,CAAC,GAAI,MAAO,IAAI,EACtCC,GAAiB,CACrB,CAAE,MAAO,GAAI,MAAO,SAAA,EACpB,CAAE,MAAO,MAAO,MAAO,gBAAA,EACvB,CAAE,MAAO,KAAM,MAAO,IAAA,CACxB,EACMC,GAAmB,CAAC,GAAI,MAAO,KAAM,QAAQ,EAEnD,SAASC,GAAoBC,EAAkC,CAC7D,GAAI,CAACA,EAAU,MAAO,GACtB,MAAM5vC,EAAa4vC,EAAS,KAAA,EAAO,YAAA,EACnC,OAAI5vC,IAAe,QAAUA,IAAe,OAAe,MACpDA,CACT,CAEA,SAAS6vC,GAAyBD,EAAmC,CACnE,OAAOD,GAAoBC,CAAQ,IAAM,KAC3C,CAEA,SAASE,GAAyBF,EAA6C,CAC7E,OAAOC,GAAyBD,CAAQ,EAAIJ,GAAsBD,EACpE,CAEA,SAASQ,GAAyB5xC,EAAe6xC,EAA2B,CAE1E,MADI,CAACA,GACD,CAAC7xC,GAASA,IAAU,MAAcA,EAC/B,IACT,CAEA,SAAS8xC,GAA4B9xC,EAAe6xC,EAAkC,CACpF,OAAK7xC,EACA6xC,GACD7xC,IAAU,KAAa,MADLA,EADH,IAIrB,CAEO,SAAS+xC,GAAe5Y,EAAsB,CACnD,MAAM6Y,EAAO7Y,EAAM,QAAQ,UAAY,CAAA,EACvC,OAAOpU;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,wCAO+BoU,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,YACnEA,EAAM,QAAU,WAAa,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAQ7BA,EAAM,aAAa;AAAA,qBAClBp8B,GACRo8B,EAAM,gBAAgB,CACpB,cAAgBp8B,EAAE,OAA4B,MAC9C,MAAOo8B,EAAM,MACb,cAAeA,EAAM,cACrB,eAAgBA,EAAM,cAAA,CACvB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAMKA,EAAM,KAAK;AAAA,qBACVp8B,GACRo8B,EAAM,gBAAgB,CACpB,cAAeA,EAAM,cACrB,MAAQp8B,EAAE,OAA4B,MACtC,cAAeo8B,EAAM,cACrB,eAAgBA,EAAM,cAAA,CACvB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAOOA,EAAM,aAAa;AAAA,sBACnBp8B,GACTo8B,EAAM,gBAAgB,CACpB,cAAeA,EAAM,cACrB,MAAOA,EAAM,MACb,cAAgBp8B,EAAE,OAA4B,QAC9C,eAAgBo8B,EAAM,cAAA,CACvB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAOOA,EAAM,cAAc;AAAA,sBACpBp8B,GACTo8B,EAAM,gBAAgB,CACpB,cAAeA,EAAM,cACrB,MAAOA,EAAM,MACb,cAAeA,EAAM,cACrB,eAAiBp8B,EAAE,OAA4B,OAAA,CAChD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,QAKRo8B,EAAM,MACJpU,0DAA6DoU,EAAM,KAAK,SACxE3B,CAAO;AAAA;AAAA;AAAA,UAGP2B,EAAM,OAAS,UAAUA,EAAM,OAAO,IAAI,GAAK,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAejD6Y,EAAK,SAAW,EACdjtB,+CACAitB,EAAK,IAAKhY,GACRiY,GAAUjY,EAAKb,EAAM,SAAUA,EAAM,QAASA,EAAM,SAAUA,EAAM,OAAO,CAAA,CAC5E;AAAA;AAAA;AAAA,GAIb,CAEA,SAAS8Y,GACPjY,EACAt4B,EACA46B,EACA4V,EACA7V,EACA,CACA,MAAMpjB,EAAU+gB,EAAI,UAAYt3B,EAAUs3B,EAAI,SAAS,EAAI,MACrDmY,EAAcnY,EAAI,eAAiB,GACnCoY,EAAmBV,GAAyB1X,EAAI,aAAa,EAC7DqY,EAAWT,GAAyBO,EAAaC,CAAgB,EACjEE,EAAcX,GAAyB3X,EAAI,aAAa,EACxDuY,EAAUvY,EAAI,cAAgB,GAC9BwY,EAAYxY,EAAI,gBAAkB,GAClCmN,EAAcnN,EAAI,aAAeA,EAAI,IACrCyY,EAAUzY,EAAI,OAAS,SACvB0Y,EAAUD,EACZ,GAAG3wC,GAAW,OAAQJ,CAAQ,CAAC,YAAY,mBAAmBs4B,EAAI,GAAG,CAAC,GACtE,KAEJ,OAAOjV;AAAAA;AAAAA,0BAEiB0tB,EAChB1tB,YAAe2tB,CAAO,yBAAyBvL,CAAW,OAC1DA,CAAW;AAAA;AAAA;AAAA,mBAGFnN,EAAI,OAAS,EAAE;AAAA,sBACZqC,CAAQ;AAAA;AAAA,oBAETt/B,GAAa,CACtB,MAAMiD,EAASjD,EAAE,OAA4B,MAAM,KAAA,EACnDu/B,EAAQtC,EAAI,IAAK,CAAE,MAAOh6B,GAAS,KAAM,CAC3C,CAAC;AAAA;AAAA;AAAA,aAGEg6B,EAAI,IAAI;AAAA,aACR/gB,CAAO;AAAA,aACPywB,GAAoB1P,CAAG,CAAC;AAAA;AAAA;AAAA,mBAGlBqY,CAAQ;AAAA,sBACLhW,CAAQ;AAAA,oBACTt/B,GAAa,CACtB,MAAMiD,EAASjD,EAAE,OAA6B,MAC9Cu/B,EAAQtC,EAAI,IAAK,CACf,cAAe8X,GAA4B9xC,EAAOoyC,CAAgB,CAAA,CACnE,CACH,CAAC;AAAA;AAAA,YAECE,EAAY,IAAKzkC,GACjBkX,kBAAqBlX,CAAK,IAAIA,GAAS,SAAS,WAAA,CACjD;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKQ0kC,CAAO;AAAA,sBACJlW,CAAQ;AAAA,oBACTt/B,GAAa,CACtB,MAAMiD,EAASjD,EAAE,OAA6B,MAC9Cu/B,EAAQtC,EAAI,IAAK,CAAE,aAAch6B,GAAS,KAAM,CAClD,CAAC;AAAA;AAAA,YAECsxC,GAAe,IACdzjC,GAAUkX,kBAAqBlX,EAAM,KAAK,IAAIA,EAAM,KAAK,WAAA,CAC3D;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKQ2kC,CAAS;AAAA,sBACNnW,CAAQ;AAAA,oBACTt/B,GAAa,CACtB,MAAMiD,EAASjD,EAAE,OAA6B,MAC9Cu/B,EAAQtC,EAAI,IAAK,CAAE,eAAgBh6B,GAAS,KAAM,CACpD,CAAC;AAAA;AAAA,YAECuxC,GAAiB,IAAK1jC,GACtBkX,kBAAqBlX,CAAK,IAAIA,GAAS,SAAS,WAAA,CACjD;AAAA;AAAA;AAAA;AAAA,+CAIoCwuB,CAAQ,WAAW,IAAM6V,EAASlY,EAAI,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,GAMzF,CCnQA,SAAS2Y,GAAgBlwC,EAAoB,CAC3C,MAAMu+B,EAAY,KAAK,IAAI,EAAGv+B,CAAE,EAC1BmwC,EAAe,KAAK,MAAM5R,EAAY,GAAI,EAChD,GAAI4R,EAAe,GAAI,MAAO,GAAGA,CAAY,IAC7C,MAAMC,EAAU,KAAK,MAAMD,EAAe,EAAE,EAC5C,OAAIC,EAAU,GAAW,GAAGA,CAAO,IAE5B,GADO,KAAK,MAAMA,EAAU,EAAE,CACtB,GACjB,CAEA,SAASC,GAAcxuC,EAAetE,EAAuB,CAC3D,OAAKA,EACE+kB,8CAAiDzgB,CAAK,gBAAgBtE,CAAK,gBAD/Dw3B,CAErB,CAEO,SAASub,GAAyBhtC,EAAqB,CAC5D,MAAMitC,EAASjtC,EAAM,kBAAkB,CAAC,EACxC,GAAI,CAACitC,EAAQ,OAAOxb,EACpB,MAAMyb,EAAUD,EAAO,QACjBE,EAAcF,EAAO,YAAc,KAAK,IAAA,EACxChS,EAAYkS,EAAc,EAAI,cAAcP,GAAgBO,CAAW,CAAC,GAAK,UAC7EC,EAAaptC,EAAM,kBAAkB,OAC3C,OAAOgf;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,6CAMoCic,CAAS;AAAA;AAAA,YAE1CmS,EAAa,EACXpuB,qCAAwCouB,CAAU,iBAClD3b,CAAO;AAAA;AAAA,kDAE6Byb,EAAQ,OAAO;AAAA;AAAA,YAErDH,GAAc,OAAQG,EAAQ,IAAI,CAAC;AAAA,YACnCH,GAAc,QAASG,EAAQ,OAAO,CAAC;AAAA,YACvCH,GAAc,UAAWG,EAAQ,UAAU,CAAC;AAAA,YAC5CH,GAAc,MAAOG,EAAQ,GAAG,CAAC;AAAA,YACjCH,GAAc,WAAYG,EAAQ,YAAY,CAAC;AAAA,YAC/CH,GAAc,WAAYG,EAAQ,QAAQ,CAAC;AAAA,YAC3CH,GAAc,MAAOG,EAAQ,GAAG,CAAC;AAAA;AAAA,UAEnCltC,EAAM,kBACJgf,qCAAwChf,EAAM,iBAAiB,SAC/DyxB,CAAO;AAAA;AAAA;AAAA;AAAA,wBAIKzxB,EAAM,gBAAgB;AAAA,qBACzB,IAAMA,EAAM,2BAA2B,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMjDA,EAAM,gBAAgB;AAAA,qBACzB,IAAMA,EAAM,2BAA2B,cAAc,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMnDA,EAAM,gBAAgB;AAAA,qBACzB,IAAMA,EAAM,2BAA2B,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQnE,CCvDO,SAASqtC,GAAaja,EAAoB,CAC/C,MAAMka,EAASla,EAAM,QAAQ,QAAU,CAAA,EACjCma,EAASna,EAAM,OAAO,KAAA,EAAO,YAAA,EAC7BmH,EAAWgT,EACbD,EAAO,OAAQE,GACb,CAACA,EAAM,KAAMA,EAAM,YAAaA,EAAM,MAAM,EACzC,KAAK,GAAG,EACR,YAAA,EACA,SAASD,CAAM,CAAA,EAEpBD,EAEJ,OAAOtuB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,wCAO+BoU,EAAM,OAAO,WAAWA,EAAM,SAAS;AAAA,YACnEA,EAAM,QAAU,WAAa,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAQ7BA,EAAM,MAAM;AAAA,qBACXp8B,GACRo8B,EAAM,eAAgBp8B,EAAE,OAA4B,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA,6BAI3CujC,EAAS,MAAM;AAAA;AAAA;AAAA,QAGpCnH,EAAM,MACJpU,0DAA6DoU,EAAM,KAAK,SACxE3B,CAAO;AAAA;AAAA,QAET8I,EAAS,SAAW,EAClBvb,uEACAA;AAAAA;AAAAA,gBAEMub,EAAS,IAAKiT,GAAUC,GAAYD,EAAOpa,CAAK,CAAC,CAAC;AAAA;AAAA,WAEvD;AAAA;AAAA,GAGX,CAEA,SAASqa,GAAYD,EAAyBpa,EAAoB,CAChE,MAAMsa,EAAOta,EAAM,UAAYoa,EAAM,SAC/B33B,EAASud,EAAM,MAAMoa,EAAM,QAAQ,GAAK,GACxC7uC,EAAUy0B,EAAM,SAASoa,EAAM,QAAQ,GAAK,KAC5CG,EACJH,EAAM,QAAQ,OAAS,GAAKA,EAAM,QAAQ,KAAK,OAAS,EACpDI,EAAU,CACd,GAAGJ,EAAM,QAAQ,KAAK,IAAKx1C,GAAM,OAAOA,CAAC,EAAE,EAC3C,GAAGw1C,EAAM,QAAQ,IAAI,IAAKx2C,GAAM,OAAOA,CAAC,EAAE,EAC1C,GAAGw2C,EAAM,QAAQ,OAAO,IAAKh2C,GAAM,UAAUA,CAAC,EAAE,EAChD,GAAGg2C,EAAM,QAAQ,GAAG,IAAKt2C,GAAM,MAAMA,CAAC,EAAE,CAAA,EAEpC22C,EAAoB,CAAA,EAC1B,OAAIL,EAAM,UAAUK,EAAQ,KAAK,UAAU,EACvCL,EAAM,oBAAoBK,EAAQ,KAAK,sBAAsB,EAC1D7uB;AAAAA;AAAAA;AAAAA;AAAAA,YAIGwuB,EAAM,MAAQ,GAAGA,EAAM,KAAK,IAAM,EAAE,GAAGA,EAAM,IAAI;AAAA;AAAA,gCAE7BrwC,GAAUqwC,EAAM,YAAa,GAAG,CAAC;AAAA;AAAA,+BAElCA,EAAM,MAAM;AAAA,8BACbA,EAAM,SAAW,UAAY,WAAW;AAAA,cACxDA,EAAM,SAAW,WAAa,SAAS;AAAA;AAAA,YAEzCA,EAAM,SAAWxuB,gDAAqDyS,CAAO;AAAA;AAAA,UAE/Emc,EAAQ,OAAS,EACf5uB;AAAAA;AAAAA,2BAEe4uB,EAAQ,KAAK,IAAI,CAAC;AAAA;AAAA,cAGjCnc,CAAO;AAAA,UACToc,EAAQ,OAAS,EACf7uB;AAAAA;AAAAA,0BAEc6uB,EAAQ,KAAK,IAAI,CAAC;AAAA;AAAA,cAGhCpc,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMKic,CAAI;AAAA,qBACP,IAAMta,EAAM,SAASoa,EAAM,SAAUA,EAAM,QAAQ,CAAC;AAAA;AAAA,cAE3DA,EAAM,SAAW,SAAW,SAAS;AAAA;AAAA,YAEvCG,EACE3uB;AAAAA;AAAAA,4BAEc0uB,CAAI;AAAA,yBACP,IACPta,EAAM,UAAUoa,EAAM,SAAUA,EAAM,KAAMA,EAAM,QAAQ,CAAC,EAAE,EAAE,CAAC;AAAA;AAAA,kBAEhEE,EAAO,cAAgBF,EAAM,QAAQ,CAAC,EAAE,KAAK;AAAA,yBAEjD/b,CAAO;AAAA;AAAA,UAEX9yB,EACEqgB;AAAAA;AAAAA,+CAGIrgB,EAAQ,OAAS,QACb,+BACA,+BACN;AAAA;AAAA,gBAEEA,EAAQ,OAAO;AAAA,oBAEnB8yB,CAAO;AAAA,UACT+b,EAAM,WACJxuB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,2BAKenJ,CAAM;AAAA,2BACL7e,GACRo8B,EAAM,OAAOoa,EAAM,SAAWx2C,EAAE,OAA4B,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAM1D02C,CAAI;AAAA,yBACP,IAAMta,EAAM,UAAUoa,EAAM,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA,cAKlD/b,CAAO;AAAA;AAAA;AAAA,GAInB,CCnKO,SAASqc,GAAU9tC,EAAqBxE,EAAU,CACvD,MAAMuyC,EAAOhyC,GAAWP,EAAKwE,EAAM,QAAQ,EAC3C,OAAOgf;AAAAA;AAAAA,aAEI+uB,CAAI;AAAA,wBACO/tC,EAAM,MAAQxE,EAAM,SAAW,EAAE;AAAA,eACzCiI,GAAsB,CAE5BA,EAAM,kBACNA,EAAM,SAAW,GACjBA,EAAM,SACNA,EAAM,SACNA,EAAM,UACNA,EAAM,SAIRA,EAAM,eAAA,EACNzD,EAAM,OAAOxE,CAAG,EAClB,CAAC;AAAA,cACOe,GAAYf,CAAG,CAAC;AAAA;AAAA,wDAE0Bc,GAAWd,CAAG,CAAC;AAAA,qCAClCe,GAAYf,CAAG,CAAC;AAAA;AAAA,GAGrD,CAEO,SAASwyC,GAAmBhuC,EAAqB,CACtD,MAAMiuC,EAAiBC,GAAsBluC,EAAM,WAAYA,EAAM,cAAc,EAC7EmuC,EAAwBnuC,EAAM,WAC9BouC,EAAqBpuC,EAAM,WAC3BquC,EAAeruC,EAAM,WAAa,GAAQA,EAAM,SAAS,iBACzDsuC,EAActuC,EAAM,WAAa,GAAOA,EAAM,SAAS,cAEvDuuC,EAAcvvB,2PACdwvB,EAAYxvB,iTAClB,OAAOA;AAAAA;AAAAA;AAAAA;AAAAA,mBAIUhf,EAAM,UAAU;AAAA,sBACb,CAACA,EAAM,SAAS;AAAA,oBACjBhJ,GAAa,CACtB,MAAM+D,EAAQ/D,EAAE,OAA6B,MAC7CgJ,EAAM,WAAajF,EACnBiF,EAAM,YAAc,GACpBA,EAAM,WAAa,KACnBA,EAAM,oBAAsB,KAC5BA,EAAM,UAAY,KAClBA,EAAM,gBAAA,EACNA,EAAM,gBAAA,EACNA,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,WAAYjF,EACZ,qBAAsBA,CAAA,CACvB,EACIiF,EAAM,sBAAA,EACX0Z,GAAsB1Z,EAAOjF,CAAU,EAClCgF,GAAgBC,CAAK,CAC5B,CAAC;AAAA;AAAA,YAECu0B,GACA0Z,EACCzsC,GAAUA,EAAM,IAChBA,GACCwd,kBAAqBxd,EAAM,GAAG;AAAA,kBAC1BA,EAAM,aAAeA,EAAM,GAAG;AAAA,wBAAA,CAErC;AAAA;AAAA;AAAA;AAAA;AAAA,oBAKSxB,EAAM,aAAe,CAACA,EAAM,SAAS;AAAA,iBACxC,IAAM,CACbA,EAAM,gBAAA,EACDD,GAAgBC,CAAK,CAC5B,CAAC;AAAA;AAAA;AAAA,UAGCuuC,CAAW;AAAA;AAAA;AAAA;AAAA,uCAIkBF,EAAe,SAAW,EAAE;AAAA,oBAC/CF,CAAqB;AAAA,iBACxB,IAAM,CACTA,GACJnuC,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,iBAAkB,CAACA,EAAM,SAAS,gBAAA,CACnC,CACH,CAAC;AAAA,uBACcquC,CAAY;AAAA,gBACnBF,EACJ,6BACA,0CAA0C;AAAA;AAAA;AAAA;AAAA;AAAA,uCAKfG,EAAc,SAAW,EAAE;AAAA,oBAC9CF,CAAkB;AAAA,iBACrB,IAAM,CACTA,GACJpuC,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,cAAe,CAACA,EAAM,SAAS,aAAA,CAChC,CACH,CAAC;AAAA,uBACcsuC,CAAW;AAAA,gBAClBF,EACJ,6BACA,gDAAgD;AAAA;AAAA,UAElDI,CAAS;AAAA;AAAA;AAAA,GAInB,CAEA,SAASN,GAAsBjzC,EAAoBwzC,EAAqC,CACtF,MAAMrK,MAAW,IACX5uB,EAAwD,CAAA,EAExDk5B,EAAkBD,GAAU,UAAU,KAAMx3C,GAAMA,EAAE,MAAQgE,CAAU,EAO5E,GAJAmpC,EAAK,IAAInpC,CAAU,EACnBua,EAAQ,KAAK,CAAE,IAAKva,EAAY,YAAayzC,GAAiB,YAAa,EAGvED,GAAU,SACZ,UAAWx3C,KAAKw3C,EAAS,SAClBrK,EAAK,IAAIntC,EAAE,GAAG,IACjBmtC,EAAK,IAAIntC,EAAE,GAAG,EACdue,EAAQ,KAAK,CAAE,IAAKve,EAAE,IAAK,YAAaA,EAAE,YAAa,GAK7D,OAAOue,CACT,CAEA,MAAMm5B,GAA2B,CAAC,SAAU,QAAS,MAAM,EAEpD,SAASC,GAAkB5uC,EAAqB,CACrD,MAAMie,EAAQ,KAAK,IAAI,EAAG0wB,GAAY,QAAQ3uC,EAAM,KAAK,CAAC,EACpDyW,EAAc1b,GAAqB0I,GAAsB,CAE7D,MAAMiT,EAAkC,CAAE,QAD1BjT,EAAM,aACoB,GACtCA,EAAM,SAAWA,EAAM,WACzBiT,EAAQ,eAAiBjT,EAAM,QAC/BiT,EAAQ,eAAiBjT,EAAM,SAEjCzD,EAAM,SAASjF,EAAM2b,CAAO,CAC9B,EAEA,OAAOsI;AAAAA,sDAC6Cf,CAAK;AAAA;AAAA;AAAA;AAAA,wCAInBje,EAAM,QAAU,SAAW,SAAW,EAAE;AAAA,mBAC7DyW,EAAW,QAAQ,CAAC;AAAA,yBACdzW,EAAM,QAAU,QAAQ;AAAA;AAAA;AAAA;AAAA,YAIrC6uC,IAAmB;AAAA;AAAA;AAAA,wCAGS7uC,EAAM,QAAU,QAAU,SAAW,EAAE;AAAA,mBAC5DyW,EAAW,OAAO,CAAC;AAAA,yBACbzW,EAAM,QAAU,OAAO;AAAA;AAAA;AAAA;AAAA,YAIpC8uC,IAAe;AAAA;AAAA;AAAA,wCAGa9uC,EAAM,QAAU,OAAS,SAAW,EAAE;AAAA,mBAC3DyW,EAAW,MAAM,CAAC;AAAA,yBACZzW,EAAM,QAAU,MAAM;AAAA;AAAA;AAAA;AAAA,YAInC+uC,IAAgB;AAAA;AAAA;AAAA;AAAA,GAK5B,CAEA,SAASD,IAAgB,CACvB,OAAO9vB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,GAaT,CAEA,SAAS+vB,IAAiB,CACxB,OAAO/vB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,GAOT,CAEA,SAAS6vB,IAAoB,CAC3B,OAAO7vB;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,GAOT,CC7JA,MAAMgwB,GAAiB,UACjBC,GAAiB,gBAEvB,SAASC,GAA0BlvC,EAAyC,CAC1E,MAAMimC,EAAOjmC,EAAM,YAAY,QAAU,CAAA,EAEnC7E,EADSH,GAAqBgF,EAAM,UAAU,GAE1C,SACRA,EAAM,YAAY,WAClB,OAEImT,EADQ8yB,EAAK,KAAMzkC,GAAUA,EAAM,KAAOrG,CAAO,GAC/B,SAClBiB,EAAY+W,GAAU,WAAaA,GAAU,OACnD,GAAK/W,EACL,OAAI4yC,GAAe,KAAK5yC,CAAS,GAAK6yC,GAAe,KAAK7yC,CAAS,EAAUA,EACtE+W,GAAU,SACnB,CAEO,SAASg8B,GAAUnvC,EAAqB,CAC7C,MAAMovC,EAAgBpvC,EAAM,gBAAgB,OACtCqvC,EAAgBrvC,EAAM,gBAAgB,OAAS,KAC/CsvC,EAAWtvC,EAAM,YAAY,cAAgB,KAC7CuvC,EAAqBvvC,EAAM,UAAY,KAAO,6BAC9CwvC,EAASxvC,EAAM,MAAQ,OACvByvC,EAAYD,IAAWxvC,EAAM,SAAS,eAAiBA,EAAM,YAC7DquC,EAAeruC,EAAM,WAAa,GAAQA,EAAM,SAAS,iBACzD0vC,EAAqBR,GAA0BlvC,CAAK,EACpD2vC,EAAgB3vC,EAAM,eAAiB0vC,GAAsB,KAEnE,OAAO1wB;AAAAA,wBACewwB,EAAS,cAAgB,EAAE,IAAIC,EAAY,oBAAsB,EAAE,IAAIzvC,EAAM,SAAS,aAAe,uBAAyB,EAAE,IAAIA,EAAM,WAAa,oBAAsB,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA,qBAKlL,IACPA,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,aAAc,CAACA,EAAM,SAAS,YAAA,CAC/B,CAAC;AAAA,qBACKA,EAAM,SAAS,aAAe,iBAAmB,kBAAkB;AAAA,0BAC9DA,EAAM,SAAS,aAAe,iBAAmB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCAWxDA,EAAM,UAAY,KAAO,EAAE;AAAA;AAAA,iCAE/BA,EAAM,UAAY,KAAO,SAAS;AAAA;AAAA,YAEvD4uC,GAAkB5uC,CAAK,CAAC;AAAA;AAAA;AAAA,0BAGVA,EAAM,SAAS,aAAe,iBAAmB,EAAE;AAAA,UACnE3E,GAAW,IAAK42B,GAAU,CAC1B,MAAM2d,EAAmB5vC,EAAM,SAAS,mBAAmBiyB,EAAM,KAAK,GAAK,GACrE4d,EAAe5d,EAAM,KAAK,KAAMz2B,GAAQA,IAAQwE,EAAM,GAAG,EAC/D,OAAOgf;AAAAA,oCACmB4wB,GAAoB,CAACC,EAAe,uBAAyB,EAAE;AAAA;AAAA;AAAA,yBAG1E,IAAM,CACb,MAAM90C,EAAO,CAAE,GAAGiF,EAAM,SAAS,kBAAA,EACjCjF,EAAKk3B,EAAM,KAAK,EAAI,CAAC2d,EACrB5vC,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,mBAAoBjF,CAAA,CACrB,CACH,CAAC;AAAA,gCACe,CAAC60C,CAAgB;AAAA;AAAA,gDAED3d,EAAM,KAAK;AAAA,mDACR2d,EAAmB,IAAM,GAAG;AAAA;AAAA;AAAA,kBAG7D3d,EAAM,KAAK,IAAKz2B,GAAQsyC,GAAU9tC,EAAOxE,CAAG,CAAC,CAAC;AAAA;AAAA;AAAA,WAIxD,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAmBmBg0C,EAAS,gBAAkB,EAAE;AAAA;AAAA;AAAA,sCAGpBjzC,GAAYyD,EAAM,GAAG,CAAC;AAAA,oCACxBxD,GAAewD,EAAM,GAAG,CAAC;AAAA;AAAA;AAAA,cAG/CA,EAAM,UACJgf,6BAAgChf,EAAM,SAAS,SAC/CyxB,CAAO;AAAA,cACT+d,EAASxB,GAAmBhuC,CAAK,EAAIyxB,CAAO;AAAA;AAAA;AAAA;AAAA,UAIhDzxB,EAAM,MAAQ,WACZ8qC,GAAe,CACb,UAAW9qC,EAAM,UACjB,MAAOA,EAAM,MACb,SAAUA,EAAM,SAChB,SAAUA,EAAM,SAChB,UAAWA,EAAM,UACjB,cAAAovC,EACA,cAAAC,EACA,YAAarvC,EAAM,YAAY,SAAW,KAC1C,SAAAsvC,EACA,oBAAqBtvC,EAAM,oBAC3B,iBAAmBjF,GAASiF,EAAM,cAAcjF,CAAI,EACpD,iBAAmBA,GAAUiF,EAAM,SAAWjF,EAC9C,mBAAqBA,GAAS,CAC5BiF,EAAM,WAAajF,EACnBiF,EAAM,YAAc,GACpBA,EAAM,gBAAA,EACNA,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,WAAYjF,EACZ,qBAAsBA,CAAA,CACvB,EACIiF,EAAM,sBAAA,CACb,EACA,UAAW,IAAMA,EAAM,QAAA,EACvB,UAAW,IAAMA,EAAM,aAAA,CAAa,CACrC,EACDyxB,CAAO;AAAA;AAAA,UAETzxB,EAAM,MAAQ,WACZmiC,GAAe,CACb,UAAWniC,EAAM,UACjB,QAASA,EAAM,gBACf,SAAUA,EAAM,iBAChB,UAAWA,EAAM,cACjB,cAAeA,EAAM,oBACrB,gBAAiBA,EAAM,qBACvB,kBAAmBA,EAAM,uBACzB,kBAAmBA,EAAM,uBACzB,aAAcA,EAAM,aACpB,aAAcA,EAAM,aACpB,oBAAqBA,EAAM,oBAC3B,WAAYA,EAAM,WAClB,cAAeA,EAAM,cACrB,aAAcA,EAAM,aACpB,gBAAiBA,EAAM,gBACvB,sBAAuBA,EAAM,sBAC7B,sBAAuBA,EAAM,sBAC7B,UAAY2G,GAAUD,GAAa1G,EAAO2G,CAAK,EAC/C,gBAAkBrE,GAAUtC,EAAM,oBAAoBsC,CAAK,EAC3D,eAAgB,IAAMtC,EAAM,mBAAA,EAC5B,iBAAkB,IAAMA,EAAM,qBAAA,EAC9B,cAAe,CAACvE,EAAMxB,IAAUsL,GAAsBvF,EAAOvE,EAAMxB,CAAK,EACxE,aAAc,IAAM+F,EAAM,wBAAA,EAC1B,eAAgB,IAAMA,EAAM,0BAAA,EAC5B,mBAAoB,CAACy/B,EAAWS,IAC9BlgC,EAAM,uBAAuBy/B,EAAWS,CAAO,EACjD,qBAAsB,IAAMlgC,EAAM,yBAAA,EAClC,0BAA2B,CAAC4/B,EAAO3lC,IACjC+F,EAAM,8BAA8B4/B,EAAO3lC,CAAK,EAClD,mBAAoB,IAAM+F,EAAM,uBAAA,EAChC,qBAAsB,IAAMA,EAAM,yBAAA,EAClC,6BAA8B,IAAMA,EAAM,iCAAA,CAAiC,CAC5E,EACDyxB,CAAO;AAAA;AAAA,UAETzxB,EAAM,MAAQ,YACZ6kC,GAAgB,CACd,QAAS7kC,EAAM,gBACf,QAASA,EAAM,gBACf,UAAWA,EAAM,cACjB,cAAeA,EAAM,eACrB,UAAW,IAAMoV,GAAapV,CAAK,CAAA,CACpC,EACDyxB,CAAO;AAAA;AAAA,UAETzxB,EAAM,MAAQ,WACZgsC,GAAe,CACb,QAAShsC,EAAM,gBACf,OAAQA,EAAM,eACd,MAAOA,EAAM,cACb,cAAeA,EAAM,qBACrB,MAAOA,EAAM,oBACb,cAAeA,EAAM,sBACrB,eAAgBA,EAAM,uBACtB,SAAUA,EAAM,SAChB,gBAAkBjF,GAAS,CACzBiF,EAAM,qBAAuBjF,EAAK,cAClCiF,EAAM,oBAAsBjF,EAAK,MACjCiF,EAAM,sBAAwBjF,EAAK,cACnCiF,EAAM,uBAAyBjF,EAAK,cACrC,EACA,UAAW,IAAM4F,GAAaX,CAAK,EACnC,QAAS,CAACgB,EAAKC,IAAUF,GAAaf,EAAOgB,EAAKC,CAAK,EACvD,SAAWD,GAAQE,GAAclB,EAAOgB,CAAG,CAAA,CAC5C,EACDywB,CAAO;AAAA;AAAA,UAEVzxB,EAAM,MAAQ,OACZqkC,GAAW,CACT,QAASrkC,EAAM,YACf,OAAQA,EAAM,WACd,KAAMA,EAAM,SACZ,MAAOA,EAAM,UACb,KAAMA,EAAM,SACZ,KAAMA,EAAM,SACZ,SAAUA,EAAM,kBAAkB,aAAa,OAC3CA,EAAM,iBAAiB,YAAY,IAAKwB,GAAUA,EAAM,EAAE,EAC1DxB,EAAM,kBAAkB,cAAgB,CAAA,EAC5C,cAAeA,EAAM,kBAAkB,eAAiB,CAAA,EACxD,YAAaA,EAAM,kBAAkB,aAAe,CAAA,EACpD,UAAWA,EAAM,cACjB,KAAMA,EAAM,SACZ,aAAeiB,GAAWjB,EAAM,SAAW,CAAE,GAAGA,EAAM,SAAU,GAAGiB,CAAA,EACnE,UAAW,IAAMjB,EAAM,SAAA,EACvB,MAAO,IAAMiG,GAAWjG,CAAK,EAC7B,SAAU,CAACmG,EAAKE,IAAYD,GAAcpG,EAAOmG,EAAKE,CAAO,EAC7D,MAAQF,GAAQG,GAAWtG,EAAOmG,CAAG,EACrC,SAAWA,GAAQK,GAAcxG,EAAOmG,CAAG,EAC3C,WAAaM,GAAUF,GAAavG,EAAOyG,CAAK,CAAA,CACjD,EACDgrB,CAAO;AAAA;AAAA,UAETzxB,EAAM,MAAQ,SACZqtC,GAAa,CACX,QAASrtC,EAAM,cACf,OAAQA,EAAM,aACd,MAAOA,EAAM,YACb,OAAQA,EAAM,aACd,MAAOA,EAAM,WACb,SAAUA,EAAM,cAChB,QAASA,EAAM,cACf,eAAiBjF,GAAUiF,EAAM,aAAejF,EAChD,UAAW,IAAMwa,GAAWvV,EAAO,CAAE,cAAe,GAAM,EAC1D,SAAU,CAACgB,EAAKqF,IAAYsP,GAAmB3V,EAAOgB,EAAKqF,CAAO,EAClE,OAAQ,CAACrF,EAAK/G,IAAUwb,GAAgBzV,EAAOgB,EAAK/G,CAAK,EACzD,UAAY+G,GAAQ4U,GAAgB5V,EAAOgB,CAAG,EAC9C,UAAW,CAAC0U,EAAUpb,EAAMyb,IAC1BD,GAAa9V,EAAO0V,EAAUpb,EAAMyb,CAAS,CAAA,CAChD,EACD0b,CAAO;AAAA;AAAA,UAETzxB,EAAM,MAAQ,QACZylC,GAAY,CACV,QAASzlC,EAAM,aACf,MAAOA,EAAM,MACb,eAAgBA,EAAM,eACtB,aAAcA,EAAM,aACpB,YAAaA,EAAM,YACnB,WAAYA,EAAM,YAAeA,EAAM,gBAAgB,OACvD,cAAeA,EAAM,cACrB,aAAcA,EAAM,aACpB,YAAaA,EAAM,gBACnB,eAAgBA,EAAM,eACtB,qBAAsBA,EAAM,qBAC5B,oBAAqBA,EAAM,oBAC3B,mBAAoBA,EAAM,mBAC1B,sBAAuBA,EAAM,sBAC7B,kBAAmBA,EAAM,kBACzB,2BAA4BA,EAAM,2BAClC,oBAAqBA,EAAM,oBAC3B,0BAA2BA,EAAM,0BACjC,UAAW,IAAMyU,GAAUzU,CAAK,EAChC,iBAAkB,IAAMmU,GAAYnU,CAAK,EACzC,gBAAkBqU,GAAcD,GAAqBpU,EAAOqU,CAAS,EACrE,eAAiBA,GAAcC,GAAoBtU,EAAOqU,CAAS,EACnE,eAAgB,CAACuyB,EAAUhoC,EAAM8U,IAC/Ba,GAAkBvU,EAAO,CAAE,SAAA4mC,EAAU,KAAAhoC,EAAM,OAAA8U,EAAQ,EACrD,eAAgB,CAACkzB,EAAUhoC,IACzB4V,GAAkBxU,EAAO,CAAE,SAAA4mC,EAAU,KAAAhoC,EAAM,EAC7C,aAAc,IAAMiG,GAAW7E,CAAK,EACpC,oBAAqB,IAAM,CACzB,MAAMkD,EACJlD,EAAM,sBAAwB,QAAUA,EAAM,0BAC1C,CAAE,KAAM,OAAiB,OAAQA,EAAM,yBAAA,EACvC,CAAE,KAAM,SAAA,EACd,OAAO6U,GAAkB7U,EAAOkD,CAAM,CACxC,EACA,cAAgByR,GAAW,CACrBA,EACFpP,GAAsBvF,EAAO,CAAC,QAAS,OAAQ,MAAM,EAAG2U,CAAM,EAE9DnP,GAAsBxF,EAAO,CAAC,QAAS,OAAQ,MAAM,CAAC,CAE1D,EACA,YAAa,CAAC8vC,EAAYn7B,IAAW,CACnC,MAAMhZ,EAAW,CAAC,SAAU,OAAQm0C,EAAY,QAAS,OAAQ,MAAM,EACnEn7B,EACFpP,GAAsBvF,EAAOrE,EAAUgZ,CAAM,EAE7CnP,GAAsBxF,EAAOrE,CAAQ,CAEzC,EACA,eAAgB,IAAMwJ,GAAWnF,CAAK,EACtC,4BAA6B,CAAC2wB,EAAMhc,IAAW,CAC7C3U,EAAM,oBAAsB2wB,EAC5B3wB,EAAM,0BAA4B2U,EAClC3U,EAAM,sBAAwB,KAC9BA,EAAM,kBAAoB,KAC1BA,EAAM,mBAAqB,GAC3BA,EAAM,2BAA6B,IACrC,EACA,2BAA6B7E,GAAY,CACvC6E,EAAM,2BAA6B7E,CACrC,EACA,qBAAsB,CAACM,EAAMxB,IAC3Bib,GAA6BlV,EAAOvE,EAAMxB,CAAK,EACjD,sBAAwBwB,GACtB0Z,GAA6BnV,EAAOvE,CAAI,EAC1C,oBAAqB,IAAM,CACzB,MAAMyH,EACJlD,EAAM,sBAAwB,QAAUA,EAAM,0BAC1C,CAAE,KAAM,OAAiB,OAAQA,EAAM,yBAAA,EACvC,CAAE,KAAM,SAAA,EACd,OAAOgV,GAAkBhV,EAAOkD,CAAM,CACxC,CAAA,CACD,EACDuuB,CAAO;AAAA;AAAA,UAETzxB,EAAM,MAAQ,OACZ6zB,GAAW,CACT,WAAY7zB,EAAM,WAClB,mBAAqBjF,GAAS,CAC5BiF,EAAM,WAAajF,EACnBiF,EAAM,YAAc,GACpBA,EAAM,WAAa,KACnBA,EAAM,oBAAsB,KAC5BA,EAAM,UAAY,KAClBA,EAAM,UAAY,CAAA,EAClBA,EAAM,gBAAA,EACNA,EAAM,gBAAA,EACNA,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,WAAYjF,EACZ,qBAAsBA,CAAA,CACvB,EACIiF,EAAM,sBAAA,EACND,GAAgBC,CAAK,EACrBsa,GAAkBta,CAAK,CAC9B,EACA,cAAeA,EAAM,kBACrB,aAAAquC,EACA,QAASruC,EAAM,YACf,QAASA,EAAM,YACf,mBAAoB2vC,EACpB,SAAU3vC,EAAM,aAChB,aAAcA,EAAM,iBACpB,OAAQA,EAAM,WACd,gBAAiBA,EAAM,oBACvB,MAAOA,EAAM,YACb,MAAOA,EAAM,UACb,UAAWA,EAAM,UACjB,QAASA,EAAM,UACf,eAAgBuvC,EAChB,MAAOvvC,EAAM,UACb,SAAUA,EAAM,eAChB,UAAWyvC,EACX,UAAW,KACTzvC,EAAM,gBAAA,EACC,QAAQ,IAAI,CAACD,GAAgBC,CAAK,EAAGsa,GAAkBta,CAAK,CAAC,CAAC,GAEvE,kBAAmB,IAAM,CACnBA,EAAM,YACVA,EAAM,cAAc,CAClB,GAAGA,EAAM,SACT,cAAe,CAACA,EAAM,SAAS,aAAA,CAChC,CACH,EACA,aAAeyD,GAAUzD,EAAM,iBAAiByD,CAAK,EACrD,cAAgB1I,GAAUiF,EAAM,YAAcjF,EAC9C,OAAQ,IAAMiF,EAAM,eAAA,EACpB,SAAU,EAAQA,EAAM,UACxB,QAAS,IAAA,CAAWA,EAAM,gBAAA,GAC1B,cAAgBkC,GAAOlC,EAAM,oBAAoBkC,CAAE,EACnD,aAAc,IACZlC,EAAM,eAAe,OAAQ,CAAE,aAAc,GAAM,EAErD,YAAaA,EAAM,YACnB,eAAgBA,EAAM,eACtB,aAAcA,EAAM,aACpB,WAAYA,EAAM,WAClB,cAAgBnB,GAAoBmB,EAAM,kBAAkBnB,CAAO,EACnE,eAAgB,IAAMmB,EAAM,mBAAA,EAC5B,mBAAqB+vC,GAAkB/vC,EAAM,uBAAuB+vC,CAAK,EACzE,cAAe/vC,EAAM,cACrB,gBAAiBA,EAAM,eAAA,CACxB,EACDyxB,CAAO;AAAA;AAAA,UAETzxB,EAAM,MAAQ,SACZu8B,GAAa,CACX,IAAKv8B,EAAM,UACX,MAAOA,EAAM,YACb,OAAQA,EAAM,aACd,QAASA,EAAM,cACf,OAAQA,EAAM,aACd,SAAUA,EAAM,eAChB,SAAUA,EAAM,cAChB,UAAWA,EAAM,UACjB,OAAQA,EAAM,aACd,cAAeA,EAAM,oBACrB,QAASA,EAAM,cACf,SAAUA,EAAM,eAChB,UAAWA,EAAM,WACjB,cAAeA,EAAM,mBACrB,YAAaA,EAAM,kBACnB,cAAeA,EAAM,oBACrB,iBAAkBA,EAAM,uBACxB,YAAcjF,GAAUiF,EAAM,UAAYjF,EAC1C,iBAAmBmb,GAAUlW,EAAM,eAAiBkW,EACpD,YAAa,CAACza,EAAMxB,IAAUsL,GAAsBvF,EAAOvE,EAAMxB,CAAK,EACtE,eAAiBq/B,GAAWt5B,EAAM,kBAAoBs5B,EACtD,gBAAkBqE,GAAY,CAC5B39B,EAAM,oBAAsB29B,EAC5B39B,EAAM,uBAAyB,IACjC,EACA,mBAAqB29B,GAAa39B,EAAM,uBAAyB29B,EACjE,SAAU,IAAM94B,GAAW7E,CAAK,EAChC,OAAQ,IAAMmF,GAAWnF,CAAK,EAC9B,QAAS,IAAMqF,GAAYrF,CAAK,EAChC,SAAU,IAAMsF,GAAUtF,CAAK,CAAA,CAChC,EACDyxB,CAAO;AAAA;AAAA,UAETzxB,EAAM,MAAQ,QACZ2kC,GAAY,CACV,QAAS3kC,EAAM,aACf,OAAQA,EAAM,YACd,OAAQA,EAAM,YACd,OAAQA,EAAM,YACd,UAAWA,EAAM,eACjB,SAAUA,EAAM,SAChB,WAAYA,EAAM,gBAClB,WAAYA,EAAM,gBAClB,WAAYA,EAAM,gBAClB,UAAWA,EAAM,eACjB,mBAAqBjF,GAAUiF,EAAM,gBAAkBjF,EACvD,mBAAqBA,GAAUiF,EAAM,gBAAkBjF,EACvD,UAAW,IAAMgM,GAAU/G,CAAK,EAChC,OAAQ,IAAMqH,GAAgBrH,CAAK,CAAA,CACpC,EACDyxB,CAAO;AAAA;AAAA,UAETzxB,EAAM,MAAQ,OACZslC,GAAW,CACT,QAAStlC,EAAM,YACf,MAAOA,EAAM,UACb,KAAMA,EAAM,SACZ,QAASA,EAAM,YACf,WAAYA,EAAM,eAClB,aAAcA,EAAM,iBACpB,WAAYA,EAAM,eAClB,UAAWA,EAAM,cACjB,mBAAqBjF,GAAUiF,EAAM,eAAiBjF,EACtD,cAAe,CAAC+M,EAAOzB,IAAY,CACjCrG,EAAM,iBAAmB,CAAE,GAAGA,EAAM,iBAAkB,CAAC8H,CAAK,EAAGzB,CAAA,CACjE,EACA,mBAAqBtL,GAAUiF,EAAM,eAAiBjF,EACtD,UAAW,IAAMmN,GAASlI,EAAO,CAAE,MAAO,GAAM,EAChD,SAAU,CAACV,EAAOf,IAAUyB,EAAM,WAAWV,EAAOf,CAAK,EACzD,SAAWkF,GAAUzD,EAAM,iBAAiByD,CAAK,CAAA,CAClD,EACDguB,CAAO;AAAA;AAAA,QAEXub,GAAyBhtC,CAAK,CAAC;AAAA;AAAA,GAGvC,CCtjBO,MAAMgwC,GAAuD,CAClE,MAAO,GACP,MAAO,GACP,KAAM,GACN,KAAM,GACN,MAAO,GACP,MAAO,EACT,EAEaC,GAAmC,CAC9C,KAAM,GACN,YAAa,GACb,QAAS,GACT,QAAS,GACT,aAAc,QACd,WAAY,GACZ,YAAa,KACb,UAAW,UACX,SAAU,YACV,OAAQ,GACR,cAAe,OACf,SAAU,iBACV,YAAa,cACb,YAAa,GACb,QAAS,GACT,QAAS,OACT,GAAI,GACJ,eAAgB,GAChB,iBAAkB,EACpB,ECrBA,eAAsBC,GAAWlwC,EAAoB,CACnD,GAAI,GAACA,EAAM,QAAU,CAACA,EAAM,YACxB,CAAAA,EAAM,cACV,CAAAA,EAAM,cAAgB,GACtBA,EAAM,YAAc,KACpB,GAAI,CACF,MAAMC,EAAO,MAAMD,EAAM,OAAO,QAAQ,cAAe,EAAE,EACrDC,MAAW,WAAaA,EAC9B,OAASC,EAAK,CACZF,EAAM,YAAc,OAAOE,CAAG,CAChC,QAAA,CACEF,EAAM,cAAgB,EACxB,EACF,CCxBO,MAAMmwC,GAAqB,CAChC,WAAY,aACZ,WAAY,sBACZ,QAAS,UACT,IAAK,MACL,eAAgB,iBAChB,UAAW,iBACX,QAAS,eACT,YAAa,mBACb,UAAW,YACX,KAAM,OACN,YAAa,cACb,MAAO,gBACT,EAKaC,GAAuBD,GAGvBE,GAAuB,CAClC,QAAS,UACT,IAAK,MACL,GAAI,KACJ,QAAS,UACT,KAAM,OACN,MAAO,QACP,KAAM,MACR,EAe8B,IAAI,IAAqB,OAAO,OAAOF,EAAkB,CAAC,EACxD,IAAI,IAAuB,OAAO,OAAOE,EAAoB,CAAC,ECjCvF,SAASC,GAAuB1vC,EAAyC,CAC9E,MAAM2iC,EAAU3iC,EAAO,UAAYA,EAAO,MAAQ,KAAO,MACnD8S,EAAS9S,EAAO,OAAO,KAAK,GAAG,EAC/BsX,EAAQtX,EAAO,OAAS,GACxBhF,EAAO,CACX2nC,EACA3iC,EAAO,SACPA,EAAO,SACPA,EAAO,WACPA,EAAO,KACP8S,EACA,OAAO9S,EAAO,UAAU,EACxBsX,CAAA,EAEF,OAAIqrB,IAAY,MACd3nC,EAAK,KAAKgF,EAAO,OAAS,EAAE,EAEvBhF,EAAK,KAAK,GAAG,CACtB,CCgCA,MAAM20C,GAA4B,KAE3B,MAAMC,EAAqB,CAUhC,YAAoBroC,EAAmC,CAAnC,KAAA,KAAAA,EATpB,KAAQ,GAAuB,KAC/B,KAAQ,YAAc,IACtB,KAAQ,OAAS,GACjB,KAAQ,QAAyB,KACjC,KAAQ,aAA8B,KACtC,KAAQ,YAAc,GACtB,KAAQ,aAA8B,KACtC,KAAQ,UAAY,GAEoC,CAExD,OAAQ,CACN,KAAK,OAAS,GACd,KAAK,QAAA,CACP,CAEA,MAAO,CACL,KAAK,OAAS,GACd,KAAK,IAAI,MAAA,EACT,KAAK,GAAK,KACV,KAAK,aAAa,IAAI,MAAM,wBAAwB,CAAC,CACvD,CAEA,IAAI,WAAY,CACd,OAAO,KAAK,IAAI,aAAe,UAAU,IAC3C,CAEQ,SAAU,CACZ,KAAK,SACT,KAAK,GAAK,IAAI,UAAU,KAAK,KAAK,GAAG,EACrC,KAAK,GAAG,OAAS,IAAM,KAAK,aAAA,EAC5B,KAAK,GAAG,UAAasoC,GAAO,KAAK,cAAc,OAAOA,EAAG,MAAQ,EAAE,CAAC,EACpE,KAAK,GAAG,QAAWA,GAAO,CACxB,MAAMC,EAAS,OAAOD,EAAG,QAAU,EAAE,EACrC,KAAK,GAAK,KACV,KAAK,aAAa,IAAI,MAAM,mBAAmBA,EAAG,IAAI,MAAMC,CAAM,EAAE,CAAC,EACrE,KAAK,KAAK,UAAU,CAAE,KAAMD,EAAG,KAAM,OAAAC,EAAQ,EAC7C,KAAK,kBAAA,CACP,EACA,KAAK,GAAG,QAAU,IAAM,CAExB,EACF,CAEQ,mBAAoB,CAC1B,GAAI,KAAK,OAAQ,OACjB,MAAMC,EAAQ,KAAK,UACnB,KAAK,UAAY,KAAK,IAAI,KAAK,UAAY,IAAK,IAAM,EACtD,OAAO,WAAW,IAAM,KAAK,QAAA,EAAWA,CAAK,CAC/C,CAEQ,aAAazwC,EAAY,CAC/B,SAAW,CAAA,CAAGtI,CAAC,IAAK,KAAK,QAASA,EAAE,OAAOsI,CAAG,EAC9C,KAAK,QAAQ,MAAA,CACf,CAEA,MAAc,aAAc,CAC1B,GAAI,KAAK,YAAa,OACtB,KAAK,YAAc,GACf,KAAK,eAAiB,OACxB,OAAO,aAAa,KAAK,YAAY,EACrC,KAAK,aAAe,MAMtB,MAAM0wC,EAAkB,OAAO,OAAW,KAAe,CAAC,CAAC,OAAO,OAE5Dl9B,EAAS,CAAC,iBAAkB,qBAAsB,kBAAkB,EACpE9U,EAAO,WACb,IAAIiyC,EAAgF,KAChFC,EAAsB,GACtBC,EAAY,KAAK,KAAK,MAE1B,GAAIH,EAAiB,CACnBC,EAAiB,MAAM79B,GAAA,EACvB,MAAMg+B,EAAcj9B,GAAoB,CACtC,SAAU88B,EAAe,SACzB,KAAAjyC,CAAA,CACD,GAAG,MACJmyC,EAAYC,GAAe,KAAK,KAAK,MACrCF,EAAsB,GAAQE,GAAe,KAAK,KAAK,MACzD,CACA,MAAMC,EACJF,GAAa,KAAK,KAAK,SACnB,CACE,MAAOA,EACP,SAAU,KAAK,KAAK,QAAA,EAEtB,OAEN,IAAIzK,EAUJ,GAAIsK,GAAmBC,EAAgB,CACrC,MAAMK,EAAa,KAAK,IAAA,EAClBC,EAAQ,KAAK,cAAgB,OAC7B1wC,EAAU6vC,GAAuB,CACrC,SAAUO,EAAe,SACzB,SAAU,KAAK,KAAK,YAAcT,GAAqB,WACvD,WAAY,KAAK,KAAK,MAAQC,GAAqB,QACnD,KAAAzxC,EACA,OAAA8U,EACA,WAAAw9B,EACA,MAAOH,GAAa,KACpB,MAAAI,CAAA,CACD,EACKC,EAAY,MAAM/9B,GAAkBw9B,EAAe,WAAYpwC,CAAO,EAC5E6lC,EAAS,CACP,GAAIuK,EAAe,SACnB,UAAWA,EAAe,UAC1B,UAAAO,EACA,SAAUF,EACV,MAAAC,CAAA,CAEJ,CACA,MAAMvwC,EAAS,CACb,YAAa,EACb,YAAa,EACb,OAAQ,CACN,GAAI,KAAK,KAAK,YAAcwvC,GAAqB,WACjD,QAAS,KAAK,KAAK,eAAiB,MACpC,SAAU,KAAK,KAAK,UAAY,UAAU,UAAY,MACtD,KAAM,KAAK,KAAK,MAAQC,GAAqB,QAC7C,WAAY,KAAK,KAAK,UAAA,EAExB,KAAAzxC,EACA,OAAA8U,EACA,OAAA4yB,EACA,KAAM,CAAA,EACN,KAAA2K,EACA,UAAW,UAAU,UACrB,OAAQ,UAAU,QAAA,EAGf,KAAK,QAAwB,UAAWrwC,CAAM,EAChD,KAAMywC,GAAU,CACXA,GAAO,MAAM,aAAeR,GAC9B78B,GAAqB,CACnB,SAAU68B,EAAe,SACzB,KAAMQ,EAAM,KAAK,MAAQzyC,EACzB,MAAOyyC,EAAM,KAAK,YAClB,OAAQA,EAAM,KAAK,QAAU,CAAA,CAAC,CAC/B,EAEH,KAAK,UAAY,IACjB,KAAK,KAAK,UAAUA,CAAK,CAC3B,CAAC,EACA,MAAM,IAAM,CACPP,GAAuBD,GACzB38B,GAAqB,CAAE,SAAU28B,EAAe,SAAU,KAAAjyC,EAAM,EAElE,KAAK,IAAI,MAAM2xC,GAA2B,gBAAgB,CAC5D,CAAC,CACL,CAEQ,cAAc31C,EAAa,CACjC,IAAIC,EACJ,GAAI,CACFA,EAAS,KAAK,MAAMD,CAAG,CACzB,MAAQ,CACN,MACF,CAEA,MAAM02C,EAAQz2C,EACd,GAAIy2C,EAAM,OAAS,QAAS,CAC1B,MAAM1M,EAAM/pC,EACZ,GAAI+pC,EAAI,QAAU,oBAAqB,CACrC,MAAMnkC,EAAUmkC,EAAI,QACduM,EAAQ1wC,GAAW,OAAOA,EAAQ,OAAU,SAAWA,EAAQ,MAAQ,KACzE0wC,IACF,KAAK,aAAeA,EACf,KAAK,YAAA,GAEZ,MACF,CACA,MAAMI,EAAM,OAAO3M,EAAI,KAAQ,SAAWA,EAAI,IAAM,KAChD2M,IAAQ,OACN,KAAK,UAAY,MAAQA,EAAM,KAAK,QAAU,GAChD,KAAK,KAAK,QAAQ,CAAE,SAAU,KAAK,QAAU,EAAG,SAAUA,EAAK,EAEjE,KAAK,QAAUA,GAEjB,KAAK,KAAK,UAAU3M,CAAG,EACvB,MACF,CAEA,GAAI0M,EAAM,OAAS,MAAO,CACxB,MAAMrxC,EAAMpF,EACNqrC,EAAU,KAAK,QAAQ,IAAIjmC,EAAI,EAAE,EACvC,GAAI,CAACimC,EAAS,OACd,KAAK,QAAQ,OAAOjmC,EAAI,EAAE,EACtBA,EAAI,GAAIimC,EAAQ,QAAQjmC,EAAI,OAAO,EAClCimC,EAAQ,OAAO,IAAI,MAAMjmC,EAAI,OAAO,SAAW,gBAAgB,CAAC,EACrE,MACF,CACF,CAEA,QAAqBuxC,EAAgB5wC,EAA8B,CACjE,GAAI,CAAC,KAAK,IAAM,KAAK,GAAG,aAAe,UAAU,KAC/C,OAAO,QAAQ,OAAO,IAAI,MAAM,uBAAuB,CAAC,EAE1D,MAAMsB,EAAKrC,GAAA,EACLyxC,EAAQ,CAAE,KAAM,MAAO,GAAApvC,EAAI,OAAAsvC,EAAQ,OAAA5wC,CAAA,EACnChJ,EAAI,IAAI,QAAW,CAAC65C,EAASC,IAAW,CAC5C,KAAK,QAAQ,IAAIxvC,EAAI,CAAE,QAAU/J,GAAMs5C,EAAQt5C,CAAM,EAAG,OAAAu5C,CAAA,CAAQ,CAClE,CAAC,EACD,YAAK,GAAG,KAAK,KAAK,UAAUJ,CAAK,CAAC,EAC3B15C,CACT,CAEQ,cAAe,CACrB,KAAK,aAAe,KACpB,KAAK,YAAc,GACf,KAAK,eAAiB,MAAM,OAAO,aAAa,KAAK,YAAY,EACrE,KAAK,aAAe,OAAO,WAAW,IAAM,CACrC,KAAK,YAAA,CACZ,EAAG,GAAG,CACR,CACF,CC3QA,SAAS+5C,GAAS13C,EAAkD,CAClE,OAAO,OAAOA,GAAU,UAAYA,IAAU,IAChD,CAEO,SAAS23C,GAA2BnxC,EAA8C,CACvF,GAAI,CAACkxC,GAASlxC,CAAO,EAAG,OAAO,KAC/B,MAAMyB,EAAK,OAAOzB,EAAQ,IAAO,SAAWA,EAAQ,GAAG,OAAS,GAC1DysC,EAAUzsC,EAAQ,QACxB,GAAI,CAACyB,GAAM,CAACyvC,GAASzE,CAAO,EAAG,OAAO,KACtC,MAAM2E,EAAU,OAAO3E,EAAQ,SAAY,SAAWA,EAAQ,QAAQ,OAAS,GAC/E,GAAI,CAAC2E,EAAS,OAAO,KACrB,MAAMC,EAAc,OAAOrxC,EAAQ,aAAgB,SAAWA,EAAQ,YAAc,EAC9EsxC,EAAc,OAAOtxC,EAAQ,aAAgB,SAAWA,EAAQ,YAAc,EACpF,MAAI,CAACqxC,GAAe,CAACC,EAAoB,KAClC,CACL,GAAA7vC,EACA,QAAS,CACP,QAAA2vC,EACA,IAAK,OAAO3E,EAAQ,KAAQ,SAAWA,EAAQ,IAAM,KACrD,KAAM,OAAOA,EAAQ,MAAS,SAAWA,EAAQ,KAAO,KACxD,SAAU,OAAOA,EAAQ,UAAa,SAAWA,EAAQ,SAAW,KACpE,IAAK,OAAOA,EAAQ,KAAQ,SAAWA,EAAQ,IAAM,KACrD,QAAS,OAAOA,EAAQ,SAAY,SAAWA,EAAQ,QAAU,KACjE,aAAc,OAAOA,EAAQ,cAAiB,SAAWA,EAAQ,aAAe,KAChF,WAAY,OAAOA,EAAQ,YAAe,SAAWA,EAAQ,WAAa,IAAA,EAE5E,YAAA4E,EACA,YAAAC,CAAA,CAEJ,CAEO,SAASC,GAA0BvxC,EAA+C,CACvF,GAAI,CAACkxC,GAASlxC,CAAO,EAAG,OAAO,KAC/B,MAAMyB,EAAK,OAAOzB,EAAQ,IAAO,SAAWA,EAAQ,GAAG,OAAS,GAChE,OAAKyB,EACE,CACL,GAAAA,EACA,SAAU,OAAOzB,EAAQ,UAAa,SAAWA,EAAQ,SAAW,KACpE,WAAY,OAAOA,EAAQ,YAAe,SAAWA,EAAQ,WAAa,KAC1E,GAAI,OAAOA,EAAQ,IAAO,SAAWA,EAAQ,GAAK,IAAA,EALpC,IAOlB,CAEO,SAASwxC,GAAuBC,EAAqD,CAC1F,MAAMtyC,EAAM,KAAK,IAAA,EACjB,OAAOsyC,EAAM,OAAQ1wC,GAAUA,EAAM,YAAc5B,CAAG,CACxD,CAEO,SAASuyC,GACdD,EACA1wC,EACuB,CACvB,MAAMzG,EAAOk3C,GAAuBC,CAAK,EAAE,OAAQpzC,GAASA,EAAK,KAAO0C,EAAM,EAAE,EAChF,OAAAzG,EAAK,KAAKyG,CAAK,EACRzG,CACT,CAEO,SAASq3C,GAAmBF,EAA8BhwC,EAAmC,CAClG,OAAO+vC,GAAuBC,CAAK,EAAE,OAAQ1wC,GAAUA,EAAM,KAAOU,CAAE,CACxE,CCrEA,eAAsBmwC,GACpBryC,EACAmI,EACA,CACA,GAAI,CAACnI,EAAM,QAAU,CAACA,EAAM,UAAW,OACvC,MAAM/E,EAAyC+E,EAAM,WAAW,KAAA,EAC1DY,EAAS3F,EAAa,CAAE,WAAAA,CAAA,EAAe,CAAA,EAC7C,GAAI,CACF,MAAMgF,EAAO,MAAMD,EAAM,OAAO,QAAQ,qBAAsBY,CAAM,EAGpE,GAAI,CAACX,EAAK,OACV,MAAMnE,EAAa1B,GAA2B6F,CAAG,EACjDD,EAAM,cAAgBlE,EAAW,KACjCkE,EAAM,gBAAkBlE,EAAW,OACnCkE,EAAM,iBAAmBlE,EAAW,SAAW,IACjD,MAAQ,CAER,CACF,CC6BA,SAASw2C,GACPr4C,EACAU,EACQ,CACR,MAAMC,GAAOX,GAAS,IAAI,KAAA,EACpBs4C,EAAiB53C,EAAS,gBAAgB,KAAA,EAChD,GAAI,CAAC43C,EAAgB,OAAO33C,EAC5B,GAAI,CAACA,EAAK,OAAO23C,EACjB,MAAMC,EAAU73C,EAAS,SAAS,KAAA,GAAU,OACtC83C,EAAiB93C,EAAS,gBAAgB,KAAA,EAOhD,OALEC,IAAQ,QACRA,IAAQ43C,GACPC,IACE73C,IAAQ,SAAS63C,CAAc,SAC9B73C,IAAQ,SAAS63C,CAAc,IAAID,CAAO,IAC/BD,EAAiB33C,CACpC,CAEA,SAAS83C,GAAqB3wC,EAAmBpH,EAAoC,CACnF,GAAI,CAACA,GAAU,eAAgB,OAC/B,MAAMg4C,EAAqBL,GAA+BvwC,EAAK,WAAYpH,CAAQ,EAC7Ei4C,EAA6BN,GACjCvwC,EAAK,SAAS,WACdpH,CAAA,EAEIk4C,EAA+BP,GACnCvwC,EAAK,SAAS,qBACdpH,CAAA,EAEIm4C,EAAiBH,GAAsBC,GAA8B7wC,EAAK,WAC1EgxC,EAAe,CACnB,GAAGhxC,EAAK,SACR,WAAY6wC,GAA8BE,EAC1C,qBAAsBD,GAAgCC,CAAA,EAElDE,EACJD,EAAa,aAAehxC,EAAK,SAAS,YAC1CgxC,EAAa,uBAAyBhxC,EAAK,SAAS,qBAClD+wC,IAAmB/wC,EAAK,aAC1BA,EAAK,WAAa+wC,GAEhBE,GACFv7B,GAAc1V,EAAwDgxC,CAAY,CAEtF,CAEO,SAASE,GAAelxC,EAAmB,CAChDA,EAAK,UAAY,KACjBA,EAAK,MAAQ,KACbA,EAAK,UAAY,GACjBA,EAAK,kBAAoB,CAAA,EACzBA,EAAK,kBAAoB,KAEzBA,EAAK,QAAQ,KAAA,EACbA,EAAK,OAAS,IAAIyuC,GAAqB,CACrC,IAAKzuC,EAAK,SAAS,WACnB,MAAOA,EAAK,SAAS,MAAM,OAASA,EAAK,SAAS,MAAQ,OAC1D,SAAUA,EAAK,SAAS,KAAA,EAASA,EAAK,SAAW,OACjD,WAAY,sBACZ,KAAM,UACN,QAAUsvC,GAAU,CAClBtvC,EAAK,UAAY,GACjBA,EAAK,MAAQsvC,EACb6B,GAAcnxC,EAAMsvC,CAAK,EACpBgB,GAAsBtwC,CAA8B,EACpDmuC,GAAWnuC,CAA8B,EACzC0S,GAAU1S,EAAgC,CAAE,MAAO,GAAM,EACzDoS,GAAYpS,EAAgC,CAAE,MAAO,GAAM,EAC3DwW,GAAiBxW,CAAyD,CACjF,EACA,QAAS,CAAC,CAAE,KAAAoxC,EAAM,OAAAzC,KAAa,CAC7B3uC,EAAK,UAAY,GACjBA,EAAK,UAAY,iBAAiBoxC,CAAI,MAAMzC,GAAU,WAAW,EACnE,EACA,QAAU9L,GAAQwO,GAAmBrxC,EAAM6iC,CAAG,EAC9C,MAAO,CAAC,CAAE,SAAAyO,EAAU,SAAAC,KAAe,CACjCvxC,EAAK,UAAY,oCAAoCsxC,CAAQ,SAASC,CAAQ,wBAChF,CAAA,CACD,EACDvxC,EAAK,OAAO,MAAA,CACd,CAEO,SAASqxC,GAAmBrxC,EAAmB6iC,EAAwB,CAS5E,GARA7iC,EAAK,eAAiB,CACpB,CAAE,GAAI,KAAK,MAAO,MAAO6iC,EAAI,MAAO,QAASA,EAAI,OAAA,EACjD,GAAG7iC,EAAK,cAAA,EACR,MAAM,EAAG,GAAG,EACVA,EAAK,MAAQ,UACfA,EAAK,SAAWA,EAAK,gBAGnB6iC,EAAI,QAAU,QAAS,CACzB,GAAI7iC,EAAK,WAAY,OACrBS,GACET,EACA6iC,EAAI,OAAA,EAEN,MACF,CAEA,GAAIA,EAAI,QAAU,OAAQ,CACxB,MAAMnkC,EAAUmkC,EAAI,QAChBnkC,GAAS,YACXkX,GACE5V,EACAtB,EAAQ,UAAA,EAGZ,MAAMT,EAAQQ,GAAgBuB,EAAgCtB,CAAO,GACjET,IAAU,SAAWA,IAAU,SAAWA,IAAU,aACtDuC,GAAgBR,CAAwD,EACnEwY,GACHxY,CAAA,GAGA/B,IAAU,SAAcD,GAAgBgC,CAA8B,EAC1E,MACF,CAEA,GAAI6iC,EAAI,QAAU,WAAY,CAC5B,MAAMnkC,EAAUmkC,EAAI,QAChBnkC,GAAS,UAAY,MAAM,QAAQA,EAAQ,QAAQ,IACrDsB,EAAK,gBAAkBtB,EAAQ,SAC/BsB,EAAK,cAAgB,KACrBA,EAAK,eAAiB,MAExB,MACF,CAUA,GARI6iC,EAAI,QAAU,QAAU7iC,EAAK,MAAQ,QAClC6W,GAAS7W,CAAiD,GAG7D6iC,EAAI,QAAU,yBAA2BA,EAAI,QAAU,yBACpDzwB,GAAYpS,EAAgC,CAAE,MAAO,GAAM,EAG9D6iC,EAAI,QAAU,0BAA2B,CAC3C,MAAMpjC,EAAQowC,GAA2BhN,EAAI,OAAO,EACpD,GAAIpjC,EAAO,CACTO,EAAK,kBAAoBowC,GAAgBpwC,EAAK,kBAAmBP,CAAK,EACtEO,EAAK,kBAAoB,KACzB,MAAM4uC,EAAQ,KAAK,IAAI,EAAGnvC,EAAM,YAAc,KAAK,IAAA,EAAQ,GAAG,EAC9D,OAAO,WAAW,IAAM,CACtBO,EAAK,kBAAoBqwC,GAAmBrwC,EAAK,kBAAmBP,EAAM,EAAE,CAC9E,EAAGmvC,CAAK,CACV,CACA,MACF,CAEA,GAAI/L,EAAI,QAAU,yBAA0B,CAC1C,MAAM3rB,EAAW+4B,GAA0BpN,EAAI,OAAO,EAClD3rB,IACFlX,EAAK,kBAAoBqwC,GAAmBrwC,EAAK,kBAAmBkX,EAAS,EAAE,EAEnF,CACF,CAEO,SAASi6B,GAAcnxC,EAAmBsvC,EAAuB,CACtE,MAAMpsC,EAAWosC,EAAM,SAOnBpsC,GAAU,UAAY,MAAM,QAAQA,EAAS,QAAQ,IACvDlD,EAAK,gBAAkBkD,EAAS,UAE9BA,GAAU,SACZlD,EAAK,YAAckD,EAAS,QAE1BA,GAAU,iBACZytC,GAAqB3wC,EAAMkD,EAAS,eAAe,CAEvD,CC5MO,SAASsuC,GAAgBxxC,EAAqB,CACnDA,EAAK,SAAW+W,GAAA,EAChBM,GACErX,EACA,EAAA,EAEFiX,GACEjX,CAAA,EAEFmX,GACEnX,CAAA,EAEF,OAAO,iBAAiB,WAAYA,EAAK,eAAe,EACxD6V,GACE7V,CAAA,EAEFkxC,GAAelxC,CAAuD,EACtEoV,GAAkBpV,CAA0D,EACxEA,EAAK,MAAQ,QACfsV,GAAiBtV,CAAyD,EAExEA,EAAK,MAAQ,SACfwV,GAAkBxV,CAA0D,CAEhF,CAEO,SAASyxC,GAAmBzxC,EAAqB,CACtDkC,GAAclC,CAAsD,CACtE,CAEO,SAAS0xC,GAAmB1xC,EAAqB,CACtD,OAAO,oBAAoB,WAAYA,EAAK,eAAe,EAC3DqV,GAAiBrV,CAAyD,EAC1EuV,GAAgBvV,CAAwD,EACxEyV,GAAiBzV,CAAyD,EAC1EoX,GACEpX,CAAA,EAEFA,EAAK,gBAAgB,WAAA,EACrBA,EAAK,eAAiB,IACxB,CAEO,SAAS2xC,GACd3xC,EACA4xC,EACA,CACA,GACE5xC,EAAK,MAAQ,SACZ4xC,EAAQ,IAAI,cAAc,GACzBA,EAAQ,IAAI,kBAAkB,GAC9BA,EAAQ,IAAI,YAAY,GACxBA,EAAQ,IAAI,aAAa,GACzBA,EAAQ,IAAI,KAAK,GACnB,CACA,MAAMC,EAAcD,EAAQ,IAAI,KAAK,EAC/BE,EACJF,EAAQ,IAAI,aAAa,GACzBA,EAAQ,IAAI,aAAa,IAAM,IAC/B5xC,EAAK,cAAgB,GACvBe,GACEf,EACA6xC,GAAeC,GAAgB,CAAC9xC,EAAK,mBAAA,CAEzC,CAEEA,EAAK,MAAQ,SACZ4xC,EAAQ,IAAI,aAAa,GAAKA,EAAQ,IAAI,gBAAgB,GAAKA,EAAQ,IAAI,KAAK,IAE7E5xC,EAAK,gBAAkBA,EAAK,cAC9BwB,GACExB,EACA4xC,EAAQ,IAAI,KAAK,GAAKA,EAAQ,IAAI,gBAAgB,CAAA,CAI1D,CCnGA,eAAsBG,GAAoB/xC,EAAmBO,EAAgB,CAC3E,MAAMsE,GAAmB7E,EAAMO,CAAK,EACpC,MAAMoE,GAAa3E,EAAM,EAAI,CAC/B,CAEA,eAAsBgyC,GAAmBhyC,EAAmB,CAC1D,MAAM8E,GAAkB9E,CAAI,EAC5B,MAAM2E,GAAa3E,EAAM,EAAI,CAC/B,CAEA,eAAsBiyC,GAAqBjyC,EAAmB,CAC5D,MAAM+E,GAAe/E,CAAI,EACzB,MAAM2E,GAAa3E,EAAM,EAAI,CAC/B,CAEA,eAAsBkyC,GAAwBlyC,EAAmB,CAC/D,MAAMoD,GAAWpD,CAAI,EACrB,MAAM8C,GAAW9C,CAAI,EACrB,MAAM2E,GAAa3E,EAAM,EAAI,CAC/B,CAEA,eAAsBmyC,GAA0BnyC,EAAmB,CACjE,MAAM8C,GAAW9C,CAAI,EACrB,MAAM2E,GAAa3E,EAAM,EAAI,CAC/B,CAEA,SAASoyC,GAAsBC,EAA0C,CACvE,GAAI,CAAC,MAAM,QAAQA,CAAO,QAAU,CAAA,EACpC,MAAMC,EAAiC,CAAA,EACvC,UAAW7yC,KAAS4yC,EAAS,CAC3B,GAAI,OAAO5yC,GAAU,SAAU,SAC/B,KAAM,CAAC8yC,EAAU,GAAGl5C,CAAI,EAAIoG,EAAM,MAAM,GAAG,EAC3C,GAAI,CAAC8yC,GAAYl5C,EAAK,SAAW,EAAG,SACpC,MAAMwkC,EAAQ0U,EAAS,KAAA,EACjB31C,EAAUvD,EAAK,KAAK,GAAG,EAAE,KAAA,EAC3BwkC,GAASjhC,IAAS01C,EAAOzU,CAAK,EAAIjhC,EACxC,CACA,OAAO01C,CACT,CAEA,SAASE,GAAsBxyC,EAA2B,CAExD,OADiBA,EAAK,kBAAkB,iBAAiB,OAAS,CAAA,GAClD,CAAC,GAAG,WAAaA,EAAK,uBAAyB,SACjE,CAEA,SAASyyC,GAAqB/U,EAAmBrf,EAAS,GAAY,CACpE,MAAO,uBAAuB,mBAAmBqf,CAAS,CAAC,WAAWrf,CAAM,EAC9E,CAEO,SAASq0B,GACd1yC,EACA09B,EACAS,EACA,CACAn+B,EAAK,sBAAwB09B,EAC7B19B,EAAK,sBAAwBk+B,GAA4BC,GAAW,MAAS,CAC/E,CAEO,SAASwU,GAAyB3yC,EAAmB,CAC1DA,EAAK,sBAAwB,KAC7BA,EAAK,sBAAwB,IAC/B,CAEO,SAAS4yC,GACd5yC,EACA69B,EACA3lC,EACA,CACA,MAAM+F,EAAQ+B,EAAK,sBACd/B,IACL+B,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,CACN,GAAGA,EAAM,OACT,CAAC4/B,CAAK,EAAG3lC,CAAA,EAEX,YAAa,CACX,GAAG+F,EAAM,YACT,CAAC4/B,CAAK,EAAG,EAAA,CACX,EAEJ,CAEO,SAASgV,GAAiC7yC,EAAmB,CAClE,MAAM/B,EAAQ+B,EAAK,sBACd/B,IACL+B,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,aAAc,CAACA,EAAM,YAAA,EAEzB,CAEA,eAAsB60C,GAAuB9yC,EAAmB,CAC9D,MAAM/B,EAAQ+B,EAAK,sBACnB,GAAI,CAAC/B,GAASA,EAAM,OAAQ,OAC5B,MAAMy/B,EAAY8U,GAAsBxyC,CAAI,EAE5CA,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,GACR,MAAO,KACP,QAAS,KACT,YAAa,CAAA,CAAC,EAGhB,GAAI,CACF,MAAM80C,EAAW,MAAM,MAAMN,GAAqB/U,CAAS,EAAG,CAC5D,OAAQ,MACR,QAAS,CACP,eAAgB,kBAAA,EAElB,KAAM,KAAK,UAAUz/B,EAAM,MAAM,CAAA,CAClC,EACKyC,EAAQ,MAAMqyC,EAAS,OAAO,MAAM,IAAM,IAAI,EAIpD,GAAI,CAACA,EAAS,IAAMryC,GAAM,KAAO,IAAS,CAACA,EAAM,CAC/C,MAAMsyC,EAAetyC,GAAM,OAAS,0BAA0BqyC,EAAS,MAAM,IAC7E/yC,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,GACR,MAAO+0C,EACP,QAAS,KACT,YAAaZ,GAAsB1xC,GAAM,OAAO,CAAA,EAElD,MACF,CAEA,GAAI,CAACA,EAAK,UAAW,CACnBV,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,GACR,MAAO,wCACP,QAAS,IAAA,EAEX,MACF,CAEA+B,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,GACR,MAAO,KACP,QAAS,+BACT,YAAa,CAAA,EACb,SAAU,CAAE,GAAGA,EAAM,MAAA,CAAO,EAE9B,MAAM0G,GAAa3E,EAAM,EAAI,CAC/B,OAAS7B,EAAK,CACZ6B,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,OAAQ,GACR,MAAO,0BAA0B,OAAOE,CAAG,CAAC,GAC5C,QAAS,IAAA,CAEb,CACF,CAEA,eAAsB80C,GAAyBjzC,EAAmB,CAChE,MAAM/B,EAAQ+B,EAAK,sBACnB,GAAI,CAAC/B,GAASA,EAAM,UAAW,OAC/B,MAAMy/B,EAAY8U,GAAsBxyC,CAAI,EAE5CA,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,UAAW,GACX,MAAO,KACP,QAAS,IAAA,EAGX,GAAI,CACF,MAAM80C,EAAW,MAAM,MAAMN,GAAqB/U,EAAW,SAAS,EAAG,CACvE,OAAQ,OACR,QAAS,CACP,eAAgB,kBAAA,EAElB,KAAM,KAAK,UAAU,CAAE,UAAW,GAAM,CAAA,CACzC,EACKh9B,EAAQ,MAAMqyC,EAAS,OAAO,MAAM,IAAM,IAAI,EAIpD,GAAI,CAACA,EAAS,IAAMryC,GAAM,KAAO,IAAS,CAACA,EAAM,CAC/C,MAAMsyC,EAAetyC,GAAM,OAAS,0BAA0BqyC,EAAS,MAAM,IAC7E/yC,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,UAAW,GACX,MAAO+0C,EACP,QAAS,IAAA,EAEX,MACF,CAEA,MAAM/M,EAASvlC,EAAK,QAAUA,EAAK,UAAY,KACzCwyC,EAAajN,EAAS,CAAE,GAAGhoC,EAAM,OAAQ,GAAGgoC,GAAWhoC,EAAM,OAC7Dk1C,EAAe,GACnBD,EAAW,QAAUA,EAAW,SAAWA,EAAW,OAASA,EAAW,OAG5ElzC,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,UAAW,GACX,OAAQi1C,EACR,MAAO,KACP,QAASxyC,EAAK,MACV,oDACA,wCACJ,aAAAyyC,CAAA,EAGEzyC,EAAK,OACP,MAAMiE,GAAa3E,EAAM,EAAI,CAEjC,OAAS7B,EAAK,CACZ6B,EAAK,sBAAwB,CAC3B,GAAG/B,EACH,UAAW,GACX,MAAO,0BAA0B,OAAOE,CAAG,CAAC,GAC5C,QAAS,IAAA,CAEb,CACF,qMCjJA,MAAMi1C,GAA4B36C,GAAA,EAElC,SAAS46C,IAAiC,CACxC,GAAI,CAAC,OAAO,SAAS,OAAQ,MAAO,GAEpC,MAAMx6C,EADS,IAAI,gBAAgB,OAAO,SAAS,MAAM,EACtC,IAAI,YAAY,EACnC,GAAI,CAACA,EAAK,MAAO,GACjB,MAAMkB,EAAalB,EAAI,KAAA,EAAO,YAAA,EAC9B,OAAOkB,IAAe,KAAOA,IAAe,QAAUA,IAAe,OAASA,IAAe,IAC/F,CAGO,IAAMu5C,EAAN,cAA0B/hB,EAAW,CAArC,aAAA,CAAA,MAAA,GAAA,SAAA,EACI,KAAA,SAAuB54B,GAAA,EACvB,KAAA,SAAW,GACX,KAAA,IAAW,OACX,KAAA,WAAa06C,GAAA,EACb,KAAA,UAAY,GACZ,KAAA,MAAmB,KAAK,SAAS,OAAS,SAC1C,KAAA,cAA+B,OAC/B,KAAA,MAA+B,KAC/B,KAAA,UAA2B,KAC3B,KAAA,SAA4B,CAAA,EACrC,KAAQ,eAAkC,CAAA,EAC1C,KAAQ,oBAAqC,KAC7C,KAAQ,kBAAmC,KAElC,KAAA,cAAgBD,GAA0B,KAC1C,KAAA,gBAAkBA,GAA0B,OAC5C,KAAA,iBAAmBA,GAA0B,SAAW,KAExD,KAAA,WAAa,KAAK,SAAS,WAC3B,KAAA,YAAc,GACd,KAAA,YAAc,GACd,KAAA,YAAc,GACd,KAAA,aAA0B,CAAA,EAC1B,KAAA,iBAA8B,CAAA,EAC9B,KAAA,WAA4B,KAC5B,KAAA,oBAAqC,KACrC,KAAA,UAA2B,KAC3B,KAAA,cAA+B,KAC/B,KAAA,kBAAmC,KACnC,KAAA,UAA6B,CAAA,EAE7B,KAAA,YAAc,GACd,KAAA,eAAgC,KAChC,KAAA,aAA8B,KAC9B,KAAA,WAAa,KAAK,SAAS,WAE3B,KAAA,aAAe,GACf,KAAA,MAAwC,CAAA,EACxC,KAAA,eAAiB,GACjB,KAAA,aAA8B,KAC9B,KAAA,YAAwC,KACxC,KAAA,qBAAuB,GACvB,KAAA,oBAAsB,GACtB,KAAA,mBAAqB,GACrB,KAAA,sBAAsD,KACtD,KAAA,kBAA8C,KAC9C,KAAA,2BAA4C,KAC5C,KAAA,oBAA0C,UAC1C,KAAA,0BAA2C,KAC3C,KAAA,kBAA2C,CAAA,EAC3C,KAAA,iBAAmB,GACnB,KAAA,kBAAmC,KAEnC,KAAA,cAAgB,GAChB,KAAA,UAAY;AAAA;AAAA,EACZ,KAAA,YAA8B,KAC9B,KAAA,aAA0B,CAAA,EAC1B,KAAA,aAAe,GACf,KAAA,eAAiB,GACjB,KAAA,cAAgB,GAChB,KAAA,gBAAkB,KAAK,SAAS,qBAChC,KAAA,eAAwC,KACxC,KAAA,aAA+B,KAC/B,KAAA,oBAAqC,KACrC,KAAA,oBAAsB,GACtB,KAAA,cAA+B,CAAA,EAC/B,KAAA,WAA6C,KAC7C,KAAA,mBAAqD,KACrD,KAAA,gBAAkB,GAClB,KAAA,eAAiC,OACjC,KAAA,kBAAoB,GACpB,KAAA,oBAAqC,KACrC,KAAA,uBAAwC,KAExC,KAAA,gBAAkB,GAClB,KAAA,iBAAkD,KAClD,KAAA,cAA+B,KAC/B,KAAA,oBAAqC,KACrC,KAAA,qBAAsC,KACtC,KAAA,uBAAwC,KACxC,KAAA,uBAAyC,KACzC,KAAA,aAAe,GACf,KAAA,sBAAsD,KACtD,KAAA,sBAAuC,KAEvC,KAAA,gBAAkB,GAClB,KAAA,gBAAmC,CAAA,EACnC,KAAA,cAA+B,KAC/B,KAAA,eAAgC,KAEhC,KAAA,cAAgB,GAChB,KAAA,WAAsC,KACtC,KAAA,YAA6B,KAE7B,KAAA,gBAAkB,GAClB,KAAA,eAA4C,KAC5C,KAAA,cAA+B,KAC/B,KAAA,qBAAuB,GACvB,KAAA,oBAAsB,MACtB,KAAA,sBAAwB,GACxB,KAAA,uBAAyB,GAEzB,KAAA,YAAc,GACd,KAAA,SAAsB,CAAA,EACtB,KAAA,WAAgC,KAChC,KAAA,UAA2B,KAC3B,KAAA,SAA0B,CAAE,GAAGlF,EAAA,EAC/B,KAAA,cAA+B,KAC/B,KAAA,SAA8B,CAAA,EAC9B,KAAA,SAAW,GAEX,KAAA,cAAgB,GAChB,KAAA,aAAyC,KACzC,KAAA,YAA6B,KAC7B,KAAA,aAAe,GACf,KAAA,WAAqC,CAAA,EACrC,KAAA,cAA+B,KAC/B,KAAA,cAA8C,CAAA,EAE9C,KAAA,aAAe,GACf,KAAA,YAAoC,KACpC,KAAA,YAAqC,KACrC,KAAA,YAAyB,CAAA,EACzB,KAAA,eAAiC,KACjC,KAAA,gBAAkB,GAClB,KAAA,gBAAkB,KAClB,KAAA,gBAAiC,KACjC,KAAA,eAAgC,KAEhC,KAAA,YAAc,GACd,KAAA,UAA2B,KAC3B,KAAA,SAA0B,KAC1B,KAAA,YAA0B,CAAA,EAC1B,KAAA,eAAiB,GACjB,KAAA,iBAA8C,CACrD,GAAGD,EAAA,EAEI,KAAA,eAAiB,GACjB,KAAA,cAAgB,GAChB,KAAA,WAA4B,KAC5B,KAAA,gBAAiC,KACjC,KAAA,UAAY,IACZ,KAAA,aAAe,KACf,KAAA,aAAe,GAExB,KAAA,OAAsC,KACtC,KAAQ,gBAAiC,KACzC,KAAQ,kBAAmC,KAC3C,KAAQ,oBAAsB,GAC9B,KAAQ,mBAAqB,GAC7B,KAAQ,kBAAmC,KAC3C,KAAQ,iBAAkC,KAC1C,KAAQ,kBAAmC,KAC3C,KAAQ,gBAAiC,KACzC,KAAQ,mBAAqB,IAC7B,KAAQ,gBAA4B,CAAA,EACpC,KAAA,SAAW,GACX,KAAQ,gBAAkB,IACxBsF,GACE,IAAA,EAEJ,KAAQ,WAAoC,KAC5C,KAAQ,kBAAmE,KAC3E,KAAQ,eAAwC,IAAA,CAEhD,kBAAmB,CACjB,OAAO,IACT,CAEA,mBAAoB,CAClB,MAAM,kBAAA,EACN/B,GAAgB,IAAwD,CAC1E,CAEU,cAAe,CACvBC,GAAmB,IAA2D,CAChF,CAEA,sBAAuB,CACrBC,GAAmB,IAA2D,EAC9E,MAAM,qBAAA,CACR,CAEU,QAAQE,EAAoC,CACpDD,GACE,KACAC,CAAA,CAEJ,CAEA,SAAU,CACR4B,GACE,IAAA,CAEJ,CAEA,iBAAiB9xC,EAAc,CAC7B+xC,GACE,KACA/xC,CAAA,CAEJ,CAEA,iBAAiBA,EAAc,CAC7BgyC,GACE,KACAhyC,CAAA,CAEJ,CAEA,WAAWnE,EAAiBf,EAAe,CACzCm3C,GAAmBp2C,EAAOf,CAAK,CACjC,CAEA,iBAAkB,CAChBo3C,GACE,IAAA,CAEJ,CAEA,iBAAkB,CAChBC,GACE,IAAA,CAEJ,CAEA,MAAM,uBAAwB,CAC5B,MAAMC,GAA8B,IAAI,CAC1C,CAEA,cAAc96C,EAAkB,CAC9B+6C,GACE,KACA/6C,CAAA,CAEJ,CAEA,OAAOA,EAAW,CAChBg7C,GAAe,KAAyDh7C,CAAI,CAC9E,CAEA,SAASA,EAAiB2b,EAAkD,CAC1Es/B,GACE,KACAj7C,EACA2b,CAAA,CAEJ,CAEA,MAAM,cAAe,CACnB,MAAMu/B,GACJ,IAAA,CAEJ,CAEA,MAAM,UAAW,CACf,MAAMC,GACJ,IAAA,CAEJ,CAEA,MAAM,iBAAkB,CACtB,MAAMC,GACJ,IAAA,CAEJ,CAEA,oBAAoBj0C,EAAY,CAC9Bk0C,GACE,KACAl0C,CAAA,CAEJ,CAEA,MAAM,eACJkY,EACAjS,EACA,CACA,MAAMkuC,GACJ,KACAj8B,EACAjS,CAAA,CAEJ,CAEA,MAAM,oBAAoB7F,EAAgB,CACxC,MAAMg0C,GAA4B,KAAMh0C,CAAK,CAC/C,CAEA,MAAM,oBAAqB,CACzB,MAAMi0C,GAA2B,IAAI,CACvC,CAEA,MAAM,sBAAuB,CAC3B,MAAMC,GAA6B,IAAI,CACzC,CAEA,MAAM,yBAA0B,CAC9B,MAAMC,GAAgC,IAAI,CAC5C,CAEA,MAAM,2BAA4B,CAChC,MAAMC,GAAkC,IAAI,CAC9C,CAEA,uBAAuBjX,EAAmBS,EAA8B,CACtEyW,GAA+B,KAAMlX,EAAWS,CAAO,CACzD,CAEA,0BAA2B,CACzB0W,GAAiC,IAAI,CACvC,CAEA,8BAA8BhX,EAA2B3lC,EAAe,CACtE48C,GAAsC,KAAMjX,EAAO3lC,CAAK,CAC1D,CAEA,MAAM,wBAAyB,CAC7B,MAAM68C,GAA+B,IAAI,CAC3C,CAEA,MAAM,0BAA2B,CAC/B,MAAMC,GAAiC,IAAI,CAC7C,CAEA,kCAAmC,CACjCC,GAAyC,IAAI,CAC/C,CAEA,MAAM,2BAA2BC,EAAkD,CACjF,MAAMhK,EAAS,KAAK,kBAAkB,CAAC,EACvC,GAAI,GAACA,GAAU,CAAC,KAAK,QAAU,KAAK,kBACpC,MAAK,iBAAmB,GACxB,KAAK,kBAAoB,KACzB,GAAI,CACF,MAAM,KAAK,OAAO,QAAQ,wBAAyB,CACjD,GAAIA,EAAO,GACX,SAAAgK,CAAA,CACD,EACD,KAAK,kBAAoB,KAAK,kBAAkB,OAAQz1C,GAAUA,EAAM,KAAOyrC,EAAO,EAAE,CAC1F,OAAS/sC,EAAK,CACZ,KAAK,kBAAoB,yBAAyB,OAAOA,CAAG,CAAC,EAC/D,QAAA,CACE,KAAK,iBAAmB,EAC1B,EACF,CAGA,kBAAkBrB,EAAiB,CAC7B,KAAK,mBAAqB,OAC5B,OAAO,aAAa,KAAK,iBAAiB,EAC1C,KAAK,kBAAoB,MAE3B,KAAK,eAAiBA,EACtB,KAAK,aAAe,KACpB,KAAK,YAAc,EACrB,CAEA,oBAAqB,CACnB,KAAK,YAAc,GAEf,KAAK,mBAAqB,MAC5B,OAAO,aAAa,KAAK,iBAAiB,EAE5C,KAAK,kBAAoB,OAAO,WAAW,IAAM,CAC3C,KAAK,cACT,KAAK,eAAiB,KACtB,KAAK,aAAe,KACpB,KAAK,kBAAoB,KAC3B,EAAG,GAAG,CACR,CAEA,uBAAuBkxC,EAAe,CACpC,MAAMtc,EAAW,KAAK,IAAI,GAAK,KAAK,IAAI,GAAKsc,CAAK,CAAC,EACnD,KAAK,WAAatc,EAClB,KAAK,cAAc,CAAE,GAAG,KAAK,SAAU,WAAYA,EAAU,CAC/D,CAEA,QAAS,CACP,OAAO0b,GAAU,IAAI,CACvB,CACF,EA7XWxb,EAAA,CAAR3zB,EAAA,CAAM,EADIq1C,EACF,UAAA,WAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAFIq1C,EAEF,UAAA,WAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAHIq1C,EAGF,UAAA,MAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAJIq1C,EAIF,UAAA,aAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EALIq1C,EAKF,UAAA,YAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EANIq1C,EAMF,UAAA,QAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAPIq1C,EAOF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EARIq1C,EAQF,UAAA,QAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EATIq1C,EASF,UAAA,YAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAVIq1C,EAUF,UAAA,WAAA,CAAA,EAKA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAfIq1C,EAeF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAhBIq1C,EAgBF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAjBIq1C,EAiBF,UAAA,mBAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAnBIq1C,EAmBF,UAAA,aAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EApBIq1C,EAoBF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EArBIq1C,EAqBF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAtBIq1C,EAsBF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAvBIq1C,EAuBF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAxBIq1C,EAwBF,UAAA,mBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAzBIq1C,EAyBF,UAAA,aAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA1BIq1C,EA0BF,UAAA,sBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA3BIq1C,EA2BF,UAAA,YAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA5BIq1C,EA4BF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA7BIq1C,EA6BF,UAAA,oBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA9BIq1C,EA8BF,UAAA,YAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAhCIq1C,EAgCF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAjCIq1C,EAiCF,UAAA,iBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAlCIq1C,EAkCF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAnCIq1C,EAmCF,UAAA,aAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EArCIq1C,EAqCF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAtCIq1C,EAsCF,UAAA,QAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAvCIq1C,EAuCF,UAAA,iBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAxCIq1C,EAwCF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAzCIq1C,EAyCF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA1CIq1C,EA0CF,UAAA,uBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA3CIq1C,EA2CF,UAAA,sBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA5CIq1C,EA4CF,UAAA,qBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA7CIq1C,EA6CF,UAAA,wBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA9CIq1C,EA8CF,UAAA,oBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA/CIq1C,EA+CF,UAAA,6BAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAhDIq1C,EAgDF,UAAA,sBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAjDIq1C,EAiDF,UAAA,4BAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAlDIq1C,EAkDF,UAAA,oBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAnDIq1C,EAmDF,UAAA,mBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EApDIq1C,EAoDF,UAAA,oBAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAtDIq1C,EAsDF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAvDIq1C,EAuDF,UAAA,YAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAxDIq1C,EAwDF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAzDIq1C,EAyDF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA1DIq1C,EA0DF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA3DIq1C,EA2DF,UAAA,iBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA5DIq1C,EA4DF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA7DIq1C,EA6DF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA9DIq1C,EA8DF,UAAA,iBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA/DIq1C,EA+DF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAhEIq1C,EAgEF,UAAA,sBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAjEIq1C,EAiEF,UAAA,sBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAlEIq1C,EAkEF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAnEIq1C,EAmEF,UAAA,aAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EApEIq1C,EAoEF,UAAA,qBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EArEIq1C,EAqEF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAtEIq1C,EAsEF,UAAA,iBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAvEIq1C,EAuEF,UAAA,oBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAxEIq1C,EAwEF,UAAA,sBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAzEIq1C,EAyEF,UAAA,yBAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA3EIq1C,EA2EF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA5EIq1C,EA4EF,UAAA,mBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA7EIq1C,EA6EF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA9EIq1C,EA8EF,UAAA,sBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA/EIq1C,EA+EF,UAAA,uBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAhFIq1C,EAgFF,UAAA,yBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAjFIq1C,EAiFF,UAAA,yBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAlFIq1C,EAkFF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAnFIq1C,EAmFF,UAAA,wBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EApFIq1C,EAoFF,UAAA,wBAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAtFIq1C,EAsFF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAvFIq1C,EAuFF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAxFIq1C,EAwFF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAzFIq1C,EAyFF,UAAA,iBAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA3FIq1C,EA2FF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA5FIq1C,EA4FF,UAAA,aAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA7FIq1C,EA6FF,UAAA,cAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA/FIq1C,EA+FF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAhGIq1C,EAgGF,UAAA,iBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAjGIq1C,EAiGF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAlGIq1C,EAkGF,UAAA,uBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAnGIq1C,EAmGF,UAAA,sBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EApGIq1C,EAoGF,UAAA,wBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EArGIq1C,EAqGF,UAAA,yBAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAvGIq1C,EAuGF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAxGIq1C,EAwGF,UAAA,WAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAzGIq1C,EAyGF,UAAA,aAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA1GIq1C,EA0GF,UAAA,YAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA3GIq1C,EA2GF,UAAA,WAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA5GIq1C,EA4GF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA7GIq1C,EA6GF,UAAA,WAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA9GIq1C,EA8GF,UAAA,WAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAhHIq1C,EAgHF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAjHIq1C,EAiHF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAlHIq1C,EAkHF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAnHIq1C,EAmHF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EApHIq1C,EAoHF,UAAA,aAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EArHIq1C,EAqHF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAtHIq1C,EAsHF,UAAA,gBAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAxHIq1C,EAwHF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAzHIq1C,EAyHF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA1HIq1C,EA0HF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA3HIq1C,EA2HF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA5HIq1C,EA4HF,UAAA,iBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA7HIq1C,EA6HF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA9HIq1C,EA8HF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA/HIq1C,EA+HF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAhIIq1C,EAgIF,UAAA,iBAAA,CAAA,EAEA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAlIIq1C,EAkIF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAnIIq1C,EAmIF,UAAA,YAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EApIIq1C,EAoIF,UAAA,WAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EArIIq1C,EAqIF,UAAA,cAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAtIIq1C,EAsIF,UAAA,iBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAvIIq1C,EAuIF,UAAA,mBAAA,CAAA,EAGA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA1IIq1C,EA0IF,UAAA,iBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA3IIq1C,EA2IF,UAAA,gBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA5IIq1C,EA4IF,UAAA,aAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA7IIq1C,EA6IF,UAAA,kBAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA9IIq1C,EA8IF,UAAA,YAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EA/IIq1C,EA+IF,UAAA,eAAA,CAAA,EACA1hB,EAAA,CAAR3zB,EAAA,CAAM,EAhJIq1C,EAgJF,UAAA,eAAA,CAAA,EAhJEA,EAAN1hB,EAAA,CADNC,GAAc,cAAc,CAAA,EAChByhB,CAAA","x_google_ignoreList":[0,1,2,3,4,5,6,24,37,38,39,41,42,43]} \ No newline at end of file diff --git a/dist/control-ui/index.html b/dist/control-ui/index.html index af79791bc90..9407eea995c 100644 --- a/dist/control-ui/index.html +++ b/dist/control-ui/index.html @@ -6,8 +6,8 @@ Clawdbot Control - - + + diff --git a/docs/channels/bluebubbles.md b/docs/channels/bluebubbles.md index eed40b68187..a1f4a089286 100644 --- a/docs/channels/bluebubbles.md +++ b/docs/channels/bluebubbles.md @@ -196,7 +196,7 @@ Provider options: - `channels.bluebubbles.sendReadReceipts`: Send read receipts (default: `true`). - `channels.bluebubbles.blockStreaming`: Enable block streaming (default: `true`). - `channels.bluebubbles.textChunkLimit`: Outbound chunk size in chars (default: 4000). -- `channels.bluebubbles.chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on every newline and sends each line immediately during streaming. +- `channels.bluebubbles.chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on blank lines (paragraph boundaries) before length chunking. - `channels.bluebubbles.mediaMaxMb`: Inbound media cap in MB (default: 8). - `channels.bluebubbles.historyLimit`: Max group messages for context (0 disables). - `channels.bluebubbles.dmHistoryLimit`: DM history limit. @@ -213,6 +213,7 @@ Prefer `chat_guid` for stable routing: - `chat_id:123` - `chat_identifier:...` - Direct handles: `+15555550123`, `user@example.com` + - If a direct handle does not have an existing DM chat, Clawdbot will create one via `POST /api/v1/chat/new`. This requires the BlueBubbles Private API to be enabled. ## Security - Webhook requests are authenticated by comparing `guid`/`password` query params or headers against `channels.bluebubbles.password`. Requests from `localhost` are also accepted. diff --git a/docs/channels/discord.md b/docs/channels/discord.md index f63fd45c9d4..12dd2808420 100644 --- a/docs/channels/discord.md +++ b/docs/channels/discord.md @@ -205,7 +205,7 @@ Notes: ## Capabilities & limits - DMs and guild text channels (threads are treated as separate channels; voice not supported). - Typing indicators sent best-effort; message chunking uses `channels.discord.textChunkLimit` (default 2000) and splits tall replies by line count (`channels.discord.maxLinesPerMessage`, default 17). -- Optional newline chunking: set `channels.discord.chunkMode="newline"` to split on each line before length chunking. +- Optional newline chunking: set `channels.discord.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. - File uploads supported up to the configured `channels.discord.mediaMaxMb` (default 8 MB). - Mention-gated guild replies by default to avoid noisy bots. - Reply context is injected when a message references another message (quoted content + ids). @@ -307,7 +307,7 @@ ack reaction after the bot replies. - `guilds..requireMention`: per-guild mention requirement (overridable per channel). - `guilds..reactionNotifications`: reaction system event mode (`off`, `own`, `all`, `allowlist`). - `textChunkLimit`: outbound text chunk size (chars). Default: 2000. -- `chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on every newline before length chunking. +- `chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on blank lines (paragraph boundaries) before length chunking. - `maxLinesPerMessage`: soft max line count per message. Default: 17. - `mediaMaxMb`: clamp inbound media saved to disk. - `historyLimit`: number of recent guild messages to include as context when replying to a mention (default 20; falls back to `messages.groupChat.historyLimit`; `0` disables). diff --git a/docs/channels/grammy.md b/docs/channels/grammy.md index ff0c92c7a69..89e5beed22a 100644 --- a/docs/channels/grammy.md +++ b/docs/channels/grammy.md @@ -17,7 +17,7 @@ read_when: - **Proxy:** optional `channels.telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`. - **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `channels.telegram.webhookUrl` is set (otherwise it long-polls). - **Sessions:** direct chats collapse into the agent main session (`agent::`); groups use `agent::telegram:group:`; replies route back to the same channel. -- **Config knobs:** `channels.telegram.botToken`, `channels.telegram.dmPolicy`, `channels.telegram.groups` (allowlist + mention defaults), `channels.telegram.allowFrom`, `channels.telegram.groupAllowFrom`, `channels.telegram.groupPolicy`, `channels.telegram.mediaMaxMb`, `channels.telegram.proxy`, `channels.telegram.webhookSecret`, `channels.telegram.webhookUrl`. +- **Config knobs:** `channels.telegram.botToken`, `channels.telegram.dmPolicy`, `channels.telegram.groups` (allowlist + mention defaults), `channels.telegram.allowFrom`, `channels.telegram.groupAllowFrom`, `channels.telegram.groupPolicy`, `channels.telegram.mediaMaxMb`, `channels.telegram.linkPreview`, `channels.telegram.proxy`, `channels.telegram.webhookSecret`, `channels.telegram.webhookUrl`. - **Draft streaming:** optional `channels.telegram.streamMode` uses `sendMessageDraft` in private topic chats (Bot API 9.3+). This is separate from channel block streaming. - **Tests:** grammy mocks cover DM + group mention gating and outbound send; more media/webhook fixtures still welcome. diff --git a/docs/channels/imessage.md b/docs/channels/imessage.md index 316822dc5c2..bae945e8cbd 100644 --- a/docs/channels/imessage.md +++ b/docs/channels/imessage.md @@ -219,7 +219,7 @@ This is useful when you want an isolated personality/model for a specific thread ## Limits - Outbound text is chunked to `channels.imessage.textChunkLimit` (default 4000). -- Optional newline chunking: set `channels.imessage.chunkMode="newline"` to split on each line before length chunking. +- Optional newline chunking: set `channels.imessage.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. - Media uploads are capped by `channels.imessage.mediaMaxMb` (default 16). ## Addressing / delivery targets @@ -254,7 +254,7 @@ Provider options: - `channels.imessage.includeAttachments`: ingest attachments into context. - `channels.imessage.mediaMaxMb`: inbound/outbound media cap (MB). - `channels.imessage.textChunkLimit`: outbound chunk size (chars). -- `channels.imessage.chunkMode`: `length` (default) or `newline` to split on newlines before length chunking. +- `channels.imessage.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. Related global options: - `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`). diff --git a/docs/channels/matrix.md b/docs/channels/matrix.md index 77a2989d5bb..2d9025f5146 100644 --- a/docs/channels/matrix.md +++ b/docs/channels/matrix.md @@ -215,7 +215,7 @@ Provider options: - `channels.matrix.initialSyncLimit`: initial sync limit. - `channels.matrix.threadReplies`: `off | inbound | always` (default: inbound). - `channels.matrix.textChunkLimit`: outbound text chunk size (chars). -- `channels.matrix.chunkMode`: `length` (default) or `newline` to split on newlines before length chunking. +- `channels.matrix.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. - `channels.matrix.dm.policy`: `pairing | allowlist | open | disabled` (default: pairing). - `channels.matrix.dm.allowFrom`: DM allowlist (user IDs or display names). `open` requires `"*"`. The wizard resolves names to IDs when possible. - `channels.matrix.groupPolicy`: `allowlist | open | disabled` (default: allowlist). diff --git a/docs/channels/msteams.md b/docs/channels/msteams.md index de3b064b2b7..2f6ed5f83fd 100644 --- a/docs/channels/msteams.md +++ b/docs/channels/msteams.md @@ -415,7 +415,7 @@ Key settings (see `/gateway/configuration` for shared channel patterns): - `channels.msteams.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing) - `channels.msteams.allowFrom`: allowlist for DMs (AAD object IDs, UPNs, or display names). The wizard resolves names to IDs during setup when Graph access is available. - `channels.msteams.textChunkLimit`: outbound text chunk size. -- `channels.msteams.chunkMode`: `length` (default) or `newline` to split on newlines before length chunking. +- `channels.msteams.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. - `channels.msteams.mediaAllowHosts`: allowlist for inbound attachment hosts (defaults to Microsoft/Teams domains). - `channels.msteams.requireMention`: require @mention in channels/groups (default true). - `channels.msteams.replyStyle`: `thread | top-level` (see [Reply Style](#reply-style-threads-vs-posts)). diff --git a/docs/channels/nextcloud-talk.md b/docs/channels/nextcloud-talk.md index 43c1595edd6..abc69644464 100644 --- a/docs/channels/nextcloud-talk.md +++ b/docs/channels/nextcloud-talk.md @@ -114,7 +114,7 @@ Provider options: - `channels.nextcloud-talk.dmHistoryLimit`: DM history limit (0 disables). - `channels.nextcloud-talk.dms`: per-DM overrides (historyLimit). - `channels.nextcloud-talk.textChunkLimit`: outbound text chunk size (chars). -- `channels.nextcloud-talk.chunkMode`: `length` (default) or `newline` to split on newlines before length chunking. +- `channels.nextcloud-talk.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. - `channels.nextcloud-talk.blockStreaming`: disable block streaming for this channel. - `channels.nextcloud-talk.blockStreamingCoalesce`: block streaming coalesce tuning. - `channels.nextcloud-talk.mediaMaxMb`: inbound media cap (MB). diff --git a/docs/channels/signal.md b/docs/channels/signal.md index 0ba89385da2..c154b059109 100644 --- a/docs/channels/signal.md +++ b/docs/channels/signal.md @@ -111,7 +111,7 @@ Groups: ## Media + limits - Outbound text is chunked to `channels.signal.textChunkLimit` (default 4000). -- Optional newline chunking: set `channels.signal.chunkMode="newline"` to split on each line before length chunking. +- Optional newline chunking: set `channels.signal.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. - Attachments supported (base64 fetched from `signal-cli`). - Default media cap: `channels.signal.mediaMaxMb` (default 8). - Use `channels.signal.ignoreAttachments` to skip downloading media. @@ -170,7 +170,7 @@ Provider options: - `channels.signal.historyLimit`: max group messages to include as context (0 disables). - `channels.signal.dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `channels.signal.dms[""].historyLimit`. - `channels.signal.textChunkLimit`: outbound chunk size (chars). -- `channels.signal.chunkMode`: `length` (default) or `newline` to split on newlines before length chunking. +- `channels.signal.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. - `channels.signal.mediaMaxMb`: inbound/outbound media cap (MB). Related global options: diff --git a/docs/channels/slack.md b/docs/channels/slack.md index bf7bcbf49d6..5f768db0e35 100644 --- a/docs/channels/slack.md +++ b/docs/channels/slack.md @@ -26,7 +26,7 @@ Minimal config: ``` ### Setup -1) Create a Slack app (From scratch) in https://api.channels.slack.com/apps. +1) Create a Slack app (From scratch) in https://api.slack.com/apps. 2) **Socket Mode** → toggle on. Then go to **Basic Information** → **App-Level Tokens** → **Generate Token and Scopes** with scope `connections:write`. Copy the **App Token** (`xapp-...`). 3) **OAuth & Permissions** → add bot token scopes (use the manifest below). Click **Install to Workspace**. Copy the **Bot User OAuth Token** (`xoxb-...`). 4) Optional: **OAuth & Permissions** → add **User Token Scopes** (see the read-only list below). Reinstall the app and copy the **User OAuth Token** (`xoxp-...`). @@ -245,29 +245,29 @@ If you enable native commands, add one `slash_commands` entry per command you wa ## Scopes (current vs optional) Slack's Conversations API is type-scoped: you only need the scopes for the conversation types you actually touch (channels, groups, im, mpim). See -https://api.channels.slack.com/docs/conversations-api for the overview. +https://docs.slack.dev/apis/web-api/using-the-conversations-api/ for the overview. ### Bot token scopes (required) - `chat:write` (send/update/delete messages via `chat.postMessage`) - https://api.channels.slack.com/methods/chat.postMessage + https://docs.slack.dev/reference/methods/chat.postMessage - `im:write` (open DMs via `conversations.open` for user DMs) - https://api.channels.slack.com/methods/conversations.open + https://docs.slack.dev/reference/methods/conversations.open - `channels:history`, `groups:history`, `im:history`, `mpim:history` - https://api.channels.slack.com/methods/conversations.history + https://docs.slack.dev/reference/methods/conversations.history - `channels:read`, `groups:read`, `im:read`, `mpim:read` - https://api.channels.slack.com/methods/conversations.info + https://docs.slack.dev/reference/methods/conversations.info - `users:read` (user lookup) - https://api.channels.slack.com/methods/users.info + https://docs.slack.dev/reference/methods/users.info - `reactions:read`, `reactions:write` (`reactions.get` / `reactions.add`) - https://api.channels.slack.com/methods/reactions.get - https://api.channels.slack.com/methods/reactions.add + https://docs.slack.dev/reference/methods/reactions.get + https://docs.slack.dev/reference/methods/reactions.add - `pins:read`, `pins:write` (`pins.list` / `pins.add` / `pins.remove`) - https://api.channels.slack.com/scopes/pins:read - https://api.channels.slack.com/scopes/pins:write + https://docs.slack.dev/reference/scopes/pins.read + https://docs.slack.dev/reference/scopes/pins.write - `emoji:read` (`emoji.list`) - https://api.channels.slack.com/scopes/emoji:read + https://docs.slack.dev/reference/scopes/emoji.read - `files:write` (uploads via `files.uploadV2`) - https://api.channels.slack.com/messaging/files/uploading + https://docs.slack.dev/messaging/working-with-files/#upload ### User token scopes (optional, read-only by default) Add these under **User Token Scopes** if you configure `channels.slack.userToken`. @@ -284,9 +284,9 @@ Add these under **User Token Scopes** if you configure `channels.slack.userToken - `mpim:write` (only if we add group-DM open/DM start via `conversations.open`) - `groups:write` (only if we add private-channel management: create/rename/invite/archive) - `chat:write.public` (only if we want to post to channels the bot isn't in) - https://api.channels.slack.com/scopes/chat:write.public + https://docs.slack.dev/reference/scopes/chat.write.public - `users:read.email` (only if we need email fields from `users.info`) - https://api.channels.slack.com/changelog/2017-04-narrowing-email-access + https://docs.slack.dev/changelog/2017-04-narrowing-email-access - `files:read` (only if we start listing/reading file metadata) ## Config @@ -349,7 +349,7 @@ ack reaction after the bot replies. ## Limits - Outbound text is chunked to `channels.slack.textChunkLimit` (default 4000). -- Optional newline chunking: set `channels.slack.chunkMode="newline"` to split on each line before length chunking. +- Optional newline chunking: set `channels.slack.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. - Media uploads are capped by `channels.slack.mediaMaxMb` (default 20). ## Reply threading diff --git a/docs/channels/telegram.md b/docs/channels/telegram.md index f88f50bb6b5..e708e2e6435 100644 --- a/docs/channels/telegram.md +++ b/docs/channels/telegram.md @@ -135,7 +135,7 @@ Notes: ## Limits - Outbound text is chunked to `channels.telegram.textChunkLimit` (default 4000). -- Optional newline chunking: set `channels.telegram.chunkMode="newline"` to split on each line before length chunking. +- Optional newline chunking: set `channels.telegram.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. - Media downloads/uploads are capped by `channels.telegram.mediaMaxMb` (default 5). - Telegram Bot API requests time out after `channels.telegram.timeoutSeconds` (default 500 via grammY). Set lower to avoid long hangs. - Group history context uses `channels.telegram.historyLimit` (or `channels.telegram.accounts.*.historyLimit`), falling back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50). @@ -524,7 +524,8 @@ Provider options: - `channels.telegram.accounts..capabilities.inlineButtons`: per-account override. - `channels.telegram.replyToMode`: `off | first | all` (default: `first`). - `channels.telegram.textChunkLimit`: outbound chunk size (chars). -- `channels.telegram.chunkMode`: `length` (default) or `newline` to split on newlines before length chunking. +- `channels.telegram.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. +- `channels.telegram.linkPreview`: toggle link previews for outbound messages (default: true). - `channels.telegram.streamMode`: `off | partial | block` (draft streaming). - `channels.telegram.mediaMaxMb`: inbound/outbound media cap (MB). - `channels.telegram.retry`: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter). diff --git a/docs/channels/whatsapp.md b/docs/channels/whatsapp.md index 517c71b930c..4759cf4c95e 100644 --- a/docs/channels/whatsapp.md +++ b/docs/channels/whatsapp.md @@ -271,7 +271,7 @@ WhatsApp can automatically send emoji reactions to incoming messages immediately ## Limits - Outbound text is chunked to `channels.whatsapp.textChunkLimit` (default 4000). -- Optional newline chunking: set `channels.whatsapp.chunkMode="newline"` to split on each line before length chunking. +- Optional newline chunking: set `channels.whatsapp.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. - Inbound media saves are capped by `channels.whatsapp.mediaMaxMb` (default 50 MB). - Outbound media items are capped by `agents.defaults.mediaMaxMb` (default 5 MB). diff --git a/docs/concepts/streaming.md b/docs/concepts/streaming.md index 8019e4cca16..6f9609ca64c 100644 --- a/docs/concepts/streaming.md +++ b/docs/concepts/streaming.md @@ -38,7 +38,7 @@ Legend: - `agents.defaults.blockStreamingChunk`: `{ minChars, maxChars, breakPreference? }`. - `agents.defaults.blockStreamingCoalesce`: `{ minChars?, maxChars?, idleMs? }` (merge streamed blocks before send). - Channel hard cap: `*.textChunkLimit` (e.g., `channels.whatsapp.textChunkLimit`). -- Channel chunk mode: `*.chunkMode` (`length` default, `newline` splits on each line before length chunking). +- Channel chunk mode: `*.chunkMode` (`length` default, `newline` splits on blank lines (paragraph boundaries) before length chunking). - Discord soft cap: `channels.discord.maxLinesPerMessage` (default 17) splits tall replies to avoid UI clipping. **Boundary semantics:** diff --git a/docs/diagnostics/flags.md b/docs/diagnostics/flags.md new file mode 100644 index 00000000000..959f1fe1120 --- /dev/null +++ b/docs/diagnostics/flags.md @@ -0,0 +1,89 @@ +--- +summary: "Diagnostics flags for targeted debug logs" +read_when: + - You need targeted debug logs without raising global logging levels + - You need to capture subsystem-specific logs for support +--- +# Diagnostics Flags + +Diagnostics flags let you enable targeted debug logs without turning on verbose logging everywhere. Flags are opt-in and have no effect unless a subsystem checks them. + +## How it works + +- Flags are strings (case-insensitive). +- You can enable flags in config or via an env override. +- Wildcards are supported: + - `telegram.*` matches `telegram.http` + - `*` enables all flags + +## Enable via config + +```json +{ + "diagnostics": { + "flags": ["telegram.http"] + } +} +``` + +Multiple flags: + +```json +{ + "diagnostics": { + "flags": ["telegram.http", "gateway.*"] + } +} +``` + +Restart the gateway after changing flags. + +## Env override (one-off) + +```bash +CLAWDBOT_DIAGNOSTICS=telegram.http,telegram.payload +``` + +Disable all flags: + +```bash +CLAWDBOT_DIAGNOSTICS=0 +``` + +## Where logs go + +Flags emit logs into the standard diagnostics log file. By default: + +``` +/tmp/clawdbot/clawdbot-YYYY-MM-DD.log +``` + +If you set `logging.file`, use that path instead. Logs are JSONL (one JSON object per line). Redaction still applies based on `logging.redactSensitive`. + +## Extract logs + +Pick the latest log file: + +```bash +ls -t /tmp/clawdbot/clawdbot-*.log | head -n 1 +``` + +Filter for Telegram HTTP diagnostics: + +```bash +rg "telegram http error" /tmp/clawdbot/clawdbot-*.log +``` + +Or tail while reproducing: + +```bash +tail -f /tmp/clawdbot/clawdbot-$(date +%F).log | rg "telegram http error" +``` + +For remote gateways, you can also use `clawdbot logs --follow` (see [/cli/logs](/cli/logs)). + +## Notes + +- If `logging.level` is set higher than `warn`, these logs may be suppressed. Default `info` is fine. +- Flags are safe to leave enabled; they only affect log volume for the specific subsystem. +- Use [/logging](/logging) to change log destinations, levels, and redaction. diff --git a/docs/docs.json b/docs/docs.json index 09b248990f6..b0f0ee802da 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -788,6 +788,14 @@ { "source": "/install/railway/", "destination": "/railway" + }, + { + "source": "/gcp", + "destination": "/platforms/gcp" + }, + { + "source": "/gcp/", + "destination": "/platforms/gcp" } ], "navigation": { @@ -827,6 +835,7 @@ "install/nix", "install/docker", "railway", + "render", "install/bun" ] }, @@ -983,6 +992,7 @@ "bedrock", "providers/moonshot", "providers/minimax", + "providers/vercel-ai-gateway", "providers/openrouter", "providers/synthetic", "providers/opencode", @@ -1055,6 +1065,7 @@ "platforms/linux", "platforms/fly", "platforms/hetzner", + "platforms/gcp", "platforms/exe-dev" ] }, diff --git a/docs/gateway/cli-backends.md b/docs/gateway/cli-backends.md index 917145cc24f..092533c2e85 100644 --- a/docs/gateway/cli-backends.md +++ b/docs/gateway/cli-backends.md @@ -182,6 +182,7 @@ Clawdbot ships a default for `claude-cli`: - `command: "claude"` - `args: ["-p", "--output-format", "json", "--dangerously-skip-permissions"]` +- `resumeArgs: ["-p", "--output-format", "json", "--dangerously-skip-permissions", "--resume", "{sessionId}"]` - `modelArg: "--model"` - `systemPromptArg: "--append-system-prompt"` - `sessionArg: "--session-id"` diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 12226e1f390..97427debe19 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -1021,6 +1021,7 @@ Set `channels.telegram.configWrites: false` to block Telegram-initiated config w ], historyLimit: 50, // include last N group messages as context (0 disables) replyToMode: "first", // off | first | all + linkPreview: true, // toggle outbound link previews streamMode: "partial", // off | partial | block (draft streaming; separate from block streaming) draftChunk: { // optional; only for streamMode=block minChars: 200, @@ -1130,7 +1131,7 @@ Reaction notification modes: - `own`: reactions on the bot's own messages (default). - `all`: all reactions on all messages. - `allowlist`: reactions from `guilds..users` on all messages (empty list disables). -Outbound text is chunked by `channels.discord.textChunkLimit` (default 2000). Set `channels.discord.chunkMode="newline"` to split on line boundaries before length chunking. Discord clients can clip very tall messages, so `channels.discord.maxLinesPerMessage` (default 17) splits long multi-line replies even when under 2000 chars. +Outbound text is chunked by `channels.discord.textChunkLimit` (default 2000). Set `channels.discord.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. Discord clients can clip very tall messages, so `channels.discord.maxLinesPerMessage` (default 17) splits long multi-line replies even when under 2000 chars. Retry policy defaults and behavior are documented in [Retry policy](/concepts/retry). ### `channels.googlechat` (Chat API webhook) @@ -2846,8 +2847,9 @@ Control UI base path: - `gateway.controlUi.basePath` sets the URL prefix where the Control UI is served. - Examples: `"/ui"`, `"/clawdbot"`, `"/apps/clawdbot"`. - Default: root (`/`) (unchanged). -- `gateway.controlUi.allowInsecureAuth` allows token-only auth over **HTTP** (no device identity). - Default: `false`. Prefer HTTPS (Tailscale Serve) or `127.0.0.1`. +- `gateway.controlUi.allowInsecureAuth` allows token-only auth for the Control UI and skips + device identity + pairing (even on HTTPS). Default: `false`. Prefer HTTPS + (Tailscale Serve) or `127.0.0.1`. Related docs: - [Control UI](/web/control-ui) @@ -2865,21 +2867,22 @@ Notes: - `gateway.port` controls the single multiplexed port used for WebSocket + HTTP (control UI, hooks, A2UI). - OpenAI Chat Completions endpoint: **disabled by default**; enable with `gateway.http.endpoints.chatCompletions.enabled: true`. - Precedence: `--port` > `CLAWDBOT_GATEWAY_PORT` > `gateway.port` > default `18789`. -- Non-loopback binds (`lan`/`tailnet`/`auto`) require auth. Use `gateway.auth.token` (or `CLAWDBOT_GATEWAY_TOKEN`). +- Gateway auth is required by default (token/password or Tailscale Serve identity). Non-loopback binds require a shared token/password. - The onboarding wizard generates a gateway token by default (even on loopback). - `gateway.remote.token` is **only** for remote CLI calls; it does not enable local gateway auth. `gateway.token` is ignored. Auth and Tailscale: -- `gateway.auth.mode` sets the handshake requirements (`token` or `password`). +- `gateway.auth.mode` sets the handshake requirements (`token` or `password`). When unset, token auth is assumed. - `gateway.auth.token` stores the shared token for token auth (used by the CLI on the same machine). - When `gateway.auth.mode` is set, only that method is accepted (plus optional Tailscale headers). - `gateway.auth.password` can be set here, or via `CLAWDBOT_GATEWAY_PASSWORD` (recommended). - `gateway.auth.allowTailscale` allows Tailscale Serve identity headers (`tailscale-user-login`) to satisfy auth when the request arrives on loopback - with `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host`. When - `true`, Serve requests do not need a token/password; set `false` to require - explicit credentials. Defaults to `true` when `tailscale.mode = "serve"` and - auth mode is not `password`. + with `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host`. Clawdbot + verifies the identity by resolving the `x-forwarded-for` address via + `tailscale whois` before accepting it. When `true`, Serve requests do not need + a token/password; set `false` to require explicit credentials. Defaults to + `true` when `tailscale.mode = "serve"` and auth mode is not `password`. - `gateway.tailscale.mode: "serve"` uses Tailscale Serve (tailnet only, loopback bind). - `gateway.tailscale.mode: "funnel"` exposes the dashboard publicly; requires auth. - `gateway.tailscale.resetOnExit` resets Serve/Funnel config on shutdown. diff --git a/docs/gateway/index.md b/docs/gateway/index.md index d37320d1b06..824984bdec5 100644 --- a/docs/gateway/index.md +++ b/docs/gateway/index.md @@ -37,7 +37,7 @@ pnpm gateway:watch - `--force` uses `lsof` to find listeners on the chosen port, sends SIGTERM, logs what it killed, then starts the gateway (fails fast if `lsof` is missing). - If you run under a supervisor (launchd/systemd/mac app child-process mode), a stop/restart typically sends **SIGTERM**; older builds may surface this as `pnpm` `ELIFECYCLE` exit code **143** (SIGTERM), which is a normal shutdown, not a crash. - **SIGUSR1** triggers an in-process restart when authorized (gateway tool/config apply/update, or enable `commands.restart` for manual restarts). -- Gateway auth: set `gateway.auth.mode=token` + `gateway.auth.token` (or pass `--token ` / `CLAWDBOT_GATEWAY_TOKEN`) to require clients to send `connect.params.auth.token`. +- Gateway auth is required by default: set `gateway.auth.token` (or `CLAWDBOT_GATEWAY_TOKEN`) or `gateway.auth.password`. Clients must send `connect.params.auth.token/password` unless using Tailscale Serve identity. - The wizard now generates a token by default, even on loopback. - Port precedence: `--port` > `CLAWDBOT_GATEWAY_PORT` > `gateway.port` > default `18789`. diff --git a/docs/gateway/security.md b/docs/gateway/security.md index 48e3fa59c54..d13d830cffd 100644 --- a/docs/gateway/security.md +++ b/docs/gateway/security.md @@ -58,11 +58,28 @@ When the audit prints findings, treat this as a priority order: The Control UI needs a **secure context** (HTTPS or localhost) to generate device identity. If you enable `gateway.controlUi.allowInsecureAuth`, the UI falls back -to **token-only auth** on plain HTTP and skips device pairing. This is a security +to **token-only auth** and skips device pairing (even on HTTPS). This is a security downgrade—prefer HTTPS (Tailscale Serve) or open the UI on `127.0.0.1`. `clawdbot security audit` warns when this setting is enabled. +## Reverse Proxy Configuration + +If you run the Gateway behind a reverse proxy (nginx, Caddy, Traefik, etc.), you should configure `gateway.trustedProxies` for proper client IP detection. + +When the Gateway detects proxy headers (`X-Forwarded-For` or `X-Real-IP`) from an address that is **not** in `trustedProxies`, it will **not** treat connections as local clients. If gateway auth is disabled, those connections are rejected. This prevents authentication bypass where proxied connections would otherwise appear to come from localhost and receive automatic trust. + +```yaml +gateway: + trustedProxies: + - "127.0.0.1" # if your proxy runs on localhost + auth: + mode: password + password: ${CLAWDBOT_GATEWAY_PASSWORD} +``` + +When `trustedProxies` is configured, the Gateway will use `X-Forwarded-For` headers to determine the real client IP for local client detection. Make sure your proxy overwrites (not appends to) incoming `X-Forwarded-For` headers to prevent spoofing. + ## Local session logs live on disk Clawdbot stores session transcripts on disk under `~/.clawdbot/agents//sessions/*.jsonl`. @@ -263,7 +280,7 @@ The Gateway multiplexes **WebSocket + HTTP** on a single port: Bind mode controls where the Gateway listens: - `gateway.bind: "loopback"` (default): only local clients can connect. -- Non-loopback binds (`"lan"`, `"tailnet"`, `"custom"`) expand the attack surface. Only use them with `gateway.auth` enabled and a real firewall. +- Non-loopback binds (`"lan"`, `"tailnet"`, `"custom"`) expand the attack surface. Only use them with a shared token/password and a real firewall. Rules of thumb: - Prefer Tailscale Serve over LAN binds (Serve keeps the Gateway on loopback, and Tailscale handles access). @@ -272,13 +289,11 @@ Rules of thumb: ### 0.5) Lock down the Gateway WebSocket (local auth) -Gateway auth is **only** enforced when you set `gateway.auth`. If it’s unset, -loopback WS clients are unauthenticated — any local process can connect and call -`config.apply`. +Gateway auth is **required by default**. If no token/password is configured, +the Gateway refuses WebSocket connections (fail‑closed). -The onboarding wizard now generates a token by default (even for loopback) so -local clients must authenticate. If you skip the wizard or remove auth, you’re -back to open loopback. +The onboarding wizard generates a token by default (even for loopback) so +local clients must authenticate. Set a token so **all** WS clients must authenticate: @@ -316,9 +331,11 @@ Rotation checklist (token/password): When `gateway.auth.allowTailscale` is `true` (default for Serve), Clawdbot accepts Tailscale Serve identity headers (`tailscale-user-login`) as -authentication. This only triggers for requests that hit loopback and include -`x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host` as injected by -Tailscale. +authentication. Clawdbot verifies the identity by resolving the +`x-forwarded-for` address through the local Tailscale daemon (`tailscale whois`) +and matching it to the header. This only triggers for requests that hit loopback +and include `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host` as +injected by Tailscale. **Security rule:** do not forward these headers from your own reverse proxy. If you terminate TLS or proxy in front of the gateway, disable diff --git a/docs/gateway/tailscale.md b/docs/gateway/tailscale.md index b57ffcc336d..e6477fbfc3e 100644 --- a/docs/gateway/tailscale.md +++ b/docs/gateway/tailscale.md @@ -25,9 +25,12 @@ Set `gateway.auth.mode` to control the handshake: When `tailscale.mode = "serve"` and `gateway.auth.allowTailscale` is `true`, valid Serve proxy requests can authenticate via Tailscale identity headers -(`tailscale-user-login`) without supplying a token/password. Clawdbot only -treats a request as Serve when it arrives from loopback with Tailscale’s -`x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host` headers. +(`tailscale-user-login`) without supplying a token/password. Clawdbot verifies +the identity by resolving the `x-forwarded-for` address via the local Tailscale +daemon (`tailscale whois`) and matching it to the header before accepting it. +Clawdbot only treats a request as Serve when it arrives from loopback with +Tailscale’s `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host` +headers. To require explicit credentials, set `gateway.auth.allowTailscale: false` or force `gateway.auth.mode: "password"`. diff --git a/docs/help/faq.md b/docs/help/faq.md index 235339247af..aadbda9de98 100644 --- a/docs/help/faq.md +++ b/docs/help/faq.md @@ -105,6 +105,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS, - [How can my agent access my computer if the Gateway is hosted remotely?](#how-can-my-agent-access-my-computer-if-the-gateway-is-hosted-remotely) - [Tailscale is connected but I get no replies. What now?](#tailscale-is-connected-but-i-get-no-replies-what-now) - [Can two Clawdbots talk to each other (local + VPS)?](#can-two-clawdbots-talk-to-each-other-local-vps) + - [Do I need separate VPSes for multiple agents](#do-i-need-separate-vpses-for-multiple-agents) - [Is there a benefit to using a node on my personal laptop instead of SSH from a VPS?](#is-there-a-benefit-to-using-a-node-on-my-personal-laptop-instead-of-ssh-from-a-vps) - [Do nodes run a gateway service?](#do-nodes-run-a-gateway-service) - [Is there an API / RPC way to apply config?](#is-there-an-api-rpc-way-to-apply-config) @@ -138,6 +139,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS, - [Can I use self-hosted models (llama.cpp, vLLM, Ollama)?](#can-i-use-selfhosted-models-llamacpp-vllm-ollama) - [What do Clawd, Flawd, and Krill use for models?](#what-do-clawd-flawd-and-krill-use-for-models) - [How do I switch models on the fly (without restarting)?](#how-do-i-switch-models-on-the-fly-without-restarting) + - [Can I use GPT 5.2 for daily tasks and Codex 5.2 for coding](#can-i-use-gpt-52-for-daily-tasks-and-codex-52-for-coding) - [Why do I see “Model … is not allowed” and then no reply?](#why-do-i-see-model-is-not-allowed-and-then-no-reply) - [Why do I see “Unknown model: minimax/MiniMax-M2.1”?](#why-do-i-see-unknown-model-minimaxminimaxm21) - [Can I use MiniMax as my default and OpenAI for complex tasks?](#can-i-use-minimax-as-my-default-and-openai-for-complex-tasks) @@ -564,7 +566,6 @@ Remote access: [Gateway remote](/gateway/remote). We keep a **hosting hub** with the common providers. Pick one and follow the guide: - [VPS hosting](/vps) (all providers in one place) -- [Railway](/railway) (one‑click, browser‑based setup) - [Fly.io](/platforms/fly) - [Hetzner](/platforms/hetzner) - [exe.dev](/platforms/exe-dev) @@ -1449,7 +1450,7 @@ Have Bot A send a message to Bot B, then let Bot B reply as usual. **CLI bridge (generic):** run a script that calls the other Gateway with `clawdbot agent --message ... --deliver`, targeting a chat where the other bot -listens. If one bot is on Railway/VPS, point your CLI at that remote Gateway +listens. If one bot is on a remote VPS, point your CLI at that remote Gateway via SSH/Tailscale (see [Remote access](/gateway/remote)). Example pattern (run from a machine that can reach the target Gateway): @@ -1462,6 +1463,16 @@ allowlists, or a "do not reply to bot messages" rule). Docs: [Remote access](/gateway/remote), [Agent CLI](/cli/agent), [Agent send](/tools/agent-send). +### Do I need separate VPSes for multiple agents + +No. One Gateway can host multiple agents, each with its own workspace, model defaults, +and routing. That is the normal setup and it is much cheaper and simpler than running +one VPS per agent. + +Use separate VPSes only when you need hard isolation (security boundaries) or very +different configs that you do not want to share. Otherwise, keep one Gateway and +use multiple agents or sub-agents. + ### Is there a benefit to using a node on my personal laptop instead of SSH from a VPS Yes - nodes are the first‑class way to reach your laptop from a remote Gateway, and they @@ -1947,6 +1958,16 @@ Re-run `/model` **without** the `@profile` suffix: If you want to return to the default, pick it from `/model` (or send `/model `). Use `/model status` to confirm which auth profile is active. +### Can I use GPT 5.2 for daily tasks and Codex 5.2 for coding + +Yes. Set one as default and switch as needed: + +- **Quick switch (per session):** `/model gpt-5.2` for daily tasks, `/model gpt-5.2-codex` for coding. +- **Default + switch:** set `agents.defaults.model.primary` to `openai-codex/gpt-5.2`, then switch to `openai-codex/gpt-5.2-codex` when coding (or the other way around). +- **Sub-agents:** route coding tasks to sub-agents with a different default model. + +See [Models](/concepts/models) and [Slash commands](/tools/slash-commands). + ### Why do I see Model is not allowed and then no reply If `agents.defaults.models` is set, it becomes the **allowlist** for `/model` and any diff --git a/docs/logging.md b/docs/logging.md index ad53c1164f6..8d1cd5a9f85 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -192,6 +192,30 @@ Use this if you want diagnostics events available to plugins or custom sinks: } ``` +### Diagnostics flags (targeted logs) + +Use flags to turn on extra, targeted debug logs without raising `logging.level`. +Flags are case-insensitive and support wildcards (e.g. `telegram.*` or `*`). + +```json +{ + "diagnostics": { + "flags": ["telegram.http"] + } +} +``` + +Env override (one-off): + +``` +CLAWDBOT_DIAGNOSTICS=telegram.http,telegram.payload +``` + +Notes: +- Flag logs go to the standard log file (same as `logging.file`). +- Output is still redacted according to `logging.redactSensitive`. +- Full guide: [/diagnostics/flags](/diagnostics/flags). + ### Export to OpenTelemetry Diagnostics can be exported via the `diagnostics-otel` plugin (OTLP/HTTP). This diff --git a/docs/platforms/digitalocean.md b/docs/platforms/digitalocean.md new file mode 100644 index 00000000000..632057c84b5 --- /dev/null +++ b/docs/platforms/digitalocean.md @@ -0,0 +1,251 @@ +--- +summary: "Clawdbot on DigitalOcean (cheapest paid VPS option)" +read_when: + - Setting up Clawdbot on DigitalOcean + - Looking for cheap VPS hosting for Clawdbot +--- + +# Clawdbot on DigitalOcean + +## Goal + +Run a persistent Clawdbot Gateway on DigitalOcean for **$6/month** (or $4/mo with reserved pricing). + +If you want something even cheaper, see [Oracle Cloud (Free Tier)](#oracle-cloud-free-alternative) at the bottom — it's **actually free forever**. + +## Cost Comparison (2026) + +| Provider | Plan | Specs | Price/mo | Notes | +|----------|------|-------|----------|-------| +| **Oracle Cloud** | Always Free ARM | 4 OCPU, 24GB RAM | **$0** | Best value, requires ARM-compatible setup | +| **Hetzner** | CX22 | 2 vCPU, 4GB RAM | €3.79 (~$4) | Cheapest paid, EU datacenters | +| **DigitalOcean** | Basic | 1 vCPU, 1GB RAM | $6 | Easy UI, good docs | +| **Vultr** | Cloud Compute | 1 vCPU, 1GB RAM | $6 | Many locations | +| **Linode** | Nanode | 1 vCPU, 1GB RAM | $5 | Now part of Akamai | + +**Recommendation:** +- **Free:** Oracle Cloud ARM (if you can handle the signup process) +- **Paid:** Hetzner CX22 (best specs per dollar) — see [Hetzner guide](/platforms/hetzner) +- **Easy:** DigitalOcean (this guide) — beginner-friendly UI + +--- + +## Prerequisites + +- DigitalOcean account ([signup with $200 free credit](https://m.do.co/c/signup)) +- SSH key pair (or willingness to use password auth) +- ~20 minutes + +## 1) Create a Droplet + +1. Log into [DigitalOcean](https://cloud.digitalocean.com/) +2. Click **Create → Droplets** +3. Choose: + - **Region:** Closest to you (or your users) + - **Image:** Ubuntu 24.04 LTS + - **Size:** Basic → Regular → **$6/mo** (1 vCPU, 1GB RAM, 25GB SSD) + - **Authentication:** SSH key (recommended) or password +4. Click **Create Droplet** +5. Note the IP address + +## 2) Connect via SSH + +```bash +ssh root@YOUR_DROPLET_IP +``` + +## 3) Install Clawdbot + +```bash +# Update system +apt update && apt upgrade -y + +# Install Node.js 22 +curl -fsSL https://deb.nodesource.com/setup_22.x | bash - +apt install -y nodejs + +# Install Clawdbot +curl -fsSL https://clawd.bot/install.sh | bash + +# Verify +clawdbot --version +``` + +## 4) Run Onboarding + +```bash +clawdbot onboard --install-daemon +``` + +The wizard will walk you through: +- Model auth (API keys or OAuth) +- Channel setup (Telegram, WhatsApp, Discord, etc.) +- Gateway token (auto-generated) +- Daemon installation (systemd) + +## 5) Verify the Gateway + +```bash +# Check status +clawdbot status + +# Check service +systemctl --user status clawdbot-gateway.service + +# View logs +journalctl --user -u clawdbot-gateway.service -f +``` + +## 6) Access the Dashboard + +The gateway binds to loopback by default. To access the Control UI: + +**Option A: SSH Tunnel (recommended)** +```bash +# From your local machine +ssh -L 18789:localhost:18789 root@YOUR_DROPLET_IP + +# Then open: http://localhost:18789 +``` + +**Option B: Tailscale Serve (HTTPS, loopback-only)** +```bash +# On the droplet +curl -fsSL https://tailscale.com/install.sh | sh +tailscale up + +# Configure Gateway to use Tailscale Serve +clawdbot config set gateway.tailscale.mode serve +clawdbot gateway restart +``` + +Open: `https:///` + +Notes: +- Serve keeps the Gateway loopback-only and authenticates via Tailscale identity headers. +- To require token/password instead, set `gateway.auth.allowTailscale: false` or use `gateway.auth.mode: "password"`. + +**Option C: Tailnet bind (no Serve)** +```bash +clawdbot config set gateway.bind tailnet +clawdbot gateway restart +``` + +Open: `http://:18789` (token required). + +## 7) Connect Your Channels + +### Telegram +```bash +clawdbot pairing list telegram +clawdbot pairing approve telegram +``` + +### WhatsApp +```bash +clawdbot channels login whatsapp +# Scan QR code +``` + +See [Channels](/channels) for other providers. + +--- + +## Optimizations for 1GB RAM + +The $6 droplet only has 1GB RAM. To keep things running smoothly: + +### Add swap (recommended) +```bash +fallocate -l 2G /swapfile +chmod 600 /swapfile +mkswap /swapfile +swapon /swapfile +echo '/swapfile none swap sw 0 0' >> /etc/fstab +``` + +### Use a lighter model +If you're hitting OOMs, consider: +- Using API-based models (Claude, GPT) instead of local models +- Setting `agents.defaults.model.primary` to a smaller model + +### Monitor memory +```bash +free -h +htop +``` + +--- + +## Persistence + +All state lives in: +- `~/.clawdbot/` — config, credentials, session data +- `~/clawd/` — workspace (SOUL.md, memory, etc.) + +These survive reboots. Back them up periodically: +```bash +tar -czvf clawdbot-backup.tar.gz ~/.clawdbot ~/clawd +``` + +--- + +## Oracle Cloud Free Alternative + +Oracle Cloud offers **Always Free** ARM instances that are significantly more powerful: + +| What you get | Specs | +|--------------|-------| +| **4 OCPUs** | ARM Ampere A1 | +| **24GB RAM** | More than enough | +| **200GB storage** | Block volume | +| **Forever free** | No credit card charges | + +### Quick setup: +1. Sign up at [oracle.com/cloud/free](https://www.oracle.com/cloud/free/) +2. Create a VM.Standard.A1.Flex instance (ARM) +3. Choose Oracle Linux or Ubuntu +4. Allocate up to 4 OCPU / 24GB RAM within free tier +5. Follow the same Clawdbot install steps above + +**Caveats:** +- Signup can be finicky (retry if it fails) +- ARM architecture — most things work, but some binaries need ARM builds +- Oracle may reclaim idle instances (keep them active) + +For the full Oracle guide, see the [community docs](https://gist.github.com/rssnyder/51e3cfedd730e7dd5f4a816143b25dbd). + +--- + +## Troubleshooting + +### Gateway won't start +```bash +clawdbot gateway status +clawdbot doctor --non-interactive +journalctl -u clawdbot --no-pager -n 50 +``` + +### Port already in use +```bash +lsof -i :18789 +kill +``` + +### Out of memory +```bash +# Check memory +free -h + +# Add more swap +# Or upgrade to $12/mo droplet (2GB RAM) +``` + +--- + +## See Also + +- [Hetzner guide](/platforms/hetzner) — cheaper, more powerful +- [Docker install](/install/docker) — containerized setup +- [Tailscale](/gateway/tailscale) — secure remote access +- [Configuration](/gateway/configuration) — full config reference diff --git a/docs/platforms/fly.md b/docs/platforms/fly.md index 5f173e17fc8..0fdf176aeac 100644 --- a/docs/platforms/fly.md +++ b/docs/platforms/fly.md @@ -80,6 +80,7 @@ primary_region = "iad" |---------|-----| | `--bind lan` | Binds to `0.0.0.0` so Fly's proxy can reach the gateway | | `--allow-unconfigured` | Starts without a config file (you'll create one after) | +| `internal_port = 3000` | Must match `--port 3000` (or `CLAWDBOT_GATEWAY_PORT`) for Fly health checks | | `memory = "2048mb"` | 512MB is too small; 2GB recommended | | `CLAWDBOT_STATE_DIR = "/data"` | Persists state on the volume | @@ -181,7 +182,7 @@ cat > /data/clawdbot.json << 'EOF' "bind": "auto" }, "meta": { - "lastTouchedVersion": "2026.1.24" + "lastTouchedVersion": "2026.1.25" } } EOF @@ -235,6 +236,12 @@ The gateway is binding to `127.0.0.1` instead of `0.0.0.0`. **Fix:** Add `--bind lan` to your process command in `fly.toml`. +### Health checks failing / connection refused + +Fly can't reach the gateway on the configured port. + +**Fix:** Ensure `internal_port` matches the gateway port (set `--port 3000` or `CLAWDBOT_GATEWAY_PORT=3000`). + ### OOM / Memory Issues Container keeps restarting or getting killed. Signs: `SIGABRT`, `v8::internal::Runtime_AllocateInYoungGeneration`, or silent restarts. @@ -268,11 +275,11 @@ The lock file is at `/data/gateway.*.lock` (not in a subdirectory). ### Config Not Being Read -If using `--allow-unconfigured`, the gateway creates a minimal config. Your custom config at `/data/.clawdbot/clawdbot.json` should be read on restart. +If using `--allow-unconfigured`, the gateway creates a minimal config. Your custom config at `/data/clawdbot.json` should be read on restart. Verify the config exists: ```bash -fly ssh console --command "cat /data/.clawdbot/clawdbot.json" +fly ssh console --command "cat /data/clawdbot.json" ``` ### Writing Config via SSH @@ -281,18 +288,24 @@ The `fly ssh console -C` command doesn't support shell redirection. To write a c ```bash # Use echo + tee (pipe from local to remote) -echo '{"your":"config"}' | fly ssh console -C "tee /data/.clawdbot/clawdbot.json" +echo '{"your":"config"}' | fly ssh console -C "tee /data/clawdbot.json" # Or use sftp fly sftp shell -> put /local/path/config.json /data/.clawdbot/clawdbot.json +> put /local/path/config.json /data/clawdbot.json ``` **Note:** `fly sftp` may fail if the file already exists. Delete first: ```bash -fly ssh console --command "rm /data/.clawdbot/clawdbot.json" +fly ssh console --command "rm /data/clawdbot.json" ``` +### State Not Persisting + +If you lose credentials or sessions after a restart, the state dir is writing to the container filesystem. + +**Fix:** Ensure `CLAWDBOT_STATE_DIR=/data` is set in `fly.toml` and redeploy. + ## Updates ```bash @@ -330,6 +343,7 @@ fly machine update --vm-memory 2048 --command "node dist/index.js g - The Dockerfile is compatible with both architectures - For WhatsApp/Telegram onboarding, use `fly ssh console` - Persistent data lives on the volume at `/data` +- Signal requires Java + signal-cli; use a custom image and keep memory at 2GB+. ## Cost diff --git a/docs/platforms/gcp.md b/docs/platforms/gcp.md new file mode 100644 index 00000000000..cffa03ace6a --- /dev/null +++ b/docs/platforms/gcp.md @@ -0,0 +1,498 @@ +--- +summary: "Run Clawdbot Gateway 24/7 on a GCP Compute Engine VM (Docker) with durable state" +read_when: + - You want Clawdbot running 24/7 on GCP + - You want a production-grade, always-on Gateway on your own VM + - You want full control over persistence, binaries, and restart behavior +--- + +# Clawdbot on GCP Compute Engine (Docker, Production VPS Guide) + +## Goal + +Run a persistent Clawdbot Gateway on a GCP Compute Engine VM using Docker, with durable state, baked-in binaries, and safe restart behavior. + +If you want "Clawdbot 24/7 for ~$5-12/mo", this is a reliable setup on Google Cloud. +Pricing varies by machine type and region; pick the smallest VM that fits your workload and scale up if you hit OOMs. + +## What are we doing (simple terms)? + +- Create a GCP project and enable billing +- Create a Compute Engine VM +- Install Docker (isolated app runtime) +- Start the Clawdbot Gateway in Docker +- Persist `~/.clawdbot` + `~/clawd` on the host (survives restarts/rebuilds) +- Access the Control UI from your laptop via an SSH tunnel + +The Gateway can be accessed via: +- SSH port forwarding from your laptop +- Direct port exposure if you manage firewalling and tokens yourself + +This guide uses Debian on GCP Compute Engine. +Ubuntu also works; map packages accordingly. +For the generic Docker flow, see [Docker](/install/docker). + +--- + +## Quick path (experienced operators) + +1) Create GCP project + enable Compute Engine API +2) Create Compute Engine VM (e2-small, Debian 12, 20GB) +3) SSH into the VM +4) Install Docker +5) Clone Clawdbot repository +6) Create persistent host directories +7) Configure `.env` and `docker-compose.yml` +8) Bake required binaries, build, and launch + +--- + +## What you need + +- GCP account (free tier eligible for e2-micro) +- gcloud CLI installed (or use Cloud Console) +- SSH access from your laptop +- Basic comfort with SSH + copy/paste +- ~20-30 minutes +- Docker and Docker Compose +- Model auth credentials +- Optional provider credentials + - WhatsApp QR + - Telegram bot token + - Gmail OAuth + +--- + +## 1) Install gcloud CLI (or use Console) + +**Option A: gcloud CLI** (recommended for automation) + +Install from https://cloud.google.com/sdk/docs/install + +Initialize and authenticate: + +```bash +gcloud init +gcloud auth login +``` + +**Option B: Cloud Console** + +All steps can be done via the web UI at https://console.cloud.google.com + +--- + +## 2) Create a GCP project + +**CLI:** + +```bash +gcloud projects create my-clawdbot-project --name="Clawdbot Gateway" +gcloud config set project my-clawdbot-project +``` + +Enable billing at https://console.cloud.google.com/billing (required for Compute Engine). + +Enable the Compute Engine API: + +```bash +gcloud services enable compute.googleapis.com +``` + +**Console:** + +1. Go to IAM & Admin > Create Project +2. Name it and create +3. Enable billing for the project +4. Navigate to APIs & Services > Enable APIs > search "Compute Engine API" > Enable + +--- + +## 3) Create the VM + +**Machine types:** + +| Type | Specs | Cost | Notes | +|------|-------|------|-------| +| e2-small | 2 vCPU, 2GB RAM | ~$12/mo | Recommended | +| e2-micro | 2 vCPU (shared), 1GB RAM | Free tier eligible | May OOM under load | + +**CLI:** + +```bash +gcloud compute instances create clawdbot-gateway \ + --zone=us-central1-a \ + --machine-type=e2-small \ + --boot-disk-size=20GB \ + --image-family=debian-12 \ + --image-project=debian-cloud +``` + +**Console:** + +1. Go to Compute Engine > VM instances > Create instance +2. Name: `clawdbot-gateway` +3. Region: `us-central1`, Zone: `us-central1-a` +4. Machine type: `e2-small` +5. Boot disk: Debian 12, 20GB +6. Create + +--- + +## 4) SSH into the VM + +**CLI:** + +```bash +gcloud compute ssh clawdbot-gateway --zone=us-central1-a +``` + +**Console:** + +Click the "SSH" button next to your VM in the Compute Engine dashboard. + +Note: SSH key propagation can take 1-2 minutes after VM creation. If connection is refused, wait and retry. + +--- + +## 5) Install Docker (on the VM) + +```bash +sudo apt-get update +sudo apt-get install -y git curl ca-certificates +curl -fsSL https://get.docker.com | sudo sh +sudo usermod -aG docker $USER +``` + +Log out and back in for the group change to take effect: + +```bash +exit +``` + +Then SSH back in: + +```bash +gcloud compute ssh clawdbot-gateway --zone=us-central1-a +``` + +Verify: + +```bash +docker --version +docker compose version +``` + +--- + +## 6) Clone the Clawdbot repository + +```bash +git clone https://github.com/clawdbot/clawdbot.git +cd clawdbot +``` + +This guide assumes you will build a custom image to guarantee binary persistence. + +--- + +## 7) Create persistent host directories + +Docker containers are ephemeral. +All long-lived state must live on the host. + +```bash +mkdir -p ~/.clawdbot +mkdir -p ~/clawd +``` + +--- + +## 8) Configure environment variables + +Create `.env` in the repository root. + +```bash +CLAWDBOT_IMAGE=clawdbot:latest +CLAWDBOT_GATEWAY_TOKEN=change-me-now +CLAWDBOT_GATEWAY_BIND=lan +CLAWDBOT_GATEWAY_PORT=18789 + +CLAWDBOT_CONFIG_DIR=/home/$USER/.clawdbot +CLAWDBOT_WORKSPACE_DIR=/home/$USER/clawd + +GOG_KEYRING_PASSWORD=change-me-now +XDG_CONFIG_HOME=/home/node/.clawdbot +``` + +Generate strong secrets: + +```bash +openssl rand -hex 32 +``` + +**Do not commit this file.** + +--- + +## 9) Docker Compose configuration + +Create or update `docker-compose.yml`. + +```yaml +services: + clawdbot-gateway: + image: ${CLAWDBOT_IMAGE} + build: . + restart: unless-stopped + env_file: + - .env + environment: + - HOME=/home/node + - NODE_ENV=production + - TERM=xterm-256color + - CLAWDBOT_GATEWAY_BIND=${CLAWDBOT_GATEWAY_BIND} + - CLAWDBOT_GATEWAY_PORT=${CLAWDBOT_GATEWAY_PORT} + - CLAWDBOT_GATEWAY_TOKEN=${CLAWDBOT_GATEWAY_TOKEN} + - GOG_KEYRING_PASSWORD=${GOG_KEYRING_PASSWORD} + - XDG_CONFIG_HOME=${XDG_CONFIG_HOME} + - PATH=/home/linuxbrew/.linuxbrew/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + volumes: + - ${CLAWDBOT_CONFIG_DIR}:/home/node/.clawdbot + - ${CLAWDBOT_WORKSPACE_DIR}:/home/node/clawd + ports: + # Recommended: keep the Gateway loopback-only on the VM; access via SSH tunnel. + # To expose it publicly, remove the `127.0.0.1:` prefix and firewall accordingly. + - "127.0.0.1:${CLAWDBOT_GATEWAY_PORT}:18789" + + # Optional: only if you run iOS/Android nodes against this VM and need Canvas host. + # If you expose this publicly, read /gateway/security and firewall accordingly. + # - "18793:18793" + command: + [ + "node", + "dist/index.js", + "gateway", + "--bind", + "${CLAWDBOT_GATEWAY_BIND}", + "--port", + "${CLAWDBOT_GATEWAY_PORT}" + ] +``` + +--- + +## 10) Bake required binaries into the image (critical) + +Installing binaries inside a running container is a trap. +Anything installed at runtime will be lost on restart. + +All external binaries required by skills must be installed at image build time. + +The examples below show three common binaries only: +- `gog` for Gmail access +- `goplaces` for Google Places +- `wacli` for WhatsApp + +These are examples, not a complete list. +You may install as many binaries as needed using the same pattern. + +If you add new skills later that depend on additional binaries, you must: +1. Update the Dockerfile +2. Rebuild the image +3. Restart the containers + +**Example Dockerfile** + +```dockerfile +FROM node:22-bookworm + +RUN apt-get update && apt-get install -y socat && rm -rf /var/lib/apt/lists/* + +# Example binary 1: Gmail CLI +RUN curl -L https://github.com/steipete/gog/releases/latest/download/gog_Linux_x86_64.tar.gz \ + | tar -xz -C /usr/local/bin && chmod +x /usr/local/bin/gog + +# Example binary 2: Google Places CLI +RUN curl -L https://github.com/steipete/goplaces/releases/latest/download/goplaces_Linux_x86_64.tar.gz \ + | tar -xz -C /usr/local/bin && chmod +x /usr/local/bin/goplaces + +# Example binary 3: WhatsApp CLI +RUN curl -L https://github.com/steipete/wacli/releases/latest/download/wacli_Linux_x86_64.tar.gz \ + | tar -xz -C /usr/local/bin && chmod +x /usr/local/bin/wacli + +# Add more binaries below using the same pattern + +WORKDIR /app +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY ui/package.json ./ui/package.json +COPY scripts ./scripts + +RUN corepack enable +RUN pnpm install --frozen-lockfile + +COPY . . +RUN pnpm build +RUN pnpm ui:install +RUN pnpm ui:build + +ENV NODE_ENV=production + +CMD ["node","dist/index.js"] +``` + +--- + +## 11) Build and launch + +```bash +docker compose build +docker compose up -d clawdbot-gateway +``` + +Verify binaries: + +```bash +docker compose exec clawdbot-gateway which gog +docker compose exec clawdbot-gateway which goplaces +docker compose exec clawdbot-gateway which wacli +``` + +Expected output: + +``` +/usr/local/bin/gog +/usr/local/bin/goplaces +/usr/local/bin/wacli +``` + +--- + +## 12) Verify Gateway + +```bash +docker compose logs -f clawdbot-gateway +``` + +Success: + +``` +[gateway] listening on ws://0.0.0.0:18789 +``` + +--- + +## 13) Access from your laptop + +Create an SSH tunnel to forward the Gateway port: + +```bash +gcloud compute ssh clawdbot-gateway --zone=us-central1-a -- -L 18789:127.0.0.1:18789 +``` + +Open in your browser: + +`http://127.0.0.1:18789/` + +Paste your gateway token. + +--- + +## What persists where (source of truth) + +Clawdbot runs in Docker, but Docker is not the source of truth. +All long-lived state must survive restarts, rebuilds, and reboots. + +| Component | Location | Persistence mechanism | Notes | +|---|---|---|---| +| Gateway config | `/home/node/.clawdbot/` | Host volume mount | Includes `clawdbot.json`, tokens | +| Model auth profiles | `/home/node/.clawdbot/` | Host volume mount | OAuth tokens, API keys | +| Skill configs | `/home/node/.clawdbot/skills/` | Host volume mount | Skill-level state | +| Agent workspace | `/home/node/clawd/` | Host volume mount | Code and agent artifacts | +| WhatsApp session | `/home/node/.clawdbot/` | Host volume mount | Preserves QR login | +| Gmail keyring | `/home/node/.clawdbot/` | Host volume + password | Requires `GOG_KEYRING_PASSWORD` | +| External binaries | `/usr/local/bin/` | Docker image | Must be baked at build time | +| Node runtime | Container filesystem | Docker image | Rebuilt every image build | +| OS packages | Container filesystem | Docker image | Do not install at runtime | +| Docker container | Ephemeral | Restartable | Safe to destroy | + +--- + +## Updates + +To update Clawdbot on the VM: + +```bash +cd ~/clawdbot +git pull +docker compose build +docker compose up -d +``` + +--- + +## Troubleshooting + +**SSH connection refused** + +SSH key propagation can take 1-2 minutes after VM creation. Wait and retry. + +**OS Login issues** + +Check your OS Login profile: + +```bash +gcloud compute os-login describe-profile +``` + +Ensure your account has the required IAM permissions (Compute OS Login or Compute OS Admin Login). + +**Out of memory (OOM)** + +If using e2-micro and hitting OOM, upgrade to e2-small or e2-medium: + +```bash +# Stop the VM first +gcloud compute instances stop clawdbot-gateway --zone=us-central1-a + +# Change machine type +gcloud compute instances set-machine-type clawdbot-gateway \ + --zone=us-central1-a \ + --machine-type=e2-small + +# Start the VM +gcloud compute instances start clawdbot-gateway --zone=us-central1-a +``` + +--- + +## Service accounts (security best practice) + +For personal use, your default user account works fine. + +For automation or CI/CD pipelines, create a dedicated service account with minimal permissions: + +1. Create a service account: + ```bash + gcloud iam service-accounts create clawdbot-deploy \ + --display-name="Clawdbot Deployment" + ``` + +2. Grant Compute Instance Admin role (or narrower custom role): + ```bash + gcloud projects add-iam-policy-binding my-clawdbot-project \ + --member="serviceAccount:clawdbot-deploy@my-clawdbot-project.iam.gserviceaccount.com" \ + --role="roles/compute.instanceAdmin.v1" + ``` + +Avoid using the Owner role for automation. Use the principle of least privilege. + +See https://cloud.google.com/iam/docs/understanding-roles for IAM role details. + +--- + +## Next steps + +- Set up messaging channels: [Channels](/channels) +- Pair local devices as nodes: [Nodes](/nodes) +- Configure the Gateway: [Gateway configuration](/gateway/configuration) diff --git a/docs/platforms/index.md b/docs/platforms/index.md index 1b5c85129df..3a1e8726764 100644 --- a/docs/platforms/index.md +++ b/docs/platforms/index.md @@ -24,9 +24,9 @@ Native companion apps for Windows are also planned; the Gateway is recommended v ## VPS & hosting - VPS hub: [VPS hosting](/vps) -- Railway (one-click): [Railway](/railway) - Fly.io: [Fly.io](/platforms/fly) - Hetzner (Docker): [Hetzner](/platforms/hetzner) +- GCP (Compute Engine): [GCP](/platforms/gcp) - exe.dev (VM + HTTPS proxy): [exe.dev](/platforms/exe-dev) ## Common links diff --git a/docs/platforms/mac/release.md b/docs/platforms/mac/release.md index 8015ffe2e71..d3bfd02c3d0 100644 --- a/docs/platforms/mac/release.md +++ b/docs/platforms/mac/release.md @@ -30,17 +30,17 @@ Notes: # From repo root; set release IDs so Sparkle feed is enabled. # APP_BUILD must be numeric + monotonic for Sparkle compare. BUNDLE_ID=com.clawdbot.mac \ -APP_VERSION=2026.1.24 \ +APP_VERSION=2026.1.25 \ APP_BUILD="$(git rev-list --count HEAD)" \ BUILD_CONFIG=release \ SIGN_IDENTITY="Developer ID Application: ()" \ scripts/package-mac-app.sh # Zip for distribution (includes resource forks for Sparkle delta support) -ditto -c -k --sequesterRsrc --keepParent dist/Clawdbot.app dist/Clawdbot-2026.1.24.zip +ditto -c -k --sequesterRsrc --keepParent dist/Clawdbot.app dist/Clawdbot-2026.1.25.zip # Optional: also build a styled DMG for humans (drag to /Applications) -scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.24.dmg +scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.25.dmg # Recommended: build + notarize/staple zip + DMG # First, create a keychain profile once: @@ -48,26 +48,26 @@ scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.24.dmg # --apple-id "" --team-id "" --password "" NOTARIZE=1 NOTARYTOOL_PROFILE=clawdbot-notary \ BUNDLE_ID=com.clawdbot.mac \ -APP_VERSION=2026.1.24 \ +APP_VERSION=2026.1.25 \ APP_BUILD="$(git rev-list --count HEAD)" \ BUILD_CONFIG=release \ SIGN_IDENTITY="Developer ID Application: ()" \ scripts/package-mac-dist.sh # Optional: ship dSYM alongside the release -ditto -c -k --keepParent apps/macos/.build/release/Clawdbot.app.dSYM dist/Clawdbot-2026.1.24.dSYM.zip +ditto -c -k --keepParent apps/macos/.build/release/Clawdbot.app.dSYM dist/Clawdbot-2026.1.25.dSYM.zip ``` ## Appcast entry Use the release note generator so Sparkle renders formatted HTML notes: ```bash -SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/Clawdbot-2026.1.24.zip https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml +SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/Clawdbot-2026.1.25.zip https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml ``` Generates HTML release notes from `CHANGELOG.md` (via [`scripts/changelog-to-html.sh`](https://github.com/clawdbot/clawdbot/blob/main/scripts/changelog-to-html.sh)) and embeds them in the appcast entry. Commit the updated `appcast.xml` alongside the release assets (zip + dSYM) when publishing. ## Publish & verify -- Upload `Clawdbot-2026.1.24.zip` (and `Clawdbot-2026.1.24.dSYM.zip`) to the GitHub release for tag `v2026.1.24`. +- Upload `Clawdbot-2026.1.25.zip` (and `Clawdbot-2026.1.25.dSYM.zip`) to the GitHub release for tag `v2026.1.25`. - Ensure the raw appcast URL matches the baked feed: `https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml`. - Sanity checks: - `curl -I https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml` returns 200. diff --git a/docs/platforms/raspberry-pi.md b/docs/platforms/raspberry-pi.md new file mode 100644 index 00000000000..b34e3fcfe60 --- /dev/null +++ b/docs/platforms/raspberry-pi.md @@ -0,0 +1,354 @@ +--- +summary: "Clawdbot on Raspberry Pi (budget self-hosted setup)" +read_when: + - Setting up Clawdbot on a Raspberry Pi + - Running Clawdbot on ARM devices + - Building a cheap always-on personal AI +--- + +# Clawdbot on Raspberry Pi + +## Goal + +Run a persistent, always-on Clawdbot Gateway on a Raspberry Pi for **~$35-80** one-time cost (no monthly fees). + +Perfect for: +- 24/7 personal AI assistant +- Home automation hub +- Low-power, always-available Telegram/WhatsApp bot + +## Hardware Requirements + +| Pi Model | RAM | Works? | Notes | +|----------|-----|--------|-------| +| **Pi 5** | 4GB/8GB | ✅ Best | Fastest, recommended | +| **Pi 4** | 4GB | ✅ Good | Sweet spot for most users | +| **Pi 4** | 2GB | ✅ OK | Works, add swap | +| **Pi 4** | 1GB | ⚠️ Tight | Possible with swap, minimal config | +| **Pi 3B+** | 1GB | ⚠️ Slow | Works but sluggish | +| **Pi Zero 2 W** | 512MB | ❌ | Not recommended | + +**Minimum specs:** 1GB RAM, 1 core, 500MB disk +**Recommended:** 2GB+ RAM, 64-bit OS, 16GB+ SD card (or USB SSD) + +## What You'll Need + +- Raspberry Pi 4 or 5 (2GB+ recommended) +- MicroSD card (16GB+) or USB SSD (better performance) +- Power supply (official Pi PSU recommended) +- Network connection (Ethernet or WiFi) +- ~30 minutes + +## 1) Flash the OS + +Use **Raspberry Pi OS Lite (64-bit)** — no desktop needed for a headless server. + +1. Download [Raspberry Pi Imager](https://www.raspberrypi.com/software/) +2. Choose OS: **Raspberry Pi OS Lite (64-bit)** +3. Click the gear icon (⚙️) to pre-configure: + - Set hostname: `gateway-host` + - Enable SSH + - Set username/password + - Configure WiFi (if not using Ethernet) +4. Flash to your SD card / USB drive +5. Insert and boot the Pi + +## 2) Connect via SSH + +```bash +ssh user@gateway-host +# or use the IP address +ssh user@192.168.x.x +``` + +## 3) System Setup + +```bash +# Update system +sudo apt update && sudo apt upgrade -y + +# Install essential packages +sudo apt install -y git curl build-essential + +# Set timezone (important for cron/reminders) +sudo timedatectl set-timezone America/Chicago # Change to your timezone +``` + +## 4) Install Node.js 22 (ARM64) + +```bash +# Install Node.js via NodeSource +curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - +sudo apt install -y nodejs + +# Verify +node --version # Should show v22.x.x +npm --version +``` + +## 5) Add Swap (Important for 2GB or less) + +Swap prevents out-of-memory crashes: + +```bash +# Create 2GB swap file +sudo fallocate -l 2G /swapfile +sudo chmod 600 /swapfile +sudo mkswap /swapfile +sudo swapon /swapfile + +# Make permanent +echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab + +# Optimize for low RAM (reduce swappiness) +echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf +sudo sysctl -p +``` + +## 6) Install Clawdbot + +### Option A: Standard Install (Recommended) + +```bash +curl -fsSL https://clawd.bot/install.sh | bash +``` + +### Option B: Hackable Install (For tinkering) + +```bash +git clone https://github.com/clawdbot/clawdbot.git +cd clawdbot +npm install +npm run build +npm link +``` + +The hackable install gives you direct access to logs and code — useful for debugging ARM-specific issues. + +## 7) Run Onboarding + +```bash +clawdbot onboard --install-daemon +``` + +Follow the wizard: +1. **Gateway mode:** Local +2. **Auth:** API keys recommended (OAuth can be finicky on headless Pi) +3. **Channels:** Telegram is easiest to start with +4. **Daemon:** Yes (systemd) + +## 8) Verify Installation + +```bash +# Check status +clawdbot status + +# Check service +sudo systemctl status clawdbot + +# View logs +journalctl -u clawdbot -f +``` + +## 9) Access the Dashboard + +Since the Pi is headless, use an SSH tunnel: + +```bash +# From your laptop/desktop +ssh -L 18789:localhost:18789 user@gateway-host + +# Then open in browser +open http://localhost:18789 +``` + +Or use Tailscale for always-on access: + +```bash +# On the Pi +curl -fsSL https://tailscale.com/install.sh | sh +sudo tailscale up + +# Update config +clawdbot config set gateway.bind tailnet +sudo systemctl restart clawdbot +``` + +--- + +## Performance Optimizations + +### Use a USB SSD (Huge Improvement) + +SD cards are slow and wear out. A USB SSD dramatically improves performance: + +```bash +# Check if booting from USB +lsblk +``` + +See [Pi USB boot guide](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#usb-mass-storage-boot) for setup. + +### Reduce Memory Usage + +```bash +# Disable GPU memory allocation (headless) +echo 'gpu_mem=16' | sudo tee -a /boot/config.txt + +# Disable Bluetooth if not needed +sudo systemctl disable bluetooth +``` + +### Monitor Resources + +```bash +# Check memory +free -h + +# Check CPU temperature +vcgencmd measure_temp + +# Live monitoring +htop +``` + +--- + +## ARM-Specific Notes + +### Binary Compatibility + +Most Clawdbot features work on ARM64, but some external binaries may need ARM builds: + +| Tool | ARM64 Status | Notes | +|------|--------------|-------| +| Node.js | ✅ | Works great | +| WhatsApp (Baileys) | ✅ | Pure JS, no issues | +| Telegram | ✅ | Pure JS, no issues | +| gog (Gmail CLI) | ⚠️ | Check for ARM release | +| Chromium (browser) | ✅ | `sudo apt install chromium-browser` | + +If a skill fails, check if its binary has an ARM build. Many Go/Rust tools do; some don't. + +### 32-bit vs 64-bit + +**Always use 64-bit OS.** Node.js and many modern tools require it. Check with: + +```bash +uname -m +# Should show: aarch64 (64-bit) not armv7l (32-bit) +``` + +--- + +## Recommended Model Setup + +Since the Pi is just the Gateway (models run in the cloud), use API-based models: + +```json +{ + "agents": { + "defaults": { + "model": { + "primary": "anthropic/claude-sonnet-4-20250514", + "fallbacks": ["openai/gpt-4o-mini"] + } + } + } +} +``` + +**Don't try to run local LLMs on a Pi** — even small models are too slow. Let Claude/GPT do the heavy lifting. + +--- + +## Auto-Start on Boot + +The onboarding wizard sets this up, but to verify: + +```bash +# Check service is enabled +sudo systemctl is-enabled clawdbot + +# Enable if not +sudo systemctl enable clawdbot + +# Start on boot +sudo systemctl start clawdbot +``` + +--- + +## Troubleshooting + +### Out of Memory (OOM) + +```bash +# Check memory +free -h + +# Add more swap (see Step 5) +# Or reduce services running on the Pi +``` + +### Slow Performance + +- Use USB SSD instead of SD card +- Disable unused services: `sudo systemctl disable cups bluetooth avahi-daemon` +- Check CPU throttling: `vcgencmd get_throttled` (should return `0x0`) + +### Service Won't Start + +```bash +# Check logs +journalctl -u clawdbot --no-pager -n 100 + +# Common fix: rebuild +cd ~/clawdbot # if using hackable install +npm run build +sudo systemctl restart clawdbot +``` + +### ARM Binary Issues + +If a skill fails with "exec format error": +1. Check if the binary has an ARM64 build +2. Try building from source +3. Or use a Docker container with ARM support + +### WiFi Drops + +For headless Pis on WiFi: + +```bash +# Disable WiFi power management +sudo iwconfig wlan0 power off + +# Make permanent +echo 'wireless-power off' | sudo tee -a /etc/network/interfaces +``` + +--- + +## Cost Comparison + +| Setup | One-Time Cost | Monthly Cost | Notes | +|-------|---------------|--------------|-------| +| **Pi 4 (2GB)** | ~$45 | $0 | + power (~$5/yr) | +| **Pi 4 (4GB)** | ~$55 | $0 | Recommended | +| **Pi 5 (4GB)** | ~$60 | $0 | Best performance | +| **Pi 5 (8GB)** | ~$80 | $0 | Overkill but future-proof | +| DigitalOcean | $0 | $6/mo | $72/year | +| Hetzner | $0 | €3.79/mo | ~$50/year | + +**Break-even:** A Pi pays for itself in ~6-12 months vs cloud VPS. + +--- + +## See Also + +- [Linux guide](/platforms/linux) — general Linux setup +- [DigitalOcean guide](/platforms/digitalocean) — cloud alternative +- [Hetzner guide](/platforms/hetzner) — Docker setup +- [Tailscale](/gateway/tailscale) — remote access +- [Nodes](/nodes) — pair your laptop/phone with the Pi gateway diff --git a/docs/plugin.md b/docs/plugin.md index ee9dfd8b04a..c57a024f2fe 100644 --- a/docs/plugin.md +++ b/docs/plugin.md @@ -67,6 +67,22 @@ Plugins can register: Plugins run **in‑process** with the Gateway, so treat them as trusted code. Tool authoring guide: [Plugin agent tools](/plugins/agent-tools). +## Runtime helpers + +Plugins can access selected core helpers via `api.runtime`. For telephony TTS: + +```ts +const result = await api.runtime.tts.textToSpeechTelephony({ + text: "Hello from Clawdbot", + cfg: api.config, +}); +``` + +Notes: +- Uses core `messages.tts` configuration (OpenAI or ElevenLabs). +- Returns PCM audio buffer + sample rate. Plugins must resample/encode for providers. +- Edge TTS is not supported for telephony. + ## Discovery & precedence Clawdbot scans, in order: diff --git a/docs/plugins/voice-call.md b/docs/plugins/voice-call.md index 5c55cec8803..eecb8013375 100644 --- a/docs/plugins/voice-call.md +++ b/docs/plugins/voice-call.md @@ -104,6 +104,87 @@ Notes: - `mock` is a local dev provider (no network calls). - `skipSignatureVerification` is for local testing only. +## TTS for calls + +Voice Call uses the core `messages.tts` configuration (OpenAI or ElevenLabs) for +streaming speech on calls. You can override it under the plugin config with the +**same shape** — it deep‑merges with `messages.tts`. + +```json5 +{ + tts: { + provider: "elevenlabs", + elevenlabs: { + voiceId: "pMsXgVXv3BLzUgSXRplE", + modelId: "eleven_multilingual_v2" + } + } +} +``` + +Notes: +- **Edge TTS is ignored for voice calls** (telephony audio needs PCM; Edge output is unreliable). +- Core TTS is used when Twilio media streaming is enabled; otherwise calls fall back to provider native voices. + +### More examples + +Use core TTS only (no override): + +```json5 +{ + messages: { + tts: { + provider: "openai", + openai: { voice: "alloy" } + } + } +} +``` + +Override to ElevenLabs just for calls (keep core default elsewhere): + +```json5 +{ + plugins: { + entries: { + "voice-call": { + config: { + tts: { + provider: "elevenlabs", + elevenlabs: { + apiKey: "elevenlabs_key", + voiceId: "pMsXgVXv3BLzUgSXRplE", + modelId: "eleven_multilingual_v2" + } + } + } + } + } + } +} +``` + +Override only the OpenAI model for calls (deep‑merge example): + +```json5 +{ + plugins: { + entries: { + "voice-call": { + config: { + tts: { + openai: { + model: "gpt-4o-mini-tts", + voice: "marin" + } + } + } + } + } + } +} +``` + ## Inbound calls Inbound policy defaults to `disabled`. To enable inbound calls, set: diff --git a/docs/providers/claude-max-api-proxy.md b/docs/providers/claude-max-api-proxy.md new file mode 100644 index 00000000000..255be62fc74 --- /dev/null +++ b/docs/providers/claude-max-api-proxy.md @@ -0,0 +1,145 @@ +--- +summary: "Use Claude Max/Pro subscription as an OpenAI-compatible API endpoint" +read_when: + - You want to use Claude Max subscription with OpenAI-compatible tools + - You want a local API server that wraps Claude Code CLI + - You want to save money by using subscription instead of API keys +--- +# Claude Max API Proxy + +**claude-max-api-proxy** is a community tool that exposes your Claude Max/Pro subscription as an OpenAI-compatible API endpoint. This allows you to use your subscription with any tool that supports the OpenAI API format. + +## Why Use This? + +| Approach | Cost | Best For | +|----------|------|----------| +| Anthropic API | Pay per token (~$15/M input, $75/M output for Opus) | Production apps, high volume | +| Claude Max subscription | $200/month flat | Personal use, development, unlimited usage | + +If you have a Claude Max subscription and want to use it with OpenAI-compatible tools, this proxy can save you significant money. + +## How It Works + +``` +Your App → claude-max-api-proxy → Claude Code CLI → Anthropic (via subscription) + (OpenAI format) (converts format) (uses your login) +``` + +The proxy: +1. Accepts OpenAI-format requests at `http://localhost:3456/v1/chat/completions` +2. Converts them to Claude Code CLI commands +3. Returns responses in OpenAI format (streaming supported) + +## Installation + +```bash +# Requires Node.js 20+ and Claude Code CLI +npm install -g claude-max-api-proxy + +# Verify Claude CLI is authenticated +claude --version +``` + +## Usage + +### Start the server + +```bash +claude-max-api +# Server runs at http://localhost:3456 +``` + +### Test it + +```bash +# Health check +curl http://localhost:3456/health + +# List models +curl http://localhost:3456/v1/models + +# Chat completion +curl http://localhost:3456/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "claude-opus-4", + "messages": [{"role": "user", "content": "Hello!"}] + }' +``` + +### With Clawdbot + +You can point Clawdbot at the proxy as a custom OpenAI-compatible endpoint: + +```json5 +{ + env: { + OPENAI_API_KEY: "not-needed", + OPENAI_BASE_URL: "http://localhost:3456/v1" + }, + agents: { + defaults: { + model: { primary: "openai/claude-opus-4" } + } + } +} +``` + +## Available Models + +| Model ID | Maps To | +|----------|---------| +| `claude-opus-4` | Claude Opus 4 | +| `claude-sonnet-4` | Claude Sonnet 4 | +| `claude-haiku-4` | Claude Haiku 4 | + +## Auto-Start on macOS + +Create a LaunchAgent to run the proxy automatically: + +```bash +cat > ~/Library/LaunchAgents/com.claude-max-api.plist << 'EOF' + + + + + Label + com.claude-max-api + RunAtLoad + + KeepAlive + + ProgramArguments + + /usr/local/bin/node + /usr/local/lib/node_modules/claude-max-api-proxy/dist/server/standalone.js + + EnvironmentVariables + + PATH + /usr/local/bin:/opt/homebrew/bin:~/.local/bin:/usr/bin:/bin + + + +EOF + +launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.claude-max-api.plist +``` + +## Links + +- **npm:** https://www.npmjs.com/package/claude-max-api-proxy +- **GitHub:** https://github.com/atalovesyou/claude-max-api-proxy +- **Issues:** https://github.com/atalovesyou/claude-max-api-proxy/issues + +## Notes + +- This is a **community tool**, not officially supported by Anthropic or Clawdbot +- Requires an active Claude Max/Pro subscription with Claude Code CLI authenticated +- The proxy runs locally and does not send data to any third-party servers +- Streaming responses are fully supported + +## See Also + +- [Anthropic provider](/providers/anthropic) - Native Clawdbot integration with Claude Code CLI OAuth +- [OpenAI provider](/providers/openai) - For OpenAI/Codex subscriptions diff --git a/docs/providers/index.md b/docs/providers/index.md index c4f02019292..b4779d20138 100644 --- a/docs/providers/index.md +++ b/docs/providers/index.md @@ -51,5 +51,9 @@ See [Venice AI](/providers/venice). - [Deepgram (audio transcription)](/providers/deepgram) +## Community tools + +- [Claude Max API Proxy](/providers/claude-max-api-proxy) - Use Claude Max/Pro subscription as an OpenAI-compatible API endpoint + For the full provider catalog (xAI, Groq, Mistral, etc.) and advanced configuration, see [Model providers](/concepts/model-providers). diff --git a/docs/providers/vercel-ai-gateway.md b/docs/providers/vercel-ai-gateway.md index bd31f0a87b9..36cf51cdacf 100644 --- a/docs/providers/vercel-ai-gateway.md +++ b/docs/providers/vercel-ai-gateway.md @@ -1,4 +1,5 @@ --- +title: "Vercel AI Gateway" summary: "Vercel AI Gateway setup (auth + model selection)" read_when: - You want to use Vercel AI Gateway with Clawdbot diff --git a/docs/railway.mdx b/docs/railway.mdx index 21e985f2ca4..b8f994a7d64 100644 --- a/docs/railway.mdx +++ b/docs/railway.mdx @@ -55,6 +55,7 @@ Attach a volume mounted at: Set these variables on the service: - `SETUP_PASSWORD` (required) +- `PORT=8080` (required — must match the port in Public Networking) - `CLAWDBOT_STATE_DIR=/data/.clawdbot` (recommended) - `CLAWDBOT_WORKSPACE_DIR=/data/workspace` (recommended) - `CLAWDBOT_GATEWAY_TOKEN` (recommended; treat as an admin secret) @@ -82,8 +83,9 @@ If Telegram DMs are set to pairing, the setup wizard can approve the pairing cod 1) Go to https://discord.com/developers/applications 2) **New Application** → choose a name 3) **Bot** → **Add Bot** -4) Copy the **Bot Token** and paste into `/setup` -5) Invite the bot to your server (OAuth2 URL Generator; scopes: `bot`, `applications.commands`) +4) **Enable MESSAGE CONTENT INTENT** under Bot → Privileged Gateway Intents (required or the bot will crash on startup) +5) Copy the **Bot Token** and paste into `/setup` +6) Invite the bot to your server (OAuth2 URL Generator; scopes: `bot`, `applications.commands`) ## Backups & migration diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md index 070abb1c34f..244757a4868 100644 --- a/docs/reference/RELEASING.md +++ b/docs/reference/RELEASING.md @@ -17,7 +17,7 @@ When the operator says “release”, immediately do this preflight (no extra qu - Use Sparkle keys from `~/Library/CloudStorage/Dropbox/Backup/Sparkle` if needed. 1) **Version & metadata** -- [ ] Bump `package.json` version (e.g., `1.1.0`). +- [ ] Bump `package.json` version (e.g., `2026.1.25`). - [ ] Run `pnpm plugins:sync` to align extension package versions + changelogs. - [ ] Update CLI/version strings: [`src/cli/program.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/cli/program.ts) and the Baileys user agent in [`src/provider-web.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/provider-web.ts). - [ ] Confirm package metadata (name, description, repository, keywords, license) and `bin` map points to [`dist/entry.js`](https://github.com/clawdbot/clawdbot/blob/main/dist/entry.js) for `clawdbot`. diff --git a/docs/render.mdx b/docs/render.mdx new file mode 100644 index 00000000000..3fcdae07aa3 --- /dev/null +++ b/docs/render.mdx @@ -0,0 +1,158 @@ +--- +title: Deploy on Render +--- + +Deploy Clawdbot on Render using Infrastructure as Code. The included `render.yaml` Blueprint defines your entire stack declaratively, service, disk, environment variables, so you can deploy with a single click and version your infrastructure alongside your code. + +## Prerequisites + +- A [Render account](https://render.com) (free tier available) +- An API key from your preferred [model provider](/providers) + +## Deploy with a Render Blueprint + +Deploy to Render + +Clicking this link will: + +1. Create a new Render service from the `render.yaml` Blueprint at the root of this repo. +2. Prompt you to set `SETUP_PASSWORD` +3. Build the Docker image and deploy + +Once deployed, your service URL follows the pattern `https://.onrender.com`. + +## Understanding the Blueprint + +Render Blueprints are YAML files that define your infrastructure. The `render.yaml` in this +repository configures everything needed to run Clawdbot: + +```yaml +services: + - type: web + name: clawdbot + runtime: docker + plan: starter + healthCheckPath: /health + envVars: + - key: PORT + value: "8080" + - key: SETUP_PASSWORD + sync: false # prompts during deploy + - key: CLAWDBOT_STATE_DIR + value: /data/.clawdbot + - key: CLAWDBOT_WORKSPACE_DIR + value: /data/workspace + - key: CLAWDBOT_GATEWAY_TOKEN + generateValue: true # auto-generates a secure token + disk: + name: clawdbot-data + mountPath: /data + sizeGB: 1 +``` + +Key Blueprint features used: + +| Feature | Purpose | +|---------|---------| +| `runtime: docker` | Builds from the repo's Dockerfile | +| `healthCheckPath` | Render monitors `/health` and restarts unhealthy instances | +| `sync: false` | Prompts for value during deploy (secrets) | +| `generateValue: true` | Auto-generates a cryptographically secure value | +| `disk` | Persistent storage that survives redeploys | + +## Choosing a plan + +| Plan | Spin-down | Disk | Best for | +|------|-----------|------|----------| +| Free | After 15 min idle | Not available | Testing, demos | +| Starter | Never | 1GB+ | Personal use, small teams | +| Standard+ | Never | 1GB+ | Production, multiple channels | + +The Blueprint defaults to `starter`. To use free tier, change `plan: free` in your fork's +`render.yaml` (but note: no persistent disk means config resets on each deploy). + +## After deployment + +### Complete the setup wizard + +1. Navigate to `https://.onrender.com/setup` +2. Enter your `SETUP_PASSWORD` +3. Select a model provider and paste your API key +4. Optionally configure messaging channels (Telegram, Discord, Slack) +5. Click **Run setup** + +### Access the Control UI + +The web dashboard is available at `https://.onrender.com/clawdbot`. + +## Render Dashboard features + +### Logs + +View real-time logs in **Dashboard → your service → Logs**. Filter by: +- Build logs (Docker image creation) +- Deploy logs (service startup) +- Runtime logs (application output) + +### Shell access + +For debugging, open a shell session via **Dashboard → your service → Shell**. The persistent disk is mounted at `/data`. + +### Environment variables + +Modify variables in **Dashboard → your service → Environment**. Changes trigger an automatic redeploy. + +### Auto-deploy + +If you use the original Clawdbot repository, Render will not auto-deploy your Clawdbot. To update it, run a manual Blueprint sync from the dashboard. + +## Custom domain + +1. Go to **Dashboard → your service → Settings → Custom Domains** +2. Add your domain +3. Configure DNS as instructed (CNAME to `*.onrender.com`) +4. Render provisions a TLS certificate automatically + +## Scaling + +Render supports horizontal and vertical scaling: + +- **Vertical**: Change the plan to get more CPU/RAM +- **Horizontal**: Increase instance count (Standard plan and above) + +For Clawdbot, vertical scaling is usually sufficient. Horizontal scaling requires sticky sessions or external state management. + +## Backups and migration + +Export your configuration and workspace at any time: + +``` +https://.onrender.com/setup/export +``` + +This downloads a portable backup you can restore on any Clawdbot host. + +## Troubleshooting + +### Service won't start + +Check the deploy logs in the Render Dashboard. Common issues: + +- Missing `SETUP_PASSWORD` — the Blueprint prompts for this, but verify it's set +- Port mismatch — ensure `PORT=8080` matches the Dockerfile's exposed port + +### Slow cold starts (free tier) + +Free tier services spin down after 15 minutes of inactivity. The first request after spin-down takes a few seconds while the container starts. Upgrade to Starter plan for always-on. + +### Data loss after redeploy + +This happens on free tier (no persistent disk). Upgrade to a paid plan, or +regularly export your config via `/setup/export`. + +### Health check failures + +Render expects a 200 response from `/health` within 30 seconds. If builds succeed but deploys fail, the service may be taking too long to start. Check: + +- Build logs for errors +- Whether the container runs locally with `docker build && docker run` diff --git a/docs/tools/creating-skills.md b/docs/tools/creating-skills.md new file mode 100644 index 00000000000..77e3415d2af --- /dev/null +++ b/docs/tools/creating-skills.md @@ -0,0 +1,41 @@ +# Creating Custom Skills 🛠 + +Clawdbot is designed to be easily extensible. "Skills" are the primary way to add new capabilities to your assistant. + +## What is a Skill? +A skill is a directory containing a `SKILL.md` file (which provides instructions and tool definitions to the LLM) and optionally some scripts or resources. + +## Step-by-Step: Your First Skill + +### 1. Create the Directory +Skills live in your workspace, usually `~/clawd/skills/`. Create a new folder for your skill: +```bash +mkdir -p ~/clawd/skills/hello-world +``` + +### 2. Define the `SKILL.md` +Create a `SKILL.md` file in that directory. This file uses YAML frontmatter for metadata and Markdown for instructions. + +```markdown +--- +name: hello_world +description: A simple skill that says hello. +--- + +# Hello World Skill +When the user asks for a greeting, use the `echo` tool to say "Hello from your custom skill!". +``` + +### 3. Add Tools (Optional) +You can define custom tools in the frontmatter or instruct the agent to use existing system tools (like `bash` or `browser`). + +### 4. Refresh Clawdbot +Ask your agent to "refresh skills" or restart the gateway. Clawdbot will discover the new directory and index the `SKILL.md`. + +## Best Practices +- **Be Concise**: Instruct the model on *what* to do, not how to be an AI. +- **Safety First**: If your skill uses `bash`, ensure the prompts don't allow arbitrary command injection from untrusted user input. +- **Test Locally**: Use `clawdbot agent --message "use my new skill"` to test. + +## Shared Skills +You can also browse and contribute skills to [ClawdHub](https://clawdhub.com). diff --git a/docs/vps.md b/docs/vps.md index a6d267513c4..d572059223c 100644 --- a/docs/vps.md +++ b/docs/vps.md @@ -1,5 +1,5 @@ --- -summary: "VPS hosting hub for Clawdbot (Railway/Fly/Hetzner/exe.dev)" +summary: "VPS hosting hub for Clawdbot (Fly/Hetzner/GCP/exe.dev)" read_when: - You want to run the Gateway in the cloud - You need a quick map of VPS/hosting guides @@ -11,9 +11,9 @@ deployments work at a high level. ## Pick a provider -- **Railway** (one‑click + browser setup): [Railway](/railway) - **Fly.io**: [Fly.io](/platforms/fly) - **Hetzner (Docker)**: [Hetzner](/platforms/hetzner) +- **GCP (Compute Engine)**: [GCP](/platforms/gcp) - **exe.dev** (VM + HTTPS proxy): [exe.dev](/platforms/exe-dev) - **AWS (EC2/Lightsail/free tier)**: works well too. Video guide: https://x.com/techfrenAJ/status/2014934471095812547 @@ -23,6 +23,8 @@ deployments work at a high level. - The **Gateway runs on the VPS** and owns state + workspace. - You connect from your laptop/phone via the **Control UI** or **Tailscale/SSH**. - Treat the VPS as the source of truth and **back up** the state + workspace. +- Secure default: keep the Gateway on loopback and access it via SSH tunnel or Tailscale Serve. + If you bind to `lan`/`tailnet`, require `gateway.auth.token` or `gateway.auth.password`. Remote access: [Gateway remote](/gateway/remote) Platforms hub: [Platforms](/platforms) diff --git a/docs/web/control-ui.md b/docs/web/control-ui.md index ede005259b1..996ed0fe475 100644 --- a/docs/web/control-ui.md +++ b/docs/web/control-ui.md @@ -70,10 +70,11 @@ Open: By default, Serve requests can authenticate via Tailscale identity headers (`tailscale-user-login`) when `gateway.auth.allowTailscale` is `true`. Clawdbot -only accepts these when the request hits loopback with Tailscale’s -`x-forwarded-*` headers. Set `gateway.auth.allowTailscale: false` (or force -`gateway.auth.mode: "password"`) if you want to require a token/password even -for Serve traffic. +verifies the identity by resolving the `x-forwarded-for` address with +`tailscale whois` and matching it to the header, and only accepts these when the +request hits loopback with Tailscale’s `x-forwarded-*` headers. Set +`gateway.auth.allowTailscale: false` (or force `gateway.auth.mode: "password"`) +if you want to require a token/password even for Serve traffic. ### Bind to tailnet + token @@ -108,8 +109,8 @@ Clawdbot **blocks** Control UI connections without device identity. } ``` -This disables device identity + pairing for the Control UI. Use only if you -trust the network. +This disables device identity + pairing for the Control UI (even on HTTPS). Use +only if you trust the network. See [Tailscale](/gateway/tailscale) for HTTPS setup guidance. diff --git a/docs/web/index.md b/docs/web/index.md index 82ca6220579..0e1fadfa497 100644 --- a/docs/web/index.md +++ b/docs/web/index.md @@ -91,7 +91,8 @@ Open: ## Security notes -- Binding the Gateway to a non-loopback address **requires** auth (`gateway.auth` or `CLAWDBOT_GATEWAY_TOKEN`). +- Gateway auth is required by default (token/password or Tailscale identity headers). +- Non-loopback binds still **require** a shared token/password (`gateway.auth` or env). - The wizard generates a gateway token by default (even on loopback). - The UI sends `connect.params.auth.token` or `connect.params.auth.password`. - With Serve, Tailscale identity headers can satisfy auth when diff --git a/docs/web/webchat.md b/docs/web/webchat.md index 2abfa67eadc..3c968e0fc64 100644 --- a/docs/web/webchat.md +++ b/docs/web/webchat.md @@ -16,7 +16,7 @@ Status: the macOS/iOS SwiftUI chat UI talks directly to the Gateway WebSocket. ## Quick start 1) Start the gateway. 2) Open the WebChat UI (macOS/iOS app) or the Control UI chat tab. -3) Ensure gateway auth is configured if you are not on loopback. +3) Ensure gateway auth is configured (required by default, even on loopback). ## How it works (behavior) - The UI connects to the Gateway WebSocket and uses `chat.history`, `chat.send`, and `chat.inject`. diff --git a/extensions/bluebubbles/package.json b/extensions/bluebubbles/package.json index 4385272beb0..7d82036a038 100644 --- a/extensions/bluebubbles/package.json +++ b/extensions/bluebubbles/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/bluebubbles", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot BlueBubbles channel plugin", "clawdbot": { diff --git a/extensions/bluebubbles/src/channel.ts b/extensions/bluebubbles/src/channel.ts index 126a7313187..88b8e5a2a48 100644 --- a/extensions/bluebubbles/src/channel.ts +++ b/extensions/bluebubbles/src/channel.ts @@ -25,9 +25,11 @@ import { resolveBlueBubblesMessageId } from "./monitor.js"; import { probeBlueBubbles, type BlueBubblesProbe } from "./probe.js"; import { sendMessageBlueBubbles } from "./send.js"; import { + extractHandleFromChatGuid, looksLikeBlueBubblesTargetId, normalizeBlueBubblesHandle, normalizeBlueBubblesMessagingTarget, + parseBlueBubblesTarget, } from "./targets.js"; import { bluebubblesMessageActions } from "./actions.js"; import { monitorBlueBubblesProvider, resolveWebhookPathFromConfig } from "./monitor.js"; @@ -148,6 +150,58 @@ export const bluebubblesPlugin: ChannelPlugin = { looksLikeId: looksLikeBlueBubblesTargetId, hint: "", }, + formatTargetDisplay: ({ target, display }) => { + const shouldParseDisplay = (value: string): boolean => { + if (looksLikeBlueBubblesTargetId(value)) return true; + return /^(bluebubbles:|chat_guid:|chat_id:|chat_identifier:)/i.test(value); + }; + + // Helper to extract a clean handle from any BlueBubbles target format + const extractCleanDisplay = (value: string | undefined): string | null => { + const trimmed = value?.trim(); + if (!trimmed) return null; + try { + const parsed = parseBlueBubblesTarget(trimmed); + if (parsed.kind === "chat_guid") { + const handle = extractHandleFromChatGuid(parsed.chatGuid); + if (handle) return handle; + } + if (parsed.kind === "handle") { + return normalizeBlueBubblesHandle(parsed.to); + } + } catch { + // Fall through + } + // Strip common prefixes and try raw extraction + const stripped = trimmed + .replace(/^bluebubbles:/i, "") + .replace(/^chat_guid:/i, "") + .replace(/^chat_id:/i, "") + .replace(/^chat_identifier:/i, ""); + const handle = extractHandleFromChatGuid(stripped); + if (handle) return handle; + // Don't return raw chat_guid formats - they contain internal routing info + if (stripped.includes(";-;") || stripped.includes(";+;")) return null; + return stripped; + }; + + // Try to get a clean display from the display parameter first + const trimmedDisplay = display?.trim(); + if (trimmedDisplay) { + if (!shouldParseDisplay(trimmedDisplay)) { + return trimmedDisplay; + } + const cleanDisplay = extractCleanDisplay(trimmedDisplay); + if (cleanDisplay) return cleanDisplay; + } + + // Fall back to extracting from target + const cleanTarget = extractCleanDisplay(target); + if (cleanTarget) return cleanTarget; + + // Last resort: return display or target as-is + return display?.trim() || target?.trim() || ""; + }, }, setup: { resolveAccountId: ({ accountId }) => normalizeAccountId(accountId), diff --git a/extensions/bluebubbles/src/send.test.ts b/extensions/bluebubbles/src/send.test.ts index 0b8b77a1fb0..84aa0ebf2b3 100644 --- a/extensions/bluebubbles/src/send.test.ts +++ b/extensions/bluebubbles/src/send.test.ts @@ -187,6 +187,47 @@ describe("send", () => { expect(result).toBe("iMessage;-;+15551234567"); }); + it("returns null when handle only exists in group chat (not DM)", async () => { + // This is the critical fix: if a phone number only exists as a participant in a group chat + // (no direct DM chat), we should NOT send to that group. Return null instead. + mockFetch + .mockResolvedValueOnce({ + ok: true, + json: () => + Promise.resolve({ + data: [ + { + guid: "iMessage;+;group-the-council", + participants: [ + { address: "+12622102921" }, + { address: "+15550001111" }, + { address: "+15550002222" }, + ], + }, + ], + }), + }) + // Empty second page to stop pagination + .mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ data: [] }), + }); + + const target: BlueBubblesSendTarget = { + kind: "handle", + address: "+12622102921", + service: "imessage", + }; + const result = await resolveChatGuidForTarget({ + baseUrl: "http://localhost:1234", + password: "test", + target, + }); + + // Should return null, NOT the group chat GUID + expect(result).toBeNull(); + }); + it("returns null when chat not found", async () => { mockFetch.mockResolvedValueOnce({ ok: true, @@ -344,14 +385,14 @@ describe("send", () => { ).rejects.toThrow("password is required"); }); - it("throws when chatGuid cannot be resolved", async () => { + it("throws when chatGuid cannot be resolved for non-handle targets", async () => { mockFetch.mockResolvedValue({ ok: true, json: () => Promise.resolve({ data: [] }), }); await expect( - sendMessageBlueBubbles("+15559999999", "Hello", { + sendMessageBlueBubbles("chat_id:999", "Hello", { serverUrl: "http://localhost:1234", password: "test", }), @@ -398,6 +439,57 @@ describe("send", () => { expect(body.method).toBeUndefined(); }); + it("creates a new chat when handle target is missing", async () => { + mockFetch + .mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ data: [] }), + }) + .mockResolvedValueOnce({ + ok: true, + text: () => + Promise.resolve( + JSON.stringify({ + data: { guid: "new-msg-guid" }, + }), + ), + }); + + const result = await sendMessageBlueBubbles("+15550009999", "Hello new chat", { + serverUrl: "http://localhost:1234", + password: "test", + }); + + expect(result.messageId).toBe("new-msg-guid"); + expect(mockFetch).toHaveBeenCalledTimes(2); + + const createCall = mockFetch.mock.calls[1]; + expect(createCall[0]).toContain("/api/v1/chat/new"); + const body = JSON.parse(createCall[1].body); + expect(body.addresses).toEqual(["+15550009999"]); + expect(body.message).toBe("Hello new chat"); + }); + + it("throws when creating a new chat requires Private API", async () => { + mockFetch + .mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ data: [] }), + }) + .mockResolvedValueOnce({ + ok: false, + status: 403, + text: () => Promise.resolve("Private API not enabled"), + }); + + await expect( + sendMessageBlueBubbles("+15550008888", "Hello", { + serverUrl: "http://localhost:1234", + password: "test", + }), + ).rejects.toThrow("Private API must be enabled"); + }); + it("uses private-api when reply metadata is present", async () => { mockFetch .mockResolvedValueOnce({ diff --git a/extensions/bluebubbles/src/send.ts b/extensions/bluebubbles/src/send.ts index 675063d6deb..bc4bb14bd73 100644 --- a/extensions/bluebubbles/src/send.ts +++ b/extensions/bluebubbles/src/send.ts @@ -257,11 +257,17 @@ export async function resolveChatGuidForTarget(params: { return guid; } if (!participantMatch && guid) { - const participants = extractParticipantAddresses(chat).map((entry) => - normalizeBlueBubblesHandle(entry), - ); - if (participants.includes(normalizedHandle)) { - participantMatch = guid; + // Only consider DM chats (`;-;` separator) as participant matches. + // Group chats (`;+;` separator) should never match when searching by handle/phone. + // This prevents routing "send to +1234567890" to a group chat that contains that number. + const isDmChat = guid.includes(";-;"); + if (isDmChat) { + const participants = extractParticipantAddresses(chat).map((entry) => + normalizeBlueBubblesHandle(entry), + ); + if (participants.includes(normalizedHandle)) { + participantMatch = guid; + } } } } @@ -270,6 +276,55 @@ export async function resolveChatGuidForTarget(params: { return participantMatch; } +/** + * Creates a new chat (DM) and optionally sends an initial message. + * Requires Private API to be enabled in BlueBubbles. + */ +async function createNewChatWithMessage(params: { + baseUrl: string; + password: string; + address: string; + message: string; + timeoutMs?: number; +}): Promise { + const url = buildBlueBubblesApiUrl({ + baseUrl: params.baseUrl, + path: "/api/v1/chat/new", + password: params.password, + }); + const payload = { + addresses: [params.address], + message: params.message, + }; + const res = await blueBubblesFetchWithTimeout( + url, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }, + params.timeoutMs, + ); + if (!res.ok) { + const errorText = await res.text(); + // Check for Private API not enabled error + if (res.status === 400 || res.status === 403 || errorText.toLowerCase().includes("private api")) { + throw new Error( + `BlueBubbles send failed: Cannot create new chat - Private API must be enabled. Original error: ${errorText || res.status}`, + ); + } + throw new Error(`BlueBubbles create chat failed (${res.status}): ${errorText || "unknown"}`); + } + const body = await res.text(); + if (!body) return { messageId: "ok" }; + try { + const parsed = JSON.parse(body) as unknown; + return { messageId: extractMessageId(parsed) }; + } catch { + return { messageId: "ok" }; + } +} + export async function sendMessageBlueBubbles( to: string, text: string, @@ -297,6 +352,17 @@ export async function sendMessageBlueBubbles( target, }); if (!chatGuid) { + // If target is a phone number/handle and no existing chat found, + // auto-create a new DM chat using the /api/v1/chat/new endpoint + if (target.kind === "handle") { + return createNewChatWithMessage({ + baseUrl, + password, + address: target.address, + message: trimmedText, + timeoutMs: opts.timeoutMs, + }); + } throw new Error( "BlueBubbles send failed: chatGuid not found for target. Use a chat_guid target or ensure the chat exists.", ); diff --git a/extensions/copilot-proxy/package.json b/extensions/copilot-proxy/package.json index 02d1cdbddbf..2a9a63c71fc 100644 --- a/extensions/copilot-proxy/package.json +++ b/extensions/copilot-proxy/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/copilot-proxy", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Copilot Proxy provider plugin", "clawdbot": { diff --git a/extensions/diagnostics-otel/package.json b/extensions/diagnostics-otel/package.json index 407ce60d115..65a6bf0cd43 100644 --- a/extensions/diagnostics-otel/package.json +++ b/extensions/diagnostics-otel/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/diagnostics-otel", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot diagnostics OpenTelemetry exporter", "clawdbot": { diff --git a/extensions/discord/package.json b/extensions/discord/package.json index 0a645718b5e..90a99d4d32a 100644 --- a/extensions/discord/package.json +++ b/extensions/discord/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/discord", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Discord channel plugin", "clawdbot": { diff --git a/extensions/google-antigravity-auth/index.ts b/extensions/google-antigravity-auth/index.ts index d6902bffe64..f349ada6af9 100644 --- a/extensions/google-antigravity-auth/index.ts +++ b/extensions/google-antigravity-auth/index.ts @@ -281,6 +281,7 @@ async function loginAntigravity(params: { openUrl: (url: string) => Promise; prompt: (message: string) => Promise; note: (message: string, title?: string) => Promise; + log: (message: string) => void; progress: { update: (msg: string) => void; stop: (msg?: string) => void }; }): Promise<{ access: string; @@ -314,6 +315,11 @@ async function loginAntigravity(params: { ].join("\n"), "Google Antigravity OAuth", ); + // Output raw URL below the box for easy copying (fixes #1772) + params.log(""); + params.log("Copy this URL:"); + params.log(authUrl); + params.log(""); } if (!needsManual) { @@ -382,6 +388,7 @@ const antigravityPlugin = { openUrl: ctx.openUrl, prompt: async (message) => String(await ctx.prompter.text({ message })), note: ctx.prompter.note, + log: (message) => ctx.runtime.log(message), progress: spin, }); diff --git a/extensions/google-antigravity-auth/package.json b/extensions/google-antigravity-auth/package.json index ff3c485f2b9..f1d8f86bd2f 100644 --- a/extensions/google-antigravity-auth/package.json +++ b/extensions/google-antigravity-auth/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/google-antigravity-auth", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Google Antigravity OAuth provider plugin", "clawdbot": { diff --git a/extensions/google-gemini-cli-auth/README.md b/extensions/google-gemini-cli-auth/README.md index 6e4bdbd2b44..99eab057f62 100644 --- a/extensions/google-gemini-cli-auth/README.md +++ b/extensions/google-gemini-cli-auth/README.md @@ -18,7 +18,18 @@ Restart the Gateway after enabling. clawdbot models auth login --provider google-gemini-cli --set-default ``` -## Env vars +## Requirements + +Requires the Gemini CLI to be installed (credentials are extracted automatically): + +```bash +brew install gemini-cli +# or: npm install -g @google/gemini-cli +``` + +## Env vars (optional) + +Override auto-detected credentials with: - `CLAWDBOT_GEMINI_OAUTH_CLIENT_ID` / `GEMINI_CLI_OAUTH_CLIENT_ID` - `CLAWDBOT_GEMINI_OAUTH_CLIENT_SECRET` / `GEMINI_CLI_OAUTH_CLIENT_SECRET` diff --git a/extensions/google-gemini-cli-auth/oauth.test.ts b/extensions/google-gemini-cli-auth/oauth.test.ts new file mode 100644 index 00000000000..a6ee8ee98fd --- /dev/null +++ b/extensions/google-gemini-cli-auth/oauth.test.ts @@ -0,0 +1,228 @@ +import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"; +import { join, parse } from "node:path"; + +// Mock fs module before importing the module under test +const mockExistsSync = vi.fn(); +const mockReadFileSync = vi.fn(); +const mockRealpathSync = vi.fn(); +const mockReaddirSync = vi.fn(); + +vi.mock("node:fs", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + existsSync: (...args: Parameters) => mockExistsSync(...args), + readFileSync: (...args: Parameters) => mockReadFileSync(...args), + realpathSync: (...args: Parameters) => mockRealpathSync(...args), + readdirSync: (...args: Parameters) => mockReaddirSync(...args), + }; +}); + +describe("extractGeminiCliCredentials", () => { + const normalizePath = (value: string) => + value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase(); + const rootDir = parse(process.cwd()).root || "/"; + const FAKE_CLIENT_ID = "123456789-abcdef.apps.googleusercontent.com"; + const FAKE_CLIENT_SECRET = "GOCSPX-FakeSecretValue123"; + const FAKE_OAUTH2_CONTENT = ` + const clientId = "${FAKE_CLIENT_ID}"; + const clientSecret = "${FAKE_CLIENT_SECRET}"; + `; + + let originalPath: string | undefined; + + beforeEach(async () => { + vi.resetModules(); + vi.clearAllMocks(); + originalPath = process.env.PATH; + }); + + afterEach(() => { + process.env.PATH = originalPath; + }); + + it("returns null when gemini binary is not in PATH", async () => { + process.env.PATH = "/nonexistent"; + mockExistsSync.mockReturnValue(false); + + const { extractGeminiCliCredentials, clearCredentialsCache } = await import("./oauth.js"); + clearCredentialsCache(); + expect(extractGeminiCliCredentials()).toBeNull(); + }); + + it("extracts credentials from oauth2.js in known path", async () => { + const fakeBinDir = join(rootDir, "fake", "bin"); + const fakeGeminiPath = join(fakeBinDir, "gemini"); + const fakeResolvedPath = join( + rootDir, + "fake", + "lib", + "node_modules", + "@google", + "gemini-cli", + "dist", + "index.js", + ); + const fakeOauth2Path = join( + rootDir, + "fake", + "lib", + "node_modules", + "@google", + "gemini-cli", + "node_modules", + "@google", + "gemini-cli-core", + "dist", + "src", + "code_assist", + "oauth2.js", + ); + + process.env.PATH = fakeBinDir; + + mockExistsSync.mockImplementation((p: string) => { + const normalized = normalizePath(p); + if (normalized === normalizePath(fakeGeminiPath)) return true; + if (normalized === normalizePath(fakeOauth2Path)) return true; + return false; + }); + mockRealpathSync.mockReturnValue(fakeResolvedPath); + mockReadFileSync.mockReturnValue(FAKE_OAUTH2_CONTENT); + + const { extractGeminiCliCredentials, clearCredentialsCache } = await import("./oauth.js"); + clearCredentialsCache(); + const result = extractGeminiCliCredentials(); + + expect(result).toEqual({ + clientId: FAKE_CLIENT_ID, + clientSecret: FAKE_CLIENT_SECRET, + }); + }); + + it("returns null when oauth2.js cannot be found", async () => { + const fakeBinDir = join(rootDir, "fake", "bin"); + const fakeGeminiPath = join(fakeBinDir, "gemini"); + const fakeResolvedPath = join( + rootDir, + "fake", + "lib", + "node_modules", + "@google", + "gemini-cli", + "dist", + "index.js", + ); + + process.env.PATH = fakeBinDir; + + mockExistsSync.mockImplementation( + (p: string) => normalizePath(p) === normalizePath(fakeGeminiPath), + ); + mockRealpathSync.mockReturnValue(fakeResolvedPath); + mockReaddirSync.mockReturnValue([]); // Empty directory for recursive search + + const { extractGeminiCliCredentials, clearCredentialsCache } = await import("./oauth.js"); + clearCredentialsCache(); + expect(extractGeminiCliCredentials()).toBeNull(); + }); + + it("returns null when oauth2.js lacks credentials", async () => { + const fakeBinDir = join(rootDir, "fake", "bin"); + const fakeGeminiPath = join(fakeBinDir, "gemini"); + const fakeResolvedPath = join( + rootDir, + "fake", + "lib", + "node_modules", + "@google", + "gemini-cli", + "dist", + "index.js", + ); + const fakeOauth2Path = join( + rootDir, + "fake", + "lib", + "node_modules", + "@google", + "gemini-cli", + "node_modules", + "@google", + "gemini-cli-core", + "dist", + "src", + "code_assist", + "oauth2.js", + ); + + process.env.PATH = fakeBinDir; + + mockExistsSync.mockImplementation((p: string) => { + const normalized = normalizePath(p); + if (normalized === normalizePath(fakeGeminiPath)) return true; + if (normalized === normalizePath(fakeOauth2Path)) return true; + return false; + }); + mockRealpathSync.mockReturnValue(fakeResolvedPath); + mockReadFileSync.mockReturnValue("// no credentials here"); + + const { extractGeminiCliCredentials, clearCredentialsCache } = await import("./oauth.js"); + clearCredentialsCache(); + expect(extractGeminiCliCredentials()).toBeNull(); + }); + + it("caches credentials after first extraction", async () => { + const fakeBinDir = join(rootDir, "fake", "bin"); + const fakeGeminiPath = join(fakeBinDir, "gemini"); + const fakeResolvedPath = join( + rootDir, + "fake", + "lib", + "node_modules", + "@google", + "gemini-cli", + "dist", + "index.js", + ); + const fakeOauth2Path = join( + rootDir, + "fake", + "lib", + "node_modules", + "@google", + "gemini-cli", + "node_modules", + "@google", + "gemini-cli-core", + "dist", + "src", + "code_assist", + "oauth2.js", + ); + + process.env.PATH = fakeBinDir; + + mockExistsSync.mockImplementation((p: string) => { + const normalized = normalizePath(p); + if (normalized === normalizePath(fakeGeminiPath)) return true; + if (normalized === normalizePath(fakeOauth2Path)) return true; + return false; + }); + mockRealpathSync.mockReturnValue(fakeResolvedPath); + mockReadFileSync.mockReturnValue(FAKE_OAUTH2_CONTENT); + + const { extractGeminiCliCredentials, clearCredentialsCache } = await import("./oauth.js"); + clearCredentialsCache(); + + // First call + const result1 = extractGeminiCliCredentials(); + expect(result1).not.toBeNull(); + + // Second call should use cache (readFileSync not called again) + const readCount = mockReadFileSync.mock.calls.length; + const result2 = extractGeminiCliCredentials(); + expect(result2).toEqual(result1); + expect(mockReadFileSync.mock.calls.length).toBe(readCount); + }); +}); diff --git a/extensions/google-gemini-cli-auth/oauth.ts b/extensions/google-gemini-cli-auth/oauth.ts index 0fc68aa5a93..405b94641b5 100644 --- a/extensions/google-gemini-cli-auth/oauth.ts +++ b/extensions/google-gemini-cli-auth/oauth.ts @@ -1,6 +1,7 @@ import { createHash, randomBytes } from "node:crypto"; -import { readFileSync } from "node:fs"; +import { existsSync, readFileSync, readdirSync, realpathSync } from "node:fs"; import { createServer } from "node:http"; +import { delimiter, dirname, join } from "node:path"; const CLIENT_ID_KEYS = ["CLAWDBOT_GEMINI_OAUTH_CLIENT_ID", "GEMINI_CLI_OAUTH_CLIENT_ID"]; const CLIENT_SECRET_KEYS = [ @@ -47,15 +48,98 @@ function resolveEnv(keys: string[]): string | undefined { return undefined; } -function resolveOAuthClientConfig(): { clientId: string; clientSecret?: string } { - const clientId = resolveEnv(CLIENT_ID_KEYS); - if (!clientId) { - throw new Error( - "Missing Gemini OAuth client ID. Set CLAWDBOT_GEMINI_OAUTH_CLIENT_ID (or GEMINI_CLI_OAUTH_CLIENT_ID).", - ); +let cachedGeminiCliCredentials: { clientId: string; clientSecret: string } | null = null; + +/** @internal */ +export function clearCredentialsCache(): void { + cachedGeminiCliCredentials = null; +} + +/** Extracts OAuth credentials from the installed Gemini CLI's bundled oauth2.js. */ +export function extractGeminiCliCredentials(): { clientId: string; clientSecret: string } | null { + if (cachedGeminiCliCredentials) return cachedGeminiCliCredentials; + + try { + const geminiPath = findInPath("gemini"); + if (!geminiPath) return null; + + const resolvedPath = realpathSync(geminiPath); + const geminiCliDir = dirname(dirname(resolvedPath)); + + const searchPaths = [ + join(geminiCliDir, "node_modules", "@google", "gemini-cli-core", "dist", "src", "code_assist", "oauth2.js"), + join(geminiCliDir, "node_modules", "@google", "gemini-cli-core", "dist", "code_assist", "oauth2.js"), + ]; + + let content: string | null = null; + for (const p of searchPaths) { + if (existsSync(p)) { + content = readFileSync(p, "utf8"); + break; + } + } + if (!content) { + const found = findFile(geminiCliDir, "oauth2.js", 10); + if (found) content = readFileSync(found, "utf8"); + } + if (!content) return null; + + const idMatch = content.match(/(\d+-[a-z0-9]+\.apps\.googleusercontent\.com)/); + const secretMatch = content.match(/(GOCSPX-[A-Za-z0-9_-]+)/); + if (idMatch && secretMatch) { + cachedGeminiCliCredentials = { clientId: idMatch[1], clientSecret: secretMatch[1] }; + return cachedGeminiCliCredentials; + } + } catch { + // Gemini CLI not installed or extraction failed } - const clientSecret = resolveEnv(CLIENT_SECRET_KEYS); - return { clientId, clientSecret }; + return null; +} + +function findInPath(name: string): string | null { + const exts = process.platform === "win32" ? [".cmd", ".bat", ".exe", ""] : [""]; + for (const dir of (process.env.PATH ?? "").split(delimiter)) { + for (const ext of exts) { + const p = join(dir, name + ext); + if (existsSync(p)) return p; + } + } + return null; +} + +function findFile(dir: string, name: string, depth: number): string | null { + if (depth <= 0) return null; + try { + for (const e of readdirSync(dir, { withFileTypes: true })) { + const p = join(dir, e.name); + if (e.isFile() && e.name === name) return p; + if (e.isDirectory() && !e.name.startsWith(".")) { + const found = findFile(p, name, depth - 1); + if (found) return found; + } + } + } catch {} + return null; +} + +function resolveOAuthClientConfig(): { clientId: string; clientSecret?: string } { + // 1. Check env vars first (user override) + const envClientId = resolveEnv(CLIENT_ID_KEYS); + const envClientSecret = resolveEnv(CLIENT_SECRET_KEYS); + if (envClientId) { + return { clientId: envClientId, clientSecret: envClientSecret }; + } + + // 2. Try to extract from installed Gemini CLI + const extracted = extractGeminiCliCredentials(); + if (extracted) { + return extracted; + } + + // 3. No credentials available + throw new Error( + "Gemini CLI not found. Install it first: brew install gemini-cli (or npm install -g @google/gemini-cli), or set GEMINI_CLI_OAUTH_CLIENT_ID.", + ); } function isWSL(): boolean { diff --git a/extensions/google-gemini-cli-auth/package.json b/extensions/google-gemini-cli-auth/package.json index f4b666ab067..7e3fef15b2c 100644 --- a/extensions/google-gemini-cli-auth/package.json +++ b/extensions/google-gemini-cli-auth/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/google-gemini-cli-auth", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Gemini CLI OAuth provider plugin", "clawdbot": { diff --git a/extensions/googlechat/package.json b/extensions/googlechat/package.json index cf73b679500..af1ccf8e1c7 100644 --- a/extensions/googlechat/package.json +++ b/extensions/googlechat/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/googlechat", - "version": "2026.1.22", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Google Chat channel plugin", "clawdbot": { @@ -34,6 +34,6 @@ "clawdbot": "workspace:*" }, "peerDependencies": { - "clawdbot": ">=2026.1.24-0" + "clawdbot": ">=2026.1.25" } } diff --git a/extensions/imessage/package.json b/extensions/imessage/package.json index a3ac1c64272..944ad06bfb4 100644 --- a/extensions/imessage/package.json +++ b/extensions/imessage/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/imessage", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot iMessage channel plugin", "clawdbot": { diff --git a/extensions/imessage/src/channel.ts b/extensions/imessage/src/channel.ts index 50615cd222f..556c2970a89 100644 --- a/extensions/imessage/src/channel.ts +++ b/extensions/imessage/src/channel.ts @@ -8,8 +8,10 @@ import { imessageOnboardingAdapter, IMessageConfigSchema, listIMessageAccountIds, + looksLikeIMessageTargetId, migrateBaseNameToDefaultAccount, normalizeAccountId, + normalizeIMessageMessagingTarget, PAIRING_APPROVED_MESSAGE, resolveChannelMediaMaxBytes, resolveDefaultIMessageAccountId, @@ -110,14 +112,9 @@ export const imessagePlugin: ChannelPlugin = { resolveToolPolicy: resolveIMessageGroupToolPolicy, }, messaging: { + normalizeTarget: normalizeIMessageMessagingTarget, targetResolver: { - looksLikeId: (raw) => { - const trimmed = raw.trim(); - if (!trimmed) return false; - if (/^(imessage:|chat_id:)/i.test(trimmed)) return true; - if (trimmed.includes("@")) return true; - return /^\+?\d{3,}$/.test(trimmed); - }, + looksLikeId: looksLikeIMessageTargetId, hint: "", }, }, diff --git a/extensions/line/clawdbot.plugin.json b/extensions/line/clawdbot.plugin.json new file mode 100644 index 00000000000..49f2bad100e --- /dev/null +++ b/extensions/line/clawdbot.plugin.json @@ -0,0 +1,11 @@ +{ + "id": "line", + "channels": [ + "line" + ], + "configSchema": { + "type": "object", + "additionalProperties": false, + "properties": {} + } +} diff --git a/extensions/line/index.ts b/extensions/line/index.ts new file mode 100644 index 00000000000..698c2d3a29b --- /dev/null +++ b/extensions/line/index.ts @@ -0,0 +1,20 @@ +import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk"; +import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk"; + +import { linePlugin } from "./src/channel.js"; +import { registerLineCardCommand } from "./src/card-command.js"; +import { setLineRuntime } from "./src/runtime.js"; + +const plugin = { + id: "line", + name: "LINE", + description: "LINE Messaging API channel plugin", + configSchema: emptyPluginConfigSchema(), + register(api: ClawdbotPluginApi) { + setLineRuntime(api.runtime); + api.registerChannel({ plugin: linePlugin }); + registerLineCardCommand(api); + }, +}; + +export default plugin; diff --git a/extensions/line/package.json b/extensions/line/package.json new file mode 100644 index 00000000000..346d664153e --- /dev/null +++ b/extensions/line/package.json @@ -0,0 +1,29 @@ +{ + "name": "@clawdbot/line", + "version": "2026.1.25", + "type": "module", + "description": "Clawdbot LINE channel plugin", + "clawdbot": { + "extensions": [ + "./index.ts" + ], + "channel": { + "id": "line", + "label": "LINE", + "selectionLabel": "LINE (Messaging API)", + "docsPath": "/channels/line", + "docsLabel": "line", + "blurb": "LINE Messaging API bot for Japan/Taiwan/Thailand markets.", + "order": 75, + "quickstartAllowFrom": true + }, + "install": { + "npmSpec": "@clawdbot/line", + "localPath": "extensions/line", + "defaultChoice": "npm" + } + }, + "devDependencies": { + "clawdbot": "workspace:*" + } +} diff --git a/extensions/line/src/card-command.ts b/extensions/line/src/card-command.ts new file mode 100644 index 00000000000..762faa012cb --- /dev/null +++ b/extensions/line/src/card-command.ts @@ -0,0 +1,338 @@ +import type { ClawdbotPluginApi, LineChannelData, ReplyPayload } from "clawdbot/plugin-sdk"; +import { + createActionCard, + createImageCard, + createInfoCard, + createListCard, + createReceiptCard, + type CardAction, + type ListItem, +} from "clawdbot/plugin-sdk"; + +const CARD_USAGE = `Usage: /card "title" "body" [options] + +Types: + info "Title" "Body" ["Footer"] + image "Title" "Caption" --url + action "Title" "Body" --actions "Btn1|url1,Btn2|text2" + list "Title" "Item1|Desc1,Item2|Desc2" + receipt "Title" "Item1:$10,Item2:$20" --total "$30" + confirm "Question?" --yes "Yes|data" --no "No|data" + buttons "Title" "Text" --actions "Btn1|url1,Btn2|data2" + +Examples: + /card info "Welcome" "Thanks for joining!" + /card image "Product" "Check it out" --url https://example.com/img.jpg + /card action "Menu" "Choose an option" --actions "Order|/order,Help|/help"`; + +function buildLineReply(lineData: LineChannelData): ReplyPayload { + return { + channelData: { + line: lineData, + }, + }; +} + +/** + * Parse action string format: "Label|data,Label2|data2" + * Data can be a URL (uri action) or plain text (message action) or key=value (postback) + */ +function parseActions(actionsStr: string | undefined): CardAction[] { + if (!actionsStr) return []; + + const results: CardAction[] = []; + + for (const part of actionsStr.split(",")) { + const [label, data] = part + .trim() + .split("|") + .map((s) => s.trim()); + if (!label) continue; + + const actionData = data || label; + + if (actionData.startsWith("http://") || actionData.startsWith("https://")) { + results.push({ + label, + action: { type: "uri", label: label.slice(0, 20), uri: actionData }, + }); + } else if (actionData.includes("=")) { + results.push({ + label, + action: { + type: "postback", + label: label.slice(0, 20), + data: actionData.slice(0, 300), + displayText: label, + }, + }); + } else { + results.push({ + label, + action: { type: "message", label: label.slice(0, 20), text: actionData }, + }); + } + } + + return results; +} + +/** + * Parse list items format: "Item1|Subtitle1,Item2|Subtitle2" + */ +function parseListItems(itemsStr: string): ListItem[] { + return itemsStr + .split(",") + .map((part) => { + const [title, subtitle] = part + .trim() + .split("|") + .map((s) => s.trim()); + return { title: title || "", subtitle }; + }) + .filter((item) => item.title); +} + +/** + * Parse receipt items format: "Item1:$10,Item2:$20" + */ +function parseReceiptItems(itemsStr: string): Array<{ name: string; value: string }> { + return itemsStr + .split(",") + .map((part) => { + const colonIndex = part.lastIndexOf(":"); + if (colonIndex === -1) { + return { name: part.trim(), value: "" }; + } + return { + name: part.slice(0, colonIndex).trim(), + value: part.slice(colonIndex + 1).trim(), + }; + }) + .filter((item) => item.name); +} + +/** + * Parse quoted arguments from command string + * Supports: /card type "arg1" "arg2" "arg3" --flag value + */ +function parseCardArgs(argsStr: string): { + type: string; + args: string[]; + flags: Record; +} { + const result: { type: string; args: string[]; flags: Record } = { + type: "", + args: [], + flags: {}, + }; + + // Extract type (first word) + const typeMatch = argsStr.match(/^(\w+)/); + if (typeMatch) { + result.type = typeMatch[1].toLowerCase(); + argsStr = argsStr.slice(typeMatch[0].length).trim(); + } + + // Extract quoted arguments + const quotedRegex = /"([^"]*?)"/g; + let match; + while ((match = quotedRegex.exec(argsStr)) !== null) { + result.args.push(match[1]); + } + + // Extract flags (--key value or --key "value") + const flagRegex = /--(\w+)\s+(?:"([^"]*?)"|(\S+))/g; + while ((match = flagRegex.exec(argsStr)) !== null) { + result.flags[match[1]] = match[2] ?? match[3]; + } + + return result; +} + +export function registerLineCardCommand(api: ClawdbotPluginApi): void { + api.registerCommand({ + name: "card", + description: "Send a rich card message (LINE).", + acceptsArgs: true, + requireAuth: false, + handler: async (ctx) => { + const argsStr = ctx.args?.trim() ?? ""; + if (!argsStr) return { text: CARD_USAGE }; + + const parsed = parseCardArgs(argsStr); + const { type, args, flags } = parsed; + + if (!type) return { text: CARD_USAGE }; + + // Only LINE supports rich cards; fallback to text elsewhere. + if (ctx.channel !== "line") { + const fallbackText = args.join(" - "); + return { text: `[${type} card] ${fallbackText}`.trim() }; + } + + try { + switch (type) { + case "info": { + const [title = "Info", body = "", footer] = args; + const bubble = createInfoCard(title, body, footer); + return buildLineReply({ + flexMessage: { + altText: `${title}: ${body}`.slice(0, 400), + contents: bubble, + }, + }); + } + + case "image": { + const [title = "Image", caption = ""] = args; + const imageUrl = flags.url || flags.image; + if (!imageUrl) { + return { text: "Error: Image card requires --url " }; + } + const bubble = createImageCard(imageUrl, title, caption); + return buildLineReply({ + flexMessage: { + altText: `${title}: ${caption}`.slice(0, 400), + contents: bubble, + }, + }); + } + + case "action": { + const [title = "Actions", body = ""] = args; + const actions = parseActions(flags.actions); + if (actions.length === 0) { + return { text: 'Error: Action card requires --actions "Label1|data1,Label2|data2"' }; + } + const bubble = createActionCard(title, body, actions, { + imageUrl: flags.url || flags.image, + }); + return buildLineReply({ + flexMessage: { + altText: `${title}: ${body}`.slice(0, 400), + contents: bubble, + }, + }); + } + + case "list": { + const [title = "List", itemsStr = ""] = args; + const items = parseListItems(itemsStr || flags.items || ""); + if (items.length === 0) { + return { + text: + 'Error: List card requires items. Usage: /card list "Title" "Item1|Desc1,Item2|Desc2"', + }; + } + const bubble = createListCard(title, items); + return buildLineReply({ + flexMessage: { + altText: `${title}: ${items.map((i) => i.title).join(", ")}`.slice(0, 400), + contents: bubble, + }, + }); + } + + case "receipt": { + const [title = "Receipt", itemsStr = ""] = args; + const items = parseReceiptItems(itemsStr || flags.items || ""); + const total = flags.total ? { label: "Total", value: flags.total } : undefined; + const footer = flags.footer; + + if (items.length === 0) { + return { + text: + 'Error: Receipt card requires items. Usage: /card receipt "Title" "Item1:$10,Item2:$20" --total "$30"', + }; + } + + const bubble = createReceiptCard({ title, items, total, footer }); + return buildLineReply({ + flexMessage: { + altText: `${title}: ${items.map((i) => `${i.name} ${i.value}`).join(", ")}`.slice( + 0, + 400, + ), + contents: bubble, + }, + }); + } + + case "confirm": { + const [question = "Confirm?"] = args; + const yesStr = flags.yes || "Yes|yes"; + const noStr = flags.no || "No|no"; + + const [yesLabel, yesData] = yesStr.split("|").map((s) => s.trim()); + const [noLabel, noData] = noStr.split("|").map((s) => s.trim()); + + return buildLineReply({ + templateMessage: { + type: "confirm", + text: question, + confirmLabel: yesLabel || "Yes", + confirmData: yesData || "yes", + cancelLabel: noLabel || "No", + cancelData: noData || "no", + altText: question, + }, + }); + } + + case "buttons": { + const [title = "Menu", text = "Choose an option"] = args; + const actionsStr = flags.actions || ""; + const actionParts = parseActions(actionsStr); + + if (actionParts.length === 0) { + return { text: 'Error: Buttons card requires --actions "Label1|data1,Label2|data2"' }; + } + + const templateActions: Array<{ + type: "message" | "uri" | "postback"; + label: string; + data?: string; + uri?: string; + }> = actionParts.map((a) => { + const action = a.action; + const label = action.label ?? a.label; + if (action.type === "uri") { + return { type: "uri" as const, label, uri: (action as { uri: string }).uri }; + } + if (action.type === "postback") { + return { + type: "postback" as const, + label, + data: (action as { data: string }).data, + }; + } + return { + type: "message" as const, + label, + data: (action as { text: string }).text, + }; + }); + + return buildLineReply({ + templateMessage: { + type: "buttons", + title, + text, + thumbnailImageUrl: flags.url || flags.image, + actions: templateActions, + }, + }); + } + + default: + return { + text: `Unknown card type: "${type}". Available types: info, image, action, list, receipt, confirm, buttons`, + }; + } + } catch (err) { + return { text: `Error creating card: ${String(err)}` }; + } + }, + }); +} diff --git a/extensions/line/src/channel.logout.test.ts b/extensions/line/src/channel.logout.test.ts new file mode 100644 index 00000000000..8523feaae8e --- /dev/null +++ b/extensions/line/src/channel.logout.test.ts @@ -0,0 +1,96 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { ClawdbotConfig, PluginRuntime } from "clawdbot/plugin-sdk"; +import { linePlugin } from "./channel.js"; +import { setLineRuntime } from "./runtime.js"; + +const DEFAULT_ACCOUNT_ID = "default"; + +type LineRuntimeMocks = { + writeConfigFile: ReturnType; + resolveLineAccount: ReturnType; +}; + +function createRuntime(): { runtime: PluginRuntime; mocks: LineRuntimeMocks } { + const writeConfigFile = vi.fn(async () => {}); + const resolveLineAccount = vi.fn(({ cfg, accountId }: { cfg: ClawdbotConfig; accountId?: string }) => { + const lineConfig = (cfg.channels?.line ?? {}) as { + tokenFile?: string; + secretFile?: string; + channelAccessToken?: string; + channelSecret?: string; + accounts?: Record>; + }; + const entry = + accountId && accountId !== DEFAULT_ACCOUNT_ID + ? lineConfig.accounts?.[accountId] ?? {} + : lineConfig; + const hasToken = + Boolean((entry as any).channelAccessToken) || Boolean((entry as any).tokenFile); + const hasSecret = + Boolean((entry as any).channelSecret) || Boolean((entry as any).secretFile); + return { tokenSource: hasToken && hasSecret ? "config" : "none" }; + }); + + const runtime = { + config: { writeConfigFile }, + channel: { line: { resolveLineAccount } }, + } as unknown as PluginRuntime; + + return { runtime, mocks: { writeConfigFile, resolveLineAccount } }; +} + +describe("linePlugin gateway.logoutAccount", () => { + beforeEach(() => { + setLineRuntime(createRuntime().runtime); + }); + + it("clears tokenFile/secretFile on default account logout", async () => { + const { runtime, mocks } = createRuntime(); + setLineRuntime(runtime); + + const cfg: ClawdbotConfig = { + channels: { + line: { + tokenFile: "/tmp/token", + secretFile: "/tmp/secret", + }, + }, + }; + + const result = await linePlugin.gateway.logoutAccount({ + accountId: DEFAULT_ACCOUNT_ID, + cfg, + }); + + expect(result.cleared).toBe(true); + expect(result.loggedOut).toBe(true); + expect(mocks.writeConfigFile).toHaveBeenCalledWith({}); + }); + + it("clears tokenFile/secretFile on account logout", async () => { + const { runtime, mocks } = createRuntime(); + setLineRuntime(runtime); + + const cfg: ClawdbotConfig = { + channels: { + line: { + accounts: { + primary: { + tokenFile: "/tmp/token", + secretFile: "/tmp/secret", + }, + }, + }, + }, + }; + + const result = await linePlugin.gateway.logoutAccount({ + accountId: "primary", + cfg, + }); + + expect(result.cleared).toBe(true); + expect(result.loggedOut).toBe(true); + expect(mocks.writeConfigFile).toHaveBeenCalledWith({}); + }); +}); diff --git a/extensions/line/src/channel.sendPayload.test.ts b/extensions/line/src/channel.sendPayload.test.ts new file mode 100644 index 00000000000..1b949bb9333 --- /dev/null +++ b/extensions/line/src/channel.sendPayload.test.ts @@ -0,0 +1,308 @@ +import { describe, expect, it, vi } from "vitest"; +import type { ClawdbotConfig, PluginRuntime } from "clawdbot/plugin-sdk"; +import { linePlugin } from "./channel.js"; +import { setLineRuntime } from "./runtime.js"; + +type LineRuntimeMocks = { + pushMessageLine: ReturnType; + pushMessagesLine: ReturnType; + pushFlexMessage: ReturnType; + pushTemplateMessage: ReturnType; + pushLocationMessage: ReturnType; + pushTextMessageWithQuickReplies: ReturnType; + createQuickReplyItems: ReturnType; + buildTemplateMessageFromPayload: ReturnType; + sendMessageLine: ReturnType; + chunkMarkdownText: ReturnType; + resolveLineAccount: ReturnType; + resolveTextChunkLimit: ReturnType; +}; + +function createRuntime(): { runtime: PluginRuntime; mocks: LineRuntimeMocks } { + const pushMessageLine = vi.fn(async () => ({ messageId: "m-text", chatId: "c1" })); + const pushMessagesLine = vi.fn(async () => ({ messageId: "m-batch", chatId: "c1" })); + const pushFlexMessage = vi.fn(async () => ({ messageId: "m-flex", chatId: "c1" })); + const pushTemplateMessage = vi.fn(async () => ({ messageId: "m-template", chatId: "c1" })); + const pushLocationMessage = vi.fn(async () => ({ messageId: "m-loc", chatId: "c1" })); + const pushTextMessageWithQuickReplies = vi.fn(async () => ({ + messageId: "m-quick", + chatId: "c1", + })); + const createQuickReplyItems = vi.fn((labels: string[]) => ({ items: labels })); + const buildTemplateMessageFromPayload = vi.fn(() => ({ type: "buttons" })); + const sendMessageLine = vi.fn(async () => ({ messageId: "m-media", chatId: "c1" })); + const chunkMarkdownText = vi.fn((text: string) => [text]); + const resolveTextChunkLimit = vi.fn(() => 123); + const resolveLineAccount = vi.fn(({ cfg, accountId }: { cfg: ClawdbotConfig; accountId?: string }) => { + const resolved = accountId ?? "default"; + const lineConfig = (cfg.channels?.line ?? {}) as { + accounts?: Record>; + }; + const accountConfig = + resolved !== "default" ? lineConfig.accounts?.[resolved] ?? {} : {}; + return { + accountId: resolved, + config: { ...lineConfig, ...accountConfig }, + }; + }); + + const runtime = { + channel: { + line: { + pushMessageLine, + pushMessagesLine, + pushFlexMessage, + pushTemplateMessage, + pushLocationMessage, + pushTextMessageWithQuickReplies, + createQuickReplyItems, + buildTemplateMessageFromPayload, + sendMessageLine, + resolveLineAccount, + }, + text: { + chunkMarkdownText, + resolveTextChunkLimit, + }, + }, + } as unknown as PluginRuntime; + + return { + runtime, + mocks: { + pushMessageLine, + pushMessagesLine, + pushFlexMessage, + pushTemplateMessage, + pushLocationMessage, + pushTextMessageWithQuickReplies, + createQuickReplyItems, + buildTemplateMessageFromPayload, + sendMessageLine, + chunkMarkdownText, + resolveLineAccount, + resolveTextChunkLimit, + }, + }; +} + +describe("linePlugin outbound.sendPayload", () => { + it("sends flex message without dropping text", async () => { + const { runtime, mocks } = createRuntime(); + setLineRuntime(runtime); + const cfg = { channels: { line: {} } } as ClawdbotConfig; + + const payload = { + text: "Now playing:", + channelData: { + line: { + flexMessage: { + altText: "Now playing", + contents: { type: "bubble" }, + }, + }, + }, + }; + + await linePlugin.outbound.sendPayload({ + to: "line:group:1", + payload, + accountId: "default", + cfg, + }); + + expect(mocks.pushFlexMessage).toHaveBeenCalledTimes(1); + expect(mocks.pushMessageLine).toHaveBeenCalledWith("line:group:1", "Now playing:", { + verbose: false, + accountId: "default", + }); + }); + + it("sends template message without dropping text", async () => { + const { runtime, mocks } = createRuntime(); + setLineRuntime(runtime); + const cfg = { channels: { line: {} } } as ClawdbotConfig; + + const payload = { + text: "Choose one:", + channelData: { + line: { + templateMessage: { + type: "confirm", + text: "Continue?", + confirmLabel: "Yes", + confirmData: "yes", + cancelLabel: "No", + cancelData: "no", + }, + }, + }, + }; + + await linePlugin.outbound.sendPayload({ + to: "line:user:1", + payload, + accountId: "default", + cfg, + }); + + expect(mocks.buildTemplateMessageFromPayload).toHaveBeenCalledTimes(1); + expect(mocks.pushTemplateMessage).toHaveBeenCalledTimes(1); + expect(mocks.pushMessageLine).toHaveBeenCalledWith("line:user:1", "Choose one:", { + verbose: false, + accountId: "default", + }); + }); + + it("attaches quick replies when no text chunks are present", async () => { + const { runtime, mocks } = createRuntime(); + setLineRuntime(runtime); + const cfg = { channels: { line: {} } } as ClawdbotConfig; + + const payload = { + channelData: { + line: { + quickReplies: ["One", "Two"], + flexMessage: { + altText: "Card", + contents: { type: "bubble" }, + }, + }, + }, + }; + + await linePlugin.outbound.sendPayload({ + to: "line:user:2", + payload, + accountId: "default", + cfg, + }); + + expect(mocks.pushFlexMessage).not.toHaveBeenCalled(); + expect(mocks.pushMessagesLine).toHaveBeenCalledWith( + "line:user:2", + [ + { + type: "flex", + altText: "Card", + contents: { type: "bubble" }, + quickReply: { items: ["One", "Two"] }, + }, + ], + { verbose: false, accountId: "default" }, + ); + expect(mocks.createQuickReplyItems).toHaveBeenCalledWith(["One", "Two"]); + }); + + it("sends media before quick-reply text so buttons stay visible", async () => { + const { runtime, mocks } = createRuntime(); + setLineRuntime(runtime); + const cfg = { channels: { line: {} } } as ClawdbotConfig; + + const payload = { + text: "Hello", + mediaUrl: "https://example.com/img.jpg", + channelData: { + line: { + quickReplies: ["One", "Two"], + }, + }, + }; + + await linePlugin.outbound.sendPayload({ + to: "line:user:3", + payload, + accountId: "default", + cfg, + }); + + expect(mocks.sendMessageLine).toHaveBeenCalledWith("line:user:3", "", { + verbose: false, + mediaUrl: "https://example.com/img.jpg", + accountId: "default", + }); + expect(mocks.pushTextMessageWithQuickReplies).toHaveBeenCalledWith( + "line:user:3", + "Hello", + ["One", "Two"], + { verbose: false, accountId: "default" }, + ); + const mediaOrder = mocks.sendMessageLine.mock.invocationCallOrder[0]; + const quickReplyOrder = mocks.pushTextMessageWithQuickReplies.mock.invocationCallOrder[0]; + expect(mediaOrder).toBeLessThan(quickReplyOrder); + }); + + it("uses configured text chunk limit for payloads", async () => { + const { runtime, mocks } = createRuntime(); + setLineRuntime(runtime); + const cfg = { channels: { line: { textChunkLimit: 123 } } } as ClawdbotConfig; + + const payload = { + text: "Hello world", + channelData: { + line: { + flexMessage: { + altText: "Card", + contents: { type: "bubble" }, + }, + }, + }, + }; + + await linePlugin.outbound.sendPayload({ + to: "line:user:3", + payload, + accountId: "primary", + cfg, + }); + + expect(mocks.resolveTextChunkLimit).toHaveBeenCalledWith( + cfg, + "line", + "primary", + { fallbackLimit: 5000 }, + ); + expect(mocks.chunkMarkdownText).toHaveBeenCalledWith("Hello world", 123); + }); +}); + +describe("linePlugin config.formatAllowFrom", () => { + it("strips line:user: prefixes without lowercasing", () => { + const formatted = linePlugin.config.formatAllowFrom({ + allowFrom: ["line:user:UABC", "line:UDEF"], + }); + expect(formatted).toEqual(["UABC", "UDEF"]); + }); +}); + +describe("linePlugin groups.resolveRequireMention", () => { + it("uses account-level group settings when provided", () => { + const { runtime } = createRuntime(); + setLineRuntime(runtime); + + const cfg = { + channels: { + line: { + groups: { + "*": { requireMention: false }, + }, + accounts: { + primary: { + groups: { + "group-1": { requireMention: true }, + }, + }, + }, + }, + }, + } as ClawdbotConfig; + + const requireMention = linePlugin.groups.resolveRequireMention({ + cfg, + accountId: "primary", + groupId: "group-1", + }); + + expect(requireMention).toBe(true); + }); +}); diff --git a/extensions/line/src/channel.ts b/extensions/line/src/channel.ts new file mode 100644 index 00000000000..c100f0a3134 --- /dev/null +++ b/extensions/line/src/channel.ts @@ -0,0 +1,773 @@ +import { + buildChannelConfigSchema, + DEFAULT_ACCOUNT_ID, + LineConfigSchema, + processLineMessage, + type ChannelPlugin, + type ClawdbotConfig, + type LineConfig, + type LineChannelData, + type ResolvedLineAccount, +} from "clawdbot/plugin-sdk"; + +import { getLineRuntime } from "./runtime.js"; + +// LINE channel metadata +const meta = { + id: "line", + label: "LINE", + selectionLabel: "LINE (Messaging API)", + detailLabel: "LINE Bot", + docsPath: "/channels/line", + docsLabel: "line", + blurb: "LINE Messaging API bot for Japan/Taiwan/Thailand markets.", + systemImage: "message.fill", +}; + +function parseThreadId(threadId?: string | number | null): number | undefined { + if (threadId == null) return undefined; + if (typeof threadId === "number") { + return Number.isFinite(threadId) ? Math.trunc(threadId) : undefined; + } + const trimmed = threadId.trim(); + if (!trimmed) return undefined; + const parsed = Number.parseInt(trimmed, 10); + return Number.isFinite(parsed) ? parsed : undefined; +} + +export const linePlugin: ChannelPlugin = { + id: "line", + meta: { + ...meta, + quickstartAllowFrom: true, + }, + pairing: { + idLabel: "lineUserId", + normalizeAllowEntry: (entry) => { + // LINE IDs are case-sensitive; only strip prefix variants (line: / line:user:). + return entry.replace(/^line:(?:user:)?/i, ""); + }, + notifyApproval: async ({ cfg, id }) => { + const line = getLineRuntime().channel.line; + const account = line.resolveLineAccount({ cfg }); + if (!account.channelAccessToken) { + throw new Error("LINE channel access token not configured"); + } + await line.pushMessageLine(id, "Clawdbot: your access has been approved.", { + channelAccessToken: account.channelAccessToken, + }); + }, + }, + capabilities: { + chatTypes: ["direct", "group"], + reactions: false, + threads: false, + media: true, + nativeCommands: false, + blockStreaming: true, + }, + reload: { configPrefixes: ["channels.line"] }, + configSchema: buildChannelConfigSchema(LineConfigSchema), + config: { + listAccountIds: (cfg) => getLineRuntime().channel.line.listLineAccountIds(cfg), + resolveAccount: (cfg, accountId) => + getLineRuntime().channel.line.resolveLineAccount({ cfg, accountId }), + defaultAccountId: (cfg) => getLineRuntime().channel.line.resolveDefaultLineAccountId(cfg), + setAccountEnabled: ({ cfg, accountId, enabled }) => { + const lineConfig = (cfg.channels?.line ?? {}) as LineConfig; + if (accountId === DEFAULT_ACCOUNT_ID) { + return { + ...cfg, + channels: { + ...cfg.channels, + line: { + ...lineConfig, + enabled, + }, + }, + }; + } + return { + ...cfg, + channels: { + ...cfg.channels, + line: { + ...lineConfig, + accounts: { + ...lineConfig.accounts, + [accountId]: { + ...lineConfig.accounts?.[accountId], + enabled, + }, + }, + }, + }, + }; + }, + deleteAccount: ({ cfg, accountId }) => { + const lineConfig = (cfg.channels?.line ?? {}) as LineConfig; + if (accountId === DEFAULT_ACCOUNT_ID) { + const { channelAccessToken, channelSecret, tokenFile, secretFile, ...rest } = lineConfig; + return { + ...cfg, + channels: { + ...cfg.channels, + line: rest, + }, + }; + } + const accounts = { ...lineConfig.accounts }; + delete accounts[accountId]; + return { + ...cfg, + channels: { + ...cfg.channels, + line: { + ...lineConfig, + accounts: Object.keys(accounts).length > 0 ? accounts : undefined, + }, + }, + }; + }, + isConfigured: (account) => Boolean(account.channelAccessToken?.trim()), + describeAccount: (account) => ({ + accountId: account.accountId, + name: account.name, + enabled: account.enabled, + configured: Boolean(account.channelAccessToken?.trim()), + tokenSource: account.tokenSource, + }), + resolveAllowFrom: ({ cfg, accountId }) => + (getLineRuntime().channel.line.resolveLineAccount({ cfg, accountId }).config.allowFrom ?? []).map( + (entry) => String(entry), + ), + formatAllowFrom: ({ allowFrom }) => + allowFrom + .map((entry) => String(entry).trim()) + .filter(Boolean) + .map((entry) => { + // LINE sender IDs are case-sensitive; keep original casing. + return entry.replace(/^line:(?:user:)?/i, ""); + }), + }, + security: { + resolveDmPolicy: ({ cfg, accountId, account }) => { + const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID; + const useAccountPath = Boolean( + (cfg.channels?.line as LineConfig | undefined)?.accounts?.[resolvedAccountId], + ); + const basePath = useAccountPath + ? `channels.line.accounts.${resolvedAccountId}.` + : "channels.line."; + return { + policy: account.config.dmPolicy ?? "pairing", + allowFrom: account.config.allowFrom ?? [], + policyPath: `${basePath}dmPolicy`, + allowFromPath: basePath, + approveHint: "clawdbot pairing approve line ", + normalizeEntry: (raw) => raw.replace(/^line:(?:user:)?/i, ""), + }; + }, + collectWarnings: ({ account, cfg }) => { + const defaultGroupPolicy = + (cfg.channels?.defaults as { groupPolicy?: string } | undefined)?.groupPolicy; + const groupPolicy = account.config.groupPolicy ?? defaultGroupPolicy ?? "allowlist"; + if (groupPolicy !== "open") return []; + return [ + `- LINE groups: groupPolicy="open" allows any member in groups to trigger. Set channels.line.groupPolicy="allowlist" + channels.line.groupAllowFrom to restrict senders.`, + ]; + }, + }, + groups: { + resolveRequireMention: ({ cfg, accountId, groupId }) => { + const account = getLineRuntime().channel.line.resolveLineAccount({ cfg, accountId }); + const groups = account.config.groups; + if (!groups) return false; + const groupConfig = groups[groupId] ?? groups["*"]; + return groupConfig?.requireMention ?? false; + }, + }, + messaging: { + normalizeTarget: (target) => { + const trimmed = target.trim(); + if (!trimmed) return null; + return trimmed.replace(/^line:(group|room|user):/i, "").replace(/^line:/i, ""); + }, + targetResolver: { + looksLikeId: (id) => { + const trimmed = id?.trim(); + if (!trimmed) return false; + // LINE user IDs are typically U followed by 32 hex characters + // Group IDs are C followed by 32 hex characters + // Room IDs are R followed by 32 hex characters + return /^[UCR][a-f0-9]{32}$/i.test(trimmed) || /^line:/i.test(trimmed); + }, + hint: "", + }, + }, + directory: { + self: async () => null, + listPeers: async () => [], + listGroups: async () => [], + }, + setup: { + resolveAccountId: ({ accountId }) => + getLineRuntime().channel.line.normalizeAccountId(accountId), + applyAccountName: ({ cfg, accountId, name }) => { + const lineConfig = (cfg.channels?.line ?? {}) as LineConfig; + if (accountId === DEFAULT_ACCOUNT_ID) { + return { + ...cfg, + channels: { + ...cfg.channels, + line: { + ...lineConfig, + name, + }, + }, + }; + } + return { + ...cfg, + channels: { + ...cfg.channels, + line: { + ...lineConfig, + accounts: { + ...lineConfig.accounts, + [accountId]: { + ...lineConfig.accounts?.[accountId], + name, + }, + }, + }, + }, + }; + }, + validateInput: ({ accountId, input }) => { + const typedInput = input as { + useEnv?: boolean; + channelAccessToken?: string; + channelSecret?: string; + tokenFile?: string; + secretFile?: string; + }; + if (typedInput.useEnv && accountId !== DEFAULT_ACCOUNT_ID) { + return "LINE_CHANNEL_ACCESS_TOKEN can only be used for the default account."; + } + if (!typedInput.useEnv && !typedInput.channelAccessToken && !typedInput.tokenFile) { + return "LINE requires channelAccessToken or --token-file (or --use-env)."; + } + if (!typedInput.useEnv && !typedInput.channelSecret && !typedInput.secretFile) { + return "LINE requires channelSecret or --secret-file (or --use-env)."; + } + return null; + }, + applyAccountConfig: ({ cfg, accountId, input }) => { + const typedInput = input as { + name?: string; + useEnv?: boolean; + channelAccessToken?: string; + channelSecret?: string; + tokenFile?: string; + secretFile?: string; + }; + const lineConfig = (cfg.channels?.line ?? {}) as LineConfig; + + if (accountId === DEFAULT_ACCOUNT_ID) { + return { + ...cfg, + channels: { + ...cfg.channels, + line: { + ...lineConfig, + enabled: true, + ...(typedInput.name ? { name: typedInput.name } : {}), + ...(typedInput.useEnv + ? {} + : typedInput.tokenFile + ? { tokenFile: typedInput.tokenFile } + : typedInput.channelAccessToken + ? { channelAccessToken: typedInput.channelAccessToken } + : {}), + ...(typedInput.useEnv + ? {} + : typedInput.secretFile + ? { secretFile: typedInput.secretFile } + : typedInput.channelSecret + ? { channelSecret: typedInput.channelSecret } + : {}), + }, + }, + }; + } + + return { + ...cfg, + channels: { + ...cfg.channels, + line: { + ...lineConfig, + enabled: true, + accounts: { + ...lineConfig.accounts, + [accountId]: { + ...lineConfig.accounts?.[accountId], + enabled: true, + ...(typedInput.name ? { name: typedInput.name } : {}), + ...(typedInput.tokenFile + ? { tokenFile: typedInput.tokenFile } + : typedInput.channelAccessToken + ? { channelAccessToken: typedInput.channelAccessToken } + : {}), + ...(typedInput.secretFile + ? { secretFile: typedInput.secretFile } + : typedInput.channelSecret + ? { channelSecret: typedInput.channelSecret } + : {}), + }, + }, + }, + }, + }; + }, + }, + outbound: { + deliveryMode: "direct", + chunker: (text, limit) => getLineRuntime().channel.text.chunkMarkdownText(text, limit), + textChunkLimit: 5000, // LINE allows up to 5000 characters per text message + sendPayload: async ({ to, payload, accountId, cfg }) => { + const runtime = getLineRuntime(); + const lineData = (payload.channelData?.line as LineChannelData | undefined) ?? {}; + const sendText = runtime.channel.line.pushMessageLine; + const sendBatch = runtime.channel.line.pushMessagesLine; + const sendFlex = runtime.channel.line.pushFlexMessage; + const sendTemplate = runtime.channel.line.pushTemplateMessage; + const sendLocation = runtime.channel.line.pushLocationMessage; + const sendQuickReplies = runtime.channel.line.pushTextMessageWithQuickReplies; + const buildTemplate = runtime.channel.line.buildTemplateMessageFromPayload; + const createQuickReplyItems = runtime.channel.line.createQuickReplyItems; + + let lastResult: { messageId: string; chatId: string } | null = null; + const hasQuickReplies = Boolean(lineData.quickReplies?.length); + const quickReply = hasQuickReplies + ? createQuickReplyItems(lineData.quickReplies!) + : undefined; + + const sendMessageBatch = async (messages: Array>) => { + if (messages.length === 0) return; + for (let i = 0; i < messages.length; i += 5) { + const result = await sendBatch(to, messages.slice(i, i + 5), { + verbose: false, + accountId: accountId ?? undefined, + }); + lastResult = { messageId: result.messageId, chatId: result.chatId }; + } + }; + + const processed = payload.text + ? processLineMessage(payload.text) + : { text: "", flexMessages: [] }; + + const chunkLimit = + runtime.channel.text.resolveTextChunkLimit?.( + cfg, + "line", + accountId ?? undefined, + { + fallbackLimit: 5000, + }, + ) ?? 5000; + + const chunks = processed.text + ? runtime.channel.text.chunkMarkdownText(processed.text, chunkLimit) + : []; + const mediaUrls = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []); + const shouldSendQuickRepliesInline = chunks.length === 0 && hasQuickReplies; + + if (!shouldSendQuickRepliesInline) { + if (lineData.flexMessage) { + lastResult = await sendFlex( + to, + lineData.flexMessage.altText, + lineData.flexMessage.contents, + { + verbose: false, + accountId: accountId ?? undefined, + }, + ); + } + + if (lineData.templateMessage) { + const template = buildTemplate(lineData.templateMessage); + if (template) { + lastResult = await sendTemplate(to, template, { + verbose: false, + accountId: accountId ?? undefined, + }); + } + } + + if (lineData.location) { + lastResult = await sendLocation(to, lineData.location, { + verbose: false, + accountId: accountId ?? undefined, + }); + } + + for (const flexMsg of processed.flexMessages) { + lastResult = await sendFlex(to, flexMsg.altText, flexMsg.contents, { + verbose: false, + accountId: accountId ?? undefined, + }); + } + } + + const sendMediaAfterText = !(hasQuickReplies && chunks.length > 0); + if (mediaUrls.length > 0 && !shouldSendQuickRepliesInline && !sendMediaAfterText) { + for (const url of mediaUrls) { + lastResult = await runtime.channel.line.sendMessageLine(to, "", { + verbose: false, + mediaUrl: url, + accountId: accountId ?? undefined, + }); + } + } + + if (chunks.length > 0) { + for (let i = 0; i < chunks.length; i += 1) { + const isLast = i === chunks.length - 1; + if (isLast && hasQuickReplies) { + lastResult = await sendQuickReplies(to, chunks[i]!, lineData.quickReplies!, { + verbose: false, + accountId: accountId ?? undefined, + }); + } else { + lastResult = await sendText(to, chunks[i]!, { + verbose: false, + accountId: accountId ?? undefined, + }); + } + } + } else if (shouldSendQuickRepliesInline) { + const quickReplyMessages: Array> = []; + if (lineData.flexMessage) { + quickReplyMessages.push({ + type: "flex", + altText: lineData.flexMessage.altText.slice(0, 400), + contents: lineData.flexMessage.contents, + }); + } + if (lineData.templateMessage) { + const template = buildTemplate(lineData.templateMessage); + if (template) { + quickReplyMessages.push(template); + } + } + if (lineData.location) { + quickReplyMessages.push({ + type: "location", + title: lineData.location.title.slice(0, 100), + address: lineData.location.address.slice(0, 100), + latitude: lineData.location.latitude, + longitude: lineData.location.longitude, + }); + } + for (const flexMsg of processed.flexMessages) { + quickReplyMessages.push({ + type: "flex", + altText: flexMsg.altText.slice(0, 400), + contents: flexMsg.contents, + }); + } + for (const url of mediaUrls) { + const trimmed = url?.trim(); + if (!trimmed) continue; + quickReplyMessages.push({ + type: "image", + originalContentUrl: trimmed, + previewImageUrl: trimmed, + }); + } + if (quickReplyMessages.length > 0 && quickReply) { + const lastIndex = quickReplyMessages.length - 1; + quickReplyMessages[lastIndex] = { + ...quickReplyMessages[lastIndex], + quickReply, + }; + await sendMessageBatch(quickReplyMessages); + } + } + + if (mediaUrls.length > 0 && !shouldSendQuickRepliesInline && sendMediaAfterText) { + for (const url of mediaUrls) { + lastResult = await runtime.channel.line.sendMessageLine(to, "", { + verbose: false, + mediaUrl: url, + accountId: accountId ?? undefined, + }); + } + } + + if (lastResult) return { channel: "line", ...lastResult }; + return { channel: "line", messageId: "empty", chatId: to }; + }, + sendText: async ({ to, text, accountId }) => { + const runtime = getLineRuntime(); + const sendText = runtime.channel.line.pushMessageLine; + const sendFlex = runtime.channel.line.pushFlexMessage; + + // Process markdown: extract tables/code blocks, strip formatting + const processed = processLineMessage(text); + + // Send cleaned text first (if non-empty) + let result: { messageId: string; chatId: string }; + if (processed.text.trim()) { + result = await sendText(to, processed.text, { + verbose: false, + accountId: accountId ?? undefined, + }); + } else { + // If text is empty after processing, still need a result + result = { messageId: "processed", chatId: to }; + } + + // Send flex messages for tables/code blocks + for (const flexMsg of processed.flexMessages) { + await sendFlex(to, flexMsg.altText, flexMsg.contents, { + verbose: false, + accountId: accountId ?? undefined, + }); + } + + return { channel: "line", ...result }; + }, + sendMedia: async ({ to, text, mediaUrl, accountId }) => { + const send = getLineRuntime().channel.line.sendMessageLine; + const result = await send(to, text, { + verbose: false, + mediaUrl, + accountId: accountId ?? undefined, + }); + return { channel: "line", ...result }; + }, + }, + status: { + defaultRuntime: { + accountId: DEFAULT_ACCOUNT_ID, + running: false, + lastStartAt: null, + lastStopAt: null, + lastError: null, + }, + collectStatusIssues: ({ account }) => { + const issues: Array<{ level: "error" | "warning"; message: string }> = []; + if (!account.channelAccessToken?.trim()) { + issues.push({ + level: "error", + message: "LINE channel access token not configured", + }); + } + if (!account.channelSecret?.trim()) { + issues.push({ + level: "error", + message: "LINE channel secret not configured", + }); + } + return issues; + }, + buildChannelSummary: ({ snapshot }) => ({ + configured: snapshot.configured ?? false, + tokenSource: snapshot.tokenSource ?? "none", + running: snapshot.running ?? false, + mode: snapshot.mode ?? null, + lastStartAt: snapshot.lastStartAt ?? null, + lastStopAt: snapshot.lastStopAt ?? null, + lastError: snapshot.lastError ?? null, + probe: snapshot.probe, + lastProbeAt: snapshot.lastProbeAt ?? null, + }), + probeAccount: async ({ account, timeoutMs }) => + getLineRuntime().channel.line.probeLineBot(account.channelAccessToken, timeoutMs), + buildAccountSnapshot: ({ account, runtime, probe }) => { + const configured = Boolean(account.channelAccessToken?.trim()); + return { + accountId: account.accountId, + name: account.name, + enabled: account.enabled, + configured, + tokenSource: account.tokenSource, + running: runtime?.running ?? false, + lastStartAt: runtime?.lastStartAt ?? null, + lastStopAt: runtime?.lastStopAt ?? null, + lastError: runtime?.lastError ?? null, + mode: "webhook", + probe, + lastInboundAt: runtime?.lastInboundAt ?? null, + lastOutboundAt: runtime?.lastOutboundAt ?? null, + }; + }, + }, + gateway: { + startAccount: async (ctx) => { + const account = ctx.account; + const token = account.channelAccessToken.trim(); + const secret = account.channelSecret.trim(); + + let lineBotLabel = ""; + try { + const probe = await getLineRuntime().channel.line.probeLineBot(token, 2500); + const displayName = probe.ok ? probe.bot?.displayName?.trim() : null; + if (displayName) lineBotLabel = ` (${displayName})`; + } catch (err) { + if (getLineRuntime().logging.shouldLogVerbose()) { + ctx.log?.debug?.(`[${account.accountId}] bot probe failed: ${String(err)}`); + } + } + + ctx.log?.info(`[${account.accountId}] starting LINE provider${lineBotLabel}`); + + return getLineRuntime().channel.line.monitorLineProvider({ + channelAccessToken: token, + channelSecret: secret, + accountId: account.accountId, + config: ctx.cfg, + runtime: ctx.runtime, + abortSignal: ctx.abortSignal, + webhookPath: account.config.webhookPath, + }); + }, + logoutAccount: async ({ accountId, cfg }) => { + const envToken = process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim() ?? ""; + const nextCfg = { ...cfg } as ClawdbotConfig; + const lineConfig = (cfg.channels?.line ?? {}) as LineConfig; + const nextLine = { ...lineConfig }; + let cleared = false; + let changed = false; + + if (accountId === DEFAULT_ACCOUNT_ID) { + if ( + nextLine.channelAccessToken || + nextLine.channelSecret || + nextLine.tokenFile || + nextLine.secretFile + ) { + delete nextLine.channelAccessToken; + delete nextLine.channelSecret; + delete nextLine.tokenFile; + delete nextLine.secretFile; + cleared = true; + changed = true; + } + } + + const accounts = nextLine.accounts ? { ...nextLine.accounts } : undefined; + if (accounts && accountId in accounts) { + const entry = accounts[accountId]; + if (entry && typeof entry === "object") { + const nextEntry = { ...entry } as Record; + if ( + "channelAccessToken" in nextEntry || + "channelSecret" in nextEntry || + "tokenFile" in nextEntry || + "secretFile" in nextEntry + ) { + cleared = true; + delete nextEntry.channelAccessToken; + delete nextEntry.channelSecret; + delete nextEntry.tokenFile; + delete nextEntry.secretFile; + changed = true; + } + if (Object.keys(nextEntry).length === 0) { + delete accounts[accountId]; + changed = true; + } else { + accounts[accountId] = nextEntry as typeof entry; + } + } + } + + if (accounts) { + if (Object.keys(accounts).length === 0) { + delete nextLine.accounts; + changed = true; + } else { + nextLine.accounts = accounts; + } + } + + if (changed) { + if (Object.keys(nextLine).length > 0) { + nextCfg.channels = { ...nextCfg.channels, line: nextLine }; + } else { + const nextChannels = { ...nextCfg.channels }; + delete (nextChannels as Record).line; + if (Object.keys(nextChannels).length > 0) { + nextCfg.channels = nextChannels; + } else { + delete nextCfg.channels; + } + } + await getLineRuntime().config.writeConfigFile(nextCfg); + } + + const resolved = getLineRuntime().channel.line.resolveLineAccount({ + cfg: changed ? nextCfg : cfg, + accountId, + }); + const loggedOut = resolved.tokenSource === "none"; + + return { cleared, envToken: Boolean(envToken), loggedOut }; + }, + }, + agentPrompt: { + messageToolHints: () => [ + "", + "### LINE Rich Messages", + "LINE supports rich visual messages. Use these directives in your reply when appropriate:", + "", + "**Quick Replies** (bottom button suggestions):", + " [[quick_replies: Option 1, Option 2, Option 3]]", + "", + "**Location** (map pin):", + " [[location: Place Name | Address | latitude | longitude]]", + "", + "**Confirm Dialog** (yes/no prompt):", + " [[confirm: Question text? | Yes Label | No Label]]", + "", + "**Button Menu** (title + text + buttons):", + " [[buttons: Title | Description | Btn1:action1, Btn2:https://url.com]]", + "", + "**Media Player Card** (music status):", + " [[media_player: Song Title | Artist Name | Source | https://albumart.url | playing]]", + " - Status: 'playing' or 'paused' (optional)", + "", + "**Event Card** (calendar events, meetings):", + " [[event: Event Title | Date | Time | Location | Description]]", + " - Time, Location, Description are optional", + "", + "**Agenda Card** (multiple events/schedule):", + " [[agenda: Schedule Title | Event1:9:00 AM, Event2:12:00 PM, Event3:3:00 PM]]", + "", + "**Device Control Card** (smart devices, TVs, etc.):", + " [[device: Device Name | Device Type | Status | Control1:data1, Control2:data2]]", + "", + "**Apple TV Remote** (full D-pad + transport):", + " [[appletv_remote: Apple TV | Playing]]", + "", + "**Auto-converted**: Markdown tables become Flex cards, code blocks become styled cards.", + "", + "When to use rich messages:", + "- Use [[quick_replies:...]] when offering 2-4 clear options", + "- Use [[confirm:...]] for yes/no decisions", + "- Use [[buttons:...]] for menus with actions/links", + "- Use [[location:...]] when sharing a place", + "- Use [[media_player:...]] when showing what's playing", + "- Use [[event:...]] for calendar event details", + "- Use [[agenda:...]] for a day's schedule or event list", + "- Use [[device:...]] for smart device status/controls", + "- Tables/code in your response auto-convert to visual cards", + ], + }, +}; diff --git a/extensions/line/src/runtime.ts b/extensions/line/src/runtime.ts new file mode 100644 index 00000000000..5706349c639 --- /dev/null +++ b/extensions/line/src/runtime.ts @@ -0,0 +1,14 @@ +import type { PluginRuntime } from "clawdbot/plugin-sdk"; + +let runtime: PluginRuntime | null = null; + +export function setLineRuntime(r: PluginRuntime): void { + runtime = r; +} + +export function getLineRuntime(): PluginRuntime { + if (!runtime) { + throw new Error("LINE runtime not initialized - plugin not registered"); + } + return runtime; +} diff --git a/extensions/llm-task/package.json b/extensions/llm-task/package.json index e27384d9e0f..d6bfbb31d47 100644 --- a/extensions/llm-task/package.json +++ b/extensions/llm-task/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/llm-task", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot JSON-only LLM task plugin", "clawdbot": { diff --git a/extensions/lobster/package.json b/extensions/lobster/package.json index ea774ecbaf1..b73dbac6912 100644 --- a/extensions/lobster/package.json +++ b/extensions/lobster/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/lobster", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)", "clawdbot": { diff --git a/extensions/matrix/package.json b/extensions/matrix/package.json index edf64c99943..7fa12bc7470 100644 --- a/extensions/matrix/package.json +++ b/extensions/matrix/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/matrix", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Matrix channel plugin", "clawdbot": { diff --git a/extensions/matrix/src/matrix/monitor/auto-join.ts b/extensions/matrix/src/matrix/monitor/auto-join.ts index 90b05202a1d..564c7899598 100644 --- a/extensions/matrix/src/matrix/monitor/auto-join.ts +++ b/extensions/matrix/src/matrix/monitor/auto-join.ts @@ -33,7 +33,7 @@ export function registerMatrixAutoJoin(params: { // For "allowlist" mode, handle invites manually client.on("room.invite", async (roomId: string, _inviteEvent: unknown) => { if (autoJoin !== "allowlist") return; - + // Get room alias if available let alias: string | undefined; let altAliases: string[] = []; diff --git a/extensions/matrix/src/matrix/monitor/handler.ts b/extensions/matrix/src/matrix/monitor/handler.ts index 2ba7cbef000..4542e113a84 100644 --- a/extensions/matrix/src/matrix/monitor/handler.ts +++ b/extensions/matrix/src/matrix/monitor/handler.ts @@ -329,16 +329,20 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam return; } - const contentType = - "info" in content && content.info && "mimetype" in content.info - ? (content.info as { mimetype?: string }).mimetype + const contentInfo = + "info" in content && content.info && typeof content.info === "object" + ? (content.info as { mimetype?: string; size?: number }) : undefined; + const contentType = contentInfo?.mimetype; + const contentSize = + typeof contentInfo?.size === "number" ? contentInfo.size : undefined; if (mediaUrl?.startsWith("mxc://")) { try { media = await downloadMatrixMedia({ client, mxcUrl: mediaUrl, contentType, + sizeBytes: contentSize, maxBytes: mediaMaxBytes, file: contentFile, }); diff --git a/extensions/matrix/src/matrix/monitor/media.test.ts b/extensions/matrix/src/matrix/monitor/media.test.ts index d8fd5188820..10cbd8b4746 100644 --- a/extensions/matrix/src/matrix/monitor/media.test.ts +++ b/extensions/matrix/src/matrix/monitor/media.test.ts @@ -25,10 +25,8 @@ describe("downloadMatrixMedia", () => { it("decrypts encrypted media when file payloads are present", async () => { const decryptMedia = vi.fn().mockResolvedValue(Buffer.from("decrypted")); - const downloadContent = vi.fn().mockResolvedValue(Buffer.from("encrypted")); const client = { - downloadContent, crypto: { decryptMedia }, mxcToHttp: vi.fn().mockReturnValue("https://example/mxc"), } as unknown as import("matrix-bot-sdk").MatrixClient; @@ -55,7 +53,8 @@ describe("downloadMatrixMedia", () => { file, }); - expect(decryptMedia).toHaveBeenCalled(); + // decryptMedia should be called with just the file object (it handles download internally) + expect(decryptMedia).toHaveBeenCalledWith(file); expect(saveMediaBuffer).toHaveBeenCalledWith( Buffer.from("decrypted"), "image/png", @@ -64,4 +63,41 @@ describe("downloadMatrixMedia", () => { ); expect(result?.path).toBe("/tmp/media"); }); + + it("rejects encrypted media that exceeds maxBytes before decrypting", async () => { + const decryptMedia = vi.fn().mockResolvedValue(Buffer.from("decrypted")); + + const client = { + crypto: { decryptMedia }, + mxcToHttp: vi.fn().mockReturnValue("https://example/mxc"), + } as unknown as import("matrix-bot-sdk").MatrixClient; + + const file = { + url: "mxc://example/file", + key: { + kty: "oct", + key_ops: ["encrypt", "decrypt"], + alg: "A256CTR", + k: "secret", + ext: true, + }, + iv: "iv", + hashes: { sha256: "hash" }, + v: "v2", + }; + + await expect( + downloadMatrixMedia({ + client, + mxcUrl: "mxc://example/file", + contentType: "image/png", + sizeBytes: 2048, + maxBytes: 1024, + file, + }), + ).rejects.toThrow("Matrix media exceeds configured size limit"); + + expect(decryptMedia).not.toHaveBeenCalled(); + expect(saveMediaBuffer).not.toHaveBeenCalled(); + }); }); diff --git a/extensions/matrix/src/matrix/monitor/media.ts b/extensions/matrix/src/matrix/monitor/media.ts index dc49e7c4585..1ade1d19c8b 100644 --- a/extensions/matrix/src/matrix/monitor/media.ts +++ b/extensions/matrix/src/matrix/monitor/media.ts @@ -25,7 +25,7 @@ async function fetchMatrixMediaBuffer(params: { // matrix-bot-sdk provides mxcToHttp helper const url = params.client.mxcToHttp(params.mxcUrl); if (!url) return null; - + // Use the client's download method which handles auth try { const buffer = await params.client.downloadContent(params.mxcUrl); @@ -40,6 +40,7 @@ async function fetchMatrixMediaBuffer(params: { /** * Download and decrypt encrypted media from a Matrix room. + * Uses matrix-bot-sdk's decryptMedia which handles both download and decryption. */ async function fetchEncryptedMediaBuffer(params: { client: MatrixClient; @@ -50,18 +51,13 @@ async function fetchEncryptedMediaBuffer(params: { throw new Error("Cannot decrypt media: crypto not enabled"); } - // Download the encrypted content - const encryptedBuffer = await params.client.downloadContent(params.file.url); - if (encryptedBuffer.byteLength > params.maxBytes) { + // decryptMedia handles downloading and decrypting the encrypted content internally + const decrypted = await params.client.crypto.decryptMedia(params.file); + + if (decrypted.byteLength > params.maxBytes) { throw new Error("Matrix media exceeds configured size limit"); } - // Decrypt using matrix-bot-sdk crypto - const decrypted = await params.client.crypto.decryptMedia( - Buffer.from(encryptedBuffer), - params.file, - ); - return { buffer: decrypted }; } @@ -69,6 +65,7 @@ export async function downloadMatrixMedia(params: { client: MatrixClient; mxcUrl: string; contentType?: string; + sizeBytes?: number; maxBytes: number; file?: EncryptedFile; }): Promise<{ @@ -77,7 +74,13 @@ export async function downloadMatrixMedia(params: { placeholder: string; } | null> { let fetched: { buffer: Buffer; headerType?: string } | null; - + if ( + typeof params.sizeBytes === "number" && + params.sizeBytes > params.maxBytes + ) { + throw new Error("Matrix media exceeds configured size limit"); + } + if (params.file) { // Encrypted media fetched = await fetchEncryptedMediaBuffer({ @@ -93,7 +96,7 @@ export async function downloadMatrixMedia(params: { maxBytes: params.maxBytes, }); } - + if (!fetched) return null; const headerType = fetched.headerType ?? params.contentType ?? undefined; const saved = await getMatrixRuntime().channel.media.saveMediaBuffer( diff --git a/extensions/matrix/src/matrix/monitor/types.ts b/extensions/matrix/src/matrix/monitor/types.ts index d77bdac6736..c77cf0282b5 100644 --- a/extensions/matrix/src/matrix/monitor/types.ts +++ b/extensions/matrix/src/matrix/monitor/types.ts @@ -29,6 +29,7 @@ export type RoomMessageEventContent = MessageEventContent & { file?: EncryptedFile; info?: { mimetype?: string; + size?: number; }; "m.relates_to"?: { rel_type?: string; diff --git a/extensions/mattermost/package.json b/extensions/mattermost/package.json index 251fe7b0b02..60c02d50f64 100644 --- a/extensions/mattermost/package.json +++ b/extensions/mattermost/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/mattermost", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Mattermost channel plugin", "clawdbot": { diff --git a/extensions/mattermost/src/group-mentions.ts b/extensions/mattermost/src/group-mentions.ts index b3fbc7e4f87..773e655ffef 100644 --- a/extensions/mattermost/src/group-mentions.ts +++ b/extensions/mattermost/src/group-mentions.ts @@ -11,4 +11,4 @@ export function resolveMattermostGroupRequireMention( }); if (typeof account.requireMention === "boolean") return account.requireMention; return true; -} \ No newline at end of file +} diff --git a/extensions/mattermost/src/mattermost/accounts.ts b/extensions/mattermost/src/mattermost/accounts.ts index 6af1b3e4c0f..e75f345938c 100644 --- a/extensions/mattermost/src/mattermost/accounts.ts +++ b/extensions/mattermost/src/mattermost/accounts.ts @@ -112,4 +112,4 @@ export function listEnabledMattermostAccounts(cfg: ClawdbotConfig): ResolvedMatt return listMattermostAccountIds(cfg) .map((accountId) => resolveMattermostAccount({ cfg, accountId })) .filter((account) => account.enabled); -} \ No newline at end of file +} diff --git a/extensions/mattermost/src/mattermost/client.ts b/extensions/mattermost/src/mattermost/client.ts index 277139d5df5..6b63f830fa6 100644 --- a/extensions/mattermost/src/mattermost/client.ts +++ b/extensions/mattermost/src/mattermost/client.ts @@ -205,4 +205,4 @@ export async function uploadMattermostFile( throw new Error("Mattermost file upload failed"); } return info; -} \ No newline at end of file +} diff --git a/extensions/mattermost/src/mattermost/monitor-helpers.ts b/extensions/mattermost/src/mattermost/monitor-helpers.ts index 2aa00f1586a..8c68a4f255d 100644 --- a/extensions/mattermost/src/mattermost/monitor-helpers.ts +++ b/extensions/mattermost/src/mattermost/monitor-helpers.ts @@ -147,4 +147,4 @@ export function resolveThreadSessionKeys(params: { ? `${params.baseSessionKey}:thread:${threadId}` : params.baseSessionKey; return { sessionKey, parentSessionKey: params.parentSessionKey }; -} \ No newline at end of file +} diff --git a/extensions/mattermost/src/mattermost/probe.ts b/extensions/mattermost/src/mattermost/probe.ts index 0286979f6f0..c0fa8ae633c 100644 --- a/extensions/mattermost/src/mattermost/probe.ts +++ b/extensions/mattermost/src/mattermost/probe.ts @@ -67,4 +67,4 @@ export async function probeMattermost( } finally { if (timer) clearTimeout(timer); } -} \ No newline at end of file +} diff --git a/extensions/mattermost/src/onboarding-helpers.ts b/extensions/mattermost/src/onboarding-helpers.ts index f442992226d..8a5d1f58524 100644 --- a/extensions/mattermost/src/onboarding-helpers.ts +++ b/extensions/mattermost/src/onboarding-helpers.ts @@ -39,4 +39,4 @@ export async function promptAccountId(params: PromptAccountIdParams): Promise=2026.1.23-1" + "clawdbot": ">=2026.1.25" } } diff --git a/extensions/memory-lancedb/package.json b/extensions/memory-lancedb/package.json index 4f0e9737714..e003f589012 100644 --- a/extensions/memory-lancedb/package.json +++ b/extensions/memory-lancedb/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/memory-lancedb", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot LanceDB-backed long-term memory plugin with auto-recall/capture", "dependencies": { diff --git a/extensions/msteams/package.json b/extensions/msteams/package.json index 80d566e7c16..b94f8e76a10 100644 --- a/extensions/msteams/package.json +++ b/extensions/msteams/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/msteams", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Microsoft Teams channel plugin", "clawdbot": { diff --git a/extensions/nextcloud-talk/package.json b/extensions/nextcloud-talk/package.json index 5c6f5e243a3..2da3f3b2ad4 100644 --- a/extensions/nextcloud-talk/package.json +++ b/extensions/nextcloud-talk/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/nextcloud-talk", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Nextcloud Talk channel plugin", "clawdbot": { diff --git a/extensions/nostr/package.json b/extensions/nostr/package.json index b415ffe830c..b2fb4b799b9 100644 --- a/extensions/nostr/package.json +++ b/extensions/nostr/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/nostr", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Nostr channel plugin for NIP-04 encrypted DMs", "clawdbot": { diff --git a/extensions/open-prose/package.json b/extensions/open-prose/package.json index 3fa6e8b1734..052201205bf 100644 --- a/extensions/open-prose/package.json +++ b/extensions/open-prose/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/open-prose", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "OpenProse VM skill pack plugin (slash command + telemetry).", "clawdbot": { diff --git a/extensions/open-prose/skills/prose/examples/28-automated-pr-review.prose b/extensions/open-prose/skills/prose/examples/28-automated-pr-review.prose index 05354416965..7e3d6921dbc 100644 --- a/extensions/open-prose/skills/prose/examples/28-automated-pr-review.prose +++ b/extensions/open-prose/skills/prose/examples/28-automated-pr-review.prose @@ -22,11 +22,11 @@ parallel: security = session: security_expert prompt: "Perform a deep security audit of the changes. Look for OWASP top 10 issues." context: overview - + perf = session: performance_expert prompt: "Analyze the performance implications. Identify potential bottlenecks or regressions." context: overview - + style = session: reviewer prompt: "Review for code style, maintainability, and adherence to best practices." context: overview diff --git a/extensions/signal/package.json b/extensions/signal/package.json index 89de3354473..65948eb7baf 100644 --- a/extensions/signal/package.json +++ b/extensions/signal/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/signal", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Signal channel plugin", "clawdbot": { diff --git a/extensions/slack/package.json b/extensions/slack/package.json index f129515f5d5..5bd452d2ead 100644 --- a/extensions/slack/package.json +++ b/extensions/slack/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/slack", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Slack channel plugin", "clawdbot": { diff --git a/extensions/telegram/package.json b/extensions/telegram/package.json index e4005c739ed..64d3d7deac3 100644 --- a/extensions/telegram/package.json +++ b/extensions/telegram/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/telegram", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Telegram channel plugin", "clawdbot": { diff --git a/extensions/tlon/package.json b/extensions/tlon/package.json index 6fd64d03fcc..06750126da8 100644 --- a/extensions/tlon/package.json +++ b/extensions/tlon/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/tlon", - "version": "2026.1.22", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Tlon/Urbit channel plugin", "clawdbot": { diff --git a/extensions/tlon/src/urbit/send.ts b/extensions/tlon/src/urbit/send.ts index 35f7f2d740b..621bbd69a31 100644 --- a/extensions/tlon/src/urbit/send.ts +++ b/extensions/tlon/src/urbit/send.ts @@ -63,16 +63,28 @@ export async function sendGroupMessage({ const story = [{ inline: [text] }]; const sentAt = Date.now(); + // Format reply ID as @ud (with dots) - required for Tlon to recognize thread replies + let formattedReplyId = replyToId; + if (replyToId && /^\d+$/.test(replyToId)) { + try { + formattedReplyId = formatUd(BigInt(replyToId)); + } catch { + // Fall back to raw ID if formatting fails + } + } + const action = { channel: { nest: `chat/${hostShip}/${channelName}`, - action: replyToId + action: formattedReplyId ? { - reply: { - id: replyToId, - delta: { - add: { - memo: { + // Thread reply - needs post wrapper around reply action + // ReplyActionAdd takes Memo: {content, author, sent} - no kind/blob/meta + post: { + reply: { + id: formattedReplyId, + action: { + add: { content: story, author: fromShip, sent: sentAt, @@ -82,6 +94,7 @@ export async function sendGroupMessage({ }, } : { + // Regular post post: { add: { content: story, diff --git a/extensions/voice-call/CHANGELOG.md b/extensions/voice-call/CHANGELOG.md index 0edc0dcb8eb..a8721d47db5 100644 --- a/extensions/voice-call/CHANGELOG.md +++ b/extensions/voice-call/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 2026.1.25 + +### Changes +- Breaking: voice-call TTS now uses core `messages.tts` (plugin TTS config deep‑merges with core). +- Telephony TTS supports OpenAI + ElevenLabs; Edge TTS is ignored for calls. +- Removed legacy `tts.model`/`tts.voice`/`tts.instructions` plugin fields. + ## 2026.1.23 ### Changes diff --git a/extensions/voice-call/README.md b/extensions/voice-call/README.md index 11ff8324ad8..d96f9039208 100644 --- a/extensions/voice-call/README.md +++ b/extensions/voice-call/README.md @@ -75,6 +75,27 @@ Notes: - Twilio/Telnyx/Plivo require a **publicly reachable** webhook URL. - `mock` is a local dev provider (no network calls). +## TTS for calls + +Voice Call uses the core `messages.tts` configuration (OpenAI or ElevenLabs) for +streaming speech on calls. You can override it under the plugin config with the +same shape — overrides deep-merge with `messages.tts`. + +```json5 +{ + tts: { + provider: "openai", + openai: { + voice: "alloy" + } + } +} +``` + +Notes: +- Edge TTS is ignored for voice calls (telephony audio needs PCM; Edge output is unreliable). +- Core TTS is used when Twilio media streaming is enabled; otherwise calls fall back to provider native voices. + ## CLI ```bash diff --git a/extensions/voice-call/clawdbot.plugin.json b/extensions/voice-call/clawdbot.plugin.json index fca4a1ea0ff..2a4f044661a 100644 --- a/extensions/voice-call/clawdbot.plugin.json +++ b/extensions/voice-call/clawdbot.plugin.json @@ -99,16 +99,39 @@ "label": "Media Stream Path", "advanced": true }, - "tts.model": { - "label": "TTS Model", + "tts.provider": { + "label": "TTS Provider Override", + "help": "Deep-merges with messages.tts (Edge is ignored for calls).", "advanced": true }, - "tts.voice": { - "label": "TTS Voice", + "tts.openai.model": { + "label": "OpenAI TTS Model", "advanced": true }, - "tts.instructions": { - "label": "TTS Instructions", + "tts.openai.voice": { + "label": "OpenAI TTS Voice", + "advanced": true + }, + "tts.openai.apiKey": { + "label": "OpenAI API Key", + "sensitive": true, + "advanced": true + }, + "tts.elevenlabs.modelId": { + "label": "ElevenLabs Model ID", + "advanced": true + }, + "tts.elevenlabs.voiceId": { + "label": "ElevenLabs Voice ID", + "advanced": true + }, + "tts.elevenlabs.apiKey": { + "label": "ElevenLabs API Key", + "sensitive": true, + "advanced": true + }, + "tts.elevenlabs.baseUrl": { + "label": "ElevenLabs Base URL", "advanced": true }, "publicUrl": { @@ -370,20 +393,193 @@ "type": "object", "additionalProperties": false, "properties": { + "auto": { + "type": "string", + "enum": [ + "off", + "always", + "inbound", + "tagged" + ] + }, + "enabled": { + "type": "boolean" + }, + "mode": { + "type": "string", + "enum": [ + "final", + "all" + ] + }, "provider": { "type": "string", "enum": [ - "openai" + "openai", + "elevenlabs", + "edge" ] }, - "model": { + "summaryModel": { "type": "string" }, - "voice": { + "modelOverrides": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "allowText": { + "type": "boolean" + }, + "allowProvider": { + "type": "boolean" + }, + "allowVoice": { + "type": "boolean" + }, + "allowModelId": { + "type": "boolean" + }, + "allowVoiceSettings": { + "type": "boolean" + }, + "allowNormalization": { + "type": "boolean" + }, + "allowSeed": { + "type": "boolean" + } + } + }, + "elevenlabs": { + "type": "object", + "additionalProperties": false, + "properties": { + "apiKey": { + "type": "string" + }, + "baseUrl": { + "type": "string" + }, + "voiceId": { + "type": "string" + }, + "modelId": { + "type": "string" + }, + "seed": { + "type": "integer", + "minimum": 0, + "maximum": 4294967295 + }, + "applyTextNormalization": { + "type": "string", + "enum": [ + "auto", + "on", + "off" + ] + }, + "languageCode": { + "type": "string" + }, + "voiceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "stability": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "similarityBoost": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "style": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "useSpeakerBoost": { + "type": "boolean" + }, + "speed": { + "type": "number", + "minimum": 0.5, + "maximum": 2 + } + } + } + } + }, + "openai": { + "type": "object", + "additionalProperties": false, + "properties": { + "apiKey": { + "type": "string" + }, + "model": { + "type": "string" + }, + "voice": { + "type": "string" + } + } + }, + "edge": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "voice": { + "type": "string" + }, + "lang": { + "type": "string" + }, + "outputFormat": { + "type": "string" + }, + "pitch": { + "type": "string" + }, + "rate": { + "type": "string" + }, + "volume": { + "type": "string" + }, + "saveSubtitles": { + "type": "boolean" + }, + "proxy": { + "type": "string" + }, + "timeoutMs": { + "type": "integer", + "minimum": 1000, + "maximum": 120000 + } + } + }, + "prefsPath": { "type": "string" }, - "instructions": { - "type": "string" + "maxTextLength": { + "type": "integer", + "minimum": 1 + }, + "timeoutMs": { + "type": "integer", + "minimum": 1000, + "maximum": 120000 } } }, diff --git a/extensions/voice-call/index.ts b/extensions/voice-call/index.ts index f0fc8e3ada9..760726faa85 100644 --- a/extensions/voice-call/index.ts +++ b/extensions/voice-call/index.ts @@ -74,9 +74,26 @@ const voiceCallConfigSchema = { }, "streaming.sttModel": { label: "Realtime STT Model", advanced: true }, "streaming.streamPath": { label: "Media Stream Path", advanced: true }, - "tts.model": { label: "TTS Model", advanced: true }, - "tts.voice": { label: "TTS Voice", advanced: true }, - "tts.instructions": { label: "TTS Instructions", advanced: true }, + "tts.provider": { + label: "TTS Provider Override", + help: "Deep-merges with messages.tts (Edge is ignored for calls).", + advanced: true, + }, + "tts.openai.model": { label: "OpenAI TTS Model", advanced: true }, + "tts.openai.voice": { label: "OpenAI TTS Voice", advanced: true }, + "tts.openai.apiKey": { + label: "OpenAI API Key", + sensitive: true, + advanced: true, + }, + "tts.elevenlabs.modelId": { label: "ElevenLabs Model ID", advanced: true }, + "tts.elevenlabs.voiceId": { label: "ElevenLabs Voice ID", advanced: true }, + "tts.elevenlabs.apiKey": { + label: "ElevenLabs API Key", + sensitive: true, + advanced: true, + }, + "tts.elevenlabs.baseUrl": { label: "ElevenLabs Base URL", advanced: true }, publicUrl: { label: "Public Webhook URL", advanced: true }, skipSignatureVerification: { label: "Skip Signature Verification", @@ -161,6 +178,7 @@ const voiceCallPlugin = { runtimePromise = createVoiceCallRuntime({ config: cfg, coreConfig: api.config as CoreConfig, + ttsRuntime: api.runtime.tts, logger: api.logger, }); } diff --git a/extensions/voice-call/package.json b/extensions/voice-call/package.json index 248b0cb8b3e..31b171f76b4 100644 --- a/extensions/voice-call/package.json +++ b/extensions/voice-call/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/voice-call", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot voice-call plugin", "dependencies": { diff --git a/extensions/voice-call/src/config.ts b/extensions/voice-call/src/config.ts index 403a2eb899d..1a3a9bbbd49 100644 --- a/extensions/voice-call/src/config.ts +++ b/extensions/voice-call/src/config.ts @@ -82,31 +82,82 @@ export const SttConfigSchema = z .default({ provider: "openai", model: "whisper-1" }); export type SttConfig = z.infer; +export const TtsProviderSchema = z.enum(["openai", "elevenlabs", "edge"]); +export const TtsModeSchema = z.enum(["final", "all"]); +export const TtsAutoSchema = z.enum(["off", "always", "inbound", "tagged"]); + export const TtsConfigSchema = z .object({ - /** TTS provider (currently only OpenAI supported) */ - provider: z.literal("openai").default("openai"), - /** - * TTS model to use: - * - gpt-4o-mini-tts: newest, supports instructions for tone/style control (recommended) - * - tts-1: lower latency - * - tts-1-hd: higher quality - */ - model: z.string().min(1).default("gpt-4o-mini-tts"), - /** - * Voice ID. For best quality, use marin or cedar. - * All voices: alloy, ash, ballad, coral, echo, fable, nova, onyx, sage, shimmer, verse, marin, cedar - */ - voice: z.string().min(1).default("coral"), - /** - * Instructions for speech style (only works with gpt-4o-mini-tts). - * Examples: "Speak in a cheerful tone", "Talk like a sympathetic customer service agent" - */ - instructions: z.string().optional(), + auto: TtsAutoSchema.optional(), + enabled: z.boolean().optional(), + mode: TtsModeSchema.optional(), + provider: TtsProviderSchema.optional(), + summaryModel: z.string().optional(), + modelOverrides: z + .object({ + enabled: z.boolean().optional(), + allowText: z.boolean().optional(), + allowProvider: z.boolean().optional(), + allowVoice: z.boolean().optional(), + allowModelId: z.boolean().optional(), + allowVoiceSettings: z.boolean().optional(), + allowNormalization: z.boolean().optional(), + allowSeed: z.boolean().optional(), + }) + .strict() + .optional(), + elevenlabs: z + .object({ + apiKey: z.string().optional(), + baseUrl: z.string().optional(), + voiceId: z.string().optional(), + modelId: z.string().optional(), + seed: z.number().int().min(0).max(4294967295).optional(), + applyTextNormalization: z.enum(["auto", "on", "off"]).optional(), + languageCode: z.string().optional(), + voiceSettings: z + .object({ + stability: z.number().min(0).max(1).optional(), + similarityBoost: z.number().min(0).max(1).optional(), + style: z.number().min(0).max(1).optional(), + useSpeakerBoost: z.boolean().optional(), + speed: z.number().min(0.5).max(2).optional(), + }) + .strict() + .optional(), + }) + .strict() + .optional(), + openai: z + .object({ + apiKey: z.string().optional(), + model: z.string().optional(), + voice: z.string().optional(), + }) + .strict() + .optional(), + edge: z + .object({ + enabled: z.boolean().optional(), + voice: z.string().optional(), + lang: z.string().optional(), + outputFormat: z.string().optional(), + pitch: z.string().optional(), + rate: z.string().optional(), + volume: z.string().optional(), + saveSubtitles: z.boolean().optional(), + proxy: z.string().optional(), + timeoutMs: z.number().int().min(1000).max(120000).optional(), + }) + .strict() + .optional(), + prefsPath: z.string().optional(), + maxTextLength: z.number().int().min(1).optional(), + timeoutMs: z.number().int().min(1000).max(120000).optional(), }) .strict() - .default({ provider: "openai", model: "gpt-4o-mini-tts", voice: "coral" }); -export type TtsConfig = z.infer; + .optional(); +export type VoiceCallTtsConfig = z.infer; // ----------------------------------------------------------------------------- // Webhook Server Configuration @@ -307,7 +358,7 @@ export const VoiceCallConfigSchema = z /** STT configuration */ stt: SttConfigSchema, - /** TTS configuration */ + /** TTS override (deep-merges with core messages.tts) */ tts: TtsConfigSchema, /** Store path for call logs */ diff --git a/extensions/voice-call/src/core-bridge.ts b/extensions/voice-call/src/core-bridge.ts index 23f3f725085..a1d01e10fd7 100644 --- a/extensions/voice-call/src/core-bridge.ts +++ b/extensions/voice-call/src/core-bridge.ts @@ -2,10 +2,16 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; +import type { VoiceCallTtsConfig } from "./config.js"; + export type CoreConfig = { session?: { store?: string; }; + messages?: { + tts?: VoiceCallTtsConfig; + }; + [key: string]: unknown; }; type CoreAgentDeps = { diff --git a/extensions/voice-call/src/manager.ts b/extensions/voice-call/src/manager.ts index 49d690053cb..2e2e4661b92 100644 --- a/extensions/voice-call/src/manager.ts +++ b/extensions/voice-call/src/manager.ts @@ -143,7 +143,7 @@ export class CallManager { // For notify mode with a message, use inline TwiML with let inlineTwiml: string | undefined; if (mode === "notify" && initialMessage) { - const pollyVoice = mapVoiceToPolly(this.config.tts.voice); + const pollyVoice = mapVoiceToPolly(this.config.tts?.openai?.voice); inlineTwiml = this.generateNotifyTwiml(initialMessage, pollyVoice); console.log( `[voice-call] Using inline TwiML for notify mode (voice: ${pollyVoice})`, @@ -210,11 +210,13 @@ export class CallManager { this.addTranscriptEntry(call, "bot", text); // Play TTS + const voice = + this.provider?.name === "twilio" ? this.config.tts?.openai?.voice : undefined; await this.provider.playTts({ callId, providerCallId: call.providerCallId, text, - voice: this.config.tts.voice, + voice, }); return { success: true }; diff --git a/extensions/voice-call/src/manager/context.ts b/extensions/voice-call/src/manager/context.ts index 38fc7aa1b90..846dd745030 100644 --- a/extensions/voice-call/src/manager/context.ts +++ b/extensions/voice-call/src/manager/context.ts @@ -19,4 +19,3 @@ export type CallManagerContext = { transcriptWaiters: Map; maxDurationTimers: Map; }; - diff --git a/extensions/voice-call/src/manager/events.ts b/extensions/voice-call/src/manager/events.ts index 8cf7ad5b482..b9da95ea222 100644 --- a/extensions/voice-call/src/manager/events.ts +++ b/extensions/voice-call/src/manager/events.ts @@ -175,4 +175,3 @@ export function processEvent(ctx: CallManagerContext, event: NormalizedEvent): v persistCallRecord(ctx.storePath, call); } - diff --git a/extensions/voice-call/src/manager/lookup.ts b/extensions/voice-call/src/manager/lookup.ts index bc0b5f8eedd..99ac246b199 100644 --- a/extensions/voice-call/src/manager/lookup.ts +++ b/extensions/voice-call/src/manager/lookup.ts @@ -31,4 +31,3 @@ export function findCall(params: { providerCallId: params.callIdOrProviderCallId, }); } - diff --git a/extensions/voice-call/src/manager/outbound.ts b/extensions/voice-call/src/manager/outbound.ts index 6cb0372528f..76bdc5a1af3 100644 --- a/extensions/voice-call/src/manager/outbound.ts +++ b/extensions/voice-call/src/manager/outbound.ts @@ -68,7 +68,7 @@ export async function initiateCall( // For notify mode with a message, use inline TwiML with . let inlineTwiml: string | undefined; if (mode === "notify" && initialMessage) { - const pollyVoice = mapVoiceToPolly(ctx.config.tts.voice); + const pollyVoice = mapVoiceToPolly(ctx.config.tts?.openai?.voice); inlineTwiml = generateNotifyTwiml(initialMessage, pollyVoice); console.log(`[voice-call] Using inline TwiML for notify mode (voice: ${pollyVoice})`); } @@ -120,11 +120,13 @@ export async function speak( addTranscriptEntry(call, "bot", text); + const voice = + ctx.provider?.name === "twilio" ? ctx.config.tts?.openai?.voice : undefined; await ctx.provider.playTts({ callId, providerCallId: call.providerCallId, text, - voice: ctx.config.tts.voice, + voice, }); return { success: true }; @@ -244,4 +246,3 @@ export async function endCall( return { success: false, error: err instanceof Error ? err.message : String(err) }; } } - diff --git a/extensions/voice-call/src/manager/state.ts b/extensions/voice-call/src/manager/state.ts index 7131d6b7d24..37d460a1df5 100644 --- a/extensions/voice-call/src/manager/state.ts +++ b/extensions/voice-call/src/manager/state.ts @@ -48,4 +48,3 @@ export function addTranscriptEntry( }; call.transcript.push(entry); } - diff --git a/extensions/voice-call/src/manager/store.ts b/extensions/voice-call/src/manager/store.ts index 96525479a67..9200b684ddd 100644 --- a/extensions/voice-call/src/manager/store.ts +++ b/extensions/voice-call/src/manager/store.ts @@ -86,4 +86,3 @@ export async function getCallHistoryFromStore( return calls; } - diff --git a/extensions/voice-call/src/manager/timers.ts b/extensions/voice-call/src/manager/timers.ts index d56a26fc7f3..2effcdf0f1a 100644 --- a/extensions/voice-call/src/manager/timers.ts +++ b/extensions/voice-call/src/manager/timers.ts @@ -84,4 +84,3 @@ export function waitForFinalTranscript( ctx.transcriptWaiters.set(callId, { resolve, reject, timeout }); }); } - diff --git a/extensions/voice-call/src/manager/twiml.ts b/extensions/voice-call/src/manager/twiml.ts index d6c1dd038ce..588df559057 100644 --- a/extensions/voice-call/src/manager/twiml.ts +++ b/extensions/voice-call/src/manager/twiml.ts @@ -7,4 +7,3 @@ export function generateNotifyTwiml(message: string, voice: string): string { `; } - diff --git a/extensions/voice-call/src/media-stream.test.ts b/extensions/voice-call/src/media-stream.test.ts new file mode 100644 index 00000000000..77344512147 --- /dev/null +++ b/extensions/voice-call/src/media-stream.test.ts @@ -0,0 +1,97 @@ +import { describe, expect, it } from "vitest"; + +import type { + OpenAIRealtimeSTTProvider, + RealtimeSTTSession, +} from "./providers/stt-openai-realtime.js"; +import { MediaStreamHandler } from "./media-stream.js"; + +const createStubSession = (): RealtimeSTTSession => ({ + connect: async () => {}, + sendAudio: () => {}, + waitForTranscript: async () => "", + onPartial: () => {}, + onTranscript: () => {}, + onSpeechStart: () => {}, + close: () => {}, + isConnected: () => true, +}); + +const createStubSttProvider = (): OpenAIRealtimeSTTProvider => + ({ + createSession: () => createStubSession(), + }) as unknown as OpenAIRealtimeSTTProvider; + +const flush = async (): Promise => { + await new Promise((resolve) => setTimeout(resolve, 0)); +}; + +const waitForAbort = (signal: AbortSignal): Promise => + new Promise((resolve) => { + if (signal.aborted) { + resolve(); + return; + } + signal.addEventListener("abort", () => resolve(), { once: true }); + }); + +describe("MediaStreamHandler TTS queue", () => { + it("serializes TTS playback and resolves in order", async () => { + const handler = new MediaStreamHandler({ + sttProvider: createStubSttProvider(), + }); + const started: number[] = []; + const finished: number[] = []; + + let resolveFirst!: () => void; + const firstGate = new Promise((resolve) => { + resolveFirst = resolve; + }); + + const first = handler.queueTts("stream-1", async () => { + started.push(1); + await firstGate; + finished.push(1); + }); + const second = handler.queueTts("stream-1", async () => { + started.push(2); + finished.push(2); + }); + + await flush(); + expect(started).toEqual([1]); + + resolveFirst(); + await first; + await second; + + expect(started).toEqual([1, 2]); + expect(finished).toEqual([1, 2]); + }); + + it("cancels active playback and clears queued items", async () => { + const handler = new MediaStreamHandler({ + sttProvider: createStubSttProvider(), + }); + + let queuedRan = false; + const started: string[] = []; + + const active = handler.queueTts("stream-1", async (signal) => { + started.push("active"); + await waitForAbort(signal); + }); + void handler.queueTts("stream-1", async () => { + queuedRan = true; + }); + + await flush(); + expect(started).toEqual(["active"]); + + handler.clearTtsQueue("stream-1"); + await active; + await flush(); + + expect(queuedRan).toBe(false); + }); +}); diff --git a/extensions/voice-call/src/media-stream.ts b/extensions/voice-call/src/media-stream.ts index 252b6b331bc..e14dc913724 100644 --- a/extensions/voice-call/src/media-stream.ts +++ b/extensions/voice-call/src/media-stream.ts @@ -29,6 +29,8 @@ export interface MediaStreamConfig { onPartialTranscript?: (callId: string, partial: string) => void; /** Callback when stream connects */ onConnect?: (callId: string, streamSid: string) => void; + /** Callback when speech starts (barge-in) */ + onSpeechStart?: (callId: string) => void; /** Callback when stream disconnects */ onDisconnect?: (callId: string) => void; } @@ -43,6 +45,13 @@ interface StreamSession { sttSession: RealtimeSTTSession; } +type TtsQueueEntry = { + playFn: (signal: AbortSignal) => Promise; + controller: AbortController; + resolve: () => void; + reject: (error: unknown) => void; +}; + /** * Manages WebSocket connections for Twilio media streams. */ @@ -50,6 +59,12 @@ export class MediaStreamHandler { private wss: WebSocketServer | null = null; private sessions = new Map(); private config: MediaStreamConfig; + /** TTS playback queues per stream (serialize audio to prevent overlap) */ + private ttsQueues = new Map(); + /** Whether TTS is currently playing per stream */ + private ttsPlaying = new Map(); + /** Active TTS playback controllers per stream */ + private ttsActiveControllers = new Map(); constructor(config: MediaStreamConfig) { this.config = config; @@ -148,6 +163,10 @@ export class MediaStreamHandler { this.config.onTranscript?.(callSid, transcript); }); + sttSession.onSpeechStart(() => { + this.config.onSpeechStart?.(callSid); + }); + const session: StreamSession = { callId: callSid, streamSid, @@ -177,6 +196,7 @@ export class MediaStreamHandler { private handleStop(session: StreamSession): void { console.log(`[MediaStream] Stream stopped: ${session.streamSid}`); + this.clearTtsState(session.streamSid); session.sttSession.close(); this.sessions.delete(session.streamSid); this.config.onDisconnect?.(session.callId); @@ -228,6 +248,46 @@ export class MediaStreamHandler { this.sendToStream(streamSid, { event: "clear", streamSid }); } + /** + * Queue a TTS operation for sequential playback. + * Only one TTS operation plays at a time per stream to prevent overlap. + */ + async queueTts( + streamSid: string, + playFn: (signal: AbortSignal) => Promise, + ): Promise { + const queue = this.getTtsQueue(streamSid); + let resolveEntry: () => void; + let rejectEntry: (error: unknown) => void; + const promise = new Promise((resolve, reject) => { + resolveEntry = resolve; + rejectEntry = reject; + }); + + queue.push({ + playFn, + controller: new AbortController(), + resolve: resolveEntry!, + reject: rejectEntry!, + }); + + if (!this.ttsPlaying.get(streamSid)) { + void this.processQueue(streamSid); + } + + return promise; + } + + /** + * Clear TTS queue and interrupt current playback (barge-in). + */ + clearTtsQueue(streamSid: string): void { + const queue = this.getTtsQueue(streamSid); + queue.length = 0; + this.ttsActiveControllers.get(streamSid)?.abort(); + this.clearAudio(streamSid); + } + /** * Get active session by call ID. */ @@ -242,11 +302,65 @@ export class MediaStreamHandler { */ closeAll(): void { for (const session of this.sessions.values()) { + this.clearTtsState(session.streamSid); session.sttSession.close(); session.ws.close(); } this.sessions.clear(); } + + private getTtsQueue(streamSid: string): TtsQueueEntry[] { + const existing = this.ttsQueues.get(streamSid); + if (existing) return existing; + const queue: TtsQueueEntry[] = []; + this.ttsQueues.set(streamSid, queue); + return queue; + } + + /** + * Process the TTS queue for a stream. + * Uses iterative approach to avoid stack accumulation from recursion. + */ + private async processQueue(streamSid: string): Promise { + this.ttsPlaying.set(streamSid, true); + + while (true) { + const queue = this.ttsQueues.get(streamSid); + if (!queue || queue.length === 0) { + this.ttsPlaying.set(streamSid, false); + this.ttsActiveControllers.delete(streamSid); + return; + } + + const entry = queue.shift()!; + this.ttsActiveControllers.set(streamSid, entry.controller); + + try { + await entry.playFn(entry.controller.signal); + entry.resolve(); + } catch (error) { + if (entry.controller.signal.aborted) { + entry.resolve(); + } else { + console.error("[MediaStream] TTS playback error:", error); + entry.reject(error); + } + } finally { + if (this.ttsActiveControllers.get(streamSid) === entry.controller) { + this.ttsActiveControllers.delete(streamSid); + } + } + } + } + + private clearTtsState(streamSid: string): void { + const queue = this.ttsQueues.get(streamSid); + if (queue) queue.length = 0; + this.ttsActiveControllers.get(streamSid)?.abort(); + this.ttsActiveControllers.delete(streamSid); + this.ttsPlaying.delete(streamSid); + this.ttsQueues.delete(streamSid); + } } /** diff --git a/extensions/voice-call/src/providers/plivo.test.ts b/extensions/voice-call/src/providers/plivo.test.ts index 0674a7dd245..e2aa6289bc4 100644 --- a/extensions/voice-call/src/providers/plivo.test.ts +++ b/extensions/voice-call/src/providers/plivo.test.ts @@ -26,4 +26,3 @@ describe("PlivoProvider", () => { expect(result.providerResponseBody).toContain('length="300"'); }); }); - diff --git a/extensions/voice-call/src/providers/stt-openai-realtime.ts b/extensions/voice-call/src/providers/stt-openai-realtime.ts index 01c698f21ab..5cd52658dfa 100644 --- a/extensions/voice-call/src/providers/stt-openai-realtime.ts +++ b/extensions/voice-call/src/providers/stt-openai-realtime.ts @@ -38,6 +38,8 @@ export interface RealtimeSTTSession { onPartial(callback: (partial: string) => void): void; /** Set callback for final transcripts */ onTranscript(callback: (transcript: string) => void): void; + /** Set callback when speech starts (VAD) */ + onSpeechStart(callback: () => void): void; /** Close the session */ close(): void; /** Check if session is connected */ @@ -91,6 +93,7 @@ class OpenAIRealtimeSTTSession implements RealtimeSTTSession { private pendingTranscript = ""; private onTranscriptCallback: ((transcript: string) => void) | null = null; private onPartialCallback: ((partial: string) => void) | null = null; + private onSpeechStartCallback: (() => void) | null = null; constructor( private readonly apiKey: string, @@ -243,6 +246,7 @@ class OpenAIRealtimeSTTSession implements RealtimeSTTSession { case "input_audio_buffer.speech_started": console.log("[RealtimeSTT] Speech started"); this.pendingTranscript = ""; + this.onSpeechStartCallback?.(); break; case "error": @@ -273,6 +277,10 @@ class OpenAIRealtimeSTTSession implements RealtimeSTTSession { this.onTranscriptCallback = callback; } + onSpeechStart(callback: () => void): void { + this.onSpeechStartCallback = callback; + } + async waitForTranscript(timeoutMs = 30000): Promise { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { diff --git a/extensions/voice-call/src/providers/twilio.ts b/extensions/voice-call/src/providers/twilio.ts index 17102b732f6..be9dd6edae3 100644 --- a/extensions/voice-call/src/providers/twilio.ts +++ b/extensions/voice-call/src/providers/twilio.ts @@ -15,9 +15,9 @@ import type { WebhookVerificationResult, } from "../types.js"; import { escapeXml, mapVoiceToPolly } from "../voice-mapping.js"; +import { chunkAudio } from "../telephony-audio.js"; +import type { TelephonyTtsProvider } from "../telephony-tts.js"; import type { VoiceCallProvider } from "./base.js"; -import type { OpenAITTSProvider } from "./tts-openai.js"; -import { chunkAudio } from "./tts-openai.js"; import { twilioApiRequest } from "./twilio/api.js"; import { verifyTwilioProviderWebhook } from "./twilio/webhook.js"; @@ -53,8 +53,8 @@ export class TwilioProvider implements VoiceCallProvider { /** Current public webhook URL (set when tunnel starts or from config) */ private currentPublicUrl: string | null = null; - /** Optional OpenAI TTS provider for streaming TTS */ - private ttsProvider: OpenAITTSProvider | null = null; + /** Optional telephony TTS provider for streaming TTS */ + private ttsProvider: TelephonyTtsProvider | null = null; /** Optional media stream handler for sending audio */ private mediaStreamHandler: MediaStreamHandler | null = null; @@ -119,7 +119,7 @@ export class TwilioProvider implements VoiceCallProvider { return this.currentPublicUrl; } - setTTSProvider(provider: OpenAITTSProvider): void { + setTTSProvider(provider: TelephonyTtsProvider): void { this.ttsProvider = provider; } @@ -135,6 +135,17 @@ export class TwilioProvider implements VoiceCallProvider { this.callStreamMap.delete(callSid); } + /** + * Clear TTS queue for a call (barge-in). + * Used when user starts speaking to interrupt current TTS playback. + */ + clearTtsQueue(callSid: string): void { + const streamSid = this.callStreamMap.get(callSid); + if (streamSid && this.mediaStreamHandler) { + this.mediaStreamHandler.clearTtsQueue(streamSid); + } + } + /** * Make an authenticated request to the Twilio API. */ @@ -454,13 +465,13 @@ export class TwilioProvider implements VoiceCallProvider { * Play TTS audio via Twilio. * * Two modes: - * 1. OpenAI TTS + Media Streams: If TTS provider and media stream are available, - * generates audio via OpenAI and streams it through WebSocket (preferred). + * 1. Core TTS + Media Streams: If TTS provider and media stream are available, + * generates audio via core TTS and streams it through WebSocket (preferred). * 2. TwiML : Falls back to Twilio's native TTS with Polly voices. * Note: This may not work on all Twilio accounts. */ async playTts(input: PlayTtsInput): Promise { - // Try OpenAI TTS via media stream first (if configured) + // Try telephony TTS via media stream first (if configured) const streamSid = this.callStreamMap.get(input.providerCallId); if (this.ttsProvider && this.mediaStreamHandler && streamSid) { try { @@ -468,7 +479,7 @@ export class TwilioProvider implements VoiceCallProvider { return; } catch (err) { console.warn( - `[voice-call] OpenAI TTS failed, falling back to Twilio :`, + `[voice-call] Telephony TTS failed, falling back to Twilio :`, err instanceof Error ? err.message : err, ); // Fall through to TwiML fallback @@ -484,7 +495,7 @@ export class TwilioProvider implements VoiceCallProvider { } console.warn( - "[voice-call] Using TwiML fallback - OpenAI TTS not configured or media stream not active", + "[voice-call] Using TwiML fallback - telephony TTS not configured or media stream not active", ); const pollyVoice = mapVoiceToPolly(input.voice); @@ -502,9 +513,9 @@ export class TwilioProvider implements VoiceCallProvider { } /** - * Play TTS via OpenAI and Twilio Media Streams. - * Generates audio with OpenAI TTS, converts to mu-law, and streams via WebSocket. - * Uses a jitter buffer to smooth out timing variations. + * Play TTS via core TTS and Twilio Media Streams. + * Generates audio with core TTS, converts to mu-law, and streams via WebSocket. + * Uses a queue to serialize playback and prevent overlapping audio. */ private async playTtsViaStream( text: string, @@ -514,22 +525,29 @@ export class TwilioProvider implements VoiceCallProvider { throw new Error("TTS provider and media stream handler required"); } - // Generate audio with OpenAI TTS (returns mu-law at 8kHz) - const muLawAudio = await this.ttsProvider.synthesizeForTwilio(text); - // Stream audio in 20ms chunks (160 bytes at 8kHz mu-law) const CHUNK_SIZE = 160; const CHUNK_DELAY_MS = 20; - for (const chunk of chunkAudio(muLawAudio, CHUNK_SIZE)) { - this.mediaStreamHandler.sendAudio(streamSid, chunk); + const handler = this.mediaStreamHandler; + const ttsProvider = this.ttsProvider; + await handler.queueTts(streamSid, async (signal) => { + // Generate audio with core TTS (returns mu-law at 8kHz) + const muLawAudio = await ttsProvider.synthesizeForTelephony(text); + for (const chunk of chunkAudio(muLawAudio, CHUNK_SIZE)) { + if (signal.aborted) break; + handler.sendAudio(streamSid, chunk); - // Pace the audio to match real-time playback - await new Promise((resolve) => setTimeout(resolve, CHUNK_DELAY_MS)); - } + // Pace the audio to match real-time playback + await new Promise((resolve) => setTimeout(resolve, CHUNK_DELAY_MS)); + if (signal.aborted) break; + } - // Send a mark to track when audio finishes - this.mediaStreamHandler.sendMark(streamSid, `tts-${Date.now()}`); + if (!signal.aborted) { + // Send a mark to track when audio finishes + handler.sendMark(streamSid, `tts-${Date.now()}`); + } + }); } /** diff --git a/extensions/voice-call/src/providers/twilio/webhook.ts b/extensions/voice-call/src/providers/twilio/webhook.ts index f59342f1432..28f445c88a4 100644 --- a/extensions/voice-call/src/providers/twilio/webhook.ts +++ b/extensions/voice-call/src/providers/twilio/webhook.ts @@ -27,4 +27,3 @@ export function verifyTwilioProviderWebhook(params: { reason: result.reason, }; } - diff --git a/extensions/voice-call/src/runtime.ts b/extensions/voice-call/src/runtime.ts index 08e7e5de23d..0770333cdcc 100644 --- a/extensions/voice-call/src/runtime.ts +++ b/extensions/voice-call/src/runtime.ts @@ -6,8 +6,9 @@ import type { VoiceCallProvider } from "./providers/base.js"; import { MockProvider } from "./providers/mock.js"; import { PlivoProvider } from "./providers/plivo.js"; import { TelnyxProvider } from "./providers/telnyx.js"; -import { OpenAITTSProvider } from "./providers/tts-openai.js"; import { TwilioProvider } from "./providers/twilio.js"; +import type { TelephonyTtsRuntime } from "./telephony-tts.js"; +import { createTelephonyTtsProvider } from "./telephony-tts.js"; import { startTunnel, type TunnelResult } from "./tunnel.js"; import { cleanupTailscaleExposure, @@ -81,9 +82,10 @@ function resolveProvider(config: VoiceCallConfig): VoiceCallProvider { export async function createVoiceCallRuntime(params: { config: VoiceCallConfig; coreConfig: CoreConfig; + ttsRuntime?: TelephonyTtsRuntime; logger?: Logger; }): Promise { - const { config, coreConfig, logger } = params; + const { config, coreConfig, ttsRuntime, logger } = params; const log = logger ?? { info: console.log, warn: console.warn, @@ -149,27 +151,24 @@ export async function createVoiceCallRuntime(params: { if (provider.name === "twilio" && config.streaming?.enabled) { const twilioProvider = provider as TwilioProvider; - const openaiApiKey = - config.streaming.openaiApiKey || process.env.OPENAI_API_KEY; - if (openaiApiKey) { + if (ttsRuntime?.textToSpeechTelephony) { try { - const ttsProvider = new OpenAITTSProvider({ - apiKey: openaiApiKey, - voice: config.tts.voice, - model: config.tts.model, - instructions: config.tts.instructions, + const ttsProvider = createTelephonyTtsProvider({ + coreConfig, + ttsOverride: config.tts, + runtime: ttsRuntime, }); twilioProvider.setTTSProvider(ttsProvider); - log.info("[voice-call] OpenAI TTS provider configured"); + log.info("[voice-call] Telephony TTS provider configured"); } catch (err) { log.warn( - `[voice-call] Failed to initialize OpenAI TTS: ${ + `[voice-call] Failed to initialize telephony TTS: ${ err instanceof Error ? err.message : String(err) }`, ); } } else { - log.warn("[voice-call] OpenAI TTS key missing; streaming TTS disabled"); + log.warn("[voice-call] Telephony TTS unavailable; streaming TTS disabled"); } const mediaHandler = webhookServer.getMediaStreamHandler(); diff --git a/extensions/voice-call/src/telephony-audio.ts b/extensions/voice-call/src/telephony-audio.ts new file mode 100644 index 00000000000..6a9a1d22216 --- /dev/null +++ b/extensions/voice-call/src/telephony-audio.ts @@ -0,0 +1,88 @@ +const TELEPHONY_SAMPLE_RATE = 8000; + +function clamp16(value: number): number { + return Math.max(-32768, Math.min(32767, value)); +} + +/** + * Resample 16-bit PCM (little-endian mono) to 8kHz using linear interpolation. + */ +export function resamplePcmTo8k(input: Buffer, inputSampleRate: number): Buffer { + if (inputSampleRate === TELEPHONY_SAMPLE_RATE) return input; + const inputSamples = Math.floor(input.length / 2); + if (inputSamples === 0) return Buffer.alloc(0); + + const ratio = inputSampleRate / TELEPHONY_SAMPLE_RATE; + const outputSamples = Math.floor(inputSamples / ratio); + const output = Buffer.alloc(outputSamples * 2); + + for (let i = 0; i < outputSamples; i++) { + const srcPos = i * ratio; + const srcIndex = Math.floor(srcPos); + const frac = srcPos - srcIndex; + + const s0 = input.readInt16LE(srcIndex * 2); + const s1Index = Math.min(srcIndex + 1, inputSamples - 1); + const s1 = input.readInt16LE(s1Index * 2); + + const sample = Math.round(s0 + frac * (s1 - s0)); + output.writeInt16LE(clamp16(sample), i * 2); + } + + return output; +} + +/** + * Convert 16-bit PCM to 8-bit mu-law (G.711). + */ +export function pcmToMulaw(pcm: Buffer): Buffer { + const samples = Math.floor(pcm.length / 2); + const mulaw = Buffer.alloc(samples); + + for (let i = 0; i < samples; i++) { + const sample = pcm.readInt16LE(i * 2); + mulaw[i] = linearToMulaw(sample); + } + + return mulaw; +} + +export function convertPcmToMulaw8k( + pcm: Buffer, + inputSampleRate: number, +): Buffer { + const pcm8k = resamplePcmTo8k(pcm, inputSampleRate); + return pcmToMulaw(pcm8k); +} + +/** + * Chunk audio buffer into 20ms frames for streaming (8kHz mono mu-law). + */ +export function chunkAudio( + audio: Buffer, + chunkSize = 160, +): Generator { + return (function* () { + for (let i = 0; i < audio.length; i += chunkSize) { + yield audio.subarray(i, Math.min(i + chunkSize, audio.length)); + } + })(); +} + +function linearToMulaw(sample: number): number { + const BIAS = 132; + const CLIP = 32635; + + const sign = sample < 0 ? 0x80 : 0; + if (sample < 0) sample = -sample; + if (sample > CLIP) sample = CLIP; + + sample += BIAS; + let exponent = 7; + for (let expMask = 0x4000; (sample & expMask) === 0 && exponent > 0; exponent--) { + expMask >>= 1; + } + + const mantissa = (sample >> (exponent + 3)) & 0x0f; + return ~(sign | (exponent << 4) | mantissa) & 0xff; +} diff --git a/extensions/voice-call/src/telephony-tts.ts b/extensions/voice-call/src/telephony-tts.ts new file mode 100644 index 00000000000..147501e85a4 --- /dev/null +++ b/extensions/voice-call/src/telephony-tts.ts @@ -0,0 +1,95 @@ +import type { CoreConfig } from "./core-bridge.js"; +import type { VoiceCallTtsConfig } from "./config.js"; +import { convertPcmToMulaw8k } from "./telephony-audio.js"; + +export type TelephonyTtsRuntime = { + textToSpeechTelephony: (params: { + text: string; + cfg: CoreConfig; + prefsPath?: string; + }) => Promise<{ + success: boolean; + audioBuffer?: Buffer; + sampleRate?: number; + provider?: string; + error?: string; + }>; +}; + +export type TelephonyTtsProvider = { + synthesizeForTelephony: (text: string) => Promise; +}; + +export function createTelephonyTtsProvider(params: { + coreConfig: CoreConfig; + ttsOverride?: VoiceCallTtsConfig; + runtime: TelephonyTtsRuntime; +}): TelephonyTtsProvider { + const { coreConfig, ttsOverride, runtime } = params; + const mergedConfig = applyTtsOverride(coreConfig, ttsOverride); + + return { + synthesizeForTelephony: async (text: string) => { + const result = await runtime.textToSpeechTelephony({ + text, + cfg: mergedConfig, + }); + + if (!result.success || !result.audioBuffer || !result.sampleRate) { + throw new Error(result.error ?? "TTS conversion failed"); + } + + return convertPcmToMulaw8k(result.audioBuffer, result.sampleRate); + }, + }; +} + +function applyTtsOverride( + coreConfig: CoreConfig, + override?: VoiceCallTtsConfig, +): CoreConfig { + if (!override) return coreConfig; + + const base = coreConfig.messages?.tts; + const merged = mergeTtsConfig(base, override); + if (!merged) return coreConfig; + + return { + ...coreConfig, + messages: { + ...(coreConfig.messages ?? {}), + tts: merged, + }, + }; +} + +function mergeTtsConfig( + base?: VoiceCallTtsConfig, + override?: VoiceCallTtsConfig, +): VoiceCallTtsConfig | undefined { + if (!base && !override) return undefined; + if (!override) return base; + if (!base) return override; + return deepMerge(base, override); +} + +function deepMerge(base: T, override: T): T { + if (!isPlainObject(base) || !isPlainObject(override)) { + return override; + } + const result: Record = { ...base }; + for (const [key, value] of Object.entries(override)) { + if (value === undefined) continue; + const existing = (base as Record)[key]; + if (isPlainObject(existing) && isPlainObject(value)) { + result[key] = deepMerge(existing, value); + } else { + result[key] = value; + } + } + return result as T; +} + +function isPlainObject(value: unknown): value is Record { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} diff --git a/extensions/voice-call/src/webhook.ts b/extensions/voice-call/src/webhook.ts index c69436d772e..6ab4d0eeda7 100644 --- a/extensions/voice-call/src/webhook.ts +++ b/extensions/voice-call/src/webhook.ts @@ -78,6 +78,11 @@ export class VoiceCallWebhookServer { `[voice-call] Transcript for ${providerCallId}: ${transcript}`, ); + // Clear TTS queue on barge-in (user started speaking, interrupt current playback) + if (this.provider.name === "twilio") { + (this.provider as TwilioProvider).clearTtsQueue(providerCallId); + } + // Look up our internal call ID from the provider call ID const call = this.manager.getCallByProviderCallId(providerCallId); if (!call) { @@ -109,6 +114,11 @@ export class VoiceCallWebhookServer { }); } }, + onSpeechStart: (providerCallId) => { + if (this.provider.name === "twilio") { + (this.provider as TwilioProvider).clearTtsQueue(providerCallId); + } + }, onPartialTranscript: (callId, partial) => { console.log(`[voice-call] Partial for ${callId}: ${partial}`); }, diff --git a/extensions/whatsapp/package.json b/extensions/whatsapp/package.json index 3dcc4cf6be1..b7b57eb51e1 100644 --- a/extensions/whatsapp/package.json +++ b/extensions/whatsapp/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/whatsapp", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot WhatsApp channel plugin", "clawdbot": { diff --git a/extensions/zalo/package.json b/extensions/zalo/package.json index 7ced3106abd..8f077a6b3e6 100644 --- a/extensions/zalo/package.json +++ b/extensions/zalo/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/zalo", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Zalo channel plugin", "clawdbot": { diff --git a/extensions/zalouser/package.json b/extensions/zalouser/package.json index 9f406c56c8a..0ab93d1cec3 100644 --- a/extensions/zalouser/package.json +++ b/extensions/zalouser/package.json @@ -1,6 +1,6 @@ { "name": "@clawdbot/zalouser", - "version": "2026.1.23", + "version": "2026.1.25", "type": "module", "description": "Clawdbot Zalo Personal Account plugin via zca-cli", "dependencies": { diff --git a/extensions/zalouser/src/channel.test.ts b/extensions/zalouser/src/channel.test.ts index 45487edd0bd..123cf358d09 100644 --- a/extensions/zalouser/src/channel.test.ts +++ b/extensions/zalouser/src/channel.test.ts @@ -15,4 +15,3 @@ describe("zalouser outbound chunker", () => { expect(chunks.every((c) => c.length <= limit)).toBe(true); }); }); - diff --git a/package.json b/package.json index bf6d003a5c6..0c63d5d694f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clawdbot", - "version": "2026.1.24-0", + "version": "2026.1.25", "description": "WhatsApp gateway CLI (Baileys web) with Pi RPC agent", "type": "module", "main": "dist/index.js", @@ -33,6 +33,7 @@ "dist/macos/**", "dist/media/**", "dist/media-understanding/**", + "dist/link-understanding/**", "dist/process/**", "dist/plugins/**", "dist/plugin-sdk/**", @@ -42,6 +43,7 @@ "dist/signal/**", "dist/slack/**", "dist/telegram/**", + "dist/line/**", "dist/tui/**", "dist/tts/**", "dist/web/**", @@ -63,6 +65,7 @@ "git-hooks/**", "dist/terminal/**", "dist/routing/**", + "dist/shared/**", "dist/utils/**", "dist/logging/**", "dist/memory/**", @@ -154,6 +157,7 @@ "@grammyjs/runner": "^2.0.3", "@grammyjs/transformer-throttler": "^1.2.1", "@homebridge/ciao": "^1.3.4", + "@line/bot-sdk": "^10.6.0", "@lydell/node-pty": "1.2.0-beta.3", "@mariozechner/pi-agent-core": "0.49.3", "@mariozechner/pi-ai": "0.49.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b36478256dd..14bef9f5ca4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,9 @@ importers: '@homebridge/ciao': specifier: ^1.3.4 version: 1.3.4 + '@line/bot-sdk': + specifier: ^10.6.0 + version: 10.6.0 '@lydell/node-pty': specifier: 1.2.0-beta.3 version: 1.2.0-beta.3 @@ -317,6 +320,12 @@ importers: extensions/imessage: {} + extensions/line: + devDependencies: + clawdbot: + specifier: workspace:* + version: link:../.. + extensions/llm-task: {} extensions/lobster: {} @@ -348,8 +357,8 @@ importers: extensions/memory-core: dependencies: clawdbot: - specifier: '>=2026.1.23-1' - version: 2026.1.23-1(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3) + specifier: '>=2026.1.25' + version: link:../.. extensions/memory-lancedb: dependencies: @@ -1260,6 +1269,10 @@ packages: peerDependencies: apache-arrow: '>=15.0.0 <=18.1.0' + '@line/bot-sdk@10.6.0': + resolution: {integrity: sha512-4hSpglL/G/cW2JCcohaYz/BS0uOSJNV9IEYdMm0EiPEvDLayoI2hGq2D86uYPQFD2gvgkyhmzdShpWLG3P5r3w==} + engines: {node: '>=20'} + '@lit-labs/signals@0.2.0': resolution: {integrity: sha512-68plyIbciumbwKaiilhLNyhz4Vg6/+nJwDufG2xxWA9r/fUw58jxLHCAlKs+q1CE5Lmh3cZ3ShyYKnOCebEpVA==} @@ -2647,6 +2660,9 @@ packages: '@types/node@20.19.30': resolution: {integrity: sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==} + '@types/node@24.10.9': + resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==} + '@types/node@25.0.10': resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==} @@ -3108,11 +3124,6 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} - clawdbot@2026.1.23-1: - resolution: {integrity: sha512-t51ks5bnTRQNCzoTunUJaoeMjamvP3zP5EyyadmI34kXYGIbWcCx242w5XMr5h4sLSw59nBw3lJ74vErWDsz9w==} - engines: {node: '>=22.12.0'} - hasBin: true - cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -6721,6 +6732,14 @@ snapshots: '@lancedb/lancedb-win32-arm64-msvc': 0.23.0 '@lancedb/lancedb-win32-x64-msvc': 0.23.0 + '@line/bot-sdk@10.6.0': + dependencies: + '@types/node': 24.10.9 + optionalDependencies: + axios: 1.13.2(debug@4.4.3) + transitivePeerDependencies: + - debug + '@lit-labs/signals@0.2.0': dependencies: lit: 3.3.2 @@ -8298,6 +8317,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@24.10.9': + dependencies: + undici-types: 7.16.0 + '@types/node@25.0.10': dependencies: undici-types: 7.16.0 @@ -8839,82 +8862,6 @@ snapshots: dependencies: clsx: 2.1.1 - clawdbot@2026.1.23-1(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3): - dependencies: - '@agentclientprotocol/sdk': 0.13.1(zod@4.3.6) - '@aws-sdk/client-bedrock': 3.975.0 - '@buape/carbon': 0.14.0(hono@4.11.4) - '@clack/prompts': 0.11.0 - '@grammyjs/runner': 2.0.3(grammy@1.39.3) - '@grammyjs/transformer-throttler': 1.2.1(grammy@1.39.3) - '@homebridge/ciao': 1.3.4 - '@lydell/node-pty': 1.2.0-beta.3 - '@mariozechner/pi-agent-core': 0.49.3(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-ai': 0.49.3(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-coding-agent': 0.49.3(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-tui': 0.49.3 - '@mozilla/readability': 0.6.0 - '@sinclair/typebox': 0.34.47 - '@slack/bolt': 4.6.0(@types/express@5.0.6) - '@slack/web-api': 7.13.0 - '@whiskeysockets/baileys': 7.0.0-rc.9(audio-decode@2.2.3)(sharp@0.34.5) - ajv: 8.17.1 - body-parser: 2.2.2 - chalk: 5.6.2 - chokidar: 5.0.0 - chromium-bidi: 13.0.1(devtools-protocol@0.0.1561482) - cli-highlight: 2.1.11 - commander: 14.0.2 - croner: 9.1.0 - detect-libc: 2.1.2 - discord-api-types: 0.38.37 - dotenv: 17.2.3 - express: 5.2.1 - file-type: 21.3.0 - grammy: 1.39.3 - hono: 4.11.4 - jiti: 2.6.1 - json5: 2.2.3 - jszip: 3.10.1 - linkedom: 0.18.12 - long: 5.3.2 - markdown-it: 14.1.0 - osc-progress: 0.3.0 - pdfjs-dist: 5.4.530 - playwright-core: 1.58.0 - proper-lockfile: 4.1.2 - qrcode-terminal: 0.12.0 - sharp: 0.34.5 - sqlite-vec: 0.1.7-alpha.2 - tar: 7.5.4 - tslog: 4.10.2 - undici: 7.19.0 - ws: 8.19.0 - yaml: 2.8.2 - zod: 4.3.6 - optionalDependencies: - '@napi-rs/canvas': 0.1.88 - node-llama-cpp: 3.15.0(typescript@5.9.3) - transitivePeerDependencies: - - '@discordjs/opus' - - '@modelcontextprotocol/sdk' - - '@types/express' - - audio-decode - - aws-crt - - bufferutil - - canvas - - debug - - devtools-protocol - - encoding - - ffmpeg-static - - jimp - - link-preview-js - - node-opus - - opusscript - - supports-color - - typescript - - utf-8-validate - cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 diff --git a/render.yaml b/render.yaml new file mode 100644 index 00000000000..01923a8f61b --- /dev/null +++ b/render.yaml @@ -0,0 +1,21 @@ +services: + - type: web + name: clawdbot + runtime: docker + plan: starter + healthCheckPath: /health + envVars: + - key: PORT + value: "8080" + - key: SETUP_PASSWORD + sync: false + - key: CLAWDBOT_STATE_DIR + value: /data/.clawdbot + - key: CLAWDBOT_WORKSPACE_DIR + value: /data/workspace + - key: CLAWDBOT_GATEWAY_TOKEN + generateValue: true + disk: + name: clawdbot-data + mountPath: /data + sizeGB: 1 diff --git a/scripts/clawlog.sh b/scripts/clawlog.sh index 49e87709011..2320e2e7d7b 100755 --- a/scripts/clawlog.sh +++ b/scripts/clawlog.sh @@ -124,7 +124,7 @@ EOF # Function to list categories list_categories() { echo -e "${BLUE}Fetching VibeTunnel log categories from the last hour...${NC}\n" - + # Get unique categories from recent logs log show --predicate "subsystem == \"$SUBSYSTEM\"" --last 1h 2>/dev/null | \ grep -E "category: \"[^\"]+\"" | \ @@ -133,7 +133,7 @@ list_categories() { while read -r cat; do echo " • $cat" done - + echo -e "\n${YELLOW}Note: Only categories with recent activity are shown${NC}" } @@ -230,29 +230,29 @@ fi if [[ "$STREAM_MODE" == true ]]; then # Streaming mode CMD="sudo log stream --predicate '$PREDICATE' --level $LOG_LEVEL --info" - + echo -e "${GREEN}Streaming VibeTunnel logs continuously...${NC}" echo -e "${YELLOW}Press Ctrl+C to stop${NC}\n" else # Show mode CMD="sudo log show --predicate '$PREDICATE'" - + # Add log level for show command if [[ "$LOG_LEVEL" == "debug" ]]; then CMD="$CMD --debug" else CMD="$CMD --info" fi - + # Add time range CMD="$CMD --last $TIME_RANGE" - + if [[ "$SHOW_TAIL" == true ]]; then echo -e "${GREEN}Showing last $TAIL_LINES log lines from the past $TIME_RANGE${NC}" else echo -e "${GREEN}Showing all logs from the past $TIME_RANGE${NC}" fi - + # Show applied filters if [[ "$ERRORS_ONLY" == true ]]; then echo -e "${RED}Filter: Errors only${NC}" @@ -277,14 +277,14 @@ if [[ -n "$OUTPUT_FILE" ]]; then if sudo -n /usr/bin/log show --last 1s 2>&1 | grep -q "password"; then handle_sudo_error fi - + echo -e "${BLUE}Exporting logs to: $OUTPUT_FILE${NC}\n" if [[ "$SHOW_TAIL" == true ]] && [[ "$STREAM_MODE" == false ]]; then eval "$CMD" 2>&1 | tail -n "$TAIL_LINES" > "$OUTPUT_FILE" else eval "$CMD" > "$OUTPUT_FILE" 2>&1 fi - + # Check if file was created and has content if [[ -s "$OUTPUT_FILE" ]]; then LINE_COUNT=$(wc -l < "$OUTPUT_FILE" | tr -d ' ') @@ -298,7 +298,7 @@ else if sudo -n /usr/bin/log show --last 1s 2>&1 | grep -q "password"; then handle_sudo_error fi - + if [[ "$SHOW_TAIL" == true ]] && [[ "$STREAM_MODE" == false ]]; then # Apply tail for non-streaming mode eval "$CMD" 2>&1 | tail -n "$TAIL_LINES" diff --git a/scripts/clawtributors-map.json b/scripts/clawtributors-map.json index 7ad1f926c32..d652938a606 100644 --- a/scripts/clawtributors-map.json +++ b/scripts/clawtributors-map.json @@ -2,6 +2,7 @@ "ensureLogins": [ "odrobnik", "alphonse-arianee", + "aaronn", "ronak-guliani", "cpojer", "carlulsoe", @@ -11,7 +12,10 @@ "manmal", "thesash", "rhjoh", - "ysqander" + "ysqander", + "atalovesyou", + "0xJonHoldsCrypto", + "hougangdev" ], "seedCommit": "d6863f87", "placeholderAvatar": "assets/avatar-placeholder.svg", diff --git a/scripts/e2e/gateway-network-docker.sh b/scripts/e2e/gateway-network-docker.sh index 8b971d3b33c..0989e764deb 100644 --- a/scripts/e2e/gateway-network-docker.sh +++ b/scripts/e2e/gateway-network-docker.sh @@ -102,12 +102,12 @@ ws.send( ); const connectRes = await onceFrame((o) => o?.type === \"res\" && o?.id === \"c1\"); if (!connectRes.ok) throw new Error(\"connect failed: \" + (connectRes.error?.message ?? \"unknown\")); - + ws.send(JSON.stringify({ type: \"req\", id: \"h1\", method: \"health\" })); const healthRes = await onceFrame((o) => o?.type === \"res\" && o?.id === \"h1\", 10000); if (!healthRes.ok) throw new Error(\"health failed: \" + (healthRes.error?.message ?? \"unknown\")); if (healthRes.payload?.ok !== true) throw new Error(\"unexpected health payload\"); - + ws.close(); console.log(\"ok\"); NODE" diff --git a/scripts/pre-commit/run-node-tool.sh b/scripts/pre-commit/run-node-tool.sh new file mode 100755 index 00000000000..34163075517 --- /dev/null +++ b/scripts/pre-commit/run-node-tool.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + +if [[ $# -lt 1 ]]; then + echo "usage: run-node-tool.sh [args...]" >&2 + exit 2 +fi + +tool="$1" +shift + +if [[ -f "$ROOT_DIR/pnpm-lock.yaml" ]] && command -v pnpm >/dev/null 2>&1; then + exec pnpm exec "$tool" "$@" +fi + +if { [[ -f "$ROOT_DIR/bun.lockb" ]] || [[ -f "$ROOT_DIR/bun.lock" ]]; } && command -v bun >/dev/null 2>&1; then + exec bunx --bun "$tool" "$@" +fi + +if command -v npm >/dev/null 2>&1; then + exec npm exec -- "$tool" "$@" +fi + +if command -v npx >/dev/null 2>&1; then + exec npx "$tool" "$@" +fi + +echo "Missing package manager: pnpm, bun, or npm required." >&2 +exit 1 diff --git a/scripts/sync-labels.ts b/scripts/sync-labels.ts new file mode 100644 index 00000000000..297644c1ee0 --- /dev/null +++ b/scripts/sync-labels.ts @@ -0,0 +1,107 @@ +import { execFileSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +type RepoLabel = { + name: string; + color?: string; +}; + +const COLOR_BY_PREFIX = new Map([ + ["channel", "1d76db"], + ["app", "6f42c1"], + ["extensions", "0e8a16"], + ["docs", "0075ca"], + ["cli", "f9d0c4"], + ["gateway", "d4c5f9"], +]); + +const configPath = resolve(".github/labeler.yml"); +const labelNames = extractLabelNames(readFileSync(configPath, "utf8")); + +if (!labelNames.length) { + throw new Error("labeler.yml must declare at least one label."); +} + +const repo = resolveRepo(); +const existing = fetchExistingLabels(repo); + +const missing = labelNames.filter((label) => !existing.has(label)); +if (!missing.length) { + console.log("All labeler labels already exist."); + process.exit(0); +} + +for (const label of missing) { + const color = pickColor(label); + execFileSync( + "gh", + [ + "api", + "-X", + "POST", + `repos/${repo}/labels`, + "-f", + `name=${label}`, + "-f", + `color=${color}`, + ], + { stdio: "inherit" }, + ); + console.log(`Created label: ${label}`); +} + +function extractLabelNames(contents: string): string[] { + const labels: string[] = []; + for (const line of contents.split("\n")) { + if (!line.trim() || line.trimStart().startsWith("#")) { + continue; + } + if (/^\s/.test(line)) { + continue; + } + const match = line.match(/^(["'])(.+)\1\s*:/) ?? line.match(/^([^:]+):/); + if (match) { + const name = (match[2] ?? match[1] ?? "").trim(); + if (name) { + labels.push(name); + } + } + } + return labels; +} + +function pickColor(label: string): string { + const prefix = label.includes(":") ? label.split(":", 1)[0].trim() : label.trim(); + return COLOR_BY_PREFIX.get(prefix) ?? "ededed"; +} + +function resolveRepo(): string { + const remote = execFileSync("git", ["config", "--get", "remote.origin.url"], { + encoding: "utf8", + }).trim(); + + if (!remote) { + throw new Error("Unable to determine repository from git remote."); + } + + if (remote.startsWith("git@github.com:")) { + return remote.replace("git@github.com:", "").replace(/\.git$/, ""); + } + + if (remote.startsWith("https://github.com/")) { + return remote.replace("https://github.com/", "").replace(/\.git$/, ""); + } + + throw new Error(`Unsupported GitHub remote: ${remote}`); +} + +function fetchExistingLabels(repo: string): Map { + const raw = execFileSync( + "gh", + ["api", `repos/${repo}/labels?per_page=100`, "--paginate"], + { encoding: "utf8" }, + ); + const labels = JSON.parse(raw) as RepoLabel[]; + return new Map(labels.map((label) => [label.name, label])); +} diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index a60ae7847f2..242b444ff87 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -28,9 +28,10 @@ const overrideWorkers = Number.parseInt(process.env.CLAWDBOT_TEST_WORKERS ?? "", const resolvedOverride = Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null; const localWorkers = Math.max(4, Math.min(16, os.cpus().length)); const perRunWorkers = Math.max(1, Math.floor(localWorkers / parallelRuns.length)); -// Keep worker counts predictable for local runs and for CI on macOS. +const macCiWorkers = isCI && isMacOS ? 1 : perRunWorkers; +// Keep worker counts predictable for local runs; trim macOS CI workers to avoid worker crashes/OOM. // In CI on linux/windows, prefer Vitest defaults to avoid cross-test interference from lower worker counts. -const maxWorkers = resolvedOverride ?? (isCI && !isMacOS ? null : perRunWorkers); +const maxWorkers = resolvedOverride ?? (isCI && !isMacOS ? null : macCiWorkers); const WARNING_SUPPRESSION_FLAGS = [ "--disable-warning=ExperimentalWarning", diff --git a/scripts/update-clawtributors.types.ts b/scripts/update-clawtributors.types.ts index 17b57652360..98526bc8a41 100644 --- a/scripts/update-clawtributors.types.ts +++ b/scripts/update-clawtributors.types.ts @@ -30,4 +30,3 @@ export type Entry = { avatar_url: string; lines: number; }; - diff --git a/skills/discord/SKILL.md b/skills/discord/SKILL.md index 0b64f14e1e9..5525a3bf533 100644 --- a/skills/discord/SKILL.md +++ b/skills/discord/SKILL.md @@ -1,6 +1,7 @@ --- name: discord description: Use when you need to control Discord from Clawdbot via the discord tool: send messages, react, post or upload stickers, upload emojis, run polls, manage threads/pins/search, create/edit/delete channels and categories, fetch permissions or member/role/channel info, or handle moderation actions in Discord DMs or channels. +metadata: {"clawdbot":{"emoji":"🎮","requires":{"config":["channels.discord"]}}} --- # Discord Actions diff --git a/skills/github/SKILL.md b/skills/github/SKILL.md index 03b2a00336e..e7c89f7ba81 100644 --- a/skills/github/SKILL.md +++ b/skills/github/SKILL.md @@ -1,6 +1,7 @@ --- name: github description: "Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries." +metadata: {"clawdbot":{"emoji":"🐙","requires":{"bins":["gh"]},"install":[{"id":"brew","kind":"brew","formula":"gh","bins":["gh"],"label":"Install GitHub CLI (brew)"},{"id":"apt","kind":"apt","package":"gh","bins":["gh"],"label":"Install GitHub CLI (apt)"}]}} --- # GitHub Skill diff --git a/skills/local-places/SKILL.md b/skills/local-places/SKILL.md index a002081f618..5b0fdc3a865 100644 --- a/skills/local-places/SKILL.md +++ b/skills/local-places/SKILL.md @@ -84,7 +84,7 @@ curl http://127.0.0.1:8000/places/{place_id} "open_now": true } ], - "next_page_token": "..." + "next_page_token": "..." } ``` diff --git a/skills/notion/SKILL.md b/skills/notion/SKILL.md index 869871b3cdc..04921e2503d 100644 --- a/skills/notion/SKILL.md +++ b/skills/notion/SKILL.md @@ -2,7 +2,7 @@ name: notion description: Notion API for creating and managing pages, databases, and blocks. homepage: https://developers.notion.com -metadata: {"clawdbot":{"emoji":"📝"}} +metadata: {"clawdbot":{"emoji":"📝","requires":{"env":["NOTION_API_KEY"]},"primaryEnv":"NOTION_API_KEY"}} --- # notion diff --git a/skills/slack/SKILL.md b/skills/slack/SKILL.md index df04f858f5d..b72bab1f3f2 100644 --- a/skills/slack/SKILL.md +++ b/skills/slack/SKILL.md @@ -1,6 +1,7 @@ --- name: slack description: Use when you need to control Slack from Clawdbot via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs. +metadata: {"clawdbot":{"emoji":"💬","requires":{"config":["channels.slack"]}}} --- # Slack Actions diff --git a/src/agents/claude-cli-runner.test.ts b/src/agents/claude-cli-runner.test.ts index 6414aecb559..7825d00da6f 100644 --- a/src/agents/claude-cli-runner.test.ts +++ b/src/agents/claude-cli-runner.test.ts @@ -61,7 +61,7 @@ describe("runClaudeCliAgent", () => { expect(argv).toContain("hi"); }); - it("uses provided --session-id when a claude session id is provided", async () => { + it("uses --resume when a claude session id is provided", async () => { runCommandWithTimeoutMock.mockResolvedValueOnce({ stdout: JSON.stringify({ message: "ok", session_id: "sid-2" }), stderr: "", @@ -83,7 +83,7 @@ describe("runClaudeCliAgent", () => { expect(runCommandWithTimeoutMock).toHaveBeenCalledTimes(1); const argv = runCommandWithTimeoutMock.mock.calls[0]?.[0] as string[]; - expect(argv).toContain("--session-id"); + expect(argv).toContain("--resume"); expect(argv).toContain("c9d7b831-1c31-4d22-80b9-1e50ca207d4b"); expect(argv).toContain("hi"); }); diff --git a/src/agents/clawdbot-tools.ts b/src/agents/clawdbot-tools.ts index 91de319372c..b420cad6f74 100644 --- a/src/agents/clawdbot-tools.ts +++ b/src/agents/clawdbot-tools.ts @@ -54,6 +54,8 @@ export function createClawdbotTools(options?: { hasRepliedRef?: { value: boolean }; /** If true, the model has native vision capability */ modelHasVision?: boolean; + /** Explicit agent ID override for cron/hook sessions. */ + requesterAgentIdOverride?: string; }): AnyAgentTool[] { const imageTool = options?.agentDir?.trim() ? createImageTool({ @@ -105,7 +107,10 @@ export function createClawdbotTools(options?: { agentSessionKey: options?.agentSessionKey, config: options?.config, }), - createAgentsListTool({ agentSessionKey: options?.agentSessionKey }), + createAgentsListTool({ + agentSessionKey: options?.agentSessionKey, + requesterAgentIdOverride: options?.requesterAgentIdOverride, + }), createSessionsListTool({ agentSessionKey: options?.agentSessionKey, sandboxed: options?.sandboxed, @@ -129,6 +134,7 @@ export function createClawdbotTools(options?: { agentGroupChannel: options?.agentGroupChannel, agentGroupSpace: options?.agentGroupSpace, sandboxed: options?.sandboxed, + requesterAgentIdOverride: options?.requesterAgentIdOverride, }), createSessionStatusTool({ agentSessionKey: options?.agentSessionKey, diff --git a/src/agents/cli-backends.ts b/src/agents/cli-backends.ts index a2fcaa8a53a..f21c04f5253 100644 --- a/src/agents/cli-backends.ts +++ b/src/agents/cli-backends.ts @@ -28,6 +28,14 @@ const CLAUDE_MODEL_ALIASES: Record = { const DEFAULT_CLAUDE_BACKEND: CliBackendConfig = { command: "claude", args: ["-p", "--output-format", "json", "--dangerously-skip-permissions"], + resumeArgs: [ + "-p", + "--output-format", + "json", + "--dangerously-skip-permissions", + "--resume", + "{sessionId}", + ], output: "json", input: "arg", modelArg: "--model", diff --git a/src/agents/model-catalog.ts b/src/agents/model-catalog.ts index 3a3db7a289d..5463542cb0c 100644 --- a/src/agents/model-catalog.ts +++ b/src/agents/model-catalog.ts @@ -8,6 +8,7 @@ export type ModelCatalogEntry = { provider: string; contextWindow?: number; reasoning?: boolean; + input?: Array<"text" | "image">; }; type DiscoveredModel = { @@ -16,6 +17,7 @@ type DiscoveredModel = { provider: string; contextWindow?: number; reasoning?: boolean; + input?: Array<"text" | "image">; }; type PiSdkModule = typeof import("@mariozechner/pi-coding-agent"); @@ -80,7 +82,10 @@ export async function loadModelCatalog(params?: { ? entry.contextWindow : undefined; const reasoning = typeof entry?.reasoning === "boolean" ? entry.reasoning : undefined; - models.push({ id, name, provider, contextWindow, reasoning }); + const input = Array.isArray(entry?.input) + ? (entry.input as Array<"text" | "image">) + : undefined; + models.push({ id, name, provider, contextWindow, reasoning, input }); } if (models.length === 0) { @@ -105,3 +110,27 @@ export async function loadModelCatalog(params?: { return modelCatalogPromise; } + +/** + * Check if a model supports image input based on its catalog entry. + */ +export function modelSupportsVision(entry: ModelCatalogEntry | undefined): boolean { + return entry?.input?.includes("image") ?? false; +} + +/** + * Find a model in the catalog by provider and model ID. + */ +export function findModelInCatalog( + catalog: ModelCatalogEntry[], + provider: string, + modelId: string, +): ModelCatalogEntry | undefined { + const normalizedProvider = provider.toLowerCase().trim(); + const normalizedModelId = modelId.toLowerCase().trim(); + return catalog.find( + (entry) => + entry.provider.toLowerCase() === normalizedProvider && + entry.id.toLowerCase() === normalizedModelId, + ); +} diff --git a/src/agents/model-selection.test.ts b/src/agents/model-selection.test.ts index 391aee3186b..5e72ad3961b 100644 --- a/src/agents/model-selection.test.ts +++ b/src/agents/model-selection.test.ts @@ -1,252 +1,139 @@ -import { describe, expect, it } from "vitest"; - -import type { ClawdbotConfig } from "../config/config.js"; -import { DEFAULT_PROVIDER } from "./defaults.js"; +import { describe, it, expect, vi } from "vitest"; import { - buildAllowedModelSet, - modelKey, parseModelRef, - resolveAllowedModelRef, - resolveHooksGmailModel, + resolveModelRefFromString, + resolveConfiguredModelRef, + buildModelAliasIndex, + normalizeProviderId, + modelKey, } from "./model-selection.js"; +import type { ClawdbotConfig } from "../config/config.js"; -const catalog = [ - { - provider: "openai", - id: "gpt-4", - name: "GPT-4", - }, -]; - -describe("buildAllowedModelSet", () => { - it("always allows the configured default model", () => { - const cfg = { - agents: { - defaults: { - models: { - "openai/gpt-4": { alias: "gpt4" }, - }, - }, - }, - } as ClawdbotConfig; - - const allowed = buildAllowedModelSet({ - cfg, - catalog, - defaultProvider: "claude-cli", - defaultModel: "opus-4.5", - }); - - expect(allowed.allowAny).toBe(false); - expect(allowed.allowedKeys.has(modelKey("openai", "gpt-4"))).toBe(true); - expect(allowed.allowedKeys.has(modelKey("claude-cli", "opus-4.5"))).toBe(true); - }); - - it("includes the default model when no allowlist is set", () => { - const cfg = { - agents: { defaults: {} }, - } as ClawdbotConfig; - - const allowed = buildAllowedModelSet({ - cfg, - catalog, - defaultProvider: "claude-cli", - defaultModel: "opus-4.5", - }); - - expect(allowed.allowAny).toBe(true); - expect(allowed.allowedKeys.has(modelKey("openai", "gpt-4"))).toBe(true); - expect(allowed.allowedKeys.has(modelKey("claude-cli", "opus-4.5"))).toBe(true); - }); - - it("allows explicit custom providers from models.providers", () => { - const cfg = { - agents: { - defaults: { - models: { - "moonshot/kimi-k2-0905-preview": { alias: "kimi" }, - }, - }, - }, - models: { - mode: "merge", - providers: { - moonshot: { - baseUrl: "https://api.moonshot.ai/v1", - apiKey: "x", - api: "openai-completions", - models: [{ id: "kimi-k2-0905-preview", name: "Kimi" }], - }, - }, - }, - } as ClawdbotConfig; - - const allowed = buildAllowedModelSet({ - cfg, - catalog: [], - defaultProvider: "anthropic", - defaultModel: "claude-opus-4-5", - }); - - expect(allowed.allowAny).toBe(false); - expect(allowed.allowedKeys.has(modelKey("moonshot", "kimi-k2-0905-preview"))).toBe(true); - }); -}); - -describe("parseModelRef", () => { - it("normalizes anthropic/opus-4.5 to claude-opus-4-5", () => { - const ref = parseModelRef("anthropic/opus-4.5", "anthropic"); - expect(ref).toEqual({ - provider: "anthropic", - model: "claude-opus-4-5", +describe("model-selection", () => { + describe("normalizeProviderId", () => { + it("should normalize provider names", () => { + expect(normalizeProviderId("Anthropic")).toBe("anthropic"); + expect(normalizeProviderId("Z.ai")).toBe("zai"); + expect(normalizeProviderId("z-ai")).toBe("zai"); + expect(normalizeProviderId("OpenCode-Zen")).toBe("opencode"); + expect(normalizeProviderId("qwen")).toBe("qwen-portal"); }); }); - it("normalizes google gemini 3 models to preview ids", () => { - expect(parseModelRef("google/gemini-3-pro", "anthropic")).toEqual({ - provider: "google", - model: "gemini-3-pro-preview", - }); - expect(parseModelRef("google/gemini-3-flash", "anthropic")).toEqual({ - provider: "google", - model: "gemini-3-flash-preview", - }); - }); - - it("normalizes default-provider google models", () => { - expect(parseModelRef("gemini-3-pro", "google")).toEqual({ - provider: "google", - model: "gemini-3-pro-preview", - }); - }); -}); - -describe("resolveHooksGmailModel", () => { - it("returns null when hooks.gmail.model is not set", () => { - const cfg = {} satisfies ClawdbotConfig; - const result = resolveHooksGmailModel({ - cfg, - defaultProvider: DEFAULT_PROVIDER, - }); - expect(result).toBeNull(); - }); - - it("returns null when hooks.gmail.model is empty", () => { - const cfg = { - hooks: { gmail: { model: "" } }, - } satisfies ClawdbotConfig; - const result = resolveHooksGmailModel({ - cfg, - defaultProvider: DEFAULT_PROVIDER, - }); - expect(result).toBeNull(); - }); - - it("parses provider/model from hooks.gmail.model", () => { - const cfg = { - hooks: { gmail: { model: "openrouter/meta-llama/llama-3.3-70b:free" } }, - } satisfies ClawdbotConfig; - const result = resolveHooksGmailModel({ - cfg, - defaultProvider: DEFAULT_PROVIDER, - }); - expect(result).toEqual({ - provider: "openrouter", - model: "meta-llama/llama-3.3-70b:free", - }); - }); - - it("resolves alias from agent.models", () => { - const cfg = { - agents: { - defaults: { - models: { - "anthropic/claude-sonnet-4-1": { alias: "Sonnet" }, - }, - }, - }, - hooks: { gmail: { model: "Sonnet" } }, - } satisfies ClawdbotConfig; - const result = resolveHooksGmailModel({ - cfg, - defaultProvider: DEFAULT_PROVIDER, - }); - expect(result).toEqual({ - provider: "anthropic", - model: "claude-sonnet-4-1", - }); - }); - - it("uses default provider when model omits provider", () => { - const cfg = { - hooks: { gmail: { model: "claude-haiku-3-5" } }, - } satisfies ClawdbotConfig; - const result = resolveHooksGmailModel({ - cfg, - defaultProvider: "anthropic", - }); - expect(result).toEqual({ - provider: "anthropic", - model: "claude-haiku-3-5", - }); - }); -}); - -describe("resolveAllowedModelRef", () => { - it("resolves aliases when allowed", () => { - const cfg = { - agents: { - defaults: { - models: { - "anthropic/claude-sonnet-4-1": { alias: "Sonnet" }, - }, - }, - }, - } satisfies ClawdbotConfig; - const resolved = resolveAllowedModelRef({ - cfg, - catalog: [ - { - provider: "anthropic", - id: "claude-sonnet-4-1", - name: "Sonnet", - }, - ], - raw: "Sonnet", - defaultProvider: "anthropic", - defaultModel: "claude-opus-4-5", - }); - expect("error" in resolved).toBe(false); - if ("ref" in resolved) { - expect(resolved.ref).toEqual({ + describe("parseModelRef", () => { + it("should parse full model refs", () => { + expect(parseModelRef("anthropic/claude-3-5-sonnet", "openai")).toEqual({ provider: "anthropic", - model: "claude-sonnet-4-1", + model: "claude-3-5-sonnet", }); - } + }); + + it("should use default provider if none specified", () => { + expect(parseModelRef("claude-3-5-sonnet", "anthropic")).toEqual({ + provider: "anthropic", + model: "claude-3-5-sonnet", + }); + }); + + it("should return null for empty strings", () => { + expect(parseModelRef("", "anthropic")).toBeNull(); + expect(parseModelRef(" ", "anthropic")).toBeNull(); + }); + + it("should handle invalid slash usage", () => { + expect(parseModelRef("/", "anthropic")).toBeNull(); + expect(parseModelRef("anthropic/", "anthropic")).toBeNull(); + expect(parseModelRef("/model", "anthropic")).toBeNull(); + }); }); - it("rejects disallowed models", () => { - const cfg = { - agents: { - defaults: { - models: { - "openai/gpt-4": { alias: "GPT4" }, + describe("buildModelAliasIndex", () => { + it("should build alias index from config", () => { + const cfg: Partial = { + agents: { + defaults: { + models: { + "anthropic/claude-3-5-sonnet": { alias: "fast" }, + "openai/gpt-4o": { alias: "smart" }, + }, }, }, - }, - } satisfies ClawdbotConfig; - const resolved = resolveAllowedModelRef({ - cfg, - catalog: [ - { provider: "openai", id: "gpt-4", name: "GPT-4" }, - { provider: "anthropic", id: "claude-sonnet-4-1", name: "Sonnet" }, - ], - raw: "anthropic/claude-sonnet-4-1", - defaultProvider: "openai", - defaultModel: "gpt-4", + }; + + const index = buildModelAliasIndex({ + cfg: cfg as ClawdbotConfig, + defaultProvider: "anthropic", + }); + + expect(index.byAlias.get("fast")?.ref).toEqual({ + provider: "anthropic", + model: "claude-3-5-sonnet", + }); + expect(index.byAlias.get("smart")?.ref).toEqual({ provider: "openai", model: "gpt-4o" }); + expect(index.byKey.get(modelKey("anthropic", "claude-3-5-sonnet"))).toEqual(["fast"]); }); - expect(resolved).toEqual({ - error: "model not allowed: anthropic/claude-sonnet-4-1", + }); + + describe("resolveModelRefFromString", () => { + it("should resolve from string with alias", () => { + const index = { + byAlias: new Map([ + ["fast", { alias: "fast", ref: { provider: "anthropic", model: "sonnet" } }], + ]), + byKey: new Map(), + }; + + const resolved = resolveModelRefFromString({ + raw: "fast", + defaultProvider: "openai", + aliasIndex: index, + }); + + expect(resolved?.ref).toEqual({ provider: "anthropic", model: "sonnet" }); + expect(resolved?.alias).toBe("fast"); + }); + + it("should resolve direct ref if no alias match", () => { + const resolved = resolveModelRefFromString({ + raw: "openai/gpt-4", + defaultProvider: "anthropic", + }); + expect(resolved?.ref).toEqual({ provider: "openai", model: "gpt-4" }); + }); + }); + + describe("resolveConfiguredModelRef", () => { + it("should fall back to anthropic and warn if provider is missing for non-alias", () => { + const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + const cfg: Partial = { + agents: { + defaults: { + model: "claude-3-5-sonnet", + }, + }, + }; + + const result = resolveConfiguredModelRef({ + cfg: cfg as ClawdbotConfig, + defaultProvider: "google", + defaultModel: "gemini-pro", + }); + + expect(result).toEqual({ provider: "anthropic", model: "claude-3-5-sonnet" }); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining('Falling back to "anthropic/claude-3-5-sonnet"'), + ); + warnSpy.mockRestore(); + }); + + it("should use default provider/model if config is empty", () => { + const cfg: Partial = {}; + const result = resolveConfiguredModelRef({ + cfg: cfg as ClawdbotConfig, + defaultProvider: "openai", + defaultModel: "gpt-4", + }); + expect(result).toEqual({ provider: "openai", model: "gpt-4" }); }); }); }); diff --git a/src/agents/model-selection.ts b/src/agents/model-selection.ts index a330423b388..e05370edd8f 100644 --- a/src/agents/model-selection.ts +++ b/src/agents/model-selection.ts @@ -131,14 +131,24 @@ export function resolveConfiguredModelRef(params: { cfg: params.cfg, defaultProvider: params.defaultProvider, }); + if (!trimmed.includes("/")) { + const aliasKey = normalizeAliasKey(trimmed); + const aliasMatch = aliasIndex.byAlias.get(aliasKey); + if (aliasMatch) return aliasMatch.ref; + + // Default to anthropic if no provider is specified, but warn as this is deprecated. + console.warn( + `[clawdbot] Model "${trimmed}" specified without provider. Falling back to "anthropic/${trimmed}". Please use "anthropic/${trimmed}" in your config.`, + ); + return { provider: "anthropic", model: trimmed }; + } + const resolved = resolveModelRefFromString({ raw: trimmed, defaultProvider: params.defaultProvider, aliasIndex, }); if (resolved) return resolved.ref; - // TODO(steipete): drop this fallback once provider-less agents.defaults.model is fully deprecated. - return { provider: "anthropic", model: trimmed }; } return { provider: params.defaultProvider, model: params.defaultModel }; } diff --git a/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts b/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts index 9fac95b9c4b..37603c26274 100644 --- a/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts +++ b/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts @@ -27,4 +27,14 @@ describe("sanitizeUserFacingText", () => { const raw = '{"type":"error","error":{"message":"Something exploded","type":"server_error"}}'; expect(sanitizeUserFacingText(raw)).toBe("LLM error server_error: Something exploded"); }); + + it("collapses consecutive duplicate paragraphs", () => { + const text = "Hello there!\n\nHello there!"; + expect(sanitizeUserFacingText(text)).toBe("Hello there!"); + }); + + it("does not collapse distinct paragraphs", () => { + const text = "Hello there!\n\nDifferent line."; + expect(sanitizeUserFacingText(text)).toBe(text); + }); }); diff --git a/src/agents/pi-embedded-helpers/errors.ts b/src/agents/pi-embedded-helpers/errors.ts index fdfed02a207..b47938c231c 100644 --- a/src/agents/pi-embedded-helpers/errors.ts +++ b/src/agents/pi-embedded-helpers/errors.ts @@ -77,6 +77,29 @@ function stripFinalTagsFromText(text: string): string { return text.replace(FINAL_TAG_RE, ""); } +function collapseConsecutiveDuplicateBlocks(text: string): string { + const trimmed = text.trim(); + if (!trimmed) return text; + const blocks = trimmed.split(/\n{2,}/); + if (blocks.length < 2) return text; + + const normalizeBlock = (value: string) => value.trim().replace(/\s+/g, " "); + const result: string[] = []; + let lastNormalized: string | null = null; + + for (const block of blocks) { + const normalized = normalizeBlock(block); + if (lastNormalized && normalized === lastNormalized) { + continue; + } + result.push(block.trim()); + lastNormalized = normalized; + } + + if (result.length === blocks.length) return text; + return result.join("\n\n"); +} + function isLikelyHttpErrorText(raw: string): boolean { const match = raw.match(HTTP_STATUS_PREFIX_RE); if (!match) return false; @@ -321,7 +344,7 @@ export function sanitizeUserFacingText(text: string): string { return formatRawAssistantErrorForUi(trimmed); } - return stripped; + return collapseConsecutiveDuplicateBlocks(stripped); } export function isRateLimitAssistantError(msg: AssistantMessage | undefined): boolean { diff --git a/src/agents/pi-embedded-runner/run.ts b/src/agents/pi-embedded-runner/run.ts index ea2488a1c7b..adbe6ad498f 100644 --- a/src/agents/pi-embedded-runner/run.ts +++ b/src/agents/pi-embedded-runner/run.ts @@ -599,7 +599,7 @@ export async function runEmbeddedPiAgent( verboseLevel: params.verboseLevel, reasoningLevel: params.reasoningLevel, toolResultFormat: resolvedToolResultFormat, - inlineToolResultsAllowed: !params.onPartialReply && !params.onToolResult, + inlineToolResultsAllowed: false, }); log.debug( diff --git a/src/agents/pi-tools.ts b/src/agents/pi-tools.ts index ba0fe13bc14..bd745da0313 100644 --- a/src/agents/pi-tools.ts +++ b/src/agents/pi-tools.ts @@ -313,6 +313,7 @@ export function createClawdbotCodingTools(options?: { replyToMode: options?.replyToMode, hasRepliedRef: options?.hasRepliedRef, modelHasVision: options?.modelHasVision, + requesterAgentIdOverride: agentId, }), ]; const coreToolNames = new Set( diff --git a/src/agents/tools/agents-list-tool.ts b/src/agents/tools/agents-list-tool.ts index 40c7172cbba..28f13684461 100644 --- a/src/agents/tools/agents-list-tool.ts +++ b/src/agents/tools/agents-list-tool.ts @@ -19,7 +19,11 @@ type AgentListEntry = { configured: boolean; }; -export function createAgentsListTool(opts?: { agentSessionKey?: string }): AnyAgentTool { +export function createAgentsListTool(opts?: { + agentSessionKey?: string; + /** Explicit agent ID override for cron/hook sessions. */ + requesterAgentIdOverride?: string; +}): AnyAgentTool { return { label: "Agents", name: "agents_list", @@ -37,7 +41,9 @@ export function createAgentsListTool(opts?: { agentSessionKey?: string }): AnyAg }) : alias; const requesterAgentId = normalizeAgentId( - parseAgentSessionKey(requesterInternalKey)?.agentId ?? DEFAULT_AGENT_ID, + opts?.requesterAgentIdOverride ?? + parseAgentSessionKey(requesterInternalKey)?.agentId ?? + DEFAULT_AGENT_ID, ); const allowAgents = resolveAgentConfig(cfg, requesterAgentId)?.subagents?.allowAgents ?? []; diff --git a/src/agents/tools/cron-tool.ts b/src/agents/tools/cron-tool.ts index a1d218dd73f..739b3ada37f 100644 --- a/src/agents/tools/cron-tool.ts +++ b/src/agents/tools/cron-tool.ts @@ -133,8 +133,50 @@ export function createCronTool(opts?: CronToolOptions): AnyAgentTool { return { label: "Cron", name: "cron", - description: - "Manage Gateway cron jobs (status/list/add/update/remove/run/runs) and send wake events. Use `jobId` as the canonical identifier; `id` is accepted for compatibility. Use `contextMessages` (0-10) to add previous messages as context to the job text.", + description: `Manage Gateway cron jobs (status/list/add/update/remove/run/runs) and send wake events. + +ACTIONS: +- status: Check cron scheduler status +- list: List jobs (use includeDisabled:true to include disabled) +- add: Create job (requires job object, see schema below) +- update: Modify job (requires jobId + patch object) +- remove: Delete job (requires jobId) +- run: Trigger job immediately (requires jobId) +- runs: Get job run history (requires jobId) +- wake: Send wake event (requires text, optional mode) + +JOB SCHEMA (for add action): +{ + "name": "string (optional)", + "schedule": { ... }, // Required: when to run + "payload": { ... }, // Required: what to execute + "sessionTarget": "main" | "isolated", // Required + "enabled": true | false // Optional, default true +} + +SCHEDULE TYPES (schedule.kind): +- "at": One-shot at absolute time + { "kind": "at", "atMs": } +- "every": Recurring interval + { "kind": "every", "everyMs": , "anchorMs": } +- "cron": Cron expression + { "kind": "cron", "expr": "", "tz": "" } + +PAYLOAD TYPES (payload.kind): +- "systemEvent": Injects text as system event into session + { "kind": "systemEvent", "text": "" } +- "agentTurn": Runs agent with message (isolated sessions only) + { "kind": "agentTurn", "message": "", "model": "", "thinking": "", "timeoutSeconds": , "deliver": , "channel": "", "to": "", "bestEffortDeliver": } + +CRITICAL CONSTRAINTS: +- sessionTarget="main" REQUIRES payload.kind="systemEvent" +- sessionTarget="isolated" REQUIRES payload.kind="agentTurn" + +WAKE MODES (for wake action): +- "next-heartbeat" (default): Wake on next heartbeat +- "now": Wake immediately + +Use jobId as the canonical identifier; id is accepted for compatibility. Use contextMessages (0-10) to add previous messages as context to the job text.`, parameters: CronToolSchema, execute: async (_toolCallId, args) => { const params = args as Record; diff --git a/src/agents/tools/message-tool.ts b/src/agents/tools/message-tool.ts index 6552564e966..eae4356db51 100644 --- a/src/agents/tools/message-tool.ts +++ b/src/agents/tools/message-tool.ts @@ -333,7 +333,13 @@ export function createMessageTool(options?: MessageToolOptions): AnyAgentTool { name: "message", description, parameters: schema, - execute: async (_toolCallId, args) => { + execute: async (_toolCallId, args, signal) => { + // Check if already aborted before doing any work + if (signal?.aborted) { + const err = new Error("Message send aborted"); + err.name = "AbortError"; + throw err; + } const params = args as Record; const cfg = options?.config ?? loadConfig(); const action = readStringParam(params, "action", { @@ -366,6 +372,9 @@ export function createMessageTool(options?: MessageToolOptions): AnyAgentTool { currentThreadTs: options?.currentThreadTs, replyToMode: options?.replyToMode, hasRepliedRef: options?.hasRepliedRef, + // Direct tool invocations should not add cross-context decoration. + // The agent is composing a message, not forwarding from another chat. + skipCrossContextDecoration: true, } : undefined; @@ -379,6 +388,7 @@ export function createMessageTool(options?: MessageToolOptions): AnyAgentTool { agentId: options?.agentSessionKey ? resolveSessionAgentId({ sessionKey: options.agentSessionKey, config: cfg }) : undefined, + abortSignal: signal, }); const toolResult = getToolResult(result); diff --git a/src/agents/tools/sessions-send-helpers.ts b/src/agents/tools/sessions-send-helpers.ts index 5e758d4260a..c9940de0f9c 100644 --- a/src/agents/tools/sessions-send-helpers.ts +++ b/src/agents/tools/sessions-send-helpers.ts @@ -14,6 +14,7 @@ export type AnnounceTarget = { channel: string; to: string; accountId?: string; + threadId?: string; // Forum topic/thread ID }; export function resolveAnnounceTargetFromKey(sessionKey: string): AnnounceTarget | null { @@ -22,7 +23,22 @@ export function resolveAnnounceTargetFromKey(sessionKey: string): AnnounceTarget if (parts.length < 3) return null; const [channelRaw, kind, ...rest] = parts; if (kind !== "group" && kind !== "channel") return null; - const id = rest.join(":").trim(); + + // Extract topic/thread ID from rest (supports both :topic: and :thread:) + // Telegram uses :topic:, other platforms use :thread: + let threadId: string | undefined; + const restJoined = rest.join(":"); + const topicMatch = restJoined.match(/:topic:(\d+)$/); + const threadMatch = restJoined.match(/:thread:(\d+)$/); + const match = topicMatch || threadMatch; + + if (match) { + threadId = match[1]; // Keep as string to match AgentCommandOpts.threadId + } + + // Remove :topic:N or :thread:N suffix from ID for target + const id = match ? restJoined.replace(/:(topic|thread):\d+$/, "") : restJoined.trim(); + if (!id) return null; if (!channelRaw) return null; const normalizedChannel = normalizeAnyChannelId(channelRaw) ?? normalizeChatChannelId(channelRaw); @@ -37,7 +53,11 @@ export function resolveAnnounceTargetFromKey(sessionKey: string): AnnounceTarget const normalized = normalizedChannel ? getChannelPlugin(normalizedChannel)?.messaging?.normalizeTarget?.(kindTarget) : undefined; - return { channel, to: normalized ?? kindTarget }; + return { + channel, + to: normalized ?? kindTarget, + threadId, + }; } export function buildAgentToAgentMessageContext(params: { diff --git a/src/agents/tools/sessions-spawn-tool.ts b/src/agents/tools/sessions-spawn-tool.ts index 838badfc3c7..e5e1391d102 100644 --- a/src/agents/tools/sessions-spawn-tool.ts +++ b/src/agents/tools/sessions-spawn-tool.ts @@ -67,6 +67,8 @@ export function createSessionsSpawnTool(opts?: { agentGroupChannel?: string | null; agentGroupSpace?: string | null; sandboxed?: boolean; + /** Explicit agent ID override for cron/hook sessions where session key parsing may not work. */ + requesterAgentIdOverride?: string; }): AnyAgentTool { return { label: "Sessions", @@ -129,7 +131,7 @@ export function createSessionsSpawnTool(opts?: { }); const requesterAgentId = normalizeAgentId( - parseAgentSessionKey(requesterInternalKey)?.agentId, + opts?.requesterAgentIdOverride ?? parseAgentSessionKey(requesterInternalKey)?.agentId, ); const targetAgentId = requestedAgentId ? normalizeAgentId(requestedAgentId) diff --git a/src/auto-reply/chunk.test.ts b/src/auto-reply/chunk.test.ts index 01069d852a1..54589984357 100644 --- a/src/auto-reply/chunk.test.ts +++ b/src/auto-reply/chunk.test.ts @@ -310,10 +310,16 @@ describe("chunkTextWithMode", () => { expect(chunks).toEqual(["Line one\nLine two"]); }); - it("uses newline-based chunking for newline mode", () => { + it("uses paragraph-based chunking for newline mode", () => { const text = "Line one\nLine two"; const chunks = chunkTextWithMode(text, 1000, "newline"); - expect(chunks).toEqual(["Line one", "Line two"]); + expect(chunks).toEqual(["Line one\nLine two"]); + }); + + it("splits on blank lines for newline mode", () => { + const text = "Para one\n\nPara two"; + const chunks = chunkTextWithMode(text, 1000, "newline"); + expect(chunks).toEqual(["Para one", "Para two"]); }); }); @@ -323,17 +329,35 @@ describe("chunkMarkdownTextWithMode", () => { expect(chunkMarkdownTextWithMode(text, 1000, "length")).toEqual(chunkMarkdownText(text, 1000)); }); - it("uses newline-based chunking for newline mode", () => { + it("uses paragraph-based chunking for newline mode", () => { const text = "Line one\nLine two"; - expect(chunkMarkdownTextWithMode(text, 1000, "newline")).toEqual(["Line one", "Line two"]); + expect(chunkMarkdownTextWithMode(text, 1000, "newline")).toEqual(["Line one\nLine two"]); }); - it("does not split inside code fences for newline mode", () => { + it("splits on blank lines for newline mode", () => { + const text = "Para one\n\nPara two"; + expect(chunkMarkdownTextWithMode(text, 1000, "newline")).toEqual(["Para one", "Para two"]); + }); + + it("does not split single-newline code fences in newline mode", () => { const text = "```js\nconst a = 1;\nconst b = 2;\n```\nAfter"; - expect(chunkMarkdownTextWithMode(text, 1000, "newline")).toEqual([ - "```js\nconst a = 1;\nconst b = 2;\n```", - "After", - ]); + expect(chunkMarkdownTextWithMode(text, 1000, "newline")).toEqual([text]); + }); + + it("defers long markdown paragraphs to markdown chunking in newline mode", () => { + const text = `\`\`\`js\n${"const a = 1;\n".repeat(20)}\`\`\``; + expect(chunkMarkdownTextWithMode(text, 40, "newline")).toEqual(chunkMarkdownText(text, 40)); + }); + + it("does not split on blank lines inside a fenced code block", () => { + const text = "```python\ndef my_function():\n x = 1\n\n y = 2\n return x + y\n```"; + expect(chunkMarkdownTextWithMode(text, 1000, "newline")).toEqual([text]); + }); + + it("splits on blank lines between a code fence and following paragraph", () => { + const fence = "```python\ndef my_function():\n x = 1\n\n y = 2\n return x + y\n```"; + const text = `${fence}\n\nAfter`; + expect(chunkMarkdownTextWithMode(text, 1000, "newline")).toEqual([fence, "After"]); }); }); diff --git a/src/auto-reply/chunk.ts b/src/auto-reply/chunk.ts index c77c0cd9f20..c4fd31ed896 100644 --- a/src/auto-reply/chunk.ts +++ b/src/auto-reply/chunk.ts @@ -13,7 +13,9 @@ export type TextChunkProvider = ChannelId | typeof INTERNAL_MESSAGE_CHANNEL; /** * Chunking mode for outbound messages: * - "length": Split only when exceeding textChunkLimit (default) - * - "newline": Split on every newline, with fallback to length-based for long lines + * - "newline": Prefer breaking on "soft" boundaries. Historically this split on every + * newline; now it only breaks on paragraph boundaries (blank lines) unless the text + * exceeds the length limit. */ export type ChunkMode = "length" | "newline"; @@ -164,44 +166,93 @@ export function chunkByNewline( return chunks; } +/** + * Split text into chunks on paragraph boundaries (blank lines), preserving lists and + * single-newline line wraps inside paragraphs. + * + * - Only breaks at paragraph separators ("\n\n" or more, allowing whitespace on blank lines) + * - Packs multiple paragraphs into a single chunk up to `limit` + * - Falls back to length-based splitting when a single paragraph exceeds `limit` + * (unless `splitLongParagraphs` is disabled) + */ +export function chunkByParagraph( + text: string, + limit: number, + opts?: { splitLongParagraphs?: boolean }, +): string[] { + if (!text) return []; + if (limit <= 0) return [text]; + const splitLongParagraphs = opts?.splitLongParagraphs !== false; + + // Normalize to \n so blank line detection is consistent. + const normalized = text.replace(/\r\n?/g, "\n"); + + // Fast-path: if there are no blank-line paragraph separators, do not split. + // (We *do not* early-return based on `limit` — newline mode is about paragraph + // boundaries, not only exceeding a length limit.) + const paragraphRe = /\n[\t ]*\n+/; + if (!paragraphRe.test(normalized)) { + if (normalized.length <= limit) return [normalized]; + if (!splitLongParagraphs) return [normalized]; + return chunkText(normalized, limit); + } + + const spans = parseFenceSpans(normalized); + + const parts: string[] = []; + const re = /\n[\t ]*\n+/g; // paragraph break: blank line(s), allowing whitespace + let lastIndex = 0; + for (const match of normalized.matchAll(re)) { + const idx = match.index ?? 0; + + // Do not split on blank lines that occur inside fenced code blocks. + if (!isSafeFenceBreak(spans, idx)) { + continue; + } + + parts.push(normalized.slice(lastIndex, idx)); + lastIndex = idx + match[0].length; + } + parts.push(normalized.slice(lastIndex)); + + const chunks: string[] = []; + for (const part of parts) { + const paragraph = part.replace(/\s+$/g, ""); + if (!paragraph.trim()) continue; + if (paragraph.length <= limit) { + chunks.push(paragraph); + } else if (!splitLongParagraphs) { + chunks.push(paragraph); + } else { + chunks.push(...chunkText(paragraph, limit)); + } + } + + return chunks; +} + /** * Unified chunking function that dispatches based on mode. */ export function chunkTextWithMode(text: string, limit: number, mode: ChunkMode): string[] { if (mode === "newline") { - const chunks: string[] = []; - const lineChunks = chunkByNewline(text, limit, { splitLongLines: false }); - for (const line of lineChunks) { - const nested = chunkText(line, limit); - if (!nested.length && line) { - chunks.push(line); - continue; - } - chunks.push(...nested); - } - return chunks; + return chunkByParagraph(text, limit); } return chunkText(text, limit); } export function chunkMarkdownTextWithMode(text: string, limit: number, mode: ChunkMode): string[] { if (mode === "newline") { - const spans = parseFenceSpans(text); - const chunks: string[] = []; - const lineChunks = chunkByNewline(text, limit, { - splitLongLines: false, - trimLines: false, - isSafeBreak: (index) => isSafeFenceBreak(spans, index), - }); - for (const line of lineChunks) { - const nested = chunkMarkdownText(line, limit); - if (!nested.length && line) { - chunks.push(line); - continue; - } - chunks.push(...nested); + // Paragraph chunking is fence-safe because we never split at arbitrary indices. + // If a paragraph must be split by length, defer to the markdown-aware chunker. + const paragraphChunks = chunkByParagraph(text, limit, { splitLongParagraphs: false }); + const out: string[] = []; + for (const chunk of paragraphChunks) { + const nested = chunkMarkdownText(chunk, limit); + if (!nested.length && chunk) out.push(chunk); + else out.push(...nested); } - return chunks; + return out; } return chunkMarkdownText(text, limit); } diff --git a/src/auto-reply/commands-registry.data.ts b/src/auto-reply/commands-registry.data.ts index 87d06b9d0c2..12fec300b04 100644 --- a/src/auto-reply/commands-registry.data.ts +++ b/src/auto-reply/commands-registry.data.ts @@ -178,6 +178,13 @@ function buildChatCommands(): ChatCommandDefinition[] { textAlias: "/context", acceptsArgs: true, }), + defineChatCommand({ + key: "tts", + nativeName: "tts", + description: "Configure text-to-speech.", + textAlias: "/tts", + acceptsArgs: true, + }), defineChatCommand({ key: "whoami", nativeName: "whoami", @@ -279,27 +286,6 @@ function buildChatCommands(): ChatCommandDefinition[] { ], argsMenu: "auto", }), - defineChatCommand({ - key: "tts", - nativeName: "tts", - description: "Control text-to-speech (TTS).", - textAlias: "/tts", - args: [ - { - name: "action", - description: "on | off | status | provider | limit | summary | audio | help", - type: "string", - choices: ["on", "off", "status", "provider", "limit", "summary", "audio", "help"], - }, - { - name: "value", - description: "Provider, limit, or text", - type: "string", - captureRemaining: true, - }, - ], - argsMenu: "auto", - }), defineChatCommand({ key: "stop", nativeName: "stop", diff --git a/src/auto-reply/heartbeat.ts b/src/auto-reply/heartbeat.ts index 50567ad87b8..cca6c6ad102 100644 --- a/src/auto-reply/heartbeat.ts +++ b/src/auto-reply/heartbeat.ts @@ -32,6 +32,8 @@ export function isHeartbeatContentEffectivelyEmpty(content: string | undefined | // This intentionally does NOT skip lines like "#TODO" or "#hashtag" which might be content // (Those aren't valid markdown headers - ATX headers require space after #) if (/^#+(\s|$)/.test(trimmed)) continue; + // Skip empty markdown list items like "- [ ]" or "* [ ]" or just "- " + if (/^[-*+]\s*(\[[\sXx]?\]\s*)?$/.test(trimmed)) continue; // Found a non-empty, non-comment line - there's actionable content return false; } diff --git a/src/auto-reply/model.test.ts b/src/auto-reply/model.test.ts index 4a5aa7714d4..de1fb6a8a85 100644 --- a/src/auto-reply/model.test.ts +++ b/src/auto-reply/model.test.ts @@ -10,11 +10,17 @@ describe("extractModelDirective", () => { expect(result.cleaned).toBe(""); }); - it("extracts /models with argument", () => { + it("does not treat /models as a /model directive", () => { const result = extractModelDirective("/models gpt-5"); - expect(result.hasDirective).toBe(true); - expect(result.rawModel).toBe("gpt-5"); - expect(result.cleaned).toBe(""); + expect(result.hasDirective).toBe(false); + expect(result.rawModel).toBeUndefined(); + expect(result.cleaned).toBe("/models gpt-5"); + }); + + it("does not parse /models as a /model directive (no args)", () => { + const result = extractModelDirective("/models"); + expect(result.hasDirective).toBe(false); + expect(result.cleaned).toBe("/models"); }); it("extracts /model with provider/model format", () => { diff --git a/src/auto-reply/model.ts b/src/auto-reply/model.ts index 24dd8ea0cda..450a016b69d 100644 --- a/src/auto-reply/model.ts +++ b/src/auto-reply/model.ts @@ -14,7 +14,7 @@ export function extractModelDirective( if (!body) return { cleaned: "", hasDirective: false }; const modelMatch = body.match( - /(?:^|\s)\/models?(?=$|\s|:)\s*:?\s*([A-Za-z0-9_.:@-]+(?:\/[A-Za-z0-9_.:@-]+)*)?/i, + /(?:^|\s)\/model(?=$|\s|:)\s*:?\s*([A-Za-z0-9_.:@-]+(?:\/[A-Za-z0-9_.:@-]+)*)?/i, ); const aliases = (options?.aliases ?? []).map((alias) => alias.trim()).filter(Boolean); diff --git a/src/auto-reply/reply/agent-runner-execution.ts b/src/auto-reply/reply/agent-runner-execution.ts index a428aa6da66..939fa92f0fe 100644 --- a/src/auto-reply/reply/agent-runner-execution.ts +++ b/src/auto-reply/reply/agent-runner-execution.ts @@ -179,6 +179,17 @@ export async function runAgentTurnWithFallback(params: { images: params.opts?.images, }) .then((result) => { + // CLI backends don't emit streaming assistant events, so we need to + // emit one with the final text so server-chat can populate its buffer + // and send the response to TUI/WebSocket clients. + const cliText = result.payloads?.[0]?.text?.trim(); + if (cliText) { + emitAgentEvent({ + runId, + stream: "assistant", + data: { text: cliText }, + }); + } emitAgentEvent({ runId, stream: "lifecycle", @@ -358,12 +369,13 @@ export async function runAgentTurnWithFallback(params: { // Use pipeline if available (block streaming enabled), otherwise send directly if (params.blockStreamingEnabled && params.blockReplyPipeline) { params.blockReplyPipeline.enqueue(blockPayload); - } else { - // Send directly when flushing before tool execution (no streaming). + } else if (params.blockStreamingEnabled) { + // Send directly when flushing before tool execution (no pipeline but streaming enabled). // Track sent key to avoid duplicate in final payloads. directlySentBlockKeys.add(createBlockReplyPayloadKey(blockPayload)); await params.opts?.onBlockReply?.(blockPayload); } + // When streaming is disabled entirely, blocks are accumulated in final text instead. } : undefined, onBlockReplyFlush: diff --git a/src/auto-reply/reply/block-streaming.ts b/src/auto-reply/reply/block-streaming.ts index 82fa859199a..8759207315a 100644 --- a/src/auto-reply/reply/block-streaming.ts +++ b/src/auto-reply/reply/block-streaming.ts @@ -7,7 +7,7 @@ import { INTERNAL_MESSAGE_CHANNEL, listDeliverableMessageChannels, } from "../../utils/message-channel.js"; -import { resolveChunkMode, resolveTextChunkLimit, type TextChunkProvider } from "../chunk.js"; +import { resolveTextChunkLimit, type TextChunkProvider } from "../chunk.js"; const DEFAULT_BLOCK_STREAM_MIN = 800; const DEFAULT_BLOCK_STREAM_MAX = 1200; @@ -69,15 +69,11 @@ export function resolveBlockStreamingChunking( }); const chunkCfg = cfg?.agents?.defaults?.blockStreamingChunk; - // When chunkMode is "newline", use newline-based streaming - const channelChunkMode = resolveChunkMode(cfg, providerKey, accountId); - if (channelChunkMode === "newline") { - // For newline mode: use very low minChars to flush quickly on newlines - const minChars = Math.max(1, Math.floor(chunkCfg?.minChars ?? 1)); - const maxRequested = Math.max(1, Math.floor(chunkCfg?.maxChars ?? textLimit)); - const maxChars = Math.max(1, Math.min(maxRequested, textLimit)); - return { minChars, maxChars, breakPreference: "newline" }; - } + // Note: chunkMode="newline" used to imply splitting on each newline, but outbound + // delivery now treats it as paragraph-aware chunking (only split on blank lines). + // Block streaming should follow the same rule, so we do NOT special-case newline + // mode here. + // (chunkMode no longer alters block streaming behavior) const maxRequested = Math.max(1, Math.floor(chunkCfg?.maxChars ?? DEFAULT_BLOCK_STREAM_MAX)); const maxChars = Math.max(1, Math.min(maxRequested, textLimit)); @@ -103,11 +99,8 @@ export function resolveBlockStreamingCoalescing( ): BlockStreamingCoalescing | undefined { const providerKey = normalizeChunkProvider(provider); - // When chunkMode is "newline", disable coalescing to send each line immediately - const channelChunkMode = resolveChunkMode(cfg, providerKey, accountId); - if (channelChunkMode === "newline") { - return undefined; - } + // Note: chunkMode="newline" is paragraph-aware in outbound delivery (blank-line splits), + // so block streaming should not disable coalescing or flush per single newline. const providerId = providerKey ? normalizeChannelId(providerKey) : null; const providerChunkLimit = providerId diff --git a/src/auto-reply/reply/commands-plugin.ts b/src/auto-reply/reply/commands-plugin.ts index 86a99e6bc1e..3b21a6aa003 100644 --- a/src/auto-reply/reply/commands-plugin.ts +++ b/src/auto-reply/reply/commands-plugin.ts @@ -15,10 +15,12 @@ import type { CommandHandler, CommandHandlerResult } from "./commands-types.js"; */ export const handlePluginCommand: CommandHandler = async ( params, - _allowTextCommands, + allowTextCommands, ): Promise => { const { command, cfg } = params; + if (!allowTextCommands) return null; + // Try to match a plugin command const match = matchPluginCommand(command.commandBodyNormalized); if (!match) return null; @@ -36,6 +38,6 @@ export const handlePluginCommand: CommandHandler = async ( return { shouldContinue: false, - reply: { text: result.text }, + reply: result, }; }; diff --git a/src/auto-reply/reply/commands.test.ts b/src/auto-reply/reply/commands.test.ts index d27b8e2a879..7078c15dce2 100644 --- a/src/auto-reply/reply/commands.test.ts +++ b/src/auto-reply/reply/commands.test.ts @@ -10,11 +10,26 @@ import { } from "../../agents/subagent-registry.js"; import type { ClawdbotConfig } from "../../config/config.js"; import * as internalHooks from "../../hooks/internal-hooks.js"; +import { clearPluginCommands, registerPluginCommand } from "../../plugins/commands.js"; import type { MsgContext } from "../templating.js"; import { resetBashChatCommandForTests } from "./bash-command.js"; import { buildCommandContext, handleCommands } from "./commands.js"; import { parseInlineDirectives } from "./directive-handling.js"; +// Avoid expensive workspace scans during /context tests. +vi.mock("./commands-context-report.js", () => ({ + buildContextReply: async (params: { command: { commandBodyNormalized: string } }) => { + const normalized = params.command.commandBodyNormalized; + if (normalized === "/context list") { + return { text: "Injected workspace files:\n- AGENTS.md" }; + } + if (normalized === "/context detail") { + return { text: "Context breakdown (detailed)\nTop tools (schema size):" }; + } + return { text: "/context\n- /context list\nInline shortcut" }; + }, +})); + let testWorkspaceDir = os.tmpdir(); beforeAll(async () => { @@ -143,6 +158,29 @@ describe("handleCommands bash alias", () => { }); }); +describe("handleCommands plugin commands", () => { + it("dispatches registered plugin commands", async () => { + clearPluginCommands(); + const result = registerPluginCommand("test-plugin", { + name: "card", + description: "Test card", + handler: async () => ({ text: "from plugin" }), + }); + expect(result.ok).toBe(true); + + const cfg = { + commands: { text: true }, + channels: { whatsapp: { allowFrom: ["*"] } }, + } as ClawdbotConfig; + const params = buildParams("/card", cfg); + const commandResult = await handleCommands(params); + + expect(commandResult.shouldContinue).toBe(false); + expect(commandResult.reply?.text).toBe("from plugin"); + clearPluginCommands(); + }); +}); + describe("handleCommands identity", () => { it("returns sender details for /whoami", async () => { const cfg = { diff --git a/src/auto-reply/reply/directives.ts b/src/auto-reply/reply/directives.ts index 15b0dcb1a01..7fc6592668c 100644 --- a/src/auto-reply/reply/directives.ts +++ b/src/auto-reply/reply/directives.ts @@ -1,7 +1,8 @@ -import type { ReasoningLevel } from "../thinking.js"; +import type { NoticeLevel, ReasoningLevel } from "../thinking.js"; import { type ElevatedLevel, normalizeElevatedLevel, + normalizeNoticeLevel, normalizeReasoningLevel, normalizeThinkLevel, normalizeVerboseLevel, @@ -112,6 +113,22 @@ export function extractVerboseDirective(body?: string): { }; } +export function extractNoticeDirective(body?: string): { + cleaned: string; + noticeLevel?: NoticeLevel; + rawLevel?: string; + hasDirective: boolean; +} { + if (!body) return { cleaned: "", hasDirective: false }; + const extracted = extractLevelDirective(body, ["notice", "notices"], normalizeNoticeLevel); + return { + cleaned: extracted.cleaned, + noticeLevel: extracted.level, + rawLevel: extracted.rawLevel, + hasDirective: extracted.hasDirective, + }; +} + export function extractElevatedDirective(body?: string): { cleaned: string; elevatedLevel?: ElevatedLevel; @@ -152,5 +169,5 @@ export function extractStatusDirective(body?: string): { return extractSimpleDirective(body, ["status"]); } -export type { ElevatedLevel, ReasoningLevel, ThinkLevel, VerboseLevel }; +export type { ElevatedLevel, NoticeLevel, ReasoningLevel, ThinkLevel, VerboseLevel }; export { extractExecDirective } from "./exec/directive.js"; diff --git a/src/auto-reply/reply/dispatch-from-config.test.ts b/src/auto-reply/reply/dispatch-from-config.test.ts index 4e19826742b..0504694bf82 100644 --- a/src/auto-reply/reply/dispatch-from-config.test.ts +++ b/src/auto-reply/reply/dispatch-from-config.test.ts @@ -138,6 +138,38 @@ describe("dispatchReplyFromConfig", () => { ); }); + it("does not provide onToolResult when routing cross-provider", async () => { + mocks.tryFastAbortFromMessage.mockResolvedValue({ + handled: false, + aborted: false, + }); + mocks.routeReply.mockClear(); + const cfg = {} as ClawdbotConfig; + const dispatcher = createDispatcher(); + const ctx = buildTestCtx({ + Provider: "slack", + OriginatingChannel: "telegram", + OriginatingTo: "telegram:999", + }); + + const replyResolver = async ( + _ctx: MsgContext, + opts: GetReplyOptions | undefined, + _cfg: ClawdbotConfig, + ) => { + expect(opts?.onToolResult).toBeUndefined(); + return { text: "hi" } satisfies ReplyPayload; + }; + + await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver }); + + expect(mocks.routeReply).toHaveBeenCalledWith( + expect.objectContaining({ + payload: expect.objectContaining({ text: "hi" }), + }), + ); + }); + it("fast-aborts without calling the reply resolver", async () => { mocks.tryFastAbortFromMessage.mockResolvedValue({ handled: true, diff --git a/src/auto-reply/reply/dispatch-from-config.ts b/src/auto-reply/reply/dispatch-from-config.ts index 16c83bf300f..f946c05f932 100644 --- a/src/auto-reply/reply/dispatch-from-config.ts +++ b/src/auto-reply/reply/dispatch-from-config.ts @@ -206,6 +206,7 @@ export async function dispatchReplyFromConfig(params: { const sendPayloadAsync = async ( payload: ReplyPayload, abortSignal?: AbortSignal, + mirror?: boolean, ): Promise => { // TypeScript doesn't narrow these from the shouldRouteToOriginating check, // but they're guaranteed non-null when this function is called. @@ -220,6 +221,7 @@ export async function dispatchReplyFromConfig(params: { threadId: ctx.MessageThreadId, cfg, abortSignal, + mirror, }); if (!result.ok) { logVerbose(`dispatch-from-config: route-reply failed: ${result.error ?? "unknown error"}`); @@ -268,24 +270,6 @@ export async function dispatchReplyFromConfig(params: { ctx, { ...params.replyOptions, - onToolResult: (payload: ReplyPayload) => { - const run = async () => { - const ttsPayload = await maybeApplyTtsToPayload({ - payload, - cfg, - channel: ttsChannel, - kind: "tool", - inboundAudio, - ttsAuto: sessionTtsAuto, - }); - if (shouldRouteToOriginating) { - await sendPayloadAsync(ttsPayload); - } else { - dispatcher.sendToolResult(ttsPayload); - } - }; - return run(); - }, onBlockReply: (payload: ReplyPayload, context) => { const run = async () => { const ttsPayload = await maybeApplyTtsToPayload({ @@ -297,7 +281,7 @@ export async function dispatchReplyFromConfig(params: { ttsAuto: sessionTtsAuto, }); if (shouldRouteToOriginating) { - await sendPayloadAsync(ttsPayload, context?.abortSignal); + await sendPayloadAsync(ttsPayload, context?.abortSignal, false); } else { dispatcher.sendBlockReply(ttsPayload); } diff --git a/src/auto-reply/reply/line-directives.test.ts b/src/auto-reply/reply/line-directives.test.ts new file mode 100644 index 00000000000..bf60232b854 --- /dev/null +++ b/src/auto-reply/reply/line-directives.test.ts @@ -0,0 +1,377 @@ +import { describe, expect, it } from "vitest"; +import { parseLineDirectives, hasLineDirectives } from "./line-directives.js"; + +const getLineData = (result: ReturnType) => + (result.channelData?.line as Record | undefined) ?? {}; + +describe("hasLineDirectives", () => { + it("detects quick_replies directive", () => { + expect(hasLineDirectives("Here are options [[quick_replies: A, B, C]]")).toBe(true); + }); + + it("detects location directive", () => { + expect(hasLineDirectives("[[location: Place | Address | 35.6 | 139.7]]")).toBe(true); + }); + + it("detects confirm directive", () => { + expect(hasLineDirectives("[[confirm: Continue? | Yes | No]]")).toBe(true); + }); + + it("detects buttons directive", () => { + expect(hasLineDirectives("[[buttons: Menu | Choose | Opt1:data1, Opt2:data2]]")).toBe(true); + }); + + it("returns false for regular text", () => { + expect(hasLineDirectives("Just regular text")).toBe(false); + }); + + it("returns false for similar but invalid patterns", () => { + expect(hasLineDirectives("[[not_a_directive: something]]")).toBe(false); + }); + + it("detects media_player directive", () => { + expect(hasLineDirectives("[[media_player: Song | Artist | Speaker]]")).toBe(true); + }); + + it("detects event directive", () => { + expect(hasLineDirectives("[[event: Meeting | Jan 24 | 2pm]]")).toBe(true); + }); + + it("detects agenda directive", () => { + expect(hasLineDirectives("[[agenda: Today | Meeting:9am, Lunch:12pm]]")).toBe(true); + }); + + it("detects device directive", () => { + expect(hasLineDirectives("[[device: TV | Room]]")).toBe(true); + }); + + it("detects appletv_remote directive", () => { + expect(hasLineDirectives("[[appletv_remote: Apple TV | Playing]]")).toBe(true); + }); +}); + +describe("parseLineDirectives", () => { + describe("quick_replies", () => { + it("parses quick_replies and removes from text", () => { + const result = parseLineDirectives({ + text: "Choose one:\n[[quick_replies: Option A, Option B, Option C]]", + }); + + expect(getLineData(result).quickReplies).toEqual(["Option A", "Option B", "Option C"]); + expect(result.text).toBe("Choose one:"); + }); + + it("handles quick_replies in middle of text", () => { + const result = parseLineDirectives({ + text: "Before [[quick_replies: A, B]] After", + }); + + expect(getLineData(result).quickReplies).toEqual(["A", "B"]); + expect(result.text).toBe("Before After"); + }); + + it("merges with existing quickReplies", () => { + const result = parseLineDirectives({ + text: "Text [[quick_replies: C, D]]", + channelData: { line: { quickReplies: ["A", "B"] } }, + }); + + expect(getLineData(result).quickReplies).toEqual(["A", "B", "C", "D"]); + }); + }); + + describe("location", () => { + it("parses location with all fields", () => { + const result = parseLineDirectives({ + text: "Here's the location:\n[[location: Tokyo Station | Tokyo, Japan | 35.6812 | 139.7671]]", + }); + + expect(getLineData(result).location).toEqual({ + title: "Tokyo Station", + address: "Tokyo, Japan", + latitude: 35.6812, + longitude: 139.7671, + }); + expect(result.text).toBe("Here's the location:"); + }); + + it("ignores invalid coordinates", () => { + const result = parseLineDirectives({ + text: "[[location: Place | Address | invalid | 139.7]]", + }); + + expect(getLineData(result).location).toBeUndefined(); + }); + + it("does not override existing location", () => { + const existing = { title: "Existing", address: "Addr", latitude: 1, longitude: 2 }; + const result = parseLineDirectives({ + text: "[[location: New | New Addr | 35.6 | 139.7]]", + channelData: { line: { location: existing } }, + }); + + expect(getLineData(result).location).toEqual(existing); + }); + }); + + describe("confirm", () => { + it("parses simple confirm", () => { + const result = parseLineDirectives({ + text: "[[confirm: Delete this item? | Yes | No]]", + }); + + expect(getLineData(result).templateMessage).toEqual({ + type: "confirm", + text: "Delete this item?", + confirmLabel: "Yes", + confirmData: "yes", + cancelLabel: "No", + cancelData: "no", + altText: "Delete this item?", + }); + // Text is undefined when directive consumes entire text + expect(result.text).toBeUndefined(); + }); + + it("parses confirm with custom data", () => { + const result = parseLineDirectives({ + text: "[[confirm: Proceed? | OK:action=confirm | Cancel:action=cancel]]", + }); + + expect(getLineData(result).templateMessage).toEqual({ + type: "confirm", + text: "Proceed?", + confirmLabel: "OK", + confirmData: "action=confirm", + cancelLabel: "Cancel", + cancelData: "action=cancel", + altText: "Proceed?", + }); + }); + }); + + describe("buttons", () => { + it("parses buttons with message actions", () => { + const result = parseLineDirectives({ + text: "[[buttons: Menu | Select an option | Help:/help, Status:/status]]", + }); + + expect(getLineData(result).templateMessage).toEqual({ + type: "buttons", + title: "Menu", + text: "Select an option", + actions: [ + { type: "message", label: "Help", data: "/help" }, + { type: "message", label: "Status", data: "/status" }, + ], + altText: "Menu: Select an option", + }); + }); + + it("parses buttons with uri actions", () => { + const result = parseLineDirectives({ + text: "[[buttons: Links | Visit us | Site:https://example.com]]", + }); + + const templateMessage = getLineData(result).templateMessage as { + type?: string; + actions?: Array>; + }; + expect(templateMessage?.type).toBe("buttons"); + if (templateMessage?.type === "buttons") { + expect(templateMessage.actions?.[0]).toEqual({ + type: "uri", + label: "Site", + uri: "https://example.com", + }); + } + }); + + it("parses buttons with postback actions", () => { + const result = parseLineDirectives({ + text: "[[buttons: Actions | Choose | Select:action=select&id=1]]", + }); + + const templateMessage = getLineData(result).templateMessage as { + type?: string; + actions?: Array>; + }; + expect(templateMessage?.type).toBe("buttons"); + if (templateMessage?.type === "buttons") { + expect(templateMessage.actions?.[0]).toEqual({ + type: "postback", + label: "Select", + data: "action=select&id=1", + }); + } + }); + + it("limits to 4 actions", () => { + const result = parseLineDirectives({ + text: "[[buttons: Menu | Text | A:a, B:b, C:c, D:d, E:e, F:f]]", + }); + + const templateMessage = getLineData(result).templateMessage as { + type?: string; + actions?: Array>; + }; + expect(templateMessage?.type).toBe("buttons"); + if (templateMessage?.type === "buttons") { + expect(templateMessage.actions?.length).toBe(4); + } + }); + }); + + describe("media_player", () => { + it("parses media_player with all fields", () => { + const result = parseLineDirectives({ + text: "Now playing:\n[[media_player: Bohemian Rhapsody | Queen | Speaker | https://example.com/album.jpg | playing]]", + }); + + const flexMessage = getLineData(result).flexMessage as { + altText?: string; + contents?: { footer?: { contents?: unknown[] } }; + }; + expect(flexMessage).toBeDefined(); + expect(flexMessage?.altText).toBe("🎵 Bohemian Rhapsody - Queen"); + const contents = flexMessage?.contents as { footer?: { contents?: unknown[] } }; + expect(contents.footer?.contents?.length).toBeGreaterThan(0); + expect(result.text).toBe("Now playing:"); + }); + + it("parses media_player with minimal fields", () => { + const result = parseLineDirectives({ + text: "[[media_player: Unknown Track]]", + }); + + const flexMessage = getLineData(result).flexMessage as { altText?: string }; + expect(flexMessage).toBeDefined(); + expect(flexMessage?.altText).toBe("🎵 Unknown Track"); + }); + + it("handles paused status", () => { + const result = parseLineDirectives({ + text: "[[media_player: Song | Artist | Player | | paused]]", + }); + + const flexMessage = getLineData(result).flexMessage as { + contents?: { body: { contents: unknown[] } }; + }; + expect(flexMessage).toBeDefined(); + const contents = flexMessage?.contents as { body: { contents: unknown[] } }; + expect(contents).toBeDefined(); + }); + }); + + describe("event", () => { + it("parses event with all fields", () => { + const result = parseLineDirectives({ + text: "[[event: Team Meeting | January 24, 2026 | 2:00 PM - 3:00 PM | Conference Room A | Discuss Q1 roadmap]]", + }); + + const flexMessage = getLineData(result).flexMessage as { altText?: string }; + expect(flexMessage).toBeDefined(); + expect(flexMessage?.altText).toBe("📅 Team Meeting - January 24, 2026 2:00 PM - 3:00 PM"); + }); + + it("parses event with minimal fields", () => { + const result = parseLineDirectives({ + text: "[[event: Birthday Party | March 15]]", + }); + + const flexMessage = getLineData(result).flexMessage as { altText?: string }; + expect(flexMessage).toBeDefined(); + expect(flexMessage?.altText).toBe("📅 Birthday Party - March 15"); + }); + }); + + describe("agenda", () => { + it("parses agenda with multiple events", () => { + const result = parseLineDirectives({ + text: "[[agenda: Today's Schedule | Team Meeting:9:00 AM, Lunch:12:00 PM, Review:3:00 PM]]", + }); + + const flexMessage = getLineData(result).flexMessage as { altText?: string }; + expect(flexMessage).toBeDefined(); + expect(flexMessage?.altText).toBe("📋 Today's Schedule (3 events)"); + }); + + it("parses agenda with events without times", () => { + const result = parseLineDirectives({ + text: "[[agenda: Tasks | Buy groceries, Call mom, Workout]]", + }); + + const flexMessage = getLineData(result).flexMessage as { altText?: string }; + expect(flexMessage).toBeDefined(); + expect(flexMessage?.altText).toBe("📋 Tasks (3 events)"); + }); + }); + + describe("device", () => { + it("parses device with controls", () => { + const result = parseLineDirectives({ + text: "[[device: TV | Streaming Box | Playing | Play/Pause:toggle, Menu:menu]]", + }); + + const flexMessage = getLineData(result).flexMessage as { altText?: string }; + expect(flexMessage).toBeDefined(); + expect(flexMessage?.altText).toBe("📱 TV: Playing"); + }); + + it("parses device with minimal fields", () => { + const result = parseLineDirectives({ + text: "[[device: Speaker]]", + }); + + const flexMessage = getLineData(result).flexMessage as { altText?: string }; + expect(flexMessage).toBeDefined(); + expect(flexMessage?.altText).toBe("📱 Speaker"); + }); + }); + + describe("appletv_remote", () => { + it("parses appletv_remote with status", () => { + const result = parseLineDirectives({ + text: "[[appletv_remote: Apple TV | Playing]]", + }); + + const flexMessage = getLineData(result).flexMessage as { altText?: string }; + expect(flexMessage).toBeDefined(); + expect(flexMessage?.altText).toContain("Apple TV"); + }); + + it("parses appletv_remote with minimal fields", () => { + const result = parseLineDirectives({ + text: "[[appletv_remote: Apple TV]]", + }); + + const flexMessage = getLineData(result).flexMessage as { altText?: string }; + expect(flexMessage).toBeDefined(); + }); + }); + + describe("combined directives", () => { + it("handles text with no directives", () => { + const result = parseLineDirectives({ + text: "Just plain text here", + }); + + expect(result.text).toBe("Just plain text here"); + expect(getLineData(result).quickReplies).toBeUndefined(); + expect(getLineData(result).location).toBeUndefined(); + expect(getLineData(result).templateMessage).toBeUndefined(); + }); + + it("preserves other payload fields", () => { + const result = parseLineDirectives({ + text: "Hello [[quick_replies: A, B]]", + mediaUrl: "https://example.com/image.jpg", + replyToId: "msg123", + }); + + expect(result.mediaUrl).toBe("https://example.com/image.jpg"); + expect(result.replyToId).toBe("msg123"); + expect(getLineData(result).quickReplies).toEqual(["A", "B"]); + }); + }); +}); diff --git a/src/auto-reply/reply/line-directives.ts b/src/auto-reply/reply/line-directives.ts new file mode 100644 index 00000000000..a28faeef7c9 --- /dev/null +++ b/src/auto-reply/reply/line-directives.ts @@ -0,0 +1,336 @@ +import type { ReplyPayload } from "../types.js"; +import type { LineChannelData } from "../../line/types.js"; +import { + createMediaPlayerCard, + createEventCard, + createAgendaCard, + createDeviceControlCard, + createAppleTvRemoteCard, +} from "../../line/flex-templates.js"; + +/** + * Parse LINE-specific directives from text and extract them into ReplyPayload fields. + * + * Supported directives: + * - [[quick_replies: option1, option2, option3]] + * - [[location: title | address | latitude | longitude]] + * - [[confirm: question | yes_label | no_label]] + * - [[buttons: title | text | btn1:data1, btn2:data2]] + * - [[media_player: title | artist | source | imageUrl | playing/paused]] + * - [[event: title | date | time | location | description]] + * - [[agenda: title | event1_title:event1_time, event2_title:event2_time, ...]] + * - [[device: name | type | status | ctrl1:data1, ctrl2:data2]] + * - [[appletv_remote: name | status]] + * + * Returns the modified payload with directives removed from text and fields populated. + */ +export function parseLineDirectives(payload: ReplyPayload): ReplyPayload { + let text = payload.text; + if (!text) return payload; + + const result: ReplyPayload = { ...payload }; + const lineData: LineChannelData = { + ...(result.channelData?.line as LineChannelData | undefined), + }; + const toSlug = (value: string): string => + value + .toLowerCase() + .replace(/[^a-z0-9]+/g, "_") + .replace(/^_+|_+$/g, "") || "device"; + const lineActionData = (action: string, extras?: Record): string => { + const base = [`line.action=${encodeURIComponent(action)}`]; + if (extras) { + for (const [key, value] of Object.entries(extras)) { + base.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); + } + } + return base.join("&"); + }; + + // Parse [[quick_replies: option1, option2, option3]] + const quickRepliesMatch = text.match(/\[\[quick_replies:\s*([^\]]+)\]\]/i); + if (quickRepliesMatch) { + const options = quickRepliesMatch[1] + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + if (options.length > 0) { + lineData.quickReplies = [...(lineData.quickReplies || []), ...options]; + } + text = text.replace(quickRepliesMatch[0], "").trim(); + } + + // Parse [[location: title | address | latitude | longitude]] + const locationMatch = text.match(/\[\[location:\s*([^\]]+)\]\]/i); + if (locationMatch && !lineData.location) { + const parts = locationMatch[1].split("|").map((s) => s.trim()); + if (parts.length >= 4) { + const [title, address, latStr, lonStr] = parts; + const latitude = parseFloat(latStr); + const longitude = parseFloat(lonStr); + if (!isNaN(latitude) && !isNaN(longitude)) { + lineData.location = { + title: title || "Location", + address: address || "", + latitude, + longitude, + }; + } + } + text = text.replace(locationMatch[0], "").trim(); + } + + // Parse [[confirm: question | yes_label | no_label]] or [[confirm: question | yes_label:yes_data | no_label:no_data]] + const confirmMatch = text.match(/\[\[confirm:\s*([^\]]+)\]\]/i); + if (confirmMatch && !lineData.templateMessage) { + const parts = confirmMatch[1].split("|").map((s) => s.trim()); + if (parts.length >= 3) { + const [question, yesPart, noPart] = parts; + + // Parse yes_label:yes_data format + const [yesLabel, yesData] = yesPart.includes(":") + ? yesPart.split(":").map((s) => s.trim()) + : [yesPart, yesPart.toLowerCase()]; + + const [noLabel, noData] = noPart.includes(":") + ? noPart.split(":").map((s) => s.trim()) + : [noPart, noPart.toLowerCase()]; + + lineData.templateMessage = { + type: "confirm", + text: question, + confirmLabel: yesLabel, + confirmData: yesData, + cancelLabel: noLabel, + cancelData: noData, + altText: question, + }; + } + text = text.replace(confirmMatch[0], "").trim(); + } + + // Parse [[buttons: title | text | btn1:data1, btn2:data2]] + const buttonsMatch = text.match(/\[\[buttons:\s*([^\]]+)\]\]/i); + if (buttonsMatch && !lineData.templateMessage) { + const parts = buttonsMatch[1].split("|").map((s) => s.trim()); + if (parts.length >= 3) { + const [title, bodyText, actionsStr] = parts; + + const actions = actionsStr.split(",").map((actionStr) => { + const trimmed = actionStr.trim(); + // Find first colon delimiter, ignoring URLs without a label. + const colonIndex = (() => { + const index = trimmed.indexOf(":"); + if (index === -1) return -1; + const lower = trimmed.toLowerCase(); + if (lower.startsWith("http://") || lower.startsWith("https://")) return -1; + return index; + })(); + + let label: string; + let data: string; + + if (colonIndex === -1) { + label = trimmed; + data = trimmed; + } else { + label = trimmed.slice(0, colonIndex).trim(); + data = trimmed.slice(colonIndex + 1).trim(); + } + + // Detect action type + if (data.startsWith("http://") || data.startsWith("https://")) { + return { type: "uri" as const, label, uri: data }; + } + if (data.includes("=")) { + return { type: "postback" as const, label, data }; + } + return { type: "message" as const, label, data: data || label }; + }); + + if (actions.length > 0) { + lineData.templateMessage = { + type: "buttons", + title, + text: bodyText, + actions: actions.slice(0, 4), // LINE limit + altText: `${title}: ${bodyText}`, + }; + } + } + text = text.replace(buttonsMatch[0], "").trim(); + } + + // Parse [[media_player: title | artist | source | imageUrl | playing/paused]] + const mediaPlayerMatch = text.match(/\[\[media_player:\s*([^\]]+)\]\]/i); + if (mediaPlayerMatch && !lineData.flexMessage) { + const parts = mediaPlayerMatch[1].split("|").map((s) => s.trim()); + if (parts.length >= 1) { + const [title, artist, source, imageUrl, statusStr] = parts; + const isPlaying = statusStr?.toLowerCase() === "playing"; + + // LINE requires HTTPS URLs for images - skip local/HTTP URLs + const validImageUrl = imageUrl?.startsWith("https://") ? imageUrl : undefined; + + const deviceKey = toSlug(source || title || "media"); + const card = createMediaPlayerCard({ + title: title || "Unknown Track", + subtitle: artist || undefined, + source: source || undefined, + imageUrl: validImageUrl, + isPlaying: statusStr ? isPlaying : undefined, + controls: { + previous: { data: lineActionData("previous", { "line.device": deviceKey }) }, + play: { data: lineActionData("play", { "line.device": deviceKey }) }, + pause: { data: lineActionData("pause", { "line.device": deviceKey }) }, + next: { data: lineActionData("next", { "line.device": deviceKey }) }, + }, + }); + + lineData.flexMessage = { + altText: `🎵 ${title}${artist ? ` - ${artist}` : ""}`, + contents: card, + }; + } + text = text.replace(mediaPlayerMatch[0], "").trim(); + } + + // Parse [[event: title | date | time | location | description]] + const eventMatch = text.match(/\[\[event:\s*([^\]]+)\]\]/i); + if (eventMatch && !lineData.flexMessage) { + const parts = eventMatch[1].split("|").map((s) => s.trim()); + if (parts.length >= 2) { + const [title, date, time, location, description] = parts; + + const card = createEventCard({ + title: title || "Event", + date: date || "TBD", + time: time || undefined, + location: location || undefined, + description: description || undefined, + }); + + lineData.flexMessage = { + altText: `📅 ${title} - ${date}${time ? ` ${time}` : ""}`, + contents: card, + }; + } + text = text.replace(eventMatch[0], "").trim(); + } + + // Parse [[appletv_remote: name | status]] + const appleTvMatch = text.match(/\[\[appletv_remote:\s*([^\]]+)\]\]/i); + if (appleTvMatch && !lineData.flexMessage) { + const parts = appleTvMatch[1].split("|").map((s) => s.trim()); + if (parts.length >= 1) { + const [deviceName, status] = parts; + const deviceKey = toSlug(deviceName || "apple_tv"); + + const card = createAppleTvRemoteCard({ + deviceName: deviceName || "Apple TV", + status: status || undefined, + actionData: { + up: lineActionData("up", { "line.device": deviceKey }), + down: lineActionData("down", { "line.device": deviceKey }), + left: lineActionData("left", { "line.device": deviceKey }), + right: lineActionData("right", { "line.device": deviceKey }), + select: lineActionData("select", { "line.device": deviceKey }), + menu: lineActionData("menu", { "line.device": deviceKey }), + home: lineActionData("home", { "line.device": deviceKey }), + play: lineActionData("play", { "line.device": deviceKey }), + pause: lineActionData("pause", { "line.device": deviceKey }), + volumeUp: lineActionData("volume_up", { "line.device": deviceKey }), + volumeDown: lineActionData("volume_down", { "line.device": deviceKey }), + mute: lineActionData("mute", { "line.device": deviceKey }), + }, + }); + + lineData.flexMessage = { + altText: `📺 ${deviceName || "Apple TV"} Remote`, + contents: card, + }; + } + text = text.replace(appleTvMatch[0], "").trim(); + } + + // Parse [[agenda: title | event1_title:event1_time, event2_title:event2_time, ...]] + const agendaMatch = text.match(/\[\[agenda:\s*([^\]]+)\]\]/i); + if (agendaMatch && !lineData.flexMessage) { + const parts = agendaMatch[1].split("|").map((s) => s.trim()); + if (parts.length >= 2) { + const [title, eventsStr] = parts; + + const events = eventsStr.split(",").map((eventStr) => { + const trimmed = eventStr.trim(); + const colonIdx = trimmed.lastIndexOf(":"); + if (colonIdx > 0) { + return { + title: trimmed.slice(0, colonIdx).trim(), + time: trimmed.slice(colonIdx + 1).trim(), + }; + } + return { title: trimmed }; + }); + + const card = createAgendaCard({ + title: title || "Agenda", + events, + }); + + lineData.flexMessage = { + altText: `📋 ${title} (${events.length} events)`, + contents: card, + }; + } + text = text.replace(agendaMatch[0], "").trim(); + } + + // Parse [[device: name | type | status | ctrl1:data1, ctrl2:data2]] + const deviceMatch = text.match(/\[\[device:\s*([^\]]+)\]\]/i); + if (deviceMatch && !lineData.flexMessage) { + const parts = deviceMatch[1].split("|").map((s) => s.trim()); + if (parts.length >= 1) { + const [deviceName, deviceType, status, controlsStr] = parts; + + const deviceKey = toSlug(deviceName || "device"); + const controls = controlsStr + ? controlsStr.split(",").map((ctrlStr) => { + const [label, data] = ctrlStr.split(":").map((s) => s.trim()); + const action = data || label.toLowerCase().replace(/\s+/g, "_"); + return { label, data: lineActionData(action, { "line.device": deviceKey }) }; + }) + : []; + + const card = createDeviceControlCard({ + deviceName: deviceName || "Device", + deviceType: deviceType || undefined, + status: status || undefined, + controls, + }); + + lineData.flexMessage = { + altText: `📱 ${deviceName}${status ? `: ${status}` : ""}`, + contents: card, + }; + } + text = text.replace(deviceMatch[0], "").trim(); + } + + // Clean up multiple whitespace/newlines + text = text.replace(/\n{3,}/g, "\n\n").trim(); + + result.text = text || undefined; + if (Object.keys(lineData).length > 0) { + result.channelData = { ...result.channelData, line: lineData }; + } + return result; +} + +/** + * Check if text contains any LINE directives + */ +export function hasLineDirectives(text: string): boolean { + return /\[\[(quick_replies|location|confirm|buttons|media_player|event|agenda|device|appletv_remote):/i.test( + text, + ); +} diff --git a/src/auto-reply/reply/normalize-reply.test.ts b/src/auto-reply/reply/normalize-reply.test.ts new file mode 100644 index 00000000000..30fb5e3f500 --- /dev/null +++ b/src/auto-reply/reply/normalize-reply.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vitest"; + +import { normalizeReplyPayload } from "./normalize-reply.js"; + +// Keep channelData-only payloads so channel-specific replies survive normalization. +describe("normalizeReplyPayload", () => { + it("keeps channelData-only replies", () => { + const payload = { + channelData: { + line: { + flexMessage: { type: "bubble" }, + }, + }, + }; + + const normalized = normalizeReplyPayload(payload); + + expect(normalized).not.toBeNull(); + expect(normalized?.text).toBeUndefined(); + expect(normalized?.channelData).toEqual(payload.channelData); + }); +}); diff --git a/src/auto-reply/reply/normalize-reply.ts b/src/auto-reply/reply/normalize-reply.ts index f3060476b1b..7968088bd52 100644 --- a/src/auto-reply/reply/normalize-reply.ts +++ b/src/auto-reply/reply/normalize-reply.ts @@ -6,6 +6,7 @@ import { resolveResponsePrefixTemplate, type ResponsePrefixContext, } from "./response-prefix-template.js"; +import { hasLineDirectives, parseLineDirectives } from "./line-directives.js"; export type NormalizeReplyOptions = { responsePrefix?: string; @@ -21,13 +22,16 @@ export function normalizeReplyPayload( opts: NormalizeReplyOptions = {}, ): ReplyPayload | null { const hasMedia = Boolean(payload.mediaUrl || (payload.mediaUrls?.length ?? 0) > 0); + const hasChannelData = Boolean( + payload.channelData && Object.keys(payload.channelData).length > 0, + ); const trimmed = payload.text?.trim() ?? ""; - if (!trimmed && !hasMedia) return null; + if (!trimmed && !hasMedia && !hasChannelData) return null; const silentToken = opts.silentToken ?? SILENT_REPLY_TOKEN; let text = payload.text ?? undefined; if (text && isSilentReplyText(text, silentToken)) { - if (!hasMedia) return null; + if (!hasMedia && !hasChannelData) return null; text = ""; } if (text && !trimmed) { @@ -39,14 +43,21 @@ export function normalizeReplyPayload( if (shouldStripHeartbeat && text?.includes(HEARTBEAT_TOKEN)) { const stripped = stripHeartbeatToken(text, { mode: "message" }); if (stripped.didStrip) opts.onHeartbeatStrip?.(); - if (stripped.shouldSkip && !hasMedia) return null; + if (stripped.shouldSkip && !hasMedia && !hasChannelData) return null; text = stripped.text; } if (text) { text = sanitizeUserFacingText(text); } - if (!text?.trim() && !hasMedia) return null; + if (!text?.trim() && !hasMedia && !hasChannelData) return null; + + // Parse LINE-specific directives from text (quick_replies, location, confirm, buttons) + let enrichedPayload: ReplyPayload = { ...payload, text }; + if (text && hasLineDirectives(text)) { + enrichedPayload = parseLineDirectives(enrichedPayload); + text = enrichedPayload.text; + } // Resolve template variables in responsePrefix if context is provided const effectivePrefix = opts.responsePrefixContext @@ -62,5 +73,5 @@ export function normalizeReplyPayload( text = `${effectivePrefix} ${text}`; } - return { ...payload, text }; + return { ...enrichedPayload, text }; } diff --git a/src/auto-reply/reply/reply-payloads.ts b/src/auto-reply/reply/reply-payloads.ts index edfdc1cbc68..ecb28cf00ef 100644 --- a/src/auto-reply/reply/reply-payloads.ts +++ b/src/auto-reply/reply/reply-payloads.ts @@ -45,7 +45,8 @@ export function isRenderablePayload(payload: ReplyPayload): boolean { payload.text || payload.mediaUrl || (payload.mediaUrls && payload.mediaUrls.length > 0) || - payload.audioAsVoice, + payload.audioAsVoice || + payload.channelData, ); } diff --git a/src/auto-reply/reply/route-reply.test.ts b/src/auto-reply/reply/route-reply.test.ts index f587ec48146..63cd59d3e94 100644 --- a/src/auto-reply/reply/route-reply.test.ts +++ b/src/auto-reply/reply/route-reply.test.ts @@ -72,6 +72,7 @@ const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], @@ -379,6 +380,23 @@ describe("routeReply", () => { }), ); }); + + it("skips mirror data when mirror is false", async () => { + mocks.deliverOutboundPayloads.mockResolvedValue([]); + await routeReply({ + payload: { text: "hi" }, + channel: "slack", + to: "channel:C123", + sessionKey: "agent:main:main", + mirror: false, + cfg: {} as never, + }); + expect(mocks.deliverOutboundPayloads).toHaveBeenCalledWith( + expect.objectContaining({ + mirror: undefined, + }), + ); + }); }); const emptyRegistry = createRegistry([]); diff --git a/src/auto-reply/reply/route-reply.ts b/src/auto-reply/reply/route-reply.ts index bbc7efa7d4b..7db417794e7 100644 --- a/src/auto-reply/reply/route-reply.ts +++ b/src/auto-reply/reply/route-reply.ts @@ -33,6 +33,8 @@ export type RouteReplyParams = { cfg: ClawdbotConfig; /** Optional abort signal for cooperative cancellation. */ abortSignal?: AbortSignal; + /** Mirror reply into session transcript (default: true when sessionKey is set). */ + mirror?: boolean; }; export type RouteReplyResult = { @@ -118,14 +120,15 @@ export async function routeReply(params: RouteReplyParams): Promise 0) { + lines.push(""); + lines.push("Plugin commands:"); + for (const command of pluginCommands) { + const pluginLabel = command.pluginId ? ` (plugin: ${command.pluginId})` : ""; + lines.push(`/${command.name}${pluginLabel} - ${command.description}`); + } + } return lines.join("\n"); } diff --git a/src/auto-reply/thinking.ts b/src/auto-reply/thinking.ts index aabb2cf1737..a0f712199ef 100644 --- a/src/auto-reply/thinking.ts +++ b/src/auto-reply/thinking.ts @@ -1,5 +1,6 @@ export type ThinkLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh"; export type VerboseLevel = "off" | "on" | "full"; +export type NoticeLevel = "off" | "on" | "full"; export type ElevatedLevel = "off" | "on" | "ask" | "full"; export type ElevatedMode = "off" | "ask" | "full"; export type ReasoningLevel = "off" | "on" | "stream"; @@ -93,6 +94,16 @@ export function normalizeVerboseLevel(raw?: string | null): VerboseLevel | undef return undefined; } +// Normalize system notice flags used to toggle system notifications. +export function normalizeNoticeLevel(raw?: string | null): NoticeLevel | undefined { + if (!raw) return undefined; + const key = raw.toLowerCase(); + if (["off", "false", "no", "0"].includes(key)) return "off"; + if (["full", "all", "everything"].includes(key)) return "full"; + if (["on", "minimal", "true", "yes", "1"].includes(key)) return "on"; + return undefined; +} + // Normalize response-usage display modes used to toggle per-response usage footers. export function normalizeUsageDisplay(raw?: string | null): UsageDisplayLevel | undefined { if (!raw) return undefined; diff --git a/src/auto-reply/types.ts b/src/auto-reply/types.ts index 250c1409191..1aa0fe0671d 100644 --- a/src/auto-reply/types.ts +++ b/src/auto-reply/types.ts @@ -52,4 +52,6 @@ export type ReplyPayload = { /** Send audio as voice message (bubble) instead of audio file. Defaults to false. */ audioAsVoice?: boolean; isError?: boolean; + /** Channel-specific payload data (per-channel envelope). */ + channelData?: Record; }; diff --git a/src/browser/pw-session.ts b/src/browser/pw-session.ts index 0c7fa9f48e8..e1dbcf7a122 100644 --- a/src/browser/pw-session.ts +++ b/src/browser/pw-session.ts @@ -337,12 +337,56 @@ async function pageTargetId(page: Page): Promise { } } -async function findPageByTargetId(browser: Browser, targetId: string): Promise { +async function findPageByTargetId( + browser: Browser, + targetId: string, + cdpUrl?: string, +): Promise { const pages = await getAllPages(browser); + // First, try the standard CDP session approach for (const page of pages) { const tid = await pageTargetId(page).catch(() => null); if (tid && tid === targetId) return page; } + // If CDP sessions fail (e.g., extension relay blocks Target.attachToBrowserTarget), + // fall back to URL-based matching using the /json/list endpoint + if (cdpUrl) { + try { + const baseUrl = cdpUrl + .replace(/\/+$/, "") + .replace(/^ws:/, "http:") + .replace(/\/cdp$/, ""); + const response = await fetch(`${baseUrl}/json/list`); + if (response.ok) { + const targets = (await response.json()) as Array<{ + id: string; + url: string; + title?: string; + }>; + const target = targets.find((t) => t.id === targetId); + if (target) { + // Try to find a page with matching URL + const urlMatch = pages.filter((p) => p.url() === target.url); + if (urlMatch.length === 1) { + return urlMatch[0]; + } + // If multiple URL matches, use index-based matching as fallback + // This works when Playwright and the relay enumerate tabs in the same order + if (urlMatch.length > 1) { + const sameUrlTargets = targets.filter((t) => t.url === target.url); + if (sameUrlTargets.length === urlMatch.length) { + const idx = sameUrlTargets.findIndex((t) => t.id === targetId); + if (idx >= 0 && idx < urlMatch.length) { + return urlMatch[idx]; + } + } + } + } + } + } catch { + // Ignore fetch errors and fall through to return null + } + } return null; } @@ -355,7 +399,7 @@ export async function getPageForTargetId(opts: { if (!pages.length) throw new Error("No pages available in the connected browser."); const first = pages[0]; if (!opts.targetId) return first; - const found = await findPageByTargetId(browser, opts.targetId); + const found = await findPageByTargetId(browser, opts.targetId, opts.cdpUrl); if (!found) { // Extension relays can block CDP attachment APIs (e.g. Target.attachToBrowserTarget), // which prevents us from resolving a page's targetId via newCDPSession(). If Playwright @@ -496,7 +540,7 @@ export async function closePageByTargetIdViaPlaywright(opts: { targetId: string; }): Promise { const { browser } = await connectBrowser(opts.cdpUrl); - const page = await findPageByTargetId(browser, opts.targetId); + const page = await findPageByTargetId(browser, opts.targetId, opts.cdpUrl); if (!page) { throw new Error("tab not found"); } @@ -512,7 +556,7 @@ export async function focusPageByTargetIdViaPlaywright(opts: { targetId: string; }): Promise { const { browser } = await connectBrowser(opts.cdpUrl); - const page = await findPageByTargetId(browser, opts.targetId); + const page = await findPageByTargetId(browser, opts.targetId, opts.cdpUrl); if (!page) { throw new Error("tab not found"); } diff --git a/src/channels/plugins/actions/telegram.ts b/src/channels/plugins/actions/telegram.ts index 18a11c79782..fe4e4130763 100644 --- a/src/channels/plugins/actions/telegram.ts +++ b/src/channels/plugins/actions/telegram.ts @@ -13,11 +13,9 @@ const providerId = "telegram"; function readTelegramSendParams(params: Record) { const to = readStringParam(params, "to", { required: true }); const mediaUrl = readStringParam(params, "media", { trim: false }); - const content = - readStringParam(params, "message", { - required: !mediaUrl, - allowEmpty: true, - }) ?? ""; + const message = readStringParam(params, "message", { required: !mediaUrl, allowEmpty: true }); + const caption = readStringParam(params, "caption", { allowEmpty: true }); + const content = message || caption || ""; const replyTo = readStringParam(params, "replyTo"); const threadId = readStringParam(params, "threadId"); const buttons = params.buttons; diff --git a/src/channels/plugins/load.test.ts b/src/channels/plugins/load.test.ts index 7281c43daf1..d2cea25e1b9 100644 --- a/src/channels/plugins/load.test.ts +++ b/src/channels/plugins/load.test.ts @@ -13,6 +13,7 @@ const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], diff --git a/src/channels/plugins/normalize/imessage.test.ts b/src/channels/plugins/normalize/imessage.test.ts new file mode 100644 index 00000000000..afb2ec358f8 --- /dev/null +++ b/src/channels/plugins/normalize/imessage.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from "vitest"; + +import { normalizeIMessageMessagingTarget } from "./imessage.js"; + +describe("imessage target normalization", () => { + it("preserves service prefixes for handles", () => { + expect(normalizeIMessageMessagingTarget("sms:+1 (555) 222-3333")).toBe("sms:+15552223333"); + }); + + it("drops service prefixes for chat targets", () => { + expect(normalizeIMessageMessagingTarget("sms:chat_id:123")).toBe("chat_id:123"); + expect(normalizeIMessageMessagingTarget("imessage:CHAT_GUID:abc")).toBe("chat_guid:abc"); + expect(normalizeIMessageMessagingTarget("auto:ChatIdentifier:foo")).toBe("chat_identifier:foo"); + }); +}); diff --git a/src/channels/plugins/normalize/imessage.ts b/src/channels/plugins/normalize/imessage.ts new file mode 100644 index 00000000000..ec04d65571c --- /dev/null +++ b/src/channels/plugins/normalize/imessage.ts @@ -0,0 +1,35 @@ +import { normalizeIMessageHandle } from "../../../imessage/targets.js"; + +// Service prefixes that indicate explicit delivery method; must be preserved during normalization +const SERVICE_PREFIXES = ["imessage:", "sms:", "auto:"] as const; +const CHAT_TARGET_PREFIX_RE = + /^(chat_id:|chatid:|chat:|chat_guid:|chatguid:|guid:|chat_identifier:|chatidentifier:|chatident:)/i; + +export function normalizeIMessageMessagingTarget(raw: string): string | undefined { + const trimmed = raw.trim(); + if (!trimmed) return undefined; + + // Preserve service prefix if present (e.g., "sms:+1555" → "sms:+15551234567") + const lower = trimmed.toLowerCase(); + for (const prefix of SERVICE_PREFIXES) { + if (lower.startsWith(prefix)) { + const remainder = trimmed.slice(prefix.length).trim(); + const normalizedHandle = normalizeIMessageHandle(remainder); + if (!normalizedHandle) return undefined; + if (CHAT_TARGET_PREFIX_RE.test(normalizedHandle)) return normalizedHandle; + return `${prefix}${normalizedHandle}`; + } + } + + const normalized = normalizeIMessageHandle(trimmed); + return normalized || undefined; +} + +export function looksLikeIMessageTargetId(raw: string): boolean { + const trimmed = raw.trim(); + if (!trimmed) return false; + if (/^(imessage:|sms:|auto:)/i.test(trimmed)) return true; + if (CHAT_TARGET_PREFIX_RE.test(trimmed)) return true; + if (trimmed.includes("@")) return true; + return /^\+?\d{3,}$/.test(trimmed); +} diff --git a/src/channels/plugins/types.adapters.ts b/src/channels/plugins/types.adapters.ts index 982975d44f4..5b293415de6 100644 --- a/src/channels/plugins/types.adapters.ts +++ b/src/channels/plugins/types.adapters.ts @@ -1,4 +1,5 @@ import type { ClawdbotConfig } from "../../config/config.js"; +import type { ReplyPayload } from "../../auto-reply/types.js"; import type { GroupToolPolicyConfig } from "../../config/types.tools.js"; import type { OutboundDeliveryResult, OutboundSendDeps } from "../../infra/outbound/deliver.js"; import type { RuntimeEnv } from "../../runtime.js"; @@ -81,6 +82,10 @@ export type ChannelOutboundContext = { deps?: OutboundSendDeps; }; +export type ChannelOutboundPayloadContext = ChannelOutboundContext & { + payload: ReplyPayload; +}; + export type ChannelOutboundAdapter = { deliveryMode: "direct" | "gateway" | "hybrid"; chunker?: ((text: string, limit: number) => string[]) | null; @@ -94,6 +99,7 @@ export type ChannelOutboundAdapter = { accountId?: string | null; mode?: ChannelOutboundTargetMode; }) => { ok: true; to: string } | { ok: false; error: Error }; + sendPayload?: (ctx: ChannelOutboundPayloadContext) => Promise; sendText?: (ctx: ChannelOutboundContext) => Promise; sendMedia?: (ctx: ChannelOutboundContext) => Promise; sendPoll?: (ctx: ChannelPollContext) => Promise; diff --git a/src/channels/plugins/types.core.ts b/src/channels/plugins/types.core.ts index 99f847fdab5..6a76743f2ad 100644 --- a/src/channels/plugins/types.core.ts +++ b/src/channels/plugins/types.core.ts @@ -240,6 +240,12 @@ export type ChannelThreadingToolContext = { currentThreadTs?: string; replyToMode?: "off" | "first" | "all"; hasRepliedRef?: { value: boolean }; + /** + * When true, skip cross-context decoration (e.g., "[from X]" prefix). + * Use this for direct tool invocations where the agent is composing a new message, + * not forwarding/relaying a message from another conversation. + */ + skipCrossContextDecoration?: boolean; }; export type ChannelMessagingAdapter = { diff --git a/src/cli/daemon-cli/install.ts b/src/cli/daemon-cli/install.ts index 6f1998d5dc8..6ac378368a8 100644 --- a/src/cli/daemon-cli/install.ts +++ b/src/cli/daemon-cli/install.ts @@ -100,6 +100,7 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) { if (json) warnings.push(message); else defaultRuntime.log(message); }, + config: cfg, }); try { diff --git a/src/cli/gateway-cli.coverage.test.ts b/src/cli/gateway-cli.coverage.test.ts index 96437d56648..002743170dd 100644 --- a/src/cli/gateway-cli.coverage.test.ts +++ b/src/cli/gateway-cli.coverage.test.ts @@ -249,7 +249,7 @@ describe("gateway-cli coverage", () => { programInvalidPort.exitOverride(); registerGatewayCli(programInvalidPort); await expect( - programInvalidPort.parseAsync(["gateway", "--port", "0"], { + programInvalidPort.parseAsync(["gateway", "--port", "0", "--token", "test-token"], { from: "user", }), ).rejects.toThrow("__exit__:1"); @@ -263,7 +263,7 @@ describe("gateway-cli coverage", () => { registerGatewayCli(programForceFail); await expect( programForceFail.parseAsync( - ["gateway", "--port", "18789", "--force", "--allow-unconfigured"], + ["gateway", "--port", "18789", "--token", "test-token", "--force", "--allow-unconfigured"], { from: "user" }, ), ).rejects.toThrow("__exit__:1"); @@ -276,9 +276,12 @@ describe("gateway-cli coverage", () => { const beforeSigterm = new Set(process.listeners("SIGTERM")); const beforeSigint = new Set(process.listeners("SIGINT")); await expect( - programStartFail.parseAsync(["gateway", "--port", "18789", "--allow-unconfigured"], { - from: "user", - }), + programStartFail.parseAsync( + ["gateway", "--port", "18789", "--token", "test-token", "--allow-unconfigured"], + { + from: "user", + }, + ), ).rejects.toThrow("__exit__:1"); for (const listener of process.listeners("SIGTERM")) { if (!beforeSigterm.has(listener)) process.removeListener("SIGTERM", listener); @@ -304,7 +307,7 @@ describe("gateway-cli coverage", () => { registerGatewayCli(program); await expect( - program.parseAsync(["gateway", "--allow-unconfigured"], { + program.parseAsync(["gateway", "--token", "test-token", "--allow-unconfigured"], { from: "user", }), ).rejects.toThrow("__exit__:1"); @@ -327,7 +330,7 @@ describe("gateway-cli coverage", () => { startGatewayServer.mockRejectedValueOnce(new Error("nope")); await expect( - program.parseAsync(["gateway", "--allow-unconfigured"], { + program.parseAsync(["gateway", "--token", "test-token", "--allow-unconfigured"], { from: "user", }), ).rejects.toThrow("__exit__:1"); diff --git a/src/cli/gateway-cli/run.ts b/src/cli/gateway-cli/run.ts index 1c2e8273c07..0de667c3cfa 100644 --- a/src/cli/gateway-cli/run.ts +++ b/src/cli/gateway-cli/run.ts @@ -203,6 +203,10 @@ async function runGatewayCommand(opts: GatewayRunOpts) { const resolvedAuthMode = resolvedAuth.mode; const tokenValue = resolvedAuth.token; const passwordValue = resolvedAuth.password; + const hasToken = typeof tokenValue === "string" && tokenValue.trim().length > 0; + const hasPassword = typeof passwordValue === "string" && passwordValue.trim().length > 0; + const hasSharedSecret = + (resolvedAuthMode === "token" && hasToken) || (resolvedAuthMode === "password" && hasPassword); const authHints: string[] = []; if (miskeys.hasGatewayToken) { authHints.push('Found "gateway.token" in config. Use "gateway.auth.token" instead.'); @@ -212,7 +216,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) { '"gateway.remote.token" is for remote CLI calls; it does not enable local gateway auth.', ); } - if (resolvedAuthMode === "token" && !tokenValue) { + if (resolvedAuthMode === "token" && !hasToken && !resolvedAuth.allowTailscale) { defaultRuntime.error( [ "Gateway auth is set to token, but no token is configured.", @@ -225,7 +229,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) { defaultRuntime.exit(1); return; } - if (resolvedAuthMode === "password" && !passwordValue) { + if (resolvedAuthMode === "password" && !hasPassword) { defaultRuntime.error( [ "Gateway auth is set to password, but no password is configured.", @@ -238,11 +242,11 @@ async function runGatewayCommand(opts: GatewayRunOpts) { defaultRuntime.exit(1); return; } - if (bind !== "loopback" && resolvedAuthMode === "none") { + if (bind !== "loopback" && !hasSharedSecret) { defaultRuntime.error( [ `Refusing to bind gateway to ${bind} without auth.`, - "Set gateway.auth.token (or CLAWDBOT_GATEWAY_TOKEN) or pass --token.", + "Set gateway.auth.token/password (or CLAWDBOT_GATEWAY_TOKEN/CLAWDBOT_GATEWAY_PASSWORD) or pass --token/--password.", ...authHints, ] .filter(Boolean) diff --git a/src/cli/program/register.onboard.ts b/src/cli/program/register.onboard.ts index 281464b6f75..ee9d5ccd26f 100644 --- a/src/cli/program/register.onboard.ts +++ b/src/cli/program/register.onboard.ts @@ -52,7 +52,7 @@ export function registerOnboardCommand(program: Command) { .option("--mode ", "Wizard mode: local|remote") .option( "--auth-choice ", - "Auth: setup-token|claude-cli|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|codex-cli|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip", + "Auth: setup-token|claude-cli|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|codex-cli|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip", ) .option( "--token-provider ", @@ -74,6 +74,7 @@ export function registerOnboardCommand(program: Command) { .option("--zai-api-key ", "Z.AI API key") .option("--minimax-api-key ", "MiniMax API key") .option("--synthetic-api-key ", "Synthetic API key") + .option("--venice-api-key ", "Venice API key") .option("--opencode-zen-api-key ", "OpenCode Zen API key") .option("--gateway-port ", "Gateway port") .option("--gateway-bind ", "Gateway bind: loopback|tailnet|lan|auto|custom") @@ -123,6 +124,7 @@ export function registerOnboardCommand(program: Command) { zaiApiKey: opts.zaiApiKey as string | undefined, minimaxApiKey: opts.minimaxApiKey as string | undefined, syntheticApiKey: opts.syntheticApiKey as string | undefined, + veniceApiKey: opts.veniceApiKey as string | undefined, opencodeZenApiKey: opts.opencodeZenApiKey as string | undefined, gatewayPort: typeof gatewayPort === "number" && Number.isFinite(gatewayPort) diff --git a/src/commands/configure.daemon.ts b/src/commands/configure.daemon.ts index 7115d49a4f5..38d8365c095 100644 --- a/src/commands/configure.daemon.ts +++ b/src/commands/configure.daemon.ts @@ -11,6 +11,7 @@ import { } from "./daemon-runtime.js"; import { guardCancel } from "./onboard-helpers.js"; import { ensureSystemdUserLingerInteractive } from "./systemd-linger.js"; +import { loadConfig } from "../config/config.js"; export async function maybeInstallDaemon(params: { runtime: RuntimeEnv; @@ -81,12 +82,14 @@ export async function maybeInstallDaemon(params: { progress.setLabel("Preparing Gateway service…"); + const cfg = loadConfig(); const { programArguments, workingDirectory, environment } = await buildGatewayInstallPlan({ env: process.env, port: params.port, token: params.gatewayToken, runtime: daemonRuntime, warn: (message, title) => note(message, title), + config: cfg, }); progress.setLabel("Installing Gateway service…"); diff --git a/src/commands/daemon-install-helpers.test.ts b/src/commands/daemon-install-helpers.test.ts index 22ae7f24dfa..8cd819185ac 100644 --- a/src/commands/daemon-install-helpers.test.ts +++ b/src/commands/daemon-install-helpers.test.ts @@ -95,6 +95,140 @@ describe("buildGatewayInstallPlan", () => { expect(warn).toHaveBeenCalledWith("Node too old", "Gateway runtime"); expect(mocks.resolvePreferredNodePath).toHaveBeenCalled(); }); + + it("merges config env vars into the environment", async () => { + mocks.resolvePreferredNodePath.mockResolvedValue("/opt/node"); + mocks.resolveGatewayProgramArguments.mockResolvedValue({ + programArguments: ["node", "gateway"], + workingDirectory: "/Users/me", + }); + mocks.resolveSystemNodeInfo.mockResolvedValue({ + path: "/opt/node", + version: "22.0.0", + supported: true, + }); + mocks.buildServiceEnvironment.mockReturnValue({ + CLAWDBOT_PORT: "3000", + HOME: "/Users/me", + }); + + const plan = await buildGatewayInstallPlan({ + env: {}, + port: 3000, + runtime: "node", + config: { + env: { + vars: { + GOOGLE_API_KEY: "test-key", + }, + CUSTOM_VAR: "custom-value", + }, + }, + }); + + // Config env vars should be present + expect(plan.environment.GOOGLE_API_KEY).toBe("test-key"); + expect(plan.environment.CUSTOM_VAR).toBe("custom-value"); + // Service environment vars should take precedence + expect(plan.environment.CLAWDBOT_PORT).toBe("3000"); + expect(plan.environment.HOME).toBe("/Users/me"); + }); + + it("does not include empty config env values", async () => { + mocks.resolvePreferredNodePath.mockResolvedValue("/opt/node"); + mocks.resolveGatewayProgramArguments.mockResolvedValue({ + programArguments: ["node", "gateway"], + workingDirectory: "/Users/me", + }); + mocks.resolveSystemNodeInfo.mockResolvedValue({ + path: "/opt/node", + version: "22.0.0", + supported: true, + }); + mocks.buildServiceEnvironment.mockReturnValue({ CLAWDBOT_PORT: "3000" }); + + const plan = await buildGatewayInstallPlan({ + env: {}, + port: 3000, + runtime: "node", + config: { + env: { + vars: { + VALID_KEY: "valid", + EMPTY_KEY: "", + }, + }, + }, + }); + + expect(plan.environment.VALID_KEY).toBe("valid"); + expect(plan.environment.EMPTY_KEY).toBeUndefined(); + }); + + it("drops whitespace-only config env values", async () => { + mocks.resolvePreferredNodePath.mockResolvedValue("/opt/node"); + mocks.resolveGatewayProgramArguments.mockResolvedValue({ + programArguments: ["node", "gateway"], + workingDirectory: "/Users/me", + }); + mocks.resolveSystemNodeInfo.mockResolvedValue({ + path: "/opt/node", + version: "22.0.0", + supported: true, + }); + mocks.buildServiceEnvironment.mockReturnValue({}); + + const plan = await buildGatewayInstallPlan({ + env: {}, + port: 3000, + runtime: "node", + config: { + env: { + vars: { + VALID_KEY: "valid", + }, + TRIMMED_KEY: " ", + }, + }, + }); + + expect(plan.environment.VALID_KEY).toBe("valid"); + expect(plan.environment.TRIMMED_KEY).toBeUndefined(); + }); + + it("keeps service env values over config env vars", async () => { + mocks.resolvePreferredNodePath.mockResolvedValue("/opt/node"); + mocks.resolveGatewayProgramArguments.mockResolvedValue({ + programArguments: ["node", "gateway"], + workingDirectory: "/Users/me", + }); + mocks.resolveSystemNodeInfo.mockResolvedValue({ + path: "/opt/node", + version: "22.0.0", + supported: true, + }); + mocks.buildServiceEnvironment.mockReturnValue({ + HOME: "/Users/service", + CLAWDBOT_PORT: "3000", + }); + + const plan = await buildGatewayInstallPlan({ + env: {}, + port: 3000, + runtime: "node", + config: { + env: { + HOME: "/Users/config", + vars: { + CLAWDBOT_PORT: "9999", + }, + }, + }, + }); + + expect(plan.environment.HOME).toBe("/Users/service"); + expect(plan.environment.CLAWDBOT_PORT).toBe("3000"); + }); }); describe("gatewayInstallErrorHint", () => { diff --git a/src/commands/daemon-install-helpers.ts b/src/commands/daemon-install-helpers.ts index 26fe7ada512..21317ea2fd3 100644 --- a/src/commands/daemon-install-helpers.ts +++ b/src/commands/daemon-install-helpers.ts @@ -7,6 +7,8 @@ import { } from "../daemon/runtime-paths.js"; import { buildServiceEnvironment } from "../daemon/service-env.js"; import { formatCliCommand } from "../cli/command-format.js"; +import { collectConfigEnvVars } from "../config/env-vars.js"; +import type { ClawdbotConfig } from "../config/types.js"; import type { GatewayDaemonRuntime } from "./daemon-runtime.js"; type WarnFn = (message: string, title?: string) => void; @@ -31,6 +33,8 @@ export async function buildGatewayInstallPlan(params: { devMode?: boolean; nodePath?: string; warn?: WarnFn; + /** Full config to extract env vars from (env vars + inline env keys). */ + config?: ClawdbotConfig; }): Promise { const devMode = params.devMode ?? resolveGatewayDevMode(); const nodePath = @@ -50,7 +54,7 @@ export async function buildGatewayInstallPlan(params: { const warning = renderSystemNodeWarning(systemNode, programArguments[0]); if (warning) params.warn?.(warning, "Gateway runtime"); } - const environment = buildServiceEnvironment({ + const serviceEnvironment = buildServiceEnvironment({ env: params.env, port: params.port, token: params.token, @@ -60,6 +64,13 @@ export async function buildGatewayInstallPlan(params: { : undefined, }); + // Merge config env vars into the service environment (vars + inline env keys). + // Config env vars are added first so service-specific vars take precedence. + const environment: Record = { + ...collectConfigEnvVars(params.config), + }; + Object.assign(environment, serviceEnvironment); + return { programArguments, workingDirectory, environment }; } diff --git a/src/commands/doctor-gateway-daemon-flow.ts b/src/commands/doctor-gateway-daemon-flow.ts index 83a4f515e3b..c209386f0c5 100644 --- a/src/commands/doctor-gateway-daemon-flow.ts +++ b/src/commands/doctor-gateway-daemon-flow.ts @@ -161,6 +161,7 @@ export async function maybeRepairGatewayDaemon(params: { token: params.cfg.gateway?.auth?.token ?? process.env.CLAWDBOT_GATEWAY_TOKEN, runtime: daemonRuntime, warn: (message, title) => note(message, title), + config: params.cfg, }); try { await service.install({ diff --git a/src/commands/doctor-gateway-services.ts b/src/commands/doctor-gateway-services.ts index e3005428de8..7da0c5c0c9f 100644 --- a/src/commands/doctor-gateway-services.ts +++ b/src/commands/doctor-gateway-services.ts @@ -110,6 +110,7 @@ export async function maybeMigrateLegacyGatewayService( token: cfg.gateway?.auth?.token ?? process.env.CLAWDBOT_GATEWAY_TOKEN, runtime: daemonRuntime, warn: (message, title) => note(message, title), + config: cfg, }); try { await service.install({ @@ -177,6 +178,7 @@ export async function maybeRepairGatewayServiceConfig( runtime: needsNodeRuntime && systemNodePath ? "node" : runtimeChoice, nodePath: systemNodePath ?? undefined, warn: (message, title) => note(message, title), + config: cfg, }); const expectedEntrypoint = findGatewayEntrypoint(programArguments); const currentEntrypoint = findGatewayEntrypoint(command.programArguments); diff --git a/src/commands/doctor-security.ts b/src/commands/doctor-security.ts index b3d82247f09..483917faa0c 100644 --- a/src/commands/doctor-security.ts +++ b/src/commands/doctor-security.ts @@ -10,6 +10,61 @@ export async function noteSecurityWarnings(cfg: ClawdbotConfig) { const warnings: string[] = []; const auditHint = `- Run: ${formatCliCommand("clawdbot security audit --deep")}`; + // =========================================== + // GATEWAY NETWORK EXPOSURE CHECK + // =========================================== + // Check for dangerous gateway binding configurations + // that expose the gateway to network without proper auth + + const gatewayBind = cfg.gateway?.bind ?? "loopback"; + const customBindHost = cfg.gateway?.customBindHost?.trim(); + const authMode = cfg.gateway?.auth?.mode ?? "off"; + const authToken = cfg.gateway?.auth?.token; + const authPassword = cfg.gateway?.auth?.password; + + const isLoopbackBindHost = (host: string) => { + const normalized = host.trim().toLowerCase(); + return ( + normalized === "localhost" || + normalized === "::1" || + normalized === "[::1]" || + normalized.startsWith("127.") + ); + }; + + // Bindings that expose gateway beyond localhost + const exposedBindings = ["all", "lan", "0.0.0.0"]; + const isExposed = + exposedBindings.includes(gatewayBind) || + (gatewayBind === "custom" && (!customBindHost || !isLoopbackBindHost(customBindHost))); + + if (isExposed) { + if (authMode === "off") { + warnings.push( + `- CRITICAL: Gateway bound to "${gatewayBind}" with NO authentication.`, + ` Anyone on your network (or internet if port-forwarded) can fully control your agent.`, + ` Fix: ${formatCliCommand("clawdbot config set gateway.bind loopback")}`, + ` Or enable auth: ${formatCliCommand("clawdbot config set gateway.auth.mode token")}`, + ); + } else if (authMode === "token" && !authToken) { + warnings.push( + `- CRITICAL: Gateway bound to "${gatewayBind}" with empty auth token.`, + ` Fix: ${formatCliCommand("clawdbot doctor --fix")} to generate a token`, + ); + } else if (authMode === "password" && !authPassword) { + warnings.push( + `- CRITICAL: Gateway bound to "${gatewayBind}" with empty password.`, + ` Fix: ${formatCliCommand("clawdbot configure")} to set a password`, + ); + } else { + // Auth is configured, but still warn about network exposure + warnings.push( + `- WARNING: Gateway bound to "${gatewayBind}" (network-accessible).`, + ` Ensure your auth credentials are strong and not exposed.`, + ); + } + } + const warnDmPolicy = async (params: { label: string; provider: ChannelId; diff --git a/src/commands/onboard-non-interactive/local/auth-choice.ts b/src/commands/onboard-non-interactive/local/auth-choice.ts index 6762fb7d216..02e0a75b9f7 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.ts @@ -20,6 +20,7 @@ import { applyOpencodeZenConfig, applyOpenrouterConfig, applySyntheticConfig, + applyVeniceConfig, applyVercelAiGatewayConfig, applyZaiConfig, setAnthropicApiKey, @@ -30,6 +31,7 @@ import { setOpencodeZenApiKey, setOpenrouterApiKey, setSyntheticApiKey, + setVeniceApiKey, setVercelAiGatewayApiKey, setZaiApiKey, } from "../../onboard-auth.js"; @@ -272,6 +274,25 @@ export async function applyNonInteractiveAuthChoice(params: { return applySyntheticConfig(nextConfig); } + if (authChoice === "venice-api-key") { + const resolved = await resolveNonInteractiveApiKey({ + provider: "venice", + cfg: baseConfig, + flagValue: opts.veniceApiKey, + flagName: "--venice-api-key", + envVar: "VENICE_API_KEY", + runtime, + }); + if (!resolved) return null; + if (resolved.source !== "profile") await setVeniceApiKey(resolved.key); + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: "venice:default", + provider: "venice", + mode: "api_key", + }); + return applyVeniceConfig(nextConfig); + } + if ( authChoice === "minimax-cloud" || authChoice === "minimax-api" || diff --git a/src/commands/onboard-non-interactive/local/daemon-install.ts b/src/commands/onboard-non-interactive/local/daemon-install.ts index 5b2e77b63be..68017b9e6fe 100644 --- a/src/commands/onboard-non-interactive/local/daemon-install.ts +++ b/src/commands/onboard-non-interactive/local/daemon-install.ts @@ -38,6 +38,7 @@ export async function installGatewayDaemonNonInteractive(params: { token: gatewayToken, runtime: daemonRuntimeRaw, warn: (message) => runtime.log(message), + config: params.nextConfig, }); try { await service.install({ diff --git a/src/config/channel-capabilities.test.ts b/src/config/channel-capabilities.test.ts index 1fbe3c2e673..bef3ba183f2 100644 --- a/src/config/channel-capabilities.test.ts +++ b/src/config/channel-capabilities.test.ts @@ -134,6 +134,7 @@ const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], diff --git a/src/config/env-vars.ts b/src/config/env-vars.ts new file mode 100644 index 00000000000..7e9c5b158f2 --- /dev/null +++ b/src/config/env-vars.ts @@ -0,0 +1,23 @@ +import type { ClawdbotConfig } from "./types.js"; + +export function collectConfigEnvVars(cfg?: ClawdbotConfig): Record { + const envConfig = cfg?.env; + if (!envConfig) return {}; + + const entries: Record = {}; + + if (envConfig.vars) { + for (const [key, value] of Object.entries(envConfig.vars)) { + if (!value) continue; + entries[key] = value; + } + } + + for (const [key, value] of Object.entries(envConfig)) { + if (key === "shellEnv" || key === "vars") continue; + if (typeof value !== "string" || !value.trim()) continue; + entries[key] = value; + } + + return entries; +} diff --git a/src/config/io.ts b/src/config/io.ts index 6994e448526..9078ef2a209 100644 --- a/src/config/io.ts +++ b/src/config/io.ts @@ -24,6 +24,7 @@ import { } from "./defaults.js"; import { VERSION } from "../version.js"; import { MissingEnvVarError, resolveConfigEnvVars } from "./env-substitution.js"; +import { collectConfigEnvVars } from "./env-vars.js"; import { ConfigIncludeError, resolveConfigIncludes } from "./includes.js"; import { findLegacyConfigIssues } from "./legacy.js"; import { normalizeConfigPaths } from "./normalize-paths.js"; @@ -149,24 +150,7 @@ function warnIfConfigFromFuture(cfg: ClawdbotConfig, logger: Pick = {}; - - if (envConfig.vars) { - for (const [key, value] of Object.entries(envConfig.vars)) { - if (!value) continue; - entries[key] = value; - } - } - - for (const [key, value] of Object.entries(envConfig)) { - if (key === "shellEnv" || key === "vars") continue; - if (typeof value !== "string" || !value.trim()) continue; - entries[key] = value; - } - + const entries = collectConfigEnvVars(cfg); for (const [key, value] of Object.entries(entries)) { if (env[key]?.trim()) continue; env[key] = value; @@ -227,6 +211,11 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) { parseJson: (raw) => deps.json5.parse(raw), }); + // Apply config.env to process.env BEFORE substitution so ${VAR} can reference config-defined vars + if (resolved && typeof resolved === "object" && "env" in resolved) { + applyConfigEnv(resolved as ClawdbotConfig, deps.env); + } + // Substitute ${VAR} env var references const substituted = resolveConfigEnvVars(resolved, deps.env); @@ -381,6 +370,11 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) { }; } + // Apply config.env to process.env BEFORE substitution so ${VAR} can reference config-defined vars + if (resolved && typeof resolved === "object" && "env" in resolved) { + applyConfigEnv(resolved as ClawdbotConfig, deps.env); + } + // Substitute ${VAR} env var references let substituted: unknown; try { diff --git a/src/config/paths.ts b/src/config/paths.ts index 6b1e4dcf7e5..c3151c1bec6 100644 --- a/src/config/paths.ts +++ b/src/config/paths.ts @@ -59,6 +59,17 @@ export const CONFIG_PATH_CLAWDBOT = resolveConfigPath(); export const DEFAULT_GATEWAY_PORT = 18789; +/** + * Gateway lock directory (ephemeral). + * Default: os.tmpdir()/clawdbot- (uid suffix when available). + */ +export function resolveGatewayLockDir(tmpdir: () => string = os.tmpdir): string { + const base = tmpdir(); + const uid = typeof process.getuid === "function" ? process.getuid() : undefined; + const suffix = uid != null ? `clawdbot-${uid}` : "clawdbot"; + return path.join(base, suffix); +} + const OAUTH_FILENAME = "oauth.json"; /** diff --git a/src/config/schema.ts b/src/config/schema.ts index d61b5964eff..6cd6381ae3b 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -107,6 +107,7 @@ const FIELD_LABELS: Record = { "update.channel": "Update Channel", "update.checkOnStart": "Update Check on Start", "diagnostics.enabled": "Diagnostics Enabled", + "diagnostics.flags": "Diagnostics Flags", "diagnostics.otel.enabled": "OpenTelemetry Enabled", "diagnostics.otel.endpoint": "OpenTelemetry Endpoint", "diagnostics.otel.protocol": "OpenTelemetry Protocol", @@ -368,7 +369,8 @@ const FIELD_HELP: Record = { "gateway.remote.sshIdentity": "Optional SSH identity file path (passed to ssh -i).", "agents.list[].identity.avatar": "Avatar image path (relative to the agent workspace only) or a remote URL/data URL.", - "gateway.auth.token": "Recommended for all gateways; required for non-loopback binds.", + "gateway.auth.token": + "Required by default for gateway access (unless using Tailscale Serve identity); required for non-loopback binds.", "gateway.auth.password": "Required for Tailscale funnel.", "gateway.controlUi.basePath": "Optional URL prefix where the Control UI is served (e.g. /clawdbot).", @@ -388,6 +390,8 @@ const FIELD_HELP: Record = { "nodeHost.browserProxy.enabled": "Expose the local browser control server via node proxy.", "nodeHost.browserProxy.allowProfiles": "Optional allowlist of browser profile names exposed via the node proxy.", + "diagnostics.flags": + 'Enable targeted diagnostics logs by flag (e.g. ["telegram.http"]). Supports wildcards like "telegram.*" or "*".', "diagnostics.cacheTrace.enabled": "Log cache trace snapshots for embedded agent runs (default: false).", "diagnostics.cacheTrace.filePath": diff --git a/src/config/types.base.ts b/src/config/types.base.ts index a84736571f9..cc805e8ecfe 100644 --- a/src/config/types.base.ts +++ b/src/config/types.base.ts @@ -135,6 +135,8 @@ export type DiagnosticsCacheTraceConfig = { export type DiagnosticsConfig = { enabled?: boolean; + /** Optional ad-hoc diagnostics flags (e.g. "telegram.http"). */ + flags?: string[]; otel?: DiagnosticsOtelConfig; cacheTrace?: DiagnosticsCacheTraceConfig; }; diff --git a/src/config/zod-schema.ts b/src/config/zod-schema.ts index a8ea7e70f56..b3d157355b7 100644 --- a/src/config/zod-schema.ts +++ b/src/config/zod-schema.ts @@ -62,6 +62,7 @@ export const ClawdbotSchema = z diagnostics: z .object({ enabled: z.boolean().optional(), + flags: z.array(z.string()).optional(), otel: z .object({ enabled: z.boolean().optional(), diff --git a/src/discord/chunk.test.ts b/src/discord/chunk.test.ts index f8e18e2b42f..13ec1b8e753 100644 --- a/src/discord/chunk.test.ts +++ b/src/discord/chunk.test.ts @@ -58,7 +58,7 @@ describe("chunkDiscordText", () => { maxLines: 50, chunkMode: "newline", }); - expect(chunks).toEqual(["```js\nconst a = 1;\nconst b = 2;\n```", "After"]); + expect(chunks).toEqual([text]); }); it("reserves space for closing fences when chunking", () => { diff --git a/src/discord/monitor.slash.test.ts b/src/discord/monitor.slash.test.ts index af098eb96b4..a6c43087d4a 100644 --- a/src/discord/monitor.slash.test.ts +++ b/src/discord/monitor.slash.test.ts @@ -34,15 +34,13 @@ vi.mock("../auto-reply/dispatch.js", async (importOriginal) => { beforeEach(() => { dispatchMock.mockReset().mockImplementation(async (params) => { if ("dispatcher" in params && params.dispatcher) { - params.dispatcher.sendToolResult({ text: "tool update" }); params.dispatcher.sendFinalReply({ text: "final reply" }); - return { queuedFinal: true, counts: { tool: 1, block: 0, final: 1 } }; + return { queuedFinal: true, counts: { tool: 0, block: 0, final: 1 } }; } if ("dispatcherOptions" in params && params.dispatcherOptions) { const { dispatcher, markDispatchIdle } = createReplyDispatcherWithTyping( params.dispatcherOptions, ); - dispatcher.sendToolResult({ text: "tool update" }); dispatcher.sendFinalReply({ text: "final reply" }); await dispatcher.waitForIdle(); markDispatchIdle(); @@ -53,7 +51,7 @@ beforeEach(() => { }); describe("discord native commands", () => { - it("streams tool results for native slash commands", { timeout: 60_000 }, async () => { + it("skips tool results for native slash commands", { timeout: 60_000 }, async () => { const { ChannelType } = await import("@buape/carbon"); const { createDiscordNativeCommand } = await import("./monitor.js"); @@ -97,8 +95,7 @@ describe("discord native commands", () => { expect(dispatchMock).toHaveBeenCalledTimes(1); expect(reply).toHaveBeenCalledTimes(1); - expect(followUp).toHaveBeenCalledTimes(1); - expect(reply.mock.calls[0]?.[0]?.content).toContain("tool"); - expect(followUp.mock.calls[0]?.[0]?.content).toContain("final"); + expect(followUp).toHaveBeenCalledTimes(0); + expect(reply.mock.calls[0]?.[0]?.content).toContain("final"); }); }); diff --git a/src/gateway/auth.test.ts b/src/gateway/auth.test.ts index aa4d5e27093..90bd5c41e7d 100644 --- a/src/gateway/auth.test.ts +++ b/src/gateway/auth.test.ts @@ -125,6 +125,7 @@ describe("gateway auth", () => { const res = await authorizeGatewayConnect({ auth: { mode: "token", token: "secret", allowTailscale: true }, connectAuth: null, + tailscaleWhois: async () => ({ login: "peter", name: "Peter" }), req: { socket: { remoteAddress: "127.0.0.1" }, headers: { @@ -143,6 +144,28 @@ describe("gateway auth", () => { expect(res.user).toBe("peter"); }); + it("rejects mismatched tailscale identity when required", async () => { + const res = await authorizeGatewayConnect({ + auth: { mode: "none", allowTailscale: true }, + connectAuth: null, + tailscaleWhois: async () => ({ login: "alice@example.com", name: "Alice" }), + req: { + socket: { remoteAddress: "127.0.0.1" }, + headers: { + host: "gateway.local", + "x-forwarded-for": "100.64.0.1", + "x-forwarded-proto": "https", + "x-forwarded-host": "ai-hub.bone-egret.ts.net", + "tailscale-user-login": "peter@example.com", + "tailscale-user-name": "Peter", + }, + } as never, + }); + + expect(res.ok).toBe(false); + expect(res.reason).toBe("tailscale_user_mismatch"); + }); + it("treats trusted proxy loopback clients as direct", async () => { const res = await authorizeGatewayConnect({ auth: { mode: "none", allowTailscale: true }, diff --git a/src/gateway/auth.ts b/src/gateway/auth.ts index cb4e868a230..f716be5ddba 100644 --- a/src/gateway/auth.ts +++ b/src/gateway/auth.ts @@ -1,7 +1,8 @@ import { timingSafeEqual } from "node:crypto"; import type { IncomingMessage } from "node:http"; import type { GatewayAuthConfig, GatewayTailscaleMode } from "../config/config.js"; -import { isTrustedProxyAddress, resolveGatewayClientIp } from "./net.js"; +import { readTailscaleWhoisIdentity, type TailscaleWhoisIdentity } from "../infra/tailscale.js"; +import { isTrustedProxyAddress, parseForwardedForClientIp, resolveGatewayClientIp } from "./net.js"; export type ResolvedGatewayAuthMode = "none" | "token" | "password"; export type ResolvedGatewayAuth = { @@ -29,11 +30,17 @@ type TailscaleUser = { profilePic?: string; }; +type TailscaleWhoisLookup = (ip: string) => Promise; + function safeEqual(a: string, b: string): boolean { if (a.length !== b.length) return false; return timingSafeEqual(Buffer.from(a), Buffer.from(b)); } +function normalizeLogin(login: string): string { + return login.trim().toLowerCase(); +} + function isLoopbackAddress(ip: string | undefined): boolean { if (!ip) return false; if (ip === "127.0.0.1") return true; @@ -58,6 +65,12 @@ function headerValue(value: string | string[] | undefined): string | undefined { return Array.isArray(value) ? value[0] : value; } +function resolveTailscaleClientIp(req?: IncomingMessage): string | undefined { + if (!req) return undefined; + const forwardedFor = headerValue(req.headers?.["x-forwarded-for"]); + return forwardedFor ? parseForwardedForClientIp(forwardedFor) : undefined; +} + function resolveRequestClientIp( req?: IncomingMessage, trustedProxies?: string[], @@ -118,6 +131,39 @@ function isTailscaleProxyRequest(req?: IncomingMessage): boolean { return isLoopbackAddress(req.socket?.remoteAddress) && hasTailscaleProxyHeaders(req); } +async function resolveVerifiedTailscaleUser(params: { + req?: IncomingMessage; + tailscaleWhois: TailscaleWhoisLookup; +}): Promise<{ ok: true; user: TailscaleUser } | { ok: false; reason: string }> { + const { req, tailscaleWhois } = params; + const tailscaleUser = getTailscaleUser(req); + if (!tailscaleUser) { + return { ok: false, reason: "tailscale_user_missing" }; + } + if (!isTailscaleProxyRequest(req)) { + return { ok: false, reason: "tailscale_proxy_missing" }; + } + const clientIp = resolveTailscaleClientIp(req); + if (!clientIp) { + return { ok: false, reason: "tailscale_whois_failed" }; + } + const whois = await tailscaleWhois(clientIp); + if (!whois?.login) { + return { ok: false, reason: "tailscale_whois_failed" }; + } + if (normalizeLogin(whois.login) !== normalizeLogin(tailscaleUser.login)) { + return { ok: false, reason: "tailscale_user_mismatch" }; + } + return { + ok: true, + user: { + login: whois.login, + name: whois.name ?? tailscaleUser.name, + profilePic: tailscaleUser.profilePic, + }, + }; +} + export function resolveGatewayAuth(params: { authConfig?: GatewayAuthConfig | null; env?: NodeJS.ProcessEnv; @@ -127,8 +173,7 @@ export function resolveGatewayAuth(params: { const env = params.env ?? process.env; const token = authConfig.token ?? env.CLAWDBOT_GATEWAY_TOKEN ?? undefined; const password = authConfig.password ?? env.CLAWDBOT_GATEWAY_PASSWORD ?? undefined; - const mode: ResolvedGatewayAuth["mode"] = - authConfig.mode ?? (password ? "password" : token ? "token" : "none"); + const mode: ResolvedGatewayAuth["mode"] = authConfig.mode ?? (password ? "password" : "token"); const allowTailscale = authConfig.allowTailscale ?? (params.tailscaleMode === "serve" && mode !== "password"); return { @@ -141,6 +186,7 @@ export function resolveGatewayAuth(params: { export function assertGatewayAuthConfigured(auth: ResolvedGatewayAuth): void { if (auth.mode === "token" && !auth.token) { + if (auth.allowTailscale) return; throw new Error( "gateway auth mode is token, but no token was configured (set gateway.auth.token or CLAWDBOT_GATEWAY_TOKEN)", ); @@ -155,29 +201,26 @@ export async function authorizeGatewayConnect(params: { connectAuth?: ConnectAuth | null; req?: IncomingMessage; trustedProxies?: string[]; + tailscaleWhois?: TailscaleWhoisLookup; }): Promise { const { auth, connectAuth, req, trustedProxies } = params; + const tailscaleWhois = params.tailscaleWhois ?? readTailscaleWhoisIdentity; const localDirect = isLocalDirectRequest(req, trustedProxies); if (auth.allowTailscale && !localDirect) { - const tailscaleUser = getTailscaleUser(req); - const tailscaleProxy = isTailscaleProxyRequest(req); - - if (tailscaleUser && tailscaleProxy) { + const tailscaleCheck = await resolveVerifiedTailscaleUser({ + req, + tailscaleWhois, + }); + if (tailscaleCheck.ok) { return { ok: true, method: "tailscale", - user: tailscaleUser.login, + user: tailscaleCheck.user.login, }; } - if (auth.mode === "none") { - if (!tailscaleUser) { - return { ok: false, reason: "tailscale_user_missing" }; - } - if (!tailscaleProxy) { - return { ok: false, reason: "tailscale_proxy_missing" }; - } + return { ok: false, reason: tailscaleCheck.reason }; } } @@ -192,7 +235,7 @@ export async function authorizeGatewayConnect(params: { if (!connectAuth?.token) { return { ok: false, reason: "token_missing" }; } - if (connectAuth.token !== auth.token) { + if (!safeEqual(connectAuth.token, auth.token)) { return { ok: false, reason: "token_mismatch" }; } return { ok: true, method: "token" }; diff --git a/src/gateway/net.ts b/src/gateway/net.ts index 608ec872f6c..6702e0e8b8f 100644 --- a/src/gateway/net.ts +++ b/src/gateway/net.ts @@ -36,7 +36,7 @@ function stripOptionalPort(ip: string): string { return ip; } -function parseForwardedForClientIp(forwardedFor?: string): string | undefined { +export function parseForwardedForClientIp(forwardedFor?: string): string | undefined { const raw = forwardedFor?.split(",")[0]?.trim(); if (!raw) return undefined; return normalizeIp(stripOptionalPort(raw)); diff --git a/src/gateway/protocol/schema/logs-chat.ts b/src/gateway/protocol/schema/logs-chat.ts index 7b684771afe..dc04a29d526 100644 --- a/src/gateway/protocol/schema/logs-chat.ts +++ b/src/gateway/protocol/schema/logs-chat.ts @@ -35,7 +35,7 @@ export const ChatHistoryParamsSchema = Type.Object( export const ChatSendParamsSchema = Type.Object( { sessionKey: NonEmptyString, - message: NonEmptyString, + message: Type.String(), thinking: Type.Optional(Type.String()), deliver: Type.Optional(Type.Boolean()), attachments: Type.Optional(Type.Array(Type.Unknown())), diff --git a/src/gateway/server-chat.agent-events.test.ts b/src/gateway/server-chat.agent-events.test.ts new file mode 100644 index 00000000000..14657464a7e --- /dev/null +++ b/src/gateway/server-chat.agent-events.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, it, vi } from "vitest"; + +import { createAgentEventHandler, createChatRunState } from "./server-chat.js"; + +describe("agent event handler", () => { + it("emits chat delta for assistant text-only events", () => { + const nowSpy = vi.spyOn(Date, "now").mockReturnValue(1_000); + const broadcast = vi.fn(); + const nodeSendToSession = vi.fn(); + const agentRunSeq = new Map(); + const chatRunState = createChatRunState(); + chatRunState.registry.add("run-1", { sessionKey: "session-1", clientRunId: "client-1" }); + + const handler = createAgentEventHandler({ + broadcast, + nodeSendToSession, + agentRunSeq, + chatRunState, + resolveSessionKeyForRun: () => undefined, + clearAgentRunContext: vi.fn(), + }); + + handler({ + runId: "run-1", + seq: 1, + stream: "assistant", + ts: Date.now(), + data: { text: "Hello world" }, + }); + + const chatCalls = broadcast.mock.calls.filter(([event]) => event === "chat"); + expect(chatCalls).toHaveLength(1); + const payload = chatCalls[0]?.[1] as { + state?: string; + message?: { content?: Array<{ text?: string }> }; + }; + expect(payload.state).toBe("delta"); + expect(payload.message?.content?.[0]?.text).toBe("Hello world"); + const sessionChatCalls = nodeSendToSession.mock.calls.filter(([, event]) => event === "chat"); + expect(sessionChatCalls).toHaveLength(1); + nowSpy.mockRestore(); + }); +}); diff --git a/src/gateway/server-http.ts b/src/gateway/server-http.ts index f8a2dbb1567..3a122ebc190 100644 --- a/src/gateway/server-http.ts +++ b/src/gateway/server-http.ts @@ -230,8 +230,6 @@ export function createGatewayHttpServer(opts: { const configSnapshot = loadConfig(); const trustedProxies = configSnapshot.gateway?.trustedProxies ?? []; if (await handleHooksRequest(req, res)) return; - if (await handleSlackHttpRequest(req, res)) return; - if (handlePluginRequest && (await handlePluginRequest(req, res))) return; if ( await handleToolsInvokeHttpRequest(req, res, { auth: resolvedAuth, @@ -239,6 +237,8 @@ export function createGatewayHttpServer(opts: { }) ) return; + if (await handleSlackHttpRequest(req, res)) return; + if (handlePluginRequest && (await handlePluginRequest(req, res))) return; if (openResponsesEnabled) { if ( await handleOpenResponsesHttpRequest(req, res, { diff --git a/src/gateway/server-methods/agent.test.ts b/src/gateway/server-methods/agent.test.ts new file mode 100644 index 00000000000..149ab4a671a --- /dev/null +++ b/src/gateway/server-methods/agent.test.ts @@ -0,0 +1,163 @@ +import { describe, expect, it, vi } from "vitest"; + +import type { GatewayRequestContext } from "./types.js"; +import { agentHandlers } from "./agent.js"; + +const mocks = vi.hoisted(() => ({ + loadSessionEntry: vi.fn(), + updateSessionStore: vi.fn(), + agentCommand: vi.fn(), + registerAgentRunContext: vi.fn(), +})); + +vi.mock("../session-utils.js", () => ({ + loadSessionEntry: mocks.loadSessionEntry, +})); + +vi.mock("../../config/sessions.js", async () => { + const actual = await vi.importActual( + "../../config/sessions.js", + ); + return { + ...actual, + updateSessionStore: mocks.updateSessionStore, + resolveAgentIdFromSessionKey: () => "main", + resolveExplicitAgentSessionKey: () => undefined, + resolveAgentMainSessionKey: () => "agent:main:main", + }; +}); + +vi.mock("../../commands/agent.js", () => ({ + agentCommand: mocks.agentCommand, +})); + +vi.mock("../../config/config.js", () => ({ + loadConfig: () => ({}), +})); + +vi.mock("../../agents/agent-scope.js", () => ({ + listAgentIds: () => ["main"], +})); + +vi.mock("../../infra/agent-events.js", () => ({ + registerAgentRunContext: mocks.registerAgentRunContext, + onAgentEvent: vi.fn(), +})); + +vi.mock("../../sessions/send-policy.js", () => ({ + resolveSendPolicy: () => "allow", +})); + +vi.mock("../../utils/delivery-context.js", async () => { + const actual = await vi.importActual( + "../../utils/delivery-context.js", + ); + return { + ...actual, + normalizeSessionDeliveryFields: () => ({}), + }; +}); + +const makeContext = (): GatewayRequestContext => + ({ + dedupe: new Map(), + addChatRun: vi.fn(), + logGateway: { info: vi.fn(), error: vi.fn() }, + }) as unknown as GatewayRequestContext; + +describe("gateway agent handler", () => { + it("preserves cliSessionIds from existing session entry", async () => { + const existingCliSessionIds = { "claude-cli": "abc-123-def" }; + const existingClaudeCliSessionId = "abc-123-def"; + + mocks.loadSessionEntry.mockReturnValue({ + cfg: {}, + storePath: "/tmp/sessions.json", + entry: { + sessionId: "existing-session-id", + updatedAt: Date.now(), + cliSessionIds: existingCliSessionIds, + claudeCliSessionId: existingClaudeCliSessionId, + }, + canonicalKey: "agent:main:main", + }); + + let capturedEntry: Record | undefined; + mocks.updateSessionStore.mockImplementation(async (_path, updater) => { + const store: Record = {}; + await updater(store); + capturedEntry = store["agent:main:main"] as Record; + }); + + mocks.agentCommand.mockResolvedValue({ + payloads: [{ text: "ok" }], + meta: { durationMs: 100 }, + }); + + const respond = vi.fn(); + await agentHandlers.agent({ + params: { + message: "test", + agentId: "main", + sessionKey: "agent:main:main", + idempotencyKey: "test-idem", + }, + respond, + context: makeContext(), + req: { type: "req", id: "1", method: "agent" }, + client: null, + isWebchatConnect: () => false, + }); + + expect(mocks.updateSessionStore).toHaveBeenCalled(); + expect(capturedEntry).toBeDefined(); + expect(capturedEntry?.cliSessionIds).toEqual(existingCliSessionIds); + expect(capturedEntry?.claudeCliSessionId).toBe(existingClaudeCliSessionId); + }); + + it("handles missing cliSessionIds gracefully", async () => { + mocks.loadSessionEntry.mockReturnValue({ + cfg: {}, + storePath: "/tmp/sessions.json", + entry: { + sessionId: "existing-session-id", + updatedAt: Date.now(), + // No cliSessionIds or claudeCliSessionId + }, + canonicalKey: "agent:main:main", + }); + + let capturedEntry: Record | undefined; + mocks.updateSessionStore.mockImplementation(async (_path, updater) => { + const store: Record = {}; + await updater(store); + capturedEntry = store["agent:main:main"] as Record; + }); + + mocks.agentCommand.mockResolvedValue({ + payloads: [{ text: "ok" }], + meta: { durationMs: 100 }, + }); + + const respond = vi.fn(); + await agentHandlers.agent({ + params: { + message: "test", + agentId: "main", + sessionKey: "agent:main:main", + idempotencyKey: "test-idem-2", + }, + respond, + context: makeContext(), + req: { type: "req", id: "2", method: "agent" }, + client: null, + isWebchatConnect: () => false, + }); + + expect(mocks.updateSessionStore).toHaveBeenCalled(); + expect(capturedEntry).toBeDefined(); + // Should be undefined, not cause an error + expect(capturedEntry?.cliSessionIds).toBeUndefined(); + expect(capturedEntry?.claudeCliSessionId).toBeUndefined(); + }); +}); diff --git a/src/gateway/server-methods/agent.ts b/src/gateway/server-methods/agent.ts index 8c5782e008e..d159d1f78e6 100644 --- a/src/gateway/server-methods/agent.ts +++ b/src/gateway/server-methods/agent.ts @@ -251,6 +251,8 @@ export const agentHandlers: GatewayRequestHandlers = { groupId: resolvedGroupId ?? entry?.groupId, groupChannel: resolvedGroupChannel ?? entry?.groupChannel, space: resolvedGroupSpace ?? entry?.space, + cliSessionIds: entry?.cliSessionIds, + claudeCliSessionId: entry?.claudeCliSessionId, }; sessionEntry = nextEntry; const sendPolicy = resolveSendPolicy({ diff --git a/src/gateway/server-methods/chat.ts b/src/gateway/server-methods/chat.ts index 50f44177913..9010a6f2100 100644 --- a/src/gateway/server-methods/chat.ts +++ b/src/gateway/server-methods/chat.ts @@ -338,6 +338,15 @@ export const chatHandlers: GatewayRequestHandlers = { : undefined, })) .filter((a) => a.content) ?? []; + const rawMessage = p.message.trim(); + if (!rawMessage && normalizedAttachments.length === 0) { + respond( + false, + undefined, + errorShape(ErrorCodes.INVALID_REQUEST, "message or attachment required"), + ); + return; + } let parsedMessage = p.message; let parsedImages: ChatImageContent[] = []; if (normalizedAttachments.length > 0) { diff --git a/src/gateway/server-plugins.test.ts b/src/gateway/server-plugins.test.ts index b4cf9503027..0097cd1b509 100644 --- a/src/gateway/server-plugins.test.ts +++ b/src/gateway/server-plugins.test.ts @@ -18,6 +18,7 @@ const createRegistry = (diagnostics: PluginDiagnostic[]): PluginRegistry => ({ providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics, diff --git a/src/gateway/server-restart-sentinel.ts b/src/gateway/server-restart-sentinel.ts index fa33b7c21a3..28719290ebd 100644 --- a/src/gateway/server-restart-sentinel.ts +++ b/src/gateway/server-restart-sentinel.ts @@ -28,11 +28,16 @@ export async function scheduleRestartSentinelWake(params: { deps: CliDeps }) { return; } - const threadMarker = ":thread:"; - const threadIndex = sessionKey.lastIndexOf(threadMarker); - const baseSessionKey = threadIndex === -1 ? sessionKey : sessionKey.slice(0, threadIndex); + // Extract topic/thread ID from sessionKey (supports both :topic: and :thread:) + // Telegram uses :topic:, other platforms use :thread: + const topicIndex = sessionKey.lastIndexOf(":topic:"); + const threadIndex = sessionKey.lastIndexOf(":thread:"); + const markerIndex = Math.max(topicIndex, threadIndex); + const marker = topicIndex > threadIndex ? ":topic:" : ":thread:"; + + const baseSessionKey = markerIndex === -1 ? sessionKey : sessionKey.slice(0, markerIndex); const threadIdRaw = - threadIndex === -1 ? undefined : sessionKey.slice(threadIndex + threadMarker.length); + markerIndex === -1 ? undefined : sessionKey.slice(markerIndex + marker.length); const sessionThreadId = threadIdRaw?.trim() || undefined; const { cfg, entry } = loadSessionEntry(sessionKey); @@ -42,7 +47,7 @@ export async function scheduleRestartSentinelWake(params: { deps: CliDeps }) { // Handles race condition where store wasn't flushed before restart const sentinelContext = payload.deliveryContext; let sessionDeliveryContext = deliveryContextFromSession(entry); - if (!sessionDeliveryContext && threadIndex !== -1 && baseSessionKey) { + if (!sessionDeliveryContext && markerIndex !== -1 && baseSessionKey) { const { entry: baseEntry } = loadSessionEntry(baseSessionKey); sessionDeliveryContext = deliveryContextFromSession(baseEntry); } @@ -74,6 +79,7 @@ export async function scheduleRestartSentinelWake(params: { deps: CliDeps }) { const threadId = payload.threadId ?? + parsedTarget?.threadId ?? // From resolveAnnounceTargetFromKey (extracts :topic:N) sessionThreadId ?? (origin?.threadId != null ? String(origin.threadId) : undefined); diff --git a/src/gateway/server-runtime-config.ts b/src/gateway/server-runtime-config.ts index a155c5d0a28..2d699988ab9 100644 --- a/src/gateway/server-runtime-config.ts +++ b/src/gateway/server-runtime-config.ts @@ -70,6 +70,11 @@ export async function resolveGatewayRuntimeConfig(params: { tailscaleMode, }); const authMode: ResolvedGatewayAuth["mode"] = resolvedAuth.mode; + const hasToken = typeof resolvedAuth.token === "string" && resolvedAuth.token.trim().length > 0; + const hasPassword = + typeof resolvedAuth.password === "string" && resolvedAuth.password.trim().length > 0; + const hasSharedSecret = + (authMode === "token" && hasToken) || (authMode === "password" && hasPassword); const hooksConfig = resolveHooksConfig(params.cfg); const canvasHostEnabled = process.env.CLAWDBOT_SKIP_CANVAS_HOST !== "1" && params.cfg.canvasHost?.enabled !== false; @@ -83,9 +88,9 @@ export async function resolveGatewayRuntimeConfig(params: { if (tailscaleMode !== "off" && !isLoopbackHost(bindHost)) { throw new Error("tailscale serve/funnel requires gateway bind=loopback (127.0.0.1)"); } - if (!isLoopbackHost(bindHost) && authMode === "none") { + if (!isLoopbackHost(bindHost) && !hasSharedSecret) { throw new Error( - `refusing to bind gateway to ${bindHost}:${params.port} without auth (set gateway.auth.token or CLAWDBOT_GATEWAY_TOKEN, or pass --token)`, + `refusing to bind gateway to ${bindHost}:${params.port} without auth (set gateway.auth.token/password, or set CLAWDBOT_GATEWAY_TOKEN/CLAWDBOT_GATEWAY_PASSWORD)`, ); } diff --git a/src/gateway/server.agent.gateway-server-agent-a.e2e.test.ts b/src/gateway/server.agent.gateway-server-agent-a.e2e.test.ts index 3a040b6901a..9be071e57f8 100644 --- a/src/gateway/server.agent.gateway-server-agent-a.e2e.test.ts +++ b/src/gateway/server.agent.gateway-server-agent-a.e2e.test.ts @@ -40,6 +40,7 @@ const registryState = vi.hoisted(() => ({ providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], @@ -81,6 +82,7 @@ const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], diff --git a/src/gateway/server.agent.gateway-server-agent-b.e2e.test.ts b/src/gateway/server.agent.gateway-server-agent-b.e2e.test.ts index 190caba6155..ca695c47267 100644 --- a/src/gateway/server.agent.gateway-server-agent-b.e2e.test.ts +++ b/src/gateway/server.agent.gateway-server-agent-b.e2e.test.ts @@ -49,6 +49,7 @@ const registryState = vi.hoisted(() => ({ providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], @@ -78,6 +79,7 @@ const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], diff --git a/src/gateway/server.auth.e2e.test.ts b/src/gateway/server.auth.e2e.test.ts index b97a2d32154..6474f285bfd 100644 --- a/src/gateway/server.auth.e2e.test.ts +++ b/src/gateway/server.auth.e2e.test.ts @@ -2,6 +2,7 @@ import { afterAll, beforeAll, describe, expect, test, vi } from "vitest"; import { WebSocket } from "ws"; import { PROTOCOL_VERSION } from "./protocol/index.js"; import { getHandshakeTimeoutMs } from "./server-constants.js"; +import { buildDeviceAuthPayload } from "./device-auth.js"; import { connectReq, getFreePort, @@ -33,7 +34,7 @@ const openWs = async (port: number) => { }; describe("gateway server auth/connect", () => { - describe("default auth", () => { + describe("default auth (token)", () => { let server: Awaited>; let port: number; @@ -233,6 +234,7 @@ describe("gateway server auth/connect", () => { test("returns control ui hint when token is missing", async () => { const ws = await openWs(port); const res = await connectReq(ws, { + skipDefaultAuth: true, client: { id: GATEWAY_CLIENT_NAMES.CONTROL_UI, version: "1.0.0", @@ -286,6 +288,92 @@ describe("gateway server auth/connect", () => { } }); + test("allows control ui with device identity when insecure auth is enabled", async () => { + testState.gatewayControlUi = { allowInsecureAuth: true }; + const { writeConfigFile } = await import("../config/config.js"); + await writeConfigFile({ + gateway: { + trustedProxies: ["127.0.0.1"], + }, + } as any); + const prevToken = process.env.CLAWDBOT_GATEWAY_TOKEN; + process.env.CLAWDBOT_GATEWAY_TOKEN = "secret"; + const port = await getFreePort(); + const server = await startGatewayServer(port); + const ws = new WebSocket(`ws://127.0.0.1:${port}`, { + headers: { "x-forwarded-for": "203.0.113.10" }, + }); + const challengePromise = onceMessage<{ payload?: unknown }>( + ws, + (o) => o.type === "event" && o.event === "connect.challenge", + ); + await new Promise((resolve) => ws.once("open", resolve)); + const challenge = await challengePromise; + const nonce = (challenge.payload as { nonce?: unknown } | undefined)?.nonce; + expect(typeof nonce).toBe("string"); + const { loadOrCreateDeviceIdentity, publicKeyRawBase64UrlFromPem, signDevicePayload } = + await import("../infra/device-identity.js"); + const identity = loadOrCreateDeviceIdentity(); + const signedAtMs = Date.now(); + const payload = buildDeviceAuthPayload({ + deviceId: identity.deviceId, + clientId: GATEWAY_CLIENT_NAMES.CONTROL_UI, + clientMode: GATEWAY_CLIENT_MODES.WEBCHAT, + role: "operator", + scopes: [], + signedAtMs, + token: "secret", + nonce: String(nonce), + }); + const device = { + id: identity.deviceId, + publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem), + signature: signDevicePayload(identity.privateKeyPem, payload), + signedAt: signedAtMs, + nonce: String(nonce), + }; + const res = await connectReq(ws, { + token: "secret", + device, + client: { + id: GATEWAY_CLIENT_NAMES.CONTROL_UI, + version: "1.0.0", + platform: "web", + mode: GATEWAY_CLIENT_MODES.WEBCHAT, + }, + }); + expect(res.ok).toBe(true); + ws.close(); + await server.close(); + if (prevToken === undefined) { + delete process.env.CLAWDBOT_GATEWAY_TOKEN; + } else { + process.env.CLAWDBOT_GATEWAY_TOKEN = prevToken; + } + }); + + test("rejects proxied connections without auth when proxy headers are untrusted", async () => { + testState.gatewayAuth = { mode: "none" }; + const prevToken = process.env.CLAWDBOT_GATEWAY_TOKEN; + delete process.env.CLAWDBOT_GATEWAY_TOKEN; + const port = await getFreePort(); + const server = await startGatewayServer(port); + const ws = new WebSocket(`ws://127.0.0.1:${port}`, { + headers: { "x-forwarded-for": "203.0.113.10" }, + }); + await new Promise((resolve) => ws.once("open", resolve)); + const res = await connectReq(ws, { skipDefaultAuth: true }); + expect(res.ok).toBe(false); + expect(res.error?.message ?? "").toContain("gateway auth required"); + ws.close(); + await server.close(); + if (prevToken === undefined) { + delete process.env.CLAWDBOT_GATEWAY_TOKEN; + } else { + process.env.CLAWDBOT_GATEWAY_TOKEN = prevToken; + } + }); + test("accepts device token auth for paired device", async () => { const { loadOrCreateDeviceIdentity } = await import("../infra/device-identity.js"); const { approveDevicePairing, getPairedDevice, listDevicePairing } = diff --git a/src/gateway/server.channels.e2e.test.ts b/src/gateway/server.channels.e2e.test.ts index 6a121c416c7..c65b87c103a 100644 --- a/src/gateway/server.channels.e2e.test.ts +++ b/src/gateway/server.channels.e2e.test.ts @@ -21,6 +21,7 @@ const registryState = vi.hoisted(() => ({ providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], @@ -47,6 +48,7 @@ const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], diff --git a/src/gateway/server.chat.gateway-server-chat.e2e.test.ts b/src/gateway/server.chat.gateway-server-chat.e2e.test.ts index 54f772580b7..6827b24c470 100644 --- a/src/gateway/server.chat.gateway-server-chat.e2e.test.ts +++ b/src/gateway/server.chat.gateway-server-chat.e2e.test.ts @@ -208,6 +208,39 @@ describe("gateway server chat", () => { | undefined; expect(imgOpts?.images).toEqual([{ type: "image", data: pngB64, mimeType: "image/png" }]); + const callsBeforeImageOnly = spy.mock.calls.length; + const reqIdOnly = "chat-img-only"; + ws.send( + JSON.stringify({ + type: "req", + id: reqIdOnly, + method: "chat.send", + params: { + sessionKey: "main", + message: "", + idempotencyKey: "idem-img-only", + attachments: [ + { + type: "image", + mimeType: "image/png", + fileName: "dot.png", + content: `data:image/png;base64,${pngB64}`, + }, + ], + }, + }), + ); + + const imgOnlyRes = await onceMessage(ws, (o) => o.type === "res" && o.id === reqIdOnly, 8000); + expect(imgOnlyRes.ok).toBe(true); + expect(imgOnlyRes.payload?.runId).toBeDefined(); + + await waitFor(() => spy.mock.calls.length > callsBeforeImageOnly, 8000); + const imgOnlyOpts = spy.mock.calls.at(-1)?.[1] as + | { images?: Array<{ type: string; data: string; mimeType: string }> } + | undefined; + expect(imgOnlyOpts?.images).toEqual([{ type: "image", data: pngB64, mimeType: "image/png" }]); + const historyDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); tempDirs.push(historyDir); testState.sessionStorePath = path.join(historyDir, "sessions.json"); diff --git a/src/gateway/server.impl.ts b/src/gateway/server.impl.ts index c89e0d69911..fdf40be6188 100644 --- a/src/gateway/server.impl.ts +++ b/src/gateway/server.impl.ts @@ -14,6 +14,7 @@ import { writeConfigFile, } from "../config/config.js"; import { isDiagnosticsEnabled } from "../infra/diagnostic-events.js"; +import { logAcceptedEnvOption } from "../infra/env.js"; import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js"; import { clearAgentRunContext, onAgentEvent } from "../infra/agent-events.js"; import { onHeartbeatEvent } from "../infra/heartbeat-events.js"; @@ -149,6 +150,14 @@ export async function startGatewayServer( ): Promise { // Ensure all default port derivations (browser/canvas) see the actual runtime port. process.env.CLAWDBOT_GATEWAY_PORT = String(port); + logAcceptedEnvOption({ + key: "CLAWDBOT_RAW_STREAM", + description: "raw stream logging enabled", + }); + logAcceptedEnvOption({ + key: "CLAWDBOT_RAW_STREAM_PATH", + description: "raw stream log path override", + }); let configSnapshot = await readConfigFileSnapshot(); if (configSnapshot.legacyIssues.length > 0) { diff --git a/src/gateway/server.models-voicewake-misc.e2e.test.ts b/src/gateway/server.models-voicewake-misc.e2e.test.ts index 05ce14123a0..aca220dc2c1 100644 --- a/src/gateway/server.models-voicewake-misc.e2e.test.ts +++ b/src/gateway/server.models-voicewake-misc.e2e.test.ts @@ -75,6 +75,7 @@ const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], diff --git a/src/gateway/server.nodes.late-invoke.test.ts b/src/gateway/server.nodes.late-invoke.test.ts index 50801583d88..52f73e898e0 100644 --- a/src/gateway/server.nodes.late-invoke.test.ts +++ b/src/gateway/server.nodes.late-invoke.test.ts @@ -28,11 +28,12 @@ let ws: WebSocket; let port: number; beforeAll(async () => { - const started = await startServerWithClient(); + const token = "test-gateway-token-1234567890"; + const started = await startServerWithClient(token); server = started.server; ws = started.ws; port = started.port; - await connectOk(ws); + await connectOk(ws, { token }); }); afterAll(async () => { @@ -60,6 +61,7 @@ describe("late-arriving invoke results", () => { mode: GATEWAY_CLIENT_MODES.NODE, }, commands: ["canvas.snapshot"], + token: "test-gateway-token-1234567890", }); // Send an invoke result with an unknown ID (simulating late arrival after timeout) diff --git a/src/gateway/server/__tests__/test-utils.ts b/src/gateway/server/__tests__/test-utils.ts index 697c9b73bc6..bfc6a687170 100644 --- a/src/gateway/server/__tests__/test-utils.ts +++ b/src/gateway/server/__tests__/test-utils.ts @@ -10,6 +10,7 @@ export const createTestRegistry = (overrides: Partial = {}): Plu providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], commands: [], @@ -20,5 +21,6 @@ export const createTestRegistry = (overrides: Partial = {}): Plu ...merged, gatewayHandlers: merged.gatewayHandlers ?? {}, httpHandlers: merged.httpHandlers ?? [], + httpRoutes: merged.httpRoutes ?? [], }; }; diff --git a/src/gateway/server/plugins-http.test.ts b/src/gateway/server/plugins-http.test.ts index e4d54a68b33..0308ebe316a 100644 --- a/src/gateway/server/plugins-http.test.ts +++ b/src/gateway/server/plugins-http.test.ts @@ -56,6 +56,35 @@ describe("createGatewayPluginRequestHandler", () => { expect(second).toHaveBeenCalledTimes(1); }); + it("handles registered http routes before generic handlers", async () => { + const routeHandler = vi.fn(async (_req, res: ServerResponse) => { + res.statusCode = 200; + }); + const fallback = vi.fn(async () => true); + const handler = createGatewayPluginRequestHandler({ + registry: createTestRegistry({ + httpRoutes: [ + { + pluginId: "route", + path: "/demo", + handler: routeHandler, + source: "route", + }, + ], + httpHandlers: [{ pluginId: "fallback", handler: fallback, source: "fallback" }], + }), + log: { warn: vi.fn() } as unknown as Parameters< + typeof createGatewayPluginRequestHandler + >[0]["log"], + }); + + const { res } = makeResponse(); + const handled = await handler({ url: "/demo" } as IncomingMessage, res); + expect(handled).toBe(true); + expect(routeHandler).toHaveBeenCalledTimes(1); + expect(fallback).not.toHaveBeenCalled(); + }); + it("logs and responds with 500 when a handler throws", async () => { const log = { warn: vi.fn() } as unknown as Parameters< typeof createGatewayPluginRequestHandler diff --git a/src/gateway/server/plugins-http.ts b/src/gateway/server/plugins-http.ts index 948a41a177b..f8a7f85fda1 100644 --- a/src/gateway/server/plugins-http.ts +++ b/src/gateway/server/plugins-http.ts @@ -16,8 +16,30 @@ export function createGatewayPluginRequestHandler(params: { }): PluginHttpRequestHandler { const { registry, log } = params; return async (req, res) => { - if (registry.httpHandlers.length === 0) return false; - for (const entry of registry.httpHandlers) { + const routes = registry.httpRoutes ?? []; + const handlers = registry.httpHandlers ?? []; + if (routes.length === 0 && handlers.length === 0) return false; + + if (routes.length > 0) { + const url = new URL(req.url ?? "/", "http://localhost"); + const route = routes.find((entry) => entry.path === url.pathname); + if (route) { + try { + await route.handler(req, res); + return true; + } catch (err) { + log.warn(`plugin http route failed (${route.pluginId ?? "unknown"}): ${String(err)}`); + if (!res.headersSent) { + res.statusCode = 500; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end("Internal Server Error"); + } + return true; + } + } + } + + for (const entry of handlers) { try { const handled = await entry.handler(req, res); if (handled) return true; diff --git a/src/gateway/server/ws-connection/message-handler.ts b/src/gateway/server/ws-connection/message-handler.ts index 5bca2e5ed72..7f8f9f2c684 100644 --- a/src/gateway/server/ws-connection/message-handler.ts +++ b/src/gateway/server/ws-connection/message-handler.ts @@ -26,7 +26,7 @@ import type { ResolvedGatewayAuth } from "../../auth.js"; import { authorizeGatewayConnect } from "../../auth.js"; import { loadConfig } from "../../../config/config.js"; import { buildDeviceAuthPayload } from "../../device-auth.js"; -import { isLocalGatewayAddress, resolveGatewayClientIp } from "../../net.js"; +import { isLocalGatewayAddress, isTrustedProxyAddress, resolveGatewayClientIp } from "../../net.js"; import { resolveNodeCommandAllowlist } from "../../node-command-policy.js"; import { type ConnectParams, @@ -100,6 +100,10 @@ function formatGatewayAuthFailureMessage(params: { return "unauthorized: tailscale identity missing (use Tailscale Serve auth or gateway token/password)"; case "tailscale_proxy_missing": return "unauthorized: tailscale proxy headers missing (use Tailscale Serve or gateway token/password)"; + case "tailscale_whois_failed": + return "unauthorized: tailscale identity check failed (use Tailscale Serve auth or gateway token/password)"; + case "tailscale_user_mismatch": + return "unauthorized: tailscale identity mismatch (use Tailscale Serve auth or gateway token/password)"; default: break; } @@ -177,7 +181,24 @@ export function attachGatewayWsMessageHandler(params: { const configSnapshot = loadConfig(); const trustedProxies = configSnapshot.gateway?.trustedProxies ?? []; const clientIp = resolveGatewayClientIp({ remoteAddr, forwardedFor, realIp, trustedProxies }); - const isLocalClient = isLocalGatewayAddress(clientIp); + + // If proxy headers are present but the remote address isn't trusted, don't treat + // the connection as local. This prevents auth bypass when running behind a reverse + // proxy without proper configuration - the proxy's loopback connection would otherwise + // cause all external requests to be treated as trusted local clients. + const hasProxyHeaders = Boolean(forwardedFor || realIp); + const remoteIsTrustedProxy = isTrustedProxyAddress(remoteAddr, trustedProxies); + const hasUntrustedProxyHeaders = hasProxyHeaders && !remoteIsTrustedProxy; + const isLocalClient = !hasUntrustedProxyHeaders && isLocalGatewayAddress(clientIp); + const reportedClientIp = hasUntrustedProxyHeaders ? undefined : clientIp; + + if (hasUntrustedProxyHeaders) { + logWsControl.warn( + "Proxy headers detected from untrusted address. " + + "Connection will not be treated as local. " + + "Configure gateway.trustedProxies to restore local client detection behind your proxy.", + ); + } const isWebchatConnect = (p: ConnectParams | null | undefined) => isWebchatClient(p?.client); @@ -318,13 +339,38 @@ export function attachGatewayWsMessageHandler(params: { let devicePublicKey: string | null = null; const hasTokenAuth = Boolean(connectParams.auth?.token); const hasPasswordAuth = Boolean(connectParams.auth?.password); + const hasSharedAuth = hasTokenAuth || hasPasswordAuth; const isControlUi = connectParams.client.id === GATEWAY_CLIENT_IDS.CONTROL_UI; + const allowInsecureControlUi = + isControlUi && configSnapshot.gateway?.controlUi?.allowInsecureAuth === true; + if (hasUntrustedProxyHeaders && resolvedAuth.mode === "none") { + setHandshakeState("failed"); + setCloseCause("proxy-auth-required", { + client: connectParams.client.id, + clientDisplayName: connectParams.client.displayName, + mode: connectParams.client.mode, + version: connectParams.client.version, + }); + send({ + type: "res", + id: frame.id, + ok: false, + error: errorShape( + ErrorCodes.INVALID_REQUEST, + "gateway auth required behind reverse proxy", + { + details: { + hint: "set gateway.auth or configure gateway.trustedProxies", + }, + }, + ), + }); + close(1008, "gateway auth required"); + return; + } if (!device) { - const allowInsecureControlUi = - isControlUi && configSnapshot.gateway?.controlUi?.allowInsecureAuth === true; - const canSkipDevice = - isControlUi && allowInsecureControlUi ? hasTokenAuth || hasPasswordAuth : hasTokenAuth; + const canSkipDevice = allowInsecureControlUi ? hasSharedAuth : hasTokenAuth; if (isControlUi && !allowInsecureControlUi) { const errorMessage = "control ui requires HTTPS or localhost (secure context)"; @@ -569,7 +615,8 @@ export function attachGatewayWsMessageHandler(params: { return; } - if (device && devicePublicKey) { + const skipPairing = allowInsecureControlUi && hasSharedAuth; + if (device && devicePublicKey && !skipPairing) { const requirePairing = async (reason: string, _paired?: { deviceId: string }) => { const pairing = await requestDevicePairing({ deviceId: device.id, @@ -580,7 +627,7 @@ export function attachGatewayWsMessageHandler(params: { clientMode: connectParams.client.mode, role, scopes, - remoteIp: clientIp, + remoteIp: reportedClientIp, silent: isLocalClient, }); const context = buildRequestContext(); @@ -664,7 +711,7 @@ export function attachGatewayWsMessageHandler(params: { clientMode: connectParams.client.mode, role, scopes, - remoteIp: clientIp, + remoteIp: reportedClientIp, }); } } @@ -713,7 +760,7 @@ export function attachGatewayWsMessageHandler(params: { if (presenceKey) { upsertPresence(presenceKey, { host: connectParams.client.displayName ?? connectParams.client.id ?? os.hostname(), - ip: isLocalClient ? undefined : clientIp, + ip: isLocalClient ? undefined : reportedClientIp, version: connectParams.client.version, platform: connectParams.client.platform, deviceFamily: connectParams.client.deviceFamily, @@ -772,7 +819,9 @@ export function attachGatewayWsMessageHandler(params: { setHandshakeState("connected"); if (role === "node") { const context = buildRequestContext(); - const nodeSession = context.nodeRegistry.register(nextClient, { remoteIp: clientIp }); + const nodeSession = context.nodeRegistry.register(nextClient, { + remoteIp: reportedClientIp, + }); const instanceIdRaw = connectParams.client.instanceId; const instanceId = typeof instanceIdRaw === "string" ? instanceIdRaw.trim() : ""; const nodeIdsForPairing = new Set([nodeSession.nodeId]); diff --git a/src/gateway/session-utils.ts b/src/gateway/session-utils.ts index c4046a08ea0..1cb4cc5c35f 100644 --- a/src/gateway/session-utils.ts +++ b/src/gateway/session-utils.ts @@ -381,6 +381,31 @@ export function resolveGatewaySessionStoreTarget(params: { cfg: ClawdbotConfig; }; } +// Merge with existing entry based on latest timestamp to ensure data consistency and avoid overwriting with less complete data. +function mergeSessionEntryIntoCombined(params: { + combined: Record; + entry: SessionEntry; + agentId: string; + canonicalKey: string; +}) { + const { combined, entry, agentId, canonicalKey } = params; + const existing = combined[canonicalKey]; + + if (existing && (existing.updatedAt ?? 0) > (entry.updatedAt ?? 0)) { + combined[canonicalKey] = { + ...entry, + ...existing, + spawnedBy: canonicalizeSpawnedByForAgent(agentId, existing.spawnedBy ?? entry.spawnedBy), + }; + } else { + combined[canonicalKey] = { + ...existing, + ...entry, + spawnedBy: canonicalizeSpawnedByForAgent(agentId, entry.spawnedBy ?? existing?.spawnedBy), + }; + } +} + export function loadCombinedSessionStoreForGateway(cfg: ClawdbotConfig): { storePath: string; store: Record; @@ -393,10 +418,12 @@ export function loadCombinedSessionStoreForGateway(cfg: ClawdbotConfig): { const combined: Record = {}; for (const [key, entry] of Object.entries(store)) { const canonicalKey = canonicalizeSessionKeyForAgent(defaultAgentId, key); - combined[canonicalKey] = { - ...entry, - spawnedBy: canonicalizeSpawnedByForAgent(defaultAgentId, entry.spawnedBy), - }; + mergeSessionEntryIntoCombined({ + combined, + entry, + agentId: defaultAgentId, + canonicalKey, + }); } return { storePath, store: combined }; } @@ -408,13 +435,12 @@ export function loadCombinedSessionStoreForGateway(cfg: ClawdbotConfig): { const store = loadSessionStore(storePath); for (const [key, entry] of Object.entries(store)) { const canonicalKey = canonicalizeSessionKeyForAgent(agentId, key); - // Merge with existing entry if present (avoid overwriting with less complete data) - const existing = combined[canonicalKey]; - combined[canonicalKey] = { - ...existing, - ...entry, - spawnedBy: canonicalizeSpawnedByForAgent(agentId, entry.spawnedBy ?? existing?.spawnedBy), - }; + mergeSessionEntryIntoCombined({ + combined, + entry, + agentId, + canonicalKey, + }); } } diff --git a/src/gateway/test-helpers.mocks.ts b/src/gateway/test-helpers.mocks.ts index 2a402201a3a..c740bba6639 100644 --- a/src/gateway/test-helpers.mocks.ts +++ b/src/gateway/test-helpers.mocks.ts @@ -138,6 +138,7 @@ const createStubPluginRegistry = (): PluginRegistry => ({ providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], commands: [], diff --git a/src/gateway/test-helpers.server.ts b/src/gateway/test-helpers.server.ts index b6e89486d51..254365564ac 100644 --- a/src/gateway/test-helpers.server.ts +++ b/src/gateway/test-helpers.server.ts @@ -111,7 +111,7 @@ async function resetGatewayTestState(options: { uniqueConfigRoot: boolean }) { sessionStoreSaveDelayMs.value = 0; testTailnetIPv4.value = undefined; testState.gatewayBind = undefined; - testState.gatewayAuth = undefined; + testState.gatewayAuth = { mode: "token", token: "test-gateway-token-1234567890" }; testState.gatewayControlUi = undefined; testState.hooksConfig = undefined; testState.canvasHostPort = undefined; @@ -260,10 +260,15 @@ export async function startGatewayServer(port: number, opts?: GatewayServerOptio export async function startServerWithClient(token?: string, opts?: GatewayServerOptions) { let port = await getFreePort(); const prev = process.env.CLAWDBOT_GATEWAY_TOKEN; - if (token === undefined) { + const fallbackToken = + token ?? + (typeof (testState.gatewayAuth as { token?: unknown } | undefined)?.token === "string" + ? (testState.gatewayAuth as { token?: string }).token + : undefined); + if (fallbackToken === undefined) { delete process.env.CLAWDBOT_GATEWAY_TOKEN; } else { - process.env.CLAWDBOT_GATEWAY_TOKEN = token; + process.env.CLAWDBOT_GATEWAY_TOKEN = fallbackToken; } let server: Awaited> | null = null; @@ -299,6 +304,7 @@ export async function connectReq( opts?: { token?: string; password?: string; + skipDefaultAuth?: boolean; minProtocol?: number; maxProtocol?: number; client?: { @@ -334,6 +340,20 @@ export async function connectReq( mode: GATEWAY_CLIENT_MODES.TEST, }; const role = opts?.role ?? "operator"; + const defaultToken = + opts?.skipDefaultAuth === true + ? undefined + : typeof (testState.gatewayAuth as { token?: unknown } | undefined)?.token === "string" + ? ((testState.gatewayAuth as { token?: string }).token ?? undefined) + : process.env.CLAWDBOT_GATEWAY_TOKEN; + const defaultPassword = + opts?.skipDefaultAuth === true + ? undefined + : typeof (testState.gatewayAuth as { password?: unknown } | undefined)?.password === "string" + ? ((testState.gatewayAuth as { password?: string }).password ?? undefined) + : process.env.CLAWDBOT_GATEWAY_PASSWORD; + const token = opts?.token ?? defaultToken; + const password = opts?.password ?? defaultPassword; const requestedScopes = Array.isArray(opts?.scopes) ? opts?.scopes : []; const device = (() => { if (opts?.device === null) return undefined; @@ -347,7 +367,7 @@ export async function connectReq( role, scopes: requestedScopes, signedAtMs, - token: opts?.token ?? null, + token: token ?? null, }); return { id: identity.deviceId, @@ -372,10 +392,10 @@ export async function connectReq( role, scopes: opts?.scopes, auth: - opts?.token || opts?.password + token || password ? { - token: opts?.token, - password: opts?.password, + token, + password, } : undefined, device, diff --git a/src/gateway/tools-invoke-http.test.ts b/src/gateway/tools-invoke-http.test.ts index c9db031e51a..18c23692da5 100644 --- a/src/gateway/tools-invoke-http.test.ts +++ b/src/gateway/tools-invoke-http.test.ts @@ -1,10 +1,18 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; +import type { IncomingMessage, ServerResponse } from "node:http"; import { installGatewayTestHooks, getFreePort, startGatewayServer } from "./test-helpers.server.js"; -import { testState } from "./test-helpers.mocks.js"; +import { resetTestPluginRegistry, setTestPluginRegistry, testState } from "./test-helpers.mocks.js"; +import { createTestRegistry } from "../test-utils/channel-plugins.js"; installGatewayTestHooks({ scope: "suite" }); +const resolveGatewayToken = (): string => { + const token = (testState.gatewayAuth as { token?: string } | undefined)?.token; + if (!token) throw new Error("test gateway token missing"); + return token; +}; + describe("POST /tools/invoke", () => { it("invokes a tool and returns {ok:true,result}", async () => { // Allow the sessions_list tool for main agent. @@ -23,10 +31,11 @@ describe("POST /tools/invoke", () => { const server = await startGatewayServer(port, { bind: "loopback", }); + const token = resolveGatewayToken(); const res = await fetch(`http://127.0.0.1:${port}/tools/invoke`, { method: "POST", - headers: { "content-type": "application/json" }, + headers: { "content-type": "application/json", authorization: `Bearer ${token}` }, body: JSON.stringify({ tool: "sessions_list", action: "json", args: {}, sessionKey: "main" }), }); @@ -70,6 +79,59 @@ describe("POST /tools/invoke", () => { await server.close(); }); + it("routes tools invoke before plugin HTTP handlers", async () => { + const pluginHandler = vi.fn(async (_req: IncomingMessage, res: ServerResponse) => { + res.statusCode = 418; + res.end("plugin"); + return true; + }); + const registry = createTestRegistry(); + registry.httpHandlers = [ + { + pluginId: "test-plugin", + source: "test", + handler: pluginHandler as unknown as ( + req: import("node:http").IncomingMessage, + res: import("node:http").ServerResponse, + ) => Promise, + }, + ]; + setTestPluginRegistry(registry); + + testState.agentsConfig = { + list: [ + { + id: "main", + tools: { + allow: ["sessions_list"], + }, + }, + ], + } as any; + + const port = await getFreePort(); + const server = await startGatewayServer(port, { bind: "loopback" }); + try { + const token = resolveGatewayToken(); + const res = await fetch(`http://127.0.0.1:${port}/tools/invoke`, { + method: "POST", + headers: { "content-type": "application/json", authorization: `Bearer ${token}` }, + body: JSON.stringify({ + tool: "sessions_list", + action: "json", + args: {}, + sessionKey: "main", + }), + }); + + expect(res.status).toBe(200); + expect(pluginHandler).not.toHaveBeenCalled(); + } finally { + await server.close(); + resetTestPluginRegistry(); + } + }); + it("rejects unauthorized when auth mode is token and header is missing", async () => { testState.agentsConfig = { list: [ @@ -113,10 +175,11 @@ describe("POST /tools/invoke", () => { const port = await getFreePort(); const server = await startGatewayServer(port, { bind: "loopback" }); + const token = resolveGatewayToken(); const res = await fetch(`http://127.0.0.1:${port}/tools/invoke`, { method: "POST", - headers: { "content-type": "application/json" }, + headers: { "content-type": "application/json", authorization: `Bearer ${token}` }, body: JSON.stringify({ tool: "sessions_list", action: "json", args: {}, sessionKey: "main" }), }); @@ -144,10 +207,11 @@ describe("POST /tools/invoke", () => { const port = await getFreePort(); const server = await startGatewayServer(port, { bind: "loopback" }); + const token = resolveGatewayToken(); const res = await fetch(`http://127.0.0.1:${port}/tools/invoke`, { method: "POST", - headers: { "content-type": "application/json" }, + headers: { "content-type": "application/json", authorization: `Bearer ${token}` }, body: JSON.stringify({ tool: "sessions_list", action: "json", args: {}, sessionKey: "main" }), }); @@ -180,17 +244,18 @@ describe("POST /tools/invoke", () => { const server = await startGatewayServer(port, { bind: "loopback" }); const payload = { tool: "sessions_list", action: "json", args: {} }; + const token = resolveGatewayToken(); const resDefault = await fetch(`http://127.0.0.1:${port}/tools/invoke`, { method: "POST", - headers: { "content-type": "application/json" }, + headers: { "content-type": "application/json", authorization: `Bearer ${token}` }, body: JSON.stringify(payload), }); expect(resDefault.status).toBe(200); const resMain = await fetch(`http://127.0.0.1:${port}/tools/invoke`, { method: "POST", - headers: { "content-type": "application/json" }, + headers: { "content-type": "application/json", authorization: `Bearer ${token}` }, body: JSON.stringify({ ...payload, sessionKey: "main" }), }); expect(resMain.status).toBe(200); diff --git a/src/imessage/monitor.skips-group-messages-without-mention-by-default.test.ts b/src/imessage/monitor.skips-group-messages-without-mention-by-default.test.ts index a0665044ebb..1c7330ab56b 100644 --- a/src/imessage/monitor.skips-group-messages-without-mention-by-default.test.ts +++ b/src/imessage/monitor.skips-group-messages-without-mention-by-default.test.ts @@ -277,15 +277,12 @@ describe("monitorIMessageProvider", () => { expect(ctx.SessionKey).toBe("agent:main:imessage:group:2"); }); - it("prefixes tool and final replies with responsePrefix", async () => { + it("prefixes final replies with responsePrefix", async () => { config = { ...config, messages: { responsePrefix: "PFX" }, }; - replyMock.mockImplementation(async (_ctx, opts) => { - await opts?.onToolResult?.({ text: "tool update" }); - return { text: "final reply" }; - }); + replyMock.mockResolvedValue({ text: "final reply" }); const run = monitorIMessageProvider(); await waitForSubscribe(); @@ -307,9 +304,8 @@ describe("monitorIMessageProvider", () => { closeResolve?.(); await run; - expect(sendMock).toHaveBeenCalledTimes(2); - expect(sendMock.mock.calls[0][1]).toBe("PFX tool update"); - expect(sendMock.mock.calls[1][1]).toBe("PFX final reply"); + expect(sendMock).toHaveBeenCalledTimes(1); + expect(sendMock.mock.calls[0][1]).toBe("PFX final reply"); }); it("defaults to dmPolicy=pairing behavior when allowFrom is empty", async () => { diff --git a/src/imessage/targets.test.ts b/src/imessage/targets.test.ts index 956dfa321f4..6350167a305 100644 --- a/src/imessage/targets.test.ts +++ b/src/imessage/targets.test.ts @@ -28,6 +28,27 @@ describe("imessage targets", () => { expect(normalizeIMessageHandle(" +1 (555) 222-3333 ")).toBe("+15552223333"); }); + it("normalizes chat_id prefixes case-insensitively", () => { + expect(normalizeIMessageHandle("CHAT_ID:123")).toBe("chat_id:123"); + expect(normalizeIMessageHandle("Chat_Id:456")).toBe("chat_id:456"); + expect(normalizeIMessageHandle("chatid:789")).toBe("chat_id:789"); + expect(normalizeIMessageHandle("CHAT:42")).toBe("chat_id:42"); + }); + + it("normalizes chat_guid prefixes case-insensitively", () => { + expect(normalizeIMessageHandle("CHAT_GUID:abc-def")).toBe("chat_guid:abc-def"); + expect(normalizeIMessageHandle("ChatGuid:XYZ")).toBe("chat_guid:XYZ"); + expect(normalizeIMessageHandle("GUID:test-guid")).toBe("chat_guid:test-guid"); + }); + + it("normalizes chat_identifier prefixes case-insensitively", () => { + expect(normalizeIMessageHandle("CHAT_IDENTIFIER:iMessage;-;chat123")).toBe( + "chat_identifier:iMessage;-;chat123", + ); + expect(normalizeIMessageHandle("ChatIdentifier:test")).toBe("chat_identifier:test"); + expect(normalizeIMessageHandle("CHATIDENT:foo")).toBe("chat_identifier:foo"); + }); + it("checks allowFrom against chat_id", () => { const ok = isAllowedIMessageSender({ allowFrom: ["chat_id:9"], diff --git a/src/imessage/targets.ts b/src/imessage/targets.ts index befb3f6d637..03fdcf3064a 100644 --- a/src/imessage/targets.ts +++ b/src/imessage/targets.ts @@ -34,6 +34,27 @@ export function normalizeIMessageHandle(raw: string): string { if (lowered.startsWith("imessage:")) return normalizeIMessageHandle(trimmed.slice(9)); if (lowered.startsWith("sms:")) return normalizeIMessageHandle(trimmed.slice(4)); if (lowered.startsWith("auto:")) return normalizeIMessageHandle(trimmed.slice(5)); + + // Normalize chat_id/chat_guid/chat_identifier prefixes case-insensitively + for (const prefix of CHAT_ID_PREFIXES) { + if (lowered.startsWith(prefix)) { + const value = trimmed.slice(prefix.length).trim(); + return `chat_id:${value}`; + } + } + for (const prefix of CHAT_GUID_PREFIXES) { + if (lowered.startsWith(prefix)) { + const value = trimmed.slice(prefix.length).trim(); + return `chat_guid:${value}`; + } + } + for (const prefix of CHAT_IDENTIFIER_PREFIXES) { + if (lowered.startsWith(prefix)) { + const value = trimmed.slice(prefix.length).trim(); + return `chat_identifier:${value}`; + } + } + if (trimmed.includes("@")) return trimmed.toLowerCase(); const normalized = normalizeE164(trimmed); if (normalized) return normalized; diff --git a/src/infra/diagnostic-flags.test.ts b/src/infra/diagnostic-flags.test.ts new file mode 100644 index 00000000000..9f1489f479e --- /dev/null +++ b/src/infra/diagnostic-flags.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from "vitest"; + +import type { ClawdbotConfig } from "../config/config.js"; +import { isDiagnosticFlagEnabled, resolveDiagnosticFlags } from "./diagnostic-flags.js"; + +describe("diagnostic flags", () => { + it("merges config + env flags", () => { + const cfg = { + diagnostics: { flags: ["telegram.http", "cache.*"] }, + } as ClawdbotConfig; + const env = { + CLAWDBOT_DIAGNOSTICS: "foo,bar", + } as NodeJS.ProcessEnv; + + const flags = resolveDiagnosticFlags(cfg, env); + expect(flags).toEqual(expect.arrayContaining(["telegram.http", "cache.*", "foo", "bar"])); + expect(isDiagnosticFlagEnabled("telegram.http", cfg, env)).toBe(true); + expect(isDiagnosticFlagEnabled("cache.hit", cfg, env)).toBe(true); + expect(isDiagnosticFlagEnabled("foo", cfg, env)).toBe(true); + }); + + it("treats env true as wildcard", () => { + const env = { CLAWDBOT_DIAGNOSTICS: "1" } as NodeJS.ProcessEnv; + expect(isDiagnosticFlagEnabled("anything.here", undefined, env)).toBe(true); + }); + + it("treats env false as disabled", () => { + const env = { CLAWDBOT_DIAGNOSTICS: "0" } as NodeJS.ProcessEnv; + expect(isDiagnosticFlagEnabled("telegram.http", undefined, env)).toBe(false); + }); +}); diff --git a/src/infra/diagnostic-flags.ts b/src/infra/diagnostic-flags.ts new file mode 100644 index 00000000000..4f7936c0a29 --- /dev/null +++ b/src/infra/diagnostic-flags.ts @@ -0,0 +1,70 @@ +import type { ClawdbotConfig } from "../config/config.js"; + +const DIAGNOSTICS_ENV = "CLAWDBOT_DIAGNOSTICS"; + +function normalizeFlag(value: string): string { + return value.trim().toLowerCase(); +} + +function parseEnvFlags(raw?: string): string[] { + if (!raw) return []; + const trimmed = raw.trim(); + if (!trimmed) return []; + const lowered = trimmed.toLowerCase(); + if (["0", "false", "off", "none"].includes(lowered)) return []; + if (["1", "true", "all", "*"].includes(lowered)) return ["*"]; + return trimmed + .split(/[,\s]+/) + .map(normalizeFlag) + .filter(Boolean); +} + +function uniqueFlags(flags: string[]): string[] { + const seen = new Set(); + const out: string[] = []; + for (const flag of flags) { + const normalized = normalizeFlag(flag); + if (!normalized || seen.has(normalized)) continue; + seen.add(normalized); + out.push(normalized); + } + return out; +} + +export function resolveDiagnosticFlags( + cfg?: ClawdbotConfig, + env: NodeJS.ProcessEnv = process.env, +): string[] { + const configFlags = Array.isArray(cfg?.diagnostics?.flags) ? cfg?.diagnostics?.flags : []; + const envFlags = parseEnvFlags(env[DIAGNOSTICS_ENV]); + return uniqueFlags([...configFlags, ...envFlags]); +} + +export function matchesDiagnosticFlag(flag: string, enabledFlags: string[]): boolean { + const target = normalizeFlag(flag); + if (!target) return false; + for (const raw of enabledFlags) { + const enabled = normalizeFlag(raw); + if (!enabled) continue; + if (enabled === "*" || enabled === "all") return true; + if (enabled.endsWith(".*")) { + const prefix = enabled.slice(0, -2); + if (target === prefix || target.startsWith(`${prefix}.`)) return true; + } + if (enabled.endsWith("*")) { + const prefix = enabled.slice(0, -1); + if (target.startsWith(prefix)) return true; + } + if (enabled === target) return true; + } + return false; +} + +export function isDiagnosticFlagEnabled( + flag: string, + cfg?: ClawdbotConfig, + env: NodeJS.ProcessEnv = process.env, +): boolean { + const flags = resolveDiagnosticFlags(cfg, env); + return matchesDiagnosticFlag(flag, flags); +} diff --git a/src/infra/env.ts b/src/infra/env.ts index 49839fcfe79..2139c65a728 100644 --- a/src/infra/env.ts +++ b/src/infra/env.ts @@ -1,5 +1,32 @@ +import { createSubsystemLogger } from "../logging/subsystem.js"; import { parseBooleanValue } from "../utils/boolean.js"; +const log = createSubsystemLogger("env"); +const loggedEnv = new Set(); + +type AcceptedEnvOption = { + key: string; + description: string; + value?: string; + redact?: boolean; +}; + +function formatEnvValue(value: string, redact?: boolean): string { + if (redact) return ""; + const singleLine = value.replace(/\s+/g, " ").trim(); + if (singleLine.length <= 160) return singleLine; + return `${singleLine.slice(0, 160)}…`; +} + +export function logAcceptedEnvOption(option: AcceptedEnvOption): void { + if (process.env.VITEST || process.env.NODE_ENV === "test") return; + if (loggedEnv.has(option.key)) return; + const rawValue = option.value ?? process.env[option.key]; + if (!rawValue || !rawValue.trim()) return; + loggedEnv.add(option.key); + log.info(`env: ${option.key}=${formatEnvValue(rawValue, option.redact)} (${option.description})`); +} + export function normalizeZaiEnv(): void { if (!process.env.ZAI_API_KEY?.trim() && process.env.Z_AI_API_KEY?.trim()) { process.env.ZAI_API_KEY = process.env.Z_AI_API_KEY; diff --git a/src/infra/gateway-lock.test.ts b/src/infra/gateway-lock.test.ts index 04b402b27ff..aac98bec5c7 100644 --- a/src/infra/gateway-lock.test.ts +++ b/src/infra/gateway-lock.test.ts @@ -7,12 +7,13 @@ import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { acquireGatewayLock, GatewayLockError } from "./gateway-lock.js"; -import { resolveConfigPath, resolveStateDir } from "../config/paths.js"; +import { resolveConfigPath, resolveGatewayLockDir, resolveStateDir } from "../config/paths.js"; async function makeEnv() { const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gateway-lock-")); const configPath = path.join(dir, "clawdbot.json"); await fs.writeFile(configPath, "{}", "utf8"); + await fs.mkdir(resolveGatewayLockDir(), { recursive: true }); return { env: { ...process.env, @@ -29,7 +30,8 @@ function resolveLockPath(env: NodeJS.ProcessEnv) { const stateDir = resolveStateDir(env); const configPath = resolveConfigPath(env, stateDir); const hash = createHash("sha1").update(configPath).digest("hex").slice(0, 8); - return { lockPath: path.join(stateDir, `gateway.${hash}.lock`), configPath }; + const lockDir = resolveGatewayLockDir(); + return { lockPath: path.join(lockDir, `gateway.${hash}.lock`), configPath }; } function makeProcStat(pid: number, startTime: number) { diff --git a/src/infra/gateway-lock.ts b/src/infra/gateway-lock.ts index 8a84c5e2f4c..4c5fe1bdeaf 100644 --- a/src/infra/gateway-lock.ts +++ b/src/infra/gateway-lock.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import fsSync from "node:fs"; import path from "node:path"; -import { resolveConfigPath, resolveStateDir } from "../config/paths.js"; +import { resolveConfigPath, resolveGatewayLockDir, resolveStateDir } from "../config/paths.js"; const DEFAULT_TIMEOUT_MS = 5000; const DEFAULT_POLL_INTERVAL_MS = 100; @@ -150,7 +150,8 @@ function resolveGatewayLockPath(env: NodeJS.ProcessEnv) { const stateDir = resolveStateDir(env); const configPath = resolveConfigPath(env, stateDir); const hash = createHash("sha1").update(configPath).digest("hex").slice(0, 8); - const lockPath = path.join(stateDir, `gateway.${hash}.lock`); + const lockDir = resolveGatewayLockDir(); + const lockPath = path.join(lockDir, `gateway.${hash}.lock`); return { lockPath, configPath }; } diff --git a/src/infra/node-shell.test.ts b/src/infra/node-shell.test.ts new file mode 100644 index 00000000000..8f95a29a824 --- /dev/null +++ b/src/infra/node-shell.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from "vitest"; + +import { buildNodeShellCommand } from "./node-shell.js"; + +describe("buildNodeShellCommand", () => { + it("uses cmd.exe for win32", () => { + expect(buildNodeShellCommand("echo hi", "win32")).toEqual([ + "cmd.exe", + "/d", + "/s", + "/c", + "echo hi", + ]); + }); + + it("uses cmd.exe for windows labels", () => { + expect(buildNodeShellCommand("echo hi", "windows")).toEqual([ + "cmd.exe", + "/d", + "/s", + "/c", + "echo hi", + ]); + expect(buildNodeShellCommand("echo hi", "Windows 11")).toEqual([ + "cmd.exe", + "/d", + "/s", + "/c", + "echo hi", + ]); + }); + + it("uses /bin/sh for darwin", () => { + expect(buildNodeShellCommand("echo hi", "darwin")).toEqual(["/bin/sh", "-lc", "echo hi"]); + }); + + it("uses /bin/sh when platform missing", () => { + expect(buildNodeShellCommand("echo hi")).toEqual(["/bin/sh", "-lc", "echo hi"]); + }); +}); diff --git a/src/infra/node-shell.ts b/src/infra/node-shell.ts index 26d5ff1d3cd..8a59fb524d9 100644 --- a/src/infra/node-shell.ts +++ b/src/infra/node-shell.ts @@ -2,7 +2,7 @@ export function buildNodeShellCommand(command: string, platform?: string | null) const normalized = String(platform ?? "") .trim() .toLowerCase(); - if (normalized.includes("win")) { + if (normalized.startsWith("win")) { return ["cmd.exe", "/d", "/s", "/c", command]; } return ["/bin/sh", "-lc", command]; diff --git a/src/infra/outbound/deliver.test.ts b/src/infra/outbound/deliver.test.ts index 2e939dfdafe..a80a3f48259 100644 --- a/src/infra/outbound/deliver.test.ts +++ b/src/infra/outbound/deliver.test.ts @@ -192,7 +192,7 @@ describe("deliverOutboundPayloads", () => { expect(sendWhatsApp).toHaveBeenNthCalledWith( 2, "+1555", - "\nLine two", + "Line two", expect.objectContaining({ verbose: false }), ); }); @@ -241,9 +241,8 @@ describe("deliverOutboundPayloads", () => { payloads: [{ text }], }); - expect(chunker).toHaveBeenCalledTimes(2); - expect(chunker).toHaveBeenNthCalledWith(1, "```js\nconst a = 1;\nconst b = 2;\n```", 4000); - expect(chunker).toHaveBeenNthCalledWith(2, "After", 4000); + expect(chunker).toHaveBeenCalledTimes(1); + expect(chunker).toHaveBeenNthCalledWith(1, text, 4000); }); it("uses iMessage media maxBytes from agent fallback", async () => { @@ -311,6 +310,28 @@ describe("deliverOutboundPayloads", () => { expect(results).toEqual([{ channel: "whatsapp", messageId: "w2", toJid: "jid" }]); }); + it("passes normalized payload to onError", async () => { + const sendWhatsApp = vi.fn().mockRejectedValue(new Error("boom")); + const onError = vi.fn(); + const cfg: ClawdbotConfig = {}; + + await deliverOutboundPayloads({ + cfg, + channel: "whatsapp", + to: "+1555", + payloads: [{ text: "hi", mediaUrl: "https://x.test/a.jpg" }], + deps: { sendWhatsApp }, + bestEffort: true, + onError, + }); + + expect(onError).toHaveBeenCalledTimes(1); + expect(onError).toHaveBeenCalledWith( + expect.any(Error), + expect.objectContaining({ text: "hi", mediaUrls: ["https://x.test/a.jpg"] }), + ); + }); + it("mirrors delivered output when mirror options are provided", async () => { const sendTelegram = vi.fn().mockResolvedValue({ messageId: "m1", chatId: "c1" }); const cfg: ClawdbotConfig = { diff --git a/src/infra/outbound/deliver.ts b/src/infra/outbound/deliver.ts index 6df384b5256..d246889e9b2 100644 --- a/src/infra/outbound/deliver.ts +++ b/src/infra/outbound/deliver.ts @@ -1,5 +1,5 @@ import { - chunkByNewline, + chunkByParagraph, chunkMarkdownTextWithMode, resolveChunkMode, resolveTextChunkLimit, @@ -22,7 +22,7 @@ import { resolveMirroredTranscriptText, } from "../../config/sessions.js"; import type { NormalizedOutboundPayload } from "./payloads.js"; -import { normalizeOutboundPayloads } from "./payloads.js"; +import { normalizeReplyPayloadsForDelivery } from "./payloads.js"; import type { OutboundChannel } from "./targets.js"; export type { NormalizedOutboundPayload } from "./payloads.js"; @@ -69,6 +69,7 @@ type ChannelHandler = { chunker: Chunker | null; chunkerMode?: "text" | "markdown"; textChunkLimit?: number; + sendPayload?: (payload: ReplyPayload) => Promise; sendText: (text: string) => Promise; sendMedia: (caption: string, mediaUrl: string) => Promise; }; @@ -132,6 +133,21 @@ function createPluginHandler(params: { chunker, chunkerMode, textChunkLimit: outbound.textChunkLimit, + sendPayload: outbound.sendPayload + ? async (payload) => + outbound.sendPayload!({ + cfg: params.cfg, + to: params.to, + text: payload.text ?? "", + mediaUrl: payload.mediaUrl, + accountId: params.accountId, + replyToId: params.replyToId, + threadId: params.threadId, + gifPlayback: params.gifPlayback, + deps: params.deps, + payload, + }) + : undefined, sendText: async (text) => sendText({ cfg: params.cfg, @@ -223,14 +239,15 @@ export async function deliverOutboundPayloads(params: { } if (chunkMode === "newline") { const mode = handler.chunkerMode ?? "text"; - const lineChunks = + const blockChunks = mode === "markdown" ? chunkMarkdownTextWithMode(text, textLimit, "newline") - : chunkByNewline(text, textLimit, { splitLongLines: false }); - if (!lineChunks.length && text) lineChunks.push(text); - for (const lineChunk of lineChunks) { - const chunks = handler.chunker(lineChunk, textLimit); - if (!chunks.length && lineChunk) chunks.push(lineChunk); + : chunkByParagraph(text, textLimit); + + if (!blockChunks.length && text) blockChunks.push(text); + for (const blockChunk of blockChunks) { + const chunks = handler.chunker(blockChunk, textLimit); + if (!chunks.length && blockChunk) chunks.push(blockChunk); for (const chunk of chunks) { throwIfAborted(abortSignal); results.push(await handler.sendText(chunk)); @@ -294,24 +311,33 @@ export async function deliverOutboundPayloads(params: { })), }; }; - const normalizedPayloads = normalizeOutboundPayloads(payloads); + const normalizedPayloads = normalizeReplyPayloadsForDelivery(payloads); for (const payload of normalizedPayloads) { + const payloadSummary: NormalizedOutboundPayload = { + text: payload.text ?? "", + mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []), + channelData: payload.channelData, + }; try { throwIfAborted(abortSignal); - params.onPayload?.(payload); - if (payload.mediaUrls.length === 0) { + params.onPayload?.(payloadSummary); + if (handler.sendPayload && payload.channelData) { + results.push(await handler.sendPayload(payload)); + continue; + } + if (payloadSummary.mediaUrls.length === 0) { if (isSignalChannel) { - await sendSignalTextChunks(payload.text); + await sendSignalTextChunks(payloadSummary.text); } else { - await sendTextChunks(payload.text); + await sendTextChunks(payloadSummary.text); } continue; } let first = true; - for (const url of payload.mediaUrls) { + for (const url of payloadSummary.mediaUrls) { throwIfAborted(abortSignal); - const caption = first ? payload.text : ""; + const caption = first ? payloadSummary.text : ""; first = false; if (isSignalChannel) { results.push(await sendSignalMedia(caption, url)); @@ -321,7 +347,7 @@ export async function deliverOutboundPayloads(params: { } } catch (err) { if (!params.bestEffort) throw err; - params.onError?.(err, payload); + params.onError?.(err, payloadSummary); } } if (params.mirror && results.length > 0) { diff --git a/src/infra/outbound/message-action-runner.test.ts b/src/infra/outbound/message-action-runner.test.ts index 9b592d9d2dc..ca1054c177d 100644 --- a/src/infra/outbound/message-action-runner.test.ts +++ b/src/infra/outbound/message-action-runner.test.ts @@ -321,6 +321,44 @@ describe("runMessageAction context isolation", () => { }), ).rejects.toThrow(/Cross-context messaging denied/); }); + + it("aborts send when abortSignal is already aborted", async () => { + const controller = new AbortController(); + controller.abort(); + + await expect( + runMessageAction({ + cfg: slackConfig, + action: "send", + params: { + channel: "slack", + target: "#C12345678", + message: "hi", + }, + dryRun: true, + abortSignal: controller.signal, + }), + ).rejects.toMatchObject({ name: "AbortError" }); + }); + + it("aborts broadcast when abortSignal is already aborted", async () => { + const controller = new AbortController(); + controller.abort(); + + await expect( + runMessageAction({ + cfg: slackConfig, + action: "broadcast", + params: { + targets: ["channel:C12345678"], + channel: "slack", + message: "hi", + }, + dryRun: true, + abortSignal: controller.signal, + }), + ).rejects.toMatchObject({ name: "AbortError" }); + }); }); describe("runMessageAction sendAttachment hydration", () => { diff --git a/src/infra/outbound/message-action-runner.ts b/src/infra/outbound/message-action-runner.ts index 50ddce227b9..8d02b743cb9 100644 --- a/src/infra/outbound/message-action-runner.ts +++ b/src/infra/outbound/message-action-runner.ts @@ -64,6 +64,7 @@ export type RunMessageActionParams = { sessionKey?: string; agentId?: string; dryRun?: boolean; + abortSignal?: AbortSignal; }; export type MessageActionRunResult = @@ -507,6 +508,7 @@ type ResolvedActionContext = { input: RunMessageActionParams; agentId?: string; resolvedTarget?: ResolvedMessagingTarget; + abortSignal?: AbortSignal; }; function resolveGateway(input: RunMessageActionParams): MessageActionRunnerGateway | undefined { if (!input.gateway) return undefined; @@ -524,6 +526,7 @@ async function handleBroadcastAction( input: RunMessageActionParams, params: Record, ): Promise { + throwIfAborted(input.abortSignal); const broadcastEnabled = input.cfg.tools?.message?.broadcast?.enabled !== false; if (!broadcastEnabled) { throw new Error("Broadcast is disabled. Set tools.message.broadcast.enabled to true."); @@ -548,8 +551,11 @@ async function handleBroadcastAction( error?: string; result?: MessageSendResult; }> = []; + const isAbortError = (err: unknown): boolean => err instanceof Error && err.name === "AbortError"; for (const targetChannel of targetChannels) { + throwIfAborted(input.abortSignal); for (const target of rawTargets) { + throwIfAborted(input.abortSignal); try { const resolved = await resolveChannelTarget({ cfg: input.cfg, @@ -573,6 +579,7 @@ async function handleBroadcastAction( result: sendResult.kind === "send" ? sendResult.sendResult : undefined, }); } catch (err) { + if (isAbortError(err)) throw err; results.push({ channel: targetChannel, to: target, @@ -592,8 +599,28 @@ async function handleBroadcastAction( }; } +function throwIfAborted(abortSignal?: AbortSignal): void { + if (abortSignal?.aborted) { + const err = new Error("Message send aborted"); + err.name = "AbortError"; + throw err; + } +} + async function handleSendAction(ctx: ResolvedActionContext): Promise { - const { cfg, params, channel, accountId, dryRun, gateway, input, agentId, resolvedTarget } = ctx; + const { + cfg, + params, + channel, + accountId, + dryRun, + gateway, + input, + agentId, + resolvedTarget, + abortSignal, + } = ctx; + throwIfAborted(abortSignal); const action: ChannelMessageActionName = "send"; const to = readStringParam(params, "to", { required: true }); // Support media, path, and filePath parameters for attachments @@ -676,6 +703,7 @@ async function handleSendAction(ctx: ResolvedActionContext): Promise 0 ? mergedMediaUrls : mediaUrl ? [mediaUrl] : undefined; + throwIfAborted(abortSignal); const send = await executeSendAction({ ctx: { cfg, @@ -695,6 +723,7 @@ async function handleSendAction(ctx: ResolvedActionContext): Promise { - const { cfg, params, channel, accountId, dryRun, gateway, input } = ctx; + const { cfg, params, channel, accountId, dryRun, gateway, input, abortSignal } = ctx; + throwIfAborted(abortSignal); const action: ChannelMessageActionName = "poll"; const to = readStringParam(params, "to", { required: true }); const question = readStringParam(params, "pollQuestion", { @@ -777,7 +807,8 @@ async function handlePollAction(ctx: ResolvedActionContext): Promise { - const { cfg, params, channel, accountId, dryRun, gateway, input } = ctx; + const { cfg, params, channel, accountId, dryRun, gateway, input, abortSignal } = ctx; + throwIfAborted(abortSignal); const action = input.action as Exclude; if (dryRun) { return { @@ -930,6 +961,7 @@ export async function runMessageAction( input, agentId: resolvedAgentId, resolvedTarget, + abortSignal: input.abortSignal, }); } @@ -942,6 +974,7 @@ export async function runMessageAction( dryRun, gateway, input, + abortSignal: input.abortSignal, }); } @@ -953,5 +986,6 @@ export async function runMessageAction( dryRun, gateway, input, + abortSignal: input.abortSignal, }); } diff --git a/src/infra/outbound/message.ts b/src/infra/outbound/message.ts index fcb90c295b5..6f5f88bd28a 100644 --- a/src/infra/outbound/message.ts +++ b/src/infra/outbound/message.ts @@ -50,6 +50,7 @@ type MessageSendParams = { text?: string; mediaUrls?: string[]; }; + abortSignal?: AbortSignal; }; export type MessageSendResult = { @@ -167,6 +168,7 @@ export async function sendMessage(params: MessageSendParams): Promise { if (!params.toolContext?.currentChannelId) return null; + // Skip decoration for direct tool sends (agent composing, not forwarding) + if (params.toolContext.skipCrossContextDecoration) return null; if (!isCrossContextTarget(params)) return null; const markerConfig = params.cfg.tools?.message?.crossContext?.marker; @@ -131,11 +133,11 @@ export async function buildCrossContextDecoration(params: { targetId: params.toolContext.currentChannelId, accountId: params.accountId ?? undefined, })) ?? params.toolContext.currentChannelId; + // Don't force group formatting here; currentChannelId can be a DM or a group. const originLabel = formatTargetDisplay({ channel: params.channel, target: params.toolContext.currentChannelId, display: currentName, - kind: "group", }); const prefixTemplate = markerConfig?.prefix ?? "[from {channel}] "; const suffixTemplate = markerConfig?.suffix ?? ""; diff --git a/src/infra/outbound/outbound-send-service.ts b/src/infra/outbound/outbound-send-service.ts index dd5dfd5e603..88a64d2519e 100644 --- a/src/infra/outbound/outbound-send-service.ts +++ b/src/infra/outbound/outbound-send-service.ts @@ -32,6 +32,7 @@ export type OutboundSendContext = { text?: string; mediaUrls?: string[]; }; + abortSignal?: AbortSignal; }; function extractToolPayload(result: AgentToolResult): unknown { @@ -56,6 +57,14 @@ function extractToolPayload(result: AgentToolResult): unknown { return result.content ?? result; } +function throwIfAborted(abortSignal?: AbortSignal): void { + if (abortSignal?.aborted) { + const err = new Error("Message send aborted"); + err.name = "AbortError"; + throw err; + } +} + export async function executeSendAction(params: { ctx: OutboundSendContext; to: string; @@ -70,6 +79,7 @@ export async function executeSendAction(params: { toolResult?: AgentToolResult; sendResult?: MessageSendResult; }> { + throwIfAborted(params.ctx.abortSignal); if (!params.ctx.dryRun) { const handled = await dispatchChannelMessageAction({ channel: params.ctx.channel, @@ -103,6 +113,7 @@ export async function executeSendAction(params: { } } + throwIfAborted(params.ctx.abortSignal); const result: MessageSendResult = await sendMessage({ cfg: params.ctx.cfg, to: params.to, @@ -117,6 +128,7 @@ export async function executeSendAction(params: { deps: params.ctx.deps, gateway: params.ctx.gateway, mirror: params.ctx.mirror, + abortSignal: params.ctx.abortSignal, }); return { diff --git a/src/infra/outbound/payloads.test.ts b/src/infra/outbound/payloads.test.ts index 24d2b7622d0..9165abed906 100644 --- a/src/infra/outbound/payloads.test.ts +++ b/src/infra/outbound/payloads.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from "vitest"; -import { formatOutboundPayloadLog, normalizeOutboundPayloadsForJson } from "./payloads.js"; +import { + formatOutboundPayloadLog, + normalizeOutboundPayloads, + normalizeOutboundPayloadsForJson, +} from "./payloads.js"; describe("normalizeOutboundPayloadsForJson", () => { it("normalizes payloads with mediaUrl and mediaUrls", () => { @@ -11,16 +15,18 @@ describe("normalizeOutboundPayloadsForJson", () => { { text: "multi", mediaUrls: ["https://x.test/1.png"] }, ]), ).toEqual([ - { text: "hi", mediaUrl: null, mediaUrls: undefined }, + { text: "hi", mediaUrl: null, mediaUrls: undefined, channelData: undefined }, { text: "photo", mediaUrl: "https://x.test/a.jpg", mediaUrls: ["https://x.test/a.jpg"], + channelData: undefined, }, { text: "multi", mediaUrl: null, mediaUrls: ["https://x.test/1.png"], + channelData: undefined, }, ]); }); @@ -37,11 +43,20 @@ describe("normalizeOutboundPayloadsForJson", () => { text: "", mediaUrl: null, mediaUrls: ["https://x.test/a.png", "https://x.test/b.png"], + channelData: undefined, }, ]); }); }); +describe("normalizeOutboundPayloads", () => { + it("keeps channelData-only payloads", () => { + const channelData = { line: { flexMessage: { altText: "Card", contents: {} } } }; + const normalized = normalizeOutboundPayloads([{ channelData }]); + expect(normalized).toEqual([{ text: "", mediaUrls: [], channelData }]); + }); +}); + describe("formatOutboundPayloadLog", () => { it("trims trailing text and appends media lines", () => { expect( diff --git a/src/infra/outbound/payloads.ts b/src/infra/outbound/payloads.ts index b3558b3567a..94eabb2bc37 100644 --- a/src/infra/outbound/payloads.ts +++ b/src/infra/outbound/payloads.ts @@ -5,12 +5,14 @@ import type { ReplyPayload } from "../../auto-reply/types.js"; export type NormalizedOutboundPayload = { text: string; mediaUrls: string[]; + channelData?: Record; }; export type OutboundPayloadJson = { text: string; mediaUrl: string | null; mediaUrls?: string[]; + channelData?: Record; }; function mergeMediaUrls(...lists: Array | undefined>): string[] { @@ -58,11 +60,23 @@ export function normalizeReplyPayloadsForDelivery(payloads: ReplyPayload[]): Rep export function normalizeOutboundPayloads(payloads: ReplyPayload[]): NormalizedOutboundPayload[] { return normalizeReplyPayloadsForDelivery(payloads) - .map((payload) => ({ - text: payload.text ?? "", - mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []), - })) - .filter((payload) => payload.text || payload.mediaUrls.length > 0); + .map((payload) => { + const channelData = payload.channelData; + const normalized: NormalizedOutboundPayload = { + text: payload.text ?? "", + mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []), + }; + if (channelData && Object.keys(channelData).length > 0) { + normalized.channelData = channelData; + } + return normalized; + }) + .filter( + (payload) => + payload.text || + payload.mediaUrls.length > 0 || + Boolean(payload.channelData && Object.keys(payload.channelData).length > 0), + ); } export function normalizeOutboundPayloadsForJson(payloads: ReplyPayload[]): OutboundPayloadJson[] { @@ -70,6 +84,7 @@ export function normalizeOutboundPayloadsForJson(payloads: ReplyPayload[]): Outb text: payload.text ?? "", mediaUrl: payload.mediaUrl ?? null, mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : undefined), + channelData: payload.channelData, })); } diff --git a/src/infra/outbound/target-resolver.ts b/src/infra/outbound/target-resolver.ts index d21685a9387..9f9d2f8d239 100644 --- a/src/infra/outbound/target-resolver.ts +++ b/src/infra/outbound/target-resolver.ts @@ -100,7 +100,12 @@ export function formatTargetDisplay(params: { if (!trimmedTarget) return trimmedTarget; if (trimmedTarget.startsWith("#") || trimmedTarget.startsWith("@")) return trimmedTarget; - const withoutPrefix = trimmedTarget.replace(/^telegram:/i, ""); + const channelPrefix = `${params.channel}:`; + const withoutProvider = trimmedTarget.toLowerCase().startsWith(channelPrefix) + ? trimmedTarget.slice(channelPrefix.length) + : trimmedTarget; + + const withoutPrefix = withoutProvider.replace(/^telegram:/i, ""); if (/^channel:/i.test(withoutPrefix)) { return `#${withoutPrefix.replace(/^channel:/i, "")}`; } @@ -119,14 +124,23 @@ function preserveTargetCase(channel: ChannelId, raw: string, normalized: string) return trimmed; } -function detectTargetKind(raw: string, preferred?: TargetResolveKind): TargetResolveKind { +function detectTargetKind( + channel: ChannelId, + raw: string, + preferred?: TargetResolveKind, +): TargetResolveKind { if (preferred) return preferred; const trimmed = raw.trim(); if (!trimmed) return "group"; + if (trimmed.startsWith("@") || /^<@!?/.test(trimmed) || /^user:/i.test(trimmed)) return "user"; - if (trimmed.startsWith("#") || /^channel:/i.test(trimmed)) { - return "group"; + if (trimmed.startsWith("#") || /^channel:/i.test(trimmed)) return "group"; + + // For some channels (e.g., BlueBubbles/iMessage), bare phone numbers are almost always DM targets. + if ((channel === "bluebubbles" || channel === "imessage") && /^\+?\d{6,}$/.test(trimmed)) { + return "user"; } + return "group"; } @@ -282,7 +296,7 @@ export async function resolveMessagingTarget(params: { const plugin = getChannelPlugin(params.channel); const providerLabel = plugin?.meta?.label ?? params.channel; const hint = plugin?.messaging?.targetResolver?.hint; - const kind = detectTargetKind(raw, params.preferredKind); + const kind = detectTargetKind(params.channel, raw, params.preferredKind); const normalized = normalizeTargetForProvider(params.channel, raw) ?? raw; const looksLikeTargetId = (): boolean => { const trimmed = raw.trim(); @@ -291,7 +305,12 @@ export async function resolveMessagingTarget(params: { if (lookup) return lookup(trimmed, normalized); if (/^(channel|group|user):/i.test(trimmed)) return true; if (/^[@#]/.test(trimmed)) return true; - if (/^\+?\d{6,}$/.test(trimmed)) return true; + if (/^\+?\d{6,}$/.test(trimmed)) { + // BlueBubbles/iMessage phone numbers should usually resolve via the directory to a DM chat, + // otherwise the provider may pick an existing group containing that handle. + if (params.channel === "bluebubbles" || params.channel === "imessage") return false; + return true; + } if (trimmed.includes("@thread")) return true; if (/^(conversation|user):/i.test(trimmed)) return true; return false; @@ -353,6 +372,24 @@ export async function resolveMessagingTarget(params: { candidates: match.entries, }; } + // For iMessage-style channels, allow sending directly to the normalized handle + // even if the directory doesn't contain an entry yet. + if ( + (params.channel === "bluebubbles" || params.channel === "imessage") && + /^\+?\d{6,}$/.test(query) + ) { + const directTarget = preserveTargetCase(params.channel, raw, normalized); + return { + ok: true, + target: { + to: directTarget, + kind, + display: stripTargetPrefixes(raw), + source: "normalized", + }, + }; + } + return { ok: false, error: unknownTargetError(providerLabel, raw, hint), @@ -367,16 +404,32 @@ export async function lookupDirectoryDisplay(params: { runtime?: RuntimeEnv; }): Promise { const normalized = normalizeTargetForProvider(params.channel, params.targetId) ?? params.targetId; - const candidates = await getDirectoryEntries({ - cfg: params.cfg, - channel: params.channel, - accountId: params.accountId, - kind: "group", - runtime: params.runtime, - preferLiveOnMiss: false, - }); - const entry = candidates.find( - (candidate) => normalizeDirectoryEntryId(params.channel, candidate) === normalized, - ); + + // Targets can resolve to either peers (DMs) or groups. Try both. + const [groups, users] = await Promise.all([ + getDirectoryEntries({ + cfg: params.cfg, + channel: params.channel, + accountId: params.accountId, + kind: "group", + runtime: params.runtime, + preferLiveOnMiss: false, + }), + getDirectoryEntries({ + cfg: params.cfg, + channel: params.channel, + accountId: params.accountId, + kind: "user", + runtime: params.runtime, + preferLiveOnMiss: false, + }), + ]); + + const findMatch = (candidates: ChannelDirectoryEntry[]) => + candidates.find( + (candidate) => normalizeDirectoryEntryId(params.channel, candidate) === normalized, + ); + + const entry = findMatch(groups) ?? findMatch(users); return entry?.name ?? entry?.handle ?? undefined; } diff --git a/src/infra/tailscale.ts b/src/infra/tailscale.ts index 8ff34018404..2350670bbc5 100644 --- a/src/infra/tailscale.ts +++ b/src/infra/tailscale.ts @@ -213,6 +213,18 @@ type ExecErrorDetails = { code?: unknown; }; +export type TailscaleWhoisIdentity = { + login: string; + name?: string; +}; + +type TailscaleWhoisCacheEntry = { + value: TailscaleWhoisIdentity | null; + expiresAt: number; +}; + +const whoisCache = new Map(); + function extractExecErrorText(err: unknown) { const errOutput = err as ExecErrorDetails; const stdout = typeof errOutput.stdout === "string" ? errOutput.stdout : ""; @@ -381,3 +393,73 @@ export async function disableTailscaleFunnel(exec: typeof runExec = runExec) { timeoutMs: 15_000, }); } + +function getString(value: unknown): string | undefined { + return typeof value === "string" && value.trim() ? value.trim() : undefined; +} + +function readRecord(value: unknown): Record | null { + return value && typeof value === "object" ? (value as Record) : null; +} + +function parseWhoisIdentity(payload: Record): TailscaleWhoisIdentity | null { + const userProfile = + readRecord(payload.UserProfile) ?? readRecord(payload.userProfile) ?? readRecord(payload.User); + const login = + getString(userProfile?.LoginName) ?? + getString(userProfile?.Login) ?? + getString(userProfile?.login) ?? + getString(payload.LoginName) ?? + getString(payload.login); + if (!login) return null; + const name = + getString(userProfile?.DisplayName) ?? + getString(userProfile?.Name) ?? + getString(userProfile?.displayName) ?? + getString(payload.DisplayName) ?? + getString(payload.name); + return { login, name }; +} + +function readCachedWhois(ip: string, now: number): TailscaleWhoisIdentity | null | undefined { + const cached = whoisCache.get(ip); + if (!cached) return undefined; + if (cached.expiresAt <= now) { + whoisCache.delete(ip); + return undefined; + } + return cached.value; +} + +function writeCachedWhois(ip: string, value: TailscaleWhoisIdentity | null, ttlMs: number) { + whoisCache.set(ip, { value, expiresAt: Date.now() + ttlMs }); +} + +export async function readTailscaleWhoisIdentity( + ip: string, + exec: typeof runExec = runExec, + opts?: { timeoutMs?: number; cacheTtlMs?: number; errorTtlMs?: number }, +): Promise { + const normalized = ip.trim(); + if (!normalized) return null; + const now = Date.now(); + const cached = readCachedWhois(normalized, now); + if (cached !== undefined) return cached; + + const cacheTtlMs = opts?.cacheTtlMs ?? 60_000; + const errorTtlMs = opts?.errorTtlMs ?? 5_000; + try { + const tailscaleBin = await getTailscaleBinary(); + const { stdout } = await exec(tailscaleBin, ["whois", "--json", normalized], { + timeoutMs: opts?.timeoutMs ?? 5_000, + maxBuffer: 200_000, + }); + const parsed = stdout ? parsePossiblyNoisyJsonObject(stdout) : {}; + const identity = parseWhoisIdentity(parsed); + writeCachedWhois(normalized, identity, cacheTtlMs); + return identity; + } catch { + writeCachedWhois(normalized, null, errorTtlMs); + return null; + } +} diff --git a/src/infra/update-check.ts b/src/infra/update-check.ts index 2e020ff8d54..518da3c2802 100644 --- a/src/infra/update-check.ts +++ b/src/infra/update-check.ts @@ -129,9 +129,10 @@ export async function checkGitUpdateStatus(params: { ).catch(() => null); const upstream = upstreamRes && upstreamRes.code === 0 ? upstreamRes.stdout.trim() : null; - const dirtyRes = await runCommandWithTimeout(["git", "-C", root, "status", "--porcelain"], { - timeoutMs, - }).catch(() => null); + const dirtyRes = await runCommandWithTimeout( + ["git", "-C", root, "status", "--porcelain", "--", ":!dist/control-ui/"], + { timeoutMs }, + ).catch(() => null); const dirty = dirtyRes && dirtyRes.code === 0 ? dirtyRes.stdout.trim().length > 0 : null; const fetchOk = params.fetch diff --git a/src/infra/update-runner.test.ts b/src/infra/update-runner.test.ts index e33159326ae..6bf450d8324 100644 --- a/src/infra/update-runner.test.ts +++ b/src/infra/update-runner.test.ts @@ -44,7 +44,7 @@ describe("runGatewayUpdate", () => { [`git -C ${tempDir} rev-parse --show-toplevel`]: { stdout: tempDir }, [`git -C ${tempDir} rev-parse HEAD`]: { stdout: "abc123" }, [`git -C ${tempDir} rev-parse --abbrev-ref HEAD`]: { stdout: "main" }, - [`git -C ${tempDir} status --porcelain`]: { stdout: " M README.md" }, + [`git -C ${tempDir} status --porcelain -- :!dist/control-ui/`]: { stdout: " M README.md" }, }); const result = await runGatewayUpdate({ @@ -69,7 +69,7 @@ describe("runGatewayUpdate", () => { [`git -C ${tempDir} rev-parse --show-toplevel`]: { stdout: tempDir }, [`git -C ${tempDir} rev-parse HEAD`]: { stdout: "abc123" }, [`git -C ${tempDir} rev-parse --abbrev-ref HEAD`]: { stdout: "main" }, - [`git -C ${tempDir} status --porcelain`]: { stdout: "" }, + [`git -C ${tempDir} status --porcelain -- :!dist/control-ui/`]: { stdout: "" }, [`git -C ${tempDir} rev-parse --abbrev-ref --symbolic-full-name @{upstream}`]: { stdout: "origin/main", }, @@ -103,7 +103,7 @@ describe("runGatewayUpdate", () => { const { runner, calls } = createRunner({ [`git -C ${tempDir} rev-parse --show-toplevel`]: { stdout: tempDir }, [`git -C ${tempDir} rev-parse HEAD`]: { stdout: "abc123" }, - [`git -C ${tempDir} status --porcelain`]: { stdout: "" }, + [`git -C ${tempDir} status --porcelain -- :!dist/control-ui/`]: { stdout: "" }, [`git -C ${tempDir} fetch --all --prune --tags`]: { stdout: "" }, [`git -C ${tempDir} tag --list v* --sort=-v:refname`]: { stdout: `${stableTag}\n${betaTag}\n`, @@ -112,6 +112,7 @@ describe("runGatewayUpdate", () => { "pnpm install": { stdout: "" }, "pnpm build": { stdout: "" }, "pnpm ui:build": { stdout: "" }, + [`git -C ${tempDir} checkout -- dist/control-ui/`]: { stdout: "" }, "pnpm clawdbot doctor --non-interactive": { stdout: "" }, }); diff --git a/src/infra/update-runner.ts b/src/infra/update-runner.ts index 0a5196fd70b..c73c3a7e76b 100644 --- a/src/infra/update-runner.ts +++ b/src/infra/update-runner.ts @@ -346,10 +346,14 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise< const channel: UpdateChannel = opts.channel ?? "dev"; const branch = channel === "dev" ? await readBranchName(runCommand, gitRoot, timeoutMs) : null; const needsCheckoutMain = channel === "dev" && branch !== DEV_BRANCH; - gitTotalSteps = channel === "dev" ? (needsCheckoutMain ? 10 : 9) : 8; + gitTotalSteps = channel === "dev" ? (needsCheckoutMain ? 11 : 10) : 9; const statusCheck = await runStep( - step("clean check", ["git", "-C", gitRoot, "status", "--porcelain"], gitRoot), + step( + "clean check", + ["git", "-C", gitRoot, "status", "--porcelain", "--", ":!dist/control-ui/"], + gitRoot, + ), ); steps.push(statusCheck); const hasUncommittedChanges = @@ -654,6 +658,17 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise< ); steps.push(uiBuildStep); + // Restore dist/control-ui/ to committed state to prevent dirty repo after update + // (ui:build regenerates assets with new hashes, which would block future updates) + const restoreUiStep = await runStep( + step( + "restore control-ui", + ["git", "-C", gitRoot, "checkout", "--", "dist/control-ui/"], + gitRoot, + ), + ); + steps.push(restoreUiStep); + const doctorStep = await runStep( step( "clawdbot doctor", diff --git a/src/line/accounts.test.ts b/src/line/accounts.test.ts new file mode 100644 index 00000000000..e0ea3dba278 --- /dev/null +++ b/src/line/accounts.test.ts @@ -0,0 +1,199 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { + resolveLineAccount, + listLineAccountIds, + resolveDefaultLineAccountId, + normalizeAccountId, + DEFAULT_ACCOUNT_ID, +} from "./accounts.js"; +import type { ClawdbotConfig } from "../config/config.js"; + +describe("LINE accounts", () => { + const originalEnv = { ...process.env }; + + beforeEach(() => { + process.env = { ...originalEnv }; + delete process.env.LINE_CHANNEL_ACCESS_TOKEN; + delete process.env.LINE_CHANNEL_SECRET; + }); + + afterEach(() => { + process.env = originalEnv; + }); + + describe("resolveLineAccount", () => { + it("resolves account from config", () => { + const cfg: ClawdbotConfig = { + channels: { + line: { + enabled: true, + channelAccessToken: "test-token", + channelSecret: "test-secret", + name: "Test Bot", + }, + }, + }; + + const account = resolveLineAccount({ cfg }); + + expect(account.accountId).toBe(DEFAULT_ACCOUNT_ID); + expect(account.enabled).toBe(true); + expect(account.channelAccessToken).toBe("test-token"); + expect(account.channelSecret).toBe("test-secret"); + expect(account.name).toBe("Test Bot"); + expect(account.tokenSource).toBe("config"); + }); + + it("resolves account from environment variables", () => { + process.env.LINE_CHANNEL_ACCESS_TOKEN = "env-token"; + process.env.LINE_CHANNEL_SECRET = "env-secret"; + + const cfg: ClawdbotConfig = { + channels: { + line: { + enabled: true, + }, + }, + }; + + const account = resolveLineAccount({ cfg }); + + expect(account.channelAccessToken).toBe("env-token"); + expect(account.channelSecret).toBe("env-secret"); + expect(account.tokenSource).toBe("env"); + }); + + it("resolves named account", () => { + const cfg: ClawdbotConfig = { + channels: { + line: { + enabled: true, + accounts: { + business: { + enabled: true, + channelAccessToken: "business-token", + channelSecret: "business-secret", + name: "Business Bot", + }, + }, + }, + }, + }; + + const account = resolveLineAccount({ cfg, accountId: "business" }); + + expect(account.accountId).toBe("business"); + expect(account.enabled).toBe(true); + expect(account.channelAccessToken).toBe("business-token"); + expect(account.channelSecret).toBe("business-secret"); + expect(account.name).toBe("Business Bot"); + }); + + it("returns empty token when not configured", () => { + const cfg: ClawdbotConfig = {}; + + const account = resolveLineAccount({ cfg }); + + expect(account.channelAccessToken).toBe(""); + expect(account.channelSecret).toBe(""); + expect(account.tokenSource).toBe("none"); + }); + }); + + describe("listLineAccountIds", () => { + it("returns default account when configured at base level", () => { + const cfg: ClawdbotConfig = { + channels: { + line: { + channelAccessToken: "test-token", + }, + }, + }; + + const ids = listLineAccountIds(cfg); + + expect(ids).toContain(DEFAULT_ACCOUNT_ID); + }); + + it("returns named accounts", () => { + const cfg: ClawdbotConfig = { + channels: { + line: { + accounts: { + business: { enabled: true }, + personal: { enabled: true }, + }, + }, + }, + }; + + const ids = listLineAccountIds(cfg); + + expect(ids).toContain("business"); + expect(ids).toContain("personal"); + }); + + it("returns default from env", () => { + process.env.LINE_CHANNEL_ACCESS_TOKEN = "env-token"; + const cfg: ClawdbotConfig = {}; + + const ids = listLineAccountIds(cfg); + + expect(ids).toContain(DEFAULT_ACCOUNT_ID); + }); + }); + + describe("resolveDefaultLineAccountId", () => { + it("returns default when configured", () => { + const cfg: ClawdbotConfig = { + channels: { + line: { + channelAccessToken: "test-token", + }, + }, + }; + + const id = resolveDefaultLineAccountId(cfg); + + expect(id).toBe(DEFAULT_ACCOUNT_ID); + }); + + it("returns first named account when default not configured", () => { + const cfg: ClawdbotConfig = { + channels: { + line: { + accounts: { + business: { enabled: true }, + }, + }, + }, + }; + + const id = resolveDefaultLineAccountId(cfg); + + expect(id).toBe("business"); + }); + }); + + describe("normalizeAccountId", () => { + it("normalizes undefined to default", () => { + expect(normalizeAccountId(undefined)).toBe(DEFAULT_ACCOUNT_ID); + }); + + it("normalizes 'default' to DEFAULT_ACCOUNT_ID", () => { + expect(normalizeAccountId("default")).toBe(DEFAULT_ACCOUNT_ID); + }); + + it("preserves other account ids", () => { + expect(normalizeAccountId("business")).toBe("business"); + }); + + it("lowercases account ids", () => { + expect(normalizeAccountId("Business")).toBe("business"); + }); + + it("trims whitespace", () => { + expect(normalizeAccountId(" business ")).toBe("business"); + }); + }); +}); diff --git a/src/line/accounts.ts b/src/line/accounts.ts new file mode 100644 index 00000000000..9542bbf06a6 --- /dev/null +++ b/src/line/accounts.ts @@ -0,0 +1,179 @@ +import fs from "node:fs"; +import type { ClawdbotConfig } from "../config/config.js"; +import type { + LineConfig, + LineAccountConfig, + ResolvedLineAccount, + LineTokenSource, +} from "./types.js"; + +export const DEFAULT_ACCOUNT_ID = "default"; + +function readFileIfExists(filePath: string | undefined): string | undefined { + if (!filePath) return undefined; + try { + return fs.readFileSync(filePath, "utf-8").trim(); + } catch { + return undefined; + } +} + +function resolveToken(params: { + accountId: string; + baseConfig?: LineConfig; + accountConfig?: LineAccountConfig; +}): { token: string; tokenSource: LineTokenSource } { + const { accountId, baseConfig, accountConfig } = params; + + // Check account-level config first + if (accountConfig?.channelAccessToken?.trim()) { + return { token: accountConfig.channelAccessToken.trim(), tokenSource: "config" }; + } + + // Check account-level token file + const accountFileToken = readFileIfExists(accountConfig?.tokenFile); + if (accountFileToken) { + return { token: accountFileToken, tokenSource: "file" }; + } + + // For default account, check base config and env + if (accountId === DEFAULT_ACCOUNT_ID) { + if (baseConfig?.channelAccessToken?.trim()) { + return { token: baseConfig.channelAccessToken.trim(), tokenSource: "config" }; + } + + const baseFileToken = readFileIfExists(baseConfig?.tokenFile); + if (baseFileToken) { + return { token: baseFileToken, tokenSource: "file" }; + } + + const envToken = process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim(); + if (envToken) { + return { token: envToken, tokenSource: "env" }; + } + } + + return { token: "", tokenSource: "none" }; +} + +function resolveSecret(params: { + accountId: string; + baseConfig?: LineConfig; + accountConfig?: LineAccountConfig; +}): string { + const { accountId, baseConfig, accountConfig } = params; + + // Check account-level config first + if (accountConfig?.channelSecret?.trim()) { + return accountConfig.channelSecret.trim(); + } + + // Check account-level secret file + const accountFileSecret = readFileIfExists(accountConfig?.secretFile); + if (accountFileSecret) { + return accountFileSecret; + } + + // For default account, check base config and env + if (accountId === DEFAULT_ACCOUNT_ID) { + if (baseConfig?.channelSecret?.trim()) { + return baseConfig.channelSecret.trim(); + } + + const baseFileSecret = readFileIfExists(baseConfig?.secretFile); + if (baseFileSecret) { + return baseFileSecret; + } + + const envSecret = process.env.LINE_CHANNEL_SECRET?.trim(); + if (envSecret) { + return envSecret; + } + } + + return ""; +} + +export function resolveLineAccount(params: { + cfg: ClawdbotConfig; + accountId?: string; +}): ResolvedLineAccount { + const { cfg, accountId = DEFAULT_ACCOUNT_ID } = params; + const lineConfig = cfg.channels?.line as LineConfig | undefined; + const accounts = lineConfig?.accounts; + const accountConfig = accountId !== DEFAULT_ACCOUNT_ID ? accounts?.[accountId] : undefined; + + const { token, tokenSource } = resolveToken({ + accountId, + baseConfig: lineConfig, + accountConfig, + }); + + const secret = resolveSecret({ + accountId, + baseConfig: lineConfig, + accountConfig, + }); + + const mergedConfig: LineConfig & LineAccountConfig = { + ...lineConfig, + ...accountConfig, + }; + + const enabled = + accountConfig?.enabled ?? + (accountId === DEFAULT_ACCOUNT_ID ? (lineConfig?.enabled ?? true) : false); + + const name = + accountConfig?.name ?? (accountId === DEFAULT_ACCOUNT_ID ? lineConfig?.name : undefined); + + return { + accountId, + name, + enabled, + channelAccessToken: token, + channelSecret: secret, + tokenSource, + config: mergedConfig, + }; +} + +export function listLineAccountIds(cfg: ClawdbotConfig): string[] { + const lineConfig = cfg.channels?.line as LineConfig | undefined; + const accounts = lineConfig?.accounts; + const ids = new Set(); + + // Add default account if configured at base level + if ( + lineConfig?.channelAccessToken?.trim() || + lineConfig?.tokenFile || + process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim() + ) { + ids.add(DEFAULT_ACCOUNT_ID); + } + + // Add named accounts + if (accounts) { + for (const id of Object.keys(accounts)) { + ids.add(id); + } + } + + return Array.from(ids); +} + +export function resolveDefaultLineAccountId(cfg: ClawdbotConfig): string { + const ids = listLineAccountIds(cfg); + if (ids.includes(DEFAULT_ACCOUNT_ID)) { + return DEFAULT_ACCOUNT_ID; + } + return ids[0] ?? DEFAULT_ACCOUNT_ID; +} + +export function normalizeAccountId(accountId: string | undefined): string { + const trimmed = accountId?.trim().toLowerCase(); + if (!trimmed || trimmed === "default") { + return DEFAULT_ACCOUNT_ID; + } + return trimmed; +} diff --git a/src/line/auto-reply-delivery.test.ts b/src/line/auto-reply-delivery.test.ts new file mode 100644 index 00000000000..48a7bf7249c --- /dev/null +++ b/src/line/auto-reply-delivery.test.ts @@ -0,0 +1,202 @@ +import { describe, expect, it, vi } from "vitest"; + +import { deliverLineAutoReply } from "./auto-reply-delivery.js"; +import { sendLineReplyChunks } from "./reply-chunks.js"; + +const createFlexMessage = (altText: string, contents: unknown) => ({ + type: "flex" as const, + altText, + contents, +}); + +const createImageMessage = (url: string) => ({ + type: "image" as const, + originalContentUrl: url, + previewImageUrl: url, +}); + +const createLocationMessage = (location: { + title: string; + address: string; + latitude: number; + longitude: number; +}) => ({ + type: "location" as const, + ...location, +}); + +describe("deliverLineAutoReply", () => { + it("uses reply token for text before sending rich messages", async () => { + const replyMessageLine = vi.fn(async () => ({})); + const pushMessageLine = vi.fn(async () => ({})); + const pushTextMessageWithQuickReplies = vi.fn(async () => ({})); + const createTextMessageWithQuickReplies = vi.fn((text: string) => ({ + type: "text" as const, + text, + })); + const createQuickReplyItems = vi.fn((labels: string[]) => ({ items: labels })); + const pushMessagesLine = vi.fn(async () => ({ messageId: "push", chatId: "u1" })); + + const lineData = { + flexMessage: { altText: "Card", contents: { type: "bubble" } }, + }; + + const result = await deliverLineAutoReply({ + payload: { text: "hello", channelData: { line: lineData } }, + lineData, + to: "line:user:1", + replyToken: "token", + replyTokenUsed: false, + accountId: "acc", + textLimit: 5000, + deps: { + buildTemplateMessageFromPayload: () => null, + processLineMessage: (text) => ({ text, flexMessages: [] }), + chunkMarkdownText: (text) => [text], + sendLineReplyChunks, + replyMessageLine, + pushMessageLine, + pushTextMessageWithQuickReplies, + createTextMessageWithQuickReplies, + createQuickReplyItems, + pushMessagesLine, + createFlexMessage, + createImageMessage, + createLocationMessage, + }, + }); + + expect(result.replyTokenUsed).toBe(true); + expect(replyMessageLine).toHaveBeenCalledTimes(1); + expect(replyMessageLine).toHaveBeenCalledWith("token", [{ type: "text", text: "hello" }], { + accountId: "acc", + }); + expect(pushMessagesLine).toHaveBeenCalledTimes(1); + expect(pushMessagesLine).toHaveBeenCalledWith( + "line:user:1", + [createFlexMessage("Card", { type: "bubble" })], + { accountId: "acc" }, + ); + expect(createQuickReplyItems).not.toHaveBeenCalled(); + }); + + it("uses reply token for rich-only payloads", async () => { + const replyMessageLine = vi.fn(async () => ({})); + const pushMessageLine = vi.fn(async () => ({})); + const pushTextMessageWithQuickReplies = vi.fn(async () => ({})); + const createTextMessageWithQuickReplies = vi.fn((text: string) => ({ + type: "text" as const, + text, + })); + const createQuickReplyItems = vi.fn((labels: string[]) => ({ items: labels })); + const pushMessagesLine = vi.fn(async () => ({ messageId: "push", chatId: "u1" })); + + const lineData = { + flexMessage: { altText: "Card", contents: { type: "bubble" } }, + quickReplies: ["A"], + }; + + const result = await deliverLineAutoReply({ + payload: { channelData: { line: lineData } }, + lineData, + to: "line:user:1", + replyToken: "token", + replyTokenUsed: false, + accountId: "acc", + textLimit: 5000, + deps: { + buildTemplateMessageFromPayload: () => null, + processLineMessage: () => ({ text: "", flexMessages: [] }), + chunkMarkdownText: () => [], + sendLineReplyChunks: vi.fn(async () => ({ replyTokenUsed: false })), + replyMessageLine, + pushMessageLine, + pushTextMessageWithQuickReplies, + createTextMessageWithQuickReplies, + createQuickReplyItems, + pushMessagesLine, + createFlexMessage, + createImageMessage, + createLocationMessage, + }, + }); + + expect(result.replyTokenUsed).toBe(true); + expect(replyMessageLine).toHaveBeenCalledTimes(1); + expect(replyMessageLine).toHaveBeenCalledWith( + "token", + [ + { + ...createFlexMessage("Card", { type: "bubble" }), + quickReply: { items: ["A"] }, + }, + ], + { accountId: "acc" }, + ); + expect(pushMessagesLine).not.toHaveBeenCalled(); + expect(createQuickReplyItems).toHaveBeenCalledWith(["A"]); + }); + + it("sends rich messages before quick-reply text so quick replies remain visible", async () => { + const replyMessageLine = vi.fn(async () => ({})); + const pushMessageLine = vi.fn(async () => ({})); + const pushTextMessageWithQuickReplies = vi.fn(async () => ({})); + const createTextMessageWithQuickReplies = vi.fn((text: string, _quickReplies: string[]) => ({ + type: "text" as const, + text, + quickReply: { items: ["A"] }, + })); + const createQuickReplyItems = vi.fn((labels: string[]) => ({ items: labels })); + const pushMessagesLine = vi.fn(async () => ({ messageId: "push", chatId: "u1" })); + + const lineData = { + flexMessage: { altText: "Card", contents: { type: "bubble" } }, + quickReplies: ["A"], + }; + + await deliverLineAutoReply({ + payload: { text: "hello", channelData: { line: lineData } }, + lineData, + to: "line:user:1", + replyToken: "token", + replyTokenUsed: false, + accountId: "acc", + textLimit: 5000, + deps: { + buildTemplateMessageFromPayload: () => null, + processLineMessage: (text) => ({ text, flexMessages: [] }), + chunkMarkdownText: (text) => [text], + sendLineReplyChunks, + replyMessageLine, + pushMessageLine, + pushTextMessageWithQuickReplies, + createTextMessageWithQuickReplies, + createQuickReplyItems, + pushMessagesLine, + createFlexMessage, + createImageMessage, + createLocationMessage, + }, + }); + + expect(pushMessagesLine).toHaveBeenCalledWith( + "line:user:1", + [createFlexMessage("Card", { type: "bubble" })], + { accountId: "acc" }, + ); + expect(replyMessageLine).toHaveBeenCalledWith( + "token", + [ + { + type: "text", + text: "hello", + quickReply: { items: ["A"] }, + }, + ], + { accountId: "acc" }, + ); + const pushOrder = pushMessagesLine.mock.invocationCallOrder[0]; + const replyOrder = replyMessageLine.mock.invocationCallOrder[0]; + expect(pushOrder).toBeLessThan(replyOrder); + }); +}); diff --git a/src/line/auto-reply-delivery.ts b/src/line/auto-reply-delivery.ts new file mode 100644 index 00000000000..ad4573ca1c5 --- /dev/null +++ b/src/line/auto-reply-delivery.ts @@ -0,0 +1,180 @@ +import type { messagingApi } from "@line/bot-sdk"; +import type { ReplyPayload } from "../auto-reply/types.js"; +import type { FlexContainer } from "./flex-templates.js"; +import type { ProcessedLineMessage } from "./markdown-to-line.js"; +import type { LineChannelData, LineTemplateMessagePayload } from "./types.js"; +import type { LineReplyMessage, SendLineReplyChunksParams } from "./reply-chunks.js"; + +export type LineAutoReplyDeps = { + buildTemplateMessageFromPayload: ( + payload: LineTemplateMessagePayload, + ) => messagingApi.TemplateMessage | null; + processLineMessage: (text: string) => ProcessedLineMessage; + chunkMarkdownText: (text: string, limit: number) => string[]; + sendLineReplyChunks: (params: SendLineReplyChunksParams) => Promise<{ replyTokenUsed: boolean }>; + replyMessageLine: ( + replyToken: string, + messages: messagingApi.Message[], + opts?: { accountId?: string }, + ) => Promise; + pushMessageLine: (to: string, text: string, opts?: { accountId?: string }) => Promise; + pushTextMessageWithQuickReplies: ( + to: string, + text: string, + quickReplies: string[], + opts?: { accountId?: string }, + ) => Promise; + createTextMessageWithQuickReplies: (text: string, quickReplies: string[]) => LineReplyMessage; + createQuickReplyItems: (labels: string[]) => messagingApi.QuickReply; + pushMessagesLine: ( + to: string, + messages: messagingApi.Message[], + opts?: { accountId?: string }, + ) => Promise; + createFlexMessage: (altText: string, contents: FlexContainer) => messagingApi.FlexMessage; + createImageMessage: ( + originalContentUrl: string, + previewImageUrl?: string, + ) => messagingApi.ImageMessage; + createLocationMessage: (location: { + title: string; + address: string; + latitude: number; + longitude: number; + }) => messagingApi.LocationMessage; + onReplyError?: (err: unknown) => void; +}; + +export async function deliverLineAutoReply(params: { + payload: ReplyPayload; + lineData: LineChannelData; + to: string; + replyToken?: string | null; + replyTokenUsed: boolean; + accountId?: string; + textLimit: number; + deps: LineAutoReplyDeps; +}): Promise<{ replyTokenUsed: boolean }> { + const { payload, lineData, replyToken, accountId, to, textLimit, deps } = params; + let replyTokenUsed = params.replyTokenUsed; + + const pushLineMessages = async (messages: messagingApi.Message[]): Promise => { + if (messages.length === 0) return; + for (let i = 0; i < messages.length; i += 5) { + await deps.pushMessagesLine(to, messages.slice(i, i + 5), { + accountId, + }); + } + }; + + const sendLineMessages = async ( + messages: messagingApi.Message[], + allowReplyToken: boolean, + ): Promise => { + if (messages.length === 0) return; + + let remaining = messages; + if (allowReplyToken && replyToken && !replyTokenUsed) { + const replyBatch = remaining.slice(0, 5); + try { + await deps.replyMessageLine(replyToken, replyBatch, { + accountId, + }); + } catch (err) { + deps.onReplyError?.(err); + await pushLineMessages(replyBatch); + } + replyTokenUsed = true; + remaining = remaining.slice(replyBatch.length); + } + + if (remaining.length > 0) { + await pushLineMessages(remaining); + } + }; + + const richMessages: messagingApi.Message[] = []; + const hasQuickReplies = Boolean(lineData.quickReplies?.length); + + if (lineData.flexMessage) { + richMessages.push( + deps.createFlexMessage( + lineData.flexMessage.altText.slice(0, 400), + lineData.flexMessage.contents as FlexContainer, + ), + ); + } + + if (lineData.templateMessage) { + const templateMsg = deps.buildTemplateMessageFromPayload(lineData.templateMessage); + if (templateMsg) { + richMessages.push(templateMsg); + } + } + + if (lineData.location) { + richMessages.push(deps.createLocationMessage(lineData.location)); + } + + const processed = payload.text + ? deps.processLineMessage(payload.text) + : { text: "", flexMessages: [] }; + + for (const flexMsg of processed.flexMessages) { + richMessages.push( + deps.createFlexMessage(flexMsg.altText.slice(0, 400), flexMsg.contents as FlexContainer), + ); + } + + const chunks = processed.text ? deps.chunkMarkdownText(processed.text, textLimit) : []; + + const mediaUrls = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []); + const mediaMessages = mediaUrls + .map((url) => url?.trim()) + .filter((url): url is string => Boolean(url)) + .map((url) => deps.createImageMessage(url)); + + if (chunks.length > 0) { + const hasRichOrMedia = richMessages.length > 0 || mediaMessages.length > 0; + if (hasQuickReplies && hasRichOrMedia) { + try { + await sendLineMessages([...richMessages, ...mediaMessages], false); + } catch (err) { + deps.onReplyError?.(err); + } + } + const { replyTokenUsed: nextReplyTokenUsed } = await deps.sendLineReplyChunks({ + to, + chunks, + quickReplies: lineData.quickReplies, + replyToken, + replyTokenUsed, + accountId, + replyMessageLine: deps.replyMessageLine, + pushMessageLine: deps.pushMessageLine, + pushTextMessageWithQuickReplies: deps.pushTextMessageWithQuickReplies, + createTextMessageWithQuickReplies: deps.createTextMessageWithQuickReplies, + }); + replyTokenUsed = nextReplyTokenUsed; + if (!hasQuickReplies || !hasRichOrMedia) { + await sendLineMessages(richMessages, false); + if (mediaMessages.length > 0) { + await sendLineMessages(mediaMessages, false); + } + } + } else { + const combined = [...richMessages, ...mediaMessages]; + if (hasQuickReplies && combined.length > 0) { + const quickReply = deps.createQuickReplyItems(lineData.quickReplies!); + const targetIndex = + replyToken && !replyTokenUsed ? Math.min(4, combined.length - 1) : combined.length - 1; + const target = combined[targetIndex] as messagingApi.Message & { + quickReply?: messagingApi.QuickReply; + }; + combined[targetIndex] = { ...target, quickReply }; + } + await sendLineMessages(combined, true); + } + + return { replyTokenUsed }; +} diff --git a/src/line/bot-access.ts b/src/line/bot-access.ts new file mode 100644 index 00000000000..2df9502fe95 --- /dev/null +++ b/src/line/bot-access.ts @@ -0,0 +1,48 @@ +export type NormalizedAllowFrom = { + entries: string[]; + hasWildcard: boolean; + hasEntries: boolean; +}; + +function normalizeAllowEntry(value: string | number): string { + const trimmed = String(value).trim(); + if (!trimmed) return ""; + if (trimmed === "*") return "*"; + return trimmed.replace(/^line:(?:user:)?/i, ""); +} + +export const normalizeAllowFrom = (list?: Array): NormalizedAllowFrom => { + const entries = (list ?? []).map((value) => normalizeAllowEntry(value)).filter(Boolean); + const hasWildcard = entries.includes("*"); + return { + entries, + hasWildcard, + hasEntries: entries.length > 0, + }; +}; + +export const normalizeAllowFromWithStore = (params: { + allowFrom?: Array; + storeAllowFrom?: string[]; +}): NormalizedAllowFrom => { + const combined = [...(params.allowFrom ?? []), ...(params.storeAllowFrom ?? [])]; + return normalizeAllowFrom(combined); +}; + +export const firstDefined = (...values: Array) => { + for (const value of values) { + if (typeof value !== "undefined") return value; + } + return undefined; +}; + +export const isSenderAllowed = (params: { + allow: NormalizedAllowFrom; + senderId?: string; +}): boolean => { + const { allow, senderId } = params; + if (!allow.hasEntries) return false; + if (allow.hasWildcard) return true; + if (!senderId) return false; + return allow.entries.includes(senderId); +}; diff --git a/src/line/bot-handlers.test.ts b/src/line/bot-handlers.test.ts new file mode 100644 index 00000000000..00f0082ed67 --- /dev/null +++ b/src/line/bot-handlers.test.ts @@ -0,0 +1,173 @@ +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import type { MessageEvent } from "@line/bot-sdk"; + +const { buildLineMessageContextMock, buildLinePostbackContextMock } = vi.hoisted(() => ({ + buildLineMessageContextMock: vi.fn(async () => ({ + ctxPayload: { From: "line:group:group-1" }, + replyToken: "reply-token", + route: { agentId: "default" }, + isGroup: true, + accountId: "default", + })), + buildLinePostbackContextMock: vi.fn(async () => null), +})); + +vi.mock("./bot-message-context.js", () => ({ + buildLineMessageContext: (...args: unknown[]) => buildLineMessageContextMock(...args), + buildLinePostbackContext: (...args: unknown[]) => buildLinePostbackContextMock(...args), +})); + +const { readAllowFromStoreMock, upsertPairingRequestMock } = vi.hoisted(() => ({ + readAllowFromStoreMock: vi.fn(async () => [] as string[]), + upsertPairingRequestMock: vi.fn(async () => ({ code: "CODE", created: true })), +})); + +let handleLineWebhookEvents: typeof import("./bot-handlers.js").handleLineWebhookEvents; + +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore: (...args: unknown[]) => readAllowFromStoreMock(...args), + upsertChannelPairingRequest: (...args: unknown[]) => upsertPairingRequestMock(...args), +})); + +describe("handleLineWebhookEvents", () => { + beforeAll(async () => { + ({ handleLineWebhookEvents } = await import("./bot-handlers.js")); + }); + + beforeEach(() => { + buildLineMessageContextMock.mockClear(); + buildLinePostbackContextMock.mockClear(); + readAllowFromStoreMock.mockClear(); + upsertPairingRequestMock.mockClear(); + }); + + it("blocks group messages when groupPolicy is disabled", async () => { + const processMessage = vi.fn(); + const event = { + type: "message", + message: { id: "m1", type: "text", text: "hi" }, + replyToken: "reply-token", + timestamp: Date.now(), + source: { type: "group", groupId: "group-1", userId: "user-1" }, + mode: "active", + webhookEventId: "evt-1", + deliveryContext: { isRedelivery: false }, + } as MessageEvent; + + await handleLineWebhookEvents([event], { + cfg: { channels: { line: { groupPolicy: "disabled" } } }, + account: { + accountId: "default", + enabled: true, + channelAccessToken: "token", + channelSecret: "secret", + tokenSource: "config", + config: { groupPolicy: "disabled" }, + }, + runtime: { error: vi.fn() }, + mediaMaxBytes: 1, + processMessage, + }); + + expect(processMessage).not.toHaveBeenCalled(); + expect(buildLineMessageContextMock).not.toHaveBeenCalled(); + }); + + it("blocks group messages when allowlist is empty", async () => { + const processMessage = vi.fn(); + const event = { + type: "message", + message: { id: "m2", type: "text", text: "hi" }, + replyToken: "reply-token", + timestamp: Date.now(), + source: { type: "group", groupId: "group-1", userId: "user-2" }, + mode: "active", + webhookEventId: "evt-2", + deliveryContext: { isRedelivery: false }, + } as MessageEvent; + + await handleLineWebhookEvents([event], { + cfg: { channels: { line: { groupPolicy: "allowlist" } } }, + account: { + accountId: "default", + enabled: true, + channelAccessToken: "token", + channelSecret: "secret", + tokenSource: "config", + config: { groupPolicy: "allowlist" }, + }, + runtime: { error: vi.fn() }, + mediaMaxBytes: 1, + processMessage, + }); + + expect(processMessage).not.toHaveBeenCalled(); + expect(buildLineMessageContextMock).not.toHaveBeenCalled(); + }); + + it("allows group messages when sender is in groupAllowFrom", async () => { + const processMessage = vi.fn(); + const event = { + type: "message", + message: { id: "m3", type: "text", text: "hi" }, + replyToken: "reply-token", + timestamp: Date.now(), + source: { type: "group", groupId: "group-1", userId: "user-3" }, + mode: "active", + webhookEventId: "evt-3", + deliveryContext: { isRedelivery: false }, + } as MessageEvent; + + await handleLineWebhookEvents([event], { + cfg: { + channels: { line: { groupPolicy: "allowlist", groupAllowFrom: ["user-3"] } }, + }, + account: { + accountId: "default", + enabled: true, + channelAccessToken: "token", + channelSecret: "secret", + tokenSource: "config", + config: { groupPolicy: "allowlist", groupAllowFrom: ["user-3"] }, + }, + runtime: { error: vi.fn() }, + mediaMaxBytes: 1, + processMessage, + }); + + expect(buildLineMessageContextMock).toHaveBeenCalledTimes(1); + expect(processMessage).toHaveBeenCalledTimes(1); + }); + + it("blocks group messages when wildcard group config disables groups", async () => { + const processMessage = vi.fn(); + const event = { + type: "message", + message: { id: "m4", type: "text", text: "hi" }, + replyToken: "reply-token", + timestamp: Date.now(), + source: { type: "group", groupId: "group-2", userId: "user-4" }, + mode: "active", + webhookEventId: "evt-4", + deliveryContext: { isRedelivery: false }, + } as MessageEvent; + + await handleLineWebhookEvents([event], { + cfg: { channels: { line: { groupPolicy: "open" } } }, + account: { + accountId: "default", + enabled: true, + channelAccessToken: "token", + channelSecret: "secret", + tokenSource: "config", + config: { groupPolicy: "open", groups: { "*": { enabled: false } } }, + }, + runtime: { error: vi.fn() }, + mediaMaxBytes: 1, + processMessage, + }); + + expect(processMessage).not.toHaveBeenCalled(); + expect(buildLineMessageContextMock).not.toHaveBeenCalled(); + }); +}); diff --git a/src/line/bot-handlers.ts b/src/line/bot-handlers.ts new file mode 100644 index 00000000000..28e91b98492 --- /dev/null +++ b/src/line/bot-handlers.ts @@ -0,0 +1,337 @@ +import type { + WebhookEvent, + MessageEvent, + FollowEvent, + UnfollowEvent, + JoinEvent, + LeaveEvent, + PostbackEvent, + EventSource, +} from "@line/bot-sdk"; +import type { ClawdbotConfig } from "../config/config.js"; +import { danger, logVerbose } from "../globals.js"; +import { resolvePairingIdLabel } from "../pairing/pairing-labels.js"; +import { buildPairingReply } from "../pairing/pairing-messages.js"; +import { + readChannelAllowFromStore, + upsertChannelPairingRequest, +} from "../pairing/pairing-store.js"; +import type { RuntimeEnv } from "../runtime.js"; +import { + buildLineMessageContext, + buildLinePostbackContext, + type LineInboundContext, +} from "./bot-message-context.js"; +import { firstDefined, isSenderAllowed, normalizeAllowFromWithStore } from "./bot-access.js"; +import { downloadLineMedia } from "./download.js"; +import { pushMessageLine, replyMessageLine } from "./send.js"; +import type { LineGroupConfig, ResolvedLineAccount } from "./types.js"; + +interface MediaRef { + path: string; + contentType?: string; +} + +export interface LineHandlerContext { + cfg: ClawdbotConfig; + account: ResolvedLineAccount; + runtime: RuntimeEnv; + mediaMaxBytes: number; + processMessage: (ctx: LineInboundContext) => Promise; +} + +type LineSourceInfo = { + userId?: string; + groupId?: string; + roomId?: string; + isGroup: boolean; +}; + +function getSourceInfo(source: EventSource): LineSourceInfo { + const userId = + source.type === "user" + ? source.userId + : source.type === "group" + ? source.userId + : source.type === "room" + ? source.userId + : undefined; + const groupId = source.type === "group" ? source.groupId : undefined; + const roomId = source.type === "room" ? source.roomId : undefined; + const isGroup = source.type === "group" || source.type === "room"; + return { userId, groupId, roomId, isGroup }; +} + +function resolveLineGroupConfig(params: { + config: ResolvedLineAccount["config"]; + groupId?: string; + roomId?: string; +}): LineGroupConfig | undefined { + const groups = params.config.groups ?? {}; + if (params.groupId) { + return groups[params.groupId] ?? groups[`group:${params.groupId}`] ?? groups["*"]; + } + if (params.roomId) { + return groups[params.roomId] ?? groups[`room:${params.roomId}`] ?? groups["*"]; + } + return groups["*"]; +} + +async function sendLinePairingReply(params: { + senderId: string; + replyToken?: string; + context: LineHandlerContext; +}): Promise { + const { senderId, replyToken, context } = params; + const { code, created } = await upsertChannelPairingRequest({ + channel: "line", + id: senderId, + }); + if (!created) return; + logVerbose(`line pairing request sender=${senderId}`); + const idLabel = (() => { + try { + return resolvePairingIdLabel("line"); + } catch { + return "lineUserId"; + } + })(); + const text = buildPairingReply({ + channel: "line", + idLine: `Your ${idLabel}: ${senderId}`, + code, + }); + try { + if (replyToken) { + await replyMessageLine(replyToken, [{ type: "text", text }], { + accountId: context.account.accountId, + channelAccessToken: context.account.channelAccessToken, + }); + return; + } + } catch (err) { + logVerbose(`line pairing reply failed for ${senderId}: ${String(err)}`); + } + try { + await pushMessageLine(`line:${senderId}`, text, { + accountId: context.account.accountId, + channelAccessToken: context.account.channelAccessToken, + }); + } catch (err) { + logVerbose(`line pairing reply failed for ${senderId}: ${String(err)}`); + } +} + +async function shouldProcessLineEvent( + event: MessageEvent | PostbackEvent, + context: LineHandlerContext, +): Promise { + const { cfg, account } = context; + const { userId, groupId, roomId, isGroup } = getSourceInfo(event.source); + const senderId = userId ?? ""; + + const storeAllowFrom = await readChannelAllowFromStore("line").catch(() => []); + const effectiveDmAllow = normalizeAllowFromWithStore({ + allowFrom: account.config.allowFrom, + storeAllowFrom, + }); + const groupConfig = resolveLineGroupConfig({ config: account.config, groupId, roomId }); + const groupAllowOverride = groupConfig?.allowFrom; + const fallbackGroupAllowFrom = account.config.allowFrom?.length + ? account.config.allowFrom + : undefined; + const groupAllowFrom = firstDefined( + groupAllowOverride, + account.config.groupAllowFrom, + fallbackGroupAllowFrom, + ); + const effectiveGroupAllow = normalizeAllowFromWithStore({ + allowFrom: groupAllowFrom, + storeAllowFrom, + }); + const dmPolicy = account.config.dmPolicy ?? "pairing"; + const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const groupPolicy = account.config.groupPolicy ?? defaultGroupPolicy ?? "allowlist"; + + if (isGroup) { + if (groupConfig?.enabled === false) { + logVerbose(`Blocked line group ${groupId ?? roomId ?? "unknown"} (group disabled)`); + return false; + } + if (typeof groupAllowOverride !== "undefined") { + if (!senderId) { + logVerbose("Blocked line group message (group allowFrom override, no sender ID)"); + return false; + } + if (!isSenderAllowed({ allow: effectiveGroupAllow, senderId })) { + logVerbose(`Blocked line group sender ${senderId} (group allowFrom override)`); + return false; + } + } + if (groupPolicy === "disabled") { + logVerbose("Blocked line group message (groupPolicy: disabled)"); + return false; + } + if (groupPolicy === "allowlist") { + if (!senderId) { + logVerbose("Blocked line group message (no sender ID, groupPolicy: allowlist)"); + return false; + } + if (!effectiveGroupAllow.hasEntries) { + logVerbose("Blocked line group message (groupPolicy: allowlist, no groupAllowFrom)"); + return false; + } + if (!isSenderAllowed({ allow: effectiveGroupAllow, senderId })) { + logVerbose(`Blocked line group message from ${senderId} (groupPolicy: allowlist)`); + return false; + } + } + return true; + } + + if (dmPolicy === "disabled") { + logVerbose("Blocked line sender (dmPolicy: disabled)"); + return false; + } + + const dmAllowed = dmPolicy === "open" || isSenderAllowed({ allow: effectiveDmAllow, senderId }); + if (!dmAllowed) { + if (dmPolicy === "pairing") { + if (!senderId) { + logVerbose("Blocked line sender (dmPolicy: pairing, no sender ID)"); + return false; + } + await sendLinePairingReply({ + senderId, + replyToken: "replyToken" in event ? event.replyToken : undefined, + context, + }); + } else { + logVerbose(`Blocked line sender ${senderId || "unknown"} (dmPolicy: ${dmPolicy})`); + } + return false; + } + + return true; +} + +async function handleMessageEvent(event: MessageEvent, context: LineHandlerContext): Promise { + const { cfg, account, runtime, mediaMaxBytes, processMessage } = context; + const message = event.message; + + if (!(await shouldProcessLineEvent(event, context))) return; + + // Download media if applicable + const allMedia: MediaRef[] = []; + + if (message.type === "image" || message.type === "video" || message.type === "audio") { + try { + const media = await downloadLineMedia(message.id, account.channelAccessToken, mediaMaxBytes); + allMedia.push({ + path: media.path, + contentType: media.contentType, + }); + } catch (err) { + const errMsg = String(err); + if (errMsg.includes("exceeds") && errMsg.includes("limit")) { + logVerbose(`line: media exceeds size limit for message ${message.id}`); + // Continue without media + } else { + runtime.error?.(danger(`line: failed to download media: ${errMsg}`)); + } + } + } + + const messageContext = await buildLineMessageContext({ + event, + allMedia, + cfg, + account, + }); + + if (!messageContext) { + logVerbose("line: skipping empty message"); + return; + } + + await processMessage(messageContext); +} + +async function handleFollowEvent(event: FollowEvent, _context: LineHandlerContext): Promise { + const userId = event.source.type === "user" ? event.source.userId : undefined; + logVerbose(`line: user ${userId ?? "unknown"} followed`); + // Could implement welcome message here +} + +async function handleUnfollowEvent( + event: UnfollowEvent, + _context: LineHandlerContext, +): Promise { + const userId = event.source.type === "user" ? event.source.userId : undefined; + logVerbose(`line: user ${userId ?? "unknown"} unfollowed`); +} + +async function handleJoinEvent(event: JoinEvent, _context: LineHandlerContext): Promise { + const groupId = event.source.type === "group" ? event.source.groupId : undefined; + const roomId = event.source.type === "room" ? event.source.roomId : undefined; + logVerbose(`line: bot joined ${groupId ? `group ${groupId}` : `room ${roomId}`}`); +} + +async function handleLeaveEvent(event: LeaveEvent, _context: LineHandlerContext): Promise { + const groupId = event.source.type === "group" ? event.source.groupId : undefined; + const roomId = event.source.type === "room" ? event.source.roomId : undefined; + logVerbose(`line: bot left ${groupId ? `group ${groupId}` : `room ${roomId}`}`); +} + +async function handlePostbackEvent( + event: PostbackEvent, + context: LineHandlerContext, +): Promise { + const data = event.postback.data; + logVerbose(`line: received postback: ${data}`); + + if (!(await shouldProcessLineEvent(event, context))) return; + + const postbackContext = await buildLinePostbackContext({ + event, + cfg: context.cfg, + account: context.account, + }); + if (!postbackContext) return; + + await context.processMessage(postbackContext); +} + +export async function handleLineWebhookEvents( + events: WebhookEvent[], + context: LineHandlerContext, +): Promise { + for (const event of events) { + try { + switch (event.type) { + case "message": + await handleMessageEvent(event, context); + break; + case "follow": + await handleFollowEvent(event, context); + break; + case "unfollow": + await handleUnfollowEvent(event, context); + break; + case "join": + await handleJoinEvent(event, context); + break; + case "leave": + await handleLeaveEvent(event, context); + break; + case "postback": + await handlePostbackEvent(event, context); + break; + default: + logVerbose(`line: unhandled event type: ${(event as WebhookEvent).type}`); + } + } catch (err) { + context.runtime.error?.(danger(`line: event handler failed: ${String(err)}`)); + } + } +} diff --git a/src/line/bot-message-context.test.ts b/src/line/bot-message-context.test.ts new file mode 100644 index 00000000000..357588263c6 --- /dev/null +++ b/src/line/bot-message-context.test.ts @@ -0,0 +1,82 @@ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import type { MessageEvent, PostbackEvent } from "@line/bot-sdk"; +import type { ClawdbotConfig } from "../config/config.js"; +import type { ResolvedLineAccount } from "./types.js"; +import { buildLineMessageContext, buildLinePostbackContext } from "./bot-message-context.js"; + +describe("buildLineMessageContext", () => { + let tmpDir: string; + let storePath: string; + let cfg: ClawdbotConfig; + const account: ResolvedLineAccount = { + accountId: "default", + enabled: true, + channelAccessToken: "token", + channelSecret: "secret", + tokenSource: "config", + config: {}, + }; + + beforeEach(async () => { + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-line-context-")); + storePath = path.join(tmpDir, "sessions.json"); + cfg = { session: { store: storePath } }; + }); + + afterEach(async () => { + await fs.rm(tmpDir, { + recursive: true, + force: true, + maxRetries: 3, + retryDelay: 50, + }); + }); + + it("routes group message replies to the group id", async () => { + const event = { + type: "message", + message: { id: "1", type: "text", text: "hello" }, + replyToken: "reply-token", + timestamp: Date.now(), + source: { type: "group", groupId: "group-1", userId: "user-1" }, + mode: "active", + webhookEventId: "evt-1", + deliveryContext: { isRedelivery: false }, + } as MessageEvent; + + const context = await buildLineMessageContext({ + event, + allMedia: [], + cfg, + account, + }); + + expect(context.ctxPayload.OriginatingTo).toBe("line:group:group-1"); + expect(context.ctxPayload.To).toBe("line:group:group-1"); + }); + + it("routes group postback replies to the group id", async () => { + const event = { + type: "postback", + postback: { data: "action=select" }, + replyToken: "reply-token", + timestamp: Date.now(), + source: { type: "group", groupId: "group-2", userId: "user-2" }, + mode: "active", + webhookEventId: "evt-2", + deliveryContext: { isRedelivery: false }, + } as PostbackEvent; + + const context = await buildLinePostbackContext({ + event, + cfg, + account, + }); + + expect(context?.ctxPayload.OriginatingTo).toBe("line:group:group-2"); + expect(context?.ctxPayload.To).toBe("line:group:group-2"); + }); +}); diff --git a/src/line/bot-message-context.ts b/src/line/bot-message-context.ts new file mode 100644 index 00000000000..2fa5089824c --- /dev/null +++ b/src/line/bot-message-context.ts @@ -0,0 +1,465 @@ +import type { + MessageEvent, + TextEventMessage, + StickerEventMessage, + LocationEventMessage, + EventSource, + PostbackEvent, +} from "@line/bot-sdk"; +import { formatInboundEnvelope, resolveEnvelopeFormatOptions } from "../auto-reply/envelope.js"; +import { finalizeInboundContext } from "../auto-reply/reply/inbound-context.js"; +import { formatLocationText, toLocationContext } from "../channels/location.js"; +import type { ClawdbotConfig } from "../config/config.js"; +import { + readSessionUpdatedAt, + recordSessionMetaFromInbound, + resolveStorePath, + updateLastRoute, +} from "../config/sessions.js"; +import { logVerbose, shouldLogVerbose } from "../globals.js"; +import { recordChannelActivity } from "../infra/channel-activity.js"; +import { resolveAgentRoute } from "../routing/resolve-route.js"; +import type { ResolvedLineAccount } from "./types.js"; + +interface MediaRef { + path: string; + contentType?: string; +} + +interface BuildLineMessageContextParams { + event: MessageEvent; + allMedia: MediaRef[]; + cfg: ClawdbotConfig; + account: ResolvedLineAccount; +} + +function getSourceInfo(source: EventSource): { + userId?: string; + groupId?: string; + roomId?: string; + isGroup: boolean; +} { + const userId = + source.type === "user" + ? source.userId + : source.type === "group" + ? source.userId + : source.type === "room" + ? source.userId + : undefined; + const groupId = source.type === "group" ? source.groupId : undefined; + const roomId = source.type === "room" ? source.roomId : undefined; + const isGroup = source.type === "group" || source.type === "room"; + + return { userId, groupId, roomId, isGroup }; +} + +function buildPeerId(source: EventSource): string { + if (source.type === "group" && source.groupId) { + return `group:${source.groupId}`; + } + if (source.type === "room" && source.roomId) { + return `room:${source.roomId}`; + } + if (source.type === "user" && source.userId) { + return source.userId; + } + return "unknown"; +} + +// Common LINE sticker package descriptions +const STICKER_PACKAGES: Record = { + "1": "Moon & James", + "2": "Cony & Brown", + "3": "Brown & Friends", + "4": "Moon Special", + "11537": "Cony", + "11538": "Brown", + "11539": "Moon", + "6136": "Cony's Happy Life", + "6325": "Brown's Life", + "6359": "Choco", + "6362": "Sally", + "6370": "Edward", + "789": "LINE Characters", +}; + +function describeStickerKeywords(sticker: StickerEventMessage): string { + // Use sticker keywords if available (LINE provides these for some stickers) + const keywords = (sticker as StickerEventMessage & { keywords?: string[] }).keywords; + if (keywords && keywords.length > 0) { + return keywords.slice(0, 3).join(", "); + } + + // Use sticker text if available + const stickerText = (sticker as StickerEventMessage & { text?: string }).text; + if (stickerText) { + return stickerText; + } + + return ""; +} + +function extractMessageText(message: MessageEvent["message"]): string { + if (message.type === "text") { + return (message as TextEventMessage).text; + } + if (message.type === "location") { + const loc = message as LocationEventMessage; + return ( + formatLocationText({ + latitude: loc.latitude, + longitude: loc.longitude, + name: loc.title, + address: loc.address, + }) ?? "" + ); + } + if (message.type === "sticker") { + const sticker = message as StickerEventMessage; + const packageName = STICKER_PACKAGES[sticker.packageId] ?? "sticker"; + const keywords = describeStickerKeywords(sticker); + + if (keywords) { + return `[Sent a ${packageName} sticker: ${keywords}]`; + } + return `[Sent a ${packageName} sticker]`; + } + return ""; +} + +function extractMediaPlaceholder(message: MessageEvent["message"]): string { + switch (message.type) { + case "image": + return ""; + case "video": + return ""; + case "audio": + return ""; + case "file": + return ""; + default: + return ""; + } +} + +export async function buildLineMessageContext(params: BuildLineMessageContextParams) { + const { event, allMedia, cfg, account } = params; + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "inbound", + }); + + const source = event.source; + const { userId, groupId, roomId, isGroup } = getSourceInfo(source); + const peerId = buildPeerId(source); + + const route = resolveAgentRoute({ + cfg, + channel: "line", + accountId: account.accountId, + peer: { + kind: isGroup ? "group" : "dm", + id: peerId, + }, + }); + + const message = event.message; + const messageId = message.id; + const timestamp = event.timestamp; + + // Build message body + const textContent = extractMessageText(message); + const placeholder = extractMediaPlaceholder(message); + + let rawBody = textContent || placeholder; + if (!rawBody && allMedia.length > 0) { + rawBody = `${allMedia.length > 1 ? ` (${allMedia.length} images)` : ""}`; + } + + if (!rawBody && allMedia.length === 0) { + return null; + } + + // Build sender info + const senderId = userId ?? "unknown"; + const senderLabel = userId ? `user:${userId}` : "unknown"; + + // Build conversation label + const conversationLabel = isGroup + ? groupId + ? `group:${groupId}` + : roomId + ? `room:${roomId}` + : "unknown-group" + : senderLabel; + + const storePath = resolveStorePath(cfg.session?.store, { + agentId: route.agentId, + }); + + const envelopeOptions = resolveEnvelopeFormatOptions(cfg); + const previousTimestamp = readSessionUpdatedAt({ + storePath, + sessionKey: route.sessionKey, + }); + + const body = formatInboundEnvelope({ + channel: "LINE", + from: conversationLabel, + timestamp, + body: rawBody, + chatType: isGroup ? "group" : "direct", + sender: { + id: senderId, + }, + previousTimestamp, + envelope: envelopeOptions, + }); + + // Build location context if applicable + let locationContext: ReturnType | undefined; + if (message.type === "location") { + const loc = message as LocationEventMessage; + locationContext = toLocationContext({ + latitude: loc.latitude, + longitude: loc.longitude, + name: loc.title, + address: loc.address, + }); + } + + const fromAddress = isGroup + ? groupId + ? `line:group:${groupId}` + : roomId + ? `line:room:${roomId}` + : `line:${peerId}` + : `line:${userId ?? peerId}`; + const toAddress = isGroup ? fromAddress : `line:${userId ?? peerId}`; + const originatingTo = isGroup ? fromAddress : `line:${userId ?? peerId}`; + + const ctxPayload = finalizeInboundContext({ + Body: body, + RawBody: rawBody, + CommandBody: rawBody, + From: fromAddress, + To: toAddress, + SessionKey: route.sessionKey, + AccountId: route.accountId, + ChatType: isGroup ? "group" : "direct", + ConversationLabel: conversationLabel, + GroupSubject: isGroup ? (groupId ?? roomId) : undefined, + SenderId: senderId, + Provider: "line", + Surface: "line", + MessageSid: messageId, + Timestamp: timestamp, + MediaPath: allMedia[0]?.path, + MediaType: allMedia[0]?.contentType, + MediaUrl: allMedia[0]?.path, + MediaPaths: allMedia.length > 0 ? allMedia.map((m) => m.path) : undefined, + MediaUrls: allMedia.length > 0 ? allMedia.map((m) => m.path) : undefined, + MediaTypes: + allMedia.length > 0 + ? (allMedia.map((m) => m.contentType).filter(Boolean) as string[]) + : undefined, + ...locationContext, + OriginatingChannel: "line" as const, + OriginatingTo: originatingTo, + }); + + void recordSessionMetaFromInbound({ + storePath, + sessionKey: ctxPayload.SessionKey ?? route.sessionKey, + ctx: ctxPayload, + }).catch((err) => { + logVerbose(`line: failed updating session meta: ${String(err)}`); + }); + + if (!isGroup) { + await updateLastRoute({ + storePath, + sessionKey: route.mainSessionKey, + deliveryContext: { + channel: "line", + to: userId ?? peerId, + accountId: route.accountId, + }, + ctx: ctxPayload, + }); + } + + if (shouldLogVerbose()) { + const preview = body.slice(0, 200).replace(/\n/g, "\\n"); + const mediaInfo = allMedia.length > 1 ? ` mediaCount=${allMedia.length}` : ""; + logVerbose( + `line inbound: from=${ctxPayload.From} len=${body.length}${mediaInfo} preview="${preview}"`, + ); + } + + return { + ctxPayload, + event, + userId, + groupId, + roomId, + isGroup, + route, + replyToken: event.replyToken, + accountId: account.accountId, + }; +} + +export async function buildLinePostbackContext(params: { + event: PostbackEvent; + cfg: ClawdbotConfig; + account: ResolvedLineAccount; +}) { + const { event, cfg, account } = params; + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "inbound", + }); + + const source = event.source; + const { userId, groupId, roomId, isGroup } = getSourceInfo(source); + const peerId = buildPeerId(source); + + const route = resolveAgentRoute({ + cfg, + channel: "line", + accountId: account.accountId, + peer: { + kind: isGroup ? "group" : "dm", + id: peerId, + }, + }); + + const timestamp = event.timestamp; + const rawData = event.postback?.data?.trim() ?? ""; + if (!rawData) return null; + let rawBody = rawData; + if (rawData.includes("line.action=")) { + const params = new URLSearchParams(rawData); + const action = params.get("line.action") ?? ""; + const device = params.get("line.device"); + rawBody = device ? `line action ${action} device ${device}` : `line action ${action}`; + } + + const senderId = userId ?? "unknown"; + const senderLabel = userId ? `user:${userId}` : "unknown"; + + const conversationLabel = isGroup + ? groupId + ? `group:${groupId}` + : roomId + ? `room:${roomId}` + : "unknown-group" + : senderLabel; + + const storePath = resolveStorePath(cfg.session?.store, { + agentId: route.agentId, + }); + + const envelopeOptions = resolveEnvelopeFormatOptions(cfg); + const previousTimestamp = readSessionUpdatedAt({ + storePath, + sessionKey: route.sessionKey, + }); + + const body = formatInboundEnvelope({ + channel: "LINE", + from: conversationLabel, + timestamp, + body: rawBody, + chatType: isGroup ? "group" : "direct", + sender: { + id: senderId, + }, + previousTimestamp, + envelope: envelopeOptions, + }); + + const fromAddress = isGroup + ? groupId + ? `line:group:${groupId}` + : roomId + ? `line:room:${roomId}` + : `line:${peerId}` + : `line:${userId ?? peerId}`; + const toAddress = isGroup ? fromAddress : `line:${userId ?? peerId}`; + const originatingTo = isGroup ? fromAddress : `line:${userId ?? peerId}`; + + const ctxPayload = finalizeInboundContext({ + Body: body, + RawBody: rawBody, + CommandBody: rawBody, + From: fromAddress, + To: toAddress, + SessionKey: route.sessionKey, + AccountId: route.accountId, + ChatType: isGroup ? "group" : "direct", + ConversationLabel: conversationLabel, + GroupSubject: isGroup ? (groupId ?? roomId) : undefined, + SenderId: senderId, + Provider: "line", + Surface: "line", + MessageSid: event.replyToken ? `postback:${event.replyToken}` : `postback:${timestamp}`, + Timestamp: timestamp, + MediaPath: "", + MediaType: undefined, + MediaUrl: "", + MediaPaths: undefined, + MediaUrls: undefined, + MediaTypes: undefined, + OriginatingChannel: "line" as const, + OriginatingTo: originatingTo, + }); + + void recordSessionMetaFromInbound({ + storePath, + sessionKey: ctxPayload.SessionKey ?? route.sessionKey, + ctx: ctxPayload, + }).catch((err) => { + logVerbose(`line: failed updating session meta: ${String(err)}`); + }); + + if (!isGroup) { + await updateLastRoute({ + storePath, + sessionKey: route.mainSessionKey, + deliveryContext: { + channel: "line", + to: userId ?? peerId, + accountId: route.accountId, + }, + ctx: ctxPayload, + }); + } + + if (shouldLogVerbose()) { + const preview = body.slice(0, 200).replace(/\n/g, "\\n"); + logVerbose(`line postback: from=${ctxPayload.From} len=${body.length} preview="${preview}"`); + } + + return { + ctxPayload, + event, + userId, + groupId, + roomId, + isGroup, + route, + replyToken: event.replyToken, + accountId: account.accountId, + }; +} + +export type LineMessageContext = NonNullable>>; +export type LinePostbackContext = NonNullable>>; +export type LineInboundContext = LineMessageContext | LinePostbackContext; diff --git a/src/line/bot.ts b/src/line/bot.ts new file mode 100644 index 00000000000..c963d2435b1 --- /dev/null +++ b/src/line/bot.ts @@ -0,0 +1,82 @@ +import type { WebhookRequestBody } from "@line/bot-sdk"; +import type { ClawdbotConfig } from "../config/config.js"; +import { loadConfig } from "../config/config.js"; +import { logVerbose } from "../globals.js"; +import type { RuntimeEnv } from "../runtime.js"; +import { resolveLineAccount } from "./accounts.js"; +import { handleLineWebhookEvents } from "./bot-handlers.js"; +import type { LineInboundContext } from "./bot-message-context.js"; +import { startLineWebhook } from "./webhook.js"; +import type { ResolvedLineAccount } from "./types.js"; + +export interface LineBotOptions { + channelAccessToken: string; + channelSecret: string; + accountId?: string; + runtime?: RuntimeEnv; + config?: ClawdbotConfig; + mediaMaxMb?: number; + onMessage?: (ctx: LineInboundContext) => Promise; +} + +export interface LineBot { + handleWebhook: (body: WebhookRequestBody) => Promise; + account: ResolvedLineAccount; +} + +export function createLineBot(opts: LineBotOptions): LineBot { + const runtime: RuntimeEnv = opts.runtime ?? { + log: console.log, + error: console.error, + exit: (code: number): never => { + throw new Error(`exit ${code}`); + }, + }; + + const cfg = opts.config ?? loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + + const mediaMaxBytes = (opts.mediaMaxMb ?? account.config.mediaMaxMb ?? 10) * 1024 * 1024; + + const processMessage = + opts.onMessage ?? + (async () => { + logVerbose("line: no message handler configured"); + }); + + const handleWebhook = async (body: WebhookRequestBody): Promise => { + if (!body.events || body.events.length === 0) { + return; + } + + await handleLineWebhookEvents(body.events, { + cfg, + account, + runtime, + mediaMaxBytes, + processMessage, + }); + }; + + return { + handleWebhook, + account, + }; +} + +export function createLineWebhookCallback( + bot: LineBot, + channelSecret: string, + path = "/line/webhook", +) { + const { handler } = startLineWebhook({ + channelSecret, + onEvents: bot.handleWebhook, + path, + }); + + return { path, handler }; +} diff --git a/src/line/config-schema.ts b/src/line/config-schema.ts new file mode 100644 index 00000000000..7e7a2be0395 --- /dev/null +++ b/src/line/config-schema.ts @@ -0,0 +1,53 @@ +import { z } from "zod"; + +const DmPolicySchema = z.enum(["open", "allowlist", "pairing", "disabled"]); +const GroupPolicySchema = z.enum(["open", "allowlist", "disabled"]); + +const LineGroupConfigSchema = z + .object({ + enabled: z.boolean().optional(), + allowFrom: z.array(z.union([z.string(), z.number()])).optional(), + requireMention: z.boolean().optional(), + systemPrompt: z.string().optional(), + skills: z.array(z.string()).optional(), + }) + .strict(); + +const LineAccountConfigSchema = z + .object({ + enabled: z.boolean().optional(), + channelAccessToken: z.string().optional(), + channelSecret: z.string().optional(), + tokenFile: z.string().optional(), + secretFile: z.string().optional(), + name: z.string().optional(), + allowFrom: z.array(z.union([z.string(), z.number()])).optional(), + groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(), + dmPolicy: DmPolicySchema.optional().default("pairing"), + groupPolicy: GroupPolicySchema.optional().default("allowlist"), + mediaMaxMb: z.number().optional(), + webhookPath: z.string().optional(), + groups: z.record(z.string(), LineGroupConfigSchema.optional()).optional(), + }) + .strict(); + +export const LineConfigSchema = z + .object({ + enabled: z.boolean().optional(), + channelAccessToken: z.string().optional(), + channelSecret: z.string().optional(), + tokenFile: z.string().optional(), + secretFile: z.string().optional(), + name: z.string().optional(), + allowFrom: z.array(z.union([z.string(), z.number()])).optional(), + groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(), + dmPolicy: DmPolicySchema.optional().default("pairing"), + groupPolicy: GroupPolicySchema.optional().default("allowlist"), + mediaMaxMb: z.number().optional(), + webhookPath: z.string().optional(), + accounts: z.record(z.string(), LineAccountConfigSchema.optional()).optional(), + groups: z.record(z.string(), LineGroupConfigSchema.optional()).optional(), + }) + .strict(); + +export type LineConfigSchemaType = z.infer; diff --git a/src/line/download.ts b/src/line/download.ts new file mode 100644 index 00000000000..e48cb0e7198 --- /dev/null +++ b/src/line/download.ts @@ -0,0 +1,120 @@ +import fs from "node:fs"; +import path from "node:path"; +import os from "node:os"; +import { messagingApi } from "@line/bot-sdk"; +import { logVerbose } from "../globals.js"; + +interface DownloadResult { + path: string; + contentType?: string; + size: number; +} + +export async function downloadLineMedia( + messageId: string, + channelAccessToken: string, + maxBytes = 10 * 1024 * 1024, +): Promise { + const client = new messagingApi.MessagingApiBlobClient({ + channelAccessToken, + }); + + const response = await client.getMessageContent(messageId); + + // response is a Readable stream + const chunks: Buffer[] = []; + let totalSize = 0; + + for await (const chunk of response as AsyncIterable) { + totalSize += chunk.length; + if (totalSize > maxBytes) { + throw new Error(`Media exceeds ${Math.round(maxBytes / (1024 * 1024))}MB limit`); + } + chunks.push(chunk); + } + + const buffer = Buffer.concat(chunks); + + // Determine content type from magic bytes + const contentType = detectContentType(buffer); + const ext = getExtensionForContentType(contentType); + + // Write to temp file + const tempDir = os.tmpdir(); + const fileName = `line-media-${messageId}-${Date.now()}${ext}`; + const filePath = path.join(tempDir, fileName); + + await fs.promises.writeFile(filePath, buffer); + + logVerbose(`line: downloaded media ${messageId} to ${filePath} (${buffer.length} bytes)`); + + return { + path: filePath, + contentType, + size: buffer.length, + }; +} + +function detectContentType(buffer: Buffer): string { + // Check magic bytes + if (buffer.length >= 2) { + // JPEG + if (buffer[0] === 0xff && buffer[1] === 0xd8) { + return "image/jpeg"; + } + // PNG + if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) { + return "image/png"; + } + // GIF + if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) { + return "image/gif"; + } + // WebP + if ( + buffer[0] === 0x52 && + buffer[1] === 0x49 && + buffer[2] === 0x46 && + buffer[3] === 0x46 && + buffer[8] === 0x57 && + buffer[9] === 0x45 && + buffer[10] === 0x42 && + buffer[11] === 0x50 + ) { + return "image/webp"; + } + // MP4 + if (buffer[4] === 0x66 && buffer[5] === 0x74 && buffer[6] === 0x79 && buffer[7] === 0x70) { + return "video/mp4"; + } + // M4A/AAC + if (buffer[0] === 0x00 && buffer[1] === 0x00 && buffer[2] === 0x00) { + if (buffer[4] === 0x66 && buffer[5] === 0x74 && buffer[6] === 0x79 && buffer[7] === 0x70) { + return "audio/mp4"; + } + } + } + + return "application/octet-stream"; +} + +function getExtensionForContentType(contentType: string): string { + switch (contentType) { + case "image/jpeg": + return ".jpg"; + case "image/png": + return ".png"; + case "image/gif": + return ".gif"; + case "image/webp": + return ".webp"; + case "video/mp4": + return ".mp4"; + case "audio/mp4": + return ".m4a"; + case "audio/mpeg": + return ".mp3"; + default: + return ".bin"; + } +} diff --git a/src/line/flex-templates.test.ts b/src/line/flex-templates.test.ts new file mode 100644 index 00000000000..cfaa2297e22 --- /dev/null +++ b/src/line/flex-templates.test.ts @@ -0,0 +1,499 @@ +import { describe, expect, it } from "vitest"; +import { + createInfoCard, + createListCard, + createImageCard, + createActionCard, + createCarousel, + createNotificationBubble, + createReceiptCard, + createEventCard, + createAgendaCard, + createMediaPlayerCard, + createAppleTvRemoteCard, + createDeviceControlCard, + toFlexMessage, +} from "./flex-templates.js"; + +describe("createInfoCard", () => { + it("creates a bubble with title and body", () => { + const card = createInfoCard("Test Title", "Test body content"); + + expect(card.type).toBe("bubble"); + expect(card.size).toBe("mega"); + expect(card.body).toBeDefined(); + expect(card.body?.type).toBe("box"); + }); + + it("includes footer when provided", () => { + const card = createInfoCard("Title", "Body", "Footer text"); + + expect(card.footer).toBeDefined(); + const footer = card.footer as { contents: Array<{ text: string }> }; + expect(footer.contents[0].text).toBe("Footer text"); + }); + + it("omits footer when not provided", () => { + const card = createInfoCard("Title", "Body"); + expect(card.footer).toBeUndefined(); + }); +}); + +describe("createListCard", () => { + it("creates a list with title and items", () => { + const items = [{ title: "Item 1", subtitle: "Description 1" }, { title: "Item 2" }]; + const card = createListCard("My List", items); + + expect(card.type).toBe("bubble"); + expect(card.body).toBeDefined(); + }); + + it("limits items to 8", () => { + const items = Array.from({ length: 15 }, (_, i) => ({ title: `Item ${i}` })); + const card = createListCard("List", items); + + const body = card.body as { contents: Array<{ type: string; contents?: unknown[] }> }; + // The list items are in the third content (after title and separator) + const listBox = body.contents[2] as { contents: unknown[] }; + expect(listBox.contents.length).toBe(8); + }); + + it("includes actions on items when provided", () => { + const items = [ + { + title: "Clickable", + action: { type: "message" as const, label: "Click", text: "clicked" }, + }, + ]; + const card = createListCard("List", items); + expect(card.body).toBeDefined(); + }); +}); + +describe("createImageCard", () => { + it("creates a card with hero image", () => { + const card = createImageCard("https://example.com/image.jpg", "Image Title"); + + expect(card.type).toBe("bubble"); + expect(card.hero).toBeDefined(); + expect((card.hero as { url: string }).url).toBe("https://example.com/image.jpg"); + }); + + it("includes body text when provided", () => { + const card = createImageCard("https://example.com/img.jpg", "Title", "Body text"); + + const body = card.body as { contents: Array<{ text: string }> }; + expect(body.contents.length).toBe(2); + expect(body.contents[1].text).toBe("Body text"); + }); + + it("applies custom aspect ratio", () => { + const card = createImageCard("https://example.com/img.jpg", "Title", undefined, { + aspectRatio: "16:9", + }); + + expect((card.hero as { aspectRatio: string }).aspectRatio).toBe("16:9"); + }); +}); + +describe("createActionCard", () => { + it("creates a card with action buttons", () => { + const actions = [ + { label: "Action 1", action: { type: "message" as const, label: "Act1", text: "action1" } }, + { + label: "Action 2", + action: { type: "uri" as const, label: "Act2", uri: "https://example.com" }, + }, + ]; + const card = createActionCard("Title", "Description", actions); + + expect(card.type).toBe("bubble"); + expect(card.footer).toBeDefined(); + + const footer = card.footer as { contents: Array<{ type: string }> }; + expect(footer.contents.length).toBe(2); + }); + + it("limits actions to 4", () => { + const actions = Array.from({ length: 6 }, (_, i) => ({ + label: `Action ${i}`, + action: { type: "message" as const, label: `A${i}`, text: `action${i}` }, + })); + const card = createActionCard("Title", "Body", actions); + + const footer = card.footer as { contents: unknown[] }; + expect(footer.contents.length).toBe(4); + }); + + it("includes hero image when provided", () => { + const card = createActionCard("Title", "Body", [], { + imageUrl: "https://example.com/hero.jpg", + }); + + expect(card.hero).toBeDefined(); + expect((card.hero as { url: string }).url).toBe("https://example.com/hero.jpg"); + }); +}); + +describe("createCarousel", () => { + it("creates a carousel from bubbles", () => { + const bubbles = [createInfoCard("Card 1", "Body 1"), createInfoCard("Card 2", "Body 2")]; + const carousel = createCarousel(bubbles); + + expect(carousel.type).toBe("carousel"); + expect(carousel.contents.length).toBe(2); + }); + + it("limits to 12 bubbles", () => { + const bubbles = Array.from({ length: 15 }, (_, i) => createInfoCard(`Card ${i}`, `Body ${i}`)); + const carousel = createCarousel(bubbles); + + expect(carousel.contents.length).toBe(12); + }); +}); + +describe("createNotificationBubble", () => { + it("creates a simple notification", () => { + const bubble = createNotificationBubble("Hello world"); + + expect(bubble.type).toBe("bubble"); + expect(bubble.body).toBeDefined(); + }); + + it("applies notification type styling", () => { + const successBubble = createNotificationBubble("Success!", { type: "success" }); + const errorBubble = createNotificationBubble("Error!", { type: "error" }); + + expect(successBubble.body).toBeDefined(); + expect(errorBubble.body).toBeDefined(); + }); + + it("includes title when provided", () => { + const bubble = createNotificationBubble("Details here", { + title: "Alert Title", + }); + + expect(bubble.body).toBeDefined(); + }); +}); + +describe("createReceiptCard", () => { + it("creates a receipt with items", () => { + const card = createReceiptCard({ + title: "Order Receipt", + items: [ + { name: "Item A", value: "$10" }, + { name: "Item B", value: "$20" }, + ], + }); + + expect(card.type).toBe("bubble"); + expect(card.body).toBeDefined(); + }); + + it("includes total when provided", () => { + const card = createReceiptCard({ + title: "Receipt", + items: [{ name: "Item", value: "$10" }], + total: { label: "Total", value: "$10" }, + }); + + expect(card.body).toBeDefined(); + }); + + it("includes footer when provided", () => { + const card = createReceiptCard({ + title: "Receipt", + items: [{ name: "Item", value: "$10" }], + footer: "Thank you!", + }); + + expect(card.footer).toBeDefined(); + }); +}); + +describe("createMediaPlayerCard", () => { + it("creates a basic player card", () => { + const card = createMediaPlayerCard({ + title: "Bohemian Rhapsody", + subtitle: "Queen", + }); + + expect(card.type).toBe("bubble"); + expect(card.body).toBeDefined(); + }); + + it("includes album art when provided", () => { + const card = createMediaPlayerCard({ + title: "Track Name", + imageUrl: "https://example.com/album.jpg", + }); + + expect(card.hero).toBeDefined(); + expect((card.hero as { url: string }).url).toBe("https://example.com/album.jpg"); + }); + + it("shows playing status", () => { + const card = createMediaPlayerCard({ + title: "Track", + isPlaying: true, + }); + + expect(card.body).toBeDefined(); + }); + + it("includes playback controls", () => { + const card = createMediaPlayerCard({ + title: "Track", + controls: { + previous: { data: "action=prev" }, + play: { data: "action=play" }, + pause: { data: "action=pause" }, + next: { data: "action=next" }, + }, + }); + + expect(card.footer).toBeDefined(); + }); + + it("includes extra actions", () => { + const card = createMediaPlayerCard({ + title: "Track", + extraActions: [ + { label: "Add to Playlist", data: "action=add_playlist" }, + { label: "Share", data: "action=share" }, + ], + }); + + expect(card.footer).toBeDefined(); + }); +}); + +describe("createDeviceControlCard", () => { + it("creates a device card with controls", () => { + const card = createDeviceControlCard({ + deviceName: "Apple TV", + deviceType: "Streaming Box", + controls: [ + { label: "Play/Pause", data: "action=playpause" }, + { label: "Menu", data: "action=menu" }, + ], + }); + + expect(card.type).toBe("bubble"); + expect(card.body).toBeDefined(); + expect(card.footer).toBeDefined(); + }); + + it("shows device status", () => { + const card = createDeviceControlCard({ + deviceName: "Apple TV", + status: "Playing", + controls: [{ label: "Pause", data: "action=pause" }], + }); + + expect(card.body).toBeDefined(); + }); + + it("includes device image", () => { + const card = createDeviceControlCard({ + deviceName: "Device", + imageUrl: "https://example.com/device.jpg", + controls: [], + }); + + expect(card.hero).toBeDefined(); + }); + + it("limits controls to 6", () => { + const card = createDeviceControlCard({ + deviceName: "Device", + controls: Array.from({ length: 10 }, (_, i) => ({ + label: `Control ${i}`, + data: `action=${i}`, + })), + }); + + expect(card.footer).toBeDefined(); + // Should have max 3 rows of 2 buttons + const footer = card.footer as { contents: unknown[] }; + expect(footer.contents.length).toBeLessThanOrEqual(3); + }); +}); + +describe("createAppleTvRemoteCard", () => { + it("creates an Apple TV remote card with controls", () => { + const card = createAppleTvRemoteCard({ + deviceName: "Apple TV", + status: "Playing", + actionData: { + up: "action=up", + down: "action=down", + left: "action=left", + right: "action=right", + select: "action=select", + menu: "action=menu", + home: "action=home", + play: "action=play", + pause: "action=pause", + volumeUp: "action=volume_up", + volumeDown: "action=volume_down", + mute: "action=mute", + }, + }); + + expect(card.type).toBe("bubble"); + expect(card.body).toBeDefined(); + }); +}); + +describe("createEventCard", () => { + it("creates an event card with required fields", () => { + const card = createEventCard({ + title: "Team Meeting", + date: "January 24, 2026", + }); + + expect(card.type).toBe("bubble"); + expect(card.body).toBeDefined(); + }); + + it("includes time when provided", () => { + const card = createEventCard({ + title: "Meeting", + date: "Jan 24", + time: "2:00 PM - 3:00 PM", + }); + + expect(card.body).toBeDefined(); + }); + + it("includes location when provided", () => { + const card = createEventCard({ + title: "Meeting", + date: "Jan 24", + location: "Conference Room A", + }); + + expect(card.body).toBeDefined(); + }); + + it("includes description when provided", () => { + const card = createEventCard({ + title: "Meeting", + date: "Jan 24", + description: "Discuss Q1 roadmap", + }); + + expect(card.body).toBeDefined(); + }); + + it("includes all optional fields together", () => { + const card = createEventCard({ + title: "Team Offsite", + date: "February 15, 2026", + time: "9:00 AM - 5:00 PM", + location: "Mountain View Office", + description: "Annual team building event", + }); + + expect(card.type).toBe("bubble"); + expect(card.body).toBeDefined(); + }); + + it("includes action when provided", () => { + const card = createEventCard({ + title: "Meeting", + date: "Jan 24", + action: { type: "uri", label: "Join", uri: "https://meet.google.com/abc" }, + }); + + expect(card.body).toBeDefined(); + expect((card.body as { action?: unknown }).action).toBeDefined(); + }); + + it("includes calendar name when provided", () => { + const card = createEventCard({ + title: "Meeting", + date: "Jan 24", + calendar: "Work Calendar", + }); + + expect(card.body).toBeDefined(); + }); + + it("uses mega size for better readability", () => { + const card = createEventCard({ + title: "Meeting", + date: "Jan 24", + }); + + expect(card.size).toBe("mega"); + }); +}); + +describe("createAgendaCard", () => { + it("creates an agenda card with title and events", () => { + const card = createAgendaCard({ + title: "Today's Schedule", + events: [ + { title: "Team Meeting", time: "9:00 AM" }, + { title: "Lunch", time: "12:00 PM" }, + ], + }); + + expect(card.type).toBe("bubble"); + expect(card.size).toBe("mega"); + expect(card.body).toBeDefined(); + }); + + it("limits events to 8", () => { + const manyEvents = Array.from({ length: 15 }, (_, i) => ({ + title: `Event ${i + 1}`, + })); + + const card = createAgendaCard({ + title: "Many Events", + events: manyEvents, + }); + + expect(card.body).toBeDefined(); + }); + + it("includes footer when provided", () => { + const card = createAgendaCard({ + title: "Today", + events: [{ title: "Event" }], + footer: "Synced from Google Calendar", + }); + + expect(card.footer).toBeDefined(); + }); + + it("shows event metadata (time, location, calendar)", () => { + const card = createAgendaCard({ + title: "Schedule", + events: [ + { + title: "Meeting", + time: "10:00 AM", + location: "Room A", + calendar: "Work", + }, + ], + }); + + expect(card.body).toBeDefined(); + }); +}); + +describe("toFlexMessage", () => { + it("wraps a container in a FlexMessage", () => { + const bubble = createInfoCard("Title", "Body"); + const message = toFlexMessage("Alt text", bubble); + + expect(message.type).toBe("flex"); + expect(message.altText).toBe("Alt text"); + expect(message.contents).toBe(bubble); + }); +}); diff --git a/src/line/flex-templates.ts b/src/line/flex-templates.ts new file mode 100644 index 00000000000..e0fe7e693c6 --- /dev/null +++ b/src/line/flex-templates.ts @@ -0,0 +1,1507 @@ +import type { messagingApi } from "@line/bot-sdk"; + +// Re-export types for convenience +type FlexContainer = messagingApi.FlexContainer; +type FlexBubble = messagingApi.FlexBubble; +type FlexCarousel = messagingApi.FlexCarousel; +type FlexBox = messagingApi.FlexBox; +type FlexText = messagingApi.FlexText; +type FlexImage = messagingApi.FlexImage; +type FlexButton = messagingApi.FlexButton; +type FlexComponent = messagingApi.FlexComponent; +type Action = messagingApi.Action; + +export interface ListItem { + title: string; + subtitle?: string; + action?: Action; +} + +export interface CardAction { + label: string; + action: Action; +} + +/** + * Create an info card with title, body, and optional footer + * + * Editorial design: Clean hierarchy with accent bar, generous spacing, + * and subtle background zones for visual separation. + */ +export function createInfoCard(title: string, body: string, footer?: string): FlexBubble { + const bubble: FlexBubble = { + type: "bubble", + size: "mega", + body: { + type: "box", + layout: "vertical", + contents: [ + // Title with accent bar + { + type: "box", + layout: "horizontal", + contents: [ + { + type: "box", + layout: "vertical", + contents: [], + width: "4px", + backgroundColor: "#06C755", + cornerRadius: "2px", + } as FlexBox, + { + type: "text", + text: title, + weight: "bold", + size: "xl", + color: "#111111", + wrap: true, + flex: 1, + margin: "lg", + } as FlexText, + ], + } as FlexBox, + // Body text in subtle container + { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: body, + size: "md", + color: "#444444", + wrap: true, + lineSpacing: "6px", + } as FlexText, + ], + margin: "xl", + paddingAll: "lg", + backgroundColor: "#F8F9FA", + cornerRadius: "lg", + } as FlexBox, + ], + paddingAll: "xl", + backgroundColor: "#FFFFFF", + }, + }; + + if (footer) { + bubble.footer = { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: footer, + size: "xs", + color: "#AAAAAA", + wrap: true, + align: "center", + } as FlexText, + ], + paddingAll: "lg", + backgroundColor: "#FAFAFA", + }; + } + + return bubble; +} + +/** + * Create a list card with title and multiple items + * + * Editorial design: Numbered/bulleted list with clear visual hierarchy, + * accent dots for each item, and generous spacing. + */ +export function createListCard(title: string, items: ListItem[]): FlexBubble { + const itemContents: FlexComponent[] = items.slice(0, 8).map((item, index) => { + const itemContents: FlexComponent[] = [ + { + type: "text", + text: item.title, + size: "md", + weight: "bold", + color: "#1a1a1a", + wrap: true, + } as FlexText, + ]; + + if (item.subtitle) { + itemContents.push({ + type: "text", + text: item.subtitle, + size: "sm", + color: "#888888", + wrap: true, + margin: "xs", + } as FlexText); + } + + const itemBox: FlexBox = { + type: "box", + layout: "horizontal", + contents: [ + // Accent dot + { + type: "box", + layout: "vertical", + contents: [ + { + type: "box", + layout: "vertical", + contents: [], + width: "8px", + height: "8px", + backgroundColor: index === 0 ? "#06C755" : "#DDDDDD", + cornerRadius: "4px", + } as FlexBox, + ], + width: "20px", + alignItems: "center", + paddingTop: "sm", + } as FlexBox, + // Item content + { + type: "box", + layout: "vertical", + contents: itemContents, + flex: 1, + } as FlexBox, + ], + margin: index > 0 ? "lg" : undefined, + }; + + if (item.action) { + itemBox.action = item.action; + } + + return itemBox; + }); + + return { + type: "bubble", + size: "mega", + body: { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: title, + weight: "bold", + size: "xl", + color: "#111111", + wrap: true, + } as FlexText, + { + type: "separator", + margin: "lg", + color: "#EEEEEE", + }, + { + type: "box", + layout: "vertical", + contents: itemContents, + margin: "lg", + } as FlexBox, + ], + paddingAll: "xl", + backgroundColor: "#FFFFFF", + }, + }; +} + +/** + * Create an image card with image, title, and optional body text + */ +export function createImageCard( + imageUrl: string, + title: string, + body?: string, + options?: { + aspectRatio?: "1:1" | "1.51:1" | "1.91:1" | "4:3" | "16:9" | "20:13" | "2:1" | "3:1"; + aspectMode?: "cover" | "fit"; + action?: Action; + }, +): FlexBubble { + const bubble: FlexBubble = { + type: "bubble", + hero: { + type: "image", + url: imageUrl, + size: "full", + aspectRatio: options?.aspectRatio ?? "20:13", + aspectMode: options?.aspectMode ?? "cover", + action: options?.action, + } as FlexImage, + body: { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: title, + weight: "bold", + size: "xl", + wrap: true, + } as FlexText, + ], + paddingAll: "lg", + }, + }; + + if (body && bubble.body) { + (bubble.body as FlexBox).contents.push({ + type: "text", + text: body, + size: "md", + wrap: true, + margin: "md", + color: "#666666", + } as FlexText); + } + + return bubble; +} + +/** + * Create an action card with title, body, and action buttons + */ +export function createActionCard( + title: string, + body: string, + actions: CardAction[], + options?: { + imageUrl?: string; + aspectRatio?: "1:1" | "1.51:1" | "1.91:1" | "4:3" | "16:9" | "20:13" | "2:1" | "3:1"; + }, +): FlexBubble { + const bubble: FlexBubble = { + type: "bubble", + body: { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: title, + weight: "bold", + size: "xl", + wrap: true, + } as FlexText, + { + type: "text", + text: body, + size: "md", + wrap: true, + margin: "md", + color: "#666666", + } as FlexText, + ], + paddingAll: "lg", + }, + footer: { + type: "box", + layout: "vertical", + contents: actions.slice(0, 4).map( + (action, index) => + ({ + type: "button", + action: action.action, + style: index === 0 ? "primary" : "secondary", + margin: index > 0 ? "sm" : undefined, + }) as FlexButton, + ), + paddingAll: "md", + }, + }; + + if (options?.imageUrl) { + bubble.hero = { + type: "image", + url: options.imageUrl, + size: "full", + aspectRatio: options.aspectRatio ?? "20:13", + aspectMode: "cover", + } as FlexImage; + } + + return bubble; +} + +/** + * Create a carousel container from multiple bubbles + * LINE allows max 12 bubbles in a carousel + */ +export function createCarousel(bubbles: FlexBubble[]): FlexCarousel { + return { + type: "carousel", + contents: bubbles.slice(0, 12), + }; +} + +/** + * Create a notification bubble (for alerts, status updates) + * + * Editorial design: Bold status indicator with accent color, + * clear typography, optional icon for context. + */ +export function createNotificationBubble( + text: string, + options?: { + icon?: string; + type?: "info" | "success" | "warning" | "error"; + title?: string; + }, +): FlexBubble { + // Color based on notification type + const colors = { + info: { accent: "#3B82F6", bg: "#EFF6FF" }, + success: { accent: "#06C755", bg: "#F0FDF4" }, + warning: { accent: "#F59E0B", bg: "#FFFBEB" }, + error: { accent: "#EF4444", bg: "#FEF2F2" }, + }; + const typeColors = colors[options?.type ?? "info"]; + + const contents: FlexComponent[] = []; + + // Accent bar + contents.push({ + type: "box", + layout: "vertical", + contents: [], + width: "4px", + backgroundColor: typeColors.accent, + cornerRadius: "2px", + } as FlexBox); + + // Content section + const textContents: FlexComponent[] = []; + + if (options?.title) { + textContents.push({ + type: "text", + text: options.title, + size: "md", + weight: "bold", + color: "#111111", + wrap: true, + } as FlexText); + } + + textContents.push({ + type: "text", + text, + size: options?.title ? "sm" : "md", + color: options?.title ? "#666666" : "#333333", + wrap: true, + margin: options?.title ? "sm" : undefined, + } as FlexText); + + contents.push({ + type: "box", + layout: "vertical", + contents: textContents, + flex: 1, + paddingStart: "lg", + } as FlexBox); + + return { + type: "bubble", + body: { + type: "box", + layout: "horizontal", + contents, + paddingAll: "xl", + backgroundColor: typeColors.bg, + }, + }; +} + +/** + * Create a receipt/summary card (for orders, transactions, data tables) + * + * Editorial design: Clean table layout with alternating row backgrounds, + * prominent total section, and clear visual hierarchy. + */ +export function createReceiptCard(params: { + title: string; + subtitle?: string; + items: Array<{ name: string; value: string; highlight?: boolean }>; + total?: { label: string; value: string }; + footer?: string; +}): FlexBubble { + const { title, subtitle, items, total, footer } = params; + + const itemRows: FlexComponent[] = items.slice(0, 12).map( + (item, index) => + ({ + type: "box", + layout: "horizontal", + contents: [ + { + type: "text", + text: item.name, + size: "sm", + color: item.highlight ? "#111111" : "#666666", + weight: item.highlight ? "bold" : "regular", + flex: 3, + wrap: true, + } as FlexText, + { + type: "text", + text: item.value, + size: "sm", + color: item.highlight ? "#06C755" : "#333333", + weight: item.highlight ? "bold" : "regular", + flex: 2, + align: "end", + wrap: true, + } as FlexText, + ], + paddingAll: "md", + backgroundColor: index % 2 === 0 ? "#FFFFFF" : "#FAFAFA", + }) as FlexBox, + ); + + // Header section + const headerContents: FlexComponent[] = [ + { + type: "text", + text: title, + weight: "bold", + size: "xl", + color: "#111111", + wrap: true, + } as FlexText, + ]; + + if (subtitle) { + headerContents.push({ + type: "text", + text: subtitle, + size: "sm", + color: "#888888", + margin: "sm", + wrap: true, + } as FlexText); + } + + const bodyContents: FlexComponent[] = [ + { + type: "box", + layout: "vertical", + contents: headerContents, + paddingBottom: "lg", + } as FlexBox, + { + type: "separator", + color: "#EEEEEE", + }, + { + type: "box", + layout: "vertical", + contents: itemRows, + margin: "md", + cornerRadius: "md", + borderWidth: "light", + borderColor: "#EEEEEE", + } as FlexBox, + ]; + + // Total section with emphasis + if (total) { + bodyContents.push({ + type: "box", + layout: "horizontal", + contents: [ + { + type: "text", + text: total.label, + size: "lg", + weight: "bold", + color: "#111111", + flex: 2, + } as FlexText, + { + type: "text", + text: total.value, + size: "xl", + weight: "bold", + color: "#06C755", + flex: 2, + align: "end", + } as FlexText, + ], + margin: "xl", + paddingAll: "lg", + backgroundColor: "#F0FDF4", + cornerRadius: "lg", + } as FlexBox); + } + + const bubble: FlexBubble = { + type: "bubble", + size: "mega", + body: { + type: "box", + layout: "vertical", + contents: bodyContents, + paddingAll: "xl", + backgroundColor: "#FFFFFF", + }, + }; + + if (footer) { + bubble.footer = { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: footer, + size: "xs", + color: "#AAAAAA", + wrap: true, + align: "center", + } as FlexText, + ], + paddingAll: "lg", + backgroundColor: "#FAFAFA", + }; + } + + return bubble; +} + +/** + * Create a calendar event card (for meetings, appointments, reminders) + * + * Editorial design: Date as hero, strong typographic hierarchy, + * color-blocked zones, full text wrapping for readability. + */ +export function createEventCard(params: { + title: string; + date: string; + time?: string; + location?: string; + description?: string; + calendar?: string; + isAllDay?: boolean; + action?: Action; +}): FlexBubble { + const { title, date, time, location, description, calendar, isAllDay, action } = params; + + // Hero date block - the most important information + const dateBlock: FlexBox = { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: date.toUpperCase(), + size: "sm", + weight: "bold", + color: "#06C755", + wrap: true, + } as FlexText, + { + type: "text", + text: isAllDay ? "ALL DAY" : (time ?? ""), + size: "xxl", + weight: "bold", + color: "#111111", + wrap: true, + margin: "xs", + } as FlexText, + ], + paddingBottom: "lg", + borderWidth: "none", + }; + + // If no time and not all day, hide the time display + if (!time && !isAllDay) { + dateBlock.contents = [ + { + type: "text", + text: date, + size: "xl", + weight: "bold", + color: "#111111", + wrap: true, + } as FlexText, + ]; + } + + // Event title with accent bar + const titleBlock: FlexBox = { + type: "box", + layout: "horizontal", + contents: [ + { + type: "box", + layout: "vertical", + contents: [], + width: "4px", + backgroundColor: "#06C755", + cornerRadius: "2px", + } as FlexBox, + { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: title, + size: "lg", + weight: "bold", + color: "#1a1a1a", + wrap: true, + } as FlexText, + ...(calendar + ? [ + { + type: "text", + text: calendar, + size: "xs", + color: "#888888", + margin: "sm", + wrap: true, + } as FlexText, + ] + : []), + ], + flex: 1, + paddingStart: "lg", + } as FlexBox, + ], + paddingTop: "lg", + paddingBottom: "lg", + borderWidth: "light", + borderColor: "#EEEEEE", + }; + + const bodyContents: FlexComponent[] = [dateBlock, titleBlock]; + + // Details section (location + description) in subtle background + const hasDetails = location || description; + if (hasDetails) { + const detailItems: FlexComponent[] = []; + + if (location) { + detailItems.push({ + type: "box", + layout: "horizontal", + contents: [ + { + type: "text", + text: "📍", + size: "sm", + flex: 0, + } as FlexText, + { + type: "text", + text: location, + size: "sm", + color: "#444444", + margin: "md", + flex: 1, + wrap: true, + } as FlexText, + ], + alignItems: "flex-start", + } as FlexBox); + } + + if (description) { + detailItems.push({ + type: "text", + text: description, + size: "sm", + color: "#666666", + wrap: true, + margin: location ? "lg" : "none", + } as FlexText); + } + + bodyContents.push({ + type: "box", + layout: "vertical", + contents: detailItems, + margin: "lg", + paddingAll: "lg", + backgroundColor: "#F8F9FA", + cornerRadius: "lg", + } as FlexBox); + } + + return { + type: "bubble", + size: "mega", + body: { + type: "box", + layout: "vertical", + contents: bodyContents, + paddingAll: "xl", + backgroundColor: "#FFFFFF", + action, + }, + }; +} + +/** + * Create a calendar agenda card showing multiple events + * + * Editorial timeline design: Time-focused left column with event details + * on the right. Visual accent bars indicate event priority/recency. + */ +export function createAgendaCard(params: { + title: string; + subtitle?: string; + events: Array<{ + title: string; + time?: string; + location?: string; + calendar?: string; + isNow?: boolean; + }>; + footer?: string; +}): FlexBubble { + const { title, subtitle, events, footer } = params; + + // Header with title and optional subtitle + const headerContents: FlexComponent[] = [ + { + type: "text", + text: title, + weight: "bold", + size: "xl", + color: "#111111", + wrap: true, + } as FlexText, + ]; + + if (subtitle) { + headerContents.push({ + type: "text", + text: subtitle, + size: "sm", + color: "#888888", + margin: "sm", + wrap: true, + } as FlexText); + } + + // Event timeline items + const eventItems: FlexComponent[] = events.slice(0, 6).map((event, index) => { + const isActive = event.isNow || index === 0; + const accentColor = isActive ? "#06C755" : "#E5E5E5"; + + // Time column (fixed width) + const timeColumn: FlexBox = { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: event.time ?? "—", + size: "sm", + weight: isActive ? "bold" : "regular", + color: isActive ? "#06C755" : "#666666", + align: "end", + wrap: true, + } as FlexText, + ], + width: "65px", + justifyContent: "flex-start", + }; + + // Accent dot + const dotColumn: FlexBox = { + type: "box", + layout: "vertical", + contents: [ + { + type: "box", + layout: "vertical", + contents: [], + width: "10px", + height: "10px", + backgroundColor: accentColor, + cornerRadius: "5px", + } as FlexBox, + ], + width: "24px", + alignItems: "center", + justifyContent: "flex-start", + paddingTop: "xs", + }; + + // Event details column + const detailContents: FlexComponent[] = [ + { + type: "text", + text: event.title, + size: "md", + weight: "bold", + color: "#1a1a1a", + wrap: true, + } as FlexText, + ]; + + // Secondary info line + const secondaryParts: string[] = []; + if (event.location) secondaryParts.push(event.location); + if (event.calendar) secondaryParts.push(event.calendar); + + if (secondaryParts.length > 0) { + detailContents.push({ + type: "text", + text: secondaryParts.join(" · "), + size: "xs", + color: "#888888", + wrap: true, + margin: "xs", + } as FlexText); + } + + const detailColumn: FlexBox = { + type: "box", + layout: "vertical", + contents: detailContents, + flex: 1, + }; + + return { + type: "box", + layout: "horizontal", + contents: [timeColumn, dotColumn, detailColumn], + margin: index > 0 ? "xl" : undefined, + alignItems: "flex-start", + } as FlexBox; + }); + + const bodyContents: FlexComponent[] = [ + { + type: "box", + layout: "vertical", + contents: headerContents, + paddingBottom: "lg", + } as FlexBox, + { + type: "separator", + color: "#EEEEEE", + }, + { + type: "box", + layout: "vertical", + contents: eventItems, + paddingTop: "xl", + } as FlexBox, + ]; + + const bubble: FlexBubble = { + type: "bubble", + size: "mega", + body: { + type: "box", + layout: "vertical", + contents: bodyContents, + paddingAll: "xl", + backgroundColor: "#FFFFFF", + }, + }; + + if (footer) { + bubble.footer = { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: footer, + size: "xs", + color: "#AAAAAA", + align: "center", + wrap: true, + } as FlexText, + ], + paddingAll: "lg", + backgroundColor: "#FAFAFA", + }; + } + + return bubble; +} + +/** + * Create a media player card for Sonos, Spotify, Apple Music, etc. + * + * Editorial design: Album art hero with gradient overlay for text, + * prominent now-playing indicator, refined playback controls. + */ +export function createMediaPlayerCard(params: { + title: string; + subtitle?: string; + source?: string; + imageUrl?: string; + isPlaying?: boolean; + progress?: string; + controls?: { + previous?: { data: string }; + play?: { data: string }; + pause?: { data: string }; + next?: { data: string }; + }; + extraActions?: Array<{ label: string; data: string }>; +}): FlexBubble { + const { title, subtitle, source, imageUrl, isPlaying, progress, controls, extraActions } = params; + + // Track info section + const trackInfo: FlexComponent[] = [ + { + type: "text", + text: title, + weight: "bold", + size: "xl", + color: "#111111", + wrap: true, + } as FlexText, + ]; + + if (subtitle) { + trackInfo.push({ + type: "text", + text: subtitle, + size: "md", + color: "#666666", + wrap: true, + margin: "sm", + } as FlexText); + } + + // Status row with source and playing indicator + const statusItems: FlexComponent[] = []; + + if (isPlaying !== undefined) { + statusItems.push({ + type: "box", + layout: "horizontal", + contents: [ + { + type: "box", + layout: "vertical", + contents: [], + width: "8px", + height: "8px", + backgroundColor: isPlaying ? "#06C755" : "#CCCCCC", + cornerRadius: "4px", + } as FlexBox, + { + type: "text", + text: isPlaying ? "Now Playing" : "Paused", + size: "xs", + color: isPlaying ? "#06C755" : "#888888", + weight: "bold", + margin: "sm", + } as FlexText, + ], + alignItems: "center", + } as FlexBox); + } + + if (source) { + statusItems.push({ + type: "text", + text: source, + size: "xs", + color: "#AAAAAA", + margin: statusItems.length > 0 ? "lg" : undefined, + } as FlexText); + } + + if (progress) { + statusItems.push({ + type: "text", + text: progress, + size: "xs", + color: "#888888", + align: "end", + flex: 1, + } as FlexText); + } + + const bodyContents: FlexComponent[] = [ + { + type: "box", + layout: "vertical", + contents: trackInfo, + } as FlexBox, + ]; + + if (statusItems.length > 0) { + bodyContents.push({ + type: "box", + layout: "horizontal", + contents: statusItems, + margin: "lg", + alignItems: "center", + } as FlexBox); + } + + const bubble: FlexBubble = { + type: "bubble", + size: "mega", + body: { + type: "box", + layout: "vertical", + contents: bodyContents, + paddingAll: "xl", + backgroundColor: "#FFFFFF", + }, + }; + + // Album art hero + if (imageUrl) { + bubble.hero = { + type: "image", + url: imageUrl, + size: "full", + aspectRatio: "1:1", + aspectMode: "cover", + } as FlexImage; + } + + // Control buttons in footer + if (controls || extraActions?.length) { + const footerContents: FlexComponent[] = []; + + // Main playback controls with refined styling + if (controls) { + const controlButtons: FlexComponent[] = []; + + if (controls.previous) { + controlButtons.push({ + type: "button", + action: { + type: "postback", + label: "⏮", + data: controls.previous.data, + }, + style: "secondary", + flex: 1, + height: "sm", + } as FlexButton); + } + + if (controls.play) { + controlButtons.push({ + type: "button", + action: { + type: "postback", + label: "▶", + data: controls.play.data, + }, + style: isPlaying ? "secondary" : "primary", + flex: 1, + height: "sm", + margin: controls.previous ? "md" : undefined, + } as FlexButton); + } + + if (controls.pause) { + controlButtons.push({ + type: "button", + action: { + type: "postback", + label: "⏸", + data: controls.pause.data, + }, + style: isPlaying ? "primary" : "secondary", + flex: 1, + height: "sm", + margin: controlButtons.length > 0 ? "md" : undefined, + } as FlexButton); + } + + if (controls.next) { + controlButtons.push({ + type: "button", + action: { + type: "postback", + label: "⏭", + data: controls.next.data, + }, + style: "secondary", + flex: 1, + height: "sm", + margin: controlButtons.length > 0 ? "md" : undefined, + } as FlexButton); + } + + if (controlButtons.length > 0) { + footerContents.push({ + type: "box", + layout: "horizontal", + contents: controlButtons, + } as FlexBox); + } + } + + // Extra actions + if (extraActions?.length) { + footerContents.push({ + type: "box", + layout: "horizontal", + contents: extraActions.slice(0, 2).map( + (action, index) => + ({ + type: "button", + action: { + type: "postback", + label: action.label.slice(0, 15), + data: action.data, + }, + style: "secondary", + flex: 1, + height: "sm", + margin: index > 0 ? "md" : undefined, + }) as FlexButton, + ), + margin: "md", + } as FlexBox); + } + + if (footerContents.length > 0) { + bubble.footer = { + type: "box", + layout: "vertical", + contents: footerContents, + paddingAll: "lg", + backgroundColor: "#FAFAFA", + }; + } + } + + return bubble; +} + +/** + * Create an Apple TV remote card with a D-pad and control rows. + */ +export function createAppleTvRemoteCard(params: { + deviceName: string; + status?: string; + actionData: { + up: string; + down: string; + left: string; + right: string; + select: string; + menu: string; + home: string; + play: string; + pause: string; + volumeUp: string; + volumeDown: string; + mute: string; + }; +}): FlexBubble { + const { deviceName, status, actionData } = params; + + const headerContents: FlexComponent[] = [ + { + type: "text", + text: deviceName, + weight: "bold", + size: "xl", + color: "#111111", + wrap: true, + } as FlexText, + ]; + + if (status) { + headerContents.push({ + type: "text", + text: status, + size: "sm", + color: "#666666", + wrap: true, + margin: "sm", + } as FlexText); + } + + const makeButton = ( + label: string, + data: string, + style: "primary" | "secondary" = "secondary", + ): FlexButton => ({ + type: "button", + action: { + type: "postback", + label, + data, + }, + style, + height: "sm", + flex: 1, + }); + + const dpadRows: FlexComponent[] = [ + { + type: "box", + layout: "horizontal", + contents: [{ type: "filler" }, makeButton("↑", actionData.up), { type: "filler" }], + } as FlexBox, + { + type: "box", + layout: "horizontal", + contents: [ + makeButton("←", actionData.left), + makeButton("OK", actionData.select, "primary"), + makeButton("→", actionData.right), + ], + margin: "md", + } as FlexBox, + { + type: "box", + layout: "horizontal", + contents: [{ type: "filler" }, makeButton("↓", actionData.down), { type: "filler" }], + margin: "md", + } as FlexBox, + ]; + + const menuRow: FlexComponent = { + type: "box", + layout: "horizontal", + contents: [makeButton("Menu", actionData.menu), makeButton("Home", actionData.home)], + margin: "lg", + } as FlexBox; + + const playbackRow: FlexComponent = { + type: "box", + layout: "horizontal", + contents: [makeButton("Play", actionData.play), makeButton("Pause", actionData.pause)], + margin: "md", + } as FlexBox; + + const volumeRow: FlexComponent = { + type: "box", + layout: "horizontal", + contents: [ + makeButton("Vol +", actionData.volumeUp), + makeButton("Mute", actionData.mute), + makeButton("Vol -", actionData.volumeDown), + ], + margin: "md", + } as FlexBox; + + return { + type: "bubble", + size: "mega", + body: { + type: "box", + layout: "vertical", + contents: [ + { + type: "box", + layout: "vertical", + contents: headerContents, + } as FlexBox, + { + type: "separator", + margin: "lg", + color: "#EEEEEE", + }, + ...dpadRows, + menuRow, + playbackRow, + volumeRow, + ], + paddingAll: "xl", + backgroundColor: "#FFFFFF", + }, + }; +} + +/** + * Create a device control card for Apple TV, smart home devices, etc. + * + * Editorial design: Device-focused header with status indicator, + * clean control grid with clear visual hierarchy. + */ +export function createDeviceControlCard(params: { + deviceName: string; + deviceType?: string; + status?: string; + isOnline?: boolean; + imageUrl?: string; + controls: Array<{ + label: string; + icon?: string; + data: string; + style?: "primary" | "secondary"; + }>; +}): FlexBubble { + const { deviceName, deviceType, status, isOnline, imageUrl, controls } = params; + + // Device header with status indicator + const headerContents: FlexComponent[] = [ + { + type: "box", + layout: "horizontal", + contents: [ + // Status dot + { + type: "box", + layout: "vertical", + contents: [], + width: "10px", + height: "10px", + backgroundColor: isOnline !== false ? "#06C755" : "#FF5555", + cornerRadius: "5px", + } as FlexBox, + { + type: "text", + text: deviceName, + weight: "bold", + size: "xl", + color: "#111111", + wrap: true, + flex: 1, + margin: "md", + } as FlexText, + ], + alignItems: "center", + } as FlexBox, + ]; + + if (deviceType) { + headerContents.push({ + type: "text", + text: deviceType, + size: "sm", + color: "#888888", + margin: "sm", + } as FlexText); + } + + if (status) { + headerContents.push({ + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: status, + size: "sm", + color: "#444444", + wrap: true, + } as FlexText, + ], + margin: "lg", + paddingAll: "md", + backgroundColor: "#F8F9FA", + cornerRadius: "md", + } as FlexBox); + } + + const bubble: FlexBubble = { + type: "bubble", + size: "mega", + body: { + type: "box", + layout: "vertical", + contents: headerContents, + paddingAll: "xl", + backgroundColor: "#FFFFFF", + }, + }; + + if (imageUrl) { + bubble.hero = { + type: "image", + url: imageUrl, + size: "full", + aspectRatio: "16:9", + aspectMode: "cover", + } as FlexImage; + } + + // Control buttons in refined grid layout (2 per row) + if (controls.length > 0) { + const rows: FlexComponent[] = []; + const limitedControls = controls.slice(0, 6); + + for (let i = 0; i < limitedControls.length; i += 2) { + const rowButtons: FlexComponent[] = []; + + for (let j = i; j < Math.min(i + 2, limitedControls.length); j++) { + const ctrl = limitedControls[j]; + const buttonLabel = ctrl.icon ? `${ctrl.icon} ${ctrl.label}` : ctrl.label; + + rowButtons.push({ + type: "button", + action: { + type: "postback", + label: buttonLabel.slice(0, 18), + data: ctrl.data, + }, + style: ctrl.style ?? "secondary", + flex: 1, + height: "sm", + margin: j > i ? "md" : undefined, + } as FlexButton); + } + + // If odd number of controls in last row, add spacer + if (rowButtons.length === 1) { + rowButtons.push({ + type: "filler", + }); + } + + rows.push({ + type: "box", + layout: "horizontal", + contents: rowButtons, + margin: i > 0 ? "md" : undefined, + } as FlexBox); + } + + bubble.footer = { + type: "box", + layout: "vertical", + contents: rows, + paddingAll: "lg", + backgroundColor: "#FAFAFA", + }; + } + + return bubble; +} + +/** + * Wrap a FlexContainer in a FlexMessage + */ +export function toFlexMessage(altText: string, contents: FlexContainer): messagingApi.FlexMessage { + return { + type: "flex", + altText, + contents, + }; +} + +// Re-export the types for consumers +export type { + FlexContainer, + FlexBubble, + FlexCarousel, + FlexBox, + FlexText, + FlexImage, + FlexButton, + FlexComponent, + Action, +}; diff --git a/src/line/http-registry.ts b/src/line/http-registry.ts new file mode 100644 index 00000000000..1d971e752cd --- /dev/null +++ b/src/line/http-registry.ts @@ -0,0 +1,45 @@ +import type { IncomingMessage, ServerResponse } from "node:http"; + +export type LineHttpRequestHandler = ( + req: IncomingMessage, + res: ServerResponse, +) => Promise | void; + +type RegisterLineHttpHandlerArgs = { + path?: string | null; + handler: LineHttpRequestHandler; + log?: (message: string) => void; + accountId?: string; +}; + +const lineHttpRoutes = new Map(); + +export function normalizeLineWebhookPath(path?: string | null): string { + const trimmed = path?.trim(); + if (!trimmed) return "/line/webhook"; + return trimmed.startsWith("/") ? trimmed : `/${trimmed}`; +} + +export function registerLineHttpHandler(params: RegisterLineHttpHandlerArgs): () => void { + const normalizedPath = normalizeLineWebhookPath(params.path); + if (lineHttpRoutes.has(normalizedPath)) { + const suffix = params.accountId ? ` for account "${params.accountId}"` : ""; + params.log?.(`line: webhook path ${normalizedPath} already registered${suffix}`); + return () => {}; + } + lineHttpRoutes.set(normalizedPath, params.handler); + return () => { + lineHttpRoutes.delete(normalizedPath); + }; +} + +export async function handleLineHttpRequest( + req: IncomingMessage, + res: ServerResponse, +): Promise { + const url = new URL(req.url ?? "/", "http://localhost"); + const handler = lineHttpRoutes.get(url.pathname); + if (!handler) return false; + await handler(req, res); + return true; +} diff --git a/src/line/index.ts b/src/line/index.ts new file mode 100644 index 00000000000..f812ee58d5d --- /dev/null +++ b/src/line/index.ts @@ -0,0 +1,155 @@ +export { + createLineBot, + createLineWebhookCallback, + type LineBot, + type LineBotOptions, +} from "./bot.js"; +export { + monitorLineProvider, + getLineRuntimeState, + type MonitorLineProviderOptions, + type LineProviderMonitor, +} from "./monitor.js"; +export { + sendMessageLine, + pushMessageLine, + pushMessagesLine, + replyMessageLine, + createImageMessage, + createLocationMessage, + createFlexMessage, + createQuickReplyItems, + createTextMessageWithQuickReplies, + showLoadingAnimation, + getUserProfile, + getUserDisplayName, + pushImageMessage, + pushLocationMessage, + pushFlexMessage, + pushTemplateMessage, + pushTextMessageWithQuickReplies, +} from "./send.js"; +export { + startLineWebhook, + createLineWebhookMiddleware, + type LineWebhookOptions, + type StartLineWebhookOptions, +} from "./webhook.js"; +export { + handleLineHttpRequest, + registerLineHttpHandler, + normalizeLineWebhookPath, +} from "./http-registry.js"; +export { + resolveLineAccount, + listLineAccountIds, + resolveDefaultLineAccountId, + normalizeAccountId, + DEFAULT_ACCOUNT_ID, +} from "./accounts.js"; +export { probeLineBot } from "./probe.js"; +export { downloadLineMedia } from "./download.js"; +export { LineConfigSchema, type LineConfigSchemaType } from "./config-schema.js"; +export { buildLineMessageContext } from "./bot-message-context.js"; +export { handleLineWebhookEvents, type LineHandlerContext } from "./bot-handlers.js"; + +// Flex Message templates +export { + createInfoCard, + createListCard, + createImageCard, + createActionCard, + createCarousel, + createNotificationBubble, + createReceiptCard, + createEventCard, + createMediaPlayerCard, + createAppleTvRemoteCard, + createDeviceControlCard, + toFlexMessage, + type ListItem, + type CardAction, + type FlexContainer, + type FlexBubble, + type FlexCarousel, +} from "./flex-templates.js"; + +// Markdown to LINE conversion +export { + processLineMessage, + hasMarkdownToConvert, + stripMarkdown, + extractMarkdownTables, + extractCodeBlocks, + extractLinks, + convertTableToFlexBubble, + convertCodeBlockToFlexBubble, + convertLinksToFlexBubble, + type ProcessedLineMessage, + type MarkdownTable, + type CodeBlock, + type MarkdownLink, +} from "./markdown-to-line.js"; + +// Rich Menu operations +export { + createRichMenu, + uploadRichMenuImage, + setDefaultRichMenu, + cancelDefaultRichMenu, + getDefaultRichMenuId, + linkRichMenuToUser, + linkRichMenuToUsers, + unlinkRichMenuFromUser, + unlinkRichMenuFromUsers, + getRichMenuIdOfUser, + getRichMenuList, + getRichMenu, + deleteRichMenu, + createRichMenuAlias, + deleteRichMenuAlias, + createGridLayout, + messageAction, + uriAction, + postbackAction, + datetimePickerAction, + createDefaultMenuConfig, + type CreateRichMenuParams, + type RichMenuSize, + type RichMenuAreaRequest, +} from "./rich-menu.js"; + +// Template messages (Button, Confirm, Carousel) +export { + createConfirmTemplate, + createButtonTemplate, + createTemplateCarousel, + createCarouselColumn, + createImageCarousel, + createImageCarouselColumn, + createYesNoConfirm, + createButtonMenu, + createLinkMenu, + createProductCarousel, + messageAction as templateMessageAction, + uriAction as templateUriAction, + postbackAction as templatePostbackAction, + datetimePickerAction as templateDatetimePickerAction, + type TemplateMessage, + type ConfirmTemplate, + type ButtonsTemplate, + type CarouselTemplate, + type CarouselColumn, +} from "./template-messages.js"; + +export type { + LineConfig, + LineAccountConfig, + LineGroupConfig, + ResolvedLineAccount, + LineTokenSource, + LineMessageType, + LineWebhookContext, + LineSendResult, + LineProbeResult, +} from "./types.js"; diff --git a/src/line/markdown-to-line.test.ts b/src/line/markdown-to-line.test.ts new file mode 100644 index 00000000000..99c37a4f499 --- /dev/null +++ b/src/line/markdown-to-line.test.ts @@ -0,0 +1,449 @@ +import { describe, expect, it } from "vitest"; +import { + extractMarkdownTables, + extractCodeBlocks, + extractLinks, + stripMarkdown, + processLineMessage, + convertTableToFlexBubble, + convertCodeBlockToFlexBubble, + hasMarkdownToConvert, +} from "./markdown-to-line.js"; + +describe("extractMarkdownTables", () => { + it("extracts a simple 2-column table", () => { + const text = `Here is a table: + +| Name | Value | +|------|-------| +| foo | 123 | +| bar | 456 | + +And some more text.`; + + const { tables, textWithoutTables } = extractMarkdownTables(text); + + expect(tables).toHaveLength(1); + expect(tables[0].headers).toEqual(["Name", "Value"]); + expect(tables[0].rows).toEqual([ + ["foo", "123"], + ["bar", "456"], + ]); + expect(textWithoutTables).toContain("Here is a table:"); + expect(textWithoutTables).toContain("And some more text."); + expect(textWithoutTables).not.toContain("|"); + }); + + it("extracts a multi-column table", () => { + const text = `| Col A | Col B | Col C | +|-------|-------|-------| +| 1 | 2 | 3 | +| a | b | c |`; + + const { tables } = extractMarkdownTables(text); + + expect(tables).toHaveLength(1); + expect(tables[0].headers).toEqual(["Col A", "Col B", "Col C"]); + expect(tables[0].rows).toHaveLength(2); + }); + + it("extracts multiple tables", () => { + const text = `Table 1: + +| A | B | +|---|---| +| 1 | 2 | + +Table 2: + +| X | Y | +|---|---| +| 3 | 4 |`; + + const { tables } = extractMarkdownTables(text); + + expect(tables).toHaveLength(2); + expect(tables[0].headers).toEqual(["A", "B"]); + expect(tables[1].headers).toEqual(["X", "Y"]); + }); + + it("handles tables with alignment markers", () => { + const text = `| Left | Center | Right | +|:-----|:------:|------:| +| a | b | c |`; + + const { tables } = extractMarkdownTables(text); + + expect(tables).toHaveLength(1); + expect(tables[0].headers).toEqual(["Left", "Center", "Right"]); + expect(tables[0].rows).toEqual([["a", "b", "c"]]); + }); + + it("returns empty when no tables present", () => { + const text = "Just some plain text without tables."; + + const { tables, textWithoutTables } = extractMarkdownTables(text); + + expect(tables).toHaveLength(0); + expect(textWithoutTables).toBe(text); + }); +}); + +describe("extractCodeBlocks", () => { + it("extracts a code block with language", () => { + const text = `Here is some code: + +\`\`\`javascript +const x = 1; +console.log(x); +\`\`\` + +And more text.`; + + const { codeBlocks, textWithoutCode } = extractCodeBlocks(text); + + expect(codeBlocks).toHaveLength(1); + expect(codeBlocks[0].language).toBe("javascript"); + expect(codeBlocks[0].code).toBe("const x = 1;\nconsole.log(x);"); + expect(textWithoutCode).toContain("Here is some code:"); + expect(textWithoutCode).toContain("And more text."); + expect(textWithoutCode).not.toContain("```"); + }); + + it("extracts a code block without language", () => { + const text = `\`\`\` +plain code +\`\`\``; + + const { codeBlocks } = extractCodeBlocks(text); + + expect(codeBlocks).toHaveLength(1); + expect(codeBlocks[0].language).toBeUndefined(); + expect(codeBlocks[0].code).toBe("plain code"); + }); + + it("extracts multiple code blocks", () => { + const text = `\`\`\`python +print("hello") +\`\`\` + +Some text + +\`\`\`bash +echo "world" +\`\`\``; + + const { codeBlocks } = extractCodeBlocks(text); + + expect(codeBlocks).toHaveLength(2); + expect(codeBlocks[0].language).toBe("python"); + expect(codeBlocks[1].language).toBe("bash"); + }); + + it("returns empty when no code blocks present", () => { + const text = "No code here, just text."; + + const { codeBlocks, textWithoutCode } = extractCodeBlocks(text); + + expect(codeBlocks).toHaveLength(0); + expect(textWithoutCode).toBe(text); + }); +}); + +describe("extractLinks", () => { + it("extracts markdown links", () => { + const text = "Check out [Google](https://google.com) and [GitHub](https://github.com)."; + + const { links, textWithLinks } = extractLinks(text); + + expect(links).toHaveLength(2); + expect(links[0]).toEqual({ text: "Google", url: "https://google.com" }); + expect(links[1]).toEqual({ text: "GitHub", url: "https://github.com" }); + expect(textWithLinks).toBe("Check out Google and GitHub."); + }); + + it("handles text without links", () => { + const text = "No links here."; + + const { links, textWithLinks } = extractLinks(text); + + expect(links).toHaveLength(0); + expect(textWithLinks).toBe(text); + }); +}); + +describe("stripMarkdown", () => { + it("strips bold markers", () => { + expect(stripMarkdown("This is **bold** text")).toBe("This is bold text"); + expect(stripMarkdown("This is __bold__ text")).toBe("This is bold text"); + }); + + it("strips italic markers", () => { + expect(stripMarkdown("This is *italic* text")).toBe("This is italic text"); + expect(stripMarkdown("This is _italic_ text")).toBe("This is italic text"); + }); + + it("strips strikethrough markers", () => { + expect(stripMarkdown("This is ~~deleted~~ text")).toBe("This is deleted text"); + }); + + it("strips headers", () => { + expect(stripMarkdown("# Heading 1")).toBe("Heading 1"); + expect(stripMarkdown("## Heading 2")).toBe("Heading 2"); + expect(stripMarkdown("### Heading 3")).toBe("Heading 3"); + }); + + it("strips blockquotes", () => { + expect(stripMarkdown("> This is a quote")).toBe("This is a quote"); + expect(stripMarkdown(">This is also a quote")).toBe("This is also a quote"); + }); + + it("removes horizontal rules", () => { + expect(stripMarkdown("Above\n---\nBelow")).toBe("Above\n\nBelow"); + expect(stripMarkdown("Above\n***\nBelow")).toBe("Above\n\nBelow"); + }); + + it("strips inline code markers", () => { + expect(stripMarkdown("Use `const` keyword")).toBe("Use const keyword"); + }); + + it("handles complex markdown", () => { + const input = `# Title + +This is **bold** and *italic* text. + +> A quote + +Some ~~deleted~~ content.`; + + const result = stripMarkdown(input); + + expect(result).toContain("Title"); + expect(result).toContain("This is bold and italic text."); + expect(result).toContain("A quote"); + expect(result).toContain("Some deleted content."); + expect(result).not.toContain("#"); + expect(result).not.toContain("**"); + expect(result).not.toContain("~~"); + expect(result).not.toContain(">"); + }); +}); + +describe("convertTableToFlexBubble", () => { + it("creates a receipt-style card for 2-column tables", () => { + const table = { + headers: ["Item", "Price"], + rows: [ + ["Apple", "$1"], + ["Banana", "$2"], + ], + }; + + const bubble = convertTableToFlexBubble(table); + + expect(bubble.type).toBe("bubble"); + expect(bubble.body).toBeDefined(); + }); + + it("creates a multi-column layout for 3+ column tables", () => { + const table = { + headers: ["A", "B", "C"], + rows: [["1", "2", "3"]], + }; + + const bubble = convertTableToFlexBubble(table); + + expect(bubble.type).toBe("bubble"); + expect(bubble.body).toBeDefined(); + }); + + it("replaces empty cells with placeholders", () => { + const table = { + headers: ["A", "B"], + rows: [["", ""]], + }; + + const bubble = convertTableToFlexBubble(table); + const body = bubble.body as { + contents: Array<{ contents?: Array<{ contents?: Array<{ text: string }> }> }>; + }; + const rowsBox = body.contents[2] as { contents: Array<{ contents: Array<{ text: string }> }> }; + + expect(rowsBox.contents[0].contents[0].text).toBe("-"); + expect(rowsBox.contents[0].contents[1].text).toBe("-"); + }); + + it("strips bold markers and applies weight for fully bold cells", () => { + const table = { + headers: ["**Name**", "Status"], + rows: [["**Alpha**", "OK"]], + }; + + const bubble = convertTableToFlexBubble(table); + const body = bubble.body as { + contents: Array<{ contents?: Array<{ text: string; weight?: string }> }>; + }; + const headerRow = body.contents[0] as { contents: Array<{ text: string; weight?: string }> }; + const dataRow = body.contents[2] as { contents: Array<{ text: string; weight?: string }> }; + + expect(headerRow.contents[0].text).toBe("Name"); + expect(headerRow.contents[0].weight).toBe("bold"); + expect(dataRow.contents[0].text).toBe("Alpha"); + expect(dataRow.contents[0].weight).toBe("bold"); + }); +}); + +describe("convertCodeBlockToFlexBubble", () => { + it("creates a code card with language label", () => { + const block = { language: "typescript", code: "const x = 1;" }; + + const bubble = convertCodeBlockToFlexBubble(block); + + expect(bubble.type).toBe("bubble"); + expect(bubble.body).toBeDefined(); + + const body = bubble.body as { contents: Array<{ text: string }> }; + expect(body.contents[0].text).toBe("Code (typescript)"); + }); + + it("creates a code card without language", () => { + const block = { code: "plain code" }; + + const bubble = convertCodeBlockToFlexBubble(block); + + const body = bubble.body as { contents: Array<{ text: string }> }; + expect(body.contents[0].text).toBe("Code"); + }); + + it("truncates very long code", () => { + const longCode = "x".repeat(3000); + const block = { code: longCode }; + + const bubble = convertCodeBlockToFlexBubble(block); + + const body = bubble.body as { contents: Array<{ contents: Array<{ text: string }> }> }; + const codeText = body.contents[1].contents[0].text; + expect(codeText.length).toBeLessThan(longCode.length); + expect(codeText).toContain("..."); + }); +}); + +describe("processLineMessage", () => { + it("processes text with tables", () => { + const text = `Here's the data: + +| Key | Value | +|-----|-------| +| a | 1 | + +Done.`; + + const result = processLineMessage(text); + + expect(result.flexMessages).toHaveLength(1); + expect(result.flexMessages[0].type).toBe("flex"); + expect(result.text).toContain("Here's the data:"); + expect(result.text).toContain("Done."); + expect(result.text).not.toContain("|"); + }); + + it("processes text with code blocks", () => { + const text = `Check this code: + +\`\`\`js +console.log("hi"); +\`\`\` + +That's it.`; + + const result = processLineMessage(text); + + expect(result.flexMessages).toHaveLength(1); + expect(result.text).toContain("Check this code:"); + expect(result.text).toContain("That's it."); + expect(result.text).not.toContain("```"); + }); + + it("processes text with markdown formatting", () => { + const text = "This is **bold** and *italic* text."; + + const result = processLineMessage(text); + + expect(result.text).toBe("This is bold and italic text."); + expect(result.flexMessages).toHaveLength(0); + }); + + it("handles mixed content", () => { + const text = `# Summary + +Here's **important** info: + +| Item | Count | +|------|-------| +| A | 5 | + +\`\`\`python +print("done") +\`\`\` + +> Note: Check the link [here](https://example.com).`; + + const result = processLineMessage(text); + + // Should have 2 flex messages (table + code) + expect(result.flexMessages).toHaveLength(2); + + // Text should be cleaned + expect(result.text).toContain("Summary"); + expect(result.text).toContain("important"); + expect(result.text).toContain("Note: Check the link here."); + expect(result.text).not.toContain("#"); + expect(result.text).not.toContain("**"); + expect(result.text).not.toContain("|"); + expect(result.text).not.toContain("```"); + expect(result.text).not.toContain("[here]"); + }); + + it("handles plain text unchanged", () => { + const text = "Just plain text with no markdown."; + + const result = processLineMessage(text); + + expect(result.text).toBe(text); + expect(result.flexMessages).toHaveLength(0); + }); +}); + +describe("hasMarkdownToConvert", () => { + it("detects tables", () => { + const text = `| A | B | +|---|---| +| 1 | 2 |`; + expect(hasMarkdownToConvert(text)).toBe(true); + }); + + it("detects code blocks", () => { + const text = "```js\ncode\n```"; + expect(hasMarkdownToConvert(text)).toBe(true); + }); + + it("detects bold", () => { + expect(hasMarkdownToConvert("**bold**")).toBe(true); + }); + + it("detects strikethrough", () => { + expect(hasMarkdownToConvert("~~deleted~~")).toBe(true); + }); + + it("detects headers", () => { + expect(hasMarkdownToConvert("# Title")).toBe(true); + }); + + it("detects blockquotes", () => { + expect(hasMarkdownToConvert("> quote")).toBe(true); + }); + + it("returns false for plain text", () => { + expect(hasMarkdownToConvert("Just plain text.")).toBe(false); + }); +}); diff --git a/src/line/markdown-to-line.ts b/src/line/markdown-to-line.ts new file mode 100644 index 00000000000..21253c36a92 --- /dev/null +++ b/src/line/markdown-to-line.ts @@ -0,0 +1,433 @@ +import type { messagingApi } from "@line/bot-sdk"; +import { createReceiptCard, toFlexMessage, type FlexBubble } from "./flex-templates.js"; + +type FlexMessage = messagingApi.FlexMessage; +type FlexComponent = messagingApi.FlexComponent; +type FlexText = messagingApi.FlexText; +type FlexBox = messagingApi.FlexBox; + +export interface ProcessedLineMessage { + /** The processed text with markdown stripped */ + text: string; + /** Flex messages extracted from tables/code blocks */ + flexMessages: FlexMessage[]; +} + +/** + * Regex patterns for markdown detection + */ +const MARKDOWN_TABLE_REGEX = /^\|(.+)\|[\r\n]+\|[-:\s|]+\|[\r\n]+((?:\|.+\|[\r\n]*)+)/gm; +const MARKDOWN_CODE_BLOCK_REGEX = /```(\w*)\n([\s\S]*?)```/g; +const MARKDOWN_LINK_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g; + +/** + * Detect and extract markdown tables from text + */ +export function extractMarkdownTables(text: string): { + tables: MarkdownTable[]; + textWithoutTables: string; +} { + const tables: MarkdownTable[] = []; + let textWithoutTables = text; + + // Reset regex state + MARKDOWN_TABLE_REGEX.lastIndex = 0; + + let match: RegExpExecArray | null; + const matches: { fullMatch: string; table: MarkdownTable }[] = []; + + while ((match = MARKDOWN_TABLE_REGEX.exec(text)) !== null) { + const fullMatch = match[0]; + const headerLine = match[1]; + const bodyLines = match[2]; + + const headers = parseTableRow(headerLine); + const rows = bodyLines + .trim() + .split(/[\r\n]+/) + .filter((line) => line.trim()) + .map(parseTableRow); + + if (headers.length > 0 && rows.length > 0) { + matches.push({ + fullMatch, + table: { headers, rows }, + }); + } + } + + // Remove tables from text in reverse order to preserve indices + for (let i = matches.length - 1; i >= 0; i--) { + const { fullMatch, table } = matches[i]; + tables.unshift(table); + textWithoutTables = textWithoutTables.replace(fullMatch, ""); + } + + return { tables, textWithoutTables }; +} + +export interface MarkdownTable { + headers: string[]; + rows: string[][]; +} + +/** + * Parse a single table row (pipe-separated values) + */ +function parseTableRow(row: string): string[] { + return row + .split("|") + .map((cell) => cell.trim()) + .filter((cell, index, arr) => { + // Filter out empty cells at start/end (from leading/trailing pipes) + if (index === 0 && cell === "") return false; + if (index === arr.length - 1 && cell === "") return false; + return true; + }); +} + +/** + * Convert a markdown table to a LINE Flex Message bubble + */ +export function convertTableToFlexBubble(table: MarkdownTable): FlexBubble { + const parseCell = ( + value: string | undefined, + ): { text: string; bold: boolean; hasMarkup: boolean } => { + const raw = value?.trim() ?? ""; + if (!raw) return { text: "-", bold: false, hasMarkup: false }; + + let hasMarkup = false; + const stripped = raw.replace(/\*\*(.+?)\*\*/g, (_, inner) => { + hasMarkup = true; + return String(inner); + }); + const text = stripped.trim() || "-"; + const bold = /^\*\*.+\*\*$/.test(raw); + + return { text, bold, hasMarkup }; + }; + + const headerCells = table.headers.map((header) => parseCell(header)); + const rowCells = table.rows.map((row) => row.map((cell) => parseCell(cell))); + const hasInlineMarkup = + headerCells.some((cell) => cell.hasMarkup) || + rowCells.some((row) => row.some((cell) => cell.hasMarkup)); + + // For simple 2-column tables, use receipt card format + if (table.headers.length === 2 && !hasInlineMarkup) { + const items = rowCells.map((row) => ({ + name: row[0]?.text ?? "-", + value: row[1]?.text ?? "-", + })); + + return createReceiptCard({ + title: headerCells.map((cell) => cell.text).join(" / "), + items, + }); + } + + // For multi-column tables, create a custom layout + const headerRow: FlexComponent = { + type: "box", + layout: "horizontal", + contents: headerCells.map((cell) => ({ + type: "text", + text: cell.text, + weight: "bold", + size: "sm", + color: "#333333", + flex: 1, + wrap: true, + })) as FlexText[], + paddingBottom: "sm", + } as FlexBox; + + const dataRows: FlexComponent[] = rowCells.slice(0, 10).map((row, rowIndex) => { + const rowContents = table.headers.map((_, colIndex) => { + const cell = row[colIndex] ?? { text: "-", bold: false, hasMarkup: false }; + return { + type: "text", + text: cell.text, + size: "sm", + color: "#666666", + flex: 1, + wrap: true, + weight: cell.bold ? "bold" : undefined, + }; + }) as FlexText[]; + + return { + type: "box", + layout: "horizontal", + contents: rowContents, + margin: rowIndex === 0 ? "md" : "sm", + } as FlexBox; + }); + + return { + type: "bubble", + body: { + type: "box", + layout: "vertical", + contents: [headerRow, { type: "separator", margin: "sm" }, ...dataRows], + paddingAll: "lg", + }, + }; +} + +/** + * Detect and extract code blocks from text + */ +export function extractCodeBlocks(text: string): { + codeBlocks: CodeBlock[]; + textWithoutCode: string; +} { + const codeBlocks: CodeBlock[] = []; + let textWithoutCode = text; + + // Reset regex state + MARKDOWN_CODE_BLOCK_REGEX.lastIndex = 0; + + let match: RegExpExecArray | null; + const matches: { fullMatch: string; block: CodeBlock }[] = []; + + while ((match = MARKDOWN_CODE_BLOCK_REGEX.exec(text)) !== null) { + const fullMatch = match[0]; + const language = match[1] || undefined; + const code = match[2]; + + matches.push({ + fullMatch, + block: { language, code: code.trim() }, + }); + } + + // Remove code blocks in reverse order + for (let i = matches.length - 1; i >= 0; i--) { + const { fullMatch, block } = matches[i]; + codeBlocks.unshift(block); + textWithoutCode = textWithoutCode.replace(fullMatch, ""); + } + + return { codeBlocks, textWithoutCode }; +} + +export interface CodeBlock { + language?: string; + code: string; +} + +/** + * Convert a code block to a LINE Flex Message bubble + */ +export function convertCodeBlockToFlexBubble(block: CodeBlock): FlexBubble { + const titleText = block.language ? `Code (${block.language})` : "Code"; + + // Truncate very long code to fit LINE's limits + const displayCode = block.code.length > 2000 ? block.code.slice(0, 2000) + "\n..." : block.code; + + return { + type: "bubble", + body: { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: titleText, + weight: "bold", + size: "sm", + color: "#666666", + } as FlexText, + { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: displayCode, + size: "xs", + color: "#333333", + wrap: true, + } as FlexText, + ], + backgroundColor: "#F5F5F5", + paddingAll: "md", + cornerRadius: "md", + margin: "sm", + } as FlexBox, + ], + paddingAll: "lg", + }, + }; +} + +/** + * Extract markdown links from text + */ +export function extractLinks(text: string): { links: MarkdownLink[]; textWithLinks: string } { + const links: MarkdownLink[] = []; + + // Reset regex state + MARKDOWN_LINK_REGEX.lastIndex = 0; + + let match: RegExpExecArray | null; + while ((match = MARKDOWN_LINK_REGEX.exec(text)) !== null) { + links.push({ + text: match[1], + url: match[2], + }); + } + + // Replace markdown links with just the text (for plain text output) + const textWithLinks = text.replace(MARKDOWN_LINK_REGEX, "$1"); + + return { links, textWithLinks }; +} + +export interface MarkdownLink { + text: string; + url: string; +} + +/** + * Create a Flex Message with tappable link buttons + */ +export function convertLinksToFlexBubble(links: MarkdownLink[]): FlexBubble { + const buttons: FlexComponent[] = links.slice(0, 4).map((link, index) => ({ + type: "button", + action: { + type: "uri", + label: link.text.slice(0, 20), // LINE button label limit + uri: link.url, + }, + style: index === 0 ? "primary" : "secondary", + margin: index > 0 ? "sm" : undefined, + })); + + return { + type: "bubble", + body: { + type: "box", + layout: "vertical", + contents: [ + { + type: "text", + text: "Links", + weight: "bold", + size: "md", + color: "#333333", + } as FlexText, + ], + paddingAll: "lg", + paddingBottom: "sm", + }, + footer: { + type: "box", + layout: "vertical", + contents: buttons, + paddingAll: "md", + }, + }; +} + +/** + * Strip markdown formatting from text (for plain text output) + * Handles: bold, italic, strikethrough, headers, blockquotes, horizontal rules + */ +export function stripMarkdown(text: string): string { + let result = text; + + // Remove bold: **text** or __text__ + result = result.replace(/\*\*(.+?)\*\*/g, "$1"); + result = result.replace(/__(.+?)__/g, "$1"); + + // Remove italic: *text* or _text_ (but not already processed) + result = result.replace(/(? text + result = result.replace(/^>\s?(.*)$/gm, "$1"); + + // Remove horizontal rules: ---, ***, ___ + result = result.replace(/^[-*_]{3,}$/gm, ""); + + // Remove inline code: `code` + result = result.replace(/`([^`]+)`/g, "$1"); + + // Clean up extra whitespace + result = result.replace(/\n{3,}/g, "\n\n"); + result = result.trim(); + + return result; +} + +/** + * Main function: Process text for LINE output + * - Extracts tables → Flex Messages + * - Extracts code blocks → Flex Messages + * - Strips remaining markdown + * - Returns processed text + Flex Messages + */ +export function processLineMessage(text: string): ProcessedLineMessage { + const flexMessages: FlexMessage[] = []; + let processedText = text; + + // 1. Extract and convert tables + const { tables, textWithoutTables } = extractMarkdownTables(processedText); + processedText = textWithoutTables; + + for (const table of tables) { + const bubble = convertTableToFlexBubble(table); + flexMessages.push(toFlexMessage("Table", bubble)); + } + + // 2. Extract and convert code blocks + const { codeBlocks, textWithoutCode } = extractCodeBlocks(processedText); + processedText = textWithoutCode; + + for (const block of codeBlocks) { + const bubble = convertCodeBlockToFlexBubble(block); + flexMessages.push(toFlexMessage("Code", bubble)); + } + + // 3. Handle links - convert [text](url) to plain text for display + // (We could also create link buttons, but that can get noisy) + const { textWithLinks } = extractLinks(processedText); + processedText = textWithLinks; + + // 4. Strip remaining markdown formatting + processedText = stripMarkdown(processedText); + + return { + text: processedText, + flexMessages, + }; +} + +/** + * Check if text contains markdown that needs conversion + */ +export function hasMarkdownToConvert(text: string): boolean { + // Check for tables + MARKDOWN_TABLE_REGEX.lastIndex = 0; + if (MARKDOWN_TABLE_REGEX.test(text)) return true; + + // Check for code blocks + MARKDOWN_CODE_BLOCK_REGEX.lastIndex = 0; + if (MARKDOWN_CODE_BLOCK_REGEX.test(text)) return true; + + // Check for other markdown patterns + if (/\*\*[^*]+\*\*/.test(text)) return true; // bold + if (/~~[^~]+~~/.test(text)) return true; // strikethrough + if (/^#{1,6}\s+/m.test(text)) return true; // headers + if (/^>\s+/m.test(text)) return true; // blockquotes + + return false; +} diff --git a/src/line/monitor.ts b/src/line/monitor.ts new file mode 100644 index 00000000000..9b40e4460eb --- /dev/null +++ b/src/line/monitor.ts @@ -0,0 +1,376 @@ +import type { WebhookRequestBody } from "@line/bot-sdk"; +import type { IncomingMessage, ServerResponse } from "node:http"; +import crypto from "node:crypto"; +import type { ClawdbotConfig } from "../config/config.js"; +import { danger, logVerbose } from "../globals.js"; +import type { RuntimeEnv } from "../runtime.js"; +import { createLineBot } from "./bot.js"; +import { normalizePluginHttpPath } from "../plugins/http-path.js"; +import { registerPluginHttpRoute } from "../plugins/http-registry.js"; +import { + replyMessageLine, + showLoadingAnimation, + getUserDisplayName, + createQuickReplyItems, + createTextMessageWithQuickReplies, + pushTextMessageWithQuickReplies, + pushMessageLine, + pushMessagesLine, + createFlexMessage, + createImageMessage, + createLocationMessage, +} from "./send.js"; +import { buildTemplateMessageFromPayload } from "./template-messages.js"; +import type { LineChannelData, ResolvedLineAccount } from "./types.js"; +import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/provider-dispatcher.js"; +import { resolveEffectiveMessagesConfig } from "../agents/identity.js"; +import { chunkMarkdownText } from "../auto-reply/chunk.js"; +import { processLineMessage } from "./markdown-to-line.js"; +import { sendLineReplyChunks } from "./reply-chunks.js"; +import { deliverLineAutoReply } from "./auto-reply-delivery.js"; + +export interface MonitorLineProviderOptions { + channelAccessToken: string; + channelSecret: string; + accountId?: string; + config: ClawdbotConfig; + runtime: RuntimeEnv; + abortSignal?: AbortSignal; + webhookUrl?: string; + webhookPath?: string; +} + +export interface LineProviderMonitor { + account: ResolvedLineAccount; + handleWebhook: (body: WebhookRequestBody) => Promise; + stop: () => void; +} + +// Track runtime state in memory (simplified version) +const runtimeState = new Map< + string, + { + running: boolean; + lastStartAt: number | null; + lastStopAt: number | null; + lastError: string | null; + lastInboundAt?: number | null; + lastOutboundAt?: number | null; + } +>(); + +function recordChannelRuntimeState(params: { + channel: string; + accountId: string; + state: Partial<{ + running: boolean; + lastStartAt: number | null; + lastStopAt: number | null; + lastError: string | null; + lastInboundAt: number | null; + lastOutboundAt: number | null; + }>; +}): void { + const key = `${params.channel}:${params.accountId}`; + const existing = runtimeState.get(key) ?? { + running: false, + lastStartAt: null, + lastStopAt: null, + lastError: null, + }; + runtimeState.set(key, { ...existing, ...params.state }); +} + +export function getLineRuntimeState(accountId: string) { + return runtimeState.get(`line:${accountId}`); +} + +function validateLineSignature(body: string, signature: string, channelSecret: string): boolean { + const hash = crypto.createHmac("SHA256", channelSecret).update(body).digest("base64"); + return hash === signature; +} + +async function readRequestBody(req: IncomingMessage): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + req.on("data", (chunk) => chunks.push(chunk)); + req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8"))); + req.on("error", reject); + }); +} + +function startLineLoadingKeepalive(params: { + userId: string; + accountId?: string; + intervalMs?: number; + loadingSeconds?: number; +}): () => void { + const intervalMs = params.intervalMs ?? 18_000; + const loadingSeconds = params.loadingSeconds ?? 20; + let stopped = false; + + const trigger = () => { + if (stopped) return; + void showLoadingAnimation(params.userId, { + accountId: params.accountId, + loadingSeconds, + }).catch(() => {}); + }; + + trigger(); + const timer = setInterval(trigger, intervalMs); + + return () => { + if (stopped) return; + stopped = true; + clearInterval(timer); + }; +} + +export async function monitorLineProvider( + opts: MonitorLineProviderOptions, +): Promise { + const { + channelAccessToken, + channelSecret, + accountId, + config, + runtime, + abortSignal, + webhookPath, + } = opts; + const resolvedAccountId = accountId ?? "default"; + + // Record starting state + recordChannelRuntimeState({ + channel: "line", + accountId: resolvedAccountId, + state: { + running: true, + lastStartAt: Date.now(), + }, + }); + + // Create the bot + const bot = createLineBot({ + channelAccessToken, + channelSecret, + accountId, + runtime, + config, + onMessage: async (ctx) => { + if (!ctx) return; + + const { ctxPayload, replyToken, route } = ctx; + + // Record inbound activity + recordChannelRuntimeState({ + channel: "line", + accountId: resolvedAccountId, + state: { + lastInboundAt: Date.now(), + }, + }); + + const shouldShowLoading = Boolean(ctx.userId && !ctx.isGroup); + + // Fetch display name for logging (non-blocking) + const displayNamePromise = ctx.userId + ? getUserDisplayName(ctx.userId, { accountId: ctx.accountId }) + : Promise.resolve(ctxPayload.From); + + // Show loading animation while processing (non-blocking, best-effort) + const stopLoading = shouldShowLoading + ? startLineLoadingKeepalive({ userId: ctx.userId!, accountId: ctx.accountId }) + : null; + + const displayName = await displayNamePromise; + logVerbose(`line: received message from ${displayName} (${ctxPayload.From})`); + + // Dispatch to auto-reply system for AI response + try { + const textLimit = 5000; // LINE max message length + let replyTokenUsed = false; // Track if we've used the one-time reply token + + const { queuedFinal } = await dispatchReplyWithBufferedBlockDispatcher({ + ctx: ctxPayload, + cfg: config, + dispatcherOptions: { + responsePrefix: resolveEffectiveMessagesConfig(config, route.agentId).responsePrefix, + deliver: async (payload, _info) => { + const lineData = (payload.channelData?.line as LineChannelData | undefined) ?? {}; + + // Show loading animation before each delivery (non-blocking) + if (ctx.userId && !ctx.isGroup) { + void showLoadingAnimation(ctx.userId, { accountId: ctx.accountId }).catch(() => {}); + } + + const { replyTokenUsed: nextReplyTokenUsed } = await deliverLineAutoReply({ + payload, + lineData, + to: ctxPayload.From, + replyToken, + replyTokenUsed, + accountId: ctx.accountId, + textLimit, + deps: { + buildTemplateMessageFromPayload, + processLineMessage, + chunkMarkdownText, + sendLineReplyChunks, + replyMessageLine, + pushMessageLine, + pushTextMessageWithQuickReplies, + createQuickReplyItems, + createTextMessageWithQuickReplies, + pushMessagesLine, + createFlexMessage, + createImageMessage, + createLocationMessage, + onReplyError: (replyErr) => { + logVerbose( + `line: reply token failed, falling back to push: ${String(replyErr)}`, + ); + }, + }, + }); + replyTokenUsed = nextReplyTokenUsed; + + recordChannelRuntimeState({ + channel: "line", + accountId: resolvedAccountId, + state: { + lastOutboundAt: Date.now(), + }, + }); + }, + onError: (err, info) => { + runtime.error?.(danger(`line ${info.kind} reply failed: ${String(err)}`)); + }, + }, + replyOptions: {}, + }); + + if (!queuedFinal) { + logVerbose(`line: no response generated for message from ${ctxPayload.From}`); + } + } catch (err) { + runtime.error?.(danger(`line: auto-reply failed: ${String(err)}`)); + + // Send error message to user + if (replyToken) { + try { + await replyMessageLine( + replyToken, + [{ type: "text", text: "Sorry, I encountered an error processing your message." }], + { accountId: ctx.accountId }, + ); + } catch (replyErr) { + runtime.error?.(danger(`line: error reply failed: ${String(replyErr)}`)); + } + } + } finally { + stopLoading?.(); + } + }, + }); + + // Register HTTP webhook handler + const normalizedPath = normalizePluginHttpPath(webhookPath, "/line/webhook") ?? "/line/webhook"; + const unregisterHttp = registerPluginHttpRoute({ + path: normalizedPath, + pluginId: "line", + accountId: resolvedAccountId, + log: (msg) => logVerbose(msg), + handler: async (req: IncomingMessage, res: ServerResponse) => { + // Handle GET requests for webhook verification + if (req.method === "GET") { + res.statusCode = 200; + res.setHeader("Content-Type", "text/plain"); + res.end("OK"); + return; + } + + // Only accept POST requests + if (req.method !== "POST") { + res.statusCode = 405; + res.setHeader("Allow", "GET, POST"); + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ error: "Method Not Allowed" })); + return; + } + + try { + const rawBody = await readRequestBody(req); + const signature = req.headers["x-line-signature"]; + + // Validate signature + if (!signature || typeof signature !== "string") { + logVerbose("line: webhook missing X-Line-Signature header"); + res.statusCode = 400; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ error: "Missing X-Line-Signature header" })); + return; + } + + if (!validateLineSignature(rawBody, signature, channelSecret)) { + logVerbose("line: webhook signature validation failed"); + res.statusCode = 401; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ error: "Invalid signature" })); + return; + } + + // Parse and process the webhook body + const body = JSON.parse(rawBody) as WebhookRequestBody; + + // Respond immediately with 200 to avoid LINE timeout + res.statusCode = 200; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ status: "ok" })); + + // Process events asynchronously + if (body.events && body.events.length > 0) { + logVerbose(`line: received ${body.events.length} webhook events`); + await bot.handleWebhook(body).catch((err) => { + runtime.error?.(danger(`line webhook handler failed: ${String(err)}`)); + }); + } + } catch (err) { + runtime.error?.(danger(`line webhook error: ${String(err)}`)); + if (!res.headersSent) { + res.statusCode = 500; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ error: "Internal server error" })); + } + } + }, + }); + + logVerbose(`line: registered webhook handler at ${normalizedPath}`); + + // Handle abort signal + const stopHandler = () => { + logVerbose(`line: stopping provider for account ${resolvedAccountId}`); + unregisterHttp(); + recordChannelRuntimeState({ + channel: "line", + accountId: resolvedAccountId, + state: { + running: false, + lastStopAt: Date.now(), + }, + }); + }; + + abortSignal?.addEventListener("abort", stopHandler); + + return { + account: bot.account, + handleWebhook: bot.handleWebhook, + stop: () => { + stopHandler(); + abortSignal?.removeEventListener("abort", stopHandler); + }, + }; +} diff --git a/src/line/probe.test.ts b/src/line/probe.test.ts new file mode 100644 index 00000000000..732295e39e0 --- /dev/null +++ b/src/line/probe.test.ts @@ -0,0 +1,51 @@ +import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; +const { getBotInfoMock, MessagingApiClientMock } = vi.hoisted(() => { + const getBotInfoMock = vi.fn(); + const MessagingApiClientMock = vi.fn(function () { + return { getBotInfo: getBotInfoMock }; + }); + return { getBotInfoMock, MessagingApiClientMock }; +}); + +vi.mock("@line/bot-sdk", () => ({ + messagingApi: { MessagingApiClient: MessagingApiClientMock }, +})); + +let probeLineBot: typeof import("./probe.js").probeLineBot; + +afterEach(() => { + vi.useRealTimers(); + getBotInfoMock.mockReset(); +}); + +describe("probeLineBot", () => { + beforeAll(async () => { + ({ probeLineBot } = await import("./probe.js")); + }); + + it("returns timeout when bot info stalls", async () => { + vi.useFakeTimers(); + getBotInfoMock.mockImplementation(() => new Promise(() => {})); + + const probePromise = probeLineBot("token", 10); + await vi.advanceTimersByTimeAsync(20); + const result = await probePromise; + + expect(result.ok).toBe(false); + expect(result.error).toBe("timeout"); + }); + + it("returns bot info when available", async () => { + getBotInfoMock.mockResolvedValue({ + displayName: "Clawdbot", + userId: "U123", + basicId: "@clawdbot", + pictureUrl: "https://example.com/bot.png", + }); + + const result = await probeLineBot("token", 50); + + expect(result.ok).toBe(true); + expect(result.bot?.userId).toBe("U123"); + }); +}); diff --git a/src/line/probe.ts b/src/line/probe.ts new file mode 100644 index 00000000000..d538d4271f2 --- /dev/null +++ b/src/line/probe.ts @@ -0,0 +1,43 @@ +import { messagingApi } from "@line/bot-sdk"; +import type { LineProbeResult } from "./types.js"; + +export async function probeLineBot( + channelAccessToken: string, + timeoutMs = 5000, +): Promise { + if (!channelAccessToken?.trim()) { + return { ok: false, error: "Channel access token not configured" }; + } + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: channelAccessToken.trim(), + }); + + try { + const profile = await withTimeout(client.getBotInfo(), timeoutMs); + + return { + ok: true, + bot: { + displayName: profile.displayName, + userId: profile.userId, + basicId: profile.basicId, + pictureUrl: profile.pictureUrl, + }, + }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + return { ok: false, error: message }; + } +} + +function withTimeout(promise: Promise, timeoutMs: number): Promise { + if (!timeoutMs || timeoutMs <= 0) return promise; + let timer: NodeJS.Timeout | null = null; + const timeout = new Promise((_, reject) => { + timer = setTimeout(() => reject(new Error("timeout")), timeoutMs); + }); + return Promise.race([promise, timeout]).finally(() => { + if (timer) clearTimeout(timer); + }); +} diff --git a/src/line/reply-chunks.test.ts b/src/line/reply-chunks.test.ts new file mode 100644 index 00000000000..60e03e688bb --- /dev/null +++ b/src/line/reply-chunks.test.ts @@ -0,0 +1,115 @@ +import { describe, expect, it, vi } from "vitest"; +import { sendLineReplyChunks } from "./reply-chunks.js"; + +describe("sendLineReplyChunks", () => { + it("uses reply token for all chunks when possible", async () => { + const replyMessageLine = vi.fn(async () => ({})); + const pushMessageLine = vi.fn(async () => ({})); + const pushTextMessageWithQuickReplies = vi.fn(async () => ({})); + const createTextMessageWithQuickReplies = vi.fn((text: string, _quickReplies: string[]) => ({ + type: "text" as const, + text, + })); + + const result = await sendLineReplyChunks({ + to: "line:group:1", + chunks: ["one", "two", "three"], + quickReplies: ["A", "B"], + replyToken: "token", + replyTokenUsed: false, + accountId: "default", + replyMessageLine, + pushMessageLine, + pushTextMessageWithQuickReplies, + createTextMessageWithQuickReplies, + }); + + expect(result.replyTokenUsed).toBe(true); + expect(replyMessageLine).toHaveBeenCalledTimes(1); + expect(createTextMessageWithQuickReplies).toHaveBeenCalledWith("three", ["A", "B"]); + expect(replyMessageLine).toHaveBeenCalledWith( + "token", + [ + { type: "text", text: "one" }, + { type: "text", text: "two" }, + { type: "text", text: "three" }, + ], + { accountId: "default" }, + ); + expect(pushMessageLine).not.toHaveBeenCalled(); + expect(pushTextMessageWithQuickReplies).not.toHaveBeenCalled(); + }); + + it("attaches quick replies to a single reply chunk", async () => { + const replyMessageLine = vi.fn(async () => ({})); + const pushMessageLine = vi.fn(async () => ({})); + const pushTextMessageWithQuickReplies = vi.fn(async () => ({})); + const createTextMessageWithQuickReplies = vi.fn((text: string, _quickReplies: string[]) => ({ + type: "text" as const, + text, + quickReply: { items: [] }, + })); + + const result = await sendLineReplyChunks({ + to: "line:user:1", + chunks: ["only"], + quickReplies: ["A"], + replyToken: "token", + replyTokenUsed: false, + replyMessageLine, + pushMessageLine, + pushTextMessageWithQuickReplies, + createTextMessageWithQuickReplies, + }); + + expect(result.replyTokenUsed).toBe(true); + expect(createTextMessageWithQuickReplies).toHaveBeenCalledWith("only", ["A"]); + expect(replyMessageLine).toHaveBeenCalledTimes(1); + expect(pushMessageLine).not.toHaveBeenCalled(); + expect(pushTextMessageWithQuickReplies).not.toHaveBeenCalled(); + }); + + it("replies with up to five chunks before pushing the rest", async () => { + const replyMessageLine = vi.fn(async () => ({})); + const pushMessageLine = vi.fn(async () => ({})); + const pushTextMessageWithQuickReplies = vi.fn(async () => ({})); + const createTextMessageWithQuickReplies = vi.fn((text: string, _quickReplies: string[]) => ({ + type: "text" as const, + text, + })); + + const chunks = ["1", "2", "3", "4", "5", "6", "7"]; + const result = await sendLineReplyChunks({ + to: "line:group:1", + chunks, + quickReplies: ["A"], + replyToken: "token", + replyTokenUsed: false, + replyMessageLine, + pushMessageLine, + pushTextMessageWithQuickReplies, + createTextMessageWithQuickReplies, + }); + + expect(result.replyTokenUsed).toBe(true); + expect(replyMessageLine).toHaveBeenCalledTimes(1); + expect(replyMessageLine).toHaveBeenCalledWith( + "token", + [ + { type: "text", text: "1" }, + { type: "text", text: "2" }, + { type: "text", text: "3" }, + { type: "text", text: "4" }, + { type: "text", text: "5" }, + ], + { accountId: undefined }, + ); + expect(pushMessageLine).toHaveBeenCalledTimes(1); + expect(pushMessageLine).toHaveBeenCalledWith("line:group:1", "6", { accountId: undefined }); + expect(pushTextMessageWithQuickReplies).toHaveBeenCalledTimes(1); + expect(pushTextMessageWithQuickReplies).toHaveBeenCalledWith("line:group:1", "7", ["A"], { + accountId: undefined, + }); + expect(createTextMessageWithQuickReplies).not.toHaveBeenCalled(); + }); +}); diff --git a/src/line/reply-chunks.ts b/src/line/reply-chunks.ts new file mode 100644 index 00000000000..e4d5c4b9db7 --- /dev/null +++ b/src/line/reply-chunks.ts @@ -0,0 +1,101 @@ +import type { messagingApi } from "@line/bot-sdk"; + +export type LineReplyMessage = messagingApi.TextMessage; + +export type SendLineReplyChunksParams = { + to: string; + chunks: string[]; + quickReplies?: string[]; + replyToken?: string | null; + replyTokenUsed?: boolean; + accountId?: string; + replyMessageLine: ( + replyToken: string, + messages: messagingApi.Message[], + opts?: { accountId?: string }, + ) => Promise; + pushMessageLine: (to: string, text: string, opts?: { accountId?: string }) => Promise; + pushTextMessageWithQuickReplies: ( + to: string, + text: string, + quickReplies: string[], + opts?: { accountId?: string }, + ) => Promise; + createTextMessageWithQuickReplies: (text: string, quickReplies: string[]) => LineReplyMessage; + onReplyError?: (err: unknown) => void; +}; + +export async function sendLineReplyChunks( + params: SendLineReplyChunksParams, +): Promise<{ replyTokenUsed: boolean }> { + const hasQuickReplies = Boolean(params.quickReplies?.length); + let replyTokenUsed = Boolean(params.replyTokenUsed); + + if (params.chunks.length === 0) { + return { replyTokenUsed }; + } + + if (params.replyToken && !replyTokenUsed) { + try { + const replyBatch = params.chunks.slice(0, 5); + const remaining = params.chunks.slice(replyBatch.length); + + const replyMessages: LineReplyMessage[] = replyBatch.map((chunk) => ({ + type: "text", + text: chunk, + })); + + if (hasQuickReplies && remaining.length === 0 && replyMessages.length > 0) { + const lastIndex = replyMessages.length - 1; + replyMessages[lastIndex] = params.createTextMessageWithQuickReplies( + replyBatch[lastIndex]!, + params.quickReplies!, + ); + } + + await params.replyMessageLine(params.replyToken, replyMessages, { + accountId: params.accountId, + }); + replyTokenUsed = true; + + for (let i = 0; i < remaining.length; i += 1) { + const isLastChunk = i === remaining.length - 1; + if (isLastChunk && hasQuickReplies) { + await params.pushTextMessageWithQuickReplies( + params.to, + remaining[i]!, + params.quickReplies!, + { accountId: params.accountId }, + ); + } else { + await params.pushMessageLine(params.to, remaining[i]!, { + accountId: params.accountId, + }); + } + } + + return { replyTokenUsed }; + } catch (err) { + params.onReplyError?.(err); + replyTokenUsed = true; + } + } + + for (let i = 0; i < params.chunks.length; i += 1) { + const isLastChunk = i === params.chunks.length - 1; + if (isLastChunk && hasQuickReplies) { + await params.pushTextMessageWithQuickReplies( + params.to, + params.chunks[i]!, + params.quickReplies!, + { accountId: params.accountId }, + ); + } else { + await params.pushMessageLine(params.to, params.chunks[i]!, { + accountId: params.accountId, + }); + } + } + + return { replyTokenUsed }; +} diff --git a/src/line/rich-menu.test.ts b/src/line/rich-menu.test.ts new file mode 100644 index 00000000000..96b069f345d --- /dev/null +++ b/src/line/rich-menu.test.ts @@ -0,0 +1,247 @@ +import { describe, expect, it } from "vitest"; +import { + createGridLayout, + messageAction, + uriAction, + postbackAction, + datetimePickerAction, + createDefaultMenuConfig, +} from "./rich-menu.js"; + +describe("messageAction", () => { + it("creates a message action", () => { + const action = messageAction("Help", "/help"); + + expect(action.type).toBe("message"); + expect(action.label).toBe("Help"); + expect((action as { text: string }).text).toBe("/help"); + }); + + it("uses label as text when text not provided", () => { + const action = messageAction("Click"); + + expect((action as { text: string }).text).toBe("Click"); + }); + + it("truncates label to 20 characters", () => { + const action = messageAction("This is a very long label text"); + + expect(action.label.length).toBe(20); + expect(action.label).toBe("This is a very long "); + }); +}); + +describe("uriAction", () => { + it("creates a URI action", () => { + const action = uriAction("Open", "https://example.com"); + + expect(action.type).toBe("uri"); + expect(action.label).toBe("Open"); + expect((action as { uri: string }).uri).toBe("https://example.com"); + }); + + it("truncates label to 20 characters", () => { + const action = uriAction("Click here to visit our website", "https://example.com"); + + expect(action.label.length).toBe(20); + }); +}); + +describe("postbackAction", () => { + it("creates a postback action", () => { + const action = postbackAction("Select", "action=select&item=1", "Selected item 1"); + + expect(action.type).toBe("postback"); + expect(action.label).toBe("Select"); + expect((action as { data: string }).data).toBe("action=select&item=1"); + expect((action as { displayText: string }).displayText).toBe("Selected item 1"); + }); + + it("truncates data to 300 characters", () => { + const longData = "x".repeat(400); + const action = postbackAction("Test", longData); + + expect((action as { data: string }).data.length).toBe(300); + }); + + it("truncates displayText to 300 characters", () => { + const longText = "y".repeat(400); + const action = postbackAction("Test", "data", longText); + + expect((action as { displayText: string }).displayText?.length).toBe(300); + }); + + it("omits displayText when not provided", () => { + const action = postbackAction("Test", "data"); + + expect((action as { displayText?: string }).displayText).toBeUndefined(); + }); +}); + +describe("datetimePickerAction", () => { + it("creates a date picker action", () => { + const action = datetimePickerAction("Pick date", "date_picked", "date"); + + expect(action.type).toBe("datetimepicker"); + expect(action.label).toBe("Pick date"); + expect((action as { mode: string }).mode).toBe("date"); + expect((action as { data: string }).data).toBe("date_picked"); + }); + + it("creates a time picker action", () => { + const action = datetimePickerAction("Pick time", "time_picked", "time"); + + expect((action as { mode: string }).mode).toBe("time"); + }); + + it("creates a datetime picker action", () => { + const action = datetimePickerAction("Pick datetime", "datetime_picked", "datetime"); + + expect((action as { mode: string }).mode).toBe("datetime"); + }); + + it("includes initial/min/max when provided", () => { + const action = datetimePickerAction("Pick", "data", "date", { + initial: "2024-06-15", + min: "2024-01-01", + max: "2024-12-31", + }); + + expect((action as { initial: string }).initial).toBe("2024-06-15"); + expect((action as { min: string }).min).toBe("2024-01-01"); + expect((action as { max: string }).max).toBe("2024-12-31"); + }); +}); + +describe("createGridLayout", () => { + it("creates a 2x3 grid layout for tall menu", () => { + const actions = [ + messageAction("A1"), + messageAction("A2"), + messageAction("A3"), + messageAction("A4"), + messageAction("A5"), + messageAction("A6"), + ] as [ + ReturnType, + ReturnType, + ReturnType, + ReturnType, + ReturnType, + ReturnType, + ]; + + const areas = createGridLayout(1686, actions); + + expect(areas.length).toBe(6); + + // Check first row positions + expect(areas[0].bounds.x).toBe(0); + expect(areas[0].bounds.y).toBe(0); + expect(areas[1].bounds.x).toBe(833); + expect(areas[1].bounds.y).toBe(0); + expect(areas[2].bounds.x).toBe(1666); + expect(areas[2].bounds.y).toBe(0); + + // Check second row positions + expect(areas[3].bounds.y).toBe(843); + expect(areas[4].bounds.y).toBe(843); + expect(areas[5].bounds.y).toBe(843); + }); + + it("creates a 2x3 grid layout for short menu", () => { + const actions = [ + messageAction("A1"), + messageAction("A2"), + messageAction("A3"), + messageAction("A4"), + messageAction("A5"), + messageAction("A6"), + ] as [ + ReturnType, + ReturnType, + ReturnType, + ReturnType, + ReturnType, + ReturnType, + ]; + + const areas = createGridLayout(843, actions); + + expect(areas.length).toBe(6); + + // Row height should be half of 843 + expect(areas[0].bounds.height).toBe(421); + expect(areas[3].bounds.y).toBe(421); + }); + + it("assigns correct actions to areas", () => { + const actions = [ + messageAction("Help", "/help"), + messageAction("Status", "/status"), + messageAction("Settings", "/settings"), + messageAction("About", "/about"), + messageAction("Feedback", "/feedback"), + messageAction("Contact", "/contact"), + ] as [ + ReturnType, + ReturnType, + ReturnType, + ReturnType, + ReturnType, + ReturnType, + ]; + + const areas = createGridLayout(843, actions); + + expect((areas[0].action as { text: string }).text).toBe("/help"); + expect((areas[1].action as { text: string }).text).toBe("/status"); + expect((areas[2].action as { text: string }).text).toBe("/settings"); + expect((areas[3].action as { text: string }).text).toBe("/about"); + expect((areas[4].action as { text: string }).text).toBe("/feedback"); + expect((areas[5].action as { text: string }).text).toBe("/contact"); + }); +}); + +describe("createDefaultMenuConfig", () => { + it("creates a valid default menu configuration", () => { + const config = createDefaultMenuConfig(); + + expect(config.size.width).toBe(2500); + expect(config.size.height).toBe(843); + expect(config.selected).toBe(false); + expect(config.name).toBe("Default Menu"); + expect(config.chatBarText).toBe("Menu"); + expect(config.areas.length).toBe(6); + }); + + it("has valid area bounds", () => { + const config = createDefaultMenuConfig(); + + for (const area of config.areas) { + expect(area.bounds.x).toBeGreaterThanOrEqual(0); + expect(area.bounds.y).toBeGreaterThanOrEqual(0); + expect(area.bounds.width).toBeGreaterThan(0); + expect(area.bounds.height).toBeGreaterThan(0); + expect(area.bounds.x + area.bounds.width).toBeLessThanOrEqual(2500); + expect(area.bounds.y + area.bounds.height).toBeLessThanOrEqual(843); + } + }); + + it("has message actions for all areas", () => { + const config = createDefaultMenuConfig(); + + for (const area of config.areas) { + expect(area.action.type).toBe("message"); + } + }); + + it("has expected default commands", () => { + const config = createDefaultMenuConfig(); + + const commands = config.areas.map((a) => (a.action as { text: string }).text); + expect(commands).toContain("/help"); + expect(commands).toContain("/status"); + expect(commands).toContain("/settings"); + }); +}); diff --git a/src/line/rich-menu.ts b/src/line/rich-menu.ts new file mode 100644 index 00000000000..6149405a90a --- /dev/null +++ b/src/line/rich-menu.ts @@ -0,0 +1,463 @@ +import { messagingApi } from "@line/bot-sdk"; +import { readFile } from "node:fs/promises"; +import { loadConfig } from "../config/config.js"; +import { logVerbose } from "../globals.js"; +import { resolveLineAccount } from "./accounts.js"; + +type RichMenuRequest = messagingApi.RichMenuRequest; +type RichMenuResponse = messagingApi.RichMenuResponse; +type RichMenuArea = messagingApi.RichMenuArea; +type Action = messagingApi.Action; + +export interface RichMenuSize { + width: 2500; + height: 1686 | 843; +} + +export interface RichMenuAreaRequest { + bounds: { + x: number; + y: number; + width: number; + height: number; + }; + action: Action; +} + +export interface CreateRichMenuParams { + size: RichMenuSize; + selected?: boolean; + name: string; + chatBarText: string; + areas: RichMenuAreaRequest[]; +} + +interface RichMenuOpts { + channelAccessToken?: string; + accountId?: string; + verbose?: boolean; +} + +function resolveToken( + explicit: string | undefined, + params: { accountId: string; channelAccessToken: string }, +): string { + if (explicit?.trim()) return explicit.trim(); + if (!params.channelAccessToken) { + throw new Error( + `LINE channel access token missing for account "${params.accountId}" (set channels.line.channelAccessToken or LINE_CHANNEL_ACCESS_TOKEN).`, + ); + } + return params.channelAccessToken.trim(); +} + +function getClient(opts: RichMenuOpts = {}): messagingApi.MessagingApiClient { + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + + return new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); +} + +function getBlobClient(opts: RichMenuOpts = {}): messagingApi.MessagingApiBlobClient { + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + + return new messagingApi.MessagingApiBlobClient({ + channelAccessToken: token, + }); +} + +/** + * Create a new rich menu + * @returns The rich menu ID + */ +export async function createRichMenu( + menu: CreateRichMenuParams, + opts: RichMenuOpts = {}, +): Promise { + const client = getClient(opts); + + const richMenuRequest: RichMenuRequest = { + size: menu.size, + selected: menu.selected ?? false, + name: menu.name.slice(0, 300), // LINE limit + chatBarText: menu.chatBarText.slice(0, 14), // LINE limit + areas: menu.areas as RichMenuArea[], + }; + + const response = await client.createRichMenu(richMenuRequest); + + if (opts.verbose) { + logVerbose(`line: created rich menu ${response.richMenuId}`); + } + + return response.richMenuId; +} + +/** + * Upload an image for a rich menu + * Image requirements: + * - Format: JPEG or PNG + * - Size: Must match the rich menu size (2500x1686 or 2500x843) + * - Max file size: 1MB + */ +export async function uploadRichMenuImage( + richMenuId: string, + imagePath: string, + opts: RichMenuOpts = {}, +): Promise { + const blobClient = getBlobClient(opts); + + const imageData = await readFile(imagePath); + const contentType = imagePath.toLowerCase().endsWith(".png") ? "image/png" : "image/jpeg"; + + await blobClient.setRichMenuImage(richMenuId, new Blob([imageData], { type: contentType })); + + if (opts.verbose) { + logVerbose(`line: uploaded image to rich menu ${richMenuId}`); + } +} + +/** + * Set the default rich menu for all users + */ +export async function setDefaultRichMenu( + richMenuId: string, + opts: RichMenuOpts = {}, +): Promise { + const client = getClient(opts); + + await client.setDefaultRichMenu(richMenuId); + + if (opts.verbose) { + logVerbose(`line: set default rich menu to ${richMenuId}`); + } +} + +/** + * Cancel the default rich menu + */ +export async function cancelDefaultRichMenu(opts: RichMenuOpts = {}): Promise { + const client = getClient(opts); + + await client.cancelDefaultRichMenu(); + + if (opts.verbose) { + logVerbose(`line: cancelled default rich menu`); + } +} + +/** + * Get the default rich menu ID + */ +export async function getDefaultRichMenuId(opts: RichMenuOpts = {}): Promise { + const client = getClient(opts); + + try { + const response = await client.getDefaultRichMenuId(); + return response.richMenuId ?? null; + } catch { + return null; + } +} + +/** + * Link a rich menu to a specific user + */ +export async function linkRichMenuToUser( + userId: string, + richMenuId: string, + opts: RichMenuOpts = {}, +): Promise { + const client = getClient(opts); + + await client.linkRichMenuIdToUser(userId, richMenuId); + + if (opts.verbose) { + logVerbose(`line: linked rich menu ${richMenuId} to user ${userId}`); + } +} + +/** + * Link a rich menu to multiple users (up to 500) + */ +export async function linkRichMenuToUsers( + userIds: string[], + richMenuId: string, + opts: RichMenuOpts = {}, +): Promise { + const client = getClient(opts); + + // LINE allows max 500 users per request + const batches = []; + for (let i = 0; i < userIds.length; i += 500) { + batches.push(userIds.slice(i, i + 500)); + } + + for (const batch of batches) { + await client.linkRichMenuIdToUsers({ + richMenuId, + userIds: batch, + }); + } + + if (opts.verbose) { + logVerbose(`line: linked rich menu ${richMenuId} to ${userIds.length} users`); + } +} + +/** + * Unlink a rich menu from a specific user + */ +export async function unlinkRichMenuFromUser( + userId: string, + opts: RichMenuOpts = {}, +): Promise { + const client = getClient(opts); + + await client.unlinkRichMenuIdFromUser(userId); + + if (opts.verbose) { + logVerbose(`line: unlinked rich menu from user ${userId}`); + } +} + +/** + * Unlink rich menus from multiple users (up to 500) + */ +export async function unlinkRichMenuFromUsers( + userIds: string[], + opts: RichMenuOpts = {}, +): Promise { + const client = getClient(opts); + + // LINE allows max 500 users per request + const batches = []; + for (let i = 0; i < userIds.length; i += 500) { + batches.push(userIds.slice(i, i + 500)); + } + + for (const batch of batches) { + await client.unlinkRichMenuIdFromUsers({ + userIds: batch, + }); + } + + if (opts.verbose) { + logVerbose(`line: unlinked rich menu from ${userIds.length} users`); + } +} + +/** + * Get the rich menu linked to a specific user + */ +export async function getRichMenuIdOfUser( + userId: string, + opts: RichMenuOpts = {}, +): Promise { + const client = getClient(opts); + + try { + const response = await client.getRichMenuIdOfUser(userId); + return response.richMenuId ?? null; + } catch { + return null; + } +} + +/** + * Get a list of all rich menus + */ +export async function getRichMenuList(opts: RichMenuOpts = {}): Promise { + const client = getClient(opts); + + const response = await client.getRichMenuList(); + return response.richmenus ?? []; +} + +/** + * Get a specific rich menu by ID + */ +export async function getRichMenu( + richMenuId: string, + opts: RichMenuOpts = {}, +): Promise { + const client = getClient(opts); + + try { + return await client.getRichMenu(richMenuId); + } catch { + return null; + } +} + +/** + * Delete a rich menu + */ +export async function deleteRichMenu(richMenuId: string, opts: RichMenuOpts = {}): Promise { + const client = getClient(opts); + + await client.deleteRichMenu(richMenuId); + + if (opts.verbose) { + logVerbose(`line: deleted rich menu ${richMenuId}`); + } +} + +/** + * Create a rich menu alias + */ +export async function createRichMenuAlias( + richMenuId: string, + aliasId: string, + opts: RichMenuOpts = {}, +): Promise { + const client = getClient(opts); + + await client.createRichMenuAlias({ + richMenuId, + richMenuAliasId: aliasId, + }); + + if (opts.verbose) { + logVerbose(`line: created alias ${aliasId} for rich menu ${richMenuId}`); + } +} + +/** + * Delete a rich menu alias + */ +export async function deleteRichMenuAlias(aliasId: string, opts: RichMenuOpts = {}): Promise { + const client = getClient(opts); + + await client.deleteRichMenuAlias(aliasId); + + if (opts.verbose) { + logVerbose(`line: deleted alias ${aliasId}`); + } +} + +// ============================================================================ +// Default Menu Template Helpers +// ============================================================================ + +/** + * Create a standard 2x3 grid layout for rich menu areas + * Returns 6 areas in a 2-row, 3-column layout + */ +export function createGridLayout( + height: 1686 | 843, + actions: [Action, Action, Action, Action, Action, Action], +): RichMenuAreaRequest[] { + const colWidth = Math.floor(2500 / 3); + const rowHeight = Math.floor(height / 2); + + return [ + // Top row + { bounds: { x: 0, y: 0, width: colWidth, height: rowHeight }, action: actions[0] }, + { bounds: { x: colWidth, y: 0, width: colWidth, height: rowHeight }, action: actions[1] }, + { bounds: { x: colWidth * 2, y: 0, width: colWidth, height: rowHeight }, action: actions[2] }, + // Bottom row + { bounds: { x: 0, y: rowHeight, width: colWidth, height: rowHeight }, action: actions[3] }, + { + bounds: { x: colWidth, y: rowHeight, width: colWidth, height: rowHeight }, + action: actions[4], + }, + { + bounds: { x: colWidth * 2, y: rowHeight, width: colWidth, height: rowHeight }, + action: actions[5], + }, + ]; +} + +/** + * Create a message action (sends text when tapped) + */ +export function messageAction(label: string, text?: string): Action { + return { + type: "message", + label: label.slice(0, 20), + text: text ?? label, + }; +} + +/** + * Create a URI action (opens a URL when tapped) + */ +export function uriAction(label: string, uri: string): Action { + return { + type: "uri", + label: label.slice(0, 20), + uri, + }; +} + +/** + * Create a postback action (sends data to webhook when tapped) + */ +export function postbackAction(label: string, data: string, displayText?: string): Action { + return { + type: "postback", + label: label.slice(0, 20), + data: data.slice(0, 300), + displayText: displayText?.slice(0, 300), + }; +} + +/** + * Create a datetime picker action + */ +export function datetimePickerAction( + label: string, + data: string, + mode: "date" | "time" | "datetime", + options?: { + initial?: string; + max?: string; + min?: string; + }, +): Action { + return { + type: "datetimepicker", + label: label.slice(0, 20), + data: data.slice(0, 300), + mode, + initial: options?.initial, + max: options?.max, + min: options?.min, + }; +} + +/** + * Create a default help/status/settings menu + * This is a convenience function to quickly set up a standard menu + */ +export function createDefaultMenuConfig(): CreateRichMenuParams { + return { + size: { width: 2500, height: 843 }, + selected: false, + name: "Default Menu", + chatBarText: "Menu", + areas: createGridLayout(843, [ + messageAction("Help", "/help"), + messageAction("Status", "/status"), + messageAction("Settings", "/settings"), + messageAction("About", "/about"), + messageAction("Feedback", "/feedback"), + messageAction("Contact", "/contact"), + ]), + }; +} + +// Re-export types +export type { RichMenuRequest, RichMenuResponse, RichMenuArea, Action }; diff --git a/src/line/send.test.ts b/src/line/send.test.ts new file mode 100644 index 00000000000..add3669f79f --- /dev/null +++ b/src/line/send.test.ts @@ -0,0 +1,95 @@ +import { describe, expect, it } from "vitest"; +import { + createFlexMessage, + createQuickReplyItems, + createTextMessageWithQuickReplies, +} from "./send.js"; + +describe("createFlexMessage", () => { + it("creates a flex message with alt text and contents", () => { + const contents = { + type: "bubble" as const, + body: { + type: "box" as const, + layout: "vertical" as const, + contents: [], + }, + }; + + const message = createFlexMessage("Alt text for flex", contents); + + expect(message.type).toBe("flex"); + expect(message.altText).toBe("Alt text for flex"); + expect(message.contents).toBe(contents); + }); +}); + +describe("createQuickReplyItems", () => { + it("creates quick reply items from labels", () => { + const quickReply = createQuickReplyItems(["Option 1", "Option 2", "Option 3"]); + + expect(quickReply.items).toHaveLength(3); + expect(quickReply.items[0].type).toBe("action"); + expect((quickReply.items[0].action as { label: string }).label).toBe("Option 1"); + expect((quickReply.items[0].action as { text: string }).text).toBe("Option 1"); + }); + + it("limits items to 13 (LINE maximum)", () => { + const labels = Array.from({ length: 20 }, (_, i) => `Option ${i + 1}`); + const quickReply = createQuickReplyItems(labels); + + expect(quickReply.items).toHaveLength(13); + }); + + it("truncates labels to 20 characters", () => { + const quickReply = createQuickReplyItems([ + "This is a very long option label that exceeds the limit", + ]); + + expect((quickReply.items[0].action as { label: string }).label).toBe("This is a very long "); + // Text is not truncated + expect((quickReply.items[0].action as { text: string }).text).toBe( + "This is a very long option label that exceeds the limit", + ); + }); + + it("creates message actions for each item", () => { + const quickReply = createQuickReplyItems(["A", "B"]); + + expect((quickReply.items[0].action as { type: string }).type).toBe("message"); + expect((quickReply.items[1].action as { type: string }).type).toBe("message"); + }); +}); + +describe("createTextMessageWithQuickReplies", () => { + it("creates a text message with quick replies attached", () => { + const message = createTextMessageWithQuickReplies("Choose an option:", ["Yes", "No"]); + + expect(message.type).toBe("text"); + expect(message.text).toBe("Choose an option:"); + expect(message.quickReply).toBeDefined(); + expect(message.quickReply.items).toHaveLength(2); + }); + + it("preserves text content", () => { + const longText = + "This is a longer message that asks the user to select from multiple options below."; + const message = createTextMessageWithQuickReplies(longText, ["A", "B", "C"]); + + expect(message.text).toBe(longText); + }); + + it("handles empty quick replies array", () => { + const message = createTextMessageWithQuickReplies("No options", []); + + expect(message.quickReply.items).toHaveLength(0); + }); + + it("quick replies use label as both label and text", () => { + const message = createTextMessageWithQuickReplies("Pick one:", ["Apple", "Banana"]); + + const firstAction = message.quickReply.items[0].action as { label: string; text: string }; + expect(firstAction.label).toBe("Apple"); + expect(firstAction.text).toBe("Apple"); + }); +}); diff --git a/src/line/send.ts b/src/line/send.ts new file mode 100644 index 00000000000..68be26a29eb --- /dev/null +++ b/src/line/send.ts @@ -0,0 +1,629 @@ +import { messagingApi } from "@line/bot-sdk"; +import { loadConfig } from "../config/config.js"; +import { logVerbose } from "../globals.js"; +import { recordChannelActivity } from "../infra/channel-activity.js"; +import { resolveLineAccount } from "./accounts.js"; +import type { LineSendResult } from "./types.js"; + +// Use the messaging API types directly +type Message = messagingApi.Message; +type TextMessage = messagingApi.TextMessage; +type ImageMessage = messagingApi.ImageMessage; +type LocationMessage = messagingApi.LocationMessage; +type FlexMessage = messagingApi.FlexMessage; +type FlexContainer = messagingApi.FlexContainer; +type TemplateMessage = messagingApi.TemplateMessage; +type QuickReply = messagingApi.QuickReply; +type QuickReplyItem = messagingApi.QuickReplyItem; + +// Cache for user profiles +const userProfileCache = new Map< + string, + { displayName: string; pictureUrl?: string; fetchedAt: number } +>(); +const PROFILE_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes + +interface LineSendOpts { + channelAccessToken?: string; + accountId?: string; + verbose?: boolean; + mediaUrl?: string; + replyToken?: string; +} + +function resolveToken( + explicit: string | undefined, + params: { accountId: string; channelAccessToken: string }, +): string { + if (explicit?.trim()) return explicit.trim(); + if (!params.channelAccessToken) { + throw new Error( + `LINE channel access token missing for account "${params.accountId}" (set channels.line.channelAccessToken or LINE_CHANNEL_ACCESS_TOKEN).`, + ); + } + return params.channelAccessToken.trim(); +} + +function normalizeTarget(to: string): string { + const trimmed = to.trim(); + if (!trimmed) throw new Error("Recipient is required for LINE sends"); + + // Strip internal prefixes + let normalized = trimmed + .replace(/^line:group:/i, "") + .replace(/^line:room:/i, "") + .replace(/^line:user:/i, "") + .replace(/^line:/i, ""); + + if (!normalized) throw new Error("Recipient is required for LINE sends"); + + return normalized; +} + +function createTextMessage(text: string): TextMessage { + return { type: "text", text }; +} + +export function createImageMessage( + originalContentUrl: string, + previewImageUrl?: string, +): ImageMessage { + return { + type: "image", + originalContentUrl, + previewImageUrl: previewImageUrl ?? originalContentUrl, + }; +} + +export function createLocationMessage(location: { + title: string; + address: string; + latitude: number; + longitude: number; +}): LocationMessage { + return { + type: "location", + title: location.title.slice(0, 100), // LINE limit + address: location.address.slice(0, 100), // LINE limit + latitude: location.latitude, + longitude: location.longitude, + }; +} + +function logLineHttpError(err: unknown, context: string): void { + if (!err || typeof err !== "object") return; + const { status, statusText, body } = err as { + status?: number; + statusText?: string; + body?: string; + }; + if (typeof body === "string") { + const summary = status ? `${status} ${statusText ?? ""}`.trim() : "unknown status"; + logVerbose(`line: ${context} failed (${summary}): ${body}`); + } +} + +export async function sendMessageLine( + to: string, + text: string, + opts: LineSendOpts = {}, +): Promise { + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + const chatId = normalizeTarget(to); + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); + + const messages: Message[] = []; + + // Add media if provided + if (opts.mediaUrl?.trim()) { + messages.push(createImageMessage(opts.mediaUrl.trim())); + } + + // Add text message + if (text?.trim()) { + messages.push(createTextMessage(text.trim())); + } + + if (messages.length === 0) { + throw new Error("Message must be non-empty for LINE sends"); + } + + // Use reply if we have a reply token, otherwise push + if (opts.replyToken) { + await client.replyMessage({ + replyToken: opts.replyToken, + messages, + }); + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "outbound", + }); + + if (opts.verbose) { + logVerbose(`line: replied to ${chatId}`); + } + + return { + messageId: "reply", + chatId, + }; + } + + // Push message (for proactive messaging) + await client.pushMessage({ + to: chatId, + messages, + }); + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "outbound", + }); + + if (opts.verbose) { + logVerbose(`line: pushed message to ${chatId}`); + } + + return { + messageId: "push", + chatId, + }; +} + +export async function pushMessageLine( + to: string, + text: string, + opts: LineSendOpts = {}, +): Promise { + // Force push (no reply token) + return sendMessageLine(to, text, { ...opts, replyToken: undefined }); +} + +export async function replyMessageLine( + replyToken: string, + messages: Message[], + opts: { channelAccessToken?: string; accountId?: string; verbose?: boolean } = {}, +): Promise { + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); + + await client.replyMessage({ + replyToken, + messages, + }); + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "outbound", + }); + + if (opts.verbose) { + logVerbose(`line: replied with ${messages.length} messages`); + } +} + +export async function pushMessagesLine( + to: string, + messages: Message[], + opts: { channelAccessToken?: string; accountId?: string; verbose?: boolean } = {}, +): Promise { + if (messages.length === 0) { + throw new Error("Message must be non-empty for LINE sends"); + } + + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + const chatId = normalizeTarget(to); + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); + + await client + .pushMessage({ + to: chatId, + messages, + }) + .catch((err) => { + logLineHttpError(err, "push message"); + throw err; + }); + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "outbound", + }); + + if (opts.verbose) { + logVerbose(`line: pushed ${messages.length} messages to ${chatId}`); + } + + return { + messageId: "push", + chatId, + }; +} + +export function createFlexMessage( + altText: string, + contents: messagingApi.FlexContainer, +): messagingApi.FlexMessage { + return { + type: "flex", + altText, + contents, + }; +} + +/** + * Push an image message to a user/group + */ +export async function pushImageMessage( + to: string, + originalContentUrl: string, + previewImageUrl?: string, + opts: { channelAccessToken?: string; accountId?: string; verbose?: boolean } = {}, +): Promise { + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + const chatId = normalizeTarget(to); + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); + + const imageMessage = createImageMessage(originalContentUrl, previewImageUrl); + + await client.pushMessage({ + to: chatId, + messages: [imageMessage], + }); + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "outbound", + }); + + if (opts.verbose) { + logVerbose(`line: pushed image to ${chatId}`); + } + + return { + messageId: "push", + chatId, + }; +} + +/** + * Push a location message to a user/group + */ +export async function pushLocationMessage( + to: string, + location: { + title: string; + address: string; + latitude: number; + longitude: number; + }, + opts: { channelAccessToken?: string; accountId?: string; verbose?: boolean } = {}, +): Promise { + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + const chatId = normalizeTarget(to); + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); + + const locationMessage = createLocationMessage(location); + + await client.pushMessage({ + to: chatId, + messages: [locationMessage], + }); + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "outbound", + }); + + if (opts.verbose) { + logVerbose(`line: pushed location to ${chatId}`); + } + + return { + messageId: "push", + chatId, + }; +} + +/** + * Push a Flex Message to a user/group + */ +export async function pushFlexMessage( + to: string, + altText: string, + contents: FlexContainer, + opts: { channelAccessToken?: string; accountId?: string; verbose?: boolean } = {}, +): Promise { + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + const chatId = normalizeTarget(to); + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); + + const flexMessage: FlexMessage = { + type: "flex", + altText: altText.slice(0, 400), // LINE limit + contents, + }; + + await client + .pushMessage({ + to: chatId, + messages: [flexMessage], + }) + .catch((err) => { + logLineHttpError(err, "push flex message"); + throw err; + }); + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "outbound", + }); + + if (opts.verbose) { + logVerbose(`line: pushed flex message to ${chatId}`); + } + + return { + messageId: "push", + chatId, + }; +} + +/** + * Push a Template Message to a user/group + */ +export async function pushTemplateMessage( + to: string, + template: TemplateMessage, + opts: { channelAccessToken?: string; accountId?: string; verbose?: boolean } = {}, +): Promise { + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + const chatId = normalizeTarget(to); + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); + + await client.pushMessage({ + to: chatId, + messages: [template], + }); + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "outbound", + }); + + if (opts.verbose) { + logVerbose(`line: pushed template message to ${chatId}`); + } + + return { + messageId: "push", + chatId, + }; +} + +/** + * Push a text message with quick reply buttons + */ +export async function pushTextMessageWithQuickReplies( + to: string, + text: string, + quickReplyLabels: string[], + opts: { channelAccessToken?: string; accountId?: string; verbose?: boolean } = {}, +): Promise { + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + const chatId = normalizeTarget(to); + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); + + const message = createTextMessageWithQuickReplies(text, quickReplyLabels); + + await client.pushMessage({ + to: chatId, + messages: [message], + }); + + recordChannelActivity({ + channel: "line", + accountId: account.accountId, + direction: "outbound", + }); + + if (opts.verbose) { + logVerbose(`line: pushed message with quick replies to ${chatId}`); + } + + return { + messageId: "push", + chatId, + }; +} + +/** + * Create quick reply buttons to attach to a message + */ +export function createQuickReplyItems(labels: string[]): QuickReply { + const items: QuickReplyItem[] = labels.slice(0, 13).map((label) => ({ + type: "action", + action: { + type: "message", + label: label.slice(0, 20), // LINE limit: 20 chars + text: label, + }, + })); + return { items }; +} + +/** + * Create a text message with quick reply buttons + */ +export function createTextMessageWithQuickReplies( + text: string, + quickReplyLabels: string[], +): TextMessage & { quickReply: QuickReply } { + return { + type: "text", + text, + quickReply: createQuickReplyItems(quickReplyLabels), + }; +} + +/** + * Show loading animation to user (lasts up to 20 seconds or until next message) + */ +export async function showLoadingAnimation( + chatId: string, + opts: { channelAccessToken?: string; accountId?: string; loadingSeconds?: number } = {}, +): Promise { + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); + + try { + await client.showLoadingAnimation({ + chatId: normalizeTarget(chatId), + loadingSeconds: opts.loadingSeconds ?? 20, + }); + logVerbose(`line: showing loading animation to ${chatId}`); + } catch (err) { + // Loading animation may fail for groups or unsupported clients - ignore + logVerbose(`line: loading animation failed (non-fatal): ${String(err)}`); + } +} + +/** + * Fetch user profile (display name, picture URL) + */ +export async function getUserProfile( + userId: string, + opts: { channelAccessToken?: string; accountId?: string; useCache?: boolean } = {}, +): Promise<{ displayName: string; pictureUrl?: string } | null> { + const useCache = opts.useCache ?? true; + + // Check cache first + if (useCache) { + const cached = userProfileCache.get(userId); + if (cached && Date.now() - cached.fetchedAt < PROFILE_CACHE_TTL_MS) { + return { displayName: cached.displayName, pictureUrl: cached.pictureUrl }; + } + } + + const cfg = loadConfig(); + const account = resolveLineAccount({ + cfg, + accountId: opts.accountId, + }); + const token = resolveToken(opts.channelAccessToken, account); + + const client = new messagingApi.MessagingApiClient({ + channelAccessToken: token, + }); + + try { + const profile = await client.getProfile(userId); + const result = { + displayName: profile.displayName, + pictureUrl: profile.pictureUrl, + }; + + // Cache the result + userProfileCache.set(userId, { + ...result, + fetchedAt: Date.now(), + }); + + return result; + } catch (err) { + logVerbose(`line: failed to fetch profile for ${userId}: ${String(err)}`); + return null; + } +} + +/** + * Get user's display name (with fallback to userId) + */ +export async function getUserDisplayName( + userId: string, + opts: { channelAccessToken?: string; accountId?: string } = {}, +): Promise { + const profile = await getUserProfile(userId, opts); + return profile?.displayName ?? userId; +} diff --git a/src/line/template-messages.test.ts b/src/line/template-messages.test.ts new file mode 100644 index 00000000000..dc43b321b8a --- /dev/null +++ b/src/line/template-messages.test.ts @@ -0,0 +1,391 @@ +import { describe, expect, it } from "vitest"; +import { + createConfirmTemplate, + createButtonTemplate, + createTemplateCarousel, + createCarouselColumn, + createImageCarousel, + createImageCarouselColumn, + createYesNoConfirm, + createButtonMenu, + createLinkMenu, + createProductCarousel, + messageAction, + uriAction, + postbackAction, + datetimePickerAction, +} from "./template-messages.js"; + +describe("messageAction", () => { + it("creates a message action", () => { + const action = messageAction("Click me", "clicked"); + + expect(action.type).toBe("message"); + expect(action.label).toBe("Click me"); + expect((action as { text: string }).text).toBe("clicked"); + }); + + it("uses label as text when text not provided", () => { + const action = messageAction("Click"); + + expect((action as { text: string }).text).toBe("Click"); + }); + + it("truncates label to 20 characters", () => { + const action = messageAction("This is a very long label that exceeds the limit"); + + expect(action.label).toBe("This is a very long "); + }); +}); + +describe("uriAction", () => { + it("creates a URI action", () => { + const action = uriAction("Visit", "https://example.com"); + + expect(action.type).toBe("uri"); + expect(action.label).toBe("Visit"); + expect((action as { uri: string }).uri).toBe("https://example.com"); + }); +}); + +describe("postbackAction", () => { + it("creates a postback action", () => { + const action = postbackAction("Select", "action=select&id=1"); + + expect(action.type).toBe("postback"); + expect(action.label).toBe("Select"); + expect((action as { data: string }).data).toBe("action=select&id=1"); + }); + + it("includes displayText when provided", () => { + const action = postbackAction("Select", "data", "Selected!"); + + expect((action as { displayText: string }).displayText).toBe("Selected!"); + }); + + it("truncates data to 300 characters", () => { + const longData = "x".repeat(400); + const action = postbackAction("Test", longData); + + expect((action as { data: string }).data.length).toBe(300); + }); +}); + +describe("datetimePickerAction", () => { + it("creates a datetime picker action", () => { + const action = datetimePickerAction("Pick date", "date_selected", "date"); + + expect(action.type).toBe("datetimepicker"); + expect(action.label).toBe("Pick date"); + expect((action as { mode: string }).mode).toBe("date"); + }); + + it("includes min/max/initial when provided", () => { + const action = datetimePickerAction("Pick", "data", "datetime", { + initial: "2024-01-01T12:00", + min: "2024-01-01T00:00", + max: "2024-12-31T23:59", + }); + + expect((action as { initial: string }).initial).toBe("2024-01-01T12:00"); + expect((action as { min: string }).min).toBe("2024-01-01T00:00"); + expect((action as { max: string }).max).toBe("2024-12-31T23:59"); + }); +}); + +describe("createConfirmTemplate", () => { + it("creates a confirm template", () => { + const confirm = messageAction("Yes"); + const cancel = messageAction("No"); + const template = createConfirmTemplate("Are you sure?", confirm, cancel); + + expect(template.type).toBe("template"); + expect(template.template.type).toBe("confirm"); + expect((template.template as { text: string }).text).toBe("Are you sure?"); + }); + + it("truncates text to 240 characters", () => { + const longText = "x".repeat(300); + const template = createConfirmTemplate(longText, messageAction("Yes"), messageAction("No")); + + expect((template.template as { text: string }).text.length).toBe(240); + }); + + it("uses custom altText when provided", () => { + const template = createConfirmTemplate( + "Question?", + messageAction("Yes"), + messageAction("No"), + "Custom alt", + ); + + expect(template.altText).toBe("Custom alt"); + }); +}); + +describe("createButtonTemplate", () => { + it("creates a button template", () => { + const actions = [messageAction("Button 1"), messageAction("Button 2")]; + const template = createButtonTemplate("Title", "Description", actions); + + expect(template.type).toBe("template"); + expect(template.template.type).toBe("buttons"); + expect((template.template as { title: string }).title).toBe("Title"); + expect((template.template as { text: string }).text).toBe("Description"); + }); + + it("limits actions to 4", () => { + const actions = Array.from({ length: 6 }, (_, i) => messageAction(`Button ${i}`)); + const template = createButtonTemplate("Title", "Text", actions); + + expect((template.template as { actions: unknown[] }).actions.length).toBe(4); + }); + + it("truncates title to 40 characters", () => { + const longTitle = "x".repeat(50); + const template = createButtonTemplate(longTitle, "Text", [messageAction("OK")]); + + expect((template.template as { title: string }).title.length).toBe(40); + }); + + it("includes thumbnail when provided", () => { + const template = createButtonTemplate("Title", "Text", [messageAction("OK")], { + thumbnailImageUrl: "https://example.com/thumb.jpg", + }); + + expect((template.template as { thumbnailImageUrl: string }).thumbnailImageUrl).toBe( + "https://example.com/thumb.jpg", + ); + }); + + it("truncates text to 60 chars when no thumbnail is provided", () => { + const longText = "x".repeat(100); + const template = createButtonTemplate("Title", longText, [messageAction("OK")]); + + expect((template.template as { text: string }).text.length).toBe(60); + }); + + it("keeps longer text when thumbnail is provided", () => { + const longText = "x".repeat(100); + const template = createButtonTemplate("Title", longText, [messageAction("OK")], { + thumbnailImageUrl: "https://example.com/thumb.jpg", + }); + + expect((template.template as { text: string }).text.length).toBe(100); + }); +}); + +describe("createTemplateCarousel", () => { + it("creates a carousel template", () => { + const columns = [ + createCarouselColumn({ text: "Column 1", actions: [messageAction("Select")] }), + createCarouselColumn({ text: "Column 2", actions: [messageAction("Select")] }), + ]; + const template = createTemplateCarousel(columns); + + expect(template.type).toBe("template"); + expect(template.template.type).toBe("carousel"); + expect((template.template as { columns: unknown[] }).columns.length).toBe(2); + }); + + it("limits columns to 10", () => { + const columns = Array.from({ length: 15 }, () => + createCarouselColumn({ text: "Text", actions: [messageAction("OK")] }), + ); + const template = createTemplateCarousel(columns); + + expect((template.template as { columns: unknown[] }).columns.length).toBe(10); + }); +}); + +describe("createCarouselColumn", () => { + it("creates a carousel column", () => { + const column = createCarouselColumn({ + title: "Item", + text: "Description", + actions: [messageAction("View")], + thumbnailImageUrl: "https://example.com/img.jpg", + }); + + expect(column.title).toBe("Item"); + expect(column.text).toBe("Description"); + expect(column.thumbnailImageUrl).toBe("https://example.com/img.jpg"); + expect(column.actions.length).toBe(1); + }); + + it("limits actions to 3", () => { + const column = createCarouselColumn({ + text: "Text", + actions: [ + messageAction("A1"), + messageAction("A2"), + messageAction("A3"), + messageAction("A4"), + messageAction("A5"), + ], + }); + + expect(column.actions.length).toBe(3); + }); + + it("truncates text to 120 characters", () => { + const longText = "x".repeat(150); + const column = createCarouselColumn({ text: longText, actions: [messageAction("OK")] }); + + expect(column.text.length).toBe(120); + }); +}); + +describe("createImageCarousel", () => { + it("creates an image carousel", () => { + const columns = [ + createImageCarouselColumn("https://example.com/1.jpg", messageAction("View 1")), + createImageCarouselColumn("https://example.com/2.jpg", messageAction("View 2")), + ]; + const template = createImageCarousel(columns); + + expect(template.type).toBe("template"); + expect(template.template.type).toBe("image_carousel"); + }); + + it("limits columns to 10", () => { + const columns = Array.from({ length: 15 }, (_, i) => + createImageCarouselColumn(`https://example.com/${i}.jpg`, messageAction("View")), + ); + const template = createImageCarousel(columns); + + expect((template.template as { columns: unknown[] }).columns.length).toBe(10); + }); +}); + +describe("createImageCarouselColumn", () => { + it("creates an image carousel column", () => { + const action = uriAction("Visit", "https://example.com"); + const column = createImageCarouselColumn("https://example.com/img.jpg", action); + + expect(column.imageUrl).toBe("https://example.com/img.jpg"); + expect(column.action).toBe(action); + }); +}); + +describe("createYesNoConfirm", () => { + it("creates a yes/no confirmation with defaults", () => { + const template = createYesNoConfirm("Continue?"); + + expect(template.type).toBe("template"); + expect(template.template.type).toBe("confirm"); + + const actions = (template.template as { actions: Array<{ label: string }> }).actions; + expect(actions[0].label).toBe("Yes"); + expect(actions[1].label).toBe("No"); + }); + + it("allows custom button text", () => { + const template = createYesNoConfirm("Delete?", { + yesText: "Delete", + noText: "Cancel", + }); + + const actions = (template.template as { actions: Array<{ label: string }> }).actions; + expect(actions[0].label).toBe("Delete"); + expect(actions[1].label).toBe("Cancel"); + }); + + it("uses postback actions when data provided", () => { + const template = createYesNoConfirm("Confirm?", { + yesData: "action=confirm", + noData: "action=cancel", + }); + + const actions = (template.template as { actions: Array<{ type: string }> }).actions; + expect(actions[0].type).toBe("postback"); + expect(actions[1].type).toBe("postback"); + }); +}); + +describe("createButtonMenu", () => { + it("creates a button menu with text buttons", () => { + const template = createButtonMenu("Menu", "Choose an option", [ + { label: "Option 1" }, + { label: "Option 2", text: "selected option 2" }, + ]); + + expect(template.type).toBe("template"); + expect(template.template.type).toBe("buttons"); + + const actions = (template.template as { actions: Array<{ type: string }> }).actions; + expect(actions.length).toBe(2); + expect(actions[0].type).toBe("message"); + }); +}); + +describe("createLinkMenu", () => { + it("creates a button menu with URL links", () => { + const template = createLinkMenu("Links", "Visit our sites", [ + { label: "Site 1", url: "https://site1.com" }, + { label: "Site 2", url: "https://site2.com" }, + ]); + + expect(template.type).toBe("template"); + + const actions = (template.template as { actions: Array<{ type: string }> }).actions; + expect(actions[0].type).toBe("uri"); + expect(actions[1].type).toBe("uri"); + }); +}); + +describe("createProductCarousel", () => { + it("creates a product carousel", () => { + const template = createProductCarousel([ + { title: "Product 1", description: "Desc 1", price: "$10" }, + { title: "Product 2", description: "Desc 2", imageUrl: "https://example.com/p2.jpg" }, + ]); + + expect(template.type).toBe("template"); + expect(template.template.type).toBe("carousel"); + + const columns = (template.template as { columns: unknown[] }).columns; + expect(columns.length).toBe(2); + }); + + it("uses URI action when actionUrl provided", () => { + const template = createProductCarousel([ + { + title: "Product", + description: "Desc", + actionLabel: "Buy", + actionUrl: "https://shop.com/buy", + }, + ]); + + const columns = (template.template as { columns: Array<{ actions: Array<{ type: string }> }> }) + .columns; + expect(columns[0].actions[0].type).toBe("uri"); + }); + + it("uses postback action when actionData provided", () => { + const template = createProductCarousel([ + { + title: "Product", + description: "Desc", + actionLabel: "Select", + actionData: "product_id=123", + }, + ]); + + const columns = (template.template as { columns: Array<{ actions: Array<{ type: string }> }> }) + .columns; + expect(columns[0].actions[0].type).toBe("postback"); + }); + + it("limits to 10 products", () => { + const products = Array.from({ length: 15 }, (_, i) => ({ + title: `Product ${i}`, + description: `Desc ${i}`, + })); + const template = createProductCarousel(products); + + const columns = (template.template as { columns: unknown[] }).columns; + expect(columns.length).toBe(10); + }); +}); diff --git a/src/line/template-messages.ts b/src/line/template-messages.ts new file mode 100644 index 00000000000..686dc8337d7 --- /dev/null +++ b/src/line/template-messages.ts @@ -0,0 +1,401 @@ +import type { messagingApi } from "@line/bot-sdk"; + +type TemplateMessage = messagingApi.TemplateMessage; +type ConfirmTemplate = messagingApi.ConfirmTemplate; +type ButtonsTemplate = messagingApi.ButtonsTemplate; +type CarouselTemplate = messagingApi.CarouselTemplate; +type CarouselColumn = messagingApi.CarouselColumn; +type ImageCarouselTemplate = messagingApi.ImageCarouselTemplate; +type ImageCarouselColumn = messagingApi.ImageCarouselColumn; +type Action = messagingApi.Action; + +/** + * Create a confirm template (yes/no style dialog) + */ +export function createConfirmTemplate( + text: string, + confirmAction: Action, + cancelAction: Action, + altText?: string, +): TemplateMessage { + const template: ConfirmTemplate = { + type: "confirm", + text: text.slice(0, 240), // LINE limit + actions: [confirmAction, cancelAction], + }; + + return { + type: "template", + altText: altText?.slice(0, 400) ?? text.slice(0, 400), + template, + }; +} + +/** + * Create a button template with title, text, and action buttons + */ +export function createButtonTemplate( + title: string, + text: string, + actions: Action[], + options?: { + thumbnailImageUrl?: string; + imageAspectRatio?: "rectangle" | "square"; + imageSize?: "cover" | "contain"; + imageBackgroundColor?: string; + defaultAction?: Action; + altText?: string; + }, +): TemplateMessage { + const hasThumbnail = Boolean(options?.thumbnailImageUrl?.trim()); + const textLimit = hasThumbnail ? 160 : 60; + const template: ButtonsTemplate = { + type: "buttons", + title: title.slice(0, 40), // LINE limit + text: text.slice(0, textLimit), // LINE limit (60 if no thumbnail, 160 with thumbnail) + actions: actions.slice(0, 4), // LINE limit: max 4 actions + thumbnailImageUrl: options?.thumbnailImageUrl, + imageAspectRatio: options?.imageAspectRatio ?? "rectangle", + imageSize: options?.imageSize ?? "cover", + imageBackgroundColor: options?.imageBackgroundColor, + defaultAction: options?.defaultAction, + }; + + return { + type: "template", + altText: options?.altText?.slice(0, 400) ?? `${title}: ${text}`.slice(0, 400), + template, + }; +} + +/** + * Create a carousel template with multiple columns + */ +export function createTemplateCarousel( + columns: CarouselColumn[], + options?: { + imageAspectRatio?: "rectangle" | "square"; + imageSize?: "cover" | "contain"; + altText?: string; + }, +): TemplateMessage { + const template: CarouselTemplate = { + type: "carousel", + columns: columns.slice(0, 10), // LINE limit: max 10 columns + imageAspectRatio: options?.imageAspectRatio ?? "rectangle", + imageSize: options?.imageSize ?? "cover", + }; + + return { + type: "template", + altText: options?.altText?.slice(0, 400) ?? "View carousel", + template, + }; +} + +/** + * Create a carousel column for use with createTemplateCarousel + */ +export function createCarouselColumn(params: { + title?: string; + text: string; + actions: Action[]; + thumbnailImageUrl?: string; + imageBackgroundColor?: string; + defaultAction?: Action; +}): CarouselColumn { + return { + title: params.title?.slice(0, 40), + text: params.text.slice(0, 120), // LINE limit + actions: params.actions.slice(0, 3), // LINE limit: max 3 actions per column + thumbnailImageUrl: params.thumbnailImageUrl, + imageBackgroundColor: params.imageBackgroundColor, + defaultAction: params.defaultAction, + }; +} + +/** + * Create an image carousel template (simpler, image-focused carousel) + */ +export function createImageCarousel( + columns: ImageCarouselColumn[], + altText?: string, +): TemplateMessage { + const template: ImageCarouselTemplate = { + type: "image_carousel", + columns: columns.slice(0, 10), // LINE limit: max 10 columns + }; + + return { + type: "template", + altText: altText?.slice(0, 400) ?? "View images", + template, + }; +} + +/** + * Create an image carousel column for use with createImageCarousel + */ +export function createImageCarouselColumn(imageUrl: string, action: Action): ImageCarouselColumn { + return { + imageUrl, + action, + }; +} + +// ============================================================================ +// Action Helpers (same as rich-menu but re-exported for convenience) +// ============================================================================ + +/** + * Create a message action (sends text when tapped) + */ +export function messageAction(label: string, text?: string): Action { + return { + type: "message", + label: label.slice(0, 20), + text: text ?? label, + }; +} + +/** + * Create a URI action (opens a URL when tapped) + */ +export function uriAction(label: string, uri: string): Action { + return { + type: "uri", + label: label.slice(0, 20), + uri, + }; +} + +/** + * Create a postback action (sends data to webhook when tapped) + */ +export function postbackAction(label: string, data: string, displayText?: string): Action { + return { + type: "postback", + label: label.slice(0, 20), + data: data.slice(0, 300), + displayText: displayText?.slice(0, 300), + }; +} + +/** + * Create a datetime picker action + */ +export function datetimePickerAction( + label: string, + data: string, + mode: "date" | "time" | "datetime", + options?: { + initial?: string; + max?: string; + min?: string; + }, +): Action { + return { + type: "datetimepicker", + label: label.slice(0, 20), + data: data.slice(0, 300), + mode, + initial: options?.initial, + max: options?.max, + min: options?.min, + }; +} + +// ============================================================================ +// Convenience Builders +// ============================================================================ + +/** + * Create a simple yes/no confirmation dialog + */ +export function createYesNoConfirm( + question: string, + options?: { + yesText?: string; + noText?: string; + yesData?: string; + noData?: string; + altText?: string; + }, +): TemplateMessage { + const yesAction: Action = options?.yesData + ? postbackAction(options.yesText ?? "Yes", options.yesData, options.yesText ?? "Yes") + : messageAction(options?.yesText ?? "Yes"); + + const noAction: Action = options?.noData + ? postbackAction(options.noText ?? "No", options.noData, options.noText ?? "No") + : messageAction(options?.noText ?? "No"); + + return createConfirmTemplate(question, yesAction, noAction, options?.altText); +} + +/** + * Create a button menu with simple text buttons + */ +export function createButtonMenu( + title: string, + text: string, + buttons: Array<{ label: string; text?: string }>, + options?: { + thumbnailImageUrl?: string; + altText?: string; + }, +): TemplateMessage { + const actions = buttons.slice(0, 4).map((btn) => messageAction(btn.label, btn.text)); + + return createButtonTemplate(title, text, actions, { + thumbnailImageUrl: options?.thumbnailImageUrl, + altText: options?.altText, + }); +} + +/** + * Create a button menu with URL links + */ +export function createLinkMenu( + title: string, + text: string, + links: Array<{ label: string; url: string }>, + options?: { + thumbnailImageUrl?: string; + altText?: string; + }, +): TemplateMessage { + const actions = links.slice(0, 4).map((link) => uriAction(link.label, link.url)); + + return createButtonTemplate(title, text, actions, { + thumbnailImageUrl: options?.thumbnailImageUrl, + altText: options?.altText, + }); +} + +/** + * Create a simple product/item carousel + */ +export function createProductCarousel( + products: Array<{ + title: string; + description: string; + imageUrl?: string; + price?: string; + actionLabel?: string; + actionUrl?: string; + actionData?: string; + }>, + altText?: string, +): TemplateMessage { + const columns = products.slice(0, 10).map((product) => { + const actions: Action[] = []; + + // Add main action + if (product.actionUrl) { + actions.push(uriAction(product.actionLabel ?? "View", product.actionUrl)); + } else if (product.actionData) { + actions.push(postbackAction(product.actionLabel ?? "Select", product.actionData)); + } else { + actions.push(messageAction(product.actionLabel ?? "Select", product.title)); + } + + return createCarouselColumn({ + title: product.title, + text: product.price + ? `${product.description}\n${product.price}`.slice(0, 120) + : product.description, + thumbnailImageUrl: product.imageUrl, + actions, + }); + }); + + return createTemplateCarousel(columns, { altText }); +} + +// ============================================================================ +// ReplyPayload Conversion +// ============================================================================ + +import type { LineTemplateMessagePayload } from "./types.js"; + +/** + * Convert a TemplateMessagePayload from ReplyPayload to a LINE TemplateMessage + */ +export function buildTemplateMessageFromPayload( + payload: LineTemplateMessagePayload, +): TemplateMessage | null { + switch (payload.type) { + case "confirm": { + const confirmAction = payload.confirmData.startsWith("http") + ? uriAction(payload.confirmLabel, payload.confirmData) + : payload.confirmData.includes("=") + ? postbackAction(payload.confirmLabel, payload.confirmData, payload.confirmLabel) + : messageAction(payload.confirmLabel, payload.confirmData); + + const cancelAction = payload.cancelData.startsWith("http") + ? uriAction(payload.cancelLabel, payload.cancelData) + : payload.cancelData.includes("=") + ? postbackAction(payload.cancelLabel, payload.cancelData, payload.cancelLabel) + : messageAction(payload.cancelLabel, payload.cancelData); + + return createConfirmTemplate(payload.text, confirmAction, cancelAction, payload.altText); + } + + case "buttons": { + const actions: Action[] = payload.actions.slice(0, 4).map((action) => { + if (action.type === "uri" && action.uri) { + return uriAction(action.label, action.uri); + } + if (action.type === "postback" && action.data) { + return postbackAction(action.label, action.data, action.label); + } + // Default to message action + return messageAction(action.label, action.data ?? action.label); + }); + + return createButtonTemplate(payload.title, payload.text, actions, { + thumbnailImageUrl: payload.thumbnailImageUrl, + altText: payload.altText, + }); + } + + case "carousel": { + const columns: CarouselColumn[] = payload.columns.slice(0, 10).map((col) => { + const colActions: Action[] = col.actions.slice(0, 3).map((action) => { + if (action.type === "uri" && action.uri) { + return uriAction(action.label, action.uri); + } + if (action.type === "postback" && action.data) { + return postbackAction(action.label, action.data, action.label); + } + return messageAction(action.label, action.data ?? action.label); + }); + + return createCarouselColumn({ + title: col.title, + text: col.text, + thumbnailImageUrl: col.thumbnailImageUrl, + actions: colActions, + }); + }); + + return createTemplateCarousel(columns, { altText: payload.altText }); + } + + default: + return null; + } +} + +// Re-export types +export type { + TemplateMessage, + ConfirmTemplate, + ButtonsTemplate, + CarouselTemplate, + CarouselColumn, + ImageCarouselTemplate, + ImageCarouselColumn, + Action, +}; diff --git a/src/line/types.ts b/src/line/types.ts new file mode 100644 index 00000000000..252fcb949ea --- /dev/null +++ b/src/line/types.ts @@ -0,0 +1,150 @@ +import type { + WebhookEvent, + TextMessage, + ImageMessage, + VideoMessage, + AudioMessage, + StickerMessage, + LocationMessage, +} from "@line/bot-sdk"; + +export type LineTokenSource = "config" | "env" | "file" | "none"; + +export interface LineConfig { + enabled?: boolean; + channelAccessToken?: string; + channelSecret?: string; + tokenFile?: string; + secretFile?: string; + name?: string; + allowFrom?: Array; + groupAllowFrom?: Array; + dmPolicy?: "open" | "allowlist" | "pairing" | "disabled"; + groupPolicy?: "open" | "allowlist" | "disabled"; + mediaMaxMb?: number; + webhookPath?: string; + accounts?: Record; + groups?: Record; +} + +export interface LineAccountConfig { + enabled?: boolean; + channelAccessToken?: string; + channelSecret?: string; + tokenFile?: string; + secretFile?: string; + name?: string; + allowFrom?: Array; + groupAllowFrom?: Array; + dmPolicy?: "open" | "allowlist" | "pairing" | "disabled"; + groupPolicy?: "open" | "allowlist" | "disabled"; + mediaMaxMb?: number; + webhookPath?: string; + groups?: Record; +} + +export interface LineGroupConfig { + enabled?: boolean; + allowFrom?: Array; + requireMention?: boolean; + systemPrompt?: string; + skills?: string[]; +} + +export interface ResolvedLineAccount { + accountId: string; + name?: string; + enabled: boolean; + channelAccessToken: string; + channelSecret: string; + tokenSource: LineTokenSource; + config: LineConfig & LineAccountConfig; +} + +export type LineMessageType = + | TextMessage + | ImageMessage + | VideoMessage + | AudioMessage + | StickerMessage + | LocationMessage; + +export interface LineWebhookContext { + event: WebhookEvent; + replyToken?: string; + userId?: string; + groupId?: string; + roomId?: string; +} + +export interface LineSendResult { + messageId: string; + chatId: string; +} + +export interface LineProbeResult { + ok: boolean; + bot?: { + displayName?: string; + userId?: string; + basicId?: string; + pictureUrl?: string; + }; + error?: string; +} + +export type LineFlexMessagePayload = { + altText: string; + contents: unknown; +}; + +export type LineTemplateMessagePayload = + | { + type: "confirm"; + text: string; + confirmLabel: string; + confirmData: string; + cancelLabel: string; + cancelData: string; + altText?: string; + } + | { + type: "buttons"; + title: string; + text: string; + actions: Array<{ + type: "message" | "uri" | "postback"; + label: string; + data?: string; + uri?: string; + }>; + thumbnailImageUrl?: string; + altText?: string; + } + | { + type: "carousel"; + columns: Array<{ + title?: string; + text: string; + thumbnailImageUrl?: string; + actions: Array<{ + type: "message" | "uri" | "postback"; + label: string; + data?: string; + uri?: string; + }>; + }>; + altText?: string; + }; + +export type LineChannelData = { + quickReplies?: string[]; + location?: { + title: string; + address: string; + latitude: number; + longitude: number; + }; + flexMessage?: LineFlexMessagePayload; + templateMessage?: LineTemplateMessagePayload; +}; diff --git a/src/line/webhook.test.ts b/src/line/webhook.test.ts new file mode 100644 index 00000000000..af30040b4b3 --- /dev/null +++ b/src/line/webhook.test.ts @@ -0,0 +1,73 @@ +import crypto from "node:crypto"; +import { describe, expect, it, vi } from "vitest"; +import { createLineWebhookMiddleware } from "./webhook.js"; + +const sign = (body: string, secret: string) => + crypto.createHmac("SHA256", secret).update(body).digest("base64"); + +const createRes = () => { + const res = { + status: vi.fn(), + json: vi.fn(), + headersSent: false, + } as any; + res.status.mockReturnValue(res); + res.json.mockReturnValue(res); + return res; +}; + +describe("createLineWebhookMiddleware", () => { + it("parses JSON from raw string body", async () => { + const onEvents = vi.fn(async () => {}); + const secret = "secret"; + const rawBody = JSON.stringify({ events: [{ type: "message" }] }); + const middleware = createLineWebhookMiddleware({ channelSecret: secret, onEvents }); + + const req = { + headers: { "x-line-signature": sign(rawBody, secret) }, + body: rawBody, + } as any; + const res = createRes(); + + await middleware(req, res, {} as any); + + expect(res.status).toHaveBeenCalledWith(200); + expect(onEvents).toHaveBeenCalledWith(expect.objectContaining({ events: expect.any(Array) })); + }); + + it("parses JSON from raw buffer body", async () => { + const onEvents = vi.fn(async () => {}); + const secret = "secret"; + const rawBody = JSON.stringify({ events: [{ type: "follow" }] }); + const middleware = createLineWebhookMiddleware({ channelSecret: secret, onEvents }); + + const req = { + headers: { "x-line-signature": sign(rawBody, secret) }, + body: Buffer.from(rawBody, "utf-8"), + } as any; + const res = createRes(); + + await middleware(req, res, {} as any); + + expect(res.status).toHaveBeenCalledWith(200); + expect(onEvents).toHaveBeenCalledWith(expect.objectContaining({ events: expect.any(Array) })); + }); + + it("rejects invalid JSON payloads", async () => { + const onEvents = vi.fn(async () => {}); + const secret = "secret"; + const rawBody = "not json"; + const middleware = createLineWebhookMiddleware({ channelSecret: secret, onEvents }); + + const req = { + headers: { "x-line-signature": sign(rawBody, secret) }, + body: rawBody, + } as any; + const res = createRes(); + + await middleware(req, res, {} as any); + + expect(res.status).toHaveBeenCalledWith(400); + expect(onEvents).not.toHaveBeenCalled(); + }); +}); diff --git a/src/line/webhook.ts b/src/line/webhook.ts new file mode 100644 index 00000000000..5f5e12441b3 --- /dev/null +++ b/src/line/webhook.ts @@ -0,0 +1,102 @@ +import type { Request, Response, NextFunction } from "express"; +import crypto from "node:crypto"; +import type { WebhookRequestBody } from "@line/bot-sdk"; +import { logVerbose, danger } from "../globals.js"; +import type { RuntimeEnv } from "../runtime.js"; + +export interface LineWebhookOptions { + channelSecret: string; + onEvents: (body: WebhookRequestBody) => Promise; + runtime?: RuntimeEnv; +} + +function validateSignature(body: string, signature: string, channelSecret: string): boolean { + const hash = crypto.createHmac("SHA256", channelSecret).update(body).digest("base64"); + return hash === signature; +} + +function readRawBody(req: Request): string | null { + const rawBody = + (req as { rawBody?: string | Buffer }).rawBody ?? + (typeof req.body === "string" || Buffer.isBuffer(req.body) ? req.body : null); + if (!rawBody) return null; + return Buffer.isBuffer(rawBody) ? rawBody.toString("utf-8") : rawBody; +} + +function parseWebhookBody(req: Request, rawBody: string): WebhookRequestBody | null { + if (req.body && typeof req.body === "object" && !Buffer.isBuffer(req.body)) { + return req.body as WebhookRequestBody; + } + try { + return JSON.parse(rawBody) as WebhookRequestBody; + } catch { + return null; + } +} + +export function createLineWebhookMiddleware(options: LineWebhookOptions) { + const { channelSecret, onEvents, runtime } = options; + + return async (req: Request, res: Response, _next: NextFunction): Promise => { + try { + const signature = req.headers["x-line-signature"]; + + if (!signature || typeof signature !== "string") { + res.status(400).json({ error: "Missing X-Line-Signature header" }); + return; + } + + const rawBody = readRawBody(req); + if (!rawBody) { + res.status(400).json({ error: "Missing raw request body for signature verification" }); + return; + } + + if (!validateSignature(rawBody, signature, channelSecret)) { + logVerbose("line: webhook signature validation failed"); + res.status(401).json({ error: "Invalid signature" }); + return; + } + + const body = parseWebhookBody(req, rawBody); + if (!body) { + res.status(400).json({ error: "Invalid webhook payload" }); + return; + } + + // Respond immediately to avoid timeout + res.status(200).json({ status: "ok" }); + + // Process events asynchronously + if (body.events && body.events.length > 0) { + logVerbose(`line: received ${body.events.length} webhook events`); + await onEvents(body).catch((err) => { + runtime?.error?.(danger(`line webhook handler failed: ${String(err)}`)); + }); + } + } catch (err) { + runtime?.error?.(danger(`line webhook error: ${String(err)}`)); + if (!res.headersSent) { + res.status(500).json({ error: "Internal server error" }); + } + } + }; +} + +export interface StartLineWebhookOptions { + channelSecret: string; + onEvents: (body: WebhookRequestBody) => Promise; + runtime?: RuntimeEnv; + path?: string; +} + +export function startLineWebhook(options: StartLineWebhookOptions) { + const path = options.path ?? "/line/webhook"; + const middleware = createLineWebhookMiddleware({ + channelSecret: options.channelSecret, + onEvents: options.onEvents, + runtime: options.runtime, + }); + + return { path, handler: middleware }; +} diff --git a/src/media-understanding/runner.ts b/src/media-understanding/runner.ts index 2e9bccb0833..9e92d67c003 100644 --- a/src/media-understanding/runner.ts +++ b/src/media-understanding/runner.ts @@ -4,6 +4,11 @@ import os from "node:os"; import path from "node:path"; import type { ClawdbotConfig } from "../config/config.js"; +import { + findModelInCatalog, + loadModelCatalog, + modelSupportsVision, +} from "../agents/model-catalog.js"; import type { MsgContext } from "../auto-reply/templating.js"; import { applyTemplate } from "../auto-reply/templating.js"; import { requireApiKey, resolveApiKeyForProvider } from "../agents/model-auth.js"; @@ -1014,6 +1019,42 @@ export async function runCapability(params: { }; } + // Skip image understanding when the primary model supports vision natively. + // The image will be injected directly into the model context instead. + const activeProvider = params.activeModel?.provider?.trim(); + if (capability === "image" && activeProvider) { + const catalog = await loadModelCatalog({ config: cfg }); + const entry = findModelInCatalog(catalog, activeProvider, params.activeModel?.model ?? ""); + if (modelSupportsVision(entry)) { + if (shouldLogVerbose()) { + logVerbose("Skipping image understanding: primary model supports vision natively"); + } + const model = params.activeModel?.model?.trim(); + const reason = "primary model supports vision natively"; + return { + outputs: [], + decision: { + capability, + outcome: "skipped", + attachments: selected.map((item) => { + const attempt = { + type: "provider" as const, + provider: activeProvider, + model: model || undefined, + outcome: "skipped" as const, + reason, + }; + return { + attachmentIndex: item.index, + attempts: [attempt], + chosen: attempt, + }; + }), + }, + }; + } + } + const entries = resolveModelEntries({ cfg, capability, diff --git a/src/media-understanding/runner.vision-skip.test.ts b/src/media-understanding/runner.vision-skip.test.ts new file mode 100644 index 00000000000..7d837194940 --- /dev/null +++ b/src/media-understanding/runner.vision-skip.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it, vi } from "vitest"; + +import type { MsgContext } from "../auto-reply/templating.js"; +import type { ClawdbotConfig } from "../config/config.js"; +import { + buildProviderRegistry, + createMediaAttachmentCache, + normalizeMediaAttachments, + runCapability, +} from "./runner.js"; + +const catalog = [ + { + id: "gpt-4.1", + name: "GPT-4.1", + provider: "openai", + input: ["text", "image"] as const, + }, +]; + +vi.mock("../agents/model-catalog.js", async () => { + const actual = await vi.importActual( + "../agents/model-catalog.js", + ); + return { + ...actual, + loadModelCatalog: vi.fn(async () => catalog), + }; +}); + +describe("runCapability image skip", () => { + it("skips image understanding when the active model supports vision", async () => { + const ctx: MsgContext = { MediaPath: "/tmp/image.png", MediaType: "image/png" }; + const media = normalizeMediaAttachments(ctx); + const cache = createMediaAttachmentCache(media); + const cfg = {} as ClawdbotConfig; + + try { + const result = await runCapability({ + capability: "image", + cfg, + ctx, + attachments: cache, + media, + providerRegistry: buildProviderRegistry(), + activeModel: { provider: "openai", model: "gpt-4.1" }, + }); + + expect(result.outputs).toHaveLength(0); + expect(result.decision.outcome).toBe("skipped"); + expect(result.decision.attachments).toHaveLength(1); + expect(result.decision.attachments[0]?.attachmentIndex).toBe(0); + expect(result.decision.attachments[0]?.attempts[0]?.outcome).toBe("skipped"); + expect(result.decision.attachments[0]?.attempts[0]?.reason).toBe( + "primary model supports vision natively", + ); + } finally { + await cache.cleanup(); + } + }); +}); diff --git a/src/plugin-sdk/index.ts b/src/plugin-sdk/index.ts index cb4e95a825a..60782ff6d74 100644 --- a/src/plugin-sdk/index.ts +++ b/src/plugin-sdk/index.ts @@ -64,6 +64,8 @@ export type { ClawdbotPluginServiceContext, } from "../plugins/types.js"; export type { PluginRuntime } from "../plugins/runtime/types.js"; +export { normalizePluginHttpPath } from "../plugins/http-path.js"; +export { registerPluginHttpRoute } from "../plugins/http-registry.js"; export { emptyPluginConfigSchema } from "../plugins/config-schema.js"; export type { ClawdbotConfig } from "../config/config.js"; export type { ChannelDock } from "../channels/dock.js"; @@ -195,12 +197,6 @@ export { } from "../channels/plugins/setup-helpers.js"; export { formatPairingApproveHint } from "../channels/plugins/helpers.js"; export { PAIRING_APPROVED_MESSAGE } from "../channels/plugins/pairing-message.js"; -export { - listIMessageAccountIds, - resolveDefaultIMessageAccountId, - resolveIMessageAccount, - type ResolvedIMessageAccount, -} from "../imessage/accounts.js"; export type { ChannelOnboardingAdapter, @@ -208,7 +204,6 @@ export type { } from "../channels/plugins/onboarding-types.js"; export { addWildcardAllowFrom, promptAccountId } from "../channels/plugins/onboarding/helpers.js"; export { promptChannelAccessConfig } from "../channels/plugins/onboarding/channel-access.js"; -export { imessageOnboardingAdapter } from "../channels/plugins/onboarding/imessage.js"; export { createActionGate, @@ -262,6 +257,19 @@ export { } from "../channels/plugins/normalize/discord.js"; export { collectDiscordStatusIssues } from "../channels/plugins/status-issues/discord.js"; +// Channel: iMessage +export { + listIMessageAccountIds, + resolveDefaultIMessageAccountId, + resolveIMessageAccount, + type ResolvedIMessageAccount, +} from "../imessage/accounts.js"; +export { imessageOnboardingAdapter } from "../channels/plugins/onboarding/imessage.js"; +export { + looksLikeIMessageTargetId, + normalizeIMessageMessagingTarget, +} from "../channels/plugins/normalize/imessage.js"; + // Channel: Slack export { listEnabledSlackAccounts, @@ -324,5 +332,35 @@ export { collectWhatsAppStatusIssues } from "../channels/plugins/status-issues/w // Channel: BlueBubbles export { collectBlueBubblesStatusIssues } from "../channels/plugins/status-issues/bluebubbles.js"; +// Channel: LINE +export { + listLineAccountIds, + normalizeAccountId as normalizeLineAccountId, + resolveDefaultLineAccountId, + resolveLineAccount, +} from "../line/accounts.js"; +export { LineConfigSchema } from "../line/config-schema.js"; +export type { + LineConfig, + LineAccountConfig, + ResolvedLineAccount, + LineChannelData, +} from "../line/types.js"; +export { + createInfoCard, + createListCard, + createImageCard, + createActionCard, + createReceiptCard, + type CardAction, + type ListItem, +} from "../line/flex-templates.js"; +export { + processLineMessage, + hasMarkdownToConvert, + stripMarkdown, +} from "../line/markdown-to-line.js"; +export type { ProcessedLineMessage } from "../line/markdown-to-line.js"; + // Media utilities export { loadWebMedia, type WebMediaResult } from "../web/media.js"; diff --git a/src/plugins/commands.ts b/src/plugins/commands.ts index 27e424303e9..bb4e4b3866c 100644 --- a/src/plugins/commands.ts +++ b/src/plugins/commands.ts @@ -6,7 +6,11 @@ */ import type { ClawdbotConfig } from "../config/config.js"; -import type { ClawdbotPluginCommandDefinition, PluginCommandContext } from "./types.js"; +import type { + ClawdbotPluginCommandDefinition, + PluginCommandContext, + PluginCommandResult, +} from "./types.js"; import { logVerbose } from "../globals.js"; type RegisteredPluginCommand = ClawdbotPluginCommandDefinition & { @@ -218,7 +222,7 @@ export async function executePluginCommand(params: { isAuthorizedSender: boolean; commandBody: string; config: ClawdbotConfig; -}): Promise<{ text: string }> { +}): Promise { const { command, args, senderId, channel, isAuthorizedSender, commandBody, config } = params; // Check authorization @@ -249,7 +253,7 @@ export async function executePluginCommand(params: { logVerbose( `Plugin command /${command.name} executed successfully for ${senderId || "unknown"}`, ); - return { text: result.text }; + return result; } catch (err) { const error = err as Error; logVerbose(`Plugin command /${command.name} error: ${error.message}`); diff --git a/src/plugins/http-path.ts b/src/plugins/http-path.ts new file mode 100644 index 00000000000..341b91dcd49 --- /dev/null +++ b/src/plugins/http-path.ts @@ -0,0 +1,12 @@ +export function normalizePluginHttpPath( + path?: string | null, + fallback?: string | null, +): string | null { + const trimmed = path?.trim(); + if (!trimmed) { + const fallbackTrimmed = fallback?.trim(); + if (!fallbackTrimmed) return null; + return fallbackTrimmed.startsWith("/") ? fallbackTrimmed : `/${fallbackTrimmed}`; + } + return trimmed.startsWith("/") ? trimmed : `/${trimmed}`; +} diff --git a/src/plugins/http-registry.ts b/src/plugins/http-registry.ts new file mode 100644 index 00000000000..ae84fc91c41 --- /dev/null +++ b/src/plugins/http-registry.ts @@ -0,0 +1,53 @@ +import type { IncomingMessage, ServerResponse } from "node:http"; + +import type { PluginHttpRouteRegistration, PluginRegistry } from "./registry.js"; +import { requireActivePluginRegistry } from "./runtime.js"; +import { normalizePluginHttpPath } from "./http-path.js"; + +export type PluginHttpRouteHandler = ( + req: IncomingMessage, + res: ServerResponse, +) => Promise | void; + +export function registerPluginHttpRoute(params: { + path?: string | null; + fallbackPath?: string | null; + handler: PluginHttpRouteHandler; + pluginId?: string; + source?: string; + accountId?: string; + log?: (message: string) => void; + registry?: PluginRegistry; +}): () => void { + const registry = params.registry ?? requireActivePluginRegistry(); + const routes = registry.httpRoutes ?? []; + registry.httpRoutes = routes; + + const normalizedPath = normalizePluginHttpPath(params.path, params.fallbackPath); + const suffix = params.accountId ? ` for account "${params.accountId}"` : ""; + if (!normalizedPath) { + params.log?.(`plugin: webhook path missing${suffix}`); + return () => {}; + } + + if (routes.some((entry) => entry.path === normalizedPath)) { + const pluginHint = params.pluginId ? ` (${params.pluginId})` : ""; + params.log?.(`plugin: webhook path ${normalizedPath} already registered${suffix}${pluginHint}`); + return () => {}; + } + + const entry: PluginHttpRouteRegistration = { + path: normalizedPath, + handler: params.handler, + pluginId: params.pluginId, + source: params.source, + }; + routes.push(entry); + + return () => { + const index = routes.indexOf(entry); + if (index >= 0) { + routes.splice(index, 1); + } + }; +} diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index f4c58148625..4417a13aac7 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -350,6 +350,33 @@ describe("loadClawdbotPlugins", () => { expect(httpPlugin?.httpHandlers).toBe(1); }); + it("registers http routes", () => { + process.env.CLAWDBOT_BUNDLED_PLUGINS_DIR = "/nonexistent/bundled/plugins"; + const plugin = writePlugin({ + id: "http-route-demo", + body: `export default { id: "http-route-demo", register(api) { + api.registerHttpRoute({ path: "/demo", handler: async (_req, res) => { res.statusCode = 200; res.end("ok"); } }); +} };`, + }); + + const registry = loadClawdbotPlugins({ + cache: false, + workspaceDir: plugin.dir, + config: { + plugins: { + load: { paths: [plugin.file] }, + allow: ["http-route-demo"], + }, + }, + }); + + const route = registry.httpRoutes.find((entry) => entry.pluginId === "http-route-demo"); + expect(route).toBeDefined(); + expect(route?.path).toBe("/demo"); + const httpPlugin = registry.plugins.find((entry) => entry.id === "http-route-demo"); + expect(httpPlugin?.httpHandlers).toBe(1); + }); + it("respects explicit disable in config", () => { process.env.CLAWDBOT_BUNDLED_PLUGINS_DIR = "/nonexistent/bundled/plugins"; const plugin = writePlugin({ diff --git a/src/plugins/registry.ts b/src/plugins/registry.ts index 048e490f345..8e9abdda565 100644 --- a/src/plugins/registry.ts +++ b/src/plugins/registry.ts @@ -13,6 +13,7 @@ import type { ClawdbotPluginCliRegistrar, ClawdbotPluginCommandDefinition, ClawdbotPluginHttpHandler, + ClawdbotPluginHttpRouteHandler, ClawdbotPluginHookOptions, ProviderPlugin, ClawdbotPluginService, @@ -31,6 +32,7 @@ import { registerPluginCommand } from "./commands.js"; import type { PluginRuntime } from "./runtime/types.js"; import type { HookEntry } from "../hooks/types.js"; import path from "node:path"; +import { normalizePluginHttpPath } from "./http-path.js"; export type PluginToolRegistration = { pluginId: string; @@ -53,6 +55,13 @@ export type PluginHttpRegistration = { source: string; }; +export type PluginHttpRouteRegistration = { + pluginId?: string; + path: string; + handler: ClawdbotPluginHttpRouteHandler; + source?: string; +}; + export type PluginChannelRegistration = { pluginId: string; plugin: ChannelPlugin; @@ -121,6 +130,7 @@ export type PluginRegistry = { providers: PluginProviderRegistration[]; gatewayHandlers: GatewayRequestHandlers; httpHandlers: PluginHttpRegistration[]; + httpRoutes: PluginHttpRouteRegistration[]; cliRegistrars: PluginCliRegistration[]; services: PluginServiceRegistration[]; commands: PluginCommandRegistration[]; @@ -143,6 +153,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) { providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], commands: [], @@ -280,6 +291,38 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) { }); }; + const registerHttpRoute = ( + record: PluginRecord, + params: { path: string; handler: ClawdbotPluginHttpRouteHandler }, + ) => { + const normalizedPath = normalizePluginHttpPath(params.path); + if (!normalizedPath) { + pushDiagnostic({ + level: "warn", + pluginId: record.id, + source: record.source, + message: "http route registration missing path", + }); + return; + } + if (registry.httpRoutes.some((entry) => entry.path === normalizedPath)) { + pushDiagnostic({ + level: "error", + pluginId: record.id, + source: record.source, + message: `http route already registered: ${normalizedPath}`, + }); + return; + } + record.httpHandlers += 1; + registry.httpRoutes.push({ + pluginId: record.id, + path: normalizedPath, + handler: params.handler, + source: record.source, + }); + }; + const registerChannel = ( record: PluginRecord, registration: ClawdbotPluginChannelRegistration | ChannelPlugin, @@ -439,6 +482,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) { registerHook: (events, handler, opts) => registerHook(record, events, handler, opts, params.config), registerHttpHandler: (handler) => registerHttpHandler(record, handler), + registerHttpRoute: (params) => registerHttpRoute(record, params), registerChannel: (registration) => registerChannel(record, registration), registerProvider: (provider) => registerProvider(record, provider), registerGatewayMethod: (method, handler) => registerGatewayMethod(record, method, handler), diff --git a/src/plugins/runtime.ts b/src/plugins/runtime.ts index 0da06ae63d8..16cb8d5c702 100644 --- a/src/plugins/runtime.ts +++ b/src/plugins/runtime.ts @@ -9,6 +9,7 @@ const createEmptyRegistry = (): PluginRegistry => ({ providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], commands: [], diff --git a/src/plugins/runtime/index.ts b/src/plugins/runtime/index.ts index a807bcacf15..685dcb38b3c 100644 --- a/src/plugins/runtime/index.ts +++ b/src/plugins/runtime/index.ts @@ -124,6 +124,26 @@ import { startWebLoginWithQr, waitForWebLogin } from "../../web/login-qr.js"; import { sendMessageWhatsApp, sendPollWhatsApp } from "../../web/outbound.js"; import { registerMemoryCli } from "../../cli/memory-cli.js"; import { formatNativeDependencyHint } from "./native-deps.js"; +import { textToSpeechTelephony } from "../../tts/tts.js"; +import { + listLineAccountIds, + normalizeAccountId as normalizeLineAccountId, + resolveDefaultLineAccountId, + resolveLineAccount, +} from "../../line/accounts.js"; +import { probeLineBot } from "../../line/probe.js"; +import { + createQuickReplyItems, + pushMessageLine, + pushMessagesLine, + pushFlexMessage, + pushTemplateMessage, + pushLocationMessage, + pushTextMessageWithQuickReplies, + sendMessageLine, +} from "../../line/send.js"; +import { monitorLineProvider } from "../../line/monitor.js"; +import { buildTemplateMessageFromPayload } from "../../line/template-messages.js"; import type { PluginRuntime } from "./types.js"; @@ -162,6 +182,9 @@ export function createPluginRuntime(): PluginRuntime { getImageMetadata, resizeToJpeg, }, + tts: { + textToSpeechTelephony, + }, tools: { createMemoryGetTool, createMemorySearchTool, @@ -295,6 +318,23 @@ export function createPluginRuntime(): PluginRuntime { handleWhatsAppAction, createLoginTool: createWhatsAppLoginTool, }, + line: { + listLineAccountIds, + resolveDefaultLineAccountId, + resolveLineAccount, + normalizeAccountId: normalizeLineAccountId, + probeLineBot, + sendMessageLine, + pushMessageLine, + pushMessagesLine, + pushFlexMessage, + pushTemplateMessage, + pushLocationMessage, + pushTextMessageWithQuickReplies, + createQuickReplyItems, + buildTemplateMessageFromPayload, + monitorLineProvider, + }, }, logging: { shouldLogVerbose, diff --git a/src/plugins/runtime/types.ts b/src/plugins/runtime/types.ts index 02755868154..b7aecaf1a3d 100644 --- a/src/plugins/runtime/types.ts +++ b/src/plugins/runtime/types.ts @@ -16,6 +16,7 @@ type UpsertChannelPairingRequest = typeof import("../../pairing/pairing-store.js").upsertChannelPairingRequest; type FetchRemoteMedia = typeof import("../../media/fetch.js").fetchRemoteMedia; type SaveMediaBuffer = typeof import("../../media/store.js").saveMediaBuffer; +type TextToSpeechTelephony = typeof import("../../tts/tts.js").textToSpeechTelephony; type BuildMentionRegexes = typeof import("../../auto-reply/reply/mentions.js").buildMentionRegexes; type MatchesMentionPatterns = typeof import("../../auto-reply/reply/mentions.js").matchesMentionPatterns; @@ -147,6 +148,26 @@ type HandleWhatsAppAction = type CreateWhatsAppLoginTool = typeof import("../../channels/plugins/agent-tools/whatsapp-login.js").createWhatsAppLoginTool; +// LINE channel types +type ListLineAccountIds = typeof import("../../line/accounts.js").listLineAccountIds; +type ResolveDefaultLineAccountId = + typeof import("../../line/accounts.js").resolveDefaultLineAccountId; +type ResolveLineAccount = typeof import("../../line/accounts.js").resolveLineAccount; +type NormalizeLineAccountId = typeof import("../../line/accounts.js").normalizeAccountId; +type ProbeLineBot = typeof import("../../line/probe.js").probeLineBot; +type SendMessageLine = typeof import("../../line/send.js").sendMessageLine; +type PushMessageLine = typeof import("../../line/send.js").pushMessageLine; +type PushMessagesLine = typeof import("../../line/send.js").pushMessagesLine; +type PushFlexMessage = typeof import("../../line/send.js").pushFlexMessage; +type PushTemplateMessage = typeof import("../../line/send.js").pushTemplateMessage; +type PushLocationMessage = typeof import("../../line/send.js").pushLocationMessage; +type PushTextMessageWithQuickReplies = + typeof import("../../line/send.js").pushTextMessageWithQuickReplies; +type CreateQuickReplyItems = typeof import("../../line/send.js").createQuickReplyItems; +type BuildTemplateMessageFromPayload = + typeof import("../../line/template-messages.js").buildTemplateMessageFromPayload; +type MonitorLineProvider = typeof import("../../line/monitor.js").monitorLineProvider; + export type RuntimeLogger = { debug?: (message: string) => void; info: (message: string) => void; @@ -173,6 +194,9 @@ export type PluginRuntime = { getImageMetadata: GetImageMetadata; resizeToJpeg: ResizeToJpeg; }; + tts: { + textToSpeechTelephony: TextToSpeechTelephony; + }; tools: { createMemoryGetTool: CreateMemoryGetTool; createMemorySearchTool: CreateMemorySearchTool; @@ -306,6 +330,23 @@ export type PluginRuntime = { handleWhatsAppAction: HandleWhatsAppAction; createLoginTool: CreateWhatsAppLoginTool; }; + line: { + listLineAccountIds: ListLineAccountIds; + resolveDefaultLineAccountId: ResolveDefaultLineAccountId; + resolveLineAccount: ResolveLineAccount; + normalizeAccountId: NormalizeLineAccountId; + probeLineBot: ProbeLineBot; + sendMessageLine: SendMessageLine; + pushMessageLine: PushMessageLine; + pushMessagesLine: PushMessagesLine; + pushFlexMessage: PushFlexMessage; + pushTemplateMessage: PushTemplateMessage; + pushLocationMessage: PushLocationMessage; + pushTextMessageWithQuickReplies: PushTextMessageWithQuickReplies; + createQuickReplyItems: CreateQuickReplyItems; + buildTemplateMessageFromPayload: BuildTemplateMessageFromPayload; + monitorLineProvider: MonitorLineProvider; + }; }; logging: { shouldLogVerbose: ShouldLogVerbose; diff --git a/src/plugins/types.ts b/src/plugins/types.ts index c5363d72eca..1ce9731ea06 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -12,6 +12,7 @@ import type { InternalHookHandler } from "../hooks/internal-hooks.js"; import type { HookEntry } from "../hooks/types.js"; import type { ModelProviderConfig } from "../config/types.js"; import type { RuntimeEnv } from "../runtime.js"; +import type { ReplyPayload } from "../auto-reply/types.js"; import type { WizardPrompter } from "../wizard/prompts.js"; import type { createVpsAwareOAuthHandlers } from "../commands/oauth-flow.js"; import type { GatewayRequestHandler } from "../gateway/server-methods/types.js"; @@ -154,10 +155,7 @@ export type PluginCommandContext = { /** * Result returned by a plugin command handler. */ -export type PluginCommandResult = { - /** Text response to send back to the user */ - text: string; -}; +export type PluginCommandResult = ReplyPayload; /** * Handler function for plugin commands. @@ -187,6 +185,11 @@ export type ClawdbotPluginHttpHandler = ( res: ServerResponse, ) => Promise | boolean; +export type ClawdbotPluginHttpRouteHandler = ( + req: IncomingMessage, + res: ServerResponse, +) => Promise | void; + export type ClawdbotPluginCliContext = { program: Command; config: ClawdbotConfig; @@ -249,6 +252,7 @@ export type ClawdbotPluginApi = { opts?: ClawdbotPluginHookOptions, ) => void; registerHttpHandler: (handler: ClawdbotPluginHttpHandler) => void; + registerHttpRoute: (params: { path: string; handler: ClawdbotPluginHttpRouteHandler }) => void; registerChannel: (registration: ClawdbotPluginChannelRegistration | ChannelPlugin) => void; registerGatewayMethod: (method: string, handler: GatewayRequestHandler) => void; registerCli: (registrar: ClawdbotPluginCliRegistrar, opts?: { commands?: string[] }) => void; diff --git a/src/plugins/voice-call.plugin.test.ts b/src/plugins/voice-call.plugin.test.ts index 55dae874f63..c29adce14d8 100644 --- a/src/plugins/voice-call.plugin.test.ts +++ b/src/plugins/voice-call.plugin.test.ts @@ -43,6 +43,7 @@ function setup(config: Record): Registered { source: "test", config: {}, pluginConfig: config, + runtime: { tts: { textToSpeechTelephony: vi.fn() } }, logger: noopLogger, registerGatewayMethod: (method, handler) => methods.set(method, handler), registerTool: (tool) => tools.push(tool), @@ -142,6 +143,7 @@ describe("voice-call plugin", () => { source: "test", config: {}, pluginConfig: { provider: "mock" }, + runtime: { tts: { textToSpeechTelephony: vi.fn() } }, logger: noopLogger, registerGatewayMethod: () => {}, registerTool: () => {}, diff --git a/src/routing/session-key.ts b/src/routing/session-key.ts index 028e657cbb8..7f9f209ede5 100644 --- a/src/routing/session-key.ts +++ b/src/routing/session-key.ts @@ -11,6 +11,12 @@ export const DEFAULT_AGENT_ID = "main"; export const DEFAULT_MAIN_KEY = "main"; export const DEFAULT_ACCOUNT_ID = "default"; +// Pre-compiled regex +const VALID_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i; +const INVALID_CHARS_RE = /[^a-z0-9_-]+/g; +const LEADING_DASH_RE = /^-+/; +const TRAILING_DASH_RE = /-+$/; + function normalizeToken(value: string | undefined | null): string { return (value ?? "").trim().toLowerCase(); } @@ -52,14 +58,14 @@ export function normalizeAgentId(value: string | undefined | null): string { const trimmed = (value ?? "").trim(); if (!trimmed) return DEFAULT_AGENT_ID; // Keep it path-safe + shell-friendly. - if (/^[a-z0-9][a-z0-9_-]{0,63}$/i.test(trimmed)) return trimmed.toLowerCase(); + if (VALID_ID_RE.test(trimmed)) return trimmed.toLowerCase(); // Best-effort fallback: collapse invalid characters to "-" return ( trimmed .toLowerCase() - .replace(/[^a-z0-9_-]+/g, "-") - .replace(/^-+/, "") - .replace(/-+$/, "") + .replace(INVALID_CHARS_RE, "-") + .replace(LEADING_DASH_RE, "") + .replace(TRAILING_DASH_RE, "") .slice(0, 64) || DEFAULT_AGENT_ID ); } @@ -67,13 +73,13 @@ export function normalizeAgentId(value: string | undefined | null): string { export function sanitizeAgentId(value: string | undefined | null): string { const trimmed = (value ?? "").trim(); if (!trimmed) return DEFAULT_AGENT_ID; - if (/^[a-z0-9][a-z0-9_-]{0,63}$/i.test(trimmed)) return trimmed.toLowerCase(); + if (VALID_ID_RE.test(trimmed)) return trimmed.toLowerCase(); return ( trimmed .toLowerCase() - .replace(/[^a-z0-9_-]+/gi, "-") - .replace(/^-+/, "") - .replace(/-+$/, "") + .replace(INVALID_CHARS_RE, "-") + .replace(LEADING_DASH_RE, "") + .replace(TRAILING_DASH_RE, "") .slice(0, 64) || DEFAULT_AGENT_ID ); } @@ -81,13 +87,13 @@ export function sanitizeAgentId(value: string | undefined | null): string { export function normalizeAccountId(value: string | undefined | null): string { const trimmed = (value ?? "").trim(); if (!trimmed) return DEFAULT_ACCOUNT_ID; - if (/^[a-z0-9][a-z0-9_-]{0,63}$/i.test(trimmed)) return trimmed.toLowerCase(); + if (VALID_ID_RE.test(trimmed)) return trimmed.toLowerCase(); return ( trimmed .toLowerCase() - .replace(/[^a-z0-9_-]+/g, "-") - .replace(/^-+/, "") - .replace(/-+$/, "") + .replace(INVALID_CHARS_RE, "-") + .replace(LEADING_DASH_RE, "") + .replace(TRAILING_DASH_RE, "") .slice(0, 64) || DEFAULT_ACCOUNT_ID ); } diff --git a/src/security/audit.test.ts b/src/security/audit.test.ts index cd7df057e1a..2ee7e27eee1 100644 --- a/src/security/audit.test.ts +++ b/src/security/audit.test.ts @@ -53,6 +53,55 @@ describe("security audit", () => { ).toBe(true); }); + it("warns when loopback control UI lacks trusted proxies", async () => { + const cfg: ClawdbotConfig = { + gateway: { + bind: "loopback", + controlUi: { enabled: true }, + }, + }; + + const res = await runSecurityAudit({ + config: cfg, + includeFilesystem: false, + includeChannelSecurity: false, + }); + + expect(res.findings).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + checkId: "gateway.trusted_proxies_missing", + severity: "warn", + }), + ]), + ); + }); + + it("flags loopback control UI without auth as critical", async () => { + const cfg: ClawdbotConfig = { + gateway: { + bind: "loopback", + controlUi: { enabled: true }, + auth: { mode: "none" as any }, + }, + }; + + const res = await runSecurityAudit({ + config: cfg, + includeFilesystem: false, + includeChannelSecurity: false, + }); + + expect(res.findings).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + checkId: "gateway.loopback_no_auth", + severity: "critical", + }), + ]), + ); + }); + it("flags logging.redactSensitive=off", async () => { const cfg: ClawdbotConfig = { logging: { redactSensitive: "off" }, diff --git a/src/security/audit.ts b/src/security/audit.ts index 87e6e339798..b2f9691c7b9 100644 --- a/src/security/audit.ts +++ b/src/security/audit.ts @@ -207,8 +207,18 @@ function collectGatewayConfigFindings(cfg: ClawdbotConfig): SecurityAuditFinding const bind = typeof cfg.gateway?.bind === "string" ? cfg.gateway.bind : "loopback"; const tailscaleMode = cfg.gateway?.tailscale?.mode ?? "off"; const auth = resolveGatewayAuth({ authConfig: cfg.gateway?.auth, tailscaleMode }); + const controlUiEnabled = cfg.gateway?.controlUi?.enabled !== false; + const trustedProxies = Array.isArray(cfg.gateway?.trustedProxies) + ? cfg.gateway.trustedProxies + : []; + const hasToken = typeof auth.token === "string" && auth.token.trim().length > 0; + const hasPassword = typeof auth.password === "string" && auth.password.trim().length > 0; + const hasSharedSecret = + (auth.mode === "token" && hasToken) || (auth.mode === "password" && hasPassword); + const hasTailscaleAuth = auth.allowTailscale === true && tailscaleMode === "serve"; + const hasGatewayAuth = hasSharedSecret || hasTailscaleAuth; - if (bind !== "loopback" && auth.mode === "none") { + if (bind !== "loopback" && !hasSharedSecret) { findings.push({ checkId: "gateway.bind_no_auth", severity: "critical", @@ -218,6 +228,32 @@ function collectGatewayConfigFindings(cfg: ClawdbotConfig): SecurityAuditFinding }); } + if (bind === "loopback" && controlUiEnabled && trustedProxies.length === 0) { + findings.push({ + checkId: "gateway.trusted_proxies_missing", + severity: "warn", + title: "Reverse proxy headers are not trusted", + detail: + "gateway.bind is loopback and gateway.trustedProxies is empty. " + + "If you expose the Control UI through a reverse proxy, configure trusted proxies " + + "so local-client checks cannot be spoofed.", + remediation: + "Set gateway.trustedProxies to your proxy IPs or keep the Control UI local-only.", + }); + } + + if (bind === "loopback" && controlUiEnabled && !hasGatewayAuth) { + findings.push({ + checkId: "gateway.loopback_no_auth", + severity: "critical", + title: "Gateway auth missing on loopback", + detail: + "gateway.bind is loopback but no gateway auth secret is configured. " + + "If the Control UI is exposed through a reverse proxy, unauthenticated access is possible.", + remediation: "Set gateway.auth (token recommended) or keep the Control UI local-only.", + }); + } + if (tailscaleMode === "funnel") { findings.push({ checkId: "gateway.tailscale_funnel", diff --git a/src/signal/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts b/src/signal/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts index c3709890ed1..2d4e9c54041 100644 --- a/src/signal/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts +++ b/src/signal/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts @@ -210,12 +210,9 @@ describe("monitorSignalProvider tool results", () => { ); }); - it("sends tool summaries with responsePrefix", async () => { + it("skips tool summaries with responsePrefix", async () => { const abortController = new AbortController(); - replyMock.mockImplementation(async (_ctx, opts) => { - await opts?.onToolResult?.({ text: "tool update" }); - return { text: "final reply" }; - }); + replyMock.mockResolvedValue({ text: "final reply" }); streamMock.mockImplementation(async ({ onEvent }) => { const payload = { @@ -243,9 +240,8 @@ describe("monitorSignalProvider tool results", () => { await flush(); - expect(sendMock).toHaveBeenCalledTimes(2); - expect(sendMock.mock.calls[0][1]).toBe("PFX tool update"); - expect(sendMock.mock.calls[1][1]).toBe("PFX final reply"); + expect(sendMock).toHaveBeenCalledTimes(1); + expect(sendMock.mock.calls[0][1]).toBe("PFX final reply"); }); it("replies with pairing code when dmPolicy is pairing and no allowFrom is set", async () => { diff --git a/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts b/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts index bcd79779345..ce7015399ed 100644 --- a/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts +++ b/src/slack/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts @@ -23,11 +23,8 @@ beforeEach(() => { }); describe("monitorSlackProvider tool results", () => { - it("sends tool summaries with responsePrefix", async () => { - replyMock.mockImplementation(async (_ctx, opts) => { - await opts?.onToolResult?.({ text: "tool update" }); - return { text: "final reply" }; - }); + it("skips tool summaries with responsePrefix", async () => { + replyMock.mockResolvedValue({ text: "final reply" }); const controller = new AbortController(); const run = monitorSlackProvider({ @@ -55,9 +52,8 @@ describe("monitorSlackProvider tool results", () => { controller.abort(); await run; - expect(sendMock).toHaveBeenCalledTimes(2); - expect(sendMock.mock.calls[0][1]).toBe("PFX tool update"); - expect(sendMock.mock.calls[1][1]).toBe("PFX final reply"); + expect(sendMock).toHaveBeenCalledTimes(1); + expect(sendMock.mock.calls[0][1]).toBe("PFX final reply"); }); it("drops events with mismatched api_app_id", async () => { @@ -130,10 +126,7 @@ describe("monitorSlackProvider tool results", () => { }, }; - replyMock.mockImplementation(async (_ctx, opts) => { - await opts?.onToolResult?.({ text: "tool update" }); - return { text: "final reply" }; - }); + replyMock.mockResolvedValue({ text: "final reply" }); const controller = new AbortController(); const run = monitorSlackProvider({ @@ -161,9 +154,8 @@ describe("monitorSlackProvider tool results", () => { controller.abort(); await run; - expect(sendMock).toHaveBeenCalledTimes(2); - expect(sendMock.mock.calls[0][1]).toBe("tool update"); - expect(sendMock.mock.calls[1][1]).toBe("final reply"); + expect(sendMock).toHaveBeenCalledTimes(1); + expect(sendMock.mock.calls[0][1]).toBe("final reply"); }); it("preserves RawBody without injecting processed room history", async () => { diff --git a/src/slack/monitor/message-handler/dispatch.ts b/src/slack/monitor/message-handler/dispatch.ts index d31885cfad6..38b69f04965 100644 --- a/src/slack/monitor/message-handler/dispatch.ts +++ b/src/slack/monitor/message-handler/dispatch.ts @@ -141,7 +141,9 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag }); markDispatchIdle(); - if (!queuedFinal) { + const anyReplyDelivered = queuedFinal || (counts.block ?? 0) > 0 || (counts.final ?? 0) > 0; + + if (!anyReplyDelivered) { if (prepared.isRoomish) { clearHistoryEntriesIfEnabled({ historyMap: ctx.channelHistories, diff --git a/src/telegram/bot.create-telegram-bot.applies-topic-skill-filters-system-prompts.test.ts b/src/telegram/bot.create-telegram-bot.applies-topic-skill-filters-system-prompts.test.ts index 0cda853be96..1a7a9d40c80 100644 --- a/src/telegram/bot.create-telegram-bot.applies-topic-skill-filters-system-prompts.test.ts +++ b/src/telegram/bot.create-telegram-bot.applies-topic-skill-filters-system-prompts.test.ts @@ -300,7 +300,7 @@ describe("createTelegramBot", () => { expect.objectContaining({ message_thread_id: 99 }), ); }); - it("streams tool summaries for native slash commands", async () => { + it("skips tool summaries for native slash commands", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); commandSpy.mockReset(); @@ -338,9 +338,8 @@ describe("createTelegramBot", () => { match: "on", }); - expect(sendMessageSpy).toHaveBeenCalledTimes(2); - expect(sendMessageSpy.mock.calls[0]?.[1]).toContain("tool update"); - expect(sendMessageSpy.mock.calls[1]?.[1]).toContain("final reply"); + expect(sendMessageSpy).toHaveBeenCalledTimes(1); + expect(sendMessageSpy.mock.calls[0]?.[1]).toContain("final reply"); }); it("dedupes duplicate message updates by update_id", async () => { onSpy.mockReset(); diff --git a/src/telegram/bot.create-telegram-bot.sends-replies-without-native-reply-threading.test.ts b/src/telegram/bot.create-telegram-bot.sends-replies-without-native-reply-threading.test.ts index 80c880b79ad..dffe8ee8809 100644 --- a/src/telegram/bot.create-telegram-bot.sends-replies-without-native-reply-threading.test.ts +++ b/src/telegram/bot.create-telegram-bot.sends-replies-without-native-reply-threading.test.ts @@ -217,15 +217,12 @@ describe("createTelegramBot", () => { expect(call[2]?.reply_to_message_id).toBeUndefined(); } }); - it("prefixes tool and final replies with responsePrefix", async () => { + it("prefixes final replies with responsePrefix", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); - replySpy.mockImplementation(async (_ctx, opts) => { - await opts?.onToolResult?.({ text: "tool result" }); - return { text: "final reply" }; - }); + replySpy.mockResolvedValue({ text: "final reply" }); loadConfig.mockReturnValue({ channels: { telegram: { dmPolicy: "open", allowFrom: ["*"] }, @@ -245,9 +242,8 @@ describe("createTelegramBot", () => { getFile: async () => ({ download: async () => new Uint8Array() }), }); - expect(sendMessageSpy).toHaveBeenCalledTimes(2); - expect(sendMessageSpy.mock.calls[0][1]).toBe("PFX tool result"); - expect(sendMessageSpy.mock.calls[1][1]).toBe("PFX final reply"); + expect(sendMessageSpy).toHaveBeenCalledTimes(1); + expect(sendMessageSpy.mock.calls[0][1]).toBe("PFX final reply"); }); it("honors replyToMode=all for threaded replies", async () => { onSpy.mockReset(); diff --git a/src/telegram/bot.test.ts b/src/telegram/bot.test.ts index 486741f5305..8dc52ab5744 100644 --- a/src/telegram/bot.test.ts +++ b/src/telegram/bot.test.ts @@ -865,15 +865,12 @@ describe("createTelegramBot", () => { } }); - it("prefixes tool and final replies with responsePrefix", async () => { + it("prefixes final replies with responsePrefix", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); - replySpy.mockImplementation(async (_ctx, opts) => { - await opts?.onToolResult?.({ text: "tool result" }); - return { text: "final reply" }; - }); + replySpy.mockResolvedValue({ text: "final reply" }); loadConfig.mockReturnValue({ channels: { telegram: { dmPolicy: "open", allowFrom: ["*"] }, @@ -893,9 +890,8 @@ describe("createTelegramBot", () => { getFile: async () => ({ download: async () => new Uint8Array() }), }); - expect(sendMessageSpy).toHaveBeenCalledTimes(2); - expect(sendMessageSpy.mock.calls[0][1]).toBe("PFX tool result"); - expect(sendMessageSpy.mock.calls[1][1]).toBe("PFX final reply"); + expect(sendMessageSpy).toHaveBeenCalledTimes(1); + expect(sendMessageSpy.mock.calls[0][1]).toBe("PFX final reply"); }); it("honors replyToMode=all for threaded replies", async () => { @@ -2288,7 +2284,7 @@ describe("createTelegramBot", () => { ); }); - it("streams tool summaries for native slash commands", async () => { + it("skips tool summaries for native slash commands", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); commandSpy.mockReset(); @@ -2326,9 +2322,8 @@ describe("createTelegramBot", () => { match: "on", }); - expect(sendMessageSpy).toHaveBeenCalledTimes(2); - expect(sendMessageSpy.mock.calls[0]?.[1]).toContain("tool update"); - expect(sendMessageSpy.mock.calls[1]?.[1]).toContain("final reply"); + expect(sendMessageSpy).toHaveBeenCalledTimes(1); + expect(sendMessageSpy.mock.calls[0]?.[1]).toContain("final reply"); }); it("dedupes duplicate message updates by update_id", async () => { diff --git a/src/telegram/bot/delivery.test.ts b/src/telegram/bot/delivery.test.ts index ca1b3f4cd28..404cc2fc266 100644 --- a/src/telegram/bot/delivery.test.ts +++ b/src/telegram/bot/delivery.test.ts @@ -17,6 +17,9 @@ vi.mock("grammy", () => ({ public fileName?: string, ) {} }, + GrammyError: class GrammyError extends Error { + description = ""; + }, })); describe("deliverReplies", () => { @@ -164,4 +167,111 @@ describe("deliverReplies", () => { }), ); }); + + it("falls back to text when sendVoice fails with VOICE_MESSAGES_FORBIDDEN", async () => { + const runtime = { error: vi.fn(), log: vi.fn() }; + const sendVoice = vi + .fn() + .mockRejectedValue( + new Error( + "GrammyError: Call to 'sendVoice' failed! (400: Bad Request: VOICE_MESSAGES_FORBIDDEN)", + ), + ); + const sendMessage = vi.fn().mockResolvedValue({ + message_id: 5, + chat: { id: "123" }, + }); + const bot = { api: { sendVoice, sendMessage } } as unknown as Bot; + + loadWebMedia.mockResolvedValueOnce({ + buffer: Buffer.from("voice"), + contentType: "audio/ogg", + fileName: "note.ogg", + }); + + await deliverReplies({ + replies: [ + { mediaUrl: "https://example.com/note.ogg", text: "Hello there", audioAsVoice: true }, + ], + chatId: "123", + token: "tok", + runtime, + bot, + replyToMode: "off", + textLimit: 4000, + }); + + // Voice was attempted but failed + expect(sendVoice).toHaveBeenCalledTimes(1); + // Fallback to text succeeded + expect(sendMessage).toHaveBeenCalledTimes(1); + expect(sendMessage).toHaveBeenCalledWith( + "123", + expect.stringContaining("Hello there"), + expect.any(Object), + ); + }); + + it("rethrows non-VOICE_MESSAGES_FORBIDDEN errors from sendVoice", async () => { + const runtime = { error: vi.fn(), log: vi.fn() }; + const sendVoice = vi.fn().mockRejectedValue(new Error("Network error")); + const sendMessage = vi.fn(); + const bot = { api: { sendVoice, sendMessage } } as unknown as Bot; + + loadWebMedia.mockResolvedValueOnce({ + buffer: Buffer.from("voice"), + contentType: "audio/ogg", + fileName: "note.ogg", + }); + + await expect( + deliverReplies({ + replies: [{ mediaUrl: "https://example.com/note.ogg", text: "Hello", audioAsVoice: true }], + chatId: "123", + token: "tok", + runtime, + bot, + replyToMode: "off", + textLimit: 4000, + }), + ).rejects.toThrow("Network error"); + + expect(sendVoice).toHaveBeenCalledTimes(1); + // Text fallback should NOT be attempted for other errors + expect(sendMessage).not.toHaveBeenCalled(); + }); + + it("rethrows VOICE_MESSAGES_FORBIDDEN when no text fallback is available", async () => { + const runtime = { error: vi.fn(), log: vi.fn() }; + const sendVoice = vi + .fn() + .mockRejectedValue( + new Error( + "GrammyError: Call to 'sendVoice' failed! (400: Bad Request: VOICE_MESSAGES_FORBIDDEN)", + ), + ); + const sendMessage = vi.fn(); + const bot = { api: { sendVoice, sendMessage } } as unknown as Bot; + + loadWebMedia.mockResolvedValueOnce({ + buffer: Buffer.from("voice"), + contentType: "audio/ogg", + fileName: "note.ogg", + }); + + await expect( + deliverReplies({ + replies: [{ mediaUrl: "https://example.com/note.ogg", audioAsVoice: true }], + chatId: "123", + token: "tok", + runtime, + bot, + replyToMode: "off", + textLimit: 4000, + }), + ).rejects.toThrow("VOICE_MESSAGES_FORBIDDEN"); + + expect(sendVoice).toHaveBeenCalledTimes(1); + expect(sendMessage).not.toHaveBeenCalled(); + }); }); diff --git a/src/telegram/bot/delivery.ts b/src/telegram/bot/delivery.ts index 2d117d748ce..4edc91c8ae9 100644 --- a/src/telegram/bot/delivery.ts +++ b/src/telegram/bot/delivery.ts @@ -1,4 +1,4 @@ -import { type Bot, InputFile } from "grammy"; +import { type Bot, GrammyError, InputFile } from "grammy"; import { markdownToTelegramChunks, markdownToTelegramHtml, @@ -22,6 +22,7 @@ import { buildTelegramThreadParams, resolveTelegramReplyId } from "./helpers.js" import type { TelegramContext } from "./types.js"; const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i; +const VOICE_FORBIDDEN_RE = /VOICE_MESSAGES_FORBIDDEN/; export async function deliverReplies(params: { replies: ReplyPayload[]; @@ -155,9 +156,39 @@ export async function deliverReplies(params: { // Voice message - displays as round playable bubble (opt-in via [[audio_as_voice]]) // Switch typing indicator to record_voice before sending. await params.onVoiceRecording?.(); - await bot.api.sendVoice(chatId, file, { - ...mediaParams, - }); + try { + await bot.api.sendVoice(chatId, file, { + ...mediaParams, + }); + } catch (voiceErr) { + // Fall back to text if voice messages are forbidden in this chat. + // This happens when the recipient has Telegram Premium privacy settings + // that block voice messages (Settings > Privacy > Voice Messages). + if (isVoiceMessagesForbidden(voiceErr)) { + const fallbackText = reply.text; + if (!fallbackText || !fallbackText.trim()) { + throw voiceErr; + } + logVerbose( + "telegram sendVoice forbidden (recipient has voice messages blocked in privacy settings); falling back to text", + ); + hasReplied = await sendTelegramVoiceFallbackText({ + bot, + chatId, + runtime, + text: fallbackText, + chunkText, + replyToId, + replyToMode, + hasReplied, + messageThreadId, + linkPreview, + }); + // Skip this media item; continue with next. + continue; + } + throw voiceErr; + } } else { // Audio file - displays with metadata (title, duration) - DEFAULT await bot.api.sendAudio(chatId, file, { @@ -228,6 +259,43 @@ export async function resolveMedia( return { path: saved.path, contentType: saved.contentType, placeholder }; } +function isVoiceMessagesForbidden(err: unknown): boolean { + if (err instanceof GrammyError) { + return VOICE_FORBIDDEN_RE.test(err.description); + } + return VOICE_FORBIDDEN_RE.test(formatErrorMessage(err)); +} + +async function sendTelegramVoiceFallbackText(opts: { + bot: Bot; + chatId: string; + runtime: RuntimeEnv; + text: string; + chunkText: (markdown: string) => ReturnType; + replyToId?: number; + replyToMode: ReplyToMode; + hasReplied: boolean; + messageThreadId?: number; + linkPreview?: boolean; +}): Promise { + const chunks = opts.chunkText(opts.text); + let hasReplied = opts.hasReplied; + for (const chunk of chunks) { + await sendTelegramText(opts.bot, opts.chatId, chunk.html, opts.runtime, { + replyToMessageId: + opts.replyToId && (opts.replyToMode === "all" || !hasReplied) ? opts.replyToId : undefined, + messageThreadId: opts.messageThreadId, + textMode: "html", + plainText: chunk.text, + linkPreview: opts.linkPreview, + }); + if (opts.replyToId && !hasReplied) { + hasReplied = true; + } + } + return hasReplied; +} + function buildTelegramSendParams(opts?: { replyToMessageId?: number; messageThreadId?: number; diff --git a/src/telegram/send.proxy.test.ts b/src/telegram/send.proxy.test.ts new file mode 100644 index 00000000000..b395662e4b6 --- /dev/null +++ b/src/telegram/send.proxy.test.ts @@ -0,0 +1,123 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const { botApi, botCtorSpy } = vi.hoisted(() => ({ + botApi: { + sendMessage: vi.fn(), + setMessageReaction: vi.fn(), + deleteMessage: vi.fn(), + }, + botCtorSpy: vi.fn(), +})); + +const { loadConfig } = vi.hoisted(() => ({ + loadConfig: vi.fn(() => ({})), +})); + +const { makeProxyFetch } = vi.hoisted(() => ({ + makeProxyFetch: vi.fn(), +})); + +const { resolveTelegramFetch } = vi.hoisted(() => ({ + resolveTelegramFetch: vi.fn(), +})); + +vi.mock("../config/config.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + loadConfig, + }; +}); + +vi.mock("./proxy.js", () => ({ + makeProxyFetch, +})); + +vi.mock("./fetch.js", () => ({ + resolveTelegramFetch, +})); + +vi.mock("grammy", () => ({ + Bot: class { + api = botApi; + constructor( + public token: string, + public options?: { client?: { fetch?: typeof fetch; timeoutSeconds?: number } }, + ) { + botCtorSpy(token, options); + } + }, + InputFile: class {}, +})); + +import { deleteMessageTelegram, reactMessageTelegram, sendMessageTelegram } from "./send.js"; + +describe("telegram proxy client", () => { + const proxyUrl = "http://proxy.test:8080"; + + beforeEach(() => { + botApi.sendMessage.mockResolvedValue({ message_id: 1, chat: { id: "123" } }); + botApi.setMessageReaction.mockResolvedValue(undefined); + botApi.deleteMessage.mockResolvedValue(true); + botCtorSpy.mockReset(); + loadConfig.mockReturnValue({ + channels: { telegram: { accounts: { foo: { proxy: proxyUrl } } } }, + }); + makeProxyFetch.mockReset(); + resolveTelegramFetch.mockReset(); + }); + + it("uses proxy fetch for sendMessage", async () => { + const proxyFetch = vi.fn(); + const fetchImpl = vi.fn(); + makeProxyFetch.mockReturnValue(proxyFetch as unknown as typeof fetch); + resolveTelegramFetch.mockReturnValue(fetchImpl as unknown as typeof fetch); + + await sendMessageTelegram("123", "hi", { token: "tok", accountId: "foo" }); + + expect(makeProxyFetch).toHaveBeenCalledWith(proxyUrl); + expect(resolveTelegramFetch).toHaveBeenCalledWith(proxyFetch); + expect(botCtorSpy).toHaveBeenCalledWith( + "tok", + expect.objectContaining({ + client: expect.objectContaining({ fetch: fetchImpl }), + }), + ); + }); + + it("uses proxy fetch for reactions", async () => { + const proxyFetch = vi.fn(); + const fetchImpl = vi.fn(); + makeProxyFetch.mockReturnValue(proxyFetch as unknown as typeof fetch); + resolveTelegramFetch.mockReturnValue(fetchImpl as unknown as typeof fetch); + + await reactMessageTelegram("123", "456", "✅", { token: "tok", accountId: "foo" }); + + expect(makeProxyFetch).toHaveBeenCalledWith(proxyUrl); + expect(resolveTelegramFetch).toHaveBeenCalledWith(proxyFetch); + expect(botCtorSpy).toHaveBeenCalledWith( + "tok", + expect.objectContaining({ + client: expect.objectContaining({ fetch: fetchImpl }), + }), + ); + }); + + it("uses proxy fetch for deleteMessage", async () => { + const proxyFetch = vi.fn(); + const fetchImpl = vi.fn(); + makeProxyFetch.mockReturnValue(proxyFetch as unknown as typeof fetch); + resolveTelegramFetch.mockReturnValue(fetchImpl as unknown as typeof fetch); + + await deleteMessageTelegram("123", "456", { token: "tok", accountId: "foo" }); + + expect(makeProxyFetch).toHaveBeenCalledWith(proxyUrl); + expect(resolveTelegramFetch).toHaveBeenCalledWith(proxyFetch); + expect(botCtorSpy).toHaveBeenCalledWith( + "tok", + expect.objectContaining({ + client: expect.objectContaining({ fetch: fetchImpl }), + }), + ); + }); +}); diff --git a/src/telegram/send.returns-undefined-empty-input.test.ts b/src/telegram/send.returns-undefined-empty-input.test.ts index bd83d746109..d659c198b93 100644 --- a/src/telegram/send.returns-undefined-empty-input.test.ts +++ b/src/telegram/send.returns-undefined-empty-input.test.ts @@ -152,6 +152,62 @@ describe("sendMessageTelegram", () => { expect(res.messageId).toBe("42"); }); + it("adds link_preview_options when previews are disabled in config", async () => { + const chatId = "123"; + const sendMessage = vi.fn().mockResolvedValue({ + message_id: 7, + chat: { id: chatId }, + }); + const api = { sendMessage } as unknown as { + sendMessage: typeof sendMessage; + }; + + loadConfig.mockReturnValue({ + channels: { telegram: { linkPreview: false } }, + }); + + await sendMessageTelegram(chatId, "hi", { token: "tok", api }); + + expect(sendMessage).toHaveBeenCalledWith(chatId, "hi", { + parse_mode: "HTML", + link_preview_options: { is_disabled: true }, + }); + }); + + it("keeps link_preview_options on plain-text fallback when disabled", async () => { + const chatId = "123"; + const parseErr = new Error( + "400: Bad Request: can't parse entities: Can't find end of the entity starting at byte offset 9", + ); + const sendMessage = vi + .fn() + .mockRejectedValueOnce(parseErr) + .mockResolvedValueOnce({ + message_id: 42, + chat: { id: chatId }, + }); + const api = { sendMessage } as unknown as { + sendMessage: typeof sendMessage; + }; + + loadConfig.mockReturnValue({ + channels: { telegram: { linkPreview: false } }, + }); + + await sendMessageTelegram(chatId, "_oops_", { + token: "tok", + api, + }); + + expect(sendMessage).toHaveBeenNthCalledWith(1, chatId, "oops", { + parse_mode: "HTML", + link_preview_options: { is_disabled: true }, + }); + expect(sendMessage).toHaveBeenNthCalledWith(2, chatId, "_oops_", { + link_preview_options: { is_disabled: true }, + }); + }); + it("uses native fetch for BAN compatibility when api is omitted", async () => { const originalFetch = globalThis.fetch; const originalBun = (globalThis as { Bun?: unknown }).Bun; diff --git a/src/telegram/send.ts b/src/telegram/send.ts index b440bbe8ad5..636676465d9 100644 --- a/src/telegram/send.ts +++ b/src/telegram/send.ts @@ -4,18 +4,22 @@ import type { ReactionType, ReactionTypeEmoji, } from "@grammyjs/types"; -import { type ApiClientOptions, Bot, InputFile } from "grammy"; +import { type ApiClientOptions, Bot, HttpError, InputFile } from "grammy"; import { loadConfig } from "../config/config.js"; import { logVerbose } from "../globals.js"; import { recordChannelActivity } from "../infra/channel-activity.js"; -import { formatErrorMessage } from "../infra/errors.js"; +import { formatErrorMessage, formatUncaughtError } from "../infra/errors.js"; +import { isDiagnosticFlagEnabled } from "../infra/diagnostic-flags.js"; import type { RetryConfig } from "../infra/retry.js"; import { createTelegramRetryRunner } from "../infra/retry-policy.js"; +import { redactSensitiveText } from "../logging/redact.js"; +import { createSubsystemLogger } from "../logging/subsystem.js"; import { mediaKindFromMime } from "../media/constants.js"; import { isGifMedia } from "../media/mime.js"; import { loadWebMedia } from "../web/media.js"; -import { resolveTelegramAccount } from "./accounts.js"; +import { type ResolvedTelegramAccount, resolveTelegramAccount } from "./accounts.js"; import { resolveTelegramFetch } from "./fetch.js"; +import { makeProxyFetch } from "./proxy.js"; import { renderTelegramHtmlText } from "./format.js"; import { resolveMarkdownTableMode } from "../config/markdown-tables.js"; import { splitTelegramCaption } from "./caption.js"; @@ -42,8 +46,6 @@ type TelegramSendOpts = { messageThreadId?: number; /** Inline keyboard buttons (reply markup). */ buttons?: Array>; - /** Controls whether link previews are shown. Default: true (previews enabled). */ - linkPreview?: boolean; }; type TelegramSendResult = { @@ -61,6 +63,38 @@ type TelegramReactionOpts = { }; const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i; +const diagLogger = createSubsystemLogger("telegram/diagnostic"); + +function createTelegramHttpLogger(cfg: ReturnType) { + const enabled = isDiagnosticFlagEnabled("telegram.http", cfg); + if (!enabled) { + return () => {}; + } + return (label: string, err: unknown) => { + if (!(err instanceof HttpError)) return; + const detail = redactSensitiveText(formatUncaughtError(err.error ?? err)); + diagLogger.warn(`telegram http error (${label}): ${detail}`); + }; +} + +function resolveTelegramClientOptions( + account: ResolvedTelegramAccount, +): ApiClientOptions | undefined { + const proxyUrl = account.config.proxy?.trim(); + const proxyFetch = proxyUrl ? makeProxyFetch(proxyUrl) : undefined; + const fetchImpl = resolveTelegramFetch(proxyFetch); + const timeoutSeconds = + typeof account.config.timeoutSeconds === "number" && + Number.isFinite(account.config.timeoutSeconds) + ? Math.max(1, Math.floor(account.config.timeoutSeconds)) + : undefined; + return fetchImpl || timeoutSeconds + ? { + ...(fetchImpl ? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] } : {}), + ...(timeoutSeconds ? { timeoutSeconds } : {}), + } + : undefined; +} function resolveToken(explicit: string | undefined, params: { accountId: string; token: string }) { if (explicit?.trim()) return explicit.trim(); @@ -148,19 +182,7 @@ export async function sendMessageTelegram( const chatId = normalizeChatId(target.chatId); // Use provided api or create a new Bot instance. The nullish coalescing // operator ensures api is always defined (Bot.api is always non-null). - const fetchImpl = resolveTelegramFetch(); - const timeoutSeconds = - typeof account.config.timeoutSeconds === "number" && - Number.isFinite(account.config.timeoutSeconds) - ? Math.max(1, Math.floor(account.config.timeoutSeconds)) - : undefined; - const client: ApiClientOptions | undefined = - fetchImpl || timeoutSeconds - ? { - ...(fetchImpl ? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] } : {}), - ...(timeoutSeconds ? { timeoutSeconds } : {}), - } - : undefined; + const client = resolveTelegramClientOptions(account); const api = opts.api ?? new Bot(token, client ? { client } : undefined).api; const mediaUrl = opts.mediaUrl?.trim(); const replyMarkup = buildInlineKeyboard(opts.buttons); @@ -180,7 +202,12 @@ export async function sendMessageTelegram( configRetry: account.config.retry, verbose: opts.verbose, }); - + const logHttpError = createTelegramHttpLogger(cfg); + const requestWithDiag = (fn: () => Promise, label?: string) => + request(fn, label).catch((err) => { + logHttpError(label ?? "request", err); + throw err; + }); const wrapChatNotFound = (err: unknown) => { if (!/400: Bad Request: chat not found/i.test(formatErrorMessage(err))) return err; return new Error( @@ -200,8 +227,8 @@ export async function sendMessageTelegram( }); const renderHtmlText = (value: string) => renderTelegramHtmlText(value, { textMode, tableMode }); - // Resolve link preview setting: explicit opt > config > default (enabled). - const linkPreviewEnabled = opts.linkPreview ?? account.config.linkPreview ?? true; + // Resolve link preview setting from config (default: enabled). + const linkPreviewEnabled = account.config.linkPreview ?? true; const linkPreviewOptions = linkPreviewEnabled ? undefined : { is_disabled: true }; const sendTelegramText = async ( @@ -210,35 +237,40 @@ export async function sendMessageTelegram( fallbackText?: string, ) => { const htmlText = renderHtmlText(rawText); + const baseParams = params ? { ...params } : {}; + if (linkPreviewOptions) { + baseParams.link_preview_options = linkPreviewOptions; + } + const hasBaseParams = Object.keys(baseParams).length > 0; const sendParams = { parse_mode: "HTML" as const, - ...(linkPreviewOptions ? { link_preview_options: linkPreviewOptions } : {}), - ...params, + ...baseParams, }; - const res = await request(() => api.sendMessage(chatId, htmlText, sendParams), "message").catch( - async (err) => { - // Telegram rejects malformed HTML (e.g., unsupported tags or entities). - // When that happens, fall back to plain text so the message still delivers. - const errText = formatErrorMessage(err); - if (PARSE_ERR_RE.test(errText)) { - if (opts.verbose) { - console.warn(`telegram HTML parse failed, retrying as plain text: ${errText}`); - } - const fallback = fallbackText ?? rawText; - const plainParams = params && Object.keys(params).length > 0 ? { ...params } : undefined; - return await request( - () => - plainParams - ? api.sendMessage(chatId, fallback, plainParams) - : api.sendMessage(chatId, fallback), - "message-plain", - ).catch((err2) => { - throw wrapChatNotFound(err2); - }); + const res = await requestWithDiag( + () => api.sendMessage(chatId, htmlText, sendParams), + "message", + ).catch(async (err) => { + // Telegram rejects malformed HTML (e.g., unsupported tags or entities). + // When that happens, fall back to plain text so the message still delivers. + const errText = formatErrorMessage(err); + if (PARSE_ERR_RE.test(errText)) { + if (opts.verbose) { + console.warn(`telegram HTML parse failed, retrying as plain text: ${errText}`); } - throw wrapChatNotFound(err); - }, - ); + const fallback = fallbackText ?? rawText; + const plainParams = hasBaseParams ? baseParams : undefined; + return await requestWithDiag( + () => + plainParams + ? api.sendMessage(chatId, fallback, plainParams) + : api.sendMessage(chatId, fallback), + "message-plain", + ).catch((err2) => { + throw wrapChatNotFound(err2); + }); + } + throw wrapChatNotFound(err); + }); return res; }; @@ -275,19 +307,20 @@ export async function sendMessageTelegram( | Awaited> | Awaited>; if (isGif) { - result = await request(() => api.sendAnimation(chatId, file, mediaParams), "animation").catch( - (err) => { - throw wrapChatNotFound(err); - }, - ); + result = await requestWithDiag( + () => api.sendAnimation(chatId, file, mediaParams), + "animation", + ).catch((err) => { + throw wrapChatNotFound(err); + }); } else if (kind === "image") { - result = await request(() => api.sendPhoto(chatId, file, mediaParams), "photo").catch( + result = await requestWithDiag(() => api.sendPhoto(chatId, file, mediaParams), "photo").catch( (err) => { throw wrapChatNotFound(err); }, ); } else if (kind === "video") { - result = await request(() => api.sendVideo(chatId, file, mediaParams), "video").catch( + result = await requestWithDiag(() => api.sendVideo(chatId, file, mediaParams), "video").catch( (err) => { throw wrapChatNotFound(err); }, @@ -300,24 +333,27 @@ export async function sendMessageTelegram( logFallback: logVerbose, }); if (useVoice) { - result = await request(() => api.sendVoice(chatId, file, mediaParams), "voice").catch( - (err) => { - throw wrapChatNotFound(err); - }, - ); + result = await requestWithDiag( + () => api.sendVoice(chatId, file, mediaParams), + "voice", + ).catch((err) => { + throw wrapChatNotFound(err); + }); } else { - result = await request(() => api.sendAudio(chatId, file, mediaParams), "audio").catch( - (err) => { - throw wrapChatNotFound(err); - }, - ); + result = await requestWithDiag( + () => api.sendAudio(chatId, file, mediaParams), + "audio", + ).catch((err) => { + throw wrapChatNotFound(err); + }); } } else { - result = await request(() => api.sendDocument(chatId, file, mediaParams), "document").catch( - (err) => { - throw wrapChatNotFound(err); - }, - ); + result = await requestWithDiag( + () => api.sendDocument(chatId, file, mediaParams), + "document", + ).catch((err) => { + throw wrapChatNotFound(err); + }); } const mediaMessageId = String(result?.message_id ?? "unknown"); const resolvedChatId = String(result?.chat?.id ?? chatId); @@ -388,16 +424,19 @@ export async function reactMessageTelegram( const token = resolveToken(opts.token, account); const chatId = normalizeChatId(String(chatIdInput)); const messageId = normalizeMessageId(messageIdInput); - const fetchImpl = resolveTelegramFetch(); - const client: ApiClientOptions | undefined = fetchImpl - ? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] } - : undefined; + const client = resolveTelegramClientOptions(account); const api = opts.api ?? new Bot(token, client ? { client } : undefined).api; const request = createTelegramRetryRunner({ retry: opts.retry, configRetry: account.config.retry, verbose: opts.verbose, }); + const logHttpError = createTelegramHttpLogger(cfg); + const requestWithDiag = (fn: () => Promise, label?: string) => + request(fn, label).catch((err) => { + logHttpError(label ?? "request", err); + throw err; + }); const remove = opts.remove === true; const trimmedEmoji = emoji.trim(); // Build the reaction array. We cast emoji to the grammY union type since @@ -409,7 +448,7 @@ export async function reactMessageTelegram( if (typeof api.setMessageReaction !== "function") { throw new Error("Telegram reactions are unavailable in this bot API."); } - await request(() => api.setMessageReaction(chatId, messageId, reactions), "reaction"); + await requestWithDiag(() => api.setMessageReaction(chatId, messageId, reactions), "reaction"); return { ok: true }; } @@ -434,17 +473,20 @@ export async function deleteMessageTelegram( const token = resolveToken(opts.token, account); const chatId = normalizeChatId(String(chatIdInput)); const messageId = normalizeMessageId(messageIdInput); - const fetchImpl = resolveTelegramFetch(); - const client: ApiClientOptions | undefined = fetchImpl - ? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] } - : undefined; + const client = resolveTelegramClientOptions(account); const api = opts.api ?? new Bot(token, client ? { client } : undefined).api; const request = createTelegramRetryRunner({ retry: opts.retry, configRetry: account.config.retry, verbose: opts.verbose, }); - await request(() => api.deleteMessage(chatId, messageId), "deleteMessage"); + const logHttpError = createTelegramHttpLogger(cfg); + const requestWithDiag = (fn: () => Promise, label?: string) => + request(fn, label).catch((err) => { + logHttpError(label ?? "request", err); + throw err; + }); + await requestWithDiag(() => api.deleteMessage(chatId, messageId), "deleteMessage"); logVerbose(`[telegram] Deleted message ${messageId} from chat ${chatId}`); return { ok: true }; } diff --git a/src/test-utils/channel-plugins.ts b/src/test-utils/channel-plugins.ts index 6bac4cfc24a..3e6bf211275 100644 --- a/src/test-utils/channel-plugins.ts +++ b/src/test-utils/channel-plugins.ts @@ -17,6 +17,7 @@ export const createTestRegistry = (channels: PluginRegistry["channels"] = []): P providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], commands: [], diff --git a/src/tts/tts.test.ts b/src/tts/tts.test.ts index a8c9dce9ce9..8462cba0184 100644 --- a/src/tts/tts.test.ts +++ b/src/tts/tts.test.ts @@ -109,13 +109,13 @@ describe("tts", () => { }); describe("isValidOpenAIModel", () => { - it("accepts gpt-4o-mini-tts model", () => { + it("accepts supported models", () => { expect(isValidOpenAIModel("gpt-4o-mini-tts")).toBe(true); + expect(isValidOpenAIModel("tts-1")).toBe(true); + expect(isValidOpenAIModel("tts-1-hd")).toBe(true); }); - it("rejects other models", () => { - expect(isValidOpenAIModel("tts-1")).toBe(false); - expect(isValidOpenAIModel("tts-1-hd")).toBe(false); + it("rejects unsupported models", () => { expect(isValidOpenAIModel("invalid")).toBe(false); expect(isValidOpenAIModel("")).toBe(false); expect(isValidOpenAIModel("gpt-4")).toBe(false); @@ -123,9 +123,11 @@ describe("tts", () => { }); describe("OPENAI_TTS_MODELS", () => { - it("contains only gpt-4o-mini-tts", () => { + it("contains supported models", () => { expect(OPENAI_TTS_MODELS).toContain("gpt-4o-mini-tts"); - expect(OPENAI_TTS_MODELS).toHaveLength(1); + expect(OPENAI_TTS_MODELS).toContain("tts-1"); + expect(OPENAI_TTS_MODELS).toContain("tts-1-hd"); + expect(OPENAI_TTS_MODELS).toHaveLength(3); }); it("is a non-empty array", () => { diff --git a/src/tts/tts.ts b/src/tts/tts.ts index 5fa06f8d454..847876d049e 100644 --- a/src/tts/tts.ts +++ b/src/tts/tts.ts @@ -76,6 +76,11 @@ const DEFAULT_OUTPUT = { voiceCompatible: false, }; +const TELEPHONY_OUTPUT = { + openai: { format: "pcm" as const, sampleRate: 24000 }, + elevenlabs: { format: "pcm_22050", sampleRate: 22050 }, +}; + const TTS_AUTO_MODES = new Set(["off", "always", "inbound", "tagged"]); export type ResolvedTtsConfig = { @@ -180,6 +185,16 @@ export type TtsResult = { voiceCompatible?: boolean; }; +export type TtsTelephonyResult = { + success: boolean; + audioBuffer?: Buffer; + error?: string; + latencyMs?: number; + provider?: string; + outputFormat?: string; + sampleRate?: number; +}; + type TtsStatusEntry = { timestamp: number; success: boolean; @@ -736,7 +751,17 @@ function parseTtsDirectives( }; } -export const OPENAI_TTS_MODELS = ["gpt-4o-mini-tts"] as const; +export const OPENAI_TTS_MODELS = ["gpt-4o-mini-tts", "tts-1", "tts-1-hd"] as const; + +/** + * Custom OpenAI-compatible TTS endpoint. + * When set, model/voice validation is relaxed to allow non-OpenAI models. + * Example: OPENAI_TTS_BASE_URL=http://localhost:8880/v1 + */ +const OPENAI_TTS_BASE_URL = ( + process.env.OPENAI_TTS_BASE_URL?.trim() || "https://api.openai.com/v1" +).replace(/\/+$/, ""); +const isCustomOpenAIEndpoint = OPENAI_TTS_BASE_URL !== "https://api.openai.com/v1"; export const OPENAI_TTS_VOICES = [ "alloy", "ash", @@ -752,10 +777,14 @@ export const OPENAI_TTS_VOICES = [ type OpenAiTtsVoice = (typeof OPENAI_TTS_VOICES)[number]; function isValidOpenAIModel(model: string): boolean { + // Allow any model when using custom endpoint (e.g., Kokoro, LocalAI) + if (isCustomOpenAIEndpoint) return true; return OPENAI_TTS_MODELS.includes(model as (typeof OPENAI_TTS_MODELS)[number]); } function isValidOpenAIVoice(voice: string): voice is OpenAiTtsVoice { + // Allow any voice when using custom endpoint (e.g., Kokoro Chinese voices) + if (isCustomOpenAIEndpoint) return true; return OPENAI_TTS_VOICES.includes(voice as OpenAiTtsVoice); } @@ -966,7 +995,7 @@ async function openaiTTS(params: { apiKey: string; model: string; voice: string; - responseFormat: "mp3" | "opus"; + responseFormat: "mp3" | "opus" | "pcm"; timeoutMs: number; }): Promise { const { text, apiKey, model, voice, responseFormat, timeoutMs } = params; @@ -982,7 +1011,7 @@ async function openaiTTS(params: { const timeout = setTimeout(() => controller.abort(), timeoutMs); try { - const response = await fetch("https://api.openai.com/v1/audio/speech", { + const response = await fetch(`${OPENAI_TTS_BASE_URL}/audio/speech`, { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, @@ -1210,6 +1239,100 @@ export async function textToSpeech(params: { }; } +export async function textToSpeechTelephony(params: { + text: string; + cfg: ClawdbotConfig; + prefsPath?: string; +}): Promise { + const config = resolveTtsConfig(params.cfg); + const prefsPath = params.prefsPath ?? resolveTtsPrefsPath(config); + + if (params.text.length > config.maxTextLength) { + return { + success: false, + error: `Text too long (${params.text.length} chars, max ${config.maxTextLength})`, + }; + } + + const userProvider = getTtsProvider(config, prefsPath); + const providers = resolveTtsProviderOrder(userProvider); + + let lastError: string | undefined; + + for (const provider of providers) { + const providerStart = Date.now(); + try { + if (provider === "edge") { + lastError = "edge: unsupported for telephony"; + continue; + } + + const apiKey = resolveTtsApiKey(config, provider); + if (!apiKey) { + lastError = `No API key for ${provider}`; + continue; + } + + if (provider === "elevenlabs") { + const output = TELEPHONY_OUTPUT.elevenlabs; + const audioBuffer = await elevenLabsTTS({ + text: params.text, + apiKey, + baseUrl: config.elevenlabs.baseUrl, + voiceId: config.elevenlabs.voiceId, + modelId: config.elevenlabs.modelId, + outputFormat: output.format, + seed: config.elevenlabs.seed, + applyTextNormalization: config.elevenlabs.applyTextNormalization, + languageCode: config.elevenlabs.languageCode, + voiceSettings: config.elevenlabs.voiceSettings, + timeoutMs: config.timeoutMs, + }); + + return { + success: true, + audioBuffer, + latencyMs: Date.now() - providerStart, + provider, + outputFormat: output.format, + sampleRate: output.sampleRate, + }; + } + + const output = TELEPHONY_OUTPUT.openai; + const audioBuffer = await openaiTTS({ + text: params.text, + apiKey, + model: config.openai.model, + voice: config.openai.voice, + responseFormat: output.format, + timeoutMs: config.timeoutMs, + }); + + return { + success: true, + audioBuffer, + latencyMs: Date.now() - providerStart, + provider, + outputFormat: output.format, + sampleRate: output.sampleRate, + }; + } catch (err) { + const error = err as Error; + if (error.name === "AbortError") { + lastError = `${provider}: request timed out`; + } else { + lastError = `${provider}: ${error.message}`; + } + } + } + + return { + success: false, + error: `TTS conversion failed: ${lastError || "no providers available"}`, + }; +} + export async function maybeApplyTtsToPayload(params: { payload: ReplyPayload; cfg: ClawdbotConfig; diff --git a/src/tui/components/filterable-select-list.ts b/src/tui/components/filterable-select-list.ts index 67361bcf178..a7b197bf5ff 100644 --- a/src/tui/components/filterable-select-list.ts +++ b/src/tui/components/filterable-select-list.ts @@ -69,7 +69,7 @@ export class FilterableSelectList implements Component { lines.push(filterLabel + inputText); // Separator - lines.push(chalk.dim("─".repeat(width))); + lines.push(chalk.dim("─".repeat(Math.max(0, width)))); // Select list const listLines = this.selectList.render(width); diff --git a/src/tui/components/searchable-select-list.ts b/src/tui/components/searchable-select-list.ts index f8e07e7908e..54fc3491847 100644 --- a/src/tui/components/searchable-select-list.ts +++ b/src/tui/components/searchable-select-list.ts @@ -214,7 +214,8 @@ export class SearchableSelectList implements Component { const maxValueWidth = Math.min(30, width - prefixWidth - 4); const truncatedValue = truncateToWidth(displayValue, maxValueWidth, ""); const valueText = this.highlightMatch(truncatedValue, query); - const spacing = " ".repeat(Math.max(1, 32 - visibleWidth(valueText))); + const spacingWidth = Math.max(1, 32 - visibleWidth(valueText)); + const spacing = " ".repeat(spacingWidth); const descriptionStart = prefixWidth + visibleWidth(valueText) + spacing.length; const remainingWidth = width - descriptionStart - 2; if (remainingWidth > 10) { diff --git a/src/utils/message-channel.test.ts b/src/utils/message-channel.test.ts index f61ee8fd363..5651b97a397 100644 --- a/src/utils/message-channel.test.ts +++ b/src/utils/message-channel.test.ts @@ -12,6 +12,7 @@ const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => providers: [], gatewayHandlers: {}, httpHandlers: [], + httpRoutes: [], cliRegistrars: [], services: [], diagnostics: [], diff --git a/src/web/auto-reply.web-auto-reply.sends-tool-summaries-immediately-responseprefix.test.ts b/src/web/auto-reply.web-auto-reply.sends-tool-summaries-immediately-responseprefix.test.ts index 41a4dfc2a00..19572b0dd99 100644 --- a/src/web/auto-reply.web-auto-reply.sends-tool-summaries-immediately-responseprefix.test.ts +++ b/src/web/auto-reply.web-auto-reply.sends-tool-summaries-immediately-responseprefix.test.ts @@ -105,7 +105,7 @@ describe("web auto-reply", () => { vi.useRealTimers(); }); - it("sends tool summaries immediately with responsePrefix", async () => { + it("skips tool summaries and sends final reply with responsePrefix", async () => { setLoadConfigMock(() => ({ channels: { whatsapp: { allowFrom: ["*"] } }, messages: { @@ -125,15 +125,7 @@ describe("web auto-reply", () => { return { close: vi.fn() }; }; - const resolver = vi - .fn() - .mockImplementation( - async (_ctx, opts?: { onToolResult?: (r: { text: string }) => Promise }) => { - await opts?.onToolResult?.({ text: "🧩 tool1" }); - await opts?.onToolResult?.({ text: "🧩 tool2" }); - return { text: "final" }; - }, - ); + const resolver = vi.fn().mockResolvedValue({ text: "final" }); await monitorWebChannel(false, listenerFactory, false, resolver); expect(capturedOnMessage).toBeDefined(); @@ -149,7 +141,7 @@ describe("web auto-reply", () => { }); const replies = reply.mock.calls.map((call) => call[0]); - expect(replies).toEqual(["🦞 🧩 tool1", "🦞 🧩 tool2", "🦞 final"]); + expect(replies).toEqual(["🦞 final"]); resetLoadConfigMock(); }); it("uses identity.name for messagePrefix when set", async () => { diff --git a/src/wizard/onboarding.finalize.ts b/src/wizard/onboarding.finalize.ts index ed9ce580dba..32ab53dcfd1 100644 --- a/src/wizard/onboarding.finalize.ts +++ b/src/wizard/onboarding.finalize.ts @@ -169,6 +169,7 @@ export async function finalizeOnboardingWizard(options: FinalizeOnboardingOption token: settings.gatewayToken, runtime: daemonRuntime, warn: (message, title) => prompter.note(message, title), + config: nextConfig, }); progress.update("Installing Gateway service…"); diff --git a/ui/src/main.ts b/ui/src/main.ts index 31d493921b1..9374bb20ec4 100644 --- a/ui/src/main.ts +++ b/ui/src/main.ts @@ -1,3 +1,2 @@ import "./styles.css"; import "./ui/app.ts"; - diff --git a/ui/src/styles/base.css b/ui/src/styles/base.css index 0b584d88faf..7baab70a234 100644 --- a/ui/src/styles/base.css +++ b/ui/src/styles/base.css @@ -1,60 +1,189 @@ -@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=Unbounded:wght@400;500;600&family=Work+Sans:wght@400;500;600;700&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"); :root { - --bg: #0a0f14; - --bg-accent: #111826; - --bg-grad-1: #162031; - --bg-grad-2: #1f2a22; - --bg-overlay: rgba(255, 255, 255, 0.05); - --bg-glow: rgba(245, 159, 74, 0.12); - --panel: rgba(14, 20, 30, 0.88); - --panel-strong: rgba(18, 26, 38, 0.96); - --chrome: rgba(9, 14, 20, 0.72); - --chrome-strong: rgba(9, 14, 20, 0.86); - --text: rgba(244, 246, 251, 0.96); - --chat-text: rgba(231, 237, 244, 0.92); - --muted: rgba(156, 169, 189, 0.72); - --border: rgba(255, 255, 255, 0.09); - --border-strong: rgba(255, 255, 255, 0.16); - --accent: #f59f4a; - --accent-2: #34c7b7; - --ok: #2bd97f; - --warn: #f2c94c; - --danger: #ff6b6b; - --focus: rgba(245, 159, 74, 0.35); + /* Background - Warmer dark with depth */ + --bg: #12141a; + --bg-accent: #14161d; + --bg-elevated: #1a1d25; + --bg-hover: #262a35; + --bg-muted: #262a35; + + /* Card / Surface - More contrast between levels */ + --card: #181b22; + --card-foreground: #f4f4f5; + --card-highlight: rgba(255, 255, 255, 0.05); + --popover: #181b22; + --popover-foreground: #f4f4f5; + + /* Panel */ + --panel: #12141a; + --panel-strong: #1a1d25; + --panel-hover: #262a35; + --chrome: rgba(18, 20, 26, 0.95); + --chrome-strong: rgba(18, 20, 26, 0.98); + + /* Text - Slightly warmer */ + --text: #e4e4e7; + --text-strong: #fafafa; + --chat-text: #e4e4e7; + --muted: #71717a; + --muted-strong: #52525b; + --muted-foreground: #71717a; + + /* Border - Subtle but defined */ + --border: #27272a; + --border-strong: #3f3f46; + --border-hover: #52525b; + --input: #27272a; + --ring: #ff5c5c; + + /* Accent - Punchy signature red */ + --accent: #ff5c5c; + --accent-hover: #ff7070; + --accent-muted: #ff5c5c; + --accent-subtle: rgba(255, 92, 92, 0.15); + --accent-foreground: #fafafa; + --accent-glow: rgba(255, 92, 92, 0.25); + --primary: #ff5c5c; + --primary-foreground: #ffffff; + + /* Secondary - Teal accent for variety */ + --secondary: #1e2028; + --secondary-foreground: #f4f4f5; + --accent-2: #14b8a6; + --accent-2-muted: rgba(20, 184, 166, 0.7); + --accent-2-subtle: rgba(20, 184, 166, 0.15); + + /* Semantic - More saturated */ + --ok: #22c55e; + --ok-muted: rgba(34, 197, 94, 0.75); + --ok-subtle: rgba(34, 197, 94, 0.12); + --destructive: #ef4444; + --destructive-foreground: #fafafa; + --warn: #f59e0b; + --warn-muted: rgba(245, 158, 11, 0.75); + --warn-subtle: rgba(245, 158, 11, 0.12); + --danger: #ef4444; + --danger-muted: rgba(239, 68, 68, 0.75); + --danger-subtle: rgba(239, 68, 68, 0.12); + --info: #3b82f6; + + /* Focus - With glow */ + --focus: rgba(255, 92, 92, 0.25); + --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--ring); + --focus-glow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ring), 0 0 20px var(--accent-glow); + + /* Grid */ --grid-line: rgba(255, 255, 255, 0.04); + + /* Theme transition */ --theme-switch-x: 50%; --theme-switch-y: 50%; - --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, - "Liberation Mono", "Courier New", monospace; - --font-body: "Work Sans", system-ui, sans-serif; - --font-display: "Unbounded", "Times New Roman", serif; + + /* Typography - Space Grotesk for personality */ + --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace; + --font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + + /* Shadows - Richer with subtle color */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.03); + --shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(255, 255, 255, 0.03); + --shadow-xl: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.03); + --shadow-glow: 0 0 30px var(--accent-glow); + + /* Radii - Slightly larger for friendlier feel */ + --radius-sm: 6px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + --radius-full: 9999px; + --radius: 8px; + + /* Transitions - Snappy but smooth */ + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); + --duration-fast: 120ms; + --duration-normal: 200ms; + --duration-slow: 350ms; + color-scheme: dark; } +/* Light theme - Clean with subtle warmth */ :root[data-theme="light"] { - --bg: #f5f1ea; - --bg-accent: #ffffff; - --bg-grad-1: #f1e6d6; - --bg-grad-2: #e5eef4; - --bg-overlay: rgba(28, 32, 46, 0.05); - --bg-glow: rgba(52, 199, 183, 0.14); - --panel: rgba(255, 255, 255, 0.9); - --panel-strong: rgba(255, 255, 255, 0.97); - --chrome: rgba(255, 255, 255, 0.75); - --chrome-strong: rgba(255, 255, 255, 0.88); - --text: rgba(27, 36, 50, 0.98); - --chat-text: rgba(36, 48, 66, 0.9); - --muted: rgba(80, 94, 114, 0.7); - --border: rgba(18, 24, 40, 0.12); - --border-strong: rgba(18, 24, 40, 0.2); - --accent: #e28a3f; - --accent-2: #1ba99d; - --ok: #1aa86c; - --warn: #b3771c; - --danger: #d44848; - --focus: rgba(226, 138, 63, 0.35); - --grid-line: rgba(18, 24, 40, 0.06); + --bg: #fafafa; + --bg-accent: #f5f5f5; + --bg-elevated: #ffffff; + --bg-hover: #f0f0f0; + --bg-muted: #f0f0f0; + --bg-content: #f5f5f5; + + --card: #ffffff; + --card-foreground: #18181b; + --card-highlight: rgba(0, 0, 0, 0.03); + --popover: #ffffff; + --popover-foreground: #18181b; + + --panel: #fafafa; + --panel-strong: #f5f5f5; + --panel-hover: #ebebeb; + --chrome: rgba(250, 250, 250, 0.95); + --chrome-strong: rgba(250, 250, 250, 0.98); + + --text: #3f3f46; + --text-strong: #18181b; + --chat-text: #3f3f46; + --muted: #71717a; + --muted-strong: #52525b; + --muted-foreground: #71717a; + + --border: #e4e4e7; + --border-strong: #d4d4d8; + --border-hover: #a1a1aa; + --input: #e4e4e7; + + --accent: #dc2626; + --accent-hover: #ef4444; + --accent-muted: #dc2626; + --accent-subtle: rgba(220, 38, 38, 0.12); + --accent-foreground: #ffffff; + --accent-glow: rgba(220, 38, 38, 0.15); + --primary: #dc2626; + --primary-foreground: #ffffff; + + --secondary: #f4f4f5; + --secondary-foreground: #3f3f46; + --accent-2: #0d9488; + --accent-2-muted: rgba(13, 148, 136, 0.75); + --accent-2-subtle: rgba(13, 148, 136, 0.12); + + --ok: #16a34a; + --ok-muted: rgba(22, 163, 74, 0.75); + --ok-subtle: rgba(22, 163, 74, 0.1); + --destructive: #dc2626; + --destructive-foreground: #fafafa; + --warn: #d97706; + --warn-muted: rgba(217, 119, 6, 0.75); + --warn-subtle: rgba(217, 119, 6, 0.1); + --danger: #dc2626; + --danger-muted: rgba(220, 38, 38, 0.75); + --danger-subtle: rgba(220, 38, 38, 0.1); + --info: #2563eb; + + --focus: rgba(220, 38, 38, 0.2); + --focus-glow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ring), 0 0 16px var(--accent-glow); + + --grid-line: rgba(0, 0, 0, 0.05); + + /* Light shadows */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 0 0 1px rgba(0, 0, 0, 0.04); + --shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.04); + --shadow-xl: 0 24px 48px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.04); + --shadow-glow: 0 0 24px var(--accent-glow); + color-scheme: light; } @@ -69,44 +198,21 @@ body { body { margin: 0; - font: 15px/1.5 var(--font-body); - background: - radial-gradient(1200px 900px at 15% -10%, var(--bg-grad-1) 0%, transparent 55%) - fixed, - radial-gradient(900px 700px at 80% 10%, var(--bg-grad-2) 0%, transparent 60%) - fixed, - linear-gradient(160deg, var(--bg) 0%, var(--bg-accent) 100%) fixed; + font: 400 14px/1.55 var(--font-body); + letter-spacing: -0.02em; + background: var(--bg); color: var(--text); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } -body::before { - content: ""; - position: fixed; - inset: 0; - background: - linear-gradient( - 140deg, - var(--bg-overlay) 0%, - rgba(255, 255, 255, 0) 40% - ), - radial-gradient(620px 420px at 75% 75%, var(--bg-glow), transparent 60%); - pointer-events: none; - z-index: 0; -} - -/* Grid overlay removed for cleaner look */ - +/* Theme transition */ @keyframes theme-circle-transition { 0% { - clip-path: circle( - 0% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%) - ); + clip-path: circle(0% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%)); } - 100% { - clip-path: circle( - 150% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%) - ); + clip-path: circle(150% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%)); } } @@ -123,7 +229,7 @@ html.theme-transition::view-transition-old(theme) { html.theme-transition::view-transition-new(theme) { mix-blend-mode: normal; z-index: 2; - animation: theme-circle-transition 0.45s ease-out forwards; + animation: theme-circle-transition 0.4s var(--ease-out) forwards; } @media (prefers-reduced-motion: reduce) { @@ -141,7 +247,12 @@ clawdbot-app { } a { - color: inherit; + color: var(--accent); + text-decoration: none; +} + +a:hover { + text-decoration: underline; } button, @@ -152,10 +263,35 @@ select { color: inherit; } +::selection { + background: var(--accent-subtle); + color: var(--text-strong); +} + +/* Scrollbar styling */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: var(--radius-full); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--border-strong); +} + +/* Animations - Polished with spring feel */ @keyframes rise { from { opacity: 0; - transform: translateY(6px); + transform: translateY(8px); } to { opacity: 1; @@ -163,6 +299,26 @@ select { } } +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes scale-in { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} + @keyframes dashboard-enter { from { opacity: 0; @@ -173,3 +329,44 @@ select { transform: translateY(0); } } + +@keyframes shimmer { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } +} + +@keyframes pulse-subtle { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.7; + } +} + +@keyframes glow-pulse { + 0%, 100% { + box-shadow: 0 0 0 rgba(255, 92, 92, 0); + } + 50% { + box-shadow: 0 0 20px var(--accent-glow); + } +} + +/* Stagger animation delays for grouped elements */ +.stagger-1 { animation-delay: 0ms; } +.stagger-2 { animation-delay: 50ms; } +.stagger-3 { animation-delay: 100ms; } +.stagger-4 { animation-delay: 150ms; } +.stagger-5 { animation-delay: 200ms; } +.stagger-6 { animation-delay: 250ms; } + +/* Focus visible styles */ +:focus-visible { + outline: none; + box-shadow: var(--focus-ring); +} diff --git a/ui/src/styles/chat/grouped.css b/ui/src/styles/chat/grouped.css index 371cf03c681..39b6418261a 100644 --- a/ui/src/styles/chat/grouped.css +++ b/ui/src/styles/chat/grouped.css @@ -8,7 +8,7 @@ gap: 12px; align-items: flex-start; margin-bottom: 16px; - margin-left: 16px; + margin-left: 4px; margin-right: 16px; } @@ -70,23 +70,23 @@ } .chat-avatar.user { - background: rgba(245, 159, 74, 0.2); - color: rgba(245, 159, 74, 1); + background: var(--accent-subtle); + color: var(--accent); } .chat-avatar.assistant { - background: rgba(52, 199, 183, 0.2); - color: rgba(52, 199, 183, 1); + background: var(--secondary); + color: var(--muted); } .chat-avatar.other { - background: rgba(150, 150, 150, 0.2); - color: rgba(150, 150, 150, 1); + background: var(--secondary); + color: var(--muted); } .chat-avatar.tool { - background: rgba(134, 142, 150, 0.2); - color: rgba(134, 142, 150, 1); + background: var(--secondary); + color: var(--muted); } /* Image avatar support */ @@ -100,9 +100,9 @@ img.chat-avatar { .chat-bubble { position: relative; display: inline-block; - border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.12); - border-radius: 12px; + border: 1px solid transparent; + background: var(--card); + border-radius: var(--radius-lg); padding: 10px 14px; box-shadow: none; transition: background 150ms ease-out, border-color 150ms ease-out; @@ -119,9 +119,9 @@ img.chat-avatar { top: 6px; right: 8px; border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.22); + background: var(--bg); color: var(--muted); - border-radius: 8px; + border-radius: var(--radius-md); padding: 4px 6px; font-size: 14px; line-height: 1; @@ -132,9 +132,40 @@ img.chat-avatar { } .chat-copy-btn__icon { - display: inline-block; - width: 1em; - text-align: center; + display: inline-flex; + width: 14px; + height: 14px; + position: relative; +} + +.chat-copy-btn__icon svg { + width: 14px; + height: 14px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; +} + +.chat-copy-btn__icon-copy, +.chat-copy-btn__icon-check { + position: absolute; + top: 0; + left: 0; + transition: opacity 150ms ease; +} + +.chat-copy-btn__icon-check { + opacity: 0; +} + +.chat-copy-btn[data-copied="1"] .chat-copy-btn__icon-copy { + opacity: 0; +} + +.chat-copy-btn[data-copied="1"] .chat-copy-btn__icon-check { + opacity: 1; } .chat-bubble:hover .chat-copy-btn { @@ -143,7 +174,7 @@ img.chat-avatar { } .chat-copy-btn:hover { - background: rgba(0, 0, 0, 0.3); + background: var(--bg-hover); } .chat-copy-btn[data-copying="1"] { @@ -154,17 +185,17 @@ img.chat-avatar { .chat-copy-btn[data-error="1"] { opacity: 1; pointer-events: auto; - border-color: rgba(255, 69, 58, 0.8); - background: rgba(255, 69, 58, 0.18); - color: rgba(255, 69, 58, 1); + border-color: var(--danger-subtle); + background: var(--danger-subtle); + color: var(--danger); } .chat-copy-btn[data-copied="1"] { opacity: 1; pointer-events: auto; - border-color: rgba(52, 199, 183, 0.8); - background: rgba(52, 199, 183, 0.18); - color: rgba(52, 199, 183, 1); + border-color: var(--ok-subtle); + background: var(--ok-subtle); + color: var(--ok); } .chat-copy-btn:focus-visible { @@ -181,18 +212,29 @@ img.chat-avatar { } } +/* Light mode: restore borders */ +:root[data-theme="light"] .chat-bubble { + border-color: var(--border); + box-shadow: inset 0 1px 0 var(--card-highlight); +} + .chat-bubble:hover { - background: rgba(0, 0, 0, 0.18); + background: var(--bg-hover); } /* User bubbles have different styling */ .chat-group.user .chat-bubble { - background: rgba(245, 159, 74, 0.15); - border-color: rgba(245, 159, 74, 0.3); + background: var(--accent-subtle); + border-color: transparent; +} + +:root[data-theme="light"] .chat-group.user .chat-bubble { + border-color: rgba(234, 88, 12, 0.2); + background: rgba(251, 146, 60, 0.12); } .chat-group.user .chat-bubble:hover { - background: rgba(245, 159, 74, 0.22); + background: rgba(255, 77, 77, 0.15); } /* Streaming animation */ diff --git a/ui/src/styles/chat/layout.css b/ui/src/styles/chat/layout.css index a416d92cdc1..e11fedb71b1 100644 --- a/ui/src/styles/chat/layout.css +++ b/ui/src/styles/chat/layout.css @@ -52,8 +52,8 @@ flex: 1 1 0; /* Grow, shrink, and use 0 base for proper scrolling */ overflow-y: auto; overflow-x: hidden; - padding: 12px; - margin: 0 -12px; + padding: 12px 4px; + margin: 0 -4px; min-height: 0; /* Allow shrinking for flex scroll behavior */ border-radius: 12px; background: transparent; @@ -87,23 +87,154 @@ border-color: var(--accent); } +.chat-focus-exit svg { + width: 16px; + height: 16px; + stroke: currentColor; + fill: none; + stroke-width: 2px; + stroke-linecap: round; + stroke-linejoin: round; +} + /* Chat compose - sticky at bottom */ .chat-compose { position: sticky; bottom: 0; flex-shrink: 0; display: flex; - align-items: flex-end; + flex-direction: column; gap: 12px; margin-top: auto; /* Push to bottom of flex container */ - padding: 16px 0 4px; + padding: 12px 4px 4px; background: linear-gradient(to bottom, transparent, var(--bg) 20%); z-index: 10; } +/* Image attachments preview */ +.chat-attachments { + display: inline-flex; + flex-wrap: wrap; + gap: 8px; + padding: 8px; + background: var(--panel); + border-radius: 8px; + border: 1px solid var(--border); + width: fit-content; + max-width: 100%; + align-self: flex-start; /* Don't stretch in flex column parent */ +} + +.chat-attachment { + position: relative; + width: 80px; + height: 80px; + border-radius: 6px; + overflow: hidden; + border: 1px solid var(--border); + background: var(--bg); +} + +.chat-attachment__img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.chat-attachment__remove { + position: absolute; + top: 4px; + right: 4px; + width: 20px; + height: 20px; + border-radius: 50%; + border: none; + background: rgba(0, 0, 0, 0.7); + color: #fff; + font-size: 12px; + line-height: 1; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 150ms ease-out; +} + +.chat-attachment:hover .chat-attachment__remove { + opacity: 1; +} + +.chat-attachment__remove:hover { + background: rgba(220, 38, 38, 0.9); +} + +.chat-attachment__remove svg { + width: 12px; + height: 12px; + stroke: currentColor; + fill: none; + stroke-width: 2px; +} + +/* Light theme attachment overrides */ +:root[data-theme="light"] .chat-attachments { + background: #f8fafc; + border-color: rgba(16, 24, 40, 0.1); +} + +:root[data-theme="light"] .chat-attachment { + border-color: rgba(16, 24, 40, 0.15); + background: #fff; +} + +:root[data-theme="light"] .chat-attachment__remove { + background: rgba(0, 0, 0, 0.6); +} + +/* Message images (sent images displayed in chat) */ +.chat-message-images { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 8px; +} + +.chat-message-image { + max-width: 300px; + max-height: 200px; + border-radius: 8px; + object-fit: contain; + cursor: pointer; + transition: transform 150ms ease-out; +} + +.chat-message-image:hover { + transform: scale(1.02); +} + +/* User message images align right */ +.chat-group.user .chat-message-images { + justify-content: flex-end; +} + +/* Compose input row - horizontal layout */ +.chat-compose__row { + display: flex; + align-items: stretch; + gap: 12px; + flex: 1; +} + +:root[data-theme="light"] .chat-compose { + background: linear-gradient(to bottom, transparent, var(--bg-content) 20%); +} + .chat-compose__field { flex: 1 1 auto; min-width: 0; + display: flex; + align-items: stretch; } /* Hide the "Message" label - keep textarea only */ @@ -114,10 +245,11 @@ /* Override .field textarea min-height (180px) from components.css */ .chat-compose .chat-compose__field textarea { width: 100%; - min-height: 36px; + height: 40px; + min-height: 40px; max-height: 150px; - padding: 8px 12px; - border-radius: 10px; + padding: 9px 12px; + border-radius: 8px; resize: vertical; white-space: pre-wrap; font-family: var(--font-body); @@ -134,13 +266,18 @@ flex-shrink: 0; display: flex; align-items: stretch; + gap: 8px; } .chat-compose .chat-compose__actions .btn { - padding: 8px 16px; + padding: 0 16px; font-size: 13px; - min-height: 36px; + height: 40px; + min-height: 40px; + max-height: 40px; + line-height: 1; white-space: nowrap; + box-sizing: border-box; } /* Chat controls - moved to content-header area, left aligned */ @@ -194,20 +331,27 @@ /* Light theme icon button overrides */ :root[data-theme="light"] .btn--icon { - background: rgba(255, 255, 255, 0.9); - border-color: rgba(16, 24, 40, 0.2); + background: #ffffff; + border-color: var(--border); box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05); - color: rgba(16, 24, 40, 0.7); + color: var(--muted); } :root[data-theme="light"] .btn--icon:hover { - background: rgba(255, 255, 255, 1); - border-color: rgba(16, 24, 40, 0.3); - color: rgba(16, 24, 40, 0.9); + background: #ffffff; + border-color: var(--border-strong); + color: var(--text); } .btn--icon svg { display: block; + width: 18px; + height: 18px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; } .chat-controls__session select { @@ -250,4 +394,3 @@ min-width: 120px; } } - diff --git a/ui/src/styles/chat/text.css b/ui/src/styles/chat/text.css index 3d01dd49367..13e245de251 100644 --- a/ui/src/styles/chat/text.css +++ b/ui/src/styles/chat/text.css @@ -14,8 +14,8 @@ } :root[data-theme="light"] .chat-thinking { - border-color: rgba(16, 24, 40, 0.18); - background: rgba(16, 24, 40, 0.03); + border-color: rgba(16, 24, 40, 0.25); + background: rgba(16, 24, 40, 0.04); } .chat-text { @@ -75,9 +75,46 @@ } .chat-text :where(blockquote) { - border-left: 3px solid var(--border); + border-left: 3px solid var(--border-strong); padding-left: 12px; + margin-left: 0; color: var(--muted); + background: rgba(255, 255, 255, 0.02); + padding: 8px 12px; + border-radius: 0 var(--radius-sm) var(--radius-sm) 0; +} + +.chat-text :where(blockquote blockquote) { + margin-top: 8px; + border-left-color: var(--border-hover); + background: rgba(255, 255, 255, 0.03); +} + +.chat-text :where(blockquote blockquote blockquote) { + border-left-color: var(--muted-strong); + background: rgba(255, 255, 255, 0.04); +} + +:root[data-theme="light"] .chat-text :where(blockquote) { + background: rgba(0, 0, 0, 0.03); +} + +:root[data-theme="light"] .chat-text :where(blockquote blockquote) { + background: rgba(0, 0, 0, 0.05); +} + +:root[data-theme="light"] .chat-text :where(blockquote blockquote blockquote) { + background: rgba(0, 0, 0, 0.04); +} + +:root[data-theme="light"] .chat-text :where(:not(pre) > code) { + background: rgba(0, 0, 0, 0.08); + border: 1px solid rgba(0, 0, 0, 0.1); +} + +:root[data-theme="light"] .chat-text :where(pre) { + background: rgba(0, 0, 0, 0.05); + border: 1px solid rgba(0, 0, 0, 0.1); } .chat-text :where(hr) { @@ -85,4 +122,3 @@ border-top: 1px solid var(--border); margin: 1em 0; } - diff --git a/ui/src/styles/chat/tool-cards.css b/ui/src/styles/chat/tool-cards.css index d6998a35c4e..052e63dbb9d 100644 --- a/ui/src/styles/chat/tool-cards.css +++ b/ui/src/styles/chat/tool-cards.css @@ -4,6 +4,8 @@ border-radius: 8px; padding: 12px; margin-top: 8px; + background: var(--card); + box-shadow: inset 0 1px 0 var(--card-highlight); transition: border-color 150ms ease-out, background 150ms ease-out; /* Fixed max-height to ensure cards don't expand too much */ max-height: 120px; @@ -11,8 +13,8 @@ } .chat-tool-card:hover { - border-color: var(--accent); - background: rgba(0, 0, 0, 0.06); + border-color: var(--border-strong); + background: var(--bg-hover); } /* First tool card in a group - no top margin */ @@ -50,33 +52,63 @@ display: inline-flex; align-items: center; justify-content: center; - width: 18px; - height: 18px; - font-size: 14px; - line-height: 1; - font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; - vertical-align: middle; + width: 16px; + height: 16px; flex-shrink: 0; } +.chat-tool-card__icon svg { + width: 14px; + height: 14px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; +} + /* "View >" action link */ .chat-tool-card__action { + display: inline-flex; + align-items: center; + gap: 4px; font-size: 12px; color: var(--accent); opacity: 0.8; transition: opacity 150ms ease-out; } +.chat-tool-card__action svg { + width: 12px; + height: 12px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; +} + .chat-tool-card--clickable:hover .chat-tool-card__action { opacity: 1; } /* Status indicator for completed/empty results */ .chat-tool-card__status { - font-size: 14px; + display: inline-flex; + align-items: center; color: var(--ok); } +.chat-tool-card__status svg { + width: 14px; + height: 14px; + stroke: currentColor; + fill: none; + stroke-width: 2px; + stroke-linecap: round; + stroke-linejoin: round; +} + .chat-tool-card__status-text { font-size: 11px; margin-top: 4px; @@ -94,18 +126,18 @@ color: var(--muted); margin-top: 8px; padding: 8px 10px; - background: rgba(0, 0, 0, 0.08); - border-radius: 6px; + background: var(--secondary); + border-radius: var(--radius-md); white-space: pre-wrap; overflow: hidden; max-height: 44px; line-height: 1.4; - border: 1px solid rgba(255, 255, 255, 0.04); + border: 1px solid var(--border); } .chat-tool-card--clickable:hover .chat-tool-card__preview { - background: rgba(0, 0, 0, 0.12); - border-color: rgba(255, 255, 255, 0.08); + background: var(--bg-hover); + border-color: var(--border-strong); } /* Short inline output */ @@ -114,8 +146,8 @@ color: var(--text); margin-top: 6px; padding: 6px 8px; - background: rgba(0, 0, 0, 0.06); - border-radius: 4px; + background: var(--secondary); + border-radius: var(--radius-sm); white-space: pre-wrap; word-break: break-word; } @@ -164,4 +196,3 @@ transform: scale(1); } } - diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css index b46f55b6f15..27dfe62d15b 100644 --- a/ui/src/styles/components.css +++ b/ui/src/styles/components.css @@ -1,45 +1,75 @@ @import './chat.css'; +/* =========================================== + Cards - Refined with depth + =========================================== */ + .card { border: 1px solid var(--border); - background: linear-gradient(160deg, rgba(255, 255, 255, 0.04), transparent 65%), - var(--panel); - border-radius: 16px; - padding: 16px; - box-shadow: 0 18px 36px rgba(0, 0, 0, 0.28); - animation: rise 0.4s ease; + background: var(--card); + border-radius: var(--radius-lg); + padding: 20px; + animation: rise 0.35s var(--ease-out) backwards; + transition: + border-color var(--duration-normal) var(--ease-out), + box-shadow var(--duration-normal) var(--ease-out), + transform var(--duration-normal) var(--ease-out); + box-shadow: var(--shadow-sm), inset 0 1px 0 var(--card-highlight); +} + +.card:hover { + border-color: var(--border-strong); + box-shadow: var(--shadow-md), inset 0 1px 0 var(--card-highlight); } .card-title { - font-family: var(--font-display); - font-size: 16px; - letter-spacing: 0.6px; - text-transform: uppercase; + font-size: 15px; + font-weight: 600; + letter-spacing: -0.02em; + color: var(--text-strong); } .card-sub { color: var(--muted); - font-size: 12px; + font-size: 13px; + margin-top: 6px; + line-height: 1.5; } +/* =========================================== + Stats - Bold values, subtle labels + =========================================== */ + .stat { - background: linear-gradient(140deg, rgba(255, 255, 255, 0.04), transparent 70%), - var(--panel-strong); - border-radius: 14px; - padding: 12px; - border: 1px solid var(--border-strong); + background: var(--card); + border-radius: var(--radius-md); + padding: 14px 16px; + border: 1px solid var(--border); + transition: + border-color var(--duration-normal) var(--ease-out), + box-shadow var(--duration-normal) var(--ease-out); + box-shadow: inset 0 1px 0 var(--card-highlight); +} + +.stat:hover { + border-color: var(--border-strong); + box-shadow: var(--shadow-sm), inset 0 1px 0 var(--card-highlight); } .stat-label { color: var(--muted); font-size: 11px; + font-weight: 500; text-transform: uppercase; - letter-spacing: 1px; + letter-spacing: 0.04em; } .stat-value { - font-size: 18px; + font-size: 24px; + font-weight: 700; margin-top: 6px; + letter-spacing: -0.03em; + line-height: 1.1; } .stat-value.ok { @@ -57,9 +87,13 @@ .note-title { font-weight: 600; - letter-spacing: 0.2px; + letter-spacing: -0.01em; } +/* =========================================== + Status List + =========================================== */ + .status-list { display: grid; gap: 8px; @@ -69,8 +103,8 @@ display: flex; justify-content: space-between; gap: 12px; - padding: 6px 0; - border-bottom: 1px dashed rgba(255, 255, 255, 0.06); + padding: 8px 0; + border-bottom: 1px solid var(--border); } .status-list div:last-child { @@ -78,25 +112,28 @@ } .account-count { - margin-top: 8px; + margin-top: 10px; font-size: 12px; - font-weight: 600; - letter-spacing: 0.4px; + font-weight: 500; color: var(--muted); } .account-card-list { margin-top: 16px; display: grid; - gap: 10px; + gap: 12px; } .account-card { border: 1px solid var(--border); - border-radius: 10px; + border-radius: var(--radius-md); padding: 12px; - background: linear-gradient(160deg, rgba(255, 255, 255, 0.06), transparent), - rgba(255, 255, 255, 0.03); + background: var(--bg-elevated); + transition: border-color var(--duration-fast) ease; +} + +.account-card:hover { + border-color: var(--border-strong); } .account-card-header { @@ -107,7 +144,7 @@ } .account-card-title { - font-weight: 600; + font-weight: 500; } .account-card-id { @@ -117,7 +154,7 @@ } .account-card-status { - margin-top: 8px; + margin-top: 10px; font-size: 13px; } @@ -126,33 +163,52 @@ } .account-card-error { - margin-top: 6px; + margin-top: 8px; color: var(--danger); font-size: 12px; } +/* =========================================== + Labels & Pills + =========================================== */ + .label { color: var(--muted); - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.9px; + font-size: 12px; + font-weight: 500; } .pill { display: inline-flex; align-items: center; - gap: 8px; - border: 1px solid var(--border-strong); + gap: 6px; + border: 1px solid var(--border); padding: 6px 12px; - border-radius: 999px; - background: linear-gradient(160deg, rgba(255, 255, 255, 0.06), transparent), - var(--panel); + border-radius: var(--radius-full); + background: var(--secondary); + font-size: 13px; + font-weight: 500; + transition: border-color var(--duration-fast) ease; } +.pill:hover { + border-color: var(--border-strong); +} + +.pill.danger { + border-color: var(--danger-subtle); + background: var(--danger-subtle); + color: var(--danger); +} + +/* =========================================== + Theme Toggle + =========================================== */ + .theme-toggle { --theme-item: 28px; - --theme-gap: 6px; - --theme-pad: 6px; + --theme-gap: 2px; + --theme-pad: 4px; position: relative; } @@ -162,9 +218,9 @@ grid-template-columns: repeat(3, var(--theme-item)); gap: var(--theme-gap); padding: var(--theme-pad); - border-radius: 999px; - border: 1px solid var(--border-strong); - background: rgba(255, 255, 255, 0.04); + border-radius: var(--radius-full); + border: 1px solid var(--border); + background: var(--secondary); } .theme-toggle__indicator { @@ -173,15 +229,11 @@ left: var(--theme-pad); width: var(--theme-item); height: var(--theme-item); - border-radius: 999px; + border-radius: var(--radius-full); transform: translateY(-50%) translateX(calc(var(--theme-index, 0) * (var(--theme-item) + var(--theme-gap)))); - background: linear-gradient(160deg, rgba(255, 255, 255, 0.12), transparent), - var(--panel-strong); - border: 1px solid var(--border-strong); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.25); - transition: transform 180ms ease-out, background 180ms ease-out, - box-shadow 180ms ease-out; + background: var(--accent); + transition: transform var(--duration-normal) var(--ease-out); z-index: 0; } @@ -191,92 +243,176 @@ display: grid; place-items: center; border: 0; - border-radius: 999px; + border-radius: var(--radius-full); background: transparent; color: var(--muted); cursor: pointer; position: relative; z-index: 1; - transition: color 150ms ease-out, background 150ms ease-out; + transition: color var(--duration-fast) ease; } .theme-toggle__button:hover { color: var(--text); - background: rgba(255, 255, 255, 0.08); } .theme-toggle__button.active { - color: var(--text); + color: var(--accent-foreground); +} + +.theme-toggle__button.active .theme-icon { + stroke: var(--accent-foreground); } .theme-icon { - width: 16px; - height: 16px; + width: 14px; + height: 14px; stroke: currentColor; fill: none; - stroke-width: 1.75px; + stroke-width: 1.5px; stroke-linecap: round; stroke-linejoin: round; } -.pill.danger { - border-color: rgba(255, 92, 92, 0.5); - color: var(--danger); -} +/* =========================================== + Status Dot - With glow for emphasis + =========================================== */ .statusDot { width: 8px; height: 8px; - border-radius: 999px; + border-radius: var(--radius-full); background: var(--danger); - box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.25); + box-shadow: 0 0 8px rgba(239, 68, 68, 0.5); + animation: pulse-subtle 2s ease-in-out infinite; } .statusDot.ok { background: var(--ok); - box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.25), 0 0 10px rgba(43, 217, 127, 0.4); + box-shadow: 0 0 8px rgba(34, 197, 94, 0.5); + animation: none; } +/* =========================================== + Buttons - Tactile with personality + =========================================== */ + .btn { - border: 1px solid var(--border-strong); - background: rgba(255, 255, 255, 0.04); - padding: 8px 14px; - border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + border: 1px solid var(--border); + background: var(--bg-elevated); + padding: 9px 16px; + border-radius: var(--radius-md); + font-size: 13px; + font-weight: 500; + letter-spacing: -0.01em; cursor: pointer; - transition: transform 150ms ease, border-color 150ms ease, background 150ms ease; + transition: + border-color var(--duration-fast) var(--ease-out), + background var(--duration-fast) var(--ease-out), + box-shadow var(--duration-fast) var(--ease-out), + transform var(--duration-fast) var(--ease-out); } .btn:hover { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-hover); + border-color: var(--border-strong); transform: translateY(-1px); + box-shadow: var(--shadow-sm); +} + +.btn:active { + background: var(--secondary); + transform: translateY(0); + box-shadow: none; +} + +.btn svg { + width: 16px; + height: 16px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; + flex-shrink: 0; } .btn.primary { - border-color: rgba(245, 159, 74, 0.45); - background: rgba(245, 159, 74, 0.2); + border-color: var(--accent); + background: var(--accent); + color: var(--primary-foreground); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +} + +.btn.primary:hover { + background: var(--accent-hover); + border-color: var(--accent-hover); + box-shadow: var(--shadow-md), 0 0 20px var(--accent-glow); +} + +/* Keyboard shortcut badge (shadcn style) */ +.btn-kbd { + display: inline-flex; + align-items: center; + justify-content: center; + margin-left: 6px; + padding: 2px 5px; + font-family: var(--mono); + font-size: 11px; + font-weight: 500; + line-height: 1; + border-radius: 4px; + background: rgba(255, 255, 255, 0.15); + color: inherit; + opacity: 0.8; +} + +.btn.primary .btn-kbd { + background: rgba(255, 255, 255, 0.2); +} + +:root[data-theme="light"] .btn-kbd { + background: rgba(0, 0, 0, 0.08); +} + +:root[data-theme="light"] .btn.primary .btn-kbd { + background: rgba(255, 255, 255, 0.25); } .btn.active { - border-color: rgba(245, 159, 74, 0.55); - background: rgba(245, 159, 74, 0.16); + border-color: var(--accent); + background: var(--accent-subtle); + color: var(--accent); } .btn.danger { - border-color: rgba(255, 107, 107, 0.45); - background: rgba(255, 107, 107, 0.18); + border-color: transparent; + background: var(--danger-subtle); + color: var(--danger); +} + +.btn.danger:hover { + background: rgba(239, 68, 68, 0.15); } .btn--sm { - padding: 5px 10px; + padding: 6px 10px; font-size: 12px; } .btn:disabled { opacity: 0.5; cursor: not-allowed; - transform: none; } +/* =========================================== + Form Fields + =========================================== */ + .field { display: grid; gap: 6px; @@ -288,56 +424,46 @@ .field span { color: var(--muted); - font-size: 11px; - letter-spacing: 0.4px; + font-size: 13px; + font-weight: 500; } .field input, .field textarea, .field select { - border: 1px solid var(--border-strong); - background: rgba(0, 0, 0, 0.22); - border-radius: 12px; - padding: 9px 11px; + border: 1px solid var(--input); + background: var(--card); + border-radius: var(--radius-md); + padding: 8px 12px; outline: none; - transition: border-color 150ms ease, box-shadow 150ms ease, - background 150ms ease; + box-shadow: inset 0 1px 0 var(--card-highlight); + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; } .field input:focus, .field textarea:focus, .field select:focus { - border-color: var(--accent); - box-shadow: 0 0 0 3px var(--focus); - background: rgba(0, 0, 0, 0.28); + border-color: var(--ring); + box-shadow: var(--focus-ring); } .field select { appearance: none; - padding-right: 38px; - background-color: var(--panel-strong); - background-image: - linear-gradient(45deg, transparent 50%, var(--muted) 50%), - linear-gradient(135deg, var(--muted) 50%, transparent 50%), - linear-gradient(to right, transparent, transparent); - background-position: - calc(100% - 18px) 50%, - calc(100% - 12px) 50%, - calc(100% - 38px) 50%; - background-size: 6px 6px, 6px 6px, 1px 60%; + padding-right: 36px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23a1a1aa' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); background-repeat: no-repeat; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); + background-position: right 10px center; + cursor: pointer; } .field textarea { font-family: var(--mono); - min-height: 180px; + min-height: 160px; resize: vertical; white-space: pre; -} - -.field textarea:focus { - background: rgba(0, 0, 0, 0.32); + line-height: 1.5; } .field.checkbox { @@ -352,6 +478,9 @@ .config-form .field.checkbox input[type="checkbox"] { margin: 0; + width: 16px; + height: 16px; + accent-color: var(--accent); } .form-grid { @@ -363,36 +492,27 @@ :root[data-theme="light"] .field input, :root[data-theme="light"] .field textarea, :root[data-theme="light"] .field select { - background: rgba(255, 255, 255, 1); - border-color: rgba(16, 24, 40, 0.25); - box-shadow: 0 1px 2px rgba(16, 24, 40, 0.06); + background: var(--card); + border-color: var(--input); } -:root[data-theme="light"] .field input:focus, -:root[data-theme="light"] .field textarea:focus, -:root[data-theme="light"] .field select:focus { - background: #ffffff; -} - -/* Light theme button overrides */ :root[data-theme="light"] .btn { - background: rgba(255, 255, 255, 0.9); - border-color: rgba(16, 24, 40, 0.2); - box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05); + background: var(--bg); + border-color: var(--input); } :root[data-theme="light"] .btn:hover { - background: rgba(255, 255, 255, 1); - border-color: rgba(16, 24, 40, 0.3); + background: var(--bg-hover); } :root[data-theme="light"] .btn.primary { - background: rgba(245, 159, 74, 0.15); + background: var(--accent); + border-color: var(--accent); } -:root[data-theme="light"] .btn.active { - background: rgba(245, 159, 74, 0.12); -} +/* =========================================== + Utilities + =========================================== */ .muted { color: var(--muted); @@ -402,34 +522,44 @@ font-family: var(--mono); } +/* =========================================== + Callouts - Informative with subtle depth + =========================================== */ + .callout { - padding: 10px 12px; - border-radius: 14px; - background: linear-gradient(160deg, rgba(255, 255, 255, 0.06), transparent), - rgba(255, 255, 255, 0.03); + padding: 14px 16px; + border-radius: var(--radius-md); + background: var(--secondary); border: 1px solid var(--border); + font-size: 13px; + line-height: 1.5; + position: relative; } .callout.danger { - border-color: rgba(255, 92, 92, 0.4); + border-color: rgba(239, 68, 68, 0.25); + background: linear-gradient(135deg, rgba(239, 68, 68, 0.08) 0%, rgba(239, 68, 68, 0.04) 100%); color: var(--danger); } .callout.info { - border-color: rgba(92, 156, 255, 0.4); - color: var(--accent); + border-color: rgba(59, 130, 246, 0.25); + background: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(59, 130, 246, 0.04) 100%); + color: var(--info); } .callout.success { - border-color: rgba(92, 255, 128, 0.4); - color: var(--positive, #5cff80); + border-color: rgba(34, 197, 94, 0.25); + background: linear-gradient(135deg, rgba(34, 197, 94, 0.08) 0%, rgba(34, 197, 94, 0.04) 100%); + color: var(--ok); } +/* Compaction indicator */ .compaction-indicator { font-size: 13px; - padding: 8px 12px; + padding: 10px 12px; margin-bottom: 8px; - animation: compaction-fade-in 0.2s ease-out; + animation: fade-in 0.2s var(--ease-out); } .compaction-indicator--active { @@ -437,18 +567,7 @@ } .compaction-indicator--complete { - animation: compaction-fade-in 0.2s ease-out; -} - -@keyframes compaction-fade-in { - from { - opacity: 0; - transform: translateY(-4px); - } - to { - opacity: 1; - transform: translateY(0); - } + animation: fade-in 0.2s var(--ease-out); } @keyframes compaction-pulse { @@ -460,44 +579,54 @@ } } +/* =========================================== + Code Blocks + =========================================== */ + .code-block { font-family: var(--mono); - font-size: 12px; - background: rgba(0, 0, 0, 0.35); - padding: 10px; - border-radius: 12px; + font-size: 13px; + line-height: 1.5; + background: var(--secondary); + padding: 12px; + border-radius: var(--radius-md); border: 1px solid var(--border); max-height: 360px; overflow: auto; + max-width: 100%; } :root[data-theme="light"] .code-block, :root[data-theme="light"] .list-item, :root[data-theme="light"] .table-row, :root[data-theme="light"] .chip { - background: rgba(255, 255, 255, 0.85); + background: var(--bg); } +/* =========================================== + Lists + =========================================== */ + .list { display: grid; - gap: 12px; + gap: 8px; container-type: inline-size; } .list-item { display: grid; - grid-template-columns: minmax(0, 1fr) minmax(220px, 260px); - gap: 14px; + grid-template-columns: minmax(0, 1fr) minmax(200px, 260px); + gap: 16px; align-items: start; border: 1px solid var(--border); - border-radius: 14px; + border-radius: var(--radius-md); padding: 12px; - background: rgba(0, 0, 0, 0.2); + background: var(--card); + transition: border-color var(--duration-fast) ease; } .list-item-clickable { cursor: pointer; - transition: border-color 0.15s ease, box-shadow 0.15s ease; } .list-item-clickable:hover { @@ -506,17 +635,17 @@ .list-item-selected { border-color: var(--accent); - box-shadow: 0 0 0 1px var(--focus); + box-shadow: var(--focus-ring); } .list-main { display: grid; - gap: 6px; + gap: 4px; min-width: 0; } .list-title { - font-weight: 600; + font-weight: 500; } .list-sub { @@ -527,10 +656,10 @@ .list-meta { text-align: right; color: var(--muted); - font-size: 11px; + font-size: 12px; display: grid; gap: 4px; - min-width: 220px; + min-width: 200px; } .list-meta .btn { @@ -554,19 +683,33 @@ } } +/* =========================================== + Chips - Compact and punchy + =========================================== */ + .chip-row { display: flex; flex-wrap: wrap; - gap: 6px; + gap: 8px; } .chip { - font-size: 11px; + font-size: 12px; + font-weight: 500; border: 1px solid var(--border); - border-radius: 999px; - padding: 4px 8px; + border-radius: var(--radius-full); + padding: 5px 12px; color: var(--muted); - background: rgba(0, 0, 0, 0.2); + background: var(--secondary); + transition: + border-color var(--duration-fast) var(--ease-out), + background var(--duration-fast) var(--ease-out), + transform var(--duration-fast) var(--ease-out); +} + +.chip:hover { + border-color: var(--border-strong); + transform: translateY(-1px); } .chip input { @@ -575,17 +718,23 @@ .chip-ok { color: var(--ok); - border-color: rgba(27, 217, 138, 0.4); + border-color: rgba(34, 197, 94, 0.3); + background: var(--ok-subtle); } .chip-warn { color: var(--warn); - border-color: rgba(242, 201, 76, 0.4); + border-color: rgba(245, 158, 11, 0.3); + background: var(--warn-subtle); } +/* =========================================== + Tables + =========================================== */ + .table { display: grid; - gap: 8px; + gap: 6px; } .table-head, @@ -597,33 +746,43 @@ } .table-head { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.8px; + font-size: 12px; + font-weight: 500; color: var(--muted); + padding: 0 12px; } .table-row { border: 1px solid var(--border); - padding: 10px; - border-radius: 12px; - background: rgba(0, 0, 0, 0.2); + padding: 10px 12px; + border-radius: var(--radius-md); + background: var(--card); + transition: border-color var(--duration-fast) ease; +} + +.table-row:hover { + border-color: var(--border-strong); } .session-link { text-decoration: none; color: var(--accent); + font-weight: 500; } .session-link:hover { text-decoration: underline; } +/* =========================================== + Log Stream + =========================================== */ + .log-stream { border: 1px solid var(--border); - border-radius: 14px; - background: rgba(0, 0, 0, 0.2); - max-height: 520px; + border-radius: var(--radius-md); + background: var(--card); + max-height: 500px; overflow: auto; container-type: inline-size; } @@ -633,9 +792,14 @@ grid-template-columns: 90px 70px minmax(140px, 200px) minmax(0, 1fr); gap: 12px; align-items: start; - padding: 6px 10px; + padding: 8px 12px; border-bottom: 1px solid var(--border); font-size: 12px; + transition: background var(--duration-fast) ease; +} + +.log-row:hover { + background: var(--bg-hover); } .log-row:last-child { @@ -644,14 +808,14 @@ .log-time { color: var(--muted); + font-family: var(--mono); } .log-level { - text-transform: uppercase; - font-size: 10px; - font-weight: 600; + font-size: 11px; + font-weight: 500; border: 1px solid var(--border); - border-radius: 999px; + border-radius: var(--radius-sm); padding: 2px 6px; width: fit-content; } @@ -663,18 +827,18 @@ .log-level.info { color: var(--info); - border-color: rgba(76, 150, 242, 0.4); + border-color: rgba(59, 130, 246, 0.3); } .log-level.warn { color: var(--warn); - border-color: rgba(242, 201, 76, 0.4); + border-color: var(--warn-subtle); } .log-level.error, .log-level.fatal { color: var(--danger); - border-color: rgba(255, 92, 92, 0.4); + border-color: var(--danger-subtle); } .log-chip.trace, @@ -684,27 +848,29 @@ .log-chip.info { color: var(--info); - border-color: rgba(76, 150, 242, 0.4); + border-color: rgba(59, 130, 246, 0.3); } .log-chip.warn { color: var(--warn); - border-color: rgba(242, 201, 76, 0.4); + border-color: var(--warn-subtle); } .log-chip.error, .log-chip.fatal { color: var(--danger); - border-color: rgba(255, 92, 92, 0.4); + border-color: var(--danger-subtle); } .log-subsystem { color: var(--muted); + font-family: var(--mono); } .log-message { white-space: pre-wrap; word-break: break-word; + font-family: var(--mono); } @container (max-width: 620px) { @@ -717,6 +883,10 @@ } } +/* =========================================== + Chat + =========================================== */ + .chat { display: flex; flex-direction: column; @@ -731,7 +901,7 @@ display: flex; justify-content: space-between; align-items: flex-end; - gap: 12px; + gap: 16px; flex-wrap: wrap; } @@ -746,7 +916,7 @@ .chat-header__right { display: flex; align-items: center; - gap: 10px; + gap: 8px; } .chat-session { @@ -754,42 +924,36 @@ } .chat-thread { - margin-top: 12px; + margin-top: 16px; display: flex; flex-direction: column; gap: 12px; flex: 1; - min-height: 0; /* Allow flex shrinking for scroll behavior */ - overflow-y: auto; /* Enable scrolling */ + min-height: 0; + overflow-y: auto; overflow-x: hidden; - padding: 14px 12px; + padding: 16px 12px; min-width: 0; border-radius: 0; border: none; background: transparent; } -:root[data-theme="light"] .chat-thread { - background: transparent; -} - +/* Chat queue */ .chat-queue { margin-top: 12px; - padding: 10px 12px; - border-radius: 16px; + padding: 12px; + border-radius: var(--radius-lg); border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.18); + background: var(--card); display: grid; gap: 8px; } -:root[data-theme="light"] .chat-queue { - background: rgba(16, 24, 40, 0.04); -} - .chat-queue__title { - font-family: var(--font-mono); + font-family: var(--mono); font-size: 12px; + font-weight: 500; color: var(--muted); } @@ -802,21 +966,17 @@ display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; - gap: 10px; - padding: 8px 10px; - border-radius: 12px; - border: 1px dashed var(--border); - background: rgba(0, 0, 0, 0.2); -} - -:root[data-theme="light"] .chat-queue__item { - background: rgba(16, 24, 40, 0.05); + gap: 12px; + padding: 10px 12px; + border-radius: var(--radius-md); + border: 1px dashed var(--border-strong); + background: var(--secondary); } .chat-queue__text { color: var(--chat-text); font-size: 13px; - line-height: 1.4; + line-height: 1.45; white-space: pre-wrap; overflow: hidden; display: -webkit-box; @@ -831,6 +991,7 @@ line-height: 1; } +/* Chat lines */ .chat-line { display: flex; } @@ -847,128 +1008,119 @@ .chat-msg { display: grid; gap: 6px; - max-width: min(720px, 82%); + max-width: min(700px, 82%); } .chat-line.user .chat-msg { justify-items: end; } +/* Chat bubbles */ .chat-bubble { - border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.24); - border-radius: 16px; - padding: 10px 12px; + border: 1px solid transparent; + background: var(--card); + border-radius: var(--radius-lg); + padding: 10px 14px; min-width: 0; - box-shadow: 0 12px 22px rgba(0, 0, 0, 0.24); } :root[data-theme="light"] .chat-bubble { - background: rgba(255, 255, 255, 0.85); - box-shadow: 0 12px 26px rgba(16, 24, 40, 0.08); + border-color: var(--border); + background: var(--bg); } .chat-line.user .chat-bubble { - border-color: rgba(245, 159, 74, 0.45); - background: linear-gradient( - 135deg, - rgba(245, 159, 74, 0.26) 0%, - rgba(245, 159, 74, 0.12) 100% - ); + border-color: transparent; + background: var(--accent-subtle); +} + +:root[data-theme="light"] .chat-line.user .chat-bubble { + border-color: rgba(234, 88, 12, 0.2); + background: rgba(251, 146, 60, 0.12); } .chat-line.assistant .chat-bubble { - border-color: rgba(52, 199, 183, 0.2); - background: linear-gradient( - 135deg, - rgba(52, 199, 183, 0.12) 0%, - rgba(0, 0, 0, 0.24) 100% - ); + border-color: transparent; + background: var(--secondary); } :root[data-theme="light"] .chat-line.assistant .chat-bubble { - background: linear-gradient( - 135deg, - rgba(27, 185, 177, 0.12) 0%, - rgba(255, 255, 255, 0.85) 100% - ); + border-color: var(--border); + background: var(--bg-muted); } @keyframes chatStreamPulse { - 0% { - box-shadow: 0 12px 22px rgba(0, 0, 0, 0.24), 0 0 0 0 rgba(52, 199, 183, 0); + 0%, 100% { + border-color: var(--border); } - 60% { - box-shadow: 0 12px 22px rgba(0, 0, 0, 0.24), 0 0 0 6px rgba(52, 199, 183, 0.08); - } - 100% { - box-shadow: 0 12px 22px rgba(0, 0, 0, 0.24), 0 0 0 0 rgba(52, 199, 183, 0); + 50% { + border-color: var(--accent); } } .chat-bubble.streaming { - border-color: rgba(52, 199, 183, 0.4); - animation: chatStreamPulse 1.6s ease-in-out infinite; + animation: chatStreamPulse 1.5s ease-in-out infinite; } @media (prefers-reduced-motion: reduce) { .chat-bubble.streaming { animation: none; + border-color: var(--accent); } } +/* Reading indicator */ .chat-bubble.chat-reading-indicator { width: fit-content; - padding: 10px 14px; + padding: 10px 16px; } .chat-reading-indicator__dots { display: inline-flex; align-items: center; - gap: 6px; - height: 10px; + gap: 4px; + height: 12px; } .chat-reading-indicator__dots > span { display: inline-block; width: 6px; height: 6px; - border-radius: 999px; - background: var(--chat-text); - opacity: 0.55; + border-radius: var(--radius-full); + background: var(--muted); + opacity: 0.6; transform: translateY(0); - animation: chatReadingDot 1.1s ease-in-out infinite; + animation: chatReadingDot 1.2s ease-in-out infinite; will-change: transform, opacity; } .chat-reading-indicator__dots > span:nth-child(2) { - animation-delay: 0.12s; + animation-delay: 0.15s; } .chat-reading-indicator__dots > span:nth-child(3) { - animation-delay: 0.24s; + animation-delay: 0.3s; } @keyframes chatReadingDot { - 0%, - 80%, - 100% { - opacity: 0.38; - transform: translateY(0) scale(0.92); + 0%, 80%, 100% { + opacity: 0.4; + transform: translateY(0); } 40% { opacity: 1; - transform: translateY(-3px) scale(1.18); + transform: translateY(-3px); } } @media (prefers-reduced-motion: reduce) { .chat-reading-indicator__dots > span { animation: none; - opacity: 0.75; + opacity: 0.6; } } +/* Chat text */ .chat-text { overflow-wrap: anywhere; word-break: break-word; @@ -985,7 +1137,7 @@ } .chat-text :where(ul, ol) { - padding-left: 1.1em; + padding-left: 1.2em; } .chat-text :where(li + li) { @@ -994,58 +1146,51 @@ .chat-text :where(a) { color: var(--accent); - text-decoration-thickness: 2px; - text-underline-offset: 2px; } .chat-text :where(a:hover) { - text-decoration-thickness: 3px; + text-decoration: underline; } .chat-text :where(blockquote) { - border-left: 2px solid rgba(255, 255, 255, 0.14); + border-left: 2px solid var(--border-strong); padding-left: 12px; color: var(--muted); } -:root[data-theme="light"] .chat-text :where(blockquote) { - border-left-color: rgba(16, 24, 40, 0.16); -} - .chat-text :where(hr) { border: 0; border-top: 1px solid var(--border); - opacity: 0.6; - margin: 0.9em 0; + margin: 1em 0; } .chat-text :where(code) { - font-family: var(--font-mono); - font-size: 0.92em; + font-family: var(--mono); + font-size: 0.9em; } .chat-text :where(:not(pre) > code) { padding: 0.15em 0.35em; - border-radius: 8px; + border-radius: var(--radius-sm); border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.2); + background: var(--secondary); } :root[data-theme="light"] .chat-text :where(:not(pre) > code) { - background: rgba(16, 24, 40, 0.05); + background: var(--bg-muted); } .chat-text :where(pre) { margin-top: 0.75em; padding: 10px 12px; - border-radius: 14px; + border-radius: var(--radius-md); border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.22); + background: var(--secondary); overflow: auto; } :root[data-theme="light"] .chat-text :where(pre) { - background: rgba(16, 24, 40, 0.04); + background: var(--bg-muted); } .chat-text :where(pre code) { @@ -1057,43 +1202,46 @@ margin-top: 0.75em; border-collapse: collapse; width: 100%; - font-size: 12px; + font-size: 13px; } .chat-text :where(th, td) { border: 1px solid var(--border); - padding: 6px 8px; + padding: 6px 10px; vertical-align: top; } .chat-text :where(th) { - font-family: var(--font-mono); - font-weight: 600; + font-family: var(--mono); + font-weight: 500; color: var(--muted); + background: var(--secondary); } +/* Tool cards */ .chat-tool-card { margin-top: 8px; - padding: 8px 10px; - border-radius: 12px; + padding: 10px 12px; + border-radius: var(--radius-md); border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.22); + background: var(--secondary); display: grid; gap: 4px; } :root[data-theme="light"] .chat-tool-card { - background: rgba(255, 255, 255, 0.7); + background: var(--bg-muted); } .chat-tool-card__title { - font-family: var(--font-mono); + font-family: var(--mono); font-size: 12px; - color: var(--chat-text); + font-weight: 500; + color: var(--text); } .chat-tool-card__detail { - font-family: var(--font-mono); + font-family: var(--mono); font-size: 11px; color: var(--muted); } @@ -1103,7 +1251,7 @@ } .chat-tool-card__summary { - font-family: var(--font-mono); + font-family: var(--mono); font-size: 11px; color: var(--muted); cursor: pointer; @@ -1119,28 +1267,28 @@ .chat-tool-card__summary-meta { color: var(--muted); - opacity: 0.8; + opacity: 0.7; } .chat-tool-card__details[open] .chat-tool-card__summary { - color: var(--chat-text); + color: var(--text); } .chat-tool-card__output { - margin-top: 6px; - font-family: var(--font-mono); + margin-top: 8px; + font-family: var(--mono); font-size: 11px; - line-height: 1.45; + line-height: 1.5; white-space: pre-wrap; color: var(--chat-text); - padding: 8px; - border-radius: 10px; + padding: 8px 10px; + border-radius: var(--radius-md); border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.2); + background: var(--card); } :root[data-theme="light"] .chat-tool-card__output { - background: rgba(16, 24, 40, 0.05); + background: var(--bg); } .chat-stamp { @@ -1152,11 +1300,11 @@ text-align: right; } +/* Chat compose */ .chat-compose { margin-top: 12px; - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - align-items: end; + display: flex; + flex-direction: column; gap: 10px; } @@ -1166,14 +1314,14 @@ z-index: 5; margin-top: 0; padding-top: 12px; - background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, var(--panel) 35%); + background: linear-gradient(180deg, transparent 0%, var(--bg) 40%); } .shell--chat-focus .chat-compose { bottom: calc(var(--shell-pad) + 8px); - padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px)); - border-bottom-left-radius: 18px; - border-bottom-right-radius: 18px; + padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)); + border-bottom-left-radius: var(--radius-lg); + border-bottom-right-radius: var(--radius-lg); } .chat-compose__field { @@ -1182,16 +1330,27 @@ .chat-compose__field textarea { min-height: 72px; - padding: 10px 12px; - border-radius: 16px; + padding: 10px 14px; + border-radius: var(--radius-lg); resize: vertical; white-space: pre-wrap; font-family: var(--font-body); - line-height: 1.45; + line-height: 1.5; + border: 1px solid var(--input); + background: var(--card); + box-shadow: inset 0 1px 0 var(--card-highlight); + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; +} + +.chat-compose__field textarea:focus { + border-color: var(--ring); + box-shadow: var(--focus-ring); } .chat-compose__field textarea:disabled { - opacity: 0.7; + opacity: 0.5; cursor: not-allowed; } @@ -1202,7 +1361,7 @@ @media (max-width: 900px) { .chat-session { - min-width: 200px; + min-width: 180px; } .chat-compose { @@ -1210,27 +1369,35 @@ } } +/* =========================================== + QR Code + =========================================== */ + .qr-wrap { - margin-top: 12px; - border-radius: 14px; - background: rgba(0, 0, 0, 0.2); - border: 1px dashed rgba(255, 255, 255, 0.18); - padding: 12px; + margin-top: 16px; + border-radius: var(--radius-md); + background: var(--card); + border: 1px dashed var(--border-strong); + padding: 16px; display: inline-flex; } .qr-wrap img { - width: 180px; - height: 180px; - border-radius: 10px; + width: 160px; + height: 160px; + border-radius: var(--radius-sm); image-rendering: pixelated; } +/* =========================================== + Exec Approval Modal + =========================================== */ + .exec-approval-overlay { position: fixed; inset: 0; - background: rgba(8, 12, 18, 0.7); - backdrop-filter: blur(6px); + background: rgba(0, 0, 0, 0.8); + backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; @@ -1239,59 +1406,58 @@ } .exec-approval-card { - width: min(560px, 100%); - background: var(--panel-strong); - border: 1px solid var(--border-strong); - border-radius: 18px; + width: min(540px, 100%); + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius-lg); padding: 20px; - box-shadow: 0 28px 60px rgba(0, 0, 0, 0.35); - animation: rise 0.25s ease; + animation: scale-in 0.2s var(--ease-out); } .exec-approval-header { display: flex; align-items: center; justify-content: space-between; - gap: 12px; + gap: 16px; } .exec-approval-title { - font-family: var(--font-display); font-size: 14px; - letter-spacing: 0.8px; - text-transform: uppercase; + font-weight: 600; } .exec-approval-sub { color: var(--muted); - font-size: 12px; + font-size: 13px; + margin-top: 4px; } .exec-approval-queue { font-size: 11px; - text-transform: uppercase; - letter-spacing: 1px; + font-weight: 500; color: var(--muted); border: 1px solid var(--border); - border-radius: 999px; + border-radius: var(--radius-full); padding: 4px 10px; } .exec-approval-command { margin-top: 12px; padding: 10px 12px; - background: rgba(0, 0, 0, 0.25); + background: var(--secondary); border: 1px solid var(--border); - border-radius: 12px; + border-radius: var(--radius-md); word-break: break-word; white-space: pre-wrap; + font-family: var(--mono); + font-size: 13px; } .exec-approval-meta { margin-top: 12px; display: grid; gap: 6px; - font-size: 12px; + font-size: 13px; color: var(--muted); } @@ -1308,7 +1474,7 @@ .exec-approval-error { margin-top: 10px; - font-size: 12px; + font-size: 13px; color: var(--danger); } @@ -1316,5 +1482,5 @@ margin-top: 16px; display: flex; flex-wrap: wrap; - gap: 10px; + gap: 8px; } diff --git a/ui/src/styles/config.css b/ui/src/styles/config.css index aa41505ae08..7d96ac13f96 100644 --- a/ui/src/styles/config.css +++ b/ui/src/styles/config.css @@ -1,15 +1,15 @@ /* =========================================== - Config Page - Modern Layout + Config Page - Carbon Design System =========================================== */ /* Layout Container */ .config-layout { display: grid; - grid-template-columns: 240px minmax(0, 1fr); + grid-template-columns: 260px minmax(0, 1fr); gap: 0; - min-height: calc(100vh - 140px); + height: calc(100vh - 160px); margin: -16px; - border-radius: 16px; + border-radius: var(--radius-xl); overflow: hidden; border: 1px solid var(--border); background: var(--panel); @@ -18,47 +18,50 @@ /* =========================================== Sidebar =========================================== */ + .config-sidebar { display: flex; flex-direction: column; - background: rgba(0, 0, 0, 0.2); + background: var(--bg-accent); border-right: 1px solid var(--border); + min-height: 0; + overflow: hidden; } :root[data-theme="light"] .config-sidebar { - background: rgba(0, 0, 0, 0.03); + background: var(--bg-hover); } .config-sidebar__header { display: flex; align-items: center; justify-content: space-between; - padding: 16px; + padding: 18px 18px; border-bottom: 1px solid var(--border); } .config-sidebar__title { font-weight: 600; font-size: 14px; - letter-spacing: 0.3px; + letter-spacing: -0.01em; } .config-sidebar__footer { margin-top: auto; - padding: 12px; + padding: 14px; border-top: 1px solid var(--border); } /* Search */ .config-search { position: relative; - padding: 12px; + padding: 14px; border-bottom: 1px solid var(--border); } .config-search__icon { position: absolute; - left: 24px; + left: 28px; top: 50%; transform: translateY(-50%); width: 16px; @@ -69,13 +72,16 @@ .config-search__input { width: 100%; - padding: 10px 32px 10px 40px; + padding: 11px 36px 11px 42px; border: 1px solid var(--border); - border-radius: 8px; - background: rgba(0, 0, 0, 0.15); + border-radius: var(--radius-md); + background: var(--bg-elevated); font-size: 13px; outline: none; - transition: border-color 150ms ease, box-shadow 150ms ease, background 150ms ease; + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease, + background var(--duration-fast) ease; } .config-search__input::placeholder { @@ -84,40 +90,42 @@ .config-search__input:focus { border-color: var(--accent); - box-shadow: 0 0 0 3px var(--focus); - background: rgba(0, 0, 0, 0.2); + box-shadow: var(--focus-ring); + background: var(--bg-hover); } :root[data-theme="light"] .config-search__input { - background: rgba(255, 255, 255, 0.8); + background: white; } :root[data-theme="light"] .config-search__input:focus { - background: #fff; + background: white; } .config-search__clear { position: absolute; - right: 20px; + right: 22px; top: 50%; transform: translateY(-50%); - width: 20px; - height: 20px; + width: 22px; + height: 22px; border: none; - border-radius: 50%; - background: rgba(255, 255, 255, 0.1); + border-radius: var(--radius-full); + background: var(--bg-hover); color: var(--muted); - font-size: 16px; + font-size: 14px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; - transition: background 150ms ease, color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .config-search__clear:hover { - background: rgba(255, 255, 255, 0.2); + background: var(--border-strong); color: var(--text); } @@ -125,7 +133,7 @@ .config-nav { flex: 1; overflow-y: auto; - padding: 8px; + padding: 10px; } .config-nav__item { @@ -133,29 +141,31 @@ align-items: center; gap: 12px; width: 100%; - padding: 10px 12px; + padding: 11px 14px; border: none; - border-radius: 8px; + border-radius: var(--radius-md); background: transparent; color: var(--muted); font-size: 13px; font-weight: 500; text-align: left; cursor: pointer; - transition: background 150ms ease, color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .config-nav__item:hover { - background: rgba(255, 255, 255, 0.05); + background: var(--bg-hover); color: var(--text); } :root[data-theme="light"] .config-nav__item:hover { - background: rgba(0, 0, 0, 0.05); + background: rgba(0, 0, 0, 0.04); } .config-nav__item.active { - background: rgba(245, 159, 74, 0.12); + background: var(--accent-subtle); color: var(--accent); } @@ -165,7 +175,13 @@ display: flex; align-items: center; justify-content: center; - font-size: 14px; + font-size: 15px; + opacity: 0.7; +} + +.config-nav__item:hover .config-nav__icon, +.config-nav__item.active .config-nav__icon { + opacity: 1; } .config-nav__icon svg { @@ -185,27 +201,30 @@ /* Mode Toggle */ .config-mode-toggle { display: flex; - padding: 3px; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; + padding: 4px; + background: var(--bg-elevated); + border-radius: var(--radius-md); border: 1px solid var(--border); } :root[data-theme="light"] .config-mode-toggle { - background: rgba(0, 0, 0, 0.06); + background: white; } .config-mode-toggle__btn { flex: 1; - padding: 8px 12px; + padding: 9px 14px; border: none; - border-radius: 6px; + border-radius: var(--radius-sm); background: transparent; color: var(--muted); font-size: 12px; font-weight: 600; cursor: pointer; - transition: background 150ms ease, color 150ms ease, box-shadow 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; } .config-mode-toggle__btn:hover { @@ -213,24 +232,22 @@ } .config-mode-toggle__btn.active { - background: rgba(255, 255, 255, 0.1); - color: var(--text); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); -} - -:root[data-theme="light"] .config-mode-toggle__btn.active { - background: #fff; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + background: var(--accent); + color: white; + box-shadow: var(--shadow-sm); } /* =========================================== Main Content =========================================== */ + .config-main { display: flex; flex-direction: column; + min-height: 0; min-width: 0; background: var(--panel); + overflow: hidden; } /* Actions Bar */ @@ -238,28 +255,28 @@ display: flex; align-items: center; justify-content: space-between; - gap: 12px; - padding: 12px 20px; - background: rgba(0, 0, 0, 0.08); + gap: 14px; + padding: 14px 22px; + background: var(--bg-accent); border-bottom: 1px solid var(--border); } :root[data-theme="light"] .config-actions { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .config-actions__left, .config-actions__right { display: flex; align-items: center; - gap: 8px; + gap: 10px; } .config-changes-badge { - padding: 5px 12px; - border-radius: 999px; - background: rgba(245, 159, 74, 0.15); - border: 1px solid rgba(245, 159, 74, 0.3); + padding: 6px 14px; + border-radius: var(--radius-full); + background: var(--accent-subtle); + border: 1px solid rgba(255, 77, 77, 0.3); color: var(--accent); font-size: 12px; font-weight: 600; @@ -272,10 +289,10 @@ /* Diff Panel */ .config-diff { - margin: 16px 20px 0; - border: 1px solid rgba(245, 159, 74, 0.3); - border-radius: 10px; - background: rgba(245, 159, 74, 0.05); + margin: 18px 22px 0; + border: 1px solid rgba(255, 77, 77, 0.25); + border-radius: var(--radius-lg); + background: var(--accent-subtle); overflow: hidden; } @@ -283,7 +300,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: 12px 16px; + padding: 14px 18px; cursor: pointer; font-size: 13px; font-weight: 600; @@ -298,7 +315,7 @@ .config-diff__chevron { width: 16px; height: 16px; - transition: transform 200ms ease; + transition: transform var(--duration-normal) var(--ease-out); } .config-diff__chevron svg { @@ -311,24 +328,24 @@ } .config-diff__content { - padding: 0 16px 16px; + padding: 0 18px 18px; display: grid; - gap: 8px; + gap: 10px; } .config-diff__item { display: flex; align-items: baseline; - gap: 12px; - padding: 8px 12px; - border-radius: 6px; - background: rgba(0, 0, 0, 0.1); + gap: 14px; + padding: 10px 14px; + border-radius: var(--radius-md); + background: var(--bg-elevated); font-size: 12px; font-family: var(--mono); } :root[data-theme="light"] .config-diff__item { - background: rgba(255, 255, 255, 0.6); + background: white; } .config-diff__path { @@ -340,14 +357,14 @@ .config-diff__values { display: flex; align-items: baseline; - gap: 8px; + gap: 10px; min-width: 0; flex-wrap: wrap; } .config-diff__from { color: var(--danger); - opacity: 0.8; + opacity: 0.85; } .config-diff__arrow { @@ -362,19 +379,19 @@ .config-section-hero { display: flex; align-items: center; - gap: 14px; - padding: 14px 20px; + gap: 16px; + padding: 16px 22px; border-bottom: 1px solid var(--border); - background: rgba(0, 0, 0, 0.04); + background: var(--bg-accent); } :root[data-theme="light"] .config-section-hero { - background: rgba(0, 0, 0, 0.015); + background: var(--bg-hover); } .config-section-hero__icon { - width: 28px; - height: 28px; + width: 30px; + height: 30px; color: var(--accent); display: flex; align-items: center; @@ -390,17 +407,21 @@ .config-section-hero__text { display: grid; - gap: 2px; + gap: 3px; min-width: 0; } .config-section-hero__title { - font-size: 15px; + font-size: 16px; font-weight: 600; + letter-spacing: -0.01em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .config-section-hero__desc { - font-size: 12px; + font-size: 13px; color: var(--muted); } @@ -408,60 +429,59 @@ .config-subnav { display: flex; gap: 8px; - padding: 10px 20px 12px; + padding: 12px 22px 14px; border-bottom: 1px solid var(--border); - background: rgba(0, 0, 0, 0.03); + background: var(--bg-accent); overflow-x: auto; } :root[data-theme="light"] .config-subnav { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .config-subnav__item { border: 1px solid transparent; - border-radius: 999px; - padding: 6px 12px; + border-radius: var(--radius-full); + padding: 7px 14px; font-size: 12px; font-weight: 600; color: var(--muted); - background: rgba(0, 0, 0, 0.12); + background: var(--bg-elevated); cursor: pointer; - transition: background 150ms ease, color 150ms ease, border-color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease, + border-color var(--duration-fast) ease; white-space: nowrap; } :root[data-theme="light"] .config-subnav__item { - background: rgba(0, 0, 0, 0.06); + background: white; } .config-subnav__item:hover { color: var(--text); - background: rgba(255, 255, 255, 0.08); -} - -:root[data-theme="light"] .config-subnav__item:hover { - background: rgba(0, 0, 0, 0.08); + border-color: var(--border); } .config-subnav__item.active { color: var(--accent); - border-color: rgba(245, 159, 74, 0.4); - background: rgba(245, 159, 74, 0.12); + border-color: rgba(255, 77, 77, 0.4); + background: var(--accent-subtle); } /* Content Area */ .config-content { flex: 1; overflow-y: auto; - padding: 20px; + padding: 22px; } .config-raw-field textarea { min-height: 500px; font-family: var(--mono); font-size: 13px; - line-height: 1.5; + line-height: 1.55; } /* Loading State */ @@ -470,22 +490,24 @@ flex-direction: column; align-items: center; justify-content: center; - gap: 16px; - padding: 80px 20px; + gap: 18px; + padding: 80px 24px; color: var(--muted); } .config-loading__spinner { - width: 36px; - height: 36px; + width: 40px; + height: 40px; border: 3px solid var(--border); border-top-color: var(--accent); - border-radius: 50%; - animation: spin 0.8s linear infinite; + border-radius: var(--radius-full); + animation: spin 0.75s linear infinite; } @keyframes spin { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } /* Empty State */ @@ -494,14 +516,14 @@ flex-direction: column; align-items: center; justify-content: center; - gap: 16px; - padding: 80px 20px; + gap: 18px; + padding: 80px 24px; text-align: center; } .config-empty__icon { font-size: 56px; - opacity: 0.4; + opacity: 0.35; } .config-empty__text { @@ -512,38 +534,44 @@ /* =========================================== Section Cards =========================================== */ + .config-form--modern { display: grid; - gap: 24px; + gap: 26px; } .config-section-card { border: 1px solid var(--border); - border-radius: 12px; - background: rgba(255, 255, 255, 0.02); + border-radius: var(--radius-lg); + background: var(--bg-elevated); overflow: hidden; + transition: border-color var(--duration-fast) ease; +} + +.config-section-card:hover { + border-color: var(--border-strong); } :root[data-theme="light"] .config-section-card { - background: rgba(255, 255, 255, 0.5); + background: white; } .config-section-card__header { display: flex; align-items: flex-start; - gap: 14px; - padding: 18px 20px; - background: rgba(0, 0, 0, 0.06); + gap: 16px; + padding: 20px 22px; + background: var(--bg-accent); border-bottom: 1px solid var(--border); } :root[data-theme="light"] .config-section-card__header { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .config-section-card__icon { - width: 32px; - height: 32px; + width: 34px; + height: 34px; color: var(--accent); flex-shrink: 0; } @@ -562,37 +590,42 @@ margin: 0; font-size: 17px; font-weight: 600; + letter-spacing: -0.01em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .config-section-card__desc { - margin: 4px 0 0; + margin: 5px 0 0; font-size: 13px; color: var(--muted); - line-height: 1.4; + line-height: 1.45; } .config-section-card__content { - padding: 20px; + padding: 22px; } /* =========================================== Form Fields =========================================== */ + .cfg-fields { display: grid; - gap: 20px; + gap: 22px; } .cfg-field { display: grid; - gap: 6px; + gap: 8px; } .cfg-field--error { - padding: 12px; - border-radius: 8px; - background: rgba(255, 92, 92, 0.1); - border: 1px solid rgba(255, 92, 92, 0.3); + padding: 14px; + border-radius: var(--radius-md); + background: var(--danger-subtle); + border: 1px solid rgba(239, 68, 68, 0.3); } .cfg-field__label { @@ -604,7 +637,7 @@ .cfg-field__help { font-size: 12px; color: var(--muted); - line-height: 1.4; + line-height: 1.45; } .cfg-field__error { @@ -615,18 +648,21 @@ /* Text Input */ .cfg-input-wrap { display: flex; - gap: 8px; + gap: 10px; } .cfg-input { flex: 1; - padding: 10px 12px; - border: 1px solid var(--border); - border-radius: 8px; - background: rgba(0, 0, 0, 0.12); + padding: 11px 14px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-md); + background: var(--bg-accent); font-size: 14px; outline: none; - transition: border-color 150ms ease, box-shadow 150ms ease, background 150ms ease; + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease, + background var(--duration-fast) ease; } .cfg-input::placeholder { @@ -636,36 +672,38 @@ .cfg-input:focus { border-color: var(--accent); - box-shadow: 0 0 0 3px var(--focus); - background: rgba(0, 0, 0, 0.18); + box-shadow: var(--focus-ring); + background: var(--bg-hover); } :root[data-theme="light"] .cfg-input { - background: #fff; + background: white; } :root[data-theme="light"] .cfg-input:focus { - background: #fff; + background: white; } .cfg-input--sm { - padding: 8px 10px; + padding: 9px 12px; font-size: 13px; } .cfg-input__reset { - padding: 8px 12px; + padding: 10px 14px; border: 1px solid var(--border); - border-radius: 8px; - background: rgba(255, 255, 255, 0.05); + border-radius: var(--radius-md); + background: var(--bg-elevated); color: var(--muted); font-size: 14px; cursor: pointer; - transition: background 150ms ease, color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .cfg-input__reset:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-hover); color: var(--text); } @@ -677,58 +715,60 @@ /* Textarea */ .cfg-textarea { width: 100%; - padding: 10px 12px; - border: 1px solid var(--border); - border-radius: 8px; - background: rgba(0, 0, 0, 0.12); + padding: 12px 14px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-md); + background: var(--bg-accent); font-family: var(--mono); font-size: 13px; - line-height: 1.5; + line-height: 1.55; resize: vertical; outline: none; - transition: border-color 150ms ease, box-shadow 150ms ease; + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; } .cfg-textarea:focus { border-color: var(--accent); - box-shadow: 0 0 0 3px var(--focus); + box-shadow: var(--focus-ring); } :root[data-theme="light"] .cfg-textarea { - background: #fff; + background: white; } .cfg-textarea--sm { - padding: 8px 10px; + padding: 10px 12px; font-size: 12px; } /* Number Input */ .cfg-number { display: inline-flex; - border: 1px solid var(--border); - border-radius: 8px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-md); overflow: hidden; - background: rgba(0, 0, 0, 0.12); + background: var(--bg-accent); } :root[data-theme="light"] .cfg-number { - background: #fff; + background: white; } .cfg-number__btn { - width: 40px; + width: 44px; border: none; - background: rgba(255, 255, 255, 0.05); + background: var(--bg-elevated); color: var(--text); font-size: 18px; font-weight: 300; cursor: pointer; - transition: background 150ms ease; + transition: background var(--duration-fast) ease; } .cfg-number__btn:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-hover); } .cfg-number__btn:disabled { @@ -737,16 +777,16 @@ } :root[data-theme="light"] .cfg-number__btn { - background: rgba(0, 0, 0, 0.03); + background: var(--bg-hover); } :root[data-theme="light"] .cfg-number__btn:hover:not(:disabled) { - background: rgba(0, 0, 0, 0.06); + background: var(--border); } .cfg-number__input { - width: 80px; - padding: 10px; + width: 85px; + padding: 11px; border: none; border-left: 1px solid var(--border); border-right: 1px solid var(--border); @@ -765,52 +805,57 @@ /* Select */ .cfg-select { - padding: 10px 36px 10px 12px; - border: 1px solid var(--border); - border-radius: 8px; - background-color: rgba(0, 0, 0, 0.12); + padding: 11px 40px 11px 14px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-md); + background-color: var(--bg-accent); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); background-repeat: no-repeat; - background-position: right 10px center; + background-position: right 12px center; font-size: 14px; cursor: pointer; outline: none; appearance: none; - transition: border-color 150ms ease, box-shadow 150ms ease; + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; } .cfg-select:focus { border-color: var(--accent); - box-shadow: 0 0 0 3px var(--focus); + box-shadow: var(--focus-ring); } :root[data-theme="light"] .cfg-select { - background-color: #fff; + background-color: white; } /* Segmented Control */ .cfg-segmented { display: inline-flex; - padding: 3px; + padding: 4px; border: 1px solid var(--border); - border-radius: 8px; - background: rgba(0, 0, 0, 0.12); + border-radius: var(--radius-md); + background: var(--bg-accent); } :root[data-theme="light"] .cfg-segmented { - background: rgba(0, 0, 0, 0.04); + background: var(--bg-hover); } .cfg-segmented__btn { - padding: 8px 16px; + padding: 9px 18px; border: none; - border-radius: 6px; + border-radius: var(--radius-sm); background: transparent; color: var(--muted); font-size: 13px; font-weight: 500; cursor: pointer; - transition: background 150ms ease, color 150ms ease, box-shadow 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; } .cfg-segmented__btn:hover:not(:disabled):not(.active) { @@ -818,14 +863,9 @@ } .cfg-segmented__btn.active { - background: rgba(255, 255, 255, 0.12); - color: var(--text); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); -} - -:root[data-theme="light"] .cfg-segmented__btn.active { - background: #fff; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + background: var(--accent); + color: white; + box-shadow: var(--shadow-sm); } .cfg-segmented__btn:disabled { @@ -838,31 +878,33 @@ display: flex; align-items: center; justify-content: space-between; - gap: 16px; - padding: 14px 16px; + gap: 18px; + padding: 16px 18px; border: 1px solid var(--border); - border-radius: 10px; - background: rgba(0, 0, 0, 0.06); + border-radius: var(--radius-lg); + background: var(--bg-accent); cursor: pointer; - transition: background 150ms ease, border-color 150ms ease; + transition: + background var(--duration-fast) ease, + border-color var(--duration-fast) ease; } .cfg-toggle-row:hover:not(.disabled) { - background: rgba(0, 0, 0, 0.1); + background: var(--bg-hover); border-color: var(--border-strong); } .cfg-toggle-row.disabled { - opacity: 0.6; + opacity: 0.55; cursor: not-allowed; } :root[data-theme="light"] .cfg-toggle-row { - background: rgba(255, 255, 255, 0.5); + background: white; } :root[data-theme="light"] .cfg-toggle-row:hover:not(.disabled) { - background: rgba(255, 255, 255, 0.8); + background: var(--bg-hover); } .cfg-toggle-row__content { @@ -879,10 +921,10 @@ .cfg-toggle-row__help { display: block; - margin-top: 2px; + margin-top: 3px; font-size: 12px; color: var(--muted); - line-height: 1.4; + line-height: 1.45; } /* Toggle Switch */ @@ -900,17 +942,19 @@ .cfg-toggle__track { display: block; - width: 48px; + width: 50px; height: 28px; - background: rgba(255, 255, 255, 0.12); - border: 1px solid var(--border); - border-radius: 999px; + background: var(--bg-elevated); + border: 1px solid var(--border-strong); + border-radius: var(--radius-full); position: relative; - transition: background 200ms ease, border-color 200ms ease; + transition: + background var(--duration-normal) ease, + border-color var(--duration-normal) ease; } :root[data-theme="light"] .cfg-toggle__track { - background: rgba(0, 0, 0, 0.1); + background: var(--border); } .cfg-toggle__track::after { @@ -921,53 +965,51 @@ width: 20px; height: 20px; background: var(--text); - border-radius: 50%; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); - transition: transform 200ms ease, background 200ms ease; + border-radius: var(--radius-full); + box-shadow: var(--shadow-sm); + transition: + transform var(--duration-normal) var(--ease-out), + background var(--duration-normal) ease; } .cfg-toggle input:checked + .cfg-toggle__track { - background: rgba(43, 217, 127, 0.25); - border-color: rgba(43, 217, 127, 0.5); + background: var(--ok-subtle); + border-color: rgba(34, 197, 94, 0.4); } .cfg-toggle input:checked + .cfg-toggle__track::after { - transform: translateX(20px); + transform: translateX(22px); background: var(--ok); } .cfg-toggle input:focus + .cfg-toggle__track { - box-shadow: 0 0 0 3px var(--focus); + box-shadow: var(--focus-ring); } /* Object (collapsible) */ .cfg-object { border: 1px solid var(--border); - border-radius: 10px; - background: rgba(0, 0, 0, 0.04); + border-radius: var(--radius-lg); + background: var(--bg-accent); overflow: hidden; } :root[data-theme="light"] .cfg-object { - background: rgba(255, 255, 255, 0.4); + background: white; } .cfg-object__header { display: flex; align-items: center; justify-content: space-between; - padding: 12px 16px; + padding: 14px 18px; cursor: pointer; list-style: none; - transition: background 150ms ease; + transition: background var(--duration-fast) ease; } .cfg-object__header:hover { - background: rgba(255, 255, 255, 0.03); -} - -:root[data-theme="light"] .cfg-object__header:hover { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .cfg-object__header::-webkit-details-marker { @@ -984,7 +1026,7 @@ width: 18px; height: 18px; color: var(--muted); - transition: transform 200ms ease; + transition: transform var(--duration-normal) var(--ease-out); } .cfg-object__chevron svg { @@ -997,36 +1039,36 @@ } .cfg-object__help { - padding: 0 16px 12px; + padding: 0 18px 14px; font-size: 12px; color: var(--muted); border-bottom: 1px solid var(--border); } .cfg-object__content { - padding: 16px; + padding: 18px; display: grid; - gap: 16px; + gap: 18px; } /* Array */ .cfg-array { border: 1px solid var(--border); - border-radius: 10px; + border-radius: var(--radius-lg); overflow: hidden; } .cfg-array__header { display: flex; align-items: center; - gap: 12px; - padding: 12px 16px; - background: rgba(0, 0, 0, 0.06); + gap: 14px; + padding: 14px 18px; + background: var(--bg-accent); border-bottom: 1px solid var(--border); } :root[data-theme="light"] .cfg-array__header { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .cfg-array__label { @@ -1039,32 +1081,32 @@ .cfg-array__count { font-size: 12px; color: var(--muted); - padding: 3px 8px; - background: rgba(255, 255, 255, 0.06); - border-radius: 999px; + padding: 4px 10px; + background: var(--bg-elevated); + border-radius: var(--radius-full); } :root[data-theme="light"] .cfg-array__count { - background: rgba(0, 0, 0, 0.06); + background: white; } .cfg-array__add { display: inline-flex; align-items: center; gap: 6px; - padding: 6px 12px; + padding: 7px 14px; border: 1px solid var(--border); - border-radius: 6px; - background: rgba(255, 255, 255, 0.05); + border-radius: var(--radius-md); + background: var(--bg-elevated); color: var(--text); font-size: 12px; font-weight: 500; cursor: pointer; - transition: background 150ms ease; + transition: background var(--duration-fast) ease; } .cfg-array__add:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-hover); } .cfg-array__add:disabled { @@ -1083,14 +1125,14 @@ } .cfg-array__help { - padding: 10px 16px; + padding: 12px 18px; font-size: 12px; color: var(--muted); border-bottom: 1px solid var(--border); } .cfg-array__empty { - padding: 32px 16px; + padding: 36px 18px; text-align: center; color: var(--muted); font-size: 13px; @@ -1110,13 +1152,13 @@ display: flex; align-items: center; justify-content: space-between; - padding: 10px 16px; - background: rgba(0, 0, 0, 0.04); + padding: 12px 18px; + background: var(--bg-accent); border-bottom: 1px solid var(--border); } :root[data-theme="light"] .cfg-array__item-header { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .cfg-array__item-index { @@ -1124,21 +1166,23 @@ font-weight: 600; color: var(--muted); text-transform: uppercase; - letter-spacing: 0.5px; + letter-spacing: 0.05em; } .cfg-array__item-remove { - width: 28px; - height: 28px; + width: 30px; + height: 30px; display: flex; align-items: center; justify-content: center; border: none; - border-radius: 6px; + border-radius: var(--radius-md); background: transparent; color: var(--muted); cursor: pointer; - transition: background 150ms ease, color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .cfg-array__item-remove svg { @@ -1147,7 +1191,7 @@ } .cfg-array__item-remove:hover:not(:disabled) { - background: rgba(255, 92, 92, 0.15); + background: var(--danger-subtle); color: var(--danger); } @@ -1157,13 +1201,13 @@ } .cfg-array__item-content { - padding: 16px; + padding: 18px; } /* Map (custom entries) */ .cfg-map { border: 1px solid var(--border); - border-radius: 10px; + border-radius: var(--radius-lg); overflow: hidden; } @@ -1171,14 +1215,14 @@ display: flex; align-items: center; justify-content: space-between; - gap: 12px; - padding: 12px 16px; - background: rgba(0, 0, 0, 0.06); + gap: 14px; + padding: 14px 18px; + background: var(--bg-accent); border-bottom: 1px solid var(--border); } :root[data-theme="light"] .cfg-map__header { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .cfg-map__label { @@ -1191,19 +1235,19 @@ display: inline-flex; align-items: center; gap: 6px; - padding: 6px 12px; + padding: 7px 14px; border: 1px solid var(--border); - border-radius: 6px; - background: rgba(255, 255, 255, 0.05); + border-radius: var(--radius-md); + background: var(--bg-elevated); color: var(--text); font-size: 12px; font-weight: 500; cursor: pointer; - transition: background 150ms ease; + transition: background var(--duration-fast) ease; } .cfg-map__add:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-hover); } .cfg-map__add-icon { @@ -1217,7 +1261,7 @@ } .cfg-map__empty { - padding: 24px 16px; + padding: 28px 18px; text-align: center; color: var(--muted); font-size: 13px; @@ -1225,14 +1269,14 @@ .cfg-map__items { display: grid; - gap: 8px; - padding: 12px; + gap: 10px; + padding: 14px; } .cfg-map__item { display: grid; - grid-template-columns: 140px 1fr auto; - gap: 8px; + grid-template-columns: 150px 1fr auto; + gap: 10px; align-items: start; } @@ -1245,17 +1289,19 @@ } .cfg-map__item-remove { - width: 32px; - height: 32px; + width: 34px; + height: 34px; display: flex; align-items: center; justify-content: center; border: none; - border-radius: 6px; + border-radius: var(--radius-md); background: transparent; color: var(--muted); cursor: pointer; - transition: background 150ms ease, color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .cfg-map__item-remove svg { @@ -1264,29 +1310,30 @@ } .cfg-map__item-remove:hover:not(:disabled) { - background: rgba(255, 92, 92, 0.15); + background: var(--danger-subtle); color: var(--danger); } /* Pill variants */ .pill--sm { - padding: 4px 10px; + padding: 5px 12px; font-size: 11px; } .pill--ok { - border-color: rgba(43, 217, 127, 0.4); + border-color: rgba(34, 197, 94, 0.35); color: var(--ok); } .pill--danger { - border-color: rgba(255, 92, 92, 0.4); + border-color: rgba(239, 68, 68, 0.35); color: var(--danger); } /* =========================================== Mobile Responsiveness =========================================== */ + @media (max-width: 768px) { .config-layout { grid-template-columns: 1fr; @@ -1298,21 +1345,21 @@ } .config-sidebar__header { - padding: 12px 16px; + padding: 14px 16px; } .config-nav { display: flex; flex-wrap: nowrap; - gap: 4px; - padding: 8px 12px; + gap: 6px; + padding: 10px 14px; overflow-x: auto; -webkit-overflow-scrolling: touch; } .config-nav__item { flex: 0 0 auto; - padding: 8px 12px; + padding: 9px 14px; white-space: nowrap; } @@ -1326,7 +1373,7 @@ .config-actions { flex-wrap: wrap; - padding: 12px 16px; + padding: 14px 16px; } .config-actions__left, @@ -1336,32 +1383,32 @@ } .config-section-hero { - padding: 12px 16px; - } - - .config-subnav { - padding: 8px 16px 10px; - } - - .config-content { - padding: 16px; - } - - .config-section-card__header { padding: 14px 16px; } + .config-subnav { + padding: 10px 16px 12px; + } + + .config-content { + padding: 18px; + } + + .config-section-card__header { + padding: 16px 18px; + } + .config-section-card__content { - padding: 16px; + padding: 18px; } .cfg-toggle-row { - padding: 12px 14px; + padding: 14px 16px; } .cfg-map__item { grid-template-columns: 1fr; - gap: 8px; + gap: 10px; } .cfg-map__item-remove { @@ -1371,9 +1418,9 @@ @media (max-width: 480px) { .config-nav__icon { - width: 24px; - height: 24px; - font-size: 16px; + width: 26px; + height: 26px; + font-size: 17px; } .config-nav__label { @@ -1381,12 +1428,12 @@ } .config-section-card__icon { - width: 28px; - height: 28px; + width: 30px; + height: 30px; } .config-section-card__title { - font-size: 15px; + font-size: 16px; } .cfg-segmented { @@ -1395,6 +1442,6 @@ .cfg-segmented__btn { flex: 1 0 auto; - min-width: 60px; + min-width: 70px; } } diff --git a/ui/src/styles/layout.css b/ui/src/styles/layout.css index 7f216144960..c2a5c6fe3f1 100644 --- a/ui/src/styles/layout.css +++ b/ui/src/styles/layout.css @@ -1,10 +1,14 @@ +/* =========================================== + Shell Layout + =========================================== */ + .shell { --shell-pad: 16px; --shell-gap: 16px; --shell-nav-width: 220px; --shell-topbar-height: 56px; - --shell-focus-duration: 220ms; - --shell-focus-ease: cubic-bezier(0.2, 0.85, 0.25, 1); + --shell-focus-duration: 200ms; + --shell-focus-ease: var(--ease-out); height: 100vh; display: grid; grid-template-columns: var(--shell-nav-width) minmax(0, 1fr); @@ -13,7 +17,7 @@ "topbar topbar" "nav content"; gap: 0; - animation: dashboard-enter 0.6s ease-out; + animation: dashboard-enter 0.4s var(--ease-out); transition: grid-template-columns var(--shell-focus-duration) var(--shell-focus-ease); overflow: hidden; } @@ -61,6 +65,10 @@ gap: 0; } +/* =========================================== + Topbar + =========================================== */ + .topbar { grid-area: topbar; position: sticky; @@ -73,8 +81,7 @@ padding: 0 20px; height: var(--shell-topbar-height); border-bottom: 1px solid var(--border); - background: var(--panel); - backdrop-filter: blur(18px); + background: var(--bg); } .topbar-left { @@ -84,49 +91,84 @@ } .topbar .nav-collapse-toggle { - width: 44px; - height: 44px; + width: 36px; + height: 36px; margin-bottom: 0; } .topbar .nav-collapse-toggle__icon { - font-size: 22px; + width: 20px; + height: 20px; } +.topbar .nav-collapse-toggle__icon svg { + width: 20px; + height: 20px; +} + +/* Brand */ .brand { + display: flex; + align-items: center; + gap: 10px; +} + +.brand-logo { + width: 28px; + height: 28px; + flex-shrink: 0; +} + +.brand-logo img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.brand-text { display: flex; flex-direction: column; - gap: 2px; + gap: 1px; } .brand-title { - font-family: var(--font-display); font-size: 16px; - letter-spacing: 1px; - text-transform: uppercase; - font-weight: 600; + font-weight: 700; + letter-spacing: -0.03em; line-height: 1.1; + color: var(--text-strong); } .brand-sub { font-size: 10px; + font-weight: 500; color: var(--muted); - letter-spacing: 0.8px; + letter-spacing: 0.05em; text-transform: uppercase; line-height: 1; } +/* Topbar status */ .topbar-status { display: flex; align-items: center; gap: 8px; } -/* Smaller pill and theme toggle in topbar */ .topbar-status .pill { - padding: 4px 10px; + padding: 6px 10px; gap: 6px; - font-size: 11px; + font-size: 12px; + font-weight: 500; + height: 32px; + box-sizing: border-box; +} + +.topbar-status .pill .mono { + display: flex; + align-items: center; + line-height: 1; + margin-top: 0px; } .topbar-status .statusDot { @@ -135,9 +177,9 @@ } .topbar-status .theme-toggle { - --theme-item: 22px; - --theme-gap: 4px; - --theme-pad: 4px; + --theme-item: 24px; + --theme-gap: 2px; + --theme-pad: 3px; } .topbar-status .theme-icon { @@ -145,17 +187,26 @@ height: 12px; } +/* =========================================== + Navigation Sidebar + =========================================== */ + .nav { grid-area: nav; overflow-y: auto; overflow-x: hidden; - padding: 16px; - border-right: 1px solid var(--border); - background: var(--panel); - backdrop-filter: blur(18px); - transition: width var(--shell-focus-duration) var(--shell-focus-ease), - padding var(--shell-focus-duration) var(--shell-focus-ease); - min-height: 0; /* Allow grid item to shrink and enable scrolling */ + padding: 16px 12px; + background: var(--bg); + scrollbar-width: none; /* Firefox */ + transition: + width var(--shell-focus-duration) var(--shell-focus-ease), + padding var(--shell-focus-duration) var(--shell-focus-ease), + opacity var(--shell-focus-duration) var(--shell-focus-ease); + min-height: 0; +} + +.nav::-webkit-scrollbar { + display: none; /* Chrome/Safari */ } .shell--chat-focus .nav { @@ -164,9 +215,9 @@ border-width: 0; overflow: hidden; pointer-events: none; + opacity: 0; } -/* Collapsed nav sidebar - completely hidden */ .nav--collapsed { width: 0; min-width: 0; @@ -177,7 +228,7 @@ pointer-events: none; } -/* Nav collapse toggle button */ +/* Nav collapse toggle */ .nav-collapse-toggle { width: 32px; height: 32px; @@ -186,71 +237,88 @@ justify-content: center; background: transparent; border: 1px solid transparent; - border-radius: 6px; + border-radius: var(--radius-md); cursor: pointer; - transition: background 150ms ease, border-color 150ms ease; + transition: + background var(--duration-fast) ease, + border-color var(--duration-fast) ease; margin-bottom: 16px; } .nav-collapse-toggle:hover { - background: rgba(255, 255, 255, 0.08); + background: var(--bg-hover); border-color: var(--border); } -:root[data-theme="light"] .nav-collapse-toggle:hover { - background: rgba(0, 0, 0, 0.06); -} - .nav-collapse-toggle__icon { - font-size: 16px; + display: flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; color: var(--muted); + transition: color var(--duration-fast) ease; } +.nav-collapse-toggle__icon svg { + width: 18px; + height: 18px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; +} + +.nav-collapse-toggle:hover .nav-collapse-toggle__icon { + color: var(--text); +} + +/* Nav groups */ .nav-group { - margin-bottom: 18px; + margin-bottom: 20px; display: grid; - gap: 6px; - padding-bottom: 12px; - border-bottom: 1px dashed rgba(255, 255, 255, 0.08); + gap: 2px; } .nav-group:last-child { margin-bottom: 0; - padding-bottom: 0; - border-bottom: none; } .nav-group__items { display: grid; - gap: 4px; + gap: 1px; } .nav-group--collapsed .nav-group__items { display: none; } +/* Nav label */ .nav-label { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; - padding: 4px 0; + padding: 6px 10px; font-size: 11px; font-weight: 500; - text-transform: uppercase; - letter-spacing: 1.4px; - color: var(--text); - opacity: 0.7; + color: var(--muted); margin-bottom: 4px; background: transparent; border: none; cursor: pointer; text-align: left; + border-radius: var(--radius-sm); + transition: + color var(--duration-fast) ease, + background var(--duration-fast) ease; } .nav-label:hover { - opacity: 1; + color: var(--text); + background: var(--bg-hover); } .nav-label--static { @@ -258,7 +326,8 @@ } .nav-label--static:hover { - opacity: 0.7; + color: var(--muted); + background: transparent; } .nav-label__text { @@ -266,131 +335,153 @@ } .nav-label__chevron { - font-size: 12px; - opacity: 0.6; + font-size: 10px; + opacity: 0.5; + transition: transform var(--duration-fast) ease; } +.nav-group--collapsed .nav-label__chevron { + transform: rotate(-90deg); +} + +/* Nav items */ .nav-item { position: relative; display: flex; align-items: center; justify-content: flex-start; - gap: 8px; - padding: 10px 12px 10px 14px; - border-radius: 12px; + gap: 10px; + padding: 8px 10px; + border-radius: var(--radius-md); border: 1px solid transparent; background: transparent; color: var(--muted); cursor: pointer; text-decoration: none; - transition: border-color 160ms ease, background 160ms ease, color 160ms ease; + transition: + border-color var(--duration-fast) ease, + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .nav-item__icon { - font-size: 16px; - width: 18px; - height: 18px; + width: 16px; + height: 16px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; + opacity: 0.7; + transition: opacity var(--duration-fast) ease; +} + +.nav-item__icon svg { + width: 16px; + height: 16px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; } .nav-item__text { font-size: 13px; + font-weight: 500; white-space: nowrap; } .nav-item:hover { color: var(--text); - border-color: rgba(255, 255, 255, 0.12); - background: rgba(255, 255, 255, 0.06); + background: var(--bg-hover); + text-decoration: none; } -.nav-item::before { - content: ""; - position: absolute; - left: 0; - top: 50%; - width: 4px; - height: 60%; - border-radius: 0 999px 999px 0; - transform: translateY(-50%); - background: transparent; +.nav-item:hover .nav-item__icon { + opacity: 1; } .nav-item.active { - color: var(--text); - border-color: rgba(245, 159, 74, 0.45); - background: rgba(245, 159, 74, 0.12); + color: var(--text-strong); + background: var(--accent-subtle); } -.nav-item.active::before { - background: var(--accent); - box-shadow: 0 0 12px rgba(245, 159, 74, 0.4); +.nav-item.active .nav-item__icon { + opacity: 1; + color: var(--accent); } +/* =========================================== + Content Area + =========================================== */ + .content { grid-area: content; - padding: 8px 6px 20px; + padding: 12px 16px 32px; display: flex; flex-direction: column; - gap: 20px; + gap: 24px; min-height: 0; - overflow-y: auto; /* Enable vertical scrolling for pages with long content */ + overflow-y: auto; overflow-x: hidden; } -/* Chat handles its own scrolling (chat-thread); avoid double scrollbars. */ +:root[data-theme="light"] .content { + background: var(--bg-content); +} + .content--chat { overflow: hidden; + padding-bottom: 0; } -.shell--chat .content { - /* No-op: keep chat layout consistent with other tabs */ -} - +/* Content header */ .content-header { display: flex; align-items: flex-end; justify-content: space-between; - gap: 12px; - padding: 0 6px; + gap: 16px; + padding: 4px 8px; overflow: hidden; transform-origin: top center; - transition: opacity var(--shell-focus-duration) var(--shell-focus-ease), + transition: + opacity var(--shell-focus-duration) var(--shell-focus-ease), transform var(--shell-focus-duration) var(--shell-focus-ease), max-height var(--shell-focus-duration) var(--shell-focus-ease), padding var(--shell-focus-duration) var(--shell-focus-ease); - max-height: 90px; + max-height: 80px; } .shell--chat-focus .content-header { opacity: 0; - transform: translateY(-10px); + transform: translateY(-8px); max-height: 0px; padding: 0; pointer-events: none; } .page-title { - font-family: var(--font-display); font-size: 26px; - letter-spacing: 0.6px; + font-weight: 700; + letter-spacing: -0.035em; + line-height: 1.15; + color: var(--text-strong); } .page-sub { color: var(--muted); - font-size: 12px; - letter-spacing: 0.4px; + font-size: 14px; + font-weight: 400; + margin-top: 6px; + letter-spacing: -0.01em; } .page-meta { display: flex; - gap: 10px; + gap: 8px; } -/* Chat view: header and controls side by side */ +/* Chat view header adjustments */ .content--chat .content-header { flex-direction: row; align-items: center; @@ -410,9 +501,13 @@ flex-shrink: 0; } +/* =========================================== + Grid Utilities + =========================================== */ + .grid { display: grid; - gap: 18px; + gap: 20px; } .grid-cols-2 { @@ -426,13 +521,13 @@ .stat-grid { display: grid; gap: 14px; - grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } .note-grid { display: grid; - gap: 14px; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 16px; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); } .row { @@ -443,21 +538,25 @@ .stack { display: grid; - gap: 14px; + gap: 12px; + grid-template-columns: minmax(0, 1fr); } .filters { display: flex; flex-wrap: wrap; - gap: 10px; + gap: 8px; align-items: center; } +/* =========================================== + Responsive - Tablet + =========================================== */ + @media (max-width: 1100px) { .shell { --shell-pad: 12px; --shell-gap: 12px; - --shell-nav-col: 1fr; grid-template-columns: 1fr; grid-template-rows: auto auto 1fr; grid-template-areas: @@ -470,17 +569,18 @@ position: static; max-height: none; display: flex; - gap: 16px; + gap: 6px; overflow-x: auto; border-right: none; - padding: 12px; + border-bottom: 1px solid var(--border); + padding: 10px 14px; + background: var(--bg); } .nav-group { grid-auto-flow: column; - grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); - border-bottom: none; - padding-bottom: 0; + grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); + margin-bottom: 0; } .grid-cols-2, @@ -490,13 +590,11 @@ .topbar { position: static; - flex-direction: column; - align-items: flex-start; - gap: 12px; + padding: 12px 14px; + gap: 10px; } .topbar-status { - width: 100%; flex-wrap: wrap; } diff --git a/ui/src/styles/layout.mobile.css b/ui/src/styles/layout.mobile.css index d3cb9d47deb..450a83608c6 100644 --- a/ui/src/styles/layout.mobile.css +++ b/ui/src/styles/layout.mobile.css @@ -1,12 +1,15 @@ -/* Tablet/Mobile nav fix (under 1100px) - single horizontal scroll row */ +/* =========================================== + Mobile Layout + =========================================== */ + +/* Tablet: Horizontal nav */ @media (max-width: 1100px) { - /* Flatten nav into single horizontal scroll */ .nav { display: flex; flex-direction: row; flex-wrap: nowrap; - gap: 6px; - padding: 10px 12px; + gap: 4px; + padding: 10px 14px; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; @@ -16,21 +19,18 @@ display: none; } - /* Nav groups should flow inline, not stack */ .nav-group { - display: contents; /* Flatten group wrapper - items flow directly into .nav */ + display: contents; } .nav-group__items { - display: contents; /* Flatten items wrapper too */ + display: contents; } - /* Hide group labels on tablet/mobile */ .nav-label { display: none; } - /* Don't hide nav items even if group is "collapsed" */ .nav-group--collapsed .nav-group__items { display: contents; } @@ -38,27 +38,22 @@ .nav-item { padding: 8px 14px; font-size: 13px; - border-radius: 10px; + border-radius: var(--radius-md); white-space: nowrap; flex-shrink: 0; } - - .nav-item::before { - display: none; - } } -/* Mobile-specific improvements */ +/* Mobile-specific styles */ @media (max-width: 600px) { .shell { --shell-pad: 8px; --shell-gap: 8px; } - /* Compact topbar for mobile */ + /* Topbar */ .topbar { padding: 10px 12px; - border-radius: 12px; gap: 8px; flex-direction: row; flex-wrap: wrap; @@ -72,8 +67,7 @@ } .brand-title { - font-size: 15px; - letter-spacing: 0.3px; + font-size: 14px; } .brand-sub { @@ -100,11 +94,10 @@ display: none; } - /* Horizontal scrollable nav for mobile */ + /* Nav */ .nav { - padding: 8px; - border-radius: 12px; - gap: 8px; + padding: 8px 10px; + gap: 4px; -webkit-overflow-scrolling: touch; scrollbar-width: none; } @@ -122,18 +115,14 @@ } .nav-item { - padding: 7px 10px; + padding: 6px 10px; font-size: 12px; - border-radius: 8px; + border-radius: var(--radius-md); white-space: nowrap; flex-shrink: 0; } - .nav-item::before { - display: none; - } - - /* Hide page title on mobile - nav already shows where you are */ + /* Content */ .content-header { display: none; } @@ -143,17 +132,17 @@ gap: 12px; } - /* Smaller cards on mobile */ + /* Cards */ .card { padding: 12px; - border-radius: 12px; + border-radius: var(--radius-md); } .card-title { - font-size: 14px; + font-size: 13px; } - /* Stat grid adjustments */ + /* Stats */ .stat-grid { gap: 8px; grid-template-columns: repeat(2, 1fr); @@ -161,24 +150,24 @@ .stat { padding: 10px; - border-radius: 10px; + border-radius: var(--radius-md); } .stat-label { - font-size: 10px; + font-size: 11px; } .stat-value { - font-size: 16px; + font-size: 18px; } - /* Notes grid */ + /* Notes */ .note-grid { grid-template-columns: 1fr; - gap: 10px; + gap: 8px; } - /* Form fields */ + /* Forms */ .form-grid { grid-template-columns: 1fr; gap: 10px; @@ -188,14 +177,14 @@ .field textarea, .field select { padding: 8px 10px; - border-radius: 10px; + border-radius: var(--radius-md); font-size: 14px; } /* Buttons */ .btn { padding: 8px 12px; - font-size: 13px; + font-size: 12px; } /* Pills */ @@ -204,7 +193,7 @@ font-size: 12px; } - /* Chat-specific mobile improvements */ + /* Chat */ .chat-header { flex-direction: column; align-items: stretch; @@ -227,17 +216,16 @@ .chat-thread { margin-top: 8px; - padding: 10px 8px; - border-radius: 12px; + padding: 12px 8px; } .chat-msg { - max-width: 92%; + max-width: 90%; } .chat-bubble { - padding: 8px 10px; - border-radius: 12px; + padding: 8px 12px; + border-radius: var(--radius-md); } .chat-compose { @@ -247,14 +235,14 @@ .chat-compose__field textarea { min-height: 60px; padding: 8px 10px; - border-radius: 12px; + border-radius: var(--radius-md); font-size: 14px; } - /* Log stream mobile */ + /* Log stream */ .log-stream { - border-radius: 10px; - max-height: 400px; + border-radius: var(--radius-md); + max-height: 380px; } .log-row { @@ -279,14 +267,14 @@ font-size: 12px; } - /* List items */ + /* Lists */ .list-item { padding: 10px; - border-radius: 10px; + border-radius: var(--radius-md); } .list-title { - font-size: 14px; + font-size: 13px; } .list-sub { @@ -296,19 +284,91 @@ /* Code blocks */ .code-block { padding: 8px; - border-radius: 10px; + border-radius: var(--radius-md); font-size: 11px; } - /* Theme toggle smaller */ + /* Theme toggle */ .theme-toggle { --theme-item: 24px; - --theme-gap: 4px; - --theme-pad: 4px; + --theme-gap: 2px; + --theme-pad: 3px; } .theme-icon { - width: 14px; - height: 14px; + width: 12px; + height: 12px; + } +} + +/* Small mobile */ +@media (max-width: 400px) { + .shell { + --shell-pad: 4px; + } + + .topbar { + padding: 8px 10px; + } + + .brand-title { + font-size: 13px; + } + + .nav { + padding: 6px 8px; + } + + .nav-item { + padding: 6px 8px; + font-size: 11px; + } + + .content { + padding: 4px 4px 12px; + gap: 10px; + } + + .card { + padding: 10px; + } + + .stat { + padding: 8px; + } + + .stat-value { + font-size: 16px; + } + + .chat-bubble { + padding: 8px 10px; + } + + .chat-compose__field textarea { + min-height: 52px; + padding: 8px 10px; + font-size: 13px; + } + + .btn { + padding: 6px 10px; + font-size: 11px; + } + + .topbar-status .pill { + padding: 3px 6px; + font-size: 10px; + } + + .theme-toggle { + --theme-item: 22px; + --theme-gap: 2px; + --theme-pad: 2px; + } + + .theme-icon { + width: 11px; + height: 11px; } } diff --git a/ui/src/ui/app-chat.ts b/ui/src/ui/app-chat.ts index 81aae3c8858..c5f88371697 100644 --- a/ui/src/ui/app-chat.ts +++ b/ui/src/ui/app-chat.ts @@ -8,11 +8,13 @@ import { normalizeBasePath } from "./navigation"; import type { GatewayHelloOk } from "./gateway"; import { parseAgentSessionKey } from "../../../src/sessions/session-key-utils.js"; import type { ClawdbotApp } from "./app"; +import type { ChatAttachment, ChatQueueItem } from "./ui-types"; type ChatHost = { connected: boolean; chatMessage: string; - chatQueue: Array<{ id: string; text: string; createdAt: number }>; + chatAttachments: ChatAttachment[]; + chatQueue: ChatQueueItem[]; chatRunId: string | null; chatSending: boolean; sessionKey: string; @@ -45,15 +47,17 @@ export async function handleAbortChat(host: ChatHost) { await abortChatRun(host as unknown as ClawdbotApp); } -function enqueueChatMessage(host: ChatHost, text: string) { +function enqueueChatMessage(host: ChatHost, text: string, attachments?: ChatAttachment[]) { const trimmed = text.trim(); - if (!trimmed) return; + const hasAttachments = Boolean(attachments && attachments.length > 0); + if (!trimmed && !hasAttachments) return; host.chatQueue = [ ...host.chatQueue, { id: generateUUID(), text: trimmed, createdAt: Date.now(), + attachments: hasAttachments ? attachments?.map((att) => ({ ...att })) : undefined, }, ]; } @@ -61,19 +65,31 @@ function enqueueChatMessage(host: ChatHost, text: string) { async function sendChatMessageNow( host: ChatHost, message: string, - opts?: { previousDraft?: string; restoreDraft?: boolean }, + opts?: { + previousDraft?: string; + restoreDraft?: boolean; + attachments?: ChatAttachment[]; + previousAttachments?: ChatAttachment[]; + restoreAttachments?: boolean; + }, ) { resetToolStream(host as unknown as Parameters[0]); - const ok = await sendChatMessage(host as unknown as ClawdbotApp, message); + const ok = await sendChatMessage(host as unknown as ClawdbotApp, message, opts?.attachments); if (!ok && opts?.previousDraft != null) { host.chatMessage = opts.previousDraft; } + if (!ok && opts?.previousAttachments) { + host.chatAttachments = opts.previousAttachments; + } if (ok) { setLastActiveSessionKey(host as unknown as Parameters[0], host.sessionKey); } if (ok && opts?.restoreDraft && opts.previousDraft?.trim()) { host.chatMessage = opts.previousDraft; } + if (ok && opts?.restoreAttachments && opts.previousAttachments?.length) { + host.chatAttachments = opts.previousAttachments; + } scheduleChatScroll(host as unknown as Parameters[0]); if (ok && !host.chatRunId) { void flushChatQueue(host); @@ -86,7 +102,7 @@ async function flushChatQueue(host: ChatHost) { const [next, ...rest] = host.chatQueue; if (!next) return; host.chatQueue = rest; - const ok = await sendChatMessageNow(host, next.text); + const ok = await sendChatMessageNow(host, next.text, { attachments: next.attachments }); if (!ok) { host.chatQueue = [next, ...host.chatQueue]; } @@ -104,7 +120,12 @@ export async function handleSendChat( if (!host.connected) return; const previousDraft = host.chatMessage; const message = (messageOverride ?? host.chatMessage).trim(); - if (!message) return; + const attachments = host.chatAttachments ?? []; + const attachmentsToSend = messageOverride == null ? attachments : []; + const hasAttachments = attachmentsToSend.length > 0; + + // Allow sending with just attachments (no message text required) + if (!message && !hasAttachments) return; if (isChatStopCommand(message)) { await handleAbortChat(host); @@ -113,16 +134,21 @@ export async function handleSendChat( if (messageOverride == null) { host.chatMessage = ""; + // Clear attachments when sending + host.chatAttachments = []; } if (isChatBusy(host)) { - enqueueChatMessage(host, message); + enqueueChatMessage(host, message, attachmentsToSend); return; } await sendChatMessageNow(host, message, { previousDraft: messageOverride == null ? previousDraft : undefined, restoreDraft: Boolean(messageOverride && opts?.restoreDraft), + attachments: hasAttachments ? attachmentsToSend : undefined, + previousAttachments: messageOverride == null ? attachments : undefined, + restoreAttachments: Boolean(messageOverride && opts?.restoreDraft), }); } diff --git a/ui/src/ui/app-events.ts b/ui/src/ui/app-events.ts index c058cf73e5a..eda3a8e1634 100644 --- a/ui/src/ui/app-events.ts +++ b/ui/src/ui/app-events.ts @@ -3,4 +3,3 @@ export type EventLogEntry = { event: string; payload?: unknown; }; - diff --git a/ui/src/ui/app-render.helpers.ts b/ui/src/ui/app-render.helpers.ts index d5a01755b3e..22f8d90dbbc 100644 --- a/ui/src/ui/app-render.helpers.ts +++ b/ui/src/ui/app-render.helpers.ts @@ -3,6 +3,7 @@ import { repeat } from "lit/directives/repeat.js"; import type { AppViewState } from "./app-view-state"; import { iconForTab, pathForTab, titleForTab, type Tab } from "./navigation"; +import { icons } from "./icons"; import { loadChatHistory } from "./controllers/chat"; import { syncUrlWithSessionKey } from "./app-settings"; import type { SessionsListResult } from "./types"; @@ -31,7 +32,7 @@ export function renderTab(state: AppViewState, tab: Tab) { }} title=${titleForTab(tab)} > - + ${titleForTab(tab)} `; @@ -108,7 +109,7 @@ export function renderChatControls(state: AppViewState) { ? "Disabled during onboarding" : "Toggle assistant thinking/working output"} > - 🧠 + ${icons.brain}
    -
    CLAWDBOT
    -
    Gateway Dashboard
    + +
    +
    CLAWDBOT
    +
    Gateway Dashboard
    +
    @@ -179,7 +185,7 @@ export function renderApp(state: AppViewState) { rel="noreferrer" title="Docs (opens in new tab)" > - + Docs
    @@ -425,6 +431,7 @@ export function renderApp(state: AppViewState) { onSessionKeyChange: (next) => { state.sessionKey = next; state.chatMessage = ""; + state.chatAttachments = []; state.chatStream = null; state.chatStreamStartedAt = null; state.chatRunId = null; @@ -471,6 +478,8 @@ export function renderApp(state: AppViewState) { }, onChatScroll: (event) => state.handleChatScroll(event), onDraftChange: (next) => (state.chatMessage = next), + attachments: state.chatAttachments, + onAttachmentsChange: (next) => (state.chatAttachments = next), onSend: () => state.handleSendChat(), canAbort: Boolean(state.chatRunId), onAbort: () => void state.handleAbortChat(), diff --git a/ui/src/ui/app-tool-stream.ts b/ui/src/ui/app-tool-stream.ts index 5c83c3a793a..2fbe12b7a89 100644 --- a/ui/src/ui/app-tool-stream.ts +++ b/ui/src/ui/app-tool-stream.ts @@ -154,13 +154,13 @@ const COMPACTION_TOAST_DURATION_MS = 5000; export function handleCompactionEvent(host: CompactionHost, payload: AgentEventPayload) { const data = payload.data ?? {}; const phase = typeof data.phase === "string" ? data.phase : ""; - + // Clear any existing timer if (host.compactionClearTimer != null) { window.clearTimeout(host.compactionClearTimer); host.compactionClearTimer = null; } - + if (phase === "start") { host.compactionStatus = { active: true, @@ -183,13 +183,13 @@ export function handleCompactionEvent(host: CompactionHost, payload: AgentEventP export function handleAgentEvent(host: ToolStreamHost, payload?: AgentEventPayload) { if (!payload) return; - + // Handle compaction events if (payload.stream === "compaction") { handleCompactionEvent(host as CompactionHost, payload); return; } - + if (payload.stream !== "tool") return; const sessionKey = typeof payload.sessionKey === "string" ? payload.sessionKey : undefined; diff --git a/ui/src/ui/app-view-state.ts b/ui/src/ui/app-view-state.ts index f589c760ca6..069465e325d 100644 --- a/ui/src/ui/app-view-state.ts +++ b/ui/src/ui/app-view-state.ts @@ -19,7 +19,7 @@ import type { SkillStatusReport, StatusSummary, } from "./types"; -import type { ChatQueueItem, CronFormState } from "./ui-types"; +import type { ChatAttachment, ChatQueueItem, CronFormState } from "./ui-types"; import type { EventLogEntry } from "./app-events"; import type { SkillMessage } from "./controllers/skills"; import type { @@ -49,6 +49,7 @@ export type AppViewState = { chatLoading: boolean; chatSending: boolean; chatMessage: string; + chatAttachments: ChatAttachment[]; chatMessages: unknown[]; chatToolMessages: unknown[]; chatStream: string | null; diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts index 0e21d283ab8..649e763422f 100644 --- a/ui/src/ui/app.ts +++ b/ui/src/ui/app.ts @@ -24,7 +24,7 @@ import type { StatusSummary, NostrProfile, } from "./types"; -import { type ChatQueueItem, type CronFormState } from "./ui-types"; +import { type ChatAttachment, type ChatQueueItem, type CronFormState } from "./ui-types"; import type { EventLogEntry } from "./app-events"; import { DEFAULT_CRON_FORM, DEFAULT_LOG_LEVEL_FILTERS } from "./app-defaults"; import type { @@ -129,6 +129,7 @@ export class ClawdbotApp extends LitElement { @state() chatAvatarUrl: string | null = null; @state() chatThinkingLevel: string | null = null; @state() chatQueue: ChatQueueItem[] = []; + @state() chatAttachments: ChatAttachment[] = []; // Sidebar state for tool output viewing @state() sidebarOpen = false; @state() sidebarContent: string | null = null; diff --git a/ui/src/ui/chat/copy-as-markdown.ts b/ui/src/ui/chat/copy-as-markdown.ts index 8786d354610..1309f08b857 100644 --- a/ui/src/ui/chat/copy-as-markdown.ts +++ b/ui/src/ui/chat/copy-as-markdown.ts @@ -1,14 +1,11 @@ import { html, type TemplateResult } from "lit"; -import { renderEmojiIcon, setEmojiIcon } from "../icons"; +import { icons } from "../icons"; const COPIED_FOR_MS = 1500; const ERROR_FOR_MS = 2000; const COPY_LABEL = "Copy as markdown"; const COPIED_LABEL = "Copied"; const ERROR_LABEL = "Copy failed"; -const COPY_ICON = "📋"; -const COPIED_ICON = "✓"; -const ERROR_ICON = "!"; type CopyButtonOptions = { text: () => string; @@ -41,7 +38,7 @@ function createCopyButton(options: CopyButtonOptions): TemplateResult { aria-label=${idleLabel} @click=${async (e: Event) => { const btn = e.currentTarget as HTMLButtonElement | null; - const icon = btn?.querySelector( + const iconContainer = btn?.querySelector( ".chat-copy-btn__icon", ) as HTMLElement | null; @@ -61,30 +58,29 @@ function createCopyButton(options: CopyButtonOptions): TemplateResult { if (!copied) { btn.dataset.error = "1"; setButtonLabel(btn, ERROR_LABEL); - setEmojiIcon(icon, ERROR_ICON); window.setTimeout(() => { if (!btn.isConnected) return; delete btn.dataset.error; setButtonLabel(btn, idleLabel); - setEmojiIcon(icon, COPY_ICON); }, ERROR_FOR_MS); return; } btn.dataset.copied = "1"; setButtonLabel(btn, COPIED_LABEL); - setEmojiIcon(icon, COPIED_ICON); window.setTimeout(() => { if (!btn.isConnected) return; delete btn.dataset.copied; setButtonLabel(btn, idleLabel); - setEmojiIcon(icon, COPY_ICON); }, COPIED_FOR_MS); }} > - ${renderEmojiIcon(COPY_ICON, "chat-copy-btn__icon")} + `; } diff --git a/ui/src/ui/chat/grouped-render.ts b/ui/src/ui/chat/grouped-render.ts index ea1c7ffdad2..4a9ccec140f 100644 --- a/ui/src/ui/chat/grouped-render.ts +++ b/ui/src/ui/chat/grouped-render.ts @@ -13,6 +13,48 @@ import { } from "./message-extract"; import { extractToolCards, renderToolCardSidebar } from "./tool-cards"; +type ImageBlock = { + url: string; + alt?: string; +}; + +function extractImages(message: unknown): ImageBlock[] { + const m = message as Record; + const content = m.content; + const images: ImageBlock[] = []; + + if (Array.isArray(content)) { + for (const block of content) { + if (typeof block !== "object" || block === null) continue; + const b = block as Record; + + if (b.type === "image") { + // Handle source object format (from sendChatMessage) + const source = b.source as Record | undefined; + if (source?.type === "base64" && typeof source.data === "string") { + const data = source.data as string; + const mediaType = (source.media_type as string) || "image/png"; + // If data is already a data URL, use it directly + const url = data.startsWith("data:") + ? data + : `data:${mediaType};base64,${data}`; + images.push({ url }); + } else if (typeof b.url === "string") { + images.push({ url: b.url }); + } + } else if (b.type === "image_url") { + // OpenAI format + const imageUrl = b.image_url as Record | undefined; + if (typeof imageUrl?.url === "string") { + images.push({ url: imageUrl.url }); + } + } + } + } + + return images; +} + export function renderReadingIndicatorGroup(assistant?: AssistantIdentity) { return html`
    @@ -163,6 +205,25 @@ function isAvatarUrl(value: string): boolean { ); } +function renderMessageImages(images: ImageBlock[]) { + if (images.length === 0) return nothing; + + return html` +
    + ${images.map( + (img) => html` + ${img.alt window.open(img.url, "_blank")} + /> + `, + )} +
    + `; +} + function renderGroupedMessage( message: unknown, opts: { isStreaming: boolean; showReasoning: boolean }, @@ -179,6 +240,8 @@ function renderGroupedMessage( const toolCards = extractToolCards(message); const hasToolCards = toolCards.length > 0; + const images = extractImages(message); + const hasImages = images.length > 0; const extractedText = extractTextCached(message); const extractedThinking = @@ -207,11 +270,12 @@ function renderGroupedMessage( )}`; } - if (!markdown && !hasToolCards) return nothing; + if (!markdown && !hasToolCards && !hasImages) return nothing; return html`
    ${canCopyMarkdown ? renderCopyAsMarkdownButton(markdown!) : nothing} + ${renderMessageImages(images)} ${reasoningMarkdown ? html`
    ${unsafeHTML( toSanitizedMarkdownHtml(reasoningMarkdown), diff --git a/ui/src/ui/chat/tool-cards.ts b/ui/src/ui/chat/tool-cards.ts index 78b5dffec53..bf82fa49a85 100644 --- a/ui/src/ui/chat/tool-cards.ts +++ b/ui/src/ui/chat/tool-cards.ts @@ -1,6 +1,7 @@ import { html, nothing } from "lit"; import { formatToolDetail, resolveToolDisplay } from "../tool-display"; +import { icons } from "../icons"; import type { ToolCard } from "../types/chat-types"; import { TOOL_INLINE_THRESHOLD } from "./constants"; import { @@ -95,13 +96,13 @@ export function renderToolCardSidebar( >
    - ${display.emoji} + ${icons[display.icon]} ${display.label}
    ${canClick - ? html`${hasText ? "View ›" : "›"}` + ? html`${hasText ? "View" : ""} ${icons.check}` : nothing} - ${isEmpty && !canClick ? html`` : nothing} + ${isEmpty && !canClick ? html`${icons.check}` : nothing}
    ${detail ? html`
    ${detail}
    ` diff --git a/ui/src/ui/controllers/chat.test.ts b/ui/src/ui/controllers/chat.test.ts new file mode 100644 index 00000000000..c75ceefc493 --- /dev/null +++ b/ui/src/ui/controllers/chat.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from "vitest"; + +import { + handleChatEvent, + type ChatEventPayload, + type ChatState, +} from "./chat"; + +function createState(overrides: Partial = {}): ChatState { + return { + client: null, + connected: true, + sessionKey: "main", + chatLoading: false, + chatMessages: [], + chatThinkingLevel: null, + chatSending: false, + chatMessage: "", + chatRunId: null, + chatStream: null, + chatStreamStartedAt: null, + lastError: null, + ...overrides, + }; +} + +describe("handleChatEvent", () => { + it("returns null when payload is missing", () => { + const state = createState(); + expect(handleChatEvent(state, undefined)).toBe(null); + }); + + it("returns null when sessionKey does not match", () => { + const state = createState({ sessionKey: "main" }); + const payload: ChatEventPayload = { + runId: "run-1", + sessionKey: "other", + state: "final", + }; + expect(handleChatEvent(state, payload)).toBe(null); + }); + + it("returns null for delta from another run", () => { + const state = createState({ + sessionKey: "main", + chatRunId: "run-user", + chatStream: "Hello", + }); + const payload: ChatEventPayload = { + runId: "run-announce", + sessionKey: "main", + state: "delta", + message: { role: "assistant", content: [{ type: "text", text: "Done" }] }, + }; + expect(handleChatEvent(state, payload)).toBe(null); + expect(state.chatRunId).toBe("run-user"); + expect(state.chatStream).toBe("Hello"); + }); + + it("returns 'final' for final from another run (e.g. sub-agent announce) without clearing state", () => { + const state = createState({ + sessionKey: "main", + chatRunId: "run-user", + chatStream: "Working...", + chatStreamStartedAt: 123, + }); + const payload: ChatEventPayload = { + runId: "run-announce", + sessionKey: "main", + state: "final", + message: { + role: "assistant", + content: [{ type: "text", text: "Sub-agent findings" }], + }, + }; + expect(handleChatEvent(state, payload)).toBe("final"); + expect(state.chatRunId).toBe("run-user"); + expect(state.chatStream).toBe("Working..."); + expect(state.chatStreamStartedAt).toBe(123); + }); + + it("processes final from own run and clears state", () => { + const state = createState({ + sessionKey: "main", + chatRunId: "run-1", + chatStream: "Reply", + chatStreamStartedAt: 100, + }); + const payload: ChatEventPayload = { + runId: "run-1", + sessionKey: "main", + state: "final", + }; + expect(handleChatEvent(state, payload)).toBe("final"); + expect(state.chatRunId).toBe(null); + expect(state.chatStream).toBe(null); + expect(state.chatStreamStartedAt).toBe(null); + }); +}); diff --git a/ui/src/ui/controllers/chat.ts b/ui/src/ui/controllers/chat.ts index 53027c6ea27..518c35fe1df 100644 --- a/ui/src/ui/controllers/chat.ts +++ b/ui/src/ui/controllers/chat.ts @@ -1,6 +1,7 @@ -import type { GatewayBrowserClient } from "../gateway"; import { extractText } from "../chat/message-extract"; +import type { GatewayBrowserClient } from "../gateway"; import { generateUUID } from "../uuid"; +import type { ChatAttachment } from "../ui-types"; export type ChatState = { client: GatewayBrowserClient | null; @@ -11,6 +12,7 @@ export type ChatState = { chatThinkingLevel: string | null; chatSending: boolean; chatMessage: string; + chatAttachments: ChatAttachment[]; chatRunId: string | null; chatStream: string | null; chatStreamStartedAt: number | null; @@ -43,17 +45,44 @@ export async function loadChatHistory(state: ChatState) { } } -export async function sendChatMessage(state: ChatState, message: string): Promise { +function dataUrlToBase64(dataUrl: string): { content: string; mimeType: string } | null { + const match = /^data:([^;]+);base64,(.+)$/.exec(dataUrl); + if (!match) return null; + return { mimeType: match[1], content: match[2] }; +} + +export async function sendChatMessage( + state: ChatState, + message: string, + attachments?: ChatAttachment[], +): Promise { if (!state.client || !state.connected) return false; const msg = message.trim(); - if (!msg) return false; + const hasAttachments = attachments && attachments.length > 0; + if (!msg && !hasAttachments) return false; const now = Date.now(); + + // Build user message content blocks + const contentBlocks: Array<{ type: string; text?: string; source?: unknown }> = []; + if (msg) { + contentBlocks.push({ type: "text", text: msg }); + } + // Add image previews to the message for display + if (hasAttachments) { + for (const att of attachments) { + contentBlocks.push({ + type: "image", + source: { type: "base64", media_type: att.mimeType, data: att.dataUrl }, + }); + } + } + state.chatMessages = [ ...state.chatMessages, { role: "user", - content: [{ type: "text", text: msg }], + content: contentBlocks, timestamp: now, }, ]; @@ -64,12 +93,29 @@ export async function sendChatMessage(state: ChatState, message: string): Promis state.chatRunId = runId; state.chatStream = ""; state.chatStreamStartedAt = now; + + // Convert attachments to API format + const apiAttachments = hasAttachments + ? attachments + .map((att) => { + const parsed = dataUrlToBase64(att.dataUrl); + if (!parsed) return null; + return { + type: "image", + mimeType: parsed.mimeType, + content: parsed.content, + }; + }) + .filter((a): a is NonNullable => a !== null) + : undefined; + try { await state.client.request("chat.send", { sessionKey: state.sessionKey, message: msg, deliver: false, idempotencyKey: runId, + attachments: apiAttachments, }); return true; } catch (err) { @@ -115,8 +161,17 @@ export function handleChatEvent( ) { if (!payload) return null; if (payload.sessionKey !== state.sessionKey) return null; - if (payload.runId && state.chatRunId && payload.runId !== state.chatRunId) + + // Final from another run (e.g. sub-agent announce): refresh history to show new message. + // See https://github.com/clawdbot/clawdbot/issues/1909 + if ( + payload.runId && + state.chatRunId && + payload.runId !== state.chatRunId + ) { + if (payload.state === "final") return "final"; return null; + } if (payload.state === "delta") { const next = extractText(payload.message); diff --git a/ui/src/ui/controllers/config/form-utils.ts b/ui/src/ui/controllers/config/form-utils.ts index dea4d7f61f4..fd40bb5aca2 100644 --- a/ui/src/ui/controllers/config/form-utils.ts +++ b/ui/src/ui/controllers/config/form-utils.ts @@ -74,4 +74,3 @@ export function removePathValue( delete (current as Record)[lastKey]; } } - diff --git a/ui/src/ui/controllers/debug.ts b/ui/src/ui/controllers/debug.ts index 78993a3aff6..5aa1eec437f 100644 --- a/ui/src/ui/controllers/debug.ts +++ b/ui/src/ui/controllers/debug.ts @@ -54,4 +54,3 @@ export async function callDebugMethod(state: DebugState) { state.debugCallError = String(err); } } - diff --git a/ui/src/ui/controllers/presence.ts b/ui/src/ui/controllers/presence.ts index 4154307b177..67ac2761d34 100644 --- a/ui/src/ui/controllers/presence.ts +++ b/ui/src/ui/controllers/presence.ts @@ -33,4 +33,3 @@ export async function loadPresence(state: PresenceState) { state.presenceLoading = false; } } - diff --git a/ui/src/ui/format.test.ts b/ui/src/ui/format.test.ts index d7acecebb12..f8b1e8e5627 100644 --- a/ui/src/ui/format.test.ts +++ b/ui/src/ui/format.test.ts @@ -39,4 +39,3 @@ describe("stripThinkingTags", () => { expect(stripThinkingTags("Hello")).toBe("Hello"); }); }); - diff --git a/ui/src/ui/icons.ts b/ui/src/ui/icons.ts index 4803f223e83..eaf8f0e2758 100644 --- a/ui/src/ui/icons.ts +++ b/ui/src/ui/icons.ts @@ -1,7 +1,59 @@ import { html, type TemplateResult } from "lit"; -export function renderEmojiIcon(icon: string, className: string): TemplateResult { - return html``; +// Lucide-style SVG icons +// All icons use currentColor for stroke + +export const icons = { + // Navigation icons + messageSquare: html``, + barChart: html``, + link: html``, + radio: html``, + fileText: html``, + zap: html``, + monitor: html``, + settings: html``, + bug: html``, + scrollText: html``, + folder: html``, + + // UI icons + menu: html``, + x: html``, + check: html``, + copy: html``, + search: html``, + brain: html``, + book: html``, + loader: html``, + + // Tool icons + wrench: html``, + fileCode: html``, + edit: html``, + penLine: html``, + paperclip: html``, + globe: html``, + image: html``, + smartphone: html``, + plug: html``, + circle: html``, + puzzle: html``, +} as const; + +export type IconName = keyof typeof icons; + +export function icon(name: IconName): TemplateResult { + return icons[name]; +} + +export function renderIcon(name: IconName, className = "nav-item__icon"): TemplateResult { + return html``; +} + +// Legacy function for compatibility +export function renderEmojiIcon(iconContent: string | TemplateResult, className: string): TemplateResult { + return html``; } export function setEmojiIcon(target: HTMLElement | null, icon: string): void { diff --git a/ui/src/ui/markdown.test.ts b/ui/src/ui/markdown.test.ts index da2c4aca0c1..396ff0fa51d 100644 --- a/ui/src/ui/markdown.test.ts +++ b/ui/src/ui/markdown.test.ts @@ -30,4 +30,3 @@ describe("toSanitizedMarkdownHtml", () => { expect(html).toContain("console.log(1)"); }); }); - diff --git a/ui/src/ui/navigation.ts b/ui/src/ui/navigation.ts index 623764a8f36..5938e25e9c1 100644 --- a/ui/src/ui/navigation.ts +++ b/ui/src/ui/navigation.ts @@ -1,3 +1,5 @@ +import type { IconName } from "./icons.js"; + export const TAB_GROUPS = [ { label: "Chat", tabs: ["chat"] }, { @@ -98,32 +100,32 @@ export function inferBasePathFromPathname(pathname: string): string { return `/${segments.join("/")}`; } -export function iconForTab(tab: Tab): string { +export function iconForTab(tab: Tab): IconName { switch (tab) { case "chat": - return "💬"; + return "messageSquare"; case "overview": - return "📊"; + return "barChart"; case "channels": - return "🔗"; + return "link"; case "instances": - return "📡"; + return "radio"; case "sessions": - return "📄"; + return "fileText"; case "cron": - return "⏰"; + return "loader"; case "skills": - return "⚡️"; + return "zap"; case "nodes": - return "🖥️"; + return "monitor"; case "config": - return "⚙️"; + return "settings"; case "debug": - return "🐞"; + return "bug"; case "logs": - return "🧾"; + return "scrollText"; default: - return "📁"; + return "folder"; } } diff --git a/ui/src/ui/presenter.ts b/ui/src/ui/presenter.ts index 9f3df3dcb96..ddb99d9c53c 100644 --- a/ui/src/ui/presenter.ts +++ b/ui/src/ui/presenter.ts @@ -55,4 +55,3 @@ export function formatCronPayload(job: CronJob) { if (p.kind === "systemEvent") return `System: ${p.text}`; return `Agent: ${p.message}`; } - diff --git a/ui/src/ui/tool-display.json b/ui/src/ui/tool-display.json index 1b978b4ae97..4a6bd0524ba 100644 --- a/ui/src/ui/tool-display.json +++ b/ui/src/ui/tool-display.json @@ -1,7 +1,7 @@ { "version": 1, "fallback": { - "emoji": "🧩", + "icon": "puzzle", "detailKeys": [ "command", "path", @@ -26,37 +26,37 @@ }, "tools": { "bash": { - "emoji": "🛠️", + "icon": "wrench", "title": "Bash", "detailKeys": ["command"] }, "process": { - "emoji": "🧰", + "icon": "wrench", "title": "Process", "detailKeys": ["sessionId"] }, "read": { - "emoji": "📖", + "icon": "fileText", "title": "Read", "detailKeys": ["path"] }, "write": { - "emoji": "✍️", + "icon": "edit", "title": "Write", "detailKeys": ["path"] }, "edit": { - "emoji": "📝", + "icon": "penLine", "title": "Edit", "detailKeys": ["path"] }, "attach": { - "emoji": "📎", + "icon": "paperclip", "title": "Attach", "detailKeys": ["path", "url", "fileName"] }, "browser": { - "emoji": "🌐", + "icon": "globe", "title": "Browser", "actions": { "status": { "label": "status" }, @@ -95,7 +95,7 @@ } }, "canvas": { - "emoji": "🖼️", + "icon": "image", "title": "Canvas", "actions": { "present": { "label": "present", "detailKeys": ["target", "node", "nodeId"] }, @@ -108,7 +108,7 @@ } }, "nodes": { - "emoji": "📱", + "icon": "smartphone", "title": "Nodes", "actions": { "status": { "label": "status" }, @@ -127,7 +127,7 @@ } }, "cron": { - "emoji": "⏰", + "icon": "loader", "title": "Cron", "actions": { "status": { "label": "status" }, @@ -144,7 +144,7 @@ } }, "gateway": { - "emoji": "🔌", + "icon": "plug", "title": "Gateway", "actions": { "restart": { "label": "restart", "detailKeys": ["reason", "delayMs"] }, @@ -161,7 +161,7 @@ } }, "whatsapp_login": { - "emoji": "🟢", + "icon": "circle", "title": "WhatsApp Login", "actions": { "start": { "label": "start" }, @@ -169,7 +169,7 @@ } }, "discord": { - "emoji": "💬", + "icon": "messageSquare", "title": "Discord", "actions": { "react": { "label": "react", "detailKeys": ["channelId", "messageId", "emoji"] }, @@ -204,7 +204,7 @@ } }, "slack": { - "emoji": "💬", + "icon": "messageSquare", "title": "Slack", "actions": { "react": { "label": "react", "detailKeys": ["channelId", "messageId", "emoji"] }, diff --git a/ui/src/ui/tool-display.ts b/ui/src/ui/tool-display.ts index 02c54b4579e..4b2de6ecb8e 100644 --- a/ui/src/ui/tool-display.ts +++ b/ui/src/ui/tool-display.ts @@ -1,4 +1,5 @@ import rawConfig from "./tool-display.json"; +import type { IconName } from "./icons"; type ToolDisplayActionSpec = { label?: string; @@ -6,7 +7,7 @@ type ToolDisplayActionSpec = { }; type ToolDisplaySpec = { - emoji?: string; + icon?: string; title?: string; label?: string; detailKeys?: string[]; @@ -21,7 +22,7 @@ type ToolDisplayConfig = { export type ToolDisplay = { name: string; - emoji: string; + icon: IconName; title: string; label: string; verb?: string; @@ -29,7 +30,7 @@ export type ToolDisplay = { }; const TOOL_DISPLAY_CONFIG = rawConfig as ToolDisplayConfig; -const FALLBACK = TOOL_DISPLAY_CONFIG.fallback ?? { emoji: "🧩" }; +const FALLBACK = TOOL_DISPLAY_CONFIG.fallback ?? { icon: "puzzle" }; const TOOL_MAP = TOOL_DISPLAY_CONFIG.tools ?? {}; function normalizeToolName(name?: string): string { @@ -135,7 +136,7 @@ export function resolveToolDisplay(params: { const name = normalizeToolName(params.name); const key = name.toLowerCase(); const spec = TOOL_MAP[key]; - const emoji = spec?.emoji ?? FALLBACK.emoji ?? "🧩"; + const icon = (spec?.icon ?? FALLBACK.icon ?? "puzzle") as IconName; const title = spec?.title ?? defaultTitle(name); const label = spec?.label ?? name; const actionRaw = @@ -168,7 +169,7 @@ export function resolveToolDisplay(params: { return { name, - emoji, + icon, title, label, verb, @@ -186,9 +187,7 @@ export function formatToolDetail(display: ToolDisplay): string | undefined { export function formatToolSummary(display: ToolDisplay): string { const detail = formatToolDetail(display); - return detail - ? `${display.emoji} ${display.label}: ${detail}` - : `${display.emoji} ${display.label}`; + return detail ? `${display.label}: ${detail}` : display.label; } function shortenHomeInString(input: string): string { diff --git a/ui/src/ui/ui-types.ts b/ui/src/ui/ui-types.ts index 428c4c381b4..196d6d114f2 100644 --- a/ui/src/ui/ui-types.ts +++ b/ui/src/ui/ui-types.ts @@ -1,7 +1,14 @@ +export type ChatAttachment = { + id: string; + dataUrl: string; + mimeType: string; +}; + export type ChatQueueItem = { id: string; text: string; createdAt: number; + attachments?: ChatAttachment[]; }; export const CRON_CHANNEL_LAST = "last"; diff --git a/ui/src/ui/uuid.test.ts b/ui/src/ui/uuid.test.ts index 62856f1b688..9f7ccbf815e 100644 --- a/ui/src/ui/uuid.test.ts +++ b/ui/src/ui/uuid.test.ts @@ -30,4 +30,3 @@ describe("generateUUID", () => { expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); }); }); - diff --git a/ui/src/ui/uuid.ts b/ui/src/ui/uuid.ts index 7124dbb8f41..f231d0f7fbe 100644 --- a/ui/src/ui/uuid.ts +++ b/ui/src/ui/uuid.ts @@ -40,4 +40,3 @@ export function generateUUID(cryptoLike: CryptoLike | null = globalThis.crypto): return uuidFromBytes(weakRandomBytes()); } - diff --git a/ui/src/ui/views/channels.shared.ts b/ui/src/ui/views/channels.shared.ts index 6238a1e1c03..9af0c2ea143 100644 --- a/ui/src/ui/views/channels.shared.ts +++ b/ui/src/ui/views/channels.shared.ts @@ -43,4 +43,3 @@ export function renderChannelAccountCount( if (count < 2) return nothing; return html``; } - diff --git a/ui/src/ui/views/channels.whatsapp.ts b/ui/src/ui/views/channels.whatsapp.ts index b40a533c65d..eae3be69570 100644 --- a/ui/src/ui/views/channels.whatsapp.ts +++ b/ui/src/ui/views/channels.whatsapp.ts @@ -116,4 +116,3 @@ export function renderWhatsAppCard(params: {
    `; } - diff --git a/ui/src/ui/views/chat.ts b/ui/src/ui/views/chat.ts index 677c2a18379..a9b4da572b6 100644 --- a/ui/src/ui/views/chat.ts +++ b/ui/src/ui/views/chat.ts @@ -1,8 +1,9 @@ import { html, nothing } from "lit"; import { repeat } from "lit/directives/repeat.js"; import type { SessionsListResult } from "../types"; -import type { ChatQueueItem } from "../ui-types"; +import type { ChatAttachment, ChatQueueItem } from "../ui-types"; import type { ChatItem, MessageGroup } from "../types/chat-types"; +import { icons } from "../icons"; import { normalizeMessage, normalizeRoleForGrouping, @@ -51,6 +52,9 @@ export type ChatProps = { splitRatio?: number; assistantName: string; assistantAvatar: string | null; + // Image attachments + attachments?: ChatAttachment[]; + onAttachmentsChange?: (attachments: ChatAttachment[]) => void; // Event handlers onRefresh: () => void; onToggleFocusMode: () => void; @@ -69,31 +73,107 @@ const COMPACTION_TOAST_DURATION_MS = 5000; function renderCompactionIndicator(status: CompactionIndicatorStatus | null | undefined) { if (!status) return nothing; - + // Show "compacting..." while active if (status.active) { return html`
    - 🧹 Compacting context... + ${icons.loader} Compacting context...
    `; } - + // Show "compaction complete" briefly after completion if (status.completedAt) { const elapsed = Date.now() - status.completedAt; if (elapsed < COMPACTION_TOAST_DURATION_MS) { return html`
    - 🧹 Context compacted + ${icons.check} Context compacted
    `; } } - + return nothing; } +function generateAttachmentId(): string { + return `att-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; +} + +function handlePaste( + e: ClipboardEvent, + props: ChatProps, +) { + const items = e.clipboardData?.items; + if (!items || !props.onAttachmentsChange) return; + + const imageItems: DataTransferItem[] = []; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + if (item.type.startsWith("image/")) { + imageItems.push(item); + } + } + + if (imageItems.length === 0) return; + + e.preventDefault(); + + for (const item of imageItems) { + const file = item.getAsFile(); + if (!file) continue; + + const reader = new FileReader(); + reader.onload = () => { + const dataUrl = reader.result as string; + const newAttachment: ChatAttachment = { + id: generateAttachmentId(), + dataUrl, + mimeType: file.type, + }; + const current = props.attachments ?? []; + props.onAttachmentsChange?.([...current, newAttachment]); + }; + reader.readAsDataURL(file); + } +} + +function renderAttachmentPreview(props: ChatProps) { + const attachments = props.attachments ?? []; + if (attachments.length === 0) return nothing; + + return html` +
    + ${attachments.map( + (att) => html` +
    + Attachment preview + +
    + `, + )} +
    + `; +} + export function renderChat(props: ChatProps) { const canCompose = props.connected; const isBusy = props.sending || props.stream !== null; @@ -108,8 +188,11 @@ export function renderChat(props: ChatProps) { avatar: props.assistantAvatar ?? props.assistantAvatarUrl ?? null, }; + const hasAttachments = (props.attachments?.length ?? 0) > 0; const composePlaceholder = props.connected - ? "Message (↩ to send, Shift+↩ for line breaks)" + ? hasAttachments + ? "Add a message or paste more images..." + : "Message (↩ to send, Shift+↩ for line breaks, paste images)" : "Connect to the gateway to start chatting…"; const splitRatio = props.splitRatio ?? 0.6; @@ -171,7 +254,7 @@ export function renderChat(props: ChatProps) { aria-label="Exit focus mode" title="Exit focus mode" > - ✕ + ${icons.x} ` : nothing} @@ -216,14 +299,19 @@ export function renderChat(props: ChatProps) { ${props.queue.map( (item) => html`
    -
    ${item.text}
    +
    + ${item.text || + (item.attachments?.length + ? `Image (${item.attachments.length})` + : "")} +
    `, @@ -234,39 +322,43 @@ export function renderChat(props: ChatProps) { : nothing}
    - -
    - - + ${renderAttachmentPreview(props)} +
    + +
    + + +
    diff --git a/ui/src/ui/views/config-form.node.ts b/ui/src/ui/views/config-form.node.ts index 07cb9f23931..9d121d7f17f 100644 --- a/ui/src/ui/views/config-form.node.ts +++ b/ui/src/ui/views/config-form.node.ts @@ -120,7 +120,7 @@ export function renderNode(params: { const hasString = normalizedTypes.has("string"); const hasNumber = normalizedTypes.has("number"); const hasBoolean = normalizedTypes.has("boolean"); - + if (hasBoolean && normalizedTypes.size === 1) { return renderNode({ ...params, @@ -383,14 +383,14 @@ function renderObject(params: { const hint = hintForPath(path, hints); const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1))); const help = hint?.help ?? schema.description; - + const fallback = value ?? schema.default; const obj = fallback && typeof fallback === "object" && !Array.isArray(fallback) ? (fallback as Record) : {}; const props = schema.properties ?? {}; const entries = Object.entries(props); - + // Sort by hint order const sorted = entries.sort((a, b) => { const orderA = hintForPath([...path, a[0]], hints)?.order ?? 0; @@ -514,7 +514,7 @@ function renderArray(params: {
    ${help ? html`
    ${help}
    ` : nothing} - + ${arr.length === 0 ? html`
    No items yet. Click "Add" to create one. @@ -597,7 +597,7 @@ function renderMapField(params: { Add Entry
    - + ${entries.length === 0 ? html`
    No custom entries.
    ` : html` diff --git a/ui/src/ui/views/config-form.render.ts b/ui/src/ui/views/config-form.render.ts index da8d38d8edc..2e7dc5f4e99 100644 --- a/ui/src/ui/views/config-form.render.ts +++ b/ui/src/ui/views/config-form.render.ts @@ -1,5 +1,6 @@ import { html, nothing } from "lit"; import type { ConfigUiHints } from "../types"; +import { icons } from "../icons"; import { hintForPath, humanize, @@ -93,16 +94,16 @@ function matchesSearch(key: string, schema: JsonSchema, query: string): boolean if (!query) return true; const q = query.toLowerCase(); const meta = SECTION_META[key]; - + // Check key name if (key.toLowerCase().includes(q)) return true; - + // Check label and description if (meta) { if (meta.label.toLowerCase().includes(q)) return true; if (meta.description.toLowerCase().includes(q)) return true; } - + return schemaMatches(schema, q); } @@ -189,10 +190,10 @@ export function renderConfigForm(props: ConfigFormProps) { if (filteredEntries.length === 0) { return html`
    -
    🔍
    +
    ${icons.search}
    - ${searchQuery - ? `No settings match "${searchQuery}"` + ${searchQuery + ? `No settings match "${searchQuery}"` : "No settings in this section"}
    diff --git a/ui/src/ui/views/config-form.shared.ts b/ui/src/ui/views/config-form.shared.ts index b37969a9352..a6a8e241608 100644 --- a/ui/src/ui/views/config-form.shared.ts +++ b/ui/src/ui/views/config-form.shared.ts @@ -89,4 +89,3 @@ export function isSensitivePath(path: Array): boolean { key.endsWith("key") ); } - diff --git a/ui/src/ui/views/config-form.ts b/ui/src/ui/views/config-form.ts index 0bcfe0a9ce9..146436ea6e3 100644 --- a/ui/src/ui/views/config-form.ts +++ b/ui/src/ui/views/config-form.ts @@ -5,4 +5,3 @@ export { } from "./config-form.analyze"; export { renderNode } from "./config-form.node"; export { schemaType, type JsonSchema } from "./config-form.shared"; - diff --git a/ui/src/ui/views/config.ts b/ui/src/ui/views/config.ts index c45849d56bc..ff6f57f3208 100644 --- a/ui/src/ui/views/config.ts +++ b/ui/src/ui/views/config.ts @@ -138,7 +138,7 @@ function computeDiff( ): Array<{ path: string; from: unknown; to: unknown }> { if (!original || !current) return []; const changes: Array<{ path: string; from: unknown; to: unknown }> = []; - + function compare(orig: unknown, curr: unknown, path: string) { if (orig === curr) return; if (typeof orig !== typeof curr) { @@ -164,7 +164,7 @@ function computeDiff( compare(origObj[key], currObj[key], path ? `${path}.${key}` : key); } } - + compare(original, current, ""); return changes; } @@ -258,7 +258,7 @@ export function renderConfig(props: ConfigProps) {
    Settings
    ${validity}
    - + - + - + - +
    @@ -358,7 +358,7 @@ export function renderConfig(props: ConfigProps) {
    - + ${hasChanges && props.formMode === "form" ? html`
    diff --git a/ui/src/ui/views/markdown-sidebar.ts b/ui/src/ui/views/markdown-sidebar.ts index 4189efef318..828c524a346 100644 --- a/ui/src/ui/views/markdown-sidebar.ts +++ b/ui/src/ui/views/markdown-sidebar.ts @@ -1,6 +1,7 @@ import { html, nothing } from "lit"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; +import { icons } from "../icons"; import { toSanitizedMarkdownHtml } from "../markdown"; export type MarkdownSidebarProps = { @@ -16,7 +17,7 @@ export function renderMarkdownSidebar(props: MarkdownSidebarProps) {