feat(artifacts): capture Claude Docs docs and session documents, claim unowned rows

- A Claude Docs `batch` that creates a doc is captured like an `Artifact`
  publish, titled by the doc's name and linked by its own frame URL.
- At Stop/SessionEnd, each pptx/docx/xlsx/pdf/html a workspace session
  created or changed is snapshotted into its owner's library (`auto`);
  an unchanged file is skipped, a changed one refreshes its snapshot.
- Server mode: a page with no owner carries a suggested owner (the
  workspace or home its session ran in). The suggested user claims or
  declines it; an admin claims or assigns any. New tab Unowned /
  Is this yours? on the Artifacts page; all three actions audited.
This commit is contained in:
2026-10-23 08:33:55 +07:00
parent 241e19b713
commit 62b4520507
14 changed files with 736 additions and 44 deletions
+5 -1
View File
@@ -82,7 +82,11 @@ const paths = {
description:
"A developer sees their own rows plus shared ones; staff see every row in `hub` scope.",
parameters: [
{ name: "scope", in: "query", schema: { type: "string", enum: ["mine", "hub"] } },
{
name: "scope",
in: "query",
schema: { type: "string", enum: ["all", "mine", "hub", "unowned"] },
},
{ name: "kind", in: "query", schema: { type: "string", enum: ["page", "file", "skill", "plugin"] } },
{ name: "q", in: "query", schema: { type: "string" } },
laneQuery,
+63
View File
@@ -1048,6 +1048,69 @@ const paths = {
},
},
},
"/api/artifacts/{id}/claim": {
post: {
tags: ["Artifacts"],
summary: "Take an unowned artifact",
operationId: "claimArtifact",
description:
"Server mode. The user the row is suggested to (the owner of the workspace or home its session ran in), or an admin, becomes its owner. Audited. Same-origin only.",
parameters: [pathParam("id", "Artifact id.")],
responses: {
200: ok("Claimed.", { item: { type: "object" } }),
400: errs("ELOCALMODE."),
403: errs("EFORBIDDEN: not the suggested user."),
404: errs("ENOTFOUND."),
409: errs("EOWNED: it already has an owner."),
},
},
},
"/api/artifacts/{id}/decline": {
post: {
tags: ["Artifacts"],
summary: "Say an unowned artifact offered to you is not yours",
operationId: "declineArtifact",
description:
"Server mode. Drops the suggestion; the row stays unowned for an admin to place. Audited. Same-origin only.",
parameters: [pathParam("id", "Artifact id.")],
responses: {
200: ok("Declined.", { id: { type: "integer" }, declined: { type: "boolean" } }),
400: errs("ELOCALMODE."),
403: errs("EFORBIDDEN: not the suggested user."),
404: errs("ENOTFOUND."),
409: errs("EOWNED."),
},
},
},
"/api/artifacts/{id}/assign": {
post: {
tags: ["Artifacts"],
summary: "Give an unowned artifact to a user (admin)",
operationId: "assignArtifact",
description:
"Server mode, admin only. Body `{userId}` names an enabled user. Audited. Same-origin only.",
parameters: [pathParam("id", "Artifact id.")],
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
required: ["userId"],
properties: { userId: { type: "integer" } },
},
},
},
},
responses: {
200: ok("Assigned.", { item: { type: "object" } }),
400: errs("ELOCALMODE / EBADFIELD."),
403: errs("EFORBIDDEN: not an admin."),
404: errs("ENOTFOUND."),
409: errs("EOWNED."),
},
},
},
};
module.exports = { tags, schemas, paths };