clawaxis 1.0.3 → 1.2.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/index.ts +8 -15
- package/openclaw.plugin.json +10 -10
- package/package.json +3 -4
- package/src/channel.ts +14 -30
- package/src/config.ts +32 -23
- package/src/governance/hooks.ts +2 -2
- package/src/message-handler.ts +26 -56
- package/src/runtime.ts +9 -38
- package/src/socket.ts +271 -0
- package/src/sync/documents.ts +35 -24
- package/src/sync/routines.ts +5 -9
- package/src/tools/content.ts +14 -19
- package/src/tools/helpers.ts +5 -17
- package/src/tools/index.ts +2 -5
- package/src/tools/memory.ts +15 -45
- package/src/tools/routines.ts +5 -13
- package/src/tools/sync.ts +5 -11
- package/src/types.ts +2 -2
- package/src/utils.ts +24 -97
- package/src/tools/media.ts +0 -21
- package/src/websocket.ts +0 -294
package/src/socket.ts
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import net from "node:net";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import readline from "node:readline";
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
import { LOG_PREFIX, PLUGIN_VERSION } from "./config.js";
|
|
6
|
+
import {
|
|
7
|
+
getSocket,
|
|
8
|
+
setSocket,
|
|
9
|
+
getIsAuthenticated,
|
|
10
|
+
setIsAuthenticated,
|
|
11
|
+
setAuthenticatedAgentId,
|
|
12
|
+
isRunning,
|
|
13
|
+
setRunning,
|
|
14
|
+
} from "./runtime.js";
|
|
15
|
+
import { handleMessage, handleCommand } from "./message-handler.js";
|
|
16
|
+
import { processPendingDocs, processDocDeletion } from "./sync/documents.js";
|
|
17
|
+
import { processPendingRoutines } from "./sync/routines.js";
|
|
18
|
+
import type { ClawAxisConfig } from "./types.js";
|
|
19
|
+
|
|
20
|
+
const RECONNECT_DELAY_MS = 1000;
|
|
21
|
+
|
|
22
|
+
let _config: ClawAxisConfig | null = null;
|
|
23
|
+
|
|
24
|
+
type PendingRequest = {
|
|
25
|
+
resolve: (data: any) => void;
|
|
26
|
+
reject: (err: Error) => void;
|
|
27
|
+
timer: NodeJS.Timeout;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const pendingRequests = new Map<string, PendingRequest>();
|
|
31
|
+
|
|
32
|
+
function generateId(): string {
|
|
33
|
+
return `msg_${crypto.randomBytes(8).toString("hex")}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Write a newline-delimited JSON message to the socket.
|
|
38
|
+
*/
|
|
39
|
+
export function socketWrite(obj: Record<string, any>): boolean {
|
|
40
|
+
const sock = getSocket();
|
|
41
|
+
if (!sock || sock.destroyed || !getIsAuthenticated()) return false;
|
|
42
|
+
sock.write(JSON.stringify(obj) + "\n");
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Send an outbound message wrapped in a relay envelope.
|
|
48
|
+
* Returns true if the message was written to the socket.
|
|
49
|
+
*/
|
|
50
|
+
export function relaySend(envelope: Record<string, any>): boolean {
|
|
51
|
+
return socketWrite({ type: "relay", id: generateId(), envelope });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Send a request to Connect and wait for the response.
|
|
56
|
+
* Used for RPC-style calls (e.g., memory_search).
|
|
57
|
+
*/
|
|
58
|
+
export function socketRequest(
|
|
59
|
+
method: string,
|
|
60
|
+
params?: Record<string, any>,
|
|
61
|
+
timeoutMs = 10000,
|
|
62
|
+
): Promise<any> {
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
const id = `req_${crypto.randomBytes(8).toString("hex")}`;
|
|
65
|
+
|
|
66
|
+
const timer = setTimeout(() => {
|
|
67
|
+
pendingRequests.delete(id);
|
|
68
|
+
reject(new Error(`Request ${method} timed out after ${timeoutMs}ms`));
|
|
69
|
+
}, timeoutMs);
|
|
70
|
+
|
|
71
|
+
pendingRequests.set(id, { resolve, reject, timer });
|
|
72
|
+
|
|
73
|
+
const written = socketWrite({ type: "request", id, method, params });
|
|
74
|
+
if (!written) {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
pendingRequests.delete(id);
|
|
77
|
+
reject(new Error("Socket not connected"));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function readToken(tokenPath: string): string | null {
|
|
83
|
+
try {
|
|
84
|
+
return fs.readFileSync(tokenPath, "utf8").trim();
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function connect() {
|
|
91
|
+
if (!_config || !isRunning()) return;
|
|
92
|
+
|
|
93
|
+
const { socketPath, tokenPath } = _config;
|
|
94
|
+
|
|
95
|
+
if (!fs.existsSync(socketPath)) {
|
|
96
|
+
console.warn(`${LOG_PREFIX} Socket not found at ${socketPath}, retrying in ${RECONNECT_DELAY_MS}ms...`);
|
|
97
|
+
scheduleReconnect();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const token = readToken(tokenPath);
|
|
102
|
+
if (!token) {
|
|
103
|
+
console.warn(`${LOG_PREFIX} Token file not found at ${tokenPath}, retrying in ${RECONNECT_DELAY_MS}ms...`);
|
|
104
|
+
scheduleReconnect();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log(`${LOG_PREFIX} [Socket] Connecting to ${socketPath}...`);
|
|
109
|
+
|
|
110
|
+
const sock = net.createConnection({ path: socketPath });
|
|
111
|
+
const rl = readline.createInterface({ input: sock });
|
|
112
|
+
|
|
113
|
+
sock.on("connect", () => {
|
|
114
|
+
console.log(`${LOG_PREFIX} [Socket] Connected, authenticating...`);
|
|
115
|
+
setSocket(sock);
|
|
116
|
+
sock.write(JSON.stringify({
|
|
117
|
+
type: "auth",
|
|
118
|
+
token,
|
|
119
|
+
plugin_version: PLUGIN_VERSION,
|
|
120
|
+
protocol_version: 1,
|
|
121
|
+
}) + "\n");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
rl.on("line", async (line: string) => {
|
|
125
|
+
try {
|
|
126
|
+
const msg = JSON.parse(line);
|
|
127
|
+
await handleSocketMessage(msg);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error(`${LOG_PREFIX} [Socket] Failed to parse:`, err);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
sock.on("close", () => {
|
|
134
|
+
console.log(`${LOG_PREFIX} [Socket] Disconnected`);
|
|
135
|
+
cleanup();
|
|
136
|
+
scheduleReconnect();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
sock.on("error", (err: Error) => {
|
|
140
|
+
console.error(`${LOG_PREFIX} [Socket] Error:`, err.message);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function cleanup() {
|
|
145
|
+
setIsAuthenticated(false);
|
|
146
|
+
setAuthenticatedAgentId(null);
|
|
147
|
+
|
|
148
|
+
for (const [id, req] of pendingRequests) {
|
|
149
|
+
clearTimeout(req.timer);
|
|
150
|
+
req.reject(new Error("Socket disconnected"));
|
|
151
|
+
pendingRequests.delete(id);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function scheduleReconnect() {
|
|
156
|
+
if (!isRunning()) return;
|
|
157
|
+
setTimeout(() => {
|
|
158
|
+
if (isRunning()) connect();
|
|
159
|
+
}, RECONNECT_DELAY_MS);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function handleSocketMessage(msg: any) {
|
|
163
|
+
switch (msg.type) {
|
|
164
|
+
case "auth_ok":
|
|
165
|
+
console.log(`${LOG_PREFIX} [Socket] Authenticated (connect v${msg.connect_version}, device=${msg.device_state})`);
|
|
166
|
+
setIsAuthenticated(true);
|
|
167
|
+
setAuthenticatedAgentId(msg.agent_id || null);
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case "auth_error":
|
|
171
|
+
console.error(`${LOG_PREFIX} [Socket] Auth failed: ${msg.reason}`);
|
|
172
|
+
setIsAuthenticated(false);
|
|
173
|
+
if (msg.reason === "already_connected") {
|
|
174
|
+
console.warn(`${LOG_PREFIX} [Socket] Another plugin instance is connected, will retry...`);
|
|
175
|
+
} else {
|
|
176
|
+
setRunning(false);
|
|
177
|
+
}
|
|
178
|
+
getSocket()?.destroy();
|
|
179
|
+
break;
|
|
180
|
+
|
|
181
|
+
case "relay": {
|
|
182
|
+
const envelope = msg.envelope;
|
|
183
|
+
if (!envelope) break;
|
|
184
|
+
await routeInboundEnvelope(envelope);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
case "relay_ack":
|
|
189
|
+
if (!msg.forwarded && !msg.queued) {
|
|
190
|
+
console.warn(`${LOG_PREFIX} [Socket] Relay not forwarded: ${msg.id}`);
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
|
|
194
|
+
case "response": {
|
|
195
|
+
const pending = pendingRequests.get(msg.id);
|
|
196
|
+
if (pending) {
|
|
197
|
+
clearTimeout(pending.timer);
|
|
198
|
+
pendingRequests.delete(msg.id);
|
|
199
|
+
if (msg.ok) {
|
|
200
|
+
pending.resolve(msg.data);
|
|
201
|
+
} else {
|
|
202
|
+
pending.reject(new Error(msg.error || "Request failed"));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
case "pong":
|
|
209
|
+
break;
|
|
210
|
+
|
|
211
|
+
default:
|
|
212
|
+
console.warn(`${LOG_PREFIX} [Socket] Unknown message type: ${msg.type}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function routeInboundEnvelope(envelope: any) {
|
|
217
|
+
switch (envelope.type) {
|
|
218
|
+
case "message":
|
|
219
|
+
await handleMessage(envelope);
|
|
220
|
+
break;
|
|
221
|
+
case "command":
|
|
222
|
+
await handleCommand(envelope);
|
|
223
|
+
break;
|
|
224
|
+
case "sync":
|
|
225
|
+
case "heartbeat_ack":
|
|
226
|
+
if (envelope.pending_docs?.length > 0) await processPendingDocs(envelope.pending_docs);
|
|
227
|
+
if (envelope.pending_routines?.length > 0) await processPendingRoutines(envelope.pending_routines);
|
|
228
|
+
break;
|
|
229
|
+
case "doc_deleted":
|
|
230
|
+
await processDocDeletion(envelope);
|
|
231
|
+
break;
|
|
232
|
+
case "content_ack":
|
|
233
|
+
console.log(`${LOG_PREFIX} Content ${envelope.action}: ${envelope.content_id}`);
|
|
234
|
+
break;
|
|
235
|
+
case "routine_ack":
|
|
236
|
+
console.log(`${LOG_PREFIX} Routine ${envelope.action}: ${envelope.routine_id}`);
|
|
237
|
+
break;
|
|
238
|
+
case "error":
|
|
239
|
+
console.warn(`${LOG_PREFIX} Server error: ${envelope.reason} — ${envelope.detail || ""}`);
|
|
240
|
+
break;
|
|
241
|
+
default:
|
|
242
|
+
console.warn(`${LOG_PREFIX} Unknown envelope type: ${envelope.type}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function createSocketService(cfg: ClawAxisConfig) {
|
|
247
|
+
_config = cfg;
|
|
248
|
+
let started = false;
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
id: "clawaxis-socket",
|
|
252
|
+
|
|
253
|
+
async start() {
|
|
254
|
+
if (started) return;
|
|
255
|
+
started = true;
|
|
256
|
+
console.log(`${LOG_PREFIX} Starting Unix socket connection...`);
|
|
257
|
+
setRunning(true);
|
|
258
|
+
connect();
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
async stop() {
|
|
262
|
+
console.log(`${LOG_PREFIX} Shutting down socket...`);
|
|
263
|
+
started = false;
|
|
264
|
+
setRunning(false);
|
|
265
|
+
cleanup();
|
|
266
|
+
const sock = getSocket();
|
|
267
|
+
sock?.destroy();
|
|
268
|
+
setSocket(null);
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
package/src/sync/documents.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import WS from "ws";
|
|
5
4
|
import { LOG_PREFIX } from "../config.js";
|
|
6
|
-
import { getRuntime
|
|
7
|
-
import {
|
|
5
|
+
import { getRuntime } from "../runtime.js";
|
|
6
|
+
import { relayLog } from "../utils.js";
|
|
7
|
+
import { relaySend } from "../socket.js";
|
|
8
8
|
import { dispatchSystemMessage } from "../message-handler.js";
|
|
9
9
|
|
|
10
10
|
export function resolveDocPath(doc: any): { dir: string; filePath: string } {
|
|
@@ -31,8 +31,18 @@ export async function processPendingDocs(docs: any[]): Promise<void> {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const isUpdate = (doc.version && doc.version > 1) || !!doc.agent_skill_path;
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
|
|
35
|
+
let resolvedSkillPath = doc.agent_skill_path;
|
|
36
|
+
if (resolvedSkillPath && !fs.existsSync(path.dirname(resolvedSkillPath))) {
|
|
37
|
+
const runtime = getRuntime();
|
|
38
|
+
const workspaceDir = runtime?.config?.workspace?.dir
|
|
39
|
+
|| path.join(homedir(), ".openclaw", "workspace");
|
|
40
|
+
const relative = resolvedSkillPath.replace(/^.*?\.openclaw\/workspace\//, "");
|
|
41
|
+
resolvedSkillPath = path.join(workspaceDir, relative);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const { dir, filePath } = resolvedSkillPath
|
|
45
|
+
? { dir: path.dirname(resolvedSkillPath), filePath: resolvedSkillPath }
|
|
36
46
|
: resolveDocPath(doc);
|
|
37
47
|
|
|
38
48
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -52,7 +62,7 @@ export async function processPendingDocs(docs: any[]): Promise<void> {
|
|
|
52
62
|
const fileContent = `${frontmatter}\n\n# ${doc.name}\n\n${doc.content}`;
|
|
53
63
|
fs.writeFileSync(filePath, fileContent, "utf8");
|
|
54
64
|
|
|
55
|
-
console.log(`${LOG_PREFIX}
|
|
65
|
+
console.log(`${LOG_PREFIX} ${isUpdate ? "Updated" : "Wrote"} document: ${filePath}`);
|
|
56
66
|
|
|
57
67
|
const docMeta = [
|
|
58
68
|
doc.doc_type ? `Type: ${doc.doc_type}` : null,
|
|
@@ -67,19 +77,11 @@ export async function processPendingDocs(docs: any[]): Promise<void> {
|
|
|
67
77
|
|
|
68
78
|
await dispatchSystemMessage(messageContent, `doc-sync-${doc.id}`, `doc-sync-${doc.id}`);
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
agent_skill_path: filePath
|
|
76
|
-
}));
|
|
77
|
-
} else {
|
|
78
|
-
await relayFetch("POST", "doc-synced", {
|
|
79
|
-
document_id: doc.id,
|
|
80
|
-
agent_skill_path: filePath
|
|
81
|
-
});
|
|
82
|
-
}
|
|
80
|
+
relaySend({
|
|
81
|
+
type: "doc_synced",
|
|
82
|
+
doc_id: doc.id,
|
|
83
|
+
agent_skill_path: filePath,
|
|
84
|
+
});
|
|
83
85
|
|
|
84
86
|
await relayLog(
|
|
85
87
|
"system",
|
|
@@ -102,16 +104,25 @@ export async function processPendingDocs(docs: any[]): Promise<void> {
|
|
|
102
104
|
|
|
103
105
|
export async function processDocDeletion(doc: { id: string; name: string; agent_skill_path?: string }): Promise<void> {
|
|
104
106
|
try {
|
|
105
|
-
|
|
107
|
+
let deletePath = doc.agent_skill_path;
|
|
108
|
+
if (deletePath && !fs.existsSync(deletePath)) {
|
|
109
|
+
const runtime = getRuntime();
|
|
110
|
+
const workspaceDir = runtime?.config?.workspace?.dir
|
|
111
|
+
|| path.join(homedir(), ".openclaw", "workspace");
|
|
112
|
+
const relative = deletePath.replace(/^.*?\.openclaw\/workspace\//, "");
|
|
113
|
+
deletePath = path.join(workspaceDir, relative);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (deletePath) {
|
|
106
117
|
try {
|
|
107
|
-
fs.unlinkSync(
|
|
108
|
-
console.log(`${LOG_PREFIX}
|
|
118
|
+
fs.unlinkSync(deletePath);
|
|
119
|
+
console.log(`${LOG_PREFIX} Deleted document file: ${deletePath}`);
|
|
109
120
|
} catch (fsErr: any) {
|
|
110
|
-
console.warn(`${LOG_PREFIX} Could not delete file ${
|
|
121
|
+
console.warn(`${LOG_PREFIX} Could not delete file ${deletePath}:`, fsErr.message);
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
|
|
114
|
-
const messageContent = `[DOC_DELETED] The user deleted document "${doc.name}"${
|
|
125
|
+
const messageContent = `[DOC_DELETED] The user deleted document "${doc.name}"${deletePath ? ` (was at: ${deletePath})` : ""}. The file has been removed from your workspace. Memory search will no longer return results from this document.`;
|
|
115
126
|
await dispatchSystemMessage(messageContent, `doc-delete-${doc.id}`, `doc-delete-${doc.id}`);
|
|
116
127
|
|
|
117
128
|
await relayLog(
|
package/src/sync/routines.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import WS from "ws";
|
|
2
1
|
import { LOG_PREFIX } from "../config.js";
|
|
3
|
-
import { getWs, getIsAuthenticated } from "../runtime.js";
|
|
4
2
|
import { relayLog } from "../utils.js";
|
|
3
|
+
import { relaySend } from "../socket.js";
|
|
5
4
|
import { dispatchSystemMessage } from "../message-handler.js";
|
|
6
5
|
|
|
7
6
|
export async function processPendingRoutines(routines: any[]): Promise<void> {
|
|
@@ -28,13 +27,10 @@ export async function processPendingRoutines(routines: any[]): Promise<void> {
|
|
|
28
27
|
|
|
29
28
|
await dispatchSystemMessage(messageContent, `routine-sync-${routine.id}`, `routine-sync-${routine.id}`);
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
routine_id: routine.id
|
|
36
|
-
}));
|
|
37
|
-
}
|
|
30
|
+
relaySend({
|
|
31
|
+
type: "routine_synced",
|
|
32
|
+
routine_id: routine.id,
|
|
33
|
+
});
|
|
38
34
|
|
|
39
35
|
await relayLog(
|
|
40
36
|
"system",
|
package/src/tools/content.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { LOG_PREFIX } from "../config.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { toolOk, toolFail, socketSend } from "./helpers.js";
|
|
4
|
+
import { socketRequest } from "../socket.js";
|
|
5
5
|
|
|
6
6
|
export function registerContentTools(api: any) {
|
|
7
7
|
api.registerTool({
|
|
@@ -17,15 +17,12 @@ export function registerContentTools(api: any) {
|
|
|
17
17
|
word_count: Type.Optional(Type.Number({ description: "Word count. Shown in blog/report/email card headers. Always provide for long-form content." })),
|
|
18
18
|
prompt_used: Type.Optional(Type.String({ description: "The prompt that generated this content. Shown in Generation Info section." })),
|
|
19
19
|
model_used: Type.Optional(Type.String({ description: "Model ID (e.g. 'anthropic/claude-sonnet-4-5'). Shown in Generation Info section." })),
|
|
20
|
-
media: Type.Optional(Type.Any({ description: "Media object: {images: [{url: string, alt?: string}], videos: [{url: string, alt?: string}]}. Use permanent URLs only
|
|
20
|
+
media: Type.Optional(Type.Any({ description: "Media object: {images: [{url: string, alt?: string}], videos: [{url: string, alt?: string}]}. Use permanent URLs only. Required for twitter, instagram, facebook, tiktok, youtube." })),
|
|
21
21
|
}),
|
|
22
22
|
async execute(_id: string, params: any) {
|
|
23
|
-
console.log(`${LOG_PREFIX} Sending content via
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
"POST", "content", params,
|
|
27
|
-
);
|
|
28
|
-
return result ? toolOk("Content posted to ClawAxis app", { result }) : toolFail("Failed to post content");
|
|
23
|
+
console.log(`${LOG_PREFIX} Sending content via socket...`);
|
|
24
|
+
const sent = socketSend({ type: "content", action: "create", ...params });
|
|
25
|
+
return sent ? toolOk("Content posted to ClawAxis app") : toolFail("Socket not connected");
|
|
29
26
|
},
|
|
30
27
|
});
|
|
31
28
|
|
|
@@ -36,11 +33,12 @@ export function registerContentTools(api: any) {
|
|
|
36
33
|
content_id: Type.Optional(Type.String({ description: "UUID of specific content item to fetch. If omitted, returns recent content list." })),
|
|
37
34
|
}),
|
|
38
35
|
async execute(_id: string, params: any) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
try {
|
|
37
|
+
const result = await socketRequest("get_content", params);
|
|
38
|
+
return toolOk(JSON.stringify(result, null, 2), { result });
|
|
39
|
+
} catch (err: any) {
|
|
40
|
+
return toolFail(`Failed to retrieve content: ${err.message}`);
|
|
41
|
+
}
|
|
44
42
|
},
|
|
45
43
|
});
|
|
46
44
|
|
|
@@ -54,11 +52,8 @@ export function registerContentTools(api: any) {
|
|
|
54
52
|
media: Type.Optional(Type.Any({ description: "Updated media: {images: [{url: string, alt?: string}], videos: [{url: string, alt?: string}]}. Omit to keep existing." })),
|
|
55
53
|
}),
|
|
56
54
|
async execute(_id: string, params: any) {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
"POST", "content-update", params,
|
|
60
|
-
);
|
|
61
|
-
return result ? toolOk("Content updated successfully", { result }) : toolFail("Failed to update content");
|
|
55
|
+
const sent = socketSend({ type: "content", action: "update", ...params });
|
|
56
|
+
return sent ? toolOk("Content updated successfully") : toolFail("Socket not connected");
|
|
62
57
|
},
|
|
63
58
|
});
|
|
64
59
|
}
|
package/src/tools/helpers.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { getWs, getIsAuthenticated } from "../runtime.js";
|
|
3
|
-
import { relayFetch } from "../utils.js";
|
|
1
|
+
import { relaySend } from "../socket.js";
|
|
4
2
|
|
|
5
3
|
export interface ToolResult {
|
|
6
4
|
content: Array<{ type: "text"; text: string }>;
|
|
@@ -22,19 +20,9 @@ export function toolFail(message: string, extra?: Record<string, any>): ToolResu
|
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
/**
|
|
25
|
-
* Send a
|
|
26
|
-
* Returns
|
|
23
|
+
* Send a message envelope through the Unix socket to Connect.
|
|
24
|
+
* Returns true if the message was written to the socket.
|
|
27
25
|
*/
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
httpMethod: string,
|
|
31
|
-
httpPath: string,
|
|
32
|
-
httpBody?: any,
|
|
33
|
-
): Promise<any> {
|
|
34
|
-
const ws = getWs();
|
|
35
|
-
if (ws?.readyState === WS.OPEN && getIsAuthenticated()) {
|
|
36
|
-
(ws as any).send(JSON.stringify(wsPayload));
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
return relayFetch(httpMethod, httpPath, httpBody ?? wsPayload);
|
|
26
|
+
export function socketSend(envelope: Record<string, any>): boolean {
|
|
27
|
+
return relaySend(envelope);
|
|
40
28
|
}
|
package/src/tools/index.ts
CHANGED
|
@@ -2,15 +2,12 @@ import { registerContentTools } from "./content.js";
|
|
|
2
2
|
import { registerLoggingTools } from "./logging.js";
|
|
3
3
|
import { registerSyncTools } from "./sync.js";
|
|
4
4
|
import { registerRoutineTools } from "./routines.js";
|
|
5
|
-
import {
|
|
6
|
-
import { registerMemoryTools, setMemoryAgentToken } from "./memory.js";
|
|
5
|
+
import { registerMemoryTools } from "./memory.js";
|
|
7
6
|
|
|
8
|
-
export function registerTools(api: any
|
|
9
|
-
setMemoryAgentToken(agentToken);
|
|
7
|
+
export function registerTools(api: any) {
|
|
10
8
|
registerContentTools(api);
|
|
11
9
|
registerLoggingTools(api);
|
|
12
10
|
registerSyncTools(api);
|
|
13
11
|
registerRoutineTools(api);
|
|
14
|
-
registerMediaTools(api);
|
|
15
12
|
registerMemoryTools(api);
|
|
16
13
|
}
|
package/src/tools/memory.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import {
|
|
2
|
+
import { LOG_PREFIX } from "../config.js";
|
|
3
3
|
import { getAuthenticatedAgentId } from "../runtime.js";
|
|
4
|
+
import { socketRequest } from "../socket.js";
|
|
4
5
|
import { toolOk, toolFail } from "./helpers.js";
|
|
5
6
|
|
|
6
|
-
let _agentToken = "";
|
|
7
|
-
|
|
8
|
-
export function setMemoryAgentToken(token: string) {
|
|
9
|
-
_agentToken = token;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
7
|
export function registerMemoryTools(api: any) {
|
|
13
8
|
api.registerTool({
|
|
14
9
|
name: "clawaxis_memory_search",
|
|
@@ -20,56 +15,31 @@ export function registerMemoryTools(api: any) {
|
|
|
20
15
|
}),
|
|
21
16
|
async execute(_id: string, params: any) {
|
|
22
17
|
try {
|
|
23
|
-
const agent_id = getAuthenticatedAgentId()
|
|
18
|
+
const agent_id = getAuthenticatedAgentId();
|
|
19
|
+
console.log(`${LOG_PREFIX} [Memory Search] Requesting via socket...`);
|
|
24
20
|
|
|
25
|
-
const
|
|
26
|
-
const requestBody = {
|
|
21
|
+
const result = await socketRequest("memory_search", {
|
|
27
22
|
query: params.query,
|
|
28
|
-
agent_id
|
|
23
|
+
agent_id,
|
|
29
24
|
match_count: params.match_count || 10,
|
|
30
|
-
match_threshold: params.match_threshold || 0.5
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
console.log(`${LOG_PREFIX} [Memory Search] Calling: ${memoryApiUrl}`);
|
|
34
|
-
|
|
35
|
-
const res = await fetch(memoryApiUrl, {
|
|
36
|
-
method: "POST",
|
|
37
|
-
headers: {
|
|
38
|
-
"Authorization": `Bearer ${ANON_KEY}`,
|
|
39
|
-
"apikey": ANON_KEY,
|
|
40
|
-
"x-agent-token": _agentToken,
|
|
41
|
-
"Content-Type": "application/json",
|
|
42
|
-
},
|
|
43
|
-
body: JSON.stringify(requestBody)
|
|
25
|
+
match_threshold: params.match_threshold || 0.5,
|
|
44
26
|
});
|
|
45
27
|
|
|
46
|
-
|
|
28
|
+
const results = result?.results || [];
|
|
29
|
+
console.log(`${LOG_PREFIX} [Memory Search] Result count: ${results.length}`);
|
|
47
30
|
|
|
48
|
-
if (
|
|
49
|
-
const
|
|
50
|
-
console.log(`${LOG_PREFIX} [Memory Search] Error response:`, errorText);
|
|
51
|
-
return toolFail(`Memory search failed: ${res.status} ${errorText}`, { error: errorText });
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const result = await res.json();
|
|
55
|
-
console.log(`${LOG_PREFIX} [Memory Search] Result count: ${result?.results?.length || 0}`);
|
|
56
|
-
|
|
57
|
-
if (result?.results && result.results.length > 0) {
|
|
58
|
-
const snippets = result.results.map((r: any, i: number) =>
|
|
31
|
+
if (results.length > 0) {
|
|
32
|
+
const snippets = results.map((r: any, i: number) =>
|
|
59
33
|
`[Result ${i + 1}] (Similarity: ${r.similarity?.toFixed(3)})\n${r.content}\n`
|
|
60
34
|
).join("\n---\n\n");
|
|
61
35
|
|
|
62
|
-
return toolOk(`Found ${
|
|
63
|
-
count:
|
|
64
|
-
results
|
|
36
|
+
return toolOk(`Found ${results.length} memories:\n\n${snippets}`, {
|
|
37
|
+
count: results.length,
|
|
38
|
+
results,
|
|
65
39
|
});
|
|
66
40
|
}
|
|
67
41
|
|
|
68
|
-
return toolOk(
|
|
69
|
-
count: 0,
|
|
70
|
-
results: [],
|
|
71
|
-
raw: result,
|
|
72
|
-
});
|
|
42
|
+
return toolOk("No memories found.", { count: 0, results: [] });
|
|
73
43
|
} catch (err: any) {
|
|
74
44
|
return toolFail(`Memory search error: ${err.message}`, { error: err.message });
|
|
75
45
|
}
|
package/src/tools/routines.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { toolOk, toolFail,
|
|
2
|
+
import { toolOk, toolFail, socketSend } from "./helpers.js";
|
|
3
3
|
|
|
4
4
|
export function registerRoutineTools(api: any) {
|
|
5
5
|
api.registerTool({
|
|
@@ -18,11 +18,8 @@ export function registerRoutineTools(api: any) {
|
|
|
18
18
|
status: Type.Optional(Type.String({ description: "Must be: active, paused, or disabled. Default: active." })),
|
|
19
19
|
}),
|
|
20
20
|
async execute(_id: string, params: any) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
"POST", "routine-created", params,
|
|
24
|
-
);
|
|
25
|
-
return result ? toolOk("Routine registered in app", { result }) : toolFail("Failed to register routine");
|
|
21
|
+
const sent = socketSend({ type: "routine_created", ...params });
|
|
22
|
+
return sent ? toolOk("Routine registered in app") : toolFail("Socket not connected");
|
|
26
23
|
},
|
|
27
24
|
});
|
|
28
25
|
|
|
@@ -42,13 +39,8 @@ export function registerRoutineTools(api: any) {
|
|
|
42
39
|
status: Type.Optional(Type.String({ description: "active, paused, or disabled" })),
|
|
43
40
|
}),
|
|
44
41
|
async execute(_id: string, params: any) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
"POST", "routine-update", params,
|
|
48
|
-
);
|
|
49
|
-
return result?.ok
|
|
50
|
-
? toolOk("Routine updated in app", { result })
|
|
51
|
-
: toolFail("Failed to update routine", { result });
|
|
42
|
+
const sent = socketSend({ type: "routine_update", ...params });
|
|
43
|
+
return sent ? toolOk("Routine updated in app") : toolFail("Socket not connected");
|
|
52
44
|
},
|
|
53
45
|
});
|
|
54
46
|
}
|
package/src/tools/sync.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { toolOk, toolFail,
|
|
2
|
+
import { toolOk, toolFail, socketSend } from "./helpers.js";
|
|
3
3
|
|
|
4
4
|
export function registerSyncTools(api: any) {
|
|
5
5
|
api.registerTool({
|
|
@@ -10,11 +10,8 @@ export function registerSyncTools(api: any) {
|
|
|
10
10
|
agent_skill_path: Type.Optional(Type.String({ description: "Path where the document was saved in agent skills" })),
|
|
11
11
|
}),
|
|
12
12
|
async execute(_id: string, params: any) {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
"POST", "doc-synced", params,
|
|
16
|
-
);
|
|
17
|
-
return result ? toolOk("Document sync confirmed", { result }) : toolFail("Failed to confirm document sync");
|
|
13
|
+
const sent = socketSend({ type: "doc_synced", ...params });
|
|
14
|
+
return sent ? toolOk("Document sync confirmed") : toolFail("Socket not connected");
|
|
18
15
|
},
|
|
19
16
|
});
|
|
20
17
|
|
|
@@ -26,11 +23,8 @@ export function registerSyncTools(api: any) {
|
|
|
26
23
|
openclaw_cron_id: Type.Optional(Type.String({ description: "OpenClaw cron job ID that was created" })),
|
|
27
24
|
}),
|
|
28
25
|
async execute(_id: string, params: any) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
"POST", "routine-synced", params,
|
|
32
|
-
);
|
|
33
|
-
return result ? toolOk("Routine sync confirmed", { result }) : toolFail("Failed to confirm routine sync");
|
|
26
|
+
const sent = socketSend({ type: "routine_synced", ...params });
|
|
27
|
+
return sent ? toolOk("Routine sync confirmed") : toolFail("Socket not connected");
|
|
34
28
|
},
|
|
35
29
|
});
|
|
36
30
|
}
|