From b4011d7b273301b14ed5512c42124dfbd92a2e82 Mon Sep 17 00:00:00 2001 From: Dax Date: Sat, 4 Jul 2026 21:31:08 +0000 Subject: [PATCH] fix(client): preserve effect schema types --- packages/client/script/build.ts | 107 ++- packages/client/src/effect/api/api.ts | 674 ++++++++++-------- packages/httpapi-codegen/src/index.ts | 146 +++- .../httpapi-codegen/test/generate.test.ts | 38 + 4 files changed, 641 insertions(+), 324 deletions(-) diff --git a/packages/client/script/build.ts b/packages/client/script/build.ts index 6a83d52b99..6361aae880 100644 --- a/packages/client/script/build.ts +++ b/packages/client/script/build.ts @@ -7,11 +7,96 @@ import { groupNames, promiseOmitEndpoints, } from "@opencode-ai/protocol/client" -import { Effect } from "effect" +import { SessionsCursor } from "@opencode-ai/protocol/groups/session" +import { Agent } from "@opencode-ai/schema/agent" +import { Command } from "@opencode-ai/schema/command" +import { Credential } from "@opencode-ai/schema/credential" +import { Event } from "@opencode-ai/schema/event" +import { EventLog } from "@opencode-ai/schema/event-log" +import { EventManifest } from "@opencode-ai/schema/event-manifest" +import { FileDiff } from "@opencode-ai/schema/file-diff" +import { FileSystem } from "@opencode-ai/schema/filesystem" +import { Form } from "@opencode-ai/schema/form" +import { Integration } from "@opencode-ai/schema/integration" +import { LLM } from "@opencode-ai/schema/llm" +import { Location } from "@opencode-ai/schema/location" +import { Mcp } from "@opencode-ai/schema/mcp" +import { Model } from "@opencode-ai/schema/model" +import { Permission } from "@opencode-ai/schema/permission" +import { PermissionSaved } from "@opencode-ai/schema/permission-saved" +import { Plugin } from "@opencode-ai/schema/plugin" +import { Project } from "@opencode-ai/schema/project" +import { ProjectCopy } from "@opencode-ai/schema/project-copy" +import { AgentAttachment, FileAttachment, Prompt, Source } from "@opencode-ai/schema/prompt" +import { PromptInput } from "@opencode-ai/schema/prompt-input" +import { Provider } from "@opencode-ai/schema/provider" +import { Pty } from "@opencode-ai/schema/pty" +import { PtyTicket } from "@opencode-ai/schema/pty-ticket" +import { Question } from "@opencode-ai/schema/question" +import { Reference } from "@opencode-ai/schema/reference" +import { Revert } from "@opencode-ai/schema/revert" +import { AbsolutePath, RelativePath } from "@opencode-ai/schema/schema" +import { Session } from "@opencode-ai/schema/session" +import { SessionContextEntry } from "@opencode-ai/schema/session-context-entry" +import { SessionEvent } from "@opencode-ai/schema/session-event" +import { SessionInput } from "@opencode-ai/schema/session-input" +import { SessionMessage } from "@opencode-ai/schema/session-message" +import { Shell } from "@opencode-ai/schema/shell" +import { Skill } from "@opencode-ai/schema/skill" +import { Vcs } from "@opencode-ai/schema/vcs" +import { Workspace } from "@opencode-ai/schema/workspace" +import { Effect, Schema } from "effect" import { fileURLToPath } from "url" const promiseContract = compile(ClientApi, { groupNames, endpointNames, omitEndpoints: promiseOmitEndpoints }) const effectContract = compile(ClientApi, { groupNames, endpointNames, omitEndpoints: effectOmitEndpoints }) +const effectTypeReferences = [ + ...namespaceTypes("Agent", "@opencode-ai/schema/agent", Agent), + ...namespaceTypes("Command", "@opencode-ai/schema/command", Command), + ...namespaceTypes("Credential", "@opencode-ai/schema/credential", Credential), + ...namespaceTypes("Event", "@opencode-ai/schema/event", Event), + ...namespaceTypes("EventLog", "@opencode-ai/schema/event-log", EventLog), + ...namespaceTypes("EventManifest", "@opencode-ai/schema/event-manifest", EventManifest), + ...namespaceTypes("FileDiff", "@opencode-ai/schema/file-diff", FileDiff), + ...namespaceTypes("FileSystem", "@opencode-ai/schema/filesystem", FileSystem), + ...namespaceTypes("Form", "@opencode-ai/schema/form", Form), + ...namespaceTypes("Integration", "@opencode-ai/schema/integration", Integration), + ...namespaceTypes("LLM", "@opencode-ai/schema/llm", LLM), + ...namespaceTypes("Location", "@opencode-ai/schema/location", Location), + ...namespaceTypes("Mcp", "@opencode-ai/schema/mcp", Mcp), + ...namespaceTypes("Model", "@opencode-ai/schema/model", Model), + ...namespaceTypes("Permission", "@opencode-ai/schema/permission", Permission), + ...namespaceTypes("PermissionSaved", "@opencode-ai/schema/permission-saved", PermissionSaved), + ...namespaceTypes("Plugin", "@opencode-ai/schema/plugin", Plugin), + ...namespaceTypes("Project", "@opencode-ai/schema/project", Project), + ...namespaceTypes("ProjectCopy", "@opencode-ai/schema/project-copy", ProjectCopy), + ...namespaceTypes("PromptInput", "@opencode-ai/schema/prompt-input", PromptInput), + ...namespaceTypes("Provider", "@opencode-ai/schema/provider", Provider), + ...namespaceTypes("Pty", "@opencode-ai/schema/pty", Pty), + ...namespaceTypes("PtyTicket", "@opencode-ai/schema/pty-ticket", PtyTicket), + ...namespaceTypes("Question", "@opencode-ai/schema/question", Question), + ...namespaceTypes("Reference", "@opencode-ai/schema/reference", Reference), + ...namespaceTypes("Revert", "@opencode-ai/schema/revert", Revert), + ...namespaceTypes("Session", "@opencode-ai/schema/session", Session), + ...namespaceTypes("SessionContextEntry", "@opencode-ai/schema/session-context-entry", SessionContextEntry), + ...namespaceTypes("SessionInput", "@opencode-ai/schema/session-input", SessionInput), + ...namespaceTypes("SessionMessage", "@opencode-ai/schema/session-message", SessionMessage), + ...namespaceTypes("SessionEvent", "@opencode-ai/schema/session-event", SessionEvent, { + Durable: "DurableEvent", + All: "Event", + }), + ...namespaceTypes("Shell", "@opencode-ai/schema/shell", Shell), + ...namespaceTypes("Skill", "@opencode-ai/schema/skill", Skill), + ...namespaceTypes("Vcs", "@opencode-ai/schema/vcs", Vcs), + ...namespaceTypes("Workspace", "@opencode-ai/schema/workspace", Workspace), + typeReference("Prompt", "@opencode-ai/schema/prompt", Prompt), + typeReference("Source", "@opencode-ai/schema/prompt", Source), + typeReference("FileAttachment", "@opencode-ai/schema/prompt", FileAttachment), + typeReference("AgentAttachment", "@opencode-ai/schema/prompt", AgentAttachment), + typeReference("AbsolutePath", "@opencode-ai/schema/schema", AbsolutePath), + typeReference("RelativePath", "@opencode-ai/schema/schema", RelativePath), + typeReference("SessionsCursor", "@opencode-ai/protocol/groups/session", SessionsCursor), +] await Effect.runPromise( Effect.all( @@ -32,10 +117,28 @@ await Effect.runPromise( fileURLToPath(new URL("../src/effect/generated", import.meta.url)), ), write( - emitEffectShape(effectContract, { module: "../../contract", api: "ClientApi" }), + emitEffectShape(effectContract, { + module: "../../contract", + api: "ClientApi", + typeReferences: effectTypeReferences, + }), fileURLToPath(new URL("../src/effect/api", import.meta.url)), ), ], { concurrency: 3, discard: true }, ).pipe(Effect.provide(NodeFileSystem.layer)), ) + +function namespaceTypes(namespace: string, module: string, values: object, names?: Readonly>) { + return Object.entries(values).flatMap(([name, schema]) => + Schema.isSchema(schema) ? [typeReference(`${namespace}.${names?.[name] ?? name}`, module, schema)] : [], + ) +} + +function typeReference(name: string, module: string, schema: Schema.Top) { + return { + schema, + name, + import: `import type { ${name.split(".")[0]} } from ${JSON.stringify(module)}`, + } +} diff --git a/packages/client/src/effect/api/api.ts b/packages/client/src/effect/api/api.ts index 3f4c852be1..79982f83f4 100644 --- a/packages/client/src/effect/api/api.ts +++ b/packages/client/src/effect/api/api.ts @@ -2,12 +2,49 @@ import type { Effect, Stream } from "effect" import type { HttpApiClient } from "effect/unstable/httpapi" import type { ClientApi } from "../../contract" +import type { Location } from "@opencode-ai/schema/location" +import type { Agent } from "@opencode-ai/schema/agent" +import type { Plugin } from "@opencode-ai/schema/plugin" +import type { Workspace } from "@opencode-ai/schema/workspace" +import type { Session } from "@opencode-ai/schema/session" +import type { AbsolutePath } from "@opencode-ai/schema/schema" +import type { Project } from "@opencode-ai/schema/project" +import type { RelativePath } from "@opencode-ai/schema/schema" +import type { SessionsCursor } from "@opencode-ai/protocol/groups/session" +import type { Event } from "@opencode-ai/schema/event" +import type { Model } from "@opencode-ai/schema/model" +import type { SessionMessage } from "@opencode-ai/schema/session-message" +import type { PromptInput } from "@opencode-ai/schema/prompt-input" +import type { SessionInput } from "@opencode-ai/schema/session-input" +import type { AgentAttachment } from "@opencode-ai/schema/prompt" +import type { Revert } from "@opencode-ai/schema/revert" +import type { SessionContextEntry } from "@opencode-ai/schema/session-context-entry" +import type { EventLog } from "@opencode-ai/schema/event-log" +import type { SessionEvent } from "@opencode-ai/schema/session-event" +import type { Provider } from "@opencode-ai/schema/provider" +import type { Integration } from "@opencode-ai/schema/integration" +import type { Mcp } from "@opencode-ai/schema/mcp" +import type { Credential } from "@opencode-ai/schema/credential" +import type { Form } from "@opencode-ai/schema/form" +import type { Permission } from "@opencode-ai/schema/permission" +import type { PermissionSaved } from "@opencode-ai/schema/permission-saved" +import type { FileSystem } from "@opencode-ai/schema/filesystem" +import type { Command } from "@opencode-ai/schema/command" +import type { Skill } from "@opencode-ai/schema/skill" +import type { Shell } from "@opencode-ai/schema/shell" +import type { LLM } from "@opencode-ai/schema/llm" +import type { Pty } from "@opencode-ai/schema/pty" +import type { Question } from "@opencode-ai/schema/question" +import type { FileDiff } from "@opencode-ai/schema/file-diff" +import type { Reference } from "@opencode-ai/schema/reference" +import type { ProjectCopy } from "@opencode-ai/schema/project-copy" +import type { Vcs } from "@opencode-ai/schema/vcs" type RawClient = HttpApiClient.ForApi type EffectValue = A extends Effect.Effect ? Success : never type StreamValue = A extends Stream.Stream ? Success : never -export type Endpoint0_0Output = EffectValue> +export type Endpoint0_0Output = { readonly healthy: true } export type HealthGetOperation = () => Effect.Effect export interface HealthApi { @@ -15,17 +52,21 @@ export interface HealthApi { } type Endpoint1_0Request = Parameters[0] -export type Endpoint1_0Input = { readonly location?: Endpoint1_0Request["query"]["location"] } -export type Endpoint1_0Output = EffectValue> -export type LocationGetOperation = (input?: Endpoint1_0Input) => Effect.Effect +export type Endpoint1_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint1_0Output = Location.Info +export type LocationGetOperation = (input?: Endpoint1_0Input) => Effect.Effect export interface LocationApi { readonly get: LocationGetOperation } type Endpoint2_0Request = Parameters[0] -export type Endpoint2_0Input = { readonly location?: Endpoint2_0Request["query"]["location"] } -export type Endpoint2_0Output = EffectValue> +export type Endpoint2_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint2_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type AgentListOperation = (input?: Endpoint2_0Input) => Effect.Effect export interface AgentApi { @@ -33,8 +74,10 @@ export interface AgentApi { } type Endpoint3_0Request = Parameters[0] -export type Endpoint3_0Input = { readonly location?: Endpoint3_0Request["query"]["location"] } -export type Endpoint3_0Output = EffectValue> +export type Endpoint3_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint3_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type PluginListOperation = (input?: Endpoint3_0Input) => Effect.Effect export interface PluginApi { @@ -43,172 +86,165 @@ export interface PluginApi { type Endpoint4_0Request = Parameters[0] export type Endpoint4_0Input = { - readonly workspace?: Endpoint4_0Request["query"]["workspace"] - readonly limit?: Endpoint4_0Request["query"]["limit"] - readonly order?: Endpoint4_0Request["query"]["order"] - readonly search?: Endpoint4_0Request["query"]["search"] - readonly parentID?: Endpoint4_0Request["query"]["parentID"] - readonly directory?: Endpoint4_0Request["query"]["directory"] - readonly project?: Endpoint4_0Request["query"]["project"] - readonly subpath?: Endpoint4_0Request["query"]["subpath"] - readonly cursor?: Endpoint4_0Request["query"]["cursor"] + readonly workspace?: Workspace.ID | undefined + readonly limit?: number | undefined + readonly order?: "asc" | "desc" | undefined + readonly search?: string | undefined + readonly parentID?: Session.ID | null | undefined + readonly directory?: AbsolutePath | undefined + readonly project?: Project.ID | undefined + readonly subpath?: RelativePath | undefined + readonly cursor?: SessionsCursor | undefined +} +export type Endpoint4_0Output = { + readonly data: ReadonlyArray + readonly watermarks: { readonly [x: Session.ID]: Event.Seq } + readonly cursor: { readonly previous?: SessionsCursor | undefined; readonly next?: SessionsCursor | undefined } } -export type Endpoint4_0Output = EffectValue> export type SessionListOperation = (input?: Endpoint4_0Input) => Effect.Effect type Endpoint4_1Request = Parameters[0] export type Endpoint4_1Input = { - readonly id?: Endpoint4_1Request["payload"]["id"] - readonly agent?: Endpoint4_1Request["payload"]["agent"] - readonly model?: Endpoint4_1Request["payload"]["model"] - readonly location?: Endpoint4_1Request["payload"]["location"] + readonly id?: Session.ID | undefined + readonly agent?: Agent.ID | undefined + readonly model?: Model.Ref | undefined + readonly location?: Location.Ref | undefined } -export type Endpoint4_1Output = EffectValue>["data"] -export type SessionCreateOperation = (input?: Endpoint4_1Input) => Effect.Effect +export type Endpoint4_1Output = Session.Info +export type SessionCreateOperation = (input?: Endpoint4_1Input) => Effect.Effect -export type Endpoint4_2Output = EffectValue> +export type Endpoint4_2Output = { + readonly data: { readonly [x: Session.ID]: { readonly type: "running" } } + readonly watermarks: { readonly [x: Session.ID]: Event.Seq } +} export type SessionActiveOperation = () => Effect.Effect type Endpoint4_3Request = Parameters[0] -export type Endpoint4_3Input = { readonly sessionID: Endpoint4_3Request["params"]["sessionID"] } -export type Endpoint4_3Output = EffectValue>["data"] -export type SessionGetOperation = (input: Endpoint4_3Input) => Effect.Effect +export type Endpoint4_3Input = { readonly sessionID: Session.ID } +export type Endpoint4_3Output = Session.Info +export type SessionGetOperation = (input: Endpoint4_3Input) => Effect.Effect type Endpoint4_4Request = Parameters[0] -export type Endpoint4_4Input = { - readonly sessionID: Endpoint4_4Request["params"]["sessionID"] - readonly messageID?: Endpoint4_4Request["payload"]["messageID"] -} -export type Endpoint4_4Output = EffectValue>["data"] -export type SessionForkOperation = (input: Endpoint4_4Input) => Effect.Effect +export type Endpoint4_4Input = { readonly sessionID: Session.ID; readonly messageID?: SessionMessage.ID | undefined } +export type Endpoint4_4Output = Session.Info +export type SessionForkOperation = (input: Endpoint4_4Input) => Effect.Effect type Endpoint4_5Request = Parameters[0] -export type Endpoint4_5Input = { - readonly sessionID: Endpoint4_5Request["params"]["sessionID"] - readonly agent: Endpoint4_5Request["payload"]["agent"] -} +export type Endpoint4_5Input = { readonly sessionID: Session.ID; readonly agent: Agent.ID } export type Endpoint4_5Output = EffectValue> export type SessionSwitchAgentOperation = (input: Endpoint4_5Input) => Effect.Effect type Endpoint4_6Request = Parameters[0] -export type Endpoint4_6Input = { - readonly sessionID: Endpoint4_6Request["params"]["sessionID"] - readonly model: Endpoint4_6Request["payload"]["model"] -} +export type Endpoint4_6Input = { readonly sessionID: Session.ID; readonly model: Model.Ref } export type Endpoint4_6Output = EffectValue> export type SessionSwitchModelOperation = (input: Endpoint4_6Input) => Effect.Effect type Endpoint4_7Request = Parameters[0] -export type Endpoint4_7Input = { - readonly sessionID: Endpoint4_7Request["params"]["sessionID"] - readonly title: Endpoint4_7Request["payload"]["title"] -} +export type Endpoint4_7Input = { readonly sessionID: Session.ID; readonly title: string } export type Endpoint4_7Output = EffectValue> export type SessionRenameOperation = (input: Endpoint4_7Input) => Effect.Effect type Endpoint4_8Request = Parameters[0] export type Endpoint4_8Input = { - readonly sessionID: Endpoint4_8Request["params"]["sessionID"] - readonly id?: Endpoint4_8Request["payload"]["id"] - readonly prompt: Endpoint4_8Request["payload"]["prompt"] - readonly delivery?: Endpoint4_8Request["payload"]["delivery"] - readonly resume?: Endpoint4_8Request["payload"]["resume"] + readonly sessionID: Session.ID + readonly id?: SessionMessage.ID | undefined + readonly prompt: PromptInput.Prompt + readonly delivery?: "steer" | "queue" | undefined + readonly resume?: boolean | undefined } -export type Endpoint4_8Output = EffectValue>["data"] -export type SessionPromptOperation = (input: Endpoint4_8Input) => Effect.Effect +export type Endpoint4_8Output = SessionInput.Admitted +export type SessionPromptOperation = (input: Endpoint4_8Input) => Effect.Effect type Endpoint4_9Request = Parameters[0] export type Endpoint4_9Input = { - readonly sessionID: Endpoint4_9Request["params"]["sessionID"] - readonly id?: Endpoint4_9Request["payload"]["id"] - readonly command: Endpoint4_9Request["payload"]["command"] - readonly arguments?: Endpoint4_9Request["payload"]["arguments"] - readonly agent?: Endpoint4_9Request["payload"]["agent"] - readonly model?: Endpoint4_9Request["payload"]["model"] - readonly files?: Endpoint4_9Request["payload"]["files"] - readonly agents?: Endpoint4_9Request["payload"]["agents"] - readonly delivery?: Endpoint4_9Request["payload"]["delivery"] - readonly resume?: Endpoint4_9Request["payload"]["resume"] + readonly sessionID: Session.ID + readonly id?: SessionMessage.ID | undefined + readonly command: string + readonly arguments?: string | undefined + readonly agent?: string | undefined + readonly model?: Model.Ref | undefined + readonly files?: ReadonlyArray | undefined + readonly agents?: ReadonlyArray | undefined + readonly delivery?: "steer" | "queue" | undefined + readonly resume?: boolean | undefined } -export type Endpoint4_9Output = EffectValue>["data"] -export type SessionCommandOperation = (input: Endpoint4_9Input) => Effect.Effect +export type Endpoint4_9Output = SessionInput.Admitted +export type SessionCommandOperation = (input: Endpoint4_9Input) => Effect.Effect type Endpoint4_10Request = Parameters[0] export type Endpoint4_10Input = { - readonly sessionID: Endpoint4_10Request["params"]["sessionID"] - readonly id?: Endpoint4_10Request["payload"]["id"] - readonly skill: Endpoint4_10Request["payload"]["skill"] - readonly resume?: Endpoint4_10Request["payload"]["resume"] + readonly sessionID: Session.ID + readonly id?: SessionMessage.ID | undefined + readonly skill: string + readonly resume?: boolean | undefined } export type Endpoint4_10Output = EffectValue> export type SessionSkillOperation = (input: Endpoint4_10Input) => Effect.Effect type Endpoint4_11Request = Parameters[0] export type Endpoint4_11Input = { - readonly sessionID: Endpoint4_11Request["params"]["sessionID"] - readonly text: Endpoint4_11Request["payload"]["text"] - readonly description?: Endpoint4_11Request["payload"]["description"] - readonly metadata?: Endpoint4_11Request["payload"]["metadata"] + readonly sessionID: Session.ID + readonly text: string + readonly description?: string | undefined + readonly metadata?: { readonly [x: string]: unknown } | undefined } export type Endpoint4_11Output = EffectValue> export type SessionSyntheticOperation = (input: Endpoint4_11Input) => Effect.Effect type Endpoint4_12Request = Parameters[0] export type Endpoint4_12Input = { - readonly sessionID: Endpoint4_12Request["params"]["sessionID"] - readonly id?: Endpoint4_12Request["payload"]["id"] - readonly command: Endpoint4_12Request["payload"]["command"] + readonly sessionID: Session.ID + readonly id?: Event.ID | undefined + readonly command: string } export type Endpoint4_12Output = EffectValue> export type SessionShellOperation = (input: Endpoint4_12Input) => Effect.Effect type Endpoint4_13Request = Parameters[0] -export type Endpoint4_13Input = { readonly sessionID: Endpoint4_13Request["params"]["sessionID"] } +export type Endpoint4_13Input = { readonly sessionID: Session.ID } export type Endpoint4_13Output = EffectValue> export type SessionCompactOperation = (input: Endpoint4_13Input) => Effect.Effect type Endpoint4_14Request = Parameters[0] -export type Endpoint4_14Input = { readonly sessionID: Endpoint4_14Request["params"]["sessionID"] } +export type Endpoint4_14Input = { readonly sessionID: Session.ID } export type Endpoint4_14Output = EffectValue> export type SessionWaitOperation = (input: Endpoint4_14Input) => Effect.Effect type Endpoint4_15Request = Parameters[0] export type Endpoint4_15Input = { - readonly sessionID: Endpoint4_15Request["params"]["sessionID"] - readonly messageID: Endpoint4_15Request["payload"]["messageID"] - readonly files?: Endpoint4_15Request["payload"]["files"] + readonly sessionID: Session.ID + readonly messageID: SessionMessage.ID + readonly files?: boolean | undefined } -export type Endpoint4_15Output = EffectValue>["data"] -export type SessionRevertStageOperation = (input: Endpoint4_15Input) => Effect.Effect +export type Endpoint4_15Output = Revert.State +export type SessionRevertStageOperation = (input: Endpoint4_15Input) => Effect.Effect type Endpoint4_16Request = Parameters[0] -export type Endpoint4_16Input = { readonly sessionID: Endpoint4_16Request["params"]["sessionID"] } +export type Endpoint4_16Input = { readonly sessionID: Session.ID } export type Endpoint4_16Output = EffectValue> export type SessionRevertClearOperation = (input: Endpoint4_16Input) => Effect.Effect type Endpoint4_17Request = Parameters[0] -export type Endpoint4_17Input = { readonly sessionID: Endpoint4_17Request["params"]["sessionID"] } +export type Endpoint4_17Input = { readonly sessionID: Session.ID } export type Endpoint4_17Output = EffectValue> export type SessionRevertCommitOperation = (input: Endpoint4_17Input) => Effect.Effect type Endpoint4_18Request = Parameters[0] -export type Endpoint4_18Input = { readonly sessionID: Endpoint4_18Request["params"]["sessionID"] } -export type Endpoint4_18Output = EffectValue>["data"] +export type Endpoint4_18Input = { readonly sessionID: Session.ID } +export type Endpoint4_18Output = ReadonlyArray export type SessionContextOperation = (input: Endpoint4_18Input) => Effect.Effect type Endpoint4_19Request = Parameters[0] -export type Endpoint4_19Input = { readonly sessionID: Endpoint4_19Request["params"]["sessionID"] } -export type Endpoint4_19Output = EffectValue< - ReturnType ->["data"] +export type Endpoint4_19Input = { readonly sessionID: Session.ID } +export type Endpoint4_19Output = ReadonlyArray export type SessionListContextEntriesOperation = ( input: Endpoint4_19Input, ) => Effect.Effect type Endpoint4_20Request = Parameters[0] export type Endpoint4_20Input = { - readonly sessionID: Endpoint4_20Request["params"]["sessionID"] - readonly key: Endpoint4_20Request["params"]["key"] + readonly sessionID: Session.ID + readonly key: SessionContextEntry.Key readonly value: Endpoint4_20Request["payload"]["value"] } export type Endpoint4_20Output = EffectValue> @@ -217,10 +253,7 @@ export type SessionPutContextEntryOperation = ( ) => Effect.Effect type Endpoint4_21Request = Parameters[0] -export type Endpoint4_21Input = { - readonly sessionID: Endpoint4_21Request["params"]["sessionID"] - readonly key: Endpoint4_21Request["params"]["key"] -} +export type Endpoint4_21Input = { readonly sessionID: Session.ID; readonly key: SessionContextEntry.Key } export type Endpoint4_21Output = EffectValue> export type SessionRemoveContextEntryOperation = ( input: Endpoint4_21Input, @@ -228,30 +261,27 @@ export type SessionRemoveContextEntryOperation = ( type Endpoint4_22Request = Parameters[0] export type Endpoint4_22Input = { - readonly sessionID: Endpoint4_22Request["params"]["sessionID"] - readonly after?: Endpoint4_22Request["query"]["after"] - readonly follow?: Endpoint4_22Request["query"]["follow"] + readonly sessionID: Session.ID + readonly after?: Event.Seq | undefined + readonly follow?: boolean | undefined } -export type Endpoint4_22Output = StreamValue>> +export type Endpoint4_22Output = SessionEvent.DurableEvent | EventLog.Synced export type SessionLogOperation = (input: Endpoint4_22Input) => Stream.Stream type Endpoint4_23Request = Parameters[0] -export type Endpoint4_23Input = { readonly sessionID: Endpoint4_23Request["params"]["sessionID"] } +export type Endpoint4_23Input = { readonly sessionID: Session.ID } export type Endpoint4_23Output = EffectValue> export type SessionInterruptOperation = (input: Endpoint4_23Input) => Effect.Effect type Endpoint4_24Request = Parameters[0] -export type Endpoint4_24Input = { readonly sessionID: Endpoint4_24Request["params"]["sessionID"] } +export type Endpoint4_24Input = { readonly sessionID: Session.ID } export type Endpoint4_24Output = EffectValue> export type SessionBackgroundOperation = (input: Endpoint4_24Input) => Effect.Effect type Endpoint4_25Request = Parameters[0] -export type Endpoint4_25Input = { - readonly sessionID: Endpoint4_25Request["params"]["sessionID"] - readonly messageID: Endpoint4_25Request["params"]["messageID"] -} -export type Endpoint4_25Output = EffectValue>["data"] -export type SessionMessageOperation = (input: Endpoint4_25Input) => Effect.Effect +export type Endpoint4_25Input = { readonly sessionID: Session.ID; readonly messageID: SessionMessage.ID } +export type Endpoint4_25Output = SessionMessage.Message +export type SessionMessageOperation = (input: Endpoint4_25Input) => Effect.Effect export interface SessionApi { readonly list: SessionListOperation @@ -284,12 +314,16 @@ export interface SessionApi { type Endpoint5_0Request = Parameters[0] export type Endpoint5_0Input = { - readonly sessionID: Endpoint5_0Request["params"]["sessionID"] - readonly limit?: Endpoint5_0Request["query"]["limit"] - readonly order?: Endpoint5_0Request["query"]["order"] - readonly cursor?: Endpoint5_0Request["query"]["cursor"] + readonly sessionID: Session.ID + readonly limit?: number | undefined + readonly order?: "asc" | "desc" | undefined + readonly cursor?: string | undefined +} +export type Endpoint5_0Output = { + readonly data: ReadonlyArray + readonly watermark?: Event.Seq | undefined + readonly cursor: { readonly previous?: string | undefined; readonly next?: string | undefined } } -export type Endpoint5_0Output = EffectValue> export type MessageListOperation = (input: Endpoint5_0Input) => Effect.Effect export interface MessageApi { @@ -297,13 +331,17 @@ export interface MessageApi { } type Endpoint6_0Request = Parameters[0] -export type Endpoint6_0Input = { readonly location?: Endpoint6_0Request["query"]["location"] } -export type Endpoint6_0Output = EffectValue> +export type Endpoint6_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint6_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type ModelListOperation = (input?: Endpoint6_0Input) => Effect.Effect type Endpoint6_1Request = Parameters[0] -export type Endpoint6_1Input = { readonly location?: Endpoint6_1Request["query"]["location"] } -export type Endpoint6_1Output = EffectValue> +export type Endpoint6_1Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint6_1Output = { readonly location: Location.Info; readonly data: Model.Info | undefined } export type ModelDefaultOperation = (input?: Endpoint6_1Input) => Effect.Effect export interface ModelApi { @@ -313,11 +351,11 @@ export interface ModelApi { type Endpoint7_0Request = Parameters[0] export type Endpoint7_0Input = { - readonly location?: Endpoint7_0Request["query"]["location"] - readonly prompt: Endpoint7_0Request["payload"]["prompt"] - readonly model?: Endpoint7_0Request["payload"]["model"] + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly prompt: string + readonly model?: Model.Ref | undefined } -export type Endpoint7_0Output = EffectValue>["data"] +export type Endpoint7_0Output = { readonly text: string } export type GenerateTextOperation = (input: Endpoint7_0Input) => Effect.Effect export interface GenerateApi { @@ -325,16 +363,18 @@ export interface GenerateApi { } type Endpoint8_0Request = Parameters[0] -export type Endpoint8_0Input = { readonly location?: Endpoint8_0Request["query"]["location"] } -export type Endpoint8_0Output = EffectValue> +export type Endpoint8_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint8_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type ProviderListOperation = (input?: Endpoint8_0Input) => Effect.Effect type Endpoint8_1Request = Parameters[0] export type Endpoint8_1Input = { - readonly providerID: Endpoint8_1Request["params"]["providerID"] - readonly location?: Endpoint8_1Request["query"]["location"] + readonly providerID: Provider.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } -export type Endpoint8_1Output = EffectValue> +export type Endpoint8_1Output = { readonly location: Location.Info; readonly data: Provider.Info } export type ProviderGetOperation = (input: Endpoint8_1Input) => Effect.Effect export interface ProviderApi { @@ -343,56 +383,58 @@ export interface ProviderApi { } type Endpoint9_0Request = Parameters[0] -export type Endpoint9_0Input = { readonly location?: Endpoint9_0Request["query"]["location"] } -export type Endpoint9_0Output = EffectValue> +export type Endpoint9_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint9_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type IntegrationListOperation = (input?: Endpoint9_0Input) => Effect.Effect type Endpoint9_1Request = Parameters[0] export type Endpoint9_1Input = { - readonly integrationID: Endpoint9_1Request["params"]["integrationID"] - readonly location?: Endpoint9_1Request["query"]["location"] + readonly integrationID: Integration.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } -export type Endpoint9_1Output = EffectValue> +export type Endpoint9_1Output = { readonly location: Location.Info; readonly data: Integration.Info | undefined } export type IntegrationGetOperation = (input: Endpoint9_1Input) => Effect.Effect type Endpoint9_2Request = Parameters[0] export type Endpoint9_2Input = { - readonly integrationID: Endpoint9_2Request["params"]["integrationID"] - readonly location?: Endpoint9_2Request["query"]["location"] - readonly key: Endpoint9_2Request["payload"]["key"] - readonly label?: Endpoint9_2Request["payload"]["label"] + readonly integrationID: Integration.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly key: string + readonly label?: string | undefined } export type Endpoint9_2Output = EffectValue> export type IntegrationConnectKeyOperation = (input: Endpoint9_2Input) => Effect.Effect type Endpoint9_3Request = Parameters[0] export type Endpoint9_3Input = { - readonly integrationID: Endpoint9_3Request["params"]["integrationID"] - readonly location?: Endpoint9_3Request["query"]["location"] - readonly methodID: Endpoint9_3Request["payload"]["methodID"] - readonly inputs: Endpoint9_3Request["payload"]["inputs"] - readonly label?: Endpoint9_3Request["payload"]["label"] + readonly integrationID: Integration.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly methodID: Integration.MethodID + readonly inputs: { readonly [x: string]: string } + readonly label?: string | undefined } -export type Endpoint9_3Output = EffectValue> +export type Endpoint9_3Output = { readonly location: Location.Info; readonly data: Integration.Attempt } export type IntegrationConnectOauthOperation = ( input: Endpoint9_3Input, ) => Effect.Effect type Endpoint9_4Request = Parameters[0] export type Endpoint9_4Input = { - readonly attemptID: Endpoint9_4Request["params"]["attemptID"] - readonly location?: Endpoint9_4Request["query"]["location"] + readonly attemptID: Integration.AttemptID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } -export type Endpoint9_4Output = EffectValue> +export type Endpoint9_4Output = { readonly location: Location.Info; readonly data: Integration.AttemptStatus } export type IntegrationAttemptStatusOperation = ( input: Endpoint9_4Input, ) => Effect.Effect type Endpoint9_5Request = Parameters[0] export type Endpoint9_5Input = { - readonly attemptID: Endpoint9_5Request["params"]["attemptID"] - readonly location?: Endpoint9_5Request["query"]["location"] - readonly code?: Endpoint9_5Request["payload"]["code"] + readonly attemptID: Integration.AttemptID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly code?: string | undefined } export type Endpoint9_5Output = EffectValue> export type IntegrationAttemptCompleteOperation = ( @@ -401,8 +443,8 @@ export type IntegrationAttemptCompleteOperation = ( type Endpoint9_6Request = Parameters[0] export type Endpoint9_6Input = { - readonly attemptID: Endpoint9_6Request["params"]["attemptID"] - readonly location?: Endpoint9_6Request["query"]["location"] + readonly attemptID: Integration.AttemptID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } export type Endpoint9_6Output = EffectValue> export type IntegrationAttemptCancelOperation = ( @@ -420,8 +462,10 @@ export interface IntegrationApi { } type Endpoint10_0Request = Parameters[0] -export type Endpoint10_0Input = { readonly location?: Endpoint10_0Request["query"]["location"] } -export type Endpoint10_0Output = EffectValue> +export type Endpoint10_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint10_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type ServerMcpListOperation = (input?: Endpoint10_0Input) => Effect.Effect export interface ServerMcpApi { @@ -430,17 +474,17 @@ export interface ServerMcpApi { type Endpoint11_0Request = Parameters[0] export type Endpoint11_0Input = { - readonly credentialID: Endpoint11_0Request["params"]["credentialID"] - readonly location?: Endpoint11_0Request["query"]["location"] - readonly label: Endpoint11_0Request["payload"]["label"] + readonly credentialID: Credential.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly label: string } export type Endpoint11_0Output = EffectValue> export type CredentialUpdateOperation = (input: Endpoint11_0Input) => Effect.Effect type Endpoint11_1Request = Parameters[0] export type Endpoint11_1Input = { - readonly credentialID: Endpoint11_1Request["params"]["credentialID"] - readonly location?: Endpoint11_1Request["query"]["location"] + readonly credentialID: Credential.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } export type Endpoint11_1Output = EffectValue> export type CredentialRemoveOperation = (input: Endpoint11_1Input) => Effect.Effect @@ -451,17 +495,19 @@ export interface CredentialApi { } type Endpoint12_0Request = Parameters[0] -export type Endpoint12_0Input = { readonly location?: Endpoint12_0Request["query"]["location"] } -export type Endpoint12_0Output = EffectValue> -export type ProjectCurrentOperation = (input?: Endpoint12_0Input) => Effect.Effect +export type Endpoint12_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint12_0Output = Project.Current +export type ProjectCurrentOperation = (input?: Endpoint12_0Input) => Effect.Effect type Endpoint12_1Request = Parameters[0] export type Endpoint12_1Input = { - readonly projectID: Endpoint12_1Request["params"]["projectID"] - readonly location?: Endpoint12_1Request["query"]["location"] + readonly projectID: Project.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } -export type Endpoint12_1Output = EffectValue> -export type ProjectDirectoriesOperation = (input: Endpoint12_1Input) => Effect.Effect +export type Endpoint12_1Output = Project.Directories +export type ProjectDirectoriesOperation = (input: Endpoint12_1Input) => Effect.Effect export interface ProjectApi { readonly current: ProjectCurrentOperation @@ -469,58 +515,52 @@ export interface ProjectApi { } type Endpoint13_0Request = Parameters[0] -export type Endpoint13_0Input = { readonly location?: Endpoint13_0Request["query"]["location"] } -export type Endpoint13_0Output = EffectValue> +export type Endpoint13_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint13_0Output = { + readonly location: Location.Info + readonly data: ReadonlyArray +} export type FormListRequestsOperation = (input?: Endpoint13_0Input) => Effect.Effect type Endpoint13_1Request = Parameters[0] -export type Endpoint13_1Input = { readonly sessionID: Endpoint13_1Request["params"]["sessionID"] } -export type Endpoint13_1Output = EffectValue>["data"] +export type Endpoint13_1Input = { readonly sessionID: string } +export type Endpoint13_1Output = ReadonlyArray export type FormListOperation = (input: Endpoint13_1Input) => Effect.Effect type Endpoint13_2Request = Parameters[0] export type Endpoint13_2Input = { - readonly sessionID: Endpoint13_2Request["params"]["sessionID"] - readonly id?: Endpoint13_2Request["payload"]["id"] - readonly title?: Endpoint13_2Request["payload"]["title"] - readonly metadata?: Endpoint13_2Request["payload"]["metadata"] - readonly mode: Endpoint13_2Request["payload"]["mode"] - readonly fields?: Endpoint13_2Request["payload"]["fields"] - readonly url?: Endpoint13_2Request["payload"]["url"] + readonly sessionID: string + readonly id?: Form.ID | undefined + readonly title?: string | undefined + readonly metadata?: Form.Metadata | undefined + readonly mode: "form" | "url" + readonly fields?: + | ReadonlyArray + | undefined + readonly url?: string | undefined } -export type Endpoint13_2Output = EffectValue>["data"] +export type Endpoint13_2Output = Form.FormInfo | Form.UrlInfo export type FormCreateOperation = (input: Endpoint13_2Input) => Effect.Effect type Endpoint13_3Request = Parameters[0] -export type Endpoint13_3Input = { - readonly sessionID: Endpoint13_3Request["params"]["sessionID"] - readonly formID: Endpoint13_3Request["params"]["formID"] -} -export type Endpoint13_3Output = EffectValue>["data"] +export type Endpoint13_3Input = { readonly sessionID: string; readonly formID: Form.ID } +export type Endpoint13_3Output = Form.FormInfo | Form.UrlInfo export type FormGetOperation = (input: Endpoint13_3Input) => Effect.Effect type Endpoint13_4Request = Parameters[0] -export type Endpoint13_4Input = { - readonly sessionID: Endpoint13_4Request["params"]["sessionID"] - readonly formID: Endpoint13_4Request["params"]["formID"] -} -export type Endpoint13_4Output = EffectValue>["data"] -export type FormStateOperation = (input: Endpoint13_4Input) => Effect.Effect +export type Endpoint13_4Input = { readonly sessionID: string; readonly formID: Form.ID } +export type Endpoint13_4Output = Form.State +export type FormStateOperation = (input: Endpoint13_4Input) => Effect.Effect type Endpoint13_5Request = Parameters[0] -export type Endpoint13_5Input = { - readonly sessionID: Endpoint13_5Request["params"]["sessionID"] - readonly formID: Endpoint13_5Request["params"]["formID"] - readonly answer: Endpoint13_5Request["payload"]["answer"] -} +export type Endpoint13_5Input = { readonly sessionID: string; readonly formID: Form.ID; readonly answer: Form.Answer } export type Endpoint13_5Output = EffectValue> export type FormReplyOperation = (input: Endpoint13_5Input) => Effect.Effect type Endpoint13_6Request = Parameters[0] -export type Endpoint13_6Input = { - readonly sessionID: Endpoint13_6Request["params"]["sessionID"] - readonly formID: Endpoint13_6Request["params"]["formID"] -} +export type Endpoint13_6Input = { readonly sessionID: string; readonly formID: Form.ID } export type Endpoint13_6Output = EffectValue> export type FormCancelOperation = (input: Endpoint13_6Input) => Effect.Effect @@ -535,23 +575,23 @@ export interface FormApi { } type Endpoint14_0Request = Parameters[0] -export type Endpoint14_0Input = { readonly location?: Endpoint14_0Request["query"]["location"] } -export type Endpoint14_0Output = EffectValue> +export type Endpoint14_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint14_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type PermissionListRequestsOperation = ( input?: Endpoint14_0Input, ) => Effect.Effect type Endpoint14_1Request = Parameters[0] -export type Endpoint14_1Input = { readonly projectID?: Endpoint14_1Request["query"]["projectID"] } -export type Endpoint14_1Output = EffectValue< - ReturnType ->["data"] +export type Endpoint14_1Input = { readonly projectID?: Project.ID | undefined } +export type Endpoint14_1Output = ReadonlyArray export type PermissionListSavedOperation = ( input?: Endpoint14_1Input, ) => Effect.Effect type Endpoint14_2Request = Parameters[0] -export type Endpoint14_2Input = { readonly id: Endpoint14_2Request["params"]["id"] } +export type Endpoint14_2Input = { readonly id: PermissionSaved.ID } export type Endpoint14_2Output = EffectValue> export type PermissionRemoveSavedOperation = ( input: Endpoint14_2Input, @@ -559,43 +599,34 @@ export type PermissionRemoveSavedOperation = ( type Endpoint14_3Request = Parameters[0] export type Endpoint14_3Input = { - readonly sessionID: Endpoint14_3Request["params"]["sessionID"] - readonly id?: Endpoint14_3Request["payload"]["id"] - readonly action: Endpoint14_3Request["payload"]["action"] - readonly resources: Endpoint14_3Request["payload"]["resources"] - readonly save?: Endpoint14_3Request["payload"]["save"] - readonly metadata?: Endpoint14_3Request["payload"]["metadata"] - readonly source?: Endpoint14_3Request["payload"]["source"] - readonly agent?: Endpoint14_3Request["payload"]["agent"] + readonly sessionID: Session.ID + readonly id?: Permission.ID | undefined + readonly action: string + readonly resources: ReadonlyArray + readonly save?: ReadonlyArray | undefined + readonly metadata?: { readonly [x: string]: unknown } | undefined + readonly source?: Permission.Source | undefined + readonly agent?: Agent.ID | undefined } -export type Endpoint14_3Output = EffectValue< - ReturnType ->["data"] +export type Endpoint14_3Output = { readonly id: Permission.ID; readonly effect: Permission.Effect } export type PermissionCreateOperation = (input: Endpoint14_3Input) => Effect.Effect type Endpoint14_4Request = Parameters[0] -export type Endpoint14_4Input = { readonly sessionID: Endpoint14_4Request["params"]["sessionID"] } -export type Endpoint14_4Output = EffectValue< - ReturnType ->["data"] +export type Endpoint14_4Input = { readonly sessionID: Session.ID } +export type Endpoint14_4Output = ReadonlyArray export type PermissionListOperation = (input: Endpoint14_4Input) => Effect.Effect type Endpoint14_5Request = Parameters[0] -export type Endpoint14_5Input = { - readonly sessionID: Endpoint14_5Request["params"]["sessionID"] - readonly requestID: Endpoint14_5Request["params"]["requestID"] -} -export type Endpoint14_5Output = EffectValue< - ReturnType ->["data"] -export type PermissionGetOperation = (input: Endpoint14_5Input) => Effect.Effect +export type Endpoint14_5Input = { readonly sessionID: Session.ID; readonly requestID: Permission.ID } +export type Endpoint14_5Output = Permission.Request +export type PermissionGetOperation = (input: Endpoint14_5Input) => Effect.Effect type Endpoint14_6Request = Parameters[0] export type Endpoint14_6Input = { - readonly sessionID: Endpoint14_6Request["params"]["sessionID"] - readonly requestID: Endpoint14_6Request["params"]["requestID"] - readonly reply: Endpoint14_6Request["payload"]["reply"] - readonly message?: Endpoint14_6Request["payload"]["message"] + readonly sessionID: Session.ID + readonly requestID: Permission.ID + readonly reply: Permission.Reply + readonly message?: string | undefined } export type Endpoint14_6Output = EffectValue> export type PermissionReplyOperation = (input: Endpoint14_6Input) => Effect.Effect @@ -612,20 +643,20 @@ export interface PermissionApi { type Endpoint15_0Request = Parameters[0] export type Endpoint15_0Input = { - readonly location?: Endpoint15_0Request["query"]["location"] - readonly path?: Endpoint15_0Request["query"]["path"] + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly path?: RelativePath | undefined } -export type Endpoint15_0Output = EffectValue> +export type Endpoint15_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type FileListOperation = (input?: Endpoint15_0Input) => Effect.Effect type Endpoint15_1Request = Parameters[0] export type Endpoint15_1Input = { - readonly location?: Endpoint15_1Request["query"]["location"] - readonly query: Endpoint15_1Request["query"]["query"] - readonly type?: Endpoint15_1Request["query"]["type"] - readonly limit?: Endpoint15_1Request["query"]["limit"] + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly query: string + readonly type?: "file" | "directory" | undefined + readonly limit?: number | undefined } -export type Endpoint15_1Output = EffectValue> +export type Endpoint15_1Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type FileFindOperation = (input: Endpoint15_1Input) => Effect.Effect export interface FileApi { @@ -634,8 +665,10 @@ export interface FileApi { } type Endpoint16_0Request = Parameters[0] -export type Endpoint16_0Input = { readonly location?: Endpoint16_0Request["query"]["location"] } -export type Endpoint16_0Output = EffectValue> +export type Endpoint16_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint16_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type CommandListOperation = (input?: Endpoint16_0Input) => Effect.Effect export interface CommandApi { @@ -643,8 +676,10 @@ export interface CommandApi { } type Endpoint17_0Request = Parameters[0] -export type Endpoint17_0Input = { readonly location?: Endpoint17_0Request["query"]["location"] } -export type Endpoint17_0Output = EffectValue> +export type Endpoint17_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint17_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type SkillListOperation = (input?: Endpoint17_0Input) => Effect.Effect export interface SkillApi { @@ -654,7 +689,7 @@ export interface SkillApi { export type Endpoint18_0Output = StreamValue>> export type EventSubscribeOperation = () => Stream.Stream -export type Endpoint18_1Output = StreamValue>> +export type Endpoint18_1Output = EventLog.Hint | EventLog.SweepRequired export type EventChangesOperation = () => Stream.Stream export interface EventApi { @@ -663,44 +698,46 @@ export interface EventApi { } type Endpoint19_0Request = Parameters[0] -export type Endpoint19_0Input = { readonly location?: Endpoint19_0Request["query"]["location"] } -export type Endpoint19_0Output = EffectValue> +export type Endpoint19_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint19_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type PtyListOperation = (input?: Endpoint19_0Input) => Effect.Effect type Endpoint19_1Request = Parameters[0] export type Endpoint19_1Input = { - readonly location?: Endpoint19_1Request["query"]["location"] - readonly command?: Endpoint19_1Request["payload"]["command"] - readonly args?: Endpoint19_1Request["payload"]["args"] - readonly cwd?: Endpoint19_1Request["payload"]["cwd"] - readonly title?: Endpoint19_1Request["payload"]["title"] - readonly env?: Endpoint19_1Request["payload"]["env"] + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly command?: string | undefined + readonly args?: ReadonlyArray | undefined + readonly cwd?: string | undefined + readonly title?: string | undefined + readonly env?: { readonly [x: string]: string } | undefined } -export type Endpoint19_1Output = EffectValue> +export type Endpoint19_1Output = { readonly location: Location.Info; readonly data: Pty.Info } export type PtyCreateOperation = (input?: Endpoint19_1Input) => Effect.Effect type Endpoint19_2Request = Parameters[0] export type Endpoint19_2Input = { - readonly ptyID: Endpoint19_2Request["params"]["ptyID"] - readonly location?: Endpoint19_2Request["query"]["location"] + readonly ptyID: Pty.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } -export type Endpoint19_2Output = EffectValue> +export type Endpoint19_2Output = { readonly location: Location.Info; readonly data: Pty.Info } export type PtyGetOperation = (input: Endpoint19_2Input) => Effect.Effect type Endpoint19_3Request = Parameters[0] export type Endpoint19_3Input = { - readonly ptyID: Endpoint19_3Request["params"]["ptyID"] - readonly location?: Endpoint19_3Request["query"]["location"] - readonly title?: Endpoint19_3Request["payload"]["title"] - readonly size?: Endpoint19_3Request["payload"]["size"] + readonly ptyID: Pty.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly title?: string | undefined + readonly size?: { readonly rows: number; readonly cols: number } | undefined } -export type Endpoint19_3Output = EffectValue> +export type Endpoint19_3Output = { readonly location: Location.Info; readonly data: Pty.Info } export type PtyUpdateOperation = (input: Endpoint19_3Input) => Effect.Effect type Endpoint19_4Request = Parameters[0] export type Endpoint19_4Input = { - readonly ptyID: Endpoint19_4Request["params"]["ptyID"] - readonly location?: Endpoint19_4Request["query"]["location"] + readonly ptyID: Pty.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } export type Endpoint19_4Output = EffectValue> export type PtyRemoveOperation = (input: Endpoint19_4Input) => Effect.Effect @@ -714,43 +751,53 @@ export interface PtyApi { } type Endpoint20_0Request = Parameters[0] -export type Endpoint20_0Input = { readonly location?: Endpoint20_0Request["query"]["location"] } -export type Endpoint20_0Output = EffectValue> +export type Endpoint20_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint20_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type ShellListOperation = (input?: Endpoint20_0Input) => Effect.Effect type Endpoint20_1Request = Parameters[0] export type Endpoint20_1Input = { - readonly location?: Endpoint20_1Request["query"]["location"] - readonly command: Endpoint20_1Request["payload"]["command"] - readonly cwd?: Endpoint20_1Request["payload"]["cwd"] - readonly timeout?: Endpoint20_1Request["payload"]["timeout"] - readonly metadata?: Endpoint20_1Request["payload"]["metadata"] + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly command: string + readonly cwd?: string | undefined + readonly timeout?: number | undefined + readonly metadata?: { readonly [x: string]: unknown } | undefined } -export type Endpoint20_1Output = EffectValue> +export type Endpoint20_1Output = { readonly location: Location.Info; readonly data: Shell.Info } export type ShellCreateOperation = (input: Endpoint20_1Input) => Effect.Effect type Endpoint20_2Request = Parameters[0] export type Endpoint20_2Input = { - readonly id: Endpoint20_2Request["params"]["id"] - readonly location?: Endpoint20_2Request["query"]["location"] + readonly id: Shell.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } -export type Endpoint20_2Output = EffectValue> +export type Endpoint20_2Output = { readonly location: Location.Info; readonly data: Shell.Info } export type ShellGetOperation = (input: Endpoint20_2Input) => Effect.Effect type Endpoint20_3Request = Parameters[0] export type Endpoint20_3Input = { - readonly id: Endpoint20_3Request["params"]["id"] - readonly location?: Endpoint20_3Request["query"]["location"] - readonly cursor?: Endpoint20_3Request["query"]["cursor"] - readonly limit?: Endpoint20_3Request["query"]["limit"] + readonly id: Shell.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly cursor?: number | undefined + readonly limit?: number | undefined +} +export type Endpoint20_3Output = { + readonly location: Location.Info + readonly data: { + readonly output: string + readonly cursor: number + readonly size: number + readonly truncated: boolean + } } -export type Endpoint20_3Output = EffectValue> export type ShellOutputOperation = (input: Endpoint20_3Input) => Effect.Effect type Endpoint20_4Request = Parameters[0] export type Endpoint20_4Input = { - readonly id: Endpoint20_4Request["params"]["id"] - readonly location?: Endpoint20_4Request["query"]["location"] + readonly id: Shell.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } export type Endpoint20_4Output = EffectValue> export type ShellRemoveOperation = (input: Endpoint20_4Input) => Effect.Effect @@ -764,31 +811,30 @@ export interface ShellApi { } type Endpoint21_0Request = Parameters[0] -export type Endpoint21_0Input = { readonly location?: Endpoint21_0Request["query"]["location"] } -export type Endpoint21_0Output = EffectValue> +export type Endpoint21_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint21_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type QuestionListRequestsOperation = ( input?: Endpoint21_0Input, ) => Effect.Effect type Endpoint21_1Request = Parameters[0] -export type Endpoint21_1Input = { readonly sessionID: Endpoint21_1Request["params"]["sessionID"] } -export type Endpoint21_1Output = EffectValue>["data"] +export type Endpoint21_1Input = { readonly sessionID: Session.ID } +export type Endpoint21_1Output = ReadonlyArray export type QuestionListOperation = (input: Endpoint21_1Input) => Effect.Effect type Endpoint21_2Request = Parameters[0] export type Endpoint21_2Input = { - readonly sessionID: Endpoint21_2Request["params"]["sessionID"] - readonly requestID: Endpoint21_2Request["params"]["requestID"] - readonly answers: Endpoint21_2Request["payload"]["answers"] + readonly sessionID: Session.ID + readonly requestID: Question.ID + readonly answers: ReadonlyArray } export type Endpoint21_2Output = EffectValue> export type QuestionReplyOperation = (input: Endpoint21_2Input) => Effect.Effect type Endpoint21_3Request = Parameters[0] -export type Endpoint21_3Input = { - readonly sessionID: Endpoint21_3Request["params"]["sessionID"] - readonly requestID: Endpoint21_3Request["params"]["requestID"] -} +export type Endpoint21_3Input = { readonly sessionID: Session.ID; readonly requestID: Question.ID } export type Endpoint21_3Output = EffectValue> export type QuestionRejectOperation = (input: Endpoint21_3Input) => Effect.Effect @@ -800,8 +846,10 @@ export interface QuestionApi { } type Endpoint22_0Request = Parameters[0] -export type Endpoint22_0Input = { readonly location?: Endpoint22_0Request["query"]["location"] } -export type Endpoint22_0Output = EffectValue> +export type Endpoint22_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint22_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type ReferenceListOperation = (input?: Endpoint22_0Input) => Effect.Effect export interface ReferenceApi { @@ -810,29 +858,29 @@ export interface ReferenceApi { type Endpoint23_0Request = Parameters[0] export type Endpoint23_0Input = { - readonly projectID: Endpoint23_0Request["params"]["projectID"] - readonly location?: Endpoint23_0Request["query"]["location"] - readonly strategy: Endpoint23_0Request["payload"]["strategy"] - readonly directory: Endpoint23_0Request["payload"]["directory"] - readonly name?: Endpoint23_0Request["payload"]["name"] + readonly projectID: Project.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly strategy: ProjectCopy.StrategyID + readonly directory: AbsolutePath + readonly name?: string | undefined } -export type Endpoint23_0Output = EffectValue> -export type ProjectCopyCreateOperation = (input: Endpoint23_0Input) => Effect.Effect +export type Endpoint23_0Output = ProjectCopy.Copy +export type ProjectCopyCreateOperation = (input: Endpoint23_0Input) => Effect.Effect type Endpoint23_1Request = Parameters[0] export type Endpoint23_1Input = { - readonly projectID: Endpoint23_1Request["params"]["projectID"] - readonly location?: Endpoint23_1Request["query"]["location"] - readonly directory: Endpoint23_1Request["payload"]["directory"] - readonly force: Endpoint23_1Request["payload"]["force"] + readonly projectID: Project.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly directory: AbsolutePath + readonly force: boolean } export type Endpoint23_1Output = EffectValue> export type ProjectCopyRemoveOperation = (input: Endpoint23_1Input) => Effect.Effect type Endpoint23_2Request = Parameters[0] export type Endpoint23_2Input = { - readonly projectID: Endpoint23_2Request["params"]["projectID"] - readonly location?: Endpoint23_2Request["query"]["location"] + readonly projectID: Project.ID + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } export type Endpoint23_2Output = EffectValue> export type ProjectCopyRefreshOperation = (input: Endpoint23_2Input) => Effect.Effect @@ -844,17 +892,19 @@ export interface ProjectCopyApi { } type Endpoint24_0Request = Parameters[0] -export type Endpoint24_0Input = { readonly location?: Endpoint24_0Request["query"]["location"] } -export type Endpoint24_0Output = EffectValue> +export type Endpoint24_0Input = { + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined +} +export type Endpoint24_0Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type VcsStatusOperation = (input?: Endpoint24_0Input) => Effect.Effect type Endpoint24_1Request = Parameters[0] export type Endpoint24_1Input = { - readonly location?: Endpoint24_1Request["query"]["location"] - readonly mode: Endpoint24_1Request["query"]["mode"] - readonly context?: Endpoint24_1Request["query"]["context"] + readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined + readonly mode: Vcs.Mode + readonly context?: number | undefined } -export type Endpoint24_1Output = EffectValue> +export type Endpoint24_1Output = { readonly location: Location.Info; readonly data: ReadonlyArray } export type VcsDiffOperation = (input: Endpoint24_1Input) => Effect.Effect export interface VcsApi { diff --git a/packages/httpapi-codegen/src/index.ts b/packages/httpapi-codegen/src/index.ts index e72eb74c7f..1afc67dc41 100644 --- a/packages/httpapi-codegen/src/index.ts +++ b/packages/httpapi-codegen/src/index.ts @@ -35,6 +35,14 @@ export type Contract = { readonly groups: ReadonlyArray } +export type EffectTypeReference = { + readonly schema: Schema.Top + readonly name: string + readonly import: string +} + +type ResolvedEffectTypeReference = Omit & { readonly ast: SchemaAST.AST } + export class GenerationError extends Schema.TaggedErrorClass()("GenerationError", { reason: Schema.String, }) { @@ -268,7 +276,11 @@ export function emitEffectImported( export function emitEffectShape( contract: Contract, - options: { readonly module: string; readonly api: string }, + options: { + readonly module: string + readonly api: string + readonly typeReferences?: ReadonlyArray + }, ): Output { return { operations: operations(contract.groups), @@ -307,7 +319,16 @@ export function emitPromise( } } -function renderEffectShape(groups: ReadonlyArray, options: { readonly module: string; readonly api: string }) { +function renderEffectShape( + groups: ReadonlyArray, + options: { + readonly module: string + readonly api: string + readonly typeReferences?: ReadonlyArray + }, +) { + const references = effectTypeReferences(options.typeReferences ?? []) + const imports = new Set() const endpointTypes = groups.map((group, groupIndex) => { const rawGroup = group.endpoints[0]?.topLevel ? "RawClient" : `RawClient[${JSON.stringify(group.sourceIdentifier)}]` const endpoints = group.endpoints.map((endpoint, endpointIndex) => { @@ -317,21 +338,29 @@ function renderEffectShape(groups: ReadonlyArray, options: { readonly mod ? "" : `type ${prefix}Request = Parameters<${rawGroup}[${JSON.stringify(endpoint.endpoint.name)}]>[0]` const input = endpoint.input - .map( - (field) => - `readonly ${JSON.stringify(field.name)}${field.optional ? "?" : ""}: ${prefix}Request[${JSON.stringify(field.source)}][${JSON.stringify(field.name)}]`, - ) + .map((field) => { + const schema = effectInputSchema(endpoint, field) + const type = schema === undefined ? undefined : effectType(schema, references, imports)?.source + return `readonly ${JSON.stringify(field.name)}${field.optional ? "?" : ""}: ${type ?? `${prefix}Request[${JSON.stringify(field.source)}][${JSON.stringify(field.name)}]`}` + }) .join("; ") const inputType = endpoint.operation.inputMode === "none" ? "" : `export type ${prefix}Input = { ${input} }` const rawOutput = `EffectValue>` - const outputType = isStreamSchema(endpoint.successes[0]) - ? `export type ${prefix}Output = StreamValue<${rawOutput}>` - : `export type ${prefix}Output = ${endpoint.unwrapData ? `(${rawOutput})["data"]` : rawOutput}` + const schema = effectOutputSchema(endpoint) + const rendered = schema === undefined ? undefined : effectType(schema, references, imports) + const type = rendered?.source + const fallback = isStreamSchema(endpoint.successes[0]) + ? `StreamValue<${rawOutput}>` + : endpoint.unwrapData + ? `(${rawOutput})["data"]` + : rawOutput + const outputType = `export type ${prefix}Output = ${type ?? fallback}` + const operationOutput = rendered?.authoritative ? rendered.source : `${prefix}Output` return [ request, endpoint.operation.inputMode === "none" ? "" : inputType, outputType, - `export type ${groupShapeTypeName(group, endpoint)} = (${endpoint.operation.inputMode === "none" ? "" : `input${endpoint.operation.inputMode === "optional" ? "?" : ""}: ${prefix}Input`}) => ${endpoint.operation.success === "stream" ? `Stream.Stream<${prefix}Output, E>` : `Effect.Effect<${prefix}Output, E>`}`, + `export type ${groupShapeTypeName(group, endpoint)} = (${endpoint.operation.inputMode === "none" ? "" : `input${endpoint.operation.inputMode === "optional" ? "?" : ""}: ${prefix}Input`}) => ${endpoint.operation.success === "stream" ? `Stream.Stream<${operationOutput}, E>` : `Effect.Effect<${operationOutput}, E>`}`, ] .filter(Boolean) .join("\n") @@ -355,6 +384,7 @@ function renderEffectShape(groups: ReadonlyArray, options: { readonly mod import type { Effect, Stream } from "effect" import type { HttpApiClient } from "effect/unstable/httpapi" import type { ${options.api} } from ${JSON.stringify(options.module)} +${[...imports].join("\n")} type RawClient = HttpApiClient.ForApi type EffectValue = A extends Effect.Effect ? Success : never @@ -368,6 +398,102 @@ ${clientFields.join("\n")} ` } +function effectTypeReferences(input: ReadonlyArray) { + const names = new Map() + const asts = new Map() + const brands = new Map() + for (const reference of input) { + const value = { name: reference.name, import: reference.import, ast: reference.schema.ast } + asts.set(reference.schema.ast, value) + asts.set(Schema.toType(reference.schema).ast, value) + const document = SchemaRepresentation.toCodeDocument( + SchemaRepresentation.fromASTs([Schema.toType(reference.schema).ast]), + ) + const name = document.codes[0]?.Type + const type = + name === undefined + ? undefined + : (document.references.nonRecursives.find((item) => item.$ref === name)?.code.Type ?? name) + if (type?.includes("Brand.Brand<") && !brands.has(type)) brands.set(type, value) + if (name === undefined || !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) continue + const previous = names.get(name) + if (previous !== undefined) { + if (previous.ast !== reference.schema.ast) { + throw new GenerationError({ reason: `Conflicting Effect type reference: ${name}` }) + } + continue + } + names.set(name, value) + } + return { names, asts, brands } +} + +function effectType(schema: Schema.Top, references: ReturnType, imports: Set) { + const projected = Schema.toType(schema) + const direct = references.asts.get(schema.ast) ?? references.asts.get(projected.ast) + if (direct !== undefined) { + imports.add(direct.import) + return { source: direct.name, authoritative: true } + } + const document = SchemaRepresentation.toCodeDocument(SchemaRepresentation.fromASTs([projected.ast])) + const source = new Map(document.references.nonRecursives.map((reference) => [reference.$ref, reference.code.Type])) + const expand = (type: string, seen = new Set()): string => { + for (const [name, value] of source) { + const pattern = typeReferencePattern(name) + if (!pattern.test(type)) continue + const reference = references.names.get(name) + if (reference !== undefined) { + imports.add(reference.import) + type = type.replace(typeReferencePattern(name), reference.name) + continue + } + if (seen.has(name)) return type + type = type.replace(typeReferencePattern(name), `(${expand(value, new Set([...seen, name]))})`) + } + return type + } + const root = document.codes[0].Type + const authoritative = references.names.has(root) + let type = expand(root) + for (const [brand, reference] of references.brands) { + if (!type.includes(brand)) continue + imports.add(reference.import) + type = type.replaceAll(brand, reference.name) + } + if (/\b(?:Brand|Schema)\./.test(type)) return undefined + return { source: type, authoritative } +} + +function effectInputSchema(endpoint: Endpoint, field: InputField) { + const schema = + field.source === "params" + ? endpoint.params + : field.source === "query" + ? endpoint.query + : field.source === "headers" + ? endpoint.headers + : endpoint.payloads[0] + if (schema === undefined) return undefined + const ast = Schema.toType(schema).ast + if (!SchemaAST.isObjects(ast)) return undefined + const property = ast.propertySignatures.find((property) => property.name === field.name) + return property === undefined ? undefined : Schema.make(property.type) +} + +function effectOutputSchema(endpoint: Endpoint) { + const schema = endpoint.successes[0] + if (HttpApiSchema.isNoContent(schema.ast)) return undefined + if (isStreamSchema(schema)) { + if (schema._tag === "StreamUint8Array") return undefined + return schema.sseMode === "data" ? streamDataSchema(schema) : schema.events + } + if (!endpoint.unwrapData) return schema + const ast = Schema.toType(schema).ast + if (!SchemaAST.isObjects(ast)) return undefined + const data = ast.propertySignatures.find((property) => property.name === "data") + return data === undefined ? undefined : Schema.make(data.type) +} + function groupShapeName(group: Group) { return `${identifierPart(group.identifier)}Api` } diff --git a/packages/httpapi-codegen/test/generate.test.ts b/packages/httpapi-codegen/test/generate.test.ts index 86a736bfdf..6c9e332d01 100644 --- a/packages/httpapi-codegen/test/generate.test.ts +++ b/packages/httpapi-codegen/test/generate.test.ts @@ -9,6 +9,7 @@ import { compile as compileContract, emitEffect, emitEffectImported, + emitEffectShape, emitPromise, generate, GenerationError, @@ -88,6 +89,43 @@ describe("HttpApiCodegen.generate", () => { ) }) + test("emits authoritative schema types in the Effect API shape", () => { + const ID = Schema.String.pipe(Schema.brand("SessionID")) + const Info = Schema.Struct({ id: Schema.String }).annotate({ identifier: "Session.Info" }) + const output = emitEffectShape( + compileContract( + api( + HttpApiEndpoint.get("get", "/session/:id", { + params: { id: ID }, + success: Schema.Struct({ data: Info }), + }), + ), + ), + { + module: "@example/api", + api: "Api", + typeReferences: [ + { + schema: ID, + name: "Session.ID", + import: 'import type { Session } from "@example/schema"', + }, + { + schema: Info, + name: "Session.Info", + import: 'import type { Session } from "@example/schema"', + }, + ], + }, + ) + const source = output.files.find((file) => file.path === "api.ts")?.content + + expect(source).toContain('import type { Session } from "@example/schema"') + expect(source).toContain('export type Endpoint0_0Input = { readonly "id": Session.ID }') + expect(source).toContain("export type Endpoint0_0Output = Session.Info") + expect(source).toContain("Effect.Effect") + }) + test("projects imported endpoint constants into a generated API", () => { const output = emitEffectImported( compileContract(