feat: v2.8.0 — Artifacts uploads and plugin links, console tab combos, account avatars

- Artifacts hub: upload a file from the computer; make a skill by uploading a
  Markdown file, files, a folder or a .zip/.skill, or by writing it in the
  dialog; add plugins by picking from the list a marketplace link answers
  (POST /api/artifacts/plugin/inspect); Settings → AI tools → Artifacts Hub.
- Console tabs are combos of up to four split panes (ConsoleTabs, PaneGrid).
- Accounts: an own avatar (PUT/DELETE /api/me/avatar, raster only, ≤ 1 MB,
  <data dir>/avatars/), shown with name and role at the sidebar's foot and on
  the workspace owner groups (GET /api/users/:id/avatar, owner_avatar_at).
- Users page: per-user storage (GET /api/users/storage, measured in a worker)
  and account-pool spend/tokens per account (server/lib/pool-usage.js).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 17:21:33 +07:00
parent 8ed5018a2f
commit 7a88b74016
63 changed files with 3833 additions and 354 deletions
+28 -7
View File
@@ -115,7 +115,7 @@ their own workspaces (`useWorkLanes`); other users' work is read on Sessions,
Analytics and Users, where Resume, Fork, Snapshot and "Open in workspace" are
hidden for a session that is not theirs. The sidebar's workspace list shows an
admin or analyst EVERY workspace, grouped under its owner (each lane carries
`owner_name` and `owner_display_name`; the group shows the display name when
`owner_name`, `owner_display_name` and `owner_avatar_at`; the group shows the owner's picture (`GET /api/users/:id/avatar`) and the display name when
the owner set one, else the username; yours first, no owner last, each group folds): for an analyst
another user's workspace there is read only — no menu, rename or new session,
no live tabs, and a session opens its analysis page (`/sessions/<id>`). An analyst's alert feed
@@ -144,6 +144,17 @@ Workspaces made before server mode, or by the host account, have none.
- `GET /api/users` (admin/analyst) lists every account with its workspaces,
sessions, and the tokens and $ it used in the last 7 and 30 days — the
Users page in the sidebar shows it, with the audit log under it.
- `GET /api/users/storage` (admin/analyst) is the page's **Storage** column:
how much disk each user's home takes (their Linux home under S3, their
folder under `AGENTHUB_USERS_ROOT` otherwise; an admin with none reads
`null`). Homes are walked in a worker thread, one at a time, `du -x`-like
(allocated blocks, `lstat` only, no symlink followed, a hard link once),
and cached for 15 minutes; the answer comes back at once with
`{storage: {<userId>: {bytes, files, partial, measuredAt} | null}, measuring}`
and the page asks again while `measuring`. `?refresh=1` (the page's Refresh)
re-measures. A walk over `AGENTHUB_STORAGE_TIMEOUT_MS` (default 120 s) or
one meeting an unreadable folder is a lower bound, `partial: true` (shown
as `≥`).
A developer also cannot open the host's own configuration, or any view across all
users: Claude Code / Codex config (except the command and skill lists the
@@ -283,8 +294,10 @@ workspace runs as the host account that every admin shares. The tick is never
remembered, so the default is always the admin's own account.
Every signed-in user has **Settings → Account**: their username, role, member
since, workspace and session counts and active logins; their own display name
and email; **Change password** (the current password is required, the login
since, workspace and session counts and active logins; their own picture
(PNG, JPEG, GIF or WebP up to 1 MB, kept in `<data dir>/avatars/`; shown with
their name and role at the foot of the sidebar, which opens this page); their
own display name and email; **Change password** (the current password is required, the login
it is changed from stays and every other one ends); and **Sign out other
devices**. These are taken from the login cookie only — an agent in a pane,
which acts as its workspace owner through its pane token, cannot change its
@@ -316,7 +329,7 @@ stays empty (changing nothing) until you add an account.
claude setup-token
# Add it to the pool (the token is typed hidden, or piped on stdin)
agenthub accounts add team-1
agenthub accounts list # live panes, $ in the last 5 h / 7 d, quota, state
agenthub accounts list # live panes, $ and tokens in the last 5 h / 7 d, quota, state
agenthub accounts remove team-1
```
@@ -345,7 +358,10 @@ Code reads it from no env var, so before a member's pane starts, AgentHub writes
it as that member's `~/.claude/.credentials.json`, as the member (0600). It is
never written into a host pane's home, never over a login the member made
themselves, and never a second account into a home where a pane of theirs runs
on one. Host panes therefore get only setup-token accounts. The refresh token of
on one. Host panes therefore get only setup-token accounts — and the login the
host itself was moved in from (the profile marked active), as long as the
host's `~/.claude/.credentials.json` still holds that pair: the pane runs on it
as it is and its usage is booked to that account. The refresh token of
a login is single use, so the server is the one that refreshes it, before it
expires (`claude-pool.refreshLogins`, checked every 60 s; `DASHBOARD_POOL_REFRESH_MS=0` turns it off). It then writes the new pair into every
home it placed the login in, and Claude Code picks up tokens another process
@@ -363,8 +379,13 @@ How a new session picks its account (`server/lib/claude-pool.js`):
weekly window goes last. It is still used if every account is that full.
2. Next, the account with the fewest live panes, so concurrent sessions spread out.
3. Then the account with the least $ spent in the last 5 hours, then the last
7 days. AgentHub sums this from the usage of the sessions it started on each
account (`dashboard_runs.account`).
7 days. AgentHub sums this from the usage of the sessions its runs held on
each account (`dashboard_runs.account`), over the hours each run held them —
a session resumed on another account counts there from then on — with
sub-agent usage included and the same prices as the cost pages
(`server/lib/pool-usage.js`). The table also shows the tokens behind it.
Sessions started outside AgentHub on the same account are not counted; the
quota column is Anthropic's own reading and does include them.
- The pane gets the token through tmux `-e CLAUDE_CODE_OAUTH_TOKEN=…`. That
token outranks the machine's own `/login` for that process only.