openclaw-observability 2026.3.23 → 2026.3.24-1
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/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +320 -5
- package/dist/index.js.map +1 -1
- package/dist/security/chain-detector.d.ts +0 -9
- package/dist/security/chain-detector.d.ts.map +1 -1
- package/dist/security/chain-detector.js +7 -25
- package/dist/security/chain-detector.js.map +1 -1
- package/dist/security/rules.d.ts.map +1 -1
- package/dist/security/rules.js +30 -24
- package/dist/security/rules.js.map +1 -1
- package/dist/storage/duckdb-local-writer.d.ts +5 -1
- package/dist/storage/duckdb-local-writer.d.ts.map +1 -1
- package/dist/storage/duckdb-local-writer.js +443 -1
- package/dist/storage/duckdb-local-writer.js.map +1 -1
- package/dist/storage/mysql-writer.d.ts +3 -1
- package/dist/storage/mysql-writer.d.ts.map +1 -1
- package/dist/storage/mysql-writer.js +175 -0
- package/dist/storage/mysql-writer.js.map +1 -1
- package/dist/storage/schema.d.ts.map +1 -1
- package/dist/storage/schema.js +144 -0
- package/dist/storage/schema.js.map +1 -1
- package/dist/storage/structured-model.d.ts +68 -0
- package/dist/storage/structured-model.d.ts.map +1 -0
- package/dist/storage/structured-model.js +313 -0
- package/dist/storage/structured-model.js.map +1 -0
- package/dist/storage/writer.d.ts +23 -0
- package/dist/storage/writer.d.ts.map +1 -1
- package/dist/web/api.d.ts +66 -0
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +269 -0
- package/dist/web/api.js.map +1 -1
- package/dist/web/routes.d.ts +6 -2
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +402 -9
- package/dist/web/routes.js.map +1 -1
- package/dist/web/ui.js +1718 -216
- package/dist/web/ui.js.map +1 -1
- package/openclaw.plugin.json +36 -1
- package/package.json +5 -2
package/dist/config.d.ts
CHANGED
|
@@ -58,6 +58,14 @@ export interface UiConfig {
|
|
|
58
58
|
/** Optional access token: when set, requests must pass ?token=... or Authorization: Bearer ... */
|
|
59
59
|
accessToken?: string;
|
|
60
60
|
}
|
|
61
|
+
export interface MetricsConfig {
|
|
62
|
+
/** Enable OTLP push ingest endpoint */
|
|
63
|
+
enabled: boolean;
|
|
64
|
+
/** OTLP base path mounted on plugin route (the receiver listens on ${otlpPath}/v1/metrics) */
|
|
65
|
+
otlpPath: string;
|
|
66
|
+
/** Max payload size for OTLP metrics requests */
|
|
67
|
+
maxPayloadBytes: number;
|
|
68
|
+
}
|
|
61
69
|
/** Full plugin config */
|
|
62
70
|
export interface AuditPluginConfig {
|
|
63
71
|
/** Storage mode: local (embedded, zero config) | remote (external MySQL) */
|
|
@@ -68,6 +76,7 @@ export interface AuditPluginConfig {
|
|
|
68
76
|
redaction: RedactionConfig;
|
|
69
77
|
security: SecurityPluginConfig;
|
|
70
78
|
ui?: UiConfig;
|
|
79
|
+
metrics: MetricsConfig;
|
|
71
80
|
}
|
|
72
81
|
/** Default DuckDB path — use a 'data' subdirectory to avoid triggering
|
|
73
82
|
* OpenClaw's hot-reload file watcher on the top-level ~/.openclaw/ dir */
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,8BAA8B;AAC9B,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,0BAA0B;AAC1B,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,oBAAoB;AACpB,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,uBAAuB;AACvB,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,gCAAgC;AAChC,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,KAAK,CAAC;QACtB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC,CAAC;IACH,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,KAAK,EAAE;QACL,aAAa,EAAE,OAAO,CAAC;QACvB,WAAW,EAAE,OAAO,CAAC;QACrB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;QACzB,WAAW,EAAE,OAAO,CAAC;QACrB,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,sDAAsD;IACtD,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,sCAAsC;AACtC,MAAM,WAAW,QAAQ;IACvB,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,yBAAyB;AACzB,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,EAAE,CAAC,EAAE,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,8BAA8B;AAC9B,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,0BAA0B;AAC1B,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,oBAAoB;AACpB,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,uBAAuB;AACvB,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,gCAAgC;AAChC,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,KAAK,CAAC;QACtB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC,CAAC;IACH,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,KAAK,EAAE;QACL,aAAa,EAAE,OAAO,CAAC;QACvB,WAAW,EAAE,OAAO,CAAC;QACrB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;QACzB,WAAW,EAAE,OAAO,CAAC;QACrB,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,sDAAsD;IACtD,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,sCAAsC;AACtC,MAAM,WAAW,QAAQ;IACvB,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,8FAA8F;IAC9F,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,yBAAyB;AACzB,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,EAAE,CAAC,EAAE,QAAQ,CAAC;IACd,OAAO,EAAE,aAAa,CAAC;CACxB;AAED;2EAC2E;AAC3E,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AAEF,qBAAqB;AACrB,eAAO,MAAM,cAAc,EAAE,iBAsD5B,CAAC;AAeF,wBAAgB,aAAa,CAC3B,UAAU,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,SAAS,GACjD,iBAAiB,CA2CnB"}
|
package/dist/config.js
CHANGED
|
@@ -93,6 +93,11 @@ exports.DEFAULT_CONFIG = {
|
|
|
93
93
|
domainWhitelist: [],
|
|
94
94
|
},
|
|
95
95
|
ui: {},
|
|
96
|
+
metrics: {
|
|
97
|
+
enabled: true,
|
|
98
|
+
otlpPath: '/plugins/observability/api/otel',
|
|
99
|
+
maxPayloadBytes: 2 * 1024 * 1024,
|
|
100
|
+
},
|
|
96
101
|
};
|
|
97
102
|
/**
|
|
98
103
|
* Smart mode inference:
|
|
@@ -145,6 +150,10 @@ function resolveConfig(userConfig) {
|
|
|
145
150
|
...exports.DEFAULT_CONFIG.ui,
|
|
146
151
|
...userConfig.ui,
|
|
147
152
|
},
|
|
153
|
+
metrics: {
|
|
154
|
+
...exports.DEFAULT_CONFIG.metrics,
|
|
155
|
+
...userConfig.metrics,
|
|
156
|
+
},
|
|
148
157
|
};
|
|
149
158
|
}
|
|
150
159
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,11 @@ interface PluginAPI {
|
|
|
27
27
|
match?: 'exact' | 'prefix';
|
|
28
28
|
replaceExisting?: boolean;
|
|
29
29
|
}) => void;
|
|
30
|
+
registerGatewayMethod?: (method: string, handler: (ctx: {
|
|
31
|
+
params?: unknown;
|
|
32
|
+
respond: (ok: boolean, payload: unknown) => void;
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}) => void | Promise<void>) => void;
|
|
30
35
|
runtime?: {
|
|
31
36
|
events?: {
|
|
32
37
|
onAgentEvent?: (listener: (evt: {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,iBAAiB,EAAiB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,iBAAiB,EAAiB,MAAM,UAAU,CAAC;AAiC5D,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC1C,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;IACjE,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,WAAW,EAAE,eAAe,EAAE,GAAG,EAAE,OAAO,WAAW,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;QACzI,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;QAC3B,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;QAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;KAC3B,KAAK,IAAI,CAAC;IACX,qBAAqB,CAAC,EAAE,CACtB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;QACjD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KACvB,IAAI,CAAC;IACV,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE;YACP,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE;gBAC9B,KAAK,EAAE,MAAM,CAAC;gBACd,MAAM,EAAE,MAAM,CAAC;gBACf,EAAE,EAAE,MAAM,CAAC;gBACX,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;aACrB,KAAK,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;SACpC,CAAC;KACH,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAu5BD,iBAAS,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG;IAAE,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CA2vDrE;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,eAAe,QAAQ,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -55,6 +55,7 @@ const mysql_writer_1 = require("./storage/mysql-writer");
|
|
|
55
55
|
const duckdb_local_writer_1 = require("./storage/duckdb-local-writer");
|
|
56
56
|
const buffer_1 = require("./storage/buffer");
|
|
57
57
|
const routes_1 = require("./web/routes");
|
|
58
|
+
const api_1 = require("./web/api");
|
|
58
59
|
const scanner_1 = require("./security/scanner");
|
|
59
60
|
const fs = __importStar(require("node:fs"));
|
|
60
61
|
const path = __importStar(require("node:path"));
|
|
@@ -907,7 +908,8 @@ function activate(api) {
|
|
|
907
908
|
'Please configure via Dashboard (Settings → Plugins → openclaw-observability Config) then restart.');
|
|
908
909
|
}
|
|
909
910
|
}
|
|
910
|
-
//
|
|
911
|
+
// UI shell stays directly open; legacy HTTP APIs still accept gateway token
|
|
912
|
+
// for backward compatibility. WS data access is authenticated at gateway connect.
|
|
911
913
|
const openclawConfigPath = process.env.OPENCLAW_CONFIG_PATH ?? path.join(stateDir, 'openclaw.json');
|
|
912
914
|
let observabilityToken;
|
|
913
915
|
try {
|
|
@@ -919,7 +921,6 @@ function activate(api) {
|
|
|
919
921
|
t = parsed?.gateway?.auth?.token;
|
|
920
922
|
}
|
|
921
923
|
catch {
|
|
922
|
-
// Config may be JSON5 (comments, unescaped newlines in strings); extract token with regex
|
|
923
924
|
const m = raw.match(/"gateway"\s*:\s*\{[\s\S]*?"auth"\s*:\s*\{[\s\S]*?"token"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
924
925
|
t = m?.[1]?.replace(/\\(.)/g, '$1');
|
|
925
926
|
}
|
|
@@ -931,10 +932,10 @@ function activate(api) {
|
|
|
931
932
|
console.warn('[openclaw-observability] Failed to read gateway token from config:', e.message);
|
|
932
933
|
}
|
|
933
934
|
if (observabilityToken) {
|
|
934
|
-
console.log('[openclaw-observability] Observability
|
|
935
|
+
console.log('[openclaw-observability] Observability HTTP API auth: token compatibility enabled (from ' + openclawConfigPath + ')');
|
|
935
936
|
}
|
|
936
937
|
else {
|
|
937
|
-
console.log('[openclaw-observability] Observability
|
|
938
|
+
console.log('[openclaw-observability] Observability HTTP API auth: no token configured');
|
|
938
939
|
}
|
|
939
940
|
// Always register HTTP routes on the current api (the latest api serves HTTP)
|
|
940
941
|
if (typeof api.registerHttpRoute === 'function') {
|
|
@@ -949,11 +950,325 @@ function activate(api) {
|
|
|
949
950
|
`customRegexRules=${next.customRegexRules.length}`);
|
|
950
951
|
return next;
|
|
951
952
|
},
|
|
952
|
-
});
|
|
953
|
+
}, config.metrics);
|
|
953
954
|
}
|
|
954
955
|
else {
|
|
955
956
|
console.warn('[openclaw-observability] registerHttpRoute not available — observability UI disabled');
|
|
956
957
|
}
|
|
958
|
+
if (typeof api.registerGatewayMethod === 'function') {
|
|
959
|
+
const toInt = (value, defaultValue) => {
|
|
960
|
+
const n = Number(value);
|
|
961
|
+
if (!Number.isFinite(n))
|
|
962
|
+
return defaultValue;
|
|
963
|
+
const i = Math.floor(n);
|
|
964
|
+
return i > 0 ? i : defaultValue;
|
|
965
|
+
};
|
|
966
|
+
const toStringOrUndefined = (value) => {
|
|
967
|
+
if (typeof value !== 'string')
|
|
968
|
+
return undefined;
|
|
969
|
+
const trimmed = value.trim();
|
|
970
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
971
|
+
};
|
|
972
|
+
const toRecord = (value) => {
|
|
973
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
974
|
+
return value;
|
|
975
|
+
}
|
|
976
|
+
return {};
|
|
977
|
+
};
|
|
978
|
+
const toBool = (value, defaultValue = false) => {
|
|
979
|
+
if (typeof value === 'boolean')
|
|
980
|
+
return value;
|
|
981
|
+
if (typeof value === 'number')
|
|
982
|
+
return value !== 0;
|
|
983
|
+
if (typeof value === 'string') {
|
|
984
|
+
const v = value.trim().toLowerCase();
|
|
985
|
+
if (!v)
|
|
986
|
+
return defaultValue;
|
|
987
|
+
if (v === '1' || v === 'true' || v === 'yes' || v === 'y' || v === 'on')
|
|
988
|
+
return true;
|
|
989
|
+
if (v === '0' || v === 'false' || v === 'no' || v === 'n' || v === 'off')
|
|
990
|
+
return false;
|
|
991
|
+
}
|
|
992
|
+
return defaultValue;
|
|
993
|
+
};
|
|
994
|
+
const resolveLogPath = (sourceRaw) => {
|
|
995
|
+
const source = toStringOrUndefined(sourceRaw) || 'gateway';
|
|
996
|
+
const logsDir = path.join(os.homedir(), '.openclaw', 'logs');
|
|
997
|
+
if (source === 'gateway') {
|
|
998
|
+
return { source, path: path.join(logsDir, 'gateway.log') };
|
|
999
|
+
}
|
|
1000
|
+
if (source === 'gateway.err' || source === 'gateway_error') {
|
|
1001
|
+
return { source: 'gateway.err', path: path.join(logsDir, 'gateway.err.log') };
|
|
1002
|
+
}
|
|
1003
|
+
if (source === 'runtime') {
|
|
1004
|
+
const runtimeDir = path.join(os.tmpdir(), 'openclaw');
|
|
1005
|
+
try {
|
|
1006
|
+
const files = fs
|
|
1007
|
+
.readdirSync(runtimeDir)
|
|
1008
|
+
.filter((f) => /^openclaw-\d{4}-\d{2}-\d{2}\.log$/.test(f))
|
|
1009
|
+
.map((f) => ({
|
|
1010
|
+
file: f,
|
|
1011
|
+
mtime: fs.statSync(path.join(runtimeDir, f)).mtimeMs,
|
|
1012
|
+
}))
|
|
1013
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
1014
|
+
if (files.length > 0) {
|
|
1015
|
+
return { source, path: path.join(runtimeDir, files[0].file) };
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
catch {
|
|
1019
|
+
return { source, path: null };
|
|
1020
|
+
}
|
|
1021
|
+
return { source, path: null };
|
|
1022
|
+
}
|
|
1023
|
+
return { source, path: null };
|
|
1024
|
+
};
|
|
1025
|
+
const registerGateway = (method, handler) => {
|
|
1026
|
+
try {
|
|
1027
|
+
api.registerGatewayMethod(method, async ({ params, respond }) => {
|
|
1028
|
+
try {
|
|
1029
|
+
await writer.ensureReady();
|
|
1030
|
+
const pool = writer.getPool();
|
|
1031
|
+
if (!pool) {
|
|
1032
|
+
respond(false, { error: 'Database not ready — will retry automatically' });
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
await handler({ params, respond });
|
|
1036
|
+
}
|
|
1037
|
+
catch (e) {
|
|
1038
|
+
respond(false, {
|
|
1039
|
+
error: e?.message || `Failed to call ${method}`,
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
catch (e) {
|
|
1045
|
+
console.warn(`[openclaw-observability] Failed to register gateway method ${method}:`, e.message);
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
registerGateway('observability.stats', async ({ respond }) => {
|
|
1049
|
+
const pool = writer.getPool();
|
|
1050
|
+
const stats = await (0, api_1.getStats)(pool);
|
|
1051
|
+
respond(true, stats);
|
|
1052
|
+
});
|
|
1053
|
+
registerGateway('observability.sessions', async ({ params, respond }) => {
|
|
1054
|
+
const pool = writer.getPool();
|
|
1055
|
+
const p = toRecord(params);
|
|
1056
|
+
const result = await (0, api_1.getSessions)(pool, {
|
|
1057
|
+
page: toInt(p.page, 1),
|
|
1058
|
+
limit: toInt(p.limit, 20),
|
|
1059
|
+
userId: toStringOrUndefined(p.user_id),
|
|
1060
|
+
model: toStringOrUndefined(p.model),
|
|
1061
|
+
sessionId: toStringOrUndefined(p.sessionId),
|
|
1062
|
+
search: toStringOrUndefined(p.search),
|
|
1063
|
+
timeFrom: toStringOrUndefined(p.timeFrom),
|
|
1064
|
+
timeTo: toStringOrUndefined(p.timeTo),
|
|
1065
|
+
});
|
|
1066
|
+
respond(true, result);
|
|
1067
|
+
});
|
|
1068
|
+
registerGateway('observability.session.actions', async ({ params, respond }) => {
|
|
1069
|
+
const pool = writer.getPool();
|
|
1070
|
+
const p = toRecord(params);
|
|
1071
|
+
const sessionId = toStringOrUndefined(p.sessionId);
|
|
1072
|
+
if (!sessionId) {
|
|
1073
|
+
respond(false, { error: 'sessionId is required' });
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
const fields = toStringOrUndefined(p.fields);
|
|
1077
|
+
const limit = p.limit == null ? undefined : toInt(p.limit, 1000);
|
|
1078
|
+
const actions = await (0, api_1.getSessionActions)(pool, sessionId, { fields, limit });
|
|
1079
|
+
respond(true, { actions });
|
|
1080
|
+
});
|
|
1081
|
+
registerGateway('observability.session.tree', async ({ params, respond }) => {
|
|
1082
|
+
const pool = writer.getPool();
|
|
1083
|
+
const p = toRecord(params);
|
|
1084
|
+
const sessionId = toStringOrUndefined(p.sessionId);
|
|
1085
|
+
if (!sessionId) {
|
|
1086
|
+
respond(false, { error: 'sessionId is required' });
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
const tree = await (0, api_1.getTraceObservationTree)(pool, sessionId);
|
|
1090
|
+
respond(true, tree);
|
|
1091
|
+
});
|
|
1092
|
+
registerGateway('observability.session.graph', async ({ params, respond }) => {
|
|
1093
|
+
const pool = writer.getPool();
|
|
1094
|
+
const p = toRecord(params);
|
|
1095
|
+
const sessionId = toStringOrUndefined(p.sessionId);
|
|
1096
|
+
if (!sessionId) {
|
|
1097
|
+
respond(false, { error: 'sessionId is required' });
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
const graph = await (0, api_1.getTraceObservationTree)(pool, sessionId);
|
|
1101
|
+
respond(true, graph);
|
|
1102
|
+
});
|
|
1103
|
+
registerGateway('observability.actions', async ({ params, respond }) => {
|
|
1104
|
+
const pool = writer.getPool();
|
|
1105
|
+
const p = toRecord(params);
|
|
1106
|
+
const result = await (0, api_1.getActions)(pool, {
|
|
1107
|
+
page: toInt(p.page, 1),
|
|
1108
|
+
limit: toInt(p.limit, 50),
|
|
1109
|
+
actionType: toStringOrUndefined(p.action_type),
|
|
1110
|
+
sessionId: toStringOrUndefined(p.session_id),
|
|
1111
|
+
});
|
|
1112
|
+
respond(true, result);
|
|
1113
|
+
});
|
|
1114
|
+
registerGateway('observability.alerts.stats', async ({ respond }) => {
|
|
1115
|
+
const pool = writer.getPool();
|
|
1116
|
+
const stats = await (0, api_1.getAlertStats)(pool);
|
|
1117
|
+
respond(true, stats);
|
|
1118
|
+
});
|
|
1119
|
+
registerGateway('observability.alerts', async ({ params, respond }) => {
|
|
1120
|
+
const pool = writer.getPool();
|
|
1121
|
+
const p = toRecord(params);
|
|
1122
|
+
const result = await (0, api_1.getAlerts)(pool, {
|
|
1123
|
+
page: toInt(p.page, 1),
|
|
1124
|
+
limit: toInt(p.limit, 20),
|
|
1125
|
+
severity: toStringOrUndefined(p.severity),
|
|
1126
|
+
category: toStringOrUndefined(p.category),
|
|
1127
|
+
status: toStringOrUndefined(p.status),
|
|
1128
|
+
sessionId: toStringOrUndefined(p.session_id),
|
|
1129
|
+
ruleId: toStringOrUndefined(p.rule_id),
|
|
1130
|
+
search: toStringOrUndefined(p.search),
|
|
1131
|
+
timeFrom: toStringOrUndefined(p.timeFrom),
|
|
1132
|
+
timeTo: toStringOrUndefined(p.timeTo),
|
|
1133
|
+
});
|
|
1134
|
+
respond(true, result);
|
|
1135
|
+
});
|
|
1136
|
+
registerGateway('observability.alerts.updateStatus', async ({ params, respond }) => {
|
|
1137
|
+
const pool = writer.getPool();
|
|
1138
|
+
const p = toRecord(params);
|
|
1139
|
+
const alertId = toStringOrUndefined(p.alertId);
|
|
1140
|
+
const status = toStringOrUndefined(p.status);
|
|
1141
|
+
const resolvedBy = toStringOrUndefined(p.resolvedBy);
|
|
1142
|
+
if (!alertId || !status) {
|
|
1143
|
+
respond(false, { error: 'alertId and status are required' });
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
const ok = await (0, api_1.updateAlertStatus)(pool, alertId, status, resolvedBy);
|
|
1147
|
+
respond(true, { success: ok });
|
|
1148
|
+
});
|
|
1149
|
+
registerGateway('observability.session.alerts', async ({ params, respond }) => {
|
|
1150
|
+
const pool = writer.getPool();
|
|
1151
|
+
const p = toRecord(params);
|
|
1152
|
+
const sessionId = toStringOrUndefined(p.sessionId);
|
|
1153
|
+
if (!sessionId) {
|
|
1154
|
+
respond(false, { error: 'sessionId is required' });
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
const alerts = await (0, api_1.getSessionAlerts)(pool, sessionId);
|
|
1158
|
+
respond(true, { alerts });
|
|
1159
|
+
});
|
|
1160
|
+
registerGateway('observability.analytics', async ({ params, respond }) => {
|
|
1161
|
+
const pool = writer.getPool();
|
|
1162
|
+
const p = toRecord(params);
|
|
1163
|
+
const result = await (0, api_1.getAnalytics)(pool, {
|
|
1164
|
+
timeFrom: toStringOrUndefined(p.timeFrom),
|
|
1165
|
+
timeTo: toStringOrUndefined(p.timeTo),
|
|
1166
|
+
});
|
|
1167
|
+
respond(true, result);
|
|
1168
|
+
});
|
|
1169
|
+
registerGateway('observability.security.config.get', async ({ respond }) => {
|
|
1170
|
+
respond(true, { config: securityScanner.getConfig() });
|
|
1171
|
+
});
|
|
1172
|
+
registerGateway('observability.security.config.update', async ({ params, respond }) => {
|
|
1173
|
+
const p = toRecord(params);
|
|
1174
|
+
const next = securityScanner.updateConfig(p);
|
|
1175
|
+
persistSecurityConfig(next);
|
|
1176
|
+
respond(true, { config: next });
|
|
1177
|
+
});
|
|
1178
|
+
registerGateway('observability.metrics.overview', async ({ params, respond }) => {
|
|
1179
|
+
const pool = writer.getPool();
|
|
1180
|
+
if (!(config.metrics.enabled && typeof writer.writeMetricSamples === 'function')) {
|
|
1181
|
+
respond(true, {
|
|
1182
|
+
enabled: false,
|
|
1183
|
+
otlpPath: config.metrics.otlpPath,
|
|
1184
|
+
totalMetrics: 0,
|
|
1185
|
+
totalSamples: 0,
|
|
1186
|
+
items: [],
|
|
1187
|
+
lastIngestAt: null,
|
|
1188
|
+
lastError: 'metrics.disabled',
|
|
1189
|
+
});
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
const p = toRecord(params);
|
|
1193
|
+
const minutes = toInt(p.minutes, 60);
|
|
1194
|
+
const limit = toInt(p.limit, 100);
|
|
1195
|
+
const overview = await (0, api_1.getMetricsOverview)(pool, { minutes, limit });
|
|
1196
|
+
respond(true, {
|
|
1197
|
+
enabled: true,
|
|
1198
|
+
otlpPath: config.metrics.otlpPath,
|
|
1199
|
+
otlpMetricsEndpoint: `${config.metrics.otlpPath}/v1/metrics`,
|
|
1200
|
+
...overview,
|
|
1201
|
+
});
|
|
1202
|
+
});
|
|
1203
|
+
registerGateway('observability.metrics.series', async ({ params, respond }) => {
|
|
1204
|
+
const pool = writer.getPool();
|
|
1205
|
+
const p = toRecord(params);
|
|
1206
|
+
const metricName = toStringOrUndefined(p.metricName);
|
|
1207
|
+
if (!metricName) {
|
|
1208
|
+
respond(false, { error: 'metricName is required' });
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
const minutes = toInt(p.minutes, 60);
|
|
1212
|
+
const stepSec = toInt(p.stepSec, 30);
|
|
1213
|
+
const result = await (0, api_1.getMetricSeries)(pool, { metricName, minutes, stepSec });
|
|
1214
|
+
respond(true, result);
|
|
1215
|
+
});
|
|
1216
|
+
registerGateway('observability.logs', async ({ params, respond }) => {
|
|
1217
|
+
const p = toRecord(params);
|
|
1218
|
+
const { source, path: logPath } = resolveLogPath(p.source);
|
|
1219
|
+
if (!logPath) {
|
|
1220
|
+
respond(false, { error: `unsupported log source: ${String(p.source || '')}` });
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
if (!fs.existsSync(logPath)) {
|
|
1224
|
+
respond(false, { error: `log file not found: ${logPath}` });
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
const limit = Math.min(toInt(p.limit, 200), 2000);
|
|
1228
|
+
const search = toStringOrUndefined(p.search);
|
|
1229
|
+
const sessionId = toStringOrUndefined(p.sessionId);
|
|
1230
|
+
const caseSensitive = toBool(p.caseSensitive, false);
|
|
1231
|
+
const beforeLine = Math.max(0, Number(p.beforeLine || 0));
|
|
1232
|
+
const hasBefore = Number.isFinite(beforeLine) && beforeLine > 0;
|
|
1233
|
+
const raw = fs.readFileSync(logPath, 'utf8');
|
|
1234
|
+
const lines = raw.split(/\r?\n/);
|
|
1235
|
+
const endExclusive = hasBefore ? Math.min(beforeLine, lines.length) : lines.length;
|
|
1236
|
+
const filtered = [];
|
|
1237
|
+
const needleRaw = (search || sessionId || '').trim();
|
|
1238
|
+
const needle = caseSensitive ? needleRaw : needleRaw.toLowerCase();
|
|
1239
|
+
for (let i = 0; i < endExclusive; i++) {
|
|
1240
|
+
const lineText = lines[i] || '';
|
|
1241
|
+
if (!lineText)
|
|
1242
|
+
continue;
|
|
1243
|
+
if (needle) {
|
|
1244
|
+
const hay = caseSensitive ? lineText : lineText.toLowerCase();
|
|
1245
|
+
if (!hay.includes(needle))
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
const tsMatch = lineText.match(/^(\d{4}-\d{2}-\d{2}T[^\s]+)/);
|
|
1249
|
+
filtered.push({
|
|
1250
|
+
line: i + 1,
|
|
1251
|
+
text: lineText,
|
|
1252
|
+
timestamp: tsMatch ? tsMatch[1] : null,
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
const items = filtered.slice(Math.max(0, filtered.length - limit));
|
|
1256
|
+
const nextBeforeLine = items.length > 0 ? items[0].line : 0;
|
|
1257
|
+
respond(true, {
|
|
1258
|
+
source,
|
|
1259
|
+
path: logPath,
|
|
1260
|
+
totalLines: lines.length,
|
|
1261
|
+
totalMatched: filtered.length,
|
|
1262
|
+
returned: items.length,
|
|
1263
|
+
nextBeforeLine,
|
|
1264
|
+
items,
|
|
1265
|
+
});
|
|
1266
|
+
});
|
|
1267
|
+
console.log('[openclaw-observability] Gateway methods registered: observability.*');
|
|
1268
|
+
}
|
|
1269
|
+
else {
|
|
1270
|
+
console.warn('[openclaw-observability] registerGatewayMethod not available — RPC observability.* disabled');
|
|
1271
|
+
}
|
|
957
1272
|
// ====================== Helper functions ======================
|
|
958
1273
|
/** Record action, update session stats, and run security scan */
|
|
959
1274
|
function recordAction(action, tokens = 0) {
|