forge-openclaw-plugin 0.2.20 → 0.2.21
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/assets/{board-DGbXWEuu.js → board-_C6oMy5w.js} +2 -2
- package/dist/assets/{board-DGbXWEuu.js.map → board-_C6oMy5w.js.map} +1 -1
- package/dist/assets/index-B4A6TooJ.js +63 -0
- package/dist/assets/index-B4A6TooJ.js.map +1 -0
- package/dist/assets/index-D6Xs_2mo.css +1 -0
- package/dist/assets/{motion-B5Qoz2Ci.js → motion-D4sZgCHd.js} +2 -2
- package/dist/assets/{motion-B5Qoz2Ci.js.map → motion-D4sZgCHd.js.map} +1 -1
- package/dist/assets/{table-D_iurDQu.js → table-BWzTaky1.js} +2 -2
- package/dist/assets/{table-D_iurDQu.js.map → table-BWzTaky1.js.map} +1 -1
- package/dist/assets/{ui-D5QUYUq4.js → ui-BzK4azQb.js} +2 -2
- package/dist/assets/{ui-D5QUYUq4.js.map → ui-BzK4azQb.js.map} +1 -1
- package/dist/assets/vendor-De38P6YR.js +729 -0
- package/dist/assets/vendor-De38P6YR.js.map +1 -0
- package/dist/assets/{viz-BD9WSxHz.js → viz-C6hfyqzu.js} +2 -2
- package/dist/assets/{viz-BD9WSxHz.js.map → viz-C6hfyqzu.js.map} +1 -1
- package/dist/index.html +8 -8
- package/dist/server/app.js +301 -19
- package/dist/server/health.js +82 -21
- package/dist/server/managers/platform/background-job-manager.js +103 -8
- package/dist/server/managers/platform/llm-manager.js +91 -5
- package/dist/server/managers/platform/openai-responses-provider.js +683 -70
- package/dist/server/repositories/diagnostic-logs.js +243 -0
- package/dist/server/repositories/wiki-memory.js +595 -62
- package/dist/server/types.js +56 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server/migrations/023_diagnostic_logs.sql +28 -0
- package/skills/forge-openclaw/SKILL.md +14 -0
- package/dist/assets/index-4-1WI9i7.css +0 -1
- package/dist/assets/index-BZbHajNK.js +0 -63
- package/dist/assets/index-BZbHajNK.js.map +0 -1
- package/dist/assets/vendor-KARp8LAR.js +0 -706
- package/dist/assets/vendor-KARp8LAR.js.map +0 -1
|
@@ -1,19 +1,49 @@
|
|
|
1
1
|
import { AbstractManager } from "../base.js";
|
|
2
|
+
import { recordDiagnosticLog } from "../../repositories/diagnostic-logs.js";
|
|
2
3
|
export class BackgroundJobManager extends AbstractManager {
|
|
4
|
+
maxConcurrentJobs;
|
|
3
5
|
name = "BackgroundJobManager";
|
|
4
6
|
queue = [];
|
|
5
7
|
active = new Set();
|
|
6
8
|
draining = false;
|
|
9
|
+
constructor(maxConcurrentJobs = 3) {
|
|
10
|
+
super();
|
|
11
|
+
this.maxConcurrentJobs = maxConcurrentJobs;
|
|
12
|
+
}
|
|
7
13
|
start() {
|
|
8
14
|
return;
|
|
9
15
|
}
|
|
10
16
|
enqueue(input) {
|
|
17
|
+
if (this.has(input.id)) {
|
|
18
|
+
this.recordLifecycleLog("info", "background_job_enqueue_skipped", {
|
|
19
|
+
task: input,
|
|
20
|
+
message: `Skipped duplicate background job ${input.label}.`,
|
|
21
|
+
details: {
|
|
22
|
+
queueDepth: this.queue.length,
|
|
23
|
+
activeCount: this.active.size
|
|
24
|
+
},
|
|
25
|
+
functionName: "enqueue"
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
11
29
|
this.queue.push(input);
|
|
30
|
+
this.recordLifecycleLog("info", "background_job_enqueued", {
|
|
31
|
+
task: input,
|
|
32
|
+
message: `Enqueued background job ${input.label}.`,
|
|
33
|
+
details: {
|
|
34
|
+
queueDepth: this.queue.length,
|
|
35
|
+
activeCount: this.active.size
|
|
36
|
+
},
|
|
37
|
+
functionName: "enqueue"
|
|
38
|
+
});
|
|
12
39
|
this.scheduleDrain();
|
|
13
40
|
}
|
|
14
41
|
isActive(jobId) {
|
|
15
42
|
return this.active.has(jobId);
|
|
16
43
|
}
|
|
44
|
+
has(jobId) {
|
|
45
|
+
return (this.active.has(jobId) || this.queue.some((task) => task.id === jobId));
|
|
46
|
+
}
|
|
17
47
|
async stop() {
|
|
18
48
|
this.draining = true;
|
|
19
49
|
while (this.active.size > 0) {
|
|
@@ -21,26 +51,69 @@ export class BackgroundJobManager extends AbstractManager {
|
|
|
21
51
|
}
|
|
22
52
|
}
|
|
23
53
|
scheduleDrain() {
|
|
24
|
-
if (this.draining ||
|
|
54
|
+
if (this.draining ||
|
|
55
|
+
this.queue.length === 0 ||
|
|
56
|
+
this.active.size >= this.maxConcurrentJobs) {
|
|
25
57
|
return;
|
|
26
58
|
}
|
|
27
59
|
queueMicrotask(() => {
|
|
28
|
-
void this.
|
|
60
|
+
void this.drainAvailable();
|
|
29
61
|
});
|
|
30
62
|
}
|
|
31
|
-
async
|
|
32
|
-
if (this.draining
|
|
63
|
+
async drainAvailable() {
|
|
64
|
+
if (this.draining) {
|
|
33
65
|
return;
|
|
34
66
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
67
|
+
while (!this.draining &&
|
|
68
|
+
this.queue.length > 0 &&
|
|
69
|
+
this.active.size < this.maxConcurrentJobs) {
|
|
70
|
+
const next = this.queue.shift();
|
|
71
|
+
if (!next) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.active.add(next.id);
|
|
75
|
+
void this.runTask(next);
|
|
38
76
|
}
|
|
39
|
-
|
|
77
|
+
}
|
|
78
|
+
async runTask(next) {
|
|
79
|
+
const startedAt = Date.now();
|
|
80
|
+
this.recordLifecycleLog("info", "background_job_started", {
|
|
81
|
+
task: next,
|
|
82
|
+
message: `Started background job ${next.label}.`,
|
|
83
|
+
details: {
|
|
84
|
+
queueDepth: this.queue.length,
|
|
85
|
+
activeCount: this.active.size,
|
|
86
|
+
maxConcurrentJobs: this.maxConcurrentJobs
|
|
87
|
+
},
|
|
88
|
+
functionName: "runTask"
|
|
89
|
+
});
|
|
40
90
|
try {
|
|
41
91
|
await next.handler();
|
|
92
|
+
this.recordLifecycleLog("info", "background_job_completed", {
|
|
93
|
+
task: next,
|
|
94
|
+
message: `Completed background job ${next.label}.`,
|
|
95
|
+
details: {
|
|
96
|
+
durationMs: Date.now() - startedAt,
|
|
97
|
+
queueDepth: this.queue.length,
|
|
98
|
+
activeCount: this.active.size,
|
|
99
|
+
maxConcurrentJobs: this.maxConcurrentJobs
|
|
100
|
+
},
|
|
101
|
+
functionName: "runTask"
|
|
102
|
+
});
|
|
42
103
|
}
|
|
43
104
|
catch (error) {
|
|
105
|
+
this.recordLifecycleLog("error", "background_job_failed", {
|
|
106
|
+
task: next,
|
|
107
|
+
message: `Background job failed for ${next.label}.`,
|
|
108
|
+
details: {
|
|
109
|
+
durationMs: Date.now() - startedAt,
|
|
110
|
+
queueDepth: this.queue.length,
|
|
111
|
+
activeCount: this.active.size,
|
|
112
|
+
maxConcurrentJobs: this.maxConcurrentJobs,
|
|
113
|
+
error
|
|
114
|
+
},
|
|
115
|
+
functionName: "runTask"
|
|
116
|
+
});
|
|
44
117
|
console.error(`[${this.name}] background job failed for ${next.label}:`, error);
|
|
45
118
|
}
|
|
46
119
|
finally {
|
|
@@ -48,4 +121,26 @@ export class BackgroundJobManager extends AbstractManager {
|
|
|
48
121
|
this.scheduleDrain();
|
|
49
122
|
}
|
|
50
123
|
}
|
|
124
|
+
recordLifecycleLog(level, eventKey, input) {
|
|
125
|
+
try {
|
|
126
|
+
recordDiagnosticLog({
|
|
127
|
+
level,
|
|
128
|
+
source: "system",
|
|
129
|
+
scope: "background_job",
|
|
130
|
+
eventKey,
|
|
131
|
+
message: input.message,
|
|
132
|
+
functionName: input.functionName,
|
|
133
|
+
entityType: "background_job",
|
|
134
|
+
entityId: input.task.id,
|
|
135
|
+
jobId: input.task.id,
|
|
136
|
+
details: {
|
|
137
|
+
label: input.task.label,
|
|
138
|
+
...(input.details ?? {})
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Diagnostics should never block job execution.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
51
146
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { AbstractManager } from "../base.js";
|
|
2
2
|
import { readEncryptedSecret } from "../../repositories/calendar.js";
|
|
3
|
+
function emitDiagnostic(logger, input) {
|
|
4
|
+
logger?.(input);
|
|
5
|
+
}
|
|
3
6
|
export class LlmManager extends AbstractManager {
|
|
4
7
|
secretsManager;
|
|
5
8
|
name = "LlmManager";
|
|
@@ -13,18 +16,101 @@ export class LlmManager extends AbstractManager {
|
|
|
13
16
|
this.providers.set(alias, provider);
|
|
14
17
|
}
|
|
15
18
|
}
|
|
16
|
-
async compileWikiIngest(profile, input) {
|
|
17
|
-
const provider = this.
|
|
18
|
-
this.providers.get("openai-responses") ??
|
|
19
|
-
null;
|
|
19
|
+
async compileWikiIngest(profile, input, options = {}, logger) {
|
|
20
|
+
const provider = this.resolveProvider(profile.provider);
|
|
20
21
|
if (!provider) {
|
|
22
|
+
emitDiagnostic(logger, {
|
|
23
|
+
level: "error",
|
|
24
|
+
message: "Wiki ingest LLM provider is not supported.",
|
|
25
|
+
details: {
|
|
26
|
+
scope: "wiki_llm",
|
|
27
|
+
eventKey: "llm_provider_missing",
|
|
28
|
+
provider: profile.provider,
|
|
29
|
+
baseUrl: profile.baseUrl,
|
|
30
|
+
model: profile.model
|
|
31
|
+
}
|
|
32
|
+
});
|
|
21
33
|
return null;
|
|
22
34
|
}
|
|
23
35
|
const apiKey = this.readApiKey(profile.secretId);
|
|
24
36
|
if (!apiKey) {
|
|
37
|
+
emitDiagnostic(logger, {
|
|
38
|
+
level: "error",
|
|
39
|
+
message: "Wiki ingest LLM profile is missing an API key.",
|
|
40
|
+
details: {
|
|
41
|
+
scope: "wiki_llm",
|
|
42
|
+
eventKey: "llm_api_key_missing",
|
|
43
|
+
provider: profile.provider,
|
|
44
|
+
baseUrl: profile.baseUrl,
|
|
45
|
+
model: profile.model,
|
|
46
|
+
secretId: profile.secretId ?? null
|
|
47
|
+
}
|
|
48
|
+
});
|
|
25
49
|
return null;
|
|
26
50
|
}
|
|
27
|
-
return provider.compile({
|
|
51
|
+
return provider.compile({
|
|
52
|
+
apiKey,
|
|
53
|
+
profile,
|
|
54
|
+
input,
|
|
55
|
+
resumeResponseId: options.resumeResponseId ?? null,
|
|
56
|
+
logger
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async testWikiConnection(profile, explicitApiKey, logger) {
|
|
60
|
+
const provider = this.resolveProvider(profile.provider);
|
|
61
|
+
if (!provider) {
|
|
62
|
+
emitDiagnostic(logger, {
|
|
63
|
+
level: "error",
|
|
64
|
+
message: "Wiki LLM connection test requested an unsupported provider.",
|
|
65
|
+
details: {
|
|
66
|
+
scope: "wiki_llm",
|
|
67
|
+
eventKey: "llm_provider_missing",
|
|
68
|
+
provider: profile.provider,
|
|
69
|
+
baseUrl: profile.baseUrl,
|
|
70
|
+
model: profile.model
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
throw new Error("Unsupported LLM provider.");
|
|
74
|
+
}
|
|
75
|
+
const apiKey = explicitApiKey?.trim() || this.readApiKey(profile.secretId);
|
|
76
|
+
if (!apiKey) {
|
|
77
|
+
emitDiagnostic(logger, {
|
|
78
|
+
level: "error",
|
|
79
|
+
message: "Wiki LLM connection test is missing an API key.",
|
|
80
|
+
details: {
|
|
81
|
+
scope: "wiki_llm",
|
|
82
|
+
eventKey: "llm_api_key_missing",
|
|
83
|
+
provider: profile.provider,
|
|
84
|
+
baseUrl: profile.baseUrl,
|
|
85
|
+
model: profile.model,
|
|
86
|
+
secretId: profile.secretId ?? null
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
throw new Error("Save an OpenAI API key first.");
|
|
90
|
+
}
|
|
91
|
+
const result = await provider.testConnection({
|
|
92
|
+
apiKey,
|
|
93
|
+
profile,
|
|
94
|
+
logger
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
provider: profile.provider,
|
|
98
|
+
model: profile.model,
|
|
99
|
+
baseUrl: profile.baseUrl,
|
|
100
|
+
reasoningEffort: typeof profile.metadata.reasoningEffort === "string"
|
|
101
|
+
? profile.metadata.reasoningEffort
|
|
102
|
+
: null,
|
|
103
|
+
verbosity: typeof profile.metadata.verbosity === "string"
|
|
104
|
+
? profile.metadata.verbosity
|
|
105
|
+
: null,
|
|
106
|
+
usingStoredKey: !explicitApiKey?.trim(),
|
|
107
|
+
outputPreview: result.outputPreview
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
resolveProvider(providerName) {
|
|
111
|
+
return (this.providers.get(providerName) ??
|
|
112
|
+
this.providers.get("openai-responses") ??
|
|
113
|
+
null);
|
|
28
114
|
}
|
|
29
115
|
readApiKey(secretId) {
|
|
30
116
|
if (!secretId) {
|