@openclawbrain/openclaw 0.3.4 → 0.3.6
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 +57 -4
- package/dist/extension/runtime-guard.js +6 -3
- package/dist/src/attachment-truth.js +7 -2
- package/dist/src/cli.js +143 -83
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.js +30 -6
- package/dist/src/openclaw-hook-truth.d.ts +8 -0
- package/dist/src/openclaw-hook-truth.js +134 -28
- package/dist/src/openclaw-plugin-install.d.ts +40 -0
- package/dist/src/openclaw-plugin-install.js +282 -0
- package/dist/src/resolve-activation-root.js +14 -9
- package/dist/src/shadow-extension-proof.d.ts +3 -0
- package/dist/src/shadow-extension-proof.js +23 -23
- package/extension/runtime-guard.ts +12 -9
- package/openclaw.plugin.json +11 -0
- package/package.json +16 -7
package/README.md
CHANGED
|
@@ -7,9 +7,62 @@ This is the default front door for first-time OpenClawBrain users.
|
|
|
7
7
|
- Start here when you want one package to import from instead of guessing across the `@openclawbrain/*` split.
|
|
8
8
|
- The stable front-door names are `@openclawbrain/openclaw` and `openclawbrain`.
|
|
9
9
|
- If you are validating this repo-tip checkout, prepare the checkout with `pnpm install --frozen-lockfile`, run `pnpm release:status`, then `pnpm release:pack`, then install the local `.release/*.tgz` tarballs.
|
|
10
|
-
-
|
|
10
|
+
- For the current published wave, install it with `npm install -g @openclawbrain/openclaw@0.3.5`.
|
|
11
11
|
- Use the `openclawbrain` CLI for status, rollback, and narrow scanner scans; `openclawbrain-ops` stays available as a compatibility alias.
|
|
12
12
|
- `npm exec openclawbrain -- --help` works after either install path, but the exact quickstart commands remain the clearest first attach path.
|
|
13
|
+
- OpenClaw can also install this same package as a native plugin package; keep the `openclawbrain` CLI available and run `openclawbrain install --openclaw-home <path>` afterward to pin the activation root for that installed copy.
|
|
14
|
+
|
|
15
|
+
The compatibility package `@jonathangu/openclawbrain@0.3.5` remains published for older plugin/wrapper installs, but it is not the main operator path.
|
|
16
|
+
|
|
17
|
+
Decision and migration note: [`docs/lifecycle.md`](../../docs/lifecycle.md)
|
|
18
|
+
|
|
19
|
+
## Operator quickstart
|
|
20
|
+
|
|
21
|
+
Install or upgrade through the same lane:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g @openclawbrain/openclaw@0.3.5
|
|
25
|
+
openclawbrain install --openclaw-home ~/.openclaw
|
|
26
|
+
openclaw gateway restart
|
|
27
|
+
openclawbrain status --openclaw-home ~/.openclaw --detailed
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
If you want OpenClaw to own the plugin package directory itself, the native package lane is:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
openclaw plugins install @openclawbrain/openclaw@0.3.5
|
|
34
|
+
openclawbrain install --openclaw-home ~/.openclaw
|
|
35
|
+
openclaw gateway restart
|
|
36
|
+
openclawbrain status --openclaw-home ~/.openclaw --detailed
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The CLI still pins the activation root for either layout. `status --openclaw-home` now tells the truth about whether the loaded hook is the generated shadow extension or the native package plugin.
|
|
40
|
+
|
|
41
|
+
Verify the installed target:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
openclawbrain status --openclaw-home ~/.openclaw --detailed
|
|
45
|
+
openclawbrain status --openclaw-home ~/.openclaw --json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Remove only the OpenClaw profile hook and keep brain data:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
openclawbrain detach --openclaw-home ~/.openclaw
|
|
52
|
+
openclaw gateway restart
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Remove the hook and purge the OpenClawBrain data for that install:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
openclawbrain uninstall --openclaw-home ~/.openclaw --purge-data
|
|
59
|
+
openclaw gateway restart
|
|
60
|
+
npm uninstall -g @openclawbrain/openclaw
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If you want explicit uninstall semantics without purging, use `openclawbrain uninstall --openclaw-home ~/.openclaw --keep-data`. `detach` remains the simpler keep-data path.
|
|
64
|
+
|
|
65
|
+
`detach` and `uninstall` remove whichever OpenClawBrain hook layout is installed for that OpenClaw home. The data outcome is still explicit: `detach` keeps data, `uninstall --keep-data` keeps it explicitly, and `uninstall --purge-data` deletes it.
|
|
13
66
|
|
|
14
67
|
Use this package when OpenClaw needs the narrow supported operator bridge captured by `OPERATOR_API_CONTRACT_V1`:
|
|
15
68
|
|
|
@@ -22,7 +75,7 @@ Use this package when OpenClaw needs the narrow supported operator bridge captur
|
|
|
22
75
|
|
|
23
76
|
The supported operator contract is intentionally decomposed into bootstrap/attach, status, export, refresh, promote, rollback, and proof/observability. The post-attach loop stays off the hot path: it exports turns, builds candidate packs, and promotes them later rather than mutating the current active pack in place.
|
|
24
77
|
|
|
25
|
-
|
|
78
|
+
Use `docs/lifecycle.md` for the copy-paste install/upgrade/verify/remove story. The rest of this README is the family-by-family contract map and the explicitly quarantined surfaces.
|
|
26
79
|
|
|
27
80
|
## Operator boundary
|
|
28
81
|
|
|
@@ -101,12 +154,12 @@ This package stays fail-open for non-learned-required compile misses, and event-
|
|
|
101
154
|
|
|
102
155
|
Keep `install` as the front-door lifecycle command:
|
|
103
156
|
|
|
104
|
-
- `openclawbrain install
|
|
157
|
+
- `openclawbrain install --openclaw-home <path>` is the default attach path. On many-profile hosts, pass `--openclaw-home <path>` or set `OPENCLAW_HOME` so the target profile is explicit instead of guessed.
|
|
105
158
|
- `openclawbrain install` now writes only the real profile hook and activation state. It no longer creates `BRAIN.md` or rewrites `AGENTS.md`.
|
|
106
159
|
- `openclawbrain detach --openclaw-home <path>` removes only the OpenClaw profile hook. It preserves OpenClawBrain activation data and now says so plainly in both human output and JSON.
|
|
107
160
|
- `openclawbrain uninstall --openclaw-home <path> --keep-data|--purge-data` removes the profile hook and forces the operator to pick the data outcome explicitly.
|
|
108
161
|
- `--restart <never|safe|external>` is guidance only for detach/uninstall. The default `safe` guidance stays conservative: restart the running OpenClaw profile before expecting hook-state changes to take effect; otherwise the next launch picks them up.
|
|
109
|
-
- plain `status` stays the human answer to “How’s the brain?” and now keeps compact lifecycle truth visible without dumping teacher/no-op or other proof chatter by default; use `--detailed` when you want the dense operator report.
|
|
162
|
+
- plain `status` stays the human answer to “How’s the brain?” and now keeps compact lifecycle truth visible without dumping teacher/no-op or other proof chatter by default; use `--openclaw-home <path> --detailed` when you want the dense operator report for one installed target.
|
|
110
163
|
|
|
111
164
|
## Narrow operator contract
|
|
112
165
|
|
|
@@ -35,13 +35,16 @@ export function normalizePromptBuildEvent(event) {
|
|
|
35
35
|
const warnings = [];
|
|
36
36
|
const sessionId = normalizeOptionalScalarField(event.sessionId, "sessionId", warnings);
|
|
37
37
|
const channel = normalizeOptionalScalarField(event.channel, "channel", warnings);
|
|
38
|
-
|
|
38
|
+
const promptFallback = extractTextContent(event.prompt);
|
|
39
|
+
let extractedMessage = promptFallback ?? "";
|
|
39
40
|
if (messages.length === 0) {
|
|
40
|
-
|
|
41
|
+
if (extractedMessage.length === 0) {
|
|
42
|
+
warnings.push(failOpenDiagnostic("runtime-messages-empty", "before_prompt_build event.messages is empty", `event=${describeValue(event)}`));
|
|
43
|
+
}
|
|
41
44
|
}
|
|
42
45
|
else {
|
|
43
46
|
const lastMessage = messages.at(-1);
|
|
44
|
-
extractedMessage = extractPromptMessage(lastMessage) ?? "";
|
|
47
|
+
extractedMessage = extractPromptMessage(lastMessage) ?? promptFallback ?? "";
|
|
45
48
|
if (extractedMessage.length === 0) {
|
|
46
49
|
warnings.push(failOpenDiagnostic("runtime-last-message-invalid", "before_prompt_build last message has no usable text content", `lastMessage=${describeValue(lastMessage)}`));
|
|
47
50
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { inspectOpenClawHome } from "./openclaw-home-layout.js";
|
|
4
|
+
import { resolveOpenClawHomeFromExtensionEntryPath } from "./openclaw-plugin-install.js";
|
|
4
5
|
const ATTACHMENT_RUNTIME_LOAD_PROOFS_CONTRACT = "openclaw_profile_runtime_load_proofs.v1";
|
|
5
6
|
const ATTACHMENT_TRUTH_DIRNAME = "attachment-truth";
|
|
6
7
|
const ATTACHMENT_RUNTIME_LOAD_PROOFS_BASENAME = "runtime-load-proofs.json";
|
|
@@ -128,7 +129,11 @@ function writeRuntimeLoadProofs(proofPath, proofs) {
|
|
|
128
129
|
writeFileSync(proofPath, `${JSON.stringify(proofs, null, 2)}\n`, "utf8");
|
|
129
130
|
}
|
|
130
131
|
function deriveOpenClawHomeFromExtensionEntryPath(extensionEntryPath) {
|
|
131
|
-
|
|
132
|
+
const openclawHome = resolveOpenClawHomeFromExtensionEntryPath(extensionEntryPath);
|
|
133
|
+
if (openclawHome === null) {
|
|
134
|
+
throw new Error(`extension entry path ${extensionEntryPath} is not nested under an OpenClaw extensions dir`);
|
|
135
|
+
}
|
|
136
|
+
return canonicalizeFilesystemPath(openclawHome);
|
|
132
137
|
}
|
|
133
138
|
export function resolveAttachmentRuntimeLoadProofsPath(activationRoot) {
|
|
134
139
|
return path.join(path.resolve(activationRoot), ATTACHMENT_TRUTH_DIRNAME, ATTACHMENT_RUNTIME_LOAD_PROOFS_BASENAME);
|
|
@@ -212,4 +217,4 @@ export function clearOpenClawProfileRuntimeLoadProof(input) {
|
|
|
212
217
|
});
|
|
213
218
|
return true;
|
|
214
219
|
}
|
|
215
|
-
//# sourceMappingURL=attachment-truth.js.map
|
|
220
|
+
//# sourceMappingURL=attachment-truth.js.map
|
package/dist/src/cli.js
CHANGED
|
@@ -14,6 +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
18
|
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";
|
|
18
19
|
import { appendLearningUpdateLogs } from "./learning-spine.js";
|
|
19
20
|
import { buildPassiveLearningSessionExportFromOpenClawSessionStore } from "./local-session-passive-learning.js";
|
|
@@ -64,7 +65,9 @@ function discoverInstallCandidateOpenClawHomes(homeDir = getCliHomeDir()) {
|
|
|
64
65
|
function summarizeSharedActivationRootReferenceProof(reference) {
|
|
65
66
|
switch (reference.installState) {
|
|
66
67
|
case "installed":
|
|
67
|
-
return
|
|
68
|
+
return reference.serveAttachmentState === "attached"
|
|
69
|
+
? "hook installed and loadable"
|
|
70
|
+
: "hook files exist, but the current install is not loadable yet";
|
|
68
71
|
case "blocked_by_allowlist":
|
|
69
72
|
return "hook files exist but OpenClaw will not load them because plugins.allow blocks openclawbrain";
|
|
70
73
|
case "not_installed":
|
|
@@ -122,7 +125,9 @@ function findInstalledHookReferencesForActivationRoot(input) {
|
|
|
122
125
|
installState: installHook.state === "installed" || installHook.state === "blocked_by_allowlist"
|
|
123
126
|
? installHook.state
|
|
124
127
|
: "not_installed",
|
|
125
|
-
serveAttachmentState: installHook.state === "installed"
|
|
128
|
+
serveAttachmentState: installHook.state === "installed" && installHook.loadability === "loadable"
|
|
129
|
+
? "attached"
|
|
130
|
+
: "half_attached",
|
|
126
131
|
hookDetail: installHook.detail
|
|
127
132
|
};
|
|
128
133
|
return path.resolve(installedActivationRoot) === resolvedActivationRoot
|
|
@@ -503,7 +508,7 @@ function operatorCliHelp() {
|
|
|
503
508
|
" --help Show this help.",
|
|
504
509
|
"",
|
|
505
510
|
"Lifecycle flow:",
|
|
506
|
-
" 1. install openclawbrain install — safe first-time default;
|
|
511
|
+
" 1. install openclawbrain install — safe first-time default; writes the generated shadow hook or pins an already-installed native package plugin for one OpenClaw home",
|
|
507
512
|
" 2. attach openclawbrain attach --openclaw-home <path> [--activation-root <path>] — explicit reattach/manual hook path for known brain data; use install first",
|
|
508
513
|
" 3. status openclawbrain status --activation-root <path> — answer \"How's the brain?\" for that boundary",
|
|
509
514
|
" 4. status --detailed openclawbrain status --activation-root <path> --detailed — explain serve path, freshness, backlog, and failure mode",
|
|
@@ -519,6 +524,7 @@ function operatorCliHelp() {
|
|
|
519
524
|
" scan inspect one recorded session or live event export without claiming a daemon is running",
|
|
520
525
|
" learn one-shot local-session learning pass against the resolved activation root",
|
|
521
526
|
" status --teacher-snapshot keeps the current live-first / principal-priority / passive-backfill learner order visible when that snapshot exists",
|
|
527
|
+
" native package installs still need the openclawbrain CLI available because install/attach pin the activation root for that package copy",
|
|
522
528
|
" watch/daemon persist their operator snapshot at <activation-root>/watch/teacher-snapshot.json; --teacher-snapshot overrides the default path",
|
|
523
529
|
" watch teacher defaults come from install-written provider-defaults.json under the activation root; OPENCLAWBRAIN_TEACHER_* and OPENCLAWBRAIN_EMBEDDER_* are host-shell overrides only, not live gateway wiring",
|
|
524
530
|
"",
|
|
@@ -678,12 +684,16 @@ function summarizeStatusInstallHook(openclawHome) {
|
|
|
678
684
|
const hook = inspectOpenClawBrainHookStatus(openclawHome);
|
|
679
685
|
return {
|
|
680
686
|
state: hook.installState === "unverified" ? "unknown" : hook.installState,
|
|
687
|
+
loadability: hook.loadability,
|
|
688
|
+
installLayout: hook.installLayout,
|
|
689
|
+
hookPath: hook.hookPath,
|
|
681
690
|
detail: hook.detail
|
|
682
691
|
};
|
|
683
692
|
}
|
|
684
693
|
function summarizeStatusHookLoad(installHook, status) {
|
|
685
694
|
return {
|
|
686
695
|
installState: installHook.state === "unknown" ? "unverified" : installHook.state,
|
|
696
|
+
loadability: installHook.loadability,
|
|
687
697
|
loadProof: status.hook.loadProof,
|
|
688
698
|
detail: status.hook.detail
|
|
689
699
|
};
|
|
@@ -1243,7 +1253,9 @@ function formatAttachedProfileTruthDetailedList(entries) {
|
|
|
1243
1253
|
.join(" ");
|
|
1244
1254
|
}
|
|
1245
1255
|
function summarizeDisplayedStatus(status, installHook) {
|
|
1246
|
-
return installHook.state === "blocked_by_allowlist"
|
|
1256
|
+
return installHook.state === "blocked_by_allowlist" || status.hook.loadability === "blocked"
|
|
1257
|
+
? "fail"
|
|
1258
|
+
: status.brainStatus.status;
|
|
1247
1259
|
}
|
|
1248
1260
|
function buildCompactStatusHeader(status, report, options) {
|
|
1249
1261
|
const installHook = summarizeStatusInstallHook(options.openclawHome);
|
|
@@ -1262,7 +1274,7 @@ function buildCompactStatusHeader(status, report, options) {
|
|
|
1262
1274
|
});
|
|
1263
1275
|
return [
|
|
1264
1276
|
`lifecycle attach=${status.attachment.state} learner=${yesNo(status.passiveLearning.learnerRunning)} watch=${summarizeStatusWatchState(status)} export=${status.passiveLearning.exportState} promote=${summarizeStatusPromotionState(status)} serve=${summarizeStatusServeReality(status)}`,
|
|
1265
|
-
`hook install=${hookLoad.installState} loadProof=${hookLoad.loadProof} detail=${hookLoad.detail}`,
|
|
1277
|
+
`hook install=${hookLoad.installState} loadability=${hookLoad.loadability} loadProof=${hookLoad.loadProof} layout=${status.hook.installLayout ?? "unverified"} additional=${status.hook.additionalInstallCount ?? 0} detail=${hookLoad.detail}`,
|
|
1266
1278
|
`attachTruth current=${attachmentTruth.currentProfileLabel} hook=${attachmentTruth.hookFiles} config=${attachmentTruth.configLoad} runtime=${attachmentTruth.runtimeLoad} watcher=${attachmentTruth.watcher} attachedSet=${formatAttachedProfileTruthCompactList(attachmentTruth.attachedProfiles)} why=${attachmentTruth.detail}`,
|
|
1267
1279
|
`passive firstExport=${yesNo(status.passiveLearning.firstExportOccurred)} backlog=${status.passiveLearning.backlogState} pending=${formatStatusNullableNumber(status.passiveLearning.pendingLive)}/${formatStatusNullableNumber(status.passiveLearning.pendingBackfill)}`,
|
|
1268
1280
|
`serving pack=${status.passiveLearning.currentServingPackId ?? "none"} lastExport=${status.passiveLearning.lastExportAt ?? "none"} lastPromotion=${status.passiveLearning.lastPromotionAt ?? "none"}`,
|
|
@@ -1293,7 +1305,7 @@ function formatCurrentProfileStatusSummary(status, report, targetInspection, opt
|
|
|
1293
1305
|
const profileIdSuffix = status.profile.profileId === null ? "" : ` id=${status.profile.profileId}`;
|
|
1294
1306
|
const targetLine = targetInspection === null
|
|
1295
1307
|
? `target activation=${status.host.activationRoot} source=activation_root_only`
|
|
1296
|
-
: `target activation=${status.host.activationRoot} ${formatOpenClawTargetLine(targetInspection)} hook=${
|
|
1308
|
+
: `target activation=${status.host.activationRoot} ${formatOpenClawTargetLine(targetInspection)} hook=${status.hook.hookPath === null ? "unverified" : shortenPath(status.hook.hookPath)}`;
|
|
1297
1309
|
return [
|
|
1298
1310
|
`STATUS ${displayedStatus}`,
|
|
1299
1311
|
...buildCompactStatusHeader(status, report, options),
|
|
@@ -1555,6 +1567,7 @@ function shouldWriteProfileHookProviderDefaults(parsed, activationPlan, isInstal
|
|
|
1555
1567
|
}
|
|
1556
1568
|
function buildInstallBrainFeedbackSummary(input) {
|
|
1557
1569
|
const providerDefaultsPath = resolveOpenClawBrainProviderDefaultsPath(input.parsed.activationRoot);
|
|
1570
|
+
const hookLayout = describeOpenClawBrainInstallLayout(input.hookLayout);
|
|
1558
1571
|
const embedderState = input.embedderProvision === null ? "unchanged" : input.embedderProvision.state;
|
|
1559
1572
|
const teacherDefaults = input.providerDefaults?.defaults.teacher;
|
|
1560
1573
|
const teacherProvider = teacherDefaults?.provider ?? "unknown";
|
|
@@ -1598,15 +1611,16 @@ function buildInstallBrainFeedbackSummary(input) {
|
|
|
1598
1611
|
gatewayStatusCommand: buildGatewayStatusCommand(profileName)
|
|
1599
1612
|
};
|
|
1600
1613
|
const provedNow = input.activationPlan.action === "bootstrap"
|
|
1601
|
-
?
|
|
1602
|
-
:
|
|
1614
|
+
? `${hookLayout} prepared, activation root ready, seed/current-profile attach bootstrapped, learner service ${input.learnerService.state}, provider defaults ${input.providerDefaults === null ? "kept" : "written"}`
|
|
1615
|
+
: `${hookLayout} prepared, activation root kept, active pack ${input.activationPlan.activePackId ?? "unknown"} preserved, learner service ${input.learnerService.state}${input.providerDefaults === null ? "" : ", provider defaults written"}`;
|
|
1603
1616
|
const notYetProved = input.learnerService.state === "deferred"
|
|
1604
1617
|
? `OpenClaw has not reloaded this hook yet, and passive learner auto-start was deferred; restart plus status still must prove serve-path load, while learner-service start remains a separate operator check`
|
|
1605
1618
|
: input.activationPlan.action === "bootstrap"
|
|
1606
1619
|
? `Passive learning is wired for this activation root, but OpenClaw has not reloaded the hook yet; restart plus status still must prove live startup/load and the first exported turn`
|
|
1607
1620
|
: `Passive learning is wired for this activation root, but this ${input.parsed.command} run does not itself prove live startup/load after restart`;
|
|
1608
1621
|
return {
|
|
1609
|
-
hookPath: input.
|
|
1622
|
+
hookPath: input.hookPath,
|
|
1623
|
+
hookLayout: input.hookLayout,
|
|
1610
1624
|
providerDefaultsPath,
|
|
1611
1625
|
profile: {
|
|
1612
1626
|
exactProfileName: profileName,
|
|
@@ -1644,7 +1658,7 @@ function buildInstallBrainFeedbackSummary(input) {
|
|
|
1644
1658
|
profileName === null
|
|
1645
1659
|
? "profile exactName=unresolved selector=current_profile casing=not_available"
|
|
1646
1660
|
: `profile exactName=${quoteShellArg(profileName)} source=${profileSource} casing=preserved`,
|
|
1647
|
-
`hook
|
|
1661
|
+
`hook layout=${input.hookLayout} path=${shortenPath(input.hookPath)}`,
|
|
1648
1662
|
`activation root=${shortenPath(input.parsed.activationRoot)} source=${formatInstallActivationRootSource(input.parsed.activationRootSource)}`,
|
|
1649
1663
|
`attachment policy=${attachment.policy} rootMode=${attachment.activationRootMode} sameGatewayProof=${attachment.sameGatewayProof} detail=${attachment.detail}`,
|
|
1650
1664
|
`defaults provider-defaults=${shortenPath(providerDefaultsPath)} state=${input.providerDefaults === null ? "unchanged" : "written"}`,
|
|
@@ -1761,6 +1775,14 @@ function buildStatusNextStep(status, report, options) {
|
|
|
1761
1775
|
`(rerun ${buildInstallCommand(options.openclawHome)} or ${buildAttachCommand(options.openclawHome, status.host.activationRoot)}) ` +
|
|
1762
1776
|
"before trusting serve-path status again.");
|
|
1763
1777
|
}
|
|
1778
|
+
if (status.hook.loadability === "blocked") {
|
|
1779
|
+
if (options.openclawHome === null) {
|
|
1780
|
+
return "Repair the installed hook so it pins a real activation root before trusting serve-path status again.";
|
|
1781
|
+
}
|
|
1782
|
+
return (`Repair the installed ${status.hook.installLayout === "native_package_plugin" ? "native package plugin" : "profile hook"} ` +
|
|
1783
|
+
`(rerun ${buildInstallCommand(options.openclawHome)} or ${buildAttachCommand(options.openclawHome, status.host.activationRoot)}) ` +
|
|
1784
|
+
"before trusting serve-path status again.");
|
|
1785
|
+
}
|
|
1764
1786
|
if (status.brainStatus.activationState === "broken_install") {
|
|
1765
1787
|
return "Repair or replace the activation root before trusting serve-path status again.";
|
|
1766
1788
|
}
|
|
@@ -3142,13 +3164,17 @@ function runProfileHookAttachCommand(parsed) {
|
|
|
3142
3164
|
const commandLabel = parsed.command.toUpperCase();
|
|
3143
3165
|
const isInstall = parsed.command === "install";
|
|
3144
3166
|
const targetInspection = inspectOpenClawHome(parsed.openclawHome);
|
|
3145
|
-
const
|
|
3167
|
+
const installTarget = resolveOpenClawBrainInstallTarget(parsed.openclawHome);
|
|
3168
|
+
const extensionDir = installTarget.extensionDir;
|
|
3146
3169
|
steps.push(`Target OpenClaw home: ${parsed.openclawHome} (${formatInstallOpenClawHomeSource(parsed.openclawHomeSource)})`);
|
|
3147
3170
|
steps.push(isInstall
|
|
3148
3171
|
? "Lifecycle mode: install is the safe first-time default for wiring one profile to one activation root."
|
|
3149
3172
|
: "Lifecycle mode: attach is the explicit reattach/manual profile-hook path; use install for first-time setup.");
|
|
3150
3173
|
steps.push(`Detected layout: ${formatOpenClawTargetExplanation(targetInspection)}`);
|
|
3151
|
-
steps.push(`Target hook path: ${
|
|
3174
|
+
steps.push(`Target hook path: ${installTarget.hookPath} (${describeOpenClawBrainInstallLayout(installTarget.installLayout)})`);
|
|
3175
|
+
if (installTarget.selectedInstall !== null && installTarget.additionalInstalls.length > 0) {
|
|
3176
|
+
steps.push(`Kept ${describeOpenClawBrainInstallLayout(installTarget.selectedInstall.installLayout)} authoritative at ${shortenPath(installTarget.selectedInstall.extensionDir)} (${describeOpenClawBrainInstallIdentity(installTarget.selectedInstall)}); additional installs remain at ${installTarget.additionalInstalls.map((install) => `${shortenPath(install.extensionDir)} (${describeOpenClawBrainInstallLayout(install.installLayout)})`).join(", ")}`);
|
|
3177
|
+
}
|
|
3152
3178
|
// 1. Validate --openclaw-home exists and has openclaw.json
|
|
3153
3179
|
validateOpenClawHome(parsed.openclawHome);
|
|
3154
3180
|
// 2. Inspect the activation root before writing profile hook artifacts.
|
|
@@ -3210,59 +3236,60 @@ function runProfileHookAttachCommand(parsed) {
|
|
|
3210
3236
|
? `Kept inspected activation state: active pack ${activationPlan.activePackId}`
|
|
3211
3237
|
: `Reused inspected activation state for explicit attach: active pack ${activationPlan.activePackId}`);
|
|
3212
3238
|
}
|
|
3213
|
-
// 7-10.
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
+
// 7-10. Prepare the hook layout that OpenClaw will actually load.
|
|
3240
|
+
if (installTarget.writeMode === "pin_native_package") {
|
|
3241
|
+
pinInstalledOpenClawBrainPluginActivationRoot(installTarget.hookPath, parsed.activationRoot);
|
|
3242
|
+
steps.push(`Pinned native package plugin loader: ${installTarget.hookPath}`);
|
|
3243
|
+
}
|
|
3244
|
+
else {
|
|
3245
|
+
mkdirSync(extensionDir, { recursive: true });
|
|
3246
|
+
const indexTsPath = path.join(extensionDir, "index.ts");
|
|
3247
|
+
writeFileSync(indexTsPath, buildExtensionIndexTs(parsed.activationRoot), "utf8");
|
|
3248
|
+
steps.push(`Wrote extension: ${indexTsPath}`);
|
|
3249
|
+
const runtimeGuardPaths = resolveExtensionRuntimeGuardPath();
|
|
3250
|
+
if (runtimeGuardPaths.ts !== null) {
|
|
3251
|
+
const runtimeGuardTsPath = path.join(extensionDir, "runtime-guard.ts");
|
|
3252
|
+
writeFileSync(runtimeGuardTsPath, readFileSync(runtimeGuardPaths.ts, "utf8"), "utf8");
|
|
3253
|
+
steps.push(`Wrote extension runtime-guard source: ${runtimeGuardTsPath}`);
|
|
3254
|
+
}
|
|
3255
|
+
const runtimeGuardJsPath = path.join(extensionDir, "runtime-guard.js");
|
|
3256
|
+
writeFileSync(runtimeGuardJsPath, readFileSync(runtimeGuardPaths.js, "utf8"), "utf8");
|
|
3257
|
+
steps.push(`Wrote extension runtime-guard: ${runtimeGuardJsPath}`);
|
|
3258
|
+
const packageJsonPath = path.join(extensionDir, "package.json");
|
|
3259
|
+
writeFileSync(packageJsonPath, buildExtensionPackageJson(), "utf8");
|
|
3260
|
+
steps.push(`Wrote package.json: ${packageJsonPath}`);
|
|
3261
|
+
const releaseTarballInstall = resolveExtensionInstallReleaseTarballs();
|
|
3262
|
+
try {
|
|
3263
|
+
if (releaseTarballInstall !== null) {
|
|
3264
|
+
execFileSync(resolveNpmCommand(), ["install", "--ignore-scripts", "--no-save", ...releaseTarballInstall.tarballs], { cwd: extensionDir, stdio: "pipe" });
|
|
3265
|
+
steps.push(`Installed extension dependencies from release artifacts: ${releaseTarballInstall.tarballs.length} tarballs from ${releaseTarballInstall.artifactDir}`);
|
|
3266
|
+
}
|
|
3267
|
+
else {
|
|
3268
|
+
execSync("npm install --ignore-scripts", { cwd: extensionDir, stdio: "pipe" });
|
|
3269
|
+
steps.push("Ran npm install --ignore-scripts");
|
|
3270
|
+
const linkedPackages = installExtensionFromLocalWorkspaceBuild(extensionDir);
|
|
3271
|
+
if (linkedPackages !== null) {
|
|
3272
|
+
steps.push(`Linked coherent local workspace packages: ${linkedPackages.join(", ")}`);
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3239
3275
|
}
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3276
|
+
catch (err) {
|
|
3277
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3278
|
+
if (releaseTarballInstall !== null) {
|
|
3279
|
+
throw new Error(`Extension dependency install from release artifacts failed: ${message}`);
|
|
3280
|
+
}
|
|
3243
3281
|
const linkedPackages = installExtensionFromLocalWorkspaceBuild(extensionDir);
|
|
3244
3282
|
if (linkedPackages !== null) {
|
|
3245
3283
|
steps.push(`Linked coherent local workspace packages: ${linkedPackages.join(", ")}`);
|
|
3246
3284
|
}
|
|
3285
|
+
else {
|
|
3286
|
+
steps.push(`npm install failed (non-fatal): ${message}`);
|
|
3287
|
+
}
|
|
3247
3288
|
}
|
|
3289
|
+
const manifestPath = path.join(extensionDir, "openclaw.plugin.json");
|
|
3290
|
+
writeFileSync(manifestPath, buildExtensionPluginManifest(), "utf8");
|
|
3291
|
+
steps.push(`Wrote manifest: ${manifestPath}`);
|
|
3248
3292
|
}
|
|
3249
|
-
catch (err) {
|
|
3250
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3251
|
-
if (releaseTarballInstall !== null) {
|
|
3252
|
-
throw new Error(`Extension dependency install from release artifacts failed: ${message}`);
|
|
3253
|
-
}
|
|
3254
|
-
const linkedPackages = installExtensionFromLocalWorkspaceBuild(extensionDir);
|
|
3255
|
-
if (linkedPackages !== null) {
|
|
3256
|
-
steps.push(`Linked coherent local workspace packages: ${linkedPackages.join(", ")}`);
|
|
3257
|
-
}
|
|
3258
|
-
else {
|
|
3259
|
-
steps.push(`npm install failed (non-fatal): ${message}`);
|
|
3260
|
-
}
|
|
3261
|
-
}
|
|
3262
|
-
// 8. Write plugin manifest
|
|
3263
|
-
const manifestPath = path.join(extensionDir, "openclaw.plugin.json");
|
|
3264
|
-
writeFileSync(manifestPath, buildExtensionPluginManifest(), "utf8");
|
|
3265
|
-
steps.push(`Wrote manifest: ${manifestPath}`);
|
|
3266
3293
|
const pluginConfigRepair = ensureOpenClawBrainPluginConfig(parsed.openclawHome);
|
|
3267
3294
|
steps.push(pluginConfigRepair.detail);
|
|
3268
3295
|
const learnerService = ensureLifecycleLearnerService(parsed.activationRoot);
|
|
@@ -3270,7 +3297,8 @@ function runProfileHookAttachCommand(parsed) {
|
|
|
3270
3297
|
const brainFeedback = buildInstallBrainFeedbackSummary({
|
|
3271
3298
|
parsed,
|
|
3272
3299
|
targetInspection,
|
|
3273
|
-
|
|
3300
|
+
hookPath: installTarget.hookPath,
|
|
3301
|
+
hookLayout: installTarget.installLayout,
|
|
3274
3302
|
activationPlan,
|
|
3275
3303
|
learnerService,
|
|
3276
3304
|
embedderProvision,
|
|
@@ -3291,7 +3319,7 @@ function runProfileHookAttachCommand(parsed) {
|
|
|
3291
3319
|
: null
|
|
3292
3320
|
].filter((step) => step !== null);
|
|
3293
3321
|
const preflightSummary = [
|
|
3294
|
-
`Hook:
|
|
3322
|
+
`Hook: ${describeOpenClawBrainInstallLayout(installTarget.installLayout)} at ${shortenPath(installTarget.hookPath)}`,
|
|
3295
3323
|
parsed.shared
|
|
3296
3324
|
? "Attachment policy: shared activation root declared; same-gateway many-profile load/serve proof is still not checked in"
|
|
3297
3325
|
: "Attachment policy: dedicated activation root for this profile/home boundary",
|
|
@@ -3324,7 +3352,7 @@ function runProfileHookAttachCommand(parsed) {
|
|
|
3324
3352
|
? `Embedder: ensured default Ollama model ${embedderProvision.model} before brain init`
|
|
3325
3353
|
: `Embedder: skipped default Ollama model ${embedderProvision.model} via ${parsed.skipEmbedderProvisionSource === "flag" ? "--skip-embedder-provision" : OPENCLAWBRAIN_INSTALL_SKIP_EMBEDDER_PROVISION_ENV}`,
|
|
3326
3354
|
...(providerDefaults === null ? [] : [`${providerDefaults.lifecycleSummary} (${shortenPath(providerDefaults.path)})`]),
|
|
3327
|
-
`Profile hook:
|
|
3355
|
+
`Profile hook: ${describeOpenClawBrainInstallLayout(installTarget.installLayout)} at ${shortenPath(installTarget.hookPath)}`,
|
|
3328
3356
|
`Learner service: ${learnerService.state} for ${shortenPath(parsed.activationRoot)}`,
|
|
3329
3357
|
activationPlan.resolution === "new_root"
|
|
3330
3358
|
? `Activation data: initialized at ${shortenPath(parsed.activationRoot)}`
|
|
@@ -3401,6 +3429,7 @@ function runProfileHookAttachCommand(parsed) {
|
|
|
3401
3429
|
learnerService,
|
|
3402
3430
|
brainFeedback: {
|
|
3403
3431
|
hookPath: brainFeedback.hookPath,
|
|
3432
|
+
hookLayout: brainFeedback.hookLayout,
|
|
3404
3433
|
providerDefaultsPath: brainFeedback.providerDefaultsPath,
|
|
3405
3434
|
profile: brainFeedback.profile,
|
|
3406
3435
|
attachment: brainFeedback.attachment,
|
|
@@ -3480,6 +3509,10 @@ function readOpenClawJsonConfig(openclawHome) {
|
|
|
3480
3509
|
}
|
|
3481
3510
|
function ensureOpenClawBrainPluginConfig(openclawHome) {
|
|
3482
3511
|
const { path: openclawJsonPath, config } = readOpenClawJsonConfig(openclawHome);
|
|
3512
|
+
const selectedInstall = findInstalledOpenClawBrainPlugin(openclawHome).selectedInstall;
|
|
3513
|
+
const knownPluginIds = selectedInstall === null
|
|
3514
|
+
? ["openclawbrain", "openclaw"]
|
|
3515
|
+
: getOpenClawBrainKnownPluginIds(selectedInstall);
|
|
3483
3516
|
const plugins = readJsonObjectRecord(config.plugins);
|
|
3484
3517
|
if (plugins === null) {
|
|
3485
3518
|
return {
|
|
@@ -3502,24 +3535,29 @@ function ensureOpenClawBrainPluginConfig(openclawHome) {
|
|
|
3502
3535
|
detail: `Left ${shortenPath(openclawJsonPath)} unchanged because plugins.allow is not an array`
|
|
3503
3536
|
};
|
|
3504
3537
|
}
|
|
3505
|
-
|
|
3538
|
+
const missingPluginIds = knownPluginIds.filter((pluginId) => !plugins.allow.includes(pluginId));
|
|
3539
|
+
if (missingPluginIds.length === 0) {
|
|
3506
3540
|
return {
|
|
3507
3541
|
path: openclawJsonPath,
|
|
3508
3542
|
changed: false,
|
|
3509
|
-
detail: `Verified ${shortenPath(openclawJsonPath)} plugins.allow already includes
|
|
3543
|
+
detail: `Verified ${shortenPath(openclawJsonPath)} plugins.allow already includes ${knownPluginIds.join(", ")}`
|
|
3510
3544
|
};
|
|
3511
3545
|
}
|
|
3512
|
-
plugins.allow = [...plugins.allow,
|
|
3546
|
+
plugins.allow = [...plugins.allow, ...missingPluginIds];
|
|
3513
3547
|
config.plugins = plugins;
|
|
3514
3548
|
writeFileSync(openclawJsonPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
3515
3549
|
return {
|
|
3516
3550
|
path: openclawJsonPath,
|
|
3517
3551
|
changed: true,
|
|
3518
|
-
detail: `Repaired ${shortenPath(openclawJsonPath)} plugins.allow by adding
|
|
3552
|
+
detail: `Repaired ${shortenPath(openclawJsonPath)} plugins.allow by adding ${missingPluginIds.join(", ")}`
|
|
3519
3553
|
};
|
|
3520
3554
|
}
|
|
3521
3555
|
function scrubOpenClawBrainPluginConfig(openclawHome) {
|
|
3522
3556
|
const { path: openclawJsonPath, config } = readOpenClawJsonConfig(openclawHome);
|
|
3557
|
+
const selectedInstall = findInstalledOpenClawBrainPlugin(openclawHome).selectedInstall;
|
|
3558
|
+
const knownPluginIds = new Set(selectedInstall === null
|
|
3559
|
+
? ["openclawbrain", "openclaw"]
|
|
3560
|
+
: getOpenClawBrainKnownPluginIds(selectedInstall));
|
|
3523
3561
|
const plugins = readJsonObjectRecord(config.plugins);
|
|
3524
3562
|
if (plugins === null) {
|
|
3525
3563
|
return {
|
|
@@ -3531,10 +3569,10 @@ function scrubOpenClawBrainPluginConfig(openclawHome) {
|
|
|
3531
3569
|
const changes = [];
|
|
3532
3570
|
let changed = false;
|
|
3533
3571
|
if (Array.isArray(plugins.allow)) {
|
|
3534
|
-
const filteredAllow = plugins.allow.filter((entry) => entry
|
|
3572
|
+
const filteredAllow = plugins.allow.filter((entry) => !knownPluginIds.has(entry));
|
|
3535
3573
|
if (filteredAllow.length !== plugins.allow.length) {
|
|
3536
3574
|
changed = true;
|
|
3537
|
-
changes.push("removed plugins.allow
|
|
3575
|
+
changes.push("removed plugins.allow entries");
|
|
3538
3576
|
if (filteredAllow.length > 0) {
|
|
3539
3577
|
plugins.allow = filteredAllow;
|
|
3540
3578
|
}
|
|
@@ -3544,10 +3582,15 @@ function scrubOpenClawBrainPluginConfig(openclawHome) {
|
|
|
3544
3582
|
}
|
|
3545
3583
|
}
|
|
3546
3584
|
const entries = readJsonObjectRecord(plugins.entries);
|
|
3547
|
-
if (entries !== null
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3585
|
+
if (entries !== null) {
|
|
3586
|
+
for (const pluginId of knownPluginIds) {
|
|
3587
|
+
if (!Object.prototype.hasOwnProperty.call(entries, pluginId)) {
|
|
3588
|
+
continue;
|
|
3589
|
+
}
|
|
3590
|
+
delete entries[pluginId];
|
|
3591
|
+
changed = true;
|
|
3592
|
+
changes.push(`removed plugins.entries.${pluginId}`);
|
|
3593
|
+
}
|
|
3551
3594
|
}
|
|
3552
3595
|
if (entries !== null && Object.keys(entries).length === 0 && Object.prototype.hasOwnProperty.call(plugins, "entries")) {
|
|
3553
3596
|
delete plugins.entries;
|
|
@@ -3584,14 +3627,29 @@ function resolveCleanupActivationRoot(openclawHome, explicitActivationRoot) {
|
|
|
3584
3627
|
return resolved.trim().length === 0 ? null : path.resolve(resolved);
|
|
3585
3628
|
}
|
|
3586
3629
|
function removeProfileHookup(openclawHome, steps) {
|
|
3587
|
-
const
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3630
|
+
const installedPlugin = findInstalledOpenClawBrainPlugin(openclawHome);
|
|
3631
|
+
const installs = installedPlugin.selectedInstall === null
|
|
3632
|
+
? []
|
|
3633
|
+
: [installedPlugin.selectedInstall, ...installedPlugin.additionalInstalls];
|
|
3634
|
+
if (installs.length === 0) {
|
|
3635
|
+
steps.push(`Profile hookup already absent under ${installedPlugin.extensionsDir}`);
|
|
3636
|
+
return {
|
|
3637
|
+
primaryPath: path.join(openclawHome, "extensions", "openclawbrain"),
|
|
3638
|
+
removedPaths: []
|
|
3639
|
+
};
|
|
3640
|
+
}
|
|
3641
|
+
const removedPaths = [];
|
|
3642
|
+
for (const install of installs
|
|
3643
|
+
.slice()
|
|
3644
|
+
.sort((left, right) => right.extensionDir.length - left.extensionDir.length)) {
|
|
3645
|
+
rmSync(install.extensionDir, { recursive: true, force: true });
|
|
3646
|
+
removedPaths.push(install.extensionDir);
|
|
3647
|
+
steps.push(`Removed ${describeOpenClawBrainInstallLayout(install.installLayout)}: ${install.extensionDir}`);
|
|
3591
3648
|
}
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3649
|
+
return {
|
|
3650
|
+
primaryPath: removedPaths[0],
|
|
3651
|
+
removedPaths
|
|
3652
|
+
};
|
|
3595
3653
|
}
|
|
3596
3654
|
function summarizeKeptActivationData(activationRoot) {
|
|
3597
3655
|
if (activationRoot === null) {
|
|
@@ -3636,7 +3694,7 @@ function runDetachCommand(parsed) {
|
|
|
3636
3694
|
clearCleanupRuntimeLoadProof(activationRoot, parsed.openclawHome, steps);
|
|
3637
3695
|
const learnerService = resolveCleanupLearnerServiceOutcome(activationRoot, parsed.openclawHome);
|
|
3638
3696
|
const pluginConfigCleanup = scrubOpenClawBrainPluginConfig(parsed.openclawHome);
|
|
3639
|
-
const
|
|
3697
|
+
const removedHookup = removeProfileHookup(parsed.openclawHome, steps);
|
|
3640
3698
|
const legacyResidue = removeLegacyProfileResidue(parsed.openclawHome);
|
|
3641
3699
|
const activationData = summarizeKeptActivationData(activationRoot);
|
|
3642
3700
|
const restartGuidance = buildRestartGuidance(parsed.restart);
|
|
@@ -3667,7 +3725,8 @@ function runDetachCommand(parsed) {
|
|
|
3667
3725
|
profileSource: targetInspection.profileSource,
|
|
3668
3726
|
configuredProfileIds: targetInspection.configuredProfileIds
|
|
3669
3727
|
},
|
|
3670
|
-
extensionDir,
|
|
3728
|
+
extensionDir: removedHookup.primaryPath,
|
|
3729
|
+
removedHookDirs: removedHookup.removedPaths,
|
|
3671
3730
|
activationRoot,
|
|
3672
3731
|
dataAction: "kept",
|
|
3673
3732
|
activationDataState: activationData.activationDataState,
|
|
@@ -3727,7 +3786,7 @@ function runUninstallCommand(parsed) {
|
|
|
3727
3786
|
learnerService.matchesRequestedActivationRoot !== false) {
|
|
3728
3787
|
throw new Error(`Refusing to purge activation root ${path.resolve(activationRoot)} because the background learner service for this exact root could not be removed. ${learnerService.detail}`);
|
|
3729
3788
|
}
|
|
3730
|
-
const
|
|
3789
|
+
const removedHookup = removeProfileHookup(parsed.openclawHome, steps);
|
|
3731
3790
|
const legacyResidue = removeLegacyProfileResidue(parsed.openclawHome);
|
|
3732
3791
|
let activationData;
|
|
3733
3792
|
if (parsed.dataMode === "purge") {
|
|
@@ -3785,7 +3844,8 @@ function runUninstallCommand(parsed) {
|
|
|
3785
3844
|
profileSource: targetInspection.profileSource,
|
|
3786
3845
|
configuredProfileIds: targetInspection.configuredProfileIds
|
|
3787
3846
|
},
|
|
3788
|
-
extensionDir,
|
|
3847
|
+
extensionDir: removedHookup.primaryPath,
|
|
3848
|
+
removedHookDirs: removedHookup.removedPaths,
|
|
3789
3849
|
activationRoot,
|
|
3790
3850
|
dataAction: parsed.dataMode,
|
|
3791
3851
|
activationDataState: activationData.activationDataState,
|
|
@@ -5520,4 +5580,4 @@ if (isDirectCliRun(process.argv[1], import.meta.url)) {
|
|
|
5520
5580
|
process.exitCode = 1;
|
|
5521
5581
|
}
|
|
5522
5582
|
}
|
|
5523
|
-
//# sourceMappingURL=cli.js.map
|
|
5583
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1561,9 +1561,16 @@ export interface OperatorRouteFnFreshnessSummary {
|
|
|
1561
1561
|
interface OperatorHookSummary {
|
|
1562
1562
|
scope: "exact_openclaw_home" | "activation_root_only";
|
|
1563
1563
|
openclawHome: string | null;
|
|
1564
|
+
extensionDir: string | null;
|
|
1564
1565
|
hookPath: string | null;
|
|
1565
1566
|
runtimeGuardPath: string | null;
|
|
1566
1567
|
manifestPath: string | null;
|
|
1568
|
+
packageJsonPath: string | null;
|
|
1569
|
+
manifestId: string | null;
|
|
1570
|
+
installId: string | null;
|
|
1571
|
+
packageName: string | null;
|
|
1572
|
+
installLayout: import("./openclaw-plugin-install.js").OpenClawBrainInstallLayout | null;
|
|
1573
|
+
additionalInstallCount: number;
|
|
1567
1574
|
installState: CurrentProfileHookInstallStateV1;
|
|
1568
1575
|
loadability: CurrentProfileHookLoadabilityV1;
|
|
1569
1576
|
loadProof: CurrentProfileHookLoadProofV1;
|