@alfe.ai/openclaw 0.0.1
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 +51 -0
- package/dist/index.d.ts +186 -0
- package/dist/index.js +4072 -0
- package/dist/plugin.d.ts +38 -0
- package/dist/plugin.js +2 -0
- package/dist/plugin2.js +455 -0
- package/package.json +44 -0
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { TSchema } from "@sinclair/typebox";
|
|
2
|
+
|
|
3
|
+
//#region src/plugin.d.ts
|
|
4
|
+
|
|
5
|
+
interface ToolDef {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
label: string;
|
|
9
|
+
parameters: TSchema;
|
|
10
|
+
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<unknown>;
|
|
11
|
+
}
|
|
12
|
+
interface PluginLogger {
|
|
13
|
+
info(msg: string, ...args: unknown[]): void;
|
|
14
|
+
warn(msg: string, ...args: unknown[]): void;
|
|
15
|
+
error(msg: string, ...args: unknown[]): void;
|
|
16
|
+
debug(msg: string, ...args: unknown[]): void;
|
|
17
|
+
}
|
|
18
|
+
interface PluginApi {
|
|
19
|
+
logger: PluginLogger;
|
|
20
|
+
config?: {
|
|
21
|
+
plugins?: {
|
|
22
|
+
entries?: Record<string, {
|
|
23
|
+
config?: Record<string, string | undefined>;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
registerTool(tool: ToolDef): void;
|
|
28
|
+
}
|
|
29
|
+
declare const plugin: {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
version: string;
|
|
34
|
+
activate(api: PluginApi): void;
|
|
35
|
+
deactivate(api: PluginApi): void;
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { plugin as default };
|
package/dist/plugin.js
ADDED
package/dist/plugin2.js
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { Type } from "@sinclair/typebox";
|
|
4
|
+
import { createConnection } from "node:net";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { EventEmitter } from "node:events";
|
|
7
|
+
import { createLogger } from "@auriclabs/logger";
|
|
8
|
+
//#region src/types.ts
|
|
9
|
+
function isIPCRequest(msg) {
|
|
10
|
+
return typeof msg === "object" && msg !== null && msg.type === "req" && typeof msg.id === "string" && typeof msg.method === "string";
|
|
11
|
+
}
|
|
12
|
+
function isIPCResponse(msg) {
|
|
13
|
+
return typeof msg === "object" && msg !== null && typeof msg.id === "string" && typeof msg.ok === "boolean" && !("type" in msg);
|
|
14
|
+
}
|
|
15
|
+
function isIPCEvent(msg) {
|
|
16
|
+
return typeof msg === "object" && msg !== null && msg.type === "event" && typeof msg.event === "string";
|
|
17
|
+
}
|
|
18
|
+
/** IPC protocol version — must match daemon's PROTOCOL_VERSION */
|
|
19
|
+
const PROTOCOL_VERSION = 1;
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/ipc-client.ts
|
|
22
|
+
/**
|
|
23
|
+
* IPC Client — connects to the Alfe gateway daemon via Unix socket.
|
|
24
|
+
*
|
|
25
|
+
* Protocol: newline-delimited JSON over Unix socket.
|
|
26
|
+
* Request: { type: 'req', id, method, params }
|
|
27
|
+
* Response: { id, ok, payload?, error? }
|
|
28
|
+
* Event: { type: 'event', event, payload }
|
|
29
|
+
*
|
|
30
|
+
* Features:
|
|
31
|
+
* - Automatic reconnection with exponential backoff (1s → 30s)
|
|
32
|
+
* - Request/response correlation via message ID
|
|
33
|
+
* - Event emission for incoming daemon events + requests
|
|
34
|
+
* - Graceful handling if daemon not running (warns, retries, doesn't crash)
|
|
35
|
+
*/
|
|
36
|
+
const MIN_BACKOFF_MS = 1e3;
|
|
37
|
+
const MAX_BACKOFF_MS = 3e4;
|
|
38
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
39
|
+
var IPCClient = class extends EventEmitter {
|
|
40
|
+
socket = null;
|
|
41
|
+
socketPath;
|
|
42
|
+
buffer = "";
|
|
43
|
+
backoffMs = MIN_BACKOFF_MS;
|
|
44
|
+
closed = false;
|
|
45
|
+
_connected = false;
|
|
46
|
+
reconnectTimer = null;
|
|
47
|
+
pending = /* @__PURE__ */ new Map();
|
|
48
|
+
log = createLogger("IpcClient");
|
|
49
|
+
constructor(socketPath) {
|
|
50
|
+
super();
|
|
51
|
+
this.socketPath = socketPath;
|
|
52
|
+
}
|
|
53
|
+
get connected() {
|
|
54
|
+
return this._connected;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Start the IPC connection with auto-reconnect.
|
|
58
|
+
*/
|
|
59
|
+
start() {
|
|
60
|
+
this.closed = false;
|
|
61
|
+
this.doConnect();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Stop the IPC connection and all timers.
|
|
65
|
+
*/
|
|
66
|
+
stop() {
|
|
67
|
+
this.closed = true;
|
|
68
|
+
this.clearReconnectTimer();
|
|
69
|
+
for (const [id, pending] of this.pending) {
|
|
70
|
+
clearTimeout(pending.timer);
|
|
71
|
+
pending.resolve({
|
|
72
|
+
id,
|
|
73
|
+
ok: false,
|
|
74
|
+
error: {
|
|
75
|
+
code: "CLIENT_STOPPED",
|
|
76
|
+
message: "IPC client stopped"
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
this.pending.clear();
|
|
81
|
+
if (this.socket) {
|
|
82
|
+
try {
|
|
83
|
+
this.socket.end();
|
|
84
|
+
} catch {}
|
|
85
|
+
this.socket = null;
|
|
86
|
+
}
|
|
87
|
+
this._connected = false;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Send a request to the daemon and wait for a response.
|
|
91
|
+
*/
|
|
92
|
+
async request(method, params = {}, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
|
|
93
|
+
if (!this._connected || !this.socket) return {
|
|
94
|
+
id: "",
|
|
95
|
+
ok: false,
|
|
96
|
+
error: {
|
|
97
|
+
code: "NOT_CONNECTED",
|
|
98
|
+
message: "Not connected to daemon"
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const id = randomUUID();
|
|
102
|
+
const request = {
|
|
103
|
+
type: "req",
|
|
104
|
+
id,
|
|
105
|
+
method,
|
|
106
|
+
params
|
|
107
|
+
};
|
|
108
|
+
return new Promise((resolve) => {
|
|
109
|
+
const timer = setTimeout(() => {
|
|
110
|
+
this.pending.delete(id);
|
|
111
|
+
resolve({
|
|
112
|
+
id,
|
|
113
|
+
ok: false,
|
|
114
|
+
error: {
|
|
115
|
+
code: "TIMEOUT",
|
|
116
|
+
message: `Request ${method} timed out after ${String(timeoutMs)}ms`
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}, timeoutMs);
|
|
120
|
+
this.pending.set(id, {
|
|
121
|
+
resolve,
|
|
122
|
+
timer
|
|
123
|
+
});
|
|
124
|
+
try {
|
|
125
|
+
const socket = this.socket;
|
|
126
|
+
if (socket) socket.write(JSON.stringify(request) + "\n");
|
|
127
|
+
} catch (err) {
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
this.pending.delete(id);
|
|
130
|
+
resolve({
|
|
131
|
+
id,
|
|
132
|
+
ok: false,
|
|
133
|
+
error: {
|
|
134
|
+
code: "SEND_FAILED",
|
|
135
|
+
message: `Failed to send: ${err instanceof Error ? err.message : String(err)}`
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Send a response to a daemon request.
|
|
143
|
+
* Used internally by the request event handler.
|
|
144
|
+
*/
|
|
145
|
+
sendResponse(response) {
|
|
146
|
+
if (!this._connected || !this.socket) return;
|
|
147
|
+
try {
|
|
148
|
+
this.socket.write(JSON.stringify(response) + "\n");
|
|
149
|
+
} catch {}
|
|
150
|
+
}
|
|
151
|
+
doConnect() {
|
|
152
|
+
if (this.closed) return;
|
|
153
|
+
this.log.debug(`Connecting to daemon at ${this.socketPath}...`);
|
|
154
|
+
this.socket = createConnection(this.socketPath, () => {
|
|
155
|
+
this.log.info(`Connected to daemon (${this.socketPath})`);
|
|
156
|
+
this._connected = true;
|
|
157
|
+
this.backoffMs = MIN_BACKOFF_MS;
|
|
158
|
+
this.buffer = "";
|
|
159
|
+
this.emit("connected");
|
|
160
|
+
});
|
|
161
|
+
this.socket.on("data", (data) => {
|
|
162
|
+
this.buffer += data.toString();
|
|
163
|
+
this.processBuffer();
|
|
164
|
+
});
|
|
165
|
+
this.socket.on("close", () => {
|
|
166
|
+
const wasConnected = this._connected;
|
|
167
|
+
this._connected = false;
|
|
168
|
+
for (const [id, pending] of this.pending) {
|
|
169
|
+
clearTimeout(pending.timer);
|
|
170
|
+
pending.resolve({
|
|
171
|
+
id,
|
|
172
|
+
ok: false,
|
|
173
|
+
error: {
|
|
174
|
+
code: "DISCONNECTED",
|
|
175
|
+
message: "Connection closed"
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
this.pending.clear();
|
|
180
|
+
if (wasConnected) {
|
|
181
|
+
this.log.warn("Disconnected from daemon");
|
|
182
|
+
this.emit("disconnected", "connection closed");
|
|
183
|
+
}
|
|
184
|
+
this.scheduleReconnect();
|
|
185
|
+
});
|
|
186
|
+
this.socket.on("error", (err) => {
|
|
187
|
+
if (err.code === "ECONNREFUSED" || err.code === "ENOENT" || err.code === "ECONNRESET") this.log.debug(`Daemon not available: ${err.code}`);
|
|
188
|
+
else {
|
|
189
|
+
this.log.error(`IPC socket error: ${err.message}`);
|
|
190
|
+
this.emit("error", err);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
processBuffer() {
|
|
195
|
+
let newlineIdx;
|
|
196
|
+
while ((newlineIdx = this.buffer.indexOf("\n")) !== -1) {
|
|
197
|
+
const line = this.buffer.slice(0, newlineIdx).trim();
|
|
198
|
+
this.buffer = this.buffer.slice(newlineIdx + 1);
|
|
199
|
+
if (!line) continue;
|
|
200
|
+
let parsed;
|
|
201
|
+
try {
|
|
202
|
+
parsed = JSON.parse(line);
|
|
203
|
+
} catch {
|
|
204
|
+
this.log.warn("Received invalid JSON from daemon");
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (isIPCResponse(parsed)) {
|
|
208
|
+
this.handleResponse(parsed);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (isIPCEvent(parsed)) {
|
|
212
|
+
this.emit("event", parsed.event, parsed.payload);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (isIPCRequest(parsed)) {
|
|
216
|
+
this.handleIncomingRequest(parsed);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
this.log.debug({ msg: parsed }, "Unhandled message from daemon");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
handleResponse(response) {
|
|
223
|
+
const pending = this.pending.get(response.id);
|
|
224
|
+
if (!pending) return;
|
|
225
|
+
clearTimeout(pending.timer);
|
|
226
|
+
this.pending.delete(response.id);
|
|
227
|
+
pending.resolve(response);
|
|
228
|
+
}
|
|
229
|
+
handleIncomingRequest(request) {
|
|
230
|
+
const respond = (partial) => {
|
|
231
|
+
const response = {
|
|
232
|
+
id: request.id,
|
|
233
|
+
...partial
|
|
234
|
+
};
|
|
235
|
+
this.sendResponse(response);
|
|
236
|
+
};
|
|
237
|
+
this.emit("request", request.method, request.params, respond);
|
|
238
|
+
}
|
|
239
|
+
scheduleReconnect() {
|
|
240
|
+
if (this.closed) return;
|
|
241
|
+
this.clearReconnectTimer();
|
|
242
|
+
const delay = this.backoffMs;
|
|
243
|
+
this.backoffMs = Math.min(this.backoffMs * 2, MAX_BACKOFF_MS);
|
|
244
|
+
this.log.debug(`Reconnecting in ${String(delay)}ms...`);
|
|
245
|
+
this.reconnectTimer = setTimeout(() => {
|
|
246
|
+
this.doConnect();
|
|
247
|
+
}, delay);
|
|
248
|
+
}
|
|
249
|
+
clearReconnectTimer() {
|
|
250
|
+
if (this.reconnectTimer) {
|
|
251
|
+
clearTimeout(this.reconnectTimer);
|
|
252
|
+
this.reconnectTimer = null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/capability-reporter.ts
|
|
258
|
+
const PLUGIN_VERSION = "0.1.0";
|
|
259
|
+
const OPENCLAW_CAPABILITIES = [
|
|
260
|
+
"sessions",
|
|
261
|
+
"tools",
|
|
262
|
+
"plugins"
|
|
263
|
+
];
|
|
264
|
+
/**
|
|
265
|
+
* Register this OpenClaw plugin with the daemon.
|
|
266
|
+
* Should be called on every (re)connect.
|
|
267
|
+
*
|
|
268
|
+
* @returns Registration result or null on failure
|
|
269
|
+
*/
|
|
270
|
+
async function registerWithDaemon(client, log) {
|
|
271
|
+
const response = await client.request("register", {
|
|
272
|
+
framework: "openclaw",
|
|
273
|
+
name: "@alfe.ai/openclaw",
|
|
274
|
+
version: PLUGIN_VERSION,
|
|
275
|
+
protocolVersion: 1,
|
|
276
|
+
capabilities: [...OPENCLAW_CAPABILITIES],
|
|
277
|
+
pid: process.pid
|
|
278
|
+
});
|
|
279
|
+
if (!response.ok) {
|
|
280
|
+
const err = response.error;
|
|
281
|
+
if (err?.code === "PROTOCOL_MISMATCH") {
|
|
282
|
+
log.error(`Daemon protocol version mismatch: ${err.message}`);
|
|
283
|
+
log.error("Update @alfe.ai/openclaw or the daemon to a compatible version.");
|
|
284
|
+
} else log.error(`Registration failed: ${err?.message ?? "unknown error"}`);
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
const result = response.payload;
|
|
288
|
+
log.info(`Registered with daemon v${result.daemonVersion} (protocol v${String(result.protocolVersion)})`);
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region src/plugin.ts
|
|
293
|
+
/**
|
|
294
|
+
* @alfe.ai/openclaw — OpenClaw plugin for Alfe.
|
|
295
|
+
*
|
|
296
|
+
* Connects to the local Alfe gateway daemon via IPC socket and:
|
|
297
|
+
* 1. Registers OpenClaw capabilities with the daemon on connect
|
|
298
|
+
* 2. Handles incoming requests from the daemon
|
|
299
|
+
* 3. Re-registers on reconnect (daemon may restart)
|
|
300
|
+
* 4. Gracefully handles daemon being unavailable
|
|
301
|
+
*
|
|
302
|
+
* Follows the same plugin pattern as @alfe.ai/openclaw-voice.
|
|
303
|
+
*/
|
|
304
|
+
const DEFAULT_SOCKET_PATH = join(homedir(), ".alfe", "gateway.sock");
|
|
305
|
+
let ipcClient = null;
|
|
306
|
+
let integrationsApiUrl = "";
|
|
307
|
+
let integrationsApiKey = "";
|
|
308
|
+
let pluginAgentId = "";
|
|
309
|
+
function ok(result) {
|
|
310
|
+
return { content: [{
|
|
311
|
+
type: "text",
|
|
312
|
+
text: JSON.stringify(result)
|
|
313
|
+
}] };
|
|
314
|
+
}
|
|
315
|
+
function errResult(message) {
|
|
316
|
+
return { content: [{
|
|
317
|
+
type: "text",
|
|
318
|
+
text: JSON.stringify({ error: message })
|
|
319
|
+
}] };
|
|
320
|
+
}
|
|
321
|
+
function defineTool(def) {
|
|
322
|
+
return {
|
|
323
|
+
name: def.name,
|
|
324
|
+
description: def.description,
|
|
325
|
+
label: def.name,
|
|
326
|
+
parameters: def.parameters,
|
|
327
|
+
execute: async (_toolCallId, params) => {
|
|
328
|
+
try {
|
|
329
|
+
return ok(await def.handler(params));
|
|
330
|
+
} catch (e) {
|
|
331
|
+
return errResult(e.message);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
async function integrationsApiFetch(path, options) {
|
|
337
|
+
const url = `${integrationsApiUrl}${path}`;
|
|
338
|
+
const res = await fetch(url, {
|
|
339
|
+
...options,
|
|
340
|
+
headers: {
|
|
341
|
+
"Content-Type": "application/json",
|
|
342
|
+
Authorization: `Bearer ${integrationsApiKey}`,
|
|
343
|
+
...options?.headers
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
if (!res.ok) {
|
|
347
|
+
const text = await res.text().catch(() => "");
|
|
348
|
+
throw new Error(`Integrations API ${String(res.status)}: ${text}`);
|
|
349
|
+
}
|
|
350
|
+
return res.json();
|
|
351
|
+
}
|
|
352
|
+
const integrationTools = [
|
|
353
|
+
defineTool({
|
|
354
|
+
name: "list_integrations",
|
|
355
|
+
description: "List all integrations installed for this agent, including their status and available config fields.",
|
|
356
|
+
parameters: Type.Object({}),
|
|
357
|
+
handler: async () => {
|
|
358
|
+
return integrationsApiFetch(`/integrations/agents/${pluginAgentId}`);
|
|
359
|
+
}
|
|
360
|
+
}),
|
|
361
|
+
defineTool({
|
|
362
|
+
name: "get_integration_config",
|
|
363
|
+
description: "Get the current configuration and editable fields for a specific integration.",
|
|
364
|
+
parameters: Type.Object({ integrationId: Type.String({ description: "The integration to inspect (e.g. \"voice\", \"slack\")" }) }),
|
|
365
|
+
handler: async (params) => {
|
|
366
|
+
const integrationId = params.integrationId;
|
|
367
|
+
return integrationsApiFetch(`/integrations/agents/${pluginAgentId}/${integrationId}/config`);
|
|
368
|
+
}
|
|
369
|
+
}),
|
|
370
|
+
defineTool({
|
|
371
|
+
name: "update_integration_config",
|
|
372
|
+
description: "Update configuration for an installed integration. Only agent-editable fields can be changed.",
|
|
373
|
+
parameters: Type.Object({
|
|
374
|
+
integrationId: Type.String({ description: "The integration to update" }),
|
|
375
|
+
fields: Type.Record(Type.String(), Type.Unknown(), { description: "Key-value pairs of config fields to update" })
|
|
376
|
+
}),
|
|
377
|
+
handler: async (params) => {
|
|
378
|
+
const integrationId = params.integrationId;
|
|
379
|
+
const fields = params.fields;
|
|
380
|
+
return integrationsApiFetch(`/integrations/agents/${pluginAgentId}/${integrationId}`, {
|
|
381
|
+
method: "PATCH",
|
|
382
|
+
body: JSON.stringify({ config: fields })
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
})
|
|
386
|
+
];
|
|
387
|
+
const plugin = {
|
|
388
|
+
id: "@alfe.ai/openclaw",
|
|
389
|
+
name: "Alfe OpenClaw Plugin",
|
|
390
|
+
description: "Connects OpenClaw to the local Alfe gateway daemon",
|
|
391
|
+
version: "0.1.0",
|
|
392
|
+
activate(api) {
|
|
393
|
+
const log = api.logger;
|
|
394
|
+
log.info("Alfe OpenClaw Plugin activating...");
|
|
395
|
+
const pluginConfig = (api.config ?? {}).plugins?.entries?.["@alfe.ai/openclaw"]?.config ?? {};
|
|
396
|
+
const socketPath = pluginConfig.socketPath ?? process.env.ALFE_GATEWAY_SOCKET ?? DEFAULT_SOCKET_PATH;
|
|
397
|
+
integrationsApiUrl = pluginConfig.apiUrl ?? process.env.ALFE_API_URL ?? "";
|
|
398
|
+
integrationsApiKey = pluginConfig.apiKey ?? process.env.ALFE_API_KEY ?? "";
|
|
399
|
+
pluginAgentId = pluginConfig.agentId ?? process.env.ALFE_AGENT_ID ?? "";
|
|
400
|
+
if (integrationsApiUrl && integrationsApiKey && pluginAgentId) {
|
|
401
|
+
for (const tool of integrationTools) api.registerTool(tool);
|
|
402
|
+
log.info(`Registered ${String(integrationTools.length)} integration tools: ${integrationTools.map((t) => t.name).join(", ")}`);
|
|
403
|
+
} else log.warn("Integration tools not registered — missing apiUrl, apiKey, or agentId config");
|
|
404
|
+
ipcClient = new IPCClient(socketPath);
|
|
405
|
+
ipcClient.on("event", (event, payload) => {
|
|
406
|
+
switch (event) {
|
|
407
|
+
case "cloud.status": {
|
|
408
|
+
const status = payload;
|
|
409
|
+
log.info(`Alfe cloud: ${status.connected ? "connected" : "disconnected"}`);
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
case "daemon.shutdown":
|
|
413
|
+
log.info("Daemon shutting down — will reconnect when available");
|
|
414
|
+
break;
|
|
415
|
+
default: log.debug(`Daemon event: ${event}`, payload);
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
ipcClient.on("request", (method, _params, respond) => {
|
|
419
|
+
log.info(`Daemon request: ${method}`);
|
|
420
|
+
respond({
|
|
421
|
+
ok: false,
|
|
422
|
+
error: {
|
|
423
|
+
code: "UNKNOWN_METHOD",
|
|
424
|
+
message: `Plugin does not handle method: ${method}`
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
const client = ipcClient;
|
|
429
|
+
ipcClient.on("connected", () => {
|
|
430
|
+
log.info("Connected to Alfe daemon — registering capabilities...");
|
|
431
|
+
registerWithDaemon(client, log).then((result) => {
|
|
432
|
+
if (!result) log.error("Failed to register with daemon — commands may not be routed");
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
ipcClient.on("disconnected", (reason) => {
|
|
436
|
+
log.warn(`Disconnected from Alfe daemon: ${reason}`);
|
|
437
|
+
});
|
|
438
|
+
ipcClient.on("error", (err) => {
|
|
439
|
+
log.error(`IPC error: ${err.message}`);
|
|
440
|
+
});
|
|
441
|
+
ipcClient.start();
|
|
442
|
+
log.info(`Alfe OpenClaw Plugin activated (socket: ${socketPath})`);
|
|
443
|
+
},
|
|
444
|
+
deactivate(api) {
|
|
445
|
+
const log = api.logger;
|
|
446
|
+
log.info("Alfe OpenClaw Plugin deactivating...");
|
|
447
|
+
if (ipcClient) {
|
|
448
|
+
ipcClient.stop();
|
|
449
|
+
ipcClient = null;
|
|
450
|
+
}
|
|
451
|
+
log.info("Alfe OpenClaw Plugin deactivated");
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
//#endregion
|
|
455
|
+
export { isIPCEvent as a, PROTOCOL_VERSION as i, registerWithDaemon as n, isIPCRequest as o, IPCClient as r, isIPCResponse as s, plugin as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alfe.ai/openclaw",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "OpenClaw plugin for Alfe — connects to local gateway daemon via IPC for integration management",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./plugin": {
|
|
14
|
+
"import": "./dist/plugin.js",
|
|
15
|
+
"types": "./dist/plugin.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"openclaw": {
|
|
19
|
+
"extensions": [
|
|
20
|
+
"./dist/plugin.js"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@auriclabs/logger": "^0.1.1",
|
|
25
|
+
"@sinclair/typebox": "^0.34.48",
|
|
26
|
+
"@alfe.ai/integrations": "^0.0.1"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@alfe/agent-client": "0.1.0"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"license": "UNLICENSED",
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsdown",
|
|
37
|
+
"dev": "tsdown --watch",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest",
|
|
40
|
+
"test:coverage": "vitest run --coverage",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"lint": "eslint ."
|
|
43
|
+
}
|
|
44
|
+
}
|