feat(artifacts): last updated time on skills and files; each installer's copy marked

A row said nothing about when its content last changed, and the install list
could not tell who still runs an old version. `artifacts.version_at` (new
column, existing rows from `updated_at`) is stamped only when a snapshot is
replaced — a rename or share toggle leaves it — and served as `versionAt`:
skill and file rows read "Updated 3h ago", the detail sheet "Last updated".
`GET /api/artifacts/:id/installs` gives each skill install a `state` —
latest / outdated / removed / unknown — from its Claude skill copies' hash
against the newest snapshot, shown as a badge.
This commit is contained in:
2026-10-30 15:21:31 +07:00
parent bc44cbc8b7
commit d09c9d6633
9 changed files with 161 additions and 12 deletions
+2 -2
View File
@@ -3147,7 +3147,7 @@ A snapshot freezes a session as it is at that moment into one immutable `session
### Artifacts hub
```http
GET /api/artifacts?scope=all|mine|hub|unowned&kind=page|file|skill|plugin&q= {items} — each with `installed` and `updateAvailable` (default scope `all`)
GET /api/artifacts?scope=all|mine|hub|unowned&kind=page|file|skill|plugin&q= {items} — each with `installed`, `updateAvailable` and `versionAt` (when its content last changed — a new snapshot; a rename or share toggle moves only `updatedAt`) (default scope `all`)
GET /api/artifacts/settings {serverMode, canShare, installsEnabled, canManage}
PUT /api/artifacts/settings {installsEnabled: boolean} — admin (any caller in local mode); the UI is Settings → AI tools → Artifacts Hub
GET /api/artifacts/candidates?laneId= {items: [{path, sessionId, at, exists, pinned}]}
@@ -3167,7 +3167,7 @@ GET /api/artifacts/:id/raw?path=&download=1 one snapshot file (sandbox CSP, n
POST /api/artifacts/:id/install {replace?, update?} → {installed, replaced?}
POST /api/artifacts/:id/uninstall → {uninstalled, paths}
POST /api/artifacts/:id/reshare → {item, changed} — Share again (skill, owner): the owner's folder on this server becomes the new version and the row is shared; 409 ENOSOURCE when the server has no copy
GET /api/artifacts/:id/installs {items: [{user, mine, at, replaced, paths?}], unrecorded}
GET /api/artifacts/:id/installs {items: [{user, mine, at, replaced, state?, paths?}], unrecorded} — state (skills): latest | outdated | removed | unknown, that install's Claude skill copies against the newest version now
POST /api/artifacts/:id/claim → {item} — the suggested user, or an admin
POST /api/artifacts/:id/decline → {id, declined} — the suggested user
POST /api/artifacts/:id/assign {userId} → {item} — admin