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
+3 -3
View File
@@ -44,7 +44,7 @@ const USAGE_COLS = [
/** $ and tokens a member's sessions spent since `sinceHour` (UTC `YYYY-MM-DDTHH`). */
function usageSince(userId, sinceHour, rules) {
const { calculateCost } = require("./pricing");
const w = sessionWhere({ user: { id: userId, role: "member" } }, "s.id", "s.lane_id");
const w = sessionWhere({ user: { id: userId, role: "developer" } }, "s.id", "s.lane_id");
const rows = db
.prepare(
`SELECT h.model, h.speed, h.inference_geo, h.service_tier,
@@ -66,7 +66,7 @@ const hourAgo = (days) => new Date(Date.now() - days * 864e5).toISOString().slic
router.get("/", (_req, res) => {
const rules = stmts.listPricing.all();
const users = listUsers().map((u) => {
const w = sessionWhere({ user: { id: u.id, role: "member" } });
const w = sessionWhere({ user: { id: u.id, role: "developer" } });
return {
...u,
workspaces: db.prepare("SELECT COUNT(*) AS n FROM lanes WHERE owner_id = ?").get(u.id).n,
@@ -132,7 +132,7 @@ function target(req) {
router.post(
"/",
accountChange(async (_req, body) => {
const role = body.role === undefined ? "member" : body.role;
const role = body.role === undefined ? "developer" : body.role;
const user = await auth.createUser(body.username, body.password, role);
return { status: 201, user, action: `create ${user.username} (${user.role})` };
})