openvibers/openvibe.live Commits

Live after the switch to PostgreSQL: N-1 fixtures re-recorded from 81dc337 (the release now in production, so the N-1 SQL replay runs again); the docs say PostgreSQL (#47)

OP OpenVibers committed 8 Oct 2026 · 96f6bf5543 · 9 files, +414 −1431

AGENTS.md, README, SETUP, architecture and deploy describe ov_live, migrations/ and the PGlite development database instead of live.db, schema.sql, migrations.js and init-db; the identity contract step is recorded as done (no email, only SSO and anonymous markers in password_hash on ov_live). better-sqlite3 becomes a dev dependency (only the N-1 tooling reads SQLite now) and the stale seed script goes.


Claude-Session: https://claude.ai/code/session_01K8AYn3FF3HE7CPfM262qdh

Co-authored-by: Claude Opus 5.5 <[email protected]>

Changes

ddiff --git a/AGENTS.md b/AGENTS.md
iindex ecfcbf2e..55b9e4f6 100644
---- a/AGENTS.md
++++ b/AGENTS.md
@@@ -2,7 +2,7 @@
## Project Overview
--Self-hosted live streaming platform — Node.js/Express monolith, vanilla JS SPA frontend, SQLite (better-sqlite3). Part of the **OpenVibe network** with SSO via openvibe.network. See [README.md](README.md) for features and [docs/architecture.md](docs/architecture.md) for system design.
++Self-hosted live streaming platform — Node.js/Express monolith, vanilla JS SPA frontend, PostgreSQL (`openvibe-sdk/db`; embedded PGlite in development and tests; on PostgreSQL since 2026-10-08). Part of the **OpenVibe network** with SSO via openvibe.network. See [README.md](README.md) for features and [docs/architecture.md](docs/architecture.md) for system design.
**Network context:** SSO/OAuth2 + the OpenCoins wallet come from **OpenVibe.Network** (`server/monetization/wallet-client.js`). The media subsystem (VODs/clips/pastes/thumbnails/files) lives in **OpenVibe.Media** — Live talks to it via [server/media-client.js](server/media-client.js), the thin proxy routers in [server/media-proxy/](server/media-proxy/), and the Media-backed recorder [server/streaming/recorder.js](server/streaming/recorder.js). Media completion events arrive over OpenVibe.Events at `POST /internal/media-events` (`MEDIA_EVENTS_AUTHORITY`; the older `POST /internal/media-webhook` stays during the transition). The legacy local `vods`/`clips`/`pastes`/`paste_likes`/`paste_comments` tables are FROZEN: never read or write them (`test/frozen-tables.test.js` enforces it; ask Media or Community through `server/media-proxy/lookups.js`, `media-client.js`, `pastes-client.js`); VOD/clip comments are OpenVibe.Community threads ([server/comments-client.js](server/comments-client.js)) and the local `comments` table is READ-ONLY too; Chat owns every chat table — the staged set (`channel_moderators`, `channel_moderation_settings`, `emotes`, `user_tags`, `chat_ai_summaries`, `chat_timeline_events`; Live reads those only through `server/chat/moderation-client.js`, capability `chat.moderation.read`, cached 30 s, and the summaries through `server/chat/insight-client.js`, cached 60 s) and the twelve the chat cutover imports covered (`chat_messages`, the DM tables, `tts_voice_overrides`, `channel_sounds`, `relay_users`, `hidden_relay_users`, `pending_ip_messages`, `stream_first_chats`, `moderation_actions`); Live keeps no copy of any of them since 2026-10-05 — chat stats, queues, history, first-chat and DM-block state all come through `server/chat/chat-reads.js`, and the twelve tables are dropped at boot by migration `007_drop_chat_tables` (`test/chat-tables-dropped.test.js` guards that nothing under `server/` names them); Live-owned AI/transcript state for Media-hosted content lives in `vod_ai_state`/`clip_ai_state`. See ../CONTRACTS.md for the binding inter-service contracts. **Money:** `BILLING_AUTHORITY` (read only in [server/monetization/money-authority.js](server/monetization/money-authority.js)) is `live` by default; `billing` makes OpenVibe.Billing the ledger ([billing-client.js](server/monetization/billing-client.js), [billing-actions.js](server/monetization/billing-actions.js)) and Live's `openvibe_bucks_*`/`transactions`/`payment_orders`/`subscriptions` read-only (a database-level tripwire throws). The owner-only `money_writes_frozen` flag (`/api/admin/money`) refuses money actions in both modes.
@@@ -11,10 +11,10 @@ Self-hosted live streaming platform — Node.js/Express monolith, vanilla JS SPA
```bash
npm run dev # Start dev server (NODE_ENV=development)
npm start # Start production server (node server/index.js)
--npm run init-db # Initialize database from schema.sql
--npm run seed # Seed sample data
++# The schema is migrations/NNNN_*.sql, applied at boot (DATABASE_DIRECT_URL, owner); without DATABASE_URL, development uses PGlite under DATA_DIR
node --check <file.js> # Syntax check (no linter configured)
--npm test # All unit/security/migration/deploy tests + size budgets (test/run.js)
++npm test # All unit/security/migration/deploy tests + size budgets (test/run.js), on PGlite
++npm run test:pg # The same on the PostgreSQL + PgBouncer containers (openvibe-sdk scripts/test-services.sh up)
node test/<file>.test.js # One test
BASE=http://127.0.0.1:3000 npm run test:browser # Browser smoke (running server + Chrome); add -- --a11y for axe (WCAG 2.1 AA)
```
@@@ -27,7 +27,7 @@ BASE=http://127.0.0.1:3000 npm run test:browser # Browser smoke (running server
- **Entry:** [server/index.js](server/index.js) — Express app, middleware, route mounting, WS upgrade handler, sub-service init
- **Config:** [server/config.js](server/config.js) reads `.env` ([.env.example](.env.example))
--- **Database:** [server/db/database.js](server/db/database.js) — all queries, schema in [server/db/schema.sql](server/db/schema.sql)
++- **Database:** [server/db/database.js](server/db/database.js) — all queries (async, `openvibe-sdk/db`), schema in [migrations/](migrations/) (`0001_analytics.sql`, `0002_live.sql`)
- **Auth:** [server/auth/auth.js](server/auth/auth.js) — RS256 JWT from openvibe.tools SSO + `hbt_` API tokens
- **Permissions:** [server/auth/permissions.js](server/auth/permissions.js) — role hierarchy: `user < streamer < global_mod < admin`
- **Frontend shell:** [public/index.html](public/index.html) — navbar, home page and empty `<section id="page-*">` shells; routing via `history.pushState` in [public/js/app.js](public/js/app.js)
@@@ -39,14 +39,14 @@ Each feature lives in its own `server/<feature>/` directory with `routes.js` + s
- **CommonJS** (`require`/`module.exports`) everywhere. No ES modules except dynamic `import()` for mediasoup-client.
- **Style:** 4-space indent, single quotes, semicolons. No linter/formatter configured.
--- **Naming:** `camelCase` for JS, `snake_case` for SQLite columns/tables.
--- **DB access:** Direct `better-sqlite3` calls in `database.js` (e.g., `db.getUserById()`, `db.run()`, `db.get()`, `db.all()`).
++- **Naming:** `camelCase` for JS, `snake_case` for PostgreSQL columns/tables.
++- **DB access:** `database.js` helpers, every one async (`await db.getUserById()`, `await db.run()`, `await db.get()`, `await db.all()`); an INSERT that needs its id says `RETURNING id`. Never leave a write un-awaited when anything after it depends on it: the pool runs un-awaited work out of order (PGlite does not, so only `npm run test:pg` shows it).
- **Auth middleware:** `requireAuth` from `auth.js`. Permission checks via `permissions.js`.
--- **DB migrations:** Idempotent `CREATE … IF NOT EXISTS`/`ADD COLUMN` may stay inline; anything that transforms data goes in [server/db/migrations.js](server/db/migrations.js) (ledger, transaction, `adopt`, `DEFER`).
++- **DB migrations:** a new `migrations/NNNN_name.sql` with its `-- phase: expand|migrate|contract` header (a contract names its expand with `-- after: NNNN` and waits out the N-1 window); never edit one that has run. No table is created at runtime.
- **Public responses:** serialize through [server/web/serializers.js](server/web/serializers.js) — never return raw `managed_streams`/`users` rows.
- **TURN:** ICE lists come from [server/net/turn.js](server/net/turn.js); set `TURN_AUTH_SECRET` (coturn `use-auth-secret`) for short-lived credentials.
- **Outbound fetches of user-chosen URLs:** [server/net/egress.js](server/net/egress.js) only. Background loops: `server/utils/jobs.js`.
--- **Files and restore drills:** every file location comes from [server/paths.js](server/paths.js) (`DATA_DIR`, `DB_PATH`), never a literal `./data`. Anything started at boot or at module load (timers, listeners, sockets, jobs, outbound calls) must stay off under `LIVE_DRILL` ([server/drill.js](server/drill.js)); `test/drill-mode.test.js` fails on any new one.
++- **Files and restore drills:** every file location comes from [server/paths.js](server/paths.js) (`DATA_DIR`), never a literal `./data`; the database is `DATABASE_URL` (drill mode refuses production's `ov_live`). Anything started at boot or at module load (timers, listeners, sockets, jobs, outbound calls) must stay off under `LIVE_DRILL` ([server/drill.js](server/drill.js)); `test/drill-mode.test.js` fails on any new one.
- **WebSocket servers:** Each has `init(server)` and `handleUpgrade(req, socket, head)` methods.
- **Frontend globals:** `currentUser`, `api()`, `navigate()`, `handleLinkClick()`. Cross-component sync via `CustomEvent` (e.g., `openvibe-auth-changed`).
@@@ -59,7 +59,7 @@ Each feature lives in its own `server/<feature>/` directory with `routes.js` + s
- **WebSocket auth lifecycle:** WS connections can start anonymous and upgrade via `join` message. On account switch, the socket must be rebuilt (not just re-joined) — see `openvibe-auth-changed` handling in `chat.js`.
- **openvibe-shared:** Pinned release of OpenVibers/OpenVibe.Shared (`"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/vX.Y.Z"`), served at `/shared/*` from `node_modules`. Change it there and bump the tag; never edit `node_modules`.
- **DM delivery:** Server verifies `dm.isParticipant()` before delivering — always maintain this check.
--- **Schema:** `ensureTables()` functions create tables on first use. Some modules (DMs, arena, etc.) have their own `ensureTables()`.
++- **Schema:** every table is in `migrations/`; `ensureTables()` is gone. The frozen and Chat-owned tables are not in the PostgreSQL schema at all.
## WebSocket Endpoints
@@@ -67,7 +67,7 @@ Each feature lives in its own `server/<feature>/` directory with `routes.js` + s
## Testing
--Standalone Node scripts in `test/` using `assert`, run together by `npm test`. They create temp SQLite databases. Browser smoke: `test/browser/smoke.js` (routes × widths, console errors, overflow, duplicate scripts, resource growth). Always `node --check` modified files before committing.
++Standalone Node scripts in `test/` using `assert`, run together by `npm test`. They run on one migrated PGlite database per run (`test/helpers/pg-preload.mjs`; `npm run test:pg` uses the containers). Browser smoke: `test/browser/smoke.js` (routes × widths, console errors, overflow, duplicate scripts, resource growth). Always `node --check` modified files before committing.
## Documentation
ddiff --git a/README.md b/README.md
iindex ac921fd2..af768edb 100644
---- a/README.md
++++ b/README.md
@@@ -16,7 +16,7 @@ Live is where people go live and watch. It takes a streamer's video in, plays it
- `server/streaming/recorder.js` — Media-backed recorder (RTMP pull + RTP ingest wiring).
- `server/monetization/wallet-client.js` — OpenCoins wallet client (Network internal API).
- `public/` — static browser UI assets (no build step).
--- `data/` — Live-local runtime storage (SQLite, live thumbnails, emotes, song-request cache, analytics).
++- `data/` — Live-local runtime files (live thumbnails, emotes, avatars, song-request cache). The database is PostgreSQL (`DATABASE_URL`, schema in `migrations/`).
- `node_modules/openvibe-shared/` — pinned OpenVibe.Shared release (`"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/vX.Y.Z"`), served at `/shared/*`. Change it in OpenVibe.Shared and bump the tag; never edit `node_modules`.
- `deploy/` — nginx / systemd / fail2ban reference configs.
- `.env.example` — runtime configuration template.
@@@ -44,7 +44,7 @@ The media subsystem lives in **OpenVibe.Media**:
- On stream start, Live creates a VOD in Media and starts ingest — RTMP streams are pulled by Media from `rtmp://127.0.0.1:1935/live/<key>`; WebRTC/WHIP streams are forwarded over RTP to ports Media allocates (UDP 12000-12199); browser MediaRecorder chunks are proxied to Media's chunks endpoints.
- The SPA's existing `/api/vods…`, `/api/clips…`, `/api/pastes…`, `/api/thumbnails/:filename` calls are preserved by thin proxies; big media files 302-redirect to `https://openvibe.media`.
- Media reports `vod.ready` / `clip.ready` (and failures, storage alerts) as `media.*` OpenVibe.Events events (`POST /internal/media-events`) and, during the transition, the direct webhook `POST /internal/media-webhook` (`X-OVMedia-Signature` HMAC) — driving recording state, AI jobs, and clip chat announcements. Each outcome is applied once; `MEDIA_EVENTS_AUTHORITY` picks the path (see [docs/architecture.md](docs/architecture.md#media-outcomes-over-events)).
--- Live-owned AI/transcript state for Media-hosted content lives in `vod_ai_state` / `clip_ai_state` in live.db.
++- Live-owned AI/transcript state for Media-hosted content lives in `vod_ai_state` / `clip_ai_state` in Live's PostgreSQL database.
### Authentication & currencies
@@@ -54,11 +54,11 @@ The media subsystem lives in **OpenVibe.Media**:
### Data storage
--- `data/live.db` — primary SQLite database (users, streams, channel state, AI state). Chat storage is OpenVibe.Chat's.
++- PostgreSQL `ov_live` (since 2026-10-08; `migrations/0001_analytics.sql`, `0002_live.sql`) — users, streams, channel state, AI state, request analytics. Chat storage is OpenVibe.Chat's. The SQLite files it replaced stay read-only next to the release for the rollback window.
- `data/live-thumbs` — ephemeral live-stream thumbnails.
- `data/emotes`, `data/avatars`, `data/offline` — Live-local assets.
- `data/media/cache` — song-request (watch-party) downloads.
--- `data/analytics.db` — request analytics (ADR-021, `openvibe-shared/analytics` since v1.4.0: no IPs or user ids, route templates, raw rows ≤ 30 days; a request with `Sec-GPC: 1` or `DNT: 1` is not recorded at all; `scripts/analytics-prune.js`; see [docs/architecture.md](docs/architecture.md#analytics-adr-021)).
++- request analytics (the `analytics_*` tables; ADR-021, `openvibe-shared/analytics` since v1.4.0: no IPs or user ids, route templates, raw rows ≤ 30 days; a request with `Sec-GPC: 1` or `DNT: 1` is not recorded at all; `scripts/analytics-prune.js`; see [docs/architecture.md](docs/architecture.md#analytics-adr-021)).
- VOD/clip/paste **files** live in OpenVibe.Media's storage.
---
@@@ -70,7 +70,7 @@ The media subsystem lives in **OpenVibe.Media**:
- The watch experience: the SPA in `public/`, the chat surface and its effects (TTS, sounds, emotes, overlays), anonymous chat identities.
- Streamer tools: the dashboard, restreaming (via OpenRe for managed ingest), song requests (watch party), AI viewers (the context Live builds; the runs are OpenVibe.AI's).
- Channel points, Vibes tipping flows (the ledger moves to OpenVibe.Billing when `BILLING_AUTHORITY=billing`), moderation of Live's own surfaces, the Arena (Battle Cam) and after-show recaps.
--- Live-local state: `data/live.db` (users' Live profiles, streams, channel state, AI state for Media-hosted recordings in `vod_ai_state` / `clip_ai_state`) and `data/analytics.db`.
++- Live-local state: PostgreSQL `ov_live` (users' Live profiles, streams, channel state, AI state for Media-hosted recordings in `vod_ai_state` / `clip_ai_state`, request analytics).
## Does not own
@@@ -135,7 +135,7 @@ See [SECURITY.md](SECURITY.md) and [SECURITY_AUDIT.md](SECURITY_AUDIT.md). The m
- `npm install` — install dependencies.
- `npm start` — start the server.
- `npm run dev` — start in development mode (`NODE_ENV=development`).
--- `npm run init-db` — initialize the SQLite database schema.
++- `npm run test:pg` — the tests on the PostgreSQL + PgBouncer containers.
- `npm test` — every test in `test/` (see Acceptance).
---
@@@ -146,8 +146,7 @@ Requirements: Node.js 22, npm, FFmpeg, a running OpenVibe.Network, and (for medi
```bash
npm install
--cp .env.example .env # then edit — see SETUP.md
--npm run init-db
++cp .env.example .env # then edit — see SETUP.md (no DATABASE_URL: an embedded PGlite database under DATA_DIR)
npm run dev
```
ddiff --git a/SETUP.md b/SETUP.md
iindex 2bec8666..d5688d37 100644
---- a/SETUP.md
++++ b/SETUP.md
@@@ -9,7 +9,7 @@ OpenVibe.Live (port **3000**) is the streaming front — ingest (RTMP / WHIP / W
- **OpenVibe.Network** (`openvibe.network`, port **4000**) — SSO/OAuth2 identity provider (client id `live`), RS256 JWTs, URL registry, notifications, and the network-wide **OpenCoins wallet**. Live calls the wallet server-to-server via `OV_NETWORK_INTERNAL_URL` with its service token (client credentials).
- **OpenVibe.Media** (`openvibe.media`, port **4100**) — owns **VODs, clips, pastes, thumbnails, and file storage** (Media API v1). Live records streams by pointing Media at its ingest (RTMP pull / RTP ports 12000-12199), proxies the SPA's `/api/vods`, `/api/clips`, `/api/pastes`, `/api/thumbnails` calls to it (`server/media-client.js`), 302-redirects file payloads to `MEDIA_PUBLIC_URL`, and receives `vod.ready` / `clip.ready` webhooks at `POST /internal/media-webhook`.
--Live keeps locally: live.db (users/streams/chat/channel state), Vibes (PayPal tipping/cashout), channel points, comments, stream memories + AI state for Media-hosted vods/clips (`vod_ai_state` / `clip_ai_state`), ephemeral live thumbnails, and the song-request queue.
++Live keeps locally, in its PostgreSQL database: users/streams/channel state, Vibes (PayPal tipping/cashout), channel points, comments, stream memories + AI state for Media-hosted vods/clips (`vod_ai_state` / `clip_ai_state`), ephemeral live thumbnails, and the song-request queue.
The shared package is a pinned OpenVibe.Shared release (`"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/vX.Y.Z"` in package.json), installed into `node_modules` by `npm ci` (the host needs outbound HTTPS to codeload.github.com) and served at `/shared/*`. Change it in OpenVibe.Shared and bump the tag; never edit `node_modules`.
@@@ -60,15 +60,14 @@ OpenVibe.Live verifies RS256 tokens from OpenVibe.Network using one of these pat
If the public key cannot be loaded, authentication will fail.
--## Database initialization
++## Database
--```bash
--npm run init-db
--```
--
--This creates the SQLite database at `data/live.db` from `server/db/schema.sql`. On normal startup, `server/index.js` also runs lightweight migrations (including the `vod_ai_state` / `clip_ai_state` tables), so existing databases recover automatically.
++Live runs on PostgreSQL (since 2026-10-08). In production, OpenVibe.Host's data role creates the database and roles
++(`sudo ovhost data provision live`) and writes `DATABASE_URL` (the pooled runtime role, through PgBouncer) and
++`DATABASE_DIRECT_URL` (the owner, for migrations) into the env file; the release applies `migrations/` at boot.
--Note: the legacy `vods` / `clips` / `pastes` tables may still exist for the Media data migration to read — Live never writes them anymore.
++In development, leave `DATABASE_URL` unset: Live opens an embedded PGlite database under `DATA_DIR` and migrates it.
++Nothing to initialize by hand.
## Storage directories
@@@ -77,7 +76,7 @@ The server writes Live-local state under `data/`:
- `data/live-thumbs` — ephemeral live-stream thumbnails
- `data/emotes`, `data/avatars`, `data/offline`
- `data/media/cache` — song-request (watch-party) downloads
--- `data/analytics.db`, `data/keys`
++- `data/keys`
VOD/clip/paste **files** live in OpenVibe.Media's storage, not here.
@@@ -100,7 +99,7 @@ MEDIA_APP_ID=live
MEDIA_WEBHOOK_SECRET=dev-webhook-secret
```
--3. Run `npm run init-db`, then `npm run dev`.
++3. Run `npm run dev` (without `DATABASE_URL`, Live migrates an embedded PGlite database under `DATA_DIR`).
Network's `local-dev` bootstrap seeds the `live` OAuth client with `http://localhost:3000/api/auth/callback`.
@@@ -167,7 +166,7 @@ Completion arrives via the `vod.ready` / `clip.ready` webhooks.
1. Run `npm run dev`.
2. Confirm the server logs show `HTTP server` and WebSocket endpoints.
3. Confirm `Effective BASE_URL` and `Effective OV_NETWORK_URL` are correct.
--4. Confirm the server created or migrated `data/live.db`.
++4. Confirm `/api/ready` reports the database check `ok` (the release migrated it at boot).
5. `curl http://localhost:3000/api/health` returns `{"status":"ok", ...}`.
## What this setup does not cover
ddiff --git a/docs/architecture.md b/docs/architecture.md
iindex 352c383a..7c7cfe65 100644
---- a/docs/architecture.md
++++ b/docs/architecture.md
@@@ -5,7 +5,7 @@
OpenVibe.Live is a self-hosted live streaming platform, one of the OpenVibe services:
- **Express.js** (CommonJS) — HTTP API, HTML documents, static assets
--- **SQLite** (better-sqlite3) — users, channels, streams, chat, tokens, AI state (`data/live.db`)
++- **PostgreSQL** (`openvibe-sdk/db`, through PgBouncer; PGlite in development and tests) — users, channels, streams, tokens, AI state, request analytics (`ov_live`, since 2026-10-08)
- **WebSocket** (`ws`) — chat, broadcast signaling, calls, hardware controls
- **mediasoup** — WebRTC SFU; **werift** — WHIP ingest; **Node-Media-Server** — RTMP ingest
- **FFmpeg** — recording hand-off, restreams, thumbnails, audio capture for transcription
@@@ -30,10 +30,9 @@ OpenVibe.Live is a self-hosted live streaming platform, one of the OpenVibe serv
`users.role` is that projection's output (Network's staff roles, plus Live's own `streamer`) and is
read locally. The key-only `POST /internal/user-role` push is gone.
- Live keeps no emails or passwords: nothing writes or reads `users.email` or `users.password_hash`
-- (`test/identity-columns.test.js`). The contract step `scripts/identity-columns-contract.js`
-- (a dry run by default; `--apply` backs up `live.db` first, never runs on deploy) clears the legacy
-- values: every email, and the old hash of every account linked to a Network subject. Accounts with
-- no Network identity keep theirs for a future claim flow. The columns themselves stay.
++ (`test/identity-columns.test.js`). The contract step cleared the legacy values before the switch to
++ PostgreSQL: no row holds an email, and `password_hash` holds only the SSO and anonymous markers (checked
++ on `ov_live` on 2026-10-08); its SQLite script went with the SQLite code. The columns themselves stay.
## Frontend loading
@@@ -154,26 +153,27 @@ updating" and refills history on reconnect. See [chat-system.md](chat-system.md)
| Limits | `server/utils/limit.js` | semaphores for CPU-heavy work (offline encodes, stream-memory captures) |
| Diagnostics | `server/diagnostics.js` | event-loop delay, memory, sockets, jobs, queues, migrations → `GET /api/admin/diagnostics` |
| Log redaction | `server/utils/redact.js` | stream keys and credentials in log lines |
--| Database | `server/db/database.js` | queries and inline table setup |
--| Migrations | `server/db/migrations.js` | versioned, transactional migrations with a ledger (`schema_migrations`) |
++| Database | `server/db/database.js` | every query, async (`openvibe-sdk/db`) |
++| Migrations | `migrations/NNNN_*.sql` | the schema, applied at boot by the owner role (`openvibe-sdk/db` migrate: ledger, advisory lock, expand/migrate/contract phases) |
| Chat delivery | `server/chat/chat-delivery.js` | Live's one seam to OpenVibe.Chat (pushes, moderation, cache hints, presence) |
| Streams | `server/streaming/routes.js` | stream and slot CRUD, channel pages |
| Media proxy | `server/media-proxy/*.js` | VODs, clips, pastes, thumbnails via OpenVibe.Media |
| Auth | `server/auth/auth.js`, `server/auth/permissions.js` | JWT/API tokens, scopes, role ranks |
--| Analytics | `openvibe-shared/analytics` | request analytics in `data/analytics.db` within ADR-021 (see [Analytics](#analytics-adr-021)) |
++| Analytics | `server/analytics/store.js` | request analytics in the `analytics_*` tables within ADR-021 (see [Analytics](#analytics-adr-021)) |
### Migrations
--Schema changes that are not idempotent `CREATE … IF NOT EXISTS` go into `server/db/migrations.js`.
--Each migration runs once, inside a transaction that records it; `adopt()` marks databases that already
--have the change; a migration waiting for a table another module creates returns `DEFER` and is retried.
--A failing `critical` migration stops the boot. `test/migrations.test.js` covers fresh, repeated,
--adopted, failing and deferred cases.
++The schema is `migrations/NNNN_*.sql`, applied at boot on the owner's direct connection (`DATABASE_DIRECT_URL`) before
++the service serves on the pooled runtime role (`DATABASE_URL`, PgBouncer in transaction mode). Each file names its
++phase (`expand`, `migrate`, `contract`); a contract migration names the expand it completes and waits out the N-1
++window. A file that has run is never edited: the next change is a new file. `0002_live.sql` was generated from
++production's SQLite schema at the switch to PostgreSQL (2026-10-08) and carries the compatibility functions the queries
++use (`datetime()`, `julianday()`, `ov_now()`).
### Analytics (ADR-021)
`openvibe-shared/analytics` (the one ADR-021 module for Live, Tools and Network, wired in `server/index.js`)
--records one row per finished request in `data/analytics.db` (separate from `live.db`)
++records one row per finished request in the `analytics_*` tables of Live's PostgreSQL database
and rolls rows up hourly and daily for the Network admin dashboards. What a raw row may carry is bound by
ADR-021 (OpenVibe.Contracts `docs/adr/ADR-021-analytics.md`):
@@@ -195,17 +195,10 @@ ADR-021 (OpenVibe.Contracts `docs/adr/ADR-021-analytics.md`):
hour. Rollups keep counts only, and a recompute never lowers a stored unique count. Raw-event
dashboards (sub-48 h summaries, realtime, top pages) count distinct sessions instead of IPs; "new vs
returning visitors" and daily `new_users` are no longer measured (NULL).
--- **Retention:** raw events older than 30 days are deleted nightly in batches of 5000 (job
-- `analytics-prune`, `retention.pruneRawEvents` from `openvibe-shared/analytics`); rollups are kept.
++- **Retention:** raw events older than 30 days are deleted nightly in batches (job `analytics-prune`,
++ `pruneRawEventsPg` in `server/analytics/store.js`); rollups are kept.
- **Opt-out:** a request with `Sec-GPC: 1` or `DNT: 1` is not recorded at all: no raw row, visitor hash,
session id or rate counter, so it is also missing from the rollups.
--- **Operator CLI:** `scripts/analytics-prune.js`, a wrapper over `openvibe-shared/analytics/prune-cli` — dry
-- run by default (counts only). `--apply` needs `--backup <new file>` (verified sqlite online backup,
-- owner-only) or an explicit `--no-backup`; `--scrub` also
-- rewrites rows written before ADR-021 (personal columns → NULL, path → template, referer → origin, user
-- agent → class, legacy session ids → NULL) and the rollups' top-path/referer lists (counts unchanged).
-- Rollup totals are compared before and after; the run ends with a VACUUM unless `--no-vacuum`.
-- Space: the backup needs about the size of `analytics.db` + its WAL; VACUUM about twice the size.
## OpenVibe Integration
@@@ -217,7 +210,7 @@ inter-service contracts are in `../CONTRACTS.md` (OpenVibers workspace). Deploym
### Durable events (OpenVibe.Events)
Live publishes through one transactional outbox (`event_outbox`, [server/events/stream-events.js](../server/events/stream-events.js);
--off unless `EVENTS_URL` and `OV_OAUTH_CLIENT_SECRET` are set). Every event is queued in the same SQLite
++off unless `EVENTS_URL` and `OV_OAUTH_CLIENT_SECRET` are set). Every event is queued in the same PostgreSQL
transaction as the change it describes.
| event | when | subject |
ddiff --git a/docs/deploy.md b/docs/deploy.md
iindex 63cc1cd0..ba20b2ce 100644
---- a/docs/deploy.md
++++ b/docs/deploy.md
@@@ -31,7 +31,7 @@ refuses a frozen service (`ovhost freeze`, exit 6). What ovhost checks is in Ope
| README, tests, scripts | files switched | none |
| `server/`, `vendor/`, `package.json` dependencies | restart, gated on `GET /api/ready` | new HTTP connections queue on the systemd socket; **established WebSocket, WHIP, WebRTC and RTMP sessions drop and reconnect** |
| `package-lock.json` | `npm ci` **before** anything is interrupted (release layout: into the new release) | as server |
--| `server/db/migrations.js`, `schema.sql` | online SQLite backup to `data/backups/`, then restart | as server |
++| `migrations/` | a database backup (`ovhost backup live`), then restart; the release applies them at boot | as server |
| `deploy/systemd/` | units installed, `daemon-reload`, restart | as server |
| `deploy/nginx/` | **not installed** — the live nginx config is managed separately (see below); the script prints a notice | none |
@@@ -56,8 +56,9 @@ but could not restore previous `node_modules`.
/opt/openvibe.live/repo git clone used to create releases
/opt/openvibe.live/releases/<time>-<sha> worktree + its own node_modules + data -> ../../shared/data
/opt/openvibe.live/current -> releases/<id> (atomic rename)
--/opt/openvibe.live/shared/data live.db, analytics.db, uploads (never copied or reset)
--/opt/openvibe.live/data -> shared/data (old absolute DB_PATH values keep working)
++/opt/openvibe.live/shared/data uploads and runtime files (never copied or reset); live.db and analytics.db stay
++ read-only there since the switch to PostgreSQL (2026-10-08), the rollback copy
++/opt/openvibe.live/data -> shared/data (old absolute data paths keep working)
```
- A deploy builds the new release while the old one serves; unchanged lockfiles hard-link
@@@ -114,12 +115,14 @@ Apply nginx changes by hand: copy the file, `nginx -t`, `systemctl reload nginx`
## Restore drills
--`ovhost drill live` (OpenVibe.Host, `docs/restore-drills.md`) restores `live.db` from the latest
--backup into a directory of its own and starts a second Live from this checkout on 127.0.0.1:13000
--with `LIVE_DRILL=1`, `DB_PATH` on the copy and `DATA_DIR` in that directory. In that mode
++`ovhost drill live` (OpenVibe.Host, `docs/restore-drills.md`) restores the latest backup of `ov_live` into a database of
++its own and starts a second Live from this checkout on 127.0.0.1:13000 with `LIVE_DRILL=1`, `DATABASE_URL` and
++`DATABASE_DIRECT_URL` on the copy and `DATA_DIR` in a directory of its own (without a database URL it uses an embedded
++PGlite database under `DATA_DIR`). In that mode
(`server/drill.js`) Live:
--- refuses to start unless `DB_PATH` and `DATA_DIR` are set and outside the checkout and
++- refuses to start when a database URL names production's `ov_live` (or names no database, or only one of the two
++ URLs is set), and unless `DATA_DIR` is set and outside the checkout and
`/opt/openvibe.live`, `HOST` is loopback, `PORT` is not 3000 and no socket was handed over by systemd;
- writes nothing outside `DATA_DIR` and the copy's directory (`server/paths.js`: the per-location
`*_PATH` variables from the env file are ignored);
ddiff --git a/package-lock.json b/package-lock.json
iindex 9e3b024c..323d2eac 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -11,7 +11,6 @@
"dependencies": {
"@roamhq/wrtc": "^0.8.0",
"bcryptjs": "^2.4.3",
-- "better-sqlite3": "^11.3.0",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
@@@ -37,7 +36,8 @@
"ws": "^8.18.0"
},
"devDependencies": {
-- "@electric-sql/pglite": "^0.5.8"
++ "@electric-sql/pglite": "^0.5.8",
++ "better-sqlite3": "^11.3.0"
},
"engines": {
"node": ">=18.0.0"
@@@ -969,6 +969,7 @@
"version": "11.10.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",
"integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==",
++ "devOptional": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@@ -980,6 +981,7 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"file-uri-to-path": "1.0.0"
@@@ -989,6 +991,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"buffer": "^5.5.0",
@@@ -1000,6 +1003,7 @@
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
@@@ -1038,6 +1042,7 @@
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
++ "devOptional": true,
"funding": [
{
"type": "github",
@@@ -1309,6 +1314,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
@@@ -1324,6 +1330,7 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
++ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=4.0.0"
@@@ -1547,6 +1554,7 @@
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
@@@ -1635,6 +1643,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
++ "devOptional": true,
"license": "(MIT OR WTFPL)",
"engines": {
"node": ">=6"
@@@ -1762,6 +1771,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
++ "devOptional": true,
"license": "MIT"
},
"node_modules/finalhandler": {
@@@ -1822,6 +1832,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
++ "devOptional": true,
"license": "MIT"
},
"node_modules/function-bind": {
@@@ -1899,6 +1910,7 @@
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
++ "devOptional": true,
"license": "MIT"
},
"node_modules/global-agent": {
@@@ -2133,6 +2145,7 @@
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
++ "devOptional": true,
"license": "ISC"
},
"node_modules/ip-address": {
@@@ -2535,6 +2548,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
++ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@@ -2589,6 +2603,7 @@
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
++ "devOptional": true,
"license": "MIT"
},
"node_modules/ms": {
@@@ -2651,6 +2666,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
++ "devOptional": true,
"license": "MIT"
},
"node_modules/negotiator": {
@@@ -2666,6 +2682,7 @@
"version": "3.94.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.94.0.tgz",
"integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"semver": "^7.3.5"
@@@ -2796,6 +2813,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
++ "devOptional": true,
"license": "ISC",
"dependencies": {
"wrappy": "1"
@@@ -3075,6 +3093,7 @@
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
"deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^2.0.0",
@@@ -3124,6 +3143,7 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
@@@ -3192,6 +3212,7 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
++ "devOptional": true,
"license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
"dependencies": {
"deep-extend": "^0.6.0",
@@@ -3494,6 +3515,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
++ "devOptional": true,
"funding": [
{
"type": "github",
@@@ -3514,6 +3536,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
++ "devOptional": true,
"funding": [
{
"type": "github",
@@@ -3598,6 +3621,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
++ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@@ -3635,6 +3659,7 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz",
"integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"chownr": "^1.1.1",
@@@ -3647,12 +3672,14 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
++ "devOptional": true,
"license": "ISC"
},
"node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"bl": "^4.0.3",
@@@ -3669,6 +3696,7 @@
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
++ "devOptional": true,
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
@@@ -3731,6 +3759,7 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
++ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
@@@ -3913,6 +3942,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
++ "devOptional": true,
"license": "ISC"
},
"node_modules/ws": {
ddiff --git a/package.json b/package.json
iindex 38c0ebe3..22ef1e10 100644
---- a/package.json
++++ b/package.json
@@@ -1,12 +1,11 @@
{
"name": "live",
"version": "1.0.0",
-- "description": "OpenVibe.Live — free & open live streaming. Part of the OpenVibe network (media by OpenVibe.Media, identity by OpenVibe.Network).",
++ "description": "OpenVibe.Live \u2014 free & open live streaming. Part of the OpenVibe network (media by OpenVibe.Media, identity by OpenVibe.Network).",
"main": "server/index.js",
"scripts": {
"start": "node server/index.js",
"dev": "NODE_ENV=development node server/index.js",
-- "seed": "node server/db/seed.js",
"test": "node test/run.js",
"test:unit": "node test/run.js",
"test:pg": "LIVE_TEST_STORE=pg node test/run.js",
@@@ -30,7 +29,6 @@
"dependencies": {
"@roamhq/wrtc": "^0.8.0",
"bcryptjs": "^2.4.3",
-- "better-sqlite3": "^11.3.0",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
@@@ -59,6 +57,7 @@
"node": ">=18.0.0"
},
"devDependencies": {
-- "@electric-sql/pglite": "^0.5.8"
++ "@electric-sql/pglite": "^0.5.8",
++ "better-sqlite3": "^11.3.0"
}
}
ddiff --git a/test/fixtures/n-1/client.json b/test/fixtures/n-1/client.json
iindex d98e71f3..8f81b8d9 100644
---- a/test/fixtures/n-1/client.json
++++ b/test/fixtures/n-1/client.json
@@@ -1,17 +1,17 @@
{
"service": "live",
-- "release": "b92d5c4027c038dc919746ed7c805fdcdc1926c9",
-- "recorded_at": "2026-10-08T14:20:39.563Z",
++ "release": "81dc337dc7ae261ccc498f5894241f1f0e9cc52f",
++ "recorded_at": "2026-10-08T18:28:45.009Z",
"note": "N-1 fixture: written by `npm run n-1:record`, replayed by test/n-1.test.js",
"manifest": {
"service": "live",
-- "release": "b92d5c4027c0",
-- "released_at": "2026-10-08T14:15:10.000Z",
-- "booted_at": "2026-10-08T14:20:37.366Z",
++ "release": "81dc337dc7ae",
++ "released_at": "2026-10-08T17:34:39.000Z",
++ "booted_at": "2026-10-08T18:28:40.404Z",
"contracts_version": "0.112.0",
"packages": {
-- "openvibe-shared": "2.13.0",
-- "openvibe-sdk": "0.35.0"
++ "openvibe-shared": "2.13.3",
++ "openvibe-sdk": "0.35.1"
},
"min_client_release": null,
"mixed_version_window_hours": 24,
@@@ -22,11 +22,11 @@
},
"shell": {
"kind": "script",
-- "version": "4bf4ac2b35d5"
++ "version": "2fb9f8dc218c"
},
"server": {
"kind": "server",
-- "version": "3a786b737f27"
++ "version": "6175874d3913"
}
},
"assets": {
@@@ -2540,8 +2540,6 @@
"live_stream.id": "number",
"live_stream.user_id": "number",
"live_stream.channel_id": "null",
-- "live_stream.managed_stream_id": "number",
-- "live_stream.control_config_id": "null",
"live_stream.title": "string",
"live_stream.description": "string",
"live_stream.category": "string",
@@@ -2557,11 +2555,14 @@
"live_stream.ended_at": "null",
"live_stream.duration_seconds": "number",
"live_stream.created_at": "string",
++ "live_stream.call_mode": "null",
++ "live_stream.control_config_id": "null",
++ "live_stream.managed_stream_id": "number",
"live_stream.ai_overview": "null",
++ "live_stream.ai_overview_short": "null",
"live_stream.ai_title": "null",
"live_stream.ai_category": "null",
"live_stream.ai_tags": "null",
-- "live_stream.ai_overview_short": "null",
"live_stream.username": "string",
"live_stream.display_name": "string",
"live_stream.avatar_url": "null",
@@@ -2617,8 +2618,6 @@
"live_stream.id": "number",
"live_stream.user_id": "number",
"live_stream.channel_id": "null",
-- "live_stream.managed_stream_id": "number",
-- "live_stream.control_config_id": "null",
"live_stream.title": "string",
"live_stream.description": "string",
"live_stream.category": "string",
@@@ -2634,11 +2633,14 @@
"live_stream.ended_at": "null",
"live_stream.duration_seconds": "number",
"live_stream.created_at": "string",
++ "live_stream.call_mode": "null",
++ "live_stream.control_config_id": "null",
++ "live_stream.managed_stream_id": "number",
"live_stream.ai_overview": "null",
++ "live_stream.ai_overview_short": "null",
"live_stream.ai_title": "null",
"live_stream.ai_category": "null",
"live_stream.ai_tags": "null",
-- "live_stream.ai_overview_short": "null",
"live_stream.username": "string",
"live_stream.display_name": "string",
"live_stream.avatar_url": "null",
@@@ -3498,8 +3500,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -3515,11 +3515,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -3539,25 +3542,25 @@
"streams[].channel.panels": "string",
"streams[].channel.default_vod_visibility": "string",
"streams[].channel.default_clip_visibility": "string",
++ "streams[].channel.created_at": "string",
++ "streams[].channel.updated_at": "string",
"streams[].channel.weather_detail": "string",
"streams[].channel.weather_show_location": "number",
"streams[].channel.control_mode": "string",
"streams[].channel.anon_controls_enabled": "number",
"streams[].channel.control_rate_limit_ms": "number",
++ "streams[].channel.active_control_config_id": "null",
"streams[].channel.video_click_enabled": "number",
"streams[].channel.video_click_rate_limit_ms": "number",
-- "streams[].channel.active_control_config_id": "null",
-- "streams[].channel.created_at": "string",
-- "streams[].channel.updated_at": "string",
-- "streams[].channel.clips_allow_creator_delete": "number",
-- "streams[].channel.hide_ai_overview": "number",
-- "streams[].channel.ai_overview_pref": "string",
-- "streams[].channel.ai_derivation_enabled": "number",
-- "streams[].channel.ai_category": "null",
"streams[].channel.offline_screen_type": "string",
"streams[].channel.offline_screen_url": "null",
"streams[].channel.offline_html": "null",
"streams[].channel.offline_css": "null",
++ "streams[].channel.clips_allow_creator_delete": "number",
++ "streams[].channel.hide_ai_overview": "number",
++ "streams[].channel.ai_overview_pref": "string",
++ "streams[].channel.ai_category": "null",
++ "streams[].channel.ai_derivation_enabled": "number",
"streams[].channel.social_links": "null",
"streams[].external_viewer_count": "number",
"streams[].total_viewer_count": "number"
@@@ -3578,8 +3581,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -3595,11 +3596,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -3619,25 +3623,25 @@
"streams[].channel.panels": "string",
"streams[].channel.default_vod_visibility": "string",
"streams[].channel.default_clip_visibility": "string",
++ "streams[].channel.created_at": "string",
++ "streams[].channel.updated_at": "string",
"streams[].channel.weather_detail": "string",
"streams[].channel.weather_show_location": "number",
"streams[].channel.control_mode": "string",
"streams[].channel.anon_controls_enabled": "number",
"streams[].channel.control_rate_limit_ms": "number",
++ "streams[].channel.active_control_config_id": "null",
"streams[].channel.video_click_enabled": "number",
"streams[].channel.video_click_rate_limit_ms": "number",
-- "streams[].channel.active_control_config_id": "null",
-- "streams[].channel.created_at": "string",
-- "streams[].channel.updated_at": "string",
-- "streams[].channel.clips_allow_creator_delete": "number",
-- "streams[].channel.hide_ai_overview": "number",
-- "streams[].channel.ai_overview_pref": "string",
-- "streams[].channel.ai_derivation_enabled": "number",
-- "streams[].channel.ai_category": "null",
"streams[].channel.offline_screen_type": "string",
"streams[].channel.offline_screen_url": "null",
"streams[].channel.offline_html": "null",
"streams[].channel.offline_css": "null",
++ "streams[].channel.clips_allow_creator_delete": "number",
++ "streams[].channel.hide_ai_overview": "number",
++ "streams[].channel.ai_overview_pref": "string",
++ "streams[].channel.ai_category": "null",
++ "streams[].channel.ai_derivation_enabled": "number",
"streams[].channel.social_links": "null",
"streams[].external_viewer_count": "number",
"streams[].total_viewer_count": "number"
@@@ -3699,26 +3703,26 @@
"panels": "string",
"default_vod_visibility": "string",
"default_clip_visibility": "string",
++ "created_at": "string",
++ "updated_at": "string",
"weather_zip": "null",
"weather_detail": "string",
"weather_show_location": "number",
"control_mode": "string",
"anon_controls_enabled": "number",
"control_rate_limit_ms": "number",
++ "active_control_config_id": "null",
"video_click_enabled": "number",
"video_click_rate_limit_ms": "number",
-- "active_control_config_id": "null",
-- "created_at": "string",
-- "updated_at": "string",
-- "clips_allow_creator_delete": "number",
-- "hide_ai_overview": "number",
-- "ai_overview_pref": "string",
-- "ai_derivation_enabled": "number",
-- "ai_category": "null",
"offline_screen_type": "string",
"offline_screen_url": "null",
"offline_html": "null",
"offline_css": "null",
++ "clips_allow_creator_delete": "number",
++ "hide_ai_overview": "number",
++ "ai_overview_pref": "string",
++ "ai_category": "null",
++ "ai_derivation_enabled": "number",
"social_links": "null"
},
"from": [
@@@ -3750,25 +3754,25 @@
"channel.panels": "string",
"channel.default_vod_visibility": "string",
"channel.default_clip_visibility": "string",
++ "channel.created_at": "string",
++ "channel.updated_at": "string",
"channel.weather_detail": "string",
"channel.weather_show_location": "number",
"channel.control_mode": "string",
"channel.anon_controls_enabled": "number",
"channel.control_rate_limit_ms": "number",
++ "channel.active_control_config_id": "null",
"channel.video_click_enabled": "number",
"channel.video_click_rate_limit_ms": "number",
-- "channel.active_control_config_id": "null",
-- "channel.created_at": "string",
-- "channel.updated_at": "string",
-- "channel.clips_allow_creator_delete": "number",
-- "channel.hide_ai_overview": "number",
-- "channel.ai_overview_pref": "string",
-- "channel.ai_derivation_enabled": "number",
-- "channel.ai_category": "null",
"channel.offline_screen_type": "string",
"channel.offline_screen_url": "null",
"channel.offline_html": "null",
"channel.offline_css": "null",
++ "channel.clips_allow_creator_delete": "number",
++ "channel.hide_ai_overview": "number",
++ "channel.ai_overview_pref": "string",
++ "channel.ai_category": "null",
++ "channel.ai_derivation_enabled": "number",
"channel.social_links": "array",
"channel.username": "string",
"channel.display_name": "string",
@@@ -3794,8 +3798,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -3811,11 +3813,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -3830,8 +3835,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -3847,11 +3850,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -3865,7 +3871,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -3877,10 +3883,6 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
"managed_streams[].default_vod_visibility": "string",
@@@ -3888,6 +3890,10 @@
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -3939,25 +3945,25 @@
"channel.panels": "string",
"channel.default_vod_visibility": "string",
"channel.default_clip_visibility": "string",
++ "channel.created_at": "string",
++ "channel.updated_at": "string",
"channel.weather_detail": "string",
"channel.weather_show_location": "number",
"channel.control_mode": "string",
"channel.anon_controls_enabled": "number",
"channel.control_rate_limit_ms": "number",
++ "channel.active_control_config_id": "null",
"channel.video_click_enabled": "number",
"channel.video_click_rate_limit_ms": "number",
-- "channel.active_control_config_id": "null",
-- "channel.created_at": "string",
-- "channel.updated_at": "string",
-- "channel.clips_allow_creator_delete": "number",
-- "channel.hide_ai_overview": "number",
-- "channel.ai_overview_pref": "string",
-- "channel.ai_derivation_enabled": "number",
-- "channel.ai_category": "null",
"channel.offline_screen_type": "string",
"channel.offline_screen_url": "null",
"channel.offline_html": "null",
"channel.offline_css": "null",
++ "channel.clips_allow_creator_delete": "number",
++ "channel.hide_ai_overview": "number",
++ "channel.ai_overview_pref": "string",
++ "channel.ai_category": "null",
++ "channel.ai_derivation_enabled": "number",
"channel.social_links": "array",
"channel.username": "string",
"channel.display_name": "string",
@@@ -3987,8 +3993,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -4004,11 +4008,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -4023,8 +4030,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -4040,11 +4045,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -4058,7 +4066,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -4070,10 +4078,6 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
"managed_streams[].default_vod_visibility": "string",
@@@ -4081,6 +4085,10 @@
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -4132,25 +4140,25 @@
"channel.panels": "string",
"channel.default_vod_visibility": "string",
"channel.default_clip_visibility": "string",
++ "channel.created_at": "string",
++ "channel.updated_at": "string",
"channel.weather_detail": "string",
"channel.weather_show_location": "number",
"channel.control_mode": "string",
"channel.anon_controls_enabled": "number",
"channel.control_rate_limit_ms": "number",
++ "channel.active_control_config_id": "null",
"channel.video_click_enabled": "number",
"channel.video_click_rate_limit_ms": "number",
-- "channel.active_control_config_id": "null",
-- "channel.created_at": "string",
-- "channel.updated_at": "string",
-- "channel.clips_allow_creator_delete": "number",
-- "channel.hide_ai_overview": "number",
-- "channel.ai_overview_pref": "string",
-- "channel.ai_derivation_enabled": "number",
-- "channel.ai_category": "null",
"channel.offline_screen_type": "string",
"channel.offline_screen_url": "null",
"channel.offline_html": "null",
"channel.offline_css": "null",
++ "channel.clips_allow_creator_delete": "number",
++ "channel.hide_ai_overview": "number",
++ "channel.ai_overview_pref": "string",
++ "channel.ai_category": "null",
++ "channel.ai_derivation_enabled": "number",
"channel.social_links": "array",
"channel.username": "string",
"channel.display_name": "string",
@@@ -4176,8 +4184,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -4193,11 +4199,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -4212,8 +4221,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -4229,11 +4236,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -4247,7 +4257,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -4259,10 +4269,6 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
"managed_streams[].default_vod_visibility": "string",
@@@ -4270,6 +4276,10 @@
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -4321,25 +4331,25 @@
"channel.panels": "string",
"channel.default_vod_visibility": "string",
"channel.default_clip_visibility": "string",
++ "channel.created_at": "string",
++ "channel.updated_at": "string",
"channel.weather_detail": "string",
"channel.weather_show_location": "number",
"channel.control_mode": "string",
"channel.anon_controls_enabled": "number",
"channel.control_rate_limit_ms": "number",
++ "channel.active_control_config_id": "null",
"channel.video_click_enabled": "number",
"channel.video_click_rate_limit_ms": "number",
-- "channel.active_control_config_id": "null",
-- "channel.created_at": "string",
-- "channel.updated_at": "string",
-- "channel.clips_allow_creator_delete": "number",
-- "channel.hide_ai_overview": "number",
-- "channel.ai_overview_pref": "string",
-- "channel.ai_derivation_enabled": "number",
-- "channel.ai_category": "null",
"channel.offline_screen_type": "string",
"channel.offline_screen_url": "null",
"channel.offline_html": "null",
"channel.offline_css": "null",
++ "channel.clips_allow_creator_delete": "number",
++ "channel.hide_ai_overview": "number",
++ "channel.ai_overview_pref": "string",
++ "channel.ai_category": "null",
++ "channel.ai_derivation_enabled": "number",
"channel.social_links": "array",
"channel.username": "string",
"channel.display_name": "string",
@@@ -4369,8 +4379,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -4386,11 +4394,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -4405,8 +4416,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -4422,11 +4431,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -4440,7 +4452,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -4452,10 +4464,6 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
"managed_streams[].default_vod_visibility": "string",
@@@ -4463,6 +4471,10 @@
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -4514,25 +4526,25 @@
"channel.panels": "string",
"channel.default_vod_visibility": "string",
"channel.default_clip_visibility": "string",
++ "channel.created_at": "string",
++ "channel.updated_at": "string",
"channel.weather_detail": "string",
"channel.weather_show_location": "number",
"channel.control_mode": "string",
"channel.anon_controls_enabled": "number",
"channel.control_rate_limit_ms": "number",
++ "channel.active_control_config_id": "null",
"channel.video_click_enabled": "number",
"channel.video_click_rate_limit_ms": "number",
-- "channel.active_control_config_id": "null",
-- "channel.created_at": "string",
-- "channel.updated_at": "string",
-- "channel.clips_allow_creator_delete": "number",
-- "channel.hide_ai_overview": "number",
-- "channel.ai_overview_pref": "string",
-- "channel.ai_derivation_enabled": "number",
-- "channel.ai_category": "null",
"channel.offline_screen_type": "string",
"channel.offline_screen_url": "null",
"channel.offline_html": "null",
"channel.offline_css": "null",
++ "channel.clips_allow_creator_delete": "number",
++ "channel.hide_ai_overview": "number",
++ "channel.ai_overview_pref": "string",
++ "channel.ai_category": "null",
++ "channel.ai_derivation_enabled": "number",
"channel.social_links": "array",
"channel.username": "string",
"channel.display_name": "string",
@@@ -4558,8 +4570,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -4575,11 +4585,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -4594,8 +4607,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -4611,11 +4622,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -4629,7 +4643,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -4641,10 +4655,6 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
"managed_streams[].default_vod_visibility": "string",
@@@ -4652,6 +4662,10 @@
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -4703,25 +4717,25 @@
"channel.panels": "string",
"channel.default_vod_visibility": "string",
"channel.default_clip_visibility": "string",
++ "channel.created_at": "string",
++ "channel.updated_at": "string",
"channel.weather_detail": "string",
"channel.weather_show_location": "number",
"channel.control_mode": "string",
"channel.anon_controls_enabled": "number",
"channel.control_rate_limit_ms": "number",
++ "channel.active_control_config_id": "null",
"channel.video_click_enabled": "number",
"channel.video_click_rate_limit_ms": "number",
-- "channel.active_control_config_id": "null",
-- "channel.created_at": "string",
-- "channel.updated_at": "string",
-- "channel.clips_allow_creator_delete": "number",
-- "channel.hide_ai_overview": "number",
-- "channel.ai_overview_pref": "string",
-- "channel.ai_derivation_enabled": "number",
-- "channel.ai_category": "null",
"channel.offline_screen_type": "string",
"channel.offline_screen_url": "null",
"channel.offline_html": "null",
"channel.offline_css": "null",
++ "channel.clips_allow_creator_delete": "number",
++ "channel.hide_ai_overview": "number",
++ "channel.ai_overview_pref": "string",
++ "channel.ai_category": "null",
++ "channel.ai_derivation_enabled": "number",
"channel.social_links": "array",
"channel.username": "string",
"channel.display_name": "string",
@@@ -4751,8 +4765,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -4768,11 +4780,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -4787,8 +4802,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -4804,11 +4817,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -4822,7 +4838,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -4834,10 +4850,6 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
"managed_streams[].default_vod_visibility": "string",
@@@ -4845,6 +4857,10 @@
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -4888,8 +4904,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -4905,11 +4919,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -4938,8 +4955,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -4955,11 +4970,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -4996,25 +5014,25 @@
"channel.panels": "string",
"channel.default_vod_visibility": "string",
"channel.default_clip_visibility": "string",
++ "channel.created_at": "string",
++ "channel.updated_at": "string",
"channel.weather_detail": "string",
"channel.weather_show_location": "number",
"channel.control_mode": "string",
"channel.anon_controls_enabled": "number",
"channel.control_rate_limit_ms": "number",
++ "channel.active_control_config_id": "null",
"channel.video_click_enabled": "number",
"channel.video_click_rate_limit_ms": "number",
-- "channel.active_control_config_id": "null",
-- "channel.created_at": "string",
-- "channel.updated_at": "string",
-- "channel.clips_allow_creator_delete": "number",
-- "channel.hide_ai_overview": "number",
-- "channel.ai_overview_pref": "string",
-- "channel.ai_derivation_enabled": "number",
-- "channel.ai_category": "null",
"channel.offline_screen_type": "string",
"channel.offline_screen_url": "null",
"channel.offline_html": "null",
"channel.offline_css": "null",
++ "channel.clips_allow_creator_delete": "number",
++ "channel.hide_ai_overview": "number",
++ "channel.ai_overview_pref": "string",
++ "channel.ai_category": "null",
++ "channel.ai_derivation_enabled": "number",
"channel.social_links": "array",
"channel.username": "string",
"channel.display_name": "string",
@@@ -5040,8 +5058,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -5057,11 +5073,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -5076,8 +5095,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -5093,11 +5110,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -5111,7 +5131,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -5123,10 +5143,6 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
"managed_streams[].default_vod_visibility": "string",
@@@ -5134,6 +5150,10 @@
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -5185,25 +5205,25 @@
"channel.panels": "string",
"channel.default_vod_visibility": "string",
"channel.default_clip_visibility": "string",
++ "channel.created_at": "string",
++ "channel.updated_at": "string",
"channel.weather_detail": "string",
"channel.weather_show_location": "number",
"channel.control_mode": "string",
"channel.anon_controls_enabled": "number",
"channel.control_rate_limit_ms": "number",
++ "channel.active_control_config_id": "null",
"channel.video_click_enabled": "number",
"channel.video_click_rate_limit_ms": "number",
-- "channel.active_control_config_id": "null",
-- "channel.created_at": "string",
-- "channel.updated_at": "string",
-- "channel.clips_allow_creator_delete": "number",
-- "channel.hide_ai_overview": "number",
-- "channel.ai_overview_pref": "string",
-- "channel.ai_derivation_enabled": "number",
-- "channel.ai_category": "null",
"channel.offline_screen_type": "string",
"channel.offline_screen_url": "null",
"channel.offline_html": "null",
"channel.offline_css": "null",
++ "channel.clips_allow_creator_delete": "number",
++ "channel.hide_ai_overview": "number",
++ "channel.ai_overview_pref": "string",
++ "channel.ai_category": "null",
++ "channel.ai_derivation_enabled": "number",
"channel.social_links": "array",
"channel.username": "string",
"channel.display_name": "string",
@@@ -5233,8 +5253,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -5250,11 +5268,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -5269,8 +5290,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -5286,11 +5305,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -5304,7 +5326,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -5316,10 +5338,6 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
"managed_streams[].default_vod_visibility": "string",
@@@ -5327,6 +5345,10 @@
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -5378,25 +5400,25 @@
"channel.panels": "string",
"channel.default_vod_visibility": "string",
"channel.default_clip_visibility": "string",
++ "channel.created_at": "string",
++ "channel.updated_at": "string",
"channel.weather_detail": "string",
"channel.weather_show_location": "number",
"channel.control_mode": "string",
"channel.anon_controls_enabled": "number",
"channel.control_rate_limit_ms": "number",
++ "channel.active_control_config_id": "null",
"channel.video_click_enabled": "number",
"channel.video_click_rate_limit_ms": "number",
-- "channel.active_control_config_id": "null",
-- "channel.created_at": "string",
-- "channel.updated_at": "string",
-- "channel.clips_allow_creator_delete": "number",
-- "channel.hide_ai_overview": "number",
-- "channel.ai_overview_pref": "string",
-- "channel.ai_derivation_enabled": "number",
-- "channel.ai_category": "null",
"channel.offline_screen_type": "string",
"channel.offline_screen_url": "null",
"channel.offline_html": "null",
"channel.offline_css": "null",
++ "channel.clips_allow_creator_delete": "number",
++ "channel.hide_ai_overview": "number",
++ "channel.ai_overview_pref": "string",
++ "channel.ai_category": "null",
++ "channel.ai_derivation_enabled": "number",
"channel.social_links": "array",
"channel.username": "string",
"channel.display_name": "string",
@@@ -5422,8 +5444,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -5439,11 +5459,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -5458,8 +5481,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -5475,11 +5496,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -5493,7 +5517,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -5505,10 +5529,6 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
"managed_streams[].default_vod_visibility": "string",
@@@ -5516,6 +5536,10 @@
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -5567,25 +5591,25 @@
"channel.panels": "string",
"channel.default_vod_visibility": "string",
"channel.default_clip_visibility": "string",
++ "channel.created_at": "string",
++ "channel.updated_at": "string",
"channel.weather_detail": "string",
"channel.weather_show_location": "number",
"channel.control_mode": "string",
"channel.anon_controls_enabled": "number",
"channel.control_rate_limit_ms": "number",
++ "channel.active_control_config_id": "null",
"channel.video_click_enabled": "number",
"channel.video_click_rate_limit_ms": "number",
-- "channel.active_control_config_id": "null",
-- "channel.created_at": "string",
-- "channel.updated_at": "string",
-- "channel.clips_allow_creator_delete": "number",
-- "channel.hide_ai_overview": "number",
-- "channel.ai_overview_pref": "string",
-- "channel.ai_derivation_enabled": "number",
-- "channel.ai_category": "null",
"channel.offline_screen_type": "string",
"channel.offline_screen_url": "null",
"channel.offline_html": "null",
"channel.offline_css": "null",
++ "channel.clips_allow_creator_delete": "number",
++ "channel.hide_ai_overview": "number",
++ "channel.ai_overview_pref": "string",
++ "channel.ai_category": "null",
++ "channel.ai_derivation_enabled": "number",
"channel.social_links": "array",
"channel.username": "string",
"channel.display_name": "string",
@@@ -5615,8 +5639,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -5632,11 +5654,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -5651,8 +5676,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -5668,11 +5691,14 @@
"streams[].ended_at": "null",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -5686,7 +5712,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -5698,10 +5724,6 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
"managed_streams[].default_vod_visibility": "string",
@@@ -5709,6 +5731,10 @@
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -5903,7 +5929,7 @@
"managed_streams[]": "object",
"managed_streams[].id": "number",
"managed_streams[].user_id": "number",
-- "managed_streams[].channel_id": "number",
++ "managed_streams[].channel_id": "null",
"managed_streams[].slug": "null",
"managed_streams[].title": "string",
"managed_streams[].description": "string",
@@@ -5916,21 +5942,21 @@
"managed_streams[].sort_order": "number",
"managed_streams[].created_at": "string",
"managed_streams[].updated_at": "string",
-- "managed_streams[].slot_clip_recording_enabled": "number",
-- "managed_streams[].slot_clip_notify_enabled": "number",
-- "managed_streams[].slot_powerchat_relay": "number",
-- "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].broadcast_settings": "string",
"managed_streams[].streaming_method": "string",
"managed_streams[].browser_mode": "string",
-- "managed_streams[].pip_source_msid": "null",
-- "managed_streams[].pip_defaults": "string",
"managed_streams[].default_vod_visibility": "string",
"managed_streams[].default_clip_visibility": "string",
"managed_streams[].slot_vod_recording_enabled": "number",
"managed_streams[].weather_zip": "null",
"managed_streams[].weather_detail": "string",
"managed_streams[].weather_show_location": "number",
++ "managed_streams[].slot_clip_recording_enabled": "number",
++ "managed_streams[].slot_clip_notify_enabled": "number",
++ "managed_streams[].pip_source_msid": "null",
++ "managed_streams[].pip_defaults": "string",
++ "managed_streams[].slot_powerchat_relay": "number",
++ "managed_streams[].slot_powerchat_count_rs_views": "number",
"managed_streams[].last_live_at": "string",
"managed_streams[].is_currently_live": "number",
"managed_streams[].live_session_id": "number",
@@@ -5962,7 +5988,7 @@
"managed_stream": "object",
"managed_stream.id": "number",
"managed_stream.user_id": "number",
-- "managed_stream.channel_id": "number",
++ "managed_stream.channel_id": "null",
"managed_stream.slug": "null",
"managed_stream.title": "string",
"managed_stream.description": "string",
@@@ -5974,21 +6000,21 @@
"managed_stream.sort_order": "number",
"managed_stream.created_at": "string",
"managed_stream.updated_at": "string",
-- "managed_stream.slot_clip_recording_enabled": "number",
-- "managed_stream.slot_clip_notify_enabled": "number",
-- "managed_stream.slot_powerchat_relay": "number",
-- "managed_stream.slot_powerchat_count_rs_views": "number",
"managed_stream.broadcast_settings": "string",
"managed_stream.streaming_method": "string",
"managed_stream.browser_mode": "string",
-- "managed_stream.pip_source_msid": "null",
-- "managed_stream.pip_defaults": "string",
"managed_stream.default_vod_visibility": "string",
"managed_stream.default_clip_visibility": "string",
"managed_stream.slot_vod_recording_enabled": "number",
"managed_stream.weather_zip": "null",
"managed_stream.weather_detail": "string",
"managed_stream.weather_show_location": "number",
++ "managed_stream.slot_clip_recording_enabled": "number",
++ "managed_stream.slot_clip_notify_enabled": "number",
++ "managed_stream.pip_source_msid": "null",
++ "managed_stream.pip_defaults": "string",
++ "managed_stream.slot_powerchat_relay": "number",
++ "managed_stream.slot_powerchat_count_rs_views": "number",
"managed_stream.username": "string",
"managed_stream.display_name": "string",
"managed_stream.avatar_url": "null",
@@@ -6062,8 +6088,6 @@
"streams[].id": "number",
"streams[].user_id": "number",
"streams[].channel_id": "null",
-- "streams[].managed_stream_id": "number",
-- "streams[].control_config_id": "null",
"streams[].title": "string",
"streams[].description": "string",
"streams[].category": "string",
@@@ -6079,11 +6103,14 @@
"streams[].ended_at": "string",
"streams[].duration_seconds": "number",
"streams[].created_at": "string",
++ "streams[].call_mode": "null",
++ "streams[].control_config_id": "null",
++ "streams[].managed_stream_id": "number",
"streams[].ai_overview": "null",
++ "streams[].ai_overview_short": "null",
"streams[].ai_title": "null",
"streams[].ai_category": "null",
"streams[].ai_tags": "null",
-- "streams[].ai_overview_short": "null",
"streams[].username": "string",
"streams[].display_name": "string",
"streams[].avatar_url": "null",
@@@ -6295,8 +6322,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -6312,11 +6337,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -6337,25 +6365,25 @@
"stream.channel.panels": "string",
"stream.channel.default_vod_visibility": "string",
"stream.channel.default_clip_visibility": "string",
++ "stream.channel.created_at": "string",
++ "stream.channel.updated_at": "string",
"stream.channel.weather_detail": "string",
"stream.channel.weather_show_location": "number",
"stream.channel.control_mode": "string",
"stream.channel.anon_controls_enabled": "number",
"stream.channel.control_rate_limit_ms": "number",
++ "stream.channel.active_control_config_id": "null",
"stream.channel.video_click_enabled": "number",
"stream.channel.video_click_rate_limit_ms": "number",
-- "stream.channel.active_control_config_id": "null",
-- "stream.channel.created_at": "string",
-- "stream.channel.updated_at": "string",
-- "stream.channel.clips_allow_creator_delete": "number",
-- "stream.channel.hide_ai_overview": "number",
-- "stream.channel.ai_overview_pref": "string",
-- "stream.channel.ai_derivation_enabled": "number",
-- "stream.channel.ai_category": "null",
"stream.channel.offline_screen_type": "string",
"stream.channel.offline_screen_url": "null",
"stream.channel.offline_html": "null",
"stream.channel.offline_css": "null",
++ "stream.channel.clips_allow_creator_delete": "number",
++ "stream.channel.hide_ai_overview": "number",
++ "stream.channel.ai_overview_pref": "string",
++ "stream.channel.ai_category": "null",
++ "stream.channel.ai_derivation_enabled": "number",
"stream.channel.social_links": "null",
"stream.pip_overlay": "null"
},
@@@ -6374,8 +6402,6 @@
"stream.id": "number",
"stream.user_id": "number",
"stream.channel_id": "null",
-- "stream.managed_stream_id": "number",
-- "stream.control_config_id": "null",
"stream.title": "string",
"stream.description": "string",
"stream.category": "string",
@@@ -6391,11 +6417,14 @@
"stream.ended_at": "null",
"stream.duration_seconds": "number",
"stream.created_at": "string",
++ "stream.call_mode": "null",
++ "stream.control_config_id": "null",
++ "stream.managed_stream_id": "number",
"stream.ai_overview": "null",
++ "stream.ai_overview_short": "null",
"stream.ai_title": "null",
"stream.ai_category": "null",
"stream.ai_tags": "null",
-- "stream.ai_overview_short": "null",
"stream.username": "string",
"stream.display_name": "string",
"stream.avatar_url": "null",
@@@ -6416,25 +6445,25 @@
"stream.channel.panels": "string",
"stream.channel.default_vod_visibility": "string",
"stream.channel.default_clip_visibility": "string",
++ "stream.channel.created_at": "string",
++ "stream.channel.updated_at": "string",
"stream.channel.weather_detail": "string",
"stream.channel.weather_show_location": "number",
"stream.channel.control_mode": "string",
"stream.channel.anon_controls_enabled": "number",
"stream.channel.control_rate_limit_ms": "number",
++ "stream.channel.active_control_config_id": "null",
"stream.channel.video_click_enabled": "number",
"stream.channel.video_click_rate_limit_ms": "number",
-- "stream.channel.active_control_config_id": "null",
-- "stream.channel.created_at": "string",
-- "stream.channel.updated_at": "string",
-- "stream.channel.clips_allow_creator_delete": "number",
-- "stream.channel.hide_ai_overview": "number",
-- "stream.channel.ai_overview_pref": "string",
-- "stream.channel.ai_derivation_enabled": "number",
-- "stream.channel.ai_category": "null",
"stream.channel.offline_screen_type": "string",
"stream.channel.offline_screen_url": "null",
"stream.channel.offline_html": "null",
"stream.channel.offline_css": "null",
++ "stream.channel.clips_allow_creator_delete": "number",
++ "stream.channel.hide_ai_overview": "number",
++ "stream.channel.ai_overview_pref": "string",
++ "stream.channel.ai_category": "null",
++ "stream.channel.ai_derivation_enabled": "number",
"stream.channel.social_links": "null",
"stream.pip_overlay": "null"
},
@@@ -7088,7 +7117,7 @@
},
{
"method": "GET",
-- "path": "/shared/navbar.js?v=8255312df076",
++ "path": "/shared/navbar.js?v=555cc6d59b78",
"auth": "anon",
"accept": "html",
"status": 200,
@@@ -7462,7 +7491,7 @@
},
{
"method": "GET",
-- "path": "/js/channel-chat-resize.js?v=13555df58bae",
++ "path": "/js/channel-chat-resize.js?v=3dfe687e2a68",
"auth": "anon",
"accept": "html",
"status": 200,
ddiff --git a/test/fixtures/n-1/worker.json b/test/fixtures/n-1/worker.json
iindex 0dfd8c76..39063d28 100644
---- a/test/fixtures/n-1/worker.json
++++ b/test/fixtures/n-1/worker.json
@@@ -1,1087 +1,18 @@
{
"service": "live",
-- "release": "b92d5c4027c038dc919746ed7c805fdcdc1926c9",
-- "recorded_at": "2026-10-08T14:20:39.563Z",
++ "release": "81dc337dc7ae261ccc498f5894241f1f0e9cc52f",
++ "recorded_at": "2026-10-08T18:28:45.009Z",
"note": "N-1 fixture: written by `npm run n-1:record`, replayed by test/n-1.test.js",
-- "user_version": 0,
-- "schema": [
-- "CREATE TABLE ai_chatbot_configs (\n user_id INTEGER PRIMARY KEY,\n enabled INTEGER DEFAULT 0,\n base_url TEXT DEFAULT 'https://api.openai.com/v1',\n api_token TEXT DEFAULT '',\n model TEXT DEFAULT 'gpt-4o-mini',\n transcribe_enabled INTEGER DEFAULT 0,\n transcribe_model TEXT DEFAULT 'whisper-1',\n num_bots INTEGER DEFAULT 3,\n post_interval_seconds INTEGER DEFAULT 45,\n persona TEXT DEFAULT '',\n vision_enabled INTEGER DEFAULT 0,\n last_validated_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE ai_timeline_cache (\n user_id INTEGER PRIMARY KEY,\n payload TEXT,\n generated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE ai_usage (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n kind TEXT, -- paste_image | paste_text | stream_memory | ai_viewers | ...\n model TEXT,\n input_tokens INTEGER DEFAULT 0,\n output_tokens INTEGER DEFAULT 0,\n cost_usd REAL DEFAULT 0,\n owner_user_id INTEGER, -- streamer this spend is attributed to (NULL = platform/global)\n source TEXT, -- feature bucket, e.g. 'ai_viewers' (NULL = legacy/global)\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n , cached_tokens INTEGER DEFAULT 0, role TEXT, provider TEXT, latency_ms INTEGER)",
-- "CREATE TABLE ai_viewer_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_user_id INTEGER NOT NULL, stream_id INTEGER,\n event TEXT NOT NULL, -- tick | line | skip | mention | fold | vision | degrade | pause | error | info\n bot_username TEXT, target TEXT, thread_id INTEGER, chat_message_id INTEGER,\n text TEXT, reason TEXT,\n tokens_in INTEGER, tokens_cached INTEGER, tokens_out INTEGER, cost_usd REAL, model TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE ai_viewer_threads (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_user_id INTEGER NOT NULL,\n stream_id INTEGER,\n kind TEXT NOT NULL, -- bot_bot | bot_viewer | bot_streamer\n participants_json TEXT NOT NULL, -- [\"botname\",\"viewer\"...]\n topic TEXT,\n state TEXT DEFAULT 'open', -- open | closed\n awaiting TEXT, -- bot username expected to speak next (or null)\n turns INTEGER DEFAULT 0,\n last_line TEXT, last_line_by TEXT, last_line_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE anon_ip_mappings (\n ip TEXT PRIMARY KEY,\n anon_num INTEGER NOT NULL UNIQUE\n , created_at DATETIME)",
-- "CREATE TABLE api_keys (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n key_hash TEXT UNIQUE NOT NULL,\n label TEXT DEFAULT 'Default',\n permissions TEXT DEFAULT '[\"control\",\"stream\"]', -- JSON array\n last_used DATETIME,\n is_active INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE api_tokens (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n token_hash TEXT UNIQUE NOT NULL,\n label TEXT DEFAULT 'Bot Token',\n scopes TEXT DEFAULT '[\"chat\",\"read\"]', -- JSON array: chat, stream, control, read, admin\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_used_at DATETIME,\n expires_at DATETIME, -- NULL = never expires\n is_active INTEGER DEFAULT 1,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE app_state (\n key TEXT PRIMARY KEY,\n value TEXT,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE approved_ips (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER NOT NULL,\n ip_address TEXT NOT NULL,\n approved_by INTEGER,\n source TEXT DEFAULT 'auto',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(channel_id, ip_address),\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (approved_by) REFERENCES users(id) ON DELETE SET NULL\n )",
-- "CREATE TABLE arena_beef_hype (\n beef_id INTEGER NOT NULL,\n side TEXT NOT NULL,\n voter_key TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (beef_id, voter_key)\n )",
-- "CREATE TABLE arena_beef_sides (\n beef_id INTEGER NOT NULL,\n voter_key TEXT NOT NULL,\n side TEXT NOT NULL,\n user_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (beef_id, voter_key)\n )",
-- "CREATE TABLE arena_beefs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n a_user_id INTEGER NOT NULL,\n b_user_id INTEGER NOT NULL,\n status TEXT NOT NULL DEFAULT 'open',\n score_a REAL DEFAULT 0,\n score_b REAL DEFAULT 0,\n hits_a INTEGER DEFAULT 0,\n hits_b INTEGER DEFAULT 0,\n crowd_a INTEGER DEFAULT 0,\n crowd_b INTEGER DEFAULT 0,\n on_clock TEXT,\n clock_until DATETIME,\n responded INTEGER DEFAULT 0,\n last_a_at DATETIME,\n last_b_at DATETIME,\n feed_json TEXT,\n opener_line TEXT,\n winner_user_id INTEGER,\n resolution TEXT,\n opened_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n ends_at DATETIME,\n resolved_at DATETIME\n , headline TEXT, result_headline TEXT, upset INTEGER DEFAULT 0, rematch INTEGER DEFAULT 0, bounty_topic_id INTEGER)",
-- "CREATE TABLE arena_mic_moments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER,\n vod_id INTEGER,\n sec INTEGER,\n kind TEXT NOT NULL DEFAULT 'trash',\n target_user_id INTEGER,\n beef_id INTEGER,\n aimed_at TEXT,\n text TEXT NOT NULL,\n about TEXT,\n quality REAL DEFAULT 0,\n announcer TEXT,\n said_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE arena_profiles (\n user_id INTEGER PRIMARY KEY,\n stats_json TEXT,\n persona_json TEXT,\n persona_model TEXT,\n persona_generated_at DATETIME,\n image_path TEXT,\n image_prompt TEXT,\n image_model TEXT,\n image_generated_at DATETIME,\n image_error TEXT,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n , quotes_json TEXT, quotes_generated_at DATETIME)",
-- "CREATE TABLE arena_trash_levels (\n user_id INTEGER PRIMARY KEY,\n xp INTEGER DEFAULT 0,\n level INTEGER DEFAULT 1,\n angles_cleared INTEGER DEFAULT 0,\n topics_conquered INTEGER DEFAULT 0,\n beef_hits INTEGER DEFAULT 0,\n best_line TEXT,\n best_line_vod_id INTEGER,\n best_line_sec INTEGER,\n best_line_score REAL DEFAULT 0,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n , topic_moments INTEGER DEFAULT 0, topics_joined INTEGER DEFAULT 0, mic_moments INTEGER DEFAULT 0)",
-- "CREATE TABLE arena_xp_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n amount INTEGER NOT NULL,\n reason TEXT NOT NULL,\n ref_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE bans (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER, -- NULL = site-wide\n user_id INTEGER,\n ip_address TEXT,\n anon_id TEXT,\n reason TEXT,\n banned_by INTEGER,\n expires_at DATETIME, -- NULL = permanent\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (banned_by) REFERENCES users(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE camera_presets (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n camera_id INTEGER NOT NULL,\n name TEXT NOT NULL,\n pan REAL NOT NULL, -- 0.0-1.0\n tilt REAL NOT NULL,\n zoom REAL NOT NULL,\n preset_token TEXT, -- ONVIF device preset token (if supported)\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (camera_id) REFERENCES camera_profiles(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE camera_profiles (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER, -- NULL = global profile\n name TEXT NOT NULL,\n onvif_url TEXT NOT NULL, -- e.g., http://192.168.1.100:8080\n username TEXT NOT NULL,\n password_hash TEXT NOT NULL, -- bcrypt hashed\n pan_speed REAL DEFAULT 0.5, -- 0.0-1.0\n tilt_speed REAL DEFAULT 0.5,\n zoom_speed REAL DEFAULT 0.5,\n is_active INTEGER DEFAULT 1,\n last_connected DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE cameras (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n camera_index INTEGER DEFAULT 0,\n label TEXT DEFAULT 'Main',\n protocol TEXT DEFAULT 'jsmpeg',\n jsmpeg_video_port INTEGER,\n jsmpeg_audio_port INTEGER,\n webrtc_room_id TEXT,\n is_active INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE channel_ai_bots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_user_id INTEGER NOT NULL, -- the streamer (owner)\n username TEXT NOT NULL,\n display_name TEXT,\n avatar_color TEXT DEFAULT '#8a8aff',\n source TEXT DEFAULT 'ambient', -- 'ambient' | 'clone'\n cloned_from_kind TEXT, -- 'user' | 'relay' | NULL\n cloned_from_ref TEXT, -- user_id (string) or \"platform:username\"\n persona_json TEXT DEFAULT '{}', -- character + typing style + identity\n brain_json TEXT DEFAULT '{}', -- rolling condensed memory + short timeline\n is_active INTEGER DEFAULT 1,\n msg_count INTEGER DEFAULT 0,\n last_active_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (channel_user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE channel_ai_config (\n user_id INTEGER PRIMARY KEY,\n enabled INTEGER DEFAULT 0,\n num_ambient_bots INTEGER DEFAULT 3,\n pacing_seconds INTEGER DEFAULT 45,\n persona TEXT DEFAULT '',\n transcribe_enabled INTEGER DEFAULT 0,\n vision_enabled INTEGER DEFAULT 0,\n use_shared_key INTEGER DEFAULT 1, -- 1 = OpenVibe.Live shared key (capped), 0 = BYO\n daily_budget_cents INTEGER DEFAULT 20, -- shared-key daily cap\n byo_key TEXT DEFAULT '',\n byo_base_url TEXT DEFAULT '',\n byo_model TEXT DEFAULT 'gpt-4o-mini',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, settings_json TEXT DEFAULT '{}', byo_in_ai INTEGER DEFAULT 0,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE channel_points (\n user_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n balance INTEGER NOT NULL DEFAULT 0,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (user_id, streamer_id)\n )",
-- "CREATE TABLE channel_points_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n idempotency_key TEXT NOT NULL UNIQUE,\n user_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n delta INTEGER NOT NULL,\n reason TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE channels (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL UNIQUE,\n title TEXT DEFAULT 'Untitled Channel',\n description TEXT DEFAULT '',\n category TEXT DEFAULT 'irl',\n tags TEXT DEFAULT '[]',\n protocol TEXT DEFAULT 'webrtc' CHECK(protocol IN ('jsmpeg', 'webrtc', 'rtmp')),\n is_nsfw INTEGER DEFAULT 0,\n auto_record INTEGER DEFAULT 0,\n offline_banner_url TEXT,\n panels TEXT DEFAULT '[]', -- JSON array of info panels\n emote_sources TEXT DEFAULT '{\"defaults\":true,\"custom\":true,\"ffz\":true,\"bttv\":true,\"7tv\":true}',\n default_vod_visibility TEXT DEFAULT 'public' CHECK(default_vod_visibility IN ('public', 'unlisted', 'private')),\n default_clip_visibility TEXT DEFAULT 'public' CHECK(default_clip_visibility IN ('public', 'unlisted', 'private')),\n vod_recording_enabled INTEGER DEFAULT 1,\n force_vod_recording_disabled INTEGER DEFAULT 0,\n weather_zip TEXT DEFAULT NULL,\n weather_detail TEXT DEFAULT 'basic' CHECK(weather_detail IN ('off', 'basic', 'hourly', 'detailed')),\n weather_show_location INTEGER DEFAULT 0,\n control_mode TEXT DEFAULT 'open' CHECK(control_mode IN ('open','whitelist','disabled')),\n anon_controls_enabled INTEGER DEFAULT 1,\n control_rate_limit_ms INTEGER DEFAULT 100,\n video_click_enabled INTEGER DEFAULT 0,\n video_click_rate_limit_ms INTEGER DEFAULT 0,\n active_control_config_id INTEGER,\n bot_robot_id TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, chat_language TEXT DEFAULT 'auto', cp_name TEXT DEFAULT 'Channel Points', cp_icon TEXT DEFAULT 'fa-coins', cp_watch_interval_min INTEGER DEFAULT 5, cp_watch_amount INTEGER DEFAULT 10, cp_game_interval_min INTEGER DEFAULT 0, clips_allow_creator_delete INTEGER DEFAULT 0, hide_ai_overview INTEGER DEFAULT 0, ai_overview_pref TEXT DEFAULT 'auto', ai_derivation_enabled INTEGER DEFAULT 1, ai_category TEXT, force_nsfw INTEGER DEFAULT 0, offline_screen_type TEXT DEFAULT 'none', offline_screen_url TEXT, offline_html TEXT, offline_css TEXT, social_links TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE clip_ai_state (\n clip_id INTEGER PRIMARY KEY,\n ai_overview_short TEXT,\n ai_transcript_json TEXT,\n transcript_status TEXT,\n transcript_attempts INTEGER DEFAULT 0,\n transcript_error TEXT,\n transcript_next_at DATETIME,\n clip_notified INTEGER DEFAULT 0,\n clip_notify_at DATETIME\n , ai_overview TEXT, transcript_partial_json TEXT, transcript_progress_sec INTEGER DEFAULT 0)",
-- "CREATE TABLE clips (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n vod_id INTEGER,\n stream_id INTEGER,\n user_id INTEGER NOT NULL,\n title TEXT DEFAULT 'Untitled Clip',\n description TEXT DEFAULT '',\n file_path TEXT,\n thumbnail_url TEXT,\n start_time REAL NOT NULL DEFAULT 0,\n end_time REAL NOT NULL DEFAULT 0,\n duration_seconds REAL DEFAULT 0,\n is_public INTEGER DEFAULT 1,\n view_count INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, storage_provider TEXT DEFAULT 'local', storage_key TEXT, ai_overview TEXT, ai_transcript TEXT, ai_analyzed_at DATETIME, clip_notified INTEGER DEFAULT 0, clip_notify_at DATETIME, visibility TEXT DEFAULT 'public', auto_generated INTEGER DEFAULT 0,\n FOREIGN KEY (vod_id) REFERENCES vods(id) ON DELETE SET NULL,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE coin_redemptions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n reward_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n stream_id INTEGER,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'fulfilled', 'rejected', 'refunded')),\n user_input TEXT, -- message if requires_input\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n resolved_at DATETIME,\n FOREIGN KEY (reward_id) REFERENCES coin_rewards(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE coin_rewards (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n streamer_id INTEGER NOT NULL, -- who created this reward\n title TEXT NOT NULL,\n description TEXT DEFAULT '',\n cost INTEGER NOT NULL DEFAULT 100, -- OpenCoins cost\n icon TEXT DEFAULT 'fa-star', -- Font Awesome icon\n color TEXT DEFAULT '#8b5cf6', -- accent color\n cooldown_seconds INTEGER DEFAULT 0, -- per-user cooldown\n max_per_stream INTEGER DEFAULT 0, -- 0 = unlimited\n requires_input INTEGER DEFAULT 0, -- viewer must type a message\n is_enabled INTEGER DEFAULT 1,\n is_global INTEGER DEFAULT 0, -- admin: available on all channels\n redemption_count INTEGER DEFAULT 0,\n sort_order INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE coin_transactions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER, -- NULL for non-stream earnings\n amount INTEGER NOT NULL, -- positive = earned, negative = spent\n type TEXT NOT NULL CHECK(type IN ('watch', 'chat_bonus', 'follow_bonus', 'redeem', 'admin_grant', 'refund')),\n reward_id INTEGER, -- if type='redeem', links to coin_rewards\n message TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (reward_id) REFERENCES coin_rewards(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE comments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_type TEXT NOT NULL CHECK(content_type IN ('vod', 'clip')),\n content_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n parent_id INTEGER, -- NULL = top-level, set = reply\n message TEXT NOT NULL,\n is_deleted INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (parent_id) REFERENCES comments(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE content_views (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content_type TEXT NOT NULL CHECK(content_type IN ('vod', 'clip')),\n content_id INTEGER NOT NULL,\n ip TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(content_type, content_id, ip)\n)",
-- "CREATE TABLE control_config_buttons (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n config_id INTEGER NOT NULL,\n label TEXT NOT NULL,\n command TEXT NOT NULL,\n icon TEXT DEFAULT 'fa-gamepad',\n control_type TEXT DEFAULT 'button' CHECK(control_type IN ('button','toggle','dpad','keyboard')),\n key_binding TEXT,\n cooldown_ms INTEGER DEFAULT 500,\n sort_order INTEGER DEFAULT 0,\n btn_color TEXT DEFAULT '',\n btn_bg TEXT DEFAULT '',\n btn_border_color TEXT DEFAULT '',\n is_enabled INTEGER DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (config_id) REFERENCES control_configs(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE control_configs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n name TEXT NOT NULL,\n description TEXT DEFAULT '',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE control_whitelist (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n channel_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n added_by INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(channel_id, user_id),\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE donation_goals (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n title TEXT NOT NULL,\n target_amount INTEGER NOT NULL, -- in Vibes (dollars)\n current_amount INTEGER DEFAULT 0,\n is_active INTEGER DEFAULT 1,\n image_url TEXT, -- optional goal media (webp image / webm video)\n media_type TEXT, -- 'image' | 'video'\n reached_at DATETIME, -- set when target hit; drives the ~1h celebration window\n sort_order INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE easter_egg_solves (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n egg_date TEXT NOT NULL,\n solver_key TEXT NOT NULL,\n user_id INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(egg_date, solver_key)\n )",
-- "CREATE TABLE follows (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n follower_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n email_notify INTEGER DEFAULT 0,\n push_notify INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(follower_id, streamer_id),\n FOREIGN KEY (follower_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE ip_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER,\n anon_id TEXT,\n ip_address TEXT NOT NULL,\n action TEXT NOT NULL DEFAULT 'chat',\n geo_country TEXT,\n geo_region TEXT,\n geo_city TEXT,\n geo_isp TEXT,\n geo_org TEXT,\n geo_ll TEXT,\n user_agent TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL\n )",
-- "CREATE TABLE kick_channel_cache (\n slug TEXT PRIMARY KEY,\n chatroom_id INTEGER,\n kick_channel_id INTEGER,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE linked_accounts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n service TEXT NOT NULL,\n service_user_id TEXT NOT NULL,\n service_username TEXT,\n linked_at DATETIME DEFAULT CURRENT_TIMESTAMP, subject_id TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n UNIQUE(service, service_user_id)\n )",
-- "CREATE TABLE managed_streams (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n channel_id INTEGER,\n slug TEXT, -- optional unique string ID (must contain non-numeric chars)\n title TEXT DEFAULT 'Untitled Stream',\n description TEXT DEFAULT '',\n category TEXT DEFAULT 'irl',\n tags TEXT DEFAULT '[]',\n protocol TEXT DEFAULT 'webrtc' CHECK(protocol IN ('jsmpeg', 'webrtc', 'rtmp')),\n stream_key TEXT UNIQUE NOT NULL, -- per-stream stable key (reusable across sessions)\n is_nsfw INTEGER DEFAULT 0,\n control_config_id INTEGER,\n sort_order INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, slot_clip_recording_enabled INTEGER DEFAULT 1, slot_clip_notify_enabled INTEGER DEFAULT 1, slot_powerchat_relay INTEGER DEFAULT 1, slot_powerchat_count_rs_views INTEGER DEFAULT 1, broadcast_settings TEXT DEFAULT '{}', streaming_method TEXT DEFAULT 'browser', browser_mode TEXT DEFAULT 'camera', pip_source_msid INTEGER, pip_defaults TEXT DEFAULT '{}', default_vod_visibility TEXT DEFAULT 'public', default_clip_visibility TEXT DEFAULT 'public', slot_vod_recording_enabled INTEGER DEFAULT 1, weather_zip TEXT DEFAULT NULL, weather_detail TEXT DEFAULT 'basic', weather_show_location INTEGER DEFAULT 0, mic_only_image TEXT DEFAULT NULL, ingest_authority TEXT NOT NULL DEFAULT 'live', openre_stream_id TEXT DEFAULT NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE SET NULL,\n FOREIGN KEY (control_config_id) REFERENCES control_configs(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE media_request_settings (\n user_id INTEGER PRIMARY KEY,\n enabled INTEGER DEFAULT 1,\n request_cost INTEGER DEFAULT 25,\n max_per_user INTEGER DEFAULT 3,\n max_duration_seconds INTEGER DEFAULT 600,\n allow_youtube INTEGER DEFAULT 1,\n allow_vimeo INTEGER DEFAULT 1,\n allow_direct_media INTEGER DEFAULT 1,\n auto_advance INTEGER DEFAULT 1,\n currency TEXT DEFAULT 'opencoins' CHECK(currency IN ('free','vibes','opencoins','points')),\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, cost_mode TEXT DEFAULT 'flat' CHECK(cost_mode IN ('flat','per_minute')), cost_per_minute INTEGER DEFAULT 5, allow_live INTEGER DEFAULT 0, download_mode TEXT DEFAULT 'stream' CHECK(download_mode IN ('stream','download')),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE media_requests (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n streamer_id INTEGER NOT NULL,\n stream_id INTEGER,\n user_id INTEGER NOT NULL,\n username TEXT NOT NULL,\n input TEXT NOT NULL,\n canonical_url TEXT NOT NULL,\n embed_url TEXT,\n provider TEXT NOT NULL CHECK(provider IN ('youtube', 'vimeo', 'audio', 'video')),\n title TEXT NOT NULL,\n thumbnail_url TEXT,\n duration_seconds INTEGER,\n cost INTEGER NOT NULL DEFAULT 25,\n currency TEXT DEFAULT 'opencoins',\n queue_position INTEGER DEFAULT 0,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'playing', 'played', 'skipped', 'removed', 'failed')),\n requested_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n started_at DATETIME,\n ended_at DATETIME,\n last_error TEXT, stream_url TEXT, download_status TEXT DEFAULT 'none' CHECK(download_status IN ('none','extracting','downloading','ready','failed')), file_path TEXT, playback_position REAL DEFAULT 0, refunded INTEGER DEFAULT 0, charge_state TEXT,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE news_settings (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope TEXT NOT NULL DEFAULT 'global',\n scope_id INTEGER,\n source_id TEXT NOT NULL,\n enabled INTEGER NOT NULL DEFAULT 0,\n config TEXT DEFAULT '{}',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(scope, scope_id, source_id)\n )",
-- "CREATE TABLE openre_sessions (\n session_id TEXT PRIMARY KEY,\n managed_stream_id INTEGER,\n stream_id INTEGER,\n state TEXT NOT NULL,\n revision INTEGER NOT NULL DEFAULT 0,\n started_at DATETIME,\n ended_at DATETIME,\n confirmed_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE paste_comments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n paste_id INTEGER NOT NULL,\n user_id INTEGER,\n parent_id INTEGER,\n anon_name TEXT,\n message TEXT NOT NULL,\n ip_address TEXT,\n is_deleted INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (paste_id) REFERENCES pastes(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (parent_id) REFERENCES paste_comments(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE paste_likes (\n paste_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (paste_id, user_id),\n FOREIGN KEY (paste_id) REFERENCES pastes(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE pastes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n slug TEXT UNIQUE NOT NULL,\n user_id INTEGER,\n type TEXT DEFAULT 'paste' CHECK(type IN ('paste', 'screenshot')),\n title TEXT NOT NULL DEFAULT 'Untitled',\n content TEXT,\n language TEXT DEFAULT 'text',\n visibility TEXT DEFAULT 'public' CHECK(visibility IN ('public', 'unlisted')),\n stream_id INTEGER,\n screenshot_path TEXT,\n metadata TEXT,\n burn_after_read INTEGER DEFAULT 0,\n forked_from INTEGER,\n pinned INTEGER DEFAULT 0,\n views INTEGER DEFAULT 0,\n ip_address TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, copies INTEGER DEFAULT 0, likes INTEGER DEFAULT 0, is_nsfw INTEGER DEFAULT 0, ai_summary TEXT, ai_tags TEXT, ai_analyzed_at DATETIME,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (forked_from) REFERENCES pastes(id) ON DELETE SET NULL\n )",
-- "CREATE TABLE payment_orders (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n provider TEXT NOT NULL,\n provider_ref TEXT,\n kind TEXT NOT NULL DEFAULT 'bucks', -- bucks | subscription\n amount_cents INTEGER NOT NULL DEFAULT 0,\n currency TEXT DEFAULT 'usd',\n bucks INTEGER DEFAULT 0,\n streamer_id INTEGER,\n status TEXT DEFAULT 'pending', -- pending | paid | failed | credited\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE platform_connections (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n platform TEXT NOT NULL CHECK(platform IN ('youtube', 'twitch', 'kick')),\n platform_user_id TEXT,\n platform_username TEXT,\n channel_url TEXT,\n access_token TEXT,\n refresh_token TEXT,\n token_expires_at INTEGER,\n scope TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, platform),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE powerchat_connections (\n user_id INTEGER PRIMARY KEY,\n powerchat_username TEXT,\n powerchat_user_id TEXT,\n access_token TEXT,\n refresh_token TEXT,\n token_expires_at INTEGER, -- epoch ms\n scope TEXT,\n tip_page_url TEXT,\n last_error TEXT,\n connected_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE powerchat_webhook_deliveries (\n delivery_id TEXT PRIMARY KEY,\n event_type TEXT,\n received_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE restream_destinations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n platform TEXT NOT NULL CHECK(platform IN ('youtube', 'twitch', 'kick', 'custom')),\n name TEXT,\n server_url TEXT,\n stream_key TEXT,\n enabled INTEGER DEFAULT 1,\n auto_start INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, connection_id INTEGER DEFAULT NULL REFERENCES platform_connections(id) ON DELETE SET NULL, quality_preset TEXT DEFAULT 'auto', custom_video_bitrate INTEGER DEFAULT NULL, custom_audio_bitrate INTEGER DEFAULT NULL, custom_fps INTEGER DEFAULT NULL, custom_encoder_preset TEXT DEFAULT NULL, srt_latency_ms INTEGER DEFAULT NULL, srt_passphrase TEXT DEFAULT NULL, channel_url TEXT DEFAULT NULL, chat_relay INTEGER DEFAULT 0, powerchat_relay INTEGER DEFAULT 1, powerchat_count_views INTEGER DEFAULT 1, consecutive_failures INTEGER DEFAULT 0, cooldown_until DATETIME DEFAULT NULL, last_error TEXT DEFAULT NULL, last_failed_at DATETIME DEFAULT NULL, managed_stream_id INTEGER REFERENCES managed_streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE \"robotstreamer_integrations\" (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n managed_stream_id INTEGER,\n enabled INTEGER DEFAULT 0,\n mirror_chat INTEGER DEFAULT 1,\n token TEXT,\n robot_id TEXT,\n owner_id TEXT,\n chat_url TEXT,\n control_url TEXT,\n rtc_sfu_url TEXT,\n stream_name TEXT,\n owner_name TEXT,\n last_validated_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE schema_migrations (\n id TEXT PRIMARY KEY,\n applied_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n mode TEXT NOT NULL CHECK (mode IN ('applied', 'adopted')),\n duration_ms INTEGER DEFAULT 0\n)",
-- "CREATE TABLE site_settings (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL DEFAULT '',\n description TEXT DEFAULT '',\n type TEXT DEFAULT 'string' CHECK(type IN ('string', 'number', 'boolean', 'json')),\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n)",
-- "CREATE TABLE stream_analytics (\n stream_id INTEGER PRIMARY KEY,\n avg_viewers REAL DEFAULT 0,\n peak_viewers INTEGER DEFAULT 0,\n unique_chatters INTEGER DEFAULT 0,\n total_messages INTEGER DEFAULT 0,\n total_watch_minutes INTEGER DEFAULT 0,\n new_followers INTEGER DEFAULT 0,\n clips_created INTEGER DEFAULT 0,\n coins_earned INTEGER DEFAULT 0,\n computed_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE stream_controls (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n label TEXT NOT NULL,\n command TEXT NOT NULL, -- command string sent to hardware\n icon TEXT DEFAULT 'fa-gamepad', -- Font Awesome icon class\n control_type TEXT DEFAULT 'button' CHECK(control_type IN ('button', 'toggle', 'slider', 'dpad', 'onvif', 'keyboard')),\n key_binding TEXT, -- keyboard shortcut\n cooldown_ms INTEGER DEFAULT 100,\n is_enabled INTEGER DEFAULT 1,\n sort_order INTEGER DEFAULT 0,\n camera_id INTEGER, -- NULL for non-ONVIF controls; references camera_profiles(id)\n onvif_movement TEXT, -- 'pan_left', 'pan_right', 'tilt_up', 'tilt_down', 'zoom_in', 'zoom_out', 'preset'\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, btn_color TEXT DEFAULT '', btn_bg TEXT DEFAULT '', btn_border_color TEXT DEFAULT '',\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE,\n FOREIGN KEY (camera_id) REFERENCES camera_profiles(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE stream_memories (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n user_id INTEGER,\n offset_seconds INTEGER DEFAULT 0, -- seconds into the stream when captured\n captured_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n tags TEXT, -- JSON array\n thumbnail_url TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, transcript_json TEXT,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE stream_recaps (\n stream_id INTEGER PRIMARY KEY,\n user_id INTEGER,\n json TEXT NOT NULL,\n ai INTEGER DEFAULT 0,\n announced_at DATETIME,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE stream_timeline_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n user_id INTEGER,\n vod_id INTEGER, -- set once the session becomes a VOD\n kind TEXT NOT NULL, -- 'speech' | 'sound'\n start_sec REAL NOT NULL, -- absolute seconds into the stream\n end_sec REAL,\n text TEXT, -- speech content\n label TEXT, -- sound event label\n confidence REAL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, lang TEXT, text_en TEXT,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE streamer_overviews (\n user_id INTEGER PRIMARY KEY,\n overview TEXT,\n model TEXT,\n sources TEXT,\n generated_at DATETIME DEFAULT CURRENT_TIMESTAMP, overview_short TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE streams (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n channel_id INTEGER,\n managed_stream_id INTEGER,\n control_config_id INTEGER,\n title TEXT DEFAULT 'Untitled Stream',\n description TEXT DEFAULT '',\n category TEXT DEFAULT 'irl',\n tags TEXT DEFAULT '[]',\n protocol TEXT DEFAULT 'webrtc' CHECK(protocol IN ('jsmpeg', 'webrtc', 'rtmp')),\n is_live INTEGER DEFAULT 0,\n is_nsfw INTEGER DEFAULT 0,\n viewer_count INTEGER DEFAULT 0,\n peak_viewers INTEGER DEFAULT 0,\n follower_count INTEGER DEFAULT 0,\n thumbnail_url TEXT,\n multi_cam INTEGER DEFAULT 0,\n started_at DATETIME,\n ended_at DATETIME,\n last_heartbeat DATETIME,\n duration_seconds INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, ai_overview TEXT, ai_title TEXT, ai_category TEXT, ai_tags TEXT, ai_overview_short TEXT,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE SET NULL,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE SET NULL,\n FOREIGN KEY (control_config_id) REFERENCES control_configs(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE subscriptions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n subscriber_id INTEGER NOT NULL,\n streamer_id INTEGER NOT NULL,\n tier INTEGER DEFAULT 1 CHECK(tier IN (1, 2, 3)),\n is_active INTEGER DEFAULT 1,\n started_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n expires_at DATETIME, provider TEXT DEFAULT NULL, provider_ref TEXT DEFAULT NULL, price_cents INTEGER DEFAULT 0, currency TEXT DEFAULT 'usd', status TEXT DEFAULT 'active', cancel_at_period_end INTEGER DEFAULT 0, auto_renew INTEGER DEFAULT 0, current_period_end DATETIME DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (subscriber_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (streamer_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE themes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n slug TEXT UNIQUE NOT NULL,\n author_id INTEGER,\n description TEXT DEFAULT '',\n mode TEXT DEFAULT 'dark' CHECK(mode IN ('dark', 'light')),\n variables TEXT NOT NULL DEFAULT '{}', -- JSON: CSS variable overrides\n preview_colors TEXT DEFAULT '{}', -- JSON: {bg, accent, text} for quick preview\n is_builtin INTEGER DEFAULT 0,\n is_public INTEGER DEFAULT 1,\n downloads INTEGER DEFAULT 0,\n rating_sum INTEGER DEFAULT 0,\n rating_count INTEGER DEFAULT 0,\n tags TEXT DEFAULT '[]', -- JSON array\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE transactions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n from_user_id INTEGER,\n to_user_id INTEGER,\n stream_id INTEGER,\n amount INTEGER NOT NULL, -- in bits (cents)\n type TEXT NOT NULL CHECK(type IN ('donation', 'purchase', 'subscription', 'cashout', 'refund', 'bonus')),\n status TEXT DEFAULT 'completed' CHECK(status IN ('pending', 'completed', 'failed', 'escrow', 'refunded')),\n message TEXT,\n paypal_transaction_id TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (from_user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (to_user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE translations (\n key TEXT PRIMARY KEY,\n src TEXT, dst TEXT,\n text TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )",
-- "CREATE TABLE user_cosmetics (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n item_id TEXT NOT NULL, -- references ITEMS key (fx_rainbow, px_sparkle, hat_crown, voice_robot etc.)\n category TEXT NOT NULL, -- 'name_effect'|'particle'|'hat'|'voice'\n unlocked_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, item_id),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE user_equipped (\n user_id INTEGER NOT NULL,\n slot TEXT NOT NULL, -- 'name_effect'|'particle'|'hat'|'voice'\n item_id TEXT NOT NULL,\n PRIMARY KEY (user_id, slot),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE user_equipped_tag (\n user_id INTEGER NOT NULL PRIMARY KEY,\n tag_id TEXT NOT NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n )",
-- "CREATE TABLE user_preferences (\n user_id INTEGER PRIMARY KEY,\n chat_settings TEXT DEFAULT '{}',\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE user_themes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n theme_id INTEGER, -- NULL = custom\n custom_variables TEXT DEFAULT '{}', -- JSON: custom overrides\n is_custom INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (theme_id) REFERENCES themes(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n username TEXT UNIQUE NOT NULL,\n email TEXT UNIQUE,\n password_hash TEXT NOT NULL,\n display_name TEXT,\n avatar_url TEXT,\n bio TEXT DEFAULT '',\n role TEXT DEFAULT 'user' CHECK(role IN ('user', 'streamer', 'global_mod', 'admin')),\n stream_key TEXT UNIQUE,\n openvibe_bucks_balance REAL DEFAULT 0.00,\n openvibe_coins_balance INTEGER DEFAULT 0,\n is_banned INTEGER DEFAULT 0,\n ban_reason TEXT,\n profile_color TEXT DEFAULT '#8b5cf6',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_seen DATETIME DEFAULT CURRENT_TIMESTAMP\n, openvibe_bucks_cashout_balance REAL DEFAULT 0.00, token_valid_after TEXT DEFAULT NULL, max_managed_streams INTEGER DEFAULT 3, avatar_paste_id INTEGER DEFAULT NULL, is_owner INTEGER DEFAULT 0)",
-- "CREATE TABLE verification_keys (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n key TEXT UNIQUE NOT NULL,\n target_username TEXT NOT NULL, -- the reserved username this key unlocks\n note TEXT DEFAULT '', -- admin notes (e.g. RS-Companion user_id)\n created_by INTEGER NOT NULL, -- admin who generated it\n used_by INTEGER, -- user who redeemed it (NULL until used)\n status TEXT DEFAULT 'active' CHECK(status IN ('active', 'used', 'revoked')),\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n used_at DATETIME,\n FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (used_by) REFERENCES users(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE vibe_coding_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n managed_stream_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n stream_id INTEGER,\n session_key TEXT,\n event_id TEXT NOT NULL,\n sequence_num INTEGER DEFAULT 0,\n event_type TEXT NOT NULL,\n visibility TEXT DEFAULT 'public' CHECK(visibility IN ('public', 'streamer')),\n depth TEXT DEFAULT 'standard',\n summary TEXT DEFAULT '',\n payload_json TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n UNIQUE (managed_stream_id, event_id)\n)",
-- "CREATE TABLE vibe_coding_sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n managed_stream_id INTEGER NOT NULL,\n user_id INTEGER NOT NULL,\n session_key TEXT NOT NULL,\n slot_slug TEXT,\n workspace_name TEXT,\n machine_name TEXT,\n extension_version TEXT,\n publisher_id TEXT,\n publisher_label TEXT,\n publisher_vendor TEXT,\n publisher_client_type TEXT,\n publisher_client_name TEXT,\n publisher_client_version TEXT,\n publisher_capabilities_json TEXT,\n publisher_depth TEXT DEFAULT 'standard',\n status TEXT DEFAULT 'active' CHECK(status IN ('active', 'ended')),\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_event_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n ended_at DATETIME,\n FOREIGN KEY (managed_stream_id) REFERENCES managed_streams(id) ON DELETE CASCADE,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n UNIQUE (managed_stream_id, session_key)\n)",
-- "CREATE TABLE viewer_samples (\n sampled_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n viewers INTEGER NOT NULL DEFAULT 0,\n live_streams INTEGER NOT NULL DEFAULT 0\n )",
-- "CREATE TABLE viewer_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER NOT NULL,\n viewer_count INTEGER DEFAULT 0,\n chat_messages_5m INTEGER DEFAULT 0, -- messages in trailing 5 min window\n recorded_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE vod_ai_state (\n vod_id INTEGER PRIMARY KEY,\n ai_overview_short TEXT,\n ai_transcript_json TEXT,\n transcript_status TEXT,\n transcript_attempts INTEGER DEFAULT 0,\n transcript_error TEXT,\n transcript_next_at DATETIME\n , ai_overview TEXT, transcript_partial_json TEXT, transcript_progress_sec INTEGER DEFAULT 0)",
-- "CREATE TABLE vods (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER,\n user_id INTEGER NOT NULL,\n title TEXT,\n description TEXT DEFAULT '',\n file_path TEXT NOT NULL,\n thumbnail_url TEXT,\n master_file_path TEXT,\n file_size INTEGER DEFAULT 0,\n duration_seconds INTEGER DEFAULT 0,\n probe_duration_seconds REAL DEFAULT 0,\n probe_format_json TEXT DEFAULT '',\n health_status TEXT DEFAULT 'unknown',\n health_score INTEGER DEFAULT 0,\n health_issues_json TEXT DEFAULT '[]',\n last_health_scan_at DATETIME,\n quarantined_at DATETIME,\n is_public INTEGER DEFAULT 1,\n is_recording INTEGER DEFAULT 0,\n view_count INTEGER DEFAULT 0,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, clips_only INTEGER DEFAULT 0, storage_provider TEXT DEFAULT 'local', storage_key TEXT, ai_overview TEXT, ai_transcript TEXT, ai_analyzed_at DATETIME, visibility TEXT DEFAULT 'public',\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n)",
-- "CREATE TABLE vpn_approvals (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n ip_address TEXT NOT NULL,\n user_id INTEGER,\n status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'approved', 'denied')),\n reviewed_by INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,\n FOREIGN KEY (reviewed_by) REFERENCES users(id) ON DELETE SET NULL\n)",
-- "CREATE TABLE watch_time (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_id INTEGER NOT NULL,\n stream_id INTEGER NOT NULL,\n minutes_watched INTEGER DEFAULT 0,\n last_heartbeat DATETIME DEFAULT CURRENT_TIMESTAMP,\n coins_earned INTEGER DEFAULT 0, -- coins earned this session\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(user_id, stream_id),\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n FOREIGN KEY (stream_id) REFERENCES streams(id) ON DELETE SET NULL\n)",
-- "CREATE INDEX idx_ai_usage_created ON ai_usage(created_at)",
-- "CREATE INDEX idx_ai_usage_owner_day ON ai_usage(owner_user_id, created_at)",
-- "CREATE INDEX idx_ai_usage_source_day ON ai_usage(source, created_at)",
-- "CREATE INDEX idx_ai_viewer_log_created ON ai_viewer_log(created_at)",
-- "CREATE INDEX idx_aiv_log_channel ON ai_viewer_log(channel_user_id, id)",
-- "CREATE INDEX idx_aiv_threads_open ON ai_viewer_threads(channel_user_id, state, updated_at)",
-- "CREATE INDEX idx_anon_ip_created ON anon_ip_mappings(created_at)",
-- "CREATE INDEX idx_approved_ips_channel ON approved_ips(channel_id)",
-- "CREATE INDEX idx_approved_ips_ip ON approved_ips(ip_address)",
-- "CREATE INDEX idx_arena_beefs_status ON arena_beefs (status, a_user_id, b_user_id)",
-- "CREATE INDEX idx_arena_mic_created ON arena_mic_moments (created_at)",
-- "CREATE INDEX idx_arena_mic_user ON arena_mic_moments (user_id, id)",
-- "CREATE INDEX idx_arena_xp_log_user ON arena_xp_log (user_id, created_at)",
-- "CREATE INDEX idx_bans_ip ON bans(ip_address)",
-- "CREATE INDEX idx_bans_stream_id ON bans(stream_id)",
-- "CREATE INDEX idx_bans_user ON bans(user_id)",
-- "CREATE INDEX idx_cameras_stream_id ON cameras(stream_id)",
-- "CREATE INDEX idx_channel_ai_bots_channel ON channel_ai_bots(channel_user_id, is_active)",
-- "CREATE UNIQUE INDEX idx_channel_ai_bots_uname ON channel_ai_bots(channel_user_id, username)",
-- "CREATE INDEX idx_channels_user_id ON channels(user_id)",
-- "CREATE UNIQUE INDEX idx_clip_ai_state_clip_id_unique ON clip_ai_state(clip_id)",
-- "CREATE INDEX idx_clips_stream_id ON clips(stream_id)",
-- "CREATE INDEX idx_clips_user_id ON clips(user_id)",
-- "CREATE INDEX idx_coin_redemptions_stream ON coin_redemptions(stream_id)",
-- "CREATE INDEX idx_coin_redemptions_user ON coin_redemptions(user_id)",
-- "CREATE INDEX idx_coin_rewards_streamer ON coin_rewards(streamer_id)",
-- "CREATE INDEX idx_coin_tx_created ON coin_transactions(created_at)",
-- "CREATE INDEX idx_coin_tx_stream ON coin_transactions(stream_id)",
-- "CREATE INDEX idx_coin_tx_user ON coin_transactions(user_id)",
-- "CREATE INDEX idx_comments_content ON comments(content_type, content_id)",
-- "CREATE INDEX idx_comments_parent ON comments(parent_id)",
-- "CREATE INDEX idx_comments_user ON comments(user_id)",
-- "CREATE INDEX idx_config_buttons_config ON control_config_buttons(config_id)",
-- "CREATE INDEX idx_content_views_lookup ON content_views(content_type, content_id)",
-- "CREATE INDEX idx_control_configs_user ON control_configs(user_id)",
-- "CREATE INDEX idx_control_whitelist_channel ON control_whitelist(channel_id)",
-- "CREATE INDEX idx_cp_log_user ON channel_points_log(user_id, streamer_id, id)",
-- "CREATE INDEX idx_egg_solves_date ON easter_egg_solves(egg_date)",
-- "CREATE INDEX idx_follows_follower ON follows(follower_id)",
-- "CREATE INDEX idx_follows_streamer ON follows(streamer_id)",
-- "CREATE INDEX idx_goals_active ON donation_goals(is_active)",
-- "CREATE INDEX idx_ip_log_action ON ip_log(action)",
-- "CREATE INDEX idx_ip_log_created ON ip_log(created_at)",
-- "CREATE INDEX idx_ip_log_ip ON ip_log(ip_address)",
-- "CREATE INDEX idx_ip_log_user ON ip_log(user_id)",
-- "CREATE INDEX idx_linked_service ON linked_accounts(service, service_user_id)",
-- "CREATE INDEX idx_linked_subject ON linked_accounts(subject_id)",
-- "CREATE INDEX idx_linked_user ON linked_accounts(user_id)",
-- "CREATE INDEX idx_managed_streams_key ON managed_streams(stream_key)",
-- "CREATE INDEX idx_managed_streams_slug ON managed_streams(slug)",
-- "CREATE INDEX idx_managed_streams_user ON managed_streams(user_id)",
-- "CREATE INDEX idx_media_requests_canonical ON media_requests(streamer_id, canonical_url, status)",
-- "CREATE INDEX idx_media_requests_streamer_status ON media_requests(streamer_id, status, queue_position, requested_at)",
-- "CREATE INDEX idx_media_requests_user_status ON media_requests(user_id, status, requested_at)",
-- "CREATE INDEX idx_openre_sessions_state ON openre_sessions(state)",
-- "CREATE INDEX idx_openre_sessions_stream ON openre_sessions(stream_id)",
-- "CREATE INDEX idx_paste_comments_ip ON paste_comments(ip_address)",
-- "CREATE INDEX idx_paste_comments_parent ON paste_comments(parent_id)",
-- "CREATE INDEX idx_paste_comments_paste ON paste_comments(paste_id)",
-- "CREATE INDEX idx_paste_comments_user ON paste_comments(user_id)",
-- "CREATE INDEX idx_pastes_created ON pastes(created_at)",
-- "CREATE INDEX idx_pastes_pinned ON pastes(pinned)",
-- "CREATE INDEX idx_pastes_slug ON pastes(slug)",
-- "CREATE INDEX idx_pastes_type ON pastes(type)",
-- "CREATE INDEX idx_pastes_user ON pastes(user_id)",
-- "CREATE INDEX idx_pastes_visibility ON pastes(visibility)",
-- "CREATE INDEX idx_payment_orders_ref ON payment_orders(provider, provider_ref)",
-- "CREATE INDEX idx_payment_orders_user ON payment_orders(user_id)",
-- "CREATE INDEX idx_powerchat_conn_pcuid ON powerchat_connections(powerchat_user_id)",
-- "CREATE INDEX idx_powerchat_conn_username ON powerchat_connections(powerchat_username)",
-- "CREATE INDEX idx_restream_dest_managed ON restream_destinations(managed_stream_id)",
-- "CREATE INDEX idx_restream_dest_user ON restream_destinations(user_id)",
-- "CREATE UNIQUE INDEX idx_rs_integrations_user_slot\n ON robotstreamer_integrations(user_id, IFNULL(managed_stream_id, 0))",
-- "CREATE UNIQUE INDEX idx_stream_memories_moment_unique ON stream_memories(stream_id, offset_seconds)",
-- "CREATE INDEX idx_stream_memories_stream ON stream_memories(stream_id, offset_seconds)",
-- "CREATE INDEX idx_stream_recaps_user ON stream_recaps(user_id, created_at)",
-- "CREATE INDEX idx_streams_channel_id ON streams(channel_id)",
-- "CREATE INDEX idx_streams_created ON streams(created_at)",
-- "CREATE INDEX idx_streams_is_live ON streams(is_live)",
-- "CREATE INDEX idx_streams_live_ended ON streams(is_live, ended_at)",
-- "CREATE INDEX idx_streams_managed_ended ON streams(managed_stream_id, ended_at)",
-- "CREATE INDEX idx_streams_started ON streams(started_at)",
-- "CREATE INDEX idx_streams_user_id ON streams(user_id)",
-- "CREATE INDEX idx_subs_provider_ref ON subscriptions(provider, provider_ref)",
-- "CREATE INDEX idx_subs_streamer ON subscriptions(streamer_id, status)",
-- "CREATE INDEX idx_subs_subscriber ON subscriptions(subscriber_id, status)",
-- "CREATE INDEX idx_themes_author ON themes(author_id)",
-- "CREATE INDEX idx_themes_public ON themes(is_public)",
-- "CREATE INDEX idx_themes_slug ON themes(slug)",
-- "CREATE INDEX idx_timeline_kind ON stream_timeline_events(stream_id, kind, start_sec)",
-- "CREATE INDEX idx_timeline_stream ON stream_timeline_events(stream_id, start_sec)",
-- "CREATE INDEX idx_timeline_user_kind_created ON stream_timeline_events(user_id, kind, created_at)",
-- "CREATE INDEX idx_timeline_vod ON stream_timeline_events(vod_id, start_sec)",
-- "CREATE INDEX idx_transactions_to_user ON transactions(to_user_id)",
-- "CREATE INDEX idx_tx_type_created ON transactions(type, created_at)",
-- "CREATE INDEX idx_user_cosmetics_user ON user_cosmetics(user_id)",
-- "CREATE INDEX idx_user_themes_user ON user_themes(user_id)",
-- "CREATE INDEX idx_users_created ON users(created_at)",
-- "CREATE INDEX idx_users_role ON users(role)",
-- "CREATE INDEX idx_users_username_nocase ON users(username COLLATE NOCASE)",
-- "CREATE INDEX idx_vibe_events_managed ON vibe_coding_events(managed_stream_id, id DESC)",
-- "CREATE INDEX idx_vibe_events_stream ON vibe_coding_events(stream_id)",
-- "CREATE INDEX idx_vibe_sessions_managed ON vibe_coding_sessions(managed_stream_id)",
-- "CREATE INDEX idx_vibe_sessions_user ON vibe_coding_sessions(user_id)",
-- "CREATE INDEX idx_viewer_samples_at ON viewer_samples(sampled_at)",
-- "CREATE INDEX idx_viewer_snapshots_stream ON viewer_snapshots(stream_id, recorded_at)",
-- "CREATE INDEX idx_vkeys_key ON verification_keys(key)",
-- "CREATE INDEX idx_vkeys_status ON verification_keys(status)",
-- "CREATE INDEX idx_vkeys_target ON verification_keys(target_username)",
-- "CREATE UNIQUE INDEX idx_vod_ai_state_vod_id_unique ON vod_ai_state(vod_id)",
-- "CREATE INDEX idx_vods_user_id ON vods(user_id)",
-- "CREATE INDEX idx_watch_time_stream ON watch_time(stream_id)",
-- "CREATE INDEX idx_watch_time_user ON watch_time(user_id)"
++ "engine": "postgresql",
++ "migrations": [
++ {
++ "name": "0001_analytics.sql",
++ "sha256": "e75f5c666e5d850c90fa2fd8c05dc357633bb945e455853882dc9641100f4055"
++ },
++ {
++ "name": "0002_live.sql",
++ "sha256": "616fa09e397160ca847d72ac7f3d91824cf4c92d9318ea55b91d49501bc5dc59"
++ }
],
-- "ledger": {
-- "schema_migrations": [
-- {
-- "id": "001_vibes_decimal_to_bits",
-- "applied_at": "2026-10-08 14:20:34",
-- "mode": "adopted",
-- "duration_ms": 1
-- },
-- {
-- "id": "002_pastes_ai_columns",
-- "applied_at": "2026-10-08 14:20:34",
-- "mode": "applied",
-- "duration_ms": 6
-- },
-- {
-- "id": "004_hot_path_indexes",
-- "applied_at": "2026-10-08 14:20:34",
-- "mode": "applied",
-- "duration_ms": 1
-- },
-- {
-- "id": "005_drop_chat_staged_tables",
-- "applied_at": "2026-10-08 14:20:34",
-- "mode": "adopted",
-- "duration_ms": 0
-- },
-- {
-- "id": "006_drop_emotes",
-- "applied_at": "2026-10-08 14:20:34",
-- "mode": "adopted",
-- "duration_ms": 0
-- },
-- {
-- "id": "007_drop_chat_tables",
-- "applied_at": "2026-10-08 14:20:34",
-- "mode": "adopted",
-- "duration_ms": 1
-- }
-- ]
-- },
-- "lazy": [
-- "CREATE TABLE IF NOT EXISTS account_data_events ( id TEXT PRIMARY KEY, kind TEXT NOT NULL, subject TEXT NOT NULL, outcome TEXT, sent_at DATETIME, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
-- "CREATE TABLE IF NOT EXISTS module_summary_pushes ( user_id INTEGER NOT NULL, namespace TEXT NOT NULL, hash TEXT NOT NULL, pushed_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, namespace) )",
-- "CREATE TABLE IF NOT EXISTS token_revocations ( subject_id TEXT PRIMARY KEY, valid_after_ms INTEGER NOT NULL, reason TEXT, updated_at INTEGER NOT NULL )",
-- "CREATE TABLE IF NOT EXISTS subject_merges ( merge_id TEXT PRIMARY KEY, from_subject TEXT NOT NULL, into_subject TEXT NOT NULL, from_user_id INTEGER, into_user_id INTEGER, outcome TEXT NOT NULL, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
-- "CREATE TABLE IF NOT EXISTS subject_projection ( subject_id TEXT PRIMARY KEY, network_user_id INTEGER, revision INTEGER NOT NULL, username TEXT NOT NULL, display_name TEXT, avatar_url TEXT, profile_color TEXT, role TEXT NOT NULL, banned INTEGER NOT NULL DEFAULT 0, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
-- "CREATE TABLE IF NOT EXISTS username_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, old_username TEXT NOT NULL, new_username TEXT NOT NULL, changed_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
-- "CREATE TABLE IF NOT EXISTS comment_thread_refs ( content_type TEXT NOT NULL, content_id INTEGER NOT NULL, thread_id INTEGER NOT NULL, access_id TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (content_type, content_id) )",
-- "CREATE TABLE IF NOT EXISTS search_doc_pushes ( user_id INTEGER PRIMARY KEY, hash TEXT NOT NULL, revision INTEGER NOT NULL, deleted INTEGER NOT NULL DEFAULT 0, pushed_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
-- "CREATE TABLE IF NOT EXISTS search_media_pushes ( kind TEXT NOT NULL, media_id INTEGER NOT NULL, hash TEXT NOT NULL, revision INTEGER NOT NULL, deleted INTEGER NOT NULL DEFAULT 0, pushed_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (kind, media_id) )",
-- "CREATE TABLE IF NOT EXISTS lineage_unresolved ( ref TEXT NOT NULL, reason TEXT NOT NULL, detail TEXT, last_caller TEXT, count INTEGER NOT NULL DEFAULT 1, first_at TEXT NOT NULL, last_at TEXT NOT NULL, PRIMARY KEY (ref, reason) )",
-- "CREATE TABLE IF NOT EXISTS billing_actions ( id INTEGER PRIMARY KEY AUTOINCREMENT, action TEXT NOT NULL, idempotency_key TEXT NOT NULL UNIQUE, live_user_id INTEGER, live_ref TEXT, method TEXT NOT NULL, path TEXT NOT NULL, request_json TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', http_status INTEGER, billing_ref TEXT, response_json TEXT, error TEXT, attempts INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )",
-- "CREATE TABLE IF NOT EXISTS opencoin_admin_grants ( id INTEGER PRIMARY KEY AUTOINCREMENT, client_key TEXT UNIQUE, admin_id INTEGER, user_id INTEGER NOT NULL, amount INTEGER NOT NULL, reason TEXT, balance INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
-- "CREATE TABLE IF NOT EXISTS follow_projection_revisions ( follower_subject TEXT NOT NULL, target_subject TEXT NOT NULL, revision INTEGER NOT NULL, PRIMARY KEY (follower_subject, target_subject) )",
-- "CREATE TABLE IF NOT EXISTS tips_deliveries ( idempotency_key TEXT PRIMARY KEY, effect TEXT, state TEXT NOT NULL DEFAULT 'pending', response_json TEXT, claimed_at INTEGER NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )",
-- "ALTER TABLE users ADD COLUMN deleted_at DATETIME",
-- "ALTER TABLE users ADD COLUMN merged_into INTEGER",
-- "ALTER TABLE users ADD COLUMN theme_id INTEGER",
-- "ALTER TABLE streams ADD COLUMN call_mode TEXT DEFAULT NULL"
-- ],
-- "statements": [
-- "DELETE FROM ai_timeline_cache WHERE user_id = ?",
-- "DELETE FROM ai_viewer_log WHERE created_at < datetime('now', ?)",
-- "DELETE FROM app_state WHERE key = ?",
-- "DELETE FROM approved_ips WHERE channel_id = ? AND ip_address = ?",
-- "DELETE FROM bans WHERE id = ?",
-- "DELETE FROM bans WHERE ip_address IS NOT NULL AND banned_by = ? AND stream_id IS NULL AND reason LIKE ?",
-- "DELETE FROM bans WHERE user_id = ? AND stream_id = ?",
-- "DELETE FROM bans WHERE user_id = ? AND stream_id IS NULL",
-- "DELETE FROM camera_presets WHERE camera_id = ?",
-- "DELETE FROM camera_presets WHERE id = ?",
-- "DELETE FROM camera_profiles WHERE id = ?",
-- "DELETE FROM channel_ai_bots WHERE id = ?",
-- "DELETE FROM channel_points WHERE user_id = ? AND streamer_id = ?",
-- "DELETE FROM clip_ai_state WHERE clip_id = ?",
-- "DELETE FROM coin_rewards WHERE id = ?",
-- "DELETE FROM content_views WHERE content_type = ? AND content_id = ?",
-- "DELETE FROM control_config_buttons WHERE id = ?",
-- "DELETE FROM control_config_buttons WHERE id = ? AND config_id = ?",
-- "DELETE FROM control_configs WHERE id = ?",
-- "DELETE FROM control_whitelist WHERE id = ? AND channel_id = ?",
-- "DELETE FROM donation_goals WHERE id = ? AND user_id = ?",
-- "DELETE FROM follows WHERE follower_id = ? AND streamer_id = ?",
-- "DELETE FROM follows WHERE id = ?",
-- "DELETE FROM lineage_unresolved WHERE last_at < ?",
-- "DELETE FROM linked_accounts WHERE service = 'hobotools'",
-- "DELETE FROM managed_streams WHERE id = ? AND user_id = ?",
-- "DELETE FROM media_requests WHERE id = ? AND charge_state = 'charging'",
-- "DELETE FROM platform_connections WHERE user_id = ? AND platform = ?",
-- "DELETE FROM powerchat_connections WHERE user_id = ?",
-- "DELETE FROM powerchat_webhook_deliveries WHERE received_at < datetime('now', ?)",
-- "DELETE FROM restream_destinations WHERE id = ?",
-- "DELETE FROM robotstreamer_integrations WHERE user_id = ? AND managed_stream_id = ?",
-- "DELETE FROM site_settings WHERE key = 'ai_viewers_engine'",
-- "DELETE FROM site_settings WHERE key = 'powerchat_site_user_id'",
-- "DELETE FROM site_settings WHERE key = ?",
-- "DELETE FROM site_settings WHERE key IN ('ai_api_key', 'ai_provider', 'ai_base_url', 'ai_model', 'ai_model_chat', 'ai_model_vision', 'ai_model_director', 'ai_model_summary', 'ai_pricing_json', 'ai_input_cost_per_mtok', 'ai_output_cost_per_mtok', 'ai_image_enabled', 'ai_image_model', 'ai_image_quality', 'ai_image_cost_usd')",
-- "DELETE FROM site_settings WHERE key IN ('auto_clip_backfill','auto_clip_log','daily_easter_egg','home_hero_moments','home_hero_slogans') OR key LIKE 'ai_whole_overview_%'",
-- "DELETE FROM stream_controls WHERE stream_id = ? AND (control_type != ? OR control_type IS NULL)",
-- "DELETE FROM stream_memories WHERE id NOT IN ( SELECT id FROM ( SELECT id, ROW_NUMBER() OVER ( PARTITION BY stream_id, offset_seconds ORDER BY LENGTH(COALESCE(description,'')) DESC, (transcript_json IS NOT NULL) DESC, id DESC) AS rn FROM stream_memories) WHERE rn = 1)",
-- "DELETE FROM themes WHERE id = ?",
-- "DELETE FROM tips_deliveries WHERE created_at < datetime('now', ?)",
-- "DELETE FROM tips_deliveries WHERE idempotency_key = ? AND state = 'pending'",
-- "DELETE FROM user_cosmetics WHERE user_id = ? AND item_id = ?",
-- "DELETE FROM user_equipped WHERE user_id = ? AND slot = ?",
-- "DELETE FROM user_equipped WHERE user_id = ? AND slot = ? AND item_id = ?",
-- "DELETE FROM viewer_samples WHERE sampled_at < datetime('now', '-400 days')",
-- "DELETE FROM vod_ai_state WHERE vod_id = ?",
-- "INSERT INTO ai_chatbot_configs (user_id, enabled, base_url, api_token, model, transcribe_enabled, transcribe_model, num_bots, post_interval_seconds, persona, vision_enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO ai_timeline_cache (user_id, payload, generated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET payload = excluded.payload, generated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO ai_usage (kind, model, input_tokens, output_tokens, cached_tokens, cost_usd, owner_user_id, source, role, provider, latency_ms) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO ai_viewer_log (channel_user_id, stream_id, event, bot_username, target, thread_id, chat_message_id, text, reason, tokens_in, tokens_cached, tokens_out, cost_usd, model) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO ai_viewer_threads (channel_user_id, stream_id, kind, participants_json, topic, awaiting) VALUES (?, ?, ?, ?, ?, ?)",
-- "INSERT INTO anon_ip_mappings (ip, anon_num, created_at) VALUES (?, ?, CURRENT_TIMESTAMP)",
-- "INSERT INTO api_keys (user_id, key_hash, label, permissions) VALUES (?, ?, ?, ?)",
-- "INSERT INTO api_tokens (user_id, token_hash, label, scopes, expires_at) VALUES (?, ?, ?, ?, ?)",
-- "INSERT INTO app_state (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO arena_beefs (a_user_id, b_user_id, on_clock, clock_until, ends_at, feed_json, opener_line, rematch, bounty_topic_id, headline) VALUES (?, ?, 'b', ?, ?, '[]', ?, ?, NULL, ?)",
-- "INSERT INTO arena_mic_moments (user_id, stream_id, vod_id, sec, kind, target_user_id, beef_id, aimed_at, text, about, quality, announcer, said_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(?, CURRENT_TIMESTAMP))",
-- "INSERT INTO arena_profiles (user_id, persona_json, persona_model, persona_generated_at, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET persona_json = excluded.persona_json, persona_model = excluded.persona_model, persona_generated_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO arena_profiles (user_id, quotes_json, quotes_generated_at, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET quotes_json = excluded.quotes_json, quotes_generated_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO arena_profiles (user_id, stats_json, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET stats_json = excluded.stats_json, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO arena_trash_levels (user_id, xp, level) VALUES (?, ?, ?) ON CONFLICT(user_id) DO UPDATE SET xp = xp + excluded.xp, level = ?, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO arena_xp_log (user_id, amount, reason, ref_id) VALUES (?, ?, ?, ?)",
-- "INSERT INTO bans (ip_address, reason, banned_by, expires_at) VALUES (?, ?, ?, ?)",
-- "INSERT INTO bans (stream_id, ip_address, anon_id, reason, banned_by) VALUES (?, ?, ?, ?, ?)",
-- "INSERT INTO bans (stream_id, ip_address, anon_id, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?, ?)",
-- "INSERT INTO bans (stream_id, user_id, ip_address, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?, ?)",
-- "INSERT INTO bans (stream_id, user_id, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?)",
-- "INSERT INTO bans (user_id, ip_address, reason, banned_by, expires_at) VALUES (?, ?, ?, ?, ?)",
-- "INSERT INTO bans (user_id, reason, banned_by, expires_at) VALUES (?, ?, ?, ?)",
-- "INSERT INTO camera_presets (camera_id, name, pan, tilt, zoom, preset_token) VALUES (?, ?, ?, ?, ?, ?)",
-- "INSERT INTO camera_profiles (user_id, stream_id, name, onvif_url, username, password_hash, pan_speed, tilt_speed, zoom_speed) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO cameras (stream_id, camera_index, label, protocol) VALUES (?, 0, 'Main', 'webrtc')",
-- "INSERT INTO cameras (stream_id, camera_index, label, protocol) VALUES (?, 0, 'Main', ?)",
-- "INSERT INTO channel_ai_bots (channel_user_id, username, display_name, avatar_color, source, cloned_from_kind, cloned_from_ref, persona_json, brain_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO channel_ai_config (user_id, enabled, num_ambient_bots, pacing_seconds, persona, transcribe_enabled, vision_enabled, use_shared_key, daily_budget_cents, byo_key, byo_base_url, byo_model) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO channel_points (user_id, streamer_id, balance) VALUES (?, ?, ?)",
-- "INSERT INTO channel_points (user_id, streamer_id, balance, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id, streamer_id) DO UPDATE SET balance = balance + excluded.balance, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO channel_points_log (idempotency_key, user_id, streamer_id, delta, reason) VALUES (?, ?, ?, ?, ?)",
-- "INSERT INTO coin_redemptions (reward_id, user_id, stream_id, user_input) VALUES (?, ?, ?, ?)",
-- "INSERT INTO coin_rewards (streamer_id, title, description, cost, icon, color, cooldown_seconds, max_per_stream, requires_input, is_global, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO coin_transactions (user_id, stream_id, amount, type, reward_id, message) VALUES (?, ?, ?, ?, ?, ?)",
-- "INSERT INTO control_config_buttons (config_id, label, command, icon, control_type, key_binding, cooldown_ms, sort_order, btn_color, btn_bg, btn_border_color) VALUES (?, ?, ?, ?, ?, ?, ?, ?, '', '', '')",
-- "INSERT INTO control_config_buttons (config_id, label, command, icon, control_type, key_binding, cooldown_ms, sort_order, btn_color, btn_bg, btn_border_color) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO control_configs (user_id, name, description) VALUES (?, ?, ?)",
-- "INSERT INTO donation_goals (user_id, title, target_amount) VALUES (?, ?, ?)",
-- "INSERT INTO donation_goals (user_id, title, target_amount, image_url, media_type, sort_order) VALUES (?, ?, ?, ?, ?, ?)",
-- "INSERT INTO follow_projection_revisions (follower_subject, target_subject, revision) VALUES (?, ?, ?) ON CONFLICT (follower_subject, target_subject) DO UPDATE SET revision = excluded.revision",
-- "INSERT INTO ip_log (user_id, anon_id, ip_address, action, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, user_agent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO kick_channel_cache (slug, chatroom_id, kick_channel_id, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(slug) DO UPDATE SET chatroom_id = excluded.chatroom_id, kick_channel_id = COALESCE(excluded.kick_channel_id, kick_channel_cache.kick_channel_id), updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO lineage_unresolved (ref, reason, detail, last_caller, count, first_at, last_at) VALUES (?, ?, ?, ?, 1, ?, ?) ON CONFLICT (ref, reason) DO UPDATE SET detail = excluded.detail, last_caller = excluded.last_caller, count = count + 1, last_at = excluded.last_at",
-- "INSERT INTO managed_streams (user_id, channel_id, slug, title, description, category, protocol, streaming_method, stream_key, is_nsfw, control_config_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO managed_streams (user_id, channel_id, title, category, protocol, stream_key, is_nsfw) VALUES (?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO media_request_settings ( user_id, enabled, request_cost, max_per_user, max_duration_seconds, allow_youtube, allow_vimeo, allow_direct_media, auto_advance, cost_mode, cost_per_minute, allow_live, download_mode, currency ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO media_requests ( streamer_id, stream_id, user_id, username, input, canonical_url, embed_url, provider, title, thumbnail_url, duration_seconds, cost, queue_position, currency, status, charge_state ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO module_summary_pushes (user_id, namespace, hash, pushed_at) VALUES (?, ?, ?, ?) ON CONFLICT(user_id, namespace) DO UPDATE SET hash = excluded.hash, pushed_at = excluded.pushed_at",
-- "INSERT INTO news_settings (scope, scope_id, source_id, enabled, config) VALUES ('global', NULL, ?, ?, ?) ON CONFLICT(scope, scope_id, source_id) DO UPDATE SET enabled = excluded.enabled, config = excluded.config, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO news_settings (scope, scope_id, source_id, enabled, config) VALUES ('user', ?, '_master', ?, '{}') ON CONFLICT(scope, scope_id, source_id) DO UPDATE SET enabled = excluded.enabled, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO opencoin_admin_grants (client_key, admin_id, user_id, amount, reason) VALUES (?, ?, ?, ?, ?)",
-- "INSERT INTO openre_sessions (session_id, managed_stream_id, stream_id, state, revision, ended_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(session_id) DO UPDATE SET state = excluded.state, revision = excluded.revision, ended_at = excluded.ended_at, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO openre_sessions (session_id, managed_stream_id, stream_id, state, revision, started_at, confirmed_at, updated_at) VALUES (?, ?, ?, 'live', ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT(session_id) DO UPDATE SET stream_id = excluded.stream_id, state = 'live', revision = excluded.revision, confirmed_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO payment_orders (user_id, provider, provider_ref, kind, amount_cents, currency, bucks, streamer_id, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO platform_connections (user_id, platform, platform_user_id, platform_username, channel_url, access_token, refresh_token, token_expires_at, scope) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO restream_destinations (user_id, managed_stream_id, platform, name, server_url, stream_key, enabled, auto_start, quality_preset, custom_video_bitrate, custom_audio_bitrate, custom_fps, custom_encoder_preset, srt_latency_ms, srt_passphrase, channel_url, chat_relay, powerchat_relay, powerchat_count_views) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO schema_migrations (id, mode, duration_ms) VALUES (?, ?, ?)",
-- "INSERT INTO search_doc_pushes (user_id, hash, revision, deleted, pushed_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET hash = excluded.hash, revision = excluded.revision, deleted = excluded.deleted, pushed_at = excluded.pushed_at",
-- "INSERT INTO search_media_pushes (kind, media_id, hash, revision, deleted, pushed_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(kind, media_id) DO UPDATE SET hash = excluded.hash, revision = excluded.revision, deleted = excluded.deleted, pushed_at = excluded.pushed_at",
-- "INSERT INTO site_settings (key, value) VALUES (?, ?)",
-- "INSERT INTO stream_analytics (stream_id, avg_viewers, peak_viewers, unique_chatters, total_messages, total_watch_minutes, new_followers, clips_created, coins_earned, computed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(stream_id) DO UPDATE SET avg_viewers = excluded.avg_viewers, peak_viewers = excluded.peak_viewers, unique_chatters = excluded.unique_chatters, total_messages = excluded.total_messages, total_watch_minutes = excluded.total_watch_minutes, new_followers = excluded.new_followers, clips_created = excluded.clips_created, coins_earned = excluded.coins_earned, computed_at = CURRENT_TIMESTAMP",
-- "INSERT INTO stream_controls (stream_id, label, command, icon, control_type, key_binding, cooldown_ms, sort_order, btn_color, btn_bg, btn_border_color) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO stream_recaps (stream_id, user_id, json, ai, created_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(stream_id) DO UPDATE SET json = excluded.json, ai = excluded.ai, created_at = CURRENT_TIMESTAMP",
-- "INSERT INTO stream_timeline_events (stream_id, user_id, vod_id, kind, start_sec, end_sec, text, label, confidence, lang, text_en) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO streamer_overviews (user_id, overview, overview_short, model, sources, generated_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET overview = excluded.overview, overview_short = excluded.overview_short, model = excluded.model, sources = excluded.sources, generated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO streams (user_id, channel_id, managed_stream_id, control_config_id, title, description, category, protocol, is_nsfw, thumbnail_url, is_live, started_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, CURRENT_TIMESTAMP)",
-- "INSERT INTO subject_projection (subject_id, network_user_id, revision, username, display_name, avatar_url, profile_color, role, banned, updated_at) VALUES (@subject, @nid, @rev, @username, @display_name, @avatar_url, @profile_color, @role, @banned, CURRENT_TIMESTAMP) ON CONFLICT(subject_id) DO UPDATE SET network_user_id = excluded.network_user_id, revision = excluded.revision, username = excluded.username, display_name = excluded.display_name, avatar_url = excluded.avatar_url, profile_color = excluded.profile_color, role = excluded.role, banned = excluded.banned, updated_at = excluded.updated_at",
-- "INSERT INTO subscriptions (subscriber_id, streamer_id, tier, provider, provider_ref, price_cents, currency, status, is_active, current_period_end, auto_renew) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO themes (name, slug, author_id, description, mode, variables, preview_colors, is_builtin, is_public, tags) VALUES (?, ?, ?, ?, ?, ?, ?, 0, 1, ?)",
-- "INSERT INTO themes (name, slug, mode, description, variables, preview_colors, is_builtin, is_public, tags) VALUES (?, ?, ?, ?, ?, ?, 1, 1, ?)",
-- "INSERT INTO token_revocations (subject_id, valid_after_ms, reason, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT(subject_id) DO UPDATE SET valid_after_ms = excluded.valid_after_ms, reason = excluded.reason, updated_at = excluded.updated_at WHERE excluded.valid_after_ms > token_revocations.valid_after_ms",
-- "INSERT INTO transactions (from_user_id, to_user_id, stream_id, amount, type, status, message) VALUES (?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO user_preferences (user_id, chat_settings, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET chat_settings = excluded.chat_settings, updated_at = CURRENT_TIMESTAMP",
-- "INSERT INTO user_themes (user_id, theme_id, custom_variables, is_custom) VALUES (?, ?, ?, ?)",
-- "INSERT INTO username_history (user_id, old_username, new_username) VALUES (?, ?, ?)",
-- "INSERT INTO users (username, email, password_hash, display_name, stream_key) VALUES (?, ?, ?, ?, ?)",
-- "INSERT INTO verification_keys (key, target_username, note, created_by) VALUES (?, ?, ?, ?)",
-- "INSERT INTO vibe_coding_events ( managed_stream_id, user_id, stream_id, session_key, event_id, sequence_num, event_type, visibility, depth, summary, payload_json ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(managed_stream_id, event_id) DO UPDATE SET stream_id = excluded.stream_id, sequence_num = excluded.sequence_num, event_type = excluded.event_type, visibility = excluded.visibility, depth = excluded.depth, summary = excluded.summary, payload_json = excluded.payload_json, created_at = CURRENT_TIMESTAMP",
-- "INSERT INTO vibe_coding_sessions ( managed_stream_id, user_id, session_key, slot_slug, workspace_name, machine_name, extension_version, publisher_id, publisher_label, publisher_vendor, publisher_client_type, publisher_client_name, publisher_client_version, publisher_capabilities_json, publisher_depth, status, last_event_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', CURRENT_TIMESTAMP) ON CONFLICT(managed_stream_id, session_key) DO UPDATE SET workspace_name = excluded.workspace_name, machine_name = excluded.machine_name, extension_version = excluded.extension_version, publisher_id = excluded.publisher_id, publisher_label = excluded.publisher_label, publisher_vendor = excluded.publisher_vendor, publisher_client_type = excluded.publisher_client_type, publisher_client_name = excluded.publisher_client_name, publisher_client_version = excluded.publisher_client_version, publisher_capabilities_json = excluded.publisher_capabilities_json, publisher_depth = excluded.publisher_depth, slot_slug = excluded.slot_slug, status = 'active', ended_at = NULL, last_event_at = CURRENT_TIMESTAMP",
-- "INSERT INTO viewer_samples (viewers, live_streams) VALUES (?, ?)",
-- "INSERT INTO viewer_snapshots (stream_id, viewer_count, chat_messages_5m) VALUES (?, ?, ?)",
-- "INSERT INTO watch_time (user_id, stream_id, minutes_watched) VALUES (?, ?, 1)",
-- "INSERT OR IGNORE INTO app_state (key, value, updated_at) SELECT key, value, COALESCE(updated_at, CURRENT_TIMESTAMP) FROM site_settings WHERE key IN ('auto_clip_backfill','auto_clip_log','daily_easter_egg','home_hero_moments','home_hero_slogans') OR key LIKE 'ai_whole_overview_%'",
-- "INSERT OR IGNORE INTO approved_ips (channel_id, ip_address, approved_by, source) VALUES (?, ?, ?, ?)",
-- "INSERT OR IGNORE INTO arena_beef_hype (beef_id, side, voter_key) VALUES (?, ?, ?)",
-- "INSERT OR IGNORE INTO billing_actions (action, idempotency_key, live_user_id, live_ref, method, path, request_json) VALUES (?, ?, ?, ?, ?, ?, ?)",
-- "INSERT OR IGNORE INTO channel_ai_config (user_id, enabled, num_ambient_bots, pacing_seconds, persona, transcribe_enabled, vision_enabled, use_shared_key, daily_budget_cents, byo_key, byo_base_url, byo_model) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT OR IGNORE INTO channel_points_log (idempotency_key, user_id, streamer_id, delta, reason) VALUES (?, ?, ?, ?, ?)",
-- "INSERT OR IGNORE INTO channels (user_id, title, description, category, protocol) VALUES (?, ?, ?, ?, ?)",
-- "INSERT OR IGNORE INTO clip_ai_state (clip_id) VALUES (?)",
-- "INSERT OR IGNORE INTO comment_thread_refs (content_type, content_id, thread_id, access_id) VALUES (?, ?, ?, ?)",
-- "INSERT OR IGNORE INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?)",
-- "INSERT OR IGNORE INTO control_whitelist (channel_id, user_id, added_by) VALUES (?, ?, ?)",
-- "INSERT OR IGNORE INTO easter_egg_solves (egg_date, solver_key, user_id) VALUES (?, ?, ?)",
-- "INSERT OR IGNORE INTO follows (follower_id, streamer_id) VALUES (?, ?)",
-- "INSERT OR IGNORE INTO linked_accounts (service, service_user_id, service_username, user_id) VALUES ('network', ?, ?, ?)",
-- "INSERT OR IGNORE INTO linked_accounts (user_id, service, service_user_id, service_username) VALUES (?, 'network', ?, ?)",
-- "INSERT OR IGNORE INTO powerchat_webhook_deliveries (delivery_id, event_type) VALUES (?, ?)",
-- "INSERT OR IGNORE INTO site_settings (key, value, description, type) VALUES (?, ?, ?, ?)",
-- "INSERT OR IGNORE INTO stream_memories (stream_id, user_id, offset_seconds, description, tags, thumbnail_url, transcript_json) VALUES (?, ?, ?, ?, ?, ?, ?)",
-- "INSERT OR IGNORE INTO subject_merges (merge_id, from_subject, into_subject, from_user_id, into_user_id, outcome) VALUES (?, ?, ?, ?, ?, ?)",
-- "INSERT OR IGNORE INTO tips_deliveries (idempotency_key, effect, state, claimed_at) VALUES (?, ?, 'pending', ?)",
-- "INSERT OR IGNORE INTO user_cosmetics (user_id, item_id, category) VALUES (?, ?, ?)",
-- "INSERT OR IGNORE INTO users (username, password_hash, display_name, role) VALUES (?, ?, ?, 'user')",
-- "INSERT OR IGNORE INTO vod_ai_state (vod_id) VALUES (?)",
-- "INSERT OR REPLACE INTO site_settings (key, value, description, type) VALUES ('bucks_bits_migration_done', '1', 'Internal: decimal→bit Vibes migration applied (see schema_migrations)', 'boolean')",
-- "INSERT OR REPLACE INTO translations (key, src, dst, text) VALUES (?, ?, ?, ?)",
-- "INSERT OR REPLACE INTO user_equipped (user_id, slot, item_id) VALUES (?, ?, ?)",
-- "SELECT * FROM ai_chatbot_configs",
-- "SELECT * FROM ai_chatbot_configs WHERE user_id = ?",
-- "SELECT * FROM ai_viewer_log WHERE channel_user_id = ?",
-- "SELECT * FROM ai_viewer_log WHERE channel_user_id = ? ORDER BY id DESC LIMIT ?",
-- "SELECT * FROM ai_viewer_threads WHERE channel_user_id = ? AND state = 'closed' AND topic IS NOT NULL ORDER BY updated_at DESC LIMIT ?",
-- "SELECT * FROM ai_viewer_threads WHERE channel_user_id = ? AND state = 'open' ORDER BY updated_at DESC LIMIT ?",
-- "SELECT * FROM ai_viewer_threads WHERE id = ?",
-- "SELECT * FROM api_keys WHERE key_hash = ? AND is_active = 1",
-- "SELECT * FROM arena_beefs WHERE a_user_id = ? OR b_user_id = ? ORDER BY CASE WHEN status = 'open' THEN 0 ELSE 1 END, COALESCE(resolved_at, opened_at) DESC LIMIT ?",
-- "SELECT * FROM arena_beefs WHERE id = ?",
-- "SELECT * FROM arena_beefs WHERE id = ? AND status = ?",
-- "SELECT * FROM arena_beefs WHERE status = 'open'",
-- "SELECT * FROM arena_beefs WHERE status = 'open' AND ((a_user_id = ? AND b_user_id = ?) OR (a_user_id = ? AND b_user_id = ?))",
-- "SELECT * FROM arena_beefs WHERE status = 'open' AND (a_user_id = ? OR b_user_id = ?) ORDER BY opened_at DESC",
-- "SELECT * FROM arena_beefs WHERE status = 'open' ORDER BY COALESCE(last_b_at, last_a_at, opened_at) DESC",
-- "SELECT * FROM arena_beefs WHERE status = 'resolved' AND ((a_user_id = ? AND b_user_id = ?) OR (a_user_id = ? AND b_user_id = ?)) ORDER BY resolved_at DESC LIMIT 10",
-- "SELECT * FROM arena_beefs WHERE status = 'resolved' ORDER BY resolved_at DESC LIMIT ?",
-- "SELECT * FROM arena_mic_moments WHERE id = ?",
-- "SELECT * FROM arena_mic_moments WHERE quality >= 5 AND user_id = ? ORDER BY COALESCE(said_at, created_at) DESC, id DESC LIMIT ?",
-- "SELECT * FROM arena_mic_moments WHERE quality >= 5 ORDER BY COALESCE(said_at, created_at) DESC, id DESC LIMIT ?",
-- "SELECT * FROM arena_mic_moments WHERE user_id = ? AND quality >= 5 ORDER BY quality DESC, id DESC LIMIT ?",
-- "SELECT * FROM arena_mic_moments WHERE user_id = ? ORDER BY COALESCE(said_at, created_at) DESC, id DESC LIMIT 1",
-- "SELECT * FROM arena_profiles WHERE user_id = ?",
-- "SELECT * FROM arena_trash_levels WHERE user_id = ?",
-- "SELECT * FROM arena_trash_levels WHERE xp > 0 ORDER BY xp DESC LIMIT ?",
-- "SELECT * FROM bans WHERE id = ?",
-- "SELECT * FROM bans WHERE ip_address IN (?, ?) AND (stream_id = ? OR stream_id IS NULL) AND (expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP) LIMIT 1",
-- "SELECT * FROM bans WHERE ip_address LIKE '%/%' AND (expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP)",
-- "SELECT * FROM bans WHERE user_id = ? AND (stream_id = ? OR stream_id IS NULL) AND (expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP) LIMIT 1",
-- "SELECT * FROM billing_actions WHERE action = 'media_charge' AND live_ref = ? AND status = 'done' ORDER BY id DESC LIMIT 1",
-- "SELECT * FROM billing_actions WHERE action IN ('checkout', 'subscribe_intent') AND status = 'done' AND json_extract(response_json, '$.intent.provider') = 'paypal' AND json_extract(response_json, '$.intent.provider_ref') = ? ORDER BY id DESC LIMIT 1",
-- "SELECT * FROM billing_actions WHERE id = ?",
-- "SELECT * FROM billing_actions WHERE idempotency_key = ?",
-- "SELECT * FROM camera_presets WHERE camera_id = ? ORDER BY name",
-- "SELECT * FROM camera_presets WHERE id = ?",
-- "SELECT * FROM camera_profiles WHERE id = ?",
-- "SELECT * FROM camera_profiles WHERE stream_id = ? AND is_active = 1 ORDER BY name",
-- "SELECT * FROM camera_profiles WHERE user_id = ? ORDER BY created_at DESC",
-- "SELECT * FROM cameras WHERE stream_id = ?",
-- "SELECT * FROM channel_ai_bots WHERE channel_user_id = ?",
-- "SELECT * FROM channel_ai_bots WHERE channel_user_id = ? AND username = ?",
-- "SELECT * FROM channel_ai_bots WHERE channel_user_id = ? ORDER BY last_active_at DESC, created_at ASC",
-- "SELECT * FROM channel_ai_bots WHERE id = ?",
-- "SELECT * FROM channel_ai_config WHERE user_id = ?",
-- "SELECT * FROM channels WHERE id = ?",
-- "SELECT * FROM channels WHERE user_id = ?",
-- "SELECT * FROM channels WHERE user_id IN (?)",
-- "SELECT * FROM clip_ai_state WHERE clip_id = ?",
-- "SELECT * FROM coin_rewards WHERE id = ?",
-- "SELECT * FROM coin_rewards WHERE is_global = 1 AND is_enabled = 1 ORDER BY sort_order, cost",
-- "SELECT * FROM coin_rewards WHERE streamer_id = ? AND is_enabled = 1 ORDER BY sort_order, cost",
-- "SELECT * FROM coin_transactions WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",
-- "SELECT * FROM control_config_buttons WHERE config_id = ? ORDER BY sort_order",
-- "SELECT * FROM control_configs WHERE id = ?",
-- "SELECT * FROM control_configs WHERE user_id = ?",
-- "SELECT * FROM control_configs WHERE user_id = ? ORDER BY created_at DESC",
-- "SELECT * FROM donation_goals WHERE id = ?",
-- "SELECT * FROM donation_goals WHERE user_id = ? AND (is_active = 1 OR (reached_at IS NOT NULL AND reached_at > datetime('now', ?))) ORDER BY sort_order ASC, created_at ASC",
-- "SELECT * FROM donation_goals WHERE user_id = ? AND is_active = 1 ORDER BY created_at",
-- "SELECT * FROM donation_goals WHERE user_id = ? AND is_active = 1 ORDER BY sort_order ASC, created_at ASC",
-- "SELECT * FROM donation_goals WHERE user_id = ? ORDER BY is_active DESC, sort_order ASC, created_at ASC",
-- "SELECT * FROM kick_channel_cache WHERE slug = ?",
-- "SELECT * FROM lineage_unresolved ORDER BY last_at DESC LIMIT ?",
-- "SELECT * FROM lineage_unresolved WHERE reason = ? ORDER BY last_at DESC LIMIT ?",
-- "SELECT * FROM linked_accounts WHERE service = 'network' AND service_user_id = ?",
-- "SELECT * FROM media_request_settings WHERE user_id = ?",
-- "SELECT * FROM media_requests WHERE charge_state IN ('unknown', 'charging') AND currency = 'opencoins' AND requested_at <= ? ORDER BY id LIMIT ?",
-- "SELECT * FROM media_requests WHERE id = ?",
-- "SELECT * FROM media_requests WHERE streamer_id = ? AND canonical_url = ? AND status IN ('pending', 'playing') ORDER BY id DESC LIMIT 1",
-- "SELECT * FROM media_requests WHERE streamer_id = ? AND id = ?",
-- "SELECT * FROM media_requests WHERE streamer_id = ? AND status = 'pending' ORDER BY queue_position ASC, requested_at ASC, id ASC LIMIT 1",
-- "SELECT * FROM media_requests WHERE streamer_id = ? AND status = 'pending' ORDER BY queue_position ASC, requested_at ASC, id ASC LIMIT ?",
-- "SELECT * FROM media_requests WHERE streamer_id = ? AND status = 'playing' ORDER BY started_at DESC, id DESC LIMIT 1",
-- "SELECT * FROM media_requests WHERE streamer_id = ? AND status IN ('played', 'skipped', 'removed', 'failed') AND charge_state IS NOT 'charging' ORDER BY COALESCE(ended_at, requested_at) DESC, id DESC LIMIT ?",
-- "SELECT * FROM news_settings WHERE scope = 'global' AND source_id != '_master'",
-- "SELECT * FROM opencoin_admin_grants WHERE client_key = ?",
-- "SELECT * FROM openre_sessions WHERE session_id = ?",
-- "SELECT * FROM openre_sessions WHERE state = 'live'",
-- "SELECT * FROM openre_sessions WHERE stream_id = ? ORDER BY updated_at DESC LIMIT 1",
-- "SELECT * FROM payment_orders WHERE id = ?",
-- "SELECT * FROM payment_orders WHERE provider = 'powerchat' AND status = 'pending' AND created_at >= datetime('now', ?) ORDER BY id ASC",
-- "SELECT * FROM payment_orders WHERE provider = ? AND provider_ref = ? ORDER BY id DESC LIMIT 1",
-- "SELECT * FROM platform_connections WHERE id = ?",
-- "SELECT * FROM platform_connections WHERE user_id = ? AND platform = ?",
-- "SELECT * FROM platform_connections WHERE user_id = ? ORDER BY platform",
-- "SELECT * FROM powerchat_connections WHERE LOWER(powerchat_username) = LOWER(?)",
-- "SELECT * FROM powerchat_connections WHERE powerchat_user_id = ?",
-- "SELECT * FROM powerchat_connections WHERE user_id = ?",
-- "SELECT * FROM restream_destinations WHERE id = ?",
-- "SELECT * FROM restream_destinations WHERE managed_stream_id = ? ORDER BY created_at",
-- "SELECT * FROM restream_destinations WHERE user_id = ? AND managed_stream_id = ? ORDER BY created_at",
-- "SELECT * FROM restream_destinations WHERE user_id = ? AND managed_stream_id IS NULL ORDER BY created_at",
-- "SELECT * FROM restream_destinations WHERE user_id = ? ORDER BY created_at",
-- "SELECT * FROM robotstreamer_integrations WHERE user_id = ? AND managed_stream_id = ?",
-- "SELECT * FROM robotstreamer_integrations WHERE user_id = ? AND managed_stream_id IS NULL",
-- "SELECT * FROM site_settings ORDER BY key",
-- "SELECT * FROM site_settings WHERE key = ?",
-- "SELECT * FROM stream_analytics WHERE stream_id = ?",
-- "SELECT * FROM stream_controls WHERE id = ? AND stream_id = ?",
-- "SELECT * FROM stream_controls WHERE id = ? AND stream_id = ? AND is_enabled = 1",
-- "SELECT * FROM stream_controls WHERE stream_id = ? AND command = ? AND is_enabled = 1 LIMIT 1",
-- "SELECT * FROM stream_controls WHERE stream_id = ? ORDER BY sort_order",
-- "SELECT * FROM stream_memories WHERE stream_id = ? AND offset_seconds BETWEEN ? AND ? ORDER BY offset_seconds ASC",
-- "SELECT * FROM stream_memories WHERE stream_id = ? ORDER BY offset_seconds ASC",
-- "SELECT * FROM stream_memories WHERE stream_id = ? ORDER BY offset_seconds DESC LIMIT 1",
-- "SELECT * FROM stream_memories WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",
-- "SELECT * FROM streamer_overviews WHERE user_id = ?",
-- "SELECT * FROM streams WHERE id = ?",
-- "SELECT * FROM streams WHERE user_id = ? AND is_live = 1 ORDER BY started_at DESC LIMIT 1",
-- "SELECT * FROM subject_projection WHERE subject_id = ?",
-- "SELECT * FROM subscriptions WHERE id = ?",
-- "SELECT * FROM subscriptions WHERE provider = ? AND provider_ref = ? ORDER BY id DESC LIMIT 1",
-- "SELECT * FROM subscriptions WHERE status = 'active' AND current_period_end IS NOT NULL AND datetime(current_period_end) <= CURRENT_TIMESTAMP ORDER BY datetime(current_period_end) ASC LIMIT ?",
-- "SELECT * FROM subscriptions WHERE subscriber_id = ? AND streamer_id = ?",
-- "SELECT * FROM subscriptions WHERE subscriber_id = ? AND streamer_id = ? AND status = 'active' AND (current_period_end IS NULL OR datetime(current_period_end) > CURRENT_TIMESTAMP) LIMIT 1",
-- "SELECT * FROM themes WHERE id = ?",
-- "SELECT * FROM tips_deliveries WHERE idempotency_key = ?",
-- "SELECT * FROM transactions WHERE from_user_id = ? OR to_user_id = ? ORDER BY created_at DESC LIMIT ?",
-- "SELECT * FROM transactions WHERE id = ? AND status = ?",
-- "SELECT * FROM user_themes WHERE user_id = ? ORDER BY id DESC LIMIT 1",
-- "SELECT * FROM users WHERE display_name = ? COLLATE NOCASE",
-- "SELECT * FROM users WHERE id = ?",
-- "SELECT * FROM users WHERE stream_key = ?",
-- "SELECT * FROM users WHERE username = ? COLLATE NOCASE",
-- "SELECT * FROM verification_keys WHERE key = ?",
-- "SELECT * FROM verification_keys WHERE target_username = ? COLLATE NOCASE AND status = 'active'",
-- "SELECT * FROM vod_ai_state WHERE vod_id = ?",
-- "SELECT * FROM watch_time WHERE user_id = ? AND stream_id = ?",
-- "SELECT 1 AS ok",
-- "SELECT 1 AS x FROM stream_timeline_events WHERE vod_id IS NULL LIMIT 1",
-- "SELECT 1 AS x FROM users WHERE username = ? COLLATE NOCASE",
-- "SELECT 1 FROM ai_chatbot_configs WHERE user_id = ?",
-- "SELECT 1 FROM approved_ips WHERE channel_id = ? AND ip_address = ?",
-- "SELECT 1 FROM channel_ai_config WHERE user_id = ?",
-- "SELECT 1 FROM channel_points WHERE user_id = ? AND streamer_id = ?",
-- "SELECT 1 FROM control_whitelist WHERE channel_id = ? AND user_id = ?",
-- "SELECT 1 FROM easter_egg_solves WHERE egg_date = ? AND solver_key = ?",
-- "SELECT 1 FROM managed_streams WHERE user_id = ? AND ingest_authority = 'openre' LIMIT 1",
-- "SELECT 1 FROM media_request_settings WHERE user_id = ? LIMIT 1",
-- "SELECT 1 FROM module_summary_pushes WHERE user_id = ? AND namespace = 'live.loyalty'",
-- "SELECT 1 FROM powerchat_connections WHERE user_id = ? LIMIT 1",
-- "SELECT 1 FROM site_settings WHERE key = 'bucks_bits_migration_done'",
-- "SELECT 1 FROM sqlite_master WHERE type = 'index' AND name = ?",
-- "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?",
-- "SELECT 1 FROM sqlite_master WHERE type='index' AND name=?",
-- "SELECT 1 FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND created_at >= datetime('now', '-30 minutes') LIMIT 1",
-- "SELECT 1 FROM streams WHERE user_id = ? AND is_live = 1 LIMIT 1",
-- "SELECT 1 FROM streams WHERE user_id = ? LIMIT 1",
-- "SELECT 1 FROM subject_merges WHERE merge_id = ?",
-- "SELECT 1 FROM user_cosmetics WHERE user_id = ? AND item_id = ?",
-- "SELECT AVG(CASE WHEN sampled_at >= datetime('now','-1 day') AND viewers > 0 THEN viewers END) AS vAvg24, MAX(CASE WHEN sampled_at >= datetime('now','-1 day') THEN viewers END) AS vPeak24, AVG(CASE WHEN viewers > 0 THEN viewers END) AS vAvg7, AVG(CASE WHEN sampled_at >= datetime('now','-1 day') AND live_streams > 0 THEN live_streams END) AS lAvg24, MAX(CASE WHEN sampled_at >= datetime('now','-1 day') THEN live_streams END) AS lPeak24, AVG(CASE WHEN live_streams > 0 THEN live_streams END) AS lAvg7, COUNT(*) AS samples FROM viewer_samples WHERE sampled_at >= datetime('now','-7 days')",
-- "SELECT AVG(viewer_count) as avg_vc FROM viewer_snapshots WHERE stream_id = ?",
-- "SELECT CASE WHEN a_user_id = ? THEN b_user_id ELSE a_user_id END AS opp, COUNT(*) AS n FROM arena_beefs WHERE (a_user_id = ? OR b_user_id = ?) GROUP BY opp HAVING n >= 2 ORDER BY n DESC LIMIT ?",
-- "SELECT COALESCE(-SUM(amount), 0) AS count FROM coin_transactions WHERE amount < 0",
-- "SELECT COALESCE(MAX(sort_order),-1)+1 AS n FROM donation_goals WHERE user_id = ?",
-- "SELECT COALESCE(NULLIF(ai_category, ''), category) AS category, COUNT(*) AS n FROM streams WHERE user_id = ? AND duration_seconds > 0 AND COALESCE(NULLIF(ai_category, ''), category) IS NOT NULL AND COALESCE(NULLIF(ai_category, ''), category) != '' GROUP BY 1 ORDER BY n DESC LIMIT 1",
-- "SELECT COALESCE(SUM(-amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND amount < 0",
-- "SELECT COALESCE(SUM(-amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', ?) AND amount < 0",
-- "SELECT COALESCE(SUM(COALESCE(end_sec, start_sec + 3) - start_sec), 0) AS s FROM stream_timeline_events WHERE user_id = ? AND kind = 'speech' AND created_at >= datetime('now', ?)",
-- "SELECT COALESCE(SUM(amount), 0) AS count FROM coin_transactions WHERE amount > 0",
-- "SELECT COALESCE(SUM(amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND amount > 0",
-- "SELECT COALESCE(SUM(amount), 0) AS count FROM coin_transactions WHERE created_at >= datetime('now', ?) AND amount > 0",
-- "SELECT COALESCE(SUM(amount), 0) AS count FROM transactions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND type = 'donation' AND created_at >= '1970-01-01 00:00:00'",
-- "SELECT COALESCE(SUM(amount), 0) AS count FROM transactions WHERE created_at >= datetime('now', ?) AND type = 'donation' AND created_at >= '1970-01-01 00:00:00'",
-- "SELECT COALESCE(SUM(amount), 0) AS count FROM transactions WHERE type = 'donation' AND created_at >= ?",
-- "SELECT COALESCE(SUM(amount), 0) AS xp FROM arena_xp_log WHERE user_id = ? AND created_at >= datetime('now', ?)",
-- "SELECT COALESCE(SUM(amount), 0) as c FROM transactions WHERE type = 'donation'",
-- "SELECT COALESCE(SUM(b_user_id = ?), 0) AS targeted, COALESCE(SUM(b_user_id = ? AND responded = 1), 0) AS answered, COALESCE(SUM(winner_user_id = ?), 0) AS wins, COALESCE(SUM(status = 'resolved' AND winner_user_id IS NOT NULL AND winner_user_id != ?), 0) AS losses, COALESCE(SUM(status = 'resolved' AND resolution = 'forfeit' AND winner_user_id != ? AND winner_user_id IS NOT NULL), 0) AS ducked FROM arena_beefs WHERE a_user_id = ? OR b_user_id = ?",
-- "SELECT COALESCE(SUM(balance), 0) AS n FROM channel_points WHERE user_id = ? AND balance > 0",
-- "SELECT COALESCE(SUM(bucks), 0) AS count FROM payment_orders WHERE kind = 'bucks' AND status = 'credited'",
-- "SELECT COALESCE(SUM(bucks), 0) AS count FROM payment_orders WHERE updated_at >= datetime('now', '-14 days') AND updated_at < datetime('now', '-7 days') AND kind = 'bucks' AND status = 'credited'",
-- "SELECT COALESCE(SUM(bucks), 0) AS count FROM payment_orders WHERE updated_at >= datetime('now', ?) AND kind = 'bucks' AND status = 'credited'",
-- "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE created_at >= date('now')",
-- "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE owner_user_id = ? AND created_at >= date('now')",
-- "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE owner_user_id = ? AND created_at >= date('now') AND source = ?",
-- "SELECT COALESCE(SUM(cost_usd),0) AS c FROM ai_usage WHERE source = ? AND COALESCE(provider,'shared') <> 'byo' AND created_at >= date('now')",
-- "SELECT COALESCE(SUM(duration_seconds), 0) AS sec FROM streams WHERE user_id = ? AND duration_seconds > 0 AND id IN (SELECT DISTINCT stream_id FROM stream_timeline_events WHERE user_id = ? AND created_at >= datetime('now', ?))",
-- "SELECT COALESCE(SUM(minutes_watched), 0) AS count FROM watch_time",
-- "SELECT COALESCE(SUM(openvibe_bucks_balance), 0) as c FROM users",
-- "SELECT COALESCE(SUM(viewer_count), 0) AS count FROM streams WHERE is_live = 1",
-- "SELECT COALESCE(SUM(viewer_count), 0) as c FROM streams WHERE is_live = 1",
-- "SELECT COALESCE(SUM(viewer_count),0) AS v, COUNT(*) AS n FROM streams WHERE is_live = 1",
-- "SELECT COALESCE(role,'legacy') AS role, COUNT(*) AS calls, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens, SUM(output_tokens) AS output_tokens, SUM(cost_usd) AS cost_usd, AVG(latency_ms) AS avg_latency_ms FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY role ORDER BY cost_usd DESC",
-- "SELECT COALESCE(source,'platform') AS source, COALESCE(provider,'shared') AS provider, COUNT(*) AS calls, SUM(cost_usd) AS cost_usd, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY source, provider ORDER BY cost_usd DESC",
-- "SELECT COALESCE(text_en, text) AS t, start_sec AS s FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' ORDER BY start_sec ASC",
-- "SELECT COUNT(*) - COUNT(DISTINCT stream_id || ':' || offset_seconds) AS extra FROM stream_memories",
-- "SELECT COUNT(*) AS c FROM channel_ai_config",
-- "SELECT COUNT(*) AS c FROM media_requests WHERE streamer_id = ? AND user_id = ? AND status IN ('pending', 'playing')",
-- "SELECT COUNT(*) AS c FROM stream_memories WHERE stream_id = ?",
-- "SELECT COUNT(*) AS c, COALESCE(MAX(id), 0) AS m, COALESCE(SUM(id * 7 + COALESCE(stream_id, 0) + LENGTH(COALESCE(expires_at, ''))), 0) AS s FROM bans",
-- "SELECT COUNT(*) AS calls, COALESCE(SUM(input_tokens),0) AS input_tokens, COALESCE(SUM(output_tokens),0) AS output_tokens, COALESCE(SUM(cached_tokens),0) AS cached_tokens, COALESCE(SUM(cost_usd),0) AS cost_usd FROM ai_usage WHERE created_at >= date('now', ?)",
-- "SELECT COUNT(*) AS count FROM anon_ip_mappings",
-- "SELECT COUNT(*) AS count FROM anon_ip_mappings WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
-- "SELECT COUNT(*) AS count FROM anon_ip_mappings WHERE created_at >= datetime('now', '-7 days')",
-- "SELECT COUNT(*) AS count FROM anon_ip_mappings WHERE created_at >= datetime('now', ?)",
-- "SELECT COUNT(*) AS count FROM coin_redemptions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND status NOT IN ('rejected', 'refunded')",
-- "SELECT COUNT(*) AS count FROM coin_redemptions WHERE created_at >= datetime('now', ?) AND status NOT IN ('rejected', 'refunded')",
-- "SELECT COUNT(*) AS count FROM coin_redemptions WHERE status NOT IN ('rejected', 'refunded')",
-- "SELECT COUNT(*) AS count FROM donation_goals WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
-- "SELECT COUNT(*) AS count FROM donation_goals WHERE created_at >= datetime('now', ?)",
-- "SELECT COUNT(*) AS count FROM donation_goals WHERE is_active = 1",
-- "SELECT COUNT(*) AS count FROM donation_goals WHERE reached_at IS NOT NULL OR current_amount >= target_amount",
-- "SELECT COUNT(*) AS count FROM follows",
-- "SELECT COUNT(*) AS count FROM follows WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
-- "SELECT COUNT(*) AS count FROM follows WHERE created_at >= datetime('now', ?)",
-- "SELECT COUNT(*) AS count FROM managed_streams WHERE user_id = ?",
-- "SELECT COUNT(*) AS count FROM stream_memories",
-- "SELECT COUNT(*) AS count FROM stream_memories WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
-- "SELECT COUNT(*) AS count FROM stream_memories WHERE created_at >= datetime('now', ?)",
-- "SELECT COUNT(*) AS count FROM stream_memories WHERE user_id = ?",
-- "SELECT COUNT(*) AS count FROM streams",
-- "SELECT COUNT(*) AS count FROM streams WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
-- "SELECT COUNT(*) AS count FROM streams WHERE created_at >= datetime('now', ?)",
-- "SELECT COUNT(*) AS count FROM streams WHERE is_live = 1",
-- "SELECT COUNT(*) AS count FROM subscriptions WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days')",
-- "SELECT COUNT(*) AS count FROM subscriptions WHERE created_at >= datetime('now', ?)",
-- "SELECT COUNT(*) AS count FROM subscriptions WHERE status = 'active' AND (current_period_end IS NULL OR datetime(current_period_end) > CURRENT_TIMESTAMP)",
-- "SELECT COUNT(*) AS count FROM users WHERE COALESCE(is_banned, 0) = 0",
-- "SELECT COUNT(*) AS count FROM users WHERE created_at >= datetime('now', '-14 days') AND created_at < datetime('now', '-7 days') AND COALESCE(is_banned, 0) = 0",
-- "SELECT COUNT(*) AS count FROM users WHERE created_at >= datetime('now', ?) AND COALESCE(is_banned, 0) = 0",
-- "SELECT COUNT(*) AS n FROM ai_usage WHERE kind = 'tts' AND created_at >= date('now')",
-- "SELECT COUNT(*) AS n FROM arena_beef_hype WHERE beef_id = ? AND side = ?",
-- "SELECT COUNT(*) AS n FROM arena_beefs WHERE winner_user_id = ? AND resolved_at >= datetime('now', ?)",
-- "SELECT COUNT(*) AS n FROM arena_mic_moments WHERE created_at >= datetime('now', '-1 day')",
-- "SELECT COUNT(*) AS n FROM arena_mic_moments WHERE said_at >= ?",
-- "SELECT COUNT(*) AS n FROM arena_mic_moments WHERE stream_id = ?",
-- "SELECT COUNT(*) AS n FROM channel_ai_bots WHERE channel_user_id = ?",
-- "SELECT COUNT(*) AS n FROM control_configs WHERE user_id = ?",
-- "SELECT COUNT(*) AS n FROM donation_goals WHERE user_id = ? AND is_active = 1",
-- "SELECT COUNT(*) AS n FROM easter_egg_solves WHERE egg_date = ?",
-- "SELECT COUNT(*) AS n FROM follows WHERE created_at >= ?",
-- "SELECT COUNT(*) AS n FROM follows WHERE streamer_id = ?",
-- "SELECT COUNT(*) AS n FROM follows WHERE streamer_id = ? AND created_at >= ?",
-- "SELECT COUNT(*) AS n FROM follows WHERE streamer_id = ? AND created_at BETWEEN ? AND ?",
-- "SELECT COUNT(*) AS n FROM stream_recaps r JOIN users u ON u.id = r.user_id WHERE r.ai = 1 AND COALESCE(u.is_banned, 0) = 0",
-- "SELECT COUNT(*) AS n FROM stream_timeline_events WHERE user_id = ? AND kind = 'sound' AND (LOWER(label) LIKE '%laugh%' OR LOWER(label) LIKE '%giggle%' OR LOWER(label) LIKE '%chuckle%') AND created_at >= datetime('now', ?)",
-- "SELECT COUNT(*) AS n FROM streams WHERE is_live = 1",
-- "SELECT COUNT(*) AS n FROM streams WHERE started_at >= ?",
-- "SELECT COUNT(*) AS n FROM streams WHERE user_id = ?",
-- "SELECT COUNT(*) AS n FROM subscriptions WHERE streamer_id = ? AND status = 'active' AND (current_period_end IS NULL OR datetime(current_period_end) > CURRENT_TIMESTAMP)",
-- "SELECT COUNT(*) AS n FROM users WHERE created_at >= ?",
-- "SELECT COUNT(*) AS n, COALESCE(AVG(quality), 0) AS avg_q, COALESCE(MAX(quality), 0) AS best_q, COALESCE(SUM(kind IN ('beef_hit', 'callout')), 0) AS beef_hits, COALESCE(SUM(kind = 'trash'), 0) AS trash, COALESCE(SUM(quality >= 7), 0) AS bangers FROM arena_mic_moments WHERE user_id = ? AND created_at >= datetime('now', ?)",
-- "SELECT COUNT(*) AS n, COALESCE(SUM(amount), 0) AS t FROM transactions WHERE type = 'donation' AND (stream_id = ? OR (to_user_id = ? AND created_at BETWEEN ? AND ?))",
-- "SELECT COUNT(*) AS n, COALESCE(SUM(amount), 0) AS total FROM transactions WHERE type = 'donation' AND COALESCE(status, 'completed') NOT IN ('failed', 'refunded', 'pending') AND ((stream_id = ?) OR (to_user_id = ? AND created_at BETWEEN ? AND ?))",
-- "SELECT COUNT(*) AS n, COALESCE(SUM(s.duration_seconds), 0) AS secs, COALESCE(MAX(s.peak_viewers), 0) AS peak, AVG(a.avg_viewers) AS avg FROM streams s LEFT JOIN stream_analytics a ON a.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? AND s.is_live = 0",
-- "SELECT COUNT(*) AS n, MAX(started_at) AS last FROM streams WHERE user_id = ?",
-- "SELECT COUNT(*) AS n, MIN(started_at) AS first FROM streams WHERE user_id = ?",
-- "SELECT COUNT(*) AS streams, COALESCE(SUM(duration_seconds), 0) / 3600.0 AS hours, MAX(ended_at) AS last_live_at FROM streams WHERE user_id = ? AND duration_seconds > 0 AND started_at >= datetime('now', ?)",
-- "SELECT COUNT(*) as c FROM coin_redemptions WHERE reward_id = ? AND stream_id = ?",
-- "SELECT COUNT(*) as c FROM content_views WHERE content_type = ? AND content_id = ?",
-- "SELECT COUNT(*) as c FROM follows WHERE follower_id = ?",
-- "SELECT COUNT(*) as c FROM follows WHERE streamer_id = ?",
-- "SELECT COUNT(*) as c FROM site_settings",
-- "SELECT COUNT(*) as c FROM streams",
-- "SELECT COUNT(*) as c FROM streams WHERE is_live = 1",
-- "SELECT COUNT(*) as c FROM transactions",
-- "SELECT COUNT(*) as c FROM transactions WHERE type = 'cashout' AND status = 'escrow'",
-- "SELECT COUNT(*) as c FROM users",
-- "SELECT COUNT(*) as c FROM users WHERE is_banned = 1",
-- "SELECT COUNT(*) as c FROM users WHERE role = 'mod'",
-- "SELECT COUNT(*) as c FROM users WHERE role IN ('streamer', 'admin')",
-- "SELECT COUNT(*) as c FROM users WHERE username LIKE ? OR display_name LIKE ?",
-- "SELECT COUNT(*) as cnt FROM follows WHERE streamer_id = ?",
-- "SELECT COUNT(*) as cnt FROM follows WHERE streamer_id = ? AND created_at >= ? AND created_at <= ?",
-- "SELECT COUNT(*) as cnt FROM streams WHERE user_id = ? AND duration_seconds > 0",
-- "SELECT COUNT(*) as count FROM follows WHERE streamer_id = ?",
-- "SELECT COUNT(*) as total_streams, SUM(duration_seconds) as total_duration, MAX(peak_viewers) as peak_viewers FROM streams WHERE user_id = ? AND duration_seconds > 0",
-- "SELECT COUNT(*) as total_streams, SUM(s.duration_seconds) as total_duration, MAX(s.peak_viewers) as all_time_peak, AVG(sa.avg_viewers) as avg_viewers_per_stream, SUM(sa.total_messages) as total_messages, SUM(sa.unique_chatters) as total_unique_chatters, SUM(sa.total_watch_minutes) as total_watch_minutes, SUM(sa.new_followers) as total_new_followers, SUM(sa.clips_created) as total_clips FROM streams s LEFT JOIN stream_analytics sa ON sa.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? AND s.duration_seconds > 0",
-- "SELECT COUNT(DISTINCT from_user_id) AS count FROM transactions WHERE type = 'donation' AND from_user_id IS NOT NULL AND created_at >= ?",
-- "SELECT COUNT(DISTINCT from_user_id) AS count FROM transactions WHERE type = 'donation' AND from_user_id IS NOT NULL AND created_at >= datetime('now', ?)",
-- "SELECT COUNT(DISTINCT from_user_id) AS count FROM transactions WHERE type = 'donation' AND from_user_id IS NOT NULL AND created_at >= datetime('now', ?) AND created_at < datetime('now', ?)",
-- "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE is_live = 0 AND ended_at IS NOT NULL",
-- "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE user_id IS NOT NULL",
-- "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE user_id IS NOT NULL AND created_at >= datetime('now', ?)",
-- "SELECT COUNT(DISTINCT user_id) AS count FROM streams WHERE user_id IS NOT NULL AND created_at >= datetime('now', ?) AND created_at < datetime('now', ?)",
-- "SELECT DISTINCT e.user_id FROM stream_timeline_events e JOIN users u ON u.id = e.user_id WHERE e.kind = 'speech' AND e.user_id IS NOT NULL AND e.created_at >= datetime('now', ?) AND COALESCE(u.is_banned, 0) = 0",
-- "SELECT DISTINCT il.user_id, u.role FROM ip_log il JOIN users u ON u.id = il.user_id WHERE il.ip_address = ? AND il.user_id IS NOT NULL",
-- "SELECT DISTINCT platform FROM restream_destinations WHERE user_id = ? AND platform IN ('twitch', 'youtube', 'kick')",
-- "SELECT DISTINCT rd.user_id FROM restream_destinations rd WHERE rd.managed_stream_id IS NULL",
-- "SELECT DISTINCT s.user_id, u.stream_key, u.username, u.display_name, c.id AS channel_id, c.title AS channel_title, c.protocol AS channel_protocol, c.category AS channel_category, c.is_nsfw AS channel_is_nsfw FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN channels c ON c.user_id = s.user_id WHERE s.user_id NOT IN (SELECT user_id FROM managed_streams) AND u.stream_key IS NOT NULL",
-- "SELECT DISTINCT title FROM streams WHERE user_id = ? AND duration_seconds > 0 ORDER BY started_at DESC LIMIT 8",
-- "SELECT DISTINCT title FROM streams WHERE user_id = ? AND title IS NOT NULL AND title != '' ORDER BY id DESC LIMIT 10",
-- "SELECT DISTINCT user_id AS id FROM streams UNION SELECT user_id FROM search_doc_pushes",
-- "SELECT DISTINCT user_id FROM linked_accounts WHERE service = 'network' AND subject_id = ?",
-- "SELECT MAX(anon_num) as m FROM anon_ip_mappings",
-- "SELECT MAX(queue_position) AS max_pos FROM media_requests WHERE streamer_id = ? AND status = 'pending'",
-- "SELECT ROUND(AVG(viewer_count), 1) AS a, COUNT(*) AS k FROM viewer_snapshots WHERE stream_id = ?",
-- "SELECT ROUND(SUM(COALESCE(duration_seconds, CASE WHEN ended_at IS NOT NULL THEN (julianday(ended_at) - julianday(started_at)) * 86400 ELSE (julianday('now') - julianday(started_at)) * 86400 END)) / 3600.0, 1) AS h FROM streams WHERE started_at >= ?",
-- "SELECT SUM(CASE WHEN event = 'line' THEN 1 ELSE 0 END) AS lines, SUM(CASE WHEN event = 'tick' THEN 1 ELSE 0 END) AS ticks, SUM(CASE WHEN event = 'skip' THEN 1 ELSE 0 END) AS skips, COALESCE(SUM(cost_usd),0) AS cost_usd, COALESCE(SUM(tokens_in),0) AS tokens_in, COALESCE(SUM(tokens_cached),0) AS tokens_cached FROM ai_viewer_log WHERE channel_user_id = ? AND created_at > datetime('now', ?)",
-- "SELECT SUM(CASE WHEN winner_user_id = ? THEN 1 ELSE 0 END) AS wins, SUM(CASE WHEN status = 'resolved' AND winner_user_id IS NOT NULL AND winner_user_id != ? THEN 1 ELSE 0 END) AS losses, SUM(CASE WHEN status = 'resolved' AND winner_user_id IS NULL THEN 1 ELSE 0 END) AS draws FROM arena_beefs WHERE a_user_id = ? OR b_user_id = ?",
-- "SELECT SUM(coins_earned) as total FROM watch_time WHERE stream_id = ?",
-- "SELECT SUM(minutes_watched) as total FROM watch_time WHERE stream_id = ?",
-- "SELECT SUM(minutes_watched) as total FROM watch_time WHERE user_id = ?",
-- "SELECT SUM(persona_json IS NOT NULL) AS personas, SUM(image_path IS NOT NULL) AS images, SUM(quotes_json IS NOT NULL) AS quotes FROM arena_profiles",
-- "SELECT SUM(status = 'open') AS open, SUM(status = 'resolved') AS resolved FROM arena_beefs",
-- "SELECT a.owner_user_id AS user_id, u.username, COUNT(*) AS calls, SUM(a.cost_usd) AS cost_usd, SUM(a.input_tokens) AS input_tokens, SUM(a.cached_tokens) AS cached_tokens, SUM(a.output_tokens) AS output_tokens, SUM(CASE WHEN a.created_at >= date('now') THEN a.cost_usd ELSE 0 END) AS cost_today, SUM(CASE WHEN a.provider = 'byo' THEN a.cost_usd ELSE 0 END) AS cost_byo FROM ai_usage a LEFT JOIN users u ON u.id = a.owner_user_id WHERE a.created_at >= date('now', ?) AND a.owner_user_id IS NOT NULL GROUP BY a.owner_user_id ORDER BY cost_usd DESC LIMIT 100",
-- "SELECT ai.*, u.username as approved_by_username FROM approved_ips ai LEFT JOIN users u ON ai.approved_by = u.id WHERE ai.channel_id = ? ORDER BY ai.created_at DESC LIMIT ? OFFSET ?",
-- "SELECT ai_derivation_enabled FROM channels WHERE user_id = ?",
-- "SELECT ai_overview_short, ai_overview FROM clip_ai_state WHERE clip_id = ?",
-- "SELECT ai_overview_short, ai_overview FROM vod_ai_state WHERE vod_id = ?",
-- "SELECT aimed_at, COUNT(*) AS n FROM arena_mic_moments WHERE user_id = ? AND aimed_at IS NOT NULL AND target_user_id IS NULL GROUP BY aimed_at ORDER BY n DESC LIMIT 6",
-- "SELECT anon_num FROM anon_ip_mappings WHERE ip = ?",
-- "SELECT applied_at FROM schema_migrations WHERE id = ?",
-- "SELECT balance FROM channel_points WHERE user_id = ? AND streamer_id = ?",
-- "SELECT broadcast_settings FROM managed_streams WHERE id = ? AND user_id = ?",
-- "SELECT c.*, u.username, u.display_name, u.avatar_url, u.profile_color, u.bio, u.stream_key, u.role, u.is_owner FROM channels c JOIN users u ON c.user_id = u.id WHERE u.username = ? COLLATE NOCASE",
-- "SELECT category, is_nsfw FROM streams WHERE id = ?",
-- "SELECT chat_language FROM channels WHERE user_id = ?",
-- "SELECT chat_settings FROM user_preferences WHERE user_id = ?",
-- "SELECT clip_id AS id, s.* FROM clip_ai_state s WHERE (ai_overview IS NULL OR ai_overview = '') AND (ai_overview_short IS NULL OR ai_overview_short = '' OR ai_overview_short LIKE '%…') ORDER BY clip_id DESC LIMIT ?",
-- "SELECT clip_id AS id, s.* FROM clip_ai_state s WHERE ai_transcript_json IS NULL AND (transcript_status IS NULL OR transcript_status IN ('pending','retry')) AND (transcript_next_at IS NULL OR transcript_next_at <= CURRENT_TIMESTAMP) ORDER BY (transcript_status='retry'), clip_id DESC LIMIT ?",
-- "SELECT clip_id FROM clip_ai_state WHERE COALESCE(clip_notified,0) = 0 AND clip_notify_at IS NOT NULL AND clip_notify_at <= CURRENT_TIMESTAMP LIMIT ?",
-- "SELECT clips_created FROM stream_analytics WHERE stream_id = ?",
-- "SELECT consecutive_failures FROM restream_destinations WHERE id = ?",
-- "SELECT created_at FROM anon_ip_mappings WHERE anon_num = ?",
-- "SELECT created_at FROM anon_ip_mappings WHERE ip = ?",
-- "SELECT created_at FROM coin_redemptions WHERE reward_id = ? AND user_id = ? ORDER BY created_at DESC LIMIT 1",
-- "SELECT cw.id, cw.user_id, u.username, u.display_name, cw.created_at FROM control_whitelist cw JOIN users u ON cw.user_id = u.id WHERE cw.channel_id = ? ORDER BY cw.created_at DESC",
-- "SELECT date(created_at) AS day, COUNT(*) AS calls, SUM(input_tokens) AS input_tokens, SUM(output_tokens) AS output_tokens, SUM(cost_usd) AS cost_usd FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY day ORDER BY day DESC",
-- "SELECT default_clip_visibility, slot_clip_recording_enabled FROM managed_streams WHERE id = ?",
-- "SELECT default_vod_visibility FROM managed_streams WHERE id = ?",
-- "SELECT description FROM stream_memories WHERE user_id = ? ORDER BY captured_at DESC LIMIT 4",
-- "SELECT description, category, chat_language FROM channels WHERE user_id = ?",
-- "SELECT e.id, e.stream_id, e.vod_id, e.start_sec, e.end_sec, e.text, s.started_at FROM stream_timeline_events e LEFT JOIN streams s ON s.id = e.stream_id WHERE e.user_id = ? AND e.kind = 'speech' AND e.id > ? AND e.created_at >= datetime('now', ?) AND NOT EXISTS (SELECT 1 FROM streams l WHERE l.id = e.stream_id AND l.is_live = 1) ORDER BY e.stream_id, e.start_sec LIMIT 4000",
-- "SELECT enabled FROM news_settings WHERE scope = 'global' AND source_id = '_master'",
-- "SELECT enabled FROM news_settings WHERE scope = 'user' AND scope_id = ? AND source_id = '_master'",
-- "SELECT f.created_at, fu.username AS follower_username, fu.display_name AS follower_display, su.username AS streamer_username, su.display_name AS streamer_display FROM follows f JOIN users fu ON fu.id = f.follower_id JOIN users su ON su.id = f.streamer_id ORDER BY f.id DESC LIMIT 1",
-- "SELECT follower_id FROM follows WHERE streamer_id = ?",
-- "SELECT from_user_id, u.username, u.display_name, u.avatar_url, SUM(amount) as total_donated FROM transactions t JOIN users u ON t.from_user_id = u.id WHERE t.stream_id = ? AND t.type = 'donation' AND t.status = 'completed' GROUP BY from_user_id ORDER BY total_donated DESC LIMIT ?",
-- "SELECT g.id, g.title, g.current_amount, g.target_amount, g.image_url, u.username, u.display_name FROM donation_goals g JOIN users u ON u.id = g.user_id WHERE g.is_active = 1 AND g.target_amount > 0 ORDER BY (CAST(g.current_amount AS REAL) / g.target_amount) DESC, g.current_amount DESC LIMIT 3",
-- "SELECT hash, revision, deleted FROM search_doc_pushes WHERE user_id = ?",
-- "SELECT hash, revision, deleted FROM search_media_pushes WHERE kind = ? AND media_id = ?",
-- "SELECT hash, strftime('%s', pushed_at) * 1000 AS at FROM module_summary_pushes WHERE user_id = ? AND namespace = ?",
-- "SELECT id FROM billing_actions WHERE idempotency_key = ?",
-- "SELECT id FROM channels WHERE user_id = ?",
-- "SELECT id FROM coin_transactions WHERE user_id = ? AND type = 'follow_bonus' AND message LIKE '%streamer:' || ? || '%'",
-- "SELECT id FROM follows WHERE follower_id = ? AND streamer_id = ?",
-- "SELECT id FROM managed_streams WHERE user_id = ? AND slug = ? COLLATE NOCASE",
-- "SELECT id FROM managed_streams WHERE user_id = ? ORDER BY created_at",
-- "SELECT id FROM media_requests WHERE streamer_id = ? AND status = 'pending' ORDER BY queue_position ASC, requested_at ASC, id ASC",
-- "SELECT id FROM schema_migrations",
-- "SELECT id FROM streams WHERE is_live = 1",
-- "SELECT id FROM streams WHERE is_live = 1 AND protocol NOT IN ('rtmp', 'jsmpeg')",
-- "SELECT id FROM streams WHERE managed_stream_id = ? AND is_live = 1 AND id != ?",
-- "SELECT id FROM streams WHERE managed_stream_id = ? AND is_live = 1 LIMIT 1",
-- "SELECT id FROM themes WHERE slug = ?",
-- "SELECT id FROM user_themes WHERE user_id = ?",
-- "SELECT id FROM users WHERE role = 'admin' LIMIT 1",
-- "SELECT id FROM users WHERE username = ? COLLATE NOCASE AND id != ?",
-- "SELECT id FROM verification_keys WHERE target_username = ? COLLATE NOCASE AND status = 'active'",
-- "SELECT id, action, idempotency_key, live_user_id, live_ref, status, error, attempts, created_at, updated_at FROM billing_actions WHERE status IN ('unknown', 'pending') ORDER BY id DESC LIMIT 50",
-- "SELECT id, ai_overview_short, ai_overview, title FROM streams WHERE user_id = ? AND (ai_title IS NULL OR ai_title = '') AND (ai_overview_short IS NOT NULL OR ai_overview IS NOT NULL) ORDER BY COALESCE(started_at, created_at) DESC LIMIT ?",
-- "SELECT id, applied_at, mode, duration_ms FROM schema_migrations",
-- "SELECT id, created_at FROM api_tokens WHERE token_hash = ?",
-- "SELECT id, ip_address, stream_id, reason FROM bans WHERE user_id = ? AND stream_id IS NULL",
-- "SELECT id, label, permissions, last_used, is_active, created_at FROM api_keys WHERE user_id = ?",
-- "SELECT id, label, scopes, created_at, last_used_at, expires_at, is_active FROM api_tokens WHERE user_id = ? ORDER BY created_at DESC",
-- "SELECT id, pip_source_msid, pip_defaults FROM managed_streams WHERE id = ?",
-- "SELECT id, start_sec, end_sec, text, lang, text_en, created_at FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND id > ? ORDER BY id DESC LIMIT ?",
-- "SELECT id, stream_id, user_id, ip_address, anon_id, expires_at FROM bans WHERE expires_at IS NULL OR datetime(expires_at) > CURRENT_TIMESTAMP",
-- "SELECT id, stream_id, vod_id, start_sec, text FROM stream_timeline_events WHERE user_id = ? AND kind = 'speech' AND created_at >= datetime('now', ?) AND LENGTH(text) BETWEEN 25 AND 220 ORDER BY created_at DESC LIMIT 1500",
-- "SELECT id, title, slug FROM managed_streams WHERE user_id = ? AND (? IS NULL OR id != ?) ORDER BY sort_order ASC, id ASC",
-- "SELECT id, title, started_at, viewer_count FROM streams WHERE user_id = ? AND is_live = 1 ORDER BY started_at DESC LIMIT 1",
-- "SELECT id, user_id FROM channels WHERE id = ?",
-- "SELECT id, user_id, channel_id, managed_stream_id, title, is_live, started_at, ended_at, created_at FROM streams",
-- "SELECT id, user_id, protocol FROM streams WHERE is_live = 1 AND ( (last_heartbeat IS NOT NULL AND last_heartbeat < datetime('now', '-5 minutes')) OR (last_heartbeat IS NULL AND started_at < datetime('now', '-6 minutes')) )",
-- "SELECT id, user_id, slug FROM managed_streams WHERE id = ?",
-- "SELECT id, user_id, slug, title, protocol, streaming_method, openre_stream_id FROM managed_streams WHERE ingest_authority = 'openre' ORDER BY id",
-- "SELECT id, user_id, slug, title, sort_order, created_at FROM managed_streams",
-- "SELECT id, user_id, title, emote_sources FROM channels",
-- "SELECT id, username FROM users WHERE id = ?",
-- "SELECT id, username FROM users WHERE username = ? COLLATE NOCASE",
-- "SELECT id, username, display_name, avatar_url, profile_color, role, openvibe_bucks_balance, openvibe_coins_balance, created_at, last_seen FROM users WHERE id = ?",
-- "SELECT id, username, display_name, bio, is_banned FROM users WHERE id = ?",
-- "SELECT id, username, display_name, is_banned FROM users WHERE id = ?",
-- "SELECT id, username, display_name, role, created_at FROM users ORDER BY created_at DESC LIMIT 10",
-- "SELECT il.user_id, il.anon_id, u.username, u.display_name, u.avatar_url, u.role, u.is_banned, u.ban_reason, u.created_at as user_created_at, COUNT(*) as hit_count, MIN(il.created_at) as first_seen, MAX(il.created_at) as last_seen, GROUP_CONCAT(DISTINCT il.action) as actions FROM ip_log il LEFT JOIN users u ON il.user_id = u.id WHERE il.ip_address = ? GROUP BY COALESCE(il.user_id, il.anon_id) ORDER BY last_seen DESC",
-- "SELECT ip, anon_num FROM anon_ip_mappings ORDER BY anon_num",
-- "SELECT ip_address, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, COUNT(*) as hit_count, MIN(created_at) as first_seen, MAX(created_at) as last_seen, GROUP_CONCAT(DISTINCT action) as actions FROM ip_log WHERE user_id = ? GROUP BY ip_address ORDER BY last_seen DESC",
-- "SELECT ip_address, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, created_at FROM ip_log WHERE anon_id = ? ORDER BY created_at DESC LIMIT 1",
-- "SELECT ip_address, geo_country, geo_region, geo_city, geo_isp, geo_org, geo_ll, created_at FROM ip_log WHERE user_id = ? ORDER BY created_at DESC LIMIT 1",
-- "SELECT item_id, category, unlocked_at FROM user_cosmetics WHERE user_id = ?",
-- "SELECT json FROM stream_recaps WHERE stream_id = ?",
-- "SELECT key FROM site_settings WHERE key = ?",
-- "SELECT key FROM site_settings WHERE key LIKE 'tts\\_%' ESCAPE '\\' OR key IN ('gif_tenor_api_key', 'gif_giphy_api_key', 'soundboard_101_api_key')",
-- "SELECT kind, COUNT(*) AS calls, SUM(cost_usd) AS cost_usd FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY kind ORDER BY cost_usd DESC",
-- "SELECT kind, start_sec, end_sec, text, label, confidence FROM stream_timeline_events WHERE stream_id = ?",
-- "SELECT kind, start_sec, end_sec, text, label, confidence FROM stream_timeline_events WHERE stream_id = ? AND kind = ? ORDER BY start_sec ASC LIMIT ?",
-- "SELECT kind, start_sec, end_sec, text, label, confidence FROM stream_timeline_events WHERE vod_id = ? ORDER BY start_sec ASC LIMIT 20000",
-- "SELECT label, COUNT(*) AS n FROM stream_timeline_events WHERE user_id = ? AND kind = 'sound' AND label IS NOT NULL AND created_at >= datetime('now', ?) GROUP BY label ORDER BY n DESC LIMIT 5",
-- "SELECT m.id AS msid, m.title, m.slug, m.user_id, s.id AS stream_id, s.is_live FROM managed_streams m LEFT JOIN streams s ON s.managed_stream_id = m.id AND s.is_live = 1 WHERE m.id = ?",
-- "SELECT m.text, m.quality, m.said_at, m.aimed_at, u.username, u.display_name FROM arena_mic_moments m JOIN users u ON u.id = m.user_id WHERE m.said_at >= ? AND m.quality >= 7 ORDER BY m.quality DESC, m.said_at DESC LIMIT 3",
-- "SELECT media_id FROM search_media_pushes WHERE kind = ? AND deleted = 0",
-- "SELECT model, COUNT(*) AS calls, SUM(cost_usd) AS cost_usd, SUM(input_tokens) AS input_tokens, SUM(cached_tokens) AS cached_tokens, SUM(output_tokens) AS output_tokens FROM ai_usage WHERE created_at >= date('now', ?) GROUP BY model ORDER BY cost_usd DESC",
-- "SELECT ms.*, (SELECT COUNT(*) FROM streams s WHERE s.managed_stream_id = ms.id) AS session_count, (SELECT MAX(s.ended_at) FROM streams s WHERE s.managed_stream_id = ms.id AND s.ended_at IS NOT NULL) AS last_live_at, (SELECT s.is_live FROM streams s WHERE s.managed_stream_id = ms.id AND s.is_live = 1 LIMIT 1) AS is_currently_live, (SELECT s.id FROM streams s WHERE s.managed_stream_id = ms.id AND s.is_live = 1 LIMIT 1) AS live_session_id FROM managed_streams ms WHERE ms.user_id = ? ORDER BY ms.sort_order ASC, ms.created_at ASC",
-- "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.id = ?",
-- "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.id = ? AND ms.user_id = ?",
-- "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.user_id = ? AND ms.slug = ? COLLATE NOCASE",
-- "SELECT ms.*, u.username, u.display_name, u.avatar_url, u.profile_color, u.stream_key AS user_stream_key FROM managed_streams ms JOIN users u ON ms.user_id = u.id WHERE ms.stream_key = ?",
-- "SELECT ns.enabled FROM news_settings ns WHERE ns.scope = 'user' AND ns.scope_id = (SELECT user_id FROM streams WHERE id = ?) AND ns.source_id = '_master'",
-- "SELECT o.*, u.username, u.display_name FROM streamer_overviews o JOIN users u ON u.id = o.user_id ORDER BY o.generated_at DESC LIMIT ?",
-- "SELECT offset_seconds, description, tags, thumbnail_url, captured_at, transcript_json FROM stream_memories WHERE stream_id = ? ORDER BY offset_seconds ASC LIMIT 400",
-- "SELECT offset_seconds, transcript_json FROM stream_memories WHERE stream_id = ? AND transcript_json IS NOT NULL ORDER BY offset_seconds ASC",
-- "SELECT overview, overview_short, generated_at FROM streamer_overviews WHERE user_id = ?",
-- "SELECT payload, generated_at FROM ai_timeline_cache WHERE user_id = ?",
-- "SELECT payload_json, created_at FROM vibe_coding_events WHERE managed_stream_id = ? ORDER BY id DESC LIMIT ?",
-- "SELECT persona_json FROM arena_profiles WHERE user_id = ?",
-- "SELECT platform, platform_username, channel_url FROM platform_connections WHERE user_id = ?",
-- "SELECT powerchat_count_views FROM restream_destinations WHERE id = ?",
-- "SELECT powerchat_relay FROM restream_destinations WHERE id = ?",
-- "SELECT powerchat_username FROM powerchat_connections WHERE user_id = ?",
-- "SELECT r.*, cr.streamer_id FROM coin_redemptions r JOIN coin_rewards cr ON r.reward_id = cr.id WHERE r.id = ?",
-- "SELECT r.*, cr.title as reward_title, cr.cost, cr.icon, cr.color, u.username, u.display_name, u.avatar_url FROM coin_redemptions r JOIN coin_rewards cr ON r.reward_id = cr.id JOIN users u ON r.user_id = u.id WHERE cr.streamer_id = ? AND r.status = 'pending' ORDER BY r.created_at ASC",
-- "SELECT r.stream_id FROM stream_recaps r JOIN streams s ON s.id = r.stream_id WHERE r.user_id = ? AND s.started_at >= ? ORDER BY r.created_at DESC LIMIT 1",
-- "SELECT r.stream_id, r.json, r.created_at, u.username, u.display_name, u.avatar_url, u.profile_color FROM stream_recaps r JOIN users u ON u.id = r.user_id WHERE (? IS NULL OR r.user_id != ?) AND COALESCE(u.is_banned, 0) = 0 ORDER BY r.created_at DESC LIMIT ?",
-- "SELECT r.stream_id, r.user_id, r.json, r.created_at FROM stream_recaps r JOIN users u ON u.id = r.user_id WHERE r.ai = 1 AND COALESCE(u.is_banned, 0) = 0 ORDER BY r.created_at DESC, r.stream_id DESC LIMIT ? OFFSET ?",
-- "SELECT reason, COUNT(*) AS refs, SUM(count) AS answers FROM lineage_unresolved GROUP BY reason",
-- "SELECT recorded_at AS t, viewer_count AS v, COALESCE(chat_messages_5m, 0) AS c FROM viewer_snapshots WHERE stream_id = ? ORDER BY recorded_at ASC",
-- "SELECT revision FROM follow_projection_revisions WHERE follower_subject = ? AND target_subject = ?",
-- "SELECT revision FROM subject_projection WHERE subject_id = ?",
-- "SELECT rowid AS rid, ai_overview AS ov FROM streams WHERE ai_overview IS NOT NULL AND TRIM(ai_overview) != '' AND (ai_overview_short IS NULL OR ai_overview_short = '')",
-- "SELECT rowid AS rid, overview AS ov FROM streamer_overviews WHERE overview IS NOT NULL AND TRIM(overview) != '' AND (overview_short IS NULL OR overview_short = '')",
-- "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color FROM streams s JOIN ( SELECT user_id, MAX(ended_at) AS latest_ended_at FROM streams WHERE is_live = 0 AND ended_at IS NOT NULL GROUP BY user_id ) latest ON latest.user_id = s.user_id AND latest.latest_ended_at = s.ended_at JOIN users u ON s.user_id = u.id WHERE s.is_live = 0 AND s.ended_at IS NOT NULL ORDER BY s.ended_at DESC LIMIT ?",
-- "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.id AS managed_stream_id, ms.stream_key AS managed_stream_key, ms.browser_mode, ms.streaming_method FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.is_live = 1 ORDER BY s.viewer_count DESC, s.started_at DESC",
-- "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.control_config_id = ? AND s.is_live = 1 ORDER BY s.started_at DESC",
-- "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.user_id = ? AND s.is_live = 1 ORDER BY s.started_at DESC",
-- "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title, ms.id AS managed_stream_ref_id FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.user_id = ? ORDER BY s.created_at DESC LIMIT ?",
-- "SELECT s.*, COALESCE(NULLIF(s.ai_category, ''), s.category) AS category, s.category AS chosen_category, u.username, u.display_name, u.avatar_url, u.profile_color, ms.slug AS managed_stream_slug, ms.stream_key AS managed_stream_key, ms.title AS managed_stream_title, ms.protocol AS managed_stream_protocol FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.id = ?",
-- "SELECT s.*, ms.slug AS managed_stream_slug FROM streams s LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.managed_stream_id = ? AND s.is_live = 1 ORDER BY COALESCE(s.started_at, s.created_at) DESC LIMIT 1",
-- "SELECT s.*, u.username AS streamer_username, u.display_name AS streamer_display, u.avatar_url AS streamer_avatar FROM subscriptions s LEFT JOIN users u ON s.streamer_id = u.id WHERE s.subscriber_id = ? ORDER BY s.started_at DESC",
-- "SELECT s.*, u.username AS subscriber_username, u.display_name AS subscriber_display, u.avatar_url AS subscriber_avatar FROM subscriptions s LEFT JOIN users u ON s.subscriber_id = u.id WHERE s.streamer_id = ? AND s.status = 'active' ORDER BY s.started_at DESC",
-- "SELECT s.id FROM streams s LEFT JOIN openre_sessions o ON o.stream_id = s.id WHERE s.managed_stream_id = ? AND s.is_live = 1 AND s.protocol = 'rtmp' AND o.session_id IS NULL ORDER BY s.id DESC LIMIT 1",
-- "SELECT s.id FROM streams s WHERE s.is_live = 0 AND s.ended_at IS NOT NULL AND s.ended_at >= datetime('now', ?) AND s.ended_at <= datetime('now', ?) AND COALESCE(s.duration_seconds, (julianday(s.ended_at) - julianday(s.started_at)) * 86400) >= ? AND NOT EXISTS (SELECT 1 FROM stream_recaps r WHERE r.stream_id = s.id) ORDER BY s.ended_at ASC LIMIT 6",
-- "SELECT s.id, COALESCE(ms.stream_key, u.stream_key) AS stream_key FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN managed_streams ms ON s.managed_stream_id = ms.id WHERE s.is_live = 1 AND s.protocol = 'rtmp'",
-- "SELECT s.id, s.title, s.ai_title, s.started_at, s.ended_at, s.created_at, s.duration_seconds, s.ai_overview, s.ai_overview_short, s.thumbnail_url, s.peak_viewers, s.category, (SELECT COUNT(*) FROM stream_memories m WHERE m.stream_id = s.id) AS memory_count FROM streams s WHERE s.user_id = ? AND (s.ai_overview IS NOT NULL OR EXISTS (SELECT 1 FROM stream_memories m WHERE m.stream_id = s.id)) ORDER BY COALESCE(s.started_at, s.created_at) DESC LIMIT 300",
-- "SELECT s.id, s.title, s.category, s.started_at, s.ended_at, s.duration_seconds, s.peak_viewers, s.viewer_count, s.thumbnail_url, sa.avg_viewers, sa.unique_chatters, sa.total_messages, sa.clips_created FROM streams s LEFT JOIN stream_analytics sa ON sa.stream_id = s.id WHERE s.user_id = ? AND s.duration_seconds > 0 ORDER BY s.started_at DESC LIMIT ? OFFSET ?",
-- "SELECT s.id, s.title, s.category, s.started_at, s.ended_at, s.duration_seconds, s.peak_viewers, s.viewer_count, sa.avg_viewers, sa.unique_chatters, sa.total_messages, sa.total_watch_minutes, sa.new_followers, sa.clips_created, sa.coins_earned FROM streams s LEFT JOIN stream_analytics sa ON sa.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? AND s.duration_seconds > 0 ORDER BY s.started_at DESC",
-- "SELECT s.id, s.title, s.protocol, s.viewer_count, s.started_at, u.username FROM streams s JOIN users u ON s.user_id = u.id WHERE s.is_live = 1 ORDER BY s.viewer_count DESC LIMIT 20",
-- "SELECT s.id, s.title, s.started_at, s.duration_seconds, (SELECT COALESCE(SUM(COALESCE(e.end_sec, e.start_sec + 3) - e.start_sec), 0) FROM stream_timeline_events e WHERE e.stream_id = s.id AND e.kind = 'speech') AS speech_sec, (SELECT COALESCE(SUM(LENGTH(e.text) - LENGTH(REPLACE(e.text, ' ', '')) + 1), 0) FROM stream_timeline_events e WHERE e.stream_id = s.id AND e.kind = 'speech') AS words, (SELECT COUNT(*) FROM arena_mic_moments m WHERE m.stream_id = s.id) AS moments, (SELECT COALESCE(AVG(m.quality), 0) FROM arena_mic_moments m WHERE m.stream_id = s.id) AS avg_q, (SELECT COUNT(*) FROM arena_mic_moments m WHERE m.stream_id = s.id AND m.kind = 'beef_hit') AS hits, (SELECT COUNT(*) FROM arena_beefs b WHERE b.winner_user_id = s.user_id AND b.resolved_at BETWEEN s.started_at AND COALESCE(s.ended_at, s.started_at)) AS wins FROM streams s WHERE s.user_id = ? AND s.duration_seconds > 0 AND s.started_at >= datetime('now', ?) ORDER BY s.started_at DESC LIMIT 14",
-- "SELECT s.id, s.title, s.started_at, s.ended_at, s.is_live, s.peak_viewers, s.viewer_count, s.duration_seconds, s.protocol, s.category FROM streams s WHERE s.managed_stream_id = ? AND s.user_id = ? ORDER BY s.started_at DESC LIMIT ?",
-- "SELECT s.id, s.user_id, s.managed_stream_id, ms.slug AS slot_slug FROM streams s LEFT JOIN managed_streams ms ON ms.id = s.managed_stream_id WHERE s.id = ?",
-- "SELECT s.id, s.user_id, s.started_at FROM streams s WHERE s.is_live = 1 AND EXISTS (SELECT 1 FROM stream_timeline_events e WHERE e.stream_id = s.id AND e.kind = 'speech' AND e.created_at >= datetime('now', '-30 minutes'))",
-- "SELECT s.id, s.user_id, s.title, s.category, s.protocol, s.is_nsfw, s.started_at, s.ended_at, s.duration_seconds, s.managed_stream_id, u.username, u.display_name FROM streams s JOIN users u ON u.id = s.user_id WHERE s.id = ?",
-- "SELECT s.id, u.stream_key FROM streams s JOIN users u ON s.user_id = u.id WHERE s.is_live = 1 AND s.protocol = 'jsmpeg'",
-- "SELECT service_user_id FROM linked_accounts WHERE service = 'network' AND user_id = ?",
-- "SELECT session_id, managed_stream_id, stream_id, state, confirmed_at FROM openre_sessions WHERE state = 'live'",
-- "SELECT slot, item_id FROM user_equipped WHERE user_id = ?",
-- "SELECT slot_clip_notify_enabled FROM managed_streams WHERE id = ?",
-- "SELECT slot_clip_recording_enabled FROM managed_streams WHERE id = ?",
-- "SELECT slot_powerchat_count_rs_views FROM managed_streams WHERE id = ?",
-- "SELECT slot_powerchat_relay FROM managed_streams WHERE id = ?",
-- "SELECT slot_vod_recording_enabled FROM managed_streams WHERE id = ?",
-- "SELECT sql FROM sqlite_master WHERE type='table' AND name='stream_controls'",
-- "SELECT started_at FROM streams WHERE id = ?",
-- "SELECT started_at, is_live FROM streams WHERE id = ?",
-- "SELECT status, COUNT(*) AS n FROM billing_actions GROUP BY status",
-- "SELECT stream_id, json, ai, created_at FROM stream_recaps WHERE user_id = ? ORDER BY created_at DESC LIMIT ?",
-- "SELECT stream_id, new_followers, clips_created, coins_earned FROM stream_analytics WHERE stream_id = ?",
-- "SELECT streamer_id AS s, balance FROM channel_points WHERE user_id = ?",
-- "SELECT streamer_id FROM follows WHERE follower_id = ?",
-- "SELECT strftime('%s', sampled_at) AS t, viewers, live_streams FROM viewer_samples WHERE sampled_at >= datetime('now', ?) ORDER BY sampled_at ASC",
-- "SELECT subject_id FROM linked_accounts WHERE service = 'network' AND user_id = ?",
-- "SELECT subject_id FROM linked_accounts WHERE user_id = ? AND service = 'network' AND subject_id IS NOT NULL ORDER BY id DESC LIMIT 1",
-- "SELECT subject_id FROM linked_accounts WHERE user_id = ? AND service = 'network' AND subject_id IS NOT NULL ORDER BY id LIMIT 1",
-- "SELECT subject_id, service_user_id FROM linked_accounts WHERE service = 'network' AND user_id = ?",
-- "SELECT t.*, fu.username AS from_username, fu.display_name AS from_display, tu.username AS to_username, tu.display_name AS to_display FROM transactions t LEFT JOIN users fu ON t.from_user_id = fu.id LEFT JOIN users tu ON t.to_user_id = tu.id WHERE t.from_user_id = ? OR t.to_user_id = ? ORDER BY t.created_at DESC LIMIT ?",
-- "SELECT t.*, u.id as uid, u.username, u.display_name, u.role, u.profile_color, u.avatar_url, u.is_banned, u.ban_reason FROM api_tokens t JOIN users u ON t.user_id = u.id WHERE t.token_hash = ? AND t.is_active = 1",
-- "SELECT t.*, u.username as author_name FROM themes t LEFT JOIN users u ON t.author_id = u.id WHERE t.id = ?",
-- "SELECT t.*, u.username as author_name FROM themes t LEFT JOIN users u ON t.author_id = u.id WHERE t.is_public = 1",
-- "SELECT t.*, u.username as author_name FROM themes t LEFT JOIN users u ON t.author_id = u.id WHERE t.slug = ?",
-- "SELECT t.*, u.username, u.display_name FROM transactions t JOIN users u ON t.from_user_id = u.id WHERE t.type = 'cashout' AND t.status = 'escrow' ORDER BY t.created_at ASC",
-- "SELECT t.amount, t.created_at, fu.username AS from_username, fu.display_name AS from_display, tu.username AS to_username, tu.display_name AS to_display FROM transactions t LEFT JOIN users fu ON fu.id = t.from_user_id JOIN users tu ON tu.id = t.to_user_id WHERE t.type = 'donation' ORDER BY t.id DESC LIMIT 1",
-- "SELECT tag_id FROM user_equipped_tag WHERE user_id = ?",
-- "SELECT target_user_id, COUNT(*) AS n FROM arena_mic_moments WHERE user_id = ? AND target_user_id IS NOT NULL GROUP BY target_user_id ORDER BY n DESC LIMIT 5",
-- "SELECT text FROM arena_mic_moments WHERE user_id = ? AND created_at >= datetime('now', '-6 hours') ORDER BY id DESC LIMIT 40",
-- "SELECT text FROM stream_timeline_events WHERE user_id = ? AND kind = 'speech' AND LENGTH(text) BETWEEN 30 AND 160 ORDER BY created_at DESC LIMIT 60",
-- "SELECT text FROM translations WHERE key = ?",
-- "SELECT text, quality, kind, aimed_at, sec, vod_id FROM arena_mic_moments WHERE stream_id = ? ORDER BY quality DESC, said_at ASC LIMIT 4",
-- "SELECT text, start_sec, vod_id FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND LENGTH(text) > 15 ORDER BY start_sec DESC LIMIT 5",
-- "SELECT text, start_sec, vod_id FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' AND start_sec > ? ORDER BY start_sec ASC LIMIT 100",
-- "SELECT text, start_sec, vod_id FROM stream_timeline_events WHERE stream_id = ? AND kind = 'speech' ORDER BY start_sec DESC LIMIT 14",
-- "SELECT thread_id, access_id FROM comment_thread_refs WHERE content_type = ? AND content_id = ?",
-- "SELECT thumbnail_url FROM streams WHERE id = ?",
-- "SELECT title, category, is_live, is_nsfw FROM streams WHERE user_id = ? ORDER BY id DESC LIMIT 1",
-- "SELECT transcript_attempts AS a FROM clip_ai_state WHERE clip_id = ?",
-- "SELECT transcript_attempts AS a FROM vod_ai_state WHERE vod_id = ?",
-- "SELECT transcript_partial_json, transcript_progress_sec FROM vod_ai_state WHERE vod_id = ?",
-- "SELECT u.id AS user_id FROM users u LEFT JOIN streamer_overviews o ON o.user_id = u.id WHERE EXISTS (SELECT 1 FROM stream_memories m WHERE m.user_id = u.id) AND ( o.user_id IS NULL OR (LENGTH(TRIM(COALESCE(o.overview,''))) >= ? AND o.generated_at <= datetime('now','-12 hours')) OR (LENGTH(TRIM(COALESCE(o.overview,''))) < ? AND o.generated_at <= datetime('now','-1 hours')) ) ORDER BY (o.generated_at IS NULL) DESC, o.generated_at ASC LIMIT ?",
-- "SELECT u.id AS user_id, u.username, u.display_name, u.avatar_url, u.profile_color, COUNT(s.id) AS sessions, MAX(s.started_at) AS last_at, MAX(COALESCE(s.peak_viewers, 0)) AS peak_viewers, ROUND(SUM(COALESCE(s.duration_seconds, CASE WHEN s.ended_at IS NOT NULL THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400 ELSE 0 END)) / 3600.0, 1) AS hours, (SELECT title FROM streams t WHERE t.user_id = u.id AND t.started_at >= ? ORDER BY t.started_at DESC LIMIT 1) AS last_title FROM streams s JOIN users u ON u.id = s.user_id WHERE s.started_at >= ? AND s.is_live = 0 AND COALESCE(u.is_banned, 0) = 0 GROUP BY u.id ORDER BY hours DESC, last_at DESC LIMIT 14",
-- "SELECT u.id AS user_id, u.username, u.display_name, u.avatar_url, u.profile_color, MAX(s.ended_at) AS last_online_at, o.overview AS ai_overview, o.overview_short AS ai_overview_short, ( SELECT json_group_array(json_object( 'managed_stream_id', ms2.id, 'slug', ms2.slug, 'title', ms2.title, 'protocol', ms2.protocol, 'last_live_at', (SELECT MAX(s2.ended_at) FROM streams s2 WHERE s2.managed_stream_id = ms2.id AND s2.ended_at IS NOT NULL), 'vod_thumbnail', NULL )) FROM managed_streams ms2 WHERE ms2.user_id = u.id AND EXISTS (SELECT 1 FROM streams sx WHERE sx.managed_stream_id = ms2.id AND sx.ended_at IS NOT NULL) ) AS managed_streams_json FROM streams s JOIN users u ON s.user_id = u.id LEFT JOIN streamer_overviews o ON o.user_id = u.id WHERE s.is_live = 0 AND s.ended_at IS NOT NULL GROUP BY u.id ORDER BY last_online_at DESC LIMIT ? OFFSET ?",
-- "SELECT u.id AS user_id, u.username, u.display_name, u.avatar_url, u.profile_color, s.title, s.viewer_count, s.started_at FROM streams s JOIN users u ON u.id = s.user_id WHERE s.is_live = 1 AND COALESCE(u.is_banned, 0) = 0 ORDER BY s.viewer_count DESC, s.started_at DESC LIMIT 10",
-- "SELECT u.id, u.username, u.display_name, u.avatar_url, u.profile_color, MAX(s.started_at) AS last_live_at, COUNT(s.id) AS sessions, ROUND(SUM(COALESCE(s.duration_seconds, CASE WHEN s.ended_at IS NOT NULL THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400 ELSE 0 END)) / 3600.0, 1) AS hours, (SELECT COUNT(*) FROM follows f WHERE f.streamer_id = u.id) AS followers, (SELECT COALESCE(ch.ai_category, ch.category) FROM channels ch WHERE ch.user_id = u.id) AS category FROM streams s JOIN users u ON u.id = s.user_id WHERE s.started_at >= datetime('now', ?) AND COALESCE(u.is_banned, 0) = 0 AND u.id != COALESCE(?, -1) GROUP BY u.id HAVING hours >= 0.25 OR sessions >= 2 ORDER BY last_live_at DESC LIMIT 40",
-- "SELECT u.id, u.username, u.display_name, u.avatar_url, u.role, u.is_banned, u.ban_reason, u.created_at, COUNT(DISTINCT shared.ip_address) as shared_ip_count, GROUP_CONCAT(DISTINCT shared.ip_address) as shared_ips, MAX(shared.created_at) as last_shared_activity FROM ip_log mine JOIN ip_log shared ON mine.ip_address = shared.ip_address AND (shared.user_id IS NOT NULL OR shared.anon_id != ?) LEFT JOIN users u ON shared.user_id = u.id WHERE mine.anon_id = ? GROUP BY COALESCE(shared.user_id, shared.anon_id) ORDER BY shared_ip_count DESC, last_shared_activity DESC",
-- "SELECT u.id, u.username, u.display_name, u.avatar_url, u.role, u.is_banned, u.ban_reason, u.created_at, COUNT(DISTINCT shared.ip_address) as shared_ip_count, GROUP_CONCAT(DISTINCT shared.ip_address) as shared_ips, MAX(shared.created_at) as last_shared_activity FROM ip_log mine JOIN ip_log shared ON mine.ip_address = shared.ip_address AND shared.user_id != ? JOIN users u ON shared.user_id = u.id WHERE mine.user_id = ? GROUP BY shared.user_id ORDER BY shared_ip_count DESC, last_shared_activity DESC",
-- "SELECT u.id, u.username, u.display_name, u.bio, COUNT(s.id) AS sessions, ROUND(SUM(COALESCE(s.duration_seconds, CASE WHEN s.ended_at IS NOT NULL THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400 ELSE 0 END)) / 3600.0, 1) AS hours, MAX(COALESCE(s.peak_viewers, 0)) AS peak_viewers, ROUND(AVG(COALESCE(s.peak_viewers, 0)), 1) AS avg_peak, MAX(s.started_at) AS last_live_at, (SELECT COUNT(*) FROM follows f WHERE f.streamer_id = u.id AND f.created_at >= datetime('now', ?)) AS new_followers, (SELECT COUNT(*) FROM follows f WHERE f.streamer_id = u.id) AS followers, (SELECT COUNT(*) FROM arena_mic_moments m WHERE m.user_id = u.id AND m.said_at >= datetime('now', ?)) AS mic_moments, (SELECT COALESCE(so.overview_short, so.overview) FROM streamer_overviews so WHERE so.user_id = u.id) AS overview, (SELECT ch.ai_category FROM channels ch WHERE ch.user_id = u.id) AS category FROM users u JOIN streams s ON s.user_id = u.id WHERE s.started_at >= datetime('now', ?) AND COALESCE(u.is_banned, 0) = 0 GROUP BY u.id ORDER BY hours DESC LIMIT 40",
-- "SELECT u.id, u.username, u.display_name, u.role, u.openvibe_bucks_balance, u.is_banned, u.ban_reason, u.created_at, u.last_seen, COALESCE(c.force_vod_recording_disabled, 0) AS force_vod_recording_disabled FROM users u LEFT JOIN channels c ON c.user_id = u.id",
-- "SELECT u.username AS channel, cp.balance AS points FROM channel_points cp JOIN users u ON u.id = cp.streamer_id WHERE cp.user_id = ? AND cp.balance > 0 ORDER BY cp.balance DESC, u.username LIMIT 10",
-- "SELECT u.username FROM username_history h JOIN users u ON u.id = h.user_id WHERE h.old_username = ? COLLATE NOCASE ORDER BY h.id DESC LIMIT 1",
-- "SELECT u.username, COALESCE(so.overview_short, so.overview) AS ov FROM streamer_overviews so JOIN users u ON so.user_id = u.id WHERE COALESCE(u.is_banned,0)=0 AND so.overview IS NOT NULL ORDER BY so.generated_at DESC LIMIT 8",
-- "SELECT u.username, u.display_name FROM easter_egg_solves s JOIN users u ON u.id = s.user_id WHERE s.egg_date = ? ORDER BY s.rowid ASC LIMIT 3",
-- "SELECT u.username, u.display_name, SUM(t.amount) AS total FROM transactions t JOIN users u ON u.id = t.from_user_id WHERE t.type = 'donation' AND ((t.stream_id = ?) OR (t.to_user_id = ? AND t.created_at BETWEEN ? AND ?)) GROUP BY t.from_user_id ORDER BY total DESC LIMIT 1",
-- "SELECT u.username, u.display_name, u.avatar_url, SUM(c.amount) AS total FROM coin_transactions c JOIN users u ON u.id = c.user_id WHERE c.amount > 0 AND c.created_at >= datetime('now', '-7 days') GROUP BY c.user_id ORDER BY total DESC LIMIT 3",
-- "SELECT u.username, u.display_name, u.avatar_url, SUM(t.amount) AS total FROM transactions t JOIN users u ON u.id = t.from_user_id WHERE t.type = 'donation' AND t.created_at >= datetime('now', '-7 days') GROUP BY t.from_user_id ORDER BY total DESC LIMIT 3",
-- "SELECT unique_chatters, total_messages FROM stream_analytics WHERE stream_id = ?",
-- "SELECT user_id AS id FROM streams WHERE (started_at >= @from AND started_at < @to) OR (ended_at >= @from AND ended_at < @to) UNION SELECT streamer_id FROM follows WHERE created_at >= @from AND created_at < @to UNION SELECT id FROM users WHERE updated_at >= @from AND updated_at < @to UNION SELECT user_id FROM channels WHERE updated_at >= @from AND updated_at < @to",
-- "SELECT user_id AS k, overview AS v FROM streamer_overviews WHERE overview LIKE '{%\"description\"%'",
-- "SELECT user_id AS u, balance FROM channel_points WHERE streamer_id = ?",
-- "SELECT user_id FROM linked_accounts WHERE service = 'network' AND (subject_id = ? OR service_user_id = ?) ORDER BY subject_id = ? DESC LIMIT 1",
-- "SELECT user_id FROM linked_accounts WHERE service = 'network' AND service_user_id = ?",
-- "SELECT user_id FROM linked_accounts WHERE service = 'network' AND service_user_id = ? ORDER BY id DESC LIMIT 1",
-- "SELECT user_id FROM linked_accounts WHERE service = 'network' AND subject_id = ? ORDER BY id DESC LIMIT 1",
-- "SELECT user_id FROM linked_accounts WHERE service = 'network' AND subject_id = ? ORDER BY id LIMIT 1",
-- "SELECT user_id FROM streams WHERE id = ?",
-- "SELECT user_id, COUNT(*) AS n FROM arena_mic_moments WHERE target_user_id = ? GROUP BY user_id ORDER BY n DESC LIMIT 5",
-- "SELECT user_id, service_user_id FROM linked_accounts WHERE service = 'network' AND service_user_id GLOB '[0-9]*' ORDER BY user_id",
-- "SELECT user_id, service_user_id FROM linked_accounts WHERE service = 'network' AND subject_id IS NULL AND service_user_id GLOB '[0-9]*' ORDER BY user_id",
-- "SELECT user_id, streamer_id, delta FROM channel_points_log WHERE idempotency_key = ?",
-- "SELECT user_id, title, current_amount, target_amount FROM donation_goals WHERE is_active = 1 AND target_amount > 0 AND user_id IN (?) ORDER BY (CAST(current_amount AS REAL) / target_amount) DESC",
-- "SELECT username FROM users WHERE id = ?",
-- "SELECT valid_after_ms FROM token_revocations WHERE subject_id = ?",
-- "SELECT value FROM app_state WHERE key = ?",
-- "SELECT value FROM site_settings WHERE key = 'bucks_per_usd'",
-- "SELECT value FROM site_settings WHERE key = 'powerchat_site_user_id'",
-- "SELECT viewer_count, chat_messages_5m, recorded_at FROM viewer_snapshots WHERE stream_id = ? ORDER BY recorded_at ASC",
-- "SELECT vk.*, u1.username as created_by_name, u2.username as used_by_name FROM verification_keys vk LEFT JOIN users u1 ON vk.created_by = u1.id LEFT JOIN users u2 ON vk.used_by = u2.id ORDER BY vk.created_at DESC",
-- "SELECT vod_id AS id, s.* FROM vod_ai_state s WHERE (ai_overview IS NULL OR ai_overview = '') AND (ai_overview_short IS NULL OR ai_overview_short = '' OR ai_overview_short LIKE '%…') ORDER BY vod_id DESC LIMIT ?",
-- "SELECT vod_id AS id, s.* FROM vod_ai_state s WHERE ai_transcript_json IS NULL AND (transcript_status IS NULL OR transcript_status IN ('pending','retry')) AND (transcript_next_at IS NULL OR transcript_next_at <= CURRENT_TIMESTAMP) ORDER BY (transcript_status='retry'), vod_id DESC LIMIT ?",
-- "SELECT vod_id FROM stream_timeline_events WHERE stream_id = ? AND vod_id IS NOT NULL LIMIT 1",
-- "SELECT winner_user_id FROM arena_beefs WHERE status = 'resolved' AND (a_user_id = ? OR b_user_id = ?) ORDER BY resolved_at DESC LIMIT 20",
-- "SELECT workspace_name, machine_name, extension_version, publisher_id, publisher_label, publisher_vendor, publisher_client_type, publisher_client_name, publisher_client_version, publisher_capabilities_json, publisher_depth FROM vibe_coding_sessions WHERE managed_stream_id = ? ORDER BY COALESCE(last_event_at, created_at) DESC, id DESC LIMIT 1",
-- "SELECT wt.user_id, u.username, u.display_name, SUM(wt.minutes_watched) as total_minutes FROM watch_time wt JOIN users u ON u.id = wt.user_id JOIN streams s ON wt.stream_id = s.id WHERE s.user_id = ? AND s.started_at >= ? GROUP BY wt.user_id ORDER BY total_minutes DESC LIMIT 20",
-- "SELECT xp, level FROM arena_trash_levels WHERE user_id = ?",
-- "UPDATE ai_viewer_threads SET state = 'closed', awaiting = NULL, updated_at = CURRENT_TIMESTAMP WHERE channel_user_id = ? AND state = 'open'",
-- "UPDATE ai_viewer_threads SET state = 'closed', awaiting = NULL, updated_at = CURRENT_TIMESTAMP WHERE channel_user_id = ? AND state = 'open' AND (updated_at < datetime('now', ?) OR turns >= ?)",
-- "UPDATE ai_viewer_threads SET state = 'closed', awaiting = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE api_tokens SET is_active = 0 WHERE id = ? AND user_id = ?",
-- "UPDATE api_tokens SET last_used_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE arena_beefs SET clock_until = ? WHERE id = ?",
-- "UPDATE arena_beefs SET headline = ? WHERE id = ?",
-- "UPDATE arena_beefs SET result_headline = ? WHERE id = ?",
-- "UPDATE arena_beefs SET status = 'resolved', resolution = ?, winner_user_id = ?, resolved_at = CURRENT_TIMESTAMP, on_clock = NULL, clock_until = NULL, feed_json = ?, upset = ?, result_headline = ? WHERE id = ? AND status = 'open'",
-- "UPDATE arena_trash_levels SET best_line = ?, best_line_vod_id = ?, best_line_sec = ?, best_line_score = ? WHERE user_id = ?",
-- "UPDATE billing_actions SET attempts = attempts + 1, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE billing_actions SET live_ref = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND action = 'media_charge'",
-- "UPDATE billing_actions SET status = 'done', http_status = 200, billing_ref = ?, response_json = ?, error = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE billing_actions SET status = ?, http_status = ?, error = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE camera_profiles SET name = ?, onvif_url = ?, username = ?, password_hash = ?, pan_speed = ?, tilt_speed = ?, zoom_speed = ?, is_active = ?, last_connected = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE channel_ai_bots SET msg_count = msg_count + 1, last_active_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE channel_points SET balance = balance + ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND streamer_id = ?",
-- "UPDATE channel_points SET balance = balance + ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND streamer_id = ? AND balance >= ?",
-- "UPDATE channels SET ai_category = ? WHERE user_id = ?",
-- "UPDATE channels SET ai_overview_pref = 'hide' WHERE hide_ai_overview = 1 AND (ai_overview_pref IS NULL OR ai_overview_pref = 'auto')",
-- "UPDATE channels SET clips_allow_creator_delete = ? WHERE id = ?",
-- "UPDATE clip_ai_state SET ai_overview = ?, ai_overview_short = ?, ai_transcript_json = COALESCE(?, ai_transcript_json) WHERE clip_id = ?",
-- "UPDATE clip_ai_state SET ai_transcript_json = ? WHERE clip_id = ?",
-- "UPDATE clip_ai_state SET clip_notified = 1, clip_notify_at = NULL WHERE clip_id = ?",
-- "UPDATE clip_ai_state SET clip_notify_at = CURRENT_TIMESTAMP WHERE clip_id = ? AND COALESCE(clip_notified,0) = 0",
-- "UPDATE clip_ai_state SET clip_notify_at = datetime('now', ?) WHERE clip_id = ? AND COALESCE(clip_notified,0) = 0",
-- "UPDATE clip_ai_state SET transcript_attempts = COALESCE(transcript_attempts,0)+1 WHERE clip_id = ?",
-- "UPDATE clip_ai_state SET transcript_status='retry', transcript_attempts=0, transcript_next_at=NULL WHERE transcript_status='failed' AND COALESCE(transcript_error,'') NOT LIKE 'media reported%' AND COALESCE(transcript_error,'') NOT LIKE 'no audio stream%'",
-- "UPDATE clip_ai_state SET transcript_status='retry', transcript_next_at=NULL WHERE transcript_status='processing'",
-- "UPDATE clips SET visibility = CASE WHEN is_public = 1 THEN 'public' ELSE 'unlisted' END",
-- "UPDATE coin_redemptions SET status = ?, resolved_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE coin_rewards SET redemption_count = redemption_count + 1 WHERE id = ?",
-- "UPDATE donation_goals SET current_amount = ? WHERE id = ?",
-- "UPDATE donation_goals SET current_amount = ?, is_active = 0, reached_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE donation_goals SET is_active = 0 WHERE id = ?",
-- "UPDATE linked_accounts SET service = 'network' WHERE service = 'hobotools' AND user_id NOT IN (SELECT user_id FROM linked_accounts WHERE service = 'network')",
-- "UPDATE linked_accounts SET subject_id = ? WHERE service = 'network' AND user_id = ? AND service_user_id = ? AND (subject_id IS NULL OR subject_id != ?)",
-- "UPDATE linked_accounts SET subject_id = ? WHERE service = 'network' AND user_id = ? AND service_user_id = ? AND subject_id IS NULL",
-- "UPDATE linked_accounts SET subject_id = ?, service_user_id = COALESCE(?, service_user_id) WHERE service = 'network' AND user_id = ?",
-- "UPDATE managed_streams SET broadcast_settings = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND user_id = ?",
-- "UPDATE managed_streams SET ingest_authority = 'live', updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE managed_streams SET ingest_authority = 'openre', openre_stream_id = ?, stream_key = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE managed_streams SET stream_key = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE managed_streams SET streaming_method = 'browser' WHERE protocol = 'webrtc'",
-- "UPDATE managed_streams SET streaming_method = 'cli' WHERE protocol = 'jsmpeg'",
-- "UPDATE managed_streams SET streaming_method = 'rtmp' WHERE protocol = 'rtmp'",
-- "UPDATE media_requests SET queue_position = ? WHERE id = ?",
-- "UPDATE opencoin_admin_grants SET balance = ? WHERE id = ?",
-- "UPDATE openre_sessions SET confirmed_at = CURRENT_TIMESTAMP WHERE session_id = ?",
-- "UPDATE openre_sessions SET state = 'detached', updated_at = CURRENT_TIMESTAMP WHERE session_id = ?",
-- "UPDATE platform_connections SET access_token = ?, refresh_token = COALESCE(?, refresh_token), token_expires_at = ?, scope = COALESCE(?, scope), updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE platform_connections SET platform_user_id = ?, platform_username = ?, channel_url = ?, access_token = ?, refresh_token = COALESCE(?, refresh_token), token_expires_at = ?, scope = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE powerchat_connections SET access_token = ?, refresh_token = COALESCE(?, refresh_token), token_expires_at = ?, scope = COALESCE(?, scope), last_error = NULL, updated_at = CURRENT_TIMESTAMP WHERE user_id = ?",
-- "UPDATE powerchat_connections SET last_error = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ?",
-- "UPDATE restream_destinations SET consecutive_failures = 0, cooldown_until = NULL, last_error = NULL WHERE id = ?",
-- "UPDATE restream_destinations SET consecutive_failures = ?, last_error = ?, last_failed_at = CURRENT_TIMESTAMP, cooldown_until = datetime('now', ?) WHERE id = ?",
-- "UPDATE restream_destinations SET managed_stream_id = ? WHERE user_id = ? AND managed_stream_id IS NULL",
-- "UPDATE site_settings SET value = '100' WHERE key = 'bucks_per_usd'",
-- "UPDATE site_settings SET value = 'alex' WHERE key = 'powerchat_sandbox_username' AND value = 'n8admin'",
-- "UPDATE site_settings SET value = ? WHERE key = 'powerchat_scopes' AND value IN (?, ?, ?)",
-- "UPDATE site_settings SET value = ? WHERE key = 'powerchat_site_tip_username' AND (value IS NULL OR value = '')",
-- "UPDATE site_settings SET value = ?, updated_at = CURRENT_TIMESTAMP WHERE key = ?",
-- "UPDATE stream_analytics SET clips_created = ? WHERE stream_id = ?",
-- "UPDATE stream_analytics SET unique_chatters = ?, total_messages = ? WHERE stream_id = ?",
-- "UPDATE stream_controls SET camera_id = NULL WHERE camera_id = ?",
-- "UPDATE stream_recaps SET announced_at = CURRENT_TIMESTAMP WHERE stream_id = ?",
-- "UPDATE stream_timeline_events AS t SET vod_id = (SELECT s.vod_id FROM stream_timeline_events s WHERE s.stream_id = t.stream_id AND s.vod_id IS NOT NULL LIMIT 1) WHERE t.vod_id IS NULL AND EXISTS (SELECT 1 FROM stream_timeline_events s WHERE s.stream_id = t.stream_id AND s.vod_id IS NOT NULL)",
-- "UPDATE stream_timeline_events SET vod_id = ? WHERE stream_id = ? AND vod_id IS NULL",
-- "UPDATE streamer_overviews SET overview = ? WHERE user_id = ?",
-- "UPDATE streamer_overviews SET overview_short = ? WHERE rowid = ?",
-- "UPDATE streams SET ai_category = ?, ai_tags = ? WHERE id = ?",
-- "UPDATE streams SET ai_overview = ?, ai_overview_short = ? WHERE id = ?",
-- "UPDATE streams SET ai_overview_short = ? WHERE rowid = ?",
-- "UPDATE streams SET ai_title = ? WHERE id = ?",
-- "UPDATE streams SET call_mode = ? WHERE id = ?",
-- "UPDATE streams SET control_config_id = ? WHERE id = ?",
-- "UPDATE streams SET control_config_id = NULL WHERE id = ?",
-- "UPDATE streams SET is_live = 0, ended_at = CURRENT_TIMESTAMP, duration_seconds = CAST((julianday(CURRENT_TIMESTAMP) - julianday(started_at)) * 86400 AS INTEGER) WHERE id = ?",
-- "UPDATE streams SET is_live = 1, started_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE streams SET last_heartbeat = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE streams SET last_heartbeat = CURRENT_TIMESTAMP WHERE id = ? AND is_live = 1",
-- "UPDATE streams SET last_heartbeat = CURRENT_TIMESTAMP WHERE is_live = 1",
-- "UPDATE streams SET managed_stream_id = ? WHERE user_id = ? AND managed_stream_id IS NULL",
-- "UPDATE streams SET managed_stream_id = NULL WHERE managed_stream_id = ?",
-- "UPDATE streams SET started_at = CURRENT_TIMESTAMP, last_heartbeat = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE streams SET tags = ? WHERE id = ?",
-- "UPDATE streams SET thumbnail_url = ? WHERE id = ?",
-- "UPDATE streams SET user_id = ? WHERE user_id = ?",
-- "UPDATE streams SET viewer_count = ?, peak_viewers = MAX(peak_viewers, ?) WHERE id = ?",
-- "UPDATE subscriptions SET status=?, is_active=?, cancel_at_period_end=?, current_period_end=COALESCE(?, current_period_end), updated_at=CURRENT_TIMESTAMP WHERE id=?",
-- "UPDATE subscriptions SET tier=?, provider=?, provider_ref=COALESCE(?, provider_ref), price_cents=?, currency=?, status=?, is_active=?, current_period_end=?, auto_renew=COALESCE(?, auto_renew), cancel_at_period_end=0, updated_at=CURRENT_TIMESTAMP WHERE id=?",
-- "UPDATE themes SET downloads = downloads + 1 WHERE id = ?",
-- "UPDATE themes SET name = ?, mode = ?, description = ?, variables = ?, preview_colors = ?, tags = ?, is_builtin = 1, is_public = 1 WHERE slug = ?",
-- "UPDATE tips_deliveries SET claimed_at = ? WHERE idempotency_key = ? AND state = 'pending' AND claimed_at = ?",
-- "UPDATE tips_deliveries SET state = 'done', response_json = ? WHERE idempotency_key = ?",
-- "UPDATE transactions SET paypal_transaction_id = ? WHERE id = ?",
-- "UPDATE transactions SET status = ? WHERE id = ?",
-- "UPDATE user_themes SET theme_id = ?, custom_variables = ?, is_custom = ? WHERE user_id = ?",
-- "UPDATE users SET avatar_url = ?, avatar_paste_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE users SET bio = ? WHERE id = ?",
-- "UPDATE users SET display_name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
-- "UPDATE users SET email = NULL WHERE email IS NOT NULL",
-- "UPDATE users SET is_banned = 0, ban_reason = NULL WHERE id = ?",
-- "UPDATE users SET is_banned = 1, ban_reason = ? WHERE id = ?",
-- "UPDATE users SET is_banned = 1, ban_reason = ? WHERE id = ? AND is_banned = 0",
-- "UPDATE users SET is_owner = 1, role = 'admin' WHERE LOWER(username) = ? AND is_owner != 1",
-- "UPDATE users SET merged_into = ? WHERE id = ?",
-- "UPDATE users SET openvibe_bucks_balance = openvibe_bucks_balance + ? WHERE id = ?",
-- "UPDATE users SET openvibe_bucks_balance = openvibe_bucks_balance - ? WHERE id = ?",
-- "UPDATE users SET openvibe_bucks_cashout_balance = openvibe_bucks_cashout_balance + ? WHERE id = ?",
-- "UPDATE users SET openvibe_bucks_cashout_balance = openvibe_bucks_cashout_balance - ? WHERE id = ?",
-- "UPDATE users SET openvibe_coins_balance = openvibe_coins_balance + ? WHERE id = ?",
-- "UPDATE users SET openvibe_coins_balance = openvibe_coins_balance - ? WHERE id = ? AND openvibe_coins_balance >= ?",
-- "UPDATE users SET profile_color = ? WHERE id = ?",
-- "UPDATE users SET role = 'admin' WHERE username = ?",
-- "UPDATE users SET role = 'global_mod' WHERE role = 'mod'",
-- "UPDATE users SET role = ? WHERE id = ?",
-- "UPDATE users SET username = ? WHERE id = ?",
-- "UPDATE verification_keys SET status = 'revoked' WHERE id = ? AND status = 'active'",
-- "UPDATE verification_keys SET status = 'used', used_by = ?, used_at = CURRENT_TIMESTAMP WHERE key = ? AND status = 'active'",
-- "UPDATE vibe_coding_sessions SET last_event_at = CURRENT_TIMESTAMP WHERE managed_stream_id = ? AND session_key = ?",
-- "UPDATE vibe_coding_sessions SET status = 'ended', ended_at = CURRENT_TIMESTAMP, last_event_at = CURRENT_TIMESTAMP WHERE managed_stream_id = ? AND session_key = ?",
-- "UPDATE vod_ai_state SET ai_overview = ?, ai_overview_short = ? WHERE vod_id = ?",
-- "UPDATE vod_ai_state SET ai_transcript_json = ?, transcript_partial_json = NULL, transcript_progress_sec = 0 WHERE vod_id = ?",
-- "UPDATE vod_ai_state SET transcript_attempts = COALESCE(transcript_attempts,0)+1 WHERE vod_id = ?",
-- "UPDATE vod_ai_state SET transcript_partial_json = ?, transcript_progress_sec = ? WHERE vod_id = ?",
-- "UPDATE vod_ai_state SET transcript_status='retry', transcript_attempts=0, transcript_next_at=NULL WHERE transcript_status='failed' AND COALESCE(transcript_error,'') NOT LIKE 'media reported%' AND COALESCE(transcript_error,'') NOT LIKE 'no audio stream%'",
-- "UPDATE vod_ai_state SET transcript_status='retry', transcript_next_at=NULL WHERE transcript_status='processing'",
-- "UPDATE vods SET visibility = CASE WHEN is_public = 1 THEN 'public' ELSE 'private' END",
-- "UPDATE watch_time SET coins_earned = coins_earned + ? WHERE id = ?",
-- "UPDATE watch_time SET minutes_watched = minutes_watched + 1, last_heartbeat = CURRENT_TIMESTAMP WHERE id = ?"
-- ]
++ "statements": []
}