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:
@@ -50,6 +50,16 @@ const schemas = {
|
||||
size: { type: "integer", nullable: true },
|
||||
shared: { type: "boolean" },
|
||||
sharedAt: { type: "string", nullable: true },
|
||||
sharedWith: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: { id: { type: "integer" }, name: { type: "string" } },
|
||||
},
|
||||
description:
|
||||
"The people it is shared with by name — listed only to its owner or an admin, else empty.",
|
||||
},
|
||||
sharedWithMe: { type: "boolean", description: "Shared with the caller by name." },
|
||||
installs: { type: "integer" },
|
||||
createdAt: { type: "string" },
|
||||
updatedAt: { type: "string" },
|
||||
@@ -88,12 +98,12 @@ const paths = {
|
||||
summary: "List artifacts",
|
||||
operationId: "listArtifacts",
|
||||
description:
|
||||
"A developer sees their own rows plus shared ones; staff see every row in `hub` scope.",
|
||||
"A developer sees their own rows plus shared ones (to everyone, or to them by name); staff see every row in `hub` scope. `withme`: the rows shared with the caller by name (server mode).",
|
||||
parameters: [
|
||||
{
|
||||
name: "scope",
|
||||
in: "query",
|
||||
schema: { type: "string", enum: ["all", "mine", "hub", "unowned"] },
|
||||
schema: { type: "string", enum: ["all", "mine", "hub", "withme", "unowned"] },
|
||||
},
|
||||
{
|
||||
name: "kind",
|
||||
@@ -116,7 +126,7 @@ const paths = {
|
||||
operationId: "getArtifactSettings",
|
||||
responses: {
|
||||
200: {
|
||||
description: "`{serverMode, canShare, installsEnabled, canManage}`.",
|
||||
description: "`{serverMode, canShare, installsEnabled, exportsEnabled, canManage}`.",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
@@ -125,6 +135,11 @@ const paths = {
|
||||
serverMode: { type: "boolean" },
|
||||
canShare: { type: "boolean" },
|
||||
installsEnabled: { type: "boolean" },
|
||||
exportsEnabled: {
|
||||
type: "boolean",
|
||||
description:
|
||||
"Claude panes are asked to save a viewable copy of a Doc / Slides / Design they publish (scripts/artifact-export-hint.js).",
|
||||
},
|
||||
canManage: { type: "boolean" },
|
||||
},
|
||||
},
|
||||
@@ -135,17 +150,46 @@ const paths = {
|
||||
},
|
||||
put: {
|
||||
tags: ["Artifacts"],
|
||||
summary: "Turn installs on or off (admin; any caller in local mode)",
|
||||
summary:
|
||||
"Turn installs, or the copy hint for Docs/Slides, on or off (admin; any caller in local mode)",
|
||||
operationId: "setArtifactSettings",
|
||||
description: "Audited in server mode (Settings → AI tools → Artifacts Hub).",
|
||||
requestBody: json({ installsEnabled: { type: "boolean" } }, ["installsEnabled"]),
|
||||
description:
|
||||
"Audited in server mode (Settings → AI tools → Artifacts Hub). Send either or both; the export switch reaches panes started afterwards.",
|
||||
requestBody: json({
|
||||
installsEnabled: { type: "boolean" },
|
||||
exportsEnabled: { type: "boolean" },
|
||||
}),
|
||||
responses: {
|
||||
200: { description: "`{installsEnabled}`." },
|
||||
200: { description: "`{installsEnabled, exportsEnabled}`." },
|
||||
400: { description: "EBADFIELD" },
|
||||
403: { description: "EADMIN / EBADORIGIN" },
|
||||
},
|
||||
},
|
||||
},
|
||||
"/api/artifacts/{id}/copy": {
|
||||
post: {
|
||||
tags: ["Artifacts"],
|
||||
summary: "Take a page's viewable copy from the file it was published from",
|
||||
operationId: "copyArtifactPage",
|
||||
description:
|
||||
"A `page` captured before pages kept a copy: if the HTML file it was published from is still in its workspace, it is snapshotted (read under the pinned-file guards) and the row's `files` name it. Once per row per server run; any viewer may ask and gets only the copy. A row that already has one, or is not a page, is answered as it is.",
|
||||
parameters: [idParam],
|
||||
responses: {
|
||||
200: { description: "`{item}`.", content: { "application/json": { schema: item } } },
|
||||
404: errs,
|
||||
},
|
||||
},
|
||||
},
|
||||
"/api/artifacts/people": {
|
||||
get: {
|
||||
tags: ["Artifacts"],
|
||||
summary: "The people the caller may share an artifact with",
|
||||
operationId: "listArtifactPeople",
|
||||
description:
|
||||
"Server mode: every enabled account but the caller, by name only (`{people: [{id, username, name}]}`); empty in local mode.",
|
||||
responses: { 200: { description: "`{people: [{id, username, name}]}`." } },
|
||||
},
|
||||
},
|
||||
"/api/artifacts/candidates": {
|
||||
get: {
|
||||
tags: ["Artifacts"],
|
||||
@@ -326,7 +370,13 @@ const paths = {
|
||||
requestBody: json({
|
||||
title: { type: "string" },
|
||||
description: { type: "string" },
|
||||
shared: { type: "boolean" },
|
||||
shared: { type: "boolean", description: "Share with everyone (the Hub)." },
|
||||
sharedWith: {
|
||||
type: "array",
|
||||
items: { type: "integer" },
|
||||
description:
|
||||
"Server mode: the user ids it is shared with by name (replaces the list; up to 100 enabled users; the owner is dropped). Out of its owner's library and shared with nobody, the row is deleted (`removed`).",
|
||||
},
|
||||
source: { type: "string", description: "Plugin only." },
|
||||
market: { type: "string", description: "Plugin only." },
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user