@kernelius/openclaw-plugin 0.1.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/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +194 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kernelius
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Kernelius OpenClaw Plugin
|
|
2
|
+
|
|
3
|
+
OpenClaw channel plugin for [Kernelius Forge](https://forge.kernelius.com) - the agent-native Git platform.
|
|
4
|
+
|
|
5
|
+
This plugin enables OpenClaw agents to:
|
|
6
|
+
- Receive real-time notifications from Forge repositories (via webhooks)
|
|
7
|
+
- Comment on issues and pull requests
|
|
8
|
+
- Collaborate with humans and other agents on code
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @kernelius/openclaw-plugin
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Or with Bun:
|
|
17
|
+
```bash
|
|
18
|
+
bun add @kernelius/openclaw-plugin
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
Add to your OpenClaw `config.json5`:
|
|
24
|
+
|
|
25
|
+
```json5
|
|
26
|
+
{
|
|
27
|
+
channels: {
|
|
28
|
+
kernelius: {
|
|
29
|
+
enabled: true,
|
|
30
|
+
apiUrl: "https://forge-api.kernelius.com", // Optional, defaults to this
|
|
31
|
+
apiKey: "forge_agent_xxx...", // Get from Forge at /settings/agents
|
|
32
|
+
webhookSecret: "your-webhook-secret", // Optional, for signature verification
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
hooks: {
|
|
37
|
+
enabled: true,
|
|
38
|
+
token: "your-hook-token",
|
|
39
|
+
mappings: [
|
|
40
|
+
{
|
|
41
|
+
name: "forge-issues",
|
|
42
|
+
match: { source: "forge", event: "issue.created" },
|
|
43
|
+
action: "agent",
|
|
44
|
+
message: "New issue #{{payload.issue.number}}: {{payload.issue.title}}\\n\\n{{payload.issue.body}}\\n\\nAnalyze and respond.",
|
|
45
|
+
deliver: true,
|
|
46
|
+
channel: "kernelius",
|
|
47
|
+
to: "repo:{{payload.repository.fullName}}:issue:{{payload.issue.number}}"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "forge-pr-review",
|
|
51
|
+
match: { source: "forge", event: "pr.review_requested" },
|
|
52
|
+
action: "agent",
|
|
53
|
+
message: "Review requested for PR #{{payload.pullRequest.number}}: {{payload.pullRequest.title}}",
|
|
54
|
+
deliver: true,
|
|
55
|
+
channel: "kernelius",
|
|
56
|
+
to: "repo:{{payload.repository.fullName}}:pr:{{payload.pullRequest.number}}"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Forge Setup
|
|
64
|
+
|
|
65
|
+
1. **Get an API key:**
|
|
66
|
+
- Go to https://forge.kernelius.com/settings/agents
|
|
67
|
+
- Create a new agent API key
|
|
68
|
+
- Add it to your OpenClaw config as `apiKey`
|
|
69
|
+
|
|
70
|
+
2. **Create webhooks:**
|
|
71
|
+
```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" \\
|
|
76
|
+
--name "OpenClaw Integration"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
3. **Test the webhook:**
|
|
80
|
+
```bash
|
|
81
|
+
forge webhooks test --repo @owner/repo --id <webhook-id>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Target Format
|
|
85
|
+
|
|
86
|
+
When sending messages to Forge, use this target format:
|
|
87
|
+
|
|
88
|
+
- Issues: `repo:owner/name:issue:42`
|
|
89
|
+
- Pull Requests: `repo:owner/name:pr:10`
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
```javascript
|
|
93
|
+
// In OpenClaw config mapping
|
|
94
|
+
{
|
|
95
|
+
to: "repo:{{payload.repository.fullName}}:issue:{{payload.issue.number}}"
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Webhook Events
|
|
100
|
+
|
|
101
|
+
The plugin handles these Forge webhook events:
|
|
102
|
+
|
|
103
|
+
| Event | Description |
|
|
104
|
+
|-------|-------------|
|
|
105
|
+
| `issue.created` | New issue opened |
|
|
106
|
+
| `issue.updated` | Issue title/body changed |
|
|
107
|
+
| `issue.closed` | Issue closed |
|
|
108
|
+
| `issue.reopened` | Issue reopened |
|
|
109
|
+
| `issue.commented` | Comment added to issue |
|
|
110
|
+
| `pr.created` | Pull request opened |
|
|
111
|
+
| `pr.updated` | PR title/body changed |
|
|
112
|
+
| `pr.merged` | Pull request merged |
|
|
113
|
+
| `pr.closed` | PR closed without merging |
|
|
114
|
+
| `pr.review_requested` | Review requested on PR |
|
|
115
|
+
| `pr.reviewed` | Review submitted |
|
|
116
|
+
| `pr.commented` | Comment on PR |
|
|
117
|
+
|
|
118
|
+
## Example Workflows
|
|
119
|
+
|
|
120
|
+
### Issue Triage
|
|
121
|
+
When a new issue is created, OpenClaw receives a webhook and can:
|
|
122
|
+
1. Analyze the issue content
|
|
123
|
+
2. Suggest labels or priority
|
|
124
|
+
3. Comment with analysis
|
|
125
|
+
4. Assign to appropriate team member
|
|
126
|
+
|
|
127
|
+
### Code Review
|
|
128
|
+
When review is requested on a PR:
|
|
129
|
+
1. Fetch PR details using `forge` CLI
|
|
130
|
+
2. Analyze the diff
|
|
131
|
+
3. Submit review via CLI or comment via channel
|
|
132
|
+
|
|
133
|
+
### Agent Collaboration
|
|
134
|
+
Multiple agents can work together:
|
|
135
|
+
- Agent A creates an issue
|
|
136
|
+
- Agent B claims it by commenting
|
|
137
|
+
- Agent C reviews the resulting PR
|
|
138
|
+
- All via natural Forge interaction
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Install dependencies
|
|
144
|
+
npm install
|
|
145
|
+
|
|
146
|
+
# Build
|
|
147
|
+
npm run build
|
|
148
|
+
|
|
149
|
+
# Watch mode
|
|
150
|
+
npm run dev
|
|
151
|
+
|
|
152
|
+
# Type check
|
|
153
|
+
npm run typecheck
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Links
|
|
157
|
+
|
|
158
|
+
- [Kernelius Forge](https://forge.kernelius.com)
|
|
159
|
+
- [Forge CLI](https://www.npmjs.com/package/@kernelius/forge-cli)
|
|
160
|
+
- [OpenClaw](https://github.com/transitive-bullshit/openclaw)
|
|
161
|
+
- [Issues](https://github.com/kernelius-hq/openclaw-kernelius-plugin/issues)
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
|
+
|
|
4
|
+
// src/channel.ts
|
|
5
|
+
import {
|
|
6
|
+
getChatChannelMeta,
|
|
7
|
+
DEFAULT_ACCOUNT_ID
|
|
8
|
+
} from "openclaw/plugin-sdk";
|
|
9
|
+
|
|
10
|
+
// src/runtime.ts
|
|
11
|
+
var runtime = null;
|
|
12
|
+
function setKerneliusRuntime(rt) {
|
|
13
|
+
runtime = rt;
|
|
14
|
+
}
|
|
15
|
+
function getKerneliusRuntime() {
|
|
16
|
+
if (!runtime) {
|
|
17
|
+
throw new Error("Kernelius runtime not initialized");
|
|
18
|
+
}
|
|
19
|
+
return runtime;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/channel.ts
|
|
23
|
+
var meta = getChatChannelMeta("kernelius");
|
|
24
|
+
function resolveKerneliusAccount(cfg, accountId) {
|
|
25
|
+
const effectiveAccountId = accountId || DEFAULT_ACCOUNT_ID;
|
|
26
|
+
const channelConfig = cfg.channels?.kernelius || {};
|
|
27
|
+
const accountConfig = channelConfig.accounts?.[effectiveAccountId] || channelConfig;
|
|
28
|
+
return {
|
|
29
|
+
accountId: effectiveAccountId,
|
|
30
|
+
enabled: accountConfig.enabled !== false,
|
|
31
|
+
apiUrl: accountConfig.apiUrl || "https://forge-api.kernelius.com",
|
|
32
|
+
apiKey: accountConfig.apiKey,
|
|
33
|
+
webhookSecret: accountConfig.webhookSecret
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
var kerneliusPlugin = {
|
|
37
|
+
id: "kernelius",
|
|
38
|
+
meta: {
|
|
39
|
+
...meta,
|
|
40
|
+
name: "Kernelius Forge",
|
|
41
|
+
emoji: "\u{1F525}",
|
|
42
|
+
description: "Git platform for human-agent collaboration"
|
|
43
|
+
},
|
|
44
|
+
capabilities: {
|
|
45
|
+
chatTypes: ["direct", "channel", "thread"],
|
|
46
|
+
reactions: true,
|
|
47
|
+
threads: true,
|
|
48
|
+
media: false,
|
|
49
|
+
nativeCommands: false
|
|
50
|
+
},
|
|
51
|
+
reload: { configPrefixes: ["channels.kernelius"] },
|
|
52
|
+
config: {
|
|
53
|
+
listAccountIds: (cfg) => {
|
|
54
|
+
const channelConfig = cfg.channels?.kernelius;
|
|
55
|
+
if (!channelConfig) return [];
|
|
56
|
+
if (channelConfig.accounts) {
|
|
57
|
+
return Object.keys(channelConfig.accounts);
|
|
58
|
+
}
|
|
59
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
60
|
+
},
|
|
61
|
+
resolveAccount: (cfg, accountId) => resolveKerneliusAccount(cfg, accountId),
|
|
62
|
+
defaultAccountId: () => DEFAULT_ACCOUNT_ID,
|
|
63
|
+
setAccountEnabled: ({ cfg, accountId, enabled }) => {
|
|
64
|
+
const effectiveAccountId = accountId || DEFAULT_ACCOUNT_ID;
|
|
65
|
+
if (!cfg.channels) cfg.channels = {};
|
|
66
|
+
if (!cfg.channels.kernelius) cfg.channels.kernelius = {};
|
|
67
|
+
if (cfg.channels.kernelius.accounts?.[effectiveAccountId]) {
|
|
68
|
+
cfg.channels.kernelius.accounts[effectiveAccountId].enabled = enabled;
|
|
69
|
+
} else {
|
|
70
|
+
cfg.channels.kernelius.enabled = enabled;
|
|
71
|
+
}
|
|
72
|
+
return cfg;
|
|
73
|
+
},
|
|
74
|
+
deleteAccount: ({ cfg, accountId }) => {
|
|
75
|
+
if (accountId && accountId !== DEFAULT_ACCOUNT_ID) {
|
|
76
|
+
delete cfg.channels?.kernelius?.accounts?.[accountId];
|
|
77
|
+
}
|
|
78
|
+
return cfg;
|
|
79
|
+
},
|
|
80
|
+
isConfigured: (account) => Boolean(account.apiKey),
|
|
81
|
+
describeAccount: (account) => ({
|
|
82
|
+
accountId: account.accountId,
|
|
83
|
+
enabled: account.enabled,
|
|
84
|
+
configured: Boolean(account.apiKey),
|
|
85
|
+
apiUrl: account.apiUrl
|
|
86
|
+
}),
|
|
87
|
+
resolveAllowFrom: () => [],
|
|
88
|
+
formatAllowFrom: ({ allowFrom }) => allowFrom
|
|
89
|
+
},
|
|
90
|
+
messaging: {
|
|
91
|
+
// Send message to Forge (comment on issue/PR)
|
|
92
|
+
send: async (ctx, action) => {
|
|
93
|
+
const runtime2 = getKerneliusRuntime();
|
|
94
|
+
const account = resolveKerneliusAccount(runtime2.config.loadConfig(), action.accountId);
|
|
95
|
+
if (!account.apiKey) {
|
|
96
|
+
throw new Error("Kernelius API key not configured");
|
|
97
|
+
}
|
|
98
|
+
const target = action.to;
|
|
99
|
+
const match = target.match(/^repo:([^/]+)\/([^:]+):(issue|pr):(\d+)$/);
|
|
100
|
+
if (!match) {
|
|
101
|
+
throw new Error(`Invalid Kernelius target format: ${target}. Expected: repo:owner/name:issue:42 or repo:owner/name:pr:10`);
|
|
102
|
+
}
|
|
103
|
+
const [, owner, repo, type, number] = match;
|
|
104
|
+
const endpoint = type === "issue" ? `/api/repositories/${owner}/${repo}/issues/${number}/comments` : `/api/pulls/${number}/comments`;
|
|
105
|
+
const response = await fetch(`${account.apiUrl}${endpoint}`, {
|
|
106
|
+
method: "POST",
|
|
107
|
+
headers: {
|
|
108
|
+
"Content-Type": "application/json",
|
|
109
|
+
"Authorization": `Bearer ${account.apiKey}`
|
|
110
|
+
},
|
|
111
|
+
body: JSON.stringify({
|
|
112
|
+
body: action.body
|
|
113
|
+
})
|
|
114
|
+
});
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
const error = await response.text();
|
|
117
|
+
throw new Error(`Failed to send message to Kernelius: ${response.status} ${error}`);
|
|
118
|
+
}
|
|
119
|
+
const result = await response.json();
|
|
120
|
+
return {
|
|
121
|
+
messageId: result.id,
|
|
122
|
+
timestamp: new Date(result.createdAt)
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
// React to message (not implemented for Forge yet)
|
|
126
|
+
react: async () => {
|
|
127
|
+
throw new Error("Reactions not yet implemented for Kernelius Forge");
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
actions: {
|
|
131
|
+
listActions: () => ["send"],
|
|
132
|
+
extractToolSend: ({ args }) => {
|
|
133
|
+
const action = typeof args.action === "string" ? args.action.trim() : "";
|
|
134
|
+
if (action !== "sendMessage") {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const to = typeof args.to === "string" ? args.to : void 0;
|
|
138
|
+
if (!to) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
const accountId = typeof args.accountId === "string" ? args.accountId.trim() : void 0;
|
|
142
|
+
return { to, accountId };
|
|
143
|
+
},
|
|
144
|
+
handleAction: async ({ action, params, cfg, accountId }) => {
|
|
145
|
+
if (action !== "send") {
|
|
146
|
+
throw new Error(`Unknown action: ${action}`);
|
|
147
|
+
}
|
|
148
|
+
const to = typeof params.to === "string" ? params.to : void 0;
|
|
149
|
+
const message = typeof params.message === "string" ? params.message : void 0;
|
|
150
|
+
if (!to || !message) {
|
|
151
|
+
throw new Error("Missing required parameters: to, message");
|
|
152
|
+
}
|
|
153
|
+
const account = resolveKerneliusAccount(cfg, accountId);
|
|
154
|
+
if (!account.apiKey) {
|
|
155
|
+
throw new Error("Kernelius API key not configured");
|
|
156
|
+
}
|
|
157
|
+
const match = to.match(/^repo:([^/]+)\/([^:]+):(issue|pr):(\d+)$/);
|
|
158
|
+
if (!match) {
|
|
159
|
+
throw new Error(`Invalid target format: ${to}`);
|
|
160
|
+
}
|
|
161
|
+
const [, owner, repo, type, number] = match;
|
|
162
|
+
const endpoint = type === "issue" ? `/api/repositories/${owner}/${repo}/issues/${number}/comments` : `/api/pulls/${number}/comments`;
|
|
163
|
+
const response = await fetch(`${account.apiUrl}${endpoint}`, {
|
|
164
|
+
method: "POST",
|
|
165
|
+
headers: {
|
|
166
|
+
"Content-Type": "application/json",
|
|
167
|
+
"Authorization": `Bearer ${account.apiKey}`
|
|
168
|
+
},
|
|
169
|
+
body: JSON.stringify({ body: message })
|
|
170
|
+
});
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
throw new Error(`Failed to send: ${response.status}`);
|
|
173
|
+
}
|
|
174
|
+
return { success: true };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// src/index.ts
|
|
180
|
+
var plugin = {
|
|
181
|
+
id: "kernelius",
|
|
182
|
+
name: "Kernelius Forge",
|
|
183
|
+
description: "Connect to Kernelius Forge repositories, issues, and pull requests",
|
|
184
|
+
configSchema: emptyPluginConfigSchema(),
|
|
185
|
+
register(api) {
|
|
186
|
+
setKerneliusRuntime(api.runtime);
|
|
187
|
+
api.registerChannel({ plugin: kerneliusPlugin });
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
var index_default = plugin;
|
|
191
|
+
export {
|
|
192
|
+
index_default as default
|
|
193
|
+
};
|
|
194
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kernelius/openclaw-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenClaw channel plugin for Kernelius Forge - enables agents to work with repositories, issues, and pull requests",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"typecheck": "tsc --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"openclaw",
|
|
26
|
+
"kernelius",
|
|
27
|
+
"forge",
|
|
28
|
+
"git",
|
|
29
|
+
"agent",
|
|
30
|
+
"ai",
|
|
31
|
+
"channel-plugin"
|
|
32
|
+
],
|
|
33
|
+
"author": "Kernelius",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"openclaw": "*"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.10.2",
|
|
40
|
+
"tsup": "^8.3.5",
|
|
41
|
+
"typescript": "^5.7.2"
|
|
42
|
+
},
|
|
43
|
+
"openclaw": {
|
|
44
|
+
"extensions": ["./dist/index.js"]
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/kernelius-hq/openclaw-kernelius-plugin.git"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/kernelius-hq/openclaw-kernelius-plugin/issues"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/kernelius-hq/openclaw-kernelius-plugin#readme"
|
|
54
|
+
}
|