@openclawbrain/cli 0.4.17 → 0.4.19
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.
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync, openSync, readSync, closeSync, statSync, readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_TAIL_BYTES = 4 * 1024 * 1024;
|
|
4
|
+
const DEFAULT_MAX_ENTRIES = 512;
|
|
5
|
+
const DEFAULT_MAX_LINE_BYTES = 128 * 1024;
|
|
6
|
+
|
|
7
|
+
export function readBoundedJsonlTail(filePath, options = {}) {
|
|
8
|
+
const tailBytes = options.tailBytes ?? DEFAULT_TAIL_BYTES;
|
|
9
|
+
const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
10
|
+
const maxLineBytes = options.maxLineBytes ?? DEFAULT_MAX_LINE_BYTES;
|
|
11
|
+
if (!existsSync(filePath)) {
|
|
12
|
+
return { entries: [], fallbackReason: null };
|
|
13
|
+
}
|
|
14
|
+
let fallbackReason = null;
|
|
15
|
+
let raw;
|
|
16
|
+
try {
|
|
17
|
+
const fileSize = statSync(filePath).size;
|
|
18
|
+
if (fileSize <= tailBytes) {
|
|
19
|
+
raw = readFileSync(filePath, "utf8");
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
fallbackReason = "tail_truncated";
|
|
23
|
+
const offset = fileSize - tailBytes;
|
|
24
|
+
const buf = Buffer.alloc(tailBytes);
|
|
25
|
+
const fd = openSync(filePath, "r");
|
|
26
|
+
try {
|
|
27
|
+
readSync(fd, buf, 0, tailBytes, offset);
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
closeSync(fd);
|
|
31
|
+
}
|
|
32
|
+
raw = buf.toString("utf8");
|
|
33
|
+
const firstNewline = raw.indexOf("\n");
|
|
34
|
+
if (firstNewline >= 0) {
|
|
35
|
+
raw = raw.slice(firstNewline + 1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return { entries: [], fallbackReason: "read_error" };
|
|
41
|
+
}
|
|
42
|
+
const lines = raw.split(/\r?\n/u).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
43
|
+
const entries = [];
|
|
44
|
+
let skippedOversized = 0;
|
|
45
|
+
for (const line of lines) {
|
|
46
|
+
if (Buffer.byteLength(line, "utf8") > maxLineBytes) {
|
|
47
|
+
skippedOversized++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
entries.push(JSON.parse(line));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// skip malformed lines
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (skippedOversized > 0) {
|
|
58
|
+
fallbackReason = fallbackReason ? `${fallbackReason}+oversized_lines_skipped` : "oversized_lines_skipped";
|
|
59
|
+
}
|
|
60
|
+
const trimmed = entries.length > maxEntries ? entries.slice(-maxEntries) : entries;
|
|
61
|
+
if (entries.length > maxEntries) {
|
|
62
|
+
fallbackReason = fallbackReason ? `${fallbackReason}+entry_count_capped` : "entry_count_capped";
|
|
63
|
+
}
|
|
64
|
+
return { entries: trimmed, fallbackReason };
|
|
65
|
+
}
|
package/dist/src/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import { ensureManagedLearnerServiceForActivationRoot, inspectManagedLearnerServ
|
|
|
10
10
|
import { exportBrain, importBrain } from "./import-export.js";
|
|
11
11
|
import { buildNormalizedEventExport } from "@openclawbrain/contracts";
|
|
12
12
|
import { buildTeacherSupervisionArtifactsFromNormalizedEventExport, createAlwaysOnLearningRuntimeState, describeAlwaysOnLearningRuntimeState, drainAlwaysOnLearningRuntime, loadOrInitBaseline, materializeAlwaysOnLearningCandidatePack, persistBaseline } from "./local-learner.js";
|
|
13
|
-
import { inspectActivationState, loadPackFromActivation, promoteCandidatePack,
|
|
13
|
+
import { inspectActivationState, loadPackFromActivation, promoteCandidatePack, resolveLearningSpineLogPath, stageCandidatePack } from "@openclawbrain/pack-format";
|
|
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";
|
|
@@ -37,6 +37,68 @@ const INSTALL_COMPATIBLE_LOCAL_TEACHER_MODEL_PREFIXES = [
|
|
|
37
37
|
"qwen3:8b",
|
|
38
38
|
"qwen2.5:7b"
|
|
39
39
|
];
|
|
40
|
+
const DEFAULT_BOUNDED_JSONL_TAIL_BYTES = 4 * 1024 * 1024;
|
|
41
|
+
const DEFAULT_BOUNDED_JSONL_MAX_ENTRIES = 512;
|
|
42
|
+
const DEFAULT_BOUNDED_JSONL_MAX_LINE_BYTES = 128 * 1024;
|
|
43
|
+
function readBoundedJsonlTail(filePath, options = {}) {
|
|
44
|
+
const tailBytes = options.tailBytes ?? DEFAULT_BOUNDED_JSONL_TAIL_BYTES;
|
|
45
|
+
const maxEntries = options.maxEntries ?? DEFAULT_BOUNDED_JSONL_MAX_ENTRIES;
|
|
46
|
+
const maxLineBytes = options.maxLineBytes ?? DEFAULT_BOUNDED_JSONL_MAX_LINE_BYTES;
|
|
47
|
+
if (!existsSync(filePath)) {
|
|
48
|
+
return { entries: [], fallbackReason: null };
|
|
49
|
+
}
|
|
50
|
+
let fallbackReason = null;
|
|
51
|
+
let raw;
|
|
52
|
+
try {
|
|
53
|
+
const fileSize = statSync(filePath).size;
|
|
54
|
+
if (fileSize <= tailBytes) {
|
|
55
|
+
raw = readFileSync(filePath, "utf8");
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
fallbackReason = "tail_truncated";
|
|
59
|
+
const offset = fileSize - tailBytes;
|
|
60
|
+
const buffer = Buffer.alloc(tailBytes);
|
|
61
|
+
const fileDescriptor = openSync(filePath, "r");
|
|
62
|
+
try {
|
|
63
|
+
readSync(fileDescriptor, buffer, 0, tailBytes, offset);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
closeSync(fileDescriptor);
|
|
67
|
+
}
|
|
68
|
+
raw = buffer.toString("utf8");
|
|
69
|
+
const firstNewline = raw.indexOf("\n");
|
|
70
|
+
if (firstNewline >= 0) {
|
|
71
|
+
raw = raw.slice(firstNewline + 1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return { entries: [], fallbackReason: "read_error" };
|
|
77
|
+
}
|
|
78
|
+
const lines = raw.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
79
|
+
const entries = [];
|
|
80
|
+
let skippedOversized = 0;
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
if (Buffer.byteLength(line, "utf8") > maxLineBytes) {
|
|
83
|
+
skippedOversized++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
entries.push(JSON.parse(line));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// skip malformed lines
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (skippedOversized > 0) {
|
|
94
|
+
fallbackReason = fallbackReason === null ? "oversized_lines_skipped" : `${fallbackReason}+oversized_lines_skipped`;
|
|
95
|
+
}
|
|
96
|
+
const trimmedEntries = entries.length > maxEntries ? entries.slice(-maxEntries) : entries;
|
|
97
|
+
if (entries.length > maxEntries) {
|
|
98
|
+
fallbackReason = fallbackReason === null ? "entry_count_capped" : `${fallbackReason}+entry_count_capped`;
|
|
99
|
+
}
|
|
100
|
+
return { entries: trimmedEntries, fallbackReason };
|
|
101
|
+
}
|
|
40
102
|
function quoteShellArg(value) {
|
|
41
103
|
return `'${value.replace(/'/g, `"'"'`)}'`;
|
|
42
104
|
}
|
|
@@ -1171,6 +1233,7 @@ function summarizeStatusEmbedder(embeddings) {
|
|
|
1171
1233
|
}
|
|
1172
1234
|
function summarizeStatusRouteFn(status, report) {
|
|
1173
1235
|
const freshness = report.servePath.refreshStatus ?? status.brain.routeFreshness;
|
|
1236
|
+
const fallbackReason = report.routeFn.fallbackReason ?? null;
|
|
1174
1237
|
if (!report.routeFn.available) {
|
|
1175
1238
|
return {
|
|
1176
1239
|
available: false,
|
|
@@ -1178,6 +1241,7 @@ function summarizeStatusRouteFn(status, report) {
|
|
|
1178
1241
|
trainedAt: report.routeFn.trainedAt,
|
|
1179
1242
|
updatedAt: report.routeFn.updatedAt,
|
|
1180
1243
|
usedAt: report.routeFn.usedAt,
|
|
1244
|
+
fallbackReason,
|
|
1181
1245
|
detail: report.routeFn.detail
|
|
1182
1246
|
};
|
|
1183
1247
|
}
|
|
@@ -1191,12 +1255,16 @@ function summarizeStatusRouteFn(status, report) {
|
|
|
1191
1255
|
else if (report.routeFn.updatedAt !== null) {
|
|
1192
1256
|
detail = `active route_fn was last updated at ${report.routeFn.updatedAt}, but no learned serve use is visible yet for the current pack`;
|
|
1193
1257
|
}
|
|
1258
|
+
if (fallbackReason !== null && !detail.includes(fallbackReason)) {
|
|
1259
|
+
detail = `${detail}; bounded_log_read=${fallbackReason}`;
|
|
1260
|
+
}
|
|
1194
1261
|
return {
|
|
1195
1262
|
available: true,
|
|
1196
1263
|
freshness,
|
|
1197
1264
|
trainedAt: report.routeFn.trainedAt,
|
|
1198
1265
|
updatedAt: report.routeFn.updatedAt,
|
|
1199
1266
|
usedAt: report.routeFn.usedAt,
|
|
1267
|
+
fallbackReason,
|
|
1200
1268
|
detail
|
|
1201
1269
|
};
|
|
1202
1270
|
}
|
|
@@ -4453,14 +4521,8 @@ function runUninstallCommand(parsed) {
|
|
|
4453
4521
|
return 0;
|
|
4454
4522
|
}
|
|
4455
4523
|
function resolveServeTimeLearningRuntimeInput(activationRoot) {
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
try {
|
|
4459
|
-
serveTimeDecisions = readLearningSpineLogEntries(activationRoot, "serveTimeRouteDecisions");
|
|
4460
|
-
}
|
|
4461
|
-
catch {
|
|
4462
|
-
fallbackReason = "serve_time_decision_log_read_failed";
|
|
4463
|
-
}
|
|
4524
|
+
const logPath = resolveLearningSpineLogPath(activationRoot, "serveTimeRouteDecisions");
|
|
4525
|
+
const { entries: serveTimeDecisions, fallbackReason } = readBoundedJsonlTail(logPath);
|
|
4464
4526
|
const decisionLogCount = serveTimeDecisions.length;
|
|
4465
4527
|
const pgVersion = decisionLogCount > 0 ? "v2" : "v1";
|
|
4466
4528
|
return {
|
|
@@ -4468,7 +4530,7 @@ function resolveServeTimeLearningRuntimeInput(activationRoot) {
|
|
|
4468
4530
|
serveTimeDecisions,
|
|
4469
4531
|
decisionLogCount,
|
|
4470
4532
|
baselineState: pgVersion === "v2" ? loadOrInitBaseline(activationRoot) : undefined,
|
|
4471
|
-
fallbackReason
|
|
4533
|
+
fallbackReason: fallbackReason === null ? null : `serve_time_decision_log_${fallbackReason}`
|
|
4472
4534
|
};
|
|
4473
4535
|
}
|
|
4474
4536
|
function resolveActivationInspectionPackId(inspection, slot) {
|
package/dist/src/index.js
CHANGED
|
@@ -7,9 +7,10 @@ import { compileRuntimeFromActivation } from "@openclawbrain/compiler";
|
|
|
7
7
|
import { CONTRACT_IDS, buildEventSemanticSurface, buildNormalizedEventExport, canonicalJson, checksumJsonPayload, createFeedbackEvent, createInteractionEvent, sortNormalizedEvents, validateKernelSurface, validateNormalizedEventExport } from "@openclawbrain/contracts";
|
|
8
8
|
import { classifyFeedbackSignalContent, describeNormalizedEventExportObservability } from "@openclawbrain/event-export";
|
|
9
9
|
import { DEFAULT_TEACHER_SUPERVISION_STALE_AFTER_MS, advanceAlwaysOnLearningRuntime, buildTeacherSupervisionArtifactsFromNormalizedEventExport, createAlwaysOnLearningRuntimeState, describeAlwaysOnLearningRuntimeState, materializeAlwaysOnLearningCandidatePack, materializeCandidatePackFromNormalizedEventExport } from "./local-learner.js";
|
|
10
|
-
import { LEARNING_SPINE_LOG_LAYOUT, activatePack, describeActivationObservability, describeActivationTarget, describePackCompileTarget, inspectActivationState, loadPackFromActivation, promoteCandidatePack,
|
|
10
|
+
import { LEARNING_SPINE_LOG_LAYOUT, activatePack, describeActivationObservability, describeActivationTarget, describePackCompileTarget, inspectActivationState, loadPackFromActivation, promoteCandidatePack, resolveLearningSpineLogPath, rollbackActivePack, stageCandidatePack } from "@openclawbrain/pack-format";
|
|
11
11
|
import { inspectOpenClawBrainHookStatus, summarizeOpenClawBrainHookLoad } from "./openclaw-hook-truth.js";
|
|
12
12
|
import { appendLearningUpdateLogs, appendServeTimeRouteDecisionLog } from "./learning-spine.js";
|
|
13
|
+
import { readBoundedJsonlTail } from "./bounded-jsonl-reader.js";
|
|
13
14
|
import { buildFeedbackSemanticMetadata, buildInteractionSemanticMetadata } from "./semantic-metadata.js";
|
|
14
15
|
export { clearOpenClawProfileRuntimeLoadProof, listOpenClawProfileRuntimeLoadProofs, recordOpenClawProfileRuntimeLoadProof, resolveAttachmentRuntimeLoadProofsPath } from "./attachment-truth.js";
|
|
15
16
|
import { createTeacherLabeler, summarizeTeacherLabelerOpportunity } from "./teacher-labeler.js";
|
|
@@ -6930,6 +6931,10 @@ function summarizeTeacherLoop(input) {
|
|
|
6930
6931
|
: "raw async teacher snapshot loaded"
|
|
6931
6932
|
};
|
|
6932
6933
|
}
|
|
6934
|
+
function readBoundedLearningSpineLogEntries(activationRoot, stream) {
|
|
6935
|
+
const logPath = resolveLearningSpineLogPath(activationRoot, stream);
|
|
6936
|
+
return readBoundedJsonlTail(logPath).entries;
|
|
6937
|
+
}
|
|
6933
6938
|
function matchesActiveRouteFnLog(input) {
|
|
6934
6939
|
if (input.activePackId !== null && input.entryPackId === input.activePackId) {
|
|
6935
6940
|
return true;
|
|
@@ -6970,8 +6975,8 @@ function summarizeRouteFnFreshness(input) {
|
|
|
6970
6975
|
: `active pack ${input.activePackId} does not require a learned route_fn`
|
|
6971
6976
|
};
|
|
6972
6977
|
}
|
|
6973
|
-
const updates =
|
|
6974
|
-
const decisions =
|
|
6978
|
+
const updates = readBoundedLearningSpineLogEntries(input.activationRoot, "pgRouteUpdates");
|
|
6979
|
+
const decisions = readBoundedLearningSpineLogEntries(input.activationRoot, "serveTimeRouteDecisions");
|
|
6975
6980
|
const updated = [...updates].reverse().find((entry) => matchesActiveRouteFnLog({
|
|
6976
6981
|
activePackId: input.activePackId,
|
|
6977
6982
|
routerChecksum: input.learnedRouting.routerChecksum,
|
|
@@ -7458,7 +7463,7 @@ function summarizeCurrentProfileLogRoot(activationRoot) {
|
|
|
7458
7463
|
return existsSync(logRoot) ? logRoot : null;
|
|
7459
7464
|
}
|
|
7460
7465
|
function summarizeCurrentProfileLastLearningUpdateAt(activationRoot, learning, teacherLoop) {
|
|
7461
|
-
const updates =
|
|
7466
|
+
const updates = readBoundedLearningSpineLogEntries(activationRoot, "pgRouteUpdates");
|
|
7462
7467
|
return updates.at(-1)?.recordedAt ?? teacherLoop.lastRunAt ?? learning.lastMaterializedAt ?? null;
|
|
7463
7468
|
}
|
|
7464
7469
|
function didCurrentProfileFirstExportOccur(report) {
|
|
@@ -284,7 +284,7 @@ function buildPersistedStatusSurfaceBridge(summary, context) {
|
|
|
284
284
|
brainRoot: context.brainRoot,
|
|
285
285
|
stateDbPath: context.dbPath,
|
|
286
286
|
persistedKey: TRACED_LEARNING_STATUS_SURFACE_STATE_KEY,
|
|
287
|
-
surfacedFrom: summary.source
|
|
287
|
+
surfacedFrom: summarizeBridgeSource(summary.source)
|
|
288
288
|
}
|
|
289
289
|
});
|
|
290
290
|
}
|
|
@@ -590,7 +590,7 @@ function buildRuntimeMaterializationMetadata(loaded) {
|
|
|
590
590
|
lastInterruptionSummary: loaded.bridge.lastInterruptionSummary,
|
|
591
591
|
fallbackReason: loaded.bridge.fallbackReason,
|
|
592
592
|
routerNoOpReason: loaded.bridge.routerNoOpReason,
|
|
593
|
-
source: loaded.bridge.source
|
|
593
|
+
source: summarizeBridgeSource(loaded.bridge.source)
|
|
594
594
|
};
|
|
595
595
|
}
|
|
596
596
|
function mergeCanonicalStatusBridge(canonicalBridge, runtimeLoaded) {
|
package/package.json
CHANGED