@kernelius/openclaw-plugin 0.2.1 → 0.2.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/dist/index.js CHANGED
@@ -35,25 +35,32 @@ function forgePayloadToInbound(payload, accountId) {
35
35
  const { event, repository, sender, issue, pullRequest, comment } = payload;
36
36
  let conversationId;
37
37
  let body;
38
- let chatType = "channel";
38
+ let messageId;
39
+ let chatType = "group";
39
40
  if (issue) {
40
41
  conversationId = `repo:${repository.fullName}:issue:${issue.number}`;
41
42
  if (event === "issue.created") {
42
43
  body = `**New Issue #${issue.number}**: ${issue.title}
43
44
 
44
45
  ${issue.body || ""}`;
46
+ messageId = `issue:${issue.id}`;
45
47
  } else if (event === "issue.commented" && comment) {
46
48
  body = `**Comment on Issue #${issue.number}** by @${sender.username}:
47
49
 
48
50
  ${comment.body}`;
51
+ messageId = `issue_comment:${comment.id}`;
49
52
  } else if (event === "issue.closed") {
50
53
  body = `**Issue #${issue.number} closed** by @${sender.username}`;
54
+ messageId = `issue:${issue.id}`;
51
55
  } else if (event === "issue.reopened") {
52
56
  body = `**Issue #${issue.number} reopened** by @${sender.username}`;
57
+ messageId = `issue:${issue.id}`;
53
58
  } else if (event === "issue.updated") {
54
59
  body = `**Issue #${issue.number} updated** by @${sender.username}: ${issue.title}`;
60
+ messageId = `issue:${issue.id}`;
55
61
  } else {
56
62
  body = `Issue #${issue.number} event: ${event}`;
63
+ messageId = `issue:${issue.id}`;
57
64
  }
58
65
  } else if (pullRequest) {
59
66
  conversationId = `repo:${repository.fullName}:pr:${pullRequest.number}`;
@@ -61,20 +68,32 @@ ${comment.body}`;
61
68
  body = `**New Pull Request #${pullRequest.number}**: ${pullRequest.title}
62
69
 
63
70
  ${pullRequest.body || ""}`;
71
+ messageId = `pr:${pullRequest.id}`;
64
72
  } else if (event === "pr.review_requested") {
65
73
  body = `**Review Requested on PR #${pullRequest.number}**: ${pullRequest.title}
66
74
 
67
75
  Please review this pull request.`;
76
+ messageId = `pr:${pullRequest.id}`;
68
77
  } else if (event === "pr.reviewed") {
69
78
  body = `**PR #${pullRequest.number} reviewed** by @${sender.username}`;
79
+ messageId = `pr:${pullRequest.id}`;
70
80
  } else if (event === "pr.merged") {
71
81
  body = `**PR #${pullRequest.number} merged** by @${sender.username}`;
82
+ messageId = `pr:${pullRequest.id}`;
72
83
  } else if (event === "pr.commented" && comment) {
73
84
  body = `**Comment on PR #${pullRequest.number}** by @${sender.username}:
74
85
 
75
86
  ${comment.body}`;
87
+ messageId = `pr_comment:${comment.id}`;
88
+ } else if (event === "pr.closed") {
89
+ body = `**PR #${pullRequest.number} closed** by @${sender.username}`;
90
+ messageId = `pr:${pullRequest.id}`;
91
+ } else if (event === "pr.reopened") {
92
+ body = `**PR #${pullRequest.number} reopened** by @${sender.username}`;
93
+ messageId = `pr:${pullRequest.id}`;
76
94
  } else {
77
95
  body = `Pull Request #${pullRequest.number} event: ${event}`;
96
+ messageId = `pr:${pullRequest.id}`;
78
97
  }
79
98
  } else {
80
99
  conversationId = `repo:${repository.fullName}`;
@@ -95,6 +114,8 @@ ${comment.body}`;
95
114
  senderName: sender.username,
96
115
  selfJid: accountId,
97
116
  selfE164: null,
117
+ messageId,
118
+ // For reactions support
98
119
  // Helpers (simplified for webhook-based channel)
99
120
  sendComposing: async () => {
100
121
  },
@@ -249,7 +270,7 @@ var kerneliusPlugin = {
249
270
  throw new Error(`Invalid Kernelius target format: ${target}. Expected: repo:owner/name:issue:42 or repo:owner/name:pr:10`);
250
271
  }
251
272
  const [, owner, repo, type, number] = match;
252
- const endpoint = type === "issue" ? `/api/repositories/${owner}/${repo}/issues/${number}/comments` : `/api/pulls/${number}/comments`;
273
+ const endpoint = type === "issue" ? `/api/repositories/${owner}/${repo}/issues/${number}/comments` : `/api/repositories/${owner}/${repo}/pulls/${number}/comments`;
253
274
  const response = await fetch(`${account.apiUrl}${endpoint}`, {
254
275
  method: "POST",
255
276
  headers: {
@@ -270,13 +291,48 @@ var kerneliusPlugin = {
270
291
  timestamp: new Date(result.createdAt)
271
292
  };
272
293
  },
273
- // React to message (not implemented for Forge yet)
274
- react: async () => {
275
- throw new Error("Reactions not yet implemented for Kernelius Forge");
294
+ // React to a comment with an emoji
295
+ // Valid emojis: +1, -1, laugh, hooray, confused, heart, rocket, eyes
296
+ react: async (ctx, action) => {
297
+ const runtime2 = getKerneliusRuntime();
298
+ const account = resolveKerneliusAccount(runtime2.config.loadConfig(), action.accountId);
299
+ if (!account.apiKey) {
300
+ throw new Error("Kernelius API key not configured");
301
+ }
302
+ const { messageId, emoji } = action;
303
+ if (!messageId || !emoji) {
304
+ throw new Error("messageId and emoji are required for reactions");
305
+ }
306
+ let endpoint;
307
+ if (messageId.startsWith("issue_comment:")) {
308
+ endpoint = `/api/issues/comments/${messageId.replace("issue_comment:", "")}/reactions`;
309
+ } else if (messageId.startsWith("pr_comment:")) {
310
+ endpoint = `/api/pulls/comments/${messageId.replace("pr_comment:", "")}/reactions`;
311
+ } else if (messageId.startsWith("issue:")) {
312
+ endpoint = `/api/issues/${messageId.replace("issue:", "")}/reactions`;
313
+ } else if (messageId.startsWith("pr:")) {
314
+ endpoint = `/api/pulls/${messageId.replace("pr:", "")}/reactions`;
315
+ } else {
316
+ throw new Error(`Invalid messageId format: ${messageId}. Expected: issue_comment:<id>, pr_comment:<id>, issue:<id>, or pr:<id>`);
317
+ }
318
+ const response = await fetch(`${account.apiUrl}${endpoint}`, {
319
+ method: "POST",
320
+ headers: {
321
+ "Content-Type": "application/json",
322
+ "Authorization": `Bearer ${account.apiKey}`
323
+ },
324
+ body: JSON.stringify({ emoji })
325
+ });
326
+ if (!response.ok) {
327
+ const error = await response.text();
328
+ throw new Error(`Failed to add reaction: ${response.status} ${error}`);
329
+ }
330
+ const result = await response.json();
331
+ return { added: result.added };
276
332
  }
277
333
  },
278
334
  actions: {
279
- listActions: () => ["send"],
335
+ listActions: () => ["send", "react"],
280
336
  extractToolSend: ({ args }) => {
281
337
  const action = typeof args.action === "string" ? args.action.trim() : "";
282
338
  if (action !== "sendMessage") {
@@ -290,6 +346,41 @@ var kerneliusPlugin = {
290
346
  return { to, accountId };
291
347
  },
292
348
  handleAction: async ({ action, params, cfg, accountId }) => {
349
+ const account = resolveKerneliusAccount(cfg, accountId);
350
+ if (!account.apiKey) {
351
+ throw new Error("Kernelius API key not configured");
352
+ }
353
+ if (action === "react") {
354
+ const messageId = typeof params.messageId === "string" ? params.messageId : void 0;
355
+ const emoji = typeof params.emoji === "string" ? params.emoji : void 0;
356
+ if (!messageId || !emoji) {
357
+ throw new Error("Missing required parameters for react: messageId, emoji");
358
+ }
359
+ let endpoint2;
360
+ if (messageId.startsWith("issue_comment:")) {
361
+ endpoint2 = `/api/issues/comments/${messageId.replace("issue_comment:", "")}/reactions`;
362
+ } else if (messageId.startsWith("pr_comment:")) {
363
+ endpoint2 = `/api/pulls/comments/${messageId.replace("pr_comment:", "")}/reactions`;
364
+ } else if (messageId.startsWith("issue:")) {
365
+ endpoint2 = `/api/issues/${messageId.replace("issue:", "")}/reactions`;
366
+ } else if (messageId.startsWith("pr:")) {
367
+ endpoint2 = `/api/pulls/${messageId.replace("pr:", "")}/reactions`;
368
+ } else {
369
+ throw new Error(`Invalid messageId format: ${messageId}`);
370
+ }
371
+ const response2 = await fetch(`${account.apiUrl}${endpoint2}`, {
372
+ method: "POST",
373
+ headers: {
374
+ "Content-Type": "application/json",
375
+ "Authorization": `Bearer ${account.apiKey}`
376
+ },
377
+ body: JSON.stringify({ emoji })
378
+ });
379
+ if (!response2.ok) {
380
+ throw new Error(`Failed to add reaction: ${response2.status}`);
381
+ }
382
+ return { success: true };
383
+ }
293
384
  if (action !== "send") {
294
385
  throw new Error(`Unknown action: ${action}`);
295
386
  }
@@ -298,16 +389,12 @@ var kerneliusPlugin = {
298
389
  if (!to || !message) {
299
390
  throw new Error("Missing required parameters: to, message");
300
391
  }
301
- const account = resolveKerneliusAccount(cfg, accountId);
302
- if (!account.apiKey) {
303
- throw new Error("Kernelius API key not configured");
304
- }
305
392
  const match = to.match(/^repo:([^/]+)\/([^:]+):(issue|pr):(\d+)$/);
306
393
  if (!match) {
307
394
  throw new Error(`Invalid target format: ${to}`);
308
395
  }
309
396
  const [, owner, repo, type, number] = match;
310
- const endpoint = type === "issue" ? `/api/repositories/${owner}/${repo}/issues/${number}/comments` : `/api/pulls/${number}/comments`;
397
+ const endpoint = type === "issue" ? `/api/repositories/${owner}/${repo}/issues/${number}/comments` : `/api/repositories/${owner}/${repo}/pulls/${number}/comments`;
311
398
  const response = await fetch(`${account.apiUrl}${endpoint}`, {
312
399
  method: "POST",
313
400
  headers: {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
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"]}
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/repositories/${owner}/${repo}/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() as { id: string; createdAt: string };\n\n return {\n messageId: result.id,\n timestamp: new Date(result.createdAt),\n };\n },\n\n // React to a comment with an emoji\n // Valid emojis: +1, -1, laugh, hooray, confused, heart, rocket, eyes\n react: 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 const { messageId, emoji } = action;\n if (!messageId || !emoji) {\n throw new Error(\"messageId and emoji are required for reactions\");\n }\n\n // Determine the reaction endpoint based on message type\n // messageId format: \"issue_comment:<id>\" or \"pr_comment:<id>\" or \"issue:<id>\" or \"pr:<id>\"\n let endpoint: string;\n if (messageId.startsWith(\"issue_comment:\")) {\n endpoint = `/api/issues/comments/${messageId.replace(\"issue_comment:\", \"\")}/reactions`;\n } else if (messageId.startsWith(\"pr_comment:\")) {\n endpoint = `/api/pulls/comments/${messageId.replace(\"pr_comment:\", \"\")}/reactions`;\n } else if (messageId.startsWith(\"issue:\")) {\n endpoint = `/api/issues/${messageId.replace(\"issue:\", \"\")}/reactions`;\n } else if (messageId.startsWith(\"pr:\")) {\n endpoint = `/api/pulls/${messageId.replace(\"pr:\", \"\")}/reactions`;\n } else {\n throw new Error(`Invalid messageId format: ${messageId}. Expected: issue_comment:<id>, pr_comment:<id>, issue:<id>, or pr:<id>`);\n }\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({ emoji }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to add reaction: ${response.status} ${error}`);\n }\n\n const result = await response.json() as { added: boolean };\n return { added: result.added };\n },\n },\n actions: {\n listActions: () => [\"send\", \"react\"],\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 const account = resolveKerneliusAccount(cfg, accountId);\n\n if (!account.apiKey) {\n throw new Error(\"Kernelius API key not configured\");\n }\n\n if (action === \"react\") {\n const messageId = typeof params.messageId === \"string\" ? params.messageId : undefined;\n const emoji = typeof params.emoji === \"string\" ? params.emoji : undefined;\n\n if (!messageId || !emoji) {\n throw new Error(\"Missing required parameters for react: messageId, emoji\");\n }\n\n // Determine the reaction endpoint based on message type\n let endpoint: string;\n if (messageId.startsWith(\"issue_comment:\")) {\n endpoint = `/api/issues/comments/${messageId.replace(\"issue_comment:\", \"\")}/reactions`;\n } else if (messageId.startsWith(\"pr_comment:\")) {\n endpoint = `/api/pulls/comments/${messageId.replace(\"pr_comment:\", \"\")}/reactions`;\n } else if (messageId.startsWith(\"issue:\")) {\n endpoint = `/api/issues/${messageId.replace(\"issue:\", \"\")}/reactions`;\n } else if (messageId.startsWith(\"pr:\")) {\n endpoint = `/api/pulls/${messageId.replace(\"pr:\", \"\")}/reactions`;\n } else {\n throw new Error(`Invalid messageId format: ${messageId}`);\n }\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({ emoji }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to add reaction: ${response.status}`);\n }\n\n return { success: true };\n }\n\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 // 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/repositories/${owner}/${repo}/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 and messageId based on event type\n let conversationId: string;\n let body: string;\n let messageId: string | undefined;\n let chatType: \"direct\" | \"group\" = \"group\"; // Issues/PRs are group conversations\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 messageId = `issue:${issue.id}`;\n } else if (event === \"issue.commented\" && comment) {\n body = `**Comment on Issue #${issue.number}** by @${sender.username}:\\n\\n${comment.body}`;\n messageId = `issue_comment:${comment.id}`;\n } else if (event === \"issue.closed\") {\n body = `**Issue #${issue.number} closed** by @${sender.username}`;\n messageId = `issue:${issue.id}`;\n } else if (event === \"issue.reopened\") {\n body = `**Issue #${issue.number} reopened** by @${sender.username}`;\n messageId = `issue:${issue.id}`;\n } else if (event === \"issue.updated\") {\n body = `**Issue #${issue.number} updated** by @${sender.username}: ${issue.title}`;\n messageId = `issue:${issue.id}`;\n } else {\n body = `Issue #${issue.number} event: ${event}`;\n messageId = `issue:${issue.id}`;\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 messageId = `pr:${pullRequest.id}`;\n } else if (event === \"pr.review_requested\") {\n body = `**Review Requested on PR #${pullRequest.number}**: ${pullRequest.title}\\n\\nPlease review this pull request.`;\n messageId = `pr:${pullRequest.id}`;\n } else if (event === \"pr.reviewed\") {\n body = `**PR #${pullRequest.number} reviewed** by @${sender.username}`;\n messageId = `pr:${pullRequest.id}`;\n } else if (event === \"pr.merged\") {\n body = `**PR #${pullRequest.number} merged** by @${sender.username}`;\n messageId = `pr:${pullRequest.id}`;\n } else if (event === \"pr.commented\" && comment) {\n body = `**Comment on PR #${pullRequest.number}** by @${sender.username}:\\n\\n${comment.body}`;\n messageId = `pr_comment:${comment.id}`;\n } else if (event === \"pr.closed\") {\n body = `**PR #${pullRequest.number} closed** by @${sender.username}`;\n messageId = `pr:${pullRequest.id}`;\n } else if (event === \"pr.reopened\") {\n body = `**PR #${pullRequest.number} reopened** by @${sender.username}`;\n messageId = `pr:${pullRequest.id}`;\n } else {\n body = `Pull Request #${pullRequest.number} event: ${event}`;\n messageId = `pr:${pullRequest.id}`;\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 messageId, // For reactions support\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":";AACA,SAAS,+BAA+B;;;ACGxC;AAAA,EACE;AAAA,EACA;AAAA,OACK;;;ACLP,IAAI,UAAkC;AAE/B,SAAS,oBAAoB,IAA2B;AAC7D,YAAU;AACZ;AAEO,SAAS,sBAAuC;AACrD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AACA,SAAO;AACT;;;ACVA,OAAO,YAAY;AAWZ,SAAS,uBACd,SACA,WACA,QACS;AACT,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,QAAM,oBAAoB,UAAU,OACjC,WAAW,UAAU,MAAM,EAC3B,OAAO,OAAO,EACd,OAAO,KAAK,CAAC;AAEhB,SAAO,OAAO;AAAA,IACZ,OAAO,KAAK,SAAS;AAAA,IACrB,OAAO,KAAK,iBAAiB;AAAA,EAC/B;AACF;AAKO,SAAS,sBACd,SACA,WAC0B;AAC1B,QAAM,EAAE,OAAO,YAAY,QAAQ,OAAO,aAAa,QAAQ,IAAI;AAGnE,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI,WAA+B;AAEnC,MAAI,OAAO;AACT,qBAAiB,QAAQ,WAAW,QAAQ,UAAU,MAAM,MAAM;AAElE,QAAI,UAAU,iBAAiB;AAC7B,aAAO,gBAAgB,MAAM,MAAM,OAAO,MAAM,KAAK;AAAA;AAAA,EAAO,MAAM,QAAQ,EAAE;AAC5E,kBAAY,SAAS,MAAM,EAAE;AAAA,IAC/B,WAAW,UAAU,qBAAqB,SAAS;AACjD,aAAO,uBAAuB,MAAM,MAAM,UAAU,OAAO,QAAQ;AAAA;AAAA,EAAQ,QAAQ,IAAI;AACvF,kBAAY,iBAAiB,QAAQ,EAAE;AAAA,IACzC,WAAW,UAAU,gBAAgB;AACnC,aAAO,YAAY,MAAM,MAAM,iBAAiB,OAAO,QAAQ;AAC/D,kBAAY,SAAS,MAAM,EAAE;AAAA,IAC/B,WAAW,UAAU,kBAAkB;AACrC,aAAO,YAAY,MAAM,MAAM,mBAAmB,OAAO,QAAQ;AACjE,kBAAY,SAAS,MAAM,EAAE;AAAA,IAC/B,WAAW,UAAU,iBAAiB;AACpC,aAAO,YAAY,MAAM,MAAM,kBAAkB,OAAO,QAAQ,KAAK,MAAM,KAAK;AAChF,kBAAY,SAAS,MAAM,EAAE;AAAA,IAC/B,OAAO;AACL,aAAO,UAAU,MAAM,MAAM,WAAW,KAAK;AAC7C,kBAAY,SAAS,MAAM,EAAE;AAAA,IAC/B;AAAA,EACF,WAAW,aAAa;AACtB,qBAAiB,QAAQ,WAAW,QAAQ,OAAO,YAAY,MAAM;AAErE,QAAI,UAAU,cAAc;AAC1B,aAAO,uBAAuB,YAAY,MAAM,OAAO,YAAY,KAAK;AAAA;AAAA,EAAO,YAAY,QAAQ,EAAE;AACrG,kBAAY,MAAM,YAAY,EAAE;AAAA,IAClC,WAAW,UAAU,uBAAuB;AAC1C,aAAO,6BAA6B,YAAY,MAAM,OAAO,YAAY,KAAK;AAAA;AAAA;AAC9E,kBAAY,MAAM,YAAY,EAAE;AAAA,IAClC,WAAW,UAAU,eAAe;AAClC,aAAO,SAAS,YAAY,MAAM,mBAAmB,OAAO,QAAQ;AACpE,kBAAY,MAAM,YAAY,EAAE;AAAA,IAClC,WAAW,UAAU,aAAa;AAChC,aAAO,SAAS,YAAY,MAAM,iBAAiB,OAAO,QAAQ;AAClE,kBAAY,MAAM,YAAY,EAAE;AAAA,IAClC,WAAW,UAAU,kBAAkB,SAAS;AAC9C,aAAO,oBAAoB,YAAY,MAAM,UAAU,OAAO,QAAQ;AAAA;AAAA,EAAQ,QAAQ,IAAI;AAC1F,kBAAY,cAAc,QAAQ,EAAE;AAAA,IACtC,WAAW,UAAU,aAAa;AAChC,aAAO,SAAS,YAAY,MAAM,iBAAiB,OAAO,QAAQ;AAClE,kBAAY,MAAM,YAAY,EAAE;AAAA,IAClC,WAAW,UAAU,eAAe;AAClC,aAAO,SAAS,YAAY,MAAM,mBAAmB,OAAO,QAAQ;AACpE,kBAAY,MAAM,YAAY,EAAE;AAAA,IAClC,OAAO;AACL,aAAO,iBAAiB,YAAY,MAAM,WAAW,KAAK;AAC1D,kBAAY,MAAM,YAAY,EAAE;AAAA,IAClC;AAAA,EACF,OAAO;AAEL,qBAAiB,QAAQ,WAAW,QAAQ;AAC5C,WAAO,qBAAqB,KAAK;AAAA,EACnC;AAEA,QAAM,YAAY,IAAI,KAAK,QAAQ,SAAS,EAAE,QAAQ;AAEtD,SAAO;AAAA,IACL,IAAI,OAAO,WAAW;AAAA,IACtB,MAAM;AAAA,IACN;AAAA,IACA,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,IACA,UAAU,OAAO;AAAA,IACjB;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,YAAY,OAAO;AAAA,IACnB,SAAS;AAAA,IACT,UAAU;AAAA,IACV;AAAA;AAAA;AAAA,IAEA,eAAe,YAAY;AAAA,IAAC;AAAA,IAC5B,OAAO,OAAO,SAAiB;AAE7B,cAAQ,IAAI,6BAA6B,IAAI,EAAE;AAAA,IACjD;AAAA,IACA,WAAW,YAAY;AACrB,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AAAA,EACF;AACF;AAKA,eAAsB,qBACpB,KACA,KACA,KACmC;AAEnC,QAAM,SAAmB,CAAC;AAC1B,mBAAiB,SAAS,KAAK;AAC7B,WAAO,KAAK,KAAe;AAAA,EAC7B;AACA,QAAM,UAAU,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO;AAGtD,QAAM,YAAY,IAAI,QAAQ,mBAAmB;AACjD,MAAI,IAAI,QAAQ,eAAe;AAC7B,QAAI,CAAC,WAAW;AACd,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,mCAAmC,CAAC,CAAC;AACrE,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,uBAAuB,SAAS,WAAW,IAAI,QAAQ,aAAa,GAAG;AAC1E,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,oBAAoB,CAAC,CAAC;AACtD,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,OAAO;AAAA,EAC9B,SAAS,OAAO;AACd,QAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,QAAI,IAAI,KAAK,UAAU,EAAE,OAAO,uBAAuB,CAAC,CAAC;AACzD,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,WAAW,SAAS;AAC9B,QAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,QAAI,IAAI,KAAK,UAAU,EAAE,OAAO,iBAAiB,CAAC,CAAC;AACnD,WAAO;AAAA,EACT;AAGA,MAAI,aAAa,EAAE,eAAe,KAAK,IAAI,EAAE,CAAC;AAG9C,QAAM,UAAU,sBAAsB,SAAS,IAAI,QAAQ,SAAS;AAEpE,MAAI,CAAC,SAAS;AACZ,QAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,QAAI,IAAI,KAAK,UAAU,EAAE,OAAO,0BAA0B,CAAC,CAAC;AAC5D,WAAO;AAAA,EACT;AAGA,MAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,MAAI,IAAI,KAAK,UAAU,EAAE,SAAS,MAAM,OAAO,QAAQ,MAAM,CAAC,CAAC;AAE/D,SAAO;AACT;AAKO,SAAS,4BACd,aACA,YACQ;AACR,MAAI,aAAa,KAAK,GAAG;AACvB,UAAM,OAAO,YAAY,KAAK;AAC9B,WAAO,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AAAA,EAC/C;AAEA,MAAI,YAAY,KAAK,GAAG;AACtB,QAAI;AACF,YAAM,SAAS,IAAI,IAAI,UAAU;AACjC,aAAO,OAAO,YAAY;AAAA,IAC5B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;;;AFhNA,IAAM,OAAO,mBAAmB,WAAW;AAG3C,SAAS,wBAAwB,KAAU,WAA8C;AACvF,QAAM,qBAAqB,aAAa;AACxC,QAAM,gBAAgB,IAAI,UAAU,aAAa,CAAC;AAGlD,QAAM,gBAAgB,cAAc,WAAW,kBAAkB,KAAK;AAEtE,SAAO;AAAA,IACL,WAAW;AAAA,IACX,SAAS,cAAc,YAAY;AAAA,IACnC,QAAQ,cAAc,UAAU;AAAA,IAChC,QAAQ,cAAc;AAAA,IACtB,eAAe,cAAc;AAAA,IAC7B,aAAa,cAAc;AAAA,IAC3B,YAAY,cAAc;AAAA,EAC5B;AACF;AAEO,IAAM,kBAAiC;AAAA,EAC5C,IAAI;AAAA,EACJ,MAAM;AAAA,IACJ,GAAG;AAAA,IACH,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,cAAc;AAAA,IACZ,WAAW,CAAC,UAAU,WAAW,QAAQ;AAAA,IACzC,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,QAAQ,EAAE,gBAAgB,CAAC,oBAAoB,EAAE;AAAA,EACjD,QAAQ;AAAA,IACN,gBAAgB,CAAC,QAAQ;AACvB,YAAM,gBAAgB,IAAI,UAAU;AACpC,UAAI,CAAC,cAAe,QAAO,CAAC;AAC5B,UAAI,cAAc,UAAU;AAC1B,eAAO,OAAO,KAAK,cAAc,QAAQ;AAAA,MAC3C;AACA,aAAO,CAAC,kBAAkB;AAAA,IAC5B;AAAA,IACA,gBAAgB,CAAC,KAAK,cAAc,wBAAwB,KAAK,SAAS;AAAA,IAC1E,kBAAkB,MAAM;AAAA,IACxB,mBAAmB,CAAC,EAAE,KAAK,WAAW,QAAQ,MAAM;AAClD,YAAM,qBAAqB,aAAa;AACxC,UAAI,CAAC,IAAI,SAAU,KAAI,WAAW,CAAC;AACnC,UAAI,CAAC,IAAI,SAAS,UAAW,KAAI,SAAS,YAAY,CAAC;AAEvD,UAAI,IAAI,SAAS,UAAU,WAAW,kBAAkB,GAAG;AACzD,YAAI,SAAS,UAAU,SAAS,kBAAkB,EAAE,UAAU;AAAA,MAChE,OAAO;AACL,YAAI,SAAS,UAAU,UAAU;AAAA,MACnC;AACA,aAAO;AAAA,IACT;AAAA,IACA,eAAe,CAAC,EAAE,KAAK,UAAU,MAAM;AACrC,UAAI,aAAa,cAAc,oBAAoB;AACjD,eAAO,IAAI,UAAU,WAAW,WAAW,SAAS;AAAA,MACtD;AACA,aAAO;AAAA,IACT;AAAA,IACA,cAAc,CAAC,YAAiB,QAAQ,QAAQ,MAAM;AAAA,IACtD,iBAAiB,CAAC,aAAkB;AAAA,MAClC,WAAW,QAAQ;AAAA,MACnB,SAAS,QAAQ;AAAA,MACjB,YAAY,QAAQ,QAAQ,MAAM;AAAA,MAClC,QAAQ,QAAQ;AAAA,IAClB;AAAA,IACA,kBAAkB,MAAM,CAAC;AAAA,IACzB,iBAAiB,CAAC,EAAE,UAAU,MAAM;AAAA,EACtC;AAAA,EACA,WAAW;AAAA;AAAA,IAET,MAAM,OAAO,KAAK,WAAW;AAC3B,YAAMA,WAAU,oBAAoB;AACpC,YAAM,UAAU,wBAAwBA,SAAQ,OAAO,WAAW,GAAG,OAAO,SAAS;AAErF,UAAI,CAAC,QAAQ,QAAQ;AACnB,cAAM,IAAI,MAAM,kCAAkC;AAAA,MACpD;AAGA,YAAM,SAAS,OAAO;AACtB,YAAM,QAAQ,OAAO,MAAM,0CAA0C;AAErE,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,MAAM,oCAAoC,MAAM,+DAA+D;AAAA,MAC3H;AAEA,YAAM,CAAC,EAAE,OAAO,MAAM,MAAM,MAAM,IAAI;AACtC,YAAM,WAAW,SAAS,UACtB,qBAAqB,KAAK,IAAI,IAAI,WAAW,MAAM,cACnD,qBAAqB,KAAK,IAAI,IAAI,UAAU,MAAM;AAEtD,YAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,MAAM,GAAG,QAAQ,IAAI;AAAA,QAC3D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB,UAAU,QAAQ,MAAM;AAAA,QAC3C;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,MAAM,OAAO;AAAA,QACf,CAAC;AAAA,MACH,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,QAAQ,MAAM,SAAS,KAAK;AAClC,cAAM,IAAI,MAAM,wCAAwC,SAAS,MAAM,IAAI,KAAK,EAAE;AAAA,MACpF;AAEA,YAAM,SAAS,MAAM,SAAS,KAAK;AAEnC,aAAO;AAAA,QACL,WAAW,OAAO;AAAA,QAClB,WAAW,IAAI,KAAK,OAAO,SAAS;AAAA,MACtC;AAAA,IACF;AAAA;AAAA;AAAA,IAIA,OAAO,OAAO,KAAK,WAAW;AAC5B,YAAMA,WAAU,oBAAoB;AACpC,YAAM,UAAU,wBAAwBA,SAAQ,OAAO,WAAW,GAAG,OAAO,SAAS;AAErF,UAAI,CAAC,QAAQ,QAAQ;AACnB,cAAM,IAAI,MAAM,kCAAkC;AAAA,MACpD;AAEA,YAAM,EAAE,WAAW,MAAM,IAAI;AAC7B,UAAI,CAAC,aAAa,CAAC,OAAO;AACxB,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE;AAIA,UAAI;AACJ,UAAI,UAAU,WAAW,gBAAgB,GAAG;AAC1C,mBAAW,wBAAwB,UAAU,QAAQ,kBAAkB,EAAE,CAAC;AAAA,MAC5E,WAAW,UAAU,WAAW,aAAa,GAAG;AAC9C,mBAAW,uBAAuB,UAAU,QAAQ,eAAe,EAAE,CAAC;AAAA,MACxE,WAAW,UAAU,WAAW,QAAQ,GAAG;AACzC,mBAAW,eAAe,UAAU,QAAQ,UAAU,EAAE,CAAC;AAAA,MAC3D,WAAW,UAAU,WAAW,KAAK,GAAG;AACtC,mBAAW,cAAc,UAAU,QAAQ,OAAO,EAAE,CAAC;AAAA,MACvD,OAAO;AACL,cAAM,IAAI,MAAM,6BAA6B,SAAS,yEAAyE;AAAA,MACjI;AAEA,YAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,MAAM,GAAG,QAAQ,IAAI;AAAA,QAC3D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB,UAAU,QAAQ,MAAM;AAAA,QAC3C;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,MAAM,CAAC;AAAA,MAChC,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,QAAQ,MAAM,SAAS,KAAK;AAClC,cAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,KAAK,EAAE;AAAA,MACvE;AAEA,YAAM,SAAS,MAAM,SAAS,KAAK;AACnC,aAAO,EAAE,OAAO,OAAO,MAAM;AAAA,IAC/B;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,aAAa,MAAM,CAAC,QAAQ,OAAO;AAAA,IACnC,iBAAiB,CAAC,EAAE,KAAK,MAAM;AAC7B,YAAM,SAAS,OAAO,KAAK,WAAW,WAAW,KAAK,OAAO,KAAK,IAAI;AACtE,UAAI,WAAW,eAAe;AAC5B,eAAO;AAAA,MACT;AACA,YAAM,KAAK,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;AACnD,UAAI,CAAC,IAAI;AACP,eAAO;AAAA,MACT;AACA,YAAM,YAAY,OAAO,KAAK,cAAc,WAAW,KAAK,UAAU,KAAK,IAAI;AAC/E,aAAO,EAAE,IAAI,UAAU;AAAA,IACzB;AAAA,IACA,cAAc,OAAO,EAAE,QAAQ,QAAQ,KAAK,UAAU,MAAM;AAC1D,YAAM,UAAU,wBAAwB,KAAK,SAAS;AAEtD,UAAI,CAAC,QAAQ,QAAQ;AACnB,cAAM,IAAI,MAAM,kCAAkC;AAAA,MACpD;AAEA,UAAI,WAAW,SAAS;AACtB,cAAM,YAAY,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;AAC5E,cAAM,QAAQ,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAEhE,YAAI,CAAC,aAAa,CAAC,OAAO;AACxB,gBAAM,IAAI,MAAM,yDAAyD;AAAA,QAC3E;AAGA,YAAIC;AACJ,YAAI,UAAU,WAAW,gBAAgB,GAAG;AAC1C,UAAAA,YAAW,wBAAwB,UAAU,QAAQ,kBAAkB,EAAE,CAAC;AAAA,QAC5E,WAAW,UAAU,WAAW,aAAa,GAAG;AAC9C,UAAAA,YAAW,uBAAuB,UAAU,QAAQ,eAAe,EAAE,CAAC;AAAA,QACxE,WAAW,UAAU,WAAW,QAAQ,GAAG;AACzC,UAAAA,YAAW,eAAe,UAAU,QAAQ,UAAU,EAAE,CAAC;AAAA,QAC3D,WAAW,UAAU,WAAW,KAAK,GAAG;AACtC,UAAAA,YAAW,cAAc,UAAU,QAAQ,OAAO,EAAE,CAAC;AAAA,QACvD,OAAO;AACL,gBAAM,IAAI,MAAM,6BAA6B,SAAS,EAAE;AAAA,QAC1D;AAEA,cAAMC,YAAW,MAAM,MAAM,GAAG,QAAQ,MAAM,GAAGD,SAAQ,IAAI;AAAA,UAC3D,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,iBAAiB,UAAU,QAAQ,MAAM;AAAA,UAC3C;AAAA,UACA,MAAM,KAAK,UAAU,EAAE,MAAM,CAAC;AAAA,QAChC,CAAC;AAED,YAAI,CAACC,UAAS,IAAI;AAChB,gBAAM,IAAI,MAAM,2BAA2BA,UAAS,MAAM,EAAE;AAAA,QAC9D;AAEA,eAAO,EAAE,SAAS,KAAK;AAAA,MACzB;AAEA,UAAI,WAAW,QAAQ;AACrB,cAAM,IAAI,MAAM,mBAAmB,MAAM,EAAE;AAAA,MAC7C;AAEA,YAAM,KAAK,OAAO,OAAO,OAAO,WAAW,OAAO,KAAK;AACvD,YAAM,UAAU,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;AAEtE,UAAI,CAAC,MAAM,CAAC,SAAS;AACnB,cAAM,IAAI,MAAM,0CAA0C;AAAA,MAC5D;AAGA,YAAM,QAAQ,GAAG,MAAM,0CAA0C;AACjE,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,MAAM,0BAA0B,EAAE,EAAE;AAAA,MAChD;AAEA,YAAM,CAAC,EAAE,OAAO,MAAM,MAAM,MAAM,IAAI;AACtC,YAAM,WAAW,SAAS,UACtB,qBAAqB,KAAK,IAAI,IAAI,WAAW,MAAM,cACnD,qBAAqB,KAAK,IAAI,IAAI,UAAU,MAAM;AAEtD,YAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,MAAM,GAAG,QAAQ,IAAI;AAAA,QAC3D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB,UAAU,QAAQ,MAAM;AAAA,QAC3C;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAAA,MACxC,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,mBAAmB,SAAS,MAAM,EAAE;AAAA,MACtD;AAEA,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,OAAO,OAAO,QAAQ;AACpB,YAAMF,WAAU,oBAAoB;AACpC,YAAM,SAASA,SAAQ,OAAO,WAAW;AACzC,YAAM,UAAU,wBAAwB,QAAQ,IAAI,SAAS;AAG7D,UAAI,CAAC,QAAQ,eAAe,CAAC,QAAQ,YAAY;AAC/C,gBAAQ,IAAI,gEAAgE;AAC5E;AAAA,MACF;AAEA,YAAM,cAAc;AAAA,QAClB,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAEA,cAAQ,IAAI,4CAA4C,QAAQ,SAAS,OAAO,WAAW,EAAE;AAG7F,UAAI,oBAAoB;AAAA,QACtB,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,OAAO,KAAK,QAAQ;AAC3B,cAAI;AACF,kBAAM,UAAU,MAAM,qBAAqB,KAAK,KAAK;AAAA,cACnD;AAAA,cACA,SAAAA;AAAA,cACA,YAAY,IAAI;AAAA,YAClB,CAAC;AAED,gBAAI,SAAS;AAEX,oBAAM,IAAI,aAAa,OAAO;AAAA,YAChC;AAAA,UACF,SAAS,OAAO;AACd,oBAAQ,MAAM,sCAAsC,KAAK;AACzD,gBAAI,CAAC,IAAI,aAAa;AACpB,kBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,kBAAI,IAAI,KAAK,UAAU,EAAE,OAAO,wBAAwB,CAAC,CAAC;AAAA,YAC5D;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAED,cAAQ,IAAI,+CAA+C,WAAW,EAAE;AAAA,IAC1E;AAAA,IACA,MAAM,OAAO,QAAQ;AACnB,cAAQ,IAAI,4CAA4C,IAAI,SAAS,EAAE;AAAA,IAEzE;AAAA,EACF;AACF;;;AD1UA,IAAM,SAAS;AAAA,EACb,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,cAAc,wBAAwB;AAAA,EACtC,SAAS,KAAwB;AAC/B,wBAAoB,IAAI,OAAO;AAC/B,QAAI,gBAAgB,EAAE,QAAQ,gBAAgB,CAAC;AAAA,EACjD;AACF;AAEA,IAAO,gBAAQ;","names":["runtime","endpoint","response"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernelius/openclaw-plugin",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
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",