summaryrefslogtreecommitdiffhomepage
path: root/packages/exec-backend/src/extension.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/exec-backend/src/extension.ts')
-rw-r--r--packages/exec-backend/src/extension.ts61
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);
},
};