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 e3914579c1..eb71526fb3 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 a5d31bfa62..49ba05c2d5 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 0305c65365..ab5f08bb17 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 2fc225d171..58aa81098c 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 abddd8c402..e1ede2274b 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 42eab762e8..cc958da303 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 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 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 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 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 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 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 resource = Effect.fn("ExperimentalHttpApi.resource")(function* () { + return yield* mcp.resources() + }) - 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 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 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 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() - }) - - 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 72133e8dea..98ee5968e0 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 + 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 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 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 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 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() + }) - 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) - }), + 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 5972395512..cd1bebec47 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 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 dispose = Effect.fn("GlobalHttpApi.dispose")(function* () { + yield* Effect.promise(() => Instance.disposeAll()) + GlobalBus.emit("event", { + directory: "global", + payload: { type: "global.disposed", properties: {} }, }) + return true + }) - const event = Effect.fn("GlobalHttpApi.event")(function* () { - return eventResponse() - }) - - 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 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 b6f3860652..c2a4503b48 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 b4d27d91de..a02f2425ce 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 a5d6dab895..2a7b6195df 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 20a5ddfb09..ae2761ac32 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 f343829d6a..c8689eabab 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 = {} - 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 = {} + 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 f2f17d4714..8558ee793c 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 53ca568cf5..3a4d316179 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 d6264b6050..65c90b9529 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 }) - } + const page = MessageV2.page({ + sessionID: ctx.params.sessionID, + limit: ctx.query.limit, + before: ctx.query.before, + }) + if (!page.cursor) return page.items - 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 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 1ad42c5261..42e973020d 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 -export class InstanceContextMiddleware extends HttpApiMiddleware.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, data: unknown) { +function writeSocket( + write: (data: string | Uint8Array | Socket.CloseEvent) => Effect.Effect, + 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 e1c03e7bde..c93261a0be 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[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 d9871c69be..17d6e0d063 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) { // 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) { 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 40e709edd4..e4aeda7989 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 } -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 b1a6ff4036..911f58efd0 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 // 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 380225316c..2a6c02349f 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 a0324cce39..5847192cb6 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 f430105714..5ce531dcc2 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 d79103df64..bb6c74b838 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",