@botcord/botcord 0.1.2 → 0.1.3
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/api.ts +3 -0
- package/index.ts +20 -28
- package/openclaw.plugin.json +1 -1
- package/package.json +12 -3
- package/runtime-api.ts +7 -0
- package/setup-entry.ts +5 -0
- package/src/channel.ts +54 -16
- package/src/commands/bind.ts +2 -2
- package/src/commands/healthcheck.ts +1 -1
- package/src/commands/register.ts +1 -2
- package/src/commands/token.ts +1 -1
- package/src/constants.ts +19 -0
- package/src/inbound.ts +15 -2
- package/src/runtime.ts +1 -1
- package/src/sanitize.ts +4 -1
- package/src/tools/account.ts +1 -0
- package/src/tools/bind.ts +1 -0
- package/src/tools/contacts.ts +1 -0
- package/src/tools/directory.ts +1 -0
- package/src/tools/messaging.ts +2 -0
- package/src/tools/notify.ts +1 -0
- package/src/tools/payment.ts +1 -0
- package/src/tools/rooms.ts +1 -0
- package/src/tools/subscription.ts +1 -0
- package/src/tools/topics.ts +1 -0
package/api.ts
ADDED
package/index.ts
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @botcord/botcord — OpenClaw plugin for BotCord A2A messaging protocol.
|
|
3
|
-
*
|
|
4
|
-
* Registers:
|
|
5
|
-
* - Channel plugin (botcord) with WebSocket + polling gateway
|
|
6
|
-
* - Agent tools: botcord_send, botcord_upload, botcord_rooms, botcord_topics, botcord_contacts, botcord_account, botcord_directory, botcord_payment, botcord_subscription, botcord_bind
|
|
7
|
-
* - Commands: /botcord_healthcheck, /botcord_token, /botcord_bind
|
|
8
|
-
* - CLI: openclaw botcord-register, openclaw botcord-import, openclaw botcord-export
|
|
9
3
|
*/
|
|
10
|
-
import type { ChannelPlugin, OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
11
|
-
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
12
4
|
import { botCordPlugin } from "./src/channel.js";
|
|
13
5
|
import { setBotCordRuntime, setConfigGetter } from "./src/runtime.js";
|
|
14
6
|
import { createMessagingTool, createUploadTool } from "./src/tools/messaging.js";
|
|
@@ -33,34 +25,36 @@ import {
|
|
|
33
25
|
shouldRunBotCordLoopRiskCheck,
|
|
34
26
|
} from "./src/loop-risk.js";
|
|
35
27
|
|
|
36
|
-
|
|
28
|
+
// Inline replacement for defineChannelPluginEntry from openclaw/plugin-sdk/core.
|
|
29
|
+
// Avoids missing dist artifacts in npm-installed openclaw (see openclaw#53685).
|
|
30
|
+
export default {
|
|
37
31
|
id: "botcord",
|
|
38
32
|
name: "BotCord",
|
|
39
33
|
description: "BotCord A2A messaging protocol — secure agent-to-agent communication with Ed25519 signing",
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
register(api: OpenClawPluginApi) {
|
|
43
|
-
// Store runtime reference and config getter
|
|
34
|
+
register(api: any) {
|
|
44
35
|
setBotCordRuntime(api.runtime);
|
|
45
|
-
|
|
36
|
+
api.registerChannel({ plugin: botCordPlugin });
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
|
|
38
|
+
if (api.registrationMode !== "full") return;
|
|
39
|
+
|
|
40
|
+
setConfigGetter(() => api.config);
|
|
49
41
|
|
|
50
|
-
//
|
|
42
|
+
// Agent tools — `as any` needed until tool execute() return types are
|
|
43
|
+
// migrated to the AgentToolResult<T> shape (P2 task).
|
|
51
44
|
api.registerTool(createMessagingTool() as any);
|
|
45
|
+
api.registerTool(createUploadTool() as any);
|
|
52
46
|
api.registerTool(createRoomsTool() as any);
|
|
53
47
|
api.registerTool(createTopicsTool() as any);
|
|
54
48
|
api.registerTool(createContactsTool() as any);
|
|
55
49
|
api.registerTool(createAccountTool() as any);
|
|
56
50
|
api.registerTool(createDirectoryTool() as any);
|
|
57
|
-
api.registerTool(createUploadTool() as any);
|
|
58
51
|
api.registerTool(createPaymentTool() as any);
|
|
59
52
|
api.registerTool(createSubscriptionTool() as any);
|
|
60
53
|
api.registerTool(createNotifyTool() as any);
|
|
61
54
|
api.registerTool(createBindTool() as any);
|
|
62
55
|
|
|
63
|
-
|
|
56
|
+
// Hooks
|
|
57
|
+
api.on("after_tool_call", async (event: any, ctx: any) => {
|
|
64
58
|
if (ctx.toolName !== "botcord_send") return;
|
|
65
59
|
if (!didBotCordSendSucceed(event.result, event.error)) return;
|
|
66
60
|
recordBotCordOutboundText({
|
|
@@ -69,7 +63,7 @@ const plugin = {
|
|
|
69
63
|
});
|
|
70
64
|
});
|
|
71
65
|
|
|
72
|
-
api.on("before_prompt_build", async (event, ctx) => {
|
|
66
|
+
api.on("before_prompt_build", async (event: any, ctx: any) => {
|
|
73
67
|
if (!shouldRunBotCordLoopRiskCheck({
|
|
74
68
|
channelId: ctx.channelId,
|
|
75
69
|
prompt: event.prompt,
|
|
@@ -88,16 +82,16 @@ const plugin = {
|
|
|
88
82
|
return { prependContext };
|
|
89
83
|
}, { priority: 10 });
|
|
90
84
|
|
|
91
|
-
api.on("session_end", async (_event, ctx) => {
|
|
85
|
+
api.on("session_end", async (_event: any, ctx: any) => {
|
|
92
86
|
clearBotCordLoopRiskSession(ctx.sessionKey);
|
|
93
87
|
});
|
|
94
88
|
|
|
95
|
-
//
|
|
96
|
-
api.registerCommand(createHealthcheckCommand()
|
|
97
|
-
api.registerCommand(createTokenCommand()
|
|
98
|
-
api.registerCommand(createBindCommand()
|
|
89
|
+
// Commands
|
|
90
|
+
api.registerCommand(createHealthcheckCommand());
|
|
91
|
+
api.registerCommand(createTokenCommand());
|
|
92
|
+
api.registerCommand(createBindCommand());
|
|
99
93
|
|
|
100
|
-
//
|
|
94
|
+
// CLI
|
|
101
95
|
const registerCli = createRegisterCli();
|
|
102
96
|
api.registerCli(registerCli.setup, { commands: registerCli.commands });
|
|
103
97
|
},
|
|
@@ -105,5 +99,3 @@ const plugin = {
|
|
|
105
99
|
|
|
106
100
|
export { TopicTracker } from "./src/topic-tracker.js";
|
|
107
101
|
export type { TopicState, TopicInfo } from "./src/topic-tracker.js";
|
|
108
|
-
|
|
109
|
-
export default plugin;
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botcord/botcord",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "OpenClaw channel plugin for BotCord A2A messaging protocol (Ed25519 signed envelopes)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"files": [
|
|
9
9
|
"index.ts",
|
|
10
|
+
"setup-entry.ts",
|
|
11
|
+
"api.ts",
|
|
12
|
+
"runtime-api.ts",
|
|
10
13
|
"src/",
|
|
11
14
|
"!src/__tests__/",
|
|
12
15
|
"skills/",
|
|
@@ -35,11 +38,11 @@
|
|
|
35
38
|
"ws": "^8.18.0"
|
|
36
39
|
},
|
|
37
40
|
"peerDependencies": {
|
|
38
|
-
"openclaw": ">=2026.
|
|
41
|
+
"openclaw": ">=2026.3.22"
|
|
39
42
|
},
|
|
40
43
|
"devDependencies": {
|
|
41
44
|
"@types/ws": "^8.5.0",
|
|
42
|
-
"openclaw": "
|
|
45
|
+
"openclaw": "^2026.3.23",
|
|
43
46
|
"typescript": "^5.9.3",
|
|
44
47
|
"vitest": "^4.0.18"
|
|
45
48
|
},
|
|
@@ -47,6 +50,12 @@
|
|
|
47
50
|
"extensions": [
|
|
48
51
|
"./index.ts"
|
|
49
52
|
],
|
|
53
|
+
"setupEntry": "./setup-entry.ts",
|
|
54
|
+
"install": {
|
|
55
|
+
"npmSpec": "@botcord/botcord",
|
|
56
|
+
"defaultChoice": "npm",
|
|
57
|
+
"minHostVersion": ">=2026.3.22"
|
|
58
|
+
},
|
|
50
59
|
"channel": {
|
|
51
60
|
"id": "botcord",
|
|
52
61
|
"label": "BotCord",
|
package/runtime-api.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// runtime-api.ts — full runtime surface
|
|
2
|
+
export { botCordPlugin } from "./src/channel.js";
|
|
3
|
+
export { BotCordClient } from "./src/client.js";
|
|
4
|
+
export { getBotCordRuntime } from "./src/runtime.js";
|
|
5
|
+
export { TopicTracker } from "./src/topic-tracker.js";
|
|
6
|
+
export type { TopicState, TopicInfo } from "./src/topic-tracker.js";
|
|
7
|
+
export type { BotCordChannelConfig, BotCordAccountConfig, MessageAttachment } from "./src/types.js";
|
package/setup-entry.ts
ADDED
package/src/channel.ts
CHANGED
|
@@ -3,28 +3,56 @@
|
|
|
3
3
|
* outbound (send via signed envelopes), gateway (start websocket/polling),
|
|
4
4
|
* security, messaging, and status adapters.
|
|
5
5
|
*/
|
|
6
|
-
import type { ChannelPlugin, ClawdbotConfig } from "openclaw/plugin-sdk";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
import type { ChannelPlugin, OpenClawConfig as ClawdbotConfig } from "openclaw/plugin-sdk/core";
|
|
7
|
+
// Inlined from openclaw/plugin-sdk/status-helpers and account-id to avoid
|
|
8
|
+
// missing dist artifacts in npm-installed openclaw (see openclaw#53685).
|
|
9
|
+
const DEFAULT_ACCOUNT_ID = "default";
|
|
10
|
+
|
|
11
|
+
function createDefaultChannelRuntimeState(accountId: string) {
|
|
12
|
+
return {
|
|
13
|
+
accountId,
|
|
14
|
+
running: false as const,
|
|
15
|
+
lastStartAt: null,
|
|
16
|
+
lastStopAt: null,
|
|
17
|
+
lastError: null,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildBaseChannelStatusSummary(snapshot: {
|
|
22
|
+
configured?: boolean | null;
|
|
23
|
+
running?: boolean | null;
|
|
24
|
+
lastStartAt?: number | null;
|
|
25
|
+
lastStopAt?: number | null;
|
|
26
|
+
lastError?: string | null;
|
|
27
|
+
}) {
|
|
28
|
+
return {
|
|
29
|
+
configured: snapshot.configured ?? false,
|
|
30
|
+
running: snapshot.running ?? false,
|
|
31
|
+
lastStartAt: snapshot.lastStartAt ?? null,
|
|
32
|
+
lastStopAt: snapshot.lastStopAt ?? null,
|
|
33
|
+
lastError: snapshot.lastError ?? null,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
12
36
|
import {
|
|
13
37
|
resolveChannelConfig,
|
|
14
38
|
resolveAccounts,
|
|
15
39
|
isAccountConfigured,
|
|
16
40
|
displayPrefix,
|
|
17
41
|
} from "./config.js";
|
|
18
|
-
import { BotCordClient } from "./client.js";
|
|
19
|
-
import { getBotCordRuntime } from "./runtime.js";
|
|
20
|
-
import { startPoller, stopPoller } from "./poller.js";
|
|
21
|
-
import { startWsClient, stopWsClient } from "./ws-client.js";
|
|
22
42
|
import type {
|
|
23
43
|
BotCordAccountConfig,
|
|
24
44
|
BotCordChannelConfig,
|
|
25
45
|
MessageAttachment,
|
|
26
46
|
} from "./types.js";
|
|
27
47
|
|
|
48
|
+
// runtime.js is lightweight (no ws/network deps) — safe for static import.
|
|
49
|
+
// Heavy deps (client, poller, ws-client) are lazy-loaded so that setup-entry.ts
|
|
50
|
+
// can import botCordPlugin without pulling in ws at module level.
|
|
51
|
+
import { getBotCordRuntime } from "./runtime.js";
|
|
52
|
+
const lazyClient = () => import("./client.js").then((m) => m.BotCordClient);
|
|
53
|
+
const lazyPoller = () => import("./poller.js");
|
|
54
|
+
const lazyWsClient = () => import("./ws-client.js");
|
|
55
|
+
|
|
28
56
|
// ── Types ────────────────────────────────────────────────────────
|
|
29
57
|
|
|
30
58
|
export interface ResolvedBotCordAccount {
|
|
@@ -291,7 +319,8 @@ export const botCordPlugin: ChannelPlugin<ResolvedBotCordAccount> = {
|
|
|
291
319
|
const account = resolveBotCordAccount({ cfg: cfg as CoreConfig, accountId });
|
|
292
320
|
if (!account.configured || !account.agentId) return null;
|
|
293
321
|
try {
|
|
294
|
-
const
|
|
322
|
+
const Client = await lazyClient();
|
|
323
|
+
const client = new Client(account.config);
|
|
295
324
|
const info = await client.resolve(account.agentId);
|
|
296
325
|
return { kind: "user", id: info.agent_id, name: info.display_name || info.agent_id };
|
|
297
326
|
} catch {
|
|
@@ -302,7 +331,8 @@ export const botCordPlugin: ChannelPlugin<ResolvedBotCordAccount> = {
|
|
|
302
331
|
const account = resolveBotCordAccount({ cfg: cfg as CoreConfig, accountId });
|
|
303
332
|
if (!account.configured) return [];
|
|
304
333
|
try {
|
|
305
|
-
const
|
|
334
|
+
const Client = await lazyClient();
|
|
335
|
+
const client = new Client(account.config);
|
|
306
336
|
const contacts = await client.listContacts();
|
|
307
337
|
const q = query?.trim().toLowerCase() ?? "";
|
|
308
338
|
return contacts
|
|
@@ -326,7 +356,8 @@ export const botCordPlugin: ChannelPlugin<ResolvedBotCordAccount> = {
|
|
|
326
356
|
const account = resolveBotCordAccount({ cfg: cfg as CoreConfig, accountId });
|
|
327
357
|
if (!account.configured) return [];
|
|
328
358
|
try {
|
|
329
|
-
const
|
|
359
|
+
const Client = await lazyClient();
|
|
360
|
+
const client = new Client(account.config);
|
|
330
361
|
const rooms = await client.listMyRooms();
|
|
331
362
|
const q = query?.trim().toLowerCase() ?? "";
|
|
332
363
|
return rooms
|
|
@@ -345,7 +376,8 @@ export const botCordPlugin: ChannelPlugin<ResolvedBotCordAccount> = {
|
|
|
345
376
|
textChunkLimit: 4000,
|
|
346
377
|
sendText: async ({ cfg, to, text, accountId }) => {
|
|
347
378
|
const account = resolveBotCordAccount({ cfg: cfg as CoreConfig, accountId: accountId ?? undefined });
|
|
348
|
-
const
|
|
379
|
+
const Client = await lazyClient();
|
|
380
|
+
const client = new Client(account.config);
|
|
349
381
|
const result = await client.sendMessage(to, text);
|
|
350
382
|
return {
|
|
351
383
|
channel: "botcord",
|
|
@@ -355,7 +387,8 @@ export const botCordPlugin: ChannelPlugin<ResolvedBotCordAccount> = {
|
|
|
355
387
|
},
|
|
356
388
|
sendMedia: async ({ cfg, to, text, mediaUrl, accountId }) => {
|
|
357
389
|
const account = resolveBotCordAccount({ cfg: cfg as CoreConfig, accountId: accountId ?? undefined });
|
|
358
|
-
const
|
|
390
|
+
const Client = await lazyClient();
|
|
391
|
+
const client = new Client(account.config);
|
|
359
392
|
const attachments: MessageAttachment[] = [];
|
|
360
393
|
if (mediaUrl) {
|
|
361
394
|
const filename = mediaUrl.split("/").pop() || "attachment";
|
|
@@ -402,11 +435,13 @@ export const botCordPlugin: ChannelPlugin<ResolvedBotCordAccount> = {
|
|
|
402
435
|
const dp = displayPrefix(account.accountId, ctx.cfg);
|
|
403
436
|
ctx.log?.info(`[${dp}] starting BotCord gateway (${account.deliveryMode} mode)`);
|
|
404
437
|
|
|
405
|
-
const
|
|
438
|
+
const Client = await lazyClient();
|
|
439
|
+
const client = new Client(account.config);
|
|
406
440
|
const mode = account.deliveryMode || "websocket";
|
|
407
441
|
|
|
408
442
|
if (mode === "websocket") {
|
|
409
443
|
ctx.log?.info(`[${dp}] starting WebSocket connection to Hub`);
|
|
444
|
+
const { startWsClient } = await lazyWsClient();
|
|
410
445
|
startWsClient({
|
|
411
446
|
client,
|
|
412
447
|
accountId: account.accountId,
|
|
@@ -415,6 +450,7 @@ export const botCordPlugin: ChannelPlugin<ResolvedBotCordAccount> = {
|
|
|
415
450
|
log: ctx.log,
|
|
416
451
|
});
|
|
417
452
|
} else {
|
|
453
|
+
const { startPoller } = await lazyPoller();
|
|
418
454
|
startPoller({
|
|
419
455
|
client,
|
|
420
456
|
accountId: account.accountId,
|
|
@@ -438,6 +474,8 @@ export const botCordPlugin: ChannelPlugin<ResolvedBotCordAccount> = {
|
|
|
438
474
|
ctx.abortSignal?.addEventListener("abort", () => resolve(), { once: true });
|
|
439
475
|
});
|
|
440
476
|
|
|
477
|
+
const { stopWsClient } = await lazyWsClient();
|
|
478
|
+
const { stopPoller } = await lazyPoller();
|
|
441
479
|
stopWsClient(account.accountId);
|
|
442
480
|
stopPoller(account.accountId);
|
|
443
481
|
ctx.setStatus({ accountId: ctx.accountId, running: false, lastStopAt: Date.now() });
|
package/src/commands/bind.ts
CHANGED
|
@@ -10,8 +10,8 @@ export function createBindCommand() {
|
|
|
10
10
|
"Bind this agent to a BotCord web dashboard account using a bind ticket.",
|
|
11
11
|
acceptsArgs: true,
|
|
12
12
|
requireAuth: true,
|
|
13
|
-
handler: async (
|
|
14
|
-
const bindTicket = (args || "").trim();
|
|
13
|
+
handler: async (ctx: any) => {
|
|
14
|
+
const bindTicket = (ctx.args || "").trim();
|
|
15
15
|
if (!bindTicket) {
|
|
16
16
|
return { text: "[FAIL] Usage: /botcord_bind <bind_ticket>" };
|
|
17
17
|
}
|
|
@@ -18,7 +18,7 @@ export function createHealthcheckCommand() {
|
|
|
18
18
|
description: "Check BotCord integration health: config, Hub connectivity, token, delivery mode.",
|
|
19
19
|
acceptsArgs: false,
|
|
20
20
|
requireAuth: true,
|
|
21
|
-
handler: async () => {
|
|
21
|
+
handler: async (_ctx: any) => {
|
|
22
22
|
const lines: string[] = [];
|
|
23
23
|
let pass = 0;
|
|
24
24
|
let warn = 0;
|
package/src/commands/register.ts
CHANGED
|
@@ -25,8 +25,7 @@ import {
|
|
|
25
25
|
} from "../config.js";
|
|
26
26
|
import { normalizeAndValidateHubUrl } from "../hub-url.js";
|
|
27
27
|
import { getBotCordRuntime } from "../runtime.js";
|
|
28
|
-
|
|
29
|
-
const DEFAULT_HUB = "https://api.botcord.chat";
|
|
28
|
+
import { DEFAULT_HUB } from "../constants.js";
|
|
30
29
|
|
|
31
30
|
interface RegisterResult {
|
|
32
31
|
agentId: string;
|
package/src/commands/token.ts
CHANGED
|
@@ -15,7 +15,7 @@ export function createTokenCommand() {
|
|
|
15
15
|
description: "Fetch and display the current BotCord JWT token.",
|
|
16
16
|
acceptsArgs: false,
|
|
17
17
|
requireAuth: true,
|
|
18
|
-
handler: async () => {
|
|
18
|
+
handler: async (_ctx: any) => {
|
|
19
19
|
const cfg = getAppConfig();
|
|
20
20
|
if (!cfg) {
|
|
21
21
|
return { text: "[FAIL] No OpenClaw configuration available" };
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Release channel and environment-dependent defaults.
|
|
3
|
+
*
|
|
4
|
+
* In the stable (production) build this file ships as-is.
|
|
5
|
+
* The CI pipeline replaces the RELEASE_CHANNEL value with "beta"
|
|
6
|
+
* before publishing to the `beta` npm dist-tag, so that beta
|
|
7
|
+
* installations automatically point at the test hub.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type ReleaseChannel = "stable" | "beta";
|
|
11
|
+
|
|
12
|
+
export const RELEASE_CHANNEL: ReleaseChannel = "stable";
|
|
13
|
+
|
|
14
|
+
const HUB_URLS: Record<ReleaseChannel, string> = {
|
|
15
|
+
stable: "https://api.botcord.chat",
|
|
16
|
+
beta: "https://test.botcord.chat",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_HUB = HUB_URLS[RELEASE_CHANNEL];
|
package/src/inbound.ts
CHANGED
|
@@ -5,7 +5,20 @@
|
|
|
5
5
|
import { getBotCordRuntime } from "./runtime.js";
|
|
6
6
|
import { resolveAccountConfig } from "./config.js";
|
|
7
7
|
import { buildSessionKey } from "./session-key.js";
|
|
8
|
-
import {
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
|
|
10
|
+
// Simplified inline replacement for loadSessionStore from openclaw/plugin-sdk/mattermost.
|
|
11
|
+
// Avoids missing dist artifacts in npm-installed openclaw (see openclaw#53685).
|
|
12
|
+
function loadSessionStore(storePath: string): Record<string, any> {
|
|
13
|
+
try {
|
|
14
|
+
const raw = readFileSync(storePath, "utf-8");
|
|
15
|
+
if (!raw) return {};
|
|
16
|
+
const parsed = JSON.parse(raw);
|
|
17
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
18
|
+
} catch {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
9
22
|
import { sanitizeUntrustedContent, sanitizeSenderName } from "./sanitize.js";
|
|
10
23
|
import { BotCordClient } from "./client.js";
|
|
11
24
|
import { createBotCordReplyDispatcher } from "./reply-dispatcher.js";
|
|
@@ -227,7 +240,7 @@ async function handleA2AMessage(
|
|
|
227
240
|
// Prompt the agent to notify its owner when receiving contact requests
|
|
228
241
|
const notifyOwnerHint =
|
|
229
242
|
envelope.type === "contact_request"
|
|
230
|
-
? `\n\n[You received a contact request from ${
|
|
243
|
+
? `\n\n[You received a contact request from ${sanitizedSender}. Use the botcord_notify tool to inform your owner about this request so they can decide whether to accept or reject it. Include the sender's agent ID and any message they attached.]`
|
|
231
244
|
: "";
|
|
232
245
|
|
|
233
246
|
const sanitizedContent = sanitizeUntrustedContent(rawContent);
|
package/src/runtime.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Plugin runtime store — holds a reference to OpenClaw's PluginRuntime
|
|
3
3
|
* and a config getter for tools/hooks that need the full app config.
|
|
4
4
|
*/
|
|
5
|
-
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
5
|
+
import type { PluginRuntime } from "openclaw/plugin-sdk/core";
|
|
6
6
|
|
|
7
7
|
let runtime: PluginRuntime | null = null;
|
|
8
8
|
let configGetter: (() => any) | null = null;
|
package/src/sanitize.ts
CHANGED
|
@@ -33,11 +33,14 @@ export function sanitizeUntrustedContent(text: string): string {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* Sanitize sender name — must not contain newlines
|
|
36
|
+
* Sanitize sender name — must not contain newlines, structural markers,
|
|
37
|
+
* or characters that could break XML attribute boundaries.
|
|
37
38
|
*/
|
|
38
39
|
export function sanitizeSenderName(name: string): string {
|
|
39
40
|
return name
|
|
40
41
|
.replace(/[\n\r]/g, " ")
|
|
41
42
|
.replace(/\[/g, "⟦").replace(/\]/g, "⟧")
|
|
43
|
+
.replace(/"/g, "'")
|
|
44
|
+
.replace(/</g, "<").replace(/>/g, ">")
|
|
42
45
|
.slice(0, 100);
|
|
43
46
|
}
|
package/src/tools/account.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { getConfig as getAppConfig } from "../runtime.js";
|
|
|
12
12
|
export function createAccountTool() {
|
|
13
13
|
return {
|
|
14
14
|
name: "botcord_account",
|
|
15
|
+
label: "Manage Account",
|
|
15
16
|
description:
|
|
16
17
|
"Manage your own BotCord agent: view identity, update profile, get/set message policy, check message delivery status.",
|
|
17
18
|
parameters: {
|
package/src/tools/bind.ts
CHANGED
package/src/tools/contacts.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { getConfig as getAppConfig } from "../runtime.js";
|
|
|
12
12
|
export function createContactsTool() {
|
|
13
13
|
return {
|
|
14
14
|
name: "botcord_contacts",
|
|
15
|
+
label: "Manage Contacts",
|
|
15
16
|
description: "Manage BotCord contacts: list/remove contacts, send/accept/reject requests, block/unblock agents.",
|
|
16
17
|
parameters: {
|
|
17
18
|
type: "object" as const,
|
package/src/tools/directory.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { getConfig as getAppConfig } from "../runtime.js";
|
|
|
12
12
|
export function createDirectoryTool() {
|
|
13
13
|
return {
|
|
14
14
|
name: "botcord_directory",
|
|
15
|
+
label: "Search Directory",
|
|
15
16
|
description: "Look up agents, discover public rooms, and query message history on BotCord.",
|
|
16
17
|
parameters: {
|
|
17
18
|
type: "object" as const,
|
package/src/tools/messaging.ts
CHANGED
|
@@ -77,6 +77,7 @@ async function uploadLocalFiles(
|
|
|
77
77
|
export function createMessagingTool() {
|
|
78
78
|
return {
|
|
79
79
|
name: "botcord_send",
|
|
80
|
+
label: "Send Message",
|
|
80
81
|
description:
|
|
81
82
|
"Send a message to another agent or room via BotCord. " +
|
|
82
83
|
"Use ag_* for direct messages, rm_* for rooms. " +
|
|
@@ -192,6 +193,7 @@ export function createMessagingTool() {
|
|
|
192
193
|
export function createUploadTool() {
|
|
193
194
|
return {
|
|
194
195
|
name: "botcord_upload",
|
|
196
|
+
label: "Upload Files",
|
|
195
197
|
description:
|
|
196
198
|
"Upload one or more local files to BotCord Hub. " +
|
|
197
199
|
"Returns file URLs that can be used later in botcord_send's file_urls parameter. " +
|
package/src/tools/notify.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { deliverNotification } from "../inbound.js";
|
|
|
11
11
|
export function createNotifyTool() {
|
|
12
12
|
return {
|
|
13
13
|
name: "botcord_notify",
|
|
14
|
+
label: "Send Notification",
|
|
14
15
|
description:
|
|
15
16
|
"Send a notification to the owner's configured channel (e.g. Telegram, Discord). " +
|
|
16
17
|
"Use this when you receive an important BotCord message that the owner should know about — " +
|
package/src/tools/payment.ts
CHANGED
|
@@ -194,6 +194,7 @@ function sanitizeLedger(data: any): any {
|
|
|
194
194
|
export function createPaymentTool(opts?: { name?: string; description?: string }) {
|
|
195
195
|
return {
|
|
196
196
|
name: opts?.name || "botcord_payment",
|
|
197
|
+
label: "Manage Payments",
|
|
197
198
|
description:
|
|
198
199
|
opts?.description ||
|
|
199
200
|
"Manage BotCord coin payments and transactions: verify recipients, check balance, view ledger, transfer coins, create topups and withdrawals, cancel withdrawals, and query transaction status.",
|
package/src/tools/rooms.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { getConfig as getAppConfig } from "../runtime.js";
|
|
|
12
12
|
export function createRoomsTool() {
|
|
13
13
|
return {
|
|
14
14
|
name: "botcord_rooms",
|
|
15
|
+
label: "Manage Rooms",
|
|
15
16
|
description:
|
|
16
17
|
"Manage BotCord rooms: create, list, join, leave, update, invite/remove members, " +
|
|
17
18
|
"set permissions, promote/transfer/dissolve, discover public rooms.",
|
|
@@ -47,6 +47,7 @@ function formatSubscriptionList(subscriptions: any[]): string {
|
|
|
47
47
|
export function createSubscriptionTool() {
|
|
48
48
|
return {
|
|
49
49
|
name: "botcord_subscription",
|
|
50
|
+
label: "Manage Subscriptions",
|
|
50
51
|
description:
|
|
51
52
|
"Create subscription products priced in BotCord coin, subscribe to products, list active subscriptions, and manage cancellation or product archiving.",
|
|
52
53
|
parameters: {
|
package/src/tools/topics.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { getConfig as getAppConfig } from "../runtime.js";
|
|
|
12
12
|
export function createTopicsTool() {
|
|
13
13
|
return {
|
|
14
14
|
name: "botcord_topics",
|
|
15
|
+
label: "Manage Topics",
|
|
15
16
|
description:
|
|
16
17
|
"Manage topics within BotCord rooms. Topics are goal-driven conversation units " +
|
|
17
18
|
"with lifecycle states: open → completed/failed/expired.",
|