@openclawbrain/cli 0.4.14 → 0.4.15

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.
@@ -18,6 +18,7 @@ import {
18
18
  } from "@openclawbrain/openclaw";
19
19
  import {
20
20
  createBeforePromptBuildHandler,
21
+ type ExtensionDiagnostic,
21
22
  isActivationRootPlaceholder,
22
23
  validateExtensionRegistrationApi
23
24
  } from "./runtime-guard.js";
@@ -52,7 +53,7 @@ async function appendLocalDiagnosticLog(message: string): Promise<void> {
52
53
  }
53
54
  }
54
55
 
55
- async function reportDiagnostic(input: { key: string; message: string; once?: boolean }): Promise<void> {
56
+ async function reportDiagnostic(input: ExtensionDiagnostic): Promise<void> {
56
57
  if (input.once) {
57
58
  if (warnedDiagnostics.has(input.key)) {
58
59
  return;
@@ -60,8 +61,30 @@ async function reportDiagnostic(input: { key: string; message: string; once?: bo
60
61
  warnedDiagnostics.add(input.key);
61
62
  }
62
63
 
63
- console.warn(input.message);
64
- await appendLocalDiagnosticLog(input.message);
64
+ const formatted = formatDiagnosticMessage(input);
65
+ console.warn(formatted);
66
+ await appendLocalDiagnosticLog(formatted);
67
+ }
68
+
69
+ function formatDiagnosticMessage(input: ExtensionDiagnostic): string {
70
+ if (
71
+ input.severity === undefined ||
72
+ input.actionability === undefined ||
73
+ input.summary === undefined ||
74
+ input.action === undefined
75
+ ) {
76
+ return input.message;
77
+ }
78
+
79
+ const detail = input.message.replace(/^\[openclawbrain\]\s*/, "");
80
+ return [
81
+ "[openclawbrain]",
82
+ `severity=${input.severity}`,
83
+ `actionability=${input.actionability}`,
84
+ `summary=${JSON.stringify(input.summary)}`,
85
+ `action=${JSON.stringify(input.action)}`,
86
+ `detail=${JSON.stringify(detail)}`
87
+ ].join(" ");
65
88
  }
66
89
 
67
90
  function announceStartupBreadcrumb(): void {
@@ -107,8 +130,12 @@ export default function register(api: unknown) {
107
130
  } catch (error) {
108
131
  const detail = error instanceof Error ? error.message : String(error);
109
132
  void reportDiagnostic({
110
- key: `runtime-load-proof:${detail}`,
133
+ key: "runtime-load-proof-failed",
111
134
  once: true,
135
+ severity: "degraded",
136
+ actionability: "inspect_local_proof_write",
137
+ summary: "runtime-load proof write failed after hook registration",
138
+ action: "Inspect local filesystem permissions and the activation-root proof path if proof capture is expected.",
112
139
  message: `[openclawbrain] runtime load proof failed: ${detail}`
113
140
  });
114
141
  }
@@ -119,6 +146,10 @@ export default function register(api: unknown) {
119
146
  void reportDiagnostic({
120
147
  key: "registration-failed",
121
148
  once: true,
149
+ severity: "blocking",
150
+ actionability: "rerun_install",
151
+ summary: "extension registration threw before the runtime hook was fully attached",
152
+ action: "Rerun openclawbrain install --openclaw-home <path>; if it still fails, inspect the extension loader/runtime.",
122
153
  message: `[openclawbrain] extension registration failed: ${detail}`
123
154
  });
124
155
  }
@@ -27,10 +27,23 @@ export type ExtensionCompileResult = ExtensionCompileSuccess | ExtensionCompileF
27
27
 
28
28
  export type ExtensionCompileRuntimeContext = (input: ExtensionCompileInput) => ExtensionCompileResult;
29
29
 
30
+ export type ExtensionDiagnosticSeverity = "degraded" | "blocking";
31
+
32
+ export type ExtensionDiagnosticActionability =
33
+ | "inspect_host_event_shape"
34
+ | "inspect_host_registration_api"
35
+ | "inspect_local_proof_write"
36
+ | "inspect_runtime_compile"
37
+ | "rerun_install";
38
+
30
39
  export interface ExtensionDiagnostic {
31
40
  key: string;
32
41
  message: string;
33
42
  once?: boolean;
43
+ severity?: ExtensionDiagnosticSeverity;
44
+ actionability?: ExtensionDiagnosticActionability;
45
+ summary?: string;
46
+ action?: string;
34
47
  }
35
48
 
36
49
  export interface ExtensionRegistrationApi {
@@ -53,13 +66,13 @@ export function validateExtensionRegistrationApi(api: unknown): { ok: true; api:
53
66
  if (!isRecord(api) || typeof api.on !== "function") {
54
67
  return {
55
68
  ok: false,
56
- diagnostic: {
69
+ diagnostic: shapeDiagnostic({
57
70
  key: "registration-api-invalid",
58
71
  once: true,
59
72
  message:
60
73
  `[openclawbrain] extension inactive: host registration API is missing api.on(event, handler, options) ` +
61
74
  `(received=${describeValue(api)})`
62
- }
75
+ })
63
76
  };
64
77
  }
65
78
 
@@ -145,12 +158,12 @@ export function createBeforePromptBuildHandler(input: {
145
158
  }): (event: unknown, ctx: unknown) => Promise<Record<string, unknown>> {
146
159
  return async (event: unknown, _ctx: unknown) => {
147
160
  if (isActivationRootPlaceholder(input.activationRoot)) {
148
- await input.reportDiagnostic({
161
+ await input.reportDiagnostic(shapeDiagnostic({
149
162
  key: "activation-root-placeholder",
150
163
  once: true,
151
164
  message:
152
165
  "[openclawbrain] BRAIN NOT YET LOADED: ACTIVATION_ROOT is still a placeholder. Install @openclawbrain/cli, then run: openclawbrain install --openclaw-home <path>"
153
- });
166
+ }));
154
167
  return {};
155
168
  }
156
169
 
@@ -190,12 +203,12 @@ export function createBeforePromptBuildHandler(input: {
190
203
 
191
204
  if (!result.ok) {
192
205
  const mode = result.hardRequirementViolated ? "hard-fail" : "fail-open";
193
- await input.reportDiagnostic({
206
+ await input.reportDiagnostic(shapeDiagnostic({
194
207
  key: `compile-${mode}`,
195
208
  message:
196
209
  `[openclawbrain] ${mode}: ${result.error} ` +
197
210
  `(activationRoot=${input.activationRoot}, sessionId=${normalized.event.sessionId ?? "unknown"}, channel=${normalized.event.channel ?? "unknown"})`
198
- });
211
+ }));
199
212
  return {};
200
213
  }
201
214
 
@@ -207,12 +220,12 @@ export function createBeforePromptBuildHandler(input: {
207
220
  }
208
221
  } catch (error) {
209
222
  const detail = error instanceof Error ? error.stack ?? error.message : String(error);
210
- await input.reportDiagnostic({
223
+ await input.reportDiagnostic(shapeDiagnostic({
211
224
  key: "compile-threw",
212
225
  message:
213
226
  `[openclawbrain] compile threw: ${detail} ` +
214
227
  `(activationRoot=${input.activationRoot}, sessionId=${normalized.event.sessionId ?? "unknown"}, channel=${normalized.event.channel ?? "unknown"})`
215
- });
228
+ }));
216
229
  }
217
230
 
218
231
  return {};
@@ -220,10 +233,10 @@ export function createBeforePromptBuildHandler(input: {
220
233
  }
221
234
 
222
235
  function failOpenDiagnostic(key: string, reason: string, detail: string): ExtensionDiagnostic {
223
- return {
236
+ return shapeDiagnostic({
224
237
  key,
225
238
  message: `[openclawbrain] fail-open: ${reason} (${detail})`
226
- };
239
+ });
227
240
  }
228
241
 
229
242
  function normalizeOptionalScalarField(
@@ -244,12 +257,12 @@ function normalizeOptionalScalarField(
244
257
  return String(value);
245
258
  }
246
259
 
247
- warnings.push({
260
+ warnings.push(shapeDiagnostic({
248
261
  key: `runtime-${fieldName}-ignored`,
249
262
  message:
250
263
  `[openclawbrain] fail-open: ignored unsupported before_prompt_build ${fieldName} ` +
251
264
  `(${fieldName}=${describeValue(value)})`
252
- });
265
+ }));
253
266
 
254
267
  return undefined;
255
268
  }
@@ -284,12 +297,12 @@ function normalizeOptionalNonNegativeIntegerField(
284
297
  }
285
298
  }
286
299
 
287
- warnings.push({
300
+ warnings.push(shapeDiagnostic({
288
301
  key: `runtime-${fieldName}-ignored`,
289
302
  message:
290
303
  `[openclawbrain] fail-open: ignored unsupported before_prompt_build ${fieldName} ` +
291
304
  `(${fieldName}=${describeValue(value)})`
292
- });
305
+ }));
293
306
 
294
307
  return undefined;
295
308
  }
@@ -402,6 +415,71 @@ function describeValue(value: unknown): string {
402
415
  return `${typeof value}(${String(value)})`;
403
416
  }
404
417
 
418
+ function shapeDiagnostic(diagnostic: ExtensionDiagnostic): ExtensionDiagnostic {
419
+ if (
420
+ diagnostic.severity !== undefined &&
421
+ diagnostic.actionability !== undefined &&
422
+ diagnostic.summary !== undefined &&
423
+ diagnostic.action !== undefined
424
+ ) {
425
+ return diagnostic;
426
+ }
427
+
428
+ if (diagnostic.key === "activation-root-placeholder") {
429
+ return {
430
+ ...diagnostic,
431
+ severity: "blocking",
432
+ actionability: "rerun_install",
433
+ summary: "extension hook is installed but ACTIVATION_ROOT is still unpinned",
434
+ action: "Run openclawbrain install --openclaw-home <path> to pin the runtime hook."
435
+ };
436
+ }
437
+
438
+ if (diagnostic.key === "registration-api-invalid") {
439
+ return {
440
+ ...diagnostic,
441
+ severity: "blocking",
442
+ actionability: "inspect_host_registration_api",
443
+ summary: "extension host registration API is missing or incompatible",
444
+ action: "Repair or upgrade the host extension API so api.on(event, handler, options) is available."
445
+ };
446
+ }
447
+
448
+ if (diagnostic.key === "compile-hard-fail") {
449
+ return {
450
+ ...diagnostic,
451
+ severity: "blocking",
452
+ actionability: "inspect_runtime_compile",
453
+ summary: "brain context compile hit a hard requirement",
454
+ action: "Inspect the activation root and compile error; rerun install if the pinned hook may be stale."
455
+ };
456
+ }
457
+
458
+ if (diagnostic.key === "compile-fail-open" || diagnostic.key === "compile-threw") {
459
+ return {
460
+ ...diagnostic,
461
+ severity: "degraded",
462
+ actionability: "inspect_runtime_compile",
463
+ summary: diagnostic.key === "compile-threw"
464
+ ? "brain context compile threw during before_prompt_build"
465
+ : "brain context compile failed open during before_prompt_build",
466
+ action: "Inspect the activation root and compile error if brain context is unexpectedly empty."
467
+ };
468
+ }
469
+
470
+ if (diagnostic.key.startsWith("runtime-")) {
471
+ return {
472
+ ...diagnostic,
473
+ severity: "degraded",
474
+ actionability: "inspect_host_event_shape",
475
+ summary: "before_prompt_build payload was partial or malformed",
476
+ action: "Inspect the host before_prompt_build event shape; OpenClawBrain fail-opened safely."
477
+ };
478
+ }
479
+
480
+ return diagnostic;
481
+ }
482
+
405
483
  function isRecord(value: unknown): value is Record<string, unknown> {
406
484
  return typeof value === "object" && value !== null;
407
485
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclawbrain/cli",
3
- "version": "0.4.14",
3
+ "version": "0.4.15",
4
4
  "description": "OpenClawBrain operator CLI package with install/status helpers, daemon controls, and import/export tooling.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",