@openclawbrain/cli 0.4.3 → 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` is the published operator CLI package for OpenClawBrain.
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.3 install --openclaw-home ~/.openclaw
9
+ npx @openclawbrain/cli@0.4.4 install --openclaw-home ~/.openclaw
10
10
  openclaw gateway restart
11
- npx @openclawbrain/cli@0.4.3 status --openclaw-home ~/.openclaw --detailed
11
+ npx @openclawbrain/cli@0.4.4 status --openclaw-home ~/.openclaw --detailed
12
12
  ```
13
13
 
14
- Patch note for `0.4.3`: the CLI now reports active-pack numeric embeddings more truthfully across alternate vector shapes, and the repo-side serve-time decision matcher is more tolerant of event-id and timestamp drift when harvesting supervision candidates.
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
- Current caveat: some hosts still warn about a plugin id mismatch because the plugin manifest uses `openclawbrain` while the package/entry hint uses `openclaw`. The install still works; treat that warning as currently cosmetic.
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.3 install --openclaw-home ~/.openclaw
24
- npx @openclawbrain/cli@0.4.3 status --openclaw-home ~/.openclaw --detailed
25
- npx @openclawbrain/cli@0.4.3 rollback --activation-root /var/openclawbrain/activation --dry-run
26
- npx @openclawbrain/cli@0.4.3 daemon status --activation-root /var/openclawbrain/activation
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,7 +14,7 @@ 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";
@@ -3581,46 +3581,42 @@ function readOpenClawJsonConfig(openclawHome) {
3581
3581
  function ensureOpenClawBrainPluginConfig(openclawHome) {
3582
3582
  const { path: openclawJsonPath, config } = readOpenClawJsonConfig(openclawHome);
3583
3583
  const selectedInstall = findInstalledOpenClawBrainPlugin(openclawHome).selectedInstall;
3584
- const knownPluginIds = selectedInstall === null
3585
- ? ["openclawbrain", "openclaw"]
3586
- : getOpenClawBrainKnownPluginIds(selectedInstall);
3587
3584
  const plugins = readJsonObjectRecord(config.plugins);
3588
3585
  if (plugins === null) {
3589
3586
  return {
3590
3587
  path: openclawJsonPath,
3591
3588
  changed: false,
3592
- detail: `Left ${shortenPath(openclawJsonPath)} unchanged because plugins.allow is not configured`
3593
- };
3594
- }
3595
- if (!Object.prototype.hasOwnProperty.call(plugins, "allow")) {
3596
- return {
3597
- path: openclawJsonPath,
3598
- changed: false,
3599
- detail: `Left ${shortenPath(openclawJsonPath)} unchanged because plugins.allow is not configured`
3600
- };
3601
- }
3602
- if (!Array.isArray(plugins.allow)) {
3603
- return {
3604
- path: openclawJsonPath,
3605
- changed: false,
3606
- detail: `Left ${shortenPath(openclawJsonPath)} unchanged because plugins.allow is not an array`
3589
+ detail: `Left ${shortenPath(openclawJsonPath)} unchanged because plugins config is not configured`
3607
3590
  };
3608
3591
  }
3609
- const missingPluginIds = knownPluginIds.filter((pluginId) => !plugins.allow.includes(pluginId));
3610
- if (missingPluginIds.length === 0) {
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
+ }
3611
3608
  return {
3612
3609
  path: openclawJsonPath,
3613
3610
  changed: false,
3614
- detail: `Verified ${shortenPath(openclawJsonPath)} plugins.allow already includes ${knownPluginIds.join(", ")}`
3611
+ detail: `Verified ${shortenPath(openclawJsonPath)} ${details.join("; ")}`
3615
3612
  };
3616
3613
  }
3617
- plugins.allow = [...plugins.allow, ...missingPluginIds];
3618
- config.plugins = plugins;
3614
+ config.plugins = normalizedPlugins.pluginsConfig;
3619
3615
  writeFileSync(openclawJsonPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
3620
3616
  return {
3621
3617
  path: openclawJsonPath,
3622
3618
  changed: true,
3623
- detail: `Repaired ${shortenPath(openclawJsonPath)} plugins.allow by adding ${missingPluginIds.join(", ")}`
3619
+ detail: `Repaired ${shortenPath(openclawJsonPath)} by ${normalizedPlugins.changes.join("; ")}`
3624
3620
  };
3625
3621
  }
3626
3622
  function scrubOpenClawBrainPluginConfig(openclawHome) {
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
- import { describeOpenClawBrainInstallIdentity, describeOpenClawBrainInstallLayout, findInstalledOpenClawBrainPlugin, getOpenClawBrainKnownPluginIds } from "./openclaw-plugin-install.js";
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 knownPluginIds = getOpenClawBrainKnownPluginIds(installedPlugin.selectedInstall);
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 = knownPluginIds.find((pluginId) => plugins.allow.includes(pluginId)) ?? null;
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 ${knownPluginIds.join(", ")}`
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 ${knownPluginIds.join(", ")}`
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 getOpenClawBrainKnownPluginIds(install) {
195
+ export function getOpenClawBrainAllowedPluginIds(install) {
183
196
  const ids = [];
184
- const push = (value) => {
185
- const normalized = normalizeOptionalString(value);
186
- if (normalized === null || ids.includes(normalized)) {
187
- return;
188
- }
189
- ids.push(normalized);
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
- push(OPENCLAWBRAIN_NATIVE_INSTALL_ID);
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=${install.installId ?? path.basename(install.extensionDir)}`,
304
+ `install=${displayInstallId}`,
203
305
  `package=${install.packageName ?? "unknown"}`
204
306
  ].join(" ");
205
307
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclawbrain/cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "OpenClawBrain operator CLI package with install/status helpers, daemon controls, and import/export tooling.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",