@kernelius/openclaw-plugin 0.1.0 → 0.2.0

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
@@ -20,6 +20,14 @@ bun add @kernelius/openclaw-plugin
20
20
 
21
21
  ## Configuration
22
22
 
23
+ This plugin supports two integration modes:
24
+
25
+ ### Option 1: Simple Webhook Mode (Recommended for Getting Started)
26
+
27
+ Uses OpenClaw's generic `/hooks/agent` endpoint with template mappings. Each webhook creates an isolated agent session.
28
+
29
+ **Best for:** Quick setup, simple workflows, disposable conversations
30
+
23
31
  Add to your OpenClaw `config.json5`:
24
32
 
25
33
  ```json5
@@ -29,7 +37,6 @@ Add to your OpenClaw `config.json5`:
29
37
  enabled: true,
30
38
  apiUrl: "https://forge-api.kernelius.com", // Optional, defaults to this
31
39
  apiKey: "forge_agent_xxx...", // Get from Forge at /settings/agents
32
- webhookSecret: "your-webhook-secret", // Optional, for signature verification
33
40
  }
34
41
  },
35
42
 
@@ -60,6 +67,42 @@ Add to your OpenClaw `config.json5`:
60
67
  }
61
68
  ```
62
69
 
70
+ Create webhooks pointing to `http://your-openclaw:18789/hooks/forge`.
71
+
72
+ ### Option 2: Gateway Mode with Persistent Sessions (Power Users)
73
+
74
+ Uses the plugin's gateway adapter for persistent conversations per issue/PR.
75
+
76
+ **Best for:** Complex workflows, conversation history, stateful interactions
77
+
78
+ Add to your OpenClaw `config.json5`:
79
+
80
+ ```json5
81
+ {
82
+ channels: {
83
+ kernelius: {
84
+ enabled: true,
85
+ apiUrl: "https://forge-api.kernelius.com",
86
+ apiKey: "forge_agent_xxx...",
87
+ webhookSecret: "your-webhook-secret", // REQUIRED for gateway mode
88
+ webhookPath: "/kernelius", // Or use webhookUrl for full URL
89
+ }
90
+ }
91
+ }
92
+ ```
93
+
94
+ Create webhooks pointing to `http://your-openclaw:18789/kernelius` (or your custom path).
95
+
96
+ **Key Differences:**
97
+
98
+ | Feature | Option 1 (Simple) | Option 2 (Gateway) |
99
+ |---------|-------------------|---------------------|
100
+ | Setup complexity | Minimal | Requires webhookSecret |
101
+ | Conversation history | Isolated per webhook | Persistent per issue/PR |
102
+ | Session management | Disposable | Stateful |
103
+ | Configuration | Hooks + templates | Channel config only |
104
+ | Best for | Quick responses | Multi-turn collaboration |
105
+
63
106
  ## Forge Setup
64
107
 
65
108
  1. **Get an API key:**
@@ -68,14 +111,28 @@ Add to your OpenClaw `config.json5`:
68
111
  - Add it to your OpenClaw config as `apiKey`
69
112
 
70
113
  2. **Create webhooks:**
114
+
115
+ **For Option 1 (Simple Mode):**
71
116
  ```bash
72
- forge webhooks create \\
73
- --repo @owner/repo \\
74
- --url "http://your-openclaw-server:18789/hooks/forge" \\
75
- --events "issue.created,issue.commented,pr.created,pr.review_requested,pr.merged" \\
117
+ forge webhooks create \
118
+ --repo @owner/repo \
119
+ --url "http://your-openclaw-server:18789/hooks/forge" \
120
+ --events "issue.created,issue.commented,pr.created,pr.review_requested,pr.merged" \
76
121
  --name "OpenClaw Integration"
77
122
  ```
78
123
 
124
+ **For Option 2 (Gateway Mode):**
125
+ ```bash
126
+ forge webhooks create \
127
+ --repo @owner/repo \
128
+ --url "http://your-openclaw-server:18789/kernelius" \
129
+ --events "issue.created,issue.commented,pr.created,pr.review_requested,pr.merged" \
130
+ --secret "your-webhook-secret" \
131
+ --name "OpenClaw Gateway"
132
+ ```
133
+
134
+ Note: The webhook secret must match `webhookSecret` in your OpenClaw config.
135
+
79
136
  3. **Test the webhook:**
80
137
  ```bash
81
138
  forge webhooks test --repo @owner/repo --id <webhook-id>
package/dist/index.js CHANGED
@@ -19,6 +19,152 @@ function getKerneliusRuntime() {
19
19
  return runtime;
20
20
  }
21
21
 
22
+ // src/inbound.ts
23
+ import crypto from "crypto";
24
+ function verifyWebhookSignature(payload, signature, secret) {
25
+ if (!secret) {
26
+ return true;
27
+ }
28
+ const expectedSignature = `sha256=${crypto.createHmac("sha256", secret).update(payload).digest("hex")}`;
29
+ return crypto.timingSafeEqual(
30
+ Buffer.from(signature),
31
+ Buffer.from(expectedSignature)
32
+ );
33
+ }
34
+ function forgePayloadToInbound(payload, accountId) {
35
+ const { event, repository, sender, issue, pullRequest, comment } = payload;
36
+ let conversationId;
37
+ let body;
38
+ let chatType = "channel";
39
+ if (issue) {
40
+ conversationId = `repo:${repository.fullName}:issue:${issue.number}`;
41
+ if (event === "issue.created") {
42
+ body = `**New Issue #${issue.number}**: ${issue.title}
43
+
44
+ ${issue.body || ""}`;
45
+ } else if (event === "issue.commented" && comment) {
46
+ body = `**Comment on Issue #${issue.number}** by @${sender.username}:
47
+
48
+ ${comment.body}`;
49
+ } else if (event === "issue.closed") {
50
+ body = `**Issue #${issue.number} closed** by @${sender.username}`;
51
+ } else if (event === "issue.reopened") {
52
+ body = `**Issue #${issue.number} reopened** by @${sender.username}`;
53
+ } else if (event === "issue.updated") {
54
+ body = `**Issue #${issue.number} updated** by @${sender.username}: ${issue.title}`;
55
+ } else {
56
+ body = `Issue #${issue.number} event: ${event}`;
57
+ }
58
+ } else if (pullRequest) {
59
+ conversationId = `repo:${repository.fullName}:pr:${pullRequest.number}`;
60
+ if (event === "pr.created") {
61
+ body = `**New Pull Request #${pullRequest.number}**: ${pullRequest.title}
62
+
63
+ ${pullRequest.body || ""}`;
64
+ } else if (event === "pr.review_requested") {
65
+ body = `**Review Requested on PR #${pullRequest.number}**: ${pullRequest.title}
66
+
67
+ Please review this pull request.`;
68
+ } else if (event === "pr.reviewed") {
69
+ body = `**PR #${pullRequest.number} reviewed** by @${sender.username}`;
70
+ } else if (event === "pr.merged") {
71
+ body = `**PR #${pullRequest.number} merged** by @${sender.username}`;
72
+ } else if (event === "pr.commented" && comment) {
73
+ body = `**Comment on PR #${pullRequest.number}** by @${sender.username}:
74
+
75
+ ${comment.body}`;
76
+ } else {
77
+ body = `Pull Request #${pullRequest.number} event: ${event}`;
78
+ }
79
+ } else {
80
+ conversationId = `repo:${repository.fullName}`;
81
+ body = `Repository event: ${event}`;
82
+ }
83
+ const timestamp = new Date(payload.timestamp).getTime();
84
+ return {
85
+ id: crypto.randomUUID(),
86
+ from: conversationId,
87
+ conversationId,
88
+ to: accountId,
89
+ accountId,
90
+ body,
91
+ pushName: sender.username,
92
+ timestamp,
93
+ chatType,
94
+ chatId: conversationId,
95
+ senderName: sender.username,
96
+ selfJid: accountId,
97
+ selfE164: null,
98
+ // Helpers (simplified for webhook-based channel)
99
+ sendComposing: async () => {
100
+ },
101
+ reply: async (text) => {
102
+ console.log(`[kernelius] Reply queued: ${text}`);
103
+ },
104
+ sendMedia: async () => {
105
+ throw new Error("Media not supported for Forge webhooks");
106
+ }
107
+ };
108
+ }
109
+ async function handleWebhookRequest(req, res, ctx) {
110
+ const chunks = [];
111
+ for await (const chunk of req) {
112
+ chunks.push(chunk);
113
+ }
114
+ const rawBody = Buffer.concat(chunks).toString("utf-8");
115
+ const signature = req.headers["x-forge-signature"];
116
+ if (ctx.account.webhookSecret) {
117
+ if (!signature) {
118
+ res.writeHead(401, { "Content-Type": "application/json" });
119
+ res.end(JSON.stringify({ error: "Missing X-Forge-Signature header" }));
120
+ return null;
121
+ }
122
+ if (!verifyWebhookSignature(rawBody, signature, ctx.account.webhookSecret)) {
123
+ res.writeHead(401, { "Content-Type": "application/json" });
124
+ res.end(JSON.stringify({ error: "Invalid signature" }));
125
+ return null;
126
+ }
127
+ }
128
+ let payload;
129
+ try {
130
+ payload = JSON.parse(rawBody);
131
+ } catch (error) {
132
+ res.writeHead(400, { "Content-Type": "application/json" });
133
+ res.end(JSON.stringify({ error: "Invalid JSON payload" }));
134
+ return null;
135
+ }
136
+ if (payload.source !== "forge") {
137
+ res.writeHead(400, { "Content-Type": "application/json" });
138
+ res.end(JSON.stringify({ error: "Invalid source" }));
139
+ return null;
140
+ }
141
+ ctx.statusSink?.({ lastInboundAt: Date.now() });
142
+ const inbound = forgePayloadToInbound(payload, ctx.account.accountId);
143
+ if (!inbound) {
144
+ res.writeHead(400, { "Content-Type": "application/json" });
145
+ res.end(JSON.stringify({ error: "Could not process event" }));
146
+ return null;
147
+ }
148
+ res.writeHead(200, { "Content-Type": "application/json" });
149
+ res.end(JSON.stringify({ success: true, event: payload.event }));
150
+ return inbound;
151
+ }
152
+ function resolveKerneliusWebhookPath(webhookPath, webhookUrl) {
153
+ if (webhookPath?.trim()) {
154
+ const path = webhookPath.trim();
155
+ return path.startsWith("/") ? path : `/${path}`;
156
+ }
157
+ if (webhookUrl?.trim()) {
158
+ try {
159
+ const parsed = new URL(webhookUrl);
160
+ return parsed.pathname || "/kernelius";
161
+ } catch {
162
+ return "/kernelius";
163
+ }
164
+ }
165
+ return "/kernelius";
166
+ }
167
+
22
168
  // src/channel.ts
23
169
  var meta = getChatChannelMeta("kernelius");
24
170
  function resolveKerneliusAccount(cfg, accountId) {
@@ -30,7 +176,9 @@ function resolveKerneliusAccount(cfg, accountId) {
30
176
  enabled: accountConfig.enabled !== false,
31
177
  apiUrl: accountConfig.apiUrl || "https://forge-api.kernelius.com",
32
178
  apiKey: accountConfig.apiKey,
33
- webhookSecret: accountConfig.webhookSecret
179
+ webhookSecret: accountConfig.webhookSecret,
180
+ webhookPath: accountConfig.webhookPath,
181
+ webhookUrl: accountConfig.webhookUrl
34
182
  };
35
183
  }
36
184
  var kerneliusPlugin = {
@@ -173,6 +321,48 @@ var kerneliusPlugin = {
173
321
  }
174
322
  return { success: true };
175
323
  }
324
+ },
325
+ gateway: {
326
+ start: async (ctx) => {
327
+ const runtime2 = getKerneliusRuntime();
328
+ const config = runtime2.config.loadConfig();
329
+ const account = resolveKerneliusAccount(config, ctx.accountId);
330
+ if (!account.webhookPath && !account.webhookUrl) {
331
+ console.log("[kernelius] No webhook path configured, skipping gateway start");
332
+ return;
333
+ }
334
+ const webhookPath = resolveKerneliusWebhookPath(
335
+ account.webhookPath,
336
+ account.webhookUrl
337
+ );
338
+ console.log(`[kernelius] Starting gateway for account ${account.accountId} at ${webhookPath}`);
339
+ ctx.registerHttpHandler({
340
+ path: webhookPath,
341
+ method: "POST",
342
+ handler: async (req, res) => {
343
+ try {
344
+ const inbound = await handleWebhookRequest(req, res, {
345
+ account,
346
+ runtime: runtime2,
347
+ statusSink: ctx.statusSink
348
+ });
349
+ if (inbound) {
350
+ await ctx.queueInbound(inbound);
351
+ }
352
+ } catch (error) {
353
+ console.error("[kernelius] Webhook handler error:", error);
354
+ if (!res.headersSent) {
355
+ res.writeHead(500, { "Content-Type": "application/json" });
356
+ res.end(JSON.stringify({ error: "Internal server error" }));
357
+ }
358
+ }
359
+ }
360
+ });
361
+ console.log(`[kernelius] Gateway started successfully at ${webhookPath}`);
362
+ },
363
+ stop: async (ctx) => {
364
+ console.log(`[kernelius] Stopping gateway for account ${ctx.accountId}`);
365
+ }
176
366
  }
177
367
  };
178
368
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/channel.ts","../src/runtime.ts"],"sourcesContent":["import type { OpenClawPluginApi } from \"openclaw/plugin-sdk\";\nimport { emptyPluginConfigSchema } from \"openclaw/plugin-sdk\";\nimport { kerneliusPlugin } from \"./channel.js\";\nimport { setKerneliusRuntime } from \"./runtime.js\";\n\nconst plugin = {\n id: \"kernelius\",\n name: \"Kernelius Forge\",\n description: \"Connect to Kernelius Forge repositories, issues, and pull requests\",\n configSchema: emptyPluginConfigSchema(),\n register(api: OpenClawPluginApi) {\n setKerneliusRuntime(api.runtime);\n api.registerChannel({ plugin: kerneliusPlugin });\n },\n};\n\nexport default plugin;\n","import type {\n ChannelPlugin,\n ChannelConfigAdapter,\n} from \"openclaw/plugin-sdk\";\nimport {\n getChatChannelMeta,\n DEFAULT_ACCOUNT_ID,\n} from \"openclaw/plugin-sdk\";\nimport { getKerneliusRuntime } from \"./runtime.js\";\nimport type { KerneliusConfig } from \"./types.js\";\n\nconst meta = getChatChannelMeta(\"kernelius\");\n\n// Resolve Kernelius account configuration\nfunction resolveKerneliusAccount(cfg: any, accountId?: string) {\n const effectiveAccountId = accountId || DEFAULT_ACCOUNT_ID;\n const channelConfig = cfg.channels?.kernelius || {};\n\n // Support both top-level config and accounts structure\n const accountConfig = channelConfig.accounts?.[effectiveAccountId] || channelConfig;\n\n return {\n accountId: effectiveAccountId,\n enabled: accountConfig.enabled !== false,\n apiUrl: accountConfig.apiUrl || \"https://forge-api.kernelius.com\",\n apiKey: accountConfig.apiKey,\n webhookSecret: accountConfig.webhookSecret,\n };\n}\n\nexport const kerneliusPlugin: ChannelPlugin = {\n id: \"kernelius\",\n meta: {\n ...meta,\n name: \"Kernelius Forge\",\n emoji: \"🔥\",\n description: \"Git platform for human-agent collaboration\",\n },\n capabilities: {\n chatTypes: [\"direct\", \"channel\", \"thread\"],\n reactions: true,\n threads: true,\n media: false,\n nativeCommands: false,\n },\n reload: { configPrefixes: [\"channels.kernelius\"] },\n config: {\n listAccountIds: (cfg) => {\n const channelConfig = cfg.channels?.kernelius;\n if (!channelConfig) return [];\n if (channelConfig.accounts) {\n return Object.keys(channelConfig.accounts);\n }\n return [DEFAULT_ACCOUNT_ID];\n },\n resolveAccount: (cfg, accountId) => resolveKerneliusAccount(cfg, accountId),\n defaultAccountId: () => DEFAULT_ACCOUNT_ID,\n setAccountEnabled: ({ cfg, accountId, enabled }) => {\n const effectiveAccountId = accountId || DEFAULT_ACCOUNT_ID;\n if (!cfg.channels) cfg.channels = {};\n if (!cfg.channels.kernelius) cfg.channels.kernelius = {};\n\n if (cfg.channels.kernelius.accounts?.[effectiveAccountId]) {\n cfg.channels.kernelius.accounts[effectiveAccountId].enabled = enabled;\n } else {\n cfg.channels.kernelius.enabled = enabled;\n }\n return cfg;\n },\n deleteAccount: ({ cfg, accountId }) => {\n if (accountId && accountId !== DEFAULT_ACCOUNT_ID) {\n delete cfg.channels?.kernelius?.accounts?.[accountId];\n }\n return cfg;\n },\n isConfigured: (account: any) => Boolean(account.apiKey),\n describeAccount: (account: any) => ({\n accountId: account.accountId,\n enabled: account.enabled,\n configured: Boolean(account.apiKey),\n apiUrl: account.apiUrl,\n }),\n resolveAllowFrom: () => [],\n formatAllowFrom: ({ allowFrom }) => allowFrom,\n },\n messaging: {\n // Send message to Forge (comment on issue/PR)\n send: async (ctx, action) => {\n const runtime = getKerneliusRuntime();\n const account = resolveKerneliusAccount(runtime.config.loadConfig(), action.accountId);\n\n if (!account.apiKey) {\n throw new Error(\"Kernelius API key not configured\");\n }\n\n // Parse target: repo:owner/name:issue:42 or repo:owner/name:pr:10\n const target = action.to;\n const match = target.match(/^repo:([^/]+)\\/([^:]+):(issue|pr):(\\d+)$/);\n\n if (!match) {\n throw new Error(`Invalid Kernelius target format: ${target}. Expected: repo:owner/name:issue:42 or repo:owner/name:pr:10`);\n }\n\n const [, owner, repo, type, number] = match;\n const endpoint = type === \"issue\"\n ? `/api/repositories/${owner}/${repo}/issues/${number}/comments`\n : `/api/pulls/${number}/comments`;\n\n const response = await fetch(`${account.apiUrl}${endpoint}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${account.apiKey}`,\n },\n body: JSON.stringify({\n body: action.body,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to send message to Kernelius: ${response.status} ${error}`);\n }\n\n const result = await response.json();\n\n return {\n messageId: result.id,\n timestamp: new Date(result.createdAt),\n };\n },\n\n // React to message (not implemented for Forge yet)\n react: async () => {\n throw new Error(\"Reactions not yet implemented for Kernelius Forge\");\n },\n },\n actions: {\n listActions: () => [\"send\"],\n extractToolSend: ({ args }) => {\n const action = typeof args.action === \"string\" ? args.action.trim() : \"\";\n if (action !== \"sendMessage\") {\n return null;\n }\n const to = typeof args.to === \"string\" ? args.to : undefined;\n if (!to) {\n return null;\n }\n const accountId = typeof args.accountId === \"string\" ? args.accountId.trim() : undefined;\n return { to, accountId };\n },\n handleAction: async ({ action, params, cfg, accountId }) => {\n if (action !== \"send\") {\n throw new Error(`Unknown action: ${action}`);\n }\n\n const to = typeof params.to === \"string\" ? params.to : undefined;\n const message = typeof params.message === \"string\" ? params.message : undefined;\n\n if (!to || !message) {\n throw new Error(\"Missing required parameters: to, message\");\n }\n\n const account = resolveKerneliusAccount(cfg, accountId);\n\n if (!account.apiKey) {\n throw new Error(\"Kernelius API key not configured\");\n }\n\n // Parse target and send\n const match = to.match(/^repo:([^/]+)\\/([^:]+):(issue|pr):(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid target format: ${to}`);\n }\n\n const [, owner, repo, type, number] = match;\n const endpoint = type === \"issue\"\n ? `/api/repositories/${owner}/${repo}/issues/${number}/comments`\n : `/api/pulls/${number}/comments`;\n\n const response = await fetch(`${account.apiUrl}${endpoint}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${account.apiKey}`,\n },\n body: JSON.stringify({ body: message }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to send: ${response.status}`);\n }\n\n return { success: true };\n },\n },\n};\n","import type { OpenClawRuntime } from \"openclaw/plugin-sdk\";\n\nlet runtime: OpenClawRuntime | null = null;\n\nexport function setKerneliusRuntime(rt: OpenClawRuntime): void {\n runtime = rt;\n}\n\nexport function getKerneliusRuntime(): OpenClawRuntime {\n if (!runtime) {\n throw new Error(\"Kernelius runtime not initialized\");\n }\n return runtime;\n}\n"],"mappings":"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","names":["runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/channel.ts","../src/runtime.ts","../src/inbound.ts"],"sourcesContent":["import type { OpenClawPluginApi } from \"openclaw/plugin-sdk\";\nimport { emptyPluginConfigSchema } from \"openclaw/plugin-sdk\";\nimport { kerneliusPlugin } from \"./channel.js\";\nimport { setKerneliusRuntime } from \"./runtime.js\";\n\nconst plugin = {\n id: \"kernelius\",\n name: \"Kernelius Forge\",\n description: \"Connect to Kernelius Forge repositories, issues, and pull requests\",\n configSchema: emptyPluginConfigSchema(),\n register(api: OpenClawPluginApi) {\n setKerneliusRuntime(api.runtime);\n api.registerChannel({ plugin: kerneliusPlugin });\n },\n};\n\nexport default plugin;\n","import type {\n ChannelPlugin,\n ChannelConfigAdapter,\n} from \"openclaw/plugin-sdk\";\nimport {\n getChatChannelMeta,\n DEFAULT_ACCOUNT_ID,\n} from \"openclaw/plugin-sdk\";\nimport { getKerneliusRuntime } from \"./runtime.js\";\nimport type { KerneliusConfig, KerneliusResolvedAccount } from \"./types.js\";\nimport {\n handleWebhookRequest,\n resolveKerneliusWebhookPath,\n} from \"./inbound.js\";\n\nconst meta = getChatChannelMeta(\"kernelius\");\n\n// Resolve Kernelius account configuration\nfunction resolveKerneliusAccount(cfg: any, accountId?: string): KerneliusResolvedAccount {\n const effectiveAccountId = accountId || DEFAULT_ACCOUNT_ID;\n const channelConfig = cfg.channels?.kernelius || {};\n\n // Support both top-level config and accounts structure\n const accountConfig = channelConfig.accounts?.[effectiveAccountId] || channelConfig;\n\n return {\n accountId: effectiveAccountId,\n enabled: accountConfig.enabled !== false,\n apiUrl: accountConfig.apiUrl || \"https://forge-api.kernelius.com\",\n apiKey: accountConfig.apiKey,\n webhookSecret: accountConfig.webhookSecret,\n webhookPath: accountConfig.webhookPath,\n webhookUrl: accountConfig.webhookUrl,\n };\n}\n\nexport const kerneliusPlugin: ChannelPlugin = {\n id: \"kernelius\",\n meta: {\n ...meta,\n name: \"Kernelius Forge\",\n emoji: \"🔥\",\n description: \"Git platform for human-agent collaboration\",\n },\n capabilities: {\n chatTypes: [\"direct\", \"channel\", \"thread\"],\n reactions: true,\n threads: true,\n media: false,\n nativeCommands: false,\n },\n reload: { configPrefixes: [\"channels.kernelius\"] },\n config: {\n listAccountIds: (cfg) => {\n const channelConfig = cfg.channels?.kernelius;\n if (!channelConfig) return [];\n if (channelConfig.accounts) {\n return Object.keys(channelConfig.accounts);\n }\n return [DEFAULT_ACCOUNT_ID];\n },\n resolveAccount: (cfg, accountId) => resolveKerneliusAccount(cfg, accountId),\n defaultAccountId: () => DEFAULT_ACCOUNT_ID,\n setAccountEnabled: ({ cfg, accountId, enabled }) => {\n const effectiveAccountId = accountId || DEFAULT_ACCOUNT_ID;\n if (!cfg.channels) cfg.channels = {};\n if (!cfg.channels.kernelius) cfg.channels.kernelius = {};\n\n if (cfg.channels.kernelius.accounts?.[effectiveAccountId]) {\n cfg.channels.kernelius.accounts[effectiveAccountId].enabled = enabled;\n } else {\n cfg.channels.kernelius.enabled = enabled;\n }\n return cfg;\n },\n deleteAccount: ({ cfg, accountId }) => {\n if (accountId && accountId !== DEFAULT_ACCOUNT_ID) {\n delete cfg.channels?.kernelius?.accounts?.[accountId];\n }\n return cfg;\n },\n isConfigured: (account: any) => Boolean(account.apiKey),\n describeAccount: (account: any) => ({\n accountId: account.accountId,\n enabled: account.enabled,\n configured: Boolean(account.apiKey),\n apiUrl: account.apiUrl,\n }),\n resolveAllowFrom: () => [],\n formatAllowFrom: ({ allowFrom }) => allowFrom,\n },\n messaging: {\n // Send message to Forge (comment on issue/PR)\n send: async (ctx, action) => {\n const runtime = getKerneliusRuntime();\n const account = resolveKerneliusAccount(runtime.config.loadConfig(), action.accountId);\n\n if (!account.apiKey) {\n throw new Error(\"Kernelius API key not configured\");\n }\n\n // Parse target: repo:owner/name:issue:42 or repo:owner/name:pr:10\n const target = action.to;\n const match = target.match(/^repo:([^/]+)\\/([^:]+):(issue|pr):(\\d+)$/);\n\n if (!match) {\n throw new Error(`Invalid Kernelius target format: ${target}. Expected: repo:owner/name:issue:42 or repo:owner/name:pr:10`);\n }\n\n const [, owner, repo, type, number] = match;\n const endpoint = type === \"issue\"\n ? `/api/repositories/${owner}/${repo}/issues/${number}/comments`\n : `/api/pulls/${number}/comments`;\n\n const response = await fetch(`${account.apiUrl}${endpoint}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${account.apiKey}`,\n },\n body: JSON.stringify({\n body: action.body,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to send message to Kernelius: ${response.status} ${error}`);\n }\n\n const result = await response.json();\n\n return {\n messageId: result.id,\n timestamp: new Date(result.createdAt),\n };\n },\n\n // React to message (not implemented for Forge yet)\n react: async () => {\n throw new Error(\"Reactions not yet implemented for Kernelius Forge\");\n },\n },\n actions: {\n listActions: () => [\"send\"],\n extractToolSend: ({ args }) => {\n const action = typeof args.action === \"string\" ? args.action.trim() : \"\";\n if (action !== \"sendMessage\") {\n return null;\n }\n const to = typeof args.to === \"string\" ? args.to : undefined;\n if (!to) {\n return null;\n }\n const accountId = typeof args.accountId === \"string\" ? args.accountId.trim() : undefined;\n return { to, accountId };\n },\n handleAction: async ({ action, params, cfg, accountId }) => {\n if (action !== \"send\") {\n throw new Error(`Unknown action: ${action}`);\n }\n\n const to = typeof params.to === \"string\" ? params.to : undefined;\n const message = typeof params.message === \"string\" ? params.message : undefined;\n\n if (!to || !message) {\n throw new Error(\"Missing required parameters: to, message\");\n }\n\n const account = resolveKerneliusAccount(cfg, accountId);\n\n if (!account.apiKey) {\n throw new Error(\"Kernelius API key not configured\");\n }\n\n // Parse target and send\n const match = to.match(/^repo:([^/]+)\\/([^:]+):(issue|pr):(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid target format: ${to}`);\n }\n\n const [, owner, repo, type, number] = match;\n const endpoint = type === \"issue\"\n ? `/api/repositories/${owner}/${repo}/issues/${number}/comments`\n : `/api/pulls/${number}/comments`;\n\n const response = await fetch(`${account.apiUrl}${endpoint}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${account.apiKey}`,\n },\n body: JSON.stringify({ body: message }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to send: ${response.status}`);\n }\n\n return { success: true };\n },\n },\n gateway: {\n start: async (ctx) => {\n const runtime = getKerneliusRuntime();\n const config = runtime.config.loadConfig();\n const account = resolveKerneliusAccount(config, ctx.accountId);\n\n // Only start gateway if webhook path/url is configured\n if (!account.webhookPath && !account.webhookUrl) {\n console.log(\"[kernelius] No webhook path configured, skipping gateway start\");\n return;\n }\n\n const webhookPath = resolveKerneliusWebhookPath(\n account.webhookPath,\n account.webhookUrl\n );\n\n console.log(`[kernelius] Starting gateway for account ${account.accountId} at ${webhookPath}`);\n\n // Register HTTP handler for webhooks\n ctx.registerHttpHandler({\n path: webhookPath,\n method: \"POST\",\n handler: async (req, res) => {\n try {\n const inbound = await handleWebhookRequest(req, res, {\n account,\n runtime,\n statusSink: ctx.statusSink,\n });\n\n if (inbound) {\n // Queue message for agent processing\n await ctx.queueInbound(inbound);\n }\n } catch (error) {\n console.error(\"[kernelius] Webhook handler error:\", error);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal server error\" }));\n }\n }\n },\n });\n\n console.log(`[kernelius] Gateway started successfully at ${webhookPath}`);\n },\n stop: async (ctx) => {\n console.log(`[kernelius] Stopping gateway for account ${ctx.accountId}`);\n // Cleanup is handled by OpenClaw when unregistering handlers\n },\n },\n};\n","import type { OpenClawRuntime } from \"openclaw/plugin-sdk\";\n\nlet runtime: OpenClawRuntime | null = null;\n\nexport function setKerneliusRuntime(rt: OpenClawRuntime): void {\n runtime = rt;\n}\n\nexport function getKerneliusRuntime(): OpenClawRuntime {\n if (!runtime) {\n throw new Error(\"Kernelius runtime not initialized\");\n }\n return runtime;\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { WebInboundMessage } from \"openclaw/plugin-sdk\";\nimport type { ForgeWebhookPayload, KerneliusResolvedAccount } from \"./types.js\";\nimport crypto from \"node:crypto\";\n\nexport interface InboundHandlerContext {\n account: KerneliusResolvedAccount;\n runtime: any;\n statusSink?: (patch: { lastInboundAt?: number }) => void;\n}\n\n/**\n * Verify webhook signature\n */\nexport function verifyWebhookSignature(\n payload: string,\n signature: string,\n secret: string\n): boolean {\n if (!secret) {\n return true; // No secret configured, skip verification\n }\n\n const expectedSignature = `sha256=${crypto\n .createHmac(\"sha256\", secret)\n .update(payload)\n .digest(\"hex\")}`;\n\n return crypto.timingSafeEqual(\n Buffer.from(signature),\n Buffer.from(expectedSignature)\n );\n}\n\n/**\n * Convert Forge webhook payload to WebInboundMessage\n */\nexport function forgePayloadToInbound(\n payload: ForgeWebhookPayload,\n accountId: string\n): WebInboundMessage | null {\n const { event, repository, sender, issue, pullRequest, comment } = payload;\n\n // Determine conversation ID based on event type\n let conversationId: string;\n let body: string;\n let chatType: \"direct\" | \"group\" = \"channel\";\n\n if (issue) {\n conversationId = `repo:${repository.fullName}:issue:${issue.number}`;\n\n if (event === \"issue.created\") {\n body = `**New Issue #${issue.number}**: ${issue.title}\\n\\n${issue.body || \"\"}`;\n } else if (event === \"issue.commented\" && comment) {\n body = `**Comment on Issue #${issue.number}** by @${sender.username}:\\n\\n${comment.body}`;\n } else if (event === \"issue.closed\") {\n body = `**Issue #${issue.number} closed** by @${sender.username}`;\n } else if (event === \"issue.reopened\") {\n body = `**Issue #${issue.number} reopened** by @${sender.username}`;\n } else if (event === \"issue.updated\") {\n body = `**Issue #${issue.number} updated** by @${sender.username}: ${issue.title}`;\n } else {\n body = `Issue #${issue.number} event: ${event}`;\n }\n } else if (pullRequest) {\n conversationId = `repo:${repository.fullName}:pr:${pullRequest.number}`;\n\n if (event === \"pr.created\") {\n body = `**New Pull Request #${pullRequest.number}**: ${pullRequest.title}\\n\\n${pullRequest.body || \"\"}`;\n } else if (event === \"pr.review_requested\") {\n body = `**Review Requested on PR #${pullRequest.number}**: ${pullRequest.title}\\n\\nPlease review this pull request.`;\n } else if (event === \"pr.reviewed\") {\n body = `**PR #${pullRequest.number} reviewed** by @${sender.username}`;\n } else if (event === \"pr.merged\") {\n body = `**PR #${pullRequest.number} merged** by @${sender.username}`;\n } else if (event === \"pr.commented\" && comment) {\n body = `**Comment on PR #${pullRequest.number}** by @${sender.username}:\\n\\n${comment.body}`;\n } else {\n body = `Pull Request #${pullRequest.number} event: ${event}`;\n }\n } else {\n // Repository-level event\n conversationId = `repo:${repository.fullName}`;\n body = `Repository event: ${event}`;\n }\n\n const timestamp = new Date(payload.timestamp).getTime();\n\n return {\n id: crypto.randomUUID(),\n from: conversationId,\n conversationId,\n to: accountId,\n accountId,\n body,\n pushName: sender.username,\n timestamp,\n chatType,\n chatId: conversationId,\n senderName: sender.username,\n selfJid: accountId,\n selfE164: null,\n // Helpers (simplified for webhook-based channel)\n sendComposing: async () => {},\n reply: async (text: string) => {\n // Would send back to Forge, but we handle that via messaging adapter\n console.log(`[kernelius] Reply queued: ${text}`);\n },\n sendMedia: async () => {\n throw new Error(\"Media not supported for Forge webhooks\");\n },\n };\n}\n\n/**\n * Handle incoming webhook request\n */\nexport async function handleWebhookRequest(\n req: IncomingMessage,\n res: ServerResponse,\n ctx: InboundHandlerContext\n): Promise<WebInboundMessage | null> {\n // Read request body\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk as Buffer);\n }\n const rawBody = Buffer.concat(chunks).toString(\"utf-8\");\n\n // Verify signature if secret is configured\n const signature = req.headers[\"x-forge-signature\"] as string;\n if (ctx.account.webhookSecret) {\n if (!signature) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing X-Forge-Signature header\" }));\n return null;\n }\n\n if (!verifyWebhookSignature(rawBody, signature, ctx.account.webhookSecret)) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid signature\" }));\n return null;\n }\n }\n\n // Parse payload\n let payload: ForgeWebhookPayload;\n try {\n payload = JSON.parse(rawBody);\n } catch (error) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON payload\" }));\n return null;\n }\n\n // Verify source is from Forge\n if (payload.source !== \"forge\") {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid source\" }));\n return null;\n }\n\n // Update status\n ctx.statusSink?.({ lastInboundAt: Date.now() });\n\n // Convert to WebInboundMessage\n const inbound = forgePayloadToInbound(payload, ctx.account.accountId);\n\n if (!inbound) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Could not process event\" }));\n return null;\n }\n\n // Respond success\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ success: true, event: payload.event }));\n\n return inbound;\n}\n\n/**\n * Resolve webhook path from config\n */\nexport function resolveKerneliusWebhookPath(\n webhookPath?: string,\n webhookUrl?: string\n): string {\n if (webhookPath?.trim()) {\n const path = webhookPath.trim();\n return path.startsWith(\"/\") ? path : `/${path}`;\n }\n\n if (webhookUrl?.trim()) {\n try {\n const parsed = new URL(webhookUrl);\n return parsed.pathname || \"/kernelius\";\n } catch {\n return \"/kernelius\";\n }\n }\n\n return \"/kernelius\";\n}\n"],"mappings":"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","names":["runtime"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernelius/openclaw-plugin",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "OpenClaw channel plugin for Kernelius Forge - enables agents to work with repositories, issues, and pull requests",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -41,11 +41,13 @@
41
41
  "typescript": "^5.7.2"
42
42
  },
43
43
  "openclaw": {
44
- "extensions": ["./dist/index.js"]
44
+ "extensions": [
45
+ "./dist/index.js"
46
+ ]
45
47
  },
46
48
  "repository": {
47
49
  "type": "git",
48
- "url": "https://github.com/kernelius-hq/openclaw-kernelius-plugin.git"
50
+ "url": "git+https://github.com/kernelius-hq/openclaw-kernelius-plugin.git"
49
51
  },
50
52
  "bugs": {
51
53
  "url": "https://github.com/kernelius-hq/openclaw-kernelius-plugin/issues"