@sw-market/openclaw-opencode-bridge 0.1.0 → 0.1.2
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 +59 -3
- package/dist/openclaw-extension.d.ts +40 -0
- package/dist/openclaw-extension.js +305 -0
- package/openclaw.plugin.json +51 -0
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -9,13 +9,69 @@ OpenClaw plugin bridge for OpenCode realtime sessions.
|
|
|
9
9
|
- Support interactive confirmations (`interaction.required` -> `chat.action` -> `interaction.resolved`).
|
|
10
10
|
- Emit file change summaries (`file.changed`) for downstream clients (Flutter/Feishu/others).
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Install To OpenClaw
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
|
|
15
|
+
openclaw plugins install @sw-market/openclaw-opencode-bridge
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
After install, verify plugin exists:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
openclaw plugins list
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## OpenClaw Config
|
|
25
|
+
|
|
26
|
+
In `~/.openclaw/openclaw.json`, enable plugin and set SDK adapter config:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"plugins": {
|
|
31
|
+
"entries": {
|
|
32
|
+
"openclaw-opencode-bridge": {
|
|
33
|
+
"enabled": true,
|
|
34
|
+
"config": {
|
|
35
|
+
"sdkAdapterModule": "/abs/path/to/opencode_sdk_adapter.mjs",
|
|
36
|
+
"sdkAdapterExport": "createOpenCodeSdkAdapter",
|
|
37
|
+
"sessionTtlMs": 1800000,
|
|
38
|
+
"cleanupIntervalMs": 60000
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
You can also set adapter location by environment variable:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
export OPENCODE_SDK_ADAPTER_MODULE=/abs/path/to/opencode_sdk_adapter.mjs
|
|
50
|
+
export OPENCODE_SDK_ADAPTER_EXPORT=createOpenCodeSdkAdapter
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Adapter module contract:
|
|
54
|
+
- export function `createOpenCodeSdkAdapter(ctx)` or export object directly.
|
|
55
|
+
- returned object must implement `createSession(args)`.
|
|
56
|
+
|
|
57
|
+
Note:
|
|
58
|
+
- Plugin installation no longer requires `sdkAdapterModule` in config.
|
|
59
|
+
- But runtime calls will fail until adapter module is provided via config or env.
|
|
60
|
+
|
|
61
|
+
## Gateway Methods Provided
|
|
62
|
+
|
|
63
|
+
This plugin registers:
|
|
64
|
+
- `opencode.chat.send`
|
|
65
|
+
- `opencode.chat.action`
|
|
66
|
+
|
|
67
|
+
Set adapter side method routing:
|
|
68
|
+
|
|
69
|
+
```env
|
|
70
|
+
BRIDGE_WS_CHAT_SEND_METHOD=opencode.chat.send
|
|
71
|
+
BRIDGE_WS_CHAT_ACTION_METHOD=opencode.chat.action
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Library Usage (standalone)
|
|
19
75
|
|
|
20
76
|
```ts
|
|
21
77
|
import {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
type JsonObject = Record<string, unknown>;
|
|
2
|
+
type PluginLogger = {
|
|
3
|
+
info: (message: string) => void;
|
|
4
|
+
warn: (message: string) => void;
|
|
5
|
+
error: (message: string) => void;
|
|
6
|
+
debug?: (message: string) => void;
|
|
7
|
+
};
|
|
8
|
+
type RespondFn = (ok: boolean, payload?: unknown, error?: {
|
|
9
|
+
code: string;
|
|
10
|
+
message: string;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}, meta?: JsonObject) => void;
|
|
13
|
+
type GatewayRequestHandlerOptions = {
|
|
14
|
+
params: JsonObject;
|
|
15
|
+
client: {
|
|
16
|
+
connId?: string;
|
|
17
|
+
} | null;
|
|
18
|
+
respond: RespondFn;
|
|
19
|
+
context: {
|
|
20
|
+
broadcast: (event: string, payload: unknown, opts?: {
|
|
21
|
+
dropIfSlow?: boolean;
|
|
22
|
+
stateVersion?: JsonObject;
|
|
23
|
+
}) => void;
|
|
24
|
+
broadcastToConnIds: (event: string, payload: unknown, connIds: ReadonlySet<string>, opts?: {
|
|
25
|
+
dropIfSlow?: boolean;
|
|
26
|
+
stateVersion?: JsonObject;
|
|
27
|
+
}) => void;
|
|
28
|
+
nodeSendToSession: (sessionKey: string, event: string, payload: unknown) => void;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
type PluginApi = {
|
|
32
|
+
id: string;
|
|
33
|
+
pluginConfig?: JsonObject;
|
|
34
|
+
logger: PluginLogger;
|
|
35
|
+
resolvePath: (input: string) => string;
|
|
36
|
+
registerGatewayMethod: (method: string, handler: (opts: GatewayRequestHandlerOptions) => void | Promise<void>) => void;
|
|
37
|
+
on?: (hookName: string, handler: () => void | Promise<void>) => void;
|
|
38
|
+
};
|
|
39
|
+
export default function register(api: PluginApi): void;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { OpenClawOpenCodeBridge, } from "./index.js";
|
|
5
|
+
const CHAT_SEND_METHOD = "opencode.chat.send";
|
|
6
|
+
const CHAT_ACTION_METHOD = "opencode.chat.action";
|
|
7
|
+
const DEFAULT_ADAPTER_EXPORT = "createOpenCodeSdkAdapter";
|
|
8
|
+
let bridgePromise = null;
|
|
9
|
+
let bridgeInstance = null;
|
|
10
|
+
function asObject(value) {
|
|
11
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function readString(source, key) {
|
|
17
|
+
const value = source[key];
|
|
18
|
+
return typeof value === "string" ? value.trim() : "";
|
|
19
|
+
}
|
|
20
|
+
function readOptionalPositiveInt(source, key) {
|
|
21
|
+
const value = source[key];
|
|
22
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const normalized = Math.floor(value);
|
|
26
|
+
if (normalized <= 0) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
function normalizeDecision(value) {
|
|
32
|
+
const lowered = value.trim().toLowerCase();
|
|
33
|
+
if (lowered === "approve" || lowered === "approved" || lowered === "yes" || lowered === "ok") {
|
|
34
|
+
return "approve";
|
|
35
|
+
}
|
|
36
|
+
if (lowered === "reject" || lowered === "rejected" || lowered === "decline" || lowered === "no") {
|
|
37
|
+
return "reject";
|
|
38
|
+
}
|
|
39
|
+
if (lowered === "cancel" || lowered === "cancelled" || lowered === "canceled") {
|
|
40
|
+
return "cancel";
|
|
41
|
+
}
|
|
42
|
+
return "approve";
|
|
43
|
+
}
|
|
44
|
+
function toErrorMessage(error) {
|
|
45
|
+
if (error instanceof Error) {
|
|
46
|
+
return error.message;
|
|
47
|
+
}
|
|
48
|
+
return String(error);
|
|
49
|
+
}
|
|
50
|
+
function parseConfig(pluginConfig) {
|
|
51
|
+
const raw = pluginConfig ?? {};
|
|
52
|
+
const sdkAdapterModuleRaw = readString(raw, "sdkAdapterModule") || String(process.env.OPENCODE_SDK_ADAPTER_MODULE || "").trim();
|
|
53
|
+
const sdkAdapterModule = sdkAdapterModuleRaw || undefined;
|
|
54
|
+
const sdkAdapterExport = readString(raw, "sdkAdapterExport") ||
|
|
55
|
+
String(process.env.OPENCODE_SDK_ADAPTER_EXPORT || "").trim() ||
|
|
56
|
+
DEFAULT_ADAPTER_EXPORT;
|
|
57
|
+
const sessionTtlMs = readOptionalPositiveInt(raw, "sessionTtlMs");
|
|
58
|
+
const cleanupIntervalMs = readOptionalPositiveInt(raw, "cleanupIntervalMs");
|
|
59
|
+
const emitToAllClients = raw.emitToAllClients === true;
|
|
60
|
+
return {
|
|
61
|
+
sdkAdapterModule,
|
|
62
|
+
sdkAdapterExport,
|
|
63
|
+
sessionTtlMs,
|
|
64
|
+
cleanupIntervalMs,
|
|
65
|
+
emitToAllClients,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function resolveModuleSpecifier(api, moduleSpecifier) {
|
|
69
|
+
const raw = moduleSpecifier.trim();
|
|
70
|
+
if (!raw) {
|
|
71
|
+
throw new Error("OpenCode SDK adapter is not configured. Set plugins.entries.openclaw-opencode-bridge.config.sdkAdapterModule " +
|
|
72
|
+
"or env OPENCODE_SDK_ADAPTER_MODULE.");
|
|
73
|
+
}
|
|
74
|
+
const looksLikePath = raw.startsWith("./") ||
|
|
75
|
+
raw.startsWith("../") ||
|
|
76
|
+
raw.startsWith("/") ||
|
|
77
|
+
raw.startsWith(".\\") ||
|
|
78
|
+
/^[a-zA-Z]:[\\/]/.test(raw);
|
|
79
|
+
if (!looksLikePath) {
|
|
80
|
+
return raw;
|
|
81
|
+
}
|
|
82
|
+
const absolutePath = path.isAbsolute(raw) ? raw : api.resolvePath(raw);
|
|
83
|
+
if (!existsSync(absolutePath)) {
|
|
84
|
+
throw new Error(`sdkAdapterModule path does not exist: ${absolutePath}`);
|
|
85
|
+
}
|
|
86
|
+
return pathToFileURL(absolutePath).href;
|
|
87
|
+
}
|
|
88
|
+
function isOpenCodeSdkAdapter(value) {
|
|
89
|
+
const obj = asObject(value);
|
|
90
|
+
if (!obj) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return typeof obj.createSession === "function";
|
|
94
|
+
}
|
|
95
|
+
async function loadSdkAdapter(api, config) {
|
|
96
|
+
const moduleSpecifier = resolveModuleSpecifier(api, config.sdkAdapterModule ?? "");
|
|
97
|
+
const loaded = await import(moduleSpecifier);
|
|
98
|
+
const exported = loaded[config.sdkAdapterExport];
|
|
99
|
+
let candidate = exported;
|
|
100
|
+
if (typeof exported === "function") {
|
|
101
|
+
candidate = await Promise.resolve(exported({
|
|
102
|
+
api,
|
|
103
|
+
pluginConfig: config,
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
if (!isOpenCodeSdkAdapter(candidate)) {
|
|
107
|
+
throw new Error(`Invalid OpenCode SDK adapter from "${config.sdkAdapterModule}" export "${config.sdkAdapterExport}". ` +
|
|
108
|
+
"Expected an object with createSession(args) function.");
|
|
109
|
+
}
|
|
110
|
+
return candidate;
|
|
111
|
+
}
|
|
112
|
+
function respondError(respond, code, message) {
|
|
113
|
+
respond(false, undefined, {
|
|
114
|
+
code,
|
|
115
|
+
message,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function readRequiredField(params, key) {
|
|
119
|
+
const value = readString(params, key);
|
|
120
|
+
if (!value) {
|
|
121
|
+
throw new Error(`${key} is required`);
|
|
122
|
+
}
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
function parseAction(params) {
|
|
126
|
+
const actionObj = asObject(params.action);
|
|
127
|
+
if (!actionObj) {
|
|
128
|
+
throw new Error("action is required and must be an object");
|
|
129
|
+
}
|
|
130
|
+
const actionType = readRequiredField(actionObj, "type");
|
|
131
|
+
if (actionType === "interaction.reply") {
|
|
132
|
+
return {
|
|
133
|
+
type: "interaction.reply",
|
|
134
|
+
interactionId: readRequiredField(actionObj, "interactionId"),
|
|
135
|
+
decision: normalizeDecision(readString(actionObj, "decision") || "approve"),
|
|
136
|
+
source: readString(actionObj, "source") || undefined,
|
|
137
|
+
payload: asObject(actionObj.payload) ?? undefined,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (actionType === "cancel_run") {
|
|
141
|
+
return {
|
|
142
|
+
type: "cancel_run",
|
|
143
|
+
reason: readString(actionObj, "reason") || undefined,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
throw new Error(`Unsupported action.type: ${actionType}`);
|
|
147
|
+
}
|
|
148
|
+
function emitEventToGateway(args) {
|
|
149
|
+
const payload = { ...args.payload };
|
|
150
|
+
if (!payload.sessionKey) {
|
|
151
|
+
payload.sessionKey = args.sessionKey;
|
|
152
|
+
}
|
|
153
|
+
const connId = typeof args.opts.client?.connId === "string" ? args.opts.client.connId.trim() : "";
|
|
154
|
+
const dropIfSlow = args.event !== "chat";
|
|
155
|
+
if (!args.emitToAllClients && connId) {
|
|
156
|
+
args.opts.context.broadcastToConnIds(args.event, payload, new Set([connId]), {
|
|
157
|
+
dropIfSlow,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
args.opts.context.broadcast(args.event, payload, {
|
|
162
|
+
dropIfSlow,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
args.opts.context.nodeSendToSession(args.sessionKey, args.event, payload);
|
|
166
|
+
}
|
|
167
|
+
function emitFailedCompletion(args) {
|
|
168
|
+
const message = toErrorMessage(args.error).trim() || "OpenCode bridge invocation failed";
|
|
169
|
+
emitEventToGateway({
|
|
170
|
+
opts: args.opts,
|
|
171
|
+
event: "chat",
|
|
172
|
+
payload: {
|
|
173
|
+
runId: args.runId,
|
|
174
|
+
state: "failed",
|
|
175
|
+
error: message,
|
|
176
|
+
},
|
|
177
|
+
sessionKey: args.sessionKey,
|
|
178
|
+
emitToAllClients: args.emitToAllClients,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
async function streamInvocation(args) {
|
|
182
|
+
try {
|
|
183
|
+
for await (const frame of args.events) {
|
|
184
|
+
if (!frame || frame.type !== "event") {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const payload = asObject(frame.payload) ?? {};
|
|
188
|
+
if (!payload.runId) {
|
|
189
|
+
payload.runId = args.runId;
|
|
190
|
+
}
|
|
191
|
+
emitEventToGateway({
|
|
192
|
+
opts: args.opts,
|
|
193
|
+
event: frame.event,
|
|
194
|
+
payload,
|
|
195
|
+
sessionKey: args.sessionKey,
|
|
196
|
+
emitToAllClients: args.emitToAllClients,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
args.logger.error(`[${CHAT_SEND_METHOD}] stream failed: ${toErrorMessage(error)}`);
|
|
202
|
+
emitFailedCompletion({
|
|
203
|
+
opts: args.opts,
|
|
204
|
+
runId: args.runId,
|
|
205
|
+
sessionKey: args.sessionKey,
|
|
206
|
+
emitToAllClients: args.emitToAllClients,
|
|
207
|
+
error,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async function ensureBridge(api) {
|
|
212
|
+
const config = parseConfig(api.pluginConfig);
|
|
213
|
+
if (bridgeInstance) {
|
|
214
|
+
return { bridge: bridgeInstance, config };
|
|
215
|
+
}
|
|
216
|
+
if (!bridgePromise) {
|
|
217
|
+
bridgePromise = (async () => {
|
|
218
|
+
const sdk = await loadSdkAdapter(api, config);
|
|
219
|
+
const bridge = new OpenClawOpenCodeBridge(sdk, {
|
|
220
|
+
sessionTtlMs: config.sessionTtlMs,
|
|
221
|
+
cleanupIntervalMs: config.cleanupIntervalMs,
|
|
222
|
+
});
|
|
223
|
+
bridgeInstance = bridge;
|
|
224
|
+
api.logger.info(`[${CHAT_SEND_METHOD}] bridge initialized`);
|
|
225
|
+
return bridge;
|
|
226
|
+
})();
|
|
227
|
+
}
|
|
228
|
+
const bridge = await bridgePromise;
|
|
229
|
+
return { bridge, config };
|
|
230
|
+
}
|
|
231
|
+
export default function register(api) {
|
|
232
|
+
api.registerGatewayMethod(CHAT_SEND_METHOD, async (opts) => {
|
|
233
|
+
try {
|
|
234
|
+
const params = asObject(opts.params) ?? {};
|
|
235
|
+
const sessionKey = readRequiredField(params, "sessionKey");
|
|
236
|
+
const message = readRequiredField(params, "message");
|
|
237
|
+
const runId = readString(params, "runId") || undefined;
|
|
238
|
+
const idempotencyKey = readString(params, "idempotencyKey") || undefined;
|
|
239
|
+
const { bridge, config } = await ensureBridge(api);
|
|
240
|
+
const invocation = await bridge.chatSend({
|
|
241
|
+
sessionKey,
|
|
242
|
+
message,
|
|
243
|
+
runId,
|
|
244
|
+
idempotencyKey,
|
|
245
|
+
});
|
|
246
|
+
opts.respond(true, {
|
|
247
|
+
runId: invocation.runId,
|
|
248
|
+
status: "started",
|
|
249
|
+
});
|
|
250
|
+
void streamInvocation({
|
|
251
|
+
opts,
|
|
252
|
+
runId: invocation.runId,
|
|
253
|
+
sessionKey,
|
|
254
|
+
emitToAllClients: config.emitToAllClients,
|
|
255
|
+
events: invocation.events,
|
|
256
|
+
logger: api.logger,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
respondError(opts.respond, "INVALID_REQUEST", toErrorMessage(error));
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
api.registerGatewayMethod(CHAT_ACTION_METHOD, async (opts) => {
|
|
264
|
+
try {
|
|
265
|
+
const params = asObject(opts.params) ?? {};
|
|
266
|
+
const sessionKey = readRequiredField(params, "sessionKey");
|
|
267
|
+
const runId = readRequiredField(params, "runId");
|
|
268
|
+
const idempotencyKey = readString(params, "idempotencyKey") || undefined;
|
|
269
|
+
const action = parseAction(params);
|
|
270
|
+
const { bridge, config } = await ensureBridge(api);
|
|
271
|
+
const invocation = await bridge.chatAction({
|
|
272
|
+
sessionKey,
|
|
273
|
+
runId,
|
|
274
|
+
action,
|
|
275
|
+
idempotencyKey,
|
|
276
|
+
});
|
|
277
|
+
opts.respond(true, {
|
|
278
|
+
runId: invocation.runId,
|
|
279
|
+
status: "started",
|
|
280
|
+
});
|
|
281
|
+
void streamInvocation({
|
|
282
|
+
opts,
|
|
283
|
+
runId: invocation.runId,
|
|
284
|
+
sessionKey,
|
|
285
|
+
emitToAllClients: config.emitToAllClients,
|
|
286
|
+
events: invocation.events,
|
|
287
|
+
logger: api.logger,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
respondError(opts.respond, "INVALID_REQUEST", toErrorMessage(error));
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
if (typeof api.on === "function") {
|
|
295
|
+
api.on("gateway_stop", async () => {
|
|
296
|
+
if (!bridgeInstance) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
await bridgeInstance.dispose();
|
|
300
|
+
bridgeInstance = null;
|
|
301
|
+
bridgePromise = null;
|
|
302
|
+
api.logger.info(`[${api.id}] bridge disposed`);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "openclaw-opencode-bridge",
|
|
3
|
+
"name": "OpenCode Bridge",
|
|
4
|
+
"description": "Bridge OpenClaw gateway chat.send/chat.action with OpenCode SDK streams",
|
|
5
|
+
"configSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"sdkAdapterModule": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Node module specifier or path to SDK adapter module (optional if OPENCODE_SDK_ADAPTER_MODULE is set)"
|
|
12
|
+
},
|
|
13
|
+
"sdkAdapterExport": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Export name from sdkAdapterModule (default: createOpenCodeSdkAdapter)"
|
|
16
|
+
},
|
|
17
|
+
"sessionTtlMs": {
|
|
18
|
+
"type": "integer",
|
|
19
|
+
"minimum": 60000
|
|
20
|
+
},
|
|
21
|
+
"cleanupIntervalMs": {
|
|
22
|
+
"type": "integer",
|
|
23
|
+
"minimum": 15000
|
|
24
|
+
},
|
|
25
|
+
"emitToAllClients": {
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"description": "Broadcast events to all gateway clients instead of only requester"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"uiHints": {
|
|
32
|
+
"sdkAdapterModule": {
|
|
33
|
+
"label": "SDK Adapter Module",
|
|
34
|
+
"help": "Absolute/relative path or npm package that exports OpenCode SDK adapter."
|
|
35
|
+
},
|
|
36
|
+
"sdkAdapterExport": {
|
|
37
|
+
"label": "SDK Adapter Export",
|
|
38
|
+
"help": "Export name in the adapter module. Default is createOpenCodeSdkAdapter."
|
|
39
|
+
},
|
|
40
|
+
"sessionTtlMs": {
|
|
41
|
+
"label": "Session TTL (ms)"
|
|
42
|
+
},
|
|
43
|
+
"cleanupIntervalMs": {
|
|
44
|
+
"label": "Cleanup Interval (ms)"
|
|
45
|
+
},
|
|
46
|
+
"emitToAllClients": {
|
|
47
|
+
"label": "Emit To All Clients",
|
|
48
|
+
"help": "Enable if you want every connected gateway client to receive run events."
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sw-market/openclaw-opencode-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "OpenClaw plugin bridge for OpenCode realtime streaming and interaction actions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
|
-
"README.md"
|
|
10
|
+
"README.md",
|
|
11
|
+
"openclaw.plugin.json"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
14
|
"build": "tsc -p tsconfig.json",
|
|
@@ -23,6 +24,11 @@
|
|
|
23
24
|
],
|
|
24
25
|
"author": "SW-Market",
|
|
25
26
|
"license": "MIT",
|
|
27
|
+
"openclaw": {
|
|
28
|
+
"extensions": [
|
|
29
|
+
"./dist/openclaw-extension.js"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
26
32
|
"devDependencies": {
|
|
27
33
|
"@types/node": "^22.15.0",
|
|
28
34
|
"rimraf": "^6.0.1",
|