mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-26 18:47:35 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb15a9b789 | ||
|
|
d74adc2beb |
@@ -444,7 +444,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/location`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -469,7 +469,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/agent`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -481,7 +481,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/agent/${encodeURIComponent(input.agentID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -495,7 +495,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/plugin`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -508,7 +508,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input?.["location"] },
|
||||
body: { target: input?.["target"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -521,7 +521,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"] },
|
||||
body: { targets: input["targets"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401, 503],
|
||||
declaredStatuses: [400, 401, 403, 503],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1109,7 +1109,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/model`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 503],
|
||||
declaredStatuses: [400, 401, 403, 503],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1121,7 +1121,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/model/default`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 503],
|
||||
declaredStatuses: [400, 401, 403, 503],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1149,7 +1149,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/provider`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 503],
|
||||
declaredStatuses: [400, 401, 403, 503],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1161,7 +1161,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/provider/${encodeURIComponent(input.providerID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404, 503],
|
||||
declaredStatuses: [400, 401, 403, 404, 503],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1175,7 +1175,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/integration`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1187,7 +1187,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/integration/${encodeURIComponent(input.integrationID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1201,7 +1201,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"] },
|
||||
body: { url: input["url"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1216,7 +1216,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"] },
|
||||
body: { key: input["key"], answer: input["answer"], label: input["label"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1231,7 +1231,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"] },
|
||||
body: { methodID: input["methodID"], answer: input["answer"], label: input["label"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1243,7 +1243,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth/${encodeURIComponent(input.attemptID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1256,7 +1256,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"] },
|
||||
body: { code: input["code"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1268,7 +1268,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth/${encodeURIComponent(input.attemptID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1283,7 +1283,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"] },
|
||||
body: { methodID: input["methodID"], label: input["label"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1295,7 +1295,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/command/${encodeURIComponent(input.attemptID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1307,7 +1307,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/command/${encodeURIComponent(input.attemptID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1322,7 +1322,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/mcp`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1335,7 +1335,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"] },
|
||||
body: { config: input["config"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1347,7 +1347,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/experimental/mcp/${encodeURIComponent(input.server)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1359,7 +1359,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/experimental/mcp/${encodeURIComponent(input.server)}/connect`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1371,7 +1371,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/experimental/mcp/${encodeURIComponent(input.server)}/disconnect`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1384,7 +1384,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/mcp/resource`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1430,7 +1430,7 @@ export function make(options: ClientOptions) {
|
||||
project: {
|
||||
list: (requestOptions?: RequestOptions) =>
|
||||
request<ProjectListOutput>(
|
||||
{ method: "GET", path: `/api/project`, successStatus: 200, declaredStatuses: [400, 401], empty: false },
|
||||
{ method: "GET", path: `/api/project`, successStatus: 200, declaredStatuses: [400, 401, 403], empty: false },
|
||||
requestOptions,
|
||||
),
|
||||
update: (input: ProjectUpdateInput, requestOptions?: RequestOptions) =>
|
||||
@@ -1445,7 +1445,7 @@ export function make(options: ClientOptions) {
|
||||
commands: input["commands"],
|
||||
},
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1459,7 +1459,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/form`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1474,7 +1474,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/permission/request`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1488,7 +1488,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/permission/saved`,
|
||||
query: { projectID: input?.["projectID"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1499,7 +1499,7 @@ export function make(options: ClientOptions) {
|
||||
method: "DELETE",
|
||||
path: `/api/permission/saved/${encodeURIComponent(input.id)}`,
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1568,7 +1568,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/fs/read/${encodePath(input.path)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
binary: true,
|
||||
},
|
||||
@@ -1581,7 +1581,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/fs/list`,
|
||||
query: { location: input?.["location"], path: input?.["path"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1593,7 +1593,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/fs/find`,
|
||||
query: { location: input["location"], query: input["query"], type: input["type"], limit: input["limit"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1606,7 +1606,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"], path: input["path"] },
|
||||
body: input["payload"],
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
binaryBody: true,
|
||||
},
|
||||
@@ -1621,7 +1621,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/command`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1635,7 +1635,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/skill`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1650,7 +1650,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"] },
|
||||
body: { input: input["input"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 500],
|
||||
declaredStatuses: [400, 401, 403, 500],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1671,7 +1671,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/pty`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1690,7 +1690,7 @@ export function make(options: ClientOptions) {
|
||||
env: input?.["env"],
|
||||
},
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1702,7 +1702,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1715,7 +1715,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"] },
|
||||
body: { title: input["title"], size: input["size"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1727,7 +1727,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1881,7 +1881,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/shell`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1899,7 +1899,7 @@ export function make(options: ClientOptions) {
|
||||
metadata: input["metadata"],
|
||||
},
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1911,7 +1911,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/shell/${encodeURIComponent(input.id)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1923,7 +1923,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/shell/${encodeURIComponent(input.id)}/output`,
|
||||
query: { location: input["location"], cursor: input["cursor"], limit: input["limit"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 404],
|
||||
declaredStatuses: [400, 401, 403, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1935,7 +1935,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/shell/${encodeURIComponent(input.id)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -1949,7 +1949,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/reference`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -2019,7 +2019,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/vcs`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -2031,7 +2031,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/vcs/base`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 503],
|
||||
declaredStatuses: [400, 401, 403, 503],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -2043,7 +2043,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/vcs/status`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -2056,7 +2056,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/vcs/branch`,
|
||||
query: { location: input?.["location"], search: input?.["search"], limit: input?.["limit"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -2069,7 +2069,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/vcs/diff`,
|
||||
query: { location: input["location"], mode: input["mode"], base: input["base"], context: input["context"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 503],
|
||||
declaredStatuses: [400, 401, 403, 503],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -2125,7 +2125,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/websearch/provider`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 503],
|
||||
declaredStatuses: [400, 401, 403, 503],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -2138,7 +2138,7 @@ export function make(options: ClientOptions) {
|
||||
query: { location: input["location"] },
|
||||
body: { query: input["query"], providerID: input["providerID"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 503],
|
||||
declaredStatuses: [400, 401, 403, 503],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
@@ -2152,14 +2152,20 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/config`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
shells: (requestOptions?: RequestOptions) =>
|
||||
request<ConfigShellsOutput>(
|
||||
{ method: "GET", path: `/api/config/shell`, successStatus: 200, declaredStatuses: [400, 401], empty: false },
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/config/shell`,
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
update: (input: ConfigUpdateInput, requestOptions?: RequestOptions) =>
|
||||
@@ -2169,7 +2175,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/experimental/config`,
|
||||
body: { shell: input["shell"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 403],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
|
||||
@@ -2484,6 +2484,22 @@ export type UnauthorizedError = { readonly _tag: "UnauthorizedError"; readonly m
|
||||
export const isUnauthorizedError = (value: unknown): value is UnauthorizedError =>
|
||||
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "UnauthorizedError"
|
||||
|
||||
export type LocationDirectoryNotFoundError = {
|
||||
readonly _tag: "LocationDirectoryNotFoundError"
|
||||
readonly directory: string
|
||||
readonly message: string
|
||||
}
|
||||
export const isLocationDirectoryNotFoundError = (value: unknown): value is LocationDirectoryNotFoundError =>
|
||||
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "LocationDirectoryNotFoundError"
|
||||
|
||||
export type LocationPermissionDeniedError = {
|
||||
readonly _tag: "LocationPermissionDeniedError"
|
||||
readonly directory: string
|
||||
readonly message: string
|
||||
}
|
||||
export const isLocationPermissionDeniedError = (value: unknown): value is LocationPermissionDeniedError =>
|
||||
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "LocationPermissionDeniedError"
|
||||
|
||||
export type ServiceUnavailableError = {
|
||||
readonly _tag: "ServiceUnavailableError"
|
||||
readonly message: string
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { InvalidRequestError, SessionNotFoundError } from "./errors.js"
|
||||
import {
|
||||
InvalidRequestError,
|
||||
LocationDirectoryNotFoundError,
|
||||
LocationPermissionDeniedError,
|
||||
SessionNotFoundError,
|
||||
} from "./errors.js"
|
||||
import { makeDefaultApi } from "./api.js"
|
||||
import type { Api } from "./api.js"
|
||||
import type { Context } from "effect"
|
||||
@@ -7,6 +12,7 @@ import type { EventGroup } from "./groups/event.js"
|
||||
|
||||
class LocationMiddleware extends HttpApiMiddleware.Service<LocationMiddleware>()(
|
||||
"@opencode/client/LocationMiddleware",
|
||||
{ error: [LocationDirectoryNotFoundError, LocationPermissionDeniedError] },
|
||||
) {}
|
||||
|
||||
class SessionLocationMiddleware extends HttpApiMiddleware.Service<SessionLocationMiddleware>()(
|
||||
|
||||
@@ -11,6 +11,24 @@ export class InvalidRequestError extends Schema.TaggedError<InvalidRequestError>
|
||||
{ httpApiStatus: 400 },
|
||||
) {}
|
||||
|
||||
export class LocationDirectoryNotFoundError extends Schema.TaggedError<LocationDirectoryNotFoundError>()(
|
||||
"LocationDirectoryNotFoundError",
|
||||
{
|
||||
directory: Schema.String,
|
||||
message: Schema.String,
|
||||
},
|
||||
{ httpApiStatus: 400 },
|
||||
) {}
|
||||
|
||||
export class LocationPermissionDeniedError extends Schema.TaggedError<LocationPermissionDeniedError>()(
|
||||
"LocationPermissionDeniedError",
|
||||
{
|
||||
directory: Schema.String,
|
||||
message: Schema.String,
|
||||
},
|
||||
{ httpApiStatus: 403 },
|
||||
) {}
|
||||
|
||||
export class RpcError extends Schema.TaggedError<RpcError>()(
|
||||
"RpcError",
|
||||
{
|
||||
|
||||
@@ -2,7 +2,9 @@ import { Location } from "@opencode/core/location"
|
||||
import { LocationServiceMap } from "@opencode/core/location-services"
|
||||
import { AbsolutePath } from "@opencode/core/schema"
|
||||
import { Session } from "@opencode/core/session"
|
||||
import { InvalidRequestError } from "@opencode/protocol/errors"
|
||||
import { InvalidRequestError, LocationDirectoryNotFoundError, LocationPermissionDeniedError } from "@opencode/protocol/errors"
|
||||
import { FSUtil } from "@opencode/util/fs-util"
|
||||
import type { PlatformError } from "effect/PlatformError"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { HttpServerRequest } from "effect/unstable/http"
|
||||
import { HttpApiMiddleware } from "effect/unstable/httpapi"
|
||||
@@ -10,9 +12,10 @@ import { missingSession } from "./handlers/session-error"
|
||||
|
||||
export type LocationServices = Layer.Success<ReturnType<(typeof LocationServiceMap.Service)["get"]>>
|
||||
|
||||
export class LocationMiddleware extends HttpApiMiddleware.Service<LocationMiddleware, { provides: LocationServices }>()(
|
||||
"@opencode/HttpApiLocation",
|
||||
) {}
|
||||
export class LocationMiddleware extends HttpApiMiddleware.Service<
|
||||
LocationMiddleware,
|
||||
{ provides: LocationServices }
|
||||
>()("@opencode/HttpApiLocation", { error: [LocationDirectoryNotFoundError, LocationPermissionDeniedError] }) {}
|
||||
|
||||
export function response<A, E, R>(data: Effect.Effect<A, E, R>) {
|
||||
return Effect.gen(function* () {
|
||||
@@ -54,15 +57,58 @@ function decode(input: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export const layer = Layer.effect(
|
||||
LocationMiddleware,
|
||||
Effect.gen(function* () {
|
||||
const locations = yield* LocationServiceMap.Service
|
||||
return LocationMiddleware.of((effect) =>
|
||||
Effect.gen(function* () {
|
||||
const request = yield* HttpServerRequest.HttpServerRequest
|
||||
return yield* effect.pipe(Effect.provide(locations.get(requestRef(request))))
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
export const layer = (directoryCheck = true) =>
|
||||
Layer.effect(
|
||||
LocationMiddleware,
|
||||
Effect.gen(function* () {
|
||||
const locations = yield* LocationServiceMap.Service
|
||||
const fs = yield* FSUtil.Service
|
||||
return LocationMiddleware.of((effect) =>
|
||||
Effect.gen(function* () {
|
||||
const request = yield* HttpServerRequest.HttpServerRequest
|
||||
const ref = requestRef(request)
|
||||
// Resolve once before booting the Location graph; otherwise both a missing folder and
|
||||
// macOS privacy denial become defects during Project/FileSystem startup.
|
||||
if (directoryCheck)
|
||||
yield* fs.realPath(ref.directory).pipe(
|
||||
Effect.catchReason(
|
||||
"PlatformError",
|
||||
"NotFound",
|
||||
() =>
|
||||
Effect.fail(
|
||||
new LocationDirectoryNotFoundError({
|
||||
directory: ref.directory,
|
||||
message: `Project directory not found: ${ref.directory}`,
|
||||
}),
|
||||
),
|
||||
),
|
||||
Effect.catchTag("PlatformError", (error) => {
|
||||
if (isPermissionDenied(error))
|
||||
return Effect.fail(
|
||||
new LocationPermissionDeniedError({
|
||||
directory: ref.directory,
|
||||
message: `Cannot access project directory: ${ref.directory}`,
|
||||
}),
|
||||
)
|
||||
return Effect.die(error)
|
||||
}),
|
||||
)
|
||||
return yield* effect.pipe(Effect.provide(locations.get(ref)))
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
// Effect maps EACCES to PermissionDenied but leaves EPERM as Unknown, which is how macOS reports a
|
||||
// folder blocked by privacy settings (e.g. `EPERM: operation not permitted, lstat '/Users/<user>/Documents'`).
|
||||
export function isPermissionDenied(error: PlatformError) {
|
||||
if (error.reason._tag === "PermissionDenied") return true
|
||||
const cause = error.cause
|
||||
return (
|
||||
error.reason._tag === "Unknown" &&
|
||||
typeof cause === "object" &&
|
||||
cause !== null &&
|
||||
"code" in cause &&
|
||||
cause.code === "EPERM"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ export const ServerOptions = Schema.Struct({
|
||||
Schema.Struct({
|
||||
filewatcher: Schema.optional(Schema.Boolean),
|
||||
fff: Schema.optional(Schema.Boolean),
|
||||
// Hosts without a local project filesystem (e.g. workerd) opt out of the request-time directory probe.
|
||||
directoryCheck: Schema.optional(Schema.Boolean),
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
@@ -31,6 +31,7 @@ import { PluginUpdate } from "@opencode/core/plugin/update"
|
||||
import { SdkPlugins } from "@opencode/core/plugin/sdk"
|
||||
import { WellKnown } from "@opencode/core/wellknown"
|
||||
import { Workspace } from "@opencode/core/workspace"
|
||||
import { FSUtil } from "@opencode/util/fs-util"
|
||||
import { Watcher } from "@opencode/core/filesystem/watcher"
|
||||
import { HttpRouter } from "effect/unstable/http"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
@@ -51,6 +52,7 @@ import type { ServerOptions } from "./options"
|
||||
|
||||
const applicationServiceNodes = [
|
||||
Global.node,
|
||||
FSUtil.node,
|
||||
Database.node,
|
||||
Bus.node,
|
||||
EventLogger.node,
|
||||
@@ -177,7 +179,7 @@ function makeRoutes<AuthError, AuthServices>(
|
||||
Layer.provide(handlers.pipe(Layer.provide(services), Layer.provide(Layer.succeed(CorsConfig, options)))),
|
||||
Layer.provide(formLocationLayer),
|
||||
Layer.provide(sessionLocationLayer),
|
||||
Layer.provide(layer),
|
||||
Layer.provide(layer(options.fs?.directoryCheck !== false && !options.simulation)),
|
||||
Layer.provide(authorizationLayer),
|
||||
Layer.provide(schemaErrorLayer),
|
||||
Layer.provide(auth),
|
||||
|
||||
@@ -62,7 +62,7 @@ export function serverOptions(options: Options): ServerOptions {
|
||||
return {
|
||||
app: options.app,
|
||||
password: options.password,
|
||||
fs: { filewatcher: false, fff: false },
|
||||
fs: { filewatcher: false, fff: false, directoryCheck: false },
|
||||
// Durable event history is how a turn orphaned by eviction is recovered:
|
||||
// the boot-time resume replays it. A runtime that dies without teardown
|
||||
// cannot opt out of it, so this is not exposed as an option.
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Agent } from "@opencode/schema/agent"
|
||||
import { Integration } from "@opencode/schema/integration"
|
||||
import { ServerInfo } from "@opencode/protocol/groups/server"
|
||||
import { Effect, Schedule, Schema } from "effect"
|
||||
import { tmpdir } from "../../core/test/fixture/tmpdir"
|
||||
import { tmpdir, tmpdirScoped } from "../../core/test/fixture/tmpdir"
|
||||
import { it } from "../../core/test/lib/effect"
|
||||
import { ServerFetch } from "../src/fetch"
|
||||
|
||||
@@ -121,6 +121,60 @@ it.live("activates credentials through the HttpApi", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("reports a missing project directory and recovers when it returns", () =>
|
||||
Effect.gen(function* () {
|
||||
const handler = yield* ServerFetch.make(options)
|
||||
const directory = yield* tmpdirScoped()
|
||||
const missing = path.join(directory.path, "moved-project")
|
||||
const request = (endpoint: string) =>
|
||||
handler(
|
||||
new Request(`http://opencode.local${endpoint}`, {
|
||||
headers: { "x-opencode-directory": encodeURIComponent(missing) },
|
||||
}),
|
||||
)
|
||||
|
||||
const response = yield* Effect.promise(() => request("/api/integration"))
|
||||
expect(response.status).toBe(400)
|
||||
expect(yield* Effect.promise(() => response.json())).toMatchObject({
|
||||
_tag: "LocationDirectoryNotFoundError",
|
||||
directory: missing,
|
||||
})
|
||||
expect((yield* Effect.promise(() => request("/api/location"))).status).toBe(400)
|
||||
|
||||
yield* Effect.promise(() => fs.mkdir(missing))
|
||||
expect((yield* Effect.promise(() => request("/api/integration"))).status).toBe(200)
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("reports denied project access without breaking other locations", () =>
|
||||
Effect.gen(function* () {
|
||||
if (process.platform === "win32") return
|
||||
const handler = yield* ServerFetch.make(options)
|
||||
const root = yield* tmpdirScoped()
|
||||
const parent = path.join(root.path, "private")
|
||||
const directory = path.join(parent, "project")
|
||||
yield* Effect.promise(() => fs.mkdir(directory, { recursive: true }))
|
||||
yield* Effect.addFinalizer(() => Effect.promise(() => fs.chmod(parent, 0o700)))
|
||||
yield* Effect.promise(() => fs.chmod(parent, 0o000))
|
||||
|
||||
const request = () =>
|
||||
handler(
|
||||
new Request("http://opencode.local/api/plugin", {
|
||||
headers: { "x-opencode-directory": encodeURIComponent(directory) },
|
||||
}),
|
||||
)
|
||||
const response = yield* Effect.promise(request)
|
||||
expect(response.status).toBe(403)
|
||||
expect(yield* Effect.promise(() => response.json())).toMatchObject({
|
||||
_tag: "LocationPermissionDeniedError",
|
||||
directory,
|
||||
})
|
||||
|
||||
yield* Effect.promise(() => fs.chmod(parent, 0o700))
|
||||
expect((yield* Effect.promise(request)).status).toBe(200)
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("serves unauthenticated and answers CORS preflight when no password is configured", () =>
|
||||
Effect.gen(function* () {
|
||||
const handler = yield* ServerFetch.make(options)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { expect, test } from "bun:test"
|
||||
import { PlatformError } from "effect"
|
||||
import { isPermissionDenied } from "../src/location"
|
||||
|
||||
// Reproduces the error macOS returns when privacy settings block a folder.
|
||||
const macPrivacyDenied = () =>
|
||||
PlatformError.systemError({
|
||||
_tag: "Unknown",
|
||||
module: "FileSystem",
|
||||
method: "realPath",
|
||||
pathOrDescriptor: "/Users/example/Documents",
|
||||
cause: Object.assign(new Error("EPERM: operation not permitted, lstat '/Users/example/Documents'"), {
|
||||
code: "EPERM",
|
||||
}),
|
||||
})
|
||||
|
||||
test("recognizes the macOS privacy denial reported as Unknown with an EPERM cause", () => {
|
||||
expect(isPermissionDenied(macPrivacyDenied())).toBe(true)
|
||||
expect(
|
||||
isPermissionDenied(
|
||||
PlatformError.systemError({ _tag: "PermissionDenied", module: "FileSystem", method: "realPath" }),
|
||||
),
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
test("does not treat other filesystem failures as permission denied", () => {
|
||||
expect(
|
||||
isPermissionDenied(PlatformError.systemError({ _tag: "NotFound", module: "FileSystem", method: "realPath" })),
|
||||
).toBe(false)
|
||||
expect(
|
||||
isPermissionDenied(
|
||||
PlatformError.systemError({
|
||||
_tag: "Unknown",
|
||||
module: "FileSystem",
|
||||
method: "realPath",
|
||||
cause: Object.assign(new Error("EIO: i/o error"), { code: "EIO" }),
|
||||
}),
|
||||
),
|
||||
).toBe(false)
|
||||
})
|
||||
Reference in New Issue
Block a user