chore: v2.8.21 — update progress steps, clone in background, extra SSH keys inline, today's spend

- Plugin update reports its running step (plugin-update-progress.json →
  plugin_update_progress); a hidden update or clone leaves a progress chip.
- Clone here can run in the background.
- Git identity: extra SSH keys are added inline per host (no More keys tab).
- Users: spend today (viewer's local day, ?tz_offset=).
- Prettier on the files touched since v2.8.20.
This commit is contained in:
2026-10-23 10:44:11 +07:00
parent 62b4520507
commit 9dd97a369c
33 changed files with 906 additions and 393 deletions
+55 -21
View File
@@ -36,9 +36,17 @@ const schemas = {
laneId: { type: "integer", nullable: true },
sessionId: { type: "string", nullable: true },
source: { type: "string", nullable: true, description: "Plugin: `name@marketplace`." },
market: { type: "string", nullable: true, description: "Plugin: owner/repo, git URL or marketplace.json URL." },
market: {
type: "string",
nullable: true,
description: "Plugin: owner/repo, git URL or marketplace.json URL.",
},
url: { type: "string", nullable: true, description: "Page: a https://claude.ai/… link." },
files: { type: "array", items: { type: "object" }, description: "The files the snapshot recorded." },
files: {
type: "array",
items: { type: "object" },
description: "The files the snapshot recorded.",
},
size: { type: "integer", nullable: true },
shared: { type: "boolean" },
sharedAt: { type: "string", nullable: true },
@@ -87,12 +95,18 @@ const paths = {
in: "query",
schema: { type: "string", enum: ["all", "mine", "hub", "unowned"] },
},
{ name: "kind", in: "query", schema: { type: "string", enum: ["page", "file", "skill", "plugin"] } },
{
name: "kind",
in: "query",
schema: { type: "string", enum: ["page", "file", "skill", "plugin"] },
},
{ name: "q", in: "query", schema: { type: "string" } },
laneQuery,
{ name: "sessionId", in: "query", schema: { type: "string" } },
],
responses: { 200: { description: "`{items}`.", content: { "application/json": { schema: items } } } },
responses: {
200: { description: "`{items}`.", content: { "application/json": { schema: items } } },
},
},
},
"/api/artifacts/settings": {
@@ -190,11 +204,14 @@ const paths = {
tags: ["Artifacts"],
summary: "Keep one uploaded file (≤ 25 MB)",
operationId: "uploadArtifactFile",
requestBody: multipart({
files: { type: "string", format: "binary" },
title: { type: "string" },
description: { type: "string" },
}, ["files"]),
requestBody: multipart(
{
files: { type: "string", format: "binary" },
title: { type: "string" },
description: { type: "string" },
},
["files"]
),
responses: created(item),
},
},
@@ -222,13 +239,16 @@ const paths = {
operationId: "uploadArtifactSkills",
description:
"`paths` is a JSON array of each file's relative path. Zip sizes are read before inflating; nothing is stored if any skill fails.",
requestBody: multipart({
files: { type: "array", items: { type: "string", format: "binary" } },
paths: { type: "string" },
name: { type: "string" },
title: { type: "string" },
description: { type: "string" },
}, ["files"]),
requestBody: multipart(
{
files: { type: "array", items: { type: "string", format: "binary" } },
paths: { type: "string" },
name: { type: "string" },
title: { type: "string" },
description: { type: "string" },
},
["files"]
),
responses: created(items),
},
},
@@ -246,7 +266,10 @@ const paths = {
},
["name", "description", "content"]
),
responses: created({ type: "object", properties: { item: item.properties.item, items: items.properties.items } }),
responses: created({
type: "object",
properties: { item: item.properties.item, items: items.properties.items },
}),
},
},
"/api/artifacts/plugin/inspect": {
@@ -254,10 +277,14 @@ const paths = {
tags: ["Artifacts"],
summary: "Read a marketplace's plugin list",
operationId: "inspectArtifactMarket",
description: "Clones a user-named https link only to read `marketplace.json`; no credential is ever sent.",
description:
"Clones a user-named https link only to read `marketplace.json`; no credential is ever sent.",
requestBody: json({ market: { type: "string" } }, ["market"]),
responses: {
200: { description: "`{market, name, description, plugins: [{name, key, description, version, category}]}`." },
200: {
description:
"`{market, name, description, plugins: [{name, key, description, version, category}]}`.",
},
502: errs,
},
},
@@ -285,7 +312,10 @@ const paths = {
summary: "One artifact",
operationId: "getArtifact",
parameters: [idParam],
responses: { 200: { description: "`{item}`.", content: { "application/json": { schema: item } } }, 404: errs },
responses: {
200: { description: "`{item}`.", content: { "application/json": { schema: item } } },
404: errs,
},
},
patch: {
tags: ["Artifacts"],
@@ -299,7 +329,11 @@ const paths = {
source: { type: "string", description: "Plugin only." },
market: { type: "string", description: "Plugin only." },
}),
responses: { 200: { description: "`{item}`.", content: { "application/json": { schema: item } } }, 403: errs, 404: errs },
responses: {
200: { description: "`{item}`.", content: { "application/json": { schema: item } } },
403: errs,
404: errs,
},
},
delete: {
tags: ["Artifacts"],