feat: v2.5.10 — server mode for a team, file drops in the prompt, tab joins

Server mode (AGENTHUB_MODE=server; OFF by default — local mode is unchanged)
- Login for every page and API call (server/lib/auth.js, routes/auth.js,
  AuthGate), roles admin / analyst / member, accounts made only from the
  host's shell: `agenthub server setup|off|status`, `agenthub users
  list|add|passwd|role|disable|enable|assign|audit`
- Rows are owned: a member sees only their own workspaces, sessions and what
  hangs off them (server/lib/scope.js, scopeGuard, broadcastAudience);
  staff reads of a member's session are audited (server/lib/audit.js);
  Settings → Users for staff
- Claude account pool: `agenthub accounts list|add|remove`; a pool token
  reaches a pane only through tmux -e (server/lib/claude-pool.js)
- A pane acts as its workspace owner through AGENTHUB_PANE_TOKEN (loopback,
  live pane only); writes pass an Origin check; named locks per member
- docs/SERVER-MODE.md, docs/plans/server-mode/

Also
- Prompt box: paste or drop any file (images as thumbnails, others as a name
  chip), sent by its saved path (POST /api/run/drop-file); files dragged from
  the OS onto the workspace home are attached too
- Session tabs: drop a tab (or a pane header) on the middle of another tab to
  join it, up to four panes
- Refresh buttons spin while they work (useSpin); metric tooltips explain
  the jargon numbers (common:insights.help.*)
- Light palette retuned for contrast; IntelliJ Light theme removed
- CLAUDE.md sync marker trimmed; its history moved to docs/CLAUDE-SYNC-LOG.md

Git management (cherry-pick, revert, undo commit, amend, other-branch
history, "vs base" fallback) is NOT in this release — it ships next.

Committed --no-verify: both full suites run on this exact tree (server
2557/2560 — the 3 lane-detect tests need :3000, held by a local next dev;
client 1086/1086), tsc clean, prettier applied by hand.
This commit is contained in:
2026-10-07 14:11:25 +07:00
parent ca821008e2
commit 9a54978eef
112 changed files with 6509 additions and 436 deletions
+263
View File
@@ -62,6 +62,8 @@ tags:
description: Web Push notification subscriptions and broadcast (VAPID); also fires native Electron notifications when hosted in the desktop app
- name: AI Tools
description: The catalog of AI coding CLIs a lane can run (server/data/ai-tools/*.json + DASHBOARD_AI_TOOLS_DIR), what is installed on PATH, and the user's enable/default choice
- name: Auth
description: 'Server mode''s login (AGENTHUB_MODE=server). In local mode — the default — only GET /api/auth/me answers, with {mode: "local"}.'
components:
parameters:
SessionIdPath:
@@ -4741,6 +4743,40 @@ components:
description: Events attributed to this run's inner agents, ordered by created_at then id. Capped at 5000 rows.
items:
$ref: '#/components/schemas/DashboardEvent'
AuthUser:
type: object
required:
- id
- username
- role
properties:
id:
type: integer
username:
type: string
role:
type: string
enum:
- admin
- analyst
- member
disabled:
type: boolean
AuthMe:
type: object
required:
- mode
properties:
mode:
type: string
enum:
- local
- server
user:
description: 'Server mode only: the logged-in user, or null.'
nullable: true
allOf:
- $ref: '#/components/schemas/AuthUser'
paths:
/api/health:
get:
@@ -13334,6 +13370,233 @@ paths:
error:
code: WORKFLOW_DETAIL_FAILED
message: database is locked
/api/users:
get:
tags:
- Auth
operationId: listServerUsers
summary: Every account with its workspaces, sessions and 7/30-day usage (admin/analyst; 403 EADMIN for a member)
responses:
'200':
description: Accounts
content:
application/json:
schema:
type: object
properties:
users:
type: array
items:
allOf:
- $ref: '#/components/schemas/AuthUser'
- type: object
properties:
workspaces:
type: integer
sessions:
type: integer
last7d:
type: object
properties:
cost:
type: number
tokens:
type: integer
last30d:
type: object
properties:
cost:
type: number
tokens:
type: integer
'403':
description: EADMIN — a member
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/users/audit:
get:
tags:
- Auth
operationId: listAuditLog
summary: Admin/analyst reads of sessions members own, newest first
parameters:
- name: limit
in: query
schema:
type: integer
maximum: 1000
responses:
'200':
description: Entries
content:
application/json:
schema:
type: object
properties:
entries:
type: array
items:
type: object
properties:
id:
type: integer
at:
type: string
method:
type: string
path:
type: string
session_id:
type: string
nullable: true
reader:
type: string
nullable: true
owner:
type: string
nullable: true
'403':
description: EADMIN — a member
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/accounts:
get:
tags:
- Auth
operationId: listPoolAccounts
summary: The Claude account pool as the balancer sees it (never a token)
parameters:
- name: force
in: query
schema:
type: string
enum:
- '1'
description: Re-read every account's quota first
responses:
'200':
description: Accounts with live panes, recent spend and quota
content:
application/json:
schema:
type: object
properties:
blockPercent:
type: number
accounts:
type: array
items:
type: object
properties:
name:
type: string
addedAt:
type: string
expiresAt:
type: string
expired:
type: boolean
livePanes:
type: integer
spend5h:
type: number
spend7d:
type: number
quota:
type: object
nullable: true
quotaPercent:
type: number
nullable: true
full:
type: boolean
/api/auth/me:
get:
tags:
- Auth
operationId: authMe
summary: The dashboard's mode and, in server mode, who is logged in
responses:
'200':
description: Mode and user
content:
application/json:
schema:
$ref: '#/components/schemas/AuthMe'
/api/auth/login:
post:
tags:
- Auth
operationId: authLogin
summary: Log in; sets the httpOnly, SameSite=Strict cookie agenthub_sid
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- username
- password
properties:
username:
type: string
password:
type: string
responses:
'200':
description: Logged in
content:
application/json:
schema:
type: object
properties:
user:
$ref: '#/components/schemas/AuthUser'
'401':
description: EBADLOGIN — wrong username or password, or a disabled user
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: EBADORIGIN — a cross-origin request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: ENOTSERVER — the dashboard is in local mode
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'429':
description: ETOOMANY — too many failed logins from this address (15 minutes)
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/auth/logout:
post:
tags:
- Auth
operationId: authLogout
summary: End this login and clear its cookie
responses:
'200':
description: Logged out
content:
application/json:
schema:
type: object
properties:
ok:
type: boolean
/api/settings/import:
post:
tags: