|
1 | | -import type { CliFlags } from '@agent-device/contracts/command'; |
2 | | -import type { |
3 | | - ConnectionProviderCapabilities, |
4 | | - ProviderConnectionVerification, |
5 | | - RemoteConfigProfile, |
6 | | -} from '@agent-device/contracts/remote'; |
7 | | -import type { EnvMap } from '@agent-device/kernel/source-value'; |
8 | | -import { installedPlugins } from './store.ts'; |
| 1 | +import type { ConnectionProviderCapabilities } from '@agent-device/contracts/remote'; |
| 2 | +import { resolveUserConfigPath } from '@agent-device/host-kit/file'; |
| 3 | +import type { PluginConnection, PluginConnectionContext } from './contract.ts'; |
| 4 | +import { legacyStoreManifests, storePluginCandidates } from './store.ts'; |
9 | 5 |
|
10 | | -export type PluginConnection = Readonly<{ |
11 | | - resolve(context: { flags: CliFlags; stateDir: string; cwd: string; env: EnvMap }): |
12 | | - | Promise<{ |
13 | | - profile: RemoteConfigProfile; |
14 | | - extraFlags?: Partial<CliFlags>; |
15 | | - }> |
16 | | - | { profile: RemoteConfigProfile; extraFlags?: Partial<CliFlags> }; |
17 | | - verify(context: { flags: CliFlags; env: EnvMap }): Promise<ProviderConnectionVerification>; |
| 6 | +/** A plugin's connection as the client calls it: the registry adds the `host` to each context. */ |
| 7 | +export type PluginConnectionCallbacks = Readonly<{ |
| 8 | + resolve(context: Omit<PluginConnectionContext, 'host'>): ReturnType<PluginConnection['resolve']>; |
| 9 | + verify( |
| 10 | + context: Omit<Pick<PluginConnectionContext, 'flags' | 'env' | 'host'>, 'host'>, |
| 11 | + ): ReturnType<PluginConnection['verify']>; |
18 | 12 | }>; |
19 | 13 |
|
| 14 | +export type RegisteredConnection = PluginConnectionCallbacks & |
| 15 | + Readonly<{ capabilities: PluginConnection['capabilities'] }>; |
| 16 | + |
| 17 | +// Capabilities live in the plugin, so a v2 store plugin answers only once its package has loaded. |
| 18 | +// A client loads the store once per process through primePluginConnections; every synchronous |
| 19 | +// reader below then answers from that registry, and a v1 plugin answers from its manifest. |
| 20 | +const primed = new Map<string, ReadonlyMap<string, RegisteredConnection>>(); |
| 21 | + |
| 22 | +/** Loads the store's plugins into the connection registry this process reads capabilities from. */ |
| 23 | +export async function primePluginConnections( |
| 24 | + env: NodeJS.ProcessEnv = process.env, |
| 25 | + builtinProviders: readonly string[] = [], |
| 26 | +): Promise<void> { |
| 27 | + if (primed.has(resolveUserConfigPath(env))) return; |
| 28 | + const { claimProviderIds, resolveProviderPlugins } = await import('./registry.ts'); |
| 29 | + const resolved = await resolveProviderPlugins([ |
| 30 | + ...claimProviderIds(builtinProviders), |
| 31 | + ...storePluginCandidates(env), |
| 32 | + ]); |
| 33 | + primed.set(resolveUserConfigPath(env), resolved.connections); |
| 34 | +} |
| 35 | + |
20 | 36 | export function pluginConnectionCapabilities( |
21 | 37 | provider: string | undefined, |
22 | 38 | env: NodeJS.ProcessEnv = process.env, |
23 | 39 | ): ConnectionProviderCapabilities | undefined { |
24 | | - return provider === undefined |
25 | | - ? undefined |
26 | | - : installedPlugins(env).find((plugin) => plugin.agentDevicePlugin.provider === provider) |
27 | | - ?.agentDevicePlugin.connection; |
| 40 | + if (provider === undefined) return undefined; |
| 41 | + return ( |
| 42 | + primed.get(resolveUserConfigPath(env))?.get(provider)?.capabilities ?? |
| 43 | + legacyStoreManifests(env).find((plugin) => plugin.agentDevicePlugin.provider === provider) |
| 44 | + ?.agentDevicePlugin.connection |
| 45 | + ); |
28 | 46 | } |
29 | 47 |
|
30 | 48 | export function pluginConnectionNames(env: NodeJS.ProcessEnv = process.env): string[] { |
31 | | - return installedPlugins(env) |
32 | | - .filter((plugin) => plugin.agentDevicePlugin.connection) |
33 | | - .map((plugin) => plugin.agentDevicePlugin.provider); |
| 49 | + const names = new Set<string>(primed.get(resolveUserConfigPath(env))?.keys()); |
| 50 | + for (const plugin of legacyStoreManifests(env)) { |
| 51 | + if (plugin.agentDevicePlugin.connection) names.add(plugin.agentDevicePlugin.provider); |
| 52 | + } |
| 53 | + return [...names]; |
34 | 54 | } |
0 commit comments