@cored-im/openclaw-plugin 0.1.7 → 0.1.11

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/README.md CHANGED
@@ -93,12 +93,9 @@ openclaw gateway restart
93
93
  | `appId` | string | required | Cored application ID |
94
94
  | `appSecret` | string | required | Cored application secret |
95
95
  | `backendUrl` | string | required | Cored backend URL |
96
+ | `enabled` | boolean | `true` | Enable/disable this account |
96
97
  | `enableEncryption` | boolean | `true` | Whether to use encrypted transport |
97
98
  | `requestTimeout` | number | `30000` | API request timeout in milliseconds |
98
- | `requireMention` | boolean | `true` | In group chats, only respond when @mentioned |
99
- | `botUserId` | string | optional | Bot's own user ID (for self-message filtering) |
100
- | `inboundWhitelist` | string[] | `[]` | If non-empty, only accept messages from these user IDs |
101
- | `enabled` | boolean | `true` | Enable/disable this account |
102
99
 
103
100
  ### Environment Variables
104
101
 
@@ -107,7 +104,6 @@ The `default` account supports environment variable fallback:
107
104
  - `CORED_APP_ID`
108
105
  - `CORED_APP_SECRET`
109
106
  - `CORED_BACKEND_URL`
110
- - `CORED_REQUIRE_MENTION`
111
107
 
112
108
  ## License
113
109
 
package/README.zh.md CHANGED
@@ -93,12 +93,9 @@ openclaw gateway restart
93
93
  | `appId` | string | 必填 | 应用 ID |
94
94
  | `appSecret` | string | 必填 | 应用密钥 |
95
95
  | `backendUrl` | string | 必填 | 后端地址 |
96
+ | `enabled` | boolean | `true` | 启用/禁用该账号 |
96
97
  | `enableEncryption` | boolean | `true` | 是否启用加密传输 |
97
98
  | `requestTimeout` | number | `30000` | API 请求超时时间(毫秒) |
98
- | `requireMention` | boolean | `true` | 群聊中是否仅在被 @提及 时响应 |
99
- | `botUserId` | string | 可选 | 机器人自身的用户 ID(用于过滤自身消息) |
100
- | `inboundWhitelist` | string[] | `[]` | 非空时,仅接受这些用户 ID 的消息 |
101
- | `enabled` | boolean | `true` | 启用/禁用该账号 |
102
99
 
103
100
  ### 环境变量
104
101
 
@@ -107,7 +104,6 @@ openclaw gateway restart
107
104
  - `CORED_APP_ID`
108
105
  - `CORED_APP_SECRET`
109
106
  - `CORED_BACKEND_URL`
110
- - `CORED_REQUIRE_MENTION`
111
107
 
112
108
  ## 许可证
113
109
 
package/dist/index.cjs CHANGED
@@ -31,9 +31,7 @@ var import_core = require("openclaw/plugin-sdk/core");
31
31
  // src/config.ts
32
32
  var DEFAULTS = {
33
33
  enableEncryption: true,
34
- requestTimeout: 3e4,
35
- requireMention: true,
36
- inboundWhitelist: []
34
+ requestTimeout: 3e4
37
35
  };
38
36
  var ENV_PREFIX = "CORED_";
39
37
  function getChannelConfig(cfg) {
@@ -52,10 +50,6 @@ function readEnvConfig() {
52
50
  result.enableEncryption = env[`${ENV_PREFIX}ENABLE_ENCRYPTION`] !== "false";
53
51
  if (env[`${ENV_PREFIX}REQUEST_TIMEOUT`])
54
52
  result.requestTimeout = Number(env[`${ENV_PREFIX}REQUEST_TIMEOUT`]);
55
- if (env[`${ENV_PREFIX}REQUIRE_MENTION`] !== void 0)
56
- result.requireMention = env[`${ENV_PREFIX}REQUIRE_MENTION`] !== "false";
57
- if (env[`${ENV_PREFIX}BOT_USER_ID`])
58
- result.botUserId = env[`${ENV_PREFIX}BOT_USER_ID`];
59
53
  return result;
60
54
  }
61
55
  function listAccountIds(cfg) {
@@ -76,15 +70,12 @@ function resolveAccountConfig(cfg, accountId) {
76
70
  const raw = ch?.accounts?.[id] ?? ch;
77
71
  return {
78
72
  accountId: id,
79
- enabled: raw?.enabled ?? true,
80
73
  appId: raw?.appId ?? envConfig.appId ?? "",
81
74
  appSecret: raw?.appSecret ?? envConfig.appSecret ?? "",
82
75
  backendUrl: raw?.backendUrl ?? envConfig.backendUrl ?? "",
76
+ enabled: raw?.enabled ?? true,
83
77
  enableEncryption: raw?.enableEncryption ?? envConfig.enableEncryption ?? DEFAULTS.enableEncryption,
84
- requestTimeout: raw?.requestTimeout ?? envConfig.requestTimeout ?? DEFAULTS.requestTimeout,
85
- requireMention: raw?.requireMention ?? envConfig.requireMention ?? DEFAULTS.requireMention,
86
- botUserId: raw?.botUserId ?? envConfig.botUserId,
87
- inboundWhitelist: raw?.inboundWhitelist ?? [...DEFAULTS.inboundWhitelist]
78
+ requestTimeout: raw?.requestTimeout ?? envConfig.requestTimeout ?? DEFAULTS.requestTimeout
88
79
  };
89
80
  }
90
81
  function validateAccountConfig(config) {
@@ -336,171 +327,177 @@ function parseTarget(to) {
336
327
  }
337
328
 
338
329
  // src/channel.ts
339
- function resolveAccount(cfg, accountId) {
340
- const section = cfg.channels?.["cored"];
341
- const accounts = section?.accounts;
342
- const defaultAccount = section?.defaultAccount;
343
- if (accounts && Object.keys(accounts).length > 0) {
344
- const targetId = accountId ?? defaultAccount ?? Object.keys(accounts)[0];
345
- const account = accounts[targetId];
346
- if (!account) {
347
- throw new Error(`cored: account "${targetId}" not found`);
348
- }
349
- return {
350
- accountId: targetId,
351
- appId: account.appId,
352
- appSecret: account.appSecret,
353
- backendUrl: account.backendUrl,
354
- enableEncryption: account.enableEncryption ?? section.enableEncryption ?? true,
355
- requestTimeout: account.requestTimeout ?? section.requestTimeout ?? 3e4,
356
- requireMention: account.requireMention ?? section.requireMention ?? true
357
- };
358
- }
359
- const appId = section?.appId;
360
- const appSecret = section?.appSecret;
361
- const backendUrl = section?.backendUrl;
362
- if (!appId || !appSecret || !backendUrl) {
363
- throw new Error("cored: appId, appSecret, and backendUrl are required");
364
- }
365
- return {
366
- accountId: null,
367
- appId,
368
- appSecret,
369
- backendUrl,
370
- enableEncryption: section?.enableEncryption ?? true,
371
- requestTimeout: section?.requestTimeout ?? 3e4,
372
- requireMention: section?.requireMention ?? true
373
- };
374
- }
375
- var coredPlugin = (0, import_core.createChatChannelPlugin)({
376
- base: (0, import_core.createChannelPluginBase)({
377
- id: "cored",
378
- setup: {
379
- resolveAccount,
380
- inspectAccount(cfg, accountId) {
381
- const section = cfg.channels?.["cored"];
382
- const hasConfig = Boolean(
383
- section?.appId && section?.appSecret && section?.backendUrl
384
- );
385
- return {
386
- enabled: Boolean(section?.enabled !== false),
387
- configured: hasConfig,
388
- tokenStatus: hasConfig ? "available" : "missing"
389
- };
390
- }
391
- }
392
- }),
393
- // Plugin metadata
330
+ var base = (0, import_core.createChannelPluginBase)({
331
+ id: "cored",
394
332
  meta: {
395
333
  id: "cored",
396
334
  label: "Cored",
397
335
  selectionLabel: "Cored",
398
336
  docsPath: "/channels/cored",
399
337
  blurb: "Connect OpenClaw to Cored",
400
- aliases: ["cored", "co"]
338
+ aliases: ["co"]
401
339
  },
402
- // Capabilities
403
340
  capabilities: {
404
341
  chatTypes: ["direct", "group"]
405
342
  },
406
- // Config
407
343
  config: {
408
344
  listAccountIds: (cfg) => listAccountIds(cfg),
409
- resolveAccount: (cfg, accountId) => resolveAccountConfig(cfg, accountId)
345
+ resolveAccount: (cfg, accountId) => resolveAccountConfig(cfg, accountId ?? void 0),
346
+ inspectAccount(cfg, accountId) {
347
+ const resolved = resolveAccountConfig(cfg, accountId ?? void 0);
348
+ const hasConfig = Boolean(
349
+ resolved.appId && resolved.appSecret && resolved.backendUrl
350
+ );
351
+ return {
352
+ enabled: resolved.enabled,
353
+ configured: hasConfig,
354
+ tokenStatus: hasConfig ? "available" : "missing"
355
+ };
356
+ }
410
357
  },
411
- // Outbound messaging
412
- outbound: {
413
- deliveryMode: "direct",
414
- resolveTarget: ({ to }) => {
415
- const target = parseTarget(to);
416
- if (!target) {
417
- return {
418
- ok: false,
419
- error: new Error(
420
- `Cored requires --to <user:ID|chat:ID>, got: ${JSON.stringify(to)}`
421
- )
422
- };
423
- }
424
- return { ok: true, to: `${target.kind}:${target.id}` };
425
- },
426
- sendText: async ({
427
- to,
428
- text,
429
- accountId
430
- }) => {
431
- const target = parseTarget(to);
432
- if (!target) {
433
- return {
434
- ok: false,
435
- error: new Error(`[cored] invalid send target: ${to}`)
436
- };
358
+ setup: {
359
+ applyAccountConfig: ({ cfg, accountId, input }) => {
360
+ const updated = structuredClone(cfg);
361
+ if (!updated.channels) updated.channels = {};
362
+ const ch = updated.channels;
363
+ if (!ch["cored"]) ch["cored"] = {};
364
+ const section = ch["cored"];
365
+ const appId = input.appToken;
366
+ const appSecret = input.token;
367
+ const backendUrl = input.url;
368
+ if (accountId && accountId !== "default") {
369
+ if (!section.accounts) section.accounts = {};
370
+ const accounts = section.accounts;
371
+ if (!accounts[accountId]) accounts[accountId] = {};
372
+ const account = accounts[accountId];
373
+ if (appId) account.appId = appId;
374
+ if (appSecret) account.appSecret = appSecret;
375
+ if (backendUrl) account.backendUrl = backendUrl;
376
+ } else {
377
+ if (appId) section.appId = appId;
378
+ if (appSecret) section.appSecret = appSecret;
379
+ if (backendUrl) section.backendUrl = backendUrl;
437
380
  }
438
- return sendText(target.id, text, accountId);
381
+ return updated;
439
382
  }
440
383
  },
441
- // Setup wizard for openclaw onboard
442
384
  setupWizard: {
443
385
  channel: "cored",
444
386
  status: {
445
387
  configuredLabel: "Connected",
446
388
  unconfiguredLabel: "Not configured",
447
389
  resolveConfigured: ({ cfg }) => {
448
- const section = cfg.channels?.["cored"];
449
- return Boolean(section?.appId && section?.appSecret && section?.backendUrl);
390
+ const ids = listAccountIds(cfg);
391
+ return ids.some((id) => {
392
+ const resolved = resolveAccountConfig(cfg, id);
393
+ return Boolean(resolved.appId && resolved.appSecret && resolved.backendUrl);
394
+ });
450
395
  }
451
396
  },
452
397
  credentials: [
453
398
  {
454
- inputKey: "appId",
399
+ inputKey: "appToken",
455
400
  providerHint: "cored",
456
401
  credentialLabel: "App ID",
457
402
  preferredEnvVar: "CORED_APP_ID",
458
403
  envPrompt: "Use CORED_APP_ID from environment?",
459
404
  keepPrompt: "Keep current App ID?",
460
405
  inputPrompt: "Enter your Cored App ID:",
461
- inspect: ({ cfg }) => {
462
- const section = cfg.channels?.["cored"];
406
+ inspect: ({ cfg, accountId }) => {
407
+ const resolved = resolveAccountConfig(cfg, accountId ?? void 0);
463
408
  return {
464
- accountConfigured: Boolean(section?.appId),
465
- hasConfiguredValue: Boolean(section?.appId)
409
+ accountConfigured: Boolean(resolved.appId),
410
+ hasConfiguredValue: Boolean(resolved.appId)
466
411
  };
467
412
  }
468
413
  },
469
414
  {
470
- inputKey: "appSecret",
415
+ inputKey: "token",
471
416
  providerHint: "cored",
472
417
  credentialLabel: "App Secret",
473
418
  preferredEnvVar: "CORED_APP_SECRET",
474
419
  envPrompt: "Use CORED_APP_SECRET from environment?",
475
420
  keepPrompt: "Keep current App Secret?",
476
421
  inputPrompt: "Enter your Cored App Secret:",
477
- inspect: ({ cfg }) => {
478
- const section = cfg.channels?.["cored"];
422
+ inspect: ({ cfg, accountId }) => {
423
+ const resolved = resolveAccountConfig(cfg, accountId ?? void 0);
479
424
  return {
480
- accountConfigured: Boolean(section?.appSecret),
481
- hasConfiguredValue: Boolean(section?.appSecret)
425
+ accountConfigured: Boolean(resolved.appSecret),
426
+ hasConfiguredValue: Boolean(resolved.appSecret)
482
427
  };
483
428
  }
484
429
  },
485
430
  {
486
- inputKey: "backendUrl",
431
+ inputKey: "url",
487
432
  providerHint: "cored",
488
433
  credentialLabel: "Backend URL",
489
434
  preferredEnvVar: "CORED_BACKEND_URL",
490
435
  envPrompt: "Use CORED_BACKEND_URL from environment?",
491
436
  keepPrompt: "Keep current Backend URL?",
492
437
  inputPrompt: "Enter your Cored backend server URL:",
493
- inspect: ({ cfg }) => {
494
- const section = cfg.channels?.["cored"];
438
+ inspect: ({ cfg, accountId }) => {
439
+ const resolved = resolveAccountConfig(cfg, accountId ?? void 0);
495
440
  return {
496
- accountConfigured: Boolean(section?.backendUrl),
497
- hasConfiguredValue: Boolean(section?.backendUrl)
441
+ accountConfigured: Boolean(resolved.backendUrl),
442
+ hasConfiguredValue: Boolean(resolved.backendUrl)
498
443
  };
499
444
  }
500
445
  }
501
446
  ]
502
447
  }
503
448
  });
449
+ var coredPlugin = (0, import_core.createChatChannelPlugin)({
450
+ base,
451
+ // DM security: who can message the bot
452
+ security: {
453
+ dm: {
454
+ channelKey: "cored",
455
+ resolvePolicy: () => void 0,
456
+ resolveAllowFrom: () => [],
457
+ defaultPolicy: "allowlist"
458
+ }
459
+ },
460
+ // Threading: how replies are delivered
461
+ threading: { topLevelReplyToMode: "reply" },
462
+ // Outbound: send messages to the platform
463
+ outbound: {
464
+ attachedResults: {
465
+ channel: "cored",
466
+ sendText: async (ctx) => {
467
+ const target = parseTarget(ctx.to);
468
+ if (!target) {
469
+ throw new Error(`[cored] invalid send target: ${ctx.to}`);
470
+ }
471
+ const result = await sendText(
472
+ target.id,
473
+ ctx.text,
474
+ ctx.accountId ?? void 0,
475
+ ctx.replyToId ?? void 0
476
+ );
477
+ if (!result.ok) {
478
+ throw result.error ?? new Error("[cored] send failed");
479
+ }
480
+ return { messageId: result.messageId ?? "" };
481
+ }
482
+ },
483
+ base: {
484
+ deliveryMode: "direct",
485
+ resolveTarget: ({ to }) => {
486
+ if (!to) return { ok: false, error: new Error("[cored] --to is required") };
487
+ const target = parseTarget(to);
488
+ if (!target) {
489
+ return {
490
+ ok: false,
491
+ error: new Error(
492
+ `Cored requires --to <user:ID|chat:ID>, got: ${JSON.stringify(to)}`
493
+ )
494
+ };
495
+ }
496
+ return { ok: true, to: `${target.kind}:${target.id}` };
497
+ }
498
+ }
499
+ }
500
+ });
504
501
 
505
502
  // src/messaging/inbound.ts
506
503
  function parseMessageEvent(event) {
@@ -549,23 +546,8 @@ function extractTextBody(msg) {
549
546
  return null;
550
547
  }
551
548
  function checkMessageGate(msg, account) {
552
- if (account.botUserId && msg.senderId === account.botUserId) {
553
- return { pass: false, reason: "self-message" };
554
- }
555
- if (account.inboundWhitelist.length > 0) {
556
- if (!account.inboundWhitelist.includes(msg.senderId)) {
557
- return { pass: false, reason: "sender-not-in-whitelist" };
558
- }
559
- }
560
- if (msg.chatType === "group" && account.requireMention && !isBotMentioned(msg, account)) {
561
- return { pass: false, reason: "group-no-mention" };
562
- }
563
549
  return { pass: true };
564
550
  }
565
- function isBotMentioned(msg, account) {
566
- if (!account.botUserId) return false;
567
- return msg.mentionUserIds.includes(account.botUserId);
568
- }
569
551
  var DEDUP_TTL_MS = 5 * 60 * 1e3;
570
552
  var DEDUP_CLEANUP_INTERVAL_MS = 60 * 1e3;
571
553
  var DEDUP_MAX_SIZE = 1e4;
@@ -600,7 +582,7 @@ function buildContext(msg, account) {
600
582
  return {
601
583
  Body: msg.body,
602
584
  From: isGroup ? `cored:chat:${msg.chatId}` : `cored:user:${msg.senderId}`,
603
- To: `cored:bot:${account.botUserId ?? account.appId}`,
585
+ To: `cored:bot:${account.appId}`,
604
586
  SessionKey: sessionKey,
605
587
  AccountId: account.accountId,
606
588
  ChatType: isGroup ? "group" : "direct",
@@ -716,43 +698,42 @@ var index_default = (0, import_core2.defineChannelPluginEntry)({
716
698
  description: "Connect OpenClaw with Cored",
717
699
  plugin: coredPlugin,
718
700
  registerFull(api) {
719
- const typedApi = api;
720
- typedApi.registerService({
701
+ api.registerService({
721
702
  id: "cored-sdk",
722
703
  start: async () => {
723
704
  if (clientCount() > 0) return;
724
- const accounts = listEnabledAccountConfigs(typedApi.config);
705
+ const accounts = listEnabledAccountConfigs(api.config);
725
706
  if (accounts.length === 0) {
726
- typedApi.logger?.warn?.("[cored] no enabled account config found \u2014 service idle");
707
+ api.logger?.warn?.("[cored] no enabled account config found \u2014 service idle");
727
708
  return;
728
709
  }
729
710
  for (const account of accounts) {
730
711
  const errors = validateAccountConfig(account);
731
712
  if (errors.length > 0) {
732
- typedApi.logger?.warn?.(
713
+ api.logger?.warn?.(
733
714
  `[cored] skipping account=${account.accountId}: ${errors.map((e) => e.message).join("; ")}`
734
715
  );
735
716
  continue;
736
717
  }
737
718
  try {
738
- await startAccount(typedApi, account);
739
- typedApi.logger?.info?.(
719
+ await startAccount(api, account);
720
+ api.logger?.info?.(
740
721
  `[cored] account=${account.accountId} connected (appId=${account.appId})`
741
722
  );
742
723
  } catch (err) {
743
- typedApi.logger?.error?.(
724
+ api.logger?.error?.(
744
725
  `[cored] account=${account.accountId} failed to start: ${err instanceof Error ? err.message : String(err)}`
745
726
  );
746
727
  }
747
728
  }
748
- typedApi.logger?.info?.(`[cored] service started with ${clientCount()} account(s)`);
729
+ api.logger?.info?.(`[cored] service started with ${clientCount()} account(s)`);
749
730
  },
750
731
  stop: async () => {
751
732
  await destroyAllClients();
752
- typedApi.logger?.info?.("[cored] service stopped \u2014 all clients disconnected");
733
+ api.logger?.info?.("[cored] service stopped \u2014 all clients disconnected");
753
734
  }
754
735
  });
755
- typedApi.logger?.info?.("[cored] plugin registered");
736
+ api.logger?.info?.("[cored] plugin registered");
756
737
  }
757
738
  });
758
739
  async function startAccount(api, account) {