feat: v4 — TUI fight loop, rate limiting, webhook tooling, expanded choreographies

- Fight loop CLI with TUI renderer (ink-style terminal UI)
- Rate limiting middleware for API routes
- Queue cooldowns wired into orchestrator after fights
- Webhook test utility for bot debugging
- API docs route
- Expanded FightScene choreographies and weapon props
- Fix Drizzle transaction execution in orchestrator
- Schema additions, scoring/challenge/mock expansions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-07 00:14:46 +00:00
co-authored by Claude Opus 4.6
parent 2c0323d5fb
commit 4d8b18a58a
24 changed files with 2973 additions and 721 deletions
+1 -1
View File
@@ -14,4 +14,4 @@ sqlite.pragma('journal_mode = WAL')
sqlite.pragma('foreign_keys = ON')
export const db = drizzle(sqlite, { schema })
export { schema }
export { schema, sqlite }
+8 -2
View File
@@ -28,6 +28,9 @@ sqlite.exec(`
best_streak INTEGER NOT NULL DEFAULT 0,
tier INTEGER NOT NULL DEFAULT 0,
is_active INTEGER NOT NULL DEFAULT 1,
last_fight_at TEXT,
consecutive_errors INTEGER NOT NULL DEFAULT 0,
last_error_at TEXT,
created_at TEXT NOT NULL
);
@@ -38,8 +41,8 @@ sqlite.exec(`
arena TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'scheduled',
winner_id TEXT REFERENCES bots(id),
bot_a_hp INTEGER NOT NULL DEFAULT 100,
bot_b_hp INTEGER NOT NULL DEFAULT 100,
bot_a_hp INTEGER NOT NULL DEFAULT 200,
bot_b_hp INTEGER NOT NULL DEFAULT 200,
total_rounds INTEGER NOT NULL DEFAULT 0,
scheduled_at TEXT,
started_at TEXT,
@@ -69,6 +72,9 @@ sqlite.exec(`
const migrations = [
`ALTER TABLE bots ADD COLUMN archetype TEXT NOT NULL DEFAULT 'standard'`,
`ALTER TABLE bots ADD COLUMN profile_pic_url TEXT`,
`ALTER TABLE bots ADD COLUMN last_fight_at TEXT`,
`ALTER TABLE bots ADD COLUMN consecutive_errors INTEGER NOT NULL DEFAULT 0`,
`ALTER TABLE bots ADD COLUMN last_error_at TEXT`,
]
for (const sql of migrations) {
+3
View File
@@ -16,6 +16,9 @@ export const bots = sqliteTable('bots', {
bestStreak: integer('best_streak').notNull().default(0),
tier: integer('tier').notNull().default(0),
isActive: integer('is_active', { mode: 'boolean' }).notNull().default(true),
lastFightAt: text('last_fight_at'),
consecutiveErrors: integer('consecutive_errors').notNull().default(0),
lastErrorAt: text('last_error_at'),
createdAt: text('created_at').notNull(),
})