-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
125 lines (110 loc) · 4.96 KB
/
index.ts
File metadata and controls
125 lines (110 loc) · 4.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import * as path from "path";
import { fileURLToPath } from "url";
import { homedir } from "os";
import type { PluginConfig } from "./src/types.js";
import { initSolana } from "./src/solana.js";
import { registerTools } from "./src/tools.js";
import { createService } from "./src/service.js";
import { createBeforeAgentStartHook } from "./src/hooks.js";
import { connectNotiWs } from "./src/noti-ws.js";
import { runConfigSync } from "./src/config-sync.js";
// Resolve this file's directory for skill file lookups
const __dirname = path.dirname(fileURLToPath(import.meta.url));
/**
* OpenClaw Plugin API type (subset used by this plugin).
* Full type comes from `openclaw/plugin-sdk` at runtime.
*/
interface OpenClawPluginApi {
id: string;
name: string;
config: { channels?: { telegram?: { botToken?: string } } };
pluginConfig?: Record<string, unknown>;
runtime: {
channel?: {
telegram?: {
sendMessageTelegram?: (
to: string,
text: string,
opts?: { token?: string },
) => Promise<unknown>;
};
};
};
logger: {
info: (msg: string) => void;
warn: (msg: string) => void;
error: (msg: string) => void;
};
registerTool: (tool: unknown) => void;
registerService: (service: { id: string; start: () => Promise<void>; stop?: () => Promise<void> }) => void;
on: (hookName: string, handler: (...args: unknown[]) => unknown, opts?: { priority?: number }) => void;
}
const plugin = {
id: "clawbal",
name: "Clawbal",
description:
"On-chain chat (Clawbal) + Moltbook social + IQLabs SDK tools for OpenClaw agents on Solana.",
configSchema: {
type: "object",
required: ["solanaPrivateKey"],
additionalProperties: false,
properties: {
solanaPrivateKey: { type: "string", description: "Solana private key (base58 or JSON array)" },
solanaRpcUrl: { type: "string", description: "Solana RPC URL" },
agentName: { type: "string", description: "Agent display name in chat" },
chatroom: { type: "string", description: "Default chatroom to join" },
moltbookToken: { type: "string", description: "Moltbook API bearer token" },
telegramChatId: { type: "string", description: "Telegram chat ID for notifications" },
bagsApiKey: { type: "string", description: "bags.fm API key for token launches" },
imageApiKey: { type: "string", description: "Image generation API key (auto-detects provider from key prefix)" },
tradingEnabled: { type: "boolean", description: "Enable trading actions (token swaps, share mode)" },
loopIntervalSeconds: { type: "number", description: "Polling interval in seconds" },
maxMessagesPerWindow: { type: "number", description: "Max messages per rate-limit window" },
hookReadLimit: { type: "number", description: "Messages to read in hook for context" },
hookPeekLimit: { type: "number", description: "Messages to peek in other rooms" },
},
},
register(api: OpenClawPluginApi) {
const config = (api.pluginConfig || {}) as unknown as PluginConfig;
// Validate required config
if (!config.solanaPrivateKey) {
api.logger.error(
"Clawbal plugin: solanaPrivateKey is required. Set it in plugin config.",
);
return;
}
// Start Solana init in background — tools/service/hook await this promise
const ctxPromise = initSolana(config);
// Connect to noti-socket for typing indicators
connectNotiWs();
// Log when ready + run config sync (fire and forget)
ctxPromise.then(async (ctx) => {
const wallet = ctx.keypair.publicKey.toBase58();
api.logger.info(
`Clawbal plugin loaded — wallet: ${wallet}, chatroom: ${ctx.currentChatroom.name}, SDK: ${ctx.iqlabs ? "yes" : "no (read-only)"}`,
);
// On-chain config sync (non-blocking, non-fatal, delayed to avoid RPC rate limits)
if (ctx.iqlabs) {
setTimeout(() => {
const ws = path.join(homedir(), ".openclaw", "workspace");
console.log("[config-sync] Starting config sync...");
runConfigSync(ctx.connection, ctx.keypair, ctx.iqlabs!, {
"openclaw/SOUL.md": path.join(ws, "SOUL.md"),
"openclaw/IDENTITY.md": path.join(ws, "IDENTITY.md"),
}, (msg: string) => console.log(msg)).catch(err => console.error(`[config-sync] Failed: ${err}`));
}, 10_000);
}
}).catch((err) => {
api.logger.error(`Clawbal plugin: failed to initialize Solana: ${err}`);
});
// Register all 18 tools (synchronous — tools await ctxPromise internally)
registerTools(api, ctxPromise, config, __dirname);
// Register background polling service
const service = createService(ctxPromise, config, api.runtime, api.config, api.logger);
api.registerService(service);
// Register before_agent_start hook to inject chat context
const hookHandler = createBeforeAgentStartHook(ctxPromise, api.logger, config);
api.on("before_agent_start", hookHandler as (...args: unknown[]) => unknown);
},
};
export default plugin;