feat: choose your own pool account; skills with "token" in a doc name upload; System Health off Linux

- Skill snapshot/upload: a key file's name (.env, *.pem, id_rsa, .netrc,
  .credentials.json…) is always refused, but a word like token/secret/
  password/credential only in a data file's name — design-tokens.md or
  count_tokens.py is skill content, not a key.
- System Health: off Linux the History, CPU threads and Busiest processes
  cards (all read from /proc) are left out instead of shown empty; the CPU
  bar without a sample and Windows' always-zero Load row go too.
- Users pick their own pool account per tool (/api/me/pool-accounts,
  Settings → AI tools), within their groups, refused while pinned.
- Admin user detail, 9router update, session replay and Antigravity
  catalog changes from parallel sessions.
This commit is contained in:
2026-10-28 10:41:53 +07:00
parent 4999325210
commit 0f17efe3b4
39 changed files with 1306 additions and 230 deletions
+98
View File
@@ -14,6 +14,7 @@
* - GET /api/users/audit (Auth) — staff reads of developers' sessions
* - GET/PUT /api/me/prefs, POST /api/me/onboarded (Auth) — the account's own UI preferences
* - GET/PUT /api/me/instructions (Auth) — the account's own agent instructions
* - GET/PUT /api/me/pool-accounts (Auth) — the account's own pick of a pool account per tool
* - GET/PATCH /api/me/account, POST /api/me/password, POST /api/me/sign-out-others
* (Auth) — Settings → Account: the account's own profile, password and logins
* - GET/PUT/DELETE /api/me/avatar (Auth) — the account's own picture
@@ -243,6 +244,103 @@ const paths = {
},
},
},
"/api/me/pool-accounts": {
get: {
tags: ["Auth"],
summary: "The account pool as this account may use it (server mode)",
description:
"Per tool with at least one account the account's pool group allows: those accounts (never a token), an admin's pin and the account's own choice. Local mode: {tools: []}.",
responses: {
200: {
description: "Tools",
content: {
"application/json": {
schema: {
type: "object",
required: ["tools"],
properties: {
tools: {
type: "array",
items: {
type: "object",
required: ["tool", "pin", "choice", "accounts"],
properties: {
tool: { type: "string", enum: ["claude", "codex", "cursor"] },
pin: { type: "string", nullable: true },
choice: { type: "string", nullable: true },
accounts: {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string" },
kind: { type: "string" },
email: { type: "string", nullable: true },
expired: { type: "boolean" },
quotaPercent: { type: "number", nullable: true },
full: { type: "boolean" },
},
},
},
},
},
},
},
},
},
},
},
401: err("EAUTH"),
},
},
put: {
tags: ["Auth"],
summary: "Run this account's new sessions of a tool on one pool account (null = automatic)",
description:
"Below an admin's pin, above the balancer. A Claude login goes into the account's home at once (`switched`); Codex and Cursor take it at the next session. Login cookie only, audited.",
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
required: ["tool"],
properties: {
tool: { type: "string", enum: ["claude", "codex", "cursor"] },
account: { type: "string", nullable: true },
},
},
},
},
},
responses: {
200: {
description: "Chosen",
content: {
"application/json": {
schema: {
type: "object",
properties: {
tool: { type: "string" },
choice: { type: "string", nullable: true },
switched: {
type: "string",
nullable: true,
enum: ["switched", "kept", "own-login", "expired", "next-session"],
},
},
},
},
},
},
400: err("EBADTOOL"),
401: err("EAUTH"),
403: err("ELOCALMODE"),
404: err("ENOACCOUNT — not in the pool, or not one the account's group may use"),
409: err("ELOCKED — an admin pinned this account's sessions of the tool"),
},
},
},
"/api/me/onboarded": {
post: {
tags: ["Auth"],