@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/dist/index.js CHANGED
@@ -10,9 +10,7 @@ import {
10
10
  // src/config.ts
11
11
  var DEFAULTS = {
12
12
  enableEncryption: true,
13
- requestTimeout: 3e4,
14
- requireMention: true,
15
- inboundWhitelist: []
13
+ requestTimeout: 3e4
16
14
  };
17
15
  var ENV_PREFIX = "CORED_";
18
16
  function getChannelConfig(cfg) {
@@ -31,10 +29,6 @@ function readEnvConfig() {
31
29
  result.enableEncryption = env[`${ENV_PREFIX}ENABLE_ENCRYPTION`] !== "false";
32
30
  if (env[`${ENV_PREFIX}REQUEST_TIMEOUT`])
33
31
  result.requestTimeout = Number(env[`${ENV_PREFIX}REQUEST_TIMEOUT`]);
34
- if (env[`${ENV_PREFIX}REQUIRE_MENTION`] !== void 0)
35
- result.requireMention = env[`${ENV_PREFIX}REQUIRE_MENTION`] !== "false";
36
- if (env[`${ENV_PREFIX}BOT_USER_ID`])
37
- result.botUserId = env[`${ENV_PREFIX}BOT_USER_ID`];
38
32
  return result;
39
33
  }
40
34
  function listAccountIds(cfg) {
@@ -55,15 +49,12 @@ function resolveAccountConfig(cfg, accountId) {
55
49
  const raw = ch?.accounts?.[id] ?? ch;
56
50
  return {
57
51
  accountId: id,
58
- enabled: raw?.enabled ?? true,
59
52
  appId: raw?.appId ?? envConfig.appId ?? "",
60
53
  appSecret: raw?.appSecret ?? envConfig.appSecret ?? "",
61
54
  backendUrl: raw?.backendUrl ?? envConfig.backendUrl ?? "",
55
+ enabled: raw?.enabled ?? true,
62
56
  enableEncryption: raw?.enableEncryption ?? envConfig.enableEncryption ?? DEFAULTS.enableEncryption,
63
- requestTimeout: raw?.requestTimeout ?? envConfig.requestTimeout ?? DEFAULTS.requestTimeout,
64
- requireMention: raw?.requireMention ?? envConfig.requireMention ?? DEFAULTS.requireMention,
65
- botUserId: raw?.botUserId ?? envConfig.botUserId,
66
- inboundWhitelist: raw?.inboundWhitelist ?? [...DEFAULTS.inboundWhitelist]
57
+ requestTimeout: raw?.requestTimeout ?? envConfig.requestTimeout ?? DEFAULTS.requestTimeout
67
58
  };
68
59
  }
69
60
  function validateAccountConfig(config) {
@@ -315,171 +306,177 @@ function parseTarget(to) {
315
306
  }
316
307
 
317
308
  // src/channel.ts
318
- function resolveAccount(cfg, accountId) {
319
- const section = cfg.channels?.["cored"];
320
- const accounts = section?.accounts;
321
- const defaultAccount = section?.defaultAccount;
322
- if (accounts && Object.keys(accounts).length > 0) {
323
- const targetId = accountId ?? defaultAccount ?? Object.keys(accounts)[0];
324
- const account = accounts[targetId];
325
- if (!account) {
326
- throw new Error(`cored: account "${targetId}" not found`);
327
- }
328
- return {
329
- accountId: targetId,
330
- appId: account.appId,
331
- appSecret: account.appSecret,
332
- backendUrl: account.backendUrl,
333
- enableEncryption: account.enableEncryption ?? section.enableEncryption ?? true,
334
- requestTimeout: account.requestTimeout ?? section.requestTimeout ?? 3e4,
335
- requireMention: account.requireMention ?? section.requireMention ?? true
336
- };
337
- }
338
- const appId = section?.appId;
339
- const appSecret = section?.appSecret;
340
- const backendUrl = section?.backendUrl;
341
- if (!appId || !appSecret || !backendUrl) {
342
- throw new Error("cored: appId, appSecret, and backendUrl are required");
343
- }
344
- return {
345
- accountId: null,
346
- appId,
347
- appSecret,
348
- backendUrl,
349
- enableEncryption: section?.enableEncryption ?? true,
350
- requestTimeout: section?.requestTimeout ?? 3e4,
351
- requireMention: section?.requireMention ?? true
352
- };
353
- }
354
- var coredPlugin = createChatChannelPlugin({
355
- base: createChannelPluginBase({
356
- id: "cored",
357
- setup: {
358
- resolveAccount,
359
- inspectAccount(cfg, accountId) {
360
- const section = cfg.channels?.["cored"];
361
- const hasConfig = Boolean(
362
- section?.appId && section?.appSecret && section?.backendUrl
363
- );
364
- return {
365
- enabled: Boolean(section?.enabled !== false),
366
- configured: hasConfig,
367
- tokenStatus: hasConfig ? "available" : "missing"
368
- };
369
- }
370
- }
371
- }),
372
- // Plugin metadata
309
+ var base = createChannelPluginBase({
310
+ id: "cored",
373
311
  meta: {
374
312
  id: "cored",
375
313
  label: "Cored",
376
314
  selectionLabel: "Cored",
377
315
  docsPath: "/channels/cored",
378
316
  blurb: "Connect OpenClaw to Cored",
379
- aliases: ["cored", "co"]
317
+ aliases: ["co"]
380
318
  },
381
- // Capabilities
382
319
  capabilities: {
383
320
  chatTypes: ["direct", "group"]
384
321
  },
385
- // Config
386
322
  config: {
387
323
  listAccountIds: (cfg) => listAccountIds(cfg),
388
- resolveAccount: (cfg, accountId) => resolveAccountConfig(cfg, accountId)
324
+ resolveAccount: (cfg, accountId) => resolveAccountConfig(cfg, accountId ?? void 0),
325
+ inspectAccount(cfg, accountId) {
326
+ const resolved = resolveAccountConfig(cfg, accountId ?? void 0);
327
+ const hasConfig = Boolean(
328
+ resolved.appId && resolved.appSecret && resolved.backendUrl
329
+ );
330
+ return {
331
+ enabled: resolved.enabled,
332
+ configured: hasConfig,
333
+ tokenStatus: hasConfig ? "available" : "missing"
334
+ };
335
+ }
389
336
  },
390
- // Outbound messaging
391
- outbound: {
392
- deliveryMode: "direct",
393
- resolveTarget: ({ to }) => {
394
- const target = parseTarget(to);
395
- if (!target) {
396
- return {
397
- ok: false,
398
- error: new Error(
399
- `Cored requires --to <user:ID|chat:ID>, got: ${JSON.stringify(to)}`
400
- )
401
- };
402
- }
403
- return { ok: true, to: `${target.kind}:${target.id}` };
404
- },
405
- sendText: async ({
406
- to,
407
- text,
408
- accountId
409
- }) => {
410
- const target = parseTarget(to);
411
- if (!target) {
412
- return {
413
- ok: false,
414
- error: new Error(`[cored] invalid send target: ${to}`)
415
- };
337
+ setup: {
338
+ applyAccountConfig: ({ cfg, accountId, input }) => {
339
+ const updated = structuredClone(cfg);
340
+ if (!updated.channels) updated.channels = {};
341
+ const ch = updated.channels;
342
+ if (!ch["cored"]) ch["cored"] = {};
343
+ const section = ch["cored"];
344
+ const appId = input.appToken;
345
+ const appSecret = input.token;
346
+ const backendUrl = input.url;
347
+ if (accountId && accountId !== "default") {
348
+ if (!section.accounts) section.accounts = {};
349
+ const accounts = section.accounts;
350
+ if (!accounts[accountId]) accounts[accountId] = {};
351
+ const account = accounts[accountId];
352
+ if (appId) account.appId = appId;
353
+ if (appSecret) account.appSecret = appSecret;
354
+ if (backendUrl) account.backendUrl = backendUrl;
355
+ } else {
356
+ if (appId) section.appId = appId;
357
+ if (appSecret) section.appSecret = appSecret;
358
+ if (backendUrl) section.backendUrl = backendUrl;
416
359
  }
417
- return sendText(target.id, text, accountId);
360
+ return updated;
418
361
  }
419
362
  },
420
- // Setup wizard for openclaw onboard
421
363
  setupWizard: {
422
364
  channel: "cored",
423
365
  status: {
424
366
  configuredLabel: "Connected",
425
367
  unconfiguredLabel: "Not configured",
426
368
  resolveConfigured: ({ cfg }) => {
427
- const section = cfg.channels?.["cored"];
428
- return Boolean(section?.appId && section?.appSecret && section?.backendUrl);
369
+ const ids = listAccountIds(cfg);
370
+ return ids.some((id) => {
371
+ const resolved = resolveAccountConfig(cfg, id);
372
+ return Boolean(resolved.appId && resolved.appSecret && resolved.backendUrl);
373
+ });
429
374
  }
430
375
  },
431
376
  credentials: [
432
377
  {
433
- inputKey: "appId",
378
+ inputKey: "appToken",
434
379
  providerHint: "cored",
435
380
  credentialLabel: "App ID",
436
381
  preferredEnvVar: "CORED_APP_ID",
437
382
  envPrompt: "Use CORED_APP_ID from environment?",
438
383
  keepPrompt: "Keep current App ID?",
439
384
  inputPrompt: "Enter your Cored App ID:",
440
- inspect: ({ cfg }) => {
441
- const section = cfg.channels?.["cored"];
385
+ inspect: ({ cfg, accountId }) => {
386
+ const resolved = resolveAccountConfig(cfg, accountId ?? void 0);
442
387
  return {
443
- accountConfigured: Boolean(section?.appId),
444
- hasConfiguredValue: Boolean(section?.appId)
388
+ accountConfigured: Boolean(resolved.appId),
389
+ hasConfiguredValue: Boolean(resolved.appId)
445
390
  };
446
391
  }
447
392
  },
448
393
  {
449
- inputKey: "appSecret",
394
+ inputKey: "token",
450
395
  providerHint: "cored",
451
396
  credentialLabel: "App Secret",
452
397
  preferredEnvVar: "CORED_APP_SECRET",
453
398
  envPrompt: "Use CORED_APP_SECRET from environment?",
454
399
  keepPrompt: "Keep current App Secret?",
455
400
  inputPrompt: "Enter your Cored App Secret:",
456
- inspect: ({ cfg }) => {
457
- const section = cfg.channels?.["cored"];
401
+ inspect: ({ cfg, accountId }) => {
402
+ const resolved = resolveAccountConfig(cfg, accountId ?? void 0);
458
403
  return {
459
- accountConfigured: Boolean(section?.appSecret),
460
- hasConfiguredValue: Boolean(section?.appSecret)
404
+ accountConfigured: Boolean(resolved.appSecret),
405
+ hasConfiguredValue: Boolean(resolved.appSecret)
461
406
  };
462
407
  }
463
408
  },
464
409
  {
465
- inputKey: "backendUrl",
410
+ inputKey: "url",
466
411
  providerHint: "cored",
467
412
  credentialLabel: "Backend URL",
468
413
  preferredEnvVar: "CORED_BACKEND_URL",
469
414
  envPrompt: "Use CORED_BACKEND_URL from environment?",
470
415
  keepPrompt: "Keep current Backend URL?",
471
416
  inputPrompt: "Enter your Cored backend server URL:",
472
- inspect: ({ cfg }) => {
473
- const section = cfg.channels?.["cored"];
417
+ inspect: ({ cfg, accountId }) => {
418
+ const resolved = resolveAccountConfig(cfg, accountId ?? void 0);
474
419
  return {
475
- accountConfigured: Boolean(section?.backendUrl),
476
- hasConfiguredValue: Boolean(section?.backendUrl)
420
+ accountConfigured: Boolean(resolved.backendUrl),
421
+ hasConfiguredValue: Boolean(resolved.backendUrl)
477
422
  };
478
423
  }
479
424
  }
480
425
  ]
481
426
  }
482
427
  });
428
+ var coredPlugin = createChatChannelPlugin({
429
+ base,
430
+ // DM security: who can message the bot
431
+ security: {
432
+ dm: {
433
+ channelKey: "cored",
434
+ resolvePolicy: () => void 0,
435
+ resolveAllowFrom: () => [],
436
+ defaultPolicy: "allowlist"
437
+ }
438
+ },
439
+ // Threading: how replies are delivered
440
+ threading: { topLevelReplyToMode: "reply" },
441
+ // Outbound: send messages to the platform
442
+ outbound: {
443
+ attachedResults: {
444
+ channel: "cored",
445
+ sendText: async (ctx) => {
446
+ const target = parseTarget(ctx.to);
447
+ if (!target) {
448
+ throw new Error(`[cored] invalid send target: ${ctx.to}`);
449
+ }
450
+ const result = await sendText(
451
+ target.id,
452
+ ctx.text,
453
+ ctx.accountId ?? void 0,
454
+ ctx.replyToId ?? void 0
455
+ );
456
+ if (!result.ok) {
457
+ throw result.error ?? new Error("[cored] send failed");
458
+ }
459
+ return { messageId: result.messageId ?? "" };
460
+ }
461
+ },
462
+ base: {
463
+ deliveryMode: "direct",
464
+ resolveTarget: ({ to }) => {
465
+ if (!to) return { ok: false, error: new Error("[cored] --to is required") };
466
+ const target = parseTarget(to);
467
+ if (!target) {
468
+ return {
469
+ ok: false,
470
+ error: new Error(
471
+ `Cored requires --to <user:ID|chat:ID>, got: ${JSON.stringify(to)}`
472
+ )
473
+ };
474
+ }
475
+ return { ok: true, to: `${target.kind}:${target.id}` };
476
+ }
477
+ }
478
+ }
479
+ });
483
480
 
484
481
  // src/messaging/inbound.ts
485
482
  function parseMessageEvent(event) {
@@ -528,23 +525,8 @@ function extractTextBody(msg) {
528
525
  return null;
529
526
  }
530
527
  function checkMessageGate(msg, account) {
531
- if (account.botUserId && msg.senderId === account.botUserId) {
532
- return { pass: false, reason: "self-message" };
533
- }
534
- if (account.inboundWhitelist.length > 0) {
535
- if (!account.inboundWhitelist.includes(msg.senderId)) {
536
- return { pass: false, reason: "sender-not-in-whitelist" };
537
- }
538
- }
539
- if (msg.chatType === "group" && account.requireMention && !isBotMentioned(msg, account)) {
540
- return { pass: false, reason: "group-no-mention" };
541
- }
542
528
  return { pass: true };
543
529
  }
544
- function isBotMentioned(msg, account) {
545
- if (!account.botUserId) return false;
546
- return msg.mentionUserIds.includes(account.botUserId);
547
- }
548
530
  var DEDUP_TTL_MS = 5 * 60 * 1e3;
549
531
  var DEDUP_CLEANUP_INTERVAL_MS = 60 * 1e3;
550
532
  var DEDUP_MAX_SIZE = 1e4;
@@ -579,7 +561,7 @@ function buildContext(msg, account) {
579
561
  return {
580
562
  Body: msg.body,
581
563
  From: isGroup ? `cored:chat:${msg.chatId}` : `cored:user:${msg.senderId}`,
582
- To: `cored:bot:${account.botUserId ?? account.appId}`,
564
+ To: `cored:bot:${account.appId}`,
583
565
  SessionKey: sessionKey,
584
566
  AccountId: account.accountId,
585
567
  ChatType: isGroup ? "group" : "direct",
@@ -695,43 +677,42 @@ var index_default = defineChannelPluginEntry({
695
677
  description: "Connect OpenClaw with Cored",
696
678
  plugin: coredPlugin,
697
679
  registerFull(api) {
698
- const typedApi = api;
699
- typedApi.registerService({
680
+ api.registerService({
700
681
  id: "cored-sdk",
701
682
  start: async () => {
702
683
  if (clientCount() > 0) return;
703
- const accounts = listEnabledAccountConfigs(typedApi.config);
684
+ const accounts = listEnabledAccountConfigs(api.config);
704
685
  if (accounts.length === 0) {
705
- typedApi.logger?.warn?.("[cored] no enabled account config found \u2014 service idle");
686
+ api.logger?.warn?.("[cored] no enabled account config found \u2014 service idle");
706
687
  return;
707
688
  }
708
689
  for (const account of accounts) {
709
690
  const errors = validateAccountConfig(account);
710
691
  if (errors.length > 0) {
711
- typedApi.logger?.warn?.(
692
+ api.logger?.warn?.(
712
693
  `[cored] skipping account=${account.accountId}: ${errors.map((e) => e.message).join("; ")}`
713
694
  );
714
695
  continue;
715
696
  }
716
697
  try {
717
- await startAccount(typedApi, account);
718
- typedApi.logger?.info?.(
698
+ await startAccount(api, account);
699
+ api.logger?.info?.(
719
700
  `[cored] account=${account.accountId} connected (appId=${account.appId})`
720
701
  );
721
702
  } catch (err) {
722
- typedApi.logger?.error?.(
703
+ api.logger?.error?.(
723
704
  `[cored] account=${account.accountId} failed to start: ${err instanceof Error ? err.message : String(err)}`
724
705
  );
725
706
  }
726
707
  }
727
- typedApi.logger?.info?.(`[cored] service started with ${clientCount()} account(s)`);
708
+ api.logger?.info?.(`[cored] service started with ${clientCount()} account(s)`);
728
709
  },
729
710
  stop: async () => {
730
711
  await destroyAllClients();
731
- typedApi.logger?.info?.("[cored] service stopped \u2014 all clients disconnected");
712
+ api.logger?.info?.("[cored] service stopped \u2014 all clients disconnected");
732
713
  }
733
714
  });
734
- typedApi.logger?.info?.("[cored] plugin registered");
715
+ api.logger?.info?.("[cored] plugin registered");
735
716
  }
736
717
  });
737
718
  async function startAccount(api, account) {