@openclawbrain/cli 0.4.2 → 0.4.4
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
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
# @openclawbrain/cli
|
|
2
2
|
|
|
3
|
-
`@openclawbrain/cli@0.4.
|
|
3
|
+
`@openclawbrain/cli@0.4.4` is the published operator CLI package for OpenClawBrain.
|
|
4
4
|
|
|
5
5
|
Primary public flow:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
openclaw plugins install @openclawbrain/openclaw@0.4.0
|
|
9
|
-
npx @openclawbrain/cli@0.4.
|
|
9
|
+
npx @openclawbrain/cli@0.4.4 install --openclaw-home ~/.openclaw
|
|
10
10
|
openclaw gateway restart
|
|
11
|
-
npx @openclawbrain/cli@0.4.
|
|
11
|
+
npx @openclawbrain/cli@0.4.4 status --openclaw-home ~/.openclaw --detailed
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Patch note for `0.4.
|
|
14
|
+
Patch note for `0.4.4`: the CLI now normalizes `plugins.allow` / `plugins.entries.openclawbrain` correctly on reinstall and reports the canonical `openclawbrain` install identity in status output.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Host release note: patched OpenClaw host builds no longer emit the old `openclaw` vs `openclawbrain` mismatch warning. Older host builds may still show that warning until the host-side alias fix is released there.
|
|
17
17
|
|
|
18
18
|
This package carries the `openclawbrain` CLI, daemon controls, import/export helpers, and install/status/operator management code. `@openclawbrain/openclaw` is the plugin/runtime payload.
|
|
19
19
|
|
|
20
20
|
## Commands
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
npx @openclawbrain/cli@0.4.
|
|
24
|
-
npx @openclawbrain/cli@0.4.
|
|
25
|
-
npx @openclawbrain/cli@0.4.
|
|
26
|
-
npx @openclawbrain/cli@0.4.
|
|
23
|
+
npx @openclawbrain/cli@0.4.4 install --openclaw-home ~/.openclaw
|
|
24
|
+
npx @openclawbrain/cli@0.4.4 status --openclaw-home ~/.openclaw --detailed
|
|
25
|
+
npx @openclawbrain/cli@0.4.4 rollback --activation-root /var/openclawbrain/activation --dry-run
|
|
26
|
+
npx @openclawbrain/cli@0.4.4 daemon status --activation-root /var/openclawbrain/activation
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
If the CLI is already on your `PATH`, `openclawbrain ...` is the same command surface. The docs lead with `npx` because that is the clean-host public-registry lane that already passed on `redogfood`.
|
package/dist/src/cli.js
CHANGED
|
@@ -14,11 +14,12 @@ import { inspectActivationState, loadPackFromActivation, promoteCandidatePack, r
|
|
|
14
14
|
import { resolveActivationRoot } from "./resolve-activation-root.js";
|
|
15
15
|
import { describeOpenClawHomeInspection, discoverOpenClawHomes, formatOpenClawHomeLayout, formatOpenClawHomeProfileSource, inspectOpenClawHome } from "./openclaw-home-layout.js";
|
|
16
16
|
import { inspectOpenClawBrainHookStatus, inspectOpenClawBrainPluginAllowlist } from "./openclaw-hook-truth.js";
|
|
17
|
-
import { describeOpenClawBrainInstallIdentity, describeOpenClawBrainInstallLayout, findInstalledOpenClawBrainPlugin, getOpenClawBrainKnownPluginIds, pinInstalledOpenClawBrainPluginActivationRoot, resolveOpenClawBrainInstallTarget } from "./openclaw-plugin-install.js";
|
|
17
|
+
import { describeOpenClawBrainInstallIdentity, describeOpenClawBrainInstallLayout, findInstalledOpenClawBrainPlugin, getOpenClawBrainKnownPluginIds, normalizeOpenClawBrainPluginsConfig, pinInstalledOpenClawBrainPluginActivationRoot, resolveOpenClawBrainInstallTarget } from "./openclaw-plugin-install.js";
|
|
18
18
|
import { loadAttachmentPolicyDeclaration, resolveEffectiveAttachmentPolicyTruth, writeAttachmentPolicyDeclaration } from "./attachment-policy-truth.js";
|
|
19
19
|
import { DEFAULT_WATCH_POLL_INTERVAL_SECONDS, buildNormalizedEventExportFromScannedEvents, bootstrapRuntimeAttach, buildOperatorSurfaceReport, clearOpenClawProfileRuntimeLoadProof, compileRuntimeContext, createAsyncTeacherLiveLoop, createOpenClawLocalSessionTail, createRuntimeEventExportScanner, describeCurrentProfileBrainStatus, formatOperatorRollbackReport, listOpenClawProfileRuntimeLoadProofs, loadRuntimeEventExportBundle, loadWatchTeacherSnapshotState, persistWatchTeacherSnapshot, rollbackRuntimeAttach, resolveAttachmentRuntimeLoadProofsPath, resolveOperatorTeacherSnapshotPath, resolveAsyncTeacherLiveLoopSnapshotPath, resolveWatchSessionTailCursorPath, resolveWatchStateRoot, resolveWatchTeacherSnapshotPath, scanLiveEventExport, scanRecordedSession, summarizeLearningPathFromMaterialization, summarizeNormalizedEventExportLabelFlow, writeScannedEventExportBundle } from "./index.js";
|
|
20
20
|
import { appendLearningUpdateLogs } from "./learning-spine.js";
|
|
21
21
|
import { buildPassiveLearningSessionExportFromOpenClawSessionStore } from "./local-session-passive-learning.js";
|
|
22
|
+
import { summarizePackVectorEmbeddingState } from "./embedding-status.js";
|
|
22
23
|
import { buildTracedLearningStatusSurface, loadBrainStoreTracedLearningBridge, mergeTracedLearningBridgePayload, persistBrainStoreTracedLearningBridge, writeTracedLearningBridge } from "./traced-learning-bridge.js";
|
|
23
24
|
import { discoverOpenClawSessionStores, loadOpenClawSessionIndex, readOpenClawSessionFile } from "./session-store.js";
|
|
24
25
|
import { readOpenClawBrainProviderDefaults, readOpenClawBrainProviderConfig, readOpenClawBrainProviderConfigFromSources, resolveOpenClawBrainProviderDefaultsPath } from "./provider-config.js";
|
|
@@ -953,11 +954,14 @@ function summarizeStatusEmbeddings(report, providerConfig) {
|
|
|
953
954
|
requireActivationReady: true
|
|
954
955
|
});
|
|
955
956
|
if (activePack !== null) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
957
|
+
const summary = summarizePackVectorEmbeddingState(activePack.vectors);
|
|
958
|
+
totalEntryCount = summary.vectorEntryCount;
|
|
959
|
+
embeddedEntryCount = summary.numericEmbeddingEntryCount;
|
|
960
|
+
models = summary.embeddingModels;
|
|
961
|
+
liveState = embeddedEntryCount === null ? "unknown" : embeddedEntryCount > 0 ? "yes" : "no";
|
|
962
|
+
liveDetail = embeddedEntryCount === null || totalEntryCount === null
|
|
963
|
+
? "active pack vector entries were unreadable during embedding inspection"
|
|
964
|
+
: `active pack stores ${embeddedEntryCount}/${totalEntryCount} numeric embeddings`;
|
|
961
965
|
}
|
|
962
966
|
}
|
|
963
967
|
catch (error) {
|
|
@@ -3577,46 +3581,42 @@ function readOpenClawJsonConfig(openclawHome) {
|
|
|
3577
3581
|
function ensureOpenClawBrainPluginConfig(openclawHome) {
|
|
3578
3582
|
const { path: openclawJsonPath, config } = readOpenClawJsonConfig(openclawHome);
|
|
3579
3583
|
const selectedInstall = findInstalledOpenClawBrainPlugin(openclawHome).selectedInstall;
|
|
3580
|
-
const knownPluginIds = selectedInstall === null
|
|
3581
|
-
? ["openclawbrain", "openclaw"]
|
|
3582
|
-
: getOpenClawBrainKnownPluginIds(selectedInstall);
|
|
3583
3584
|
const plugins = readJsonObjectRecord(config.plugins);
|
|
3584
3585
|
if (plugins === null) {
|
|
3585
3586
|
return {
|
|
3586
3587
|
path: openclawJsonPath,
|
|
3587
3588
|
changed: false,
|
|
3588
|
-
detail: `Left ${shortenPath(openclawJsonPath)} unchanged because plugins
|
|
3589
|
-
};
|
|
3590
|
-
}
|
|
3591
|
-
if (!Object.prototype.hasOwnProperty.call(plugins, "allow")) {
|
|
3592
|
-
return {
|
|
3593
|
-
path: openclawJsonPath,
|
|
3594
|
-
changed: false,
|
|
3595
|
-
detail: `Left ${shortenPath(openclawJsonPath)} unchanged because plugins.allow is not configured`
|
|
3596
|
-
};
|
|
3597
|
-
}
|
|
3598
|
-
if (!Array.isArray(plugins.allow)) {
|
|
3599
|
-
return {
|
|
3600
|
-
path: openclawJsonPath,
|
|
3601
|
-
changed: false,
|
|
3602
|
-
detail: `Left ${shortenPath(openclawJsonPath)} unchanged because plugins.allow is not an array`
|
|
3589
|
+
detail: `Left ${shortenPath(openclawJsonPath)} unchanged because plugins config is not configured`
|
|
3603
3590
|
};
|
|
3604
3591
|
}
|
|
3605
|
-
const
|
|
3606
|
-
if (
|
|
3592
|
+
const normalizedPlugins = normalizeOpenClawBrainPluginsConfig(plugins, selectedInstall);
|
|
3593
|
+
if (!normalizedPlugins.changed) {
|
|
3594
|
+
const details = [];
|
|
3595
|
+
if (!Object.prototype.hasOwnProperty.call(plugins, "allow")) {
|
|
3596
|
+
details.push("plugins.allow is not configured");
|
|
3597
|
+
}
|
|
3598
|
+
else if (!Array.isArray(plugins.allow)) {
|
|
3599
|
+
details.push("plugins.allow is not an array");
|
|
3600
|
+
}
|
|
3601
|
+
else {
|
|
3602
|
+
details.push(`plugins.allow already includes ${normalizedPlugins.allowedPluginIds.join(", ")}`);
|
|
3603
|
+
}
|
|
3604
|
+
const entries = readJsonObjectRecord(plugins.entries);
|
|
3605
|
+
if (entries !== null && Object.prototype.hasOwnProperty.call(entries, normalizedPlugins.canonicalEntryId)) {
|
|
3606
|
+
details.push(`plugins.entries already uses ${normalizedPlugins.canonicalEntryId}`);
|
|
3607
|
+
}
|
|
3607
3608
|
return {
|
|
3608
3609
|
path: openclawJsonPath,
|
|
3609
3610
|
changed: false,
|
|
3610
|
-
detail: `Verified ${shortenPath(openclawJsonPath)}
|
|
3611
|
+
detail: `Verified ${shortenPath(openclawJsonPath)} ${details.join("; ")}`
|
|
3611
3612
|
};
|
|
3612
3613
|
}
|
|
3613
|
-
plugins
|
|
3614
|
-
config.plugins = plugins;
|
|
3614
|
+
config.plugins = normalizedPlugins.pluginsConfig;
|
|
3615
3615
|
writeFileSync(openclawJsonPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
3616
3616
|
return {
|
|
3617
3617
|
path: openclawJsonPath,
|
|
3618
3618
|
changed: true,
|
|
3619
|
-
detail: `Repaired ${shortenPath(openclawJsonPath)}
|
|
3619
|
+
detail: `Repaired ${shortenPath(openclawJsonPath)} by ${normalizedPlugins.changes.join("; ")}`
|
|
3620
3620
|
};
|
|
3621
3621
|
}
|
|
3622
3622
|
function scrubOpenClawBrainPluginConfig(openclawHome) {
|
|
@@ -4544,19 +4544,7 @@ function exportLocalSessionTailChangesToScanRoot(input) {
|
|
|
4544
4544
|
};
|
|
4545
4545
|
}
|
|
4546
4546
|
function summarizeVectorEmbeddingState(vectors) {
|
|
4547
|
-
|
|
4548
|
-
return {
|
|
4549
|
-
vectorEntryCount: null,
|
|
4550
|
-
numericEmbeddingEntryCount: null,
|
|
4551
|
-
embeddingModels: []
|
|
4552
|
-
};
|
|
4553
|
-
}
|
|
4554
|
-
const embeddingModels = [...new Set(vectors.entries.flatMap((entry) => (entry.embedding === undefined ? [] : [entry.embedding.model])))].sort((left, right) => left.localeCompare(right));
|
|
4555
|
-
return {
|
|
4556
|
-
vectorEntryCount: vectors.entries.length,
|
|
4557
|
-
numericEmbeddingEntryCount: vectors.entries.filter((entry) => entry.embedding !== undefined).length,
|
|
4558
|
-
embeddingModels
|
|
4559
|
-
};
|
|
4547
|
+
return summarizePackVectorEmbeddingState(vectors);
|
|
4560
4548
|
}
|
|
4561
4549
|
function buildWatchEmbedTracePoint(input) {
|
|
4562
4550
|
const summary = summarizeVectorEmbeddingState(input.vectors);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function isNumericArray(value) {
|
|
2
|
+
return Array.isArray(value) &&
|
|
3
|
+
value.length > 0 &&
|
|
4
|
+
value.every((entry) => typeof entry === "number" && Number.isFinite(entry));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function isNumericTypedArray(value) {
|
|
8
|
+
return ArrayBuffer.isView(value) &&
|
|
9
|
+
!(value instanceof DataView) &&
|
|
10
|
+
typeof value.length === "number" &&
|
|
11
|
+
value.length > 0 &&
|
|
12
|
+
Array.from(value).every((entry) => typeof entry === "number" && Number.isFinite(entry));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function hasNumericValues(value) {
|
|
16
|
+
return isNumericArray(value) || isNumericTypedArray(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function normalizeOptionalString(value) {
|
|
20
|
+
if (typeof value !== "string") {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const trimmed = value.trim();
|
|
24
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function extractNumericEmbeddingShape(entry) {
|
|
28
|
+
const embedding = entry?.embedding;
|
|
29
|
+
const candidates = [
|
|
30
|
+
embedding,
|
|
31
|
+
embedding?.values,
|
|
32
|
+
embedding?.vector,
|
|
33
|
+
embedding?.embedding,
|
|
34
|
+
entry?.values,
|
|
35
|
+
entry?.vector,
|
|
36
|
+
entry?.numericEmbedding,
|
|
37
|
+
entry?.numericEmbeddingValues,
|
|
38
|
+
];
|
|
39
|
+
return candidates.find((candidate) => hasNumericValues(candidate)) ?? null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function extractEmbeddingModel(entry) {
|
|
43
|
+
const candidates = [
|
|
44
|
+
entry?.embedding?.model,
|
|
45
|
+
entry?.embeddingModel,
|
|
46
|
+
entry?.model,
|
|
47
|
+
];
|
|
48
|
+
for (const candidate of candidates) {
|
|
49
|
+
const normalized = normalizeOptionalString(candidate);
|
|
50
|
+
if (normalized !== null) {
|
|
51
|
+
return normalized;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function summarizePackVectorEmbeddingState(vectors) {
|
|
58
|
+
if (!vectors || !Array.isArray(vectors.entries)) {
|
|
59
|
+
return {
|
|
60
|
+
vectorEntryCount: null,
|
|
61
|
+
numericEmbeddingEntryCount: null,
|
|
62
|
+
embeddingModels: []
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const embeddingModels = [...new Set(vectors.entries
|
|
66
|
+
.flatMap((entry) => {
|
|
67
|
+
if (extractNumericEmbeddingShape(entry) === null) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
const model = extractEmbeddingModel(entry);
|
|
71
|
+
return model === null ? [] : [model];
|
|
72
|
+
}))].sort((left, right) => left.localeCompare(right));
|
|
73
|
+
return {
|
|
74
|
+
vectorEntryCount: vectors.entries.length,
|
|
75
|
+
numericEmbeddingEntryCount: vectors.entries.filter((entry) => extractNumericEmbeddingShape(entry) !== null).length,
|
|
76
|
+
embeddingModels
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -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
|
}
|
package/package.json
CHANGED