@memtensor/memos-local-openclaw-plugin 1.0.4-beta.6 → 1.0.4-beta.8
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 +38 -21
- package/dist/capture/index.d.ts +1 -1
- package/dist/capture/index.d.ts.map +1 -1
- package/dist/capture/index.js +29 -3
- package/dist/capture/index.js.map +1 -1
- package/dist/client/connector.d.ts +29 -0
- package/dist/client/connector.d.ts.map +1 -0
- package/dist/client/connector.js +231 -0
- package/dist/client/connector.js.map +1 -0
- package/dist/client/hub.d.ts +61 -0
- package/dist/client/hub.d.ts.map +1 -0
- package/dist/client/hub.js +170 -0
- package/dist/client/hub.js.map +1 -0
- package/dist/client/skill-sync.d.ts +36 -0
- package/dist/client/skill-sync.d.ts.map +1 -0
- package/dist/client/skill-sync.js +226 -0
- package/dist/client/skill-sync.js.map +1 -0
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +70 -3
- package/dist/config.js.map +1 -1
- package/dist/embedding/index.d.ts +4 -2
- package/dist/embedding/index.d.ts.map +1 -1
- package/dist/embedding/index.js +17 -1
- package/dist/embedding/index.js.map +1 -1
- package/dist/hub/auth.d.ts +19 -0
- package/dist/hub/auth.d.ts.map +1 -0
- package/dist/hub/auth.js +70 -0
- package/dist/hub/auth.js.map +1 -0
- package/dist/hub/server.d.ts +48 -0
- package/dist/hub/server.d.ts.map +1 -0
- package/dist/hub/server.js +922 -0
- package/dist/hub/server.js.map +1 -0
- package/dist/hub/user-manager.d.ts +31 -0
- package/dist/hub/user-manager.d.ts.map +1 -0
- package/dist/hub/user-manager.js +129 -0
- package/dist/hub/user-manager.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/ingest/providers/index.d.ts +10 -2
- package/dist/ingest/providers/index.d.ts.map +1 -1
- package/dist/ingest/providers/index.js +203 -6
- package/dist/ingest/providers/index.js.map +1 -1
- package/dist/ingest/providers/openai.d.ts +1 -0
- package/dist/ingest/providers/openai.d.ts.map +1 -1
- package/dist/ingest/providers/openai.js +1 -0
- package/dist/ingest/providers/openai.js.map +1 -1
- package/dist/ingest/task-processor.js +1 -1
- package/dist/ingest/task-processor.js.map +1 -1
- package/dist/openclaw-api.d.ts +53 -0
- package/dist/openclaw-api.d.ts.map +1 -0
- package/dist/openclaw-api.js +189 -0
- package/dist/openclaw-api.js.map +1 -0
- package/dist/recall/engine.js +1 -1
- package/dist/recall/engine.js.map +1 -1
- package/dist/shared/llm-call.d.ts +4 -1
- package/dist/shared/llm-call.d.ts.map +1 -1
- package/dist/shared/llm-call.js +14 -1
- package/dist/shared/llm-call.js.map +1 -1
- package/dist/sharing/types.contract.d.ts +2 -0
- package/dist/sharing/types.contract.d.ts.map +1 -0
- package/dist/sharing/types.contract.js +3 -0
- package/dist/sharing/types.contract.js.map +1 -0
- package/dist/sharing/types.d.ts +80 -0
- package/dist/sharing/types.d.ts.map +1 -0
- package/dist/sharing/types.js +3 -0
- package/dist/sharing/types.js.map +1 -0
- package/dist/skill/evaluator.d.ts.map +1 -1
- package/dist/skill/evaluator.js +2 -2
- package/dist/skill/evaluator.js.map +1 -1
- package/dist/skill/generator.d.ts.map +1 -1
- package/dist/skill/generator.js +4 -4
- package/dist/skill/generator.js.map +1 -1
- package/dist/skill/upgrader.js +1 -1
- package/dist/skill/upgrader.js.map +1 -1
- package/dist/skill/validator.js +1 -1
- package/dist/skill/validator.js.map +1 -1
- package/dist/storage/ensure-binding.d.ts.map +1 -1
- package/dist/storage/ensure-binding.js +3 -1
- package/dist/storage/ensure-binding.js.map +1 -1
- package/dist/storage/sqlite.d.ts +332 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +913 -4
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/memory-search.d.ts +5 -2
- package/dist/tools/memory-search.d.ts.map +1 -1
- package/dist/tools/memory-search.js +50 -7
- package/dist/tools/memory-search.js.map +1 -1
- package/dist/tools/network-memory-detail.d.ts +4 -0
- package/dist/tools/network-memory-detail.d.ts.map +1 -0
- package/dist/tools/network-memory-detail.js +34 -0
- package/dist/tools/network-memory-detail.js.map +1 -0
- package/dist/types.d.ts +48 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +4299 -511
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +65 -0
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +1844 -90
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +767 -41
- package/openclaw.plugin.json +3 -2
- package/package.json +3 -3
- package/scripts/postinstall.cjs +282 -45
- package/skill/memos-memory-guide/SKILL.md +82 -20
- package/src/capture/index.ts +30 -2
- package/src/client/connector.ts +225 -0
- package/src/client/hub.ts +207 -0
- package/src/client/skill-sync.ts +216 -0
- package/src/config.ts +92 -3
- package/src/embedding/index.ts +21 -1
- package/src/hub/auth.ts +78 -0
- package/src/hub/server.ts +906 -0
- package/src/hub/user-manager.ts +143 -0
- package/src/index.ts +13 -5
- package/src/ingest/providers/index.ts +240 -6
- package/src/ingest/providers/openai.ts +1 -1
- package/src/ingest/task-processor.ts +1 -1
- package/src/openclaw-api.ts +287 -0
- package/src/recall/engine.ts +1 -1
- package/src/shared/llm-call.ts +18 -2
- package/src/sharing/types.contract.ts +40 -0
- package/src/sharing/types.ts +102 -0
- package/src/skill/evaluator.ts +3 -2
- package/src/skill/generator.ts +6 -4
- package/src/skill/upgrader.ts +1 -1
- package/src/skill/validator.ts +1 -1
- package/src/storage/ensure-binding.ts +3 -1
- package/src/storage/sqlite.ts +1164 -4
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-search.ts +58 -8
- package/src/tools/network-memory-detail.ts +34 -0
- package/src/types.ts +43 -2
- package/src/viewer/html.ts +4299 -511
- package/src/viewer/server.ts +1688 -73
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw Host Model Proxy
|
|
3
|
+
*
|
|
4
|
+
* Reads the host's configured model providers (api.config.models.providers)
|
|
5
|
+
* and proxies completion / embedding requests via OpenAI-compatible HTTP calls.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Logger, OpenClawAPI } from "./types";
|
|
9
|
+
|
|
10
|
+
// ── Request / Response types ────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
export interface OpenClawEmbedRequest {
|
|
13
|
+
texts: string[];
|
|
14
|
+
model?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface OpenClawEmbedResponse {
|
|
18
|
+
embeddings: number[][];
|
|
19
|
+
dimensions: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface OpenClawCompleteRequest {
|
|
23
|
+
prompt: string;
|
|
24
|
+
maxTokens?: number;
|
|
25
|
+
temperature?: number;
|
|
26
|
+
model?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface OpenClawCompleteResponse {
|
|
30
|
+
text: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Host model config (mirrors OpenClaw SDK types loosely) ──────────
|
|
34
|
+
|
|
35
|
+
export interface HostModelDefinition {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
api?: string;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface HostModelProvider {
|
|
43
|
+
baseUrl: string;
|
|
44
|
+
apiKey?: string | { source: string; provider: string; id: string };
|
|
45
|
+
api?: string;
|
|
46
|
+
headers?: Record<string, string>;
|
|
47
|
+
models: HostModelDefinition[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface HostModelsConfig {
|
|
51
|
+
providers?: Record<string, HostModelProvider>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ── Resolved provider info (internal) ───────────────────────────────
|
|
55
|
+
|
|
56
|
+
interface ResolvedProvider {
|
|
57
|
+
name: string;
|
|
58
|
+
baseUrl: string;
|
|
59
|
+
apiKey?: string;
|
|
60
|
+
api?: string;
|
|
61
|
+
headers?: Record<string, string>;
|
|
62
|
+
model: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Client ──────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
export class OpenClawAPIClient implements OpenClawAPI {
|
|
68
|
+
private completionProvider: ResolvedProvider | undefined;
|
|
69
|
+
private embeddingProvider: ResolvedProvider | undefined;
|
|
70
|
+
|
|
71
|
+
constructor(
|
|
72
|
+
private log: Logger,
|
|
73
|
+
hostModels?: HostModelsConfig,
|
|
74
|
+
) {
|
|
75
|
+
if (hostModels?.providers) {
|
|
76
|
+
this.completionProvider = pickCompletionProvider(hostModels.providers, log);
|
|
77
|
+
this.embeddingProvider = pickEmbeddingProvider(hostModels.providers, log);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── Embedding ─────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
async embed(request: OpenClawEmbedRequest): Promise<OpenClawEmbedResponse> {
|
|
84
|
+
const provider = this.embeddingProvider;
|
|
85
|
+
if (!provider) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
"No host embedding provider available. " +
|
|
88
|
+
"Configure a model provider with an embedding model in OpenClaw, " +
|
|
89
|
+
"or use an explicit embedding provider (openai, gemini, cohere, etc.)."
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const model = request.model ?? provider.model;
|
|
94
|
+
const endpoint = normalizeEndpoint(provider.baseUrl, "/embeddings");
|
|
95
|
+
|
|
96
|
+
this.log.debug(`OpenClawAPI.embed → ${provider.name} (${model}) [${request.texts.length} texts]`);
|
|
97
|
+
|
|
98
|
+
const resp = await fetch(endpoint, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: buildHeaders(provider),
|
|
101
|
+
body: JSON.stringify({ input: request.texts, model }),
|
|
102
|
+
signal: AbortSignal.timeout(30_000),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (!resp.ok) {
|
|
106
|
+
const body = await resp.text();
|
|
107
|
+
throw new Error(`Host embedding failed (${provider.name} ${resp.status}): ${body}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const json = (await resp.json()) as {
|
|
111
|
+
data: Array<{ embedding: number[] }>;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const embeddings = json.data.map((d) => d.embedding);
|
|
115
|
+
const dimensions = embeddings[0]?.length ?? 0;
|
|
116
|
+
|
|
117
|
+
this.log.debug(`OpenClawAPI.embed ← ${embeddings.length} vectors, dim=${dimensions}`);
|
|
118
|
+
return { embeddings, dimensions };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Completion ────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
async complete(request: OpenClawCompleteRequest): Promise<OpenClawCompleteResponse> {
|
|
124
|
+
const provider = this.completionProvider;
|
|
125
|
+
if (!provider) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
"No host completion provider available. " +
|
|
128
|
+
"Configure a model provider in OpenClaw, " +
|
|
129
|
+
"or use an explicit summarizer provider (openai, anthropic, gemini, etc.)."
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const model = request.model ?? provider.model;
|
|
134
|
+
const endpoint = normalizeEndpoint(provider.baseUrl, "/chat/completions");
|
|
135
|
+
|
|
136
|
+
this.log.debug(`OpenClawAPI.complete → ${provider.name} (${model})`);
|
|
137
|
+
|
|
138
|
+
const body: Record<string, unknown> = {
|
|
139
|
+
model,
|
|
140
|
+
messages: [{ role: "user", content: request.prompt }],
|
|
141
|
+
temperature: request.temperature ?? 0,
|
|
142
|
+
};
|
|
143
|
+
if (request.maxTokens) {
|
|
144
|
+
body.max_tokens = request.maxTokens;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const resp = await fetch(endpoint, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: buildHeaders(provider),
|
|
150
|
+
body: JSON.stringify(body),
|
|
151
|
+
signal: AbortSignal.timeout(60_000),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (!resp.ok) {
|
|
155
|
+
const respBody = await resp.text();
|
|
156
|
+
throw new Error(`Host completion failed (${provider.name} ${resp.status}): ${respBody}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const json = (await resp.json()) as {
|
|
160
|
+
choices: Array<{ message: { content: string } }>;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const text = json.choices?.[0]?.message?.content ?? "";
|
|
164
|
+
this.log.debug(`OpenClawAPI.complete ← ${text.length} chars`);
|
|
165
|
+
return { text };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Resolve a SecretInput (string | SecretRef) to a plain string.
|
|
173
|
+
* For env-sourced SecretRef, reads from process.env.
|
|
174
|
+
* Other sources are not supported — returns undefined.
|
|
175
|
+
*/
|
|
176
|
+
function resolveApiKey(
|
|
177
|
+
input: string | { source: string; provider: string; id: string } | undefined,
|
|
178
|
+
): string | undefined {
|
|
179
|
+
if (!input) return undefined;
|
|
180
|
+
if (typeof input === "string") return input;
|
|
181
|
+
// SecretRef — only env source is supported in plugin context
|
|
182
|
+
if (input.source === "env") return process.env[input.id];
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function buildHeaders(provider: ResolvedProvider): Record<string, string> {
|
|
187
|
+
const headers: Record<string, string> = {
|
|
188
|
+
"Content-Type": "application/json",
|
|
189
|
+
...provider.headers,
|
|
190
|
+
};
|
|
191
|
+
if (provider.apiKey) {
|
|
192
|
+
headers["Authorization"] = `Bearer ${provider.apiKey}`;
|
|
193
|
+
}
|
|
194
|
+
return headers;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Normalize base URL + path suffix.
|
|
199
|
+
* e.g. "https://api.openai.com/v1" + "/chat/completions"
|
|
200
|
+
* → "https://api.openai.com/v1/chat/completions"
|
|
201
|
+
*/
|
|
202
|
+
function normalizeEndpoint(baseUrl: string, suffix: string): string {
|
|
203
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
204
|
+
// If baseUrl already ends with the suffix, don't double-append
|
|
205
|
+
if (base.endsWith(suffix)) return base;
|
|
206
|
+
return `${base}${suffix}`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Pick the best provider for chat completions.
|
|
211
|
+
* Priority: openai-completions API > has apiKey > first with models.
|
|
212
|
+
*/
|
|
213
|
+
function pickCompletionProvider(
|
|
214
|
+
providers: Record<string, HostModelProvider>,
|
|
215
|
+
log: Logger,
|
|
216
|
+
): ResolvedProvider | undefined {
|
|
217
|
+
const entries = Object.entries(providers).filter(([, p]) => p.models?.length > 0);
|
|
218
|
+
if (entries.length === 0) return undefined;
|
|
219
|
+
|
|
220
|
+
// Sort: prefer openai-completions api, then providers with apiKey
|
|
221
|
+
entries.sort(([, a], [, b]) => {
|
|
222
|
+
const aScore = (a.api === "openai-completions" ? 2 : 0) + (resolveApiKey(a.apiKey) ? 1 : 0);
|
|
223
|
+
const bScore = (b.api === "openai-completions" ? 2 : 0) + (resolveApiKey(b.apiKey) ? 1 : 0);
|
|
224
|
+
return bScore - aScore;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const [name, provider] = entries[0];
|
|
228
|
+
const model = provider.models[0].id;
|
|
229
|
+
log.info(`Host completion provider: ${name} → ${model}`);
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
name,
|
|
233
|
+
baseUrl: provider.baseUrl,
|
|
234
|
+
apiKey: resolveApiKey(provider.apiKey),
|
|
235
|
+
api: provider.api,
|
|
236
|
+
headers: provider.headers,
|
|
237
|
+
model,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Pick the best provider for embeddings.
|
|
243
|
+
* Priority: model name contains "embed" > first provider with apiKey.
|
|
244
|
+
*/
|
|
245
|
+
function pickEmbeddingProvider(
|
|
246
|
+
providers: Record<string, HostModelProvider>,
|
|
247
|
+
log: Logger,
|
|
248
|
+
): ResolvedProvider | undefined {
|
|
249
|
+
const entries = Object.entries(providers).filter(([, p]) => p.models?.length > 0);
|
|
250
|
+
if (entries.length === 0) return undefined;
|
|
251
|
+
|
|
252
|
+
// Try to find a provider that has an embedding model
|
|
253
|
+
for (const [name, provider] of entries) {
|
|
254
|
+
const embedModel = provider.models.find((m) =>
|
|
255
|
+
m.id.toLowerCase().includes("embed") || m.name.toLowerCase().includes("embed"),
|
|
256
|
+
);
|
|
257
|
+
if (embedModel) {
|
|
258
|
+
log.info(`Host embedding provider: ${name} → ${embedModel.id}`);
|
|
259
|
+
return {
|
|
260
|
+
name,
|
|
261
|
+
baseUrl: provider.baseUrl,
|
|
262
|
+
apiKey: resolveApiKey(provider.apiKey),
|
|
263
|
+
api: provider.api,
|
|
264
|
+
headers: provider.headers,
|
|
265
|
+
model: embedModel.id,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Fallback: use first provider with apiKey, pick first model
|
|
271
|
+
const withKey = entries.find(([, p]) => !!resolveApiKey(p.apiKey));
|
|
272
|
+
if (withKey) {
|
|
273
|
+
const [name, provider] = withKey;
|
|
274
|
+
const model = provider.models[0].id;
|
|
275
|
+
log.info(`Host embedding provider (fallback): ${name} → ${model}`);
|
|
276
|
+
return {
|
|
277
|
+
name,
|
|
278
|
+
baseUrl: provider.baseUrl,
|
|
279
|
+
apiKey: resolveApiKey(provider.apiKey),
|
|
280
|
+
api: provider.api,
|
|
281
|
+
headers: provider.headers,
|
|
282
|
+
model,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
package/src/recall/engine.ts
CHANGED
|
@@ -246,7 +246,7 @@ export class RecallEngine {
|
|
|
246
246
|
if (candidateSkills.length === 0) return [];
|
|
247
247
|
|
|
248
248
|
// LLM relevance judgment
|
|
249
|
-
const summarizer = new Summarizer(this.ctx.config.summarizer, this.ctx.log);
|
|
249
|
+
const summarizer = new Summarizer(this.ctx.config.summarizer, this.ctx.log, this.ctx.openclawAPI);
|
|
250
250
|
const relevantIndices = await this.judgeSkillRelevance(summarizer, query, candidateSkills);
|
|
251
251
|
|
|
252
252
|
return relevantIndices.map((idx) => {
|
package/src/shared/llm-call.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import type { SummarizerConfig, SummaryProvider, Logger, PluginContext } from "../types";
|
|
3
|
+
import type { SummarizerConfig, SummaryProvider, Logger, PluginContext, OpenClawAPI } from "../types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Detect provider type from provider key name or base URL.
|
|
@@ -25,7 +25,6 @@ function defaultEndpointForProvider(provider: SummaryProvider, baseUrl: string):
|
|
|
25
25
|
if (stripped.endsWith("/v1/messages")) return stripped;
|
|
26
26
|
return `${stripped}/v1/messages`;
|
|
27
27
|
}
|
|
28
|
-
// OpenAI-compatible providers
|
|
29
28
|
if (stripped.endsWith("/chat/completions")) return stripped;
|
|
30
29
|
if (stripped.endsWith("/completions")) return stripped;
|
|
31
30
|
return `${stripped}/chat/completions`;
|
|
@@ -94,6 +93,8 @@ export interface LLMCallOptions {
|
|
|
94
93
|
maxTokens?: number;
|
|
95
94
|
temperature?: number;
|
|
96
95
|
timeoutMs?: number;
|
|
96
|
+
/** Pass ctx.openclawAPI so callLLMOnce can handle provider === "openclaw" */
|
|
97
|
+
openclawAPI?: OpenClawAPI;
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
function normalizeOpenAIEndpoint(url: string): string {
|
|
@@ -116,6 +117,7 @@ function isAnthropicProvider(cfg: SummarizerConfig): boolean {
|
|
|
116
117
|
|
|
117
118
|
/**
|
|
118
119
|
* Make a single LLM call with the given config. Throws on failure.
|
|
120
|
+
* When cfg.provider === "openclaw", delegates to the OpenClaw host completion API.
|
|
119
121
|
* Dispatches to Anthropic or OpenAI-compatible format based on provider.
|
|
120
122
|
*/
|
|
121
123
|
export async function callLLMOnce(
|
|
@@ -123,6 +125,20 @@ export async function callLLMOnce(
|
|
|
123
125
|
prompt: string,
|
|
124
126
|
opts: LLMCallOptions = {},
|
|
125
127
|
): Promise<string> {
|
|
128
|
+
if (cfg.provider === "openclaw") {
|
|
129
|
+
const api = opts.openclawAPI;
|
|
130
|
+
if (!api) {
|
|
131
|
+
throw new Error("OpenClaw API not available. Ensure sharing.capabilities.hostCompletion is enabled.");
|
|
132
|
+
}
|
|
133
|
+
const response = await api.complete({
|
|
134
|
+
prompt,
|
|
135
|
+
maxTokens: opts.maxTokens ?? 1024,
|
|
136
|
+
temperature: opts.temperature ?? 0.1,
|
|
137
|
+
model: cfg.model,
|
|
138
|
+
});
|
|
139
|
+
return response.text.trim();
|
|
140
|
+
}
|
|
141
|
+
|
|
126
142
|
if (isAnthropicProvider(cfg)) {
|
|
127
143
|
return callLLMOnceAnthropic(cfg, prompt, opts);
|
|
128
144
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ClientModeConfig as RootClientModeConfig,
|
|
3
|
+
HubModeConfig as RootHubModeConfig,
|
|
4
|
+
SharingCapabilities as RootSharingCapabilities,
|
|
5
|
+
SharingConfig as RootSharingConfig,
|
|
6
|
+
SharingRole as RootSharingRole,
|
|
7
|
+
} from "../types";
|
|
8
|
+
import type {
|
|
9
|
+
ClientModeConfig as SharingClientModeConfig,
|
|
10
|
+
GroupInfo,
|
|
11
|
+
HubModeConfig as SharingHubModeConfig,
|
|
12
|
+
HubSearchHit,
|
|
13
|
+
NetworkSearchResult,
|
|
14
|
+
SharingCapabilities as SharingSharingCapabilities,
|
|
15
|
+
SharingConfig as SharingSharingConfig,
|
|
16
|
+
SharingRole as SharingSharingRole,
|
|
17
|
+
SkillBundle,
|
|
18
|
+
UserInfo,
|
|
19
|
+
} from "./types";
|
|
20
|
+
|
|
21
|
+
type Assert<T extends true> = T;
|
|
22
|
+
type Equal<A, B> =
|
|
23
|
+
(<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2)
|
|
24
|
+
? ((<T>() => T extends B ? 1 : 2) extends (<T>() => T extends A ? 1 : 2) ? true : false)
|
|
25
|
+
: false;
|
|
26
|
+
type Extends<A, B> = A extends B ? true : false;
|
|
27
|
+
|
|
28
|
+
type _SharingRoleMatchesRoot = Assert<Equal<SharingSharingRole, RootSharingRole>>;
|
|
29
|
+
type _SharingCapabilitiesMatchRoot = Assert<Equal<SharingSharingCapabilities, RootSharingCapabilities>>;
|
|
30
|
+
type _HubModeConfigMatchesRoot = Assert<Equal<SharingHubModeConfig, RootHubModeConfig>>;
|
|
31
|
+
type _ClientModeConfigMatchesRoot = Assert<Equal<SharingClientModeConfig, RootClientModeConfig>>;
|
|
32
|
+
type _SharingConfigMatchesRoot = Assert<Equal<SharingSharingConfig, RootSharingConfig>>;
|
|
33
|
+
|
|
34
|
+
type _GroupInfoExists = Assert<Extends<GroupInfo, { id: string; name: string }>>;
|
|
35
|
+
type _UserInfoExists = Assert<Extends<UserInfo, { id: string; username: string }>>;
|
|
36
|
+
type _HubSearchHitExists = Assert<Extends<HubSearchHit, { remoteHitId: string; hubRank: number }>>;
|
|
37
|
+
type _NetworkSearchResultExists = Assert<Extends<NetworkSearchResult, { local: unknown; hub: unknown }>>;
|
|
38
|
+
type _SkillBundleExists = Assert<Extends<SkillBundle, { metadata: unknown; bundle: unknown }>>;
|
|
39
|
+
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ClientModeConfig,
|
|
3
|
+
HubModeConfig,
|
|
4
|
+
Role,
|
|
5
|
+
SearchResult,
|
|
6
|
+
SharingCapabilities,
|
|
7
|
+
SharingConfig,
|
|
8
|
+
SharingRole,
|
|
9
|
+
SkillGenerateOutput,
|
|
10
|
+
} from "../types";
|
|
11
|
+
|
|
12
|
+
export type HubScope = "local" | "group" | "all";
|
|
13
|
+
export type SharedVisibility = "group" | "public";
|
|
14
|
+
export type UserRole = "admin" | "member";
|
|
15
|
+
export type UserStatus = "pending" | "active" | "blocked" | "rejected";
|
|
16
|
+
|
|
17
|
+
export type { ClientModeConfig, HubModeConfig, SharingCapabilities, SharingConfig, SharingRole };
|
|
18
|
+
|
|
19
|
+
export interface GroupInfo {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface UserInfo {
|
|
26
|
+
id: string;
|
|
27
|
+
username: string;
|
|
28
|
+
deviceName?: string;
|
|
29
|
+
role: UserRole;
|
|
30
|
+
status: UserStatus;
|
|
31
|
+
groups: GroupInfo[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface HubSearchHit {
|
|
35
|
+
remoteHitId: string;
|
|
36
|
+
summary: string;
|
|
37
|
+
excerpt: string;
|
|
38
|
+
hubRank: number;
|
|
39
|
+
taskTitle: string | null;
|
|
40
|
+
ownerName: string;
|
|
41
|
+
groupName: string | null;
|
|
42
|
+
visibility: SharedVisibility;
|
|
43
|
+
source: {
|
|
44
|
+
ts: number;
|
|
45
|
+
role: Role;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface HubSearchMeta {
|
|
50
|
+
totalCandidates: number;
|
|
51
|
+
searchedGroups: string[];
|
|
52
|
+
includedPublic: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface HubSearchResult {
|
|
56
|
+
hits: HubSearchHit[];
|
|
57
|
+
meta: HubSearchMeta;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface HubMemoryDetail {
|
|
61
|
+
remoteHitId: string;
|
|
62
|
+
content: string;
|
|
63
|
+
summary: string;
|
|
64
|
+
source: {
|
|
65
|
+
ts: number;
|
|
66
|
+
role: Role;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface HubSkillHit {
|
|
71
|
+
skillId: string;
|
|
72
|
+
name: string;
|
|
73
|
+
description: string;
|
|
74
|
+
version: number;
|
|
75
|
+
visibility: SharedVisibility;
|
|
76
|
+
groupName: string | null;
|
|
77
|
+
ownerName: string;
|
|
78
|
+
qualityScore: number | null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface HubSkillSearchResult {
|
|
82
|
+
hits: HubSkillHit[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface NetworkSearchResult {
|
|
86
|
+
local: SearchResult;
|
|
87
|
+
hub: HubSearchResult;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface SkillBundleMetadata {
|
|
91
|
+
id: string;
|
|
92
|
+
name: string;
|
|
93
|
+
description: string;
|
|
94
|
+
version: number;
|
|
95
|
+
qualityScore: number | null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface SkillBundle {
|
|
99
|
+
metadata: SkillBundleMetadata;
|
|
100
|
+
bundle: SkillGenerateOutput;
|
|
101
|
+
}
|
|
102
|
+
|
package/src/skill/evaluator.ts
CHANGED
|
@@ -145,7 +145,7 @@ export class SkillEvaluator {
|
|
|
145
145
|
.replace("{SUMMARY}", task.summary.slice(0, 3000));
|
|
146
146
|
|
|
147
147
|
try {
|
|
148
|
-
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillEvaluator.create");
|
|
148
|
+
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillEvaluator.create", { openclawAPI: this.ctx.openclawAPI });
|
|
149
149
|
return this.parseJSON<CreateEvalResult>(raw, {
|
|
150
150
|
shouldGenerate: false, reason: "parse failed", suggestedName: "", suggestedTags: [], confidence: 0,
|
|
151
151
|
});
|
|
@@ -169,7 +169,7 @@ export class SkillEvaluator {
|
|
|
169
169
|
.replace("{SUMMARY}", task.summary.slice(0, 3000));
|
|
170
170
|
|
|
171
171
|
try {
|
|
172
|
-
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillEvaluator.upgrade");
|
|
172
|
+
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillEvaluator.upgrade", { openclawAPI: this.ctx.openclawAPI });
|
|
173
173
|
return this.parseJSON<UpgradeEvalResult>(raw, {
|
|
174
174
|
shouldUpgrade: false, upgradeType: "refine", dimensions: [], reason: "parse failed", mergeStrategy: "", confidence: 0,
|
|
175
175
|
});
|
|
@@ -179,6 +179,7 @@ export class SkillEvaluator {
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
|
|
182
183
|
private parseJSON<T>(raw: string, fallback: T): T {
|
|
183
184
|
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
184
185
|
if (!jsonMatch) return fallback;
|
package/src/skill/generator.ts
CHANGED
|
@@ -356,7 +356,7 @@ export class SkillGenerator {
|
|
|
356
356
|
.replace("{CONVERSATION}", conversationText.slice(0, 12000))
|
|
357
357
|
+ langInstruction;
|
|
358
358
|
|
|
359
|
-
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillGenerator.step1", { maxTokens: 6000, temperature: 0.2, timeoutMs: 120_000 });
|
|
359
|
+
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillGenerator.step1", { maxTokens: 6000, temperature: 0.2, timeoutMs: 120_000, openclawAPI: this.ctx.openclawAPI });
|
|
360
360
|
|
|
361
361
|
const trimmed = raw.trim();
|
|
362
362
|
if (trimmed.startsWith("---")) return trimmed;
|
|
@@ -379,7 +379,7 @@ export class SkillGenerator {
|
|
|
379
379
|
.replace("{CONVERSATION}", conversationText.slice(0, 6000));
|
|
380
380
|
|
|
381
381
|
try {
|
|
382
|
-
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillGenerator.scripts", { maxTokens: 3000, temperature: 0.1, timeoutMs: 120_000 });
|
|
382
|
+
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillGenerator.scripts", { maxTokens: 3000, temperature: 0.1, timeoutMs: 120_000, openclawAPI: this.ctx.openclawAPI });
|
|
383
383
|
return this.parseJSONArray<{ filename: string; content: string }>(raw);
|
|
384
384
|
} catch (err) {
|
|
385
385
|
this.ctx.log.warn(`SkillGenerator: script extraction failed: ${err}`);
|
|
@@ -401,7 +401,7 @@ export class SkillGenerator {
|
|
|
401
401
|
.replace("{CONVERSATION}", conversationText.slice(0, 6000));
|
|
402
402
|
|
|
403
403
|
try {
|
|
404
|
-
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillGenerator.refs", { maxTokens: 3000, temperature: 0.1, timeoutMs: 120_000 });
|
|
404
|
+
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillGenerator.refs", { maxTokens: 3000, temperature: 0.1, timeoutMs: 120_000, openclawAPI: this.ctx.openclawAPI });
|
|
405
405
|
return this.parseJSONArray<{ filename: string; content: string }>(raw);
|
|
406
406
|
} catch (err) {
|
|
407
407
|
this.ctx.log.warn(`SkillGenerator: reference extraction failed: ${err}`);
|
|
@@ -423,7 +423,7 @@ export class SkillGenerator {
|
|
|
423
423
|
+ `\n\n⚠️ LANGUAGE: Write test prompts and expectations in ${lang}, matching the skill's language.\n`;
|
|
424
424
|
|
|
425
425
|
try {
|
|
426
|
-
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillGenerator.evals", { maxTokens: 2000, temperature: 0.3, timeoutMs: 120_000 });
|
|
426
|
+
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillGenerator.evals", { maxTokens: 2000, temperature: 0.3, timeoutMs: 120_000, openclawAPI: this.ctx.openclawAPI });
|
|
427
427
|
return this.parseJSONArray(raw);
|
|
428
428
|
} catch (err) {
|
|
429
429
|
this.ctx.log.warn(`SkillGenerator: eval generation failed: ${err}`);
|
|
@@ -467,6 +467,7 @@ export class SkillGenerator {
|
|
|
467
467
|
return { hitCount, results };
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
+
|
|
470
471
|
// ─── Helpers ───
|
|
471
472
|
|
|
472
473
|
private parseJSONArray<T>(raw: string): T[] {
|
|
@@ -499,4 +500,5 @@ export class SkillGenerator {
|
|
|
499
500
|
return "";
|
|
500
501
|
}
|
|
501
502
|
|
|
503
|
+
|
|
502
504
|
}
|
package/src/skill/upgrader.ts
CHANGED
|
@@ -190,7 +190,7 @@ export class SkillUpgrader {
|
|
|
190
190
|
.replace("{TASK_ID}", task.id)
|
|
191
191
|
+ langInstruction;
|
|
192
192
|
|
|
193
|
-
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillUpgrader.upgrade", { maxTokens: 6000, temperature: 0.2, timeoutMs: 90_000 });
|
|
193
|
+
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillUpgrader.upgrade", { maxTokens: 6000, temperature: 0.2, timeoutMs: 90_000, openclawAPI: this.ctx.openclawAPI });
|
|
194
194
|
|
|
195
195
|
const changelogSep = raw.indexOf("---CHANGELOG---");
|
|
196
196
|
if (changelogSep !== -1) {
|
package/src/skill/validator.ts
CHANGED
|
@@ -143,7 +143,7 @@ export class SkillValidator {
|
|
|
143
143
|
const prompt = QUALITY_PROMPT.replace("{SKILL_CONTENT}", content.slice(0, 6000));
|
|
144
144
|
|
|
145
145
|
try {
|
|
146
|
-
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillValidator.quality");
|
|
146
|
+
const raw = await callLLMWithFallback(chain, prompt, this.ctx.log, "SkillValidator.quality", { openclawAPI: this.ctx.openclawAPI });
|
|
147
147
|
|
|
148
148
|
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
149
149
|
if (!jsonMatch) return;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, copyFileSync } from "fs";
|
|
2
2
|
import { execSync } from "child_process";
|
|
3
3
|
import path from "path";
|
|
4
|
+
import { createRequire } from "module";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Ensure the better-sqlite3 native binary is available.
|
|
@@ -10,7 +11,8 @@ import path from "path";
|
|
|
10
11
|
* and restores it from bundled prebuilds if missing.
|
|
11
12
|
*/
|
|
12
13
|
export function ensureSqliteBinding(log?: { info: (msg: string) => void; warn: (msg: string) => void }): void {
|
|
13
|
-
const
|
|
14
|
+
const _req = typeof require !== "undefined" ? require : createRequire(__filename);
|
|
15
|
+
const bsqlPkg = _req.resolve("better-sqlite3/package.json");
|
|
14
16
|
const bsqlDir = path.dirname(bsqlPkg);
|
|
15
17
|
const bindingPath = path.join(bsqlDir, "build", "Release", "better_sqlite3.node");
|
|
16
18
|
|