@openclawbrain/cli 0.4.3 → 0.4.5
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 +9 -9
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.js +120 -64
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/learning-spine.d.ts +1 -1
- package/dist/src/learning-spine.js +1 -1
- package/dist/src/local-learner.d.ts +490 -0
- package/dist/src/local-learner.js +4979 -0
- package/dist/src/openclaw-hook-truth.js +5 -5
- package/dist/src/openclaw-plugin-install.d.ts +8 -0
- package/dist/src/openclaw-plugin-install.js +116 -14
- package/dist/src/traced-learning-bridge.js +48 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { describeOpenClawBrainInstallIdentity, describeOpenClawBrainInstallLayout, findInstalledOpenClawBrainPlugin,
|
|
3
|
+
import { describeOpenClawBrainInstallIdentity, describeOpenClawBrainInstallLayout, findInstalledOpenClawBrainPlugin, getOpenClawBrainAllowedPluginIds } from "./openclaw-plugin-install.js";
|
|
4
4
|
function toErrorMessage(error) {
|
|
5
5
|
return error instanceof Error ? error.message : String(error);
|
|
6
6
|
}
|
|
@@ -77,7 +77,7 @@ function describeAdditionalInstallDetail(additionalInstalls) {
|
|
|
77
77
|
export function inspectOpenClawBrainPluginAllowlist(openclawHome) {
|
|
78
78
|
const { path: openclawJsonPath, config } = readOpenClawJsonConfig(openclawHome);
|
|
79
79
|
const installedPlugin = findInstalledOpenClawBrainPlugin(openclawHome);
|
|
80
|
-
const
|
|
80
|
+
const allowedPluginIds = getOpenClawBrainAllowedPluginIds(installedPlugin.selectedInstall);
|
|
81
81
|
const plugins = readJsonObjectRecord(config.plugins);
|
|
82
82
|
if (plugins === null) {
|
|
83
83
|
return {
|
|
@@ -97,15 +97,15 @@ export function inspectOpenClawBrainPluginAllowlist(openclawHome) {
|
|
|
97
97
|
detail: `${shortenPath(openclawJsonPath)} has a non-array plugins.allow value, so OpenClawBrain load cannot be proven from config`
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
|
-
const matchedPluginId =
|
|
100
|
+
const matchedPluginId = allowedPluginIds.find((pluginId) => plugins.allow.includes(pluginId)) ?? null;
|
|
101
101
|
return matchedPluginId !== null
|
|
102
102
|
? {
|
|
103
103
|
state: "allowed",
|
|
104
|
-
detail: `${shortenPath(openclawJsonPath)} plugins.allow explicitly includes ${matchedPluginId}; recognized OpenClawBrain ids are ${
|
|
104
|
+
detail: `${shortenPath(openclawJsonPath)} plugins.allow explicitly includes ${matchedPluginId}; recognized OpenClawBrain ids are ${allowedPluginIds.join(", ")}`
|
|
105
105
|
}
|
|
106
106
|
: {
|
|
107
107
|
state: "blocked",
|
|
108
|
-
detail: `${shortenPath(openclawJsonPath)} plugins.allow excludes recognized OpenClawBrain ids ${
|
|
108
|
+
detail: `${shortenPath(openclawJsonPath)} plugins.allow excludes recognized OpenClawBrain ids ${allowedPluginIds.join(", ")}`
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
111
|
export function inspectOpenClawBrainHookStatus(openclawHome) {
|
|
@@ -32,7 +32,15 @@ export declare const OPENCLAWBRAIN_SHADOW_PACKAGE_NAME = "openclawbrain";
|
|
|
32
32
|
export declare const OPENCLAWBRAIN_NATIVE_PACKAGE_NAME = "@openclawbrain/openclaw";
|
|
33
33
|
export declare const OPENCLAWBRAIN_NATIVE_INSTALL_ID = "openclaw";
|
|
34
34
|
export declare function describeOpenClawBrainInstallLayout(installLayout: OpenClawBrainInstallLayout): string;
|
|
35
|
+
export declare function getOpenClawBrainAllowedPluginIds(install: OpenClawBrainInstalledPlugin | null | undefined): string[];
|
|
35
36
|
export declare function getOpenClawBrainKnownPluginIds(install: OpenClawBrainInstalledPlugin | null | undefined): string[];
|
|
37
|
+
export declare function normalizeOpenClawBrainPluginsConfig(pluginsConfig: Record<string, unknown>, install: OpenClawBrainInstalledPlugin | null | undefined): {
|
|
38
|
+
changed: boolean;
|
|
39
|
+
pluginsConfig: Record<string, unknown>;
|
|
40
|
+
changes: string[];
|
|
41
|
+
allowedPluginIds: string[];
|
|
42
|
+
canonicalEntryId: string;
|
|
43
|
+
};
|
|
36
44
|
export declare function describeOpenClawBrainInstallIdentity(install: OpenClawBrainInstalledPlugin): string;
|
|
37
45
|
export declare function findInstalledOpenClawBrainPlugin(openclawHome: string, pluginId?: string): OpenClawBrainInstalledPluginLookup;
|
|
38
46
|
export declare function resolveOpenClawBrainInstallTarget(openclawHome: string): OpenClawBrainInstallTarget;
|
|
@@ -46,6 +46,19 @@ function normalizeOptionalString(value) {
|
|
|
46
46
|
const trimmed = value.trim();
|
|
47
47
|
return trimmed.length > 0 ? trimmed : null;
|
|
48
48
|
}
|
|
49
|
+
function readJsonObjectRecord(value) {
|
|
50
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
function pushUniqueNormalizedId(ids, value) {
|
|
56
|
+
const normalized = normalizeOptionalString(value);
|
|
57
|
+
if (normalized === null || ids.includes(normalized)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
ids.push(normalized);
|
|
61
|
+
}
|
|
49
62
|
function normalizeInstallId(value) {
|
|
50
63
|
const normalized = normalizeOptionalString(value);
|
|
51
64
|
if (normalized === null) {
|
|
@@ -179,27 +192,116 @@ export function describeOpenClawBrainInstallLayout(installLayout) {
|
|
|
179
192
|
? "native package plugin"
|
|
180
193
|
: "generated shadow extension";
|
|
181
194
|
}
|
|
182
|
-
export function
|
|
195
|
+
export function getOpenClawBrainAllowedPluginIds(install) {
|
|
183
196
|
const ids = [];
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
push(OPENCLAWBRAIN_PLUGIN_ID);
|
|
192
|
-
push(install?.manifestId ?? null);
|
|
193
|
-
push(install?.installId ?? null);
|
|
197
|
+
pushUniqueNormalizedId(ids, OPENCLAWBRAIN_PLUGIN_ID);
|
|
198
|
+
pushUniqueNormalizedId(ids, install?.manifestId ?? null);
|
|
199
|
+
return ids;
|
|
200
|
+
}
|
|
201
|
+
function getOpenClawBrainLegacyInstallHintIds(install) {
|
|
202
|
+
const ids = [];
|
|
203
|
+
pushUniqueNormalizedId(ids, install?.installId ?? null);
|
|
194
204
|
if (install?.packageName === OPENCLAWBRAIN_NATIVE_PACKAGE_NAME) {
|
|
195
|
-
|
|
205
|
+
pushUniqueNormalizedId(ids, OPENCLAWBRAIN_NATIVE_INSTALL_ID);
|
|
196
206
|
}
|
|
197
|
-
return ids;
|
|
207
|
+
return ids.filter((id) => !getOpenClawBrainAllowedPluginIds(install).includes(id));
|
|
208
|
+
}
|
|
209
|
+
export function getOpenClawBrainKnownPluginIds(install) {
|
|
210
|
+
return [
|
|
211
|
+
...getOpenClawBrainAllowedPluginIds(install),
|
|
212
|
+
...getOpenClawBrainLegacyInstallHintIds(install)
|
|
213
|
+
];
|
|
214
|
+
}
|
|
215
|
+
function mergePluginEntryValues(preferredValue, fallbackValue) {
|
|
216
|
+
if (preferredValue === undefined) {
|
|
217
|
+
return fallbackValue;
|
|
218
|
+
}
|
|
219
|
+
if (fallbackValue === undefined) {
|
|
220
|
+
return preferredValue;
|
|
221
|
+
}
|
|
222
|
+
const preferredRecord = readJsonObjectRecord(preferredValue);
|
|
223
|
+
const fallbackRecord = readJsonObjectRecord(fallbackValue);
|
|
224
|
+
if (preferredRecord === null || fallbackRecord === null) {
|
|
225
|
+
return preferredValue;
|
|
226
|
+
}
|
|
227
|
+
const mergedValue = {
|
|
228
|
+
...fallbackRecord,
|
|
229
|
+
...preferredRecord
|
|
230
|
+
};
|
|
231
|
+
const preferredConfig = readJsonObjectRecord(preferredRecord.config);
|
|
232
|
+
const fallbackConfig = readJsonObjectRecord(fallbackRecord.config);
|
|
233
|
+
if (preferredConfig !== null || fallbackConfig !== null) {
|
|
234
|
+
mergedValue.config = {
|
|
235
|
+
...(fallbackConfig ?? {}),
|
|
236
|
+
...(preferredConfig ?? {})
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return mergedValue;
|
|
240
|
+
}
|
|
241
|
+
export function normalizeOpenClawBrainPluginsConfig(pluginsConfig, install) {
|
|
242
|
+
const nextPluginsConfig = {
|
|
243
|
+
...pluginsConfig
|
|
244
|
+
};
|
|
245
|
+
const changes = [];
|
|
246
|
+
let changed = false;
|
|
247
|
+
const allowedPluginIds = getOpenClawBrainAllowedPluginIds(install);
|
|
248
|
+
const legacyInstallHintIds = getOpenClawBrainLegacyInstallHintIds(install);
|
|
249
|
+
if (Array.isArray(nextPluginsConfig.allow)) {
|
|
250
|
+
const removedAllowEntries = nextPluginsConfig.allow.filter((entry) => legacyInstallHintIds.includes(entry));
|
|
251
|
+
const filteredAllow = nextPluginsConfig.allow.filter((entry) => !legacyInstallHintIds.includes(entry));
|
|
252
|
+
const addedAllowEntries = allowedPluginIds.filter((pluginId) => !filteredAllow.includes(pluginId));
|
|
253
|
+
if (removedAllowEntries.length > 0 || addedAllowEntries.length > 0) {
|
|
254
|
+
nextPluginsConfig.allow = [...filteredAllow, ...addedAllowEntries];
|
|
255
|
+
changed = true;
|
|
256
|
+
if (removedAllowEntries.length > 0) {
|
|
257
|
+
changes.push(`removed plugins.allow entries ${removedAllowEntries.join(", ")}`);
|
|
258
|
+
}
|
|
259
|
+
if (addedAllowEntries.length > 0) {
|
|
260
|
+
changes.push(`added plugins.allow entries ${addedAllowEntries.join(", ")}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const canonicalEntryId = allowedPluginIds[0] ?? OPENCLAWBRAIN_PLUGIN_ID;
|
|
265
|
+
const entries = readJsonObjectRecord(nextPluginsConfig.entries);
|
|
266
|
+
if (entries !== null) {
|
|
267
|
+
const legacyEntryIds = legacyInstallHintIds.filter((entryId) => Object.prototype.hasOwnProperty.call(entries, entryId));
|
|
268
|
+
if (legacyEntryIds.length > 0) {
|
|
269
|
+
const nextEntries = {
|
|
270
|
+
...entries
|
|
271
|
+
};
|
|
272
|
+
let mergedEntryValue = Object.prototype.hasOwnProperty.call(nextEntries, canonicalEntryId)
|
|
273
|
+
? nextEntries[canonicalEntryId]
|
|
274
|
+
: undefined;
|
|
275
|
+
for (const entryId of legacyEntryIds) {
|
|
276
|
+
mergedEntryValue = mergePluginEntryValues(mergedEntryValue, nextEntries[entryId]);
|
|
277
|
+
}
|
|
278
|
+
nextEntries[canonicalEntryId] = mergedEntryValue;
|
|
279
|
+
for (const entryId of legacyEntryIds) {
|
|
280
|
+
if (entryId === canonicalEntryId) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
delete nextEntries[entryId];
|
|
284
|
+
}
|
|
285
|
+
nextPluginsConfig.entries = nextEntries;
|
|
286
|
+
changed = true;
|
|
287
|
+
changes.push(`${Object.prototype.hasOwnProperty.call(entries, canonicalEntryId) ? "merged" : "moved"} plugins.entries.${legacyEntryIds.join(", plugins.entries.")} to plugins.entries.${canonicalEntryId}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
changed,
|
|
292
|
+
pluginsConfig: nextPluginsConfig,
|
|
293
|
+
changes,
|
|
294
|
+
allowedPluginIds,
|
|
295
|
+
canonicalEntryId
|
|
296
|
+
};
|
|
198
297
|
}
|
|
199
298
|
export function describeOpenClawBrainInstallIdentity(install) {
|
|
299
|
+
const displayInstallId = install.packageName === OPENCLAWBRAIN_NATIVE_PACKAGE_NAME
|
|
300
|
+
? OPENCLAWBRAIN_PLUGIN_ID
|
|
301
|
+
: install.installId ?? path.basename(install.extensionDir);
|
|
200
302
|
return [
|
|
201
303
|
`manifest=${install.manifestId ?? OPENCLAWBRAIN_PLUGIN_ID}`,
|
|
202
|
-
`install=${
|
|
304
|
+
`install=${displayInstallId}`,
|
|
203
305
|
`package=${install.packageName ?? "unknown"}`
|
|
204
306
|
].join(" ");
|
|
205
307
|
}
|
|
@@ -132,6 +132,48 @@ function loadTrainingStateJson(db, key) {
|
|
|
132
132
|
function writeTrainingStateJson(db, key, value) {
|
|
133
133
|
db.prepare(`INSERT OR REPLACE INTO brain_training_state (key, value) VALUES (?, ?)`).run(key, JSON.stringify(value));
|
|
134
134
|
}
|
|
135
|
+
function buildTracedLearningRoutingBuildFromServeTime(serveTimeLearning) {
|
|
136
|
+
return {
|
|
137
|
+
learnedRoutingPath: serveTimeLearning.pgVersion === "v2" ? "policy_gradient_v2" : "policy_gradient_v1",
|
|
138
|
+
pgVersionRequested: serveTimeLearning.pgVersion,
|
|
139
|
+
pgVersionUsed: serveTimeLearning.pgVersion,
|
|
140
|
+
decisionLogCount: normalizeCount(serveTimeLearning.decisionLogCount),
|
|
141
|
+
fallbackReason: serveTimeLearning.pgVersion === "v1"
|
|
142
|
+
? normalizeOptionalString(serveTimeLearning.fallbackReason) ?? "no_serve_time_decisions"
|
|
143
|
+
: null,
|
|
144
|
+
updatedBaseline: null
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
export function buildTracedLearningBridgePayloadFromRuntime(input) {
|
|
148
|
+
const lastMaterialization = input?.lastMaterialization ?? null;
|
|
149
|
+
const serveTimeLearning = input?.serveTimeLearning ?? {
|
|
150
|
+
pgVersion: "v1",
|
|
151
|
+
decisionLogCount: 0,
|
|
152
|
+
fallbackReason: null
|
|
153
|
+
};
|
|
154
|
+
const learnedRouter = lastMaterialization?.candidate?.summary?.learnedRouter ?? null;
|
|
155
|
+
const routingBuild = lastMaterialization?.candidate?.routingBuild ?? buildTracedLearningRoutingBuildFromServeTime(serveTimeLearning);
|
|
156
|
+
const fallbackReason = routingBuild.fallbackReason ??
|
|
157
|
+
(routingBuild.pgVersionUsed === "v1"
|
|
158
|
+
? normalizeOptionalString(serveTimeLearning.fallbackReason) ?? "no_serve_time_decisions"
|
|
159
|
+
: null);
|
|
160
|
+
return normalizeBridgePayload({
|
|
161
|
+
updatedAt: input?.updatedAt,
|
|
162
|
+
routeTraceCount: learnedRouter?.routeTraceCount ?? serveTimeLearning.decisionLogCount,
|
|
163
|
+
supervisionCount: learnedRouter?.supervisionCount ?? 0,
|
|
164
|
+
routerUpdateCount: learnedRouter?.updateCount ?? 0,
|
|
165
|
+
teacherArtifactCount: input?.teacherArtifactCount ?? 0,
|
|
166
|
+
pgVersionRequested: routingBuild.pgVersionRequested,
|
|
167
|
+
pgVersionUsed: routingBuild.pgVersionUsed,
|
|
168
|
+
decisionLogCount: routingBuild.decisionLogCount,
|
|
169
|
+
fallbackReason,
|
|
170
|
+
routerNoOpReason: learnedRouter?.noOpReason ?? null,
|
|
171
|
+
materializedPackId: input?.materializedPackId ?? lastMaterialization?.candidate?.summary?.packId ?? null,
|
|
172
|
+
promoted: input?.promoted === true,
|
|
173
|
+
baselinePersisted: input?.baselinePersisted === true,
|
|
174
|
+
source: input?.source
|
|
175
|
+
});
|
|
176
|
+
}
|
|
135
177
|
function countRows(db, tableName) {
|
|
136
178
|
const row = db.prepare(`SELECT COUNT(*) as count FROM ${tableName}`).get();
|
|
137
179
|
return normalizeCount(row?.count);
|
|
@@ -323,6 +365,12 @@ export function persistBrainStoreTracedLearningBridge(payload, options = {}) {
|
|
|
323
365
|
}
|
|
324
366
|
}
|
|
325
367
|
}
|
|
368
|
+
export function persistTracedLearningBridgeState(activationRoot, payload, options = {}) {
|
|
369
|
+
const bridge = mergeTracedLearningBridgePayload(payload, loadBrainStoreTracedLearningBridge(options));
|
|
370
|
+
persistBrainStoreTracedLearningBridge(bridge, options);
|
|
371
|
+
writeTracedLearningBridge(activationRoot, bridge);
|
|
372
|
+
return bridge;
|
|
373
|
+
}
|
|
326
374
|
export function loadBrainStoreTracedLearningBridge(options = {}) {
|
|
327
375
|
const brainRoot = resolveBrainRoot(options.env ?? process.env);
|
|
328
376
|
const dbPath = path.join(brainRoot, "state.db");
|
package/package.json
CHANGED