Commit Graph

195 Commits

Author SHA1 Message Date
nntrivi2001 7e41c81d53 feat(git-identity): import an existing SSH key and sign commits with it
Settings → Git gains Commit signing: one picker signs every commit
(terminal, Changes panel, agents) with one of the account's keys —
gpg.format ssh, user.signingkey = the key's private file, commit.gpgsign
true in its own ~/.gitconfig; a signing key the user set up themselves is
replaced only after a confirm (EFOREIGNSIGN without `replace`). Import a key
you have takes a key file or paste (OpenSSH or PEM, ed25519 or RSA >= 2048)
into ~/.config/agenthub/ssh/ as a named key, written like a generated one
(member: write-as, 0600), and by default signs with it in the same step.
A passphrase key, a .pub, an ecdsa key, a mismatched paste and the same key
twice are refused; only the public half ever comes back. Deleting the
signing key stops signing so commits keep working.

The SSH tab is renamed "Git (GitHub, GitLab, Gitea, Backlog…)" with the
Gitea and Backlog steps, and the File Explorer's ESECRET toast now says
that top-level dot folders of a home are edited in a terminal, and where
the SSH keys live instead.
2026-11-02 16:12:57 +07:00
nntrivi2001 e0b8d5641e chore: v2.8.60 — Claude panes scroll faster 2026-11-02 15:32:46 +07:00
nntrivi2001 68c3cfaf26 chore: v2.8.59 — Preview explains a blank outside site 2026-11-02 15:10:55 +07:00
nntrivi2001 5d62003fe5 feat(workspace): Preview tab in the session panel; local links open there
The session panel's header becomes two tabs, Changes and Preview. Preview
shows a web page by address (Reload, Open in a browser tab, the workspace's
ports as chips, last address per workspace) in an iframe on the page's own
origin: http(s) only, no userinfo, never the dashboard's origin, no top
navigation. One rule for every link (lib/openInPreview.ts): a chat or
terminal link to localhost/127.0.0.1/0.0.0.0/[::1] opens in Preview while a
session panel answers and the dashboard is on loopback; anything else, or a
Ctrl/Cmd click, opens a browser tab as before.
2026-11-02 15:06:04 +07:00
nntrivi2001 4263b3eb26 chore: v2.8.57 — chat column widens to 64rem 2026-11-02 14:42:01 +07:00
nntrivi2001 81b3cbda65 chore: v2.8.56 — new tab / split pane starts in the Session defaults 2026-11-02 13:34:36 +07:00
nntrivi2001 1ec9f2e592 chore: v2.8.55 — chat + menu no longer covered by the controls under the prompt box 2026-11-02 13:30:33 +07:00
nntrivi2001 5caad0df2d chore: v2.8.54 — chat Stop shows while Claude thinks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:29:05 +07:00
nntrivi2001 923c1635b4 chore: v2.8.53 — chat + menu opens downward when there is no room above
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:25:38 +07:00
nntrivi2001 841263bce1 chore: v2.8.52 — git: switch with changes + shelves, branches & tags, history graph + multi cherry-pick, merge editor, rebase/reset/force-with-lease 2026-11-02 12:28:58 +07:00
nntrivi2001 e1f433804c feat(git): history graph with search, commit details, file history, blame/browse at a revision, compare refs, and multi-commit cherry-pick/revert with preview, pause banner and undo 2026-11-02 12:22:46 +07:00
nntrivi2001 2b5d7268ed feat(git): 3-way merge editor, reflog, rebase/interactive rebase, reset with undo, force-with-lease push, remote branches and remotes
Server: git-rewrite.js (rebase + continue/skip/abort, rebase-interactive with
no editor, reset soft/mixed/keep/hard, undo-rewrite, force-push with lease,
remote-branch-delete, remote add/rename/remove) and git-conflicts.js
(conflict stages read + conflict-save, reflog), dispatched from runGitAction;
operationInProgress now reports a rebase and only ALLOWED_WHILE_REBASING
writes run during one. New reads: /git/conflict, /git/reflog, /git/remotes,
/git/rewrite-plan. Client: History tools menu, MergeEditor, ReflogView,
RebaseDialog, InteractiveRebase, ResetDialog, ForcePushDialog, RemotesDialog,
rebase banner Skip. CLAUDE.md git-writes rule rewritten for the new actions.
2026-11-02 12:12:45 +07:00
nntrivi2001 2cc492bb88 feat(git): Branches & tags panel — rename, branch from any ref, upstream, tags
GET /git/refs lists local branches (upstream, ahead/behind, last commit,
merged, held by a worktree, protected), remote branches grouped by remote
and tags (target, annotated message/tagger/date). New writes in
server/lib/git-refs.js, dispatched from git-ops.js: branch-rename,
branch-upstream (set/unset), tag-create (light/annotated), tag-delete
(confirm), tag-push (one tag to origin, no --tags, no force), and
branch-create with a looked-up from + switch on a clean tree. Every ref is
looked up, never passed through; repo-wide writes are repo-root only
(ESUBDIR) and refused during any operation (EMERGING).

Client: git/BranchManager + TagManager dialog opened from the git bar's
tags icon, en+vi (gitRefs namespace), keyboard navigable.

# Conflicts:
#	CLAUDE.md
#	client/src/components/lanes/LaneGitBar.tsx
#	docs/API.md
#	server/__tests__/lanes-api.test.js
#	server/routes/lanes.js
2026-11-02 12:00:55 +07:00
nntrivi2001 f6eb39eb73 feat(git): switch with uncommitted changes (carry / shelve), Create & switch, and shelves with Restore/Delete
- switch takes changes: carry (plain git switch, 409 ECARRYCONFLICT with files) or shelve (shelf first, re-applied if the switch fails)
- branch-create switch: true runs git switch -c, the changes come along
- shelve / unshelve / shelf-drop + GET /git/shelves: only agenthub-shelf: stash entries, addressed by sha, repo root only, never mid-operation; a clean restore drops the shelf, a conflicted one keeps it
- 409 EAGENTRUNNING until confirm: true while the workspace's agent is working
- git bar: Bring my changes / Shelve and switch choice, Create & switch, restore offer after a switch; Changes panel: Shelve (n) and the Shelves list
- fix: after a switch the Changes panel showed the old branch — the post-write re-read joined an in-flight GET started before the write (request() dedupe); writes now close in-flight GETs, and every git write fires agenthub:git-changed so every view of the workspace re-reads
2026-11-02 11:44:35 +07:00
nntrivi2001 1f18f151d9 chore: v2.8.51 — clones and uploads in one global dock; a clone survives an update; CodeCommit needs only the key ID 2026-11-02 10:49:01 +07:00
nntrivi2001 a27589418c feat(clone): background clone jobs that survive a reload and a server restart
Both clone POSTs take background: true: every check answers synchronously,
then 202 {job}. git runs in a detached runner (scripts/clone-runner.js, its
own process group, stderr to a file — no SIGPIPE when the server restarts);
the server tails it for progress and runs the after-step (workspace / extra
folder) once when it exits. Jobs persist in <data dir>/runtime/clone-jobs.json
(0600, atomic, throttled progress writes); on start a live runner is adopted
(counted against the clone limit), an ended one finished, a queued one
requeued (interrupted if it carried a typed login), the rest interrupted.
GET /clone/jobs, DELETE /clone/jobs/:id, POST /clone/jobs/:id/cancel (queued:
dequeued; running: SIGTERM to the runner's group -> cancelled, no after-step)
and /retry (new job from the stored request, never a credential). The
held-open path is unchanged; worktree.gitCommand builds the runner's argv
and git-identity.cloneFailure the shared error classification.
2026-11-02 10:47:51 +07:00
nntrivi2001 e7060a5e42 feat(artifacts): Share again takes a skill's newest version; a same-named upload updates it
A shared skill followed its owner's ~/.claude/skills folder only, so one
uploaded from a browser (or living in a plugin) stayed at its first snapshot
for good. Share again (a skill's ⋯ menu and detail sheet) now takes the
owner's copy on the server — its source folder, else the same-named skill of
a plugin they installed at user scope — and shares it; with no copy it asks
for the folder and uploads it onto that row (`id`). An upload whose skill is
named like one the uploader already has replaces that row's version instead
of adding a twin. Installers are then offered Update, as before.
2026-10-30 14:20:18 +07:00
nntrivi2001 da7b86d5c8 fix(changes): Discard takes only the ticked files and blocks (#87)
The Changes panel's "Discard all" threw away every changed file, ticked or
not. It is now "Discard (n)": the same selection Commit sends — whole ticked
files as `paths`, a partly ticked file's ticked blocks as `hunks` — and is
disabled with nothing ticked. The server's `discard` accepts `hunks`
(`selectionPatch` in reverse, `git apply -R`), building every patch before
applying any so a stale selection undoes nothing.

Closes #87
2026-10-30 13:05:13 +07:00
nntrivi2001 783b7f4101 fix(cost): bill a session outside every workspace to its Linux account's user
A script's claude -p in a temp folder (the SG Flow eval runner strips the
pane's env) had no workspace, so its spend reached nobody's Today, the Users
page or a spend quota — ~$57 of a $74 day was missing from the status bar.

sessions.account records the Linux account a session ran as (the hook's
peer uid, else its folder for poller-made and old rows); a session no
workspace owns is owned by that account's user — a member's to them, the
host's to the admin ticked 'Owns this server's host account' (or the only
admin). Workspace ownership still wins.
2026-10-30 08:50:34 +07:00
nntrivi2001 c28395402b chore: v2.8.40 — a Codex account switch reaches running sessions 2026-10-29 15:59:19 +07:00
nntrivi2001 7798530528 fix(profiles): a Codex switch reaches running and new sessions
Codex >= 0.164 sends every request through a shared app-server daemon that
outlives its TUIs and keeps the login it started with, so writing a new
auth.json changed nothing: open sessions and even new ones stayed on the
old (out-of-quota) account. After a switch, restart that CODEX_HOME's
daemon when one runs; open TUIs reconnect to it on their own (verified on
0.164.0 with two keys). The response says so (following), and the toast
tells the user running sessions follow.
2026-10-29 15:54:20 +07:00
nntrivi2001 d70a7c9403 chore: v2.8.39 — no one can take a colleague's display name 2026-10-29 13:47:31 +07:00
nntrivi2001 9f8dc9082e chore: v2.8.38 — every user row shows its avatar 2026-10-29 13:40:39 +07:00
nntrivi2001 db5a7776cc chore: v2.8.37 — the Artifacts installs list is never cut off 2026-10-29 11:25:10 +07:00
nntrivi2001 609a35f251 chore: v2.8.36 — an HTML page opens in a modal over the screen (Changes Preview, Chat) 2026-10-29 11:20:54 +07:00
nntrivi2001 4a82790734 chore: v2.8.35 — an HTML file's Preview sits beside Whole file 2026-10-29 10:47:39 +07:00
nntrivi2001 39bea78a9c chore: v2.8.34 — HTML page on Preview in the Changes panel (also from Changes) 2026-10-29 10:38:15 +07:00
nntrivi2001 38ea78b489 chore: v2.8.33 — an HTML file opens as the page (Files panel, Chat preview, File Explorer) 2026-10-28 16:46:21 +07:00
nntrivi2001 7b5abc6ad9 feat(files): an HTML file opens as the page — Files panel, Chat preview, File Explorer
Clicking an .html/.htm file now renders it instead of showing its source or
downloading it:
- Changes/Files panel: a whole HTML file opens on Preview (Raw = the editor).
- Chat file preview: a workspace page brings its relative files along.
- File Explorer: double-click / Enter opens a viewer; Download stays on its
  header and right-click menu.

The page runs in the chat artifact's sandbox (iframe srcdoc, scripts, never
allow-same-origin). Its relative stylesheets (and their url()s), scripts,
images, media and icons are read by the dashboard through the existing
download routes and inlined as <style>/data: URLs (lib/htmlInline.ts: never
above the folder's root, ≤ 60 files / 30 MB) — the login cookie is
SameSite=Strict, so a frame could not fetch them itself, and no new route
serves files without it.
2026-10-28 16:46:07 +07:00
nntrivi2001 ca55ff8ec5 chore: v2.8.32 — a Claude plan is the one Anthropic reports now (Max 5x upgraded from Pro) 2026-10-28 15:11:22 +07:00
nntrivi2001 33d30f5e1f chore: v2.8.31 — remote-only branches in the branch switcher, spend quota per user 2026-10-28 15:01:16 +07:00
nntrivi2001 14e979837e fix(git): the branch switcher lists and checks out branches only the remote has
GET /api/lanes/:id/git/branches read only refs/heads, so a branch someone
else pushed never showed after a fetch or pull, and `switch` refused it
(ENOBRANCH). It now also returns `remotes`: fetched remote-tracking branches
with no local branch of that name (origin/HEAD left out), listed under
"On the remote"; switching to one runs `git switch -c <b> --track
refs/remotes/<remote>/<b>` with the remote ref looked up, never passed
through (bare name only when exactly one remote has it, else EAMBIGUOUS).
The bar also re-reads the branch list after a fetch or pull instead of
keeping the list it loaded first.
2026-10-28 14:57:32 +07:00
nntrivi2001 fa27b299e7 feat(server-mode): spend quota per user — no new session, prompts refused once used up
An admin sets a default spend quota on the Users page and overrides it per
user (Edit user → Spend quota: default / no limit / their own), in dollars
per local day or per sliding 5 hours. Spend is the users page's own
hourlyTokenRows + calculateCost over the sessions a user owns.

Over the quota, until it opens again:
- spawnRun refuses every start/resume/home prompt with EQUOTA (429);
- a minute sweep writes each pane's AGENTHUB_QUOTA_FILE (host: data dir,
  member: handoff dir via write-as) and hook-handler.js exits 2 on
  UserPromptSubmit from that file alone (Claude Code, Codex with trusted
  hooks) — fail-open, never asks the dashboard, never for Antigravity;
- a member's omp on the shared login loses its gateway key (clientsFor);
- panes no hook can stop (Antigravity, Cursor, Codex without trusted
  hooks, omp on its own login) are ended.

GET /api/me/quota feeds a "Quota N%" chip in the status bar.
New: server/lib/user-quota.js, user_quotas table, PUT /api/users/quota-default,
PATCH /api/users/:id {quota}, docs in SERVER-MODE.md § Spend quotas.
2026-10-28 14:31:11 +07:00
nntrivi2001 9e3d6f4ca1 chore: v2.8.30 — account pool as one board of pools, real Claude plan names 2026-10-28 11:47:19 +07:00
nntrivi2001 0f17efe3b4 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.
2026-10-28 10:41:53 +07:00
nntrivi2001 4999325210 chore: v2.8.28 — System Health grouped, a health score that sees lag 2026-10-27 16:40:01 +07:00
nntrivi2001 272a3e7d6e chore: v2.8.27 — Update 9router from Settings → AI Proxy 2026-10-26 17:08:09 +07:00
nntrivi2001 631ea96f19 chore: v2.8.26 — React 19 / Express 5 / xterm 6, bigger file drops, hidden-tab terminal size fix 2026-10-26 16:36:49 +07:00
nntrivi2001 8546748759 chore: maintenance — dependency majors, rename step 4, member Claude config, OpenAPI coverage
Dependencies
- Security: body-parser and multer DoS advisories fixed (npm audit: 0).
- Server: Express 5 (query parser kept "extended", req.body defaults to {},
  SPA catch-all /{*splat}, send-file helper restores Express 4's dot-folder
  rule for sendFile/download, alerts ack route without inline regex),
  better-sqlite3 13, js-yaml 5 (server/lib/yaml.js keeps js-yaml 4's default
  schema), uuid dropped for crypto.randomUUID, node-pty beta.18, multer 2.5.
- Client: React 19, xterm 6 (WebGL renderer replaces the removed canvas one,
  DOM fallback on context loss; Alt+arrow word moves re-mapped), lucide-react 1,
  TypeScript 7, i18next/react-i18next/codemirror minors.

Workspace rename step 4
- `agenthub lanes` and `--lane` exit 1 with "renamed: use …"; client api.ts,
  the CLI and the MCP server call /api/workspaces; /api/lanes stays mounted.

Server mode (S3): members manage their own Claude Code config
- server/lib/cc-target.js resolves whose config a /cc-config request touches
  and gates the router by allowlist; /profiles* and /plugins/update-all stay
  admin-only. Member writes go through write-as, CLI calls through sudo as the
  member (never -y), reads through openSafe; cc_config_changed reaches only
  its owner. Not verified live on an S3 install yet.

Other
- Chat: an HTML file is an artifact only when written whole and not a project
  page (index.html, or under src/, public/, templates/, …).
- OpenAPI covers every /api route; openapi-coverage.test.js guards it.
- read-pool test keeps its event loop alive (its worker is unref'd).
- Docs: plan statuses, checklists, CLI.md, LANES.md, SERVER-MODE.md, API.md.
2026-10-26 14:22:41 +07:00
nntrivi2001 6b602c76b8 chore: v2.8.25 — per-user Today, Alt+V image paste on Windows, local paste/drop by path 2026-10-26 11:18:05 +07:00
nntrivi2001 c59e196e41 chore: v2.8.24 — sub-agent edits in Edited N files, OpenCode/Grok dropped, Artifacts list view and skill sync
Edited N files (#86): a turn's card counts the files its subagents wrote
or edited (read from their own transcripts, nested ones folded in), and a
turn longer than the chat's first page keeps the edits made before the
page cut. A non-Claude transcript page honours its limit again.

AI tools: OpenCode and Grok Build left the catalog (opencode-usage.js
gone; a Grok payload on the Claude hook route is dropped), and so did the
leftover Kimi Code quota reader.

Artifacts: a list view, skill packs and badges only by exception; a
shared skill follows its owner's folder, and an installer whose copy
differs is offered Update.
2026-10-26 09:46:45 +07:00
nntrivi2001 72aef2426c feat(artifacts): a shared skill follows its owner's folder; installers get Update
A skill in the Hub was a snapshot taken once: the owner changing
~/.claude/skills/<name> changed nothing there, and an installer had to
uninstall and install again to get a newer version.

- syncSkillSources (on GET /api/artifacts, at most every 15 s): a skill row
  taken from a folder — the owner's ~/.claude/skills/<name>, or the
  workspace's .claude/skills/<name> — is brought up to it. Only a folder
  whose names/sizes/mtimes changed is read again, with a pick's checks (no
  symlink, no secret, <= 5 MB, a member's files as theirs); a change is a
  NEW snapshot, the old one dropped, and an artifact_changed broadcast.
- artifacts.version (snapshot content hash) and source_hash (the folder's
  when last taken, additive columns): an upload or a Hub edit records the
  folder as it is then, so only a LATER folder change replaces it.
- A list row's updateAvailable: installed, and the viewer's copy differs
  from version. The page shows Update (a pack: Update N); install with
  {update: true} replaces the copies (old ones moved aside) and is not
  counted as another install.
2026-10-26 09:34:21 +07:00
nntrivi2001 127da516ac chore(quota): drop the Kimi Code quota reader (Kimi already left the catalog)
Redone on v2.8.22 from 3179d25 (feat/grok-usage, never merged).
2026-10-26 09:31:37 +07:00
nntrivi2001 c0832182f2 feat(ai-tools): drop OpenCode and Grok Build
- opencode.json / grok.json leave the built-in catalog; opencode-usage.js,
  its poller, transcript branch and table creation go with it (existing
  tables and session rows stay as history).
- A Grok payload reaching the Claude hook route (Grok runs the Claude hooks
  itself) is dropped instead of recorded as tool = 'grok'.
- quota.js no longer reads Grok or OpenCode Go logins.
- Redone on v2.8.22 from c6b2892 (feat/grok-usage, never merged).
2026-10-26 09:31:30 +07:00
nntrivi2001 c8bc4a95a9 chore: v2.8.23 — workspace groups, inner repos in Changes (opt-in), sub-agent tokens counted
Workspace groups: named, foldable, ordered sidebar sections kept per
account (agenthub.workspace.groups); a workspace joins one from its menu,
a drop on the header, a drag under a row, or Alt+Up/Down past a group's
edge; headers drag to reorder; a folded header shows its most urgent
state; New workspace in this group; fold/unfold all.

Inner repos: GET /api/lanes/:id/repos + ?repo= on git/fs routes; the
Changes panel shows a parent folder's repos only when Settings -> Git ->
Changes panel turns it on (agenthub.changes.innerRepos, off by default).

Cost: the hook writer adds a session's sub-agent transcripts to its
tokens and hours (server/lib/subagent-usage.js) — a sub-agent on the
session's own model was never counted live (~8% of a week here); an
imported session is re-raised on every pass, so a claude -p run first
seen with an empty transcript no longer stays at $0 (~10%). A one-time
raise-only catch-up (user_version 5) re-reads ~/.claude/projects.

Also: omp install script may redirect to its exact install.redirect URL
(#85, repo-links test exempts it), native paste in the terminal.
2026-10-23 16:41:45 +07:00
nntrivi2001 572d615d4e chore: v2.8.22 — Users today/7/30-day spend counts every tool, update chip follows a running update
- Users: today / 7 / 30-day spend read only token_usage_hourly, which holds
  Claude transcript hours only — omp/Codex/Antigravity/Cursor sessions,
  sub-agent files, baselines and long-context pricing were missing. Now
  booked by hourlyTokenRows + calculateCost like the cost routes.
- Update: a page adopts a plugin update the server reports as running
  (after a reload, another tab, or one started from an older bundle), and
  the progress chip shows whenever an update runs and the modal is not
  visible — also when the status stops offering it mid-run.
2026-10-23 11:26:12 +07:00
nntrivi2001 9dd97a369c chore: v2.8.21 — update progress steps, clone in background, extra SSH keys inline, today's spend
- Plugin update reports its running step (plugin-update-progress.json →
  plugin_update_progress); a hidden update or clone leaves a progress chip.
- Clone here can run in the background.
- Git identity: extra SSH keys are added inline per host (no More keys tab).
- Users: spend today (viewer's local day, ?tz_offset=).
- Prettier on the files touched since v2.8.20.
2026-10-23 10:44:11 +07:00
nntrivi2001 685eb748e8 feat: account pool per tool with groups and a Cursor pool; update runs in the background
- The update dialog can be closed while an update applies: it keeps running,
  the page still reloads itself when the new server is up, and a failure
  re-opens the dialog with its error.
- Account pool card: one tab per AI tool (Claude Code, Codex, Cursor CLI,
  Oh My Pi, and every other installed tool, which says it has no pool),
  accounts as cards with 5-hour and weekly quota shown as what is LEFT plus
  their reset times, and the saved profiles of Settings → AI tools →
  Profiles as cards with "Share to pool".
- Cursor account pool (server/lib/cursor-pool.js): Cursor API keys handed to
  a cursor-agent pane only through tmux -e CURSOR_API_KEY;
  `agenthub accounts add <name> --tool cursor`.
- Pool groups (server/lib/pool-groups.js, /api/accounts/groups): a group's
  members run only on its accounts of each tool, a user in no group on the
  accounts no group lists, a pin still wins. account_pins loses its tool
  CHECK (rebuilt, pins kept); three new tables.
- Oh My Pi login sharing can be switched from its pool tab too.
2026-10-22 17:02:08 +07:00
nntrivi2001 44426faf16 chore: v2.8.19 — avatar from a link the browser loads, Files groups workspaces by owner 2026-10-22 14:34:50 +07:00
nntrivi2001 649f0599f8 chore: v2.8.18 — pasted file-manager copies attach by path locally, copy/IME/stale-reply fixes, react-router 7, full OpenAPI 2026-10-22 13:58:46 +07:00