207 lines · 9.4 KB · TypeScript
RawDownload
import {  type GitActionProgressEvent,  type GitRunStackedActionInput,  type GitRunStackedActionResult,  type NativeApi,  ORCHESTRATION_WS_METHODS,  type ServerSettingsPatch,  WS_METHODS,} from "@t3tools/contracts";import { Effect, Stream } from "effect"; import { type WsRpcProtocolClient } from "./rpc/protocol";import { WsTransport } from "./wsTransport"; type RpcTag = keyof WsRpcProtocolClient & string;type RpcMethod<TTag extends RpcTag> = WsRpcProtocolClient[TTag];type RpcInput<TTag extends RpcTag> = Parameters<RpcMethod<TTag>>[0]; type RpcUnaryMethod<TTag extends RpcTag> =  RpcMethod<TTag> extends (input: any, options?: any) => Effect.Effect<infer TSuccess, any, any>    ? (input: RpcInput<TTag>) => Promise<TSuccess>    : never; type RpcUnaryNoArgMethod<TTag extends RpcTag> =  RpcMethod<TTag> extends (input: any, options?: any) => Effect.Effect<infer TSuccess, any, any>    ? () => Promise<TSuccess>    : never; type RpcStreamMethod<TTag extends RpcTag> =  RpcMethod<TTag> extends (input: any, options?: any) => Stream.Stream<infer TEvent, any, any>    ? (listener: (event: TEvent) => void) => () => void    : never; interface GitRunStackedActionOptions {  readonly onProgress?: (event: GitActionProgressEvent) => void;} export interface WsRpcClient {  readonly dispose: () => Promise<void>;  readonly terminal: {    readonly open: RpcUnaryMethod<typeof WS_METHODS.terminalOpen>;    readonly write: RpcUnaryMethod<typeof WS_METHODS.terminalWrite>;    readonly resize: RpcUnaryMethod<typeof WS_METHODS.terminalResize>;    readonly clear: RpcUnaryMethod<typeof WS_METHODS.terminalClear>;    readonly restart: RpcUnaryMethod<typeof WS_METHODS.terminalRestart>;    readonly close: RpcUnaryMethod<typeof WS_METHODS.terminalClose>;    readonly onEvent: RpcStreamMethod<typeof WS_METHODS.subscribeTerminalEvents>;  };  readonly projects: {    readonly searchEntries: RpcUnaryMethod<typeof WS_METHODS.projectsSearchEntries>;    readonly writeFile: RpcUnaryMethod<typeof WS_METHODS.projectsWriteFile>;  };  readonly shell: {    readonly openInEditor: (input: {      readonly cwd: Parameters<NativeApi["shell"]["openInEditor"]>[0];      readonly editor: Parameters<NativeApi["shell"]["openInEditor"]>[1];    }) => ReturnType<NativeApi["shell"]["openInEditor"]>;  };  readonly git: {    readonly pull: RpcUnaryMethod<typeof WS_METHODS.gitPull>;    readonly status: RpcUnaryMethod<typeof WS_METHODS.gitStatus>;    readonly runStackedAction: (      input: GitRunStackedActionInput,      options?: GitRunStackedActionOptions,    ) => Promise<GitRunStackedActionResult>;    readonly listBranches: RpcUnaryMethod<typeof WS_METHODS.gitListBranches>;    readonly createWorktree: RpcUnaryMethod<typeof WS_METHODS.gitCreateWorktree>;    readonly removeWorktree: RpcUnaryMethod<typeof WS_METHODS.gitRemoveWorktree>;    readonly createBranch: RpcUnaryMethod<typeof WS_METHODS.gitCreateBranch>;    readonly checkout: RpcUnaryMethod<typeof WS_METHODS.gitCheckout>;    readonly init: RpcUnaryMethod<typeof WS_METHODS.gitInit>;    readonly resolvePullRequest: RpcUnaryMethod<typeof WS_METHODS.gitResolvePullRequest>;    readonly preparePullRequestThread: RpcUnaryMethod<      typeof WS_METHODS.gitPreparePullRequestThread    >;  };  readonly server: {    readonly getConfig: RpcUnaryNoArgMethod<typeof WS_METHODS.serverGetConfig>;    readonly refreshProviders: RpcUnaryNoArgMethod<typeof WS_METHODS.serverRefreshProviders>;    readonly upsertKeybinding: RpcUnaryMethod<typeof WS_METHODS.serverUpsertKeybinding>;    readonly getSettings: RpcUnaryNoArgMethod<typeof WS_METHODS.serverGetSettings>;    readonly updateSettings: (      patch: ServerSettingsPatch,    ) => ReturnType<RpcUnaryMethod<typeof WS_METHODS.serverUpdateSettings>>;    readonly subscribeConfig: RpcStreamMethod<typeof WS_METHODS.subscribeServerConfig>;    readonly subscribeLifecycle: RpcStreamMethod<typeof WS_METHODS.subscribeServerLifecycle>;  };  readonly orchestration: {    readonly getSnapshot: RpcUnaryNoArgMethod<typeof ORCHESTRATION_WS_METHODS.getSnapshot>;    readonly dispatchCommand: RpcUnaryMethod<typeof ORCHESTRATION_WS_METHODS.dispatchCommand>;    readonly getTurnDiff: RpcUnaryMethod<typeof ORCHESTRATION_WS_METHODS.getTurnDiff>;    readonly getFullThreadDiff: RpcUnaryMethod<typeof ORCHESTRATION_WS_METHODS.getFullThreadDiff>;    readonly replayEvents: RpcUnaryMethod<typeof ORCHESTRATION_WS_METHODS.replayEvents>;    readonly onDomainEvent: RpcStreamMethod<typeof WS_METHODS.subscribeOrchestrationDomainEvents>;  };} let sharedWsRpcClient: WsRpcClient | null = null; export function getWsRpcClient(): WsRpcClient {  if (sharedWsRpcClient) {    return sharedWsRpcClient;  }  sharedWsRpcClient = createWsRpcClient();  return sharedWsRpcClient;} export async function __resetWsRpcClientForTests() {  await sharedWsRpcClient?.dispose();  sharedWsRpcClient = null;} export function createWsRpcClient(transport = new WsTransport()): WsRpcClient {  return {    dispose: () => transport.dispose(),    terminal: {      open: (input) => transport.request((client) => client[WS_METHODS.terminalOpen](input)),      write: (input) => transport.request((client) => client[WS_METHODS.terminalWrite](input)),      resize: (input) => transport.request((client) => client[WS_METHODS.terminalResize](input)),      clear: (input) => transport.request((client) => client[WS_METHODS.terminalClear](input)),      restart: (input) => transport.request((client) => client[WS_METHODS.terminalRestart](input)),      close: (input) => transport.request((client) => client[WS_METHODS.terminalClose](input)),      onEvent: (listener) =>        transport.subscribe((client) => client[WS_METHODS.subscribeTerminalEvents]({}), listener),    },    projects: {      searchEntries: (input) =>        transport.request((client) => client[WS_METHODS.projectsSearchEntries](input)),      writeFile: (input) =>        transport.request((client) => client[WS_METHODS.projectsWriteFile](input)),    },    shell: {      openInEditor: (input) =>        transport.request((client) => client[WS_METHODS.shellOpenInEditor](input)),    },    git: {      pull: (input) => transport.request((client) => client[WS_METHODS.gitPull](input)),      status: (input) => transport.request((client) => client[WS_METHODS.gitStatus](input)),      runStackedAction: async (input, options) => {        let result: GitRunStackedActionResult | null = null;         await transport.requestStream(          (client) => client[WS_METHODS.gitRunStackedAction](input),          (event) => {            options?.onProgress?.(event);            if (event.kind === "action_finished") {              result = event.result;            }          },        );         if (result) {          return result;        }         throw new Error("Git action stream completed without a final result.");      },      listBranches: (input) =>        transport.request((client) => client[WS_METHODS.gitListBranches](input)),      createWorktree: (input) =>        transport.request((client) => client[WS_METHODS.gitCreateWorktree](input)),      removeWorktree: (input) =>        transport.request((client) => client[WS_METHODS.gitRemoveWorktree](input)),      createBranch: (input) =>        transport.request((client) => client[WS_METHODS.gitCreateBranch](input)),      checkout: (input) => transport.request((client) => client[WS_METHODS.gitCheckout](input)),      init: (input) => transport.request((client) => client[WS_METHODS.gitInit](input)),      resolvePullRequest: (input) =>        transport.request((client) => client[WS_METHODS.gitResolvePullRequest](input)),      preparePullRequestThread: (input) =>        transport.request((client) => client[WS_METHODS.gitPreparePullRequestThread](input)),    },    server: {      getConfig: () => transport.request((client) => client[WS_METHODS.serverGetConfig]({})),      refreshProviders: () =>        transport.request((client) => client[WS_METHODS.serverRefreshProviders]({})),      upsertKeybinding: (input) =>        transport.request((client) => client[WS_METHODS.serverUpsertKeybinding](input)),      getSettings: () => transport.request((client) => client[WS_METHODS.serverGetSettings]({})),      updateSettings: (patch) =>        transport.request((client) => client[WS_METHODS.serverUpdateSettings]({ patch })),      subscribeConfig: (listener) =>        transport.subscribe((client) => client[WS_METHODS.subscribeServerConfig]({}), listener),      subscribeLifecycle: (listener) =>        transport.subscribe((client) => client[WS_METHODS.subscribeServerLifecycle]({}), listener),    },    orchestration: {      getSnapshot: () =>        transport.request((client) => client[ORCHESTRATION_WS_METHODS.getSnapshot]({})),      dispatchCommand: (input) =>        transport.request((client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand](input)),      getTurnDiff: (input) =>        transport.request((client) => client[ORCHESTRATION_WS_METHODS.getTurnDiff](input)),      getFullThreadDiff: (input) =>        transport.request((client) => client[ORCHESTRATION_WS_METHODS.getFullThreadDiff](input)),      replayEvents: (input) =>        transport          .request((client) => client[ORCHESTRATION_WS_METHODS.replayEvents](input))          .then((events) => [...events]),      onDomainEvent: (listener) =>        transport.subscribe(          (client) => client[WS_METHODS.subscribeOrchestrationDomainEvents]({}),          listener,        ),    },  };}