@nextclaw/openclaw-compat 0.1.6 → 0.1.8

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +50 -1
  2. package/dist/index.js +500 -684
  3. package/package.json +11 -2
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // src/plugin-sdk/index.ts
2
+ import { listBuiltinChannelPlugins } from "@nextclaw/core";
2
3
  function emptyPluginConfigSchema() {
3
4
  return {
4
5
  type: "object",
@@ -44,6 +45,19 @@ function normalizeAccountId(accountId) {
44
45
  const trimmed = accountId?.trim();
45
46
  return trimmed || DEFAULT_ACCOUNT_ID;
46
47
  }
48
+ function createNextclawBuiltinChannelPlugin(channelId) {
49
+ const builtin = listBuiltinChannelPlugins().find((entry) => entry.id === channelId);
50
+ if (!builtin) {
51
+ throw new Error(`builtin channel plugin not found: ${channelId}`);
52
+ }
53
+ return {
54
+ id: channelId,
55
+ nextclaw: {
56
+ isEnabled: builtin.isEnabled,
57
+ createChannel: builtin.create
58
+ }
59
+ };
60
+ }
47
61
  var __nextclawPluginSdkCompat = true;
48
62
 
49
63
  // src/plugins/config-state.ts
@@ -224,10 +238,10 @@ function resolvePluginChannelMessageToolHints(params) {
224
238
  }
225
239
  }
226
240
  function getPluginUiMetadataFromRegistry(registry) {
227
- return registry.plugins.map((plugin10) => ({
228
- id: plugin10.id,
229
- configSchema: plugin10.configJsonSchema,
230
- configUiHints: plugin10.configUiHints
241
+ return registry.plugins.map((plugin) => ({
242
+ id: plugin.id,
243
+ configSchema: plugin.configJsonSchema,
244
+ configUiHints: plugin.configUiHints
231
245
  }));
232
246
  }
233
247
  async function startPluginChannelGateways(params) {
@@ -1057,11 +1071,11 @@ async function installPluginFromPath(params) {
1057
1071
 
1058
1072
  // src/plugins/loader.ts
1059
1073
  import fs5 from "fs";
1060
- import path4 from "path";
1074
+ import path5 from "path";
1061
1075
  import { fileURLToPath } from "url";
1076
+ import { createRequire } from "module";
1062
1077
  import createJitiImport from "jiti";
1063
1078
  import { getWorkspacePathFromConfig } from "@nextclaw/core";
1064
- import { expandHome as expandHome2 } from "@nextclaw/core";
1065
1079
 
1066
1080
  // src/plugins/manifest-registry.ts
1067
1081
  import fs4 from "fs";
@@ -1208,6 +1222,43 @@ function loadPluginUiMetadata(params) {
1208
1222
  return toPluginUiMetadata(registry.plugins);
1209
1223
  }
1210
1224
 
1225
+ // src/plugins/schema-validator.ts
1226
+ import AjvPkg from "ajv";
1227
+ var AjvCtor = AjvPkg;
1228
+ var ajv = new AjvCtor({
1229
+ allErrors: true,
1230
+ strict: false,
1231
+ removeAdditional: false
1232
+ });
1233
+ var schemaCache = /* @__PURE__ */ new Map();
1234
+ function formatAjvErrors(errors) {
1235
+ if (!errors || errors.length === 0) {
1236
+ return ["invalid config"];
1237
+ }
1238
+ return errors.map((error) => {
1239
+ const path7 = error.instancePath?.replace(/^\//, "").replace(/\//g, ".") || "<root>";
1240
+ const message = error.message ?? "invalid";
1241
+ return `${path7}: ${message}`;
1242
+ });
1243
+ }
1244
+ function validateJsonSchemaValue(params) {
1245
+ let cached = schemaCache.get(params.cacheKey);
1246
+ if (!cached || cached.schema !== params.schema) {
1247
+ const validate = ajv.compile(params.schema);
1248
+ cached = { validate, schema: params.schema };
1249
+ schemaCache.set(params.cacheKey, cached);
1250
+ }
1251
+ const ok = cached.validate(params.value);
1252
+ if (ok) {
1253
+ return { ok: true };
1254
+ }
1255
+ return { ok: false, errors: formatAjvErrors(cached.validate.errors) };
1256
+ }
1257
+
1258
+ // src/plugins/registry.ts
1259
+ import path4 from "path";
1260
+ import { expandHome as expandHome2 } from "@nextclaw/core";
1261
+
1211
1262
  // src/plugins/runtime.ts
1212
1263
  import { getPackageVersion } from "@nextclaw/core";
1213
1264
  import { MemoryGetTool, MemorySearchTool } from "@nextclaw/core";
@@ -1260,165 +1311,339 @@ function createPluginRuntime(params) {
1260
1311
  };
1261
1312
  }
1262
1313
 
1263
- // src/plugins/schema-validator.ts
1264
- import AjvPkg from "ajv";
1265
- var AjvCtor = AjvPkg;
1266
- var ajv = new AjvCtor({
1267
- allErrors: true,
1268
- strict: false,
1269
- removeAdditional: false
1270
- });
1271
- var schemaCache = /* @__PURE__ */ new Map();
1272
- function formatAjvErrors(errors) {
1273
- if (!errors || errors.length === 0) {
1274
- return ["invalid config"];
1314
+ // src/plugins/registry.ts
1315
+ function buildPluginLogger(base, pluginId) {
1316
+ const withPrefix = (message) => `[plugins:${pluginId}] ${message}`;
1317
+ return {
1318
+ info: (message) => base.info(withPrefix(message)),
1319
+ warn: (message) => base.warn(withPrefix(message)),
1320
+ error: (message) => base.error(withPrefix(message)),
1321
+ debug: base.debug ? (message) => base.debug?.(withPrefix(message)) : void 0
1322
+ };
1323
+ }
1324
+ function ensureUniqueNames(params) {
1325
+ const accepted = [];
1326
+ for (const rawName of params.names) {
1327
+ const name = rawName.trim();
1328
+ if (!name) {
1329
+ continue;
1330
+ }
1331
+ if (params.reserved.has(name)) {
1332
+ params.diagnostics.push({
1333
+ level: "error",
1334
+ pluginId: params.pluginId,
1335
+ source: params.source,
1336
+ message: `${params.kind} already registered by core: ${name}`
1337
+ });
1338
+ continue;
1339
+ }
1340
+ const owner = params.owners.get(name);
1341
+ if (owner && owner !== params.pluginId) {
1342
+ params.diagnostics.push({
1343
+ level: "error",
1344
+ pluginId: params.pluginId,
1345
+ source: params.source,
1346
+ message: `${params.kind} already registered: ${name} (${owner})`
1347
+ });
1348
+ continue;
1349
+ }
1350
+ params.owners.set(name, params.pluginId);
1351
+ accepted.push(name);
1275
1352
  }
1276
- return errors.map((error) => {
1277
- const path6 = error.instancePath?.replace(/^\//, "").replace(/\//g, ".") || "<root>";
1278
- const message = error.message ?? "invalid";
1279
- return `${path6}: ${message}`;
1353
+ return accepted;
1354
+ }
1355
+ function normalizeToolList(value) {
1356
+ if (!value) {
1357
+ return [];
1358
+ }
1359
+ const list = Array.isArray(value) ? value : [value];
1360
+ return list.filter((entry) => {
1361
+ if (!entry || typeof entry !== "object") {
1362
+ return false;
1363
+ }
1364
+ const candidate = entry;
1365
+ return typeof candidate.name === "string" && candidate.name.trim().length > 0 && candidate.parameters !== void 0 && typeof candidate.execute === "function";
1280
1366
  });
1281
1367
  }
1282
- function validateJsonSchemaValue(params) {
1283
- let cached = schemaCache.get(params.cacheKey);
1284
- if (!cached || cached.schema !== params.schema) {
1285
- const validate = ajv.compile(params.schema);
1286
- cached = { validate, schema: params.schema };
1287
- schemaCache.set(params.cacheKey, cached);
1368
+ function createPluginRegisterRuntime(params) {
1369
+ return {
1370
+ config: params.config,
1371
+ workspaceDir: params.workspaceDir,
1372
+ logger: params.logger,
1373
+ registry: params.registry,
1374
+ toolNameOwners: /* @__PURE__ */ new Map(),
1375
+ channelIdOwners: /* @__PURE__ */ new Map(),
1376
+ providerIdOwners: /* @__PURE__ */ new Map(),
1377
+ resolvedToolNames: /* @__PURE__ */ new Set(),
1378
+ reservedToolNames: params.reservedToolNames,
1379
+ reservedChannelIds: params.reservedChannelIds,
1380
+ reservedProviderIds: params.reservedProviderIds
1381
+ };
1382
+ }
1383
+ function registerPluginTool(params) {
1384
+ const toolInput = params.tool;
1385
+ const normalizedNames = [];
1386
+ if (Array.isArray(params.opts?.names)) {
1387
+ for (const name of params.opts?.names ?? []) {
1388
+ const trimmed = String(name).trim();
1389
+ if (trimmed) {
1390
+ normalizedNames.push(trimmed);
1391
+ }
1392
+ }
1393
+ } else if (params.opts?.name && String(params.opts.name).trim()) {
1394
+ normalizedNames.push(String(params.opts.name).trim());
1288
1395
  }
1289
- const ok = cached.validate(params.value);
1290
- if (ok) {
1291
- return { ok: true };
1396
+ if (typeof toolInput !== "function") {
1397
+ const intrinsic = toolInput.name.trim();
1398
+ if (intrinsic) {
1399
+ normalizedNames.push(intrinsic);
1400
+ }
1401
+ }
1402
+ const acceptedNames = ensureUniqueNames({
1403
+ names: normalizedNames,
1404
+ pluginId: params.pluginId,
1405
+ diagnostics: params.runtime.registry.diagnostics,
1406
+ source: params.source,
1407
+ owners: params.runtime.toolNameOwners,
1408
+ reserved: params.runtime.reservedToolNames,
1409
+ kind: "tool"
1410
+ });
1411
+ if (acceptedNames.length === 0) {
1412
+ return;
1413
+ }
1414
+ const factory = typeof toolInput === "function" ? toolInput : () => toolInput;
1415
+ const registration = {
1416
+ pluginId: params.pluginId,
1417
+ factory,
1418
+ names: acceptedNames,
1419
+ optional: params.opts?.optional === true,
1420
+ source: params.source
1421
+ };
1422
+ params.runtime.registry.tools.push(registration);
1423
+ params.record.toolNames.push(...acceptedNames);
1424
+ if (typeof toolInput === "function") {
1425
+ return;
1426
+ }
1427
+ if (!params.runtime.resolvedToolNames.has(toolInput.name)) {
1428
+ params.runtime.resolvedToolNames.add(toolInput.name);
1429
+ params.runtime.registry.resolvedTools.push(toolInput);
1292
1430
  }
1293
- return { ok: false, errors: formatAjvErrors(cached.validate.errors) };
1294
1431
  }
1295
-
1296
- // src/plugins/bundled/channels/factory.ts
1297
- import { listBuiltinChannelPlugins } from "@nextclaw/core";
1298
- var EMPTY_PLUGIN_CONFIG_SCHEMA = {
1299
- type: "object",
1300
- additionalProperties: false,
1301
- properties: {}
1302
- };
1303
- function resolveBuiltinChannelPlugin(channelId) {
1304
- const plugin10 = listBuiltinChannelPlugins().find((entry) => entry.id === channelId);
1305
- if (!plugin10) {
1306
- throw new Error(`builtin channel plugin not found: ${channelId}`);
1432
+ function registerPluginChannel(params) {
1433
+ const normalizedChannel = params.registration && typeof params.registration === "object" && "plugin" in params.registration ? params.registration.plugin : params.registration;
1434
+ if (!normalizedChannel || typeof normalizedChannel !== "object") {
1435
+ params.runtime.registry.diagnostics.push({
1436
+ level: "error",
1437
+ pluginId: params.pluginId,
1438
+ source: params.source,
1439
+ message: "channel registration missing plugin object"
1440
+ });
1441
+ return;
1442
+ }
1443
+ const channelObj = normalizedChannel;
1444
+ const rawId = typeof channelObj.id === "string" ? channelObj.id : String(channelObj.id ?? "");
1445
+ const accepted = ensureUniqueNames({
1446
+ names: [rawId],
1447
+ pluginId: params.pluginId,
1448
+ diagnostics: params.runtime.registry.diagnostics,
1449
+ source: params.source,
1450
+ owners: params.runtime.channelIdOwners,
1451
+ reserved: params.runtime.reservedChannelIds,
1452
+ kind: "channel"
1453
+ });
1454
+ if (accepted.length === 0) {
1455
+ return;
1456
+ }
1457
+ const channelPlugin = normalizedChannel;
1458
+ params.runtime.registry.channels.push({
1459
+ pluginId: params.pluginId,
1460
+ channel: channelPlugin,
1461
+ source: params.source
1462
+ });
1463
+ const channelId = accepted[0];
1464
+ params.record.channelIds.push(channelId);
1465
+ const configSchema = channelPlugin.configSchema;
1466
+ if (configSchema && typeof configSchema === "object") {
1467
+ const schema = configSchema.schema;
1468
+ if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1469
+ params.record.configJsonSchema = schema;
1470
+ params.record.configSchema = true;
1471
+ }
1472
+ const uiHints = configSchema.uiHints;
1473
+ if (uiHints && typeof uiHints === "object" && !Array.isArray(uiHints)) {
1474
+ params.record.configUiHints = {
1475
+ ...params.record.configUiHints ?? {},
1476
+ ...uiHints
1477
+ };
1478
+ }
1479
+ }
1480
+ const pushChannelTools = (value, optional, sourceLabel, resolveValue) => {
1481
+ const previewTools = normalizeToolList(value);
1482
+ if (previewTools.length === 0) {
1483
+ return;
1484
+ }
1485
+ const declaredNames = previewTools.map((tool) => tool.name);
1486
+ const acceptedNames = ensureUniqueNames({
1487
+ names: declaredNames,
1488
+ pluginId: params.pluginId,
1489
+ diagnostics: params.runtime.registry.diagnostics,
1490
+ source: params.source,
1491
+ owners: params.runtime.toolNameOwners,
1492
+ reserved: params.runtime.reservedToolNames,
1493
+ kind: "tool"
1494
+ });
1495
+ if (acceptedNames.length === 0) {
1496
+ return;
1497
+ }
1498
+ const factory = (ctx) => {
1499
+ const tools = normalizeToolList(resolveValue(ctx));
1500
+ if (tools.length === 0) {
1501
+ return [];
1502
+ }
1503
+ const byName = new Map(tools.map((tool) => [tool.name, tool]));
1504
+ return acceptedNames.map((name) => byName.get(name)).filter(Boolean);
1505
+ };
1506
+ params.runtime.registry.tools.push({
1507
+ pluginId: params.pluginId,
1508
+ factory,
1509
+ names: acceptedNames,
1510
+ optional,
1511
+ source: params.source
1512
+ });
1513
+ params.record.toolNames.push(...acceptedNames);
1514
+ const previewByName = new Map(previewTools.map((tool) => [tool.name, tool]));
1515
+ for (const name of acceptedNames) {
1516
+ const resolvedTool = previewByName.get(name);
1517
+ if (!resolvedTool || params.runtime.resolvedToolNames.has(resolvedTool.name)) {
1518
+ continue;
1519
+ }
1520
+ params.runtime.resolvedToolNames.add(resolvedTool.name);
1521
+ params.runtime.registry.resolvedTools.push(resolvedTool);
1522
+ }
1523
+ params.runtime.registry.diagnostics.push({
1524
+ level: "warn",
1525
+ pluginId: params.pluginId,
1526
+ source: params.source,
1527
+ message: `${sourceLabel} registered channel-owned tools: ${acceptedNames.join(", ")}`
1528
+ });
1529
+ };
1530
+ const agentTools = channelPlugin.agentTools;
1531
+ if (typeof agentTools === "function") {
1532
+ pushChannelTools(
1533
+ normalizeToolList(agentTools()),
1534
+ false,
1535
+ `channel "${channelId}"`,
1536
+ () => agentTools()
1537
+ );
1538
+ } else if (agentTools) {
1539
+ pushChannelTools(normalizeToolList(agentTools), false, `channel "${channelId}"`, () => agentTools);
1307
1540
  }
1308
- return plugin10;
1309
1541
  }
1310
- function createBundledChannelPlugin(params) {
1311
- const pluginId = `builtin-channel-${params.channelId}`;
1312
- const sourceDescription = params.description ?? `Builtin NextClaw channel plugin for ${params.channelId}`;
1313
- return {
1314
- id: pluginId,
1315
- name: params.name,
1316
- description: sourceDescription,
1317
- configSchema: EMPTY_PLUGIN_CONFIG_SCHEMA,
1318
- register(api) {
1319
- const builtin = resolveBuiltinChannelPlugin(params.channelId);
1320
- api.registerChannel({
1321
- plugin: {
1322
- id: params.channelId,
1323
- nextclaw: {
1324
- isEnabled: builtin.isEnabled,
1325
- createChannel: builtin.create
1326
- }
1327
- }
1542
+ function registerPluginProvider(params) {
1543
+ const accepted = ensureUniqueNames({
1544
+ names: [params.provider.id],
1545
+ pluginId: params.pluginId,
1546
+ diagnostics: params.runtime.registry.diagnostics,
1547
+ source: params.source,
1548
+ owners: params.runtime.providerIdOwners,
1549
+ reserved: params.runtime.reservedProviderIds,
1550
+ kind: "provider"
1551
+ });
1552
+ if (accepted.length === 0) {
1553
+ return;
1554
+ }
1555
+ params.runtime.registry.providers.push({
1556
+ pluginId: params.pluginId,
1557
+ provider: params.provider,
1558
+ source: params.source
1559
+ });
1560
+ params.record.providerIds.push(accepted[0]);
1561
+ }
1562
+ function registerPluginWithApi(params) {
1563
+ const pluginRuntime = createPluginRuntime({ workspace: params.runtime.workspaceDir, config: params.runtime.config });
1564
+ const pluginLogger = buildPluginLogger(params.runtime.logger, params.pluginId);
1565
+ const pushUnsupported = (capability) => {
1566
+ params.runtime.registry.diagnostics.push({
1567
+ level: "warn",
1568
+ pluginId: params.pluginId,
1569
+ source: params.source,
1570
+ message: `${capability} is not supported by nextclaw compat layer yet`
1571
+ });
1572
+ pluginLogger.warn(`${capability} is ignored (not supported yet)`);
1573
+ };
1574
+ const api = {
1575
+ id: params.pluginId,
1576
+ name: params.record.name,
1577
+ version: params.record.version,
1578
+ description: params.record.description,
1579
+ source: params.source,
1580
+ config: params.runtime.config,
1581
+ pluginConfig: params.pluginConfig,
1582
+ runtime: pluginRuntime,
1583
+ logger: pluginLogger,
1584
+ registerTool: (tool, opts) => {
1585
+ registerPluginTool({
1586
+ runtime: params.runtime,
1587
+ record: params.record,
1588
+ pluginId: params.pluginId,
1589
+ source: params.source,
1590
+ tool,
1591
+ opts
1328
1592
  });
1593
+ },
1594
+ registerChannel: (registration) => {
1595
+ registerPluginChannel({
1596
+ runtime: params.runtime,
1597
+ record: params.record,
1598
+ pluginId: params.pluginId,
1599
+ source: params.source,
1600
+ registration
1601
+ });
1602
+ },
1603
+ registerProvider: (provider) => {
1604
+ registerPluginProvider({
1605
+ runtime: params.runtime,
1606
+ record: params.record,
1607
+ pluginId: params.pluginId,
1608
+ source: params.source,
1609
+ provider
1610
+ });
1611
+ },
1612
+ registerHook: () => pushUnsupported("registerHook"),
1613
+ registerGatewayMethod: () => pushUnsupported("registerGatewayMethod"),
1614
+ registerCli: () => pushUnsupported("registerCli"),
1615
+ registerService: () => pushUnsupported("registerService"),
1616
+ registerCommand: () => pushUnsupported("registerCommand"),
1617
+ registerHttpHandler: () => pushUnsupported("registerHttpHandler"),
1618
+ registerHttpRoute: () => pushUnsupported("registerHttpRoute"),
1619
+ resolvePath: (input) => {
1620
+ const trimmed = input.trim();
1621
+ if (!trimmed) {
1622
+ return params.rootDir;
1623
+ }
1624
+ if (path4.isAbsolute(trimmed)) {
1625
+ return path4.resolve(expandHome2(trimmed));
1626
+ }
1627
+ return path4.resolve(params.rootDir, trimmed);
1329
1628
  }
1330
1629
  };
1630
+ try {
1631
+ const result = params.register(api);
1632
+ if (result && typeof result === "object" && "then" in result) {
1633
+ params.runtime.registry.diagnostics.push({
1634
+ level: "warn",
1635
+ pluginId: params.pluginId,
1636
+ source: params.source,
1637
+ message: "plugin register returned a promise; async registration is ignored"
1638
+ });
1639
+ }
1640
+ return { ok: true };
1641
+ } catch (err) {
1642
+ const error = `plugin failed during register: ${String(err)}`;
1643
+ return { ok: false, error };
1644
+ }
1331
1645
  }
1332
1646
 
1333
- // src/plugins/bundled/channels/telegram.ts
1334
- var plugin = createBundledChannelPlugin({
1335
- channelId: "telegram",
1336
- name: "Builtin Telegram Channel",
1337
- description: "Builtin NextClaw channel plugin for telegram"
1338
- });
1339
- var telegram_default = plugin;
1340
-
1341
- // src/plugins/bundled/channels/whatsapp.ts
1342
- var plugin2 = createBundledChannelPlugin({
1343
- channelId: "whatsapp",
1344
- name: "Builtin WhatsApp Channel",
1345
- description: "Builtin NextClaw channel plugin for whatsapp"
1346
- });
1347
- var whatsapp_default = plugin2;
1348
-
1349
- // src/plugins/bundled/channels/discord.ts
1350
- var plugin3 = createBundledChannelPlugin({
1351
- channelId: "discord",
1352
- name: "Builtin Discord Channel",
1353
- description: "Builtin NextClaw channel plugin for discord"
1354
- });
1355
- var discord_default = plugin3;
1356
-
1357
- // src/plugins/bundled/channels/feishu.ts
1358
- var plugin4 = createBundledChannelPlugin({
1359
- channelId: "feishu",
1360
- name: "Builtin Feishu Channel",
1361
- description: "Builtin NextClaw channel plugin for feishu"
1362
- });
1363
- var feishu_default = plugin4;
1364
-
1365
- // src/plugins/bundled/channels/mochat.ts
1366
- var plugin5 = createBundledChannelPlugin({
1367
- channelId: "mochat",
1368
- name: "Builtin Mochat Channel",
1369
- description: "Builtin NextClaw channel plugin for mochat"
1370
- });
1371
- var mochat_default = plugin5;
1372
-
1373
- // src/plugins/bundled/channels/dingtalk.ts
1374
- var plugin6 = createBundledChannelPlugin({
1375
- channelId: "dingtalk",
1376
- name: "Builtin DingTalk Channel",
1377
- description: "Builtin NextClaw channel plugin for dingtalk"
1378
- });
1379
- var dingtalk_default = plugin6;
1380
-
1381
- // src/plugins/bundled/channels/email.ts
1382
- var plugin7 = createBundledChannelPlugin({
1383
- channelId: "email",
1384
- name: "Builtin Email Channel",
1385
- description: "Builtin NextClaw channel plugin for email"
1386
- });
1387
- var email_default = plugin7;
1388
-
1389
- // src/plugins/bundled/channels/slack.ts
1390
- var plugin8 = createBundledChannelPlugin({
1391
- channelId: "slack",
1392
- name: "Builtin Slack Channel",
1393
- description: "Builtin NextClaw channel plugin for slack"
1394
- });
1395
- var slack_default = plugin8;
1396
-
1397
- // src/plugins/bundled/channels/qq.ts
1398
- var plugin9 = createBundledChannelPlugin({
1399
- channelId: "qq",
1400
- name: "Builtin QQ Channel",
1401
- description: "Builtin NextClaw channel plugin for qq"
1402
- });
1403
- var qq_default = plugin9;
1404
-
1405
- // src/plugins/bundled/channels/index.ts
1406
- var BUNDLED_CHANNEL_PLUGINS = [
1407
- telegram_default,
1408
- whatsapp_default,
1409
- discord_default,
1410
- feishu_default,
1411
- mochat_default,
1412
- dingtalk_default,
1413
- email_default,
1414
- slack_default,
1415
- qq_default
1416
- ];
1417
- function listBundledChannelPluginDefinitions() {
1418
- return [...BUNDLED_CHANNEL_PLUGINS];
1419
- }
1420
- var BUNDLED_CHANNEL_PLUGIN_IDS = BUNDLED_CHANNEL_PLUGINS.map((plugin10) => plugin10.id).filter((id) => typeof id === "string" && id.trim().length > 0);
1421
-
1422
1647
  // src/plugins/loader.ts
1423
1648
  var createJiti = createJitiImport;
1424
1649
  var defaultLogger2 = {
@@ -1427,21 +1652,47 @@ var defaultLogger2 = {
1427
1652
  error: (message) => console.error(message),
1428
1653
  debug: (message) => console.debug(message)
1429
1654
  };
1655
+ var BUNDLED_CHANNEL_PLUGIN_PACKAGES = [
1656
+ "@nextclaw/channel-plugin-telegram",
1657
+ "@nextclaw/channel-plugin-whatsapp",
1658
+ "@nextclaw/channel-plugin-discord",
1659
+ "@nextclaw/channel-plugin-feishu",
1660
+ "@nextclaw/channel-plugin-mochat",
1661
+ "@nextclaw/channel-plugin-dingtalk",
1662
+ "@nextclaw/channel-plugin-email",
1663
+ "@nextclaw/channel-plugin-slack",
1664
+ "@nextclaw/channel-plugin-qq"
1665
+ ];
1666
+ function resolvePackageRootFromEntry(entryFile) {
1667
+ let cursor = path5.dirname(entryFile);
1668
+ for (let i = 0; i < 8; i += 1) {
1669
+ const candidate = path5.join(cursor, "package.json");
1670
+ if (fs5.existsSync(candidate)) {
1671
+ return cursor;
1672
+ }
1673
+ const parent = path5.dirname(cursor);
1674
+ if (parent === cursor) {
1675
+ break;
1676
+ }
1677
+ cursor = parent;
1678
+ }
1679
+ return path5.dirname(entryFile);
1680
+ }
1430
1681
  function resolvePluginSdkAliasFile(params) {
1431
1682
  try {
1432
1683
  const modulePath = fileURLToPath(import.meta.url);
1433
1684
  const isProduction = process.env.NODE_ENV === "production";
1434
- let cursor = path4.dirname(modulePath);
1685
+ let cursor = path5.dirname(modulePath);
1435
1686
  for (let i = 0; i < 6; i += 1) {
1436
- const srcCandidate = path4.join(cursor, "src", "plugin-sdk", params.srcFile);
1437
- const distCandidate = path4.join(cursor, "dist", "plugin-sdk", params.distFile);
1687
+ const srcCandidate = path5.join(cursor, "src", "plugin-sdk", params.srcFile);
1688
+ const distCandidate = path5.join(cursor, "dist", "plugin-sdk", params.distFile);
1438
1689
  const candidates = isProduction ? [distCandidate, srcCandidate] : [srcCandidate, distCandidate];
1439
1690
  for (const candidate of candidates) {
1440
1691
  if (fs5.existsSync(candidate)) {
1441
1692
  return candidate;
1442
1693
  }
1443
1694
  }
1444
- const parent = path4.dirname(cursor);
1695
+ const parent = path5.dirname(cursor);
1445
1696
  if (parent === cursor) {
1446
1697
  break;
1447
1698
  }
@@ -1471,19 +1722,6 @@ function resolvePluginModuleExport(moduleExport) {
1471
1722
  }
1472
1723
  return {};
1473
1724
  }
1474
- function normalizeToolList(value) {
1475
- if (!value) {
1476
- return [];
1477
- }
1478
- const list = Array.isArray(value) ? value : [value];
1479
- return list.filter((entry) => {
1480
- if (!entry || typeof entry !== "object") {
1481
- return false;
1482
- }
1483
- const candidate = entry;
1484
- return typeof candidate.name === "string" && candidate.name.trim().length > 0 && candidate.parameters !== void 0 && typeof candidate.execute === "function";
1485
- });
1486
- }
1487
1725
  function createPluginRecord(params) {
1488
1726
  return {
1489
1727
  id: params.id,
@@ -1504,46 +1742,6 @@ function createPluginRecord(params) {
1504
1742
  configJsonSchema: params.configJsonSchema
1505
1743
  };
1506
1744
  }
1507
- function buildPluginLogger(base, pluginId) {
1508
- const withPrefix = (message) => `[plugins:${pluginId}] ${message}`;
1509
- return {
1510
- info: (message) => base.info(withPrefix(message)),
1511
- warn: (message) => base.warn(withPrefix(message)),
1512
- error: (message) => base.error(withPrefix(message)),
1513
- debug: base.debug ? (message) => base.debug?.(withPrefix(message)) : void 0
1514
- };
1515
- }
1516
- function ensureUniqueNames(params) {
1517
- const accepted = [];
1518
- for (const rawName of params.names) {
1519
- const name = rawName.trim();
1520
- if (!name) {
1521
- continue;
1522
- }
1523
- if (params.reserved.has(name)) {
1524
- params.diagnostics.push({
1525
- level: "error",
1526
- pluginId: params.pluginId,
1527
- source: params.source,
1528
- message: `${params.kind} already registered by core: ${name}`
1529
- });
1530
- continue;
1531
- }
1532
- const owner = params.owners.get(name);
1533
- if (owner && owner !== params.pluginId) {
1534
- params.diagnostics.push({
1535
- level: "error",
1536
- pluginId: params.pluginId,
1537
- source: params.source,
1538
- message: `${params.kind} already registered: ${name} (${owner})`
1539
- });
1540
- continue;
1541
- }
1542
- params.owners.set(name, params.pluginId);
1543
- accepted.push(name);
1544
- }
1545
- return accepted;
1546
- }
1547
1745
  function isPlaceholderConfigSchema(schema) {
1548
1746
  if (!schema || typeof schema !== "object") {
1549
1747
  return false;
@@ -1573,9 +1771,37 @@ function validatePluginConfig(params) {
1573
1771
  return { ok: false, errors: result.errors };
1574
1772
  }
1575
1773
  function appendBundledChannelPlugins(params) {
1576
- for (const definition of listBundledChannelPluginDefinitions()) {
1577
- const pluginId = typeof definition.id === "string" ? definition.id.trim() : "";
1578
- const source = `builtin:nextclaw-openclaw-compat/channels/${pluginId || "unknown"}`;
1774
+ const require2 = createRequire(import.meta.url);
1775
+ for (const packageName of BUNDLED_CHANNEL_PLUGIN_PACKAGES) {
1776
+ let entryFile = "";
1777
+ let rootDir = "";
1778
+ try {
1779
+ entryFile = require2.resolve(packageName);
1780
+ rootDir = resolvePackageRootFromEntry(entryFile);
1781
+ } catch (err) {
1782
+ params.registry.diagnostics.push({
1783
+ level: "error",
1784
+ source: packageName,
1785
+ message: `bundled plugin package not resolvable: ${String(err)}`
1786
+ });
1787
+ continue;
1788
+ }
1789
+ let moduleExport = null;
1790
+ try {
1791
+ moduleExport = params.jiti(entryFile);
1792
+ } catch (err) {
1793
+ params.registry.diagnostics.push({
1794
+ level: "error",
1795
+ source: entryFile,
1796
+ message: `failed to load bundled plugin: ${String(err)}`
1797
+ });
1798
+ continue;
1799
+ }
1800
+ const resolved = resolvePluginModuleExport(moduleExport);
1801
+ const definition = resolved.definition;
1802
+ const register = resolved.register;
1803
+ const pluginId = typeof definition?.id === "string" ? definition.id.trim() : "";
1804
+ const source = entryFile;
1579
1805
  if (!pluginId) {
1580
1806
  params.registry.diagnostics.push({
1581
1807
  level: "error",
@@ -1586,18 +1812,17 @@ function appendBundledChannelPlugins(params) {
1586
1812
  }
1587
1813
  const record = createPluginRecord({
1588
1814
  id: pluginId,
1589
- name: definition.name ?? pluginId,
1590
- description: definition.description,
1591
- version: definition.version,
1592
- kind: definition.kind,
1815
+ name: definition?.name ?? pluginId,
1816
+ description: definition?.description,
1817
+ version: definition?.version,
1818
+ kind: definition?.kind,
1593
1819
  source,
1594
1820
  origin: "bundled",
1595
- workspaceDir: params.workspaceDir,
1821
+ workspaceDir: params.runtime.workspaceDir,
1596
1822
  enabled: true,
1597
- configSchema: Boolean(definition.configSchema),
1598
- configJsonSchema: definition.configSchema
1823
+ configSchema: Boolean(definition?.configSchema),
1824
+ configJsonSchema: definition?.configSchema
1599
1825
  });
1600
- const register = definition.register ?? definition.activate;
1601
1826
  if (typeof register !== "function") {
1602
1827
  record.status = "error";
1603
1828
  record.error = "plugin export missing register/activate";
@@ -1610,203 +1835,26 @@ function appendBundledChannelPlugins(params) {
1610
1835
  });
1611
1836
  continue;
1612
1837
  }
1613
- const pluginRuntime = createPluginRuntime({ workspace: params.workspaceDir, config: params.config });
1614
- const pluginLogger = buildPluginLogger(params.logger, pluginId);
1615
- const pushUnsupported = (capability) => {
1616
- params.registry.diagnostics.push({
1617
- level: "warn",
1618
- pluginId,
1619
- source,
1620
- message: `${capability} is not supported by nextclaw compat layer yet`
1621
- });
1622
- pluginLogger.warn(`${capability} is ignored (not supported yet)`);
1623
- };
1624
- const api = {
1625
- id: pluginId,
1626
- name: record.name,
1627
- version: record.version,
1628
- description: record.description,
1838
+ const result = registerPluginWithApi({
1839
+ runtime: params.runtime,
1840
+ record,
1841
+ pluginId,
1629
1842
  source,
1630
- config: params.config,
1631
- pluginConfig: void 0,
1632
- runtime: pluginRuntime,
1633
- logger: pluginLogger,
1634
- registerTool: () => pushUnsupported("registerTool"),
1635
- registerChannel: (registration) => {
1636
- const normalizedChannel = registration && typeof registration === "object" && "plugin" in registration ? registration.plugin : registration;
1637
- if (!normalizedChannel || typeof normalizedChannel !== "object") {
1638
- params.registry.diagnostics.push({
1639
- level: "error",
1640
- pluginId,
1641
- source,
1642
- message: "channel registration missing plugin object"
1643
- });
1644
- return;
1645
- }
1646
- const channelObj = normalizedChannel;
1647
- const rawId = typeof channelObj.id === "string" ? channelObj.id : String(channelObj.id ?? "");
1648
- const accepted = ensureUniqueNames({
1649
- names: [rawId],
1650
- pluginId,
1651
- diagnostics: params.registry.diagnostics,
1652
- source,
1653
- owners: params.channelIdOwners,
1654
- reserved: params.reservedChannelIds,
1655
- kind: "channel"
1656
- });
1657
- if (accepted.length === 0) {
1658
- return;
1659
- }
1660
- const channelPlugin = normalizedChannel;
1661
- params.registry.channels.push({
1662
- pluginId,
1663
- channel: channelPlugin,
1664
- source
1665
- });
1666
- const channelId = accepted[0];
1667
- record.channelIds.push(channelId);
1668
- const configSchema = channelPlugin.configSchema;
1669
- if (configSchema && typeof configSchema === "object") {
1670
- const schema = configSchema.schema;
1671
- if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1672
- record.configJsonSchema = schema;
1673
- record.configSchema = true;
1674
- }
1675
- const uiHints = configSchema.uiHints;
1676
- if (uiHints && typeof uiHints === "object" && !Array.isArray(uiHints)) {
1677
- record.configUiHints = {
1678
- ...record.configUiHints ?? {},
1679
- ...uiHints
1680
- };
1681
- }
1682
- }
1683
- const pushChannelTools = (value, optional, sourceLabel, resolveValue) => {
1684
- const previewTools = normalizeToolList(value);
1685
- if (previewTools.length === 0) {
1686
- return;
1687
- }
1688
- const declaredNames = previewTools.map((tool) => tool.name);
1689
- const acceptedNames = ensureUniqueNames({
1690
- names: declaredNames,
1691
- pluginId,
1692
- diagnostics: params.registry.diagnostics,
1693
- source,
1694
- owners: params.toolNameOwners,
1695
- reserved: params.reservedToolNames,
1696
- kind: "tool"
1697
- });
1698
- if (acceptedNames.length === 0) {
1699
- return;
1700
- }
1701
- const factory = (ctx) => {
1702
- const tools = normalizeToolList(resolveValue(ctx));
1703
- if (tools.length === 0) {
1704
- return [];
1705
- }
1706
- const byName = new Map(tools.map((tool) => [tool.name, tool]));
1707
- return acceptedNames.map((name) => byName.get(name)).filter(Boolean);
1708
- };
1709
- params.registry.tools.push({
1710
- pluginId,
1711
- factory,
1712
- names: acceptedNames,
1713
- optional,
1714
- source
1715
- });
1716
- record.toolNames.push(...acceptedNames);
1717
- const previewByName = new Map(previewTools.map((tool) => [tool.name, tool]));
1718
- for (const name of acceptedNames) {
1719
- const resolvedTool = previewByName.get(name);
1720
- if (!resolvedTool || params.resolvedToolNames.has(resolvedTool.name)) {
1721
- continue;
1722
- }
1723
- params.resolvedToolNames.add(resolvedTool.name);
1724
- params.registry.resolvedTools.push(resolvedTool);
1725
- }
1726
- params.registry.diagnostics.push({
1727
- level: "warn",
1728
- pluginId,
1729
- source,
1730
- message: `${sourceLabel} registered channel-owned tools: ${acceptedNames.join(", ")}`
1731
- });
1732
- };
1733
- const agentTools = channelPlugin.agentTools;
1734
- if (typeof agentTools === "function") {
1735
- pushChannelTools(
1736
- normalizeToolList(agentTools()),
1737
- false,
1738
- `channel "${channelId}"`,
1739
- () => agentTools()
1740
- );
1741
- } else if (agentTools) {
1742
- pushChannelTools(
1743
- normalizeToolList(agentTools),
1744
- false,
1745
- `channel "${channelId}"`,
1746
- () => agentTools
1747
- );
1748
- }
1749
- },
1750
- registerProvider: (provider) => {
1751
- const accepted = ensureUniqueNames({
1752
- names: [provider.id],
1753
- pluginId,
1754
- diagnostics: params.registry.diagnostics,
1755
- source,
1756
- owners: params.providerIdOwners,
1757
- reserved: params.reservedProviderIds,
1758
- kind: "provider"
1759
- });
1760
- if (accepted.length === 0) {
1761
- return;
1762
- }
1763
- params.registry.providers.push({
1764
- pluginId,
1765
- provider,
1766
- source
1767
- });
1768
- record.providerIds.push(accepted[0]);
1769
- },
1770
- registerHook: () => pushUnsupported("registerHook"),
1771
- registerGatewayMethod: () => pushUnsupported("registerGatewayMethod"),
1772
- registerCli: () => pushUnsupported("registerCli"),
1773
- registerService: () => pushUnsupported("registerService"),
1774
- registerCommand: () => pushUnsupported("registerCommand"),
1775
- registerHttpHandler: () => pushUnsupported("registerHttpHandler"),
1776
- registerHttpRoute: () => pushUnsupported("registerHttpRoute"),
1777
- resolvePath: (input) => {
1778
- const trimmed = input.trim();
1779
- if (!trimmed) {
1780
- return params.workspaceDir;
1781
- }
1782
- if (path4.isAbsolute(trimmed)) {
1783
- return path4.resolve(expandHome2(trimmed));
1784
- }
1785
- return path4.resolve(params.workspaceDir, trimmed);
1786
- }
1787
- };
1788
- try {
1789
- const result = register(api);
1790
- if (result && typeof result === "object" && "then" in result) {
1791
- params.registry.diagnostics.push({
1792
- level: "warn",
1793
- pluginId,
1794
- source,
1795
- message: "plugin register returned a promise; async registration is ignored"
1796
- });
1797
- }
1798
- params.registry.plugins.push(record);
1799
- } catch (err) {
1843
+ rootDir,
1844
+ register,
1845
+ pluginConfig: void 0
1846
+ });
1847
+ if (!result.ok) {
1800
1848
  record.status = "error";
1801
- record.error = `plugin failed during register: ${String(err)}`;
1802
- params.registry.plugins.push(record);
1849
+ record.error = result.error;
1803
1850
  params.registry.diagnostics.push({
1804
1851
  level: "error",
1805
1852
  pluginId,
1806
1853
  source,
1807
- message: record.error
1854
+ message: result.error
1808
1855
  });
1809
1856
  }
1857
+ params.registry.plugins.push(record);
1810
1858
  }
1811
1859
  }
1812
1860
  function loadOpenClawPlugins(options) {
@@ -1823,26 +1871,33 @@ function loadOpenClawPlugins(options) {
1823
1871
  diagnostics: [],
1824
1872
  resolvedTools: []
1825
1873
  };
1826
- const toolNameOwners = /* @__PURE__ */ new Map();
1827
- const channelIdOwners = /* @__PURE__ */ new Map();
1828
- const providerIdOwners = /* @__PURE__ */ new Map();
1829
- const resolvedToolNames = /* @__PURE__ */ new Set();
1830
1874
  const reservedToolNames = new Set(options.reservedToolNames ?? []);
1831
1875
  const reservedChannelIds = new Set(options.reservedChannelIds ?? []);
1832
1876
  const reservedProviderIds = new Set(options.reservedProviderIds ?? []);
1833
- appendBundledChannelPlugins({
1834
- registry,
1877
+ const registerRuntime = createPluginRegisterRuntime({
1835
1878
  config: options.config,
1836
- logger,
1837
1879
  workspaceDir,
1838
- toolNameOwners,
1839
- channelIdOwners,
1840
- providerIdOwners,
1841
- resolvedToolNames,
1880
+ logger,
1881
+ registry,
1842
1882
  reservedToolNames,
1843
1883
  reservedChannelIds,
1844
1884
  reservedProviderIds
1845
1885
  });
1886
+ const pluginSdkAlias = resolvePluginSdkAlias();
1887
+ const jiti = createJiti(import.meta.url, {
1888
+ interopDefault: true,
1889
+ extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".json"],
1890
+ ...pluginSdkAlias ? {
1891
+ alias: {
1892
+ "openclaw/plugin-sdk": pluginSdkAlias
1893
+ }
1894
+ } : {}
1895
+ });
1896
+ appendBundledChannelPlugins({
1897
+ registry,
1898
+ runtime: registerRuntime,
1899
+ jiti
1900
+ });
1846
1901
  if (!loadExternalPlugins) {
1847
1902
  return registry;
1848
1903
  }
@@ -1859,17 +1914,9 @@ function loadOpenClawPlugins(options) {
1859
1914
  });
1860
1915
  registry.diagnostics.push(...manifestRegistry.diagnostics);
1861
1916
  const manifestByRoot = new Map(manifestRegistry.plugins.map((entry) => [entry.rootDir, entry]));
1862
- const seenIds = /* @__PURE__ */ new Map();
1863
- const pluginSdkAlias = resolvePluginSdkAlias();
1864
- const jiti = createJiti(import.meta.url, {
1865
- interopDefault: true,
1866
- extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".json"],
1867
- ...pluginSdkAlias ? {
1868
- alias: {
1869
- "openclaw/plugin-sdk": pluginSdkAlias
1870
- }
1871
- } : {}
1872
- });
1917
+ const seenIds = new Map(
1918
+ registry.plugins.map((entry) => [entry.id, entry.origin])
1919
+ );
1873
1920
  for (const candidate of discovery.candidates) {
1874
1921
  const manifest = manifestByRoot.get(candidate.rootDir);
1875
1922
  if (!manifest) {
@@ -2000,264 +2047,30 @@ function loadOpenClawPlugins(options) {
2000
2047
  });
2001
2048
  continue;
2002
2049
  }
2003
- const pluginRuntime = createPluginRuntime({ workspace: workspaceDir, config: options.config });
2004
- const pluginLogger = buildPluginLogger(logger, pluginId);
2005
- const pushUnsupported = (capability) => {
2006
- registry.diagnostics.push({
2007
- level: "warn",
2008
- pluginId,
2009
- source: candidate.source,
2010
- message: `${capability} is not supported by nextclaw compat layer yet`
2011
- });
2012
- pluginLogger.warn(`${capability} is ignored (not supported yet)`);
2013
- };
2014
- const api = {
2015
- id: pluginId,
2016
- name: record.name,
2017
- version: record.version,
2018
- description: record.description,
2050
+ const registerResult = registerPluginWithApi({
2051
+ runtime: registerRuntime,
2052
+ record,
2053
+ pluginId,
2019
2054
  source: candidate.source,
2020
- config: options.config,
2021
- pluginConfig: validatedConfig.value,
2022
- runtime: pluginRuntime,
2023
- logger: pluginLogger,
2024
- registerTool: (tool, opts) => {
2025
- const declaredNames = opts && Array.isArray(opts.names) ? opts.names : [];
2026
- const names = [...declaredNames, ...opts && opts.name ? [opts.name] : []];
2027
- if (typeof tool !== "function" && typeof tool.name === "string") {
2028
- names.push(tool.name);
2029
- }
2030
- const uniqueNames = Array.from(new Set(names.map((name) => name.trim()).filter(Boolean)));
2031
- const acceptedNames = ensureUniqueNames({
2032
- names: uniqueNames,
2033
- pluginId,
2034
- diagnostics: registry.diagnostics,
2035
- source: candidate.source,
2036
- owners: toolNameOwners,
2037
- reserved: reservedToolNames,
2038
- kind: "tool"
2039
- });
2040
- if (acceptedNames.length === 0) {
2041
- registry.diagnostics.push({
2042
- level: "warn",
2043
- pluginId,
2044
- source: candidate.source,
2045
- message: "tool registration skipped: no available tool names"
2046
- });
2047
- return;
2048
- }
2049
- const factory = typeof tool === "function" ? tool : (_ctx) => tool;
2050
- registry.tools.push({
2051
- pluginId,
2052
- factory,
2053
- names: acceptedNames,
2054
- optional: opts?.optional === true,
2055
- source: candidate.source
2056
- });
2057
- record.toolNames.push(...acceptedNames);
2058
- try {
2059
- const previewTools = normalizeToolList(
2060
- factory({
2061
- config: options.config,
2062
- workspaceDir,
2063
- sandboxed: false
2064
- })
2065
- );
2066
- const byName = new Map(previewTools.map((entry2) => [entry2.name, entry2]));
2067
- for (const name of acceptedNames) {
2068
- const resolvedTool = byName.get(name);
2069
- if (!resolvedTool || resolvedToolNames.has(resolvedTool.name)) {
2070
- continue;
2071
- }
2072
- resolvedToolNames.add(resolvedTool.name);
2073
- registry.resolvedTools.push(resolvedTool);
2074
- }
2075
- } catch (err) {
2076
- registry.diagnostics.push({
2077
- level: "warn",
2078
- pluginId,
2079
- source: candidate.source,
2080
- message: `tool preview failed: ${String(err)}`
2081
- });
2082
- }
2083
- },
2084
- registerChannel: (registration) => {
2085
- const normalizedChannel = registration && typeof registration === "object" && "plugin" in registration ? registration.plugin : registration;
2086
- if (!normalizedChannel || typeof normalizedChannel !== "object") {
2087
- registry.diagnostics.push({
2088
- level: "error",
2089
- pluginId,
2090
- source: candidate.source,
2091
- message: "channel registration missing plugin object"
2092
- });
2093
- return;
2094
- }
2095
- const channelObj = normalizedChannel;
2096
- const rawId = typeof channelObj.id === "string" ? channelObj.id : String(channelObj.id ?? "");
2097
- const accepted = ensureUniqueNames({
2098
- names: [rawId],
2099
- pluginId,
2100
- diagnostics: registry.diagnostics,
2101
- source: candidate.source,
2102
- owners: channelIdOwners,
2103
- reserved: reservedChannelIds,
2104
- kind: "channel"
2105
- });
2106
- if (accepted.length === 0) {
2107
- return;
2108
- }
2109
- const channelPlugin = normalizedChannel;
2110
- registry.channels.push({
2111
- pluginId,
2112
- channel: channelPlugin,
2113
- source: candidate.source
2114
- });
2115
- const channelId = accepted[0];
2116
- record.channelIds.push(channelId);
2117
- const configSchema = channelPlugin.configSchema;
2118
- if (configSchema && typeof configSchema === "object") {
2119
- const schema = configSchema.schema;
2120
- if (schema && typeof schema === "object" && !Array.isArray(schema)) {
2121
- record.configJsonSchema = schema;
2122
- record.configSchema = true;
2123
- }
2124
- const uiHints = configSchema.uiHints;
2125
- if (uiHints && typeof uiHints === "object" && !Array.isArray(uiHints)) {
2126
- record.configUiHints = {
2127
- ...record.configUiHints ?? {},
2128
- ...uiHints
2129
- };
2130
- }
2131
- }
2132
- const pushChannelTools = (value, optional, sourceLabel, resolveValue) => {
2133
- const previewTools = normalizeToolList(value);
2134
- if (previewTools.length === 0) {
2135
- return;
2136
- }
2137
- const declaredNames = previewTools.map((tool) => tool.name);
2138
- const acceptedNames = ensureUniqueNames({
2139
- names: declaredNames,
2140
- pluginId,
2141
- diagnostics: registry.diagnostics,
2142
- source: candidate.source,
2143
- owners: toolNameOwners,
2144
- reserved: reservedToolNames,
2145
- kind: "tool"
2146
- });
2147
- if (acceptedNames.length === 0) {
2148
- return;
2149
- }
2150
- const factory = (ctx) => {
2151
- const tools = normalizeToolList(resolveValue(ctx));
2152
- if (tools.length === 0) {
2153
- return [];
2154
- }
2155
- const byName = new Map(tools.map((tool) => [tool.name, tool]));
2156
- return acceptedNames.map((name) => byName.get(name)).filter(Boolean);
2157
- };
2158
- registry.tools.push({
2159
- pluginId,
2160
- factory,
2161
- names: acceptedNames,
2162
- optional,
2163
- source: candidate.source
2164
- });
2165
- record.toolNames.push(...acceptedNames);
2166
- const previewByName = new Map(previewTools.map((tool) => [tool.name, tool]));
2167
- for (const name of acceptedNames) {
2168
- const resolvedTool = previewByName.get(name);
2169
- if (!resolvedTool || resolvedToolNames.has(resolvedTool.name)) {
2170
- continue;
2171
- }
2172
- resolvedToolNames.add(resolvedTool.name);
2173
- registry.resolvedTools.push(resolvedTool);
2174
- }
2175
- registry.diagnostics.push({
2176
- level: "warn",
2177
- pluginId,
2178
- source: candidate.source,
2179
- message: `${sourceLabel} registered channel-owned tools: ${acceptedNames.join(", ")}`
2180
- });
2181
- };
2182
- const agentTools = channelPlugin.agentTools;
2183
- if (typeof agentTools === "function") {
2184
- pushChannelTools(
2185
- normalizeToolList(agentTools()),
2186
- false,
2187
- `channel "${channelId}"`,
2188
- () => agentTools()
2189
- );
2190
- } else if (agentTools) {
2191
- pushChannelTools(
2192
- normalizeToolList(agentTools),
2193
- false,
2194
- `channel "${channelId}"`,
2195
- () => agentTools
2196
- );
2197
- }
2198
- },
2199
- registerProvider: (provider) => {
2200
- const accepted = ensureUniqueNames({
2201
- names: [provider.id],
2202
- pluginId,
2203
- diagnostics: registry.diagnostics,
2204
- source: candidate.source,
2205
- owners: providerIdOwners,
2206
- reserved: reservedProviderIds,
2207
- kind: "provider"
2208
- });
2209
- if (accepted.length === 0) {
2210
- return;
2211
- }
2212
- registry.providers.push({
2213
- pluginId,
2214
- provider,
2215
- source: candidate.source
2216
- });
2217
- record.providerIds.push(accepted[0]);
2218
- },
2219
- registerHook: () => pushUnsupported("registerHook"),
2220
- registerGatewayMethod: () => pushUnsupported("registerGatewayMethod"),
2221
- registerCli: () => pushUnsupported("registerCli"),
2222
- registerService: () => pushUnsupported("registerService"),
2223
- registerCommand: () => pushUnsupported("registerCommand"),
2224
- registerHttpHandler: () => pushUnsupported("registerHttpHandler"),
2225
- registerHttpRoute: () => pushUnsupported("registerHttpRoute"),
2226
- resolvePath: (input) => {
2227
- const trimmed = input.trim();
2228
- if (!trimmed) {
2229
- return candidate.rootDir;
2230
- }
2231
- if (path4.isAbsolute(trimmed)) {
2232
- return path4.resolve(expandHome2(trimmed));
2233
- }
2234
- return path4.resolve(candidate.rootDir, trimmed);
2235
- }
2236
- };
2237
- try {
2238
- const result = register(api);
2239
- if (result && typeof result === "object" && "then" in result) {
2240
- registry.diagnostics.push({
2241
- level: "warn",
2242
- pluginId,
2243
- source: candidate.source,
2244
- message: "plugin register returned a promise; async registration is ignored"
2245
- });
2246
- }
2247
- registry.plugins.push(record);
2248
- seenIds.set(pluginId, candidate.origin);
2249
- } catch (err) {
2055
+ rootDir: candidate.rootDir,
2056
+ register,
2057
+ pluginConfig: validatedConfig.value
2058
+ });
2059
+ if (!registerResult.ok) {
2250
2060
  record.status = "error";
2251
- record.error = `plugin failed during register: ${String(err)}`;
2252
- registry.plugins.push(record);
2253
- seenIds.set(pluginId, candidate.origin);
2061
+ record.error = registerResult.error;
2254
2062
  registry.diagnostics.push({
2255
2063
  level: "error",
2256
2064
  pluginId,
2257
2065
  source: candidate.source,
2258
- message: record.error
2066
+ message: registerResult.error
2259
2067
  });
2068
+ registry.plugins.push(record);
2069
+ seenIds.set(pluginId, candidate.origin);
2070
+ continue;
2260
2071
  }
2072
+ registry.plugins.push(record);
2073
+ seenIds.set(pluginId, candidate.origin);
2261
2074
  }
2262
2075
  return registry;
2263
2076
  }
@@ -2282,7 +2095,7 @@ function buildPluginStatusReport(params) {
2282
2095
 
2283
2096
  // src/plugins/uninstall.ts
2284
2097
  import fs6 from "fs/promises";
2285
- import path5 from "path";
2098
+ import path6 from "path";
2286
2099
  function isLinkedPathInstall(record) {
2287
2100
  if (!record || record.source !== "path") {
2288
2101
  return false;
@@ -2290,7 +2103,7 @@ function isLinkedPathInstall(record) {
2290
2103
  if (!record.sourcePath || !record.installPath) {
2291
2104
  return true;
2292
2105
  }
2293
- return path5.resolve(record.sourcePath) === path5.resolve(record.installPath);
2106
+ return path6.resolve(record.sourcePath) === path6.resolve(record.installPath);
2294
2107
  }
2295
2108
  function resolveUninstallDirectoryTarget(params) {
2296
2109
  if (!params.hasInstall) {
@@ -2309,7 +2122,7 @@ function resolveUninstallDirectoryTarget(params) {
2309
2122
  if (!configuredPath) {
2310
2123
  return defaultPath;
2311
2124
  }
2312
- if (path5.resolve(configuredPath) === path5.resolve(defaultPath)) {
2125
+ if (path6.resolve(configuredPath) === path6.resolve(defaultPath)) {
2313
2126
  return configuredPath;
2314
2127
  }
2315
2128
  return defaultPath;
@@ -2435,6 +2248,8 @@ export {
2435
2248
  buildChannelConfigSchema,
2436
2249
  buildOauthProviderAuthResult,
2437
2250
  buildPluginStatusReport,
2251
+ createNextclawBuiltinChannelPlugin,
2252
+ createPluginRegisterRuntime,
2438
2253
  createPluginRuntime,
2439
2254
  disablePluginInConfig,
2440
2255
  discoverOpenClawPlugins,
@@ -2456,6 +2271,7 @@ export {
2456
2271
  normalizePluginHttpPath,
2457
2272
  normalizePluginsConfig,
2458
2273
  recordPluginInstall,
2274
+ registerPluginWithApi,
2459
2275
  removePluginFromConfig,
2460
2276
  resolveEnableState,
2461
2277
  resolvePluginChannelMessageToolHints,