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:
+61
-2
@@ -553,7 +553,7 @@ export type AuditEntry = {
|
||||
};
|
||||
|
||||
/** Who is using the dashboard: local mode has no login at all. */
|
||||
export type AuthUser = { id: number; username: string; role: "admin" | "analyst" | "member" };
|
||||
export type AuthUser = { id: number; username: string; role: "admin" | "analyst" | "developer" };
|
||||
export type AuthState = { mode: "local" } | { mode: "server"; user: AuthUser | null };
|
||||
|
||||
/** GETs in flight, by path: concurrent callers of the same read (several
|
||||
@@ -596,7 +596,12 @@ async function send<T>(path: string, options?: RequestInit): Promise<T> {
|
||||
if (res.status === 401 && body?.error?.code === "EAUTH") {
|
||||
window.dispatchEvent(new Event(AUTH_REQUIRED_EVENT));
|
||||
}
|
||||
throw Object.assign(new Error(body?.error?.message || `HTTP ${res.status}`), {
|
||||
// Server mode: a role refusal reads the same in every toast, translated.
|
||||
const message =
|
||||
body?.error?.code === "EADMIN"
|
||||
? i18n.t("errors:adminOnly")
|
||||
: body?.error?.message || `HTTP ${res.status}`;
|
||||
throw Object.assign(new Error(message), {
|
||||
code: body?.error?.code,
|
||||
status: res.status,
|
||||
// EGITAUTH from a lane git action: which host wants a login.
|
||||
@@ -670,6 +675,14 @@ export const api = {
|
||||
/** POST /api/auth/logout — ends this login. */
|
||||
logout: () => request<{ ok: true }>("/auth/logout", { method: "POST" }),
|
||||
},
|
||||
// ───────────────────────── The account's own UI prefs (routes/me.js) ─────────────────────────
|
||||
me: {
|
||||
/** GET /api/me/prefs — this account's prefs (`local` in local mode). */
|
||||
prefs: () => request<MePrefs>("/me/prefs"),
|
||||
/** PUT /api/me/prefs — merges `prefs` (a string per key, null removes it). */
|
||||
savePrefs: (prefs: Record<string, string | null>) =>
|
||||
request<MePrefs>("/me/prefs", { method: "PUT", body: JSON.stringify({ prefs }) }),
|
||||
},
|
||||
// ───────────────────────── Server mode staff views (routes/users.js) ─────────────────────────
|
||||
users: {
|
||||
/** GET /api/users — every account with its recent usage (admin/analyst). */
|
||||
@@ -1253,6 +1266,13 @@ export const api = {
|
||||
/** POST /api/settings/quit — close every workspace terminal and stop the
|
||||
* server until `agenthub start` (or a boot/login with autostart on). */
|
||||
quit: () => request<{ ok: true; killed: number }>("/settings/quit", { method: "POST" }),
|
||||
/** POST /api/settings/uninstall — remove AgentHub from this machine, its
|
||||
* database included, in a detached worker (scripts/uninstall.js). */
|
||||
uninstall: () =>
|
||||
request<{ ok: true; log: string }>("/settings/uninstall", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ confirm: true }),
|
||||
}),
|
||||
/** Opt-in secret redaction of stored payloads and the folders whose hooks
|
||||
* are dropped (`server/lib/privacy.js`). */
|
||||
privacy: {
|
||||
@@ -1265,6 +1285,19 @@ export const api = {
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
},
|
||||
/** The daily database backup (`server/lib/db-backup.js`). */
|
||||
backup: {
|
||||
/** GET /api/settings/backup */
|
||||
get: () => request<BackupState>("/settings/backup"),
|
||||
/** PUT /api/settings/backup — any subset; an empty `dir` turns it off */
|
||||
save: (data: { dir?: string; hour?: number; keep?: number }) =>
|
||||
request<BackupState & { ok: boolean }>("/settings/backup", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
/** POST /api/settings/backup/run — starts one; poll `get` while `running` */
|
||||
run: () => request<BackupState & { ok: boolean }>("/settings/backup/run", { method: "POST" }),
|
||||
},
|
||||
/** The bearer token for `POST /api/hooks/ingest-batch`. Only its hash is
|
||||
* stored: `rotate` returns the new token this once, `get` never does. */
|
||||
pushToken: {
|
||||
@@ -4279,6 +4312,12 @@ export interface RunStartArgs {
|
||||
|
||||
/** A lane's tmux-backed terminal run — one per lane, id is the tmux session
|
||||
* name (`agenthub-lane-<laneId>`). */
|
||||
/** One account's own UI preferences (server/routes/me.js): known keys → string values. */
|
||||
export interface MePrefs {
|
||||
prefs: Record<string, string>;
|
||||
onboardedAt: string | null;
|
||||
}
|
||||
|
||||
export interface RunHandle {
|
||||
id: string;
|
||||
laneId: number | null;
|
||||
@@ -4510,6 +4549,26 @@ export interface PrivacySettings {
|
||||
env_owned: { redact_secrets: boolean; ignore_cwd: boolean };
|
||||
}
|
||||
|
||||
/** GET /api/settings/backup. `files` are AgentHub's own backups in `dir`,
|
||||
* newest first; `last` is this server process's last run. */
|
||||
export interface BackupState {
|
||||
dir: string;
|
||||
hour: number;
|
||||
keep: number;
|
||||
running: boolean;
|
||||
last: {
|
||||
at: string;
|
||||
ok: boolean;
|
||||
file?: string;
|
||||
size?: number;
|
||||
ms?: number;
|
||||
error?: string;
|
||||
} | null;
|
||||
files: { name: string; size: number; mtime: string }[];
|
||||
dir_error: string | null;
|
||||
env_owned: { dir: boolean; hour: boolean; keep: boolean };
|
||||
}
|
||||
|
||||
/** GET /api/settings/push-token — whether push ingest is on; never the token. */
|
||||
export interface PushTokenStatus {
|
||||
enabled: boolean;
|
||||
|
||||
Reference in New Issue
Block a user