feat(server-mode): native-like folder browser, a developer kept to their own folder, no password minimum
- Folder browser (the Browse fallback for a remote browser): back/forward/up, a clickable breadcrumb that becomes a path field (Ctrl+L), Places (Home, the user's workspaces, recent picks), a filter, Name/Modified columns, Show hidden and New folder. A click selects; a double-click or Enter opens a folder or picks a file; Backspace goes up; typing jumps to a name. - GET /api/lanes/browse: `hidden=1` (never the dot entries at the top of a non-admin's own folder), each entry's `mtime`, the answer's `home`. - POST /api/lanes/browse/mkdir: one plain folder name inside an existing folder; a non-admin only in their own folder, none in server mode without one; under S3 a member's folder is made as the member. - scope.ownFolder falls back to server.conf's USERS_ROOT under S3, so a settings file without AGENTHUB_USERS_ROOT no longer opens the host's home to every developer. - Passwords: any non-empty password is accepted (no 8-character minimum). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+11
-3
@@ -235,9 +235,17 @@ back on ANY of those to the in-app browser `GET /api/lanes/browse?path=<abs>`.
|
||||
A file field asks it with `files=1`: entries then include the folder's files
|
||||
(`file: true`, listed after the folders), and `path` opens the way the native
|
||||
picker's `start` does (`~` expanded, a file's folder, home when missing) —
|
||||
a click on a file picks it. In server mode with
|
||||
`AGENTHUB_USERS_ROOT` set, a developer's browser opens on their own folder
|
||||
(`parent: null` there) and any path whose real path leaves it is `403 EFOLDER`.
|
||||
a double-click on a file picks it. `hidden=1` lists dot entries too (never
|
||||
those at the top of a non-admin's own folder), every entry carries its `mtime`
|
||||
(ms) and the answer its `home` (the browser's Home place). In server mode with
|
||||
`AGENTHUB_USERS_ROOT` set (under S3, else server.conf's `USERS_ROOT`), a
|
||||
developer's browser opens on their own folder (`parent: null` there) and any
|
||||
path whose real path leaves it is `403 EFOLDER`. The browser's **New folder**
|
||||
is `POST /api/lanes/browse/mkdir` (`{parent, name}` → `201 {path}`): one plain
|
||||
folder name inside an existing folder, `400 EBADNAME`/`ENOTFOUND`,
|
||||
`409 EEXISTS`; a non-admin only inside their own folder, and in server mode
|
||||
not at all without one (`403 EFOLDER`); a member's folder is made as the
|
||||
member (S3).
|
||||
|
||||
#### Update a workspace
|
||||
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ than behind a disclosure — a disclosure around a single field only hid it.
|
||||
|
||||
A workspace has no AI Proxy of its own: each NEW session picks one. The **AI Proxy** select in the workspace home's prompt box lists the profiles saved in Settings › AI Proxy (9Router, OpenRouter, a self-hosted gateway) plus **Native** (the vendor's own API); it shows Native alone, disabled, when the session's AI tool cannot use a proxy (only Claude and Codex can), and it remembers the last pick (`agenthub.session.proxy`, falling back to the pre-rename `agenthub.session.router`). The start sends `proxy: "<name>"` (`router` is still accepted); a Claude pane then runs with `--settings=<data dir>/proxy-settings/<name>.json`, which outranks the workspace's own settings, and a Codex pane with `-p <name>` plus its key through tmux `-e`. So two sessions of one workspace can run on two proxies, a worktree session gets its proxy too, and switching never touches a running session. A resume goes back to the proxy its session last ran on (`dashboard_runs.proxy`). Editing a profile in Settings rewrites those files (a rotated key reaches live Claude sessions); deleting it removes them. Older builds wrote a workspace's proxy into its `.claude/settings.local.json`; every server start takes those eight keys back out. See `docs/API.md` § AI Proxy profiles.
|
||||
|
||||
Both modes' path field has a **Browse** button next to it that opens the operating system's own folder picker (`POST /api/lanes/pick-path`). A browser cannot hand a web page an absolute path from its own dialog, so the SERVER opens the dialog on its desktop (zenity/kdialog on Linux, `osascript` on macOS, PowerShell on Windows) — which is your screen only when the browser runs on the same machine. A browser on another machine, or a server with no desktop or no zenity/kdialog, gets the small in-app folder browser instead (`GET /api/lanes/browse?path=<abs>`): click a subfolder to descend, ".." to go up, "Select this folder" to fill the path field; git repos are marked. The same button sits beside the workspace home's **Folder** field, Import history's folder, the project-memory folder, CLAUDE_HOME/CODEX_HOME and a remote source's SSH identity file (a file picker: its in-app fallback lists the folder's files too, and a click on a file picks it).
|
||||
Both modes' path field has a **Browse** button next to it that opens the operating system's own folder picker (`POST /api/lanes/pick-path`). A browser cannot hand a web page an absolute path from its own dialog, so the SERVER opens the dialog on its desktop (zenity/kdialog on Linux, `osascript` on macOS, PowerShell on Windows) — which is your screen only when the browser runs on the same machine. A browser on another machine, or a server with no desktop or no zenity/kdialog, gets the in-app folder browser instead (`GET /api/lanes/browse?path=<abs>`), laid out like a native picker: Back/Forward/Up, a clickable breadcrumb that becomes a path field (✎ or Ctrl+L), Places (Home, your workspaces, recent picks), a filter, Name/Modified columns (click to sort), **Show hidden**, and **New folder**. A click selects, a double-click or Enter opens (Backspace goes up, typing jumps to a name), and "Select this folder" fills the path field with the selected folder, else the one shown; git repos are marked. The same button sits beside the workspace home's **Folder** field, Import history's folder, the project-memory folder, CLAUDE_HOME/CODEX_HOME and a remote source's SSH identity file (a file picker: its in-app fallback lists the folder's files too, and a double-click on a file picks it).
|
||||
|
||||
## Workspace tool: Claude or Codex
|
||||
|
||||
|
||||
+6
-3
@@ -196,7 +196,9 @@ that already belongs to another user's workspace gets `409 EOWNED`. Admins are
|
||||
not limited. Without [one Linux user per account](#one-linux-user-per-account)
|
||||
you create each user's folder yourself, and this keeps workspaces apart, not
|
||||
agents. `install-system` sets this value to `/srv/agenthub/users`, and adding a
|
||||
user makes the folder (it is the Linux account's home).
|
||||
user makes the folder (it is the Linux account's home). With S3 installed, a
|
||||
settings file that lacks the variable falls back to `USERS_ROOT` in
|
||||
`/etc/agenthub/server.conf`, so a developer is never shown the host's home.
|
||||
|
||||
## What the gate does
|
||||
|
||||
@@ -222,7 +224,7 @@ user makes the folder (it is the Linux account's home).
|
||||
- A login lasts 30 days in the httpOnly, `SameSite=Strict` cookie
|
||||
`agenthub_sid`. Only the token's SHA-256 is stored. Logging out, a password
|
||||
change and disabling the user each end it.
|
||||
- Passwords are stored as scrypt hashes and must be at least 8 characters.
|
||||
- Passwords are stored as scrypt hashes; any non-empty password is accepted (no minimum length).
|
||||
Every login attempt counts before its password is checked, so attempts sent
|
||||
in parallel count too. After 10 failed logins from one address, or 30 for one
|
||||
username, in 15 minutes, further logins answer `429 ETOOMANY` until the
|
||||
@@ -539,7 +541,8 @@ that are not theirs. Each case below is refused, with a test in
|
||||
line AgentHub adds for a new worktree or `.mcp.json` is written only when the
|
||||
repo's real git dir is the same account's; a FIFO there cannot hang the
|
||||
dashboard.
|
||||
- **Browsing and branch lists.** A developer's `GET /api/lanes/browse` and
|
||||
- **Browsing and branch lists.** A developer's `GET /api/lanes/browse`, the
|
||||
browser's New folder (`POST /api/lanes/browse/mkdir`, made as the member) and
|
||||
`GET /api/lanes/branches` stay inside their own folder (`403 EFOLDER`), and
|
||||
so does a workspace's extra folder (`POST /api/lanes/:id/sources/folder`,
|
||||
refused before the path is looked at, so the answer says nothing about
|
||||
|
||||
Reference in New Issue
Block a user