feat: workspace aliases, tab groups of split panes, native path picker (v2.1.0)
- /api/workspaces and `agenthub workspace(s)` / `--workspace` as aliases of /api/lanes and `agenthub lanes` / `--lane`; old names print one stderr note. REPL `help lanes|workspace|workspaces` resolves through the aliases. - A workspace holds up to ten sessions (slots 0-9); every console tab is a group of 1-4 split panes (PaneGroups, agenthub.workspace.paneLayout); parseGroups keeps the stored active tab when an earlier one is dropped. - Browse button beside path fields: POST /api/lanes/pick-path opens the OS picker on the server's desktop — loopback only and no forwarding header (EREMOTE), a display failure is ENODIALOG not a Cancel, the dialog is killed when the request closes, a folder field falls back to the in-app browser on any failure. - Changes rail counts chip, image viewer (/git/raw), status bar session counts and per-day cost (`date=` on /api/pricing/cost). - /agenthub-open and /agenthub-doctor run from the INSTALLED build (supersededBy), doctor offers fixes behind two confirmations; plugin-open treats a locked bootstrap as non-fatal. - New-terminal shell waits for the first attach (issue #55). - repo-onboarding / debug-live-issue skills removed. - OpenAPI: pick-path, lane/date on /pricing/cost, tool on /run/history, /api/workspaces mount.
This commit is contained in:
@@ -12,7 +12,8 @@
|
||||
const tags = [
|
||||
{
|
||||
name: "Lanes",
|
||||
description: "Durable parallel-work lanes and dashboard-managed git worktrees",
|
||||
description:
|
||||
"Durable parallel-work workspaces (called lanes in the API) and dashboard-managed git worktrees. Every /api/lanes route is also mounted at /api/workspaces — the same handlers, the user-facing name.",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -111,6 +112,45 @@ const schemas = {
|
||||
};
|
||||
|
||||
const paths = {
|
||||
"/api/lanes/pick-path": {
|
||||
post: {
|
||||
tags: ["Lanes"],
|
||||
summary: "Open the OS's own folder/file picker on the server's desktop",
|
||||
description:
|
||||
"Shows zenity/kdialog (Linux), osascript (macOS) or a WinForms dialog (Windows) on the SERVER's desktop and waits for the user (up to 10 minutes; closed when the request goes away). Only for a browser on the same machine: a non-loopback client, or a request carrying X-Forwarded-For/Forwarded, gets EREMOTE and should fall back to GET /api/lanes/browse. One dialog at a time. Nothing from the request reaches argv but the starting folder, resolved to an existing absolute directory first.",
|
||||
operationId: "pickLanePath",
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
kind: { type: "string", enum: ["dir", "file"], default: "dir" },
|
||||
start: { type: "string", description: "Folder (or file) to open the dialog at." },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: "`{path}` — the chosen absolute path — or `{cancelled: true}`.",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: { path: { type: "string" }, cancelled: { type: "boolean" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
403: { description: "EBADORIGIN (not same-origin) or EREMOTE (not a local browser)." },
|
||||
409: { description: "EBUSY — a picker is already open." },
|
||||
500: { description: "EDIALOG — the dialog program failed." },
|
||||
501: { description: "ENODIALOG — no desktop session or no dialog program." },
|
||||
},
|
||||
},
|
||||
},
|
||||
"/api/lanes/ensure": {
|
||||
post: {
|
||||
tags: ["Lanes"],
|
||||
@@ -551,6 +591,26 @@ const paths = {
|
||||
},
|
||||
},
|
||||
},
|
||||
"/api/lanes/{id}/git/raw": {
|
||||
get: {
|
||||
tags: ["Lanes"],
|
||||
summary: "One image file's bytes at a revision",
|
||||
description:
|
||||
"For the viewer's <img>. Same path, rev and secret guards as /git/file, plus an extension allowlist (png, jpg, jpeg, gif, webp, avif, bmp, ico, svg) that sets the Content-Type, and a 20 MB cap. Served with `Content-Security-Policy: …; sandbox` and `X-Content-Type-Options: nosniff`, so an SVG opened from this URL runs no script. Read-only, no same-origin guard.",
|
||||
operationId: "readLaneImage",
|
||||
parameters: [
|
||||
{ name: "id", in: "path", required: true, schema: { type: "integer" } },
|
||||
{ name: "path", in: "query", required: true, schema: { type: "string" } },
|
||||
{ name: "rev", in: "query", schema: { type: "string", default: "work" } },
|
||||
],
|
||||
responses: {
|
||||
200: { description: "The image bytes, typed by extension." },
|
||||
400: { description: "EBADPATH, EBADREV, ENOFILE, ENOTIMAGE or ETOOBIG." },
|
||||
403: { description: "ESECRET." },
|
||||
404: { description: "Lane not found." },
|
||||
},
|
||||
},
|
||||
},
|
||||
"/api/lanes/{id}/git/ls": {
|
||||
get: {
|
||||
tags: ["Lanes"],
|
||||
|
||||
@@ -335,6 +335,27 @@ module.exports = {
|
||||
"for all sources. Narrows the aggregate cost to the given origins.",
|
||||
example: "local",
|
||||
},
|
||||
{
|
||||
name: "lane",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: { type: "integer" },
|
||||
description:
|
||||
"Only the sessions this workspace owns — the same rule as " +
|
||||
"GET /api/sessions?lane=. 400 INVALID_INPUT when not an id.",
|
||||
example: 5,
|
||||
},
|
||||
{
|
||||
name: "date",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: { type: "string", format: "date" },
|
||||
description:
|
||||
"Only that local day (YYYY-MM-DD, in `tz_offset`'s timezone): the " +
|
||||
"total and breakdown count the tokens spent that day. 400 " +
|
||||
"INVALID_INPUT for any other shape.",
|
||||
example: "2026-10-06",
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
|
||||
@@ -812,6 +812,15 @@ const paths = {
|
||||
"Return only the runs started through this lane (POST /api/lanes/{id}/start). Omit for every run; runs spawned from POST /api/run carry no lane and are excluded whenever this is set.",
|
||||
example: 5,
|
||||
},
|
||||
{
|
||||
name: "tool",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: { type: "string" },
|
||||
description:
|
||||
"Return only the runs of this AI Tools id — `shell` for a plain terminal tab (the sidebar's recently closed Terminal tabs).",
|
||||
example: "shell",
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
|
||||
Reference in New Issue
Block a user