@kafca/agentdock 0.1.45 → 0.1.47
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 +11 -0
- package/dist/renderer/assets/{Badge-BIojwD6s.js → Badge-BYREM3t3.js} +1 -1
- package/dist/renderer/assets/Chat-Dtb5X7sB.js +2 -0
- package/dist/renderer/assets/{ChatMarkdown-DqszsPB_.js → ChatMarkdown-_slLloCh.js} +1 -1
- package/dist/renderer/assets/Config-pHichs7H.js +21 -0
- package/dist/renderer/assets/CronList-CJQMeCjz.js +1 -0
- package/dist/renderer/assets/Dashboard-CY77Imde.js +26 -0
- package/dist/renderer/assets/{EmptyState-BqCx1oWr.js → EmptyState-DCqmct2y.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-BwK8Gpfa.js → KnowledgeDetail-DFrxa-pv.js} +1 -1
- package/dist/renderer/assets/KnowledgeHome-DL-vAfrH.js +11 -0
- package/dist/renderer/assets/Logs-jw9Uv590.js +1 -0
- package/dist/renderer/assets/{Modal-D2U4r2V0.js → Modal-ChSm7tk6.js} +1 -1
- package/dist/renderer/assets/MonitorList-DrEwyS71.js +1 -0
- package/dist/renderer/assets/{Page-urf5GcT_.js → Page-DhD-zgMU.js} +1 -1
- package/dist/renderer/assets/ProjectDetail-BVSjpfpL.js +11 -0
- package/dist/renderer/assets/ProjectList-CHGbTJjk.js +1 -0
- package/dist/renderer/assets/{Select-CAscWpy5.js → Select-rHs9CSam.js} +1 -1
- package/dist/renderer/assets/SessionChat-CDc2FNMA.js +1 -0
- package/dist/renderer/assets/SessionList-Mr1uAnRW.js +1 -0
- package/dist/renderer/assets/ThreadChat-Detu5UfN.js +23 -0
- package/dist/renderer/assets/Workspace-MRRg8ndJ.js +19 -0
- package/dist/renderer/assets/{arrow-left-7HrNuS-V.js → arrow-left-BU2NB2LA.js} +1 -1
- package/dist/renderer/assets/{book-open-BDoAu82s.js → book-open-D-TGvWwa.js} +1 -1
- package/dist/renderer/assets/bot-IYNh8tH5.js +6 -0
- package/dist/renderer/assets/{check-DGlyq4xX.js → check-CCZ0JO_S.js} +1 -1
- package/dist/renderer/assets/{chevron-down-Bb_3EHq9.js → chevron-down-00bRZi15.js} +1 -1
- package/dist/renderer/assets/{circle-DIpqZnYw.js → circle-CpQ8x4k_.js} +1 -1
- package/dist/renderer/assets/{desktop-Ci1fnRDR.js → desktop-D0k2aGSj.js} +1 -1
- package/dist/renderer/assets/{desktop-Do_UrC-o.js → desktop-JQkhNOF4.js} +1 -1
- package/dist/renderer/assets/{funnel-Bbf0ERLP.js → funnel-UbcrCBCp.js} +1 -1
- package/dist/renderer/assets/{heart-CGqU-qfw.js → heart-DvLsV7tL.js} +1 -1
- package/dist/renderer/assets/{index-Df-Trovt.js → index-B2L4G-b_.js} +1 -1
- package/dist/renderer/assets/index-CO4fGqmv.css +11 -0
- package/dist/renderer/assets/{index-DwhTjLA1.js → index-dy-g2Bh4.js} +3 -3
- package/dist/renderer/assets/{pencil-CHiy1MvK.js → pencil-qBYOgW5M.js} +1 -1
- package/dist/renderer/assets/{play-CGYnmC4Q.js → play-Dn6b4AGj.js} +1 -1
- package/dist/renderer/assets/{plus-B_Q5-Oj7.js → plus-CaApUwEm.js} +1 -1
- package/dist/renderer/assets/projects-BwF5RlNV.js +1 -0
- package/dist/renderer/assets/save-CN6yvTfD.js +11 -0
- package/dist/renderer/assets/{search-D8Dkhsyq.js → search-Dn2MxYiO.js} +1 -1
- package/dist/renderer/assets/{server-Cd96R7WL.js → server-DpcVLHeh.js} +1 -1
- package/dist/renderer/assets/{session-utils-BNUbQBfF.js → session-utils-DnD2Q_y1.js} +1 -1
- package/dist/renderer/assets/{sessions-BACfV8s1.js → sessions-eK5D4FLt.js} +1 -1
- package/dist/renderer/assets/{shield-check-Cnn-yQUA.js → shield-check-DSSJkacw.js} +1 -1
- package/dist/renderer/assets/{trash-2-oWCxwQ9D.js → trash-2-DmYJlEvO.js} +1 -1
- package/dist/renderer/assets/user-Cvf_uiyQ.js +6 -0
- package/dist/renderer/assets/{wifi-off-DHxnNkdY.js → wifi-off-CCTloj-V.js} +1 -1
- package/dist/renderer/index.html +2 -2
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +2 -1
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +1 -1
- package/dist-electron/services/local-ai-core/src/agents/shared/launch-utils.js +22 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/inbound.js +55 -1
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +132 -348
- package/dist-electron/services/local-ai-core/src/channel/shared/base-channel-gateway.js +303 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +131 -382
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +2 -1
- package/dist-electron/services/local-ai-core/src/runtime/channel-service.js +150 -0
- package/dist-electron/services/local-ai-core/src/runtime/env-utils.js +12 -0
- package/dist-electron/services/local-ai-core/src/runtime/external-service.js +234 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/automation-handler.js +34 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/capabilities-handler.js +12 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/channel-handler.js +65 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/external-handler.js +17 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/knowledge-handler.js +65 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/openai-handler.js +496 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/provider-handler.js +24 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/runtime-handler.js +68 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/runtimes-handler.js +37 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/scheduler-handler.js +34 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/security-handler.js +49 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/task-handler.js +27 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/thread-handler.js +42 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/workspace-handler.js +29 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +40 -592
- package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +96 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +107 -1084
- package/dist-electron/services/local-ai-core/src/runtime/standalone.js +13 -1
- package/dist-electron/src/pages/Projects/project-provider-config.js +75 -0
- package/dist-electron/tests/contracts/architecture-docs.test.js +1 -0
- package/dist-electron/tests/electron/agent-runtime-detector.test.js +18 -0
- package/dist-electron/tests/electron/knowledge-skill-script.test.js +15 -5
- package/dist-electron/tests/electron/lac-cli.test.js +591 -688
- package/dist-electron/tests/electron/plugin-kernel.test.js +47 -31
- package/dist-electron/tests/electron/project-provider-config.test.js +71 -0
- package/dist-electron/tests/electron/workspace-task-store.test.js +19 -9
- package/dist-electron/tests/integration/lark-gateway.test.js +80 -0
- package/dist-electron/tests/integration/local-core-cors.test.js +22 -0
- package/package.json +1 -1
- package/dist/renderer/assets/Chat-wjBR3XEX.js +0 -2
- package/dist/renderer/assets/Config-BDCHs5A2.js +0 -21
- package/dist/renderer/assets/CronList-CEWQYTov.js +0 -1
- package/dist/renderer/assets/Dashboard-DrWftfk3.js +0 -26
- package/dist/renderer/assets/KnowledgeHome-hhDTZMDO.js +0 -11
- package/dist/renderer/assets/Logs-C9xPEhFT.js +0 -1
- package/dist/renderer/assets/MonitorList-CayKbfwJ.js +0 -1
- package/dist/renderer/assets/ProjectDetail-B1GyAr7a.js +0 -11
- package/dist/renderer/assets/ProjectList-BoipQypE.js +0 -1
- package/dist/renderer/assets/SessionChat-BAVy5221.js +0 -1
- package/dist/renderer/assets/SessionList-BZpuMvh0.js +0 -1
- package/dist/renderer/assets/ThreadChat-JX7m9biG.js +0 -23
- package/dist/renderer/assets/Workspace-CFW8otjU.js +0 -14
- package/dist/renderer/assets/index-RaTga2BC.css +0 -11
- package/dist/renderer/assets/plug-Mexu4gyV.js +0 -6
- package/dist/renderer/assets/projects-C6bbkC-O.js +0 -1
- package/dist/renderer/assets/save-CJ_QFPw0.js +0 -6
- package/dist/renderer/assets/user-BzE9BwsY.js +0 -11
|
@@ -3,456 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.LocalAiCoreServer = void 0;
|
|
4
4
|
const node_http_1 = require("node:http");
|
|
5
5
|
const server_routes_js_1 = require("./server-routes.js");
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.options.response.writeHead(200, {
|
|
22
|
-
'Content-Type': 'text/event-stream',
|
|
23
|
-
'Cache-Control': 'no-cache, no-transform',
|
|
24
|
-
Connection: 'keep-alive',
|
|
25
|
-
});
|
|
26
|
-
this.sendChunk({ role: 'assistant' }, {
|
|
27
|
-
event: 'run_started',
|
|
28
|
-
kind: 'assistant',
|
|
29
|
-
run_id: this.options.runId,
|
|
30
|
-
});
|
|
31
|
-
this.roleSent = true;
|
|
32
|
-
}
|
|
33
|
-
handleBridgeEvent(event) {
|
|
34
|
-
if (this.closed || String(event.replyCtx || '') !== this.options.runId) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (!this.roleSent) {
|
|
38
|
-
this.start();
|
|
39
|
-
}
|
|
40
|
-
if (event.type === 'typing_stop') {
|
|
41
|
-
this.finish('stop', { event: 'run_finished', kind: 'status' });
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
if ((event.type === 'status' || event.type === 'card') && event.error) {
|
|
45
|
-
this.error(event.error, 'runtime_error');
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
if (event.type === 'buttons' && event.bridgeKind === 'permission') {
|
|
49
|
-
this.error('Unexpected permission request for yolo OpenAI chat run.', 'unexpected_permission_request');
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
if (event.type === 'preview_start' || event.type === 'update_message') {
|
|
53
|
-
this.handlePreviewEvent(event);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (event.type === 'reply') {
|
|
57
|
-
this.handleReplyEvent(event);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (event.type === 'status' || event.type === 'card') {
|
|
61
|
-
this.sendChunk({}, {
|
|
62
|
-
event: event.type,
|
|
63
|
-
kind: event.bridgeKind || 'status',
|
|
64
|
-
run_id: this.options.runId,
|
|
65
|
-
content: event.content,
|
|
66
|
-
ok: event.ok,
|
|
67
|
-
card: event.card,
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
replayMessage(input) {
|
|
72
|
-
if (this.closed || this.emittedMessageIds.has(input.id)) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
this.emittedMessageIds.add(input.id);
|
|
76
|
-
const event = {
|
|
77
|
-
type: 'reply',
|
|
78
|
-
replyCtx: this.options.runId,
|
|
79
|
-
messageId: input.id,
|
|
80
|
-
content: input.content,
|
|
81
|
-
bridgeKind: input.bridgeKind,
|
|
82
|
-
bridgeStatus: input.bridgeStatus,
|
|
83
|
-
toolCall: input.toolCall,
|
|
84
|
-
};
|
|
85
|
-
this.handleReplyEvent(event);
|
|
86
|
-
}
|
|
87
|
-
finish(finishReason = 'stop', agentdock = {}) {
|
|
88
|
-
if (this.closed) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const chunk = this.baseChunk({}, {
|
|
92
|
-
run_id: this.options.runId,
|
|
93
|
-
...agentdock,
|
|
94
|
-
}, finishReason);
|
|
95
|
-
this.options.response.write(createOpenAiSseData(chunk));
|
|
96
|
-
this.options.response.write(createOpenAiDone());
|
|
97
|
-
this.closed = true;
|
|
98
|
-
this.options.onClose();
|
|
99
|
-
this.options.response.end();
|
|
100
|
-
}
|
|
101
|
-
error(message, code = 'runtime_error') {
|
|
102
|
-
if (this.closed) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const chunk = this.baseChunk({}, {
|
|
106
|
-
run_id: this.options.runId,
|
|
107
|
-
event: 'error',
|
|
108
|
-
kind: 'status',
|
|
109
|
-
});
|
|
110
|
-
chunk.error = {
|
|
111
|
-
message,
|
|
112
|
-
type: code,
|
|
113
|
-
code,
|
|
114
|
-
};
|
|
115
|
-
this.options.response.write(createOpenAiSseData(chunk));
|
|
116
|
-
this.options.response.write(createOpenAiDone());
|
|
117
|
-
this.closed = true;
|
|
118
|
-
this.options.onClose();
|
|
119
|
-
this.options.response.end();
|
|
120
|
-
}
|
|
121
|
-
handlePreviewEvent(event) {
|
|
122
|
-
const content = String(event.content || '');
|
|
123
|
-
const handle = String(event.previewHandle || event.messageId || event.replyCtx || '');
|
|
124
|
-
if (!content || !handle) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (event.bridgeKind === 'thought') {
|
|
128
|
-
const prior = this.thoughtContentByHandle.get(handle) || '';
|
|
129
|
-
const delta = diffAccumulatedText(prior, content);
|
|
130
|
-
this.thoughtContentByHandle.set(handle, content);
|
|
131
|
-
if (!delta) {
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
this.sendProgressChunk(this.progressText('thinking', delta), {
|
|
135
|
-
event: 'thought_delta',
|
|
136
|
-
kind: 'thought',
|
|
137
|
-
run_id: this.options.runId,
|
|
138
|
-
thought: {
|
|
139
|
-
content,
|
|
140
|
-
delta,
|
|
141
|
-
preview_handle: handle,
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
if (event.bridgeKind && event.bridgeKind !== 'assistant') {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
const prior = this.assistantPreviewContentByHandle.get(handle) || '';
|
|
150
|
-
const delta = diffAccumulatedText(prior, content);
|
|
151
|
-
this.assistantPreviewContentByHandle.set(handle, content);
|
|
152
|
-
if (!delta) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
this.sendChunk({ content: delta }, {
|
|
156
|
-
event: 'assistant_delta',
|
|
157
|
-
kind: 'assistant',
|
|
158
|
-
run_id: this.options.runId,
|
|
159
|
-
preview_handle: handle,
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
handleReplyEvent(event) {
|
|
163
|
-
const content = String(event.content || '');
|
|
164
|
-
if (event.messageId) {
|
|
165
|
-
if (this.emittedMessageIds.has(event.messageId)) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
this.emittedMessageIds.add(event.messageId);
|
|
169
|
-
}
|
|
170
|
-
if (event.bridgeKind === 'tool') {
|
|
171
|
-
this.sendProgressChunk(this.formatToolProgressContent(event), {
|
|
172
|
-
event: 'tool_update',
|
|
173
|
-
kind: 'tool',
|
|
174
|
-
run_id: this.options.runId,
|
|
175
|
-
message_id: event.messageId,
|
|
176
|
-
tool: event.toolCall || {
|
|
177
|
-
output: content,
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (event.bridgeKind === 'plan') {
|
|
183
|
-
this.sendProgressChunk(this.progressText('plan', content), {
|
|
184
|
-
event: 'plan_update',
|
|
185
|
-
kind: 'plan',
|
|
186
|
-
run_id: this.options.runId,
|
|
187
|
-
message_id: event.messageId,
|
|
188
|
-
plan: {
|
|
189
|
-
content,
|
|
190
|
-
},
|
|
191
|
-
});
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
if (event.bridgeKind === 'thought') {
|
|
195
|
-
this.sendProgressChunk(this.progressText('thinking', content), {
|
|
196
|
-
event: 'thought_message',
|
|
197
|
-
kind: 'thought',
|
|
198
|
-
run_id: this.options.runId,
|
|
199
|
-
message_id: event.messageId,
|
|
200
|
-
thought: {
|
|
201
|
-
content,
|
|
202
|
-
delta: content,
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
if (event.bridgeKind === 'status' || event.bridgeKind === 'permission') {
|
|
208
|
-
this.sendProgressChunk(this.progressText(event.bridgeKind, content), {
|
|
209
|
-
event: event.bridgeKind === 'permission' ? 'permission_required' : 'status',
|
|
210
|
-
kind: event.bridgeKind,
|
|
211
|
-
run_id: this.options.runId,
|
|
212
|
-
message_id: event.messageId,
|
|
213
|
-
content,
|
|
214
|
-
bridge_status: event.bridgeStatus,
|
|
215
|
-
});
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
if (content) {
|
|
219
|
-
this.sendChunk({ content }, {
|
|
220
|
-
event: 'assistant_message',
|
|
221
|
-
kind: 'assistant',
|
|
222
|
-
run_id: this.options.runId,
|
|
223
|
-
message_id: event.messageId,
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
sendProgressChunk(content, agentdock) {
|
|
228
|
-
this.sendChunk(this.options.progressMode === 'content' && content ? { content } : {}, agentdock);
|
|
229
|
-
}
|
|
230
|
-
sendChunk(delta, agentdock, finishReason = null) {
|
|
231
|
-
if (this.closed) {
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
this.options.response.write(createOpenAiSseData(this.baseChunk(delta, agentdock, finishReason)));
|
|
235
|
-
}
|
|
236
|
-
baseChunk(delta, agentdock, finishReason = null) {
|
|
237
|
-
return {
|
|
238
|
-
id: this.completionId,
|
|
239
|
-
object: 'chat.completion.chunk',
|
|
240
|
-
created: this.created,
|
|
241
|
-
model: this.options.model,
|
|
242
|
-
choices: [
|
|
243
|
-
{
|
|
244
|
-
index: 0,
|
|
245
|
-
delta,
|
|
246
|
-
finish_reason: finishReason,
|
|
247
|
-
},
|
|
248
|
-
],
|
|
249
|
-
agentdock,
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
progressText(kind, content) {
|
|
253
|
-
return content ? `\n\n[${kind}]\n${content}` : '';
|
|
254
|
-
}
|
|
255
|
-
formatToolProgressContent(event) {
|
|
256
|
-
const tool = event.toolCall;
|
|
257
|
-
const name = tool?.name || tool?.label || 'tool';
|
|
258
|
-
const status = tool?.status || '';
|
|
259
|
-
const output = tool?.output || tool?.detail || event.content || '';
|
|
260
|
-
return output ? `\n\n[tool:${name}${status ? ` ${status}` : ''}]\n${output}` : '';
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
function json(res, statusCode, data, ok = true, error) {
|
|
264
|
-
res.statusCode = statusCode;
|
|
265
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
266
|
-
res.end(JSON.stringify(ok ? { ok: true, data } : { ok: false, error }));
|
|
267
|
-
}
|
|
268
|
-
function rawJson(res, statusCode, data) {
|
|
269
|
-
res.statusCode = statusCode;
|
|
270
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
271
|
-
res.end(JSON.stringify(data));
|
|
272
|
-
}
|
|
273
|
-
function openAiJsonError(res, statusCode, message, code = 'invalid_request_error') {
|
|
274
|
-
rawJson(res, statusCode, {
|
|
275
|
-
error: {
|
|
276
|
-
message,
|
|
277
|
-
type: code,
|
|
278
|
-
code,
|
|
279
|
-
},
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
function jsonError(res, statusCode, error) {
|
|
283
|
-
const info = (0, local_core_errors_js_1.toLocalCoreErrorInfo)(error);
|
|
284
|
-
res.statusCode = statusCode;
|
|
285
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
286
|
-
res.end(JSON.stringify((0, local_core_errors_js_1.errorInfoToHttpBody)(info)));
|
|
287
|
-
}
|
|
288
|
-
async function readJsonBody(req) {
|
|
289
|
-
const body = await readRawBody(req);
|
|
290
|
-
if (!body.length) {
|
|
291
|
-
return {};
|
|
292
|
-
}
|
|
293
|
-
return JSON.parse(Buffer.from(body).toString('utf8'));
|
|
294
|
-
}
|
|
295
|
-
async function readRawBody(req) {
|
|
296
|
-
const chunks = [];
|
|
297
|
-
for await (const chunk of req) {
|
|
298
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
299
|
-
}
|
|
300
|
-
return Buffer.concat(chunks);
|
|
301
|
-
}
|
|
302
|
-
function setCorsHeaders(req, res) {
|
|
303
|
-
const origin = String(req.headers.origin || '');
|
|
304
|
-
if (origin === 'null' || origin.startsWith('http://127.0.0.1:') || origin.startsWith('http://localhost:')) {
|
|
305
|
-
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
306
|
-
res.setHeader('Vary', 'Origin');
|
|
307
|
-
}
|
|
308
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PATCH,DELETE,OPTIONS');
|
|
309
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
310
|
-
}
|
|
311
|
-
function createSseEvent(name, payload) {
|
|
312
|
-
return `event: ${name}\ndata: ${JSON.stringify(payload)}\n\n`;
|
|
313
|
-
}
|
|
314
|
-
function createOpenAiSseData(payload) {
|
|
315
|
-
return `data: ${JSON.stringify(payload)}\n\n`;
|
|
316
|
-
}
|
|
317
|
-
function createOpenAiDone() {
|
|
318
|
-
return 'data: [DONE]\n\n';
|
|
319
|
-
}
|
|
320
|
-
function sanitizeOpenAiId(value) {
|
|
321
|
-
return String(value || '')
|
|
322
|
-
.replace(/[^A-Za-z0-9_-]/g, '_')
|
|
323
|
-
.slice(0, 120) || 'run';
|
|
324
|
-
}
|
|
325
|
-
function diffAccumulatedText(previous, next) {
|
|
326
|
-
if (!next) {
|
|
327
|
-
return '';
|
|
328
|
-
}
|
|
329
|
-
if (!previous) {
|
|
330
|
-
return next;
|
|
331
|
-
}
|
|
332
|
-
if (next.startsWith(previous)) {
|
|
333
|
-
return next.slice(previous.length);
|
|
334
|
-
}
|
|
335
|
-
if (previous === next) {
|
|
336
|
-
return '';
|
|
337
|
-
}
|
|
338
|
-
return next;
|
|
339
|
-
}
|
|
340
|
-
function isTerminalAgentTaskStatus(status) {
|
|
341
|
-
return status === 'completed' || status === 'failed' || status === 'cancelled';
|
|
342
|
-
}
|
|
343
|
-
function extractMetadataValue(metadata, key) {
|
|
344
|
-
const value = metadata[key];
|
|
345
|
-
return String(value === undefined || value === null ? '' : value).trim();
|
|
346
|
-
}
|
|
347
|
-
function parseOpenAiChatCompletionRequest(input) {
|
|
348
|
-
const metadata = input.metadata && typeof input.metadata === 'object' && !Array.isArray(input.metadata)
|
|
349
|
-
? input.metadata
|
|
350
|
-
: {};
|
|
351
|
-
const userId = extractMetadataValue(metadata, 'user_id') || String(input.user || '').trim();
|
|
352
|
-
const projectId = extractMetadataValue(metadata, 'project_id');
|
|
353
|
-
if (!userId) {
|
|
354
|
-
throw new Error('metadata.user_id is required.');
|
|
355
|
-
}
|
|
356
|
-
if (!projectId) {
|
|
357
|
-
throw new Error('metadata.project_id is required.');
|
|
358
|
-
}
|
|
359
|
-
const unsupported = [
|
|
360
|
-
input.tools !== undefined ? 'tools' : '',
|
|
361
|
-
input.tool_choice !== undefined ? 'tool_choice' : '',
|
|
362
|
-
input.response_format !== undefined ? 'response_format' : '',
|
|
363
|
-
input.audio !== undefined ? 'audio' : '',
|
|
364
|
-
input.logprobs !== undefined ? 'logprobs' : '',
|
|
365
|
-
].filter(Boolean);
|
|
366
|
-
if (unsupported.length > 0) {
|
|
367
|
-
throw new Error(`Unsupported OpenAI chat field(s): ${unsupported.join(', ')}.`);
|
|
368
|
-
}
|
|
369
|
-
if (input.n !== undefined && Number(input.n) !== 1) {
|
|
370
|
-
throw new Error('Only n=1 is supported.');
|
|
371
|
-
}
|
|
372
|
-
const messages = Array.isArray(input.messages) ? input.messages : [];
|
|
373
|
-
if (messages.length === 0) {
|
|
374
|
-
throw new Error('messages must contain at least one text message.');
|
|
375
|
-
}
|
|
376
|
-
const prompt = messages
|
|
377
|
-
.map((message) => formatOpenAiMessage(message))
|
|
378
|
-
.filter(Boolean)
|
|
379
|
-
.join('\n\n');
|
|
380
|
-
if (!prompt.trim()) {
|
|
381
|
-
throw new Error('messages must contain text content.');
|
|
382
|
-
}
|
|
383
|
-
const progressMode = extractMetadataValue(metadata, 'agentdock_progress_mode') === 'content'
|
|
384
|
-
? 'content'
|
|
385
|
-
: 'extension';
|
|
386
|
-
const model = String(input.model || extractMetadataValue(metadata, 'model') || '').trim();
|
|
387
|
-
const externalRun = {
|
|
388
|
-
user_id: userId,
|
|
389
|
-
external_project_id: projectId,
|
|
390
|
-
external_thread_id: extractMetadataValue(metadata, 'thread_id') || undefined,
|
|
391
|
-
display_name: extractMetadataValue(metadata, 'display_name') || undefined,
|
|
392
|
-
agent_type: extractMetadataValue(metadata, 'agent_type') || 'pi',
|
|
393
|
-
provider_id: extractMetadataValue(metadata, 'provider_id') || undefined,
|
|
394
|
-
model: model || undefined,
|
|
395
|
-
title: extractMetadataValue(metadata, 'title') || undefined,
|
|
396
|
-
metadata: {
|
|
397
|
-
...metadata,
|
|
398
|
-
openai_compatible: true,
|
|
399
|
-
},
|
|
400
|
-
prompt,
|
|
401
|
-
permission_mode: 'bypassPermissions',
|
|
402
|
-
runtime_env: {
|
|
403
|
-
AGENTDOCK_OPENAI_COMPAT: '1',
|
|
404
|
-
},
|
|
405
|
-
};
|
|
406
|
-
return {
|
|
407
|
-
externalRun,
|
|
408
|
-
model: model || externalRun.agent_type || 'agentdock',
|
|
409
|
-
stream: Boolean(input.stream),
|
|
410
|
-
progressMode,
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
function formatOpenAiMessage(message) {
|
|
414
|
-
const role = String(message?.role || 'user').trim() || 'user';
|
|
415
|
-
const content = extractOpenAiMessageText(message?.content);
|
|
416
|
-
if (!content.trim()) {
|
|
417
|
-
return '';
|
|
418
|
-
}
|
|
419
|
-
if (role === 'user') {
|
|
420
|
-
return content;
|
|
421
|
-
}
|
|
422
|
-
return `[${role}]\n${content}`;
|
|
423
|
-
}
|
|
424
|
-
function extractOpenAiMessageText(content) {
|
|
425
|
-
if (typeof content === 'string') {
|
|
426
|
-
return content;
|
|
427
|
-
}
|
|
428
|
-
if (content === null || content === undefined) {
|
|
429
|
-
return '';
|
|
430
|
-
}
|
|
431
|
-
if (!Array.isArray(content)) {
|
|
432
|
-
throw new Error('Only text message content is supported.');
|
|
433
|
-
}
|
|
434
|
-
return content.map((part) => {
|
|
435
|
-
const type = String(part?.type || 'text');
|
|
436
|
-
if (type !== 'text') {
|
|
437
|
-
throw new Error(`Only text message parts are supported; received ${type}.`);
|
|
438
|
-
}
|
|
439
|
-
return String(part?.text || '');
|
|
440
|
-
}).join('');
|
|
441
|
-
}
|
|
442
|
-
function collectAssistantMessagesForRun(snapshot) {
|
|
443
|
-
const thread = snapshot.thread;
|
|
444
|
-
const startedAt = Date.parse(snapshot.task?.startedAt || snapshot.task?.createdAt || '');
|
|
445
|
-
if (!thread || !Number.isFinite(startedAt)) {
|
|
446
|
-
return [];
|
|
447
|
-
}
|
|
448
|
-
return (thread.messages || []).filter((message) => {
|
|
449
|
-
if (message.role !== 'assistant') {
|
|
450
|
-
return false;
|
|
451
|
-
}
|
|
452
|
-
const messageAt = Date.parse(message.timestamp || '');
|
|
453
|
-
return Number.isFinite(messageAt) && messageAt >= startedAt;
|
|
454
|
-
});
|
|
455
|
-
}
|
|
6
|
+
const server_helpers_js_1 = require("./server-helpers.js");
|
|
7
|
+
const runtime_handler_js_1 = require("./handlers/runtime-handler.js");
|
|
8
|
+
const runtimes_handler_js_1 = require("./handlers/runtimes-handler.js");
|
|
9
|
+
const thread_handler_js_1 = require("./handlers/thread-handler.js");
|
|
10
|
+
const workspace_handler_js_1 = require("./handlers/workspace-handler.js");
|
|
11
|
+
const security_handler_js_1 = require("./handlers/security-handler.js");
|
|
12
|
+
const task_handler_js_1 = require("./handlers/task-handler.js");
|
|
13
|
+
const scheduler_handler_js_1 = require("./handlers/scheduler-handler.js");
|
|
14
|
+
const automation_handler_js_1 = require("./handlers/automation-handler.js");
|
|
15
|
+
const knowledge_handler_js_1 = require("./handlers/knowledge-handler.js");
|
|
16
|
+
const capabilities_handler_js_1 = require("./handlers/capabilities-handler.js");
|
|
17
|
+
const provider_handler_js_1 = require("./handlers/provider-handler.js");
|
|
18
|
+
const channel_handler_js_1 = require("./handlers/channel-handler.js");
|
|
19
|
+
const external_handler_js_1 = require("./handlers/external-handler.js");
|
|
20
|
+
const openai_handler_js_1 = require("./handlers/openai-handler.js");
|
|
456
21
|
class LocalAiCoreServer {
|
|
457
22
|
bindings;
|
|
458
23
|
host;
|
|
@@ -462,6 +27,7 @@ class LocalAiCoreServer {
|
|
|
462
27
|
externalReplayTimers = new Map();
|
|
463
28
|
externalRunSseClients = new Map();
|
|
464
29
|
openAiRunStreams = new Map();
|
|
30
|
+
handlers = new Map();
|
|
465
31
|
server = (0, node_http_1.createServer)((req, res) => {
|
|
466
32
|
void this.handleRequest(req, res);
|
|
467
33
|
});
|
|
@@ -469,44 +35,8 @@ class LocalAiCoreServer {
|
|
|
469
35
|
this.bindings = bindings;
|
|
470
36
|
this.host = options.host || '127.0.0.1';
|
|
471
37
|
this.port = options.port ?? 9831;
|
|
472
|
-
this.
|
|
473
|
-
|
|
474
|
-
});
|
|
475
|
-
this.bindings.on('bridge', (bridge) => {
|
|
476
|
-
this.broadcast({ type: 'stream.updated', stream: bridge });
|
|
477
|
-
if (bridge.replyCtx) {
|
|
478
|
-
const runId = String(bridge.replyCtx);
|
|
479
|
-
this.broadcastExternalRunStream(runId, bridge);
|
|
480
|
-
this.broadcastOpenAiRunStream(runId, bridge);
|
|
481
|
-
}
|
|
482
|
-
if (bridge.sessionKey) {
|
|
483
|
-
const threadId = this.findThreadIdFromSessionKey(bridge.sessionKey);
|
|
484
|
-
this.broadcast({
|
|
485
|
-
type: 'presence.updated',
|
|
486
|
-
threadId,
|
|
487
|
-
live: bridge.type !== 'typing_stop',
|
|
488
|
-
stream: bridge,
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
});
|
|
492
|
-
this.bindings.on('thread-session-activated', (event) => {
|
|
493
|
-
this.broadcast({ type: 'thread.session.activated', ...event });
|
|
494
|
-
});
|
|
495
|
-
this.bindings.on('scheduler-job', (job) => {
|
|
496
|
-
this.broadcast({ type: 'scheduler.job.updated', job });
|
|
497
|
-
});
|
|
498
|
-
this.bindings.on('scheduler-run', (run) => {
|
|
499
|
-
this.broadcast({ type: 'scheduler.run.updated', run });
|
|
500
|
-
});
|
|
501
|
-
this.bindings.on('automation-monitor', (monitor) => {
|
|
502
|
-
this.broadcast({ type: 'automation.monitor.updated', monitor });
|
|
503
|
-
});
|
|
504
|
-
this.bindings.on('automation-monitor-run', (run) => {
|
|
505
|
-
this.broadcast({ type: 'automation.monitor.run.updated', run });
|
|
506
|
-
});
|
|
507
|
-
this.bindings.on('runtime-detection', (event) => {
|
|
508
|
-
this.broadcast(event);
|
|
509
|
-
});
|
|
38
|
+
this.registerHandlers();
|
|
39
|
+
this.wireEvents();
|
|
510
40
|
}
|
|
511
41
|
async start() {
|
|
512
42
|
await new Promise((resolve, reject) => {
|
|
@@ -552,8 +82,82 @@ class LocalAiCoreServer {
|
|
|
552
82
|
});
|
|
553
83
|
});
|
|
554
84
|
}
|
|
85
|
+
registerHandlers() {
|
|
86
|
+
const b = this.bindings;
|
|
87
|
+
(0, runtime_handler_js_1.registerRuntimeHandlers)(this.handlers, b.controller, b.errorReporter, (res) => this.attachSseClient(res));
|
|
88
|
+
(0, runtimes_handler_js_1.registerRuntimesHandlers)(this.handlers, b.runtimeDetection);
|
|
89
|
+
(0, thread_handler_js_1.registerThreadHandlers)(this.handlers, b.workspaceRouter);
|
|
90
|
+
(0, workspace_handler_js_1.registerWorkspaceHandlers)(this.handlers, b.workspaceRouter);
|
|
91
|
+
(0, security_handler_js_1.registerSecurityHandlers)(this.handlers, b.workspaceRouter);
|
|
92
|
+
(0, task_handler_js_1.registerTaskHandlers)(this.handlers, b.workspaceRouter);
|
|
93
|
+
(0, scheduler_handler_js_1.registerSchedulerHandlers)(this.handlers, b.scheduledJobs);
|
|
94
|
+
(0, automation_handler_js_1.registerAutomationHandlers)(this.handlers, b.automationMonitors);
|
|
95
|
+
(0, knowledge_handler_js_1.registerKnowledgeHandlers)(this.handlers, b.knowledgeProvider);
|
|
96
|
+
(0, capabilities_handler_js_1.registerCapabilitiesHandlers)(this.handlers, b.kernel);
|
|
97
|
+
(0, provider_handler_js_1.registerProviderHandlers)(this.handlers, b.store);
|
|
98
|
+
(0, channel_handler_js_1.registerChannelHandlers)(this.handlers, b.channelService);
|
|
99
|
+
(0, external_handler_js_1.registerExternalHandlers)(this.handlers, b.externalService, (runId, res) => this.attachExternalRunSseClient(runId, res));
|
|
100
|
+
const openAiReg = {
|
|
101
|
+
addAdapter: (runId, adapter) => {
|
|
102
|
+
const adapters = this.openAiRunStreams.get(runId) || new Set();
|
|
103
|
+
adapters.add(adapter);
|
|
104
|
+
this.openAiRunStreams.set(runId, adapters);
|
|
105
|
+
},
|
|
106
|
+
removeAdapter: (runId, adapter) => {
|
|
107
|
+
const adapters = this.openAiRunStreams.get(runId);
|
|
108
|
+
if (adapters) {
|
|
109
|
+
adapters.delete(adapter);
|
|
110
|
+
if (adapters.size === 0) {
|
|
111
|
+
this.openAiRunStreams.delete(runId);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
(0, openai_handler_js_1.registerOpenAiHandler)(this.handlers, b.externalService, openAiReg);
|
|
117
|
+
}
|
|
118
|
+
wireEvents() {
|
|
119
|
+
const b = this.bindings;
|
|
120
|
+
b.controller.on('runtime', (runtime) => {
|
|
121
|
+
this.broadcast({ type: 'runtime.updated', runtime });
|
|
122
|
+
});
|
|
123
|
+
b.controller.on('bridge', (bridge) => {
|
|
124
|
+
this.broadcast({ type: 'stream.updated', stream: bridge });
|
|
125
|
+
if (bridge.replyCtx) {
|
|
126
|
+
const runId = String(bridge.replyCtx);
|
|
127
|
+
this.broadcastExternalRunStream(runId, bridge);
|
|
128
|
+
this.broadcastOpenAiRunStream(runId, bridge);
|
|
129
|
+
}
|
|
130
|
+
if (bridge.sessionKey) {
|
|
131
|
+
const threadId = this.findThreadIdFromSessionKey(bridge.sessionKey);
|
|
132
|
+
this.broadcast({
|
|
133
|
+
type: 'presence.updated',
|
|
134
|
+
threadId,
|
|
135
|
+
live: bridge.type !== 'typing_stop',
|
|
136
|
+
stream: bridge,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
b.controller.on('thread-session-activated', (event) => {
|
|
141
|
+
this.broadcast({ type: 'thread.session.activated', ...event });
|
|
142
|
+
});
|
|
143
|
+
b.controller.on('scheduler-job', (job) => {
|
|
144
|
+
this.broadcast({ type: 'scheduler.job.updated', job });
|
|
145
|
+
});
|
|
146
|
+
b.controller.on('scheduler-run', (run) => {
|
|
147
|
+
this.broadcast({ type: 'scheduler.run.updated', run });
|
|
148
|
+
});
|
|
149
|
+
b.controller.on('automation-monitor', (monitor) => {
|
|
150
|
+
this.broadcast({ type: 'automation.monitor.updated', monitor });
|
|
151
|
+
});
|
|
152
|
+
b.controller.on('automation-monitor-run', (run) => {
|
|
153
|
+
this.broadcast({ type: 'automation.monitor.run.updated', run });
|
|
154
|
+
});
|
|
155
|
+
b.controller.on('runtime-detection', (event) => {
|
|
156
|
+
this.broadcast(event);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
555
159
|
async handleRequest(req, res) {
|
|
556
|
-
setCorsHeaders(req, res);
|
|
160
|
+
(0, server_helpers_js_1.setCorsHeaders)(req, res);
|
|
557
161
|
if (req.method === 'OPTIONS') {
|
|
558
162
|
res.statusCode = 204;
|
|
559
163
|
res.end();
|
|
@@ -564,467 +168,19 @@ class LocalAiCoreServer {
|
|
|
564
168
|
try {
|
|
565
169
|
const route = (0, server_routes_js_1.parseLocalAiCoreRoute)(req.method, path);
|
|
566
170
|
if (route) {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
jsonError(res, 404, new Error(`Unknown route: ${path}`));
|
|
571
|
-
}
|
|
572
|
-
catch (error) {
|
|
573
|
-
jsonError(res, 500, error);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
async handleParsedRoute(route, req, res, url) {
|
|
577
|
-
switch (route.name) {
|
|
578
|
-
case 'health':
|
|
579
|
-
json(res, 200, { name: 'local-ai-core', version: '0.1.0' });
|
|
580
|
-
return;
|
|
581
|
-
case 'runtime.status':
|
|
582
|
-
json(res, 200, await this.bindings.getRuntimeStatus());
|
|
583
|
-
return;
|
|
584
|
-
case 'runtime.service.start':
|
|
585
|
-
json(res, 200, await this.bindings.startService());
|
|
586
|
-
return;
|
|
587
|
-
case 'runtime.service.stop':
|
|
588
|
-
json(res, 200, await this.bindings.stopService());
|
|
589
|
-
return;
|
|
590
|
-
case 'runtime.service.restart':
|
|
591
|
-
json(res, 200, await this.bindings.restartService());
|
|
592
|
-
return;
|
|
593
|
-
case 'runtime.logs': {
|
|
594
|
-
const limit = Number(url.searchParams.get('limit') || '200');
|
|
595
|
-
json(res, 200, this.bindings.getLogs(limit));
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
case 'logs.list': {
|
|
599
|
-
const level = url.searchParams.get('level') || 'sys';
|
|
600
|
-
const limit = Number(url.searchParams.get('limit') || '200');
|
|
601
|
-
json(res, 200, {
|
|
602
|
-
entries: this.bindings.getLogEntries(level, limit).map((entry) => ({
|
|
603
|
-
time: entry.ts,
|
|
604
|
-
level: entry.level,
|
|
605
|
-
scope: entry.scope,
|
|
606
|
-
message: entry.message,
|
|
607
|
-
meta: entry.meta,
|
|
608
|
-
})),
|
|
609
|
-
});
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
case 'runtime.agent-runtimes':
|
|
613
|
-
case 'runtimes.list':
|
|
614
|
-
json(res, 200, await this.runtimeDetectionResponse());
|
|
615
|
-
return;
|
|
616
|
-
case 'runtimes.detail': {
|
|
617
|
-
const runtimes = await this.bindings.listInstalledAgentRuntimes();
|
|
618
|
-
const runtime = runtimes.find((entry) => entry.runtimeId === route.runtimeId || entry.agentType === route.runtimeId);
|
|
619
|
-
if (!runtime) {
|
|
620
|
-
json(res, 404, null, false, 'Runtime not found');
|
|
171
|
+
const handler = this.handlers.get(route.name);
|
|
172
|
+
if (handler) {
|
|
173
|
+
await handler(route, req, res, url);
|
|
621
174
|
return;
|
|
622
175
|
}
|
|
623
|
-
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
case 'runtimes.refresh': {
|
|
627
|
-
const runtimes = await this.bindings.refreshInstalledAgentRuntimes();
|
|
628
|
-
json(res, 200, { runtimes, checking: this.bindings.isRuntimeDetectionRunning() });
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
|
-
case 'runtimes.refresh-one': {
|
|
632
|
-
const runtimes = await this.bindings.refreshInstalledAgentRuntimes(route.runtimeId);
|
|
633
|
-
json(res, 200, { runtimes, checking: this.bindings.isRuntimeDetectionRunning(route.runtimeId) });
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
case 'runtime.config.read':
|
|
637
|
-
json(res, 200, await this.bindings.readConfigFile());
|
|
638
|
-
return;
|
|
639
|
-
case 'runtime.config.save-raw': {
|
|
640
|
-
const body = await readJsonBody(req);
|
|
641
|
-
json(res, 200, await this.bindings.saveRawConfigFile(String(body.raw || '')));
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
case 'runtime.config.save-structured': {
|
|
645
|
-
const body = await readJsonBody(req);
|
|
646
|
-
json(res, 200, await this.bindings.saveStructuredConfigFile((body.config || {})));
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
case 'runtime.settings.save': {
|
|
650
|
-
const body = await readJsonBody(req);
|
|
651
|
-
json(res, 200, await this.bindings.saveSettings(body));
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
case 'scheduler.jobs.list': {
|
|
655
|
-
const workspaceId = String(url.searchParams.get('workspace_id') || '');
|
|
656
|
-
json(res, 200, { jobs: await this.bindings.listScheduledJobs(workspaceId || undefined) });
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
case 'scheduler.jobs.create': {
|
|
660
|
-
const body = await readJsonBody(req);
|
|
661
|
-
json(res, 200, await this.bindings.createScheduledJob(body));
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
case 'scheduler.job.get':
|
|
665
|
-
json(res, 200, await this.bindings.getScheduledJob(route.jobId));
|
|
666
|
-
return;
|
|
667
|
-
case 'scheduler.job.runs':
|
|
668
|
-
json(res, 200, { runs: await this.bindings.listScheduledJobRuns(route.jobId) });
|
|
669
|
-
return;
|
|
670
|
-
case 'scheduler.job.run':
|
|
671
|
-
json(res, 200, await this.bindings.runScheduledJob(route.jobId));
|
|
672
|
-
return;
|
|
673
|
-
case 'scheduler.job.update': {
|
|
674
|
-
const body = await readJsonBody(req);
|
|
675
|
-
json(res, 200, await this.bindings.updateScheduledJob(route.jobId, body));
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
case 'scheduler.job.delete':
|
|
679
|
-
json(res, 200, await this.bindings.deleteScheduledJob(route.jobId));
|
|
680
|
-
return;
|
|
681
|
-
case 'automation.monitors.list': {
|
|
682
|
-
const workspaceId = String(url.searchParams.get('workspace_id') || '');
|
|
683
|
-
json(res, 200, { monitors: await this.bindings.listAutomationMonitors(workspaceId || undefined) });
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
case 'automation.monitors.create': {
|
|
687
|
-
const body = await readJsonBody(req);
|
|
688
|
-
json(res, 200, await this.bindings.createAutomationMonitor(body));
|
|
689
|
-
return;
|
|
690
|
-
}
|
|
691
|
-
case 'automation.monitor.get':
|
|
692
|
-
json(res, 200, await this.bindings.getAutomationMonitor(route.monitorId));
|
|
693
|
-
return;
|
|
694
|
-
case 'automation.monitor.runs':
|
|
695
|
-
json(res, 200, { runs: await this.bindings.listAutomationMonitorRuns(route.monitorId) });
|
|
696
|
-
return;
|
|
697
|
-
case 'automation.monitor.run':
|
|
698
|
-
json(res, 200, await this.bindings.runAutomationMonitor(route.monitorId));
|
|
699
|
-
return;
|
|
700
|
-
case 'automation.monitor.update': {
|
|
701
|
-
const body = await readJsonBody(req);
|
|
702
|
-
json(res, 200, await this.bindings.updateAutomationMonitor(route.monitorId, body));
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
|
-
case 'automation.monitor.delete':
|
|
706
|
-
json(res, 200, await this.bindings.deleteAutomationMonitor(route.monitorId));
|
|
707
|
-
return;
|
|
708
|
-
case 'threads.list': {
|
|
709
|
-
const workspaceId = String(url.searchParams.get('workspace_id') || '');
|
|
710
|
-
json(res, 200, { threads: workspaceId ? await this.bindings.listThreads(workspaceId) : [] });
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
case 'threads.create': {
|
|
714
|
-
const body = await readJsonBody(req);
|
|
715
|
-
json(res, 200, await this.bindings.createThread(String(body.workspaceId || ''), String(body.title || '') || undefined));
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
case 'thread.get':
|
|
719
|
-
json(res, 200, await this.bindings.getThread(route.threadId));
|
|
720
|
-
return;
|
|
721
|
-
case 'thread.update-knowledge-bases': {
|
|
722
|
-
const body = await readJsonBody(req);
|
|
723
|
-
const knowledgeBaseIds = Array.isArray(body.knowledgeBaseIds)
|
|
724
|
-
? body.knowledgeBaseIds.map((value) => String(value || ''))
|
|
725
|
-
: [];
|
|
726
|
-
json(res, 200, await this.bindings.updateThreadKnowledgeBases(route.threadId, knowledgeBaseIds));
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
case 'thread.rename': {
|
|
730
|
-
const body = await readJsonBody(req);
|
|
731
|
-
json(res, 200, await this.bindings.renameThread(route.threadId, String(body.title || '')));
|
|
176
|
+
(0, server_helpers_js_1.jsonError)(res, 404, new Error(`No handler for route: ${route.name}`));
|
|
732
177
|
return;
|
|
733
178
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
const body = await readJsonBody(req);
|
|
739
|
-
json(res, 200, await this.bindings.sendThreadMessage(route.threadId, String(body.content || '')));
|
|
740
|
-
return;
|
|
741
|
-
}
|
|
742
|
-
case 'thread.actions.send': {
|
|
743
|
-
const body = await readJsonBody(req);
|
|
744
|
-
json(res, 200, await this.bindings.sendThreadAction(route.threadId, String(body.content || '')));
|
|
745
|
-
return;
|
|
746
|
-
}
|
|
747
|
-
case 'run.interrupt':
|
|
748
|
-
json(res, 200, await this.bindings.interruptRun(route.runId));
|
|
749
|
-
return;
|
|
750
|
-
case 'workspaces.list':
|
|
751
|
-
json(res, 200, { workspaces: await this.bindings.listWorkspaces() });
|
|
752
|
-
return;
|
|
753
|
-
case 'workspace.streaming-probe':
|
|
754
|
-
json(res, 200, await this.bindings.probeWorkspaceStreaming(route.workspaceId));
|
|
755
|
-
return;
|
|
756
|
-
case 'workspace-registry.list':
|
|
757
|
-
json(res, 200, { workspaces: await this.bindings.listWorkspaceRegistry() });
|
|
758
|
-
return;
|
|
759
|
-
case 'workspace-registry.create': {
|
|
760
|
-
const body = await readJsonBody(req);
|
|
761
|
-
json(res, 200, await this.bindings.createWorkspaceRegistryEntry(body));
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
case 'workspace-registry.get':
|
|
765
|
-
json(res, 200, await this.bindings.getWorkspaceRegistryEntry(route.workspaceId));
|
|
766
|
-
return;
|
|
767
|
-
case 'workspace-registry.update': {
|
|
768
|
-
const body = await readJsonBody(req);
|
|
769
|
-
json(res, 200, await this.bindings.updateWorkspaceRegistryEntry(route.workspaceId, body));
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
case 'workspace-registry.delete':
|
|
773
|
-
json(res, 200, await this.bindings.deleteWorkspaceRegistryEntry(route.workspaceId));
|
|
774
|
-
return;
|
|
775
|
-
case 'providers.list':
|
|
776
|
-
json(res, 200, await this.bindings.listModelProviders());
|
|
777
|
-
return;
|
|
778
|
-
case 'providers.create': {
|
|
779
|
-
const body = await readJsonBody(req);
|
|
780
|
-
json(res, 200, await this.bindings.createModelProvider(body));
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
case 'provider.update': {
|
|
784
|
-
const body = await readJsonBody(req);
|
|
785
|
-
json(res, 200, await this.bindings.updateModelProvider(route.providerId, body));
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
case 'provider.delete':
|
|
789
|
-
json(res, 200, await this.bindings.deleteModelProvider(route.providerId));
|
|
790
|
-
return;
|
|
791
|
-
case 'external.project.ensure': {
|
|
792
|
-
const body = await readJsonBody(req);
|
|
793
|
-
json(res, 200, await this.bindings.ensureExternalProject(body));
|
|
794
|
-
return;
|
|
795
|
-
}
|
|
796
|
-
case 'external.run.create': {
|
|
797
|
-
const body = await readJsonBody(req);
|
|
798
|
-
json(res, 200, await this.bindings.createExternalRun(body));
|
|
799
|
-
return;
|
|
800
|
-
}
|
|
801
|
-
case 'external.run.events':
|
|
802
|
-
await this.attachExternalRunSseClient(route.runId, res);
|
|
803
|
-
return;
|
|
804
|
-
case 'openai.chat.completions':
|
|
805
|
-
await this.handleOpenAiChatCompletions(req, res);
|
|
806
|
-
return;
|
|
807
|
-
case 'workspace-security.get':
|
|
808
|
-
json(res, 200, await this.bindings.getWorkspaceSecuritySettings(route.workspaceId));
|
|
809
|
-
return;
|
|
810
|
-
case 'workspace-security.update': {
|
|
811
|
-
const body = await readJsonBody(req);
|
|
812
|
-
json(res, 200, await this.bindings.updateWorkspaceSecuritySettings(route.workspaceId, body));
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
case 'security.command-risk.classify': {
|
|
816
|
-
const body = await readJsonBody(req);
|
|
817
|
-
json(res, 200, await this.bindings.classifyCommand(String(body.command || ''), String(body.workspaceId || '') || undefined));
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
case 'approvals.list': {
|
|
821
|
-
const statusParam = url.searchParams.get('status') || '';
|
|
822
|
-
const status = statusParam ? statusParam.split(',').map((item) => item.trim()).filter(Boolean) : undefined;
|
|
823
|
-
json(res, 200, await this.bindings.listApprovalRequests({
|
|
824
|
-
workspaceId: url.searchParams.get('workspace_id') || undefined,
|
|
825
|
-
taskId: url.searchParams.get('task_id') || undefined,
|
|
826
|
-
status,
|
|
827
|
-
limit: Number(url.searchParams.get('limit') || '50'),
|
|
828
|
-
}));
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
case 'approvals.create': {
|
|
832
|
-
const body = await readJsonBody(req);
|
|
833
|
-
json(res, 200, await this.bindings.createApprovalRequest(body));
|
|
834
|
-
return;
|
|
835
|
-
}
|
|
836
|
-
case 'approval.get':
|
|
837
|
-
json(res, 200, await this.bindings.getApprovalRequest(route.approvalId));
|
|
838
|
-
return;
|
|
839
|
-
case 'approval.resolve': {
|
|
840
|
-
const body = await readJsonBody(req);
|
|
841
|
-
json(res, 200, await this.bindings.resolveApprovalRequest(route.approvalId, body));
|
|
842
|
-
return;
|
|
843
|
-
}
|
|
844
|
-
case 'audit-events.list': {
|
|
845
|
-
const typeParam = url.searchParams.get('type') || '';
|
|
846
|
-
const type = typeParam ? typeParam.split(',').map((item) => item.trim()).filter(Boolean) : undefined;
|
|
847
|
-
json(res, 200, await this.bindings.listAuditEvents({
|
|
848
|
-
workspaceId: url.searchParams.get('workspace_id') || undefined,
|
|
849
|
-
taskId: url.searchParams.get('task_id') || undefined,
|
|
850
|
-
approvalId: url.searchParams.get('approval_id') || undefined,
|
|
851
|
-
type,
|
|
852
|
-
limit: Number(url.searchParams.get('limit') || '50'),
|
|
853
|
-
}));
|
|
854
|
-
return;
|
|
855
|
-
}
|
|
856
|
-
case 'tasks.list': {
|
|
857
|
-
const statusParam = url.searchParams.get('status') || '';
|
|
858
|
-
const status = statusParam ? statusParam.split(',').map((item) => item.trim()).filter(Boolean) : undefined;
|
|
859
|
-
json(res, 200, await this.bindings.listAgentTasks({
|
|
860
|
-
workspaceId: url.searchParams.get('workspace_id') || undefined,
|
|
861
|
-
runtimeId: url.searchParams.get('runtime_id') || undefined,
|
|
862
|
-
status,
|
|
863
|
-
limit: Number(url.searchParams.get('limit') || '50'),
|
|
864
|
-
}));
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
case 'tasks.create': {
|
|
868
|
-
const body = await readJsonBody(req);
|
|
869
|
-
json(res, 200, await this.bindings.createAgentTask(body));
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
case 'task.get':
|
|
873
|
-
json(res, 200, await this.bindings.getAgentTask(route.taskId));
|
|
874
|
-
return;
|
|
875
|
-
case 'task.update': {
|
|
876
|
-
const body = await readJsonBody(req);
|
|
877
|
-
json(res, 200, await this.bindings.updateAgentTask(route.taskId, body));
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
case 'knowledge.sources.list':
|
|
881
|
-
json(res, 200, { sources: await this.bindings.listKnowledgeSources() });
|
|
882
|
-
return;
|
|
883
|
-
case 'knowledge.config.read':
|
|
884
|
-
json(res, 200, await this.bindings.getKnowledgeConfig());
|
|
885
|
-
return;
|
|
886
|
-
case 'knowledge.config.update': {
|
|
887
|
-
const body = await readJsonBody(req);
|
|
888
|
-
json(res, 200, await this.bindings.updateKnowledgeConfig(body));
|
|
889
|
-
return;
|
|
890
|
-
}
|
|
891
|
-
case 'knowledge.folders.list':
|
|
892
|
-
json(res, 200, { folders: await this.bindings.listKnowledgeFolders() });
|
|
893
|
-
return;
|
|
894
|
-
case 'knowledge.folders.create': {
|
|
895
|
-
const body = await readJsonBody(req);
|
|
896
|
-
json(res, 200, await this.bindings.createKnowledgeFolder(body));
|
|
897
|
-
return;
|
|
898
|
-
}
|
|
899
|
-
case 'knowledge.folder.update': {
|
|
900
|
-
const body = await readJsonBody(req);
|
|
901
|
-
json(res, 200, await this.bindings.updateKnowledgeFolder(route.folderId, body));
|
|
902
|
-
return;
|
|
903
|
-
}
|
|
904
|
-
case 'knowledge.folder.delete':
|
|
905
|
-
json(res, 200, await this.bindings.deleteKnowledgeFolder(route.folderId));
|
|
906
|
-
return;
|
|
907
|
-
case 'knowledge.bases.list':
|
|
908
|
-
json(res, 200, { bases: await this.bindings.listKnowledgeBases() });
|
|
909
|
-
return;
|
|
910
|
-
case 'knowledge.bases.create': {
|
|
911
|
-
const body = await readJsonBody(req);
|
|
912
|
-
json(res, 200, await this.bindings.createKnowledgeBase(body));
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
case 'knowledge.base.get':
|
|
916
|
-
json(res, 200, await this.bindings.getKnowledgeBase(route.knowledgeBaseId));
|
|
917
|
-
return;
|
|
918
|
-
case 'knowledge.base.update': {
|
|
919
|
-
const body = await readJsonBody(req);
|
|
920
|
-
json(res, 200, await this.bindings.updateKnowledgeBase(route.knowledgeBaseId, body));
|
|
921
|
-
return;
|
|
922
|
-
}
|
|
923
|
-
case 'knowledge.base.delete':
|
|
924
|
-
json(res, 200, await this.bindings.deleteKnowledgeBase(route.knowledgeBaseId));
|
|
925
|
-
return;
|
|
926
|
-
case 'knowledge.base.files.list':
|
|
927
|
-
json(res, 200, { files: await this.bindings.listKnowledgeBaseFiles(route.knowledgeBaseId) });
|
|
928
|
-
return;
|
|
929
|
-
case 'knowledge.base.files.upload': {
|
|
930
|
-
const contentType = String(req.headers['content-type'] || '').trim();
|
|
931
|
-
if (!contentType) {
|
|
932
|
-
throw new Error('Upload content type is required.');
|
|
933
|
-
}
|
|
934
|
-
const body = await readRawBody(req);
|
|
935
|
-
json(res, 200, { results: await this.bindings.uploadKnowledgeBaseFiles(route.knowledgeBaseId, { contentType, body }) });
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
case 'knowledge.base.file.delete':
|
|
939
|
-
json(res, 200, await this.bindings.deleteKnowledgeBaseFile(route.knowledgeBaseId, route.fileId));
|
|
940
|
-
return;
|
|
941
|
-
case 'knowledge.base.search': {
|
|
942
|
-
const body = await readJsonBody(req);
|
|
943
|
-
json(res, 200, { results: await this.bindings.searchKnowledgeBase(route.knowledgeBaseId, body) });
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
case 'capabilities.read':
|
|
947
|
-
json(res, 200, await this.bindings.getCapabilities());
|
|
948
|
-
return;
|
|
949
|
-
case 'capabilities.snapshot':
|
|
950
|
-
json(res, 200, await this.bindings.getCapabilitySnapshot());
|
|
951
|
-
return;
|
|
952
|
-
case 'diagnostics.errors':
|
|
953
|
-
json(res, 200, { errors: await this.bindings.listDiagnosticErrors() });
|
|
954
|
-
return;
|
|
955
|
-
case 'diagnostics.doctor':
|
|
956
|
-
json(res, 200, await this.bindings.runDiagnosticsDoctor());
|
|
957
|
-
return;
|
|
958
|
-
case 'diagnostics.deployment':
|
|
959
|
-
json(res, 200, await this.bindings.runDeploymentDiagnostics());
|
|
960
|
-
return;
|
|
961
|
-
case 'plugins.diagnostics':
|
|
962
|
-
json(res, 200, await this.bindings.getPluginDiagnostics());
|
|
963
|
-
return;
|
|
964
|
-
case 'events.stream':
|
|
965
|
-
this.attachSseClient(res);
|
|
966
|
-
return;
|
|
967
|
-
case 'platform.gateways.list':
|
|
968
|
-
json(res, 200, { gateways: await this.bindings.listChannelGatewayStatuses(route.platform) });
|
|
969
|
-
return;
|
|
970
|
-
case 'platform.pairings.list': {
|
|
971
|
-
const workspaceId = String(url.searchParams.get('workspace_id') || '');
|
|
972
|
-
json(res, 200, { pairings: await this.bindings.listChannelPendingPairings(route.platform, workspaceId || undefined) });
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
case 'platform.users.list': {
|
|
976
|
-
const workspaceId = String(url.searchParams.get('workspace_id') || '');
|
|
977
|
-
json(res, 200, { users: await this.bindings.listChannelAuthorizedUsers(route.platform, workspaceId || undefined) });
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
case 'platform.gateway.get':
|
|
981
|
-
json(res, 200, await this.bindings.getChannelGatewayStatus(route.platform, route.workspaceId, this.channelInstanceId(url)));
|
|
982
|
-
return;
|
|
983
|
-
case 'platform.qrcode.status': {
|
|
984
|
-
const ticket = String(url.searchParams.get('ticket') || '');
|
|
985
|
-
if (!ticket) {
|
|
986
|
-
jsonError(res, 400, new Error('Missing ticket parameter'));
|
|
987
|
-
return;
|
|
988
|
-
}
|
|
989
|
-
json(res, 200, await this.bindings.checkChannelQrCodeStatus(route.platform, route.workspaceId, ticket, this.channelInstanceId(url)));
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
case 'platform.pairing.approve': {
|
|
993
|
-
const body = await readJsonBody(req);
|
|
994
|
-
json(res, 200, await this.bindings.approveChannelPairing(route.platform, String(body.code || '')));
|
|
995
|
-
return;
|
|
996
|
-
}
|
|
997
|
-
case 'platform.pairing.reject': {
|
|
998
|
-
const body = await readJsonBody(req);
|
|
999
|
-
json(res, 200, await this.bindings.rejectChannelPairing(route.platform, String(body.code || '')));
|
|
1000
|
-
return;
|
|
1001
|
-
}
|
|
1002
|
-
case 'platform.gateway.test':
|
|
1003
|
-
json(res, 200, await this.bindings.testChannelConnection(route.platform, route.workspaceId, this.channelInstanceId(url)));
|
|
1004
|
-
return;
|
|
1005
|
-
case 'platform.gateway.enable':
|
|
1006
|
-
json(res, 200, await this.bindings.enableChannelGateway(route.platform, route.workspaceId, this.channelInstanceId(url)));
|
|
1007
|
-
return;
|
|
1008
|
-
case 'platform.gateway.disable':
|
|
1009
|
-
json(res, 200, await this.bindings.disableChannelGateway(route.platform, route.workspaceId, this.channelInstanceId(url)));
|
|
1010
|
-
return;
|
|
1011
|
-
case 'platform.file.send': {
|
|
1012
|
-
const body = await readJsonBody(req);
|
|
1013
|
-
json(res, 200, await this.bindings.sendChannelFile(route.platform, route.workspaceId, body));
|
|
1014
|
-
return;
|
|
1015
|
-
}
|
|
1016
|
-
case 'platform.message.send': {
|
|
1017
|
-
const body = await readJsonBody(req);
|
|
1018
|
-
json(res, 200, await this.bindings.sendChannelMessage(route.platform, route.workspaceId, body));
|
|
1019
|
-
return;
|
|
1020
|
-
}
|
|
1021
|
-
case 'platform.qrcode.create':
|
|
1022
|
-
json(res, 200, await this.bindings.getChannelQrCode(route.platform, route.workspaceId, this.channelInstanceId(url)));
|
|
1023
|
-
return;
|
|
179
|
+
(0, server_helpers_js_1.jsonError)(res, 404, new Error(`Unknown route: ${path}`));
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
(0, server_helpers_js_1.jsonError)(res, 500, error);
|
|
1024
183
|
}
|
|
1025
|
-
}
|
|
1026
|
-
channelInstanceId(url) {
|
|
1027
|
-
return String(url.searchParams.get('instance_id') || url.searchParams.get('instanceId') || '').trim() || undefined;
|
|
1028
184
|
}
|
|
1029
185
|
attachSseClient(res) {
|
|
1030
186
|
res.writeHead(200, {
|
|
@@ -1060,7 +216,7 @@ class LocalAiCoreServer {
|
|
|
1060
216
|
}, 15000);
|
|
1061
217
|
this.heartbeatTimers.set(res, heartbeat);
|
|
1062
218
|
const replayTimer = setInterval(() => {
|
|
1063
|
-
void this.bindings.
|
|
219
|
+
void this.bindings.externalService.getRunSnapshot(runId)
|
|
1064
220
|
.then((snapshot) => this.replayExternalRunMessages(res, runId, snapshot, replayedMessageIds))
|
|
1065
221
|
.catch(() => undefined);
|
|
1066
222
|
}, 1000);
|
|
@@ -1075,131 +231,15 @@ class LocalAiCoreServer {
|
|
|
1075
231
|
this.externalRunSseClients.delete(runId);
|
|
1076
232
|
}
|
|
1077
233
|
});
|
|
1078
|
-
const snapshot = await this.bindings.
|
|
1079
|
-
res.write(createSseEvent('external.run.snapshot', {
|
|
234
|
+
const snapshot = await this.bindings.externalService.getRunSnapshot(runId);
|
|
235
|
+
res.write((0, server_helpers_js_1.createSseEvent)('external.run.snapshot', {
|
|
1080
236
|
type: 'external.run.snapshot',
|
|
1081
237
|
snapshot,
|
|
1082
238
|
}));
|
|
1083
239
|
this.replayExternalRunMessages(res, runId, snapshot, replayedMessageIds);
|
|
1084
240
|
}
|
|
1085
|
-
async handleOpenAiChatCompletions(req, res) {
|
|
1086
|
-
let parsed;
|
|
1087
|
-
try {
|
|
1088
|
-
parsed = parseOpenAiChatCompletionRequest(await readJsonBody(req));
|
|
1089
|
-
}
|
|
1090
|
-
catch (error) {
|
|
1091
|
-
openAiJsonError(res, 400, error instanceof Error ? error.message : String(error));
|
|
1092
|
-
return;
|
|
1093
|
-
}
|
|
1094
|
-
if (parsed.stream) {
|
|
1095
|
-
await this.handleOpenAiStreamingChatCompletion(parsed, res);
|
|
1096
|
-
return;
|
|
1097
|
-
}
|
|
1098
|
-
await this.handleOpenAiNonStreamingChatCompletion(parsed, res);
|
|
1099
|
-
}
|
|
1100
|
-
async handleOpenAiStreamingChatCompletion(parsed, res) {
|
|
1101
|
-
try {
|
|
1102
|
-
const created = await this.bindings.createExternalRun(parsed.externalRun);
|
|
1103
|
-
const adapters = this.openAiRunStreams.get(created.run_id) || new Set();
|
|
1104
|
-
const adapter = new OpenAiChatCompletionStreamAdapter({
|
|
1105
|
-
runId: created.run_id,
|
|
1106
|
-
model: parsed.model,
|
|
1107
|
-
response: res,
|
|
1108
|
-
progressMode: parsed.progressMode,
|
|
1109
|
-
onClose: () => {
|
|
1110
|
-
adapters.delete(adapter);
|
|
1111
|
-
if (adapters.size === 0) {
|
|
1112
|
-
this.openAiRunStreams.delete(created.run_id);
|
|
1113
|
-
}
|
|
1114
|
-
},
|
|
1115
|
-
});
|
|
1116
|
-
adapters.add(adapter);
|
|
1117
|
-
this.openAiRunStreams.set(created.run_id, adapters);
|
|
1118
|
-
adapter.start();
|
|
1119
|
-
res.on('close', () => {
|
|
1120
|
-
adapters.delete(adapter);
|
|
1121
|
-
if (adapters.size === 0) {
|
|
1122
|
-
this.openAiRunStreams.delete(created.run_id);
|
|
1123
|
-
}
|
|
1124
|
-
});
|
|
1125
|
-
const snapshot = await this.bindings.getExternalRunSnapshot(created.run_id);
|
|
1126
|
-
this.replayOpenAiRunMessages(adapter, snapshot);
|
|
1127
|
-
if (isTerminalAgentTaskStatus(snapshot.task?.status)) {
|
|
1128
|
-
adapter.finish('stop', { event: 'run_finished', kind: 'status' });
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
catch (error) {
|
|
1132
|
-
openAiJsonError(res, 500, error instanceof Error ? error.message : String(error), 'agentdock_run_error');
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
async handleOpenAiNonStreamingChatCompletion(parsed, res) {
|
|
1136
|
-
let created;
|
|
1137
|
-
try {
|
|
1138
|
-
created = await this.bindings.createExternalRun(parsed.externalRun);
|
|
1139
|
-
}
|
|
1140
|
-
catch (error) {
|
|
1141
|
-
openAiJsonError(res, 500, error instanceof Error ? error.message : String(error), 'agentdock_run_error');
|
|
1142
|
-
return;
|
|
1143
|
-
}
|
|
1144
|
-
const started = Date.now();
|
|
1145
|
-
const timeoutMs = 10 * 60 * 1000;
|
|
1146
|
-
let snapshot = await this.bindings.getExternalRunSnapshot(created.run_id);
|
|
1147
|
-
while (!isTerminalAgentTaskStatus(snapshot.task?.status) && Date.now() - started < timeoutMs) {
|
|
1148
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1149
|
-
snapshot = await this.bindings.getExternalRunSnapshot(created.run_id);
|
|
1150
|
-
}
|
|
1151
|
-
if (!isTerminalAgentTaskStatus(snapshot.task?.status)) {
|
|
1152
|
-
openAiJsonError(res, 504, 'OpenAI-compatible chat completion timed out waiting for the agent run.', 'run_timeout');
|
|
1153
|
-
return;
|
|
1154
|
-
}
|
|
1155
|
-
if (snapshot.task?.status === 'failed') {
|
|
1156
|
-
openAiJsonError(res, 500, snapshot.task.error || 'Agent run failed.', 'agentdock_run_failed');
|
|
1157
|
-
return;
|
|
1158
|
-
}
|
|
1159
|
-
if (snapshot.task?.status === 'cancelled') {
|
|
1160
|
-
openAiJsonError(res, 500, snapshot.task.error || 'Agent run was cancelled.', 'agentdock_run_cancelled');
|
|
1161
|
-
return;
|
|
1162
|
-
}
|
|
1163
|
-
const messages = collectAssistantMessagesForRun(snapshot);
|
|
1164
|
-
const finalContent = messages
|
|
1165
|
-
.filter((message) => message.kind === 'final' || !message.bridgeKind || message.bridgeKind === 'assistant')
|
|
1166
|
-
.map((message) => message.content)
|
|
1167
|
-
.filter(Boolean)
|
|
1168
|
-
.join('\n\n');
|
|
1169
|
-
const response = {
|
|
1170
|
-
id: `chatcmpl_${sanitizeOpenAiId(created.run_id)}`,
|
|
1171
|
-
object: 'chat.completion',
|
|
1172
|
-
created: Math.floor(Date.now() / 1000),
|
|
1173
|
-
model: parsed.model,
|
|
1174
|
-
choices: [
|
|
1175
|
-
{
|
|
1176
|
-
index: 0,
|
|
1177
|
-
message: {
|
|
1178
|
-
role: 'assistant',
|
|
1179
|
-
content: finalContent,
|
|
1180
|
-
},
|
|
1181
|
-
finish_reason: 'stop',
|
|
1182
|
-
},
|
|
1183
|
-
],
|
|
1184
|
-
agentdock: {
|
|
1185
|
-
run_id: created.run_id,
|
|
1186
|
-
workspace_id: created.workspace_id,
|
|
1187
|
-
thread_id: created.thread_id,
|
|
1188
|
-
task_id: created.task_id,
|
|
1189
|
-
events: messages
|
|
1190
|
-
.filter((message) => message.bridgeKind && message.bridgeKind !== 'assistant')
|
|
1191
|
-
.map((message) => ({
|
|
1192
|
-
event: `${message.bridgeKind}_update`,
|
|
1193
|
-
kind: message.bridgeKind,
|
|
1194
|
-
content: message.content,
|
|
1195
|
-
tool: message.toolCall,
|
|
1196
|
-
})),
|
|
1197
|
-
},
|
|
1198
|
-
};
|
|
1199
|
-
rawJson(res, 200, response);
|
|
1200
|
-
}
|
|
1201
241
|
broadcast(event) {
|
|
1202
|
-
const payload = createSseEvent(event.type, event);
|
|
242
|
+
const payload = (0, server_helpers_js_1.createSseEvent)(event.type, event);
|
|
1203
243
|
for (const client of this.sseClients) {
|
|
1204
244
|
client.write(payload);
|
|
1205
245
|
}
|
|
@@ -1209,7 +249,7 @@ class LocalAiCoreServer {
|
|
|
1209
249
|
if (!clients?.size) {
|
|
1210
250
|
return;
|
|
1211
251
|
}
|
|
1212
|
-
const payload = createSseEvent('external.run.stream', {
|
|
252
|
+
const payload = (0, server_helpers_js_1.createSseEvent)('external.run.stream', {
|
|
1213
253
|
type: 'external.run.stream',
|
|
1214
254
|
runId,
|
|
1215
255
|
stream,
|
|
@@ -1227,17 +267,6 @@ class LocalAiCoreServer {
|
|
|
1227
267
|
adapter.handleBridgeEvent(stream);
|
|
1228
268
|
}
|
|
1229
269
|
}
|
|
1230
|
-
replayOpenAiRunMessages(adapter, snapshot) {
|
|
1231
|
-
for (const message of collectAssistantMessagesForRun(snapshot)) {
|
|
1232
|
-
adapter.replayMessage({
|
|
1233
|
-
id: message.id,
|
|
1234
|
-
content: message.content,
|
|
1235
|
-
bridgeKind: message.bridgeKind,
|
|
1236
|
-
bridgeStatus: message.bridgeStatus,
|
|
1237
|
-
toolCall: message.toolCall,
|
|
1238
|
-
});
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
270
|
replayExternalRunMessages(res, runId, snapshot, replayedMessageIds) {
|
|
1242
271
|
const thread = snapshot.thread;
|
|
1243
272
|
const startedAt = Date.parse(snapshot.task?.startedAt || snapshot.task?.createdAt || '');
|
|
@@ -1256,7 +285,7 @@ class LocalAiCoreServer {
|
|
|
1256
285
|
continue;
|
|
1257
286
|
}
|
|
1258
287
|
replayedMessageIds.add(message.id);
|
|
1259
|
-
res.write(createSseEvent('external.run.stream', {
|
|
288
|
+
res.write((0, server_helpers_js_1.createSseEvent)('external.run.stream', {
|
|
1260
289
|
type: 'external.run.stream',
|
|
1261
290
|
runId,
|
|
1262
291
|
stream: {
|
|
@@ -1270,12 +299,6 @@ class LocalAiCoreServer {
|
|
|
1270
299
|
}));
|
|
1271
300
|
}
|
|
1272
301
|
}
|
|
1273
|
-
async runtimeDetectionResponse() {
|
|
1274
|
-
return {
|
|
1275
|
-
runtimes: await this.bindings.listInstalledAgentRuntimes(),
|
|
1276
|
-
checking: this.bindings.isRuntimeDetectionRunning(),
|
|
1277
|
-
};
|
|
1278
|
-
}
|
|
1279
302
|
findThreadIdFromSessionKey(sessionKey) {
|
|
1280
303
|
const parts = sessionKey.split(':');
|
|
1281
304
|
if (parts.length < 3) {
|