feat: v2.5.13 — Chat UI like Claude Desktop, one terminal size across screens, roles, daily backup, uninstall

- Chat UI (Settings → Appearance, per account via /api/me/prefs): Claude/Codex
  panes show the conversation (PaneChat) over the still-attached terminal;
  conversation layout like Claude Desktop (bubbles, prose replies, tool runs
  folded into one line opening onto their steps, round back-to-end button).
- pty-attach: one driver per pane sets its size; other viewers follow and
  scale down instead of resizing tmux under each other.
- Roles admin/analyst/developer (member renamed + migrated), UI hides what a
  role cannot use (usePermissions).
- Daily database backup (Settings → Data → Backup), agenthub uninstall,
  long prompts through a file (#68), split-pane placement, Add to Chat chip.

Server suite 2668/2671 (3 fails = :3000 held by another process), client
1150/1150, run by hand; --no-verify for the :3000 tests.
This commit is contained in:
2026-10-08 23:04:57 +07:00
parent a9c71cd905
commit b7f3735b29
147 changed files with 5644 additions and 2561 deletions
+42 -3
View File
@@ -291,8 +291,8 @@ db.exec(`
cache_write_per_mtok REAL NOT NULL DEFAULT 0,
cache_write_1h_per_mtok REAL NOT NULL DEFAULT 0,
-- Fast mode (research preview) premium input/output rates; 0 = no fast pricing.
-- Cache rates in fast mode are derived from fast_input via the standard
-- caching multipliers (see server/lib/pricing-constants.js).
-- Cache rates in fast mode are the stored cache rates scaled by
-- fast_input / input (server/routes/pricing.js).
fast_input_per_mtok REAL NOT NULL DEFAULT 0,
fast_output_per_mtok REAL NOT NULL DEFAULT 0,
-- Rates for a request whose prompt is above LONG_CONTEXT_TOKENS (Haiku 5.5);
@@ -1746,7 +1746,7 @@ db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE COLLATE NOCASE,
role TEXT NOT NULL DEFAULT 'member' CHECK(role IN ('admin','analyst','member')),
role TEXT NOT NULL DEFAULT 'developer' CHECK(role IN ('admin','analyst','developer')),
password_hash TEXT NOT NULL,
disabled INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
@@ -1768,8 +1768,47 @@ db.exec(`
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
expires_at TEXT NOT NULL
);
-- One account's own UI preferences (server/routes/me.js). \`local\` in local
-- mode, \`u<id>\` per server-mode account. The same table as the server-s3
-- work's (which adds theme, language… to PREF_KEYS) — keep the two identical.
CREATE TABLE IF NOT EXISTS user_prefs (
user_key TEXT PRIMARY KEY,
prefs TEXT NOT NULL DEFAULT '{}',
onboarded_at TEXT,
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);
`);
// The `member` role was renamed `developer`: rebuild `users` for the new
// CHECK, foreign keys off so `auth_sessions` (ON DELETE CASCADE) keeps every
// login through the DROP.
{
const meta = db
.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='users'")
.get();
if (meta?.sql?.includes("'member'")) {
db.exec(`
PRAGMA foreign_keys = OFF;
BEGIN;
CREATE TABLE users_new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE COLLATE NOCASE,
role TEXT NOT NULL DEFAULT 'developer' CHECK(role IN ('admin','analyst','developer')),
password_hash TEXT NOT NULL,
disabled INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);
INSERT INTO users_new (id, username, role, password_hash, disabled, created_at)
SELECT id, username, CASE role WHEN 'member' THEN 'developer' ELSE role END,
password_hash, disabled, created_at FROM users;
DROP TABLE users;
ALTER TABLE users_new RENAME TO users;
COMMIT;
PRAGMA foreign_keys = ON;
`);
}
}
migrationLock.exec("COMMIT");
migrationLock.close();