chore: v2.8.71 — remote clients (company hub), artifacts shared with chosen people, folders and viewable copies, fixes
Remote clients (docs/plans/remote-clients/README.md, B1–B3 for the Claude pool): - server/hub/ holds the hub-only code (devices, remote sessions, upload ingest, LAN gateway), required only in server mode; hub-boundary.test.js keeps local code from importing it. - A local install enrolls with `agenthub remote login` or Settings → AI tools → Company hub; the "Company account" AI Proxy (reserved name agenthub.company) sends a session through the hub's gateway, which adds a pool account's token — no credential reaches the machine. Only those sessions are uploaded, hourly and at their end; secret-shaped strings are redacted on the client. - Gateway refuses: unknown device (401), a message without a session id, an unregistered session under AGENTHUB_REMOTE_GATE=block, a user over quota (EQUOTA), a purged session (410). Device tokens 90 days, conversations 30. - New alert rule type remote_unmonitored: a company-account session its device never registered; a late registration clears the flag instead. - The hub imports a session once per upload round (chunks carry `more`), not once per chunk. Artifacts: - Share an artifact with chosen people (artifact_shares, PATCH sharedWith, GET /api/artifacts/people) besides everyone; a "Shared with you" tab. Only the owner or an admin sees or edits who it is shared with; audited. - Folders: each account's own arrangement (agenthub.artifacts.folders pref). - A claude.ai page keeps a copy viewable without the publishing account: its HTML file (and supporting files) snapshotted at publish, or the copy Claude saves for a Doc / Slides / Design in artifacts-export/ (a synchronous PostToolUse hint hook, scripts/artifact-export-hint.js, admin switch), attached to the page at Stop. Viewed in HtmlFrame's sandbox, Markdown, or pdf.js (pdfjs-dist 6.4.299, legacy build, loaded only for a PDF). Fixes: - tmux: a cached /proc/<pid>/exe of an exited server falls back to PATH's tmux (stop the last pane, start again within 10 s failed with ENOENT). - shelves: a shelf restores beside changes in other files. - chat: a finished reply ends the turn even with no hook at all. - Settings → AI tools: profile list display tweaks.
This commit is contained in:
+18
-2
@@ -664,6 +664,19 @@ const DUMMY_HASH = `scrypt$${"A".repeat(22)}==$${"A".repeat(86)}==`;
|
||||
|
||||
/** `{user}` for a right username + password, else throws EBADLOGIN/ETOOMANY. */
|
||||
async function login(req, username, password) {
|
||||
const u = await checkLogin(req, username, password);
|
||||
sql("UPDATE users SET last_login_at = ? WHERE id = ?").run(new Date().toISOString(), u.id);
|
||||
return { user: publicUser(u), token: startSession(u.id) };
|
||||
}
|
||||
|
||||
/**
|
||||
* The enabled user row a username + password names, throttled exactly like a
|
||||
* login (the same per-address and per-username counters) but starting no
|
||||
* session — a remote client's device enrollment (server/hub/devices.js) is a
|
||||
* login that yields a device token instead of a cookie. Throws
|
||||
* EBADLOGIN/ETOOMANY.
|
||||
*/
|
||||
async function checkLogin(req, username, password) {
|
||||
const name = String(username || "").trim();
|
||||
const ipKey = clientKey(req);
|
||||
const userKey = `user:${name.toLowerCase()}`;
|
||||
@@ -678,8 +691,7 @@ async function login(req, username, password) {
|
||||
if (!u || !ok || u.disabled) throw err("EBADLOGIN", "wrong username or password");
|
||||
failures.delete(ipKey);
|
||||
failures.delete(userKey);
|
||||
sql("UPDATE users SET last_login_at = ? WHERE id = ?").run(new Date().toISOString(), u.id);
|
||||
return { user: publicUser(u), token: startSession(u.id) };
|
||||
return u;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -776,6 +788,9 @@ function authGuard(req, res, next) {
|
||||
const lp = p.toLowerCase();
|
||||
if (lp === "/health" || lp.startsWith("/auth/")) return next();
|
||||
if (lp === "/hooks/ingest-batch") return next(); // its own bearer token
|
||||
// A remote client's enrollment and its device-token calls (server/hub/):
|
||||
// each checks its own password or device token, never a cookie.
|
||||
if (underPrefix(p, "/remote/device") || underPrefix(p, "/remote/gw")) return next();
|
||||
if (underPrefix(p, "/hooks")) {
|
||||
return isLocalRequest(req)
|
||||
? next()
|
||||
@@ -880,6 +895,7 @@ module.exports = {
|
||||
hasAdmin,
|
||||
userFromRequest,
|
||||
login,
|
||||
checkLogin,
|
||||
confirmPassword,
|
||||
endSession,
|
||||
cookieToken,
|
||||
|
||||
Reference in New Issue
Block a user