@cored-im/openclaw-plugin 0.1.4 → 0.1.5

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/src/channel.ts CHANGED
@@ -1,28 +1,113 @@
1
1
  // Copyright (c) 2026 Cored Limited
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
+ import {
5
+ createChatChannelPlugin,
6
+ createChannelPluginBase,
7
+ } from "openclaw/plugin-sdk/core";
8
+ import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
4
9
  import { listAccountIds, resolveAccountConfig } from "./config.js";
5
10
  import { sendText } from "./messaging/outbound.js";
6
11
  import { parseTarget } from "./targets.js";
7
12
 
8
- export const coredPlugin = {
9
- id: "cored",
13
+ type ResolvedAccount = {
14
+ accountId: string | null;
15
+ appId: string;
16
+ appSecret: string;
17
+ backendUrl: string;
18
+ enableEncryption: boolean;
19
+ requestTimeout: number;
20
+ requireMention: boolean;
21
+ };
22
+
23
+ function resolveAccount(
24
+ cfg: OpenClawConfig,
25
+ accountId?: string | null,
26
+ ): ResolvedAccount {
27
+ const section = (cfg.channels as Record<string, any>)?.["cored"];
28
+ const accounts = section?.accounts;
29
+ const defaultAccount = section?.defaultAccount;
30
+
31
+ // If multi-account mode, resolve the specific account
32
+ if (accounts && Object.keys(accounts).length > 0) {
33
+ const targetId = accountId ?? defaultAccount ?? Object.keys(accounts)[0];
34
+ const account = accounts[targetId];
35
+ if (!account) {
36
+ throw new Error(`cored: account "${targetId}" not found`);
37
+ }
38
+ return {
39
+ accountId: targetId,
40
+ appId: account.appId,
41
+ appSecret: account.appSecret,
42
+ backendUrl: account.backendUrl,
43
+ enableEncryption: account.enableEncryption ?? section.enableEncryption ?? true,
44
+ requestTimeout: account.requestTimeout ?? section.requestTimeout ?? 30000,
45
+ requireMention: account.requireMention ?? section.requireMention ?? true,
46
+ };
47
+ }
48
+
49
+ // Single-account mode
50
+ const appId = section?.appId;
51
+ const appSecret = section?.appSecret;
52
+ const backendUrl = section?.backendUrl;
53
+
54
+ if (!appId || !appSecret || !backendUrl) {
55
+ throw new Error("cored: appId, appSecret, and backendUrl are required");
56
+ }
57
+
58
+ return {
59
+ accountId: null,
60
+ appId,
61
+ appSecret,
62
+ backendUrl,
63
+ enableEncryption: section?.enableEncryption ?? true,
64
+ requestTimeout: section?.requestTimeout ?? 30000,
65
+ requireMention: section?.requireMention ?? true,
66
+ };
67
+ }
68
+
69
+ export const coredPlugin = createChatChannelPlugin<ResolvedAccount>({
70
+ base: createChannelPluginBase({
71
+ id: "cored",
72
+ setup: {
73
+ resolveAccount,
74
+ inspectAccount(cfg, accountId) {
75
+ const section = (cfg.channels as Record<string, any>)?.["cored"];
76
+ const hasConfig = Boolean(
77
+ section?.appId && section?.appSecret && section?.backendUrl,
78
+ );
79
+ return {
80
+ enabled: Boolean(section?.enabled !== false),
81
+ configured: hasConfig,
82
+ tokenStatus: hasConfig ? "available" : "missing",
83
+ };
84
+ },
85
+ },
86
+ }),
87
+
88
+ // Plugin metadata
10
89
  meta: {
11
90
  id: "cored",
12
91
  label: "Cored",
13
92
  selectionLabel: "Cored",
14
93
  docsPath: "/channels/cored",
15
- blurb: "Cored enterprise IM channel",
94
+ blurb: "Connect OpenClaw to Cored",
16
95
  aliases: ["cored", "co"],
17
96
  },
97
+
98
+ // Capabilities
18
99
  capabilities: {
19
100
  chatTypes: ["direct", "group"] as const,
20
101
  },
102
+
103
+ // Config
21
104
  config: {
22
105
  listAccountIds: (cfg: unknown) => listAccountIds(cfg),
23
106
  resolveAccount: (cfg: unknown, accountId?: string) =>
24
107
  resolveAccountConfig(cfg, accountId),
25
108
  },
109
+
110
+ // Outbound messaging
26
111
  outbound: {
27
112
  deliveryMode: "direct" as const,
28
113
  resolveTarget: ({ to }: { to?: string }) => {
@@ -58,4 +143,67 @@ export const coredPlugin = {
58
143
  return sendText(target.id, text, accountId);
59
144
  },
60
145
  },
61
- };
146
+
147
+ // Setup wizard for openclaw onboard
148
+ setupWizard: {
149
+ channel: "cored",
150
+ status: {
151
+ configuredLabel: "Connected",
152
+ unconfiguredLabel: "Not configured",
153
+ resolveConfigured: ({ cfg }: { cfg: OpenClawConfig }) => {
154
+ const section = (cfg.channels as Record<string, any>)?.["cored"];
155
+ return Boolean(section?.appId && section?.appSecret && section?.backendUrl);
156
+ },
157
+ },
158
+ credentials: [
159
+ {
160
+ inputKey: "appId",
161
+ providerHint: "cored",
162
+ credentialLabel: "App ID",
163
+ preferredEnvVar: "CORED_APP_ID",
164
+ envPrompt: "Use CORED_APP_ID from environment?",
165
+ keepPrompt: "Keep current App ID?",
166
+ inputPrompt: "Enter your Cored App ID:",
167
+ inspect: ({ cfg }: { cfg: OpenClawConfig }) => {
168
+ const section = (cfg.channels as Record<string, any>)?.["cored"];
169
+ return {
170
+ accountConfigured: Boolean(section?.appId),
171
+ hasConfiguredValue: Boolean(section?.appId),
172
+ };
173
+ },
174
+ },
175
+ {
176
+ inputKey: "appSecret",
177
+ providerHint: "cored",
178
+ credentialLabel: "App Secret",
179
+ preferredEnvVar: "CORED_APP_SECRET",
180
+ envPrompt: "Use CORED_APP_SECRET from environment?",
181
+ keepPrompt: "Keep current App Secret?",
182
+ inputPrompt: "Enter your Cored App Secret:",
183
+ inspect: ({ cfg }: { cfg: OpenClawConfig }) => {
184
+ const section = (cfg.channels as Record<string, any>)?.["cored"];
185
+ return {
186
+ accountConfigured: Boolean(section?.appSecret),
187
+ hasConfiguredValue: Boolean(section?.appSecret),
188
+ };
189
+ },
190
+ },
191
+ {
192
+ inputKey: "backendUrl",
193
+ providerHint: "cored",
194
+ credentialLabel: "Backend URL",
195
+ preferredEnvVar: "CORED_BACKEND_URL",
196
+ envPrompt: "Use CORED_BACKEND_URL from environment?",
197
+ keepPrompt: "Keep current Backend URL?",
198
+ inputPrompt: "Enter your Cored backend server URL:",
199
+ inspect: ({ cfg }: { cfg: OpenClawConfig }) => {
200
+ const section = (cfg.channels as Record<string, any>)?.["cored"];
201
+ return {
202
+ accountConfigured: Boolean(section?.backendUrl),
203
+ hasConfiguredValue: Boolean(section?.backendUrl),
204
+ };
205
+ },
206
+ },
207
+ ],
208
+ },
209
+ });
package/src/index.test.ts CHANGED
@@ -2,8 +2,13 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { describe, it, expect, vi, beforeEach } from "vitest";
5
- import register from "./index.js";
6
- import type { PluginApi } from "./types.js";
5
+
6
+ // Mock the SDK module before any imports
7
+ vi.mock("openclaw/plugin-sdk/core", () => ({
8
+ defineChannelPluginEntry: (config: unknown) => config,
9
+ createChatChannelPlugin: (config: unknown) => config,
10
+ createChannelPluginBase: (config: unknown) => config,
11
+ }));
7
12
 
8
13
  vi.mock("./core/cored-client.js", () => ({
9
14
  createClient: vi.fn(),
@@ -18,20 +23,29 @@ vi.mock("./messaging/outbound.js", () => ({
18
23
  readMessage: vi.fn().mockResolvedValue(undefined),
19
24
  }));
20
25
 
26
+ // Import after mocks are set up
21
27
  import { createClient, clientCount } from "./core/cored-client.js";
22
28
 
23
- function createMockApi(config: unknown = {}): PluginApi {
29
+ // Import the entry module - need to use dynamic import for esm
30
+ const entry = await import("./index.js");
31
+
32
+ interface MockPluginApi {
33
+ registerChannel: ReturnType<typeof vi.fn>;
34
+ registerService: ReturnType<typeof vi.fn>;
35
+ config: { channels?: Record<string, unknown> };
36
+ logger?: {
37
+ info: ReturnType<typeof vi.fn>;
38
+ warn: ReturnType<typeof vi.fn>;
39
+ error: ReturnType<typeof vi.fn>;
40
+ debug: ReturnType<typeof vi.fn>;
41
+ };
42
+ }
43
+
44
+ function createMockApi(config: unknown = {}): MockPluginApi {
24
45
  return {
25
46
  registerChannel: vi.fn(),
26
47
  registerService: vi.fn(),
27
- config: config as PluginApi["config"],
28
- runtime: {
29
- channel: {
30
- reply: { dispatchReplyWithBufferedBlockDispatcher: vi.fn() },
31
- session: { recordInboundSession: vi.fn() },
32
- routing: { resolveAgentRoute: vi.fn() },
33
- },
34
- },
48
+ config: config as MockPluginApi["config"],
35
49
  logger: {
36
50
  info: vi.fn(),
37
51
  warn: vi.fn(),
@@ -41,25 +55,37 @@ function createMockApi(config: unknown = {}): PluginApi {
41
55
  };
42
56
  }
43
57
 
44
- function extractStartFn(api: PluginApi): () => Promise<void> {
58
+ function extractStartFn(api: MockPluginApi): () => Promise<void> {
45
59
  const call = (api.registerService as ReturnType<typeof vi.fn>).mock.calls[0];
46
60
  return call[0].start;
47
61
  }
48
62
 
49
- describe("register", () => {
63
+ describe("entry module", () => {
50
64
  beforeEach(() => {
51
65
  vi.mocked(clientCount).mockReturnValue(0);
52
66
  vi.mocked(createClient).mockResolvedValue({ client: {}, config: {} } as any);
53
67
  });
54
68
 
55
- it("registers channel and service", () => {
56
- const api = createMockApi();
57
- register(api);
69
+ it("exports entry with correct id", () => {
70
+ expect(entry.default.id).toBe("cored");
71
+ });
58
72
 
59
- expect(api.registerChannel).toHaveBeenCalledOnce();
60
- expect(api.registerChannel).toHaveBeenCalledWith(
61
- expect.objectContaining({ plugin: expect.objectContaining({ id: "cored" }) }),
62
- );
73
+ it("exports entry with correct name", () => {
74
+ expect(entry.default.name).toBe("Cored");
75
+ });
76
+
77
+ it("exports entry with plugin", () => {
78
+ expect(entry.default.plugin).toBeDefined();
79
+ expect(entry.default.plugin.base.id).toBe("cored");
80
+ });
81
+
82
+ it("exports registerFull function", () => {
83
+ expect(entry.default.registerFull).toBeInstanceOf(Function);
84
+ });
85
+
86
+ it("registerFull registers service", () => {
87
+ const api = createMockApi();
88
+ entry.default.registerFull(api);
63
89
 
64
90
  expect(api.registerService).toHaveBeenCalledOnce();
65
91
  expect(api.registerService).toHaveBeenCalledWith(
@@ -67,9 +93,9 @@ describe("register", () => {
67
93
  );
68
94
  });
69
95
 
70
- it("logs plugin registration", () => {
96
+ it("registerFull logs plugin registration", () => {
71
97
  const api = createMockApi();
72
- register(api);
98
+ entry.default.registerFull(api);
73
99
  expect(api.logger!.info).toHaveBeenCalledWith("[cored] plugin registered");
74
100
  });
75
101
  });
@@ -91,7 +117,7 @@ describe("service start — config validation", () => {
91
117
  },
92
118
  },
93
119
  });
94
- register(api);
120
+ entry.default.registerFull(api);
95
121
  await extractStartFn(api)();
96
122
 
97
123
  expect(api.logger!.warn).toHaveBeenCalledWith(
@@ -113,7 +139,7 @@ describe("service start — config validation", () => {
113
139
  },
114
140
  },
115
141
  });
116
- register(api);
142
+ entry.default.registerFull(api);
117
143
  await extractStartFn(api)();
118
144
 
119
145
  expect(api.logger!.warn).toHaveBeenCalledWith(
@@ -128,7 +154,7 @@ describe("service start — config validation", () => {
128
154
  cored: { appId: "a", appSecret: "s", backendUrl: "https://ok.io" },
129
155
  },
130
156
  });
131
- register(api);
157
+ entry.default.registerFull(api);
132
158
  await extractStartFn(api)();
133
159
 
134
160
  expect(createClient).toHaveBeenCalledOnce();
@@ -148,7 +174,7 @@ describe("service start — config validation", () => {
148
174
  },
149
175
  },
150
176
  });
151
- register(api);
177
+ entry.default.registerFull(api);
152
178
  await extractStartFn(api)();
153
179
 
154
180
  // bad account skipped with warning
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // Copyright (c) 2026 Cored Limited
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
+ import { defineChannelPluginEntry, type PluginApi } from "openclaw/plugin-sdk/core";
4
5
  import { coredPlugin } from "./channel.js";
5
6
  import { listEnabledAccountConfigs, validateAccountConfig } from "./config.js";
6
7
  import {
@@ -8,72 +9,83 @@ import {
8
9
  destroyAllClients,
9
10
  clientCount,
10
11
  } from "./core/cored-client.js";
11
- import { processInboundMessage } from "./messaging/inbound.js";
12
+ import { processInboundMessage, type ExtendedPluginApi } from "./messaging/inbound.js";
12
13
  import { makeDeliver, setTyping, clearTyping, readMessage } from "./messaging/outbound.js";
13
- import type { PluginApi, CoredAccountConfig, CoredMessageEvent } from "./types.js";
14
+ import type { CoredAccountConfig, CoredMessageEvent } from "./types.js";
14
15
 
15
- export default function register(api: PluginApi): void {
16
- api.registerChannel({ plugin: coredPlugin });
16
+ // Extended PluginApi with config type for service registration
17
+ interface ServicePluginApi extends ExtendedPluginApi {
18
+ config: { channels?: Record<string, unknown> };
19
+ }
17
20
 
18
- api.registerService({
19
- id: "cored-sdk",
20
- start: async () => {
21
- if (clientCount() > 0) return;
21
+ export default defineChannelPluginEntry({
22
+ id: "cored",
23
+ name: "Cored",
24
+ description: "Connect OpenClaw with Cored",
25
+ plugin: coredPlugin,
26
+ registerFull(api) {
27
+ const typedApi = api as ServicePluginApi;
22
28
 
23
- const accounts = listEnabledAccountConfigs(api.config);
24
- if (accounts.length === 0) {
25
- api.logger?.warn("[cored] no enabled account config found — service idle");
26
- return;
27
- }
29
+ typedApi.registerService({
30
+ id: "cored-sdk",
31
+ start: async () => {
32
+ if (clientCount() > 0) return;
28
33
 
29
- for (const account of accounts) {
30
- const errors = validateAccountConfig(account);
31
- if (errors.length > 0) {
32
- api.logger?.warn(
33
- `[cored] skipping account=${account.accountId}: ${errors.map((e) => e.message).join("; ")}`,
34
- );
35
- continue;
34
+ const accounts = listEnabledAccountConfigs(typedApi.config);
35
+ if (accounts.length === 0) {
36
+ typedApi.logger?.warn?.("[cored] no enabled account config found — service idle");
37
+ return;
36
38
  }
37
39
 
38
- try {
39
- await startAccount(api, account);
40
- api.logger?.info(
41
- `[cored] account=${account.accountId} connected (appId=${account.appId})`,
42
- );
43
- } catch (err) {
44
- api.logger?.error(
45
- `[cored] account=${account.accountId} failed to start: ${err instanceof Error ? err.message : String(err)}`,
46
- );
40
+ for (const account of accounts) {
41
+ const errors = validateAccountConfig(account);
42
+ if (errors.length > 0) {
43
+ typedApi.logger?.warn?.(
44
+ `[cored] skipping account=${account.accountId}: ${errors.map((e) => e.message).join("; ")}`,
45
+ );
46
+ continue;
47
+ }
48
+
49
+ try {
50
+ await startAccount(typedApi, account);
51
+ typedApi.logger?.info?.(
52
+ `[cored] account=${account.accountId} connected (appId=${account.appId})`,
53
+ );
54
+ } catch (err) {
55
+ typedApi.logger?.error?.(
56
+ `[cored] account=${account.accountId} failed to start: ${err instanceof Error ? err.message : String(err)}`,
57
+ );
58
+ }
47
59
  }
48
- }
49
60
 
50
- api.logger?.info(`[cored] service started with ${clientCount()} account(s)`);
51
- },
52
- stop: async () => {
53
- await destroyAllClients();
54
- api.logger?.info("[cored] service stopped — all clients disconnected");
55
- },
56
- });
61
+ typedApi.logger?.info?.(`[cored] service started with ${clientCount()} account(s)`);
62
+ },
63
+ stop: async () => {
64
+ await destroyAllClients();
65
+ typedApi.logger?.info?.("[cored] service stopped — all clients disconnected");
66
+ },
67
+ });
57
68
 
58
- api.logger?.info("[cored] plugin registered");
59
- }
69
+ typedApi.logger?.info?.("[cored] plugin registered");
70
+ },
71
+ });
60
72
 
61
73
  /**
62
74
  * Start a single account — create client, subscribe to inbound events.
63
75
  */
64
76
  async function startAccount(
65
- api: PluginApi,
77
+ api: ServicePluginApi,
66
78
  account: CoredAccountConfig,
67
79
  ): Promise<void> {
68
- const deliver = makeDeliver(account.accountId, (msg) => api.logger?.warn(msg));
80
+ const deliver = makeDeliver(account.accountId, (msg) => api.logger?.warn?.(msg));
69
81
 
70
82
  await createClient({
71
83
  config: account,
72
- log: (msg: string) => api.logger?.debug(msg),
84
+ log: (msg: string) => api.logger?.debug?.(msg),
73
85
  onMessage: (event: CoredMessageEvent, accountConfig: CoredAccountConfig) => {
74
86
  // Fire-and-forget: process inbound with typing indicator
75
87
  handleInbound(api, accountConfig, event, deliver).catch((err) => {
76
- api.logger?.error(
88
+ api.logger?.error?.(
77
89
  `[cored] unhandled inbound error for account=${accountConfig.accountId}: ${err}`,
78
90
  );
79
91
  });
@@ -85,7 +97,7 @@ async function startAccount(
85
97
  * Handle a single inbound message with typing indicator lifecycle.
86
98
  */
87
99
  async function handleInbound(
88
- api: PluginApi,
100
+ api: ServicePluginApi,
89
101
  account: CoredAccountConfig,
90
102
  event: CoredMessageEvent,
91
103
  deliver: (chatId: string, text: string) => Promise<void>,
@@ -17,6 +17,30 @@ import type {
17
17
  PluginApi,
18
18
  } from "../types.js";
19
19
 
20
+ // Extended PluginApi with runtime surfaces used by this module
21
+ export interface ExtendedPluginApi extends PluginApi {
22
+ runtime?: {
23
+ channel?: {
24
+ reply?: {
25
+ dispatchReplyWithBufferedBlockDispatcher?: (opts: unknown) => Promise<void>;
26
+ };
27
+ session?: {
28
+ recordInboundSession?: (opts: unknown) => Promise<void>;
29
+ resolveStorePath?: (store: unknown, opts: unknown) => string;
30
+ };
31
+ routing?: {
32
+ resolveAgentRoute?: (opts: unknown) => unknown;
33
+ };
34
+ };
35
+ };
36
+ logger?: {
37
+ debug?: (msg: string) => void;
38
+ info?: (msg: string) => void;
39
+ warn?: (msg: string) => void;
40
+ error?: (msg: string) => void;
41
+ };
42
+ }
43
+
20
44
  // ---------------------------------------------------------------------------
21
45
  // Parse — extract usable text body from incoming message
22
46
  // ---------------------------------------------------------------------------
@@ -290,17 +314,25 @@ export interface InboundDispatchOptions {
290
314
  * Returns `true` if the message was dispatched, `false` if filtered.
291
315
  */
292
316
  export async function processInboundMessage(
293
- api: PluginApi,
317
+ api: ExtendedPluginApi,
294
318
  account: CoredAccountConfig,
295
319
  event: CoredMessageEvent,
296
320
  opts: InboundDispatchOptions,
297
321
  ): Promise<boolean> {
298
322
  const logger = api.logger;
299
323
 
324
+ // Helper for safe logging
325
+ const log = {
326
+ debug: (msg: string) => { logger?.debug?.(msg); },
327
+ info: (msg: string) => { logger?.info?.(msg); },
328
+ warn: (msg: string) => { logger?.warn?.(msg); },
329
+ error: (msg: string) => { logger?.error?.(msg); },
330
+ };
331
+
300
332
  // 1. Parse
301
333
  const parsed = parseMessageEvent(event);
302
334
  if (!parsed) {
303
- logger?.debug(
335
+ log.debug(
304
336
  `[cored] ignoring unparseable event (messageId=${event?.message?.messageId ?? "unknown"} messageType=${event?.message?.messageType ?? "undefined"})`,
305
337
  );
306
338
  return false;
@@ -309,7 +341,7 @@ export async function processInboundMessage(
309
341
  // 2. Gate
310
342
  const gate = checkMessageGate(parsed, account);
311
343
  if (!gate.pass) {
312
- logger?.debug(
344
+ log.debug(
313
345
  `[cored] gated message=${parsed.messageId} reason=${gate.reason} chat=${parsed.chatId}`,
314
346
  );
315
347
  return false;
@@ -317,7 +349,7 @@ export async function processInboundMessage(
317
349
 
318
350
  // 3. Dedup
319
351
  if (isDuplicate(parsed.messageId)) {
320
- logger?.debug(
352
+ log.debug(
321
353
  `[cored] duplicate message=${parsed.messageId} chat=${parsed.chatId}`,
322
354
  );
323
355
  return false;
@@ -326,14 +358,14 @@ export async function processInboundMessage(
326
358
  // 4. Build context
327
359
  const ctx = buildContext(parsed, account);
328
360
 
329
- logger?.info(
361
+ log.info(
330
362
  `[cored] dispatching message=${parsed.messageId} chat=${parsed.chatId} sender=${parsed.senderId} type=${parsed.chatType}`,
331
363
  );
332
364
 
333
365
  // 5. Dispatch
334
366
  const runtime = api.runtime;
335
367
  if (!runtime?.channel?.reply?.dispatchReplyWithBufferedBlockDispatcher) {
336
- logger?.warn("[cored] runtime.channel.reply not available — cannot dispatch");
368
+ log.warn("[cored] runtime.channel.reply not available — cannot dispatch");
337
369
  return false;
338
370
  }
339
371
 
@@ -361,7 +393,7 @@ export async function processInboundMessage(
361
393
  });
362
394
 
363
395
  // Dispatch reply with buffered block dispatcher
364
- logger?.debug(
396
+ log.debug(
365
397
  `[cored] dispatch starting for message=${parsed.messageId} session=${ctx.SessionKey}`,
366
398
  );
367
399
 
@@ -370,25 +402,25 @@ export async function processInboundMessage(
370
402
  cfg: api.config,
371
403
  dispatcherOptions: {
372
404
  deliver: async (payload: { text?: string }) => {
373
- logger?.info(
405
+ log.info(
374
406
  `[cored] deliver callback called for message=${parsed.messageId} hasText=${!!payload.text} textLen=${payload.text?.length ?? 0}`,
375
407
  );
376
408
  if (payload.text) {
377
409
  await opts.deliver(parsed.chatId, payload.text);
378
- logger?.info(
410
+ log.info(
379
411
  `[cored] deliver completed for message=${parsed.messageId} chat=${parsed.chatId}`,
380
412
  );
381
413
  }
382
414
  },
383
415
  onError: (err: unknown, info?: { kind?: string }) => {
384
- logger?.error(
416
+ log.error(
385
417
  `[cored] ${info?.kind ?? "reply"} error for message=${parsed.messageId}: ${err}`,
386
418
  );
387
419
  },
388
420
  },
389
421
  });
390
422
 
391
- logger?.info(
423
+ log.info(
392
424
  `[cored] dispatch finished for message=${parsed.messageId}`,
393
425
  );
394
426
 
@@ -2,50 +2,6 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
5
+ import { coredPlugin } from "./channel.js";
5
6
 
6
- export default defineSetupPluginEntry({
7
- async onSetup(context) {
8
- const appId = await context.prompt({
9
- type: "text",
10
- message: "Enter your Cored App ID:",
11
- validate: (val: string) => val.length > 0 || "This field is required",
12
- });
13
-
14
- const appSecret = await context.prompt({
15
- type: "password",
16
- message: "Enter your Cored App Secret:",
17
- validate: (val: string) => val.length > 0 || "This field is required",
18
- });
19
-
20
- const backendUrl = await context.prompt({
21
- type: "text",
22
- message: "Enter Cored backend server URL:",
23
- validate: (val: string) => {
24
- if (val.length === 0) return "This field is required";
25
- try {
26
- new URL(val);
27
- return true;
28
- } catch {
29
- return "Please enter a valid URL";
30
- }
31
- },
32
- });
33
-
34
- const enableEncryption = await context.prompt({
35
- type: "confirm",
36
- message: "Enable message encryption?",
37
- default: true,
38
- });
39
-
40
- // Write config to ~/.openclaw/openclaw.json
41
- await context.updateConfig("channels.cored.appId", appId);
42
- await context.updateConfig("channels.cored.appSecret", appSecret);
43
- await context.updateConfig("channels.cored.backendUrl", backendUrl);
44
- await context.updateConfig("channels.cored.enableEncryption", enableEncryption);
45
- await context.updateConfig("channels.cored.enabled", true);
46
-
47
- console.log("✅ Cored channel configuration saved successfully!");
48
- console.log("📄 Config file: ~/.openclaw/openclaw.json");
49
- console.log("📖 Deploy docs: https://coredim.com/docs/admin/bots/openclaw");
50
- },
51
- });
7
+ export default defineSetupPluginEntry(coredPlugin);