@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 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
@@ -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":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AA8BrC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAA;AACrC,IAAI,YAAY,GAAG,CAAC,CAAA;AACpB,MAAM,MAAM,GAAG,IAAI,YAAY,EAAE,CAAA;AAEjC,MAAM,MAAM,GAAG;IACb,IAAI,EAAE,IAAI;IACV,kBAAkB,EAAE,EAAE;IACtB,cAAc,EAAE,EAAE;IAClB,kBAAkB,EAAE,CAAC;CACtB,CAAA;AAED,MAAM,QAAQ,GAAG,CAAC,GAAQ,EAAmB,EAAE;IAC7C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAA;QAC3B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QACrD,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QACrE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,GAAkB,EAAE;IACtC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;YACjC,OAAO,EAAE,CAAA;QACX,CAAC,CAAA;QACD,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IACpC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,cAAc,GAAG,KAAK,EAAE,IAAU,EAAiB,EAAE;IACzD,IAAI,CAAC,IAAI,CAAC,WAAW;QAAE,OAAM;IAE7B,MAAM,OAAO,GAAG;QACd,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE;KAC1C,CAAA;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE;YACxC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,UAAU,MAAM,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACzF;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;SAC9B,CAAC,CAAA;QAEF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,uCAAuC,IAAI,CAAC,EAAE,KAAK,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;QAClG,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;QAC9D,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,+CAA+C,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;IAC/E,CAAC;AACH,CAAC,CAAA;AAED,MAAM,cAAc,GAAG,KAAK,EAAE,EAAE,MAAM,EAAe,EAAE,EAAE;IACvD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC7C,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAA;QAC1B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,CAAA;QAElC,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAA;QAEjD,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YAC1C,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;YAClB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,CAAA;YACnF,OAAM;QACR,CAAC;QAED,IAAI,GAAG,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YAC1C,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAA;gBAChC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA6D,CAAA;gBAE5F,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBACvC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;oBAClB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAC,CAAC,CAAA;oBAC7E,OAAM;gBACR,CAAC;gBAED,MAAM,IAAI,GAAS;oBACjB,EAAE,EAAE,OAAO,CAAC,MAAM;oBAClB,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC,kBAAkB;oBAC7D,MAAM,EAAE,SAAS;oBACjB,SAAS,EAAE,IAAI,IAAI,EAAE;oBACrB,SAAS,EAAE,IAAI,IAAI,EAAE;iBACtB,CAAA;gBAED,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAA;gBACxB,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;gBAEnD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;gBAClB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAA;gBAEhE,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;oBAC7B,IAAI,YAAY,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;wBAC9C,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,CAAC,EAAE,kCAAkC,CAAC,CAAA;wBACzE,MAAM,WAAW,EAAE,CAAA;oBACrB,CAAC;oBAED,YAAY,EAAE,CAAA;oBACd,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;oBACvB,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;oBAE3B,IAAI,CAAC;wBACH,OAAO,CAAC,GAAG,CAAC,8BAA8B,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;wBAEpD,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAA;wBACnD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,IAAI,aAAa,CAAA;wBACnD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,EAAE,CAAA;wBAE3B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAC1C,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAC5B,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,CACzB,CAAA;wBACD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAA;wBAEzC,IAAI,CAAC,MAAM,GAAG,WAAW,CAAA;wBACzB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;wBACjF,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;wBAE3B,OAAO,CAAC,GAAG,CAAC,8BAA8B,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;wBAEpD,MAAM,cAAc,CAAC,IAAI,CAAC,CAAA;oBAC5B,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAA;wBACtB,IAAI,CAAC,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;wBAC7D,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;wBAE3B,OAAO,CAAC,KAAK,CAAC,2BAA2B,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,CAAA;wBAExD,MAAM,cAAc,CAAC,IAAI,CAAC,CAAA;oBAC5B,CAAC;4BAAS,CAAC;wBACT,YAAY,EAAE,CAAA;wBACd,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;oBAC3B,CAAC;gBACH,CAAC,CAAA;gBAED,WAAW,EAAE,CAAA;YACf,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,GAAG,CAAC,CAAA;gBAC/D,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;gBAClB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC,CAAA;YAC7D,CAAC;YACD,OAAM;QACR,CAAC;QAED,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YAClD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;YAChC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;gBAClB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAA;gBACpD,OAAM;YACR,CAAC;YAED,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;YAClB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;gBACrB,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B,CAAC,CAAC,CAAA;YACH,OAAM;QACR,CAAC;QAED,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;QAClB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YAC9B,OAAO,CAAC,GAAG,CAAC,+CAA+C,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;YACzE,OAAO,EAAE,CAAA;QACX,CAAC,CAAC,CAAA;QACF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC5B,CAAC,CAAC,CAAA;IAEF,OAAO;QACL,MAAM,EAAE,KAAK,EAAE,GAAkC,EAAE,EAAE;YACnD,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAA;YAChC,IAAI,WAAW,EAAE,CAAC;gBAChB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;YACpC,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC/B,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAA;YAC9D,CAAC;QACH,CAAC;QAED,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,CAAC,KAAK,EAAE,CAAA;YACd,MAAM,CAAC,kBAAkB,EAAE,CAAA;YAC3B,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAA;QAC3C,CAAC;KACF,CAAA;AACH,CAAC,CAAA;AAED,eAAe,cAAc,CAAA"}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -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
+ }