@openclawbrain/cli 0.4.10 → 0.4.12
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 +18 -17
- package/dist/src/cli.js +53 -10
- package/dist/src/daemon.d.ts +6 -1
- package/dist/src/daemon.js +229 -41
- package/dist/src/index.js +5 -2
- package/dist/src/local-learner.d.ts +4 -0
- package/dist/src/local-learner.js +212 -5
- package/dist/src/proof-command.js +654 -0
- package/dist/src/status-learning-path.js +28 -0
- package/package.json +1 -2
package/dist/src/daemon.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Commands:
|
|
9
9
|
* daemon start — generate and load a launchd plist
|
|
10
10
|
* daemon stop — unload the plist
|
|
11
|
-
* daemon status — show running/stopped + PID + last log lines
|
|
11
|
+
* daemon status — show running/stopped + PID + launch command + last log lines
|
|
12
12
|
* daemon logs — tail the daemon log file
|
|
13
13
|
*/
|
|
14
14
|
import { execSync } from "node:child_process";
|
|
@@ -22,6 +22,8 @@ const LOG_ROOT_DIRNAME = "daemon";
|
|
|
22
22
|
const DEFAULT_SCAN_ROOT_DIRNAME = "event-exports";
|
|
23
23
|
const BASELINE_STATE_BASENAME = "baseline-state.json";
|
|
24
24
|
const SCANNER_CHECKPOINT_BASENAME = ".openclawbrain-scanner-checkpoint.json";
|
|
25
|
+
const CLI_PACKAGE_NAME = "@openclawbrain/cli";
|
|
26
|
+
const CLI_BIN_NAME = "openclawbrain";
|
|
25
27
|
const DEFAULT_DAEMON_COMMAND_RUNNER = (command) => execSync(command, {
|
|
26
28
|
encoding: "utf8",
|
|
27
29
|
stdio: "pipe",
|
|
@@ -62,15 +64,81 @@ export function buildDaemonServiceIdentity(activationRoot) {
|
|
|
62
64
|
export function setDaemonCommandRunnerForTesting(runner) {
|
|
63
65
|
daemonCommandRunner = runner ?? DEFAULT_DAEMON_COMMAND_RUNNER;
|
|
64
66
|
}
|
|
65
|
-
function
|
|
67
|
+
function readPackageMetadata(packageRoot) {
|
|
68
|
+
if (packageRoot === null) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
72
|
+
if (!existsSync(packageJsonPath)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
66
75
|
try {
|
|
67
|
-
const
|
|
68
|
-
|
|
76
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
77
|
+
const name = typeof packageJson.name === "string" && packageJson.name.trim().length > 0
|
|
78
|
+
? packageJson.name.trim()
|
|
79
|
+
: null;
|
|
80
|
+
const version = typeof packageJson.version === "string" && packageJson.version.trim().length > 0
|
|
81
|
+
? packageJson.version.trim()
|
|
82
|
+
: null;
|
|
83
|
+
if (name === null) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return { name, version };
|
|
69
87
|
}
|
|
70
88
|
catch {
|
|
71
89
|
return null;
|
|
72
90
|
}
|
|
73
91
|
}
|
|
92
|
+
function isCliScriptPath(filePath) {
|
|
93
|
+
const basename = path.basename(filePath);
|
|
94
|
+
return basename === "cli.js" || basename === "cli.cjs" || basename === "cli.mjs";
|
|
95
|
+
}
|
|
96
|
+
function isNodeExecutablePath(filePath) {
|
|
97
|
+
return /^node(?:\.exe)?$/i.test(path.basename(filePath));
|
|
98
|
+
}
|
|
99
|
+
function isNpxCachePath(filePath) {
|
|
100
|
+
const resolvedPath = safeRealpath(path.resolve(filePath));
|
|
101
|
+
return resolvedPath.split(path.sep).includes("_npx");
|
|
102
|
+
}
|
|
103
|
+
function formatCommandArgument(value) {
|
|
104
|
+
return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value) ? value : JSON.stringify(value);
|
|
105
|
+
}
|
|
106
|
+
function formatCommand(programArguments) {
|
|
107
|
+
return programArguments.map((argument) => formatCommandArgument(argument)).join(" ");
|
|
108
|
+
}
|
|
109
|
+
function escapePlistString(value) {
|
|
110
|
+
return value
|
|
111
|
+
.replace(/&/g, "&")
|
|
112
|
+
.replace(/</g, "<")
|
|
113
|
+
.replace(/>/g, ">")
|
|
114
|
+
.replace(/"/g, """)
|
|
115
|
+
.replace(/'/g, "'");
|
|
116
|
+
}
|
|
117
|
+
function unescapePlistString(value) {
|
|
118
|
+
return value
|
|
119
|
+
.replace(/'/g, "'")
|
|
120
|
+
.replace(/"/g, "\"")
|
|
121
|
+
.replace(/>/g, ">")
|
|
122
|
+
.replace(/</g, "<")
|
|
123
|
+
.replace(/&/g, "&");
|
|
124
|
+
}
|
|
125
|
+
function getCommandPaths(commandName) {
|
|
126
|
+
try {
|
|
127
|
+
return daemonCommandRunner(`which -a ${commandName}`)
|
|
128
|
+
.split("\n")
|
|
129
|
+
.map((entry) => entry.trim())
|
|
130
|
+
.filter((entry) => entry.length > 0);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
try {
|
|
134
|
+
const resolved = daemonCommandRunner(`command -v ${commandName}`).trim();
|
|
135
|
+
return resolved.length > 0 ? [resolved] : [];
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
74
142
|
function safeRealpath(filePath) {
|
|
75
143
|
try {
|
|
76
144
|
return realpathSync(filePath);
|
|
@@ -110,62 +178,129 @@ function resolveCliScriptCandidate(candidatePath) {
|
|
|
110
178
|
return null;
|
|
111
179
|
}
|
|
112
180
|
const resolvedCandidate = safeRealpath(absoluteCandidate);
|
|
113
|
-
|
|
114
|
-
if (basename !== "cli.js" && basename !== "cli.cjs" && basename !== "cli.mjs") {
|
|
181
|
+
if (!isCliScriptPath(resolvedCandidate)) {
|
|
115
182
|
return null;
|
|
116
183
|
}
|
|
117
184
|
return resolvedCandidate;
|
|
118
185
|
}
|
|
119
|
-
function
|
|
186
|
+
function resolveCliPackageRoot(startDir) {
|
|
187
|
+
const packageRoot = resolvePackageRoot(startDir);
|
|
188
|
+
const packageMetadata = readPackageMetadata(packageRoot);
|
|
189
|
+
if (packageMetadata?.name === CLI_PACKAGE_NAME) {
|
|
190
|
+
return packageRoot;
|
|
191
|
+
}
|
|
192
|
+
if (packageRoot !== null) {
|
|
193
|
+
const siblingCliRoot = path.join(path.dirname(packageRoot), "cli");
|
|
194
|
+
const siblingMetadata = readPackageMetadata(siblingCliRoot);
|
|
195
|
+
if (siblingMetadata?.name === CLI_PACKAGE_NAME) {
|
|
196
|
+
return siblingCliRoot;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
function resolveDaemonPackageManagerLaunchSpec(moduleDir) {
|
|
202
|
+
const cliPackageRoot = resolveCliPackageRoot(moduleDir);
|
|
203
|
+
const cliPackageMetadata = readPackageMetadata(cliPackageRoot);
|
|
204
|
+
if (cliPackageMetadata === null) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
const npmPath = getCommandPaths("npm").find((candidate) => !isNpxCachePath(candidate)) ?? null;
|
|
208
|
+
if (npmPath === null) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
const packageSpec = cliPackageMetadata.version === null
|
|
212
|
+
? CLI_PACKAGE_NAME
|
|
213
|
+
: `${CLI_PACKAGE_NAME}@${cliPackageMetadata.version}`;
|
|
214
|
+
return {
|
|
215
|
+
programArguments: [npmPath, "exec", "--yes", `--package=${packageSpec}`, "--", CLI_BIN_NAME],
|
|
216
|
+
runtimePath: npmPath,
|
|
217
|
+
runtimePackageSpec: packageSpec,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function getOpenclawbrainCliScriptPathCandidates() {
|
|
120
221
|
const moduleFilePath = fileURLToPath(import.meta.url);
|
|
121
222
|
const moduleDir = path.dirname(moduleFilePath);
|
|
122
223
|
const packageRoot = resolvePackageRoot(moduleDir);
|
|
123
|
-
|
|
224
|
+
return [
|
|
124
225
|
process.argv[1],
|
|
125
226
|
path.join(moduleDir, "cli.js"),
|
|
126
227
|
packageRoot === null ? null : path.join(packageRoot, "dist", "src", "cli.js")
|
|
127
228
|
];
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
229
|
+
}
|
|
230
|
+
function buildDaemonLaunchProgramArguments(serviceIdentity, programArguments) {
|
|
231
|
+
return [...programArguments, "watch", "--activation-root", serviceIdentity.requestedActivationRoot];
|
|
232
|
+
}
|
|
233
|
+
function describeDaemonProgramArguments(programArguments) {
|
|
234
|
+
if (programArguments === null || programArguments.length === 0) {
|
|
235
|
+
return {
|
|
236
|
+
configuredProgramArguments: null,
|
|
237
|
+
configuredCommand: null,
|
|
238
|
+
configuredRuntimePath: null,
|
|
239
|
+
configuredRuntimePackageSpec: null,
|
|
240
|
+
configuredRuntimeLooksEphemeral: null
|
|
241
|
+
};
|
|
133
242
|
}
|
|
134
|
-
|
|
243
|
+
const runtimePath = programArguments.length >= 2 && isNodeExecutablePath(programArguments[0]) && isCliScriptPath(programArguments[1])
|
|
244
|
+
? programArguments[1]
|
|
245
|
+
: programArguments[0];
|
|
246
|
+
const runtimePackageSpec = programArguments.find((argument) => argument.startsWith("--package="))?.slice("--package=".length) ?? null;
|
|
247
|
+
return {
|
|
248
|
+
configuredProgramArguments: programArguments,
|
|
249
|
+
configuredCommand: formatCommand(programArguments),
|
|
250
|
+
configuredRuntimePath: runtimePath,
|
|
251
|
+
configuredRuntimePackageSpec: runtimePackageSpec,
|
|
252
|
+
configuredRuntimeLooksEphemeral: runtimePath === null ? null : isNpxCachePath(runtimePath)
|
|
253
|
+
};
|
|
135
254
|
}
|
|
136
255
|
function resolveDaemonProgramArguments() {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
256
|
+
for (const candidate of getOpenclawbrainCliScriptPathCandidates()) {
|
|
257
|
+
const cliScriptPath = resolveCliScriptCandidate(candidate);
|
|
258
|
+
if (cliScriptPath !== null && !isNpxCachePath(cliScriptPath)) {
|
|
259
|
+
return {
|
|
260
|
+
programArguments: [process.execPath, cliScriptPath],
|
|
261
|
+
runtimePath: cliScriptPath,
|
|
262
|
+
runtimePackageSpec: null,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
140
265
|
}
|
|
141
|
-
const
|
|
142
|
-
if (
|
|
143
|
-
return
|
|
266
|
+
const durableBinPath = getCommandPaths(CLI_BIN_NAME).find((candidate) => !isNpxCachePath(candidate)) ?? null;
|
|
267
|
+
if (durableBinPath !== null) {
|
|
268
|
+
return {
|
|
269
|
+
programArguments: [durableBinPath],
|
|
270
|
+
runtimePath: durableBinPath,
|
|
271
|
+
runtimePackageSpec: null,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
const moduleFilePath = fileURLToPath(import.meta.url);
|
|
275
|
+
const moduleDir = path.dirname(moduleFilePath);
|
|
276
|
+
const packageManagerLaunchSpec = resolveDaemonPackageManagerLaunchSpec(moduleDir);
|
|
277
|
+
if (packageManagerLaunchSpec !== null) {
|
|
278
|
+
return packageManagerLaunchSpec;
|
|
144
279
|
}
|
|
145
280
|
return null;
|
|
146
281
|
}
|
|
147
282
|
function buildPlistXml(serviceIdentity, programArguments) {
|
|
148
283
|
const logPath = serviceIdentity.logPath;
|
|
149
284
|
const homeDir = getHomeDir();
|
|
150
|
-
const daemonProgramArguments =
|
|
151
|
-
.map((argument) => ` <string>${argument}</string>`)
|
|
285
|
+
const daemonProgramArguments = buildDaemonLaunchProgramArguments(serviceIdentity, programArguments)
|
|
286
|
+
.map((argument) => ` <string>${escapePlistString(argument)}</string>`)
|
|
152
287
|
.join("\n");
|
|
153
288
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
154
289
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
155
290
|
<plist version="1.0">
|
|
156
291
|
<dict>
|
|
157
292
|
<key>Label</key>
|
|
158
|
-
<string>${serviceIdentity.label}</string>
|
|
293
|
+
<string>${escapePlistString(serviceIdentity.label)}</string>
|
|
159
294
|
<key>ProgramArguments</key>
|
|
160
295
|
<array>
|
|
161
296
|
${daemonProgramArguments}
|
|
162
297
|
</array>
|
|
163
298
|
<key>WorkingDirectory</key>
|
|
164
|
-
<string>${serviceIdentity.requestedActivationRoot}</string>
|
|
299
|
+
<string>${escapePlistString(serviceIdentity.requestedActivationRoot)}</string>
|
|
165
300
|
<key>StandardOutPath</key>
|
|
166
|
-
<string>${logPath}</string>
|
|
301
|
+
<string>${escapePlistString(logPath)}</string>
|
|
167
302
|
<key>StandardErrorPath</key>
|
|
168
|
-
<string>${logPath}</string>
|
|
303
|
+
<string>${escapePlistString(logPath)}</string>
|
|
169
304
|
<key>KeepAlive</key>
|
|
170
305
|
<true/>
|
|
171
306
|
<key>RunAtLoad</key>
|
|
@@ -173,9 +308,9 @@ ${daemonProgramArguments}
|
|
|
173
308
|
<key>EnvironmentVariables</key>
|
|
174
309
|
<dict>
|
|
175
310
|
<key>HOME</key>
|
|
176
|
-
<string>${homeDir}</string>
|
|
311
|
+
<string>${escapePlistString(homeDir)}</string>
|
|
177
312
|
<key>PATH</key>
|
|
178
|
-
<string
|
|
313
|
+
<string>${escapePlistString("/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin")}</string>
|
|
179
314
|
</dict>
|
|
180
315
|
</dict>
|
|
181
316
|
</plist>
|
|
@@ -235,6 +370,7 @@ function getLaunchctlInfo(label) {
|
|
|
235
370
|
function inspectManagedLearnerServiceInternal(activationRoot) {
|
|
236
371
|
const serviceIdentity = buildDaemonServiceIdentity(activationRoot);
|
|
237
372
|
const configuredActivationRoot = readDaemonActivationRoot(serviceIdentity.plistPath);
|
|
373
|
+
const configuredProgramArguments = readDaemonProgramArguments(serviceIdentity.plistPath);
|
|
238
374
|
const info = getLaunchctlInfo(serviceIdentity.label);
|
|
239
375
|
return {
|
|
240
376
|
requestedActivationRoot: serviceIdentity.requestedActivationRoot,
|
|
@@ -246,6 +382,7 @@ function inspectManagedLearnerServiceInternal(activationRoot) {
|
|
|
246
382
|
running: info.running,
|
|
247
383
|
pid: info.pid,
|
|
248
384
|
configuredActivationRoot,
|
|
385
|
+
...describeDaemonProgramArguments(configuredProgramArguments),
|
|
249
386
|
matchesRequestedActivationRoot: configuredActivationRoot === null
|
|
250
387
|
? null
|
|
251
388
|
: canonicalizeActivationRoot(configuredActivationRoot) === serviceIdentity.canonicalActivationRoot,
|
|
@@ -262,11 +399,11 @@ function startManagedLearnerService(activationRoot) {
|
|
|
262
399
|
inspection: inspectionBeforeStart
|
|
263
400
|
};
|
|
264
401
|
}
|
|
265
|
-
const
|
|
266
|
-
if (
|
|
402
|
+
const launchSpec = resolveDaemonProgramArguments();
|
|
403
|
+
if (launchSpec === null) {
|
|
267
404
|
return {
|
|
268
405
|
ok: false,
|
|
269
|
-
message: "Failed to resolve an OpenClawBrain CLI launch command.",
|
|
406
|
+
message: "Failed to resolve an OpenClawBrain CLI launch command without pinning an npx cache path.",
|
|
270
407
|
inspection: inspectionBeforeStart
|
|
271
408
|
};
|
|
272
409
|
}
|
|
@@ -275,7 +412,7 @@ function startManagedLearnerService(activationRoot) {
|
|
|
275
412
|
mkdirSync(launchAgentsDir, { recursive: true });
|
|
276
413
|
}
|
|
277
414
|
ensureLogDir(serviceIdentity.logPath);
|
|
278
|
-
const plistContent = buildPlistXml(serviceIdentity, programArguments);
|
|
415
|
+
const plistContent = buildPlistXml(serviceIdentity, launchSpec.programArguments);
|
|
279
416
|
writeFileSync(inspectionBeforeStart.plistPath, plistContent, "utf8");
|
|
280
417
|
const result = launchctlLoad(inspectionBeforeStart.plistPath);
|
|
281
418
|
if (!result.ok && !inspectionBeforeStart.installed) {
|
|
@@ -347,7 +484,7 @@ export function ensureManagedLearnerServiceForActivationRoot(activationRoot) {
|
|
|
347
484
|
}
|
|
348
485
|
const reason = !inspection.launchctlAvailable
|
|
349
486
|
? "launchctl_unavailable"
|
|
350
|
-
: startResult.message === "Failed to resolve an OpenClawBrain CLI launch command."
|
|
487
|
+
: startResult.message === "Failed to resolve an OpenClawBrain CLI launch command without pinning an npx cache path."
|
|
351
488
|
? "launch_command_unavailable"
|
|
352
489
|
: "launch_failed";
|
|
353
490
|
return {
|
|
@@ -419,19 +556,39 @@ function readOptionalJsonFile(filePath) {
|
|
|
419
556
|
return null;
|
|
420
557
|
}
|
|
421
558
|
}
|
|
422
|
-
function
|
|
559
|
+
function readDaemonProgramArguments(plistPath) {
|
|
423
560
|
if (!existsSync(plistPath)) {
|
|
424
561
|
return null;
|
|
425
562
|
}
|
|
426
563
|
try {
|
|
427
564
|
const plist = readFileSync(plistPath, "utf8");
|
|
428
|
-
const
|
|
429
|
-
|
|
565
|
+
const sectionMatch = plist.match(/<key>ProgramArguments<\/key>\s*<array>([\s\S]*?)<\/array>/);
|
|
566
|
+
if (sectionMatch === null) {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
const programArguments = [];
|
|
570
|
+
const stringPattern = /<string>([\s\S]*?)<\/string>/g;
|
|
571
|
+
let match = stringPattern.exec(sectionMatch[1]);
|
|
572
|
+
while (match !== null) {
|
|
573
|
+
programArguments.push(unescapePlistString(match[1]));
|
|
574
|
+
match = stringPattern.exec(sectionMatch[1]);
|
|
575
|
+
}
|
|
576
|
+
return programArguments.length > 0 ? programArguments : null;
|
|
430
577
|
}
|
|
431
578
|
catch {
|
|
432
579
|
return null;
|
|
433
580
|
}
|
|
434
581
|
}
|
|
582
|
+
function readDaemonActivationRoot(plistPath) {
|
|
583
|
+
const programArguments = readDaemonProgramArguments(plistPath);
|
|
584
|
+
if (programArguments !== null) {
|
|
585
|
+
const activationRootIndex = programArguments.indexOf("--activation-root");
|
|
586
|
+
if (activationRootIndex !== -1) {
|
|
587
|
+
return programArguments[activationRootIndex + 1] ?? null;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
435
592
|
function getWatchStatePaths(activationRoot) {
|
|
436
593
|
if (activationRoot === null) {
|
|
437
594
|
return {
|
|
@@ -611,9 +768,9 @@ export function daemonStart(activationRoot, json) {
|
|
|
611
768
|
const serviceIdentity = buildDaemonServiceIdentity(activationRoot);
|
|
612
769
|
const plistPath = serviceIdentity.plistPath;
|
|
613
770
|
const logPath = serviceIdentity.logPath;
|
|
614
|
-
const
|
|
615
|
-
if (
|
|
616
|
-
const message = "Failed to resolve an OpenClawBrain CLI launch command. Install/build
|
|
771
|
+
const launchSpec = resolveDaemonProgramArguments();
|
|
772
|
+
if (launchSpec === null) {
|
|
773
|
+
const message = "Failed to resolve an OpenClawBrain CLI launch command without pinning an npx cache path. Install/build @openclawbrain/cli or use a durable repo/runtime checkout.";
|
|
617
774
|
if (json) {
|
|
618
775
|
console.log(JSON.stringify({
|
|
619
776
|
command: "daemon start",
|
|
@@ -630,6 +787,8 @@ export function daemonStart(activationRoot, json) {
|
|
|
630
787
|
}
|
|
631
788
|
return 1;
|
|
632
789
|
}
|
|
790
|
+
const daemonProgramArguments = buildDaemonLaunchProgramArguments(serviceIdentity, launchSpec.programArguments);
|
|
791
|
+
const daemonLaunchDescription = describeDaemonProgramArguments(daemonProgramArguments);
|
|
633
792
|
// Ensure LaunchAgents dir exists
|
|
634
793
|
const launchAgentsDir = path.dirname(plistPath);
|
|
635
794
|
if (!existsSync(launchAgentsDir)) {
|
|
@@ -637,7 +796,7 @@ export function daemonStart(activationRoot, json) {
|
|
|
637
796
|
}
|
|
638
797
|
ensureLogDir(logPath);
|
|
639
798
|
// Write the plist
|
|
640
|
-
const plistContent = buildPlistXml(serviceIdentity, programArguments);
|
|
799
|
+
const plistContent = buildPlistXml(serviceIdentity, launchSpec.programArguments);
|
|
641
800
|
writeFileSync(plistPath, plistContent, "utf8");
|
|
642
801
|
// Load it
|
|
643
802
|
const result = launchctlLoad(plistPath);
|
|
@@ -649,6 +808,7 @@ export function daemonStart(activationRoot, json) {
|
|
|
649
808
|
logPath,
|
|
650
809
|
activationRoot: serviceIdentity.requestedActivationRoot,
|
|
651
810
|
serviceLabel: serviceIdentity.label,
|
|
811
|
+
...daemonLaunchDescription,
|
|
652
812
|
message: result.message,
|
|
653
813
|
}, null, 2));
|
|
654
814
|
}
|
|
@@ -659,6 +819,15 @@ export function daemonStart(activationRoot, json) {
|
|
|
659
819
|
console.log(` Plist: ${plistPath}`);
|
|
660
820
|
console.log(` Log: ${logPath}`);
|
|
661
821
|
console.log(` Root: ${serviceIdentity.requestedActivationRoot}`);
|
|
822
|
+
if (daemonLaunchDescription.configuredRuntimePath !== null) {
|
|
823
|
+
const runtimePackageSuffix = daemonLaunchDescription.configuredRuntimePackageSpec === null
|
|
824
|
+
? ""
|
|
825
|
+
: ` (${daemonLaunchDescription.configuredRuntimePackageSpec})`;
|
|
826
|
+
console.log(` Runtime: ${daemonLaunchDescription.configuredRuntimePath}${runtimePackageSuffix}`);
|
|
827
|
+
}
|
|
828
|
+
if (daemonLaunchDescription.configuredCommand !== null) {
|
|
829
|
+
console.log(` Command: ${daemonLaunchDescription.configuredCommand}`);
|
|
830
|
+
}
|
|
662
831
|
}
|
|
663
832
|
else {
|
|
664
833
|
console.error(`✗ ${result.message}`);
|
|
@@ -725,12 +894,14 @@ export function daemonStatus(activationRoot, json) {
|
|
|
725
894
|
const info = getLaunchctlInfo(serviceIdentity.label);
|
|
726
895
|
const lastLogLines = readLastLines(logPath, 5);
|
|
727
896
|
const configuredActivationRoot = readDaemonActivationRoot(plistPath);
|
|
897
|
+
const configuredProgramArguments = readDaemonProgramArguments(plistPath);
|
|
728
898
|
const requestedActivationRoot = serviceIdentity.requestedActivationRoot;
|
|
729
899
|
const watchStatePaths = getWatchStatePaths(requestedActivationRoot);
|
|
730
900
|
const watchState = readWatchStateSummary(requestedActivationRoot);
|
|
731
901
|
const matchesRequestedActivationRoot = configuredActivationRoot === null
|
|
732
902
|
? null
|
|
733
903
|
: canonicalizeActivationRoot(configuredActivationRoot) === serviceIdentity.canonicalActivationRoot;
|
|
904
|
+
const daemonLaunchDescription = describeDaemonProgramArguments(configuredProgramArguments);
|
|
734
905
|
if (json) {
|
|
735
906
|
console.log(JSON.stringify({
|
|
736
907
|
command: "daemon status",
|
|
@@ -742,6 +913,7 @@ export function daemonStatus(activationRoot, json) {
|
|
|
742
913
|
logPath,
|
|
743
914
|
activationRoot: requestedActivationRoot,
|
|
744
915
|
configuredActivationRoot,
|
|
916
|
+
...daemonLaunchDescription,
|
|
745
917
|
matchesRequestedActivationRoot,
|
|
746
918
|
...watchStatePaths,
|
|
747
919
|
watchState,
|
|
@@ -766,6 +938,22 @@ export function daemonStatus(activationRoot, json) {
|
|
|
766
938
|
if (matchesRequestedActivationRoot === false) {
|
|
767
939
|
console.log(" Requested root does not match the installed daemon plist.");
|
|
768
940
|
}
|
|
941
|
+
if (daemonLaunchDescription.configuredRuntimePath !== null) {
|
|
942
|
+
const runtimePackageSuffix = daemonLaunchDescription.configuredRuntimePackageSpec === null
|
|
943
|
+
? ""
|
|
944
|
+
: ` (${daemonLaunchDescription.configuredRuntimePackageSpec})`;
|
|
945
|
+
const runtimeWarning = daemonLaunchDescription.configuredRuntimeLooksEphemeral ? " [ephemeral]" : "";
|
|
946
|
+
console.log(` Runtime: ${daemonLaunchDescription.configuredRuntimePath}${runtimePackageSuffix}${runtimeWarning}`);
|
|
947
|
+
}
|
|
948
|
+
if (configuredProgramArguments !== null && configuredProgramArguments.length > 0) {
|
|
949
|
+
console.log(` Program: ${configuredProgramArguments[0]}`);
|
|
950
|
+
if (configuredProgramArguments.length > 1) {
|
|
951
|
+
console.log(` Args: ${configuredProgramArguments.slice(1).map((argument) => formatCommandArgument(argument)).join(" ")}`);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
if (daemonLaunchDescription.configuredCommand !== null) {
|
|
955
|
+
console.log(` Command: ${daemonLaunchDescription.configuredCommand}`);
|
|
956
|
+
}
|
|
769
957
|
console.log(` Log: ${logPath}`);
|
|
770
958
|
if (watchState.scanRoot !== null) {
|
|
771
959
|
console.log(` Scan root: ${watchState.scanRoot}`);
|
|
@@ -904,7 +1092,7 @@ export function daemonHelp() {
|
|
|
904
1092
|
"Subcommands:",
|
|
905
1093
|
" start Generate a macOS launchd plist and start the daemon (runs openclawbrain watch).",
|
|
906
1094
|
" stop Stop the daemon and remove the launchd plist.",
|
|
907
|
-
" status Show whether the daemon is running, its PID, and recent log lines.",
|
|
1095
|
+
" status Show whether the daemon is running, its PID, configured launch command, and recent log lines.",
|
|
908
1096
|
" logs Show the last 50 lines of the per-activation-root daemon log under ~/.openclawbrain/daemon/.",
|
|
909
1097
|
"",
|
|
910
1098
|
"Options:",
|
package/dist/src/index.js
CHANGED
|
@@ -887,7 +887,8 @@ export class AsyncTeacherLiveLoop {
|
|
|
887
887
|
...(this.input.cadence !== undefined ? { cadence: this.input.cadence } : {}),
|
|
888
888
|
...(learnedRoutingState.pgVersion !== undefined ? { pgVersion: learnedRoutingState.pgVersion } : {}),
|
|
889
889
|
...(learnedRoutingState.serveTimeDecisions !== undefined ? { serveTimeDecisions: learnedRoutingState.serveTimeDecisions } : {}),
|
|
890
|
-
...(learnedRoutingState.baselineState !== undefined ? { baselineState: learnedRoutingState.baselineState } : {})
|
|
890
|
+
...(learnedRoutingState.baselineState !== undefined ? { baselineState: learnedRoutingState.baselineState } : {}),
|
|
891
|
+
...(this.input.activationRoot !== undefined ? { activationRoot: this.input.activationRoot } : {})
|
|
891
892
|
});
|
|
892
893
|
this.learnerState = structuredClone(learnerResult.state);
|
|
893
894
|
this.lastMaterialization = cloneAlwaysOnLearningMaterializationJobOrNull(learnerResult.materialization);
|
|
@@ -996,6 +997,7 @@ export function scanLiveEventExport(input) {
|
|
|
996
997
|
learnedRouting: input.learnedRouting ?? true,
|
|
997
998
|
state: createAlwaysOnLearningRuntimeState(),
|
|
998
999
|
builtAt: normalizeIsoTimestamp(input.builtAt, "builtAt", observedAt),
|
|
1000
|
+
...(input.activationRoot !== undefined ? { activationRoot: input.activationRoot } : {}),
|
|
999
1001
|
...(input.liveSliceSize !== undefined ? { liveSliceSize: input.liveSliceSize } : {}),
|
|
1000
1002
|
...(input.backfillSliceSize !== undefined ? { backfillSliceSize: input.backfillSliceSize } : {})
|
|
1001
1003
|
});
|
|
@@ -4037,7 +4039,8 @@ export function runContinuousProductLoopTurn(input) {
|
|
|
4037
4039
|
...(input.sparseFeedback !== undefined ? { sparseFeedback: input.sparseFeedback } : {}),
|
|
4038
4040
|
...(input.liveSliceSize !== undefined ? { liveSliceSize: input.liveSliceSize } : {}),
|
|
4039
4041
|
...(input.backfillSliceSize !== undefined ? { backfillSliceSize: input.backfillSliceSize } : {}),
|
|
4040
|
-
...(input.cadence !== undefined ? { cadence: input.cadence } : {})
|
|
4042
|
+
...(input.cadence !== undefined ? { cadence: input.cadence } : {}),
|
|
4043
|
+
activationRoot
|
|
4041
4044
|
});
|
|
4042
4045
|
currentState.learner = structuredClone(learnerResult.state);
|
|
4043
4046
|
currentState.runtimePlasticity = learnerResult.state.runtimePlasticity === null ? null : structuredClone(learnerResult.state.runtimePlasticity);
|
|
@@ -29,6 +29,7 @@ export interface CandidatePackBuildInput {
|
|
|
29
29
|
serveTimeDecisions?: LearningSpineServeRouteDecisionLogEntryV1[];
|
|
30
30
|
/** Baseline state for V2 variance reduction. */
|
|
31
31
|
baselineState?: BaselineStateV1;
|
|
32
|
+
activationRoot?: string;
|
|
32
33
|
}
|
|
33
34
|
export interface CandidatePackFromNormalizedEventExportInput {
|
|
34
35
|
packLabel: string;
|
|
@@ -45,6 +46,7 @@ export interface CandidatePackFromNormalizedEventExportInput {
|
|
|
45
46
|
pgVersion?: "v1" | "v2";
|
|
46
47
|
serveTimeDecisions?: LearningSpineServeRouteDecisionLogEntryV1[];
|
|
47
48
|
baselineState?: BaselineStateV1;
|
|
49
|
+
activationRoot?: string;
|
|
48
50
|
}
|
|
49
51
|
export interface BuildTeacherSupervisionArtifactsInput {
|
|
50
52
|
normalizedEventExport: NormalizedEventExportV1;
|
|
@@ -66,6 +68,7 @@ interface CandidatePackBridgeInputBase {
|
|
|
66
68
|
pgVersion?: "v1" | "v2";
|
|
67
69
|
serveTimeDecisions?: LearningSpineServeRouteDecisionLogEntryV1[];
|
|
68
70
|
baselineState?: BaselineStateV1;
|
|
71
|
+
activationRoot?: string;
|
|
69
72
|
}
|
|
70
73
|
export interface CandidatePackFromNormalizedEventExportSliceInput extends CandidatePackBridgeInputBase {
|
|
71
74
|
normalizedEventExportSlice: NormalizedEventExportSliceV1;
|
|
@@ -325,6 +328,7 @@ export interface AdvanceAlwaysOnLearningRuntimeInput {
|
|
|
325
328
|
pgVersion?: "v1" | "v2";
|
|
326
329
|
serveTimeDecisions?: LearningSpineServeRouteDecisionLogEntryV1[];
|
|
327
330
|
baselineState?: BaselineStateV1;
|
|
331
|
+
activationRoot?: string;
|
|
328
332
|
}
|
|
329
333
|
export interface AlwaysOnLearningMaterializationJobV1 {
|
|
330
334
|
jobId: string;
|