@laceletho/plugin-openclaw 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/README.md +314 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +180 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# @opencode-ai/plugin-openclaw
|
|
2
|
+
|
|
3
|
+
OpenCode plugin for asynchronous task execution with OpenClaw callback support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Webhook Receiver**: HTTP endpoint to receive tasks from OpenClaw
|
|
8
|
+
- **Async Task Queue**: Configurable concurrent task execution
|
|
9
|
+
- **Automatic Callback**: Reports task results back to OpenClaw via webhook
|
|
10
|
+
- **Session Integration**: Uses OpenCode's session API for task execution
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
┌─────────────┐ POST /tasks ┌─────────────────────┐
|
|
16
|
+
│ OpenClaw │ ─────────────────────→ │ OpenclawPlugin │
|
|
17
|
+
│ (External) │ │ (Webhook Server) │
|
|
18
|
+
└─────────────┘ └──────────┬──────────┘
|
|
19
|
+
│
|
|
20
|
+
┌───────────────────────┘
|
|
21
|
+
│
|
|
22
|
+
▼
|
|
23
|
+
┌───────────────┐
|
|
24
|
+
│ Task Queue │
|
|
25
|
+
└───────┬───────┘
|
|
26
|
+
│
|
|
27
|
+
▼
|
|
28
|
+
┌───────────────┐
|
|
29
|
+
│ OpenCode │
|
|
30
|
+
│ Session │
|
|
31
|
+
└───────┬───────┘
|
|
32
|
+
│
|
|
33
|
+
▼
|
|
34
|
+
┌───────────────┐
|
|
35
|
+
│ Callback │
|
|
36
|
+
│ to OpenClaw │
|
|
37
|
+
│ /hooks/agent │
|
|
38
|
+
└───────────────┘
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install @opencode-ai/plugin-openclaw
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
### OpenCode Plugin Configuration
|
|
50
|
+
|
|
51
|
+
Add to your `opencode.json`:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"plugins": ["@opencode-ai/plugin-openclaw"],
|
|
56
|
+
"openclaw": {
|
|
57
|
+
"port": 9090,
|
|
58
|
+
"openclawWebhookUrl": "http://localhost:18789/hooks/agent",
|
|
59
|
+
"openclawApiKey": "your-openclaw-hooks-token",
|
|
60
|
+
"maxConcurrentTasks": 5
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### OpenClaw Configuration (Required)
|
|
66
|
+
|
|
67
|
+
To receive webhook callbacks from this plugin, OpenClaw must have its **hooks system enabled**. Add this to your OpenClaw configuration file (`~/.openclaw/openclaw.json` or `openclaw.json`):
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"hooks": {
|
|
72
|
+
"enabled": true,
|
|
73
|
+
"token": "your-openclaw-hooks-token",
|
|
74
|
+
"path": "/hooks",
|
|
75
|
+
"allowedAgentIds": ["main", "hooks"],
|
|
76
|
+
"defaultSessionKey": "hook:opencode",
|
|
77
|
+
"allowRequestSessionKey": false,
|
|
78
|
+
"allowedSessionKeyPrefixes": ["hook:"]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Important security settings:**
|
|
84
|
+
- `token`: Must match the `openclawApiKey` in the plugin config
|
|
85
|
+
- `allowedAgentIds`: Restrict which agents can receive hook messages
|
|
86
|
+
- `allowRequestSessionKey: false`: Prevents external callers from specifying session keys (recommended)
|
|
87
|
+
- `allowedSessionKeyPrefixes`: When `allowRequestSessionKey` is false, OpenClaw generates session keys with these prefixes
|
|
88
|
+
|
|
89
|
+
### Environment Variables
|
|
90
|
+
|
|
91
|
+
| Variable | Description | Default |
|
|
92
|
+
|----------|-------------|---------|
|
|
93
|
+
| `OPENCLAW_PORT` | Webhook server port | `9090` |
|
|
94
|
+
| `OPENCLAW_WEBHOOK_URL` | OpenClaw hooks endpoint | `http://localhost:18789/hooks/agent` |
|
|
95
|
+
| `OPENCLAW_API_KEY` | OpenClaw hooks token | - |
|
|
96
|
+
| `OPENCLAW_MAX_CONCURRENT` | Max concurrent tasks | `5` |
|
|
97
|
+
|
|
98
|
+
## API Endpoints
|
|
99
|
+
|
|
100
|
+
### POST /tasks
|
|
101
|
+
|
|
102
|
+
Submit a new task for execution.
|
|
103
|
+
|
|
104
|
+
**Request:**
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"taskId": "unique-task-id",
|
|
108
|
+
"prompt": "Write a Python function to calculate fibonacci numbers",
|
|
109
|
+
"callbackUrl": "http://localhost:18789/hooks/agent",
|
|
110
|
+
"callbackConfig": {
|
|
111
|
+
"name": "OpenCode Task",
|
|
112
|
+
"agentId": "main",
|
|
113
|
+
"deliver": true,
|
|
114
|
+
"channel": "telegram"
|
|
115
|
+
},
|
|
116
|
+
"metadata": {
|
|
117
|
+
"userId": "user-123",
|
|
118
|
+
"priority": "high"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Response:**
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"taskId": "unique-task-id",
|
|
127
|
+
"status": "accepted"
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### GET /tasks/:taskId
|
|
132
|
+
|
|
133
|
+
Check task status.
|
|
134
|
+
|
|
135
|
+
**Response:**
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"taskId": "unique-task-id",
|
|
139
|
+
"status": "completed",
|
|
140
|
+
"result": "Here's the Python function...",
|
|
141
|
+
"createdAt": "2024-03-12T10:00:00Z",
|
|
142
|
+
"updatedAt": "2024-03-12T10:05:00Z"
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### GET /health
|
|
147
|
+
|
|
148
|
+
Health check endpoint.
|
|
149
|
+
|
|
150
|
+
**Response:**
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"status": "ok",
|
|
154
|
+
"tasks": 3,
|
|
155
|
+
"running": 2
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Callback to OpenClaw
|
|
160
|
+
|
|
161
|
+
When a task completes, the plugin sends a POST request to OpenClaw's `/hooks/agent` endpoint:
|
|
162
|
+
|
|
163
|
+
### OpenClaw /hooks/agent Payload Format
|
|
164
|
+
|
|
165
|
+
The plugin sends task results to OpenClaw using its native hooks format:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"message": "Task completed: File processing finished\n\nResults:\nHere's the Python function...",
|
|
170
|
+
"name": "OpenCode Task",
|
|
171
|
+
"agentId": "main",
|
|
172
|
+
"wakeMode": "now",
|
|
173
|
+
"deliver": true,
|
|
174
|
+
"channel": "last",
|
|
175
|
+
"model": "anthropic/claude-sonnet-4-5",
|
|
176
|
+
"timeoutSeconds": 300
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Authentication:**
|
|
181
|
+
```
|
|
182
|
+
Authorization: Bearer <openclawApiKey>
|
|
183
|
+
Content-Type: application/json
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### OpenClaw /hooks/agent Endpoint Reference
|
|
187
|
+
|
|
188
|
+
OpenClaw's `/hooks/agent` endpoint accepts the following payload structure:
|
|
189
|
+
|
|
190
|
+
| Field | Type | Required | Description |
|
|
191
|
+
|-------|------|----------|-------------|
|
|
192
|
+
| `message` | string | Yes | The message to send to the agent |
|
|
193
|
+
| `name` | string | Yes | Display name for this hook invocation |
|
|
194
|
+
| `agentId` | string | No | Target agent ID (falls back to default) |
|
|
195
|
+
| `wakeMode` | string | No | `"now"` or `"next-heartbeat"` (default: `"now"`) |
|
|
196
|
+
| `sessionKey` | string | No | Session identifier (requires `allowRequestSessionKey: true`) |
|
|
197
|
+
| `deliver` | boolean | No | Whether to deliver response to messaging channel (default: `true`) |
|
|
198
|
+
| `channel` | string | No | Target channel: `"last"`, `"telegram"`, `"slack"`, `"discord"`, etc. |
|
|
199
|
+
| `to` | string | No | Recipient identifier for the channel |
|
|
200
|
+
| `model` | string | No | Model override (e.g., `"anthropic/claude-sonnet-4-5"`) |
|
|
201
|
+
| `thinking` | string | No | Thinking level: `"low"`, `"medium"`, `"high"` |
|
|
202
|
+
| `timeoutSeconds` | number | No | Maximum duration for the agent run |
|
|
203
|
+
|
|
204
|
+
## Usage Example
|
|
205
|
+
|
|
206
|
+
### 1. Configure OpenClaw
|
|
207
|
+
|
|
208
|
+
Edit your `~/.openclaw/openclaw.json`:
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"hooks": {
|
|
213
|
+
"enabled": true,
|
|
214
|
+
"token": "my-secure-webhook-token",
|
|
215
|
+
"path": "/hooks",
|
|
216
|
+
"allowedAgentIds": ["main"]
|
|
217
|
+
},
|
|
218
|
+
"channels": {
|
|
219
|
+
"telegram": {
|
|
220
|
+
"botToken": "${TELEGRAM_BOT_TOKEN}",
|
|
221
|
+
"allowFrom": ["*"]
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 2. Start OpenCode with the plugin
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
export OPENCLAW_WEBHOOK_URL="http://localhost:18789/hooks/agent"
|
|
231
|
+
export OPENCLAW_API_KEY="my-secure-webhook-token"
|
|
232
|
+
opencode serve
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 3. Send a task from OpenClaw
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
curl -X POST http://localhost:9090/tasks \
|
|
239
|
+
-H "Content-Type: application/json" \
|
|
240
|
+
-d '{
|
|
241
|
+
"taskId": "task-001",
|
|
242
|
+
"prompt": "Create a React component for a todo list",
|
|
243
|
+
"callbackUrl": "http://localhost:18789/hooks/agent",
|
|
244
|
+
"callbackConfig": {
|
|
245
|
+
"name": "OpenCode Task",
|
|
246
|
+
"agentId": "main",
|
|
247
|
+
"deliver": true,
|
|
248
|
+
"channel": "telegram"
|
|
249
|
+
}
|
|
250
|
+
}'
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 4. Receive callback in OpenClaw
|
|
254
|
+
|
|
255
|
+
OpenClaw will receive the task completion via its `/hooks/agent` endpoint and can forward it to your configured messaging channel (Telegram, Slack, Discord, etc.).
|
|
256
|
+
|
|
257
|
+
## Testing Webhook Callback
|
|
258
|
+
|
|
259
|
+
Test that OpenClaw can receive webhooks:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
curl -X POST http://localhost:18789/hooks/agent \
|
|
263
|
+
-H "Authorization: Bearer your-openclaw-hooks-token" \
|
|
264
|
+
-H "Content-Type: application/json" \
|
|
265
|
+
-d '{
|
|
266
|
+
"message": "Test message from OpenCode plugin",
|
|
267
|
+
"name": "Test Hook",
|
|
268
|
+
"deliver": true,
|
|
269
|
+
"channel": "last"
|
|
270
|
+
}'
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Comparison with claude-code-hooks
|
|
274
|
+
|
|
275
|
+
| Feature | claude-code-hooks | @opencode-ai/plugin-openclaw |
|
|
276
|
+
|---------|------------------|------------------------------|
|
|
277
|
+
| Trigger | Stop/SessionEnd hooks | Webhook HTTP endpoint |
|
|
278
|
+
| Integration | Shell scripts | TypeScript plugin |
|
|
279
|
+
| Orchestration | Agent Teams | Session API |
|
|
280
|
+
| Notification | Telegram + file | OpenClaw /hooks/agent |
|
|
281
|
+
| Metadata | task-meta.json | In-memory + HTTP API |
|
|
282
|
+
| Callback Format | Shell + CLI | HTTP POST to /hooks/agent |
|
|
283
|
+
|
|
284
|
+
## Troubleshooting
|
|
285
|
+
|
|
286
|
+
### "Unauthorized" errors
|
|
287
|
+
|
|
288
|
+
Ensure the `openclawApiKey` in plugin config matches the `hooks.token` in OpenClaw config.
|
|
289
|
+
|
|
290
|
+
### Callbacks not received
|
|
291
|
+
|
|
292
|
+
1. Verify OpenClaw Gateway is running: `curl http://localhost:18789/health`
|
|
293
|
+
2. Check hooks are enabled in OpenClaw config
|
|
294
|
+
3. Ensure network connectivity between OpenCode and OpenClaw
|
|
295
|
+
4. Check OpenClaw logs for incoming requests
|
|
296
|
+
|
|
297
|
+
### Security Considerations
|
|
298
|
+
|
|
299
|
+
- Keep `hooks.token` secret and use a strong random value
|
|
300
|
+
- Run OpenClaw Gateway behind a firewall or Tailscale for remote access
|
|
301
|
+
- Use `allowRequestSessionKey: false` to prevent session key injection
|
|
302
|
+
- Restrict `allowedAgentIds` to only necessary agents
|
|
303
|
+
|
|
304
|
+
## Development
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
npm install
|
|
308
|
+
npm run build
|
|
309
|
+
npm run typecheck
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## License
|
|
313
|
+
|
|
314
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface OpenclawConfig {
|
|
2
|
+
port?: number;
|
|
3
|
+
openclawWebhookUrl?: string;
|
|
4
|
+
openclawApiKey?: string;
|
|
5
|
+
maxConcurrentTasks?: number;
|
|
6
|
+
}
|
|
7
|
+
interface PluginInput {
|
|
8
|
+
client: {
|
|
9
|
+
session: {
|
|
10
|
+
create: () => Promise<any>;
|
|
11
|
+
prompt: (pathParams: {
|
|
12
|
+
path: {
|
|
13
|
+
id: string;
|
|
14
|
+
};
|
|
15
|
+
}, body: {
|
|
16
|
+
content: string;
|
|
17
|
+
}) => Promise<any>;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
declare const OpenclawPlugin: ({ client }: PluginInput) => Promise<{
|
|
22
|
+
config: (cfg: {
|
|
23
|
+
openclaw?: OpenclawConfig;
|
|
24
|
+
}) => Promise<void>;
|
|
25
|
+
dispose: () => Promise<void>;
|
|
26
|
+
}>;
|
|
27
|
+
export default OpenclawPlugin;
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAcD,UAAU,WAAW;IACnB,MAAM,EAAE;QACN,OAAO,EAAE;YACP,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAA;YAC1B,MAAM,EAAE,CAAC,UAAU,EAAE;gBAAE,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,EAAE,IAAI,EAAE;gBAAE,OAAO,EAAE,MAAM,CAAA;aAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;SAC1F,CAAA;KACF,CAAA;CACF;AAgED,QAAA,MAAM,cAAc,GAAU,YAAY,WAAW;kBA8H7B;QAAE,QAAQ,CAAC,EAAE,cAAc,CAAA;KAAE;;EAgBpD,CAAA;AAED,eAAe,cAAc,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { createServer } from "http";
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
|
+
const tasks = new Map();
|
|
4
|
+
let runningTasks = 0;
|
|
5
|
+
const events = new EventEmitter();
|
|
6
|
+
const config = {
|
|
7
|
+
port: 9090,
|
|
8
|
+
openclawWebhookUrl: "",
|
|
9
|
+
openclawApiKey: "",
|
|
10
|
+
maxConcurrentTasks: 5
|
|
11
|
+
};
|
|
12
|
+
const readBody = (req) => {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const chunks = [];
|
|
15
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
16
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
17
|
+
req.on("error", reject);
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
const waitForSlot = () => {
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
const handler = () => {
|
|
23
|
+
events.off("slot-freed", handler);
|
|
24
|
+
resolve();
|
|
25
|
+
};
|
|
26
|
+
events.once("slot-freed", handler);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
const notifyOpenclaw = async (task) => {
|
|
30
|
+
if (!task.callbackUrl)
|
|
31
|
+
return;
|
|
32
|
+
const payload = {
|
|
33
|
+
taskId: task.id,
|
|
34
|
+
status: task.status,
|
|
35
|
+
result: task.result,
|
|
36
|
+
error: task.error,
|
|
37
|
+
sessionId: task.sessionId,
|
|
38
|
+
completedAt: task.updatedAt.toISOString()
|
|
39
|
+
};
|
|
40
|
+
try {
|
|
41
|
+
const res = await fetch(task.callbackUrl, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: {
|
|
44
|
+
"Content-Type": "application/json",
|
|
45
|
+
...(config.openclawApiKey ? { "Authorization": `Bearer ${config.openclawApiKey}` } : {})
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify(payload)
|
|
48
|
+
});
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
console.error(`[openclaw] Callback failed for task ${task.id}: ${res.status} ${res.statusText}`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
console.log(`[openclaw] Callback sent for task: ${task.id}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
console.error(`[openclaw] Failed to send callback for task ${task.id}:`, err);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const OpenclawPlugin = async ({ client }) => {
|
|
61
|
+
const server = createServer(async (req, res) => {
|
|
62
|
+
const url = req.url || "/";
|
|
63
|
+
const method = req.method || "GET";
|
|
64
|
+
res.setHeader("Content-Type", "application/json");
|
|
65
|
+
if (url === "/health" && method === "GET") {
|
|
66
|
+
res.writeHead(200);
|
|
67
|
+
res.end(JSON.stringify({ status: "ok", tasks: tasks.size, running: runningTasks }));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (url === "/tasks" && method === "POST") {
|
|
71
|
+
try {
|
|
72
|
+
const body = await readBody(req);
|
|
73
|
+
const payload = JSON.parse(body);
|
|
74
|
+
if (!payload.taskId || !payload.prompt) {
|
|
75
|
+
res.writeHead(400);
|
|
76
|
+
res.end(JSON.stringify({ error: "Missing required fields: taskId, prompt" }));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const task = {
|
|
80
|
+
id: payload.taskId,
|
|
81
|
+
prompt: payload.prompt,
|
|
82
|
+
callbackUrl: payload.callbackUrl || config.openclawWebhookUrl,
|
|
83
|
+
status: "pending",
|
|
84
|
+
createdAt: new Date(),
|
|
85
|
+
updatedAt: new Date()
|
|
86
|
+
};
|
|
87
|
+
tasks.set(task.id, task);
|
|
88
|
+
console.log(`[openclaw] Task received: ${task.id}`);
|
|
89
|
+
res.writeHead(202);
|
|
90
|
+
res.end(JSON.stringify({ taskId: task.id, status: "accepted" }));
|
|
91
|
+
const executeTask = async () => {
|
|
92
|
+
if (runningTasks >= config.maxConcurrentTasks) {
|
|
93
|
+
console.log(`[openclaw] Task ${task.id} queued (max concurrent reached)`);
|
|
94
|
+
await waitForSlot();
|
|
95
|
+
}
|
|
96
|
+
runningTasks++;
|
|
97
|
+
task.status = "running";
|
|
98
|
+
task.updatedAt = new Date();
|
|
99
|
+
try {
|
|
100
|
+
console.log(`[openclaw] Executing task: ${task.id}`);
|
|
101
|
+
const sessionResult = await client.session.create();
|
|
102
|
+
const session = sessionResult.data || sessionResult;
|
|
103
|
+
task.sessionId = session.id;
|
|
104
|
+
const response = await client.session.prompt({ path: { id: session.id } }, { content: task.prompt });
|
|
105
|
+
const message = response.data || response;
|
|
106
|
+
task.status = "completed";
|
|
107
|
+
task.result = message.info?.content || message.content || JSON.stringify(message);
|
|
108
|
+
task.updatedAt = new Date();
|
|
109
|
+
console.log(`[openclaw] Task completed: ${task.id}`);
|
|
110
|
+
await notifyOpenclaw(task);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
task.status = "failed";
|
|
114
|
+
task.error = err instanceof Error ? err.message : String(err);
|
|
115
|
+
task.updatedAt = new Date();
|
|
116
|
+
console.error(`[openclaw] Task failed: ${task.id}`, err);
|
|
117
|
+
await notifyOpenclaw(task);
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
runningTasks--;
|
|
121
|
+
events.emit("slot-freed");
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
executeTask();
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
console.error("[openclaw] Failed to handle task request:", err);
|
|
128
|
+
res.writeHead(500);
|
|
129
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (url.startsWith("/tasks/") && method === "GET") {
|
|
134
|
+
const taskId = url.split("/")[2];
|
|
135
|
+
const task = tasks.get(taskId);
|
|
136
|
+
if (!task) {
|
|
137
|
+
res.writeHead(404);
|
|
138
|
+
res.end(JSON.stringify({ error: "Task not found" }));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
res.writeHead(200);
|
|
142
|
+
res.end(JSON.stringify({
|
|
143
|
+
taskId: task.id,
|
|
144
|
+
status: task.status,
|
|
145
|
+
result: task.result,
|
|
146
|
+
error: task.error,
|
|
147
|
+
createdAt: task.createdAt,
|
|
148
|
+
updatedAt: task.updatedAt
|
|
149
|
+
}));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
res.writeHead(404);
|
|
153
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
154
|
+
});
|
|
155
|
+
await new Promise((resolve, reject) => {
|
|
156
|
+
server.listen(config.port, () => {
|
|
157
|
+
console.log(`[openclaw] Webhook server listening on port ${config.port}`);
|
|
158
|
+
resolve();
|
|
159
|
+
});
|
|
160
|
+
server.on("error", reject);
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
config: async (cfg) => {
|
|
164
|
+
const openclawCfg = cfg.openclaw;
|
|
165
|
+
if (openclawCfg) {
|
|
166
|
+
Object.assign(config, openclawCfg);
|
|
167
|
+
}
|
|
168
|
+
if (!config.openclawWebhookUrl) {
|
|
169
|
+
console.log("[openclaw] No default callback URL configured");
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
dispose: async () => {
|
|
173
|
+
server.close();
|
|
174
|
+
events.removeAllListeners();
|
|
175
|
+
console.log("[openclaw] Plugin disposed");
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
export default OpenclawPlugin;
|
|
180
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PluginInput, Hooks } from "@opencode-ai/plugin";
|
|
2
|
+
export interface OpenclawConfig {
|
|
3
|
+
port?: number;
|
|
4
|
+
openclawWebhookUrl?: string;
|
|
5
|
+
openclawApiKey?: string;
|
|
6
|
+
maxConcurrentTasks?: number;
|
|
7
|
+
}
|
|
8
|
+
declare module "@opencode-ai/plugin" {
|
|
9
|
+
interface Config {
|
|
10
|
+
openclaw?: OpenclawConfig;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export type { PluginInput, Hooks };
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAE7D,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd,QAAQ,CAAC,EAAE,cAAc,CAAA;KAC1B;CACF;AAED,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,CAAA"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@laceletho/plugin-openclaw",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenCode plugin for async task execution with OpenClaw callback",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"typecheck": "tsc --noEmit"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"opencode",
|
|
18
|
+
"plugin",
|
|
19
|
+
"openclaw",
|
|
20
|
+
"webhook"
|
|
21
|
+
],
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@opencode-ai/plugin": ">=0.1.0"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"typescript": "^5.3.0",
|
|
27
|
+
"@types/node": "^20.0.0"
|
|
28
|
+
}
|
|
29
|
+
}
|