forge-openclaw-plugin 0.2.20 → 0.2.22

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.
Files changed (33) hide show
  1. package/dist/assets/{board-DGbXWEuu.js → board-_C6oMy5w.js} +2 -2
  2. package/dist/assets/{board-DGbXWEuu.js.map → board-_C6oMy5w.js.map} +1 -1
  3. package/dist/assets/index-Ch_xeZ2u.js +63 -0
  4. package/dist/assets/index-Ch_xeZ2u.js.map +1 -0
  5. package/dist/assets/index-DvVM7K6j.css +1 -0
  6. package/dist/assets/{motion-B5Qoz2Ci.js → motion-D4sZgCHd.js} +2 -2
  7. package/dist/assets/{motion-B5Qoz2Ci.js.map → motion-D4sZgCHd.js.map} +1 -1
  8. package/dist/assets/{table-D_iurDQu.js → table-BWzTaky1.js} +2 -2
  9. package/dist/assets/{table-D_iurDQu.js.map → table-BWzTaky1.js.map} +1 -1
  10. package/dist/assets/{ui-D5QUYUq4.js → ui-BzK4azQb.js} +2 -2
  11. package/dist/assets/{ui-D5QUYUq4.js.map → ui-BzK4azQb.js.map} +1 -1
  12. package/dist/assets/vendor-De38P6YR.js +729 -0
  13. package/dist/assets/vendor-De38P6YR.js.map +1 -0
  14. package/dist/assets/{viz-BD9WSxHz.js → viz-C6hfyqzu.js} +2 -2
  15. package/dist/assets/{viz-BD9WSxHz.js.map → viz-C6hfyqzu.js.map} +1 -1
  16. package/dist/index.html +8 -8
  17. package/dist/server/app.js +328 -19
  18. package/dist/server/health.js +82 -21
  19. package/dist/server/managers/platform/background-job-manager.js +103 -8
  20. package/dist/server/managers/platform/llm-manager.js +91 -5
  21. package/dist/server/managers/platform/openai-responses-provider.js +683 -70
  22. package/dist/server/repositories/diagnostic-logs.js +243 -0
  23. package/dist/server/repositories/wiki-memory.js +619 -66
  24. package/dist/server/types.js +56 -0
  25. package/openclaw.plugin.json +1 -1
  26. package/package.json +1 -1
  27. package/server/migrations/023_diagnostic_logs.sql +28 -0
  28. package/skills/forge-openclaw/SKILL.md +14 -0
  29. package/dist/assets/index-4-1WI9i7.css +0 -1
  30. package/dist/assets/index-BZbHajNK.js +0 -63
  31. package/dist/assets/index-BZbHajNK.js.map +0 -1
  32. package/dist/assets/vendor-KARp8LAR.js +0 -706
  33. package/dist/assets/vendor-KARp8LAR.js.map +0 -1
@@ -0,0 +1,243 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { Buffer } from "node:buffer";
3
+ import { getDatabase } from "../db.js";
4
+ import { createDiagnosticLogSchema, diagnosticLogEntrySchema } from "../types.js";
5
+ const MAX_LOG_ENTRIES = 5_000;
6
+ export const DIAGNOSTIC_LOG_RETENTION_DAYS = 14;
7
+ export const DIAGNOSTIC_LOG_RETENTION_SWEEP_INTERVAL_MS = 15 * 60 * 1000;
8
+ const MAX_STRING_LENGTH = 4_000;
9
+ const MAX_ARRAY_ITEMS = 24;
10
+ const MAX_OBJECT_KEYS = 40;
11
+ const MAX_DEPTH = 4;
12
+ let nextRetentionSweepAt = 0;
13
+ function nowIso() {
14
+ return new Date().toISOString();
15
+ }
16
+ function sanitizeDiagnosticValue(value, depth = 0) {
17
+ if (value === null ||
18
+ typeof value === "boolean" ||
19
+ typeof value === "number") {
20
+ return value;
21
+ }
22
+ if (typeof value === "string") {
23
+ return value.length > MAX_STRING_LENGTH
24
+ ? `${value.slice(0, MAX_STRING_LENGTH)}…`
25
+ : value;
26
+ }
27
+ if (typeof value === "bigint") {
28
+ return value.toString();
29
+ }
30
+ if (typeof value === "function" || typeof value === "symbol") {
31
+ return String(value);
32
+ }
33
+ if (value instanceof Date) {
34
+ return value.toISOString();
35
+ }
36
+ if (value instanceof Error) {
37
+ return {
38
+ name: value.name,
39
+ message: value.message,
40
+ stack: typeof value.stack === "string"
41
+ ? sanitizeDiagnosticValue(value.stack, depth + 1)
42
+ : null
43
+ };
44
+ }
45
+ if (Buffer.isBuffer(value)) {
46
+ return {
47
+ type: "Buffer",
48
+ bytes: value.byteLength
49
+ };
50
+ }
51
+ if (depth >= MAX_DEPTH) {
52
+ if (Array.isArray(value)) {
53
+ return `[Array(${value.length})]`;
54
+ }
55
+ return "[Object]";
56
+ }
57
+ if (Array.isArray(value)) {
58
+ return value
59
+ .slice(0, MAX_ARRAY_ITEMS)
60
+ .map((entry) => sanitizeDiagnosticValue(entry, depth + 1));
61
+ }
62
+ if (value && typeof value === "object") {
63
+ const entries = Object.entries(value).slice(0, MAX_OBJECT_KEYS);
64
+ return Object.fromEntries(entries.map(([key, entry]) => [
65
+ key,
66
+ sanitizeDiagnosticValue(entry, depth + 1)
67
+ ]));
68
+ }
69
+ return String(value);
70
+ }
71
+ function sanitizeDetails(details) {
72
+ if (!details) {
73
+ return {};
74
+ }
75
+ return Object.fromEntries(Object.entries(details).map(([key, value]) => [
76
+ key,
77
+ sanitizeDiagnosticValue(value)
78
+ ]));
79
+ }
80
+ function mapRow(row) {
81
+ return diagnosticLogEntrySchema.parse({
82
+ id: row.id,
83
+ level: row.level,
84
+ source: row.source,
85
+ scope: row.scope,
86
+ eventKey: row.event_key,
87
+ message: row.message,
88
+ route: row.route,
89
+ functionName: row.function_name,
90
+ requestId: row.request_id,
91
+ entityType: row.entity_type,
92
+ entityId: row.entity_id,
93
+ jobId: row.job_id,
94
+ details: JSON.parse(row.details_json),
95
+ createdAt: row.created_at
96
+ });
97
+ }
98
+ function pruneDiagnosticLogs() {
99
+ const expiredBefore = new Date(Date.now() - DIAGNOSTIC_LOG_RETENTION_DAYS * 24 * 60 * 60 * 1_000).toISOString();
100
+ const expiredResult = getDatabase()
101
+ .prepare(`DELETE FROM diagnostic_logs
102
+ WHERE created_at < ?`)
103
+ .run(expiredBefore);
104
+ const overflowResult = getDatabase().prepare(`DELETE FROM diagnostic_logs
105
+ WHERE id IN (
106
+ SELECT id
107
+ FROM diagnostic_logs
108
+ ORDER BY created_at DESC
109
+ LIMIT -1 OFFSET ?
110
+ )`).run(MAX_LOG_ENTRIES);
111
+ return (expiredResult.changes ?? 0) + (overflowResult.changes ?? 0);
112
+ }
113
+ function checkpointDiagnosticLogStore() {
114
+ getDatabase().exec("PRAGMA wal_checkpoint(TRUNCATE);");
115
+ }
116
+ export function enforceDiagnosticLogRetention(options = {}) {
117
+ const now = options.now ?? new Date();
118
+ const startedAt = now.getTime();
119
+ if (!options.force && startedAt < nextRetentionSweepAt) {
120
+ return { prunedCount: 0, ran: false };
121
+ }
122
+ nextRetentionSweepAt =
123
+ startedAt + DIAGNOSTIC_LOG_RETENTION_SWEEP_INTERVAL_MS;
124
+ const prunedCount = pruneDiagnosticLogs();
125
+ if (prunedCount > 0) {
126
+ checkpointDiagnosticLogStore();
127
+ }
128
+ return {
129
+ prunedCount,
130
+ ran: true
131
+ };
132
+ }
133
+ export function recordDiagnosticLog(input, now = new Date()) {
134
+ const parsed = createDiagnosticLogSchema.parse(input);
135
+ const entry = diagnosticLogEntrySchema.parse({
136
+ id: `diag_${randomUUID().replaceAll("-", "").slice(0, 10)}`,
137
+ level: parsed.level,
138
+ source: parsed.source ?? "server",
139
+ scope: parsed.scope,
140
+ eventKey: parsed.eventKey,
141
+ message: parsed.message,
142
+ route: parsed.route ?? null,
143
+ functionName: parsed.functionName ?? null,
144
+ requestId: parsed.requestId ?? null,
145
+ entityType: parsed.entityType ?? null,
146
+ entityId: parsed.entityId ?? null,
147
+ jobId: parsed.jobId ?? null,
148
+ details: sanitizeDetails(parsed.details),
149
+ createdAt: now.toISOString()
150
+ });
151
+ getDatabase().prepare(`INSERT INTO diagnostic_logs (
152
+ id, level, source, scope, event_key, message, route, function_name,
153
+ request_id, entity_type, entity_id, job_id, details_json, created_at
154
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(entry.id, entry.level, entry.source, entry.scope, entry.eventKey, entry.message, entry.route, entry.functionName, entry.requestId, entry.entityType, entry.entityId, entry.jobId, JSON.stringify(entry.details), entry.createdAt);
155
+ enforceDiagnosticLogRetention({ now });
156
+ return entry;
157
+ }
158
+ export function listDiagnosticLogs(filters = {}) {
159
+ enforceDiagnosticLogRetention();
160
+ const whereClauses = [];
161
+ const params = [];
162
+ if (filters.level) {
163
+ whereClauses.push("level = ?");
164
+ params.push(filters.level);
165
+ }
166
+ if (filters.source) {
167
+ whereClauses.push("source = ?");
168
+ params.push(filters.source);
169
+ }
170
+ if (filters.scope) {
171
+ whereClauses.push("scope = ?");
172
+ params.push(filters.scope);
173
+ }
174
+ if (filters.route) {
175
+ whereClauses.push("route = ?");
176
+ params.push(filters.route);
177
+ }
178
+ if (filters.entityType) {
179
+ whereClauses.push("entity_type = ?");
180
+ params.push(filters.entityType);
181
+ }
182
+ if (filters.entityId) {
183
+ whereClauses.push("entity_id = ?");
184
+ params.push(filters.entityId);
185
+ }
186
+ if (filters.jobId) {
187
+ whereClauses.push("job_id = ?");
188
+ params.push(filters.jobId);
189
+ }
190
+ if (filters.search) {
191
+ whereClauses.push("(message LIKE ? OR scope LIKE ? OR event_key LIKE ? OR IFNULL(route, '') LIKE ? OR details_json LIKE ?)");
192
+ const term = `%${filters.search}%`;
193
+ params.push(term, term, term, term, term);
194
+ }
195
+ if (filters.beforeCreatedAt && filters.beforeId) {
196
+ whereClauses.push("(created_at < ? OR (created_at = ? AND id < ?))");
197
+ params.push(filters.beforeCreatedAt, filters.beforeCreatedAt, filters.beforeId);
198
+ }
199
+ const whereSql = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
200
+ const limit = filters.limit ?? 200;
201
+ const rows = getDatabase()
202
+ .prepare(`SELECT id, level, source, scope, event_key, message, route, function_name,
203
+ request_id, entity_type, entity_id, job_id, details_json, created_at
204
+ FROM diagnostic_logs
205
+ ${whereSql}
206
+ ORDER BY created_at DESC, id DESC
207
+ LIMIT ?`)
208
+ .all(...params, limit);
209
+ const logs = rows.map(mapRow);
210
+ const tail = rows.at(-1) ?? null;
211
+ return {
212
+ logs,
213
+ nextCursor: rows.length >= limit && tail
214
+ ? {
215
+ beforeCreatedAt: tail.created_at,
216
+ beforeId: tail.id
217
+ }
218
+ : null
219
+ };
220
+ }
221
+ export function normalizeDiagnosticSource(value) {
222
+ return value === "ui" ||
223
+ value === "openclaw" ||
224
+ value === "agent" ||
225
+ value === "system" ||
226
+ value === "server"
227
+ ? value
228
+ : "server";
229
+ }
230
+ export function serializeDiagnosticError(error) {
231
+ return sanitizeDiagnosticValue(error);
232
+ }
233
+ export function createDiagnosticMessage(input) {
234
+ const method = input.method?.toUpperCase() || "CALL";
235
+ const route = input.route || "unknown-route";
236
+ if (typeof input.statusCode === "number") {
237
+ return `${method} ${route} -> ${input.statusCode}`;
238
+ }
239
+ return `${method} ${route}`;
240
+ }
241
+ export function createDiagnosticTimestamp() {
242
+ return nowIso();
243
+ }