summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoropencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>2026-04-29 13:36:05 +0000
committeropencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>2026-04-29 13:36:05 +0000
commitdf147b65fd7f0739bc4e39f0698c788387cb4974 (patch)
tree5b3765aea343ac99a5a3f18cb230ed712b5d4416
parent6015084fa2502bf4dc941ae39c538f089a0d89b4 (diff)
downloadopencode-df147b65fd7f0739bc4e39f0698c788387cb4974.tar.gz
opencode-df147b65fd7f0739bc4e39f0698c788387cb4974.zip
chore: generate
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/groups/pty.ts8
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/groups/tui.ts55
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/groups/workspace.ts4
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/config.ts40
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts40
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts246
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts88
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts154
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/instance.ts108
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/mcp.ts102
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/permission.ts34
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/project.ts54
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts140
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/pty.ts4
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/question.ts40
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts83
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/instance-context.ts51
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts5
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/public.ts34
-rw-r--r--packages/opencode/src/server/server.ts9
-rw-r--r--packages/opencode/src/session/message-v2.ts4
-rw-r--r--packages/opencode/src/util/schema.ts2
-rw-r--r--packages/opencode/test/server/httpapi-bridge.test.ts6
-rw-r--r--packages/opencode/test/server/httpapi-workspace.test.ts6
-rw-r--r--packages/sdk/openapi.json362
25 files changed, 971 insertions, 708 deletions
diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/pty.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/pty.ts
index e3914579c..eb71526fb 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/groups/pty.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/groups/pty.ts
@@ -29,14 +29,18 @@ export const PtyApi = HttpApi.make("pty")
.add(
HttpApiGroup.make("pty")
.add(
- HttpApiEndpoint.get("shells", PtyPaths.shells, { success: described(Schema.Array(ShellItem), "List of shells") }).annotateMerge(
+ HttpApiEndpoint.get("shells", PtyPaths.shells, {
+ success: described(Schema.Array(ShellItem), "List of shells"),
+ }).annotateMerge(
OpenApi.annotations({
identifier: "pty.shells",
summary: "List available shells",
description: "Get a list of available shells on the system.",
}),
),
- HttpApiEndpoint.get("list", PtyPaths.list, { success: described(Schema.Array(Pty.Info), "List of sessions") }).annotateMerge(
+ HttpApiEndpoint.get("list", PtyPaths.list, {
+ success: described(Schema.Array(Pty.Info), "List of sessions"),
+ }).annotateMerge(
OpenApi.annotations({
identifier: "pty.list",
summary: "List PTY sessions",
diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/tui.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/tui.ts
index a5d31bfa6..49ba05c2d 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/groups/tui.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/groups/tui.ts
@@ -11,11 +11,28 @@ export const TuiRequestPayload = Schema.Struct({
path: Schema.String,
body: Schema.Unknown,
})
-const EventTuiPromptAppend = Schema.Struct({ type: Schema.Literal(TuiEvent.PromptAppend.type), properties: TuiEvent.PromptAppend.properties }).annotate({ identifier: "EventTuiPromptAppend" })
-const EventTuiCommandExecute = Schema.Struct({ type: Schema.Literal(TuiEvent.CommandExecute.type), properties: TuiEvent.CommandExecute.properties }).annotate({ identifier: "EventTuiCommandExecute" })
-const EventTuiToastShow = Schema.Struct({ type: Schema.Literal(TuiEvent.ToastShow.type), properties: TuiEvent.ToastShow.properties }).annotate({ identifier: "EventTuiToastShow" })
-const EventTuiSessionSelect = Schema.Struct({ type: Schema.Literal(TuiEvent.SessionSelect.type), properties: TuiEvent.SessionSelect.properties }).annotate({ identifier: "EventTuiSessionSelect" })
-export const TuiPublishPayload = Schema.Union([EventTuiPromptAppend, EventTuiCommandExecute, EventTuiToastShow, EventTuiSessionSelect])
+const EventTuiPromptAppend = Schema.Struct({
+ type: Schema.Literal(TuiEvent.PromptAppend.type),
+ properties: TuiEvent.PromptAppend.properties,
+}).annotate({ identifier: "EventTuiPromptAppend" })
+const EventTuiCommandExecute = Schema.Struct({
+ type: Schema.Literal(TuiEvent.CommandExecute.type),
+ properties: TuiEvent.CommandExecute.properties,
+}).annotate({ identifier: "EventTuiCommandExecute" })
+const EventTuiToastShow = Schema.Struct({
+ type: Schema.Literal(TuiEvent.ToastShow.type),
+ properties: TuiEvent.ToastShow.properties,
+}).annotate({ identifier: "EventTuiToastShow" })
+const EventTuiSessionSelect = Schema.Struct({
+ type: Schema.Literal(TuiEvent.SessionSelect.type),
+ properties: TuiEvent.SessionSelect.properties,
+}).annotate({ identifier: "EventTuiSessionSelect" })
+export const TuiPublishPayload = Schema.Union([
+ EventTuiPromptAppend,
+ EventTuiCommandExecute,
+ EventTuiToastShow,
+ EventTuiSessionSelect,
+])
export const TuiPaths = {
appendPrompt: `${root}/append-prompt`,
@@ -48,42 +65,54 @@ export const TuiApi = HttpApi.make("tui")
description: "Append prompt to the TUI.",
}),
),
- HttpApiEndpoint.post("openHelp", TuiPaths.openHelp, { success: described(Schema.Boolean, "Help dialog opened successfully") }).annotateMerge(
+ HttpApiEndpoint.post("openHelp", TuiPaths.openHelp, {
+ success: described(Schema.Boolean, "Help dialog opened successfully"),
+ }).annotateMerge(
OpenApi.annotations({
identifier: "tui.openHelp",
summary: "Open help dialog",
description: "Open the help dialog in the TUI to display user assistance information.",
}),
),
- HttpApiEndpoint.post("openSessions", TuiPaths.openSessions, { success: described(Schema.Boolean, "Session dialog opened successfully") }).annotateMerge(
+ HttpApiEndpoint.post("openSessions", TuiPaths.openSessions, {
+ success: described(Schema.Boolean, "Session dialog opened successfully"),
+ }).annotateMerge(
OpenApi.annotations({
identifier: "tui.openSessions",
summary: "Open sessions dialog",
description: "Open the session dialog.",
}),
),
- HttpApiEndpoint.post("openThemes", TuiPaths.openThemes, { success: described(Schema.Boolean, "Theme dialog opened successfully") }).annotateMerge(
+ HttpApiEndpoint.post("openThemes", TuiPaths.openThemes, {
+ success: described(Schema.Boolean, "Theme dialog opened successfully"),
+ }).annotateMerge(
OpenApi.annotations({
identifier: "tui.openThemes",
summary: "Open themes dialog",
description: "Open the theme dialog.",
}),
),
- HttpApiEndpoint.post("openModels", TuiPaths.openModels, { success: described(Schema.Boolean, "Model dialog opened successfully") }).annotateMerge(
+ HttpApiEndpoint.post("openModels", TuiPaths.openModels, {
+ success: described(Schema.Boolean, "Model dialog opened successfully"),
+ }).annotateMerge(
OpenApi.annotations({
identifier: "tui.openModels",
summary: "Open models dialog",
description: "Open the model dialog.",
}),
),
- HttpApiEndpoint.post("submitPrompt", TuiPaths.submitPrompt, { success: described(Schema.Boolean, "Prompt submitted successfully") }).annotateMerge(
+ HttpApiEndpoint.post("submitPrompt", TuiPaths.submitPrompt, {
+ success: described(Schema.Boolean, "Prompt submitted successfully"),
+ }).annotateMerge(
OpenApi.annotations({
identifier: "tui.submitPrompt",
summary: "Submit TUI prompt",
description: "Submit the prompt.",
}),
),
- HttpApiEndpoint.post("clearPrompt", TuiPaths.clearPrompt, { success: described(Schema.Boolean, "Prompt cleared successfully") }).annotateMerge(
+ HttpApiEndpoint.post("clearPrompt", TuiPaths.clearPrompt, {
+ success: described(Schema.Boolean, "Prompt cleared successfully"),
+ }).annotateMerge(
OpenApi.annotations({
identifier: "tui.clearPrompt",
summary: "Clear TUI prompt",
@@ -133,7 +162,9 @@ export const TuiApi = HttpApi.make("tui")
description: "Navigate the TUI to display the specified session.",
}),
),
- HttpApiEndpoint.get("controlNext", TuiPaths.controlNext, { success: described(TuiRequestPayload, "Next TUI request") }).annotateMerge(
+ HttpApiEndpoint.get("controlNext", TuiPaths.controlNext, {
+ success: described(TuiRequestPayload, "Next TUI request"),
+ }).annotateMerge(
OpenApi.annotations({
identifier: "tui.control.next",
summary: "Get next TUI request",
diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/workspace.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/workspace.ts
index 0305c6536..ab5f08bb1 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/groups/workspace.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/groups/workspace.ts
@@ -9,9 +9,7 @@ import { described } from "./metadata"
const root = "/experimental/workspace"
export const CreatePayload = Schema.Struct(Struct.omit(Workspace.CreateInput.fields, ["projectID"]))
-export const SessionRestorePayload = Schema.Struct(
- Struct.omit(Workspace.SessionRestoreInput.fields, ["workspaceID"]),
-)
+export const SessionRestorePayload = Schema.Struct(Struct.omit(Workspace.SessionRestoreInput.fields, ["workspaceID"]))
export const SessionRestoreResponse = Schema.Struct({
total: NonNegativeInt,
})
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/config.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/config.ts
index 2fc225d17..58aa81098 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/config.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/config.ts
@@ -7,28 +7,28 @@ import { InstanceHttpApi } from "../api"
import { markInstanceForDisposal } from "../lifecycle"
export const configHandlers = HttpApiBuilder.group(InstanceHttpApi, "config", (handlers) =>
- Effect.gen(function* () {
- const providerSvc = yield* Provider.Service
- const configSvc = yield* Config.Service
+ Effect.gen(function* () {
+ const providerSvc = yield* Provider.Service
+ const configSvc = yield* Config.Service
- const get = Effect.fn("ConfigHttpApi.get")(function* () {
- return yield* configSvc.get()
- })
+ const get = Effect.fn("ConfigHttpApi.get")(function* () {
+ return yield* configSvc.get()
+ })
- const update = Effect.fn("ConfigHttpApi.update")(function* (ctx) {
- yield* configSvc.update(ctx.payload, { dispose: false })
- yield* markInstanceForDisposal(yield* InstanceState.context)
- return ctx.payload
- })
+ const update = Effect.fn("ConfigHttpApi.update")(function* (ctx) {
+ yield* configSvc.update(ctx.payload, { dispose: false })
+ yield* markInstanceForDisposal(yield* InstanceState.context)
+ return ctx.payload
+ })
- const providers = Effect.fn("ConfigHttpApi.providers")(function* () {
- const providers = yield* providerSvc.list()
- return {
- providers: Object.values(providers),
- default: Provider.defaultModelIDs(providers),
- }
- })
+ const providers = Effect.fn("ConfigHttpApi.providers")(function* () {
+ const providers = yield* providerSvc.list()
+ return {
+ providers: Object.values(providers),
+ default: Provider.defaultModelIDs(providers),
+ }
+ })
- return handlers.handle("get", get).handle("update", update).handle("providers", providers)
- }),
+ return handlers.handle("get", get).handle("update", update).handle("providers", providers)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts
index abddd8c40..e1ede2274 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts
@@ -7,28 +7,28 @@ import { RootHttpApi } from "../api"
import { LogInput } from "../groups/control"
export const controlHandlers = HttpApiBuilder.group(RootHttpApi, "control", (handlers) =>
- Effect.gen(function* () {
- const auth = yield* Auth.Service
+ Effect.gen(function* () {
+ const auth = yield* Auth.Service
- const authSet = Effect.fn("ControlHttpApi.authSet")(function* (ctx: {
- params: { providerID: ProviderID }
- payload: Auth.Info
- }) {
- yield* auth.set(ctx.params.providerID, ctx.payload).pipe(Effect.orDie)
- return true
- })
+ const authSet = Effect.fn("ControlHttpApi.authSet")(function* (ctx: {
+ params: { providerID: ProviderID }
+ payload: Auth.Info
+ }) {
+ yield* auth.set(ctx.params.providerID, ctx.payload).pipe(Effect.orDie)
+ return true
+ })
- const authRemove = Effect.fn("ControlHttpApi.authRemove")(function* (ctx: { params: { providerID: ProviderID } }) {
- yield* auth.remove(ctx.params.providerID).pipe(Effect.orDie)
- return true
- })
+ const authRemove = Effect.fn("ControlHttpApi.authRemove")(function* (ctx: { params: { providerID: ProviderID } }) {
+ yield* auth.remove(ctx.params.providerID).pipe(Effect.orDie)
+ return true
+ })
- const log = Effect.fn("ControlHttpApi.log")(function* (ctx: { payload: typeof LogInput.Type }) {
- const logger = Log.create({ service: ctx.payload.service })
- logger[ctx.payload.level](ctx.payload.message, ctx.payload.extra)
- return true
- })
+ const log = Effect.fn("ControlHttpApi.log")(function* (ctx: { payload: typeof LogInput.Type }) {
+ const logger = Log.create({ service: ctx.payload.service })
+ logger[ctx.payload.level](ctx.payload.message, ctx.payload.extra)
+ return true
+ })
- return handlers.handle("authSet", authSet).handle("authRemove", authRemove).handle("log", log)
- }),
+ return handlers.handle("authSet", authSet).handle("authRemove", authRemove).handle("log", log)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts
index 42eab762e..cc958da30 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts
@@ -15,141 +15,141 @@ import { InstanceHttpApi } from "../api"
import { ConsoleSwitchPayload, SessionListQuery, ToolListQuery } from "../groups/experimental"
export const experimentalHandlers = HttpApiBuilder.group(InstanceHttpApi, "experimental", (handlers) =>
- Effect.gen(function* () {
- const account = yield* Account.Service
- const agents = yield* Agent.Service
- const config = yield* Config.Service
- const mcp = yield* MCP.Service
- const project = yield* Project.Service
- const registry = yield* ToolRegistry.Service
- const worktreeSvc = yield* Worktree.Service
+ Effect.gen(function* () {
+ const account = yield* Account.Service
+ const agents = yield* Agent.Service
+ const config = yield* Config.Service
+ const mcp = yield* MCP.Service
+ const project = yield* Project.Service
+ const registry = yield* ToolRegistry.Service
+ const worktreeSvc = yield* Worktree.Service
- const getConsole = Effect.fn("ExperimentalHttpApi.console")(function* () {
- const [state, groups] = yield* Effect.all(
- [config.getConsoleState(), account.orgsByAccount().pipe(Effect.orDie)],
- {
- concurrency: "unbounded",
- },
- )
- return {
- consoleManagedProviders: state.consoleManagedProviders,
- ...(state.activeOrgName ? { activeOrgName: state.activeOrgName } : {}),
- switchableOrgCount: groups.reduce((count, group) => count + group.orgs.length, 0),
- }
- })
+ const getConsole = Effect.fn("ExperimentalHttpApi.console")(function* () {
+ const [state, groups] = yield* Effect.all(
+ [config.getConsoleState(), account.orgsByAccount().pipe(Effect.orDie)],
+ {
+ concurrency: "unbounded",
+ },
+ )
+ return {
+ consoleManagedProviders: state.consoleManagedProviders,
+ ...(state.activeOrgName ? { activeOrgName: state.activeOrgName } : {}),
+ switchableOrgCount: groups.reduce((count, group) => count + group.orgs.length, 0),
+ }
+ })
- const listConsoleOrgs = Effect.fn("ExperimentalHttpApi.consoleOrgs")(function* () {
- const [groups, active] = yield* Effect.all(
- [account.orgsByAccount().pipe(Effect.orDie), account.active().pipe(Effect.orDie)],
- {
- concurrency: "unbounded",
- },
- )
- const info = Option.getOrUndefined(active)
- return {
- orgs: groups.flatMap((group) =>
- group.orgs.map((org) => ({
- accountID: group.account.id,
- accountEmail: group.account.email,
- accountUrl: group.account.url,
- orgID: org.id,
- orgName: org.name,
- active: !!info && info.id === group.account.id && info.active_org_id === org.id,
- })),
- ),
- }
- })
+ const listConsoleOrgs = Effect.fn("ExperimentalHttpApi.consoleOrgs")(function* () {
+ const [groups, active] = yield* Effect.all(
+ [account.orgsByAccount().pipe(Effect.orDie), account.active().pipe(Effect.orDie)],
+ {
+ concurrency: "unbounded",
+ },
+ )
+ const info = Option.getOrUndefined(active)
+ return {
+ orgs: groups.flatMap((group) =>
+ group.orgs.map((org) => ({
+ accountID: group.account.id,
+ accountEmail: group.account.email,
+ accountUrl: group.account.url,
+ orgID: org.id,
+ orgName: org.name,
+ active: !!info && info.id === group.account.id && info.active_org_id === org.id,
+ })),
+ ),
+ }
+ })
- const switchConsole = Effect.fn("ExperimentalHttpApi.consoleSwitch")(function* (ctx: {
- payload: typeof ConsoleSwitchPayload.Type
- }) {
- yield* account
- .use(ctx.payload.accountID, Option.some(ctx.payload.orgID))
- .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
- return true
- })
+ const switchConsole = Effect.fn("ExperimentalHttpApi.consoleSwitch")(function* (ctx: {
+ payload: typeof ConsoleSwitchPayload.Type
+ }) {
+ yield* account
+ .use(ctx.payload.accountID, Option.some(ctx.payload.orgID))
+ .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
+ return true
+ })
- const tool = Effect.fn("ExperimentalHttpApi.tool")(function* (ctx: { query: typeof ToolListQuery.Type }) {
- const list = yield* registry.tools({
- providerID: ctx.query.provider,
- modelID: ctx.query.model,
- agent: yield* agents.get(yield* agents.defaultAgent()),
- })
- return list.map((item) => ({
- id: item.id,
- description: item.description,
- parameters: EffectZod.toJsonSchema(item.parameters),
- }))
+ const tool = Effect.fn("ExperimentalHttpApi.tool")(function* (ctx: { query: typeof ToolListQuery.Type }) {
+ const list = yield* registry.tools({
+ providerID: ctx.query.provider,
+ modelID: ctx.query.model,
+ agent: yield* agents.get(yield* agents.defaultAgent()),
})
+ return list.map((item) => ({
+ id: item.id,
+ description: item.description,
+ parameters: EffectZod.toJsonSchema(item.parameters),
+ }))
+ })
- const toolIDs = Effect.fn("ExperimentalHttpApi.toolIDs")(function* () {
- return yield* registry.ids()
- })
+ const toolIDs = Effect.fn("ExperimentalHttpApi.toolIDs")(function* () {
+ return yield* registry.ids()
+ })
- const worktree = Effect.fn("ExperimentalHttpApi.worktree")(function* () {
- const ctx = yield* InstanceState.context
- return yield* project.sandboxes(ctx.project.id)
- })
+ const worktree = Effect.fn("ExperimentalHttpApi.worktree")(function* () {
+ const ctx = yield* InstanceState.context
+ return yield* project.sandboxes(ctx.project.id)
+ })
- const worktreeCreate = Effect.fn("ExperimentalHttpApi.worktreeCreate")(function* (ctx: {
- payload: Worktree.CreateInput | undefined
- }) {
- return yield* worktreeSvc.create(ctx.payload)
- })
+ const worktreeCreate = Effect.fn("ExperimentalHttpApi.worktreeCreate")(function* (ctx: {
+ payload: Worktree.CreateInput | undefined
+ }) {
+ return yield* worktreeSvc.create(ctx.payload)
+ })
- const worktreeRemove = Effect.fn("ExperimentalHttpApi.worktreeRemove")(function* (input: {
- payload: Worktree.RemoveInput
- }) {
- const ctx = yield* InstanceState.context
- yield* worktreeSvc.remove(input.payload)
- yield* project.removeSandbox(ctx.project.id, input.payload.directory)
- return true
- })
+ const worktreeRemove = Effect.fn("ExperimentalHttpApi.worktreeRemove")(function* (input: {
+ payload: Worktree.RemoveInput
+ }) {
+ const ctx = yield* InstanceState.context
+ yield* worktreeSvc.remove(input.payload)
+ yield* project.removeSandbox(ctx.project.id, input.payload.directory)
+ return true
+ })
- const worktreeReset = Effect.fn("ExperimentalHttpApi.worktreeReset")(function* (ctx: {
- payload: Worktree.ResetInput
- }) {
- yield* worktreeSvc.reset(ctx.payload)
- return true
- })
+ const worktreeReset = Effect.fn("ExperimentalHttpApi.worktreeReset")(function* (ctx: {
+ payload: Worktree.ResetInput
+ }) {
+ yield* worktreeSvc.reset(ctx.payload)
+ return true
+ })
- const session = Effect.fn("ExperimentalHttpApi.session")(function* (ctx: { query: typeof SessionListQuery.Type }) {
- const limit = ctx.query.limit ?? 100
- const sessions = Array.from(
- Session.listGlobal({
- directory: ctx.query.directory,
- roots: ctx.query.roots,
- start: ctx.query.start,
- cursor: ctx.query.cursor,
- search: ctx.query.search,
- limit: limit + 1,
- archived: ctx.query.archived,
- }),
- )
- const list = sessions.length > limit ? sessions.slice(0, limit) : sessions
- return HttpServerResponse.jsonUnsafe(list, {
- headers:
- sessions.length > limit && list.length > 0
- ? { "x-next-cursor": String(list[list.length - 1].time.updated) }
- : undefined,
- })
+ const session = Effect.fn("ExperimentalHttpApi.session")(function* (ctx: { query: typeof SessionListQuery.Type }) {
+ const limit = ctx.query.limit ?? 100
+ const sessions = Array.from(
+ Session.listGlobal({
+ directory: ctx.query.directory,
+ roots: ctx.query.roots,
+ start: ctx.query.start,
+ cursor: ctx.query.cursor,
+ search: ctx.query.search,
+ limit: limit + 1,
+ archived: ctx.query.archived,
+ }),
+ )
+ const list = sessions.length > limit ? sessions.slice(0, limit) : sessions
+ return HttpServerResponse.jsonUnsafe(list, {
+ headers:
+ sessions.length > limit && list.length > 0
+ ? { "x-next-cursor": String(list[list.length - 1].time.updated) }
+ : undefined,
})
+ })
- const resource = Effect.fn("ExperimentalHttpApi.resource")(function* () {
- return yield* mcp.resources()
- })
+ const resource = Effect.fn("ExperimentalHttpApi.resource")(function* () {
+ return yield* mcp.resources()
+ })
- return handlers
- .handle("console", getConsole)
- .handle("consoleOrgs", listConsoleOrgs)
- .handle("consoleSwitch", switchConsole)
- .handle("tool", tool)
- .handle("toolIDs", toolIDs)
- .handle("worktree", worktree)
- .handle("worktreeCreate", worktreeCreate)
- .handle("worktreeRemove", worktreeRemove)
- .handle("worktreeReset", worktreeReset)
- .handle("session", session)
- .handle("resource", resource)
- }),
+ return handlers
+ .handle("console", getConsole)
+ .handle("consoleOrgs", listConsoleOrgs)
+ .handle("consoleSwitch", switchConsole)
+ .handle("tool", tool)
+ .handle("toolIDs", toolIDs)
+ .handle("worktree", worktree)
+ .handle("worktreeCreate", worktreeCreate)
+ .handle("worktreeRemove", worktreeRemove)
+ .handle("worktreeReset", worktreeReset)
+ .handle("session", session)
+ .handle("resource", resource)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts
index 72133e8de..98ee5968e 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts
@@ -6,49 +6,49 @@ import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../api"
export const fileHandlers = HttpApiBuilder.group(InstanceHttpApi, "file", (handlers) =>
- Effect.gen(function* () {
- const svc = yield* File.Service
- const ripgrep = yield* Ripgrep.Service
-
- const findText = Effect.fn("FileHttpApi.findText")(function* (ctx: { query: { pattern: string } }) {
- return (yield* ripgrep
- .search({ cwd: (yield* InstanceState.context).directory, pattern: ctx.query.pattern, limit: 10 })
- .pipe(Effect.orDie)).items
+ Effect.gen(function* () {
+ const svc = yield* File.Service
+ const ripgrep = yield* Ripgrep.Service
+
+ const findText = Effect.fn("FileHttpApi.findText")(function* (ctx: { query: { pattern: string } }) {
+ return (yield* ripgrep
+ .search({ cwd: (yield* InstanceState.context).directory, pattern: ctx.query.pattern, limit: 10 })
+ .pipe(Effect.orDie)).items
+ })
+
+ const findFile = Effect.fn("FileHttpApi.findFile")(function* (ctx: {
+ query: { query: string; dirs?: "true" | "false"; type?: "file" | "directory"; limit?: number }
+ }) {
+ return yield* svc.search({
+ query: ctx.query.query,
+ limit: ctx.query.limit ?? 10,
+ dirs: ctx.query.dirs !== "false",
+ type: ctx.query.type,
})
-
- const findFile = Effect.fn("FileHttpApi.findFile")(function* (ctx: {
- query: { query: string; dirs?: "true" | "false"; type?: "file" | "directory"; limit?: number }
- }) {
- return yield* svc.search({
- query: ctx.query.query,
- limit: ctx.query.limit ?? 10,
- dirs: ctx.query.dirs !== "false",
- type: ctx.query.type,
- })
- })
-
- const findSymbol = Effect.fn("FileHttpApi.findSymbol")(function* () {
- return []
- })
-
- const list = Effect.fn("FileHttpApi.list")(function* (ctx: { query: { path: string } }) {
- return yield* svc.list(ctx.query.path)
- })
-
- const content = Effect.fn("FileHttpApi.content")(function* (ctx: { query: { path: string } }) {
- return yield* svc.read(ctx.query.path)
- })
-
- const status = Effect.fn("FileHttpApi.status")(function* () {
- return yield* svc.status()
- })
-
- return handlers
- .handle("findText", findText)
- .handle("findFile", findFile)
- .handle("findSymbol", findSymbol)
- .handle("list", list)
- .handle("content", content)
- .handle("status", status)
- }),
+ })
+
+ const findSymbol = Effect.fn("FileHttpApi.findSymbol")(function* () {
+ return []
+ })
+
+ const list = Effect.fn("FileHttpApi.list")(function* (ctx: { query: { path: string } }) {
+ return yield* svc.list(ctx.query.path)
+ })
+
+ const content = Effect.fn("FileHttpApi.content")(function* (ctx: { query: { path: string } }) {
+ return yield* svc.read(ctx.query.path)
+ })
+
+ const status = Effect.fn("FileHttpApi.status")(function* () {
+ return yield* svc.status()
+ })
+
+ return handlers
+ .handle("findText", findText)
+ .handle("findFile", findFile)
+ .handle("findSymbol", findSymbol)
+ .handle("list", list)
+ .handle("content", content)
+ .handle("status", status)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts
index 597239551..cd1bebec4 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts
@@ -65,92 +65,92 @@ function eventResponse() {
}
export const globalHandlers = HttpApiBuilder.group(RootHttpApi, "global", (handlers) =>
- Effect.gen(function* () {
- const config = yield* Config.Service
- const installation = yield* Installation.Service
+ Effect.gen(function* () {
+ const config = yield* Config.Service
+ const installation = yield* Installation.Service
- const health = Effect.fn("GlobalHttpApi.health")(function* () {
- return { healthy: true as const, version: InstallationVersion }
- })
+ const health = Effect.fn("GlobalHttpApi.health")(function* () {
+ return { healthy: true as const, version: InstallationVersion }
+ })
- const event = Effect.fn("GlobalHttpApi.event")(function* () {
- return eventResponse()
- })
+ const event = Effect.fn("GlobalHttpApi.event")(function* () {
+ return eventResponse()
+ })
- const configGet = Effect.fn("GlobalHttpApi.configGet")(function* () {
- return yield* config.getGlobal()
- })
+ const configGet = Effect.fn("GlobalHttpApi.configGet")(function* () {
+ return yield* config.getGlobal()
+ })
- const configUpdate = Effect.fn("GlobalHttpApi.configUpdate")(function* (ctx) {
- return yield* config.updateGlobal(ctx.payload)
- })
+ const configUpdate = Effect.fn("GlobalHttpApi.configUpdate")(function* (ctx) {
+ return yield* config.updateGlobal(ctx.payload)
+ })
- const dispose = Effect.fn("GlobalHttpApi.dispose")(function* () {
- yield* Effect.promise(() => Instance.disposeAll())
- GlobalBus.emit("event", {
- directory: "global",
- payload: { type: "global.disposed", properties: {} },
- })
- return true
+ const dispose = Effect.fn("GlobalHttpApi.dispose")(function* () {
+ yield* Effect.promise(() => Instance.disposeAll())
+ GlobalBus.emit("event", {
+ directory: "global",
+ payload: { type: "global.disposed", properties: {} },
})
+ return true
+ })
- const upgrade = Effect.fn("GlobalHttpApi.upgrade")(function* (ctx: { payload: typeof GlobalUpgradeInput.Type }) {
- const method = yield* installation.method()
- if (method === "unknown") {
- return {
- status: 400,
- body: { success: false as const, error: "Unknown installation method" },
- }
+ const upgrade = Effect.fn("GlobalHttpApi.upgrade")(function* (ctx: { payload: typeof GlobalUpgradeInput.Type }) {
+ const method = yield* installation.method()
+ if (method === "unknown") {
+ return {
+ status: 400,
+ body: { success: false as const, error: "Unknown installation method" },
}
- const target = ctx.payload.target || (yield* installation.latest(method))
- const result = yield* installation.upgrade(method, target).pipe(
- Effect.as({ status: 200, body: { success: true as const, version: target } }),
- Effect.catch((err) =>
- Effect.succeed({
- status: 500,
- body: {
- success: false as const,
- error: err instanceof Error ? err.message : String(err),
- },
- }),
- ),
- )
- if (!result.body.success) return result
- GlobalBus.emit("event", {
- directory: "global",
- payload: {
- type: Installation.Event.Updated.type,
- properties: { version: target },
- },
- })
- return result
+ }
+ const target = ctx.payload.target || (yield* installation.latest(method))
+ const result = yield* installation.upgrade(method, target).pipe(
+ Effect.as({ status: 200, body: { success: true as const, version: target } }),
+ Effect.catch((err) =>
+ Effect.succeed({
+ status: 500,
+ body: {
+ success: false as const,
+ error: err instanceof Error ? err.message : String(err),
+ },
+ }),
+ ),
+ )
+ if (!result.body.success) return result
+ GlobalBus.emit("event", {
+ directory: "global",
+ payload: {
+ type: Installation.Event.Updated.type,
+ properties: { version: target },
+ },
})
+ return result
+ })
- const upgradeRaw = Effect.fn("GlobalHttpApi.upgradeRaw")(function* (ctx: {
- request: HttpServerRequest.HttpServerRequest
- }) {
- const body = yield* Effect.orDie(ctx.request.text)
- const json = parseBody(body)
- if (json === undefined) {
- return HttpServerResponse.jsonUnsafe({ success: false, error: "Invalid request body" }, { status: 400 })
- }
- const payload = yield* Schema.decodeUnknownEffect(GlobalUpgradeInput)(json).pipe(
- Effect.map((payload) => ({ valid: true as const, payload })),
- Effect.catch(() => Effect.succeed({ valid: false as const })),
- )
- if (!payload.valid) {
- return HttpServerResponse.jsonUnsafe({ success: false, error: "Invalid request body" }, { status: 400 })
- }
- const result = yield* upgrade({ payload: payload.payload })
- return HttpServerResponse.jsonUnsafe(result.body, { status: result.status })
- })
+ const upgradeRaw = Effect.fn("GlobalHttpApi.upgradeRaw")(function* (ctx: {
+ request: HttpServerRequest.HttpServerRequest
+ }) {
+ const body = yield* Effect.orDie(ctx.request.text)
+ const json = parseBody(body)
+ if (json === undefined) {
+ return HttpServerResponse.jsonUnsafe({ success: false, error: "Invalid request body" }, { status: 400 })
+ }
+ const payload = yield* Schema.decodeUnknownEffect(GlobalUpgradeInput)(json).pipe(
+ Effect.map((payload) => ({ valid: true as const, payload })),
+ Effect.catch(() => Effect.succeed({ valid: false as const })),
+ )
+ if (!payload.valid) {
+ return HttpServerResponse.jsonUnsafe({ success: false, error: "Invalid request body" }, { status: 400 })
+ }
+ const result = yield* upgrade({ payload: payload.payload })
+ return HttpServerResponse.jsonUnsafe(result.body, { status: result.status })
+ })
- return handlers
- .handle("health", health)
- .handleRaw("event", event)
- .handle("configGet", configGet)
- .handle("configUpdate", configUpdate)
- .handle("dispose", dispose)
- .handleRaw("upgrade", upgradeRaw)
- }),
+ return handlers
+ .handle("health", health)
+ .handleRaw("event", event)
+ .handle("configGet", configGet)
+ .handle("configUpdate", configUpdate)
+ .handle("dispose", dispose)
+ .handleRaw("upgrade", upgradeRaw)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/instance.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/instance.ts
index b6f386065..c2a4503b4 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/instance.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/instance.ts
@@ -12,68 +12,68 @@ import { InstanceHttpApi } from "../api"
import { markInstanceForDisposal } from "../lifecycle"
export const instanceHandlers = HttpApiBuilder.group(InstanceHttpApi, "instance", (handlers) =>
- Effect.gen(function* () {
- const agent = yield* Agent.Service
- const command = yield* Command.Service
- const format = yield* Format.Service
- const lsp = yield* LSP.Service
- const skill = yield* Skill.Service
- const vcs = yield* Vcs.Service
+ Effect.gen(function* () {
+ const agent = yield* Agent.Service
+ const command = yield* Command.Service
+ const format = yield* Format.Service
+ const lsp = yield* LSP.Service
+ const skill = yield* Skill.Service
+ const vcs = yield* Vcs.Service
- const dispose = Effect.fn("InstanceHttpApi.dispose")(function* () {
- yield* markInstanceForDisposal(yield* InstanceState.context)
- return true
- })
+ const dispose = Effect.fn("InstanceHttpApi.dispose")(function* () {
+ yield* markInstanceForDisposal(yield* InstanceState.context)
+ return true
+ })
- const getPath = Effect.fn("InstanceHttpApi.path")(function* () {
- const ctx = yield* InstanceState.context
- return {
- home: Global.Path.home,
- state: Global.Path.state,
- config: Global.Path.config,
- worktree: ctx.worktree,
- directory: ctx.directory,
- }
- })
+ const getPath = Effect.fn("InstanceHttpApi.path")(function* () {
+ const ctx = yield* InstanceState.context
+ return {
+ home: Global.Path.home,
+ state: Global.Path.state,
+ config: Global.Path.config,
+ worktree: ctx.worktree,
+ directory: ctx.directory,
+ }
+ })
- const getVcs = Effect.fn("InstanceHttpApi.vcs")(function* () {
- const [branch, default_branch] = yield* Effect.all([vcs.branch(), vcs.defaultBranch()], { concurrency: 2 })
- return { branch, default_branch }
- })
+ const getVcs = Effect.fn("InstanceHttpApi.vcs")(function* () {
+ const [branch, default_branch] = yield* Effect.all([vcs.branch(), vcs.defaultBranch()], { concurrency: 2 })
+ return { branch, default_branch }
+ })
- const getVcsDiff = Effect.fn("InstanceHttpApi.vcsDiff")(function* (ctx: { query: { mode: Vcs.Mode } }) {
- return yield* vcs.diff(ctx.query.mode)
- })
+ const getVcsDiff = Effect.fn("InstanceHttpApi.vcsDiff")(function* (ctx: { query: { mode: Vcs.Mode } }) {
+ return yield* vcs.diff(ctx.query.mode)
+ })
- const getCommand = Effect.fn("InstanceHttpApi.command")(function* () {
- return yield* command.list()
- })
+ const getCommand = Effect.fn("InstanceHttpApi.command")(function* () {
+ return yield* command.list()
+ })
- const getAgent = Effect.fn("InstanceHttpApi.agent")(function* () {
- return yield* agent.list()
- })
+ const getAgent = Effect.fn("InstanceHttpApi.agent")(function* () {
+ return yield* agent.list()
+ })
- const getSkill = Effect.fn("InstanceHttpApi.skill")(function* () {
- return yield* skill.all()
- })
+ const getSkill = Effect.fn("InstanceHttpApi.skill")(function* () {
+ return yield* skill.all()
+ })
- const getLsp = Effect.fn("InstanceHttpApi.lsp")(function* () {
- return yield* lsp.status()
- })
+ const getLsp = Effect.fn("InstanceHttpApi.lsp")(function* () {
+ return yield* lsp.status()
+ })
- const getFormatter = Effect.fn("InstanceHttpApi.formatter")(function* () {
- return yield* format.status()
- })
+ const getFormatter = Effect.fn("InstanceHttpApi.formatter")(function* () {
+ return yield* format.status()
+ })
- return handlers
- .handle("dispose", dispose)
- .handle("path", getPath)
- .handle("vcs", getVcs)
- .handle("vcsDiff", getVcsDiff)
- .handle("command", getCommand)
- .handle("agent", getAgent)
- .handle("skill", getSkill)
- .handle("lsp", getLsp)
- .handle("formatter", getFormatter)
- }),
+ return handlers
+ .handle("dispose", dispose)
+ .handle("path", getPath)
+ .handle("vcs", getVcs)
+ .handle("vcsDiff", getVcsDiff)
+ .handle("command", getCommand)
+ .handle("agent", getAgent)
+ .handle("skill", getSkill)
+ .handle("lsp", getLsp)
+ .handle("formatter", getFormatter)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/mcp.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/mcp.ts
index b4d27d91d..a02f2425c 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/mcp.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/mcp.ts
@@ -5,64 +5,64 @@ import { InstanceHttpApi } from "../api"
import { AddPayload, AuthCallbackPayload, StatusMap, UnsupportedOAuthError } from "../groups/mcp"
export const mcpHandlers = HttpApiBuilder.group(InstanceHttpApi, "mcp", (handlers) =>
- Effect.gen(function* () {
- const mcp = yield* MCP.Service
+ Effect.gen(function* () {
+ const mcp = yield* MCP.Service
- const status = Effect.fn("McpHttpApi.status")(function* () {
- return yield* mcp.status()
- })
+ const status = Effect.fn("McpHttpApi.status")(function* () {
+ return yield* mcp.status()
+ })
- const add = Effect.fn("McpHttpApi.add")(function* (ctx: { payload: typeof AddPayload.Type }) {
- const result = (yield* mcp.add(ctx.payload.name, ctx.payload.config)).status
- return yield* Schema.decodeUnknownEffect(StatusMap)(
- "status" in result ? { [ctx.payload.name]: result } : result,
- ).pipe(Effect.mapError(() => new HttpApiError.BadRequest({})))
- })
+ const add = Effect.fn("McpHttpApi.add")(function* (ctx: { payload: typeof AddPayload.Type }) {
+ const result = (yield* mcp.add(ctx.payload.name, ctx.payload.config)).status
+ return yield* Schema.decodeUnknownEffect(StatusMap)(
+ "status" in result ? { [ctx.payload.name]: result } : result,
+ ).pipe(Effect.mapError(() => new HttpApiError.BadRequest({})))
+ })
- const authStart = Effect.fn("McpHttpApi.authStart")(function* (ctx: { params: { name: string } }) {
- if (!(yield* mcp.supportsOAuth(ctx.params.name))) {
- return yield* new UnsupportedOAuthError({ error: `MCP server ${ctx.params.name} does not support OAuth` })
- }
- return yield* mcp.startAuth(ctx.params.name)
- })
+ const authStart = Effect.fn("McpHttpApi.authStart")(function* (ctx: { params: { name: string } }) {
+ if (!(yield* mcp.supportsOAuth(ctx.params.name))) {
+ return yield* new UnsupportedOAuthError({ error: `MCP server ${ctx.params.name} does not support OAuth` })
+ }
+ return yield* mcp.startAuth(ctx.params.name)
+ })
- const authCallback = Effect.fn("McpHttpApi.authCallback")(function* (ctx: {
- params: { name: string }
- payload: typeof AuthCallbackPayload.Type
- }) {
- return yield* mcp.finishAuth(ctx.params.name, ctx.payload.code)
- })
+ const authCallback = Effect.fn("McpHttpApi.authCallback")(function* (ctx: {
+ params: { name: string }
+ payload: typeof AuthCallbackPayload.Type
+ }) {
+ return yield* mcp.finishAuth(ctx.params.name, ctx.payload.code)
+ })
- const authAuthenticate = Effect.fn("McpHttpApi.authAuthenticate")(function* (ctx: { params: { name: string } }) {
- if (!(yield* mcp.supportsOAuth(ctx.params.name))) {
- return yield* new UnsupportedOAuthError({ error: `MCP server ${ctx.params.name} does not support OAuth` })
- }
- return yield* mcp.authenticate(ctx.params.name)
- })
+ const authAuthenticate = Effect.fn("McpHttpApi.authAuthenticate")(function* (ctx: { params: { name: string } }) {
+ if (!(yield* mcp.supportsOAuth(ctx.params.name))) {
+ return yield* new UnsupportedOAuthError({ error: `MCP server ${ctx.params.name} does not support OAuth` })
+ }
+ return yield* mcp.authenticate(ctx.params.name)
+ })
- const authRemove = Effect.fn("McpHttpApi.authRemove")(function* (ctx: { params: { name: string } }) {
- yield* mcp.removeAuth(ctx.params.name)
- return { success: true as const }
- })
+ const authRemove = Effect.fn("McpHttpApi.authRemove")(function* (ctx: { params: { name: string } }) {
+ yield* mcp.removeAuth(ctx.params.name)
+ return { success: true as const }
+ })
- const connect = Effect.fn("McpHttpApi.connect")(function* (ctx: { params: { name: string } }) {
- yield* mcp.connect(ctx.params.name)
- return true
- })
+ const connect = Effect.fn("McpHttpApi.connect")(function* (ctx: { params: { name: string } }) {
+ yield* mcp.connect(ctx.params.name)
+ return true
+ })
- const disconnect = Effect.fn("McpHttpApi.disconnect")(function* (ctx: { params: { name: string } }) {
- yield* mcp.disconnect(ctx.params.name)
- return true
- })
+ const disconnect = Effect.fn("McpHttpApi.disconnect")(function* (ctx: { params: { name: string } }) {
+ yield* mcp.disconnect(ctx.params.name)
+ return true
+ })
- return handlers
- .handle("status", status)
- .handle("add", add)
- .handle("authStart", authStart)
- .handle("authCallback", authCallback)
- .handle("authAuthenticate", authAuthenticate)
- .handle("authRemove", authRemove)
- .handle("connect", connect)
- .handle("disconnect", disconnect)
- }),
+ return handlers
+ .handle("status", status)
+ .handle("add", add)
+ .handle("authStart", authStart)
+ .handle("authCallback", authCallback)
+ .handle("authAuthenticate", authAuthenticate)
+ .handle("authRemove", authRemove)
+ .handle("connect", connect)
+ .handle("disconnect", disconnect)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/permission.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/permission.ts
index a5d6dab89..2a7b6195d 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/permission.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/permission.ts
@@ -5,25 +5,25 @@ import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../api"
export const permissionHandlers = HttpApiBuilder.group(InstanceHttpApi, "permission", (handlers) =>
- Effect.gen(function* () {
- const svc = yield* Permission.Service
+ Effect.gen(function* () {
+ const svc = yield* Permission.Service
- const list = Effect.fn("PermissionHttpApi.list")(function* () {
- return yield* svc.list()
- })
+ const list = Effect.fn("PermissionHttpApi.list")(function* () {
+ return yield* svc.list()
+ })
- const reply = Effect.fn("PermissionHttpApi.reply")(function* (ctx: {
- params: { requestID: PermissionID }
- payload: Permission.ReplyBody
- }) {
- yield* svc.reply({
- requestID: ctx.params.requestID,
- reply: ctx.payload.reply,
- message: ctx.payload.message,
- })
- return true
+ const reply = Effect.fn("PermissionHttpApi.reply")(function* (ctx: {
+ params: { requestID: PermissionID }
+ payload: Permission.ReplyBody
+ }) {
+ yield* svc.reply({
+ requestID: ctx.params.requestID,
+ reply: ctx.payload.reply,
+ message: ctx.payload.message,
})
+ return true
+ })
- return handlers.handle("list", list).handle("reply", reply)
- }),
+ return handlers.handle("list", list).handle("reply", reply)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/project.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/project.ts
index 20a5ddfb0..ae2761ac3 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/project.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/project.ts
@@ -9,38 +9,38 @@ import { InstanceHttpApi } from "../api"
import { markInstanceForReload } from "../lifecycle"
export const projectHandlers = HttpApiBuilder.group(InstanceHttpApi, "project", (handlers) =>
- Effect.gen(function* () {
- const svc = yield* Project.Service
+ Effect.gen(function* () {
+ const svc = yield* Project.Service
- const list = Effect.fn("ProjectHttpApi.list")(function* () {
- return yield* svc.list()
- })
+ const list = Effect.fn("ProjectHttpApi.list")(function* () {
+ return yield* svc.list()
+ })
- const current = Effect.fn("ProjectHttpApi.current")(function* () {
- return (yield* InstanceState.context).project
- })
+ const current = Effect.fn("ProjectHttpApi.current")(function* () {
+ return (yield* InstanceState.context).project
+ })
- const initGit = Effect.fn("ProjectHttpApi.initGit")(function* () {
- const ctx = yield* InstanceState.context
- const next = yield* svc.initGit({ directory: ctx.directory, project: ctx.project })
- if (next.id === ctx.project.id && next.vcs === ctx.project.vcs && next.worktree === ctx.project.worktree)
- return next
- yield* markInstanceForReload(ctx, {
- directory: ctx.directory,
- worktree: ctx.directory,
- project: next,
- init: () => AppRuntime.runPromise(InstanceBootstrap),
- })
+ const initGit = Effect.fn("ProjectHttpApi.initGit")(function* () {
+ const ctx = yield* InstanceState.context
+ const next = yield* svc.initGit({ directory: ctx.directory, project: ctx.project })
+ if (next.id === ctx.project.id && next.vcs === ctx.project.vcs && next.worktree === ctx.project.worktree)
return next
+ yield* markInstanceForReload(ctx, {
+ directory: ctx.directory,
+ worktree: ctx.directory,
+ project: next,
+ init: () => AppRuntime.runPromise(InstanceBootstrap),
})
+ return next
+ })
- const update = Effect.fn("ProjectHttpApi.update")(function* (ctx: {
- params: { projectID: ProjectID }
- payload: Project.UpdatePayload
- }) {
- return yield* svc.update({ ...ctx.payload, projectID: ctx.params.projectID })
- })
+ const update = Effect.fn("ProjectHttpApi.update")(function* (ctx: {
+ params: { projectID: ProjectID }
+ payload: Project.UpdatePayload
+ }) {
+ return yield* svc.update({ ...ctx.payload, projectID: ctx.params.projectID })
+ })
- return handlers.handle("list", list).handle("current", current).handle("initGit", initGit).handle("update", update)
- }),
+ return handlers.handle("list", list).handle("current", current).handle("initGit", initGit).handle("update", update)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts
index f343829d6..c8689eaba 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts
@@ -10,80 +10,80 @@ import { HttpApiBuilder, HttpApiError } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../api"
export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider", (handlers) =>
- Effect.gen(function* () {
- const cfg = yield* Config.Service
- const provider = yield* Provider.Service
- const svc = yield* ProviderAuth.Service
+ Effect.gen(function* () {
+ const cfg = yield* Config.Service
+ const provider = yield* Provider.Service
+ const svc = yield* ProviderAuth.Service
- const list = Effect.fn("ProviderHttpApi.list")(function* () {
- const config = yield* cfg.get()
- const all = yield* Effect.promise(() => ModelsDev.get())
- const disabled = new Set(config.disabled_providers ?? [])
- const enabled = config.enabled_providers ? new Set(config.enabled_providers) : undefined
- const filtered: Record<string, (typeof all)[string]> = {}
- for (const [key, value] of Object.entries(all)) {
- if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) filtered[key] = value
- }
- const connected = yield* provider.list()
- const providers = Object.assign(
- mapValues(filtered, (item) => Provider.fromModelsDevProvider(item)),
- connected,
- )
- return {
- all: Object.values(providers),
- default: Provider.defaultModelIDs(providers),
- connected: Object.keys(connected),
- }
- })
+ const list = Effect.fn("ProviderHttpApi.list")(function* () {
+ const config = yield* cfg.get()
+ const all = yield* Effect.promise(() => ModelsDev.get())
+ const disabled = new Set(config.disabled_providers ?? [])
+ const enabled = config.enabled_providers ? new Set(config.enabled_providers) : undefined
+ const filtered: Record<string, (typeof all)[string]> = {}
+ for (const [key, value] of Object.entries(all)) {
+ if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) filtered[key] = value
+ }
+ const connected = yield* provider.list()
+ const providers = Object.assign(
+ mapValues(filtered, (item) => Provider.fromModelsDevProvider(item)),
+ connected,
+ )
+ return {
+ all: Object.values(providers),
+ default: Provider.defaultModelIDs(providers),
+ connected: Object.keys(connected),
+ }
+ })
- const auth = Effect.fn("ProviderHttpApi.auth")(function* () {
- return yield* svc.methods()
- })
+ const auth = Effect.fn("ProviderHttpApi.auth")(function* () {
+ return yield* svc.methods()
+ })
- const authorize = Effect.fn("ProviderHttpApi.authorize")(function* (ctx: {
- params: { providerID: ProviderID }
- payload: ProviderAuth.AuthorizeInput
- }) {
- return yield* svc
- .authorize({
- providerID: ctx.params.providerID,
- method: ctx.payload.method,
- inputs: ctx.payload.inputs,
- })
- .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
- })
+ const authorize = Effect.fn("ProviderHttpApi.authorize")(function* (ctx: {
+ params: { providerID: ProviderID }
+ payload: ProviderAuth.AuthorizeInput
+ }) {
+ return yield* svc
+ .authorize({
+ providerID: ctx.params.providerID,
+ method: ctx.payload.method,
+ inputs: ctx.payload.inputs,
+ })
+ .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
+ })
- const authorizeRaw = Effect.fn("ProviderHttpApi.authorizeRaw")(function* (ctx: {
- params: { providerID: ProviderID }
- request: HttpServerRequest.HttpServerRequest
- }) {
- const body = yield* Effect.orDie(ctx.request.text)
- const payload = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(ProviderAuth.AuthorizeInput))(body).pipe(
- Effect.mapError(() => new HttpApiError.BadRequest({})),
- )
- const result = yield* authorize({ params: ctx.params, payload })
- if (result === undefined) return HttpServerResponse.empty({ status: 200 })
- return HttpServerResponse.jsonUnsafe(result)
- })
+ const authorizeRaw = Effect.fn("ProviderHttpApi.authorizeRaw")(function* (ctx: {
+ params: { providerID: ProviderID }
+ request: HttpServerRequest.HttpServerRequest
+ }) {
+ const body = yield* Effect.orDie(ctx.request.text)
+ const payload = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(ProviderAuth.AuthorizeInput))(body).pipe(
+ Effect.mapError(() => new HttpApiError.BadRequest({})),
+ )
+ const result = yield* authorize({ params: ctx.params, payload })
+ if (result === undefined) return HttpServerResponse.empty({ status: 200 })
+ return HttpServerResponse.jsonUnsafe(result)
+ })
- const callback = Effect.fn("ProviderHttpApi.callback")(function* (ctx: {
- params: { providerID: ProviderID }
- payload: ProviderAuth.CallbackInput
- }) {
- yield* svc
- .callback({
- providerID: ctx.params.providerID,
- method: ctx.payload.method,
- code: ctx.payload.code,
- })
- .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
- return true
- })
+ const callback = Effect.fn("ProviderHttpApi.callback")(function* (ctx: {
+ params: { providerID: ProviderID }
+ payload: ProviderAuth.CallbackInput
+ }) {
+ yield* svc
+ .callback({
+ providerID: ctx.params.providerID,
+ method: ctx.payload.method,
+ code: ctx.payload.code,
+ })
+ .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
+ return true
+ })
- return handlers
- .handle("list", list)
- .handle("auth", auth)
- .handleRaw("authorize", authorizeRaw)
- .handle("callback", callback)
- }),
+ return handlers
+ .handle("list", list)
+ .handle("auth", auth)
+ .handleRaw("authorize", authorizeRaw)
+ .handle("callback", callback)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/pty.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/pty.ts
index f2f17d471..8558ee793 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/pty.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/pty.ts
@@ -88,7 +88,9 @@ export const ptyConnectRoute = HttpRouter.add(
},
send: (data: string | Uint8Array | ArrayBuffer) => {
if (closed) return
- Effect.runFork(write(data instanceof ArrayBuffer ? new Uint8Array(data) : data).pipe(Effect.catch(() => Effect.void)))
+ Effect.runFork(
+ write(data instanceof ArrayBuffer ? new Uint8Array(data) : data).pipe(Effect.catch(() => Effect.void)),
+ )
},
close: (code?: number, reason?: string) => {
if (closed) return
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/question.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/question.ts
index 53ca568cf..3a4d31617 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/question.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/question.ts
@@ -5,29 +5,29 @@ import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../api"
export const questionHandlers = HttpApiBuilder.group(InstanceHttpApi, "question", (handlers) =>
- Effect.gen(function* () {
- const svc = yield* Question.Service
+ Effect.gen(function* () {
+ const svc = yield* Question.Service
- const list = Effect.fn("QuestionHttpApi.list")(function* () {
- return yield* svc.list()
- })
+ const list = Effect.fn("QuestionHttpApi.list")(function* () {
+ return yield* svc.list()
+ })
- const reply = Effect.fn("QuestionHttpApi.reply")(function* (ctx: {
- params: { requestID: QuestionID }
- payload: Question.Reply
- }) {
- yield* svc.reply({
- requestID: ctx.params.requestID,
- answers: ctx.payload.answers,
- })
- return true
+ const reply = Effect.fn("QuestionHttpApi.reply")(function* (ctx: {
+ params: { requestID: QuestionID }
+ payload: Question.Reply
+ }) {
+ yield* svc.reply({
+ requestID: ctx.params.requestID,
+ answers: ctx.payload.answers,
})
+ return true
+ })
- const reject = Effect.fn("QuestionHttpApi.reject")(function* (ctx: { params: { requestID: QuestionID } }) {
- yield* svc.reject(ctx.params.requestID)
- return true
- })
+ const reject = Effect.fn("QuestionHttpApi.reject")(function* (ctx: { params: { requestID: QuestionID } }) {
+ yield* svc.reject(ctx.params.requestID)
+ return true
+ })
- return handlers.handle("list", list).handle("reply", reply).handle("reject", reject)
- }),
+ return handlers.handle("list", list).handle("reply", reply).handle("reject", reject)
+ }),
)
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts
index d6264b605..65c90b952 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts
@@ -25,7 +25,20 @@ import * as Stream from "effect/Stream"
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
import { HttpApiBuilder, HttpApiError, HttpApiSchema } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../api"
-import { CommandPayload, DiffQuery, ForkPayload, InitPayload, ListQuery, MessagesQuery, PermissionResponsePayload, PromptPayload, RevertPayload, ShellPayload, SummarizePayload, UpdatePayload } from "../groups/session"
+import {
+ CommandPayload,
+ DiffQuery,
+ ForkPayload,
+ InitPayload,
+ ListQuery,
+ MessagesQuery,
+ PermissionResponsePayload,
+ PromptPayload,
+ RevertPayload,
+ ShellPayload,
+ SummarizePayload,
+ UpdatePayload,
+} from "../groups/session"
const log = Log.create({ service: "server" })
@@ -88,40 +101,42 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session",
params: { sessionID: SessionID }
query: typeof MessagesQuery.Type
}) {
- return yield* mapNotFound(Effect.gen(function* () {
- if (ctx.query.before && ctx.query.limit === undefined) return yield* new HttpApiError.BadRequest({})
- if (ctx.query.before) {
- const before = ctx.query.before
- yield* Effect.try({
- try: () => MessageV2.cursor.decode(before),
- catch: () => new HttpApiError.BadRequest({}),
- })
- }
- if (ctx.query.limit === undefined || ctx.query.limit === 0) {
+ return yield* mapNotFound(
+ Effect.gen(function* () {
+ if (ctx.query.before && ctx.query.limit === undefined) return yield* new HttpApiError.BadRequest({})
+ if (ctx.query.before) {
+ const before = ctx.query.before
+ yield* Effect.try({
+ try: () => MessageV2.cursor.decode(before),
+ catch: () => new HttpApiError.BadRequest({}),
+ })
+ }
+ if (ctx.query.limit === undefined || ctx.query.limit === 0) {
+ yield* session.get(ctx.params.sessionID)
+ return yield* session.messages({ sessionID: ctx.params.sessionID })
+ }
+
yield* session.get(ctx.params.sessionID)
- return yield* session.messages({ sessionID: ctx.params.sessionID })
- }
-
- yield* session.get(ctx.params.sessionID)
- const page = MessageV2.page({
- sessionID: ctx.params.sessionID,
- limit: ctx.query.limit,
- before: ctx.query.before,
- })
- if (!page.cursor) return page.items
-
- const request = yield* HttpServerRequest.HttpServerRequest
- const url = new URL(request.url, "http://localhost")
- url.searchParams.set("limit", ctx.query.limit.toString())
- url.searchParams.set("before", page.cursor)
- return HttpServerResponse.jsonUnsafe(page.items, {
- headers: {
- "Access-Control-Expose-Headers": "Link, X-Next-Cursor",
- Link: `<${url.toString()}>; rel="next"`,
- "X-Next-Cursor": page.cursor,
- },
- })
- }))
+ const page = MessageV2.page({
+ sessionID: ctx.params.sessionID,
+ limit: ctx.query.limit,
+ before: ctx.query.before,
+ })
+ if (!page.cursor) return page.items
+
+ const request = yield* HttpServerRequest.HttpServerRequest
+ const url = new URL(request.url, "http://localhost")
+ url.searchParams.set("limit", ctx.query.limit.toString())
+ url.searchParams.set("before", page.cursor)
+ return HttpServerResponse.jsonUnsafe(page.items, {
+ headers: {
+ "Access-Control-Expose-Headers": "Link, X-Next-Cursor",
+ Link: `<${url.toString()}>; rel="next"`,
+ "X-Next-Cursor": page.cursor,
+ },
+ })
+ }),
+ )
})
const message = Effect.fn("SessionHttpApi.message")(function* (ctx: {
diff --git a/packages/opencode/src/server/routes/instance/httpapi/instance-context.ts b/packages/opencode/src/server/routes/instance/httpapi/instance-context.ts
index 1ad42c526..42e973020 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/instance-context.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/instance-context.ts
@@ -19,11 +19,12 @@ import * as Socket from "effect/unstable/socket/Socket"
type HandlerEffect = Effect.Effect<HttpServerResponse.HttpServerResponse, unhandled, never>
-export class InstanceContextMiddleware extends HttpApiMiddleware.Service<InstanceContextMiddleware, {
- requires: Session.Service
-}>()(
- "@opencode/ExperimentalHttpApiInstanceContext",
-) {}
+export class InstanceContextMiddleware extends HttpApiMiddleware.Service<
+ InstanceContextMiddleware,
+ {
+ requires: Session.Service
+ }
+>()("@opencode/ExperimentalHttpApiInstanceContext") {}
function decode(input: string) {
try {
@@ -53,9 +54,14 @@ function requestHeaders(request: HttpServerRequest.HttpServerRequest) {
return sourceRequest(request).headers
}
-function writeSocket(write: (data: string | Uint8Array | Socket.CloseEvent) => Effect.Effect<void, unknown>, data: unknown) {
+function writeSocket(
+ write: (data: string | Uint8Array | Socket.CloseEvent) => Effect.Effect<void, unknown>,
+ data: unknown,
+) {
if (data instanceof Blob) {
- void data.arrayBuffer().then((buffer) => Effect.runFork(write(new Uint8Array(buffer)).pipe(Effect.catch(() => Effect.void))))
+ void data
+ .arrayBuffer()
+ .then((buffer) => Effect.runFork(write(new Uint8Array(buffer)).pipe(Effect.catch(() => Effect.void))))
return
}
if (typeof data === "string" || data instanceof Uint8Array) {
@@ -78,7 +84,8 @@ function proxyWebSocket(request: HttpServerRequest.HttpServerRequest, target: st
queue.length = 0
}
remote.onmessage = (event) => writeSocket(write, event.data)
- remote.onerror = () => Effect.runFork(write(new Socket.CloseEvent(1011, "proxy error")).pipe(Effect.catch(() => Effect.void)))
+ remote.onerror = () =>
+ Effect.runFork(write(new Socket.CloseEvent(1011, "proxy error")).pipe(Effect.catch(() => Effect.void)))
remote.onclose = (event) =>
Effect.runFork(write(new Socket.CloseEvent(event.code, event.reason)).pipe(Effect.catch(() => Effect.void)))
@@ -109,7 +116,9 @@ function proxyRemote(
const url = workspaceProxyURL(target.url, requestURL)
const source = sourceRequest(request)
if (source.headers.get("upgrade")?.toLowerCase() === "websocket") return proxyWebSocket(request, url)
- return Effect.promise(() => ServerProxy.http(url, target.headers, source, workspace.id)).pipe(Effect.map(HttpServerResponse.raw))
+ return Effect.promise(() => ServerProxy.http(url, target.headers, source, workspace.id)).pipe(
+ Effect.map(HttpServerResponse.raw),
+ )
}
function requestContext() {
@@ -118,14 +127,19 @@ function requestContext() {
)
}
-function provideRequestContext(effect: HandlerEffect, request: HttpServerRequest.HttpServerRequest, sessionWorkspaceID?: WorkspaceID) {
+function provideRequestContext(
+ effect: HandlerEffect,
+ request: HttpServerRequest.HttpServerRequest,
+ sessionWorkspaceID?: WorkspaceID,
+) {
return Effect.gen(function* () {
const url = new URL(request.url, "http://localhost")
const headers = requestHeaders(request)
const envWorkspaceID = Flag.OPENCODE_WORKSPACE_ID ? WorkspaceID.make(Flag.OPENCODE_WORKSPACE_ID) : undefined
const workspaceParam = url.searchParams.get("workspace")
const workspaceID = sessionWorkspaceID ?? (workspaceParam ? WorkspaceID.make(workspaceParam) : undefined)
- const workspace = workspaceID && !envWorkspaceID ? yield* Effect.promise(() => Workspace.get(workspaceID)) : undefined
+ const workspace =
+ workspaceID && !envWorkspaceID ? yield* Effect.promise(() => Workspace.get(workspaceID)) : undefined
if (workspaceID && !workspace && !envWorkspaceID) {
return HttpServerResponse.text(`Workspace not found: ${workspaceID}`, {
@@ -134,7 +148,12 @@ function provideRequestContext(effect: HandlerEffect, request: HttpServerRequest
})
}
- if (workspace && !isLocalWorkspaceRoute(request.method, url.pathname) && !url.pathname.startsWith("/console") && !envWorkspaceID) {
+ if (
+ workspace &&
+ !isLocalWorkspaceRoute(request.method, url.pathname) &&
+ !url.pathname.startsWith("/console") &&
+ !envWorkspaceID
+ ) {
const adaptor = yield* Effect.promise(() => getAdaptor(workspace.projectID, workspace.type))
const target = yield* Effect.promise(() => Promise.resolve(adaptor.target(workspace)))
if (target.type === "remote") return yield* proxyRemote(request, workspace, target, url)
@@ -186,6 +205,8 @@ export const instanceContextLayer = Layer.succeed(
InstanceContextMiddleware.of((effect) => provideInstanceContext(effect)),
)
-export const instanceRouterLayer = HttpRouter.middleware()(Effect.succeed((effect) =>
- requestContext().pipe(Effect.flatMap((request) => provideRequestContext(effect, request))),
-)).layer
+export const instanceRouterLayer = HttpRouter.middleware()(
+ Effect.succeed((effect) =>
+ requestContext().pipe(Effect.flatMap((request) => provideRequestContext(effect, request))),
+ ),
+).layer
diff --git a/packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts b/packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts
index e1c03e7bd..c93261a0b 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts
@@ -14,7 +14,10 @@ export const markInstanceForDisposal = (ctx: InstanceContext) =>
export const markInstanceForReload = (ctx: InstanceContext, next: Parameters<typeof Instance.reload>[0]) =>
HttpEffect.appendPreResponseHandler((_request, response) =>
- Effect.as(Effect.uninterruptible(Effect.promise(() => Instance.restore(ctx, () => Instance.reload(next)))), response),
+ Effect.as(
+ Effect.uninterruptible(Effect.promise(() => Instance.restore(ctx, () => Instance.reload(next)))),
+ response,
+ ),
)
export const disposeMiddleware: HttpMiddleware.HttpMiddleware = (effect) =>
diff --git a/packages/opencode/src/server/routes/instance/httpapi/public.ts b/packages/opencode/src/server/routes/instance/httpapi/public.ts
index d9871c69b..17d6e0d06 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/public.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/public.ts
@@ -126,8 +126,13 @@ function matchLegacyOpenApi(input: Record<string, unknown>) {
// Workspace creation fields `branch` and `extra` are Schema.NullOr —
// genuinely nullable, not just optional. Re-add the null that the
// component-level strip above removed.
- const ref = operation.requestBody.content?.["application/json"]?.schema?.$ref?.replace("#/components/schemas/", "")
- const properties = ref ? spec.components?.schemas?.[ref]?.properties : operation.requestBody.content?.["application/json"]?.schema?.properties
+ const ref = operation.requestBody.content?.["application/json"]?.schema?.$ref?.replace(
+ "#/components/schemas/",
+ "",
+ )
+ const properties = ref
+ ? spec.components?.schemas?.[ref]?.properties
+ : operation.requestBody.content?.["application/json"]?.schema?.properties
if (properties?.branch) properties.branch = { anyOf: [properties.branch, { type: "null" }] }
if (properties?.extra) properties.extra = { anyOf: [properties.extra, { type: "null" }] }
}
@@ -150,7 +155,10 @@ function matchLegacyOpenApi(input: Record<string, unknown>) {
description: "Event stream",
content: {
"text/event-stream": {
- schema: path === "/event" ? { $ref: "#/components/schemas/Event" } : { $ref: "#/components/schemas/GlobalEvent" },
+ schema:
+ path === "/event"
+ ? { $ref: "#/components/schemas/Event" }
+ : { $ref: "#/components/schemas/GlobalEvent" },
},
},
}
@@ -251,7 +259,8 @@ function applyLegacySchemaOverrides(spec: OpenApiSpec) {
schemas.Workspace.properties.directory = nullable(schemas.Workspace.properties.directory)
schemas.Workspace.properties.extra = nullable(schemas.Workspace.properties.extra)
}
- if (schemas.GlobalSession?.properties?.project) schemas.GlobalSession.properties.project = nullable(schemas.GlobalSession.properties.project)
+ if (schemas.GlobalSession?.properties?.project)
+ schemas.GlobalSession.properties.project = nullable(schemas.GlobalSession.properties.project)
const providerOptions = schemas.ProviderConfig?.properties?.options
if (providerOptions) providerOptions.additionalProperties = {}
const model = schemas.ProviderConfig?.properties?.models?.additionalProperties
@@ -486,12 +495,11 @@ function normalizeParameter(param: OpenApiParameter, route: string) {
param.schema = stripOptionalNull(param.schema)
}
-export const PublicApi = OpenCodeHttpApi
- .annotateMerge(
- OpenApi.annotations({
- title: "opencode",
- version: "1.0.0",
- description: "opencode api",
- transform: matchLegacyOpenApi,
- }),
- )
+export const PublicApi = OpenCodeHttpApi.annotateMerge(
+ OpenApi.annotations({
+ title: "opencode",
+ version: "1.0.0",
+ description: "opencode api",
+ transform: matchLegacyOpenApi,
+ }),
+)
diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts
index 40e709edd..e4aeda798 100644
--- a/packages/opencode/src/server/server.ts
+++ b/packages/opencode/src/server/server.ts
@@ -38,7 +38,9 @@ type ServerApp = {
request(input: string | URL | Request, init?: RequestInit): Response | Promise<Response>
}
-const DefaultHono = lazy(() => withBackend({ backend: "hono", reason: "stable" }, createHono({}, { backend: "hono", reason: "stable" })))
+const DefaultHono = lazy(() =>
+ withBackend({ backend: "hono", reason: "stable" }, createHono({}, { backend: "hono", reason: "stable" })),
+)
const DefaultHttpApi = lazy(() => createDefaultHttpApi())
function select() {
@@ -86,7 +88,10 @@ function createHttpApi() {
}
}
-function createHono(opts: { cors?: string[] }, selection: ServerBackend.Selection = ServerBackend.force(select(), "hono")) {
+function createHono(
+ opts: { cors?: string[] },
+ selection: ServerBackend.Selection = ServerBackend.force(select(), "hono"),
+) {
const backendAttributes = ServerBackend.attributes(selection)
const app = new Hono()
.onError(ErrorMiddleware)
diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts
index b1a6ff403..911f58efd 100644
--- a/packages/opencode/src/session/message-v2.ts
+++ b/packages/opencode/src/session/message-v2.ts
@@ -461,7 +461,9 @@ type AssistantError = z.infer<typeof AssistantErrorZod>
// Effect Schema for the same union — used by HttpApi OpenAPI generation.
const AssistantErrorSchema = Schema.Union([
AuthError.EffectSchema,
- Schema.Struct({ name: Schema.Literal("UnknownError"), data: Schema.Struct({ message: Schema.String }) }).annotate({ identifier: "UnknownError" }),
+ Schema.Struct({ name: Schema.Literal("UnknownError"), data: Schema.Struct({ message: Schema.String }) }).annotate({
+ identifier: "UnknownError",
+ }),
OutputLengthError.EffectSchema,
AbortedError.EffectSchema,
StructuredOutputError.EffectSchema,
diff --git a/packages/opencode/src/util/schema.ts b/packages/opencode/src/util/schema.ts
index 380225316..2a6c02349 100644
--- a/packages/opencode/src/util/schema.ts
+++ b/packages/opencode/src/util/schema.ts
@@ -11,8 +11,6 @@ export const PositiveInt = Schema.Int.check(Schema.isGreaterThan(0))
*/
export const NonNegativeInt = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
-
-
/**
* Optional public JSON field that can hold explicit `undefined` on the type
* side but encodes it as an omitted key, matching legacy `JSON.stringify`.
diff --git a/packages/opencode/test/server/httpapi-bridge.test.ts b/packages/opencode/test/server/httpapi-bridge.test.ts
index a0324cce3..5847192cb 100644
--- a/packages/opencode/test/server/httpapi-bridge.test.ts
+++ b/packages/opencode/test/server/httpapi-bridge.test.ts
@@ -126,9 +126,9 @@ function requestBodyKey(spec: OpenApiSpec, body: unknown) {
function requestBodySchemaKind(spec: OpenApiSpec, schema: OpenApiSchema | undefined) {
if (!schema) return ""
- const resolved = (schema.$ref ? spec.components?.schemas?.[schema.$ref.replace("#/components/schemas/", "")] : schema) as
- | OpenApiSchema
- | undefined
+ const resolved = (
+ schema.$ref ? spec.components?.schemas?.[schema.$ref.replace("#/components/schemas/", "")] : schema
+ ) as OpenApiSchema | undefined
if (resolved?.properties) return "object"
if (resolved?.anyOf ?? resolved?.oneOf ?? resolved?.allOf) return "object"
return resolved?.type ?? schema.type ?? "inline"
diff --git a/packages/opencode/test/server/httpapi-workspace.test.ts b/packages/opencode/test/server/httpapi-workspace.test.ts
index f43010571..5ce531dcc 100644
--- a/packages/opencode/test/server/httpapi-workspace.test.ts
+++ b/packages/opencode/test/server/httpapi-workspace.test.ts
@@ -214,7 +214,11 @@ describe("workspace HttpApi", () => {
const workspace = await Instance.provide({
directory: tmp.path,
fn: async () => {
- registerAdaptor(Instance.project.id, "remote-target", remoteAdaptor(path.join(tmp.path, ".remote"), "https://remote.test/base"))
+ registerAdaptor(
+ Instance.project.id,
+ "remote-target",
+ remoteAdaptor(path.join(tmp.path, ".remote"), "https://remote.test/base"),
+ )
return Workspace.create({
type: "remote-target",
branch: null,
diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json
index d79103df6..bb6c74b83 100644
--- a/packages/sdk/openapi.json
+++ b/packages/sdk/openapi.json
@@ -1339,10 +1339,14 @@
"type": "object",
"properties": {
"rows": {
- "type": "number"
+ "type": "integer",
+ "exclusiveMinimum": 0,
+ "maximum": 9007199254740991
},
"cols": {
- "type": "number"
+ "type": "integer",
+ "exclusiveMinimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["rows", "cols"]
@@ -5595,10 +5599,14 @@
"required": ["text"]
},
"line_number": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"absolute_offset": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"submatches": {
"type": "array",
@@ -5615,10 +5623,14 @@
"required": ["text"]
},
"start": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"end": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["match", "start", "end"]
@@ -6901,7 +6913,9 @@
},
"duration": {
"description": "Duration in milliseconds",
- "type": "number"
+ "type": "integer",
+ "exclusiveMinimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["message", "variant"]
@@ -7621,13 +7635,19 @@
"type": "object",
"properties": {
"created": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"updated": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"initialized": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["created", "updated"]
@@ -7913,10 +7933,14 @@
"type": "string"
},
"additions": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"deletions": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"status": {
"type": "string",
@@ -8039,7 +8063,9 @@
"type": "string"
},
"retries": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["message", "retries"]
@@ -8083,7 +8109,9 @@
"type": "string"
},
"statusCode": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"isRetryable": {
"type": "boolean"
@@ -8420,13 +8448,17 @@
"const": "retry"
},
"attempt": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"message": {
"type": "string"
},
"next": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["type", "attempt", "message", "next"]
@@ -8591,7 +8623,9 @@
},
"duration": {
"description": "Duration in milliseconds",
- "type": "number"
+ "type": "integer",
+ "exclusiveMinimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["message", "variant"]
@@ -8777,7 +8811,9 @@
"enum": ["running", "exited"]
},
"pid": {
- "type": "number"
+ "type": "integer",
+ "exclusiveMinimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["id", "title", "command", "args", "cwd", "status", "pid"]
@@ -8835,7 +8871,9 @@
"pattern": "^pty.*"
},
"exitCode": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["id", "exitCode"]
@@ -9024,7 +9062,9 @@
"type": "object",
"properties": {
"created": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["created"]
@@ -9102,10 +9142,14 @@
"type": "object",
"properties": {
"created": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"completed": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["created"]
@@ -9173,25 +9217,37 @@
"type": "object",
"properties": {
"total": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"input": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"output": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"reasoning": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"cache": {
"type": "object",
"properties": {
"read": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"write": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["read", "write"]
@@ -9311,10 +9367,14 @@
"type": "object",
"properties": {
"start": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"end": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["start"]
@@ -9408,10 +9468,14 @@
"type": "object",
"properties": {
"start": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"end": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["start"]
@@ -9427,12 +9491,12 @@
},
"start": {
"type": "integer",
- "minimum": -9007199254740991,
+ "minimum": 0,
"maximum": 9007199254740991
},
"end": {
"type": "integer",
- "minimum": -9007199254740991,
+ "minimum": 0,
"maximum": 9007199254740991
}
},
@@ -9461,10 +9525,14 @@
"type": "object",
"properties": {
"line": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"character": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["line", "character"]
@@ -9473,10 +9541,14 @@
"type": "object",
"properties": {
"line": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"character": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["line", "character"]
@@ -9505,7 +9577,7 @@
},
"kind": {
"type": "integer",
- "minimum": -9007199254740991,
+ "minimum": 0,
"maximum": 9007199254740991
}
},
@@ -9625,7 +9697,9 @@
"type": "object",
"properties": {
"start": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["start"]
@@ -9664,13 +9738,19 @@
"type": "object",
"properties": {
"start": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"end": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"compacted": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["start", "end"]
@@ -9712,10 +9792,14 @@
"type": "object",
"properties": {
"start": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"end": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["start", "end"]
@@ -9834,25 +9918,37 @@
"type": "object",
"properties": {
"total": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"input": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"output": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"reasoning": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"cache": {
"type": "object",
"properties": {
"read": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"write": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["read", "write"]
@@ -9949,12 +10045,12 @@
},
"start": {
"type": "integer",
- "minimum": -9007199254740991,
+ "minimum": 0,
"maximum": 9007199254740991
},
"end": {
"type": "integer",
- "minimum": -9007199254740991,
+ "minimum": 0,
"maximum": 9007199254740991
}
},
@@ -9983,7 +10079,9 @@
"const": "retry"
},
"attempt": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"error": {
"$ref": "#/components/schemas/APIError"
@@ -9992,7 +10090,9 @@
"type": "object",
"properties": {
"created": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["created"]
@@ -10090,7 +10190,9 @@
"$ref": "#/components/schemas/Part"
},
"time": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["sessionID", "part", "time"]
@@ -10182,13 +10284,19 @@
"type": "object",
"properties": {
"additions": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"deletions": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"files": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"diffs": {
"type": "array",
@@ -10218,16 +10326,24 @@
"type": "object",
"properties": {
"created": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"updated": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"compacting": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"archived": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["created", "updated"]
@@ -10434,7 +10550,9 @@
"$ref": "#/components/schemas/Part"
},
"time": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["sessionID", "part", "time"]
@@ -10631,13 +10749,19 @@
"type": "object",
"properties": {
"additions": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"deletions": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"files": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"diffs": {
"type": "array",
@@ -10694,7 +10818,9 @@
"created": {
"anyOf": [
{
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
{
"type": "null"
@@ -10704,7 +10830,9 @@
"updated": {
"anyOf": [
{
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
{
"type": "null"
@@ -10714,7 +10842,9 @@
"compacting": {
"anyOf": [
{
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
{
"type": "null"
@@ -10724,7 +10854,9 @@
"archived": {
"anyOf": [
{
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
{
"type": "null"
@@ -11481,7 +11613,9 @@
},
"timeout": {
"description": "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.",
- "type": "number"
+ "type": "integer",
+ "exclusiveMinimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["type", "command"]
@@ -11547,7 +11681,9 @@
},
"timeout": {
"description": "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.",
- "type": "number"
+ "type": "integer",
+ "exclusiveMinimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["type", "url"]
@@ -12055,7 +12191,9 @@
"type": "string"
},
"expires": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"accountId": {
"type": "string"
@@ -12458,7 +12596,9 @@
"type": "string"
},
"switchableOrgCount": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["consoleManagedProviders", "switchableOrgCount"]
@@ -12579,13 +12719,19 @@
"type": "object",
"properties": {
"additions": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"deletions": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"files": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"diffs": {
"type": "array",
@@ -12615,16 +12761,24 @@
"type": "object",
"properties": {
"created": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"updated": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"compacting": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"archived": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["created", "updated"]
@@ -12710,10 +12864,14 @@
"type": "object",
"properties": {
"start": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"end": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
}
},
"required": ["start"]
@@ -12776,12 +12934,12 @@
},
"start": {
"type": "integer",
- "minimum": -9007199254740991,
+ "minimum": 0,
"maximum": 9007199254740991
},
"end": {
"type": "integer",
- "minimum": -9007199254740991,
+ "minimum": 0,
"maximum": 9007199254740991
}
},
@@ -12956,7 +13114,9 @@
"type": "string"
},
"kind": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"location": {
"type": "object",
@@ -13029,16 +13189,24 @@
"type": "object",
"properties": {
"oldStart": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"oldLines": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"newStart": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"newLines": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"lines": {
"type": "array",
@@ -13074,12 +13242,12 @@
},
"added": {
"type": "integer",
- "minimum": -9007199254740991,
+ "minimum": 0,
"maximum": 9007199254740991
},
"removed": {
"type": "integer",
- "minimum": -9007199254740991,
+ "minimum": 0,
"maximum": 9007199254740991
},
"status": {
@@ -13360,10 +13528,14 @@
"type": "string"
},
"additions": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"deletions": {
- "type": "number"
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 9007199254740991
},
"status": {
"type": "string",