feat(cli): add preview server daemon
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"lockfileVersion": 2,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
@@ -87,14 +87,18 @@
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.15.13",
|
||||
"bin": {
|
||||
"opencode": "./src/index.ts",
|
||||
"lildax": "./src/index.ts",
|
||||
},
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opencode-ai/server": "workspace:*",
|
||||
"@parcel/watcher": "2.5.1",
|
||||
"effect": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
@@ -512,6 +516,7 @@
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opencode-ai/server": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "2.8.1",
|
||||
"@opentelemetry/api": "1.9.0",
|
||||
@@ -649,6 +654,20 @@
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@opencode-ai/server",
|
||||
"version": "1.15.13",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.15.13",
|
||||
@@ -839,23 +858,23 @@
|
||||
},
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"esbuild",
|
||||
"tree-sitter-powershell",
|
||||
"protobufjs",
|
||||
"electron",
|
||||
"web-tree-sitter",
|
||||
"tree-sitter-bash",
|
||||
"esbuild",
|
||||
"electron",
|
||||
"protobufjs",
|
||||
],
|
||||
"patchedDependencies": {
|
||||
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
|
||||
"virtua@0.49.1": "patches/virtua@0.49.1.patch",
|
||||
"gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch",
|
||||
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
|
||||
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
|
||||
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
||||
"@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch",
|
||||
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
||||
"pacote@21.5.0": "patches/pacote@21.5.0.patch",
|
||||
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
|
||||
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
||||
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
||||
"gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch",
|
||||
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
|
||||
},
|
||||
"overrides": {
|
||||
"@opentui/core": "catalog:",
|
||||
@@ -1729,6 +1748,8 @@
|
||||
|
||||
"@opencode-ai/sdk": ["@opencode-ai/sdk@workspace:packages/sdk/js"],
|
||||
|
||||
"@opencode-ai/server": ["@opencode-ai/server@workspace:packages/server"],
|
||||
|
||||
"@opencode-ai/slack": ["@opencode-ai/slack@workspace:packages/slack"],
|
||||
|
||||
"@opencode-ai/stats-app": ["@opencode-ai/stats-app@workspace:packages/stats/app"],
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"opencode": "./src/index.ts"
|
||||
"lildax": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "bun run script/build.ts",
|
||||
@@ -16,9 +16,13 @@
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opencode-ai/server": "workspace:*",
|
||||
"@parcel/watcher": "2.5.1",
|
||||
"effect": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@typescript/native-preview": "catalog:"
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { $ } from "bun"
|
||||
import { rm } from "fs/promises"
|
||||
import path from "path"
|
||||
import { Script } from "@opencode-ai/script"
|
||||
import { modelsData } from "./generate"
|
||||
import pkg from "../package.json"
|
||||
|
||||
const dir = path.resolve(import.meta.dirname, "..")
|
||||
const binary = "lildax"
|
||||
process.chdir(dir)
|
||||
|
||||
await rm("dist", { recursive: true, force: true })
|
||||
|
||||
const singleFlag = process.argv.includes("--single")
|
||||
const baselineFlag = process.argv.includes("--baseline")
|
||||
const skipInstall = process.argv.includes("--skip-install")
|
||||
const sourcemapsFlag = process.argv.includes("--sourcemaps")
|
||||
|
||||
const allTargets: {
|
||||
os: string
|
||||
arch: "arm64" | "x64"
|
||||
abi?: "musl"
|
||||
avx2?: false
|
||||
}[] = [
|
||||
{ os: "linux", arch: "arm64" },
|
||||
{ os: "linux", arch: "x64" },
|
||||
{ os: "linux", arch: "x64", avx2: false },
|
||||
{ os: "linux", arch: "arm64", abi: "musl" },
|
||||
{ os: "linux", arch: "x64", abi: "musl" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", avx2: false },
|
||||
{ os: "darwin", arch: "arm64" },
|
||||
{ os: "darwin", arch: "x64" },
|
||||
{ os: "darwin", arch: "x64", avx2: false },
|
||||
{ os: "win32", arch: "arm64" },
|
||||
{ os: "win32", arch: "x64" },
|
||||
{ os: "win32", arch: "x64", avx2: false },
|
||||
]
|
||||
|
||||
const targets = singleFlag
|
||||
? allTargets.filter((item) => {
|
||||
if (item.os !== process.platform || item.arch !== process.arch) return false
|
||||
if (item.avx2 === false) return baselineFlag
|
||||
return item.abi === undefined
|
||||
})
|
||||
: allTargets
|
||||
|
||||
if (!skipInstall) await $`bun install --os="*" --cpu="*" @parcel/watcher@${pkg.dependencies["@parcel/watcher"]}`
|
||||
|
||||
for (const item of targets) {
|
||||
const name = [
|
||||
binary,
|
||||
item.os === "win32" ? "windows" : item.os,
|
||||
item.arch,
|
||||
item.avx2 === false ? "baseline" : undefined,
|
||||
item.abi,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("-")
|
||||
console.log(`building ${name}`)
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["./src/index.ts"],
|
||||
tsconfig: "./tsconfig.json",
|
||||
external: ["node-gyp"],
|
||||
format: "esm",
|
||||
minify: true,
|
||||
sourcemap: sourcemapsFlag ? "linked" : "none",
|
||||
splitting: true,
|
||||
compile: {
|
||||
autoloadBunfig: false,
|
||||
autoloadDotenv: false,
|
||||
autoloadTsconfig: true,
|
||||
autoloadPackageJson: true,
|
||||
target: name.replace(binary, "bun") as Bun.Build.CompileTarget,
|
||||
outfile: `./dist/${name}/bin/${binary}`,
|
||||
execArgv: [`--user-agent=${binary}/${Script.version}`, "--use-system-ca", "--"],
|
||||
windows: {},
|
||||
},
|
||||
define: {
|
||||
OPENCODE_VERSION: `'${Script.version}'`,
|
||||
OPENCODE_CLI_NAME: `'${binary}'`,
|
||||
OPENCODE_MODELS_DEV: modelsData,
|
||||
OPENCODE_CHANNEL: `'${Script.channel}'`,
|
||||
OPENCODE_LIBC: item.os === "linux" ? `'${item.abi ?? "glibc"}'` : "undefined",
|
||||
},
|
||||
})
|
||||
|
||||
if (result.success) continue
|
||||
for (const log of result.logs) console.error(log)
|
||||
process.exit(1)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
const modelsUrl = process.env.OPENCODE_MODELS_URL || "https://models.dev"
|
||||
|
||||
export const modelsData = process.env.MODELS_DEV_API_JSON
|
||||
? await Bun.file(process.env.MODELS_DEV_API_JSON).text()
|
||||
: await fetch(`${modelsUrl}/api.json`).then((response) => response.text())
|
||||
|
||||
console.log("Loaded models.dev snapshot")
|
||||
@@ -1,12 +0,0 @@
|
||||
import { CliApi } from "./cli-api"
|
||||
|
||||
export const Api = CliApi.make("opencode", {
|
||||
description: "OpenCode command line interface",
|
||||
commands: [
|
||||
CliApi.make("debug", {
|
||||
description: "Debugging and troubleshooting tools",
|
||||
commands: [CliApi.make("agents", { description: "List all agents" })],
|
||||
}),
|
||||
CliApi.make("migrate", { description: "Migrate v1 data to v2" }),
|
||||
],
|
||||
})
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Argument, Flag } from "effect/unstable/cli"
|
||||
import { Spec } from "../framework/spec"
|
||||
|
||||
declare const OPENCODE_CLI_NAME: string | undefined
|
||||
|
||||
export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCODE_CLI_NAME : "opencode", {
|
||||
description: "OpenCode 2.0 preview command line interface",
|
||||
commands: [
|
||||
Spec.make("debug", {
|
||||
description: "Debugging and troubleshooting tools",
|
||||
commands: [Spec.make("agents", { description: "List all agents" })],
|
||||
}),
|
||||
Spec.make("migrate", { description: "Migrate v1 data to v2" }),
|
||||
Spec.make("service", {
|
||||
description: "Manage the background server",
|
||||
commands: [
|
||||
Spec.make("start", { description: "Start the background server" }),
|
||||
Spec.make("restart", { description: "Restart the background server" }),
|
||||
Spec.make("status", { description: "Show background server status" }),
|
||||
Spec.make("stop", { description: "Stop the background server" }),
|
||||
Spec.make("password", {
|
||||
description: "Get or set the server password",
|
||||
params: { value: Argument.string("value").pipe(Argument.optional) },
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Spec.make("serve", {
|
||||
description: "Start the v2 API server",
|
||||
params: {
|
||||
hostname: Flag.string("hostname").pipe(Flag.withDefault("127.0.0.1")),
|
||||
port: Flag.integer("port").pipe(Flag.optional),
|
||||
register: Flag.boolean("register").pipe(Flag.withDefault(false)),
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
@@ -0,0 +1,21 @@
|
||||
import { EOL } from "os"
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Commands } from "../../commands"
|
||||
import { Runtime } from "../../../framework/runtime"
|
||||
import { Daemon } from "../../../services/daemon"
|
||||
|
||||
export default Runtime.handler(
|
||||
Commands.commands.debug.commands.agents,
|
||||
Effect.fn("cli.debug.agents")(function* () {
|
||||
const daemon = yield* Daemon.Service
|
||||
const client = yield* daemon.client()
|
||||
const response = yield* Effect.promise(() => client.v2.agent.list({ location: { directory: process.cwd() } }))
|
||||
process.stdout.write(
|
||||
JSON.stringify(
|
||||
response.data?.data.toSorted((a, b) => a.id.localeCompare(b.id)),
|
||||
null,
|
||||
2,
|
||||
) + EOL,
|
||||
)
|
||||
}),
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Commands } from "../commands"
|
||||
import { Runtime } from "../../framework/runtime"
|
||||
|
||||
export default Runtime.handler(Commands.commands.migrate, (_input) => Effect.log("No migrations to run."))
|
||||
@@ -0,0 +1,39 @@
|
||||
import { NodeHttpServer } from "@effect/platform-node"
|
||||
import { Context, Layer, Option } from "effect"
|
||||
import * as Effect from "effect/Effect"
|
||||
import { HttpRouter, HttpServer } from "effect/unstable/http"
|
||||
import { createServer } from "node:http"
|
||||
import { createRoutes } from "@opencode-ai/server/routes"
|
||||
import { Commands } from "../commands"
|
||||
import { Runtime } from "../../framework/runtime"
|
||||
import { Daemon } from "../../services/daemon"
|
||||
|
||||
export default Runtime.handler(
|
||||
Commands.commands.serve,
|
||||
Effect.fn("cli.serve")(function* (input) {
|
||||
return yield* Effect.scoped(
|
||||
Effect.gen(function* () {
|
||||
const daemon = yield* Daemon.Service
|
||||
const address = yield* listen(input.hostname, input.port, yield* daemon.password())
|
||||
if (input.register) yield* daemon.register(address)
|
||||
console.log(`server listening on ${HttpServer.formatAddress(address)}`)
|
||||
return yield* Effect.never
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
function listen(hostname: string, port: Option.Option<number>, password: string) {
|
||||
if (Option.isSome(port)) return bind(hostname, port.value, password)
|
||||
// Preserve the familiar default when available, but let the OS choose a free
|
||||
// port when another local server already owns 4096.
|
||||
return bind(hostname, 4096, password).pipe(Effect.catch(() => bind(hostname, 0, password)))
|
||||
}
|
||||
|
||||
function bind(hostname: string, port: number, password: string) {
|
||||
return Layer.build(
|
||||
HttpRouter.serve(createRoutes(password), { disableListenLog: true, disableLogger: true }).pipe(
|
||||
Layer.provideMerge(NodeHttpServer.layer(() => createServer(), { port, host: hostname })),
|
||||
),
|
||||
).pipe(Effect.map((context) => Context.get(context, HttpServer.HttpServer).address))
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { EOL } from "os"
|
||||
import { Option } from "effect"
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Commands } from "../../commands"
|
||||
import { Runtime } from "../../../framework/runtime"
|
||||
import { Daemon } from "../../../services/daemon"
|
||||
|
||||
export default Runtime.handler(
|
||||
Commands.commands.service.commands.password,
|
||||
Effect.fn("cli.service.password")(function* (input) {
|
||||
const daemon = yield* Daemon.Service
|
||||
const value = Option.getOrUndefined(input.value)
|
||||
if (value !== undefined) yield* daemon.stop()
|
||||
process.stdout.write((yield* daemon.password(value)) + EOL)
|
||||
}),
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
import { EOL } from "os"
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Commands } from "../../commands"
|
||||
import { Runtime } from "../../../framework/runtime"
|
||||
import { Daemon } from "../../../services/daemon"
|
||||
|
||||
export default Runtime.handler(
|
||||
Commands.commands.service.commands.restart,
|
||||
Effect.fn("cli.service.restart")(function* () {
|
||||
const daemon = yield* Daemon.Service
|
||||
yield* daemon.stop()
|
||||
process.stdout.write((yield* daemon.start()) + EOL)
|
||||
}),
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
import { EOL } from "os"
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Commands } from "../../commands"
|
||||
import { Runtime } from "../../../framework/runtime"
|
||||
import { Daemon } from "../../../services/daemon"
|
||||
|
||||
export default Runtime.handler(
|
||||
Commands.commands.service.commands.start,
|
||||
Effect.fn("cli.service.start")(function* () {
|
||||
process.stdout.write((yield* (yield* Daemon.Service).start()) + EOL)
|
||||
}),
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
import { EOL } from "os"
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Commands } from "../../commands"
|
||||
import { Runtime } from "../../../framework/runtime"
|
||||
import { Daemon } from "../../../services/daemon"
|
||||
|
||||
export default Runtime.handler(
|
||||
Commands.commands.service.commands.status,
|
||||
Effect.fn("cli.service.status")(function* () {
|
||||
const url = yield* (yield* Daemon.Service).status()
|
||||
process.stdout.write((url ? `running ${url}` : "stopped") + EOL)
|
||||
}),
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Commands } from "../../commands"
|
||||
import { Runtime } from "../../../framework/runtime"
|
||||
import { Daemon } from "../../../services/daemon"
|
||||
|
||||
export default Runtime.handler(
|
||||
Commands.commands.service.commands.stop,
|
||||
Effect.fn("cli.service.stop")(function* () {
|
||||
yield* (yield* Daemon.Service).stop()
|
||||
}),
|
||||
)
|
||||
@@ -1,19 +1,20 @@
|
||||
import * as Effect from "effect/Effect"
|
||||
import * as Command from "effect/unstable/cli/Command"
|
||||
import { CliApi } from "./cli-api"
|
||||
import { Spec } from "./spec"
|
||||
import { Daemon } from "../services/daemon"
|
||||
|
||||
export type Input<Value> =
|
||||
Value extends CliApi.Node<infer _Name, infer Spec, infer _Commands>
|
||||
? Input<Spec>
|
||||
Value extends Spec.Node<infer _Name, infer Command, infer _Commands>
|
||||
? Input<Command>
|
||||
: Value extends Command.Command<infer _Name, infer Input, infer _Context, infer _Error, infer _Requirements>
|
||||
? Input
|
||||
: never
|
||||
|
||||
type RuntimeHandler = (input: unknown) => Effect.Effect<void, unknown>
|
||||
type Loader<Node extends CliApi.Any> = () => Promise<{ default: (input: Input<Node>) => Effect.Effect<void, any> }>
|
||||
type ProvidedCommand = Command.Command<string, unknown, unknown, unknown, never>
|
||||
type RuntimeHandler = (input: unknown) => Effect.Effect<void, unknown, Daemon.Service>
|
||||
type Loader<Node extends Spec.Any> = () => Promise<{ default: (input: Input<Node>) => Effect.Effect<void, any, Daemon.Service> }>
|
||||
type ProvidedCommand = Command.Command<string, unknown, unknown, unknown, Daemon.Service>
|
||||
|
||||
export type Handlers<Node extends CliApi.Any> = keyof Node["commands"] extends never
|
||||
export type Handlers<Node extends Spec.Any> = keyof Node["commands"] extends never
|
||||
? Loader<Node>
|
||||
: { readonly $?: Loader<Node> } & { readonly [Key in keyof Node["commands"]]: Handlers<Node["commands"][Key]> }
|
||||
|
||||
@@ -29,17 +30,17 @@ type RuntimeHandlers =
|
||||
readonly [key: string]: RuntimeHandlers | (() => Promise<{ default: RuntimeHandler }>) | undefined
|
||||
}
|
||||
|
||||
export function handler<const Node extends CliApi.Any, Error>(
|
||||
export function handler<const Node extends Spec.Any, Error, Requirements>(
|
||||
_node: Node,
|
||||
run: (input: Input<Node>) => Effect.Effect<void, Error>,
|
||||
run: (input: Input<Node>) => Effect.Effect<void, Error, Requirements>,
|
||||
) {
|
||||
return run
|
||||
}
|
||||
|
||||
export function handlers<const Root extends CliApi.Any>(root: Root, handlers: Handlers<Root>) {
|
||||
export function handlers<const Root extends Spec.Any>(root: Root, handlers: Handlers<Root>) {
|
||||
const result: LazyHandler[] = []
|
||||
|
||||
function add(node: CliApi.Any, value: RuntimeHandlers) {
|
||||
function add(node: Spec.Any, value: RuntimeHandlers) {
|
||||
if (typeof value === "function") {
|
||||
result.push({ spec: node.spec, load: value as () => Promise<{ default: RuntimeHandler }> })
|
||||
return
|
||||
@@ -52,11 +53,11 @@ export function handlers<const Root extends CliApi.Any>(root: Root, handlers: Ha
|
||||
return result
|
||||
}
|
||||
|
||||
export function run(api: CliApi.Any, handlers: ReadonlyArray<LazyHandler>, options: { readonly version: string }) {
|
||||
return Command.run(provide(api, handlers), options) as Effect.Effect<void, unknown, Command.Environment>
|
||||
export function run(commands: Spec.Any, handlers: ReadonlyArray<LazyHandler>, options: { readonly version: string }) {
|
||||
return Command.run(provide(commands, handlers), options) as Effect.Effect<void, unknown, Command.Environment>
|
||||
}
|
||||
|
||||
function provide(node: CliApi.Any, handlers: ReadonlyArray<LazyHandler>): ProvidedCommand {
|
||||
function provide(node: Spec.Any, handlers: ReadonlyArray<LazyHandler>): ProvidedCommand {
|
||||
const spec: Command.Command.Any = Object.keys(node.commands).length
|
||||
? (node.spec as Command.Command<string, unknown>).pipe(
|
||||
Command.withSubcommands(Object.values(node.commands).map((child) => provide(child, handlers))),
|
||||
@@ -65,8 +66,12 @@ function provide(node: CliApi.Any, handlers: ReadonlyArray<LazyHandler>): Provid
|
||||
const handler = handlers.find((handler) => handler.spec === node.spec)
|
||||
if (!handler) return spec as ProvidedCommand
|
||||
return spec.pipe(
|
||||
Command.withHandler((input) => Effect.flatMap(Effect.promise(handler.load), (module) => module.default(input))),
|
||||
Command.withHandler((input) =>
|
||||
Effect.gen(function* () {
|
||||
yield* Effect.flatMap(Effect.promise(handler.load), (module) => module.default(input))
|
||||
}),
|
||||
),
|
||||
) as ProvidedCommand
|
||||
}
|
||||
|
||||
export * as CliBuilder from "./cli-builder"
|
||||
export * as Runtime from "./runtime"
|
||||
@@ -39,4 +39,4 @@ type ChildrenOf<Commands extends ReadonlyArray<Any>> = {
|
||||
readonly [Node in Commands[number] as Node["name"]]: Node
|
||||
}
|
||||
|
||||
export * as CliApi from "./cli-api"
|
||||
export * as Spec from "./spec"
|
||||
@@ -1,30 +0,0 @@
|
||||
import { EOL } from "os"
|
||||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { LocationServiceMap } from "@opencode-ai/core/location-layer"
|
||||
import { PluginBoot } from "@opencode-ai/core/plugin/boot"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Api } from "../../api"
|
||||
import { CliBuilder } from "../../cli-builder"
|
||||
|
||||
export default CliBuilder.handler(
|
||||
Api.commands.debug.commands.agents,
|
||||
Effect.fn("cli.debug.agents")(
|
||||
function* () {
|
||||
const svc = {
|
||||
plugin: yield* PluginBoot.Service,
|
||||
agent: yield* AgentV2.Service,
|
||||
}
|
||||
yield* svc.plugin.wait()
|
||||
process.stdout.write(
|
||||
JSON.stringify(
|
||||
(yield* svc.agent.all()).sort((a, b) => a.id.localeCompare(b.id)),
|
||||
null,
|
||||
2,
|
||||
) + EOL,
|
||||
)
|
||||
},
|
||||
Effect.provide(LocationServiceMap.get({ directory: AbsolutePath.make(process.cwd()) })),
|
||||
Effect.provide(LocationServiceMap.layer),
|
||||
),
|
||||
)
|
||||
@@ -1,5 +0,0 @@
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Api } from "../api"
|
||||
import { CliBuilder } from "../cli-builder"
|
||||
|
||||
export default CliBuilder.handler(Api.commands.migrate, (_input) => Effect.log("No migrations to run."))
|
||||
@@ -3,17 +3,27 @@
|
||||
import * as NodeRuntime from "@effect/platform-node/NodeRuntime"
|
||||
import * as NodeServices from "@effect/platform-node/NodeServices"
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Api } from "./api"
|
||||
import { CliBuilder } from "./cli-builder"
|
||||
import { Commands } from "./commands/commands"
|
||||
import { Runtime } from "./framework/runtime"
|
||||
import { Daemon } from "./services/daemon"
|
||||
|
||||
const Handlers = CliBuilder.handlers(Api, {
|
||||
const Handlers = Runtime.handlers(Commands, {
|
||||
debug: {
|
||||
agents: () => import("./handlers/debug/agents"),
|
||||
agents: () => import("./commands/handlers/debug/agents"),
|
||||
},
|
||||
migrate: () => import("./handlers/migrate"),
|
||||
migrate: () => import("./commands/handlers/migrate"),
|
||||
service: {
|
||||
start: () => import("./commands/handlers/service/start"),
|
||||
restart: () => import("./commands/handlers/service/restart"),
|
||||
status: () => import("./commands/handlers/service/status"),
|
||||
stop: () => import("./commands/handlers/service/stop"),
|
||||
password: () => import("./commands/handlers/service/password"),
|
||||
},
|
||||
serve: () => import("./commands/handlers/serve"),
|
||||
})
|
||||
|
||||
CliBuilder.run(Api, Handlers, { version: "local" }).pipe(
|
||||
Runtime.run(Commands, Handlers, { version: "local" }).pipe(
|
||||
Effect.provide(Daemon.defaultLayer),
|
||||
Effect.provide(NodeServices.layer),
|
||||
Effect.scoped,
|
||||
NodeRuntime.runMain,
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client"
|
||||
import { ServerAuth } from "@opencode-ai/server/auth"
|
||||
import { Context, Effect, FileSystem, Layer, Option, Schedule, Schema, Scope } from "effect"
|
||||
import { HttpServer } from "effect/unstable/http"
|
||||
import { randomBytes } from "crypto"
|
||||
import path from "path"
|
||||
|
||||
export interface Interface {
|
||||
readonly client: () => Effect.Effect<ReturnType<typeof createOpencodeClient>, unknown>
|
||||
readonly start: () => Effect.Effect<string, Error>
|
||||
readonly status: () => Effect.Effect<string | undefined>
|
||||
readonly stop: () => Effect.Effect<void, unknown>
|
||||
readonly password: (value?: string) => Effect.Effect<string, unknown>
|
||||
readonly register: (address: HttpServer.Address) => Effect.Effect<void, unknown, Scope.Scope>
|
||||
}
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/cli/Daemon") {}
|
||||
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const fs = yield* FileSystem.FileSystem
|
||||
const directory = Global.Path.state
|
||||
const file = path.join(directory, "server.json")
|
||||
const passwordFile = path.join(directory, "password")
|
||||
const decodeRegistration = Schema.decodeUnknownEffect(
|
||||
Schema.fromJsonString(Schema.Struct({ url: Schema.String, pid: Schema.Number })),
|
||||
)
|
||||
|
||||
const password = Effect.fn("cli.daemon.password")(function* (value?: string) {
|
||||
const existing = yield* fs.readFileString(passwordFile).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
if (value === undefined && existing) return existing
|
||||
|
||||
// Keep one private credential across server restarts so discovered clients
|
||||
// can reconnect without exposing a password flag or environment variable.
|
||||
const generated = value ?? randomBytes(32).toString("base64url")
|
||||
const temp = passwordFile + ".tmp"
|
||||
yield* fs.makeDirectory(directory, { recursive: true })
|
||||
yield* fs.writeFileString(temp, generated, { mode: 0o600 })
|
||||
yield* fs.rename(temp, passwordFile)
|
||||
return generated
|
||||
})
|
||||
|
||||
const registration = Effect.fnUntraced(function* () {
|
||||
return yield* fs.readFileString(file).pipe(Effect.flatMap(decodeRegistration))
|
||||
})
|
||||
|
||||
const createClient = Effect.fnUntraced(function* (url: string) {
|
||||
return createOpencodeClient({ baseUrl: url, headers: ServerAuth.headers({ password: yield* password() }) })
|
||||
})
|
||||
|
||||
const healthy = Effect.fnUntraced(function* () {
|
||||
const info = yield* registration()
|
||||
const client = yield* createClient(info.url)
|
||||
const response = yield* Effect.tryPromise(() => client.v2.health.get())
|
||||
if (response.data?.healthy === true) return info
|
||||
return yield* Effect.fail(new Error("Registered server is not healthy"))
|
||||
})
|
||||
|
||||
const start = Effect.fn("cli.daemon.start")(function* () {
|
||||
const existing = yield* healthy().pipe(Effect.option)
|
||||
const found = Option.getOrUndefined(existing)
|
||||
if (found) return found.url
|
||||
|
||||
yield* Effect.sync(() => {
|
||||
const compiled = path.basename(process.execPath).replace(/\.exe$/, "") !== "bun"
|
||||
Bun.spawn([process.execPath, ...(compiled ? [] : [Bun.main]), "serve", "--register"], {
|
||||
stdin: "ignore",
|
||||
stdout: "ignore",
|
||||
stderr: "ignore",
|
||||
}).unref()
|
||||
})
|
||||
|
||||
return yield* healthy().pipe(
|
||||
Effect.retry(Schedule.spaced("50 millis").pipe(Schedule.both(Schedule.recurs(100)))),
|
||||
Effect.map((info) => info.url),
|
||||
Effect.mapError(() => new Error("Failed to start server")),
|
||||
)
|
||||
})
|
||||
|
||||
const client = Effect.fn("cli.daemon.client")(function* () {
|
||||
return yield* createClient(yield* start())
|
||||
})
|
||||
|
||||
const status = Effect.fn("cli.daemon.status")(function* () {
|
||||
const existing = yield* healthy().pipe(Effect.option)
|
||||
const found = Option.getOrUndefined(existing)
|
||||
if (found) return found.url
|
||||
yield* fs.remove(file).pipe(Effect.ignore)
|
||||
return undefined
|
||||
})
|
||||
|
||||
const signal = (pid: number, signal: NodeJS.Signals) =>
|
||||
Effect.try({ try: () => process.kill(pid, signal), catch: (cause) => cause }).pipe(Effect.ignore)
|
||||
|
||||
const awaitStopped = Effect.fnUntraced(function* (pid: number) {
|
||||
const running = yield* Effect.try({ try: () => process.kill(pid, 0), catch: () => false }).pipe(
|
||||
Effect.orElseSucceed(() => false),
|
||||
)
|
||||
if (!running) return true
|
||||
return yield* Effect.fail(new Error(`Server process ${pid} is still running`))
|
||||
})
|
||||
|
||||
const stop = Effect.fn("cli.daemon.stop")(function* () {
|
||||
const existing = yield* healthy().pipe(Effect.option)
|
||||
// A stale registration may point at a PID that has since been reused by
|
||||
// another process. Only signal the PID after authenticating the server.
|
||||
if (Option.isNone(existing)) return yield* fs.remove(file).pipe(Effect.ignore)
|
||||
const pid = existing.value.pid
|
||||
yield* signal(pid, "SIGTERM")
|
||||
const stopped = yield* awaitStopped(pid).pipe(
|
||||
Effect.retry(Schedule.spaced("50 millis").pipe(Schedule.both(Schedule.recurs(100)))),
|
||||
Effect.option,
|
||||
)
|
||||
if (Option.isNone(stopped)) {
|
||||
yield* signal(pid, "SIGKILL")
|
||||
yield* awaitStopped(pid).pipe(
|
||||
Effect.retry(Schedule.spaced("50 millis").pipe(Schedule.both(Schedule.recurs(100)))),
|
||||
)
|
||||
}
|
||||
yield* fs.remove(file).pipe(Effect.ignore)
|
||||
})
|
||||
|
||||
const register = Effect.fn("cli.daemon.register")(function* (address: HttpServer.Address) {
|
||||
const temp = file + ".tmp"
|
||||
yield* fs.makeDirectory(directory, { recursive: true })
|
||||
yield* fs.writeFileString(
|
||||
temp,
|
||||
JSON.stringify({ url: HttpServer.formatAddress(address), pid: process.pid }),
|
||||
{ mode: 0o600 },
|
||||
)
|
||||
yield* fs.rename(temp, file)
|
||||
// The metadata file represents this live listener, not persistent config.
|
||||
// Scope shutdown removes it when the server exits normally.
|
||||
yield* Effect.addFinalizer(() => fs.remove(file).pipe(Effect.ignore))
|
||||
})
|
||||
|
||||
return Service.of({ client, start, status, stop, password, register })
|
||||
}),
|
||||
)
|
||||
|
||||
export const defaultLayer = layer
|
||||
|
||||
export * as Daemon from "./daemon"
|
||||
@@ -2,6 +2,7 @@
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@tsconfig/bun/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"noUncheckedIndexedAccess": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
"@opencode-ai/llm": "workspace:*",
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@opencode-ai/server": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "2.8.1",
|
||||
|
||||
@@ -20,7 +20,7 @@ import { SessionApi } from "./groups/session"
|
||||
import { SyncApi } from "./groups/sync"
|
||||
import { TuiApi } from "./groups/tui"
|
||||
import { WorkspaceApi } from "./groups/workspace"
|
||||
import { V2Api } from "./groups/v2"
|
||||
import { V2Api } from "@opencode-ai/server/api"
|
||||
// GlobalEventSchema snapshots the registry after event-producing groups register their variants.
|
||||
import { GlobalApi } from "./groups/global"
|
||||
import { Authorization } from "./middleware/authorization"
|
||||
@@ -60,7 +60,6 @@ export const InstanceHttpApi = HttpApi.make("opencode-instance")
|
||||
.addHttpApi(ProviderApi)
|
||||
.addHttpApi(SessionApi)
|
||||
.addHttpApi(SyncApi)
|
||||
.addHttpApi(V2Api)
|
||||
.addHttpApi(TuiApi)
|
||||
.addHttpApi(WorkspaceApi)
|
||||
.middleware(SchemaErrorMiddleware)
|
||||
@@ -69,6 +68,7 @@ export const OpenCodeHttpApi = HttpApi.make("opencode")
|
||||
.addHttpApi(RootHttpApi)
|
||||
.addHttpApi(EventApi)
|
||||
.addHttpApi(InstanceHttpApi)
|
||||
.addHttpApi(V2Api)
|
||||
.addHttpApi(PtyConnectApi)
|
||||
.annotate(HttpApi.AdditionalSchemas, [EventSchema, Question.Replied, Question.Rejected])
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { HttpApi, OpenApi } from "effect/unstable/httpapi"
|
||||
import { MessageGroup } from "./v2/message"
|
||||
import { ModelGroup } from "./v2/model"
|
||||
import { ProviderGroup } from "./v2/provider"
|
||||
import { SessionGroup } from "./v2/session"
|
||||
import { PermissionGroup, PermissionSavedGroup, SessionPermissionGroup } from "./v2/permission"
|
||||
import { FileSystemGroup } from "./v2/fs"
|
||||
import { CommandGroup } from "./v2/command"
|
||||
import { SkillGroup } from "./v2/skill"
|
||||
import { EventGroup } from "./v2/event"
|
||||
|
||||
export const V2Api = HttpApi.make("v2")
|
||||
.add(SessionGroup)
|
||||
.add(MessageGroup)
|
||||
.add(ModelGroup)
|
||||
.add(ProviderGroup)
|
||||
.add(PermissionGroup)
|
||||
.add(SessionPermissionGroup)
|
||||
.add(PermissionSavedGroup)
|
||||
.add(FileSystemGroup)
|
||||
.add(CommandGroup)
|
||||
.add(SkillGroup)
|
||||
.add(EventGroup)
|
||||
.annotateMerge(
|
||||
OpenApi.annotations({
|
||||
title: "opencode experimental HttpApi",
|
||||
version: "0.0.1",
|
||||
description: "Experimental HttpApi surface for selected instance routes.",
|
||||
}),
|
||||
)
|
||||
@@ -1,60 +0,0 @@
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { EventV2Bridge } from "@/event-v2-bridge"
|
||||
import { Effect, Stream } from "effect"
|
||||
import { HttpServerResponse } from "effect/unstable/http"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import * as Sse from "effect/unstable/encoding/Sse"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
|
||||
function eventData(data: unknown): Sse.Event {
|
||||
return {
|
||||
_tag: "Event",
|
||||
event: "message",
|
||||
id: undefined,
|
||||
data: JSON.stringify(data),
|
||||
}
|
||||
}
|
||||
|
||||
export const eventHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.event", (handlers) =>
|
||||
handlers.handleRaw("events", () =>
|
||||
Effect.gen(function* () {
|
||||
const events = yield* EventV2Bridge.Service
|
||||
const location = yield* Location.Service
|
||||
const connected = {
|
||||
id: EventV2.ID.create(),
|
||||
type: "server.connected",
|
||||
location: new Location.Info({
|
||||
directory: location.directory,
|
||||
workspaceID: location.workspaceID,
|
||||
project: location.project,
|
||||
}),
|
||||
data: {},
|
||||
}
|
||||
return HttpServerResponse.stream(
|
||||
Stream.make(connected).pipe(
|
||||
Stream.concat(
|
||||
events.all().pipe(
|
||||
Stream.filter(
|
||||
(event) =>
|
||||
event.location?.directory === location.directory &&
|
||||
event.location.workspaceID === location.workspaceID,
|
||||
),
|
||||
),
|
||||
),
|
||||
Stream.map(eventData),
|
||||
Stream.pipeThroughChannel(Sse.encode()),
|
||||
Stream.encodeText,
|
||||
),
|
||||
{
|
||||
contentType: "text/event-stream",
|
||||
headers: {
|
||||
"Cache-Control": "no-cache, no-transform",
|
||||
"X-Accel-Buffering": "no",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
},
|
||||
},
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
@@ -4,7 +4,7 @@ import { HttpEffect, HttpRouter, HttpServerRequest, HttpServerResponse } from "e
|
||||
import { HttpApiError, HttpApiMiddleware } from "effect/unstable/httpapi"
|
||||
import { hasPtyConnectTicketURL } from "@/server/shared/pty-ticket"
|
||||
import { isPublicUIPath } from "@/server/shared/public-ui"
|
||||
import { UnauthorizedError } from "../errors"
|
||||
export { V2Authorization, v2AuthorizationLayer } from "@opencode-ai/server/middleware/authorization"
|
||||
|
||||
const AUTH_TOKEN_QUERY = "auth_token"
|
||||
const UNAUTHORIZED = 401
|
||||
@@ -20,13 +20,6 @@ export class Authorization extends HttpApiMiddleware.Service<Authorization>()(
|
||||
},
|
||||
) {}
|
||||
|
||||
export class V2Authorization extends HttpApiMiddleware.Service<V2Authorization>()(
|
||||
"@opencode/ExperimentalHttpApiV2Authorization",
|
||||
{
|
||||
error: UnauthorizedError,
|
||||
},
|
||||
) {}
|
||||
|
||||
export class PtyConnectAuthorization extends HttpApiMiddleware.Service<PtyConnectAuthorization>()(
|
||||
"@opencode/ExperimentalHttpApiPtyConnectAuthorization",
|
||||
{
|
||||
@@ -152,27 +145,3 @@ export const ptyConnectAuthorizationLayer = Layer.effect(
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
export const v2AuthorizationLayer = Layer.effect(
|
||||
V2Authorization,
|
||||
Effect.gen(function* () {
|
||||
const config = yield* ServerAuth.Config
|
||||
if (!ServerAuth.required(config)) return V2Authorization.of((effect) => effect)
|
||||
return V2Authorization.of((effect) =>
|
||||
Effect.gen(function* () {
|
||||
const request = yield* HttpServerRequest.HttpServerRequest
|
||||
return yield* credentialFromRequest(request).pipe(
|
||||
Effect.flatMap((credential) =>
|
||||
Effect.gen(function* () {
|
||||
if (ServerAuth.authorized(credential, config)) return yield* effect
|
||||
yield* HttpEffect.appendPreResponseHandler((_request, response) =>
|
||||
Effect.succeed(HttpServerResponse.setHeader(response, "www-authenticate", WWW_AUTHENTICATE)),
|
||||
)
|
||||
return yield* new UnauthorizedError({ message: "Authentication required" })
|
||||
}),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -44,6 +44,7 @@ import { Todo } from "@/session/todo"
|
||||
import { SessionShare } from "@/share/session"
|
||||
import { ShareNext } from "@/share/share-next"
|
||||
import { EventV2Bridge } from "@/event-v2-bridge"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { Skill } from "@/skill"
|
||||
import { Snapshot } from "@/snapshot"
|
||||
@@ -56,6 +57,7 @@ import { CorsConfig, isAllowedCorsOrigin, type CorsOptions } from "@/server/cors
|
||||
import { serveUIEffect } from "@/server/shared/ui"
|
||||
import { ServerAuth } from "@/server/auth"
|
||||
import { InstanceHttpApi, RootHttpApi } from "./api"
|
||||
import { V2Api } from "@opencode-ai/server/api"
|
||||
import { PublicApi } from "./public"
|
||||
import {
|
||||
authorizationLayer,
|
||||
@@ -82,7 +84,8 @@ import { questionHandlers } from "./handlers/question"
|
||||
import { sessionHandlers } from "./handlers/session"
|
||||
import { syncHandlers } from "./handlers/sync"
|
||||
import { tuiHandlers } from "./handlers/tui"
|
||||
import { v2Handlers } from "./handlers/v2"
|
||||
import { v2Handlers } from "@opencode-ai/server/handlers"
|
||||
import { schemaErrorLayer as v2SchemaErrorLayer } from "@opencode-ai/server/middleware/schema-error"
|
||||
import { workspaceHandlers } from "./handlers/workspace"
|
||||
import { instanceContextLayer } from "./middleware/instance-context"
|
||||
import { workspaceRoutingLayer } from "./middleware/workspace-routing"
|
||||
@@ -144,14 +147,17 @@ const instanceApiRoutes = HttpApiBuilder.layer(InstanceHttpApi).pipe(
|
||||
providerHandlers,
|
||||
sessionHandlers,
|
||||
syncHandlers,
|
||||
v2Handlers,
|
||||
tuiHandlers,
|
||||
workspaceHandlers,
|
||||
]),
|
||||
)
|
||||
|
||||
const instanceRoutes = instanceApiRoutes.pipe(
|
||||
Layer.provide([httpApiAuthLayer, v2HttpApiAuthLayer, workspaceRoutingLive, instanceContextLayer, schemaErrorLayer]),
|
||||
Layer.provide([httpApiAuthLayer, workspaceRoutingLive, instanceContextLayer, schemaErrorLayer]),
|
||||
)
|
||||
const v2Routes = HttpApiBuilder.layer(V2Api).pipe(
|
||||
Layer.provide(v2Handlers),
|
||||
Layer.provide([v2HttpApiAuthLayer, v2SchemaErrorLayer]),
|
||||
)
|
||||
|
||||
// `OpenApi.fromApi` is non-trivial; defer until /doc is actually hit so
|
||||
@@ -186,7 +192,7 @@ type RouteRequirements =
|
||||
export function createRoutes(
|
||||
corsOptions?: CorsOptions,
|
||||
): Layer.Layer<never, EffectConfig.ConfigError, RouteRequirements> {
|
||||
return Layer.mergeAll(rootApiRoutes, eventApiRoutes, ptyConnectApiRoutes, instanceRoutes, docRoute, uiRoute).pipe(
|
||||
return Layer.mergeAll(rootApiRoutes, eventApiRoutes, ptyConnectApiRoutes, instanceRoutes, v2Routes, docRoute, uiRoute).pipe(
|
||||
Layer.provide([
|
||||
errorLayer,
|
||||
compressionLayer,
|
||||
@@ -226,6 +232,7 @@ export function createRoutes(
|
||||
ShareNext.defaultLayer,
|
||||
Snapshot.defaultLayer,
|
||||
EventV2Bridge.defaultLayer,
|
||||
EventV2.defaultLayer,
|
||||
Skill.defaultLayer,
|
||||
Todo.defaultLayer,
|
||||
ToolRegistry.defaultLayer,
|
||||
|
||||
@@ -624,6 +624,11 @@ const scenarios: Scenario[] = [
|
||||
check(auth.test === undefined, "auth remove should delete provider from isolated auth file")
|
||||
}),
|
||||
),
|
||||
http.protected.get("/api/health", "v2.health.get").json(200, (body) => {
|
||||
object(body)
|
||||
check(body.healthy === true, "v2 server should report healthy")
|
||||
}),
|
||||
http.protected.get("/api/agent", "v2.agent.list").json(200, locationData(array)),
|
||||
http.protected.get("/api/model", "v2.model.list").json(200, locationData(array)),
|
||||
http.protected.get("/api/provider", "v2.provider.list").json(200, locationData(array)),
|
||||
http.protected.get("/api/command", "v2.command.list").json(200, locationData(array)),
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
SessionPaths,
|
||||
} from "../../src/server/routes/instance/httpapi/groups/session"
|
||||
import { PtyPaths } from "../../src/server/routes/instance/httpapi/groups/pty"
|
||||
import { MessagesQuery as V2MessagesQuery } from "../../src/server/routes/instance/httpapi/groups/v2/message"
|
||||
import { MessagesQuery as V2MessagesQuery } from "@opencode-ai/server/groups/v2/message"
|
||||
import { QueryBoolean, QueryBooleanOpenApi } from "../../src/server/routes/instance/httpapi/groups/query"
|
||||
import { resetDatabase } from "../fixture/db"
|
||||
import { disposeAllInstances, tmpdir } from "../fixture/fixture"
|
||||
|
||||
@@ -255,6 +255,8 @@ import type {
|
||||
TuiShowToastResponses,
|
||||
TuiSubmitPromptErrors,
|
||||
TuiSubmitPromptResponses,
|
||||
V2AgentListErrors,
|
||||
V2AgentListResponses,
|
||||
V2CommandListErrors,
|
||||
V2CommandListResponses,
|
||||
V2EventSubscribeErrors,
|
||||
@@ -263,6 +265,8 @@ import type {
|
||||
V2FsListResponses,
|
||||
V2FsReadErrors,
|
||||
V2FsReadResponses,
|
||||
V2HealthGetErrors,
|
||||
V2HealthGetResponses,
|
||||
V2ModelListErrors,
|
||||
V2ModelListResponses,
|
||||
V2PermissionRequestListErrors,
|
||||
@@ -4463,653 +4467,6 @@ export class Sync extends HeyApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
export class Permission2 extends HeyApiClient {
|
||||
/**
|
||||
* List session permission requests
|
||||
*
|
||||
* Retrieve pending permission requests owned by a session.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "sessionID" }] }])
|
||||
return (options?.client ?? this.client).get<
|
||||
V2SessionPermissionListResponses,
|
||||
V2SessionPermissionListErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/api/session/{sessionID}/permission/request",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Reply to pending permission request
|
||||
*
|
||||
* Respond to a pending permission request owned by a session.
|
||||
*/
|
||||
public reply<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
requestID: string
|
||||
reply?: PermissionV2Reply
|
||||
message?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "sessionID" },
|
||||
{ in: "path", key: "requestID" },
|
||||
{ in: "body", key: "reply" },
|
||||
{ in: "body", key: "message" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<
|
||||
V2SessionPermissionReplyResponses,
|
||||
V2SessionPermissionReplyErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/api/session/{sessionID}/permission/request/{requestID}/reply",
|
||||
...options,
|
||||
...params,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
...params.headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Session3 extends HeyApiClient {
|
||||
/**
|
||||
* List v2 sessions
|
||||
*
|
||||
* Retrieve sessions in the requested order. Items keep that order across pages; use cursor.next or cursor.previous to move through the ordered list.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
workspace?: string
|
||||
limit?: number
|
||||
order?: "asc" | "desc"
|
||||
search?: string
|
||||
directory?: string
|
||||
project?: string
|
||||
subpath?: string
|
||||
cursor?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "workspace" },
|
||||
{ in: "query", key: "limit" },
|
||||
{ in: "query", key: "order" },
|
||||
{ in: "query", key: "search" },
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "project" },
|
||||
{ in: "query", key: "subpath" },
|
||||
{ in: "query", key: "cursor" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2SessionListResponses, V2SessionListErrors, ThrowOnError>({
|
||||
url: "/api/session",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Send v2 message
|
||||
*
|
||||
* Create a v2 session message and queue it for the agent loop.
|
||||
*/
|
||||
public prompt<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
directory?: string
|
||||
workspace?: string
|
||||
prompt?: Prompt
|
||||
delivery?: SessionDelivery
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "sessionID" },
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
{ in: "body", key: "prompt" },
|
||||
{ in: "body", key: "delivery" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<V2SessionPromptResponses, V2SessionPromptErrors, ThrowOnError>({
|
||||
url: "/api/session/{sessionID}/prompt",
|
||||
...options,
|
||||
...params,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
...params.headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact v2 session
|
||||
*
|
||||
* Compact a v2 session conversation.
|
||||
*/
|
||||
public compact<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
directory?: string
|
||||
workspace?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "sessionID" },
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<V2SessionCompactResponses, V2SessionCompactErrors, ThrowOnError>({
|
||||
url: "/api/session/{sessionID}/compact",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for v2 session
|
||||
*
|
||||
* Wait for a v2 session agent loop to become idle.
|
||||
*/
|
||||
public wait<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
directory?: string
|
||||
workspace?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "sessionID" },
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<V2SessionWaitResponses, V2SessionWaitErrors, ThrowOnError>({
|
||||
url: "/api/session/{sessionID}/wait",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get v2 session context
|
||||
*
|
||||
* Retrieve the active context messages for a v2 session (all messages after the last compaction).
|
||||
*/
|
||||
public context<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
directory?: string
|
||||
workspace?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "sessionID" },
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2SessionContextResponses, V2SessionContextErrors, ThrowOnError>({
|
||||
url: "/api/session/{sessionID}/context",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get v2 session messages
|
||||
*
|
||||
* Retrieve projected v2 messages for a session. Items keep the requested order across pages; use cursor.next or cursor.previous to move through the ordered timeline.
|
||||
*/
|
||||
public messages<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
directory?: string
|
||||
workspace?: string
|
||||
limit?: number
|
||||
order?: "asc" | "desc"
|
||||
cursor?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "sessionID" },
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
{ in: "query", key: "limit" },
|
||||
{ in: "query", key: "order" },
|
||||
{ in: "query", key: "cursor" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2SessionMessagesResponses, V2SessionMessagesErrors, ThrowOnError>({
|
||||
url: "/api/session/{sessionID}/message",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
private _permission?: Permission2
|
||||
get permission(): Permission2 {
|
||||
return (this._permission ??= new Permission2({ client: this.client }))
|
||||
}
|
||||
}
|
||||
|
||||
export class Model extends HeyApiClient {
|
||||
/**
|
||||
* List v2 models
|
||||
*
|
||||
* Retrieve available v2 models ordered by release date.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<V2ModelListResponses, V2ModelListErrors, ThrowOnError>({
|
||||
url: "/api/model",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Provider2 extends HeyApiClient {
|
||||
/**
|
||||
* List v2 providers
|
||||
*
|
||||
* Retrieve active v2 AI providers so clients can show provider availability and configuration.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<V2ProviderListResponses, V2ProviderListErrors, ThrowOnError>({
|
||||
url: "/api/provider",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get v2 provider
|
||||
*
|
||||
* Retrieve a single v2 AI provider so clients can inspect its availability and endpoint settings.
|
||||
*/
|
||||
public get<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
providerID: string
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "providerID" },
|
||||
{ in: "query", key: "location" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2ProviderGetResponses, V2ProviderGetErrors, ThrowOnError>({
|
||||
url: "/api/provider/{providerID}",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Request extends HeyApiClient {
|
||||
/**
|
||||
* List pending permission requests
|
||||
*
|
||||
* Retrieve pending permission requests for a location.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<
|
||||
V2PermissionRequestListResponses,
|
||||
V2PermissionRequestListErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/api/permission/request",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Saved extends HeyApiClient {
|
||||
/**
|
||||
* List saved permissions
|
||||
*
|
||||
* Retrieve saved permissions, optionally filtered by project.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
projectID?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "projectID" }] }])
|
||||
return (options?.client ?? this.client).get<
|
||||
V2PermissionSavedListResponses,
|
||||
V2PermissionSavedListErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/api/permission/saved",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove saved permission
|
||||
*
|
||||
* Remove a saved permission by ID.
|
||||
*/
|
||||
public remove<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
id: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "id" }] }])
|
||||
return (options?.client ?? this.client).delete<
|
||||
V2PermissionSavedRemoveResponses,
|
||||
V2PermissionSavedRemoveErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/api/permission/saved/{id}",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Permission3 extends HeyApiClient {
|
||||
private _request?: Request
|
||||
get request(): Request {
|
||||
return (this._request ??= new Request({ client: this.client }))
|
||||
}
|
||||
|
||||
private _saved?: Saved
|
||||
get saved(): Saved {
|
||||
return (this._saved ??= new Saved({ client: this.client }))
|
||||
}
|
||||
}
|
||||
|
||||
export class Fs extends HeyApiClient {
|
||||
/**
|
||||
* Read file
|
||||
*
|
||||
* Read one file relative to the requested location.
|
||||
*/
|
||||
public read<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
path: string
|
||||
reference?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "location" },
|
||||
{ in: "query", key: "path" },
|
||||
{ in: "query", key: "reference" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2FsReadResponses, V2FsReadErrors, ThrowOnError>({
|
||||
url: "/api/fs/read",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* List directory
|
||||
*
|
||||
* List direct children of one directory relative to the requested location.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
path?: string
|
||||
reference?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "location" },
|
||||
{ in: "query", key: "path" },
|
||||
{ in: "query", key: "reference" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2FsListResponses, V2FsListErrors, ThrowOnError>({
|
||||
url: "/api/fs/list",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Command2 extends HeyApiClient {
|
||||
/**
|
||||
* List v2 commands
|
||||
*
|
||||
* Retrieve currently registered v2 commands.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<V2CommandListResponses, V2CommandListErrors, ThrowOnError>({
|
||||
url: "/api/command",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Skill extends HeyApiClient {
|
||||
/**
|
||||
* List v2 skills
|
||||
*
|
||||
* Retrieve currently registered v2 skills.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<V2SkillListResponses, V2SkillListErrors, ThrowOnError>({
|
||||
url: "/api/skill",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Event2 extends HeyApiClient {
|
||||
/**
|
||||
* Subscribe to v2 events
|
||||
*
|
||||
* Subscribe to native EventV2 payloads for a location.
|
||||
*/
|
||||
public subscribe<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).sse.get<V2EventSubscribeResponses, V2EventSubscribeErrors, ThrowOnError>({
|
||||
url: "/api/event",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class V2 extends HeyApiClient {
|
||||
private _session?: Session3
|
||||
get session(): Session3 {
|
||||
return (this._session ??= new Session3({ client: this.client }))
|
||||
}
|
||||
|
||||
private _model?: Model
|
||||
get model(): Model {
|
||||
return (this._model ??= new Model({ client: this.client }))
|
||||
}
|
||||
|
||||
private _provider?: Provider2
|
||||
get provider(): Provider2 {
|
||||
return (this._provider ??= new Provider2({ client: this.client }))
|
||||
}
|
||||
|
||||
private _permission?: Permission3
|
||||
get permission(): Permission3 {
|
||||
return (this._permission ??= new Permission3({ client: this.client }))
|
||||
}
|
||||
|
||||
private _fs?: Fs
|
||||
get fs(): Fs {
|
||||
return (this._fs ??= new Fs({ client: this.client }))
|
||||
}
|
||||
|
||||
private _command?: Command2
|
||||
get command(): Command2 {
|
||||
return (this._command ??= new Command2({ client: this.client }))
|
||||
}
|
||||
|
||||
private _skill?: Skill
|
||||
get skill(): Skill {
|
||||
return (this._skill ??= new Skill({ client: this.client }))
|
||||
}
|
||||
|
||||
private _event?: Event2
|
||||
get event(): Event2 {
|
||||
return (this._event ??= new Event2({ client: this.client }))
|
||||
}
|
||||
}
|
||||
|
||||
export class Control extends HeyApiClient {
|
||||
/**
|
||||
* Get next TUI request
|
||||
@@ -5557,6 +4914,654 @@ export class Tui extends HeyApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
export class Health extends HeyApiClient {
|
||||
/**
|
||||
* Check v2 server health
|
||||
*
|
||||
* Check whether the v2 API server is ready to accept requests.
|
||||
*/
|
||||
public get<ThrowOnError extends boolean = false>(options?: Options<never, ThrowOnError>) {
|
||||
return (options?.client ?? this.client).get<V2HealthGetResponses, V2HealthGetErrors, ThrowOnError>({
|
||||
url: "/api/health",
|
||||
...options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Agent extends HeyApiClient {
|
||||
/**
|
||||
* List v2 agents
|
||||
*
|
||||
* Retrieve currently registered v2 agents.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<V2AgentListResponses, V2AgentListErrors, ThrowOnError>({
|
||||
url: "/api/agent",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Permission2 extends HeyApiClient {
|
||||
/**
|
||||
* List session permission requests
|
||||
*
|
||||
* Retrieve pending permission requests owned by a session.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "sessionID" }] }])
|
||||
return (options?.client ?? this.client).get<
|
||||
V2SessionPermissionListResponses,
|
||||
V2SessionPermissionListErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/api/session/{sessionID}/permission/request",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Reply to pending permission request
|
||||
*
|
||||
* Respond to a pending permission request owned by a session.
|
||||
*/
|
||||
public reply<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
requestID: string
|
||||
reply?: PermissionV2Reply
|
||||
message?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "sessionID" },
|
||||
{ in: "path", key: "requestID" },
|
||||
{ in: "body", key: "reply" },
|
||||
{ in: "body", key: "message" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<
|
||||
V2SessionPermissionReplyResponses,
|
||||
V2SessionPermissionReplyErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/api/session/{sessionID}/permission/request/{requestID}/reply",
|
||||
...options,
|
||||
...params,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
...params.headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Session3 extends HeyApiClient {
|
||||
/**
|
||||
* List v2 sessions
|
||||
*
|
||||
* Retrieve sessions in the requested order. Items keep that order across pages; use cursor.next or cursor.previous to move through the ordered list.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
workspace?: string
|
||||
limit?: number
|
||||
order?: "asc" | "desc"
|
||||
search?: string
|
||||
directory?: string
|
||||
project?: string
|
||||
subpath?: string
|
||||
cursor?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "workspace" },
|
||||
{ in: "query", key: "limit" },
|
||||
{ in: "query", key: "order" },
|
||||
{ in: "query", key: "search" },
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "project" },
|
||||
{ in: "query", key: "subpath" },
|
||||
{ in: "query", key: "cursor" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2SessionListResponses, V2SessionListErrors, ThrowOnError>({
|
||||
url: "/api/session",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Send v2 message
|
||||
*
|
||||
* Create a v2 session message and queue it for the agent loop.
|
||||
*/
|
||||
public prompt<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
prompt?: Prompt
|
||||
delivery?: SessionDelivery
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "sessionID" },
|
||||
{ in: "body", key: "prompt" },
|
||||
{ in: "body", key: "delivery" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<V2SessionPromptResponses, V2SessionPromptErrors, ThrowOnError>({
|
||||
url: "/api/session/{sessionID}/prompt",
|
||||
...options,
|
||||
...params,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
...params.headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact v2 session
|
||||
*
|
||||
* Compact a v2 session conversation.
|
||||
*/
|
||||
public compact<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "sessionID" }] }])
|
||||
return (options?.client ?? this.client).post<V2SessionCompactResponses, V2SessionCompactErrors, ThrowOnError>({
|
||||
url: "/api/session/{sessionID}/compact",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for v2 session
|
||||
*
|
||||
* Wait for a v2 session agent loop to become idle.
|
||||
*/
|
||||
public wait<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "sessionID" }] }])
|
||||
return (options?.client ?? this.client).post<V2SessionWaitResponses, V2SessionWaitErrors, ThrowOnError>({
|
||||
url: "/api/session/{sessionID}/wait",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get v2 session context
|
||||
*
|
||||
* Retrieve the active context messages for a v2 session (all messages after the last compaction).
|
||||
*/
|
||||
public context<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "sessionID" }] }])
|
||||
return (options?.client ?? this.client).get<V2SessionContextResponses, V2SessionContextErrors, ThrowOnError>({
|
||||
url: "/api/session/{sessionID}/context",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get v2 session messages
|
||||
*
|
||||
* Retrieve projected v2 messages for a session. Items keep the requested order across pages; use cursor.next or cursor.previous to move through the ordered timeline.
|
||||
*/
|
||||
public messages<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
sessionID: string
|
||||
limit?: number
|
||||
order?: "asc" | "desc"
|
||||
cursor?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "sessionID" },
|
||||
{ in: "query", key: "limit" },
|
||||
{ in: "query", key: "order" },
|
||||
{ in: "query", key: "cursor" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2SessionMessagesResponses, V2SessionMessagesErrors, ThrowOnError>({
|
||||
url: "/api/session/{sessionID}/message",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
private _permission?: Permission2
|
||||
get permission(): Permission2 {
|
||||
return (this._permission ??= new Permission2({ client: this.client }))
|
||||
}
|
||||
}
|
||||
|
||||
export class Model extends HeyApiClient {
|
||||
/**
|
||||
* List v2 models
|
||||
*
|
||||
* Retrieve available v2 models ordered by release date.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<V2ModelListResponses, V2ModelListErrors, ThrowOnError>({
|
||||
url: "/api/model",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Provider2 extends HeyApiClient {
|
||||
/**
|
||||
* List v2 providers
|
||||
*
|
||||
* Retrieve active v2 AI providers so clients can show provider availability and configuration.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<V2ProviderListResponses, V2ProviderListErrors, ThrowOnError>({
|
||||
url: "/api/provider",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get v2 provider
|
||||
*
|
||||
* Retrieve a single v2 AI provider so clients can inspect its availability and endpoint settings.
|
||||
*/
|
||||
public get<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
providerID: string
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "providerID" },
|
||||
{ in: "query", key: "location" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2ProviderGetResponses, V2ProviderGetErrors, ThrowOnError>({
|
||||
url: "/api/provider/{providerID}",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Request extends HeyApiClient {
|
||||
/**
|
||||
* List pending permission requests
|
||||
*
|
||||
* Retrieve pending permission requests for a location.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<
|
||||
V2PermissionRequestListResponses,
|
||||
V2PermissionRequestListErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/api/permission/request",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Saved extends HeyApiClient {
|
||||
/**
|
||||
* List saved permissions
|
||||
*
|
||||
* Retrieve saved permissions, optionally filtered by project.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
projectID?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "projectID" }] }])
|
||||
return (options?.client ?? this.client).get<
|
||||
V2PermissionSavedListResponses,
|
||||
V2PermissionSavedListErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/api/permission/saved",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove saved permission
|
||||
*
|
||||
* Remove a saved permission by ID.
|
||||
*/
|
||||
public remove<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
id: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "id" }] }])
|
||||
return (options?.client ?? this.client).delete<
|
||||
V2PermissionSavedRemoveResponses,
|
||||
V2PermissionSavedRemoveErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/api/permission/saved/{id}",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Permission3 extends HeyApiClient {
|
||||
private _request?: Request
|
||||
get request(): Request {
|
||||
return (this._request ??= new Request({ client: this.client }))
|
||||
}
|
||||
|
||||
private _saved?: Saved
|
||||
get saved(): Saved {
|
||||
return (this._saved ??= new Saved({ client: this.client }))
|
||||
}
|
||||
}
|
||||
|
||||
export class Fs extends HeyApiClient {
|
||||
/**
|
||||
* Read file
|
||||
*
|
||||
* Read one file relative to the requested location.
|
||||
*/
|
||||
public read<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
path: string
|
||||
reference?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "location" },
|
||||
{ in: "query", key: "path" },
|
||||
{ in: "query", key: "reference" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2FsReadResponses, V2FsReadErrors, ThrowOnError>({
|
||||
url: "/api/fs/read",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* List directory
|
||||
*
|
||||
* List direct children of one directory relative to the requested location.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
path?: string
|
||||
reference?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "location" },
|
||||
{ in: "query", key: "path" },
|
||||
{ in: "query", key: "reference" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<V2FsListResponses, V2FsListErrors, ThrowOnError>({
|
||||
url: "/api/fs/list",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Command2 extends HeyApiClient {
|
||||
/**
|
||||
* List v2 commands
|
||||
*
|
||||
* Retrieve currently registered v2 commands.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<V2CommandListResponses, V2CommandListErrors, ThrowOnError>({
|
||||
url: "/api/command",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Skill extends HeyApiClient {
|
||||
/**
|
||||
* List v2 skills
|
||||
*
|
||||
* Retrieve currently registered v2 skills.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).get<V2SkillListResponses, V2SkillListErrors, ThrowOnError>({
|
||||
url: "/api/skill",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Event2 extends HeyApiClient {
|
||||
/**
|
||||
* Subscribe to v2 events
|
||||
*
|
||||
* Subscribe to native EventV2 payloads for a location.
|
||||
*/
|
||||
public subscribe<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "location" }] }])
|
||||
return (options?.client ?? this.client).sse.get<V2EventSubscribeResponses, V2EventSubscribeErrors, ThrowOnError>({
|
||||
url: "/api/event",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class V2 extends HeyApiClient {
|
||||
private _health?: Health
|
||||
get health(): Health {
|
||||
return (this._health ??= new Health({ client: this.client }))
|
||||
}
|
||||
|
||||
private _agent?: Agent
|
||||
get agent(): Agent {
|
||||
return (this._agent ??= new Agent({ client: this.client }))
|
||||
}
|
||||
|
||||
private _session?: Session3
|
||||
get session(): Session3 {
|
||||
return (this._session ??= new Session3({ client: this.client }))
|
||||
}
|
||||
|
||||
private _model?: Model
|
||||
get model(): Model {
|
||||
return (this._model ??= new Model({ client: this.client }))
|
||||
}
|
||||
|
||||
private _provider?: Provider2
|
||||
get provider(): Provider2 {
|
||||
return (this._provider ??= new Provider2({ client: this.client }))
|
||||
}
|
||||
|
||||
private _permission?: Permission3
|
||||
get permission(): Permission3 {
|
||||
return (this._permission ??= new Permission3({ client: this.client }))
|
||||
}
|
||||
|
||||
private _fs?: Fs
|
||||
get fs(): Fs {
|
||||
return (this._fs ??= new Fs({ client: this.client }))
|
||||
}
|
||||
|
||||
private _command?: Command2
|
||||
get command(): Command2 {
|
||||
return (this._command ??= new Command2({ client: this.client }))
|
||||
}
|
||||
|
||||
private _skill?: Skill
|
||||
get skill(): Skill {
|
||||
return (this._skill ??= new Skill({ client: this.client }))
|
||||
}
|
||||
|
||||
private _event?: Event2
|
||||
get event(): Event2 {
|
||||
return (this._event ??= new Event2({ client: this.client }))
|
||||
}
|
||||
}
|
||||
|
||||
export class OpencodeClient extends HeyApiClient {
|
||||
public static readonly __registry = new HeyApiRegistry<OpencodeClient>()
|
||||
|
||||
@@ -5690,13 +5695,13 @@ export class OpencodeClient extends HeyApiClient {
|
||||
return (this._sync ??= new Sync({ client: this.client }))
|
||||
}
|
||||
|
||||
private _v2?: V2
|
||||
get v2(): V2 {
|
||||
return (this._v2 ??= new V2({ client: this.client }))
|
||||
}
|
||||
|
||||
private _tui?: Tui
|
||||
get tui(): Tui {
|
||||
return (this._tui ??= new Tui({ client: this.client }))
|
||||
}
|
||||
|
||||
private _v2?: V2
|
||||
get v2(): V2 {
|
||||
return (this._v2 ??= new V2({ client: this.client }))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2499,56 +2499,6 @@ export type SessionBusyError = {
|
||||
message: string
|
||||
}
|
||||
|
||||
export type V2SessionsResponse = {
|
||||
items: Array<SessionV2Info>
|
||||
cursor: {
|
||||
previous?: string
|
||||
next?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type InvalidCursorError = {
|
||||
_tag: "InvalidCursorError"
|
||||
message: string
|
||||
}
|
||||
|
||||
export type UnauthorizedError = {
|
||||
_tag: "UnauthorizedError"
|
||||
message: string
|
||||
}
|
||||
|
||||
export type SessionNotFoundError = {
|
||||
_tag: "SessionNotFoundError"
|
||||
sessionID: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export type ServiceUnavailableError = {
|
||||
_tag: "ServiceUnavailableError"
|
||||
message: string
|
||||
service?: string
|
||||
}
|
||||
|
||||
export type UnknownError1 = {
|
||||
_tag: "UnknownError"
|
||||
message: string
|
||||
ref?: string
|
||||
}
|
||||
|
||||
export type V2SessionMessagesResponse = {
|
||||
items: Array<SessionMessage>
|
||||
cursor: {
|
||||
previous?: string
|
||||
next?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type ProviderNotFoundError = {
|
||||
_tag: "ProviderNotFoundError"
|
||||
providerID: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export type EventTuiPromptAppend = {
|
||||
type: "tui.prompt.append"
|
||||
properties: {
|
||||
@@ -2625,6 +2575,56 @@ export type WorkspaceWarpError = {
|
||||
}
|
||||
}
|
||||
|
||||
export type UnauthorizedError = {
|
||||
_tag: "UnauthorizedError"
|
||||
message: string
|
||||
}
|
||||
|
||||
export type V2SessionsResponse = {
|
||||
items: Array<SessionV2Info>
|
||||
cursor: {
|
||||
previous?: string
|
||||
next?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type InvalidCursorError = {
|
||||
_tag: "InvalidCursorError"
|
||||
message: string
|
||||
}
|
||||
|
||||
export type SessionNotFoundError = {
|
||||
_tag: "SessionNotFoundError"
|
||||
sessionID: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export type ServiceUnavailableError = {
|
||||
_tag: "ServiceUnavailableError"
|
||||
message: string
|
||||
service?: string
|
||||
}
|
||||
|
||||
export type UnknownError1 = {
|
||||
_tag: "UnknownError"
|
||||
message: string
|
||||
ref?: string
|
||||
}
|
||||
|
||||
export type V2SessionMessagesResponse = {
|
||||
items: Array<SessionMessage>
|
||||
cursor: {
|
||||
previous?: string
|
||||
next?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type ProviderNotFoundError = {
|
||||
_tag: "ProviderNotFoundError"
|
||||
providerID: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export type EffectHttpApiErrorForbidden = {
|
||||
_tag: "Forbidden"
|
||||
}
|
||||
@@ -3452,6 +3452,49 @@ export type ProjectCopyCopy = {
|
||||
directory: string
|
||||
}
|
||||
|
||||
export type LocationInfo = {
|
||||
directory: string
|
||||
workspaceID?: string
|
||||
project: {
|
||||
id: string
|
||||
directory: string
|
||||
}
|
||||
}
|
||||
|
||||
export type PermissionV2Effect = "allow" | "deny" | "ask"
|
||||
|
||||
export type PermissionV2Rule = {
|
||||
action: string
|
||||
resource: string
|
||||
effect: PermissionV2Effect
|
||||
}
|
||||
|
||||
export type PermissionV2Ruleset = Array<PermissionV2Rule>
|
||||
|
||||
export type AgentV2Info = {
|
||||
id: string
|
||||
model?: {
|
||||
id: string
|
||||
providerID: string
|
||||
variant?: string
|
||||
}
|
||||
request: {
|
||||
headers: {
|
||||
[key: string]: string
|
||||
}
|
||||
body: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
system?: string
|
||||
description?: string
|
||||
mode: "subagent" | "primary" | "all"
|
||||
hidden: boolean
|
||||
color?: string | "primary" | "secondary" | "accent" | "success" | "warning" | "error" | "info"
|
||||
steps?: number
|
||||
permissions: PermissionV2Ruleset
|
||||
}
|
||||
|
||||
export type LocationRef = {
|
||||
directory: string
|
||||
workspaceID?: string
|
||||
@@ -3692,15 +3735,6 @@ export type SessionMessage =
|
||||
| SessionMessageAssistant
|
||||
| SessionMessageCompaction
|
||||
|
||||
export type LocationInfo = {
|
||||
directory: string
|
||||
workspaceID?: string
|
||||
project: {
|
||||
id: string
|
||||
directory: string
|
||||
}
|
||||
}
|
||||
|
||||
export type ProviderV2Info = {
|
||||
id: string
|
||||
name: string
|
||||
@@ -8188,767 +8222,6 @@ export type SyncHistoryListResponses = {
|
||||
|
||||
export type SyncHistoryListResponse = SyncHistoryListResponses[keyof SyncHistoryListResponses]
|
||||
|
||||
export type V2SessionListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
workspace?: string
|
||||
limit?: number
|
||||
order?: "asc" | "desc"
|
||||
search?: string
|
||||
directory?: string
|
||||
project?: string
|
||||
subpath?: string
|
||||
/**
|
||||
* Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response.
|
||||
*/
|
||||
cursor?: string
|
||||
}
|
||||
url: "/api/session"
|
||||
}
|
||||
|
||||
export type V2SessionListErrors = {
|
||||
/**
|
||||
* InvalidCursorError | InvalidRequestError
|
||||
*/
|
||||
400: InvalidCursorError | InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2SessionListError = V2SessionListErrors[keyof V2SessionListErrors]
|
||||
|
||||
export type V2SessionListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: V2SessionsResponse
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SessionListResponse = V2SessionListResponses[keyof V2SessionListResponses]
|
||||
|
||||
export type V2SessionPromptData = {
|
||||
body?: {
|
||||
prompt: Prompt
|
||||
delivery?: SessionDelivery
|
||||
}
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/api/session/{sessionID}/prompt"
|
||||
}
|
||||
|
||||
export type V2SessionPromptErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2SessionPromptError = V2SessionPromptErrors[keyof V2SessionPromptErrors]
|
||||
|
||||
export type V2SessionPromptResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: SessionMessage
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SessionPromptResponse = V2SessionPromptResponses[keyof V2SessionPromptResponses]
|
||||
|
||||
export type V2SessionCompactData = {
|
||||
body?: never
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/api/session/{sessionID}/compact"
|
||||
}
|
||||
|
||||
export type V2SessionCompactErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2SessionCompactError = V2SessionCompactErrors[keyof V2SessionCompactErrors]
|
||||
|
||||
export type V2SessionCompactResponses = {
|
||||
/**
|
||||
* <No Content>
|
||||
*/
|
||||
204: void
|
||||
}
|
||||
|
||||
export type V2SessionCompactResponse = V2SessionCompactResponses[keyof V2SessionCompactResponses]
|
||||
|
||||
export type V2SessionWaitData = {
|
||||
body?: never
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/api/session/{sessionID}/wait"
|
||||
}
|
||||
|
||||
export type V2SessionWaitErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2SessionWaitError = V2SessionWaitErrors[keyof V2SessionWaitErrors]
|
||||
|
||||
export type V2SessionWaitResponses = {
|
||||
/**
|
||||
* <No Content>
|
||||
*/
|
||||
204: void
|
||||
}
|
||||
|
||||
export type V2SessionWaitResponse = V2SessionWaitResponses[keyof V2SessionWaitResponses]
|
||||
|
||||
export type V2SessionContextData = {
|
||||
body?: never
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/api/session/{sessionID}/context"
|
||||
}
|
||||
|
||||
export type V2SessionContextErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
/**
|
||||
* UnknownError
|
||||
*/
|
||||
500: UnknownError1
|
||||
}
|
||||
|
||||
export type V2SessionContextError = V2SessionContextErrors[keyof V2SessionContextErrors]
|
||||
|
||||
export type V2SessionContextResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: Array<SessionMessage>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SessionContextResponse = V2SessionContextResponses[keyof V2SessionContextResponses]
|
||||
|
||||
export type V2SessionMessagesData = {
|
||||
body?: never
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
limit?: number
|
||||
order?: "asc" | "desc"
|
||||
/**
|
||||
* Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response. Do not combine with order.
|
||||
*/
|
||||
cursor?: string
|
||||
}
|
||||
url: "/api/session/{sessionID}/message"
|
||||
}
|
||||
|
||||
export type V2SessionMessagesErrors = {
|
||||
/**
|
||||
* InvalidCursorError | InvalidRequestError
|
||||
*/
|
||||
400: InvalidCursorError | InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
/**
|
||||
* UnknownError
|
||||
*/
|
||||
500: UnknownError1
|
||||
}
|
||||
|
||||
export type V2SessionMessagesError = V2SessionMessagesErrors[keyof V2SessionMessagesErrors]
|
||||
|
||||
export type V2SessionMessagesResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: V2SessionMessagesResponse
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SessionMessagesResponse2 = V2SessionMessagesResponses[keyof V2SessionMessagesResponses]
|
||||
|
||||
export type V2ModelListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/model"
|
||||
}
|
||||
|
||||
export type V2ModelListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2ModelListError = V2ModelListErrors[keyof V2ModelListErrors]
|
||||
|
||||
export type V2ModelListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<ModelV2Info>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2ModelListResponse = V2ModelListResponses[keyof V2ModelListResponses]
|
||||
|
||||
export type V2ProviderListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/provider"
|
||||
}
|
||||
|
||||
export type V2ProviderListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2ProviderListError = V2ProviderListErrors[keyof V2ProviderListErrors]
|
||||
|
||||
export type V2ProviderListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<ProviderV2Info>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2ProviderListResponse = V2ProviderListResponses[keyof V2ProviderListResponses]
|
||||
|
||||
export type V2ProviderGetData = {
|
||||
body?: never
|
||||
path: {
|
||||
providerID: string
|
||||
}
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/provider/{providerID}"
|
||||
}
|
||||
|
||||
export type V2ProviderGetErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* ProviderNotFoundError
|
||||
*/
|
||||
404: ProviderNotFoundError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2ProviderGetError = V2ProviderGetErrors[keyof V2ProviderGetErrors]
|
||||
|
||||
export type V2ProviderGetResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: ProviderV2Info
|
||||
}
|
||||
}
|
||||
|
||||
export type V2ProviderGetResponse = V2ProviderGetResponses[keyof V2ProviderGetResponses]
|
||||
|
||||
export type V2PermissionRequestListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/permission/request"
|
||||
}
|
||||
|
||||
export type V2PermissionRequestListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2PermissionRequestListError = V2PermissionRequestListErrors[keyof V2PermissionRequestListErrors]
|
||||
|
||||
export type V2PermissionRequestListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<PermissionV2Request>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2PermissionRequestListResponse = V2PermissionRequestListResponses[keyof V2PermissionRequestListResponses]
|
||||
|
||||
export type V2SessionPermissionListData = {
|
||||
body?: never
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: never
|
||||
url: "/api/session/{sessionID}/permission/request"
|
||||
}
|
||||
|
||||
export type V2SessionPermissionListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
}
|
||||
|
||||
export type V2SessionPermissionListError = V2SessionPermissionListErrors[keyof V2SessionPermissionListErrors]
|
||||
|
||||
export type V2SessionPermissionListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: Array<PermissionV2Request>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SessionPermissionListResponse = V2SessionPermissionListResponses[keyof V2SessionPermissionListResponses]
|
||||
|
||||
export type V2SessionPermissionReplyData = {
|
||||
body?: {
|
||||
reply: PermissionV2Reply
|
||||
message?: string
|
||||
}
|
||||
path: {
|
||||
sessionID: string
|
||||
requestID: string
|
||||
}
|
||||
query?: never
|
||||
url: "/api/session/{sessionID}/permission/request/{requestID}/reply"
|
||||
}
|
||||
|
||||
export type V2SessionPermissionReplyErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError | PermissionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError | PermissionNotFoundError
|
||||
}
|
||||
|
||||
export type V2SessionPermissionReplyError = V2SessionPermissionReplyErrors[keyof V2SessionPermissionReplyErrors]
|
||||
|
||||
export type V2SessionPermissionReplyResponses = {
|
||||
/**
|
||||
* <No Content>
|
||||
*/
|
||||
204: void
|
||||
}
|
||||
|
||||
export type V2SessionPermissionReplyResponse =
|
||||
V2SessionPermissionReplyResponses[keyof V2SessionPermissionReplyResponses]
|
||||
|
||||
export type V2PermissionSavedListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
projectID?: string
|
||||
}
|
||||
url: "/api/permission/saved"
|
||||
}
|
||||
|
||||
export type V2PermissionSavedListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2PermissionSavedListError = V2PermissionSavedListErrors[keyof V2PermissionSavedListErrors]
|
||||
|
||||
export type V2PermissionSavedListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: Array<PermissionSavedInfo>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2PermissionSavedListResponse = V2PermissionSavedListResponses[keyof V2PermissionSavedListResponses]
|
||||
|
||||
export type V2PermissionSavedRemoveData = {
|
||||
body?: never
|
||||
path: {
|
||||
id: string
|
||||
}
|
||||
query?: never
|
||||
url: "/api/permission/saved/{id}"
|
||||
}
|
||||
|
||||
export type V2PermissionSavedRemoveErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2PermissionSavedRemoveError = V2PermissionSavedRemoveErrors[keyof V2PermissionSavedRemoveErrors]
|
||||
|
||||
export type V2PermissionSavedRemoveResponses = {
|
||||
/**
|
||||
* <No Content>
|
||||
*/
|
||||
204: void
|
||||
}
|
||||
|
||||
export type V2PermissionSavedRemoveResponse = V2PermissionSavedRemoveResponses[keyof V2PermissionSavedRemoveResponses]
|
||||
|
||||
export type V2FsReadData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
path: string
|
||||
reference?: string
|
||||
}
|
||||
url: "/api/fs/read"
|
||||
}
|
||||
|
||||
export type V2FsReadErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2FsReadError = V2FsReadErrors[keyof V2FsReadErrors]
|
||||
|
||||
export type V2FsReadResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: LocationFileSystemTextContent | LocationFileSystemBinaryContent
|
||||
}
|
||||
}
|
||||
|
||||
export type V2FsReadResponse = V2FsReadResponses[keyof V2FsReadResponses]
|
||||
|
||||
export type V2FsListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
path?: string
|
||||
reference?: string
|
||||
}
|
||||
url: "/api/fs/list"
|
||||
}
|
||||
|
||||
export type V2FsListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2FsListError = V2FsListErrors[keyof V2FsListErrors]
|
||||
|
||||
export type V2FsListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<LocationFileSystemEntry>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2FsListResponse = V2FsListResponses[keyof V2FsListResponses]
|
||||
|
||||
export type V2CommandListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/command"
|
||||
}
|
||||
|
||||
export type V2CommandListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2CommandListError = V2CommandListErrors[keyof V2CommandListErrors]
|
||||
|
||||
export type V2CommandListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<CommandV2Info>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2CommandListResponse = V2CommandListResponses[keyof V2CommandListResponses]
|
||||
|
||||
export type V2SkillListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/skill"
|
||||
}
|
||||
|
||||
export type V2SkillListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2SkillListError = V2SkillListErrors[keyof V2SkillListErrors]
|
||||
|
||||
export type V2SkillListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<SkillV2Info>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SkillListResponse = V2SkillListResponses[keyof V2SkillListResponses]
|
||||
|
||||
export type V2EventSubscribeData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/event"
|
||||
}
|
||||
|
||||
export type V2EventSubscribeErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2EventSubscribeError = V2EventSubscribeErrors[keyof V2EventSubscribeErrors]
|
||||
|
||||
export type V2EventSubscribeResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: string
|
||||
}
|
||||
|
||||
export type V2EventSubscribeResponse = V2EventSubscribeResponses[keyof V2EventSubscribeResponses]
|
||||
|
||||
export type TuiAppendPromptData = {
|
||||
body?: {
|
||||
text: string
|
||||
@@ -9564,6 +8837,821 @@ export type ExperimentalWorkspaceWarpResponses = {
|
||||
export type ExperimentalWorkspaceWarpResponse =
|
||||
ExperimentalWorkspaceWarpResponses[keyof ExperimentalWorkspaceWarpResponses]
|
||||
|
||||
export type V2HealthGetData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: never
|
||||
url: "/api/health"
|
||||
}
|
||||
|
||||
export type V2HealthGetErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2HealthGetError = V2HealthGetErrors[keyof V2HealthGetErrors]
|
||||
|
||||
export type V2HealthGetResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
healthy: true
|
||||
}
|
||||
}
|
||||
|
||||
export type V2HealthGetResponse = V2HealthGetResponses[keyof V2HealthGetResponses]
|
||||
|
||||
export type V2AgentListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/agent"
|
||||
}
|
||||
|
||||
export type V2AgentListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2AgentListError = V2AgentListErrors[keyof V2AgentListErrors]
|
||||
|
||||
export type V2AgentListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<AgentV2Info>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2AgentListResponse = V2AgentListResponses[keyof V2AgentListResponses]
|
||||
|
||||
export type V2SessionListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
workspace?: string
|
||||
limit?: number
|
||||
order?: "asc" | "desc"
|
||||
search?: string
|
||||
directory?: string
|
||||
project?: string
|
||||
subpath?: string
|
||||
/**
|
||||
* Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response.
|
||||
*/
|
||||
cursor?: string
|
||||
}
|
||||
url: "/api/session"
|
||||
}
|
||||
|
||||
export type V2SessionListErrors = {
|
||||
/**
|
||||
* InvalidCursorError | InvalidRequestError
|
||||
*/
|
||||
400: InvalidCursorError | InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2SessionListError = V2SessionListErrors[keyof V2SessionListErrors]
|
||||
|
||||
export type V2SessionListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: V2SessionsResponse
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SessionListResponse = V2SessionListResponses[keyof V2SessionListResponses]
|
||||
|
||||
export type V2SessionPromptData = {
|
||||
body?: {
|
||||
prompt: Prompt
|
||||
delivery?: SessionDelivery
|
||||
}
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: never
|
||||
url: "/api/session/{sessionID}/prompt"
|
||||
}
|
||||
|
||||
export type V2SessionPromptErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2SessionPromptError = V2SessionPromptErrors[keyof V2SessionPromptErrors]
|
||||
|
||||
export type V2SessionPromptResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: SessionMessage
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SessionPromptResponse = V2SessionPromptResponses[keyof V2SessionPromptResponses]
|
||||
|
||||
export type V2SessionCompactData = {
|
||||
body?: never
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: never
|
||||
url: "/api/session/{sessionID}/compact"
|
||||
}
|
||||
|
||||
export type V2SessionCompactErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2SessionCompactError = V2SessionCompactErrors[keyof V2SessionCompactErrors]
|
||||
|
||||
export type V2SessionCompactResponses = {
|
||||
/**
|
||||
* <No Content>
|
||||
*/
|
||||
204: void
|
||||
}
|
||||
|
||||
export type V2SessionCompactResponse = V2SessionCompactResponses[keyof V2SessionCompactResponses]
|
||||
|
||||
export type V2SessionWaitData = {
|
||||
body?: never
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: never
|
||||
url: "/api/session/{sessionID}/wait"
|
||||
}
|
||||
|
||||
export type V2SessionWaitErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2SessionWaitError = V2SessionWaitErrors[keyof V2SessionWaitErrors]
|
||||
|
||||
export type V2SessionWaitResponses = {
|
||||
/**
|
||||
* <No Content>
|
||||
*/
|
||||
204: void
|
||||
}
|
||||
|
||||
export type V2SessionWaitResponse = V2SessionWaitResponses[keyof V2SessionWaitResponses]
|
||||
|
||||
export type V2SessionContextData = {
|
||||
body?: never
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: never
|
||||
url: "/api/session/{sessionID}/context"
|
||||
}
|
||||
|
||||
export type V2SessionContextErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
/**
|
||||
* UnknownError
|
||||
*/
|
||||
500: UnknownError1
|
||||
}
|
||||
|
||||
export type V2SessionContextError = V2SessionContextErrors[keyof V2SessionContextErrors]
|
||||
|
||||
export type V2SessionContextResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: Array<SessionMessage>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SessionContextResponse = V2SessionContextResponses[keyof V2SessionContextResponses]
|
||||
|
||||
export type V2SessionMessagesData = {
|
||||
body?: never
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: {
|
||||
limit?: number
|
||||
order?: "asc" | "desc"
|
||||
/**
|
||||
* Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response. Do not combine with order.
|
||||
*/
|
||||
cursor?: string
|
||||
}
|
||||
url: "/api/session/{sessionID}/message"
|
||||
}
|
||||
|
||||
export type V2SessionMessagesErrors = {
|
||||
/**
|
||||
* InvalidCursorError | InvalidRequestError
|
||||
*/
|
||||
400: InvalidCursorError | InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
/**
|
||||
* UnknownError
|
||||
*/
|
||||
500: UnknownError1
|
||||
}
|
||||
|
||||
export type V2SessionMessagesError = V2SessionMessagesErrors[keyof V2SessionMessagesErrors]
|
||||
|
||||
export type V2SessionMessagesResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: V2SessionMessagesResponse
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SessionMessagesResponse2 = V2SessionMessagesResponses[keyof V2SessionMessagesResponses]
|
||||
|
||||
export type V2ModelListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/model"
|
||||
}
|
||||
|
||||
export type V2ModelListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2ModelListError = V2ModelListErrors[keyof V2ModelListErrors]
|
||||
|
||||
export type V2ModelListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<ModelV2Info>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2ModelListResponse = V2ModelListResponses[keyof V2ModelListResponses]
|
||||
|
||||
export type V2ProviderListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/provider"
|
||||
}
|
||||
|
||||
export type V2ProviderListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2ProviderListError = V2ProviderListErrors[keyof V2ProviderListErrors]
|
||||
|
||||
export type V2ProviderListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<ProviderV2Info>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2ProviderListResponse = V2ProviderListResponses[keyof V2ProviderListResponses]
|
||||
|
||||
export type V2ProviderGetData = {
|
||||
body?: never
|
||||
path: {
|
||||
providerID: string
|
||||
}
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/provider/{providerID}"
|
||||
}
|
||||
|
||||
export type V2ProviderGetErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* ProviderNotFoundError
|
||||
*/
|
||||
404: ProviderNotFoundError
|
||||
/**
|
||||
* ServiceUnavailableError
|
||||
*/
|
||||
503: ServiceUnavailableError
|
||||
}
|
||||
|
||||
export type V2ProviderGetError = V2ProviderGetErrors[keyof V2ProviderGetErrors]
|
||||
|
||||
export type V2ProviderGetResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: ProviderV2Info
|
||||
}
|
||||
}
|
||||
|
||||
export type V2ProviderGetResponse = V2ProviderGetResponses[keyof V2ProviderGetResponses]
|
||||
|
||||
export type V2PermissionRequestListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/permission/request"
|
||||
}
|
||||
|
||||
export type V2PermissionRequestListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2PermissionRequestListError = V2PermissionRequestListErrors[keyof V2PermissionRequestListErrors]
|
||||
|
||||
export type V2PermissionRequestListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<PermissionV2Request>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2PermissionRequestListResponse = V2PermissionRequestListResponses[keyof V2PermissionRequestListResponses]
|
||||
|
||||
export type V2SessionPermissionListData = {
|
||||
body?: never
|
||||
path: {
|
||||
sessionID: string
|
||||
}
|
||||
query?: never
|
||||
url: "/api/session/{sessionID}/permission/request"
|
||||
}
|
||||
|
||||
export type V2SessionPermissionListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError
|
||||
}
|
||||
|
||||
export type V2SessionPermissionListError = V2SessionPermissionListErrors[keyof V2SessionPermissionListErrors]
|
||||
|
||||
export type V2SessionPermissionListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: Array<PermissionV2Request>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SessionPermissionListResponse = V2SessionPermissionListResponses[keyof V2SessionPermissionListResponses]
|
||||
|
||||
export type V2SessionPermissionReplyData = {
|
||||
body?: {
|
||||
reply: PermissionV2Reply
|
||||
message?: string
|
||||
}
|
||||
path: {
|
||||
sessionID: string
|
||||
requestID: string
|
||||
}
|
||||
query?: never
|
||||
url: "/api/session/{sessionID}/permission/request/{requestID}/reply"
|
||||
}
|
||||
|
||||
export type V2SessionPermissionReplyErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
/**
|
||||
* SessionNotFoundError | PermissionNotFoundError
|
||||
*/
|
||||
404: SessionNotFoundError | PermissionNotFoundError
|
||||
}
|
||||
|
||||
export type V2SessionPermissionReplyError = V2SessionPermissionReplyErrors[keyof V2SessionPermissionReplyErrors]
|
||||
|
||||
export type V2SessionPermissionReplyResponses = {
|
||||
/**
|
||||
* <No Content>
|
||||
*/
|
||||
204: void
|
||||
}
|
||||
|
||||
export type V2SessionPermissionReplyResponse =
|
||||
V2SessionPermissionReplyResponses[keyof V2SessionPermissionReplyResponses]
|
||||
|
||||
export type V2PermissionSavedListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
projectID?: string
|
||||
}
|
||||
url: "/api/permission/saved"
|
||||
}
|
||||
|
||||
export type V2PermissionSavedListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2PermissionSavedListError = V2PermissionSavedListErrors[keyof V2PermissionSavedListErrors]
|
||||
|
||||
export type V2PermissionSavedListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
data: Array<PermissionSavedInfo>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2PermissionSavedListResponse = V2PermissionSavedListResponses[keyof V2PermissionSavedListResponses]
|
||||
|
||||
export type V2PermissionSavedRemoveData = {
|
||||
body?: never
|
||||
path: {
|
||||
id: string
|
||||
}
|
||||
query?: never
|
||||
url: "/api/permission/saved/{id}"
|
||||
}
|
||||
|
||||
export type V2PermissionSavedRemoveErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2PermissionSavedRemoveError = V2PermissionSavedRemoveErrors[keyof V2PermissionSavedRemoveErrors]
|
||||
|
||||
export type V2PermissionSavedRemoveResponses = {
|
||||
/**
|
||||
* <No Content>
|
||||
*/
|
||||
204: void
|
||||
}
|
||||
|
||||
export type V2PermissionSavedRemoveResponse = V2PermissionSavedRemoveResponses[keyof V2PermissionSavedRemoveResponses]
|
||||
|
||||
export type V2FsReadData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
path: string
|
||||
reference?: string
|
||||
}
|
||||
url: "/api/fs/read"
|
||||
}
|
||||
|
||||
export type V2FsReadErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2FsReadError = V2FsReadErrors[keyof V2FsReadErrors]
|
||||
|
||||
export type V2FsReadResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: LocationFileSystemTextContent | LocationFileSystemBinaryContent
|
||||
}
|
||||
}
|
||||
|
||||
export type V2FsReadResponse = V2FsReadResponses[keyof V2FsReadResponses]
|
||||
|
||||
export type V2FsListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
path?: string
|
||||
reference?: string
|
||||
}
|
||||
url: "/api/fs/list"
|
||||
}
|
||||
|
||||
export type V2FsListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2FsListError = V2FsListErrors[keyof V2FsListErrors]
|
||||
|
||||
export type V2FsListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<LocationFileSystemEntry>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2FsListResponse = V2FsListResponses[keyof V2FsListResponses]
|
||||
|
||||
export type V2CommandListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/command"
|
||||
}
|
||||
|
||||
export type V2CommandListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2CommandListError = V2CommandListErrors[keyof V2CommandListErrors]
|
||||
|
||||
export type V2CommandListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<CommandV2Info>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2CommandListResponse = V2CommandListResponses[keyof V2CommandListResponses]
|
||||
|
||||
export type V2SkillListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/skill"
|
||||
}
|
||||
|
||||
export type V2SkillListErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2SkillListError = V2SkillListErrors[keyof V2SkillListErrors]
|
||||
|
||||
export type V2SkillListResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: {
|
||||
location: LocationInfo
|
||||
data: Array<SkillV2Info>
|
||||
}
|
||||
}
|
||||
|
||||
export type V2SkillListResponse = V2SkillListResponses[keyof V2SkillListResponses]
|
||||
|
||||
export type V2EventSubscribeData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
location?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
}
|
||||
url: "/api/event"
|
||||
}
|
||||
|
||||
export type V2EventSubscribeErrors = {
|
||||
/**
|
||||
* InvalidRequestError
|
||||
*/
|
||||
400: InvalidRequestError
|
||||
/**
|
||||
* UnauthorizedError
|
||||
*/
|
||||
401: UnauthorizedError
|
||||
}
|
||||
|
||||
export type V2EventSubscribeError = V2EventSubscribeErrors[keyof V2EventSubscribeErrors]
|
||||
|
||||
export type V2EventSubscribeResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: string
|
||||
}
|
||||
|
||||
export type V2EventSubscribeResponse = V2EventSubscribeResponses[keyof V2EventSubscribeResponses]
|
||||
|
||||
export type PtyConnectData = {
|
||||
body?: never
|
||||
path: {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/server",
|
||||
"version": "1.15.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
"./*": "./src/*.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@typescript/native-preview": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { HttpApi, OpenApi } from "effect/unstable/httpapi"
|
||||
import { SchemaErrorMiddleware } from "./middleware/schema-error"
|
||||
import { MessageGroup } from "./groups/v2/message"
|
||||
import { ModelGroup } from "./groups/v2/model"
|
||||
import { ProviderGroup } from "./groups/v2/provider"
|
||||
import { SessionGroup } from "./groups/v2/session"
|
||||
import { PermissionGroup, PermissionSavedGroup, SessionPermissionGroup } from "./groups/v2/permission"
|
||||
import { FileSystemGroup } from "./groups/v2/fs"
|
||||
import { CommandGroup } from "./groups/v2/command"
|
||||
import { SkillGroup } from "./groups/v2/skill"
|
||||
import { EventGroup } from "./groups/v2/event"
|
||||
import { AgentGroup } from "./groups/v2/agent"
|
||||
import { HealthGroup } from "./groups/v2/health"
|
||||
|
||||
export const V2Api = HttpApi.make("v2")
|
||||
.add(HealthGroup)
|
||||
.add(AgentGroup)
|
||||
.add(SessionGroup)
|
||||
.add(MessageGroup)
|
||||
.add(ModelGroup)
|
||||
.add(ProviderGroup)
|
||||
.add(PermissionGroup)
|
||||
.add(SessionPermissionGroup)
|
||||
.add(PermissionSavedGroup)
|
||||
.add(FileSystemGroup)
|
||||
.add(CommandGroup)
|
||||
.add(SkillGroup)
|
||||
.add(EventGroup)
|
||||
.annotateMerge(
|
||||
OpenApi.annotations({
|
||||
title: "opencode experimental HttpApi",
|
||||
version: "0.0.1",
|
||||
description: "Experimental HttpApi surface for selected instance routes.",
|
||||
}),
|
||||
)
|
||||
.middleware(SchemaErrorMiddleware)
|
||||
@@ -0,0 +1,63 @@
|
||||
export * as ServerAuth from "./auth"
|
||||
|
||||
import { Config as EffectConfig, Context, Effect, Layer, Option, Redacted } from "effect"
|
||||
|
||||
export type Credentials = {
|
||||
password?: string
|
||||
username?: string
|
||||
}
|
||||
|
||||
export type DecodedCredentials = {
|
||||
readonly username: string
|
||||
readonly password: Redacted.Redacted
|
||||
}
|
||||
|
||||
export type Info = {
|
||||
readonly password: Option.Option<string>
|
||||
readonly username: string
|
||||
}
|
||||
|
||||
export class Config extends Context.Service<Config, Info>()("@opencode/ServerAuthConfig") {
|
||||
static layer(input: Info) {
|
||||
return Layer.succeed(this, this.of(input))
|
||||
}
|
||||
|
||||
static get defaultLayer() {
|
||||
return Layer.effect(
|
||||
this,
|
||||
Effect.gen(function* () {
|
||||
return Config.of(
|
||||
yield* EffectConfig.all({
|
||||
password: EffectConfig.string("OPENCODE_SERVER_PASSWORD").pipe(EffectConfig.option),
|
||||
username: EffectConfig.string("OPENCODE_SERVER_USERNAME").pipe(EffectConfig.withDefault("opencode")),
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function required(config: Info) {
|
||||
return Option.isSome(config.password) && config.password.value !== ""
|
||||
}
|
||||
|
||||
export function authorized(credentials: DecodedCredentials, config: Info) {
|
||||
return (
|
||||
Option.isSome(config.password) &&
|
||||
credentials.username === config.username &&
|
||||
Redacted.value(credentials.password) === config.password.value
|
||||
)
|
||||
}
|
||||
|
||||
export function header(credentials?: Credentials) {
|
||||
const password = credentials?.password ?? process.env.OPENCODE_SERVER_PASSWORD
|
||||
if (!password) return undefined
|
||||
|
||||
return `Basic ${Buffer.from(`${credentials?.username ?? process.env.OPENCODE_SERVER_USERNAME ?? "opencode"}:${password}`).toString("base64")}`
|
||||
}
|
||||
|
||||
export function headers(credentials?: Credentials) {
|
||||
const authorization = header(credentials)
|
||||
if (!authorization) return undefined
|
||||
return { Authorization: authorization }
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Schema } from "effect"
|
||||
|
||||
export class InvalidRequestError extends Schema.TaggedErrorClass<InvalidRequestError>()(
|
||||
"InvalidRequestError",
|
||||
{
|
||||
message: Schema.String,
|
||||
kind: Schema.optional(Schema.String),
|
||||
field: Schema.optional(Schema.String),
|
||||
},
|
||||
{ httpApiStatus: 400 },
|
||||
) {}
|
||||
|
||||
export class UnauthorizedError extends Schema.TaggedErrorClass<UnauthorizedError>()(
|
||||
"UnauthorizedError",
|
||||
{ message: Schema.String },
|
||||
{ httpApiStatus: 401 },
|
||||
) {}
|
||||
|
||||
export class ServiceUnavailableError extends Schema.TaggedErrorClass<ServiceUnavailableError>()(
|
||||
"ServiceUnavailableError",
|
||||
{
|
||||
message: Schema.String,
|
||||
service: Schema.optional(Schema.String),
|
||||
},
|
||||
{ httpApiStatus: 503 },
|
||||
) {}
|
||||
|
||||
export class UnknownError extends Schema.TaggedErrorClass<UnknownError>()(
|
||||
"UnknownError",
|
||||
{
|
||||
message: Schema.String,
|
||||
ref: Schema.optional(Schema.String),
|
||||
},
|
||||
{ httpApiStatus: 500 },
|
||||
) {}
|
||||
|
||||
export class ProviderNotFoundError extends Schema.TaggedErrorClass<ProviderNotFoundError>()(
|
||||
"ProviderNotFoundError",
|
||||
{
|
||||
providerID: Schema.String,
|
||||
message: Schema.String,
|
||||
},
|
||||
{ httpApiStatus: 404 },
|
||||
) {}
|
||||
|
||||
export class SessionNotFoundError extends Schema.TaggedErrorClass<SessionNotFoundError>()(
|
||||
"SessionNotFoundError",
|
||||
{
|
||||
sessionID: Schema.String,
|
||||
message: Schema.String,
|
||||
},
|
||||
{ httpApiStatus: 404 },
|
||||
) {}
|
||||
|
||||
export class InvalidCursorError extends Schema.TaggedErrorClass<InvalidCursorError>()(
|
||||
"InvalidCursorError",
|
||||
{ message: Schema.String },
|
||||
{ httpApiStatus: 400 },
|
||||
) {}
|
||||
|
||||
export class PermissionNotFoundError extends Schema.TaggedErrorClass<PermissionNotFoundError>()(
|
||||
"PermissionNotFoundError",
|
||||
{
|
||||
requestID: Schema.String,
|
||||
message: Schema.String,
|
||||
},
|
||||
{ httpApiStatus: 404 },
|
||||
) {}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { Schema } from "effect"
|
||||
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { V2Authorization } from "../../middleware/authorization"
|
||||
import { LocationQuery, locationQueryOpenApi, V2LocationMiddleware } from "./location"
|
||||
|
||||
export const AgentGroup = HttpApiGroup.make("v2.agent")
|
||||
.add(
|
||||
HttpApiEndpoint.get("agents", "/api/agent", {
|
||||
query: LocationQuery,
|
||||
success: Location.response(Schema.Array(AgentV2.Info)),
|
||||
})
|
||||
.annotateMerge(locationQueryOpenApi)
|
||||
.annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "v2.agent.list",
|
||||
summary: "List v2 agents",
|
||||
description: "Retrieve currently registered v2 agents.",
|
||||
}),
|
||||
),
|
||||
)
|
||||
.middleware(V2LocationMiddleware)
|
||||
.middleware(V2Authorization)
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Schema } from "effect"
|
||||
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { V2Authorization } from "../../middleware/authorization"
|
||||
|
||||
export const HealthGroup = HttpApiGroup.make("v2.health")
|
||||
.add(
|
||||
HttpApiEndpoint.get("health", "/api/health", {
|
||||
success: Schema.Struct({ healthy: Schema.Literal(true) }),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "v2.health.get",
|
||||
summary: "Check v2 server health",
|
||||
description: "Check whether the v2 API server is ready to accept requests.",
|
||||
}),
|
||||
),
|
||||
)
|
||||
.middleware(V2Authorization)
|
||||
+3
@@ -1,4 +1,5 @@
|
||||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { CommandV2 } from "@opencode-ai/core/command"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { LocationServiceMap } from "@opencode-ai/core/location-layer"
|
||||
@@ -55,7 +56,9 @@ export class V2LocationMiddleware extends HttpApiMiddleware.Service<
|
||||
{
|
||||
provides:
|
||||
| Catalog.Service
|
||||
| AgentV2.Service
|
||||
| CommandV2.Service
|
||||
| Location.Service
|
||||
| PluginBoot.Service
|
||||
| PermissionV2.Service
|
||||
| ProjectReference.Service
|
||||
+2
-4
@@ -1,14 +1,12 @@
|
||||
import { SessionID } from "@/session/schema"
|
||||
import { SessionV2 } from "@opencode-ai/core/session"
|
||||
import { SessionMessage } from "@opencode-ai/core/session/message"
|
||||
import { Schema } from "effect"
|
||||
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { InvalidCursorError, SessionNotFoundError, UnknownError } from "../../errors"
|
||||
import { V2Authorization } from "../../middleware/authorization"
|
||||
import { WorkspaceRoutingQueryFields } from "../../middleware/workspace-routing"
|
||||
import { data } from "./response"
|
||||
|
||||
export const MessagesQuery = Schema.Struct({
|
||||
...WorkspaceRoutingQueryFields,
|
||||
limit: Schema.optional(
|
||||
Schema.NumberFromString.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(1), Schema.isLessThanOrEqualTo(200)),
|
||||
).annotate({
|
||||
@@ -28,7 +26,7 @@ export const MessagesQuery = Schema.Struct({
|
||||
export const MessageGroup = HttpApiGroup.make("v2.message")
|
||||
.add(
|
||||
HttpApiEndpoint.get("messages", "/api/session/:sessionID/message", {
|
||||
params: { sessionID: SessionID },
|
||||
params: { sessionID: SessionV2.ID },
|
||||
query: MessagesQuery,
|
||||
success: data(Schema.Struct({
|
||||
items: Schema.Array(SessionMessage.Message),
|
||||
+4
-10
@@ -1,4 +1,3 @@
|
||||
import { SessionID } from "@/session/schema"
|
||||
import { SessionMessage } from "@opencode-ai/core/session/message"
|
||||
import { Prompt } from "@opencode-ai/core/session/prompt"
|
||||
import { SessionV2 } from "@opencode-ai/core/session"
|
||||
@@ -15,7 +14,6 @@ import {
|
||||
UnknownError,
|
||||
} from "../../errors"
|
||||
import { V2Authorization } from "../../middleware/authorization"
|
||||
import { WorkspaceRoutingQuery } from "../../middleware/workspace-routing"
|
||||
import { data } from "./response"
|
||||
|
||||
const SessionsQueryFields = {
|
||||
@@ -108,8 +106,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
|
||||
)
|
||||
.add(
|
||||
HttpApiEndpoint.post("prompt", "/api/session/:sessionID/prompt", {
|
||||
params: { sessionID: SessionID },
|
||||
query: WorkspaceRoutingQuery,
|
||||
params: { sessionID: SessionV2.ID },
|
||||
payload: Schema.Struct({
|
||||
prompt: Prompt,
|
||||
delivery: SessionV2.Delivery.pipe(Schema.optional),
|
||||
@@ -126,8 +123,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
|
||||
)
|
||||
.add(
|
||||
HttpApiEndpoint.post("compact", "/api/session/:sessionID/compact", {
|
||||
params: { sessionID: SessionID },
|
||||
query: WorkspaceRoutingQuery,
|
||||
params: { sessionID: SessionV2.ID },
|
||||
success: HttpApiSchema.NoContent,
|
||||
error: [SessionNotFoundError, ServiceUnavailableError],
|
||||
}).annotateMerge(
|
||||
@@ -140,8 +136,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
|
||||
)
|
||||
.add(
|
||||
HttpApiEndpoint.post("wait", "/api/session/:sessionID/wait", {
|
||||
params: { sessionID: SessionID },
|
||||
query: WorkspaceRoutingQuery,
|
||||
params: { sessionID: SessionV2.ID },
|
||||
success: HttpApiSchema.NoContent,
|
||||
error: [SessionNotFoundError, ServiceUnavailableError],
|
||||
}).annotateMerge(
|
||||
@@ -154,8 +149,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
|
||||
)
|
||||
.add(
|
||||
HttpApiEndpoint.get("context", "/api/session/:sessionID/context", {
|
||||
params: { sessionID: SessionID },
|
||||
query: WorkspaceRoutingQuery,
|
||||
params: { sessionID: SessionV2.ID },
|
||||
success: data(Schema.Array(SessionMessage.Message)),
|
||||
error: [SessionNotFoundError, UnknownError],
|
||||
}).annotateMerge(
|
||||
+14
-10
@@ -2,18 +2,22 @@ import { SessionV2 } from "@opencode-ai/core/session"
|
||||
import { LocationServiceMap } from "@opencode-ai/core/location-layer"
|
||||
import { PermissionSaved } from "@opencode-ai/core/permission/saved"
|
||||
import { Layer } from "effect"
|
||||
import { layer as v2LocationLayer } from "../groups/v2/location"
|
||||
import { messageHandlers } from "./v2/message"
|
||||
import { modelHandlers } from "./v2/model"
|
||||
import { providerHandlers } from "./v2/provider"
|
||||
import { sessionHandlers } from "./v2/session"
|
||||
import { permissionHandlers, savedPermissionHandlers, sessionPermissionHandlers } from "./v2/permission"
|
||||
import { fileSystemHandlers } from "./v2/fs"
|
||||
import { commandHandlers } from "./v2/command"
|
||||
import { skillHandlers } from "./v2/skill"
|
||||
import { eventHandlers } from "./v2/event"
|
||||
import { layer as v2LocationLayer } from "./groups/v2/location"
|
||||
import { messageHandlers } from "./handlers/v2/message"
|
||||
import { modelHandlers } from "./handlers/v2/model"
|
||||
import { providerHandlers } from "./handlers/v2/provider"
|
||||
import { sessionHandlers } from "./handlers/v2/session"
|
||||
import { permissionHandlers, savedPermissionHandlers, sessionPermissionHandlers } from "./handlers/v2/permission"
|
||||
import { fileSystemHandlers } from "./handlers/v2/fs"
|
||||
import { commandHandlers } from "./handlers/v2/command"
|
||||
import { skillHandlers } from "./handlers/v2/skill"
|
||||
import { eventHandlers } from "./handlers/v2/event"
|
||||
import { agentHandlers } from "./handlers/v2/agent"
|
||||
import { healthHandlers } from "./handlers/v2/health"
|
||||
|
||||
export const v2Handlers = Layer.mergeAll(
|
||||
healthHandlers,
|
||||
agentHandlers,
|
||||
sessionHandlers,
|
||||
messageHandlers,
|
||||
modelHandlers,
|
||||
@@ -0,0 +1,15 @@
|
||||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { PluginBoot } from "@opencode-ai/core/plugin/boot"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { V2Api } from "../../api"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
export const agentHandlers = HttpApiBuilder.group(V2Api, "v2.agent", (handlers) =>
|
||||
handlers.handle("agents", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* PluginBoot.Service.use((plugin) => plugin.wait())
|
||||
return yield* response(AgentV2.Service.use((agent) => agent.all()))
|
||||
}),
|
||||
),
|
||||
)
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
import { CommandV2 } from "@opencode-ai/core/command"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { V2Api } from "../../api"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
export const commandHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.command", (handlers) =>
|
||||
export const commandHandlers = HttpApiBuilder.group(V2Api, "v2.command", (handlers) =>
|
||||
handlers.handle("commands", () => response(CommandV2.Service.use((command) => command.list()))),
|
||||
)
|
||||
@@ -0,0 +1,61 @@
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { Effect, Stream } from "effect"
|
||||
import { HttpServerResponse } from "effect/unstable/http"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import * as Sse from "effect/unstable/encoding/Sse"
|
||||
import { V2Api } from "../../api"
|
||||
|
||||
function eventData(data: unknown): Sse.Event {
|
||||
return {
|
||||
_tag: "Event",
|
||||
event: "message",
|
||||
id: undefined,
|
||||
data: JSON.stringify(data),
|
||||
}
|
||||
}
|
||||
|
||||
export const eventHandlers = HttpApiBuilder.group(V2Api, "v2.event", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
const events = yield* EventV2.Service
|
||||
return handlers.handleRaw("events", () =>
|
||||
Effect.gen(function* () {
|
||||
const location = yield* Location.Service
|
||||
const connected = {
|
||||
id: EventV2.ID.create(),
|
||||
type: "server.connected",
|
||||
location: new Location.Info({
|
||||
directory: location.directory,
|
||||
workspaceID: location.workspaceID,
|
||||
project: location.project,
|
||||
}),
|
||||
data: {},
|
||||
}
|
||||
return HttpServerResponse.stream(
|
||||
Stream.make(connected).pipe(
|
||||
Stream.concat(
|
||||
events.all().pipe(
|
||||
Stream.filter(
|
||||
(event) =>
|
||||
event.location?.directory === location.directory &&
|
||||
event.location.workspaceID === location.workspaceID,
|
||||
),
|
||||
),
|
||||
),
|
||||
Stream.map(eventData),
|
||||
Stream.pipeThroughChannel(Sse.encode()),
|
||||
Stream.encodeText,
|
||||
),
|
||||
{
|
||||
contentType: "text/event-stream",
|
||||
headers: {
|
||||
"Cache-Control": "no-cache, no-transform",
|
||||
"X-Accel-Buffering": "no",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
},
|
||||
},
|
||||
)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
import { FileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { V2Api } from "../../api"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
export const fileSystemHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.fs", (handlers) =>
|
||||
export const fileSystemHandlers = HttpApiBuilder.group(V2Api, "v2.fs", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
return handlers
|
||||
.handle("read", (ctx) => response(FileSystem.Service.use((fs) => fs.read(ctx.query))))
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { V2Api } from "../../api"
|
||||
|
||||
export const healthHandlers = HttpApiBuilder.group(V2Api, "v2.health", (handlers) =>
|
||||
handlers.handle("health", () => Effect.succeed({ healthy: true as const })),
|
||||
)
|
||||
+2
-2
@@ -3,7 +3,7 @@ import { SessionV2 } from "@opencode-ai/core/session"
|
||||
import { Effect, Schema } from "effect"
|
||||
import * as DateTime from "effect/DateTime"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { V2Api } from "../../api"
|
||||
import { InvalidCursorError, SessionNotFoundError, UnknownError } from "../../errors"
|
||||
import { make } from "../../groups/v2/response"
|
||||
|
||||
@@ -29,7 +29,7 @@ const cursor = {
|
||||
},
|
||||
}
|
||||
|
||||
export const messageHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.message", (handlers) =>
|
||||
export const messageHandlers = HttpApiBuilder.group(V2Api, "v2.message", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* SessionV2.Service
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { PluginBoot } from "@opencode-ai/core/plugin/boot"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { V2Api } from "../../api"
|
||||
import { ServiceUnavailableError } from "../../errors"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
@@ -11,7 +11,7 @@ const catalogUnavailable = new ServiceUnavailableError({
|
||||
service: "catalog",
|
||||
})
|
||||
|
||||
export const modelHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.model", (handlers) =>
|
||||
export const modelHandlers = HttpApiBuilder.group(V2Api, "v2.model", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
return handlers.handle(
|
||||
"models",
|
||||
+4
-4
@@ -7,7 +7,7 @@ import { SessionTable } from "@opencode-ai/core/session/sql"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { V2Api } from "../../api"
|
||||
import { PermissionNotFoundError, SessionNotFoundError } from "../../errors"
|
||||
import { response } from "../../groups/v2/location"
|
||||
import { make } from "../../groups/v2/response"
|
||||
@@ -16,7 +16,7 @@ function missingRequest(id: PermissionV2.ID) {
|
||||
return new PermissionNotFoundError({ requestID: id, message: `Permission request not found: ${id}` })
|
||||
}
|
||||
|
||||
export const permissionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.permission", (handlers) =>
|
||||
export const permissionHandlers = HttpApiBuilder.group(V2Api, "v2.permission", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
return handlers.handle(
|
||||
"permissionRequests",
|
||||
@@ -27,7 +27,7 @@ export const permissionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.perm
|
||||
}),
|
||||
)
|
||||
|
||||
export const sessionPermissionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.session.permission", (handlers) =>
|
||||
export const sessionPermissionHandlers = HttpApiBuilder.group(V2Api, "v2.session.permission", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
const { db } = yield* Database.Service
|
||||
const locations = yield* LocationServiceMap
|
||||
@@ -86,7 +86,7 @@ export const sessionPermissionHandlers = HttpApiBuilder.group(InstanceHttpApi, "
|
||||
}),
|
||||
)
|
||||
|
||||
export const savedPermissionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.permission.saved", (handlers) =>
|
||||
export const savedPermissionHandlers = HttpApiBuilder.group(V2Api, "v2.permission.saved", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
const saved = yield* PermissionSaved.Service
|
||||
return handlers
|
||||
+2
-2
@@ -2,7 +2,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { PluginBoot } from "@opencode-ai/core/plugin/boot"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { V2Api } from "../../api"
|
||||
import { ProviderNotFoundError, ServiceUnavailableError } from "../../errors"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
@@ -11,7 +11,7 @@ const catalogUnavailable = new ServiceUnavailableError({
|
||||
service: "catalog",
|
||||
})
|
||||
|
||||
export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.provider", (handlers) =>
|
||||
export const providerHandlers = HttpApiBuilder.group(V2Api, "v2.provider", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
return handlers
|
||||
.handle(
|
||||
+2
-2
@@ -1,14 +1,14 @@
|
||||
import { SessionV2 } from "@opencode-ai/core/session"
|
||||
import { DateTime, Effect } from "effect"
|
||||
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { V2Api } from "../../api"
|
||||
import { SessionsCursor } from "../../groups/v2/session"
|
||||
import { InvalidCursorError, ServiceUnavailableError, SessionNotFoundError, UnknownError } from "../../errors"
|
||||
import { make } from "../../groups/v2/response"
|
||||
|
||||
const DefaultSessionsLimit = 50
|
||||
|
||||
export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.session", (handlers) =>
|
||||
export const sessionHandlers = HttpApiBuilder.group(V2Api, "v2.session", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* SessionV2.Service
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { SkillV2 } from "@opencode-ai/core/skill"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { InstanceHttpApi } from "../../api"
|
||||
import { V2Api } from "../../api"
|
||||
import { response } from "../../groups/v2/location"
|
||||
|
||||
export const skillHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.skill", (handlers) =>
|
||||
export const skillHandlers = HttpApiBuilder.group(V2Api, "v2.skill", (handlers) =>
|
||||
handlers.handle("skills", () => response(SkillV2.Service.use((skill) => skill.list()))),
|
||||
)
|
||||
@@ -0,0 +1,60 @@
|
||||
import { ServerAuth } from "../auth"
|
||||
import { UnauthorizedError } from "../errors"
|
||||
import { Effect, Encoding, Layer, Redacted } from "effect"
|
||||
import { HttpEffect, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
|
||||
import { HttpApiMiddleware } from "effect/unstable/httpapi"
|
||||
|
||||
const AUTH_TOKEN_QUERY = "auth_token"
|
||||
const WWW_AUTHENTICATE = 'Basic realm="Secure Area"'
|
||||
|
||||
export class V2Authorization extends HttpApiMiddleware.Service<V2Authorization>()(
|
||||
"@opencode/ExperimentalHttpApiV2Authorization",
|
||||
{
|
||||
error: UnauthorizedError,
|
||||
},
|
||||
) {}
|
||||
|
||||
function emptyCredential() {
|
||||
return { username: "", password: Redacted.make("") }
|
||||
}
|
||||
|
||||
function decodeCredential(input: string) {
|
||||
return Effect.fromResult(Encoding.decodeBase64String(input)).pipe(
|
||||
Effect.match({
|
||||
onFailure: emptyCredential,
|
||||
onSuccess: (header) => {
|
||||
const separator = header.indexOf(":")
|
||||
if (separator === -1) return emptyCredential()
|
||||
return { username: header.slice(0, separator), password: Redacted.make(header.slice(separator + 1)) }
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function credentialFromRequest(request: HttpServerRequest.HttpServerRequest) {
|
||||
const url = new URL(request.url, "http://localhost")
|
||||
const token = url.searchParams.get(AUTH_TOKEN_QUERY)
|
||||
if (token) return decodeCredential(token)
|
||||
const match = /^Basic\s+(.+)$/i.exec(request.headers.authorization ?? "")
|
||||
if (match) return decodeCredential(match[1])
|
||||
return Effect.succeed(emptyCredential())
|
||||
}
|
||||
|
||||
export const v2AuthorizationLayer = Layer.effect(
|
||||
V2Authorization,
|
||||
Effect.gen(function* () {
|
||||
const config = yield* ServerAuth.Config
|
||||
if (!ServerAuth.required(config)) return V2Authorization.of((effect) => effect)
|
||||
return V2Authorization.of((effect) =>
|
||||
Effect.gen(function* () {
|
||||
const request = yield* HttpServerRequest.HttpServerRequest
|
||||
const credential = yield* credentialFromRequest(request)
|
||||
if (ServerAuth.authorized(credential, config)) return yield* effect
|
||||
yield* HttpEffect.appendPreResponseHandler((_request, response) =>
|
||||
Effect.succeed(HttpServerResponse.setHeader(response, "www-authenticate", WWW_AUTHENTICATE)),
|
||||
)
|
||||
return yield* new UnauthorizedError({ message: "Authentication required" })
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiMiddleware } from "effect/unstable/httpapi"
|
||||
import { InvalidRequestError } from "../errors"
|
||||
|
||||
const log = Log.create({ service: "server" })
|
||||
const REASON_LIMIT = 1024
|
||||
|
||||
function truncateReason(reason: string) {
|
||||
if (reason.length <= REASON_LIMIT) return reason
|
||||
return reason.slice(0, REASON_LIMIT) + `... (${reason.length - REASON_LIMIT} more chars)`
|
||||
}
|
||||
|
||||
export class SchemaErrorMiddleware extends HttpApiMiddleware.Service<SchemaErrorMiddleware>()(
|
||||
"@opencode/HttpApiSchemaError",
|
||||
{ error: InvalidRequestError },
|
||||
) {}
|
||||
|
||||
export const schemaErrorLayer = HttpApiMiddleware.layerSchemaErrorTransform(SchemaErrorMiddleware, (error) => {
|
||||
const reason = truncateReason(error.cause.message)
|
||||
log.warn("schema rejection", { kind: error.kind, reason })
|
||||
return Effect.fail(new InvalidRequestError({ message: reason, kind: error.kind }))
|
||||
})
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { LocationServiceMap } from "@opencode-ai/core/location-layer"
|
||||
import { PermissionSaved } from "@opencode-ai/core/permission/saved"
|
||||
import { SessionV2 } from "@opencode-ai/core/session"
|
||||
import { FetchHttpClient, HttpRouter, HttpServer } from "effect/unstable/http"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { Layer, Option } from "effect"
|
||||
import { V2Api } from "./api"
|
||||
import { ServerAuth } from "./auth"
|
||||
import { v2Handlers } from "./handlers"
|
||||
import { v2AuthorizationLayer } from "./middleware/authorization"
|
||||
import { schemaErrorLayer } from "./middleware/schema-error"
|
||||
|
||||
export function createRoutes(password?: string) {
|
||||
return HttpApiBuilder.layer(V2Api).pipe(
|
||||
Layer.provide(v2Handlers),
|
||||
Layer.provide(v2AuthorizationLayer),
|
||||
Layer.provide(schemaErrorLayer),
|
||||
Layer.provide(
|
||||
password
|
||||
? ServerAuth.Config.layer({ username: "opencode", password: Option.some(password) })
|
||||
: ServerAuth.Config.defaultLayer,
|
||||
),
|
||||
Layer.provide(LocationServiceMap.layer),
|
||||
Layer.provide(PermissionSaved.layer),
|
||||
Layer.provide(SessionV2.defaultLayer),
|
||||
Layer.provide(Database.defaultLayer),
|
||||
Layer.provide(EventV2.defaultLayer),
|
||||
Layer.provide(FetchHttpClient.layer),
|
||||
)
|
||||
}
|
||||
|
||||
export const routes = createRoutes()
|
||||
|
||||
export const webHandler = () => HttpRouter.toWebHandler(routes.pipe(Layer.provide(HttpServer.layerServices)), { disableLogger: true })
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@tsconfig/bun/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noUncheckedIndexedAccess": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user