diff options
Diffstat (limited to 'packages/exec-backend/src/extension.ts')
| -rw-r--r-- | packages/exec-backend/src/extension.ts | 61 |
1 files changed, 43 insertions, 18 deletions
diff --git a/packages/exec-backend/src/extension.ts b/packages/exec-backend/src/extension.ts index c07b7a8..9d6840a 100644 --- a/packages/exec-backend/src/extension.ts +++ b/packages/exec-backend/src/extension.ts @@ -1,8 +1,11 @@ -import type { Extension, Manifest } from "@dispatch/kernel"; +import type { Extension, HostAPI, Manifest } from "@dispatch/kernel"; import type { ExecBackend } from "./backend.js"; import { localExecBackend } from "./local.js"; -import type { ExecBackendResolver } from "./service.js"; -import { execBackendHandle } from "./service.js"; +import { + type ExecBackendResolver, + execBackendHandle, + remoteExecBackendFactoryHandle, +} from "./service.js"; export const manifest: Manifest = { id: "exec-backend", @@ -15,33 +18,55 @@ export const manifest: Manifest = { }; /** - * The resolver provided by this extension. + * Build the `ExecBackendResolver` for a given host. * - * - `computerId` undefined → `LocalExecBackend` (today's local behavior). - * - `computerId` set → throws. Remote execution is wired by `host-bin` + the - * `ssh` package in a later wave (`SshExecBackend` implements the same - * `ExecBackend` interface). For now only the local path exists — failing - * loudly here is safer than silently running locally when remote was requested. + * - `computerId` undefined → `localExecBackend` (byte-identical local path; + * no host lookup, no remote machinery — unchanged from the original behavior). + * - `computerId` set → remote: lazily look up the factory the `ssh` extension + * provides via `remoteExecBackendFactoryHandle` and call it with the alias. + * The lookup is deferred to resolve time (tool-EXECUTE time, after every + * extension has activated), so a missing provider (ssh not loaded) degrades + * gracefully into a clear error instead of crashing activation. This mirrors + * the lazy `host.getService(lspServiceHandle)` try/catch pattern `tool-edit-file` + * uses for its diagnostics hook. + * + * The resolver stays SYNCHRONOUS and side-effect-free with respect to + * connections: looking up the factory and calling it returns a backend whose + * methods are async, so any remote connection acquisition happens lazily + * inside the first backend method call, not at resolve time. */ -function resolveBackend(computerId?: string): ExecBackend { - if (computerId === undefined) return localExecBackend; - throw new Error( - `Remote execution (computerId="${computerId}") is not yet configured. ` + - "The SSH backend will be wired by the ssh package.", - ); +function createResolver(host: HostAPI): ExecBackendResolver { + return (computerId?: string): ExecBackend => { + if (computerId === undefined) return localExecBackend; + // computerId set → remote. Look up the factory the `ssh` extension provides. + // `host.getService` throws when nothing provided the handle (ssh not loaded); + // convert that into a clear "not configured" error rather than a crash. + let factory: (computerId: string) => ExecBackend; + try { + factory = host.getService(remoteExecBackendFactoryHandle); + } catch { + throw new Error( + `SSH remote execution is not configured: the ssh extension is not loaded ` + + `(requested computerId="${computerId}"). Load the ssh package to enable remote execution.`, + ); + } + return factory(computerId); + }; } /** * Factory: create the `exec-backend` core extension. * - * `activate` provides the local-only `ExecBackendResolver` via the typed - * service handle. Remote resolution is added in a later wave. + * `activate` captures the host and provides the `ExecBackendResolver` via the + * typed service handle. The resolver lazily delegates the remote branch to a + * factory the `ssh` extension will provide (see `remoteExecBackendFactoryHandle`); + * until `ssh` is loaded, a remote request fails with a clear error. */ export function createExecBackendExtension(): Extension { return { manifest, activate(host) { - const resolver: ExecBackendResolver = resolveBackend; + const resolver: ExecBackendResolver = createResolver(host); host.provideService(execBackendHandle, resolver); }, }; |
