File size: 4,731 Bytes
fcd8223
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/** Loads capability providers through the canonical scoped plugin loader. */
import { createSubsystemLogger } from "../logging/subsystem.js";
import { withBundledPluginEnablementCompat } from "./bundled-compat.js";
import { getGatewayPluginMetadataSnapshot } from "./current-plugin-metadata-state.js";
import { discoverOpenClawPlugins, type PluginDiscoveryResult } from "./discovery.js";
import {
  acquirePluginRegistryWithInternalOverrides,
  loadOpenClawPluginsWithInternalOverrides,
} from "./loader-runtime-load.js";
import type { PluginLoadOptions } from "./loader.js";
import { loadPluginManifestRegistryCore } from "./manifest-registry.js";
import type { PluginRuntime } from "./runtime/types.js";

const log = createSubsystemLogger("plugins");

function createCapabilityRegistrationRuntime(
  config: NonNullable<PluginLoadOptions["config"]>,
): Pick<PluginRuntime, "config"> {
  return {
    config: {
      current: () => config,
      mutateConfigFile: async () => {
        throw new Error("Capability discovery cannot mutate plugin configuration.");
      },
      replaceConfigFile: async () => {
        throw new Error("Capability discovery cannot replace plugin configuration.");
      },
    },
  };
}

type BundledCapabilityRuntimeParams = Pick<
  PluginLoadOptions,
  | "env"
  | "config"
  | "workspaceDir"
  | "installRecords"
  | "manifestRegistry"
  | "activationSourceConfig"
  | "autoEnabledReasons"
  | "preferBuiltPluginArtifacts"
  | "pluginSdkResolution"
> & {
  pluginIds: readonly string[];
  discovery?: PluginDiscoveryResult;
};

export function loadBundledCapabilityRuntimeRegistry(params: BundledCapabilityRuntimeParams) {
  const { options, overrides } = prepareBundledCapabilityRuntime(params);
  return loadOpenClawPluginsWithInternalOverrides(options, overrides);
}

export function acquireBundledCapabilityRuntimeRegistry(params: BundledCapabilityRuntimeParams) {
  const { options, overrides } = prepareBundledCapabilityRuntime(params);
  return acquirePluginRegistryWithInternalOverrides(options, overrides);
}

function prepareBundledCapabilityRuntime(params: BundledCapabilityRuntimeParams): {
  options: PluginLoadOptions & { cache: false; activate: false };
  overrides: Parameters<typeof loadOpenClawPluginsWithInternalOverrides>[1];
} {
  const { pluginIds: requestedPluginIds, ...loadOptions } = params;
  const env = params.env ?? process.env;
  // Only the speech owner may opt into legacy global-disable compatibility before capture.
  const config =
    params.config?.plugins?.enabled === false
      ? params.config
      : (withBundledPluginEnablementCompat({
          config: params.config,
          pluginIds: params.pluginIds,
          env,
        }) ?? {});
  const snapshot =
    !params.discovery && !params.installRecords ? getGatewayPluginMetadataSnapshot() : undefined;
  const preparedRegistry =
    params.manifestRegistry ??
    (snapshot ? (snapshot.bundledManifestRegistry ?? { plugins: [], diagnostics: [] }) : undefined);
  const discovery = preparedRegistry
    ? undefined
    : (params.discovery ??
      discoverOpenClawPlugins({
        env,
        workspaceDir: params.workspaceDir,
        installRecords: params.installRecords,
      }));
  const pluginIds = new Set(params.pluginIds);
  const manifestRegistry =
    preparedRegistry ??
    loadPluginManifestRegistryCore({
      config,
      env,
      workspaceDir: params.workspaceDir,
      installRecords: params.installRecords,
      candidates: discovery?.candidates,
      diagnostics: discovery?.diagnostics,
    });
  const scopedManifestRegistry = {
    plugins: manifestRegistry.plugins.filter(
      (plugin) => plugin.origin === "bundled" && pluginIds.has(plugin.id),
    ),
    diagnostics: manifestRegistry.diagnostics,
  };
  return {
    options: {
      ...loadOptions,
      config,
      env,
      onlyPluginIds: [...requestedPluginIds],
      cache: false,
      activate: false,
      // Channel setup entries cannot register providers; keep their runtime entry in discovery mode.
      channelPluginLoadIntent: "full",
      manifestRegistry: scopedManifestRegistry,
      logger: {
        info: (message) => log.info(message),
        warn: (message) => log.warn(message),
        error: (message) => log.error(message),
        debug: (message) => log.debug(message),
      },
    },
    overrides: {
      // Discovery needs the current config, but not the full host runtime graph. The registry
      // still supplies its scoped lazy methods around this narrow base runtime.
      runtime: createCapabilityRegistrationRuntime(config),
      moduleLoader: {
        installNativeSdkResolver: false,
        loaderFilename: import.meta.url,
      },
    },
  };
}