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:
@@ -49,6 +49,52 @@ const { getCodexHome } = require("./codex-home");
|
||||
const { PROXY_CAPABLE } = require("./ai-tools");
|
||||
const { gatewayBase } = require("./proxy-gateway");
|
||||
|
||||
/** The remote client's Company account (server/lib/remote-client.js `HUB_PROXY`). */
|
||||
const COMPANY = proxies.COMPANY_NAME;
|
||||
|
||||
/**
|
||||
* The Company account as a profile `proxyForSpawn` understands. Local mode on
|
||||
* an enrolled machine only (a hub never enrolls itself), Claude only.
|
||||
*/
|
||||
function companyProfile(tool) {
|
||||
if (require("./auth").isServerMode()) {
|
||||
throw Object.assign(new Error("the Company account is for a local AgentHub"), {
|
||||
code: "EBADPROXY",
|
||||
});
|
||||
}
|
||||
if (tool !== "claude") {
|
||||
throw Object.assign(new Error("only Claude sessions can use the Company account"), {
|
||||
code: "EUNSUPPORTED",
|
||||
});
|
||||
}
|
||||
const st = require("./remote-client").status();
|
||||
if (!st.enrolled) {
|
||||
throw Object.assign(
|
||||
new Error(
|
||||
st.expired
|
||||
? "this machine's hub login expired — run agenthub remote login again"
|
||||
: "this machine is not enrolled with a company hub — run agenthub remote login"
|
||||
),
|
||||
{ code: "ENOHUB" }
|
||||
);
|
||||
}
|
||||
return { name: COMPANY, label: "Company account", company: true, builtin: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* The Company account's `--settings` file: the hub's gateway and this
|
||||
* machine's device token (its own, in a 0600 file of ours — the pool
|
||||
* credential stays on the hub). Rewritten at every spawn. A pane keeps the
|
||||
* token it started with (Claude Code reads it once — checked 2026-11-04), so
|
||||
* after a new `agenthub remote login` an open pane is resumed, not refreshed.
|
||||
*/
|
||||
function writeCompanySettings() {
|
||||
const env = require("./remote-client").claudeEnv();
|
||||
const file = claudeSettingsPath(COMPANY);
|
||||
writeSecret(file, `${JSON.stringify({ env }, null, 2)}\n`);
|
||||
return file;
|
||||
}
|
||||
|
||||
/** Server mode: panes reach a proxy only through the key gateway. */
|
||||
const viaGateway = () => require("./auth").isServerMode();
|
||||
|
||||
@@ -191,6 +237,9 @@ function codexOverrides(profile) {
|
||||
*/
|
||||
function resolveProxy(tool, name, ownerId = null) {
|
||||
if (name == null || name === "" || name === proxies.BUILTIN_NAME) return null;
|
||||
// A remote client's Company account: the hub's gateway, Claude only, and
|
||||
// only on a machine enrolled with a hub (server/lib/remote-client.js).
|
||||
if (name === COMPANY) return companyProfile(tool);
|
||||
if (typeof name !== "string") {
|
||||
throw Object.assign(new Error("proxy must be a profile name"), { code: "EBADPROXY" });
|
||||
}
|
||||
@@ -225,6 +274,9 @@ function resumeProxy(sessionId, ownerId = null) {
|
||||
if (!sessionId) return null;
|
||||
const name = require("./dashboard-runs").proxyForSession(sessionId);
|
||||
if (!name) return null;
|
||||
// A Company-account session resumes on it (refused at spawn if this machine
|
||||
// is no longer enrolled — never silently moved to the user's own login).
|
||||
if (name === COMPANY) return name;
|
||||
const owner = proxies.ownerOfName(name);
|
||||
if (owner != null && owner !== ownerId) return null;
|
||||
try {
|
||||
@@ -244,6 +296,7 @@ function resumeProxy(sessionId, ownerId = null) {
|
||||
*/
|
||||
function proxyForSpawn(tool, profile, paneToken = null, member = null, runId = null) {
|
||||
if (!profile) return { argv: [], env: {} };
|
||||
if (profile.company) return { argv: [`--settings=${writeCompanySettings()}`], env: {} };
|
||||
const gw = viaGateway();
|
||||
if (gw && !paneToken) {
|
||||
// Server mode always mints one (auth.newPaneToken); without it the pane
|
||||
|
||||
Reference in New Issue
Block a user