@echomem/echo-memory-cloud-openclaw-plugin 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/clawdbot.plugin.json +1 -1
- package/index.js +254 -216
- package/lib/api-client.js +125 -44
- package/lib/local-server.js +537 -381
- package/lib/local-ui/src/App.jsx +133 -25
- package/lib/local-ui/src/canvas/Viewport.jsx +10 -8
- package/lib/local-ui/src/cards/Card.css +13 -0
- package/lib/local-ui/src/cards/Card.jsx +10 -1
- package/lib/local-ui/src/styles/global.css +8 -0
- package/lib/local-ui/src/sync/api.js +16 -14
- package/lib/sync.js +507 -215
- package/moltbot.plugin.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/lib/api-client.js
CHANGED
|
@@ -1,35 +1,111 @@
|
|
|
1
|
-
function buildHeaders(apiKey) {
|
|
2
|
-
return {
|
|
3
|
-
"Content-Type": "application/json",
|
|
4
|
-
Authorization: `Bearer ${apiKey}`,
|
|
5
|
-
};
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
async function
|
|
9
|
-
const
|
|
10
|
-
const
|
|
1
|
+
function buildHeaders(apiKey) {
|
|
2
|
+
return {
|
|
3
|
+
"Content-Type": "application/json",
|
|
4
|
+
Authorization: `Bearer ${apiKey}`,
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async function parseErrorResponse(response) {
|
|
9
|
+
const payload = await response.json().catch(() => ({}));
|
|
10
|
+
const detail = typeof payload?.details === "string" ? payload.details : payload?.error;
|
|
11
|
+
throw new Error(detail || `HTTP ${response.status}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function requestJson(url, options, timeoutMs) {
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
11
17
|
|
|
12
18
|
try {
|
|
13
|
-
const response = await fetch(url, {
|
|
14
|
-
...options,
|
|
15
|
-
signal: controller.signal,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
const response = await fetch(url, {
|
|
20
|
+
...options,
|
|
21
|
+
signal: controller.signal,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
await parseErrorResponse(response);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return await response.json().catch(() => ({}));
|
|
29
|
+
} finally {
|
|
30
|
+
clearTimeout(timeoutId);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function requestStreamJson(url, options, timeoutMs, { onStageEvent } = {}) {
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(url, {
|
|
40
|
+
...options,
|
|
41
|
+
signal: controller.signal,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
await parseErrorResponse(response);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const contentType = response.headers.get("content-type") || "";
|
|
49
|
+
if (!contentType.includes("application/x-ndjson") || !response.body) {
|
|
50
|
+
return await response.json().catch(() => ({}));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const reader = response.body.getReader();
|
|
54
|
+
const decoder = new TextDecoder();
|
|
55
|
+
let buffer = "";
|
|
56
|
+
let finalPayload = null;
|
|
57
|
+
|
|
58
|
+
while (true) {
|
|
59
|
+
const { done, value } = await reader.read();
|
|
60
|
+
if (done) break;
|
|
61
|
+
buffer += decoder.decode(value, { stream: true });
|
|
62
|
+
|
|
63
|
+
let newlineIndex = buffer.indexOf("\n");
|
|
64
|
+
while (newlineIndex >= 0) {
|
|
65
|
+
const line = buffer.slice(0, newlineIndex).trim();
|
|
66
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
67
|
+
if (line) {
|
|
68
|
+
const message = JSON.parse(line);
|
|
69
|
+
if (message?.type === "stage") {
|
|
70
|
+
onStageEvent?.(message);
|
|
71
|
+
} else if (message?.type === "result") {
|
|
72
|
+
finalPayload = message.payload ?? null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
newlineIndex = buffer.indexOf("\n");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const tail = buffer.trim();
|
|
80
|
+
if (tail) {
|
|
81
|
+
const message = JSON.parse(tail);
|
|
82
|
+
if (message?.type === "stage") {
|
|
83
|
+
onStageEvent?.(message);
|
|
84
|
+
} else if (message?.type === "result") {
|
|
85
|
+
finalPayload = message.payload ?? null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!finalPayload) {
|
|
90
|
+
throw new Error("Stream completed without a final result payload");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
finalPayload?.error
|
|
95
|
+
&& (!Array.isArray(finalPayload?.results) || finalPayload.results.length === 0)
|
|
96
|
+
) {
|
|
97
|
+
throw new Error(finalPayload.error);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return finalPayload;
|
|
101
|
+
} finally {
|
|
102
|
+
clearTimeout(timeoutId);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function createApiClient(cfg) {
|
|
107
|
+
const whoamiUrl = `${cfg.baseUrl}/api/openclaw/v1/whoami`;
|
|
108
|
+
const importUrl = `${cfg.baseUrl}/api/openclaw/v1/import-markdown`;
|
|
33
109
|
const statusUrl = `${cfg.baseUrl}/api/openclaw/v1/import-status`;
|
|
34
110
|
const sourcesUrl = `${cfg.baseUrl}/api/openclaw/v1/sources`;
|
|
35
111
|
const searchUrl = `${cfg.baseUrl}/api/extension/memories/search`;
|
|
@@ -48,20 +124,25 @@ export function createApiClient(cfg) {
|
|
|
48
124
|
);
|
|
49
125
|
}
|
|
50
126
|
|
|
51
|
-
async function importMarkdown(files) {
|
|
52
|
-
if (!cfg.apiKey) {
|
|
53
|
-
throw new Error("Missing Echo API key");
|
|
54
|
-
}
|
|
55
|
-
return
|
|
56
|
-
importUrl
|
|
57
|
-
{
|
|
58
|
-
method: "POST",
|
|
59
|
-
headers:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
127
|
+
async function importMarkdown(files, opts = {}) {
|
|
128
|
+
if (!cfg.apiKey) {
|
|
129
|
+
throw new Error("Missing Echo API key");
|
|
130
|
+
}
|
|
131
|
+
return requestStreamJson(
|
|
132
|
+
`${importUrl}?stream=1`,
|
|
133
|
+
{
|
|
134
|
+
method: "POST",
|
|
135
|
+
headers: {
|
|
136
|
+
...buildHeaders(cfg.apiKey),
|
|
137
|
+
Accept: "application/x-ndjson",
|
|
138
|
+
"X-OpenClaw-Stream": "1",
|
|
139
|
+
},
|
|
140
|
+
body: JSON.stringify({ files }),
|
|
141
|
+
},
|
|
142
|
+
cfg.requestTimeoutMs,
|
|
143
|
+
opts,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
65
146
|
|
|
66
147
|
async function getImportStatus() {
|
|
67
148
|
if (!cfg.apiKey) {
|