@nextclaw/openclaw-compat 0.3.0 → 0.3.1

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
@@ -1,5 +1,7 @@
1
1
  import * as _nextclaw_core from '@nextclaw/core';
2
2
  import { Config, ExtensionChannel, AgentEngineFactory } from '@nextclaw/core';
3
+ import { NcpAgentRuntime } from '@nextclaw/ncp';
4
+ import { RuntimeFactoryParams } from '@nextclaw/ncp-toolkit';
3
5
 
4
6
  type PluginConfigUiHint = {
5
7
  label?: string;
@@ -54,6 +56,11 @@ type OpenClawPluginToolOptions = {
54
56
  type OpenClawPluginEngineOptions = {
55
57
  kind: string;
56
58
  };
59
+ type OpenClawPluginNcpAgentRuntimeRegistration = {
60
+ kind: string;
61
+ label?: string;
62
+ createRuntime: (params: RuntimeFactoryParams) => NcpAgentRuntime;
63
+ };
57
64
  type OpenClawProviderPlugin = {
58
65
  id: string;
59
66
  label?: string;
@@ -175,6 +182,7 @@ type PluginRecord = {
175
182
  channelIds: string[];
176
183
  providerIds: string[];
177
184
  engineKinds: string[];
185
+ ncpAgentRuntimeKinds: string[];
178
186
  configSchema: boolean;
179
187
  configUiHints?: Record<string, PluginConfigUiHint>;
180
188
  configJsonSchema?: Record<string, unknown>;
@@ -202,6 +210,13 @@ type PluginEngineRegistration = {
202
210
  factory: AgentEngineFactory;
203
211
  source: string;
204
212
  };
213
+ type PluginNcpAgentRuntimeRegistration = {
214
+ pluginId: string;
215
+ kind: string;
216
+ label: string;
217
+ createRuntime: (params: RuntimeFactoryParams) => NcpAgentRuntime;
218
+ source: string;
219
+ };
205
220
  type PluginReplyDispatchParams = {
206
221
  ctx: {
207
222
  Body?: string;
@@ -264,6 +279,7 @@ type OpenClawPluginApi = {
264
279
  registerChannel: (registration: OpenClawPluginChannelRegistration) => void;
265
280
  registerProvider: (provider: OpenClawProviderPlugin) => void;
266
281
  registerEngine: (factory: AgentEngineFactory, opts: OpenClawPluginEngineOptions) => void;
282
+ registerNcpAgentRuntime: (registration: OpenClawPluginNcpAgentRuntimeRegistration) => void;
267
283
  registerHook: (_events: string | string[], _handler: unknown, _opts?: unknown) => void;
268
284
  registerGatewayMethod: (_method: string, _handler: unknown) => void;
269
285
  registerCli: (_registrar: unknown, _opts?: unknown) => void;
@@ -282,6 +298,7 @@ type PluginRegistry = {
282
298
  channels: PluginChannelRegistration[];
283
299
  providers: PluginProviderRegistration[];
284
300
  engines: PluginEngineRegistration[];
301
+ ncpAgentRuntimes: PluginNcpAgentRuntimeRegistration[];
285
302
  diagnostics: PluginDiagnostic[];
286
303
  resolvedTools: OpenClawPluginTool[];
287
304
  };
@@ -473,11 +490,14 @@ type PluginLoadOptions = {
473
490
  workspaceDir?: string;
474
491
  logger?: PluginLogger;
475
492
  mode?: "full" | "validate";
493
+ excludeRoots?: string[];
476
494
  reservedToolNames?: string[];
477
495
  reservedChannelIds?: string[];
478
496
  reservedProviderIds?: string[];
479
497
  reservedEngineKinds?: string[];
498
+ reservedNcpAgentRuntimeKinds?: string[];
480
499
  };
500
+ declare function buildPluginLoaderAliases(): Record<string, string>;
481
501
  declare function loadOpenClawPlugins(options: PluginLoadOptions): PluginRegistry;
482
502
 
483
503
  type PluginManifestRecord = {
@@ -554,11 +574,13 @@ type PluginRegisterRuntime = {
554
574
  channelIdOwners: Map<string, string>;
555
575
  providerIdOwners: Map<string, string>;
556
576
  engineKindOwners: Map<string, string>;
577
+ ncpAgentRuntimeKindOwners: Map<string, string>;
557
578
  resolvedToolNames: Set<string>;
558
579
  reservedToolNames: Set<string>;
559
580
  reservedChannelIds: Set<string>;
560
581
  reservedProviderIds: Set<string>;
561
582
  reservedEngineKinds: Set<string>;
583
+ reservedNcpAgentRuntimeKinds: Set<string>;
562
584
  };
563
585
  declare function createPluginRegisterRuntime(params: {
564
586
  config: Config;
@@ -569,6 +591,7 @@ declare function createPluginRegisterRuntime(params: {
569
591
  reservedChannelIds: Set<string>;
570
592
  reservedProviderIds: Set<string>;
571
593
  reservedEngineKinds: Set<string>;
594
+ reservedNcpAgentRuntimeKinds: Set<string>;
572
595
  }): PluginRegisterRuntime;
573
596
  declare function registerPluginWithApi(params: {
574
597
  runtime: PluginRegisterRuntime;
@@ -607,6 +630,7 @@ declare function buildPluginStatusReport(params: {
607
630
  reservedChannelIds?: string[];
608
631
  reservedProviderIds?: string[];
609
632
  reservedEngineKinds?: string[];
633
+ reservedNcpAgentRuntimeKinds?: string[];
610
634
  }): PluginStatusReport;
611
635
 
612
636
  type UninstallActions = {
@@ -633,6 +657,13 @@ declare function resolveUninstallDirectoryTarget(params: {
633
657
  installRecord?: PluginInstallRecord;
634
658
  extensionsDir?: string;
635
659
  }): string | null;
660
+ declare function resolveUninstallDirectoryTargets(params: {
661
+ config: Config;
662
+ pluginId: string;
663
+ hasInstall: boolean;
664
+ installRecord?: PluginInstallRecord;
665
+ extensionsDir?: string;
666
+ }): string[];
636
667
  declare function removePluginFromConfig(config: Config, pluginId: string): {
637
668
  config: Config;
638
669
  actions: Omit<UninstallActions, "directory">;
@@ -644,4 +675,4 @@ declare function uninstallPlugin(params: {
644
675
  extensionsDir?: string;
645
676
  }): Promise<UninstallPluginResult>;
646
677
 
647
- 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 OpenClawPluginEngineOptions, 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 PluginEngineRegistration, 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 PluginRegisterRuntime, 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, createNextclawBuiltinChannelPlugin, createPluginRegisterRuntime, createPluginRuntime, disablePluginInConfig, discoverOpenClawPlugins, emptyPluginConfigSchema, enablePluginInConfig, getPackageManifestMetadata, getPluginChannelBindings, getPluginUiMetadataFromRegistry, installPluginFromArchive, installPluginFromDir, installPluginFromFile, installPluginFromNpmSpec, installPluginFromPath, loadOpenClawPlugins, loadPluginManifest, loadPluginManifestRegistry, loadPluginUiMetadata, normalizeAccountId, normalizePluginHttpPath, normalizePluginsConfig, recordPluginInstall, registerPluginWithApi, removePluginFromConfig, resolveEnableState, resolvePluginChannelMessageToolHints, resolvePluginInstallDir, resolvePluginManifestPath, resolveUninstallDirectoryTarget, setPluginRuntimeBridge, sleep, startPluginChannelGateways, stopPluginChannelGateways, toPluginUiMetadata, uninstallPlugin, validateJsonSchemaValue };
678
+ 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 OpenClawPluginEngineOptions, type OpenClawPluginModule, type OpenClawPluginNcpAgentRuntimeRegistration, 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 PluginEngineRegistration, type PluginInstallLogger, type PluginInstallSource, type PluginInstallUpdate, type PluginKind, type PluginLoadOptions, type PluginLogger, type PluginManifest, type PluginManifestLoadResult, type PluginManifestRecord, type PluginManifestRegistry, type PluginNcpAgentRuntimeRegistration, type PluginOrigin, type PluginProviderRegistration, type PluginRecord, type PluginRegisterRuntime, type PluginRegistry, type PluginReplyDispatchParams, type PluginRuntime, type PluginRuntimeBridge, type PluginStatusReport, type PluginToolRegistration, type PluginUiMetadata, type UninstallActions, type UninstallPluginResult, type _CompatOnly, __nextclawPluginSdkCompat, addPluginLoadPath, buildChannelConfigSchema, buildOauthProviderAuthResult, buildPluginLoaderAliases, buildPluginStatusReport, createNextclawBuiltinChannelPlugin, createPluginRegisterRuntime, createPluginRuntime, disablePluginInConfig, discoverOpenClawPlugins, emptyPluginConfigSchema, enablePluginInConfig, getPackageManifestMetadata, getPluginChannelBindings, getPluginUiMetadataFromRegistry, installPluginFromArchive, installPluginFromDir, installPluginFromFile, installPluginFromNpmSpec, installPluginFromPath, loadOpenClawPlugins, loadPluginManifest, loadPluginManifestRegistry, loadPluginUiMetadata, normalizeAccountId, normalizePluginHttpPath, normalizePluginsConfig, recordPluginInstall, registerPluginWithApi, removePluginFromConfig, resolveEnableState, resolvePluginChannelMessageToolHints, resolvePluginInstallDir, resolvePluginManifestPath, resolveUninstallDirectoryTarget, resolveUninstallDirectoryTargets, setPluginRuntimeBridge, sleep, startPluginChannelGateways, stopPluginChannelGateways, toPluginUiMetadata, uninstallPlugin, validateJsonSchemaValue };
package/dist/index.js CHANGED
@@ -1068,12 +1068,34 @@ async function installPluginFromPath(params) {
1068
1068
 
1069
1069
  // src/plugins/loader.ts
1070
1070
  import fs5 from "fs";
1071
- import path5 from "path";
1071
+ import path6 from "path";
1072
1072
  import { fileURLToPath } from "url";
1073
1073
  import { createRequire } from "module";
1074
1074
  import createJitiImport from "jiti";
1075
1075
  import { getWorkspacePathFromConfig } from "@nextclaw/core";
1076
1076
 
1077
+ // src/plugins/candidate-filter.ts
1078
+ import path4 from "path";
1079
+ function isPathInsideRoot(candidatePath, rootPath) {
1080
+ const normalizedCandidate = path4.resolve(candidatePath);
1081
+ const normalizedRoot = path4.resolve(rootPath);
1082
+ return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}${path4.sep}`);
1083
+ }
1084
+ function filterPluginCandidatesByExcludedRoots(candidates, excludedRoots) {
1085
+ const normalizedRoots = excludedRoots.map((entry) => path4.resolve(entry));
1086
+ if (normalizedRoots.length === 0) {
1087
+ return [...candidates];
1088
+ }
1089
+ return candidates.filter((candidate) => {
1090
+ const candidatePaths = [candidate.source, candidate.rootDir, candidate.packageDir].filter(
1091
+ (entry) => typeof entry === "string" && entry.trim().length > 0
1092
+ );
1093
+ return !normalizedRoots.some(
1094
+ (rootPath) => candidatePaths.some((candidatePath) => isPathInsideRoot(candidatePath, rootPath))
1095
+ );
1096
+ });
1097
+ }
1098
+
1077
1099
  // src/plugins/manifest-registry.ts
1078
1100
  import fs4 from "fs";
1079
1101
  var PLUGIN_ORIGIN_RANK = {
@@ -1233,9 +1255,9 @@ function formatAjvErrors(errors) {
1233
1255
  return ["invalid config"];
1234
1256
  }
1235
1257
  return errors.map((error) => {
1236
- const path7 = error.instancePath?.replace(/^\//, "").replace(/\//g, ".") || "<root>";
1258
+ const path8 = error.instancePath?.replace(/^\//, "").replace(/\//g, ".") || "<root>";
1237
1259
  const message = error.message ?? "invalid";
1238
- return `${path7}: ${message}`;
1260
+ return `${path8}: ${message}`;
1239
1261
  });
1240
1262
  }
1241
1263
  function validateJsonSchemaValue(params) {
@@ -1252,10 +1274,148 @@ function validateJsonSchemaValue(params) {
1252
1274
  return { ok: false, errors: formatAjvErrors(cached.validate.errors) };
1253
1275
  }
1254
1276
 
1277
+ // src/plugins/plugin-loader-utils.ts
1278
+ function createPluginRecord(params) {
1279
+ return {
1280
+ id: params.id,
1281
+ name: params.name ?? params.id,
1282
+ description: params.description,
1283
+ version: params.version,
1284
+ kind: params.kind,
1285
+ source: params.source,
1286
+ origin: params.origin,
1287
+ workspaceDir: params.workspaceDir,
1288
+ enabled: params.enabled,
1289
+ status: params.enabled ? "loaded" : "disabled",
1290
+ toolNames: [],
1291
+ channelIds: [],
1292
+ providerIds: [],
1293
+ engineKinds: [],
1294
+ ncpAgentRuntimeKinds: [],
1295
+ configSchema: params.configSchema,
1296
+ configUiHints: params.configUiHints,
1297
+ configJsonSchema: params.configJsonSchema
1298
+ };
1299
+ }
1300
+ function isPlaceholderConfigSchema(schema) {
1301
+ if (!schema || typeof schema !== "object") {
1302
+ return false;
1303
+ }
1304
+ const type = schema.type;
1305
+ const isObjectType = type === "object" || Array.isArray(type) && type.includes("object");
1306
+ if (!isObjectType) {
1307
+ return false;
1308
+ }
1309
+ const properties = schema.properties;
1310
+ const noProperties = !properties || typeof properties === "object" && !Array.isArray(properties) && Object.keys(properties).length === 0;
1311
+ return noProperties && schema.additionalProperties === false;
1312
+ }
1313
+ function validatePluginConfig(params) {
1314
+ if (!params.schema || isPlaceholderConfigSchema(params.schema)) {
1315
+ return { ok: true, value: params.value };
1316
+ }
1317
+ const cacheKey = params.cacheKey ?? JSON.stringify(params.schema);
1318
+ const result = validateJsonSchemaValue({
1319
+ schema: params.schema,
1320
+ cacheKey,
1321
+ value: params.value ?? {}
1322
+ });
1323
+ if (result.ok) {
1324
+ return { ok: true, value: params.value };
1325
+ }
1326
+ return { ok: false, errors: result.errors };
1327
+ }
1328
+
1255
1329
  // src/plugins/registry.ts
1256
- import path4 from "path";
1330
+ import path5 from "path";
1257
1331
  import { expandHome as expandHome2 } from "@nextclaw/core";
1258
1332
 
1333
+ // src/plugins/plugin-capability-registration.ts
1334
+ function ensureUniqueNames(params) {
1335
+ const accepted = [];
1336
+ for (const rawName of params.names) {
1337
+ const name = rawName.trim();
1338
+ if (!name) {
1339
+ continue;
1340
+ }
1341
+ if (params.reserved.has(name)) {
1342
+ params.diagnostics.push({
1343
+ level: "error",
1344
+ pluginId: params.pluginId,
1345
+ source: params.source,
1346
+ message: `${params.kind} already registered by core: ${name}`
1347
+ });
1348
+ continue;
1349
+ }
1350
+ const owner = params.owners.get(name);
1351
+ if (owner && owner !== params.pluginId) {
1352
+ params.diagnostics.push({
1353
+ level: "error",
1354
+ pluginId: params.pluginId,
1355
+ source: params.source,
1356
+ message: `${params.kind} already registered: ${name} (${owner})`
1357
+ });
1358
+ continue;
1359
+ }
1360
+ params.owners.set(name, params.pluginId);
1361
+ accepted.push(name);
1362
+ }
1363
+ return accepted;
1364
+ }
1365
+ function registerPluginEngine(params) {
1366
+ const accepted = ensureUniqueNames({
1367
+ names: [params.kind],
1368
+ pluginId: params.pluginId,
1369
+ diagnostics: params.runtime.registry.diagnostics,
1370
+ source: params.source,
1371
+ owners: params.runtime.engineKindOwners,
1372
+ reserved: params.runtime.reservedEngineKinds,
1373
+ kind: "engine"
1374
+ });
1375
+ if (accepted.length === 0) {
1376
+ return;
1377
+ }
1378
+ params.runtime.registry.engines.push({
1379
+ pluginId: params.pluginId,
1380
+ kind: accepted[0],
1381
+ factory: params.factory,
1382
+ source: params.source
1383
+ });
1384
+ params.record.engineKinds.push(accepted[0]);
1385
+ }
1386
+ function registerPluginNcpAgentRuntime(params) {
1387
+ const rawKind = params.registration.kind?.trim().toLowerCase();
1388
+ if (!rawKind) {
1389
+ params.runtime.registry.diagnostics.push({
1390
+ level: "error",
1391
+ pluginId: params.pluginId,
1392
+ source: params.source,
1393
+ message: "registerNcpAgentRuntime requires registration.kind"
1394
+ });
1395
+ return;
1396
+ }
1397
+ const accepted = ensureUniqueNames({
1398
+ names: [rawKind],
1399
+ pluginId: params.pluginId,
1400
+ diagnostics: params.runtime.registry.diagnostics,
1401
+ source: params.source,
1402
+ owners: params.runtime.ncpAgentRuntimeKindOwners,
1403
+ reserved: params.runtime.reservedNcpAgentRuntimeKinds,
1404
+ kind: "ncp-runtime"
1405
+ });
1406
+ if (accepted.length === 0) {
1407
+ return;
1408
+ }
1409
+ params.runtime.registry.ncpAgentRuntimes.push({
1410
+ pluginId: params.pluginId,
1411
+ kind: accepted[0],
1412
+ label: params.registration.label?.trim() || accepted[0],
1413
+ createRuntime: params.registration.createRuntime,
1414
+ source: params.source
1415
+ });
1416
+ params.record.ncpAgentRuntimeKinds.push(accepted[0]);
1417
+ }
1418
+
1259
1419
  // src/plugins/runtime.ts
1260
1420
  import { getPackageVersion } from "@nextclaw/core";
1261
1421
  import { MemoryGetTool, MemorySearchTool } from "@nextclaw/core";
@@ -1318,37 +1478,6 @@ function buildPluginLogger(base, pluginId) {
1318
1478
  debug: base.debug ? (message) => base.debug?.(withPrefix(message)) : void 0
1319
1479
  };
1320
1480
  }
1321
- function ensureUniqueNames(params) {
1322
- const accepted = [];
1323
- for (const rawName of params.names) {
1324
- const name = rawName.trim();
1325
- if (!name) {
1326
- continue;
1327
- }
1328
- if (params.reserved.has(name)) {
1329
- params.diagnostics.push({
1330
- level: "error",
1331
- pluginId: params.pluginId,
1332
- source: params.source,
1333
- message: `${params.kind} already registered by core: ${name}`
1334
- });
1335
- continue;
1336
- }
1337
- const owner = params.owners.get(name);
1338
- if (owner && owner !== params.pluginId) {
1339
- params.diagnostics.push({
1340
- level: "error",
1341
- pluginId: params.pluginId,
1342
- source: params.source,
1343
- message: `${params.kind} already registered: ${name} (${owner})`
1344
- });
1345
- continue;
1346
- }
1347
- params.owners.set(name, params.pluginId);
1348
- accepted.push(name);
1349
- }
1350
- return accepted;
1351
- }
1352
1481
  function normalizeToolList(value) {
1353
1482
  if (!value) {
1354
1483
  return [];
@@ -1372,11 +1501,13 @@ function createPluginRegisterRuntime(params) {
1372
1501
  channelIdOwners: /* @__PURE__ */ new Map(),
1373
1502
  providerIdOwners: /* @__PURE__ */ new Map(),
1374
1503
  engineKindOwners: /* @__PURE__ */ new Map(),
1504
+ ncpAgentRuntimeKindOwners: /* @__PURE__ */ new Map(),
1375
1505
  resolvedToolNames: /* @__PURE__ */ new Set(),
1376
1506
  reservedToolNames: params.reservedToolNames,
1377
1507
  reservedChannelIds: params.reservedChannelIds,
1378
1508
  reservedProviderIds: params.reservedProviderIds,
1379
- reservedEngineKinds: params.reservedEngineKinds
1509
+ reservedEngineKinds: params.reservedEngineKinds,
1510
+ reservedNcpAgentRuntimeKinds: params.reservedNcpAgentRuntimeKinds
1380
1511
  };
1381
1512
  }
1382
1513
  function registerPluginTool(params) {
@@ -1558,27 +1689,6 @@ function registerPluginProvider(params) {
1558
1689
  });
1559
1690
  params.record.providerIds.push(accepted[0]);
1560
1691
  }
1561
- function registerPluginEngine(params) {
1562
- const accepted = ensureUniqueNames({
1563
- names: [params.kind],
1564
- pluginId: params.pluginId,
1565
- diagnostics: params.runtime.registry.diagnostics,
1566
- source: params.source,
1567
- owners: params.runtime.engineKindOwners,
1568
- reserved: params.runtime.reservedEngineKinds,
1569
- kind: "engine"
1570
- });
1571
- if (accepted.length === 0) {
1572
- return;
1573
- }
1574
- params.runtime.registry.engines.push({
1575
- pluginId: params.pluginId,
1576
- kind: accepted[0],
1577
- factory: params.factory,
1578
- source: params.source
1579
- });
1580
- params.record.engineKinds.push(accepted[0]);
1581
- }
1582
1692
  function registerPluginWithApi(params) {
1583
1693
  const pluginRuntime = createPluginRuntime({ workspace: params.runtime.workspaceDir, config: params.runtime.config });
1584
1694
  const pluginLogger = buildPluginLogger(params.runtime.logger, params.pluginId);
@@ -1649,6 +1759,15 @@ function registerPluginWithApi(params) {
1649
1759
  factory
1650
1760
  });
1651
1761
  },
1762
+ registerNcpAgentRuntime: (registration) => {
1763
+ registerPluginNcpAgentRuntime({
1764
+ runtime: params.runtime,
1765
+ record: params.record,
1766
+ pluginId: params.pluginId,
1767
+ source: params.source,
1768
+ registration
1769
+ });
1770
+ },
1652
1771
  registerHook: () => pushUnsupported("registerHook"),
1653
1772
  registerGatewayMethod: () => pushUnsupported("registerGatewayMethod"),
1654
1773
  registerCli: () => pushUnsupported("registerCli"),
@@ -1661,10 +1780,10 @@ function registerPluginWithApi(params) {
1661
1780
  if (!trimmed) {
1662
1781
  return params.rootDir;
1663
1782
  }
1664
- if (path4.isAbsolute(trimmed)) {
1665
- return path4.resolve(expandHome2(trimmed));
1783
+ if (path5.isAbsolute(trimmed)) {
1784
+ return path5.resolve(expandHome2(trimmed));
1666
1785
  }
1667
- return path4.resolve(params.rootDir, trimmed);
1786
+ return path5.resolve(params.rootDir, trimmed);
1668
1787
  }
1669
1788
  };
1670
1789
  try {
@@ -1705,35 +1824,35 @@ var BUNDLED_CHANNEL_PLUGIN_PACKAGES = [
1705
1824
  "@nextclaw/channel-plugin-qq"
1706
1825
  ];
1707
1826
  function resolvePackageRootFromEntry(entryFile) {
1708
- let cursor = path5.dirname(entryFile);
1827
+ let cursor = path6.dirname(entryFile);
1709
1828
  for (let i = 0; i < 8; i += 1) {
1710
- const candidate = path5.join(cursor, "package.json");
1829
+ const candidate = path6.join(cursor, "package.json");
1711
1830
  if (fs5.existsSync(candidate)) {
1712
1831
  return cursor;
1713
1832
  }
1714
- const parent = path5.dirname(cursor);
1833
+ const parent = path6.dirname(cursor);
1715
1834
  if (parent === cursor) {
1716
1835
  break;
1717
1836
  }
1718
1837
  cursor = parent;
1719
1838
  }
1720
- return path5.dirname(entryFile);
1839
+ return path6.dirname(entryFile);
1721
1840
  }
1722
1841
  function resolvePluginSdkAliasFile(params) {
1723
1842
  try {
1724
1843
  const modulePath = fileURLToPath(import.meta.url);
1725
1844
  const isProduction = process.env.NODE_ENV === "production";
1726
- let cursor = path5.dirname(modulePath);
1845
+ let cursor = path6.dirname(modulePath);
1727
1846
  for (let i = 0; i < 6; i += 1) {
1728
- const srcCandidate = path5.join(cursor, "src", "plugin-sdk", params.srcFile);
1729
- const distCandidate = path5.join(cursor, "dist", "plugin-sdk", params.distFile);
1847
+ const srcCandidate = path6.join(cursor, "src", "plugin-sdk", params.srcFile);
1848
+ const distCandidate = path6.join(cursor, "dist", "plugin-sdk", params.distFile);
1730
1849
  const candidates = isProduction ? [distCandidate, srcCandidate] : [srcCandidate, distCandidate];
1731
1850
  for (const candidate of candidates) {
1732
1851
  if (fs5.existsSync(candidate)) {
1733
1852
  return candidate;
1734
1853
  }
1735
1854
  }
1736
- const parent = path5.dirname(cursor);
1855
+ const parent = path6.dirname(cursor);
1737
1856
  if (parent === cursor) {
1738
1857
  break;
1739
1858
  }
@@ -1747,6 +1866,46 @@ function resolvePluginSdkAliasFile(params) {
1747
1866
  function resolvePluginSdkAlias() {
1748
1867
  return resolvePluginSdkAliasFile({ srcFile: "index.ts", distFile: "index.js" });
1749
1868
  }
1869
+ function buildScopedPackageAliases(scope) {
1870
+ const aliases = {};
1871
+ const require2 = createRequire(import.meta.url);
1872
+ let cursor = path6.dirname(fileURLToPath(import.meta.url));
1873
+ for (let i = 0; i < 8; i += 1) {
1874
+ const scopeDir = path6.join(cursor, "node_modules", scope);
1875
+ if (fs5.existsSync(scopeDir)) {
1876
+ let entries = [];
1877
+ try {
1878
+ entries = fs5.readdirSync(scopeDir, { withFileTypes: true });
1879
+ } catch {
1880
+ entries = [];
1881
+ }
1882
+ for (const entry of entries) {
1883
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) {
1884
+ continue;
1885
+ }
1886
+ const packageName = `${scope}/${entry.name}`;
1887
+ try {
1888
+ aliases[packageName] = require2.resolve(packageName);
1889
+ } catch {
1890
+ }
1891
+ }
1892
+ }
1893
+ const parent = path6.dirname(cursor);
1894
+ if (parent === cursor) {
1895
+ break;
1896
+ }
1897
+ cursor = parent;
1898
+ }
1899
+ return aliases;
1900
+ }
1901
+ function buildPluginLoaderAliases() {
1902
+ const aliases = buildScopedPackageAliases("@nextclaw");
1903
+ const pluginSdkAlias = resolvePluginSdkAlias();
1904
+ if (pluginSdkAlias) {
1905
+ aliases["openclaw/plugin-sdk"] = pluginSdkAlias;
1906
+ }
1907
+ return aliases;
1908
+ }
1750
1909
  function resolvePluginModuleExport(moduleExport) {
1751
1910
  const resolved = moduleExport && typeof moduleExport === "object" && "default" in moduleExport ? moduleExport.default : moduleExport;
1752
1911
  if (typeof resolved === "function") {
@@ -1763,55 +1922,6 @@ function resolvePluginModuleExport(moduleExport) {
1763
1922
  }
1764
1923
  return {};
1765
1924
  }
1766
- function createPluginRecord(params) {
1767
- return {
1768
- id: params.id,
1769
- name: params.name ?? params.id,
1770
- description: params.description,
1771
- version: params.version,
1772
- kind: params.kind,
1773
- source: params.source,
1774
- origin: params.origin,
1775
- workspaceDir: params.workspaceDir,
1776
- enabled: params.enabled,
1777
- status: params.enabled ? "loaded" : "disabled",
1778
- toolNames: [],
1779
- channelIds: [],
1780
- providerIds: [],
1781
- engineKinds: [],
1782
- configSchema: params.configSchema,
1783
- configUiHints: params.configUiHints,
1784
- configJsonSchema: params.configJsonSchema
1785
- };
1786
- }
1787
- function isPlaceholderConfigSchema(schema) {
1788
- if (!schema || typeof schema !== "object") {
1789
- return false;
1790
- }
1791
- const type = schema.type;
1792
- const isObjectType = type === "object" || Array.isArray(type) && type.includes("object");
1793
- if (!isObjectType) {
1794
- return false;
1795
- }
1796
- const properties = schema.properties;
1797
- const noProperties = !properties || typeof properties === "object" && !Array.isArray(properties) && Object.keys(properties).length === 0;
1798
- return noProperties && schema.additionalProperties === false;
1799
- }
1800
- function validatePluginConfig(params) {
1801
- if (!params.schema || isPlaceholderConfigSchema(params.schema)) {
1802
- return { ok: true, value: params.value };
1803
- }
1804
- const cacheKey = params.cacheKey ?? JSON.stringify(params.schema);
1805
- const result = validateJsonSchemaValue({
1806
- schema: params.schema,
1807
- cacheKey,
1808
- value: params.value ?? {}
1809
- });
1810
- if (result.ok) {
1811
- return { ok: true, value: params.value };
1812
- }
1813
- return { ok: false, errors: result.errors };
1814
- }
1815
1925
  function appendBundledChannelPlugins(params) {
1816
1926
  const require2 = createRequire(import.meta.url);
1817
1927
  for (const packageName of BUNDLED_CHANNEL_PLUGIN_PACKAGES) {
@@ -1918,6 +2028,7 @@ function loadOpenClawPlugins(options) {
1918
2028
  channels: [],
1919
2029
  providers: [],
1920
2030
  engines: [],
2031
+ ncpAgentRuntimes: [],
1921
2032
  diagnostics: [],
1922
2033
  resolvedTools: []
1923
2034
  };
@@ -1925,6 +2036,9 @@ function loadOpenClawPlugins(options) {
1925
2036
  const reservedChannelIds = new Set(options.reservedChannelIds ?? []);
1926
2037
  const reservedProviderIds = new Set(options.reservedProviderIds ?? []);
1927
2038
  const reservedEngineKinds = new Set((options.reservedEngineKinds ?? ["native"]).map((entry) => entry.toLowerCase()));
2039
+ const reservedNcpAgentRuntimeKinds = new Set(
2040
+ (options.reservedNcpAgentRuntimeKinds ?? ["native"]).map((entry) => entry.toLowerCase())
2041
+ );
1928
2042
  const registerRuntime = createPluginRegisterRuntime({
1929
2043
  config: options.config,
1930
2044
  workspaceDir,
@@ -1933,17 +2047,13 @@ function loadOpenClawPlugins(options) {
1933
2047
  reservedToolNames,
1934
2048
  reservedChannelIds,
1935
2049
  reservedProviderIds,
1936
- reservedEngineKinds
2050
+ reservedEngineKinds,
2051
+ reservedNcpAgentRuntimeKinds
1937
2052
  });
1938
- const pluginSdkAlias = resolvePluginSdkAlias();
1939
2053
  const jiti = createJiti(import.meta.url, {
1940
2054
  interopDefault: true,
1941
2055
  extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".json"],
1942
- ...pluginSdkAlias ? {
1943
- alias: {
1944
- "openclaw/plugin-sdk": pluginSdkAlias
1945
- }
1946
- } : {}
2056
+ alias: buildPluginLoaderAliases()
1947
2057
  });
1948
2058
  appendBundledChannelPlugins({
1949
2059
  registry,
@@ -1959,10 +2069,11 @@ function loadOpenClawPlugins(options) {
1959
2069
  workspaceDir,
1960
2070
  extraPaths: normalized.loadPaths
1961
2071
  });
2072
+ const filteredCandidates = filterPluginCandidatesByExcludedRoots(discovery.candidates, options.excludeRoots ?? []);
1962
2073
  const manifestRegistry = loadPluginManifestRegistry({
1963
2074
  config: options.config,
1964
2075
  workspaceDir,
1965
- candidates: discovery.candidates,
2076
+ candidates: filteredCandidates,
1966
2077
  diagnostics: discovery.diagnostics
1967
2078
  });
1968
2079
  registry.diagnostics.push(...manifestRegistry.diagnostics);
@@ -1970,7 +2081,7 @@ function loadOpenClawPlugins(options) {
1970
2081
  const seenIds = new Map(
1971
2082
  registry.plugins.map((entry) => [entry.id, entry.origin])
1972
2083
  );
1973
- for (const candidate of discovery.candidates) {
2084
+ for (const candidate of filteredCandidates) {
1974
2085
  const manifest = manifestByRoot.get(candidate.rootDir);
1975
2086
  if (!manifest) {
1976
2087
  continue;
@@ -2139,7 +2250,8 @@ function buildPluginStatusReport(params) {
2139
2250
  reservedToolNames: params.reservedToolNames,
2140
2251
  reservedChannelIds: params.reservedChannelIds,
2141
2252
  reservedProviderIds: params.reservedProviderIds,
2142
- reservedEngineKinds: params.reservedEngineKinds
2253
+ reservedEngineKinds: params.reservedEngineKinds,
2254
+ reservedNcpAgentRuntimeKinds: params.reservedNcpAgentRuntimeKinds
2143
2255
  });
2144
2256
  return {
2145
2257
  workspaceDir,
@@ -2149,7 +2261,9 @@ function buildPluginStatusReport(params) {
2149
2261
 
2150
2262
  // src/plugins/uninstall.ts
2151
2263
  import fs6 from "fs/promises";
2152
- import path6 from "path";
2264
+ import { existsSync, statSync } from "fs";
2265
+ import path7 from "path";
2266
+ import { getWorkspacePathFromConfig as getWorkspacePathFromConfig3 } from "@nextclaw/core";
2153
2267
  function isLinkedPathInstall(record) {
2154
2268
  if (!record || record.source !== "path") {
2155
2269
  return false;
@@ -2157,7 +2271,16 @@ function isLinkedPathInstall(record) {
2157
2271
  if (!record.sourcePath || !record.installPath) {
2158
2272
  return true;
2159
2273
  }
2160
- return path6.resolve(record.sourcePath) === path6.resolve(record.installPath);
2274
+ return path7.resolve(record.sourcePath) === path7.resolve(record.installPath);
2275
+ }
2276
+ function pushUniquePath(targets, candidate) {
2277
+ if (!candidate) {
2278
+ return;
2279
+ }
2280
+ const resolved = path7.resolve(candidate);
2281
+ if (!targets.includes(resolved)) {
2282
+ targets.push(resolved);
2283
+ }
2161
2284
  }
2162
2285
  function resolveUninstallDirectoryTarget(params) {
2163
2286
  if (!params.hasInstall) {
@@ -2176,11 +2299,30 @@ function resolveUninstallDirectoryTarget(params) {
2176
2299
  if (!configuredPath) {
2177
2300
  return defaultPath;
2178
2301
  }
2179
- if (path6.resolve(configuredPath) === path6.resolve(defaultPath)) {
2302
+ if (path7.resolve(configuredPath) === path7.resolve(defaultPath)) {
2180
2303
  return configuredPath;
2181
2304
  }
2182
2305
  return defaultPath;
2183
2306
  }
2307
+ function resolveUninstallDirectoryTargets(params) {
2308
+ if (!params.hasInstall || isLinkedPathInstall(params.installRecord)) {
2309
+ return [];
2310
+ }
2311
+ const targets = [];
2312
+ pushUniquePath(
2313
+ targets,
2314
+ resolveUninstallDirectoryTarget({
2315
+ pluginId: params.pluginId,
2316
+ hasInstall: params.hasInstall,
2317
+ installRecord: params.installRecord,
2318
+ extensionsDir: params.extensionsDir
2319
+ })
2320
+ );
2321
+ pushUniquePath(targets, params.installRecord?.installPath);
2322
+ const workspaceDir = getWorkspacePathFromConfig3(params.config);
2323
+ pushUniquePath(targets, path7.join(workspaceDir, ".nextclaw", "extensions", params.pluginId));
2324
+ return targets;
2325
+ }
2184
2326
  function removePluginFromConfig(config, pluginId) {
2185
2327
  const actions = {
2186
2328
  entry: false,
@@ -2213,14 +2355,15 @@ function removePluginFromConfig(config, pluginId) {
2213
2355
  actions.allowlist = true;
2214
2356
  }
2215
2357
  let load = pluginsConfig.load;
2216
- if (installRecord?.source === "path" && installRecord.sourcePath) {
2217
- const sourcePath = installRecord.sourcePath;
2218
- const loadPaths = load?.paths;
2219
- if (Array.isArray(loadPaths) && loadPaths.includes(sourcePath)) {
2220
- const nextLoadPaths = loadPaths.filter((entry) => entry !== sourcePath);
2221
- load = nextLoadPaths.length > 0 ? { ...load, paths: nextLoadPaths } : void 0;
2222
- actions.loadPath = true;
2223
- }
2358
+ const configuredLoadPaths = Array.isArray(load?.paths) ? load.paths : [];
2359
+ const nextLoadPaths = configuredLoadPaths.filter((entry) => !matchesPluginLoadPath(entry, pluginId));
2360
+ if (nextLoadPaths.length !== configuredLoadPaths.length) {
2361
+ load = nextLoadPaths.length > 0 ? { ...load, paths: nextLoadPaths } : void 0;
2362
+ actions.loadPath = true;
2363
+ } else if (installRecord?.source === "path" && installRecord.sourcePath && configuredLoadPaths.includes(installRecord.sourcePath)) {
2364
+ const filteredLoadPaths = configuredLoadPaths.filter((entry) => entry !== installRecord.sourcePath);
2365
+ load = filteredLoadPaths.length > 0 ? { ...load, paths: filteredLoadPaths } : void 0;
2366
+ actions.loadPath = true;
2224
2367
  }
2225
2368
  const nextPlugins = {
2226
2369
  ...pluginsConfig
@@ -2253,6 +2396,28 @@ function removePluginFromConfig(config, pluginId) {
2253
2396
  actions
2254
2397
  };
2255
2398
  }
2399
+ function matchesPluginLoadPath(rawPath, pluginId) {
2400
+ const normalizedPath = rawPath.trim();
2401
+ if (!normalizedPath) {
2402
+ return false;
2403
+ }
2404
+ const resolvedPath = path7.resolve(normalizedPath);
2405
+ if (!existsSync(resolvedPath)) {
2406
+ return false;
2407
+ }
2408
+ const candidateRoot = (() => {
2409
+ try {
2410
+ return statSync(resolvedPath).isDirectory() ? resolvedPath : path7.dirname(resolvedPath);
2411
+ } catch {
2412
+ return null;
2413
+ }
2414
+ })();
2415
+ if (!candidateRoot) {
2416
+ return false;
2417
+ }
2418
+ const manifest = loadPluginManifest(candidateRoot);
2419
+ return manifest.ok && manifest.manifest.id === pluginId;
2420
+ }
2256
2421
  async function uninstallPlugin(params) {
2257
2422
  const { config, pluginId, deleteFiles = true, extensionsDir } = params;
2258
2423
  const hasEntry = pluginId in (config.plugins.entries ?? {});
@@ -2268,17 +2433,18 @@ async function uninstallPlugin(params) {
2268
2433
  directory: false
2269
2434
  };
2270
2435
  const warnings = [];
2271
- const deleteTarget = deleteFiles && !isLinked ? resolveUninstallDirectoryTarget({
2436
+ const deleteTargets = deleteFiles && !isLinked ? resolveUninstallDirectoryTargets({
2437
+ config,
2272
2438
  pluginId,
2273
2439
  hasInstall,
2274
2440
  installRecord,
2275
2441
  extensionsDir
2276
- }) : null;
2277
- if (deleteTarget) {
2278
- const existed = await fs6.access(deleteTarget).then(() => true).catch(() => false) ?? false;
2442
+ }) : [];
2443
+ for (const deleteTarget of deleteTargets) {
2444
+ const existed = await fs6.access(deleteTarget).then(() => true).catch(() => false);
2279
2445
  try {
2280
2446
  await fs6.rm(deleteTarget, { recursive: true, force: true });
2281
- actions.directory = existed;
2447
+ actions.directory = actions.directory || existed;
2282
2448
  } catch (error) {
2283
2449
  warnings.push(
2284
2450
  `Failed to remove plugin directory ${deleteTarget}: ${error instanceof Error ? error.message : String(error)}`
@@ -2301,6 +2467,7 @@ export {
2301
2467
  addPluginLoadPath,
2302
2468
  buildChannelConfigSchema,
2303
2469
  buildOauthProviderAuthResult,
2470
+ buildPluginLoaderAliases,
2304
2471
  buildPluginStatusReport,
2305
2472
  createNextclawBuiltinChannelPlugin,
2306
2473
  createPluginRegisterRuntime,
@@ -2332,6 +2499,7 @@ export {
2332
2499
  resolvePluginInstallDir,
2333
2500
  resolvePluginManifestPath,
2334
2501
  resolveUninstallDirectoryTarget,
2502
+ resolveUninstallDirectoryTargets,
2335
2503
  setPluginRuntimeBridge,
2336
2504
  sleep,
2337
2505
  startPluginChannelGateways,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/openclaw-compat",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "description": "OpenClaw plugin compatibility layer for NextClaw.",
6
6
  "type": "module",
@@ -20,17 +20,19 @@
20
20
  "jszip": "^3.10.1",
21
21
  "tar": "^7.4.3",
22
22
  "@nextclaw/channel-plugin-dingtalk": "0.2.0",
23
- "@nextclaw/channel-plugin-email": "0.2.0",
24
23
  "@nextclaw/channel-plugin-discord": "0.2.0",
24
+ "@nextclaw/channel-plugin-email": "0.2.0",
25
25
  "@nextclaw/channel-plugin-feishu": "0.2.0",
26
- "@nextclaw/channel-plugin-qq": "0.2.0",
27
26
  "@nextclaw/channel-plugin-mochat": "0.2.0",
28
27
  "@nextclaw/channel-plugin-slack": "0.2.0",
29
- "@nextclaw/core": "0.9.0",
28
+ "@nextclaw/channel-plugin-qq": "0.2.0",
30
29
  "@nextclaw/channel-plugin-telegram": "0.2.0",
31
- "@nextclaw/channel-runtime": "0.2.0",
30
+ "@nextclaw/channel-plugin-wecom": "0.2.0",
32
31
  "@nextclaw/channel-plugin-whatsapp": "0.2.0",
33
- "@nextclaw/channel-plugin-wecom": "0.2.0"
32
+ "@nextclaw/channel-runtime": "0.2.0",
33
+ "@nextclaw/core": "0.9.0",
34
+ "@nextclaw/ncp": "0.3.0",
35
+ "@nextclaw/ncp-toolkit": "0.4.0"
34
36
  },
35
37
  "devDependencies": {
36
38
  "@types/node": "^20.17.6",