@nextclaw/openclaw-compat 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -59,10 +59,58 @@ type OpenClawProviderPlugin = {
59
59
  models?: Record<string, unknown>;
60
60
  auth?: Array<Record<string, unknown>>;
61
61
  };
62
+ type OpenClawChannelConfigSchema = {
63
+ schema: Record<string, unknown>;
64
+ uiHints?: Record<string, PluginConfigUiHint>;
65
+ };
66
+ type OpenClawChannelSetup = {
67
+ validateInput?: (params: {
68
+ cfg: Record<string, unknown>;
69
+ input: unknown;
70
+ accountId?: string | null;
71
+ }) => string | null;
72
+ applyAccountConfig?: (params: {
73
+ cfg: Record<string, unknown>;
74
+ input: unknown;
75
+ accountId?: string | null;
76
+ }) => Record<string, unknown>;
77
+ };
78
+ type OpenClawChannelGatewayStartContext = {
79
+ accountId?: string | null;
80
+ log?: {
81
+ debug?: (message: string) => void;
82
+ info?: (message: string) => void;
83
+ warn?: (message: string) => void;
84
+ error?: (message: string) => void;
85
+ };
86
+ };
87
+ type OpenClawChannelGateway = {
88
+ startAccount?: (ctx: OpenClawChannelGatewayStartContext) => Promise<void | {
89
+ stop?: () => void | Promise<void>;
90
+ }> | void | {
91
+ stop?: () => void | Promise<void>;
92
+ };
93
+ };
94
+ type OpenClawChannelConfigAdapter = {
95
+ listAccountIds?: (cfg?: Record<string, unknown>) => string[];
96
+ defaultAccountId?: (cfg?: Record<string, unknown>) => string;
97
+ };
98
+ type OpenClawChannelAgentPrompt = {
99
+ messageToolHints?: (params: {
100
+ cfg: Config;
101
+ accountId?: string | null;
102
+ }) => string[];
103
+ };
62
104
  type OpenClawChannelPlugin = {
63
105
  id: string;
64
106
  meta?: Record<string, unknown>;
65
107
  capabilities?: Record<string, unknown>;
108
+ configSchema?: OpenClawChannelConfigSchema;
109
+ config?: OpenClawChannelConfigAdapter;
110
+ setup?: OpenClawChannelSetup;
111
+ gateway?: OpenClawChannelGateway;
112
+ agentTools?: OpenClawPluginTool[] | (() => OpenClawPluginTool | OpenClawPluginTool[] | null | undefined);
113
+ agentPrompt?: OpenClawChannelAgentPrompt;
66
114
  outbound?: {
67
115
  sendText?: (ctx: {
68
116
  cfg: Config;
@@ -141,8 +189,38 @@ type PluginProviderRegistration = {
141
189
  provider: OpenClawProviderPlugin;
142
190
  source: string;
143
191
  };
192
+ type PluginReplyDispatchParams = {
193
+ ctx: {
194
+ Body?: string;
195
+ BodyForAgent?: string;
196
+ BodyForCommands?: string;
197
+ ChatType?: string;
198
+ SenderId?: string;
199
+ SenderName?: string;
200
+ SessionKey?: string;
201
+ AccountId?: string;
202
+ OriginatingChannel?: string;
203
+ OriginatingTo?: string;
204
+ Provider?: string;
205
+ Surface?: string;
206
+ [key: string]: unknown;
207
+ };
208
+ cfg?: unknown;
209
+ dispatcherOptions: {
210
+ deliver: (replyPayload: {
211
+ text?: string;
212
+ }, info: {
213
+ kind: string;
214
+ }) => Promise<void> | void;
215
+ onError?: (error: unknown) => void;
216
+ };
217
+ };
144
218
  type PluginRuntime = {
145
219
  version: string;
220
+ config: {
221
+ loadConfig: () => Record<string, unknown>;
222
+ writeConfigFile: (next: Record<string, unknown>) => Promise<void>;
223
+ };
146
224
  tools: {
147
225
  createMemorySearchTool: (params: {
148
226
  config?: Config;
@@ -153,6 +231,11 @@ type PluginRuntime = {
153
231
  agentSessionKey?: string;
154
232
  }) => OpenClawPluginTool | null;
155
233
  };
234
+ channel: {
235
+ reply: {
236
+ dispatchReplyWithBufferedBlockDispatcher: (params: PluginReplyDispatchParams) => Promise<void>;
237
+ };
238
+ };
156
239
  };
157
240
  type OpenClawPluginApi = {
158
241
  id: string;
@@ -254,6 +337,34 @@ declare function enablePluginInConfig(config: Config, pluginId: string): Config;
254
337
  declare function disablePluginInConfig(config: Config, pluginId: string): Config;
255
338
  declare function addPluginLoadPath(config: Config, loadPath: string): Config;
256
339
 
340
+ type PluginChannelBinding = {
341
+ pluginId: string;
342
+ channelId: string;
343
+ channel: OpenClawChannelPlugin;
344
+ };
345
+ type PluginChannelGatewayHandle = {
346
+ pluginId: string;
347
+ channelId: string;
348
+ accountId: string;
349
+ stop?: () => void | Promise<void>;
350
+ };
351
+ declare function getPluginChannelBindings(registry: PluginRegistry): PluginChannelBinding[];
352
+ declare function resolvePluginChannelMessageToolHints(params: {
353
+ registry: PluginRegistry;
354
+ channel?: string | null;
355
+ cfg?: Config;
356
+ accountId?: string | null;
357
+ }): string[];
358
+ declare function getPluginUiMetadataFromRegistry(registry: PluginRegistry): PluginUiMetadata[];
359
+ declare function startPluginChannelGateways(params: {
360
+ registry: PluginRegistry;
361
+ logger?: PluginLogger;
362
+ }): Promise<{
363
+ handles: PluginChannelGatewayHandle[];
364
+ diagnostics: PluginDiagnostic[];
365
+ }>;
366
+ declare function stopPluginChannelGateways(handles: PluginChannelGatewayHandle[]): Promise<void>;
367
+
257
368
  type PluginCandidate = {
258
369
  idHint: string;
259
370
  source: string;
@@ -396,6 +507,12 @@ type PackageManifest = {
396
507
  };
397
508
  declare function getPackageManifestMetadata(manifest: PackageManifest | undefined): OpenClawPackageManifest | undefined;
398
509
 
510
+ type PluginRuntimeBridge = {
511
+ loadConfig?: () => Record<string, unknown>;
512
+ writeConfigFile?: (next: Record<string, unknown>) => Promise<void>;
513
+ dispatchReplyWithBufferedBlockDispatcher?: (params: PluginReplyDispatchParams) => Promise<void>;
514
+ };
515
+ declare function setPluginRuntimeBridge(next: PluginRuntimeBridge | null): void;
399
516
  declare function createPluginRuntime(params: {
400
517
  workspace: string;
401
518
  config?: Config;
@@ -459,4 +576,4 @@ declare function uninstallPlugin(params: {
459
576
  extensionsDir?: string;
460
577
  }): Promise<UninstallPluginResult>;
461
578
 
462
- export { DEFAULT_ACCOUNT_ID, type InstallPluginResult, type NormalizedPluginsConfig, type OpenClawChannelPlugin, type OpenClawPluginApi, type OpenClawPluginChannelRegistration, type OpenClawPluginConfigSchema, type OpenClawPluginDefinition, type OpenClawPluginModule, type OpenClawPluginTool, type OpenClawPluginToolContext, type OpenClawPluginToolFactory, type OpenClawPluginToolOptions, type OpenClawProviderPlugin, PLUGIN_MANIFEST_FILENAME, PLUGIN_MANIFEST_FILENAMES, type PackageManifest, type PluginCandidate, type PluginChannelRegistration, type PluginConfigUiHint, type PluginDiagnostic, type PluginDiscoveryResult, type PluginInstallLogger, type PluginInstallSource, type PluginInstallUpdate, type PluginKind, type PluginLoadOptions, type PluginLogger, type PluginManifest, type PluginManifestLoadResult, type PluginManifestRecord, type PluginManifestRegistry, type PluginOrigin, type PluginProviderRegistration, type PluginRecord, type PluginRegistry, type PluginRuntime, type PluginStatusReport, type PluginToolRegistration, type PluginUiMetadata, type UninstallActions, type UninstallPluginResult, type _CompatOnly, __nextclawPluginSdkCompat, addPluginLoadPath, buildChannelConfigSchema, buildOauthProviderAuthResult, buildPluginStatusReport, createPluginRuntime, disablePluginInConfig, discoverOpenClawPlugins, emptyPluginConfigSchema, enablePluginInConfig, getPackageManifestMetadata, installPluginFromArchive, installPluginFromDir, installPluginFromFile, installPluginFromNpmSpec, installPluginFromPath, loadOpenClawPlugins, loadPluginManifest, loadPluginManifestRegistry, loadPluginUiMetadata, normalizeAccountId, normalizePluginHttpPath, normalizePluginsConfig, recordPluginInstall, removePluginFromConfig, resolveEnableState, resolvePluginInstallDir, resolvePluginManifestPath, resolveUninstallDirectoryTarget, sleep, toPluginUiMetadata, uninstallPlugin, validateJsonSchemaValue };
579
+ export { DEFAULT_ACCOUNT_ID, type InstallPluginResult, type NormalizedPluginsConfig, type OpenClawChannelAgentPrompt, type OpenClawChannelConfigAdapter, type OpenClawChannelConfigSchema, type OpenClawChannelGateway, type OpenClawChannelGatewayStartContext, type OpenClawChannelPlugin, type OpenClawChannelSetup, type OpenClawPluginApi, type OpenClawPluginChannelRegistration, type OpenClawPluginConfigSchema, type OpenClawPluginDefinition, type OpenClawPluginModule, type OpenClawPluginTool, type OpenClawPluginToolContext, type OpenClawPluginToolFactory, type OpenClawPluginToolOptions, type OpenClawProviderPlugin, PLUGIN_MANIFEST_FILENAME, PLUGIN_MANIFEST_FILENAMES, type PackageManifest, type PluginCandidate, type PluginChannelBinding, type PluginChannelGatewayHandle, type PluginChannelRegistration, type PluginConfigUiHint, type PluginDiagnostic, type PluginDiscoveryResult, type PluginInstallLogger, type PluginInstallSource, type PluginInstallUpdate, type PluginKind, type PluginLoadOptions, type PluginLogger, type PluginManifest, type PluginManifestLoadResult, type PluginManifestRecord, type PluginManifestRegistry, type PluginOrigin, type PluginProviderRegistration, type PluginRecord, type PluginRegistry, type PluginReplyDispatchParams, type PluginRuntime, type PluginRuntimeBridge, type PluginStatusReport, type PluginToolRegistration, type PluginUiMetadata, type UninstallActions, type UninstallPluginResult, type _CompatOnly, __nextclawPluginSdkCompat, addPluginLoadPath, buildChannelConfigSchema, buildOauthProviderAuthResult, buildPluginStatusReport, createPluginRuntime, disablePluginInConfig, discoverOpenClawPlugins, emptyPluginConfigSchema, enablePluginInConfig, getPackageManifestMetadata, getPluginChannelBindings, getPluginUiMetadataFromRegistry, installPluginFromArchive, installPluginFromDir, installPluginFromFile, installPluginFromNpmSpec, installPluginFromPath, loadOpenClawPlugins, loadPluginManifest, loadPluginManifestRegistry, loadPluginUiMetadata, normalizeAccountId, normalizePluginHttpPath, normalizePluginsConfig, recordPluginInstall, removePluginFromConfig, resolveEnableState, resolvePluginChannelMessageToolHints, resolvePluginInstallDir, resolvePluginManifestPath, resolveUninstallDirectoryTarget, setPluginRuntimeBridge, sleep, startPluginChannelGateways, stopPluginChannelGateways, toPluginUiMetadata, uninstallPlugin, validateJsonSchemaValue };
package/dist/index.js CHANGED
@@ -169,6 +169,125 @@ function addPluginLoadPath(config, loadPath) {
169
169
  };
170
170
  }
171
171
 
172
+ // src/plugins/channel-runtime.ts
173
+ function normalizeChannelId(channel) {
174
+ return (channel ?? "").trim().toLowerCase();
175
+ }
176
+ function toBinding(registration) {
177
+ const channelId = registration.channel.id?.trim();
178
+ if (!channelId) {
179
+ return null;
180
+ }
181
+ return {
182
+ pluginId: registration.pluginId,
183
+ channelId,
184
+ channel: registration.channel
185
+ };
186
+ }
187
+ function getPluginChannelBindings(registry) {
188
+ const bindings = [];
189
+ for (const entry of registry.channels) {
190
+ const binding = toBinding(entry);
191
+ if (!binding) {
192
+ continue;
193
+ }
194
+ bindings.push(binding);
195
+ }
196
+ return bindings;
197
+ }
198
+ function resolvePluginChannelMessageToolHints(params) {
199
+ const channelId = normalizeChannelId(params.channel);
200
+ if (!channelId) {
201
+ return [];
202
+ }
203
+ const binding = getPluginChannelBindings(params.registry).find(
204
+ (entry) => normalizeChannelId(entry.channelId) === channelId
205
+ );
206
+ if (!binding) {
207
+ return [];
208
+ }
209
+ const resolveHints = binding.channel.agentPrompt?.messageToolHints;
210
+ if (typeof resolveHints !== "function") {
211
+ return [];
212
+ }
213
+ try {
214
+ const hinted = resolveHints({
215
+ cfg: params.cfg ?? {},
216
+ accountId: params.accountId
217
+ });
218
+ if (!Array.isArray(hinted)) {
219
+ return [];
220
+ }
221
+ return hinted.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
222
+ } catch {
223
+ return [];
224
+ }
225
+ }
226
+ function getPluginUiMetadataFromRegistry(registry) {
227
+ return registry.plugins.map((plugin) => ({
228
+ id: plugin.id,
229
+ configSchema: plugin.configJsonSchema,
230
+ configUiHints: plugin.configUiHints
231
+ }));
232
+ }
233
+ async function startPluginChannelGateways(params) {
234
+ const logger = params.logger;
235
+ const diagnostics = [];
236
+ const handles = [];
237
+ for (const binding of getPluginChannelBindings(params.registry)) {
238
+ const gateway = binding.channel.gateway;
239
+ if (!gateway?.startAccount) {
240
+ continue;
241
+ }
242
+ const accountIdsRaw = binding.channel.config?.listAccountIds?.() ?? [binding.channel.config?.defaultAccountId?.() ?? "default"];
243
+ const accountIds = Array.from(
244
+ new Set(accountIdsRaw.map((id) => typeof id === "string" ? id.trim() : "").filter(Boolean))
245
+ );
246
+ const finalAccountIds = accountIds.length > 0 ? accountIds : ["default"];
247
+ for (const accountId of finalAccountIds) {
248
+ try {
249
+ const started = await gateway.startAccount({
250
+ accountId,
251
+ log: logger
252
+ });
253
+ handles.push({
254
+ pluginId: binding.pluginId,
255
+ channelId: binding.channelId,
256
+ accountId,
257
+ stop: started && typeof started === "object" && "stop" in started && typeof started.stop === "function" ? started.stop : void 0
258
+ });
259
+ } catch (error) {
260
+ const raw = String(error);
261
+ const lower = raw.toLowerCase();
262
+ const level = lower.includes("required") || lower.includes("not configured") || lower.includes("missing") ? "warn" : "error";
263
+ const message = `failed to start channel gateway for ${binding.channelId}/${accountId}: ${raw}`;
264
+ diagnostics.push({
265
+ level,
266
+ pluginId: binding.pluginId,
267
+ message
268
+ });
269
+ if (level === "error") {
270
+ logger?.error(message);
271
+ } else {
272
+ logger?.warn(message);
273
+ }
274
+ }
275
+ }
276
+ }
277
+ return { handles, diagnostics };
278
+ }
279
+ async function stopPluginChannelGateways(handles) {
280
+ for (const handle of handles) {
281
+ if (!handle.stop) {
282
+ continue;
283
+ }
284
+ try {
285
+ await handle.stop();
286
+ } catch {
287
+ }
288
+ }
289
+ }
290
+
172
291
  // src/plugins/discovery.ts
173
292
  import fs2 from "fs";
174
293
  import path2 from "path";
@@ -736,19 +855,23 @@ async function installPluginFromPackageDir(params) {
736
855
  }
737
856
  logger.info?.(`Installing to ${targetDir}...`);
738
857
  await fs3.cp(packageDir, targetDir, { recursive: true, force: true });
739
- for (const extensionPath of extensions) {
740
- const resolved = path3.resolve(targetDir, extensionPath);
741
- if (!resolved.startsWith(path3.resolve(targetDir) + path3.sep) && resolved !== path3.resolve(targetDir)) {
742
- return { ok: false, error: `extension entry escapes plugin directory: ${extensionPath}` };
743
- }
744
- if (!await exists(resolved)) {
745
- logger.warn?.(`extension entry not found: ${extensionPath}`);
746
- }
747
- }
748
858
  try {
859
+ for (const extensionPath of extensions) {
860
+ const resolved = path3.resolve(targetDir, extensionPath);
861
+ if (!resolved.startsWith(path3.resolve(targetDir) + path3.sep) && resolved !== path3.resolve(targetDir)) {
862
+ throw new Error(`extension entry escapes plugin directory: ${extensionPath}`);
863
+ }
864
+ if (!await exists(resolved)) {
865
+ throw new Error(`extension entry not found after install: ${extensionPath}`);
866
+ }
867
+ }
749
868
  await installDependenciesIfNeeded(targetDir, packageManifest, logger);
750
869
  } catch (err) {
751
- return { ok: false, error: `failed to install dependencies: ${String(err)}` };
870
+ await fs3.rm(targetDir, { recursive: true, force: true }).catch(() => void 0);
871
+ return {
872
+ ok: false,
873
+ error: err instanceof Error ? err.message : `failed to install dependencies: ${String(err)}`
874
+ };
752
875
  }
753
876
  return {
754
877
  ok: true,
@@ -785,7 +908,7 @@ async function installPluginFromArchive(params) {
785
908
  } catch (err) {
786
909
  return { ok: false, error: String(err) };
787
910
  }
788
- return installPluginFromPackageDir({
911
+ return await installPluginFromPackageDir({
789
912
  packageDir,
790
913
  extensionsDir: params.extensionsDir,
791
914
  logger,
@@ -892,7 +1015,7 @@ async function installPluginFromNpmSpec(params) {
892
1015
  return { ok: false, error: "npm pack produced no archive" };
893
1016
  }
894
1017
  const archivePath = path3.join(tempDir, archiveName);
895
- return installPluginFromArchive({
1018
+ return await installPluginFromArchive({
896
1019
  archivePath,
897
1020
  extensionsDir: params.extensionsDir,
898
1021
  logger,
@@ -1096,6 +1219,10 @@ function loadPluginUiMetadata(params) {
1096
1219
  // src/plugins/runtime.ts
1097
1220
  import { getPackageVersion } from "@nextclaw/core";
1098
1221
  import { MemoryGetTool, MemorySearchTool } from "@nextclaw/core";
1222
+ var bridge = {};
1223
+ function setPluginRuntimeBridge(next) {
1224
+ bridge = next ?? {};
1225
+ }
1099
1226
  function toPluginTool(tool) {
1100
1227
  return {
1101
1228
  name: tool.name,
@@ -1104,12 +1231,39 @@ function toPluginTool(tool) {
1104
1231
  execute: (params) => tool.execute(params)
1105
1232
  };
1106
1233
  }
1234
+ function loadConfigWithFallback(config) {
1235
+ if (bridge.loadConfig) {
1236
+ return bridge.loadConfig();
1237
+ }
1238
+ return config ?? {};
1239
+ }
1240
+ async function writeConfigWithFallback(next) {
1241
+ if (!bridge.writeConfigFile) {
1242
+ throw new Error("plugin runtime config.writeConfigFile is unavailable outside gateway runtime");
1243
+ }
1244
+ await bridge.writeConfigFile(next);
1245
+ }
1246
+ async function dispatchReplyWithFallback(params) {
1247
+ if (!bridge.dispatchReplyWithBufferedBlockDispatcher) {
1248
+ throw new Error("plugin runtime channel.reply dispatcher is unavailable outside gateway runtime");
1249
+ }
1250
+ await bridge.dispatchReplyWithBufferedBlockDispatcher(params);
1251
+ }
1107
1252
  function createPluginRuntime(params) {
1108
1253
  return {
1109
1254
  version: getPackageVersion(),
1255
+ config: {
1256
+ loadConfig: () => loadConfigWithFallback(params.config),
1257
+ writeConfigFile: async (next) => writeConfigWithFallback(next)
1258
+ },
1110
1259
  tools: {
1111
1260
  createMemorySearchTool: () => toPluginTool(new MemorySearchTool(params.workspace)),
1112
1261
  createMemoryGetTool: () => toPluginTool(new MemoryGetTool(params.workspace))
1262
+ },
1263
+ channel: {
1264
+ reply: {
1265
+ dispatchReplyWithBufferedBlockDispatcher: async (dispatchParams) => dispatchReplyWithFallback(dispatchParams)
1266
+ }
1113
1267
  }
1114
1268
  };
1115
1269
  }
@@ -1272,8 +1426,21 @@ function ensureUniqueNames(params) {
1272
1426
  }
1273
1427
  return accepted;
1274
1428
  }
1429
+ function isPlaceholderConfigSchema(schema) {
1430
+ if (!schema || typeof schema !== "object") {
1431
+ return false;
1432
+ }
1433
+ const type = schema.type;
1434
+ const isObjectType = type === "object" || Array.isArray(type) && type.includes("object");
1435
+ if (!isObjectType) {
1436
+ return false;
1437
+ }
1438
+ const properties = schema.properties;
1439
+ const noProperties = !properties || typeof properties === "object" && !Array.isArray(properties) && Object.keys(properties).length === 0;
1440
+ return noProperties && schema.additionalProperties === false;
1441
+ }
1275
1442
  function validatePluginConfig(params) {
1276
- if (!params.schema) {
1443
+ if (!params.schema || isPlaceholderConfigSchema(params.schema)) {
1277
1444
  return { ok: true, value: params.value };
1278
1445
  }
1279
1446
  const cacheKey = params.cacheKey ?? JSON.stringify(params.schema);
@@ -1567,12 +1734,95 @@ function loadOpenClawPlugins(options) {
1567
1734
  if (accepted.length === 0) {
1568
1735
  return;
1569
1736
  }
1737
+ const channelPlugin = normalizedChannel;
1570
1738
  registry.channels.push({
1571
1739
  pluginId,
1572
- channel: normalizedChannel,
1740
+ channel: channelPlugin,
1573
1741
  source: candidate.source
1574
1742
  });
1575
- record.channelIds.push(accepted[0]);
1743
+ const channelId = accepted[0];
1744
+ record.channelIds.push(channelId);
1745
+ const configSchema = channelPlugin.configSchema;
1746
+ if (configSchema && typeof configSchema === "object") {
1747
+ const schema = configSchema.schema;
1748
+ if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1749
+ record.configJsonSchema = schema;
1750
+ record.configSchema = true;
1751
+ }
1752
+ const uiHints = configSchema.uiHints;
1753
+ if (uiHints && typeof uiHints === "object" && !Array.isArray(uiHints)) {
1754
+ record.configUiHints = {
1755
+ ...record.configUiHints ?? {},
1756
+ ...uiHints
1757
+ };
1758
+ }
1759
+ }
1760
+ const pushChannelTools = (value, optional, sourceLabel, resolveValue) => {
1761
+ const previewTools = normalizeToolList(value);
1762
+ if (previewTools.length === 0) {
1763
+ return;
1764
+ }
1765
+ const declaredNames = previewTools.map((tool) => tool.name);
1766
+ const acceptedNames = ensureUniqueNames({
1767
+ names: declaredNames,
1768
+ pluginId,
1769
+ diagnostics: registry.diagnostics,
1770
+ source: candidate.source,
1771
+ owners: toolNameOwners,
1772
+ reserved: reservedToolNames,
1773
+ kind: "tool"
1774
+ });
1775
+ if (acceptedNames.length === 0) {
1776
+ return;
1777
+ }
1778
+ const factory = (ctx) => {
1779
+ const tools = normalizeToolList(resolveValue(ctx));
1780
+ if (tools.length === 0) {
1781
+ return [];
1782
+ }
1783
+ const byName = new Map(tools.map((tool) => [tool.name, tool]));
1784
+ return acceptedNames.map((name) => byName.get(name)).filter(Boolean);
1785
+ };
1786
+ registry.tools.push({
1787
+ pluginId,
1788
+ factory,
1789
+ names: acceptedNames,
1790
+ optional,
1791
+ source: candidate.source
1792
+ });
1793
+ record.toolNames.push(...acceptedNames);
1794
+ const previewByName = new Map(previewTools.map((tool) => [tool.name, tool]));
1795
+ for (const name of acceptedNames) {
1796
+ const resolvedTool = previewByName.get(name);
1797
+ if (!resolvedTool || resolvedToolNames.has(resolvedTool.name)) {
1798
+ continue;
1799
+ }
1800
+ resolvedToolNames.add(resolvedTool.name);
1801
+ registry.resolvedTools.push(resolvedTool);
1802
+ }
1803
+ registry.diagnostics.push({
1804
+ level: "warn",
1805
+ pluginId,
1806
+ source: candidate.source,
1807
+ message: `${sourceLabel} registered channel-owned tools: ${acceptedNames.join(", ")}`
1808
+ });
1809
+ };
1810
+ const agentTools = channelPlugin.agentTools;
1811
+ if (typeof agentTools === "function") {
1812
+ pushChannelTools(
1813
+ normalizeToolList(agentTools()),
1814
+ false,
1815
+ `channel "${channelId}"`,
1816
+ () => agentTools()
1817
+ );
1818
+ } else if (agentTools) {
1819
+ pushChannelTools(
1820
+ normalizeToolList(agentTools),
1821
+ false,
1822
+ `channel "${channelId}"`,
1823
+ () => agentTools
1824
+ );
1825
+ }
1576
1826
  },
1577
1827
  registerProvider: (provider) => {
1578
1828
  const accepted = ensureUniqueNames({
@@ -1810,6 +2060,8 @@ export {
1810
2060
  emptyPluginConfigSchema,
1811
2061
  enablePluginInConfig,
1812
2062
  getPackageManifestMetadata,
2063
+ getPluginChannelBindings,
2064
+ getPluginUiMetadataFromRegistry,
1813
2065
  installPluginFromArchive,
1814
2066
  installPluginFromDir,
1815
2067
  installPluginFromFile,
@@ -1825,10 +2077,14 @@ export {
1825
2077
  recordPluginInstall,
1826
2078
  removePluginFromConfig,
1827
2079
  resolveEnableState,
2080
+ resolvePluginChannelMessageToolHints,
1828
2081
  resolvePluginInstallDir,
1829
2082
  resolvePluginManifestPath,
1830
2083
  resolveUninstallDirectoryTarget,
2084
+ setPluginRuntimeBridge,
1831
2085
  sleep,
2086
+ startPluginChannelGateways,
2087
+ stopPluginChannelGateways,
1832
2088
  toPluginUiMetadata,
1833
2089
  uninstallPlugin,
1834
2090
  validateJsonSchemaValue
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/openclaw-compat",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "description": "OpenClaw plugin compatibility layer for NextClaw.",
6
6
  "type": "module",
@@ -18,7 +18,7 @@
18
18
  "jiti": "^1.21.7",
19
19
  "jszip": "^3.10.1",
20
20
  "tar": "^7.4.3",
21
- "@nextclaw/core": "^0.4.8"
21
+ "@nextclaw/core": "^0.4.9"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^20.17.6",