From b1f6d21ef3bfcf55aea99eded5ae568432a0ce15 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 15 Sep 2026 20:18:45 -0400 Subject: [PATCH] refactor(protocol): refine vcs and shell APIs --- V2_HTTP_API_AUDIT.md | 78 +++++----- .../new-session-workspace-branch.spec.ts | 2 +- packages/app/e2e/utils/mock-api.ts | 2 +- .../src/new-session/workspace/controller.ts | 2 +- .../src/runtime/server/request-queue.test.ts | 2 +- packages/client/src/effect/api/api.ts | 19 +-- .../client/src/effect/generated/client.ts | 24 +-- .../client/src/promise/generated/client.ts | 47 +++--- .../client/src/promise/generated/types.ts | 24 +-- packages/core/src/plugin/host.ts | 4 +- packages/core/src/shell.ts | 2 +- packages/core/src/vcs.ts | 5 +- packages/core/test/plugin/host.ts | 4 +- packages/plugin/src/promise/adapter.ts | 4 +- packages/protocol/openapi.json | 144 ++---------------- packages/protocol/src/groups/shell.ts | 25 +-- packages/protocol/src/groups/vcs.ts | 4 +- packages/schema/src/shell.ts | 8 +- packages/schema/src/vcs.ts | 1 + packages/server/src/handlers/shell.ts | 27 +--- packages/server/src/handlers/vcs.ts | 2 +- .../feature-plugins/system/diff-viewer.tsx | 2 +- .../tui/test/cli/tui/diff-viewer.test.tsx | 2 +- services/www/openapi.json | 144 ++---------------- services/www/public/openapi.json | 144 ++---------------- 25 files changed, 133 insertions(+), 589 deletions(-) diff --git a/V2_HTTP_API_AUDIT.md b/V2_HTTP_API_AUDIT.md index 371e93eba84..2a6f0e70aa6 100644 --- a/V2_HTTP_API_AUDIT.md +++ b/V2_HTTP_API_AUDIT.md @@ -111,7 +111,7 @@ Review endpoints in document order. For each endpoint, select one disposition an |---|---|---|---|---|---| | [x] 025 | `GET` | `/api/integration` | `integration.list` | Keep | Full integration inventory is consumed by authentication and integration-selection clients. | | [x] 026 | `GET` | `/api/integration/{integrationID}` | `integration.get` | Change | Missing integration now returns typed `404` instead of optional data. | -| [x] | `POST` | `/api/experimental/integration/wellknown` | `experimental.integration.wellknown.add` | Experimental-only | Retained outside the stable API commitment. | +| [x] 027 | `POST` | `/api/experimental/integration/wellknown` | `experimental.integration.wellknown.add` | Experimental-only | Retained outside the stable API commitment. | | [ ] 028 | `POST` | `/api/integration/{integrationID}/connect/key` | `integration.connect.key` | | | | [ ] 029 | `POST` | `/api/integration/{integrationID}/connect/oauth` | `integration.oauth.connect` | | | | [ ] 030 | `GET` | `/api/integration/{integrationID}/connect/oauth/{attemptID}` | `integration.oauth.status` | | | @@ -185,7 +185,7 @@ Review endpoints in document order. For each endpoint, select one disposition an | [x] 077 | `GET` | `/api/experimental/session/{sessionID}/instructions/entries` | `experimental.session.instructions.entry.list` | Experimental-only | API-managed durable context entries retained outside the stable API. | | [x] 078 | `PUT` | `/api/experimental/session/{sessionID}/instructions/entries/{key}` | `experimental.session.instructions.entry.put` | Experimental-only | API-managed durable context entries retained outside the stable API. | | [x] 079 | `DELETE` | `/api/experimental/session/{sessionID}/instructions/entries/{key}` | `experimental.session.instructions.entry.remove` | Experimental-only | API-managed durable context entries retained outside the stable API. | -| [x] | `GET` | `/api/experimental/session/{sessionID}/log` | `session.log` | Experimental-only | Retained outside the stable API commitment. | +| [x] 080 | `GET` | `/api/experimental/session/{sessionID}/log` | `session.log` | Experimental-only | Retained outside the stable API commitment. | | [x] 081 | `GET` | `/api/session/{sessionID}/message/{messageID}` | `session.message.get` | Change | Normalized specific-message operation ID. | | [x] 082 | `GET` | `/api/session/{sessionID}/message` | `session.message.list` | Change | Normalized session-scoped message-list operation ID. | @@ -221,18 +221,18 @@ Review endpoints in document order. For each endpoint, select one disposition an | Done | Method | Path | Operation ID | Decision | Notes | |---|---|---|---|---|---| -| [ ] 102 | `GET` | `/api/fs/read/*` | `fs.read` | | | -| [ ] 103 | `GET` | `/api/fs/list` | `fs.list` | | | -| [ ] 104 | `GET` | `/api/fs/find` | `fs.find` | | | -| [ ] 105 | `GET` | `/api/worktree` | `worktree.list` | | | -| [ ] 106 | `POST` | `/api/worktree` | `worktree.create` | | | -| [ ] 107 | `DELETE` | `/api/worktree` | `worktree.remove` | | | -| [ ] 108 | `POST` | `/api/worktree/refresh` | `worktree.refresh` | | | -| [ ] 109 | `GET` | `/api/vcs` | `vcs.get` | | | -| [ ] 110 | `GET` | `/api/vcs/base` | `vcs.base` | | | -| [ ] 111 | `GET` | `/api/vcs/status` | `vcs.status` | | | -| [ ] 112 | `GET` | `/api/vcs/branches` | `vcs.branches` | | | -| [ ] 113 | `GET` | `/api/vcs/diff` | `vcs.diff` | | | +| [x] 102 | `GET` | `/api/fs/read/*` | `fs.read` | Keep | Relative wildcard file reads and raw byte responses retained. | +| [x] 103 | `GET` | `/api/fs/list` | `fs.list` | Keep | Existing path scope and minimal entry metadata retained. | +| [x] 104 | `GET` | `/api/fs/find` | `fs.find` | Keep | Existing ranked filesystem search retained. | +| [x] 105 | `GET` | `/api/worktree` | `worktree.list` | Keep | Reviewed separately by coworker. | +| [x] 106 | `POST` | `/api/worktree` | `worktree.create` | Keep | Reviewed separately by coworker. | +| [x] 107 | `DELETE` | `/api/worktree` | `worktree.remove` | Keep | Reviewed separately by coworker. | +| [x] 108 | `POST` | `/api/worktree/refresh` | `worktree.refresh` | Keep | Reviewed separately by coworker. | +| [x] 109 | `GET` | `/api/vcs` | `vcs.get` | Change | Preserved branch nesting and added selected VCS provider ID. | +| [x] 110 | `GET` | `/api/vcs/base` | `vcs.base` | Keep | Review-base inference and nullable unavailable state retained. | +| [x] 111 | `GET` | `/api/vcs/status` | `vcs.status` | Keep | Existing working-copy status shape retained for now. | +| [x] 112 | `GET` | `/api/vcs/branch` | `vcs.branch.list` | Change | Singular collection path and normalized operation ID. | +| [x] 113 | `GET` | `/api/vcs/diff` | `vcs.diff` | Keep | Existing working, branch, and committed comparison modes retained. | ## Group 9: PTYs, persistent terminals, and shells @@ -240,30 +240,30 @@ Review endpoints in document order. For each endpoint, select one disposition an | Done | Method | Path | Operation ID | Decision | Notes | |---|---|---|---|---|---| -| [ ] 114 | `GET` | `/api/pty` | `pty.list` | | | -| [ ] 115 | `POST` | `/api/pty` | `pty.create` | | | -| [ ] 116 | `GET` | `/api/pty/{ptyID}` | `pty.get` | | | -| [ ] 117 | `PUT` | `/api/pty/{ptyID}` | `pty.update` | | | -| [ ] 118 | `DELETE` | `/api/pty/{ptyID}` | `pty.remove` | | | -| [ ] 119 | `POST` | `/api/pty/{ptyID}/connect-token` | `pty.connect.token` | | | -| [ ] 120 | `GET` | `/api/pty/{ptyID}/connect` | `pty.connect` | | | -| [x] | `GET` | `/api/experimental/session/{sessionID}/terminal/read` | `server.experimental.persistentPty.read` | Experimental-only | Retained outside the stable API commitment. | -| [x] | `GET` | `/api/experimental/session/{sessionID}/terminal` | `server.experimental.persistentPty.list` | Experimental-only | Retained outside the stable API commitment. | -| [x] | `POST` | `/api/experimental/session/{sessionID}/terminal` | `server.experimental.persistentPty.create` | Experimental-only | Retained outside the stable API commitment. | -| [x] | `POST` | `/api/experimental/persistent-pty/shutdown` | `server.experimental.persistentPty.shutdown` | Experimental-only | Retained outside the stable API commitment. | -| [x] | `POST` | `/api/experimental/persistent-pty/handoff` | `server.experimental.persistentPty.handoff` | Experimental-only | Retained outside the stable API commitment. | -| [x] | `GET` | `/api/experimental/persistent-pty/{ptyID}` | `server.experimental.persistentPty.get` | Experimental-only | Retained outside the stable API commitment. | -| [x] | `PUT` | `/api/experimental/persistent-pty/{ptyID}` | `server.experimental.persistentPty.update` | Experimental-only | Retained outside the stable API commitment. | -| [x] | `DELETE` | `/api/experimental/persistent-pty/{ptyID}` | `server.experimental.persistentPty.remove` | Experimental-only | Retained outside the stable API commitment. | -| [x] | `GET` | `/api/experimental/persistent-pty/{ptyID}/snapshot` | `server.experimental.persistentPty.snapshot` | Experimental-only | Retained outside the stable API commitment. | -| [x] | `POST` | `/api/experimental/persistent-pty/{ptyID}/connect-token` | `server.experimental.persistentPty.connectToken` | Experimental-only | Retained outside the stable API commitment. | -| [x] | `GET` | `/api/experimental/persistent-pty/{ptyID}/connect` | `persistentPty.connect` | Experimental-only | Retained outside the stable API commitment. | -| [ ] 132 | `GET` | `/api/shell` | `shell.list` | | | -| [ ] 133 | `POST` | `/api/shell` | `shell.create` | | | -| [ ] 134 | `GET` | `/api/shell/{id}` | `shell.get` | | | -| [ ] 135 | `DELETE` | `/api/shell/{id}` | `shell.remove` | | | -| [ ] 136 | `PATCH` | `/api/shell/{id}/timeout` | `shell.timeout` | | | -| [ ] 137 | `GET` | `/api/shell/{id}/output` | `shell.output` | | | +| [x] 114 | `GET` | `/api/pty` | `pty.list` | Keep | PTY endpoints reviewed together and retained. | +| [x] 115 | `POST` | `/api/pty` | `pty.create` | Keep | PTY endpoints reviewed together and retained. | +| [x] 116 | `GET` | `/api/pty/{ptyID}` | `pty.get` | Keep | PTY endpoints reviewed together and retained. | +| [x] 117 | `PUT` | `/api/pty/{ptyID}` | `pty.update` | Keep | PTY endpoints reviewed together and retained. | +| [x] 118 | `DELETE` | `/api/pty/{ptyID}` | `pty.remove` | Keep | PTY endpoints reviewed together and retained. | +| [x] 119 | `POST` | `/api/pty/{ptyID}/connect-token` | `pty.connect.token` | Keep | PTY endpoints reviewed together and retained. | +| [x] 120 | `GET` | `/api/pty/{ptyID}/connect` | `pty.connect` | Keep | PTY endpoints reviewed together and retained. | +| [x] 121 | `GET` | `/api/experimental/session/{sessionID}/terminal/read` | `server.experimental.persistentPty.read` | Experimental-only | Retained outside the stable API commitment. | +| [x] 122 | `GET` | `/api/experimental/session/{sessionID}/terminal` | `server.experimental.persistentPty.list` | Experimental-only | Retained outside the stable API commitment. | +| [x] 123 | `POST` | `/api/experimental/session/{sessionID}/terminal` | `server.experimental.persistentPty.create` | Experimental-only | Retained outside the stable API commitment. | +| [x] 124 | `POST` | `/api/experimental/persistent-pty/shutdown` | `server.experimental.persistentPty.shutdown` | Experimental-only | Retained outside the stable API commitment. | +| [x] 125 | `POST` | `/api/experimental/persistent-pty/handoff` | `server.experimental.persistentPty.handoff` | Experimental-only | Retained outside the stable API commitment. | +| [x] 126 | `GET` | `/api/experimental/persistent-pty/{ptyID}` | `server.experimental.persistentPty.get` | Experimental-only | Retained outside the stable API commitment. | +| [x] 127 | `PUT` | `/api/experimental/persistent-pty/{ptyID}` | `server.experimental.persistentPty.update` | Experimental-only | Retained outside the stable API commitment. | +| [x] 128 | `DELETE` | `/api/experimental/persistent-pty/{ptyID}` | `server.experimental.persistentPty.remove` | Experimental-only | Retained outside the stable API commitment. | +| [x] 129 | `GET` | `/api/experimental/persistent-pty/{ptyID}/snapshot` | `server.experimental.persistentPty.snapshot` | Experimental-only | Retained outside the stable API commitment. | +| [x] 130 | `POST` | `/api/experimental/persistent-pty/{ptyID}/connect-token` | `server.experimental.persistentPty.connectToken` | Experimental-only | Retained outside the stable API commitment. | +| [x] 131 | `GET` | `/api/experimental/persistent-pty/{ptyID}/connect` | `persistentPty.connect` | Experimental-only | Retained outside the stable API commitment. | +| [x] 132 | `GET` | `/api/shell` | `shell.list` | Change | Stable shell inventory retained; numeric timestamps documented as epoch milliseconds. | +| [x] 133 | `POST` | `/api/shell` | `shell.create` | Change | Timeout is optional and defaults to zero; caller metadata retained. | +| [x] 134 | `GET` | `/api/shell/{id}` | `shell.get` | Keep | Specific running or retained shell read retained. | +| [x] 135 | `DELETE` | `/api/shell/{id}` | `shell.remove` | Change | Shell deletion is idempotent and returns `204` when already absent. | +| [x] 136 | — | — | — | Remove | Timeout mutation remains an internal Core shell operation. | +| [x] 137 | `GET` | `/api/shell/{id}/output` | `shell.output` | Keep | Existing byte-cursor text output paging retained. | ## Group 10: Events, RPC, and experimental operations @@ -276,4 +276,4 @@ Review endpoints in document order. For each endpoint, select one disposition an | [ ] 140 | `GET` | `/api/event` | `event.subscribe` | | | | [ ] 141 | `GET` | `/api/debug/location` | `debug.location.list` | | | | [ ] 142 | `DELETE` | `/api/debug/location` | `debug.location.evict` | | | -| [x] | `GET` | `/api/experimental/migration/v1` | `experimental.migration.v1.status` | Experimental-only | Retained outside the stable API commitment. | +| [x] 143 | `GET` | `/api/experimental/migration/v1` | `experimental.migration.v1.status` | Experimental-only | Retained outside the stable API commitment. | diff --git a/packages/app/e2e/regression/new-session-workspace-branch.spec.ts b/packages/app/e2e/regression/new-session-workspace-branch.spec.ts index e2e05a88901..a98cdb2446a 100644 --- a/packages/app/e2e/regression/new-session-workspace-branch.spec.ts +++ b/packages/app/e2e/regression/new-session-workspace-branch.spec.ts @@ -22,7 +22,7 @@ test("selects a base branch for a new workspace", async ({ page }) => { pageMessages: () => ({ items: [] }), vcsBranches: ["feature/api", "main", "origin/release"], }) - await page.route("**/api/vcs/branches?*", (route) => { + await page.route("**/api/vcs/branch?*", (route) => { if (new URL(route.request().url()).searchParams.get("search") !== "feature") return route.fallback() return route.fulfill({ json: { location: { directory }, data: ["feature/api"] } }) }) diff --git a/packages/app/e2e/utils/mock-api.ts b/packages/app/e2e/utils/mock-api.ts index dd72eafd20d..f4b009dbbad 100644 --- a/packages/app/e2e/utils/mock-api.ts +++ b/packages/app/e2e/utils/mock-api.ts @@ -115,7 +115,7 @@ const Group = HttpApiGroup.make("mock") .add(HttpApiEndpoint.get("formRequests", "/api/form", { success: Json })) .add(HttpApiEndpoint.get("vcs", "/api/vcs", { success: Json })) .add(HttpApiEndpoint.get("vcsStatus", "/api/vcs/status", { success: Json })) - .add(HttpApiEndpoint.get("vcsBranches", "/api/vcs/branches", { success: Json })) + .add(HttpApiEndpoint.get("vcsBranches", "/api/vcs/branch", { success: Json })) .add(HttpApiEndpoint.get("vcsDiff", "/api/vcs/diff", { success: Json })) .add(HttpApiEndpoint.get("fsList", "/api/fs/list", { query: Query, success: Json })) .add( diff --git a/packages/app/src/new-session/workspace/controller.ts b/packages/app/src/new-session/workspace/controller.ts index 820a6ed7475..316257c71a8 100644 --- a/packages/app/src/new-session/workspace/controller.ts +++ b/packages/app/src/new-session/workspace/controller.ts @@ -142,7 +142,7 @@ export function createNewSessionWorkspaceController(input: { () => (visible() ? { directory: projectRoot(), search: state.search } : undefined), ({ directory, search }) => serverSDK.api.vcs - .branches({ location: { directory }, search, limit: 50 }) + .branch.list({ location: { directory }, search, limit: 50 }) .then((response) => ({ directory, search, data: response.data })) .catch(() => ({ directory, search, data: [] })), ) diff --git a/packages/app/src/runtime/server/request-queue.test.ts b/packages/app/src/runtime/server/request-queue.test.ts index a45b68f5846..78f419d984b 100644 --- a/packages/app/src/runtime/server/request-queue.test.ts +++ b/packages/app/src/runtime/server/request-queue.test.ts @@ -98,7 +98,7 @@ describe("createRequestQueue", () => { test("classifies git and worktree endpoints as slow", () => { expect(isSlowRequest("/api/vcs")).toBe(true) - expect(isSlowRequest("/api/vcs/branches")).toBe(true) + expect(isSlowRequest("/api/vcs/branch")).toBe(true) expect(isSlowRequest("/api/worktree")).toBe(true) expect(isSlowRequest("/api/vcsx")).toBe(false) expect(isSlowRequest("/api/session")).toBe(false) diff --git a/packages/client/src/effect/api/api.ts b/packages/client/src/effect/api/api.ts index 6a2de5f085a..2bce924ee05 100644 --- a/packages/client/src/effect/api/api.ts +++ b/packages/client/src/effect/api/api.ts @@ -2102,7 +2102,7 @@ export type ShellCreateInput = { readonly location?: { readonly directory?: string | undefined } | undefined readonly command: string readonly cwd?: string | undefined - readonly timeout: number + readonly timeout?: number | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined } export type ShellCreateOutput = { readonly location: Location.PublicRef; readonly data: Shell.Info } @@ -2115,14 +2115,6 @@ export type ShellGetInput = { export type ShellGetOutput = { readonly location: Location.PublicRef; readonly data: Shell.Info } export type ShellGetOperation = (input: ShellGetInput) => Effect.Effect -export type ShellTimeoutInput = { - readonly id: Shell.ID - readonly location?: { readonly directory?: string | undefined } | undefined - readonly timeout: number -} -export type ShellTimeoutOutput = { readonly location: Location.PublicRef; readonly data: Shell.Info } -export type ShellTimeoutOperation = (input: ShellTimeoutInput) => Effect.Effect - export type ShellOutputInput = { readonly id: Shell.ID readonly location?: { readonly directory?: string | undefined } | undefined @@ -2151,7 +2143,6 @@ export interface ShellApi { readonly list: ShellListOperation readonly create: ShellCreateOperation readonly get: ShellGetOperation - readonly timeout: ShellTimeoutOperation readonly output: ShellOutputOperation readonly remove: ShellRemoveOperation } @@ -2214,13 +2205,13 @@ export type VcsStatusInput = { readonly location?: { readonly directory?: string export type VcsStatusOutput = { readonly location: Location.PublicRef; readonly data: ReadonlyArray } export type VcsStatusOperation = (input?: VcsStatusInput) => Effect.Effect -export type VcsBranchesInput = { +export type VcsBranchListInput = { readonly location?: { readonly directory?: string | undefined } | undefined readonly search?: string | undefined readonly limit?: number | undefined } -export type VcsBranchesOutput = { readonly location: Location.PublicRef; readonly data: Vcs.BranchList } -export type VcsBranchesOperation = (input?: VcsBranchesInput) => Effect.Effect +export type VcsBranchListOutput = { readonly location: Location.PublicRef; readonly data: Vcs.BranchList } +export type VcsBranchListOperation = (input?: VcsBranchListInput) => Effect.Effect export type VcsDiffInput = { readonly location?: { readonly directory?: string | undefined } | undefined @@ -2235,7 +2226,7 @@ export interface VcsApi { readonly get: VcsGetOperation readonly base: VcsBaseOperation readonly status: VcsStatusOperation - readonly branches: VcsBranchesOperation + readonly branch: { readonly list: VcsBranchListOperation } readonly diff: VcsDiffOperation } diff --git a/packages/client/src/effect/generated/client.ts b/packages/client/src/effect/generated/client.ts index b661e486c6a..07b3621348a 100644 --- a/packages/client/src/effect/generated/client.ts +++ b/packages/client/src/effect/generated/client.ts @@ -221,8 +221,6 @@ import type { ShellCreateOutput, ShellGetInput, ShellGetOutput, - ShellTimeoutInput, - ShellTimeoutOutput, ShellOutputInput, ShellOutputOutput, ShellRemoveInput, @@ -243,8 +241,8 @@ import type { VcsBaseOutput, VcsStatusInput, VcsStatusOutput, - VcsBranchesInput, - VcsBranchesOutput, + VcsBranchListInput, + VcsBranchListOutput, VcsDiffInput, VcsDiffOutput, DebugLocationListOutput, @@ -1363,15 +1361,6 @@ const EndpointShellGet = (raw: RawClient["server.shell"]) => (input: ShellGetInp ), ) -const EndpointShellTimeout = (raw: RawClient["server.shell"]) => (input: ShellTimeoutInput) => - preserveEffect()( - raw["shell.timeout"]({ - params: { id: input["id"] }, - query: { location: input["location"] }, - payload: { timeout: input["timeout"] }, - }).pipe(Effect.mapError(mapClientError)), - ) - const EndpointShellOutput = (raw: RawClient["server.shell"]) => (input: ShellOutputInput) => preserveEffect()( raw["shell.output"]({ @@ -1391,7 +1380,6 @@ const adaptGroupShell = (raw: RawClient["server.shell"]) => ({ list: EndpointShellList(raw), create: EndpointShellCreate(raw), get: EndpointShellGet(raw), - timeout: EndpointShellTimeout(raw), output: EndpointShellOutput(raw), remove: EndpointShellRemove(raw), }) @@ -1455,9 +1443,9 @@ const EndpointVcsStatus = (raw: RawClient["server.vcs"]) => (input?: VcsStatusIn raw["vcs.status"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError)), ) -const EndpointVcsBranches = (raw: RawClient["server.vcs"]) => (input?: VcsBranchesInput) => - preserveEffect()( - raw["vcs.branches"]({ +const EndpointVcsBranchList = (raw: RawClient["server.vcs"]) => (input?: VcsBranchListInput) => + preserveEffect()( + raw["vcs.branch.list"]({ query: { location: input?.["location"], search: input?.["search"], limit: input?.["limit"] }, }).pipe(Effect.mapError(mapClientError)), ) @@ -1473,7 +1461,7 @@ const adaptGroupVcs = (raw: RawClient["server.vcs"]) => ({ get: EndpointVcsGet(raw), base: EndpointVcsBase(raw), status: EndpointVcsStatus(raw), - branches: EndpointVcsBranches(raw), + branch: { list: EndpointVcsBranchList(raw) }, diff: EndpointVcsDiff(raw), }) diff --git a/packages/client/src/promise/generated/client.ts b/packages/client/src/promise/generated/client.ts index 416b3ab7b09..999bb3c4839 100644 --- a/packages/client/src/promise/generated/client.ts +++ b/packages/client/src/promise/generated/client.ts @@ -217,8 +217,6 @@ import type { ShellCreateOutput, ShellGetInput, ShellGetOutput, - ShellTimeoutInput, - ShellTimeoutOutput, ShellOutputInput, ShellOutputOutput, ShellRemoveInput, @@ -239,8 +237,8 @@ import type { VcsBaseOutput, VcsStatusInput, VcsStatusOutput, - VcsBranchesInput, - VcsBranchesOutput, + VcsBranchListInput, + VcsBranchListOutput, VcsDiffInput, VcsDiffOutput, DebugLocationListOutput, @@ -1860,19 +1858,6 @@ export function make(options: ClientOptions) { }, requestOptions, ), - timeout: (input: ShellTimeoutInput, requestOptions?: RequestOptions) => - request( - { - method: "PATCH", - path: `/api/shell/${encodeURIComponent(input.id)}/timeout`, - query: { location: input["location"] }, - body: { timeout: input["timeout"] }, - successStatus: 200, - declaredStatuses: [400, 401, 404], - empty: false, - }, - requestOptions, - ), output: (input: ShellOutputInput, requestOptions?: RequestOptions) => request( { @@ -1892,7 +1877,7 @@ export function make(options: ClientOptions) { path: `/api/shell/${encodeURIComponent(input.id)}`, query: { location: input["location"] }, successStatus: 204, - declaredStatuses: [400, 401, 404], + declaredStatuses: [400, 401], empty: true, }, requestOptions, @@ -2005,18 +1990,20 @@ export function make(options: ClientOptions) { }, requestOptions, ), - branches: (input?: VcsBranchesInput, requestOptions?: RequestOptions) => - request( - { - method: "GET", - path: `/api/vcs/branches`, - query: { location: input?.["location"], search: input?.["search"], limit: input?.["limit"] }, - successStatus: 200, - declaredStatuses: [400, 401], - empty: false, - }, - requestOptions, - ), + branch: { + list: (input?: VcsBranchListInput, requestOptions?: RequestOptions) => + request( + { + method: "GET", + path: `/api/vcs/branch`, + query: { location: input?.["location"], search: input?.["search"], limit: input?.["limit"] }, + successStatus: 200, + declaredStatuses: [400, 401], + empty: false, + }, + requestOptions, + ), + }, diff: (input: VcsDiffInput, requestOptions?: RequestOptions) => request( { diff --git a/packages/client/src/promise/generated/types.ts b/packages/client/src/promise/generated/types.ts index 63ac3c3f8b5..aacaea0d8ba 100644 --- a/packages/client/src/promise/generated/types.ts +++ b/packages/client/src/promise/generated/types.ts @@ -1632,7 +1632,7 @@ export type ReferenceSource = ReferenceLocalSource | ReferenceGitSource export type WorktreeList = Array -export type VcsInfo = { branch: VcsBranch } +export type VcsInfo = { provider?: string; branch: VcsBranch } export type SessionInboxMove = { id: string @@ -5998,25 +5998,25 @@ export type ShellCreateInput = { readonly command: { readonly command: string readonly cwd?: string - readonly timeout: number + readonly timeout?: number readonly metadata?: { readonly [x: string]: JsonValue } }["command"] readonly cwd?: { readonly command: string readonly cwd?: string - readonly timeout: number + readonly timeout?: number readonly metadata?: { readonly [x: string]: JsonValue } }["cwd"] - readonly timeout: { + readonly timeout?: { readonly command: string readonly cwd?: string - readonly timeout: number + readonly timeout?: number readonly metadata?: { readonly [x: string]: JsonValue } }["timeout"] readonly metadata?: { readonly command: string readonly cwd?: string - readonly timeout: number + readonly timeout?: number readonly metadata?: { readonly [x: string]: JsonValue } }["metadata"] } @@ -6030,14 +6030,6 @@ export type ShellGetInput = { export type ShellGetOutput = { location: LocationPublicRef; data: ShellInfo1 } -export type ShellTimeoutInput = { - readonly id: { readonly id: string }["id"] - readonly location?: { readonly location?: { readonly directory?: string | undefined } | undefined }["location"] - readonly timeout: { readonly timeout: number }["timeout"] -} - -export type ShellTimeoutOutput = { location: LocationPublicRef; data: ShellInfo1 } - export type ShellOutputInput = { readonly id: { readonly id: string }["id"] readonly location?: { @@ -6149,7 +6141,7 @@ export type VcsStatusInput = { export type VcsStatusOutput = { location: LocationPublicRef; data: Array } -export type VcsBranchesInput = { +export type VcsBranchListInput = { readonly location?: { readonly location?: { readonly directory?: string | undefined } | undefined readonly search?: string | undefined @@ -6167,7 +6159,7 @@ export type VcsBranchesInput = { }["limit"] } -export type VcsBranchesOutput = { location: LocationPublicRef; data: VcsBranchList } +export type VcsBranchListOutput = { location: LocationPublicRef; data: VcsBranchList } export type VcsDiffInput = { readonly location?: { diff --git a/packages/core/src/plugin/host.ts b/packages/core/src/plugin/host.ts index 8d5d244c7da..129dbe77513 100644 --- a/packages/core/src/plugin/host.ts +++ b/packages/core/src/plugin/host.ts @@ -458,7 +458,9 @@ export const make = Effect.fn("PluginHost.make")(function* ( vcs: { get: () => response(vcs.info()), base: () => response(vcs.base()), - branches: (input) => response(vcs.branches({ search: input?.search, limit: input?.limit })), + branch: { + list: (input) => response(vcs.branches({ search: input?.search, limit: input?.limit })), + }, status: () => response(vcs.status()), diff: (input) => response(vcs.diff(input.mode, { context: input.context, base: input.base })), transform: vcs.transform, diff --git a/packages/core/src/shell.ts b/packages/core/src/shell.ts index 6f1d0363515..6089863c7fb 100644 --- a/packages/core/src/shell.ts +++ b/packages/core/src/shell.ts @@ -257,7 +257,7 @@ const layer = () => const invocation: ShellCreateBefore = { command: input.command, cwd: input.cwd ?? location.directory, - timeout: input.timeout, + timeout: input.timeout ?? 0, shell: input.shell ?? (yield* shell.resolve({ priority: "config" })), env: { ...(sessionEnvironment ?? process.env), diff --git a/packages/core/src/vcs.ts b/packages/core/src/vcs.ts index 1bbe9196def..3861a28e1b7 100644 --- a/packages/core/src/vcs.ts +++ b/packages/core/src/vcs.ts @@ -123,7 +123,10 @@ const layer = Layer.effect( const changed = yield* Effect.gen(function* () { const provider = selected() const next: Info = provider - ? yield* protect(provider, "info", provider.info(scope).pipe(Effect.flatMap(decodeInfo)), { branch: {} }) + ? { + ...(yield* protect(provider, "info", provider.info(scope).pipe(Effect.flatMap(decodeInfo)), { branch: {} })), + provider: provider.id, + } : { branch: {} } const changed = current.info.branch.current !== next.branch.current current.info = next diff --git a/packages/core/test/plugin/host.ts b/packages/core/test/plugin/host.ts index 88a92f6cc01..6cbb5230a73 100644 --- a/packages/core/test/plugin/host.ts +++ b/packages/core/test/plugin/host.ts @@ -138,7 +138,9 @@ export function host(overrides: Overrides = {}): Plugin.Context { vcs: overrides.vcs ?? { base: () => Effect.die("unused vcs.base"), get: () => Effect.die("unused vcs.get"), - branches: () => Effect.die("unused vcs.branches"), + branch: { + list: () => Effect.die("unused vcs.branch.list"), + }, status: () => Effect.die("unused vcs.status"), diff: () => Effect.die("unused vcs.diff"), transform: () => Effect.die("unused vcs.transform"), diff --git a/packages/plugin/src/promise/adapter.ts b/packages/plugin/src/promise/adapter.ts index 766fff446ef..ac0c845f121 100644 --- a/packages/plugin/src/promise/adapter.ts +++ b/packages/plugin/src/promise/adapter.ts @@ -501,7 +501,9 @@ export function fromPromise(plugin: Plugin) { vcs: { get: adaptApiMethod(VcsEndpoints["vcs.get"], host.vcs.get), base: adaptApiMethod(VcsEndpoints["vcs.base"], host.vcs.base), - branches: adaptApiMethod(VcsEndpoints["vcs.branches"], host.vcs.branches), + branch: { + list: adaptApiMethod(VcsEndpoints["vcs.branch.list"], host.vcs.branch.list), + }, status: adaptApiMethod(VcsEndpoints["vcs.status"], host.vcs.status), diff: adaptApiMethod(VcsEndpoints["vcs.diff"], host.vcs.diff), reload: () => run(host.vcs.reload()), diff --git a/packages/protocol/openapi.json b/packages/protocol/openapi.json index cfe4ae20994..b2f39efcc66 100644 --- a/packages/protocol/openapi.json +++ b/packages/protocol/openapi.json @@ -10613,7 +10613,7 @@ "type": "object" } }, - "required": ["command", "timeout"], + "required": ["command"], "additionalProperties": false } } @@ -10791,142 +10791,12 @@ } } } - }, - "404": { - "description": "ShellNotFoundError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ShellNotFoundErrorEncoded" - } - } - } } }, "description": "Terminate and remove one shell command and its retained output.", "summary": "Remove shell command" } }, - "/api/shell/{id}/timeout": { - "patch": { - "tags": ["shell"], - "operationId": "shell.timeout", - "parameters": [ - { - "name": "id", - "in": "path", - "schema": { - "type": "string", - "pattern": "^sh_" - }, - "required": true - }, - { - "name": "location", - "in": "query", - "schema": { - "anyOf": [ - { - "type": "object", - "properties": { - "directory": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "null" - } - ] - }, - "required": false, - "style": "deepObject", - "explode": true - } - ], - "security": [], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "location": { - "$ref": "#/components/schemas/Location.PublicRef" - }, - "data": { - "$ref": "#/components/schemas/Shell.Info" - } - }, - "required": ["location", "data"], - "additionalProperties": false - } - } - } - }, - "400": { - "description": "InvalidRequestError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InvalidRequestErrorEncoded" - } - } - } - }, - "401": { - "description": "UnauthorizedError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UnauthorizedErrorEncoded" - } - } - } - }, - "404": { - "description": "ShellNotFoundError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ShellNotFoundErrorEncoded" - } - } - } - } - }, - "description": "Replace a running shell command's timeout from now, or clear it with zero.", - "summary": "Update shell timeout", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "timeout": { - "type": "integer", - "minimum": 0 - } - }, - "required": ["timeout"], - "additionalProperties": false - } - } - }, - "required": true - } - } - }, "/api/shell/{id}/output": { "get": { "tags": ["shell"], @@ -11677,10 +11547,10 @@ "summary": "VCS status" } }, - "/api/vcs/branches": { + "/api/vcs/branch": { "get": { "tags": ["vcs"], - "operationId": "vcs.branches", + "operationId": "vcs.branch.list", "parameters": [ { "name": "location", @@ -18498,7 +18368,8 @@ "type": "object", "properties": { "started": { - "type": "number" + "type": "number", + "description": "Start time in milliseconds since the Unix epoch" }, "completed": { "type": "number" @@ -18752,6 +18623,9 @@ "Vcs.Info": { "type": "object", "properties": { + "provider": { + "type": "string" + }, "branch": { "$ref": "#/components/schemas/Vcs.Branch" } @@ -19006,7 +18880,7 @@ }, { "name": "shell", - "description": "Experimental location-scoped shell command routes." + "description": "Location-scoped shell command routes." }, { "name": "reference", diff --git a/packages/protocol/src/groups/shell.ts b/packages/protocol/src/groups/shell.ts index 3823eeb9970..ab908f4c826 100644 --- a/packages/protocol/src/groups/shell.ts +++ b/packages/protocol/src/groups/shell.ts @@ -1,15 +1,10 @@ import { Shell } from "@opencode/schema/shell" import { Location } from "@opencode/schema/location" -import { NonNegativeInt } from "@opencode/schema/schema" import { Schema } from "effect" import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi" import { ShellNotFoundError } from "../errors.js" import { LocationQuery, locationQueryOpenApi } from "./location.js" -const TimeoutInput = Schema.Struct({ - timeout: NonNegativeInt, -}) - export const ShellGroup = HttpApiGroup.make("server.shell") .add( HttpApiEndpoint.get("shell.list", "/api/shell", { @@ -57,23 +52,6 @@ export const ShellGroup = HttpApiGroup.make("server.shell") }), ), ) - .add( - HttpApiEndpoint.patch("shell.timeout", "/api/shell/:id/timeout", { - params: { id: Shell.ID }, - query: LocationQuery, - payload: TimeoutInput, - success: Location.response(Shell.Info), - error: ShellNotFoundError, - }) - .annotateMerge(locationQueryOpenApi) - .annotateMerge( - OpenApi.annotations({ - identifier: "shell.timeout", - summary: "Update shell timeout", - description: "Replace a running shell command's timeout from now, or clear it with zero.", - }), - ), - ) .add( HttpApiEndpoint.get("shell.output", "/api/shell/:id/output", { params: { id: Shell.ID }, @@ -95,7 +73,6 @@ export const ShellGroup = HttpApiGroup.make("server.shell") params: { id: Shell.ID }, query: LocationQuery, success: HttpApiSchema.NoContent, - error: ShellNotFoundError, }) .annotateMerge(locationQueryOpenApi) .annotateMerge( @@ -107,5 +84,5 @@ export const ShellGroup = HttpApiGroup.make("server.shell") ), ) .annotateMerge( - OpenApi.annotations({ title: "shell", description: "Experimental location-scoped shell command routes." }), + OpenApi.annotations({ title: "shell", description: "Location-scoped shell command routes." }), ) diff --git a/packages/protocol/src/groups/vcs.ts b/packages/protocol/src/groups/vcs.ts index ba047eac0bb..f2f4062baf7 100644 --- a/packages/protocol/src/groups/vcs.ts +++ b/packages/protocol/src/groups/vcs.ts @@ -66,14 +66,14 @@ export const VcsGroup = HttpApiGroup.make("server.vcs") ), ) .add( - HttpApiEndpoint.get("vcs.branches", "/api/vcs/branches", { + HttpApiEndpoint.get("vcs.branch.list", "/api/vcs/branch", { query: BranchesQuery, success: Location.response(Vcs.BranchList), }) .annotateMerge(locationQueryOpenApi) .annotateMerge( OpenApi.annotations({ - identifier: "vcs.branches", + identifier: "vcs.branch.list", summary: "VCS branches", description: "List local and remote branches available at the requested location.", }), diff --git a/packages/schema/src/shell.ts b/packages/schema/src/shell.ts index bcd09cd901a..2a30c77d86a 100644 --- a/packages/schema/src/shell.ts +++ b/packages/schema/src/shell.ts @@ -23,8 +23,10 @@ export const Status = Schema.Literals(["running", "exited", "timeout", "killed"] export type Status = typeof Status.Type export const Time = Schema.Struct({ - started: Schema.Finite, - completed: optional(Schema.Finite), + started: Schema.Finite.annotate({ description: "Start time in milliseconds since the Unix epoch" }), + completed: optional(Schema.Finite).annotate({ + description: "Completion time in milliseconds since the Unix epoch", + }), }) export interface Time extends Schema.Schema.Type {} @@ -57,7 +59,7 @@ export const Event = { Created, Exited, Deleted, Definitions: inventory(Created, export const CreateInput = Schema.Struct({ command: Schema.String, cwd: optional(Schema.String), - timeout: NonNegativeInt, + timeout: optional(NonNegativeInt), metadata: optional(Metadata), }) export interface CreateInput extends Schema.Schema.Type {} diff --git a/packages/schema/src/vcs.ts b/packages/schema/src/vcs.ts index e4f82108f18..a578c2ba9ce 100644 --- a/packages/schema/src/vcs.ts +++ b/packages/schema/src/vcs.ts @@ -10,6 +10,7 @@ export const Branch = Schema.Struct({ export interface Branch extends Schema.Schema.Type {} export const Info = Schema.Struct({ + provider: optional(Schema.String), branch: Branch, }).annotate({ identifier: "Vcs.Info" }) export interface Info extends Schema.Schema.Type {} diff --git a/packages/server/src/handlers/shell.ts b/packages/server/src/handlers/shell.ts index c53480fbd89..0beeafc279a 100644 --- a/packages/server/src/handlers/shell.ts +++ b/packages/server/src/handlers/shell.ts @@ -43,23 +43,6 @@ export const ShellHandler = HttpApiBuilder.group(Api, "server.shell", (handlers) ) }), ) - .handle( - "shell.timeout", - Effect.fn(function* (ctx) { - const shell = yield* Shell.Service - return yield* response( - shell - .timeout(ctx.params.id, ctx.payload.timeout) - .pipe( - Effect.catchTag( - "Shell.NotFoundError", - () => - new ShellNotFoundError({ id: ctx.params.id, message: `Shell command not found: ${ctx.params.id}` }), - ), - ), - ) - }), - ) .handle( "shell.output", Effect.fn(function* (ctx) { @@ -81,15 +64,7 @@ export const ShellHandler = HttpApiBuilder.group(Api, "server.shell", (handlers) "shell.remove", Effect.fn(function* (ctx) { const shell = yield* Shell.Service - yield* shell - .remove(ctx.params.id) - .pipe( - Effect.catchTag( - "Shell.NotFoundError", - () => - new ShellNotFoundError({ id: ctx.params.id, message: `Shell command not found: ${ctx.params.id}` }), - ), - ) + yield* shell.remove(ctx.params.id).pipe(Effect.catchTag("Shell.NotFoundError", () => Effect.void)) return HttpApiSchema.NoContent.make() }), ) diff --git a/packages/server/src/handlers/vcs.ts b/packages/server/src/handlers/vcs.ts index 2c0fbd8c07f..655c1814ad6 100644 --- a/packages/server/src/handlers/vcs.ts +++ b/packages/server/src/handlers/vcs.ts @@ -34,7 +34,7 @@ export const VcsHandler = HttpApiBuilder.group(Api, "server.vcs", (handlers) => }), ), ) - .handle("vcs.branches", (ctx) => + .handle("vcs.branch.list", (ctx) => response( Effect.gen(function* () { const vcs = yield* Vcs.Service diff --git a/packages/tui/src/feature-plugins/system/diff-viewer.tsx b/packages/tui/src/feature-plugins/system/diff-viewer.tsx index 3ba35761ee2..f5a40e85db8 100644 --- a/packages/tui/src/feature-plugins/system/diff-viewer.tsx +++ b/packages/tui/src/feature-plugins/system/diff-viewer.tsx @@ -217,7 +217,7 @@ function DiffBaseDialog(props: { const theme = props.context.theme.contextual.elevated const [search, setSearch] = createDebouncedSignal("", 150) const [branches] = createResource(search, (search) => - props.context.client.vcs.branches({ location: props.location, search, limit: 100 }), + props.context.client.vcs.branch.list({ location: props.location, search, limit: 100 }), ) const Empty = () => ( diff --git a/packages/tui/test/cli/tui/diff-viewer.test.tsx b/packages/tui/test/cli/tui/diff-viewer.test.tsx index 94ee08840d4..cae2c1f1ce8 100644 --- a/packages/tui/test/cli/tui/diff-viewer.test.tsx +++ b/packages/tui/test/cli/tui/diff-viewer.test.tsx @@ -1847,7 +1847,7 @@ async function renderDiffViewer( ? options.baseResponse() : json({ location: session.location, data: options.base === undefined ? baseFixture : options.base }) } - if (url.pathname === "/api/vcs/branches") { + if (url.pathname === "/api/vcs/branch") { branchesRequests.push(url) return options.branchesResponse ? options.branchesResponse(url) diff --git a/services/www/openapi.json b/services/www/openapi.json index cfe4ae20994..b2f39efcc66 100644 --- a/services/www/openapi.json +++ b/services/www/openapi.json @@ -10613,7 +10613,7 @@ "type": "object" } }, - "required": ["command", "timeout"], + "required": ["command"], "additionalProperties": false } } @@ -10791,142 +10791,12 @@ } } } - }, - "404": { - "description": "ShellNotFoundError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ShellNotFoundErrorEncoded" - } - } - } } }, "description": "Terminate and remove one shell command and its retained output.", "summary": "Remove shell command" } }, - "/api/shell/{id}/timeout": { - "patch": { - "tags": ["shell"], - "operationId": "shell.timeout", - "parameters": [ - { - "name": "id", - "in": "path", - "schema": { - "type": "string", - "pattern": "^sh_" - }, - "required": true - }, - { - "name": "location", - "in": "query", - "schema": { - "anyOf": [ - { - "type": "object", - "properties": { - "directory": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "null" - } - ] - }, - "required": false, - "style": "deepObject", - "explode": true - } - ], - "security": [], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "location": { - "$ref": "#/components/schemas/Location.PublicRef" - }, - "data": { - "$ref": "#/components/schemas/Shell.Info" - } - }, - "required": ["location", "data"], - "additionalProperties": false - } - } - } - }, - "400": { - "description": "InvalidRequestError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InvalidRequestErrorEncoded" - } - } - } - }, - "401": { - "description": "UnauthorizedError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UnauthorizedErrorEncoded" - } - } - } - }, - "404": { - "description": "ShellNotFoundError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ShellNotFoundErrorEncoded" - } - } - } - } - }, - "description": "Replace a running shell command's timeout from now, or clear it with zero.", - "summary": "Update shell timeout", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "timeout": { - "type": "integer", - "minimum": 0 - } - }, - "required": ["timeout"], - "additionalProperties": false - } - } - }, - "required": true - } - } - }, "/api/shell/{id}/output": { "get": { "tags": ["shell"], @@ -11677,10 +11547,10 @@ "summary": "VCS status" } }, - "/api/vcs/branches": { + "/api/vcs/branch": { "get": { "tags": ["vcs"], - "operationId": "vcs.branches", + "operationId": "vcs.branch.list", "parameters": [ { "name": "location", @@ -18498,7 +18368,8 @@ "type": "object", "properties": { "started": { - "type": "number" + "type": "number", + "description": "Start time in milliseconds since the Unix epoch" }, "completed": { "type": "number" @@ -18752,6 +18623,9 @@ "Vcs.Info": { "type": "object", "properties": { + "provider": { + "type": "string" + }, "branch": { "$ref": "#/components/schemas/Vcs.Branch" } @@ -19006,7 +18880,7 @@ }, { "name": "shell", - "description": "Experimental location-scoped shell command routes." + "description": "Location-scoped shell command routes." }, { "name": "reference", diff --git a/services/www/public/openapi.json b/services/www/public/openapi.json index cfe4ae20994..b2f39efcc66 100644 --- a/services/www/public/openapi.json +++ b/services/www/public/openapi.json @@ -10613,7 +10613,7 @@ "type": "object" } }, - "required": ["command", "timeout"], + "required": ["command"], "additionalProperties": false } } @@ -10791,142 +10791,12 @@ } } } - }, - "404": { - "description": "ShellNotFoundError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ShellNotFoundErrorEncoded" - } - } - } } }, "description": "Terminate and remove one shell command and its retained output.", "summary": "Remove shell command" } }, - "/api/shell/{id}/timeout": { - "patch": { - "tags": ["shell"], - "operationId": "shell.timeout", - "parameters": [ - { - "name": "id", - "in": "path", - "schema": { - "type": "string", - "pattern": "^sh_" - }, - "required": true - }, - { - "name": "location", - "in": "query", - "schema": { - "anyOf": [ - { - "type": "object", - "properties": { - "directory": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "null" - } - ] - }, - "required": false, - "style": "deepObject", - "explode": true - } - ], - "security": [], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "location": { - "$ref": "#/components/schemas/Location.PublicRef" - }, - "data": { - "$ref": "#/components/schemas/Shell.Info" - } - }, - "required": ["location", "data"], - "additionalProperties": false - } - } - } - }, - "400": { - "description": "InvalidRequestError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InvalidRequestErrorEncoded" - } - } - } - }, - "401": { - "description": "UnauthorizedError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UnauthorizedErrorEncoded" - } - } - } - }, - "404": { - "description": "ShellNotFoundError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ShellNotFoundErrorEncoded" - } - } - } - } - }, - "description": "Replace a running shell command's timeout from now, or clear it with zero.", - "summary": "Update shell timeout", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "timeout": { - "type": "integer", - "minimum": 0 - } - }, - "required": ["timeout"], - "additionalProperties": false - } - } - }, - "required": true - } - } - }, "/api/shell/{id}/output": { "get": { "tags": ["shell"], @@ -11677,10 +11547,10 @@ "summary": "VCS status" } }, - "/api/vcs/branches": { + "/api/vcs/branch": { "get": { "tags": ["vcs"], - "operationId": "vcs.branches", + "operationId": "vcs.branch.list", "parameters": [ { "name": "location", @@ -18498,7 +18368,8 @@ "type": "object", "properties": { "started": { - "type": "number" + "type": "number", + "description": "Start time in milliseconds since the Unix epoch" }, "completed": { "type": "number" @@ -18752,6 +18623,9 @@ "Vcs.Info": { "type": "object", "properties": { + "provider": { + "type": "string" + }, "branch": { "$ref": "#/components/schemas/Vcs.Branch" } @@ -19006,7 +18880,7 @@ }, { "name": "shell", - "description": "Experimental location-scoped shell command routes." + "description": "Location-scoped shell command routes." }, { "name": "reference",