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/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
+ }
@@ -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, getWs, getIsAuthenticated } from "../runtime.js";
7
- import { relayFetch, relayLog } from "../utils.js";
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
- const { dir, filePath } = doc.agent_skill_path
35
- ? { dir: path.dirname(doc.agent_skill_path), filePath: doc.agent_skill_path }
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} \u2713 ${isUpdate ? "Updated" : "Wrote"} document: ${filePath}`);
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
- const ws = getWs();
71
- if (ws?.readyState === WS.OPEN && getIsAuthenticated()) {
72
- ws.send(JSON.stringify({
73
- type: "doc_synced",
74
- doc_id: doc.id,
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
- if (doc.agent_skill_path) {
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(doc.agent_skill_path);
108
- console.log(`${LOG_PREFIX} \u2713 Deleted document file: ${doc.agent_skill_path}`);
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 ${doc.agent_skill_path}:`, fsErr.message);
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}"${doc.agent_skill_path ? ` (was at: ${doc.agent_skill_path})` : ""}. The file has been removed from your workspace. Memory search will no longer return results from this document.`;
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(
@@ -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
- const ws = getWs();
32
- if (ws?.readyState === WS.OPEN && getIsAuthenticated()) {
33
- ws.send(JSON.stringify({
34
- type: "routine_synced",
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",
@@ -1,7 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { LOG_PREFIX } from "../config.js";
3
- import { relayFetch } from "../utils.js";
4
- import { toolOk, toolFail, wsSendOrHttp } from "./helpers.js";
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 (upload via clawaxis_upload_media first). Required for twitter, instagram, facebook, tiktok, youtube." })),
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 WebSocket/HTTP...`);
24
- const result = await wsSendOrHttp(
25
- { type: "content", action: "create", ...params },
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
- const path = params.content_id ? `content?id=${params.content_id}` : "content";
40
- const result = await relayFetch("GET", path, null);
41
- return result
42
- ? toolOk(JSON.stringify(result, null, 2), { result })
43
- : toolFail("Failed to retrieve content");
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 result = await wsSendOrHttp(
58
- { type: "content", action: "update", content_id: params.content_id, body: params.body, title: params.title, media: params.media },
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
  }
@@ -1,6 +1,4 @@
1
- import WS from "ws";
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 payload over WebSocket if connected, otherwise fall back to HTTP.
26
- * Returns the result (truthy on WS success, parsed JSON on HTTP, null on failure).
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 async function wsSendOrHttp(
29
- wsPayload: Record<string, any>,
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
  }
@@ -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 { registerMediaTools } from "./media.js";
6
- import { registerMemoryTools, setMemoryAgentToken } from "./memory.js";
5
+ import { registerMemoryTools } from "./memory.js";
7
6
 
8
- export function registerTools(api: any, agentToken: string) {
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
  }
@@ -1,14 +1,9 @@
1
1
  import { Type } from "@sinclair/typebox";
2
- import { ANON_KEY, LOG_PREFIX } from "../config.js";
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() || "520bc407-81db-4ce3-85d5-d19bd437da0f";
18
+ const agent_id = getAuthenticatedAgentId();
19
+ console.log(`${LOG_PREFIX} [Memory Search] Requesting via socket...`);
24
20
 
25
- const memoryApiUrl = "https://fqwpwypyzcbmdeajlkzt.supabase.co/functions/v1/memory-search";
26
- const requestBody = {
21
+ const result = await socketRequest("memory_search", {
27
22
  query: params.query,
28
- agent_id: 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
- console.log(`${LOG_PREFIX} [Memory Search] Response status: ${res.status}`);
28
+ const results = result?.results || [];
29
+ console.log(`${LOG_PREFIX} [Memory Search] Result count: ${results.length}`);
47
30
 
48
- if (!res.ok) {
49
- const errorText = await res.text();
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 ${result.results.length} memories:\n\n${snippets}`, {
63
- count: result.results.length,
64
- results: result.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(`No memories found. Raw response: ${JSON.stringify(result).substring(0, 500)}`, {
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
  }
@@ -1,5 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
- import { toolOk, toolFail, wsSendOrHttp } from "./helpers.js";
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 result = await wsSendOrHttp(
22
- { type: "routine_created", ...params },
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 result = await wsSendOrHttp(
46
- { type: "routine_update", ...params },
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, wsSendOrHttp } from "./helpers.js";
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 result = await wsSendOrHttp(
14
- { type: "doc_synced", ...params },
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 result = await wsSendOrHttp(
30
- { type: "routine_synced", ...params },
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
  }