@openclawbrain/cli 0.4.12 → 0.4.14
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 +17 -11
- package/dist/extension/index.js +1 -1
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/runtime-guard.d.ts +2 -0
- package/dist/extension/runtime-guard.js +33 -1
- package/dist/extension/runtime-guard.js.map +1 -1
- package/dist/src/attachment-truth.d.ts +32 -22
- package/dist/src/attachment-truth.js +338 -186
- package/dist/src/cli.d.ts +13 -1
- package/dist/src/cli.js +439 -95
- package/dist/src/install-converge.js +217 -0
- package/dist/src/learning-spine.d.ts +2 -1
- package/dist/src/learning-spine.js +7 -19
- package/dist/src/local-learner.d.ts +25 -0
- package/dist/src/local-learner.js +224 -37
- package/dist/src/proof-command.js +105 -41
- package/dist/src/runtime-core.js +578 -0
- package/dist/src/teacher-decision-match.js +240 -0
- package/dist/src/teacher-labeler.js +4 -30
- package/extension/runtime-guard.ts +45 -0
- package/package.json +5 -3
package/dist/src/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { execFileSync, execSync } from "node:child_process";
|
|
2
|
+
import { execFileSync, execSync, spawnSync } from "node:child_process";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, readSync, openSync, closeSync, realpathSync, rmSync, statSync, writeFileSync, symlinkSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -15,6 +15,7 @@ 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
17
|
import { describeOpenClawBrainInstallIdentity, describeOpenClawBrainInstallLayout, findInstalledOpenClawBrainPlugin, getOpenClawBrainKnownPluginIds, normalizeOpenClawBrainPluginsConfig, pinInstalledOpenClawBrainPluginActivationRoot, resolveOpenClawBrainInstallTarget } from "./openclaw-plugin-install.js";
|
|
18
|
+
import { buildOpenClawBrainConvergeRestartPlan, classifyOpenClawBrainConvergeVerification, describeOpenClawBrainConvergeChangeReasons, diffOpenClawBrainConvergeRuntimeFingerprint, finalizeOpenClawBrainConvergeResult, planOpenClawBrainConvergePluginAction } from "./install-converge.js";
|
|
18
19
|
import { loadAttachmentPolicyDeclaration, resolveEffectiveAttachmentPolicyTruth, writeAttachmentPolicyDeclaration } from "./attachment-policy-truth.js";
|
|
19
20
|
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
21
|
import { appendLearningUpdateLogs } from "./learning-spine.js";
|
|
@@ -516,7 +517,7 @@ function operatorCliHelp() {
|
|
|
516
517
|
" --help Show this help.",
|
|
517
518
|
"",
|
|
518
519
|
"Lifecycle flow:",
|
|
519
|
-
" 1. install openclawbrain install —
|
|
520
|
+
" 1. install openclawbrain install — converge one OpenClaw home: plugin-manager install/update/repair, hook repair, conditional restart, then status verification",
|
|
520
521
|
" 2. attach openclawbrain attach --openclaw-home <path> [--activation-root <path>] — explicit reattach/manual hook path for known brain data; use install first",
|
|
521
522
|
" 3. status openclawbrain status --activation-root <path> — answer \"How's the brain?\" for that boundary",
|
|
522
523
|
" 4. status --detailed openclawbrain status --activation-root <path> --detailed — explain serve path, freshness, backlog, and failure mode",
|
|
@@ -1452,6 +1453,182 @@ function buildGatewayRestartCommand(profileId) {
|
|
|
1452
1453
|
function buildGatewayStatusCommand(profileId) {
|
|
1453
1454
|
return `env -i HOME="$HOME" PATH="$PATH" openclaw --profile ${quoteShellArg(profileId)} gateway status`;
|
|
1454
1455
|
}
|
|
1456
|
+
function buildGatewayRestartArgs(profileId) {
|
|
1457
|
+
return profileId === null ? ["gateway", "restart"] : ["gateway", "restart", "--profile", profileId];
|
|
1458
|
+
}
|
|
1459
|
+
function shellJoin(parts) {
|
|
1460
|
+
return parts
|
|
1461
|
+
.map((part) => {
|
|
1462
|
+
if (/^[A-Za-z0-9_./:@=-]+$/.test(part)) {
|
|
1463
|
+
return part;
|
|
1464
|
+
}
|
|
1465
|
+
return JSON.stringify(part);
|
|
1466
|
+
})
|
|
1467
|
+
.join(" ");
|
|
1468
|
+
}
|
|
1469
|
+
function runCapturedExternalCommand(command, args, options = {}) {
|
|
1470
|
+
const result = spawnSync(command, args, {
|
|
1471
|
+
cwd: options.cwd ?? process.cwd(),
|
|
1472
|
+
env: options.env ?? process.env,
|
|
1473
|
+
encoding: "utf8",
|
|
1474
|
+
stdio: "pipe"
|
|
1475
|
+
});
|
|
1476
|
+
return {
|
|
1477
|
+
command,
|
|
1478
|
+
args,
|
|
1479
|
+
shellCommand: shellJoin([command, ...args]),
|
|
1480
|
+
stdout: result.stdout ?? "",
|
|
1481
|
+
stderr: result.stderr ?? "",
|
|
1482
|
+
exitCode: typeof result.status === "number" ? result.status : null,
|
|
1483
|
+
signal: result.signal ?? null,
|
|
1484
|
+
error: result.error ? toErrorMessage(result.error) : null
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
function summarizeCapturedCommandFailure(capture) {
|
|
1488
|
+
const parts = [];
|
|
1489
|
+
if (capture.error !== null) {
|
|
1490
|
+
parts.push(capture.error);
|
|
1491
|
+
}
|
|
1492
|
+
if (capture.stderr.trim().length > 0) {
|
|
1493
|
+
parts.push(capture.stderr.trim());
|
|
1494
|
+
}
|
|
1495
|
+
if (capture.stdout.trim().length > 0) {
|
|
1496
|
+
parts.push(capture.stdout.trim());
|
|
1497
|
+
}
|
|
1498
|
+
if (capture.exitCode !== null) {
|
|
1499
|
+
parts.push(`exitCode=${capture.exitCode}`);
|
|
1500
|
+
}
|
|
1501
|
+
return parts.length === 0 ? "no command output was captured" : parts.join(" | ");
|
|
1502
|
+
}
|
|
1503
|
+
function readTextFileIfExists(filePath) {
|
|
1504
|
+
if (filePath === null || !existsSync(filePath)) {
|
|
1505
|
+
return null;
|
|
1506
|
+
}
|
|
1507
|
+
try {
|
|
1508
|
+
return readFileSync(filePath, "utf8");
|
|
1509
|
+
}
|
|
1510
|
+
catch {
|
|
1511
|
+
return null;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
function readInstallRuntimeFingerprint(openclawHome) {
|
|
1515
|
+
const installedPlugin = findInstalledOpenClawBrainPlugin(openclawHome);
|
|
1516
|
+
const selectedInstall = installedPlugin.selectedInstall;
|
|
1517
|
+
const hook = inspectOpenClawBrainHookStatus(openclawHome);
|
|
1518
|
+
const resolvedActivationRoot = resolveActivationRoot({
|
|
1519
|
+
openclawHome,
|
|
1520
|
+
quiet: true
|
|
1521
|
+
});
|
|
1522
|
+
const { config } = readOpenClawJsonConfig(openclawHome);
|
|
1523
|
+
return {
|
|
1524
|
+
selectedInstall: selectedInstall === null
|
|
1525
|
+
? null
|
|
1526
|
+
: {
|
|
1527
|
+
extensionDir: selectedInstall.extensionDir,
|
|
1528
|
+
manifestId: selectedInstall.manifestId,
|
|
1529
|
+
installId: selectedInstall.installId,
|
|
1530
|
+
packageName: selectedInstall.packageName,
|
|
1531
|
+
installLayout: selectedInstall.installLayout
|
|
1532
|
+
},
|
|
1533
|
+
installLayout: hook.installLayout,
|
|
1534
|
+
hookPath: hook.hookPath,
|
|
1535
|
+
hookState: hook.installState,
|
|
1536
|
+
loadability: hook.loadability,
|
|
1537
|
+
activationRoot: resolvedActivationRoot.trim().length === 0 ? null : path.resolve(resolvedActivationRoot),
|
|
1538
|
+
loaderSource: readTextFileIfExists(selectedInstall?.loaderEntryPath ?? null),
|
|
1539
|
+
runtimeGuardSource: readTextFileIfExists(selectedInstall?.runtimeGuardPath ?? null),
|
|
1540
|
+
pluginsConfig: JSON.stringify(config.plugins ?? null)
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
function runOpenClawBrainConvergePluginStep(openclawHome) {
|
|
1544
|
+
const before = readInstallRuntimeFingerprint(openclawHome);
|
|
1545
|
+
const plan = planOpenClawBrainConvergePluginAction(before);
|
|
1546
|
+
const commandArgs = plan.action === "install"
|
|
1547
|
+
? ["plugins", "install", plan.packageSpec]
|
|
1548
|
+
: ["plugins", "update", plan.pluginId];
|
|
1549
|
+
const capture = runCapturedExternalCommand("openclaw", commandArgs);
|
|
1550
|
+
if (capture.error !== null || capture.exitCode !== 0) {
|
|
1551
|
+
const hasAuthoritativeNativePlugin = before.selectedInstall !== null && before.installLayout === "native_package_plugin";
|
|
1552
|
+
if (plan.action === "update" && hasAuthoritativeNativePlugin) {
|
|
1553
|
+
return {
|
|
1554
|
+
plan,
|
|
1555
|
+
command: capture.shellCommand,
|
|
1556
|
+
changed: false,
|
|
1557
|
+
changeReasons: [],
|
|
1558
|
+
detail: `Skipped plugin-manager refresh because the existing split-package plugin is already authoritative and \
|
|
1559
|
+
\`${capture.shellCommand}\` failed: ${summarizeCapturedCommandFailure(capture)}`,
|
|
1560
|
+
warning: `plugin-manager refresh skipped after \`${capture.shellCommand}\` failed; keeping the existing authoritative split-package plugin for this converge run`,
|
|
1561
|
+
capture,
|
|
1562
|
+
before,
|
|
1563
|
+
after: before
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
throw new Error(`OpenClaw plugin-manager ${plan.action} failed for ${path.resolve(openclawHome)}. Tried \`${capture.shellCommand}\`. Detail: ${summarizeCapturedCommandFailure(capture)}`);
|
|
1567
|
+
}
|
|
1568
|
+
const after = readInstallRuntimeFingerprint(openclawHome);
|
|
1569
|
+
const diff = diffOpenClawBrainConvergeRuntimeFingerprint(before, after);
|
|
1570
|
+
return {
|
|
1571
|
+
plan,
|
|
1572
|
+
command: capture.shellCommand,
|
|
1573
|
+
changed: diff.changed,
|
|
1574
|
+
changeReasons: diff.reasons,
|
|
1575
|
+
detail: diff.changed
|
|
1576
|
+
? `${plan.action === "install" ? "Installed" : "Refreshed"} plugin-manager state: ${describeOpenClawBrainConvergeChangeReasons(diff.reasons)}`
|
|
1577
|
+
: `${plan.action === "install" ? "Ran install" : "Ran update"} through the OpenClaw plugin manager, but no runtime-affecting plugin delta was detected`,
|
|
1578
|
+
warning: null,
|
|
1579
|
+
capture,
|
|
1580
|
+
before,
|
|
1581
|
+
after
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
function inspectInstallConvergeVerification(parsed) {
|
|
1585
|
+
const targetInspection = inspectOpenClawHome(parsed.openclawHome);
|
|
1586
|
+
const operatorInput = {
|
|
1587
|
+
activationRoot: parsed.activationRoot,
|
|
1588
|
+
eventExportPath: null,
|
|
1589
|
+
teacherSnapshotPath: resolveOperatorTeacherSnapshotPath(parsed.activationRoot, null),
|
|
1590
|
+
updatedAt: null,
|
|
1591
|
+
brainAttachmentPolicy: null,
|
|
1592
|
+
openclawHome: parsed.openclawHome,
|
|
1593
|
+
...(targetInspection.profileId === null ? {} : { profileId: targetInspection.profileId })
|
|
1594
|
+
};
|
|
1595
|
+
const status = describeCurrentProfileBrainStatus(operatorInput);
|
|
1596
|
+
const report = buildOperatorSurfaceReport(operatorInput);
|
|
1597
|
+
const normalizedStatusAndReport = applyAttachmentPolicyTruth(status, report);
|
|
1598
|
+
const installHook = summarizeStatusInstallHook(parsed.openclawHome);
|
|
1599
|
+
const attachmentTruth = summarizeStatusAttachmentTruth({
|
|
1600
|
+
activationRoot: parsed.activationRoot,
|
|
1601
|
+
openclawHome: parsed.openclawHome,
|
|
1602
|
+
status: normalizedStatusAndReport.status
|
|
1603
|
+
});
|
|
1604
|
+
const displayedStatus = summarizeDisplayedStatus(normalizedStatusAndReport.status, installHook);
|
|
1605
|
+
const routeFn = summarizeStatusRouteFn(normalizedStatusAndReport.status, normalizedStatusAndReport.report);
|
|
1606
|
+
return {
|
|
1607
|
+
targetInspection,
|
|
1608
|
+
status: normalizedStatusAndReport.status,
|
|
1609
|
+
report: normalizedStatusAndReport.report,
|
|
1610
|
+
installHook,
|
|
1611
|
+
attachmentTruth,
|
|
1612
|
+
displayedStatus,
|
|
1613
|
+
routeFn,
|
|
1614
|
+
nextStep: buildStatusNextStep(normalizedStatusAndReport.status, normalizedStatusAndReport.report, {
|
|
1615
|
+
openclawHome: parsed.openclawHome,
|
|
1616
|
+
installHook
|
|
1617
|
+
}),
|
|
1618
|
+
summaryLine: `STATUS ${displayedStatus}; hook=${installHook.state}/${installHook.loadability}; runtime=${attachmentTruth.runtimeLoad}; loadProof=${normalizedStatusAndReport.status.hook.loadProof}; serve=${normalizedStatusAndReport.status.brainStatus.serveState}`,
|
|
1619
|
+
facts: {
|
|
1620
|
+
installLayout: normalizedStatusAndReport.status.hook.installLayout ?? installHook.installLayout ?? null,
|
|
1621
|
+
installState: installHook.state,
|
|
1622
|
+
loadability: installHook.loadability,
|
|
1623
|
+
displayedStatus,
|
|
1624
|
+
runtimeLoad: attachmentTruth.runtimeLoad,
|
|
1625
|
+
loadProof: normalizedStatusAndReport.status.hook.loadProof,
|
|
1626
|
+
serveState: normalizedStatusAndReport.status.brainStatus.serveState,
|
|
1627
|
+
routeFnAvailable: routeFn.available,
|
|
1628
|
+
awaitingFirstExport: normalizedStatusAndReport.status.brainStatus.awaitingFirstExport
|
|
1629
|
+
}
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1455
1632
|
function buildInstallCommand(openclawHome) {
|
|
1456
1633
|
return `openclawbrain install --openclaw-home ${quoteShellArg(openclawHome)}`;
|
|
1457
1634
|
}
|
|
@@ -3238,7 +3415,7 @@ function runHistoryCommand(parsed) {
|
|
|
3238
3415
|
}
|
|
3239
3416
|
return 0;
|
|
3240
3417
|
}
|
|
3241
|
-
function
|
|
3418
|
+
function executeProfileHookAttachCommand(parsed) {
|
|
3242
3419
|
const steps = [];
|
|
3243
3420
|
const commandLabel = parsed.command.toUpperCase();
|
|
3244
3421
|
const isInstall = parsed.command === "install";
|
|
@@ -3458,106 +3635,273 @@ function runProfileHookAttachCommand(parsed) {
|
|
|
3458
3635
|
? `Install: kept healthy active pack ${activationPlan.activePackId} in place`
|
|
3459
3636
|
: `Attach: rewired the profile hook to healthy active pack ${activationPlan.activePackId}`
|
|
3460
3637
|
];
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3638
|
+
return {
|
|
3639
|
+
command: parsed.command,
|
|
3640
|
+
commandLabel,
|
|
3641
|
+
openclawHome: parsed.openclawHome,
|
|
3642
|
+
openclawHomeSource: parsed.openclawHomeSource,
|
|
3643
|
+
openclawTarget: {
|
|
3644
|
+
layout: targetInspection.layout,
|
|
3645
|
+
detail: describeOpenClawHomeInspection(targetInspection),
|
|
3646
|
+
profileId: targetInspection.profileId,
|
|
3647
|
+
profileSource: targetInspection.profileSource,
|
|
3648
|
+
configuredProfileIds: targetInspection.configuredProfileIds
|
|
3649
|
+
},
|
|
3650
|
+
activationRoot: parsed.activationRoot,
|
|
3651
|
+
resolvedInputs: {
|
|
3652
|
+
activationRoot: {
|
|
3653
|
+
value: parsed.activationRoot,
|
|
3654
|
+
source: parsed.activationRootSource
|
|
3473
3655
|
},
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3656
|
+
workspaceId: {
|
|
3657
|
+
value: parsed.workspaceId,
|
|
3658
|
+
source: parsed.workspaceIdSource
|
|
3659
|
+
}
|
|
3660
|
+
},
|
|
3661
|
+
workspaceId: parsed.workspaceId,
|
|
3662
|
+
shared: parsed.shared,
|
|
3663
|
+
embedderProvision: embedderProvision === null
|
|
3664
|
+
? null
|
|
3665
|
+
: {
|
|
3666
|
+
skipped: parsed.skipEmbedderProvision,
|
|
3667
|
+
source: parsed.skipEmbedderProvisionSource,
|
|
3668
|
+
model: embedderProvision.model,
|
|
3669
|
+
baseUrl: embedderProvision.baseUrl
|
|
3484
3670
|
},
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
:
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
detectedLocally: providerDefaults.defaults.teacher.detectedLocally ?? false
|
|
3505
|
-
},
|
|
3506
|
-
embedder: providerDefaults.defaults.embedder === undefined
|
|
3507
|
-
? null
|
|
3508
|
-
: {
|
|
3509
|
-
provider: providerDefaults.defaults.embedder.provider ?? null,
|
|
3510
|
-
model: providerDefaults.defaults.embedder.model ?? null
|
|
3511
|
-
},
|
|
3512
|
-
teacherBaseUrl: providerDefaults.defaults.teacherBaseUrl ?? null,
|
|
3513
|
-
embedderBaseUrl: providerDefaults.defaults.embedderBaseUrl ?? null
|
|
3514
|
-
},
|
|
3515
|
-
pluginConfigRepair,
|
|
3516
|
-
learnerService,
|
|
3517
|
-
brainFeedback: {
|
|
3518
|
-
hookPath: brainFeedback.hookPath,
|
|
3519
|
-
hookLayout: brainFeedback.hookLayout,
|
|
3520
|
-
providerDefaultsPath: brainFeedback.providerDefaultsPath,
|
|
3521
|
-
profile: brainFeedback.profile,
|
|
3522
|
-
attachment: brainFeedback.attachment,
|
|
3523
|
-
restart: brainFeedback.restart,
|
|
3524
|
-
embedder: brainFeedback.embedder,
|
|
3525
|
-
teacher: brainFeedback.teacher,
|
|
3526
|
-
learnerService: brainFeedback.learnerService,
|
|
3527
|
-
startup: brainFeedback.startup,
|
|
3528
|
-
provedNow: brainFeedback.provedNow,
|
|
3529
|
-
notYetProved: brainFeedback.notYetProved,
|
|
3530
|
-
lines: brainFeedback.lines
|
|
3671
|
+
providerDefaults: providerDefaults === null
|
|
3672
|
+
? null
|
|
3673
|
+
: {
|
|
3674
|
+
path: providerDefaults.path,
|
|
3675
|
+
teacher: providerDefaults.defaults.teacher === undefined
|
|
3676
|
+
? null
|
|
3677
|
+
: {
|
|
3678
|
+
provider: providerDefaults.defaults.teacher.provider ?? null,
|
|
3679
|
+
model: providerDefaults.defaults.teacher.model ?? null,
|
|
3680
|
+
detectedLocally: providerDefaults.defaults.teacher.detectedLocally ?? false
|
|
3681
|
+
},
|
|
3682
|
+
embedder: providerDefaults.defaults.embedder === undefined
|
|
3683
|
+
? null
|
|
3684
|
+
: {
|
|
3685
|
+
provider: providerDefaults.defaults.embedder.provider ?? null,
|
|
3686
|
+
model: providerDefaults.defaults.embedder.model ?? null
|
|
3687
|
+
},
|
|
3688
|
+
teacherBaseUrl: providerDefaults.defaults.teacherBaseUrl ?? null,
|
|
3689
|
+
embedderBaseUrl: providerDefaults.defaults.embedderBaseUrl ?? null
|
|
3531
3690
|
},
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3691
|
+
pluginConfigRepair,
|
|
3692
|
+
learnerService,
|
|
3693
|
+
brainFeedback: {
|
|
3694
|
+
hookPath: brainFeedback.hookPath,
|
|
3695
|
+
hookLayout: brainFeedback.hookLayout,
|
|
3696
|
+
providerDefaultsPath: brainFeedback.providerDefaultsPath,
|
|
3697
|
+
profile: brainFeedback.profile,
|
|
3698
|
+
attachment: brainFeedback.attachment,
|
|
3699
|
+
restart: brainFeedback.restart,
|
|
3700
|
+
embedder: brainFeedback.embedder,
|
|
3701
|
+
teacher: brainFeedback.teacher,
|
|
3702
|
+
learnerService: brainFeedback.learnerService,
|
|
3703
|
+
startup: brainFeedback.startup,
|
|
3704
|
+
provedNow: brainFeedback.provedNow,
|
|
3705
|
+
notYetProved: brainFeedback.notYetProved,
|
|
3706
|
+
lines: brainFeedback.lines
|
|
3707
|
+
},
|
|
3708
|
+
extensionDir,
|
|
3709
|
+
lifecycleSummary,
|
|
3710
|
+
preflightSummary,
|
|
3711
|
+
restartGuidance,
|
|
3712
|
+
nextSteps,
|
|
3713
|
+
steps
|
|
3714
|
+
};
|
|
3715
|
+
}
|
|
3716
|
+
function emitProfileHookAttachCommandResult(result, parsed) {
|
|
3717
|
+
if (parsed.json) {
|
|
3718
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3719
|
+
return;
|
|
3539
3720
|
}
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
console.log(`Gateway: Confirm OpenClaw after restart: ${brainFeedback.restart.gatewayStatusCommand}`);
|
|
3549
|
-
}
|
|
3550
|
-
console.log(`Check: ${buildInstallStatusCommand(parsed.activationRoot)}`);
|
|
3551
|
-
console.log(`Proof: ${buildProofCommandForOpenClawHome(parsed.openclawHome)}`);
|
|
3552
|
-
console.log(`Learner: ${buildLearnerServiceStatusCommand(parsed.activationRoot)}`);
|
|
3553
|
-
if (embedderProvision !== null && embedderProvision.state === "skipped") {
|
|
3554
|
-
console.log(`Embedder: ${buildInstallEmbedderProvisionCommand(embedderProvision.baseUrl, embedderProvision.model)}`);
|
|
3555
|
-
}
|
|
3721
|
+
console.log(`${result.commandLabel} complete\n`);
|
|
3722
|
+
console.log("Brain feedback:");
|
|
3723
|
+
for (const line of result.brainFeedback.lines) {
|
|
3724
|
+
console.log(` ${line}`);
|
|
3725
|
+
}
|
|
3726
|
+
console.log(`Restart: ${result.restartGuidance}`);
|
|
3727
|
+
if (result.brainFeedback.restart.gatewayStatusCommand !== null) {
|
|
3728
|
+
console.log(`Gateway: Confirm OpenClaw after restart: ${result.brainFeedback.restart.gatewayStatusCommand}`);
|
|
3556
3729
|
}
|
|
3730
|
+
console.log(`Check: ${buildInstallStatusCommand(result.activationRoot)}`);
|
|
3731
|
+
console.log(`Proof: ${buildProofCommandForOpenClawHome(result.openclawHome)}`);
|
|
3732
|
+
console.log(`Learner: ${buildLearnerServiceStatusCommand(result.activationRoot)}`);
|
|
3733
|
+
if (result.embedderProvision !== null && result.embedderProvision.skipped) {
|
|
3734
|
+
console.log(`Embedder: ${buildInstallEmbedderProvisionCommand(result.embedderProvision.baseUrl, result.embedderProvision.model)}`);
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
function runProfileHookAttachCommand(parsed) {
|
|
3738
|
+
const result = executeProfileHookAttachCommand(parsed);
|
|
3739
|
+
emitProfileHookAttachCommandResult(result, parsed);
|
|
3557
3740
|
return 0;
|
|
3558
3741
|
}
|
|
3742
|
+
function emitInstallConvergeResult(result, parsed) {
|
|
3743
|
+
if (parsed.json) {
|
|
3744
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3745
|
+
return;
|
|
3746
|
+
}
|
|
3747
|
+
const heading = result.verdict.verdict === "failed"
|
|
3748
|
+
? "INSTALL converge failed"
|
|
3749
|
+
: "INSTALL converge complete";
|
|
3750
|
+
console.log(`${heading}\n`);
|
|
3751
|
+
console.log(`Plugin: ${result.plugin.detail}`);
|
|
3752
|
+
console.log(`Attach: ${result.attach.detail}`);
|
|
3753
|
+
console.log(`Restart: ${result.restart.detail}`);
|
|
3754
|
+
console.log(`Verify: ${result.verification.summaryLine}`);
|
|
3755
|
+
console.log(`Verdict: ${result.verdict.verdict}`);
|
|
3756
|
+
console.log(`Why: ${result.verdict.why}`);
|
|
3757
|
+
if (result.verdict.warnings.length > 0) {
|
|
3758
|
+
console.log(`Warnings: ${result.verdict.warnings.join("; ")}`);
|
|
3759
|
+
}
|
|
3760
|
+
console.log(`Next: ${result.verification.nextStep}`);
|
|
3761
|
+
console.log(`Proof: ${buildProofCommandForOpenClawHome(result.openclawHome)}`);
|
|
3762
|
+
console.log(`Learner: ${buildLearnerServiceStatusCommand(result.activationRoot)}`);
|
|
3763
|
+
}
|
|
3559
3764
|
function runInstallCommand(parsed) {
|
|
3560
|
-
|
|
3765
|
+
let pluginResult = null;
|
|
3766
|
+
let attachResult = null;
|
|
3767
|
+
try {
|
|
3768
|
+
validateOpenClawHome(parsed.openclawHome);
|
|
3769
|
+
pluginResult = runOpenClawBrainConvergePluginStep(parsed.openclawHome);
|
|
3770
|
+
attachResult = executeProfileHookAttachCommand(parsed);
|
|
3771
|
+
}
|
|
3772
|
+
catch (error) {
|
|
3773
|
+
const verdict = finalizeOpenClawBrainConvergeResult({
|
|
3774
|
+
stepFailure: toErrorMessage(error),
|
|
3775
|
+
verification: null,
|
|
3776
|
+
warnings: []
|
|
3777
|
+
});
|
|
3778
|
+
const failureResult = {
|
|
3779
|
+
command: "install",
|
|
3780
|
+
openclawHome: parsed.openclawHome,
|
|
3781
|
+
activationRoot: parsed.activationRoot,
|
|
3782
|
+
plugin: pluginResult ?? {
|
|
3783
|
+
action: null,
|
|
3784
|
+
command: null,
|
|
3785
|
+
changed: false,
|
|
3786
|
+
changeReasons: [],
|
|
3787
|
+
detail: "plugin-manager converge did not complete"
|
|
3788
|
+
},
|
|
3789
|
+
attach: attachResult ?? {
|
|
3790
|
+
changed: false,
|
|
3791
|
+
changeReasons: [],
|
|
3792
|
+
detail: "under-the-hood install/attach repair did not complete",
|
|
3793
|
+
hookLayout: null,
|
|
3794
|
+
hookPath: null
|
|
3795
|
+
},
|
|
3796
|
+
restart: {
|
|
3797
|
+
required: false,
|
|
3798
|
+
automatic: false,
|
|
3799
|
+
performed: false,
|
|
3800
|
+
command: null,
|
|
3801
|
+
detail: "restart did not run because install failed earlier",
|
|
3802
|
+
error: null
|
|
3803
|
+
},
|
|
3804
|
+
verification: {
|
|
3805
|
+
summaryLine: "verification did not run because install failed earlier",
|
|
3806
|
+
nextStep: "Fix the install failure and rerun openclawbrain install.",
|
|
3807
|
+
displayedStatus: "unknown",
|
|
3808
|
+
installState: "unknown",
|
|
3809
|
+
loadability: "unknown",
|
|
3810
|
+
runtimeLoad: "unknown",
|
|
3811
|
+
loadProof: "unknown",
|
|
3812
|
+
serveState: "unknown",
|
|
3813
|
+
routeFnAvailable: false,
|
|
3814
|
+
awaitingFirstExport: false
|
|
3815
|
+
},
|
|
3816
|
+
verdict
|
|
3817
|
+
};
|
|
3818
|
+
emitInstallConvergeResult(failureResult, parsed);
|
|
3819
|
+
return 1;
|
|
3820
|
+
}
|
|
3821
|
+
const attachDiff = diffOpenClawBrainConvergeRuntimeFingerprint(pluginResult.after, readInstallRuntimeFingerprint(parsed.openclawHome));
|
|
3822
|
+
const changeReasons = [...new Set([...pluginResult.changeReasons, ...attachDiff.reasons])];
|
|
3823
|
+
const restartPlan = buildOpenClawBrainConvergeRestartPlan({
|
|
3824
|
+
profileName: attachResult.brainFeedback.profile.exactProfileName,
|
|
3825
|
+
changeReasons
|
|
3826
|
+
});
|
|
3827
|
+
let restartCapture = null;
|
|
3828
|
+
let restartError = null;
|
|
3829
|
+
if (restartPlan.required && restartPlan.automatic) {
|
|
3830
|
+
restartCapture = runCapturedExternalCommand("openclaw", buildGatewayRestartArgs(attachResult.brainFeedback.profile.exactProfileName));
|
|
3831
|
+
if (restartCapture.error !== null || restartCapture.exitCode !== 0) {
|
|
3832
|
+
restartError = `Automatic restart failed after converge. Tried \`${restartCapture.shellCommand}\`. Detail: ${summarizeCapturedCommandFailure(restartCapture)}`;
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
const verificationSnapshot = inspectInstallConvergeVerification(parsed);
|
|
3836
|
+
const verification = classifyOpenClawBrainConvergeVerification({
|
|
3837
|
+
...verificationSnapshot.facts,
|
|
3838
|
+
restartRequired: restartPlan.required,
|
|
3839
|
+
restartPerformed: restartPlan.required && restartPlan.automatic && restartError === null
|
|
3840
|
+
});
|
|
3841
|
+
const convergeWarnings = [];
|
|
3842
|
+
if (pluginResult.warning) {
|
|
3843
|
+
convergeWarnings.push(pluginResult.warning);
|
|
3844
|
+
}
|
|
3845
|
+
if (restartError !== null) {
|
|
3846
|
+
convergeWarnings.push(restartError);
|
|
3847
|
+
}
|
|
3848
|
+
const verdict = finalizeOpenClawBrainConvergeResult({
|
|
3849
|
+
stepFailure: null,
|
|
3850
|
+
verification,
|
|
3851
|
+
warnings: convergeWarnings
|
|
3852
|
+
});
|
|
3853
|
+
const result = {
|
|
3854
|
+
command: "install",
|
|
3855
|
+
openclawHome: parsed.openclawHome,
|
|
3856
|
+
activationRoot: parsed.activationRoot,
|
|
3857
|
+
plugin: {
|
|
3858
|
+
action: pluginResult.plan.action,
|
|
3859
|
+
command: pluginResult.command,
|
|
3860
|
+
changed: pluginResult.changed,
|
|
3861
|
+
changeReasons: pluginResult.changeReasons,
|
|
3862
|
+
detail: pluginResult.detail
|
|
3863
|
+
},
|
|
3864
|
+
attach: {
|
|
3865
|
+
changed: attachDiff.changed,
|
|
3866
|
+
changeReasons: attachDiff.reasons,
|
|
3867
|
+
detail: attachDiff.changed
|
|
3868
|
+
? `Converged hook/install repair: ${describeOpenClawBrainConvergeChangeReasons(attachDiff.reasons)}`
|
|
3869
|
+
: "Converged hook/install repair confirmed the selected activation root and hook wiring without additional runtime-affecting changes",
|
|
3870
|
+
hookLayout: attachResult.brainFeedback.hookLayout,
|
|
3871
|
+
hookPath: attachResult.brainFeedback.hookPath
|
|
3872
|
+
},
|
|
3873
|
+
restart: {
|
|
3874
|
+
required: restartPlan.required,
|
|
3875
|
+
automatic: restartPlan.automatic,
|
|
3876
|
+
performed: restartPlan.required && restartPlan.automatic && restartError === null,
|
|
3877
|
+
command: restartCapture?.shellCommand ?? null,
|
|
3878
|
+
detail: restartPlan.required
|
|
3879
|
+
? restartPlan.automatic
|
|
3880
|
+
? restartError === null
|
|
3881
|
+
? `Ran automatic gateway restart: ${restartCapture.shellCommand}`
|
|
3882
|
+
: restartError
|
|
3883
|
+
: `${restartPlan.detail} Restart it manually, then rerun status.`
|
|
3884
|
+
: restartPlan.detail,
|
|
3885
|
+
error: restartError
|
|
3886
|
+
},
|
|
3887
|
+
verification: {
|
|
3888
|
+
summaryLine: verificationSnapshot.summaryLine,
|
|
3889
|
+
nextStep: verificationSnapshot.nextStep,
|
|
3890
|
+
displayedStatus: verificationSnapshot.displayedStatus,
|
|
3891
|
+
installState: verificationSnapshot.facts.installState,
|
|
3892
|
+
installLayout: verificationSnapshot.facts.installLayout,
|
|
3893
|
+
loadability: verificationSnapshot.facts.loadability,
|
|
3894
|
+
runtimeLoad: verificationSnapshot.facts.runtimeLoad,
|
|
3895
|
+
loadProof: verificationSnapshot.facts.loadProof,
|
|
3896
|
+
serveState: verificationSnapshot.facts.serveState,
|
|
3897
|
+
routeFnAvailable: verificationSnapshot.facts.routeFnAvailable,
|
|
3898
|
+
awaitingFirstExport: verificationSnapshot.facts.awaitingFirstExport
|
|
3899
|
+
},
|
|
3900
|
+
verdict,
|
|
3901
|
+
underlyingInstall: attachResult
|
|
3902
|
+
};
|
|
3903
|
+
emitInstallConvergeResult(result, parsed);
|
|
3904
|
+
return verdict.verdict === "failed" || verdict.verdict === "manual_action_required" ? 1 : 0;
|
|
3561
3905
|
}
|
|
3562
3906
|
function runAttachCommand(parsed) {
|
|
3563
3907
|
return runProfileHookAttachCommand(parsed);
|