agentxjs 0.0.0-dev-20260312143810
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 +293 -0
- package/dist/chunk-X44CQZPK.js +569 -0
- package/dist/chunk-X44CQZPK.js.map +1 -0
- package/dist/index.d.ts +787 -0
- package/dist/index.js +1088 -0
- package/dist/index.js.map +1 -0
- package/dist/server-BWI5JE4B.js +7 -0
- package/dist/server-BWI5JE4B.js.map +1 -0
- package/package.json +39 -0
- package/src/CommandHandler.ts +557 -0
- package/src/LocalClient.ts +118 -0
- package/src/RemoteClient.ts +132 -0
- package/src/index.ts +228 -0
- package/src/namespaces/agents.ts +121 -0
- package/src/namespaces/containers.ts +68 -0
- package/src/namespaces/images.ts +192 -0
- package/src/namespaces/llm.ts +140 -0
- package/src/namespaces/presentations.ts +22 -0
- package/src/namespaces/sessions.ts +60 -0
- package/src/presentation/Presentation.ts +193 -0
- package/src/presentation/index.ts +31 -0
- package/src/presentation/reducer.ts +486 -0
- package/src/presentation/types.ts +121 -0
- package/src/server.ts +346 -0
- package/src/types.ts +558 -0
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandHandler - Handles JSON-RPC requests directly
|
|
3
|
+
*
|
|
4
|
+
* No longer uses EventBus for request/response. Instead:
|
|
5
|
+
* - Receives RPC requests directly
|
|
6
|
+
* - Returns RPC responses directly
|
|
7
|
+
* - EventBus is only used for stream events (notifications)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { UserContentPart } from "@agentxjs/core/agent";
|
|
11
|
+
import type { RpcMethod } from "@agentxjs/core/network";
|
|
12
|
+
import type { AgentXRuntime } from "@agentxjs/core/runtime";
|
|
13
|
+
import { createLogger } from "@deepracticex/logger";
|
|
14
|
+
|
|
15
|
+
const logger = createLogger("server/CommandHandler");
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* RPC Result type
|
|
19
|
+
*/
|
|
20
|
+
export interface RpcResult<T = unknown> {
|
|
21
|
+
success: true;
|
|
22
|
+
data: T;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface RpcError {
|
|
26
|
+
success: false;
|
|
27
|
+
code: number;
|
|
28
|
+
message: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type RpcResponse<T = unknown> = RpcResult<T> | RpcError;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Helper to create success result
|
|
35
|
+
*/
|
|
36
|
+
function ok<T>(data: T): RpcResult<T> {
|
|
37
|
+
return { success: true, data };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Helper to create error result
|
|
42
|
+
*/
|
|
43
|
+
function err(code: number, message: string): RpcError {
|
|
44
|
+
return { success: false, code, message };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* CommandHandler - Processes RPC requests directly
|
|
49
|
+
*/
|
|
50
|
+
export class CommandHandler {
|
|
51
|
+
private readonly runtime: AgentXRuntime;
|
|
52
|
+
|
|
53
|
+
constructor(runtime: AgentXRuntime) {
|
|
54
|
+
this.runtime = runtime;
|
|
55
|
+
logger.debug("CommandHandler created");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Handle an RPC request and return response
|
|
60
|
+
*/
|
|
61
|
+
async handle(method: RpcMethod, params: unknown): Promise<RpcResponse> {
|
|
62
|
+
logger.debug("Handling RPC request", { method });
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
switch (method) {
|
|
66
|
+
// Container
|
|
67
|
+
case "container.create":
|
|
68
|
+
return await this.handleContainerCreate(params);
|
|
69
|
+
case "container.get":
|
|
70
|
+
return await this.handleContainerGet(params);
|
|
71
|
+
case "container.list":
|
|
72
|
+
return await this.handleContainerList(params);
|
|
73
|
+
|
|
74
|
+
// Image
|
|
75
|
+
case "image.create":
|
|
76
|
+
return await this.handleImageCreate(params);
|
|
77
|
+
case "image.get":
|
|
78
|
+
return await this.handleImageGet(params);
|
|
79
|
+
case "image.list":
|
|
80
|
+
return await this.handleImageList(params);
|
|
81
|
+
case "image.delete":
|
|
82
|
+
return await this.handleImageDelete(params);
|
|
83
|
+
case "image.run":
|
|
84
|
+
return await this.handleImageRun(params);
|
|
85
|
+
case "image.stop":
|
|
86
|
+
return await this.handleImageStop(params);
|
|
87
|
+
case "image.update":
|
|
88
|
+
return await this.handleImageUpdate(params);
|
|
89
|
+
case "image.messages":
|
|
90
|
+
return await this.handleImageMessages(params);
|
|
91
|
+
|
|
92
|
+
// Agent
|
|
93
|
+
case "agent.get":
|
|
94
|
+
return await this.handleAgentGet(params);
|
|
95
|
+
case "agent.list":
|
|
96
|
+
return await this.handleAgentList(params);
|
|
97
|
+
case "agent.destroy":
|
|
98
|
+
return await this.handleAgentDestroy(params);
|
|
99
|
+
case "agent.destroyAll":
|
|
100
|
+
return await this.handleAgentDestroyAll(params);
|
|
101
|
+
case "agent.interrupt":
|
|
102
|
+
return await this.handleAgentInterrupt(params);
|
|
103
|
+
|
|
104
|
+
// Message
|
|
105
|
+
case "message.send":
|
|
106
|
+
return await this.handleMessageSend(params);
|
|
107
|
+
|
|
108
|
+
// LLM Provider
|
|
109
|
+
case "llm.create":
|
|
110
|
+
return await this.handleLLMCreate(params);
|
|
111
|
+
case "llm.get":
|
|
112
|
+
return await this.handleLLMGet(params);
|
|
113
|
+
case "llm.list":
|
|
114
|
+
return await this.handleLLMList(params);
|
|
115
|
+
case "llm.update":
|
|
116
|
+
return await this.handleLLMUpdate(params);
|
|
117
|
+
case "llm.delete":
|
|
118
|
+
return await this.handleLLMDelete(params);
|
|
119
|
+
case "llm.default":
|
|
120
|
+
return await this.handleLLMDefault(params);
|
|
121
|
+
|
|
122
|
+
default:
|
|
123
|
+
return err(-32601, `Method not found: ${method}`);
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
logger.error("RPC handler error", { method, error });
|
|
127
|
+
return err(-32000, error instanceof Error ? error.message : String(error));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ==================== Container Commands ====================
|
|
132
|
+
|
|
133
|
+
private async handleContainerCreate(params: unknown): Promise<RpcResponse> {
|
|
134
|
+
const { containerId } = params as { containerId: string };
|
|
135
|
+
const { getOrCreateContainer } = await import("@agentxjs/core/container");
|
|
136
|
+
const { containerRepository, imageRepository, sessionRepository } = this.runtime.platform;
|
|
137
|
+
|
|
138
|
+
const container = await getOrCreateContainer(containerId, {
|
|
139
|
+
containerRepository,
|
|
140
|
+
imageRepository,
|
|
141
|
+
sessionRepository,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return ok({ containerId: container.containerId });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private async handleContainerGet(params: unknown): Promise<RpcResponse> {
|
|
148
|
+
const { containerId } = params as { containerId: string };
|
|
149
|
+
const exists = await this.runtime.platform.containerRepository.containerExists(containerId);
|
|
150
|
+
return ok({ containerId, exists });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private async handleContainerList(_params: unknown): Promise<RpcResponse> {
|
|
154
|
+
const containers = await this.runtime.platform.containerRepository.findAllContainers();
|
|
155
|
+
return ok({ containerIds: containers.map((c) => c.containerId) });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ==================== Image Commands ====================
|
|
159
|
+
|
|
160
|
+
private async handleImageCreate(params: unknown): Promise<RpcResponse> {
|
|
161
|
+
const { containerId, name, description, systemPrompt, mcpServers, customData } = params as {
|
|
162
|
+
containerId: string;
|
|
163
|
+
name?: string;
|
|
164
|
+
description?: string;
|
|
165
|
+
systemPrompt?: string;
|
|
166
|
+
mcpServers?: Record<string, unknown>;
|
|
167
|
+
customData?: Record<string, unknown>;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const { imageRepository, sessionRepository } = this.runtime.platform;
|
|
171
|
+
const { createImage } = await import("@agentxjs/core/image");
|
|
172
|
+
|
|
173
|
+
const image = await createImage(
|
|
174
|
+
{ containerId, name, description, systemPrompt, mcpServers: mcpServers as any, customData },
|
|
175
|
+
{ imageRepository, sessionRepository }
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
return ok({
|
|
179
|
+
record: image.toRecord(),
|
|
180
|
+
__subscriptions: [image.sessionId],
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private async handleImageGet(params: unknown): Promise<RpcResponse> {
|
|
185
|
+
const { imageId } = params as { imageId: string };
|
|
186
|
+
const record = await this.runtime.platform.imageRepository.findImageById(imageId);
|
|
187
|
+
return ok({
|
|
188
|
+
record,
|
|
189
|
+
__subscriptions: record?.sessionId ? [record.sessionId] : undefined,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private async handleImageList(params: unknown): Promise<RpcResponse> {
|
|
194
|
+
const { containerId } = params as { containerId?: string };
|
|
195
|
+
const records = containerId
|
|
196
|
+
? await this.runtime.platform.imageRepository.findImagesByContainerId(containerId)
|
|
197
|
+
: await this.runtime.platform.imageRepository.findAllImages();
|
|
198
|
+
|
|
199
|
+
return ok({
|
|
200
|
+
records,
|
|
201
|
+
__subscriptions: records.map((r) => r.sessionId),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private async handleImageDelete(params: unknown): Promise<RpcResponse> {
|
|
206
|
+
const { imageId } = params as { imageId: string };
|
|
207
|
+
const { loadImage } = await import("@agentxjs/core/image");
|
|
208
|
+
const { imageRepository, sessionRepository } = this.runtime.platform;
|
|
209
|
+
|
|
210
|
+
const image = await loadImage(imageId, { imageRepository, sessionRepository });
|
|
211
|
+
if (image) {
|
|
212
|
+
await image.delete();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return ok({ imageId });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private async handleImageRun(params: unknown): Promise<RpcResponse> {
|
|
219
|
+
const { imageId, agentId: requestedAgentId } = params as {
|
|
220
|
+
imageId: string;
|
|
221
|
+
agentId?: string;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Check if already have a running agent for this image
|
|
225
|
+
const existingAgent = this.runtime
|
|
226
|
+
.getAgents()
|
|
227
|
+
.find((a) => a.imageId === imageId && a.lifecycle === "running");
|
|
228
|
+
|
|
229
|
+
if (existingAgent) {
|
|
230
|
+
logger.debug("Reusing existing agent for image", {
|
|
231
|
+
imageId,
|
|
232
|
+
agentId: existingAgent.agentId,
|
|
233
|
+
});
|
|
234
|
+
return ok({
|
|
235
|
+
imageId,
|
|
236
|
+
agentId: existingAgent.agentId,
|
|
237
|
+
sessionId: existingAgent.sessionId,
|
|
238
|
+
containerId: existingAgent.containerId,
|
|
239
|
+
reused: true,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Create new agent (with optional custom agentId)
|
|
244
|
+
const agent = await this.runtime.createAgent({
|
|
245
|
+
imageId,
|
|
246
|
+
agentId: requestedAgentId,
|
|
247
|
+
});
|
|
248
|
+
logger.info("Created new agent for image", {
|
|
249
|
+
imageId,
|
|
250
|
+
agentId: agent.agentId,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
return ok({
|
|
254
|
+
imageId,
|
|
255
|
+
agentId: agent.agentId,
|
|
256
|
+
sessionId: agent.sessionId,
|
|
257
|
+
containerId: agent.containerId,
|
|
258
|
+
reused: false,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private async handleImageStop(params: unknown): Promise<RpcResponse> {
|
|
263
|
+
const { imageId } = params as { imageId: string };
|
|
264
|
+
|
|
265
|
+
// Find running agent for this image
|
|
266
|
+
const agent = this.runtime
|
|
267
|
+
.getAgents()
|
|
268
|
+
.find((a) => a.imageId === imageId && a.lifecycle === "running");
|
|
269
|
+
|
|
270
|
+
if (agent) {
|
|
271
|
+
await this.runtime.stopAgent(agent.agentId);
|
|
272
|
+
logger.info("Stopped agent for image", { imageId, agentId: agent.agentId });
|
|
273
|
+
} else {
|
|
274
|
+
logger.debug("No running agent found for image", { imageId });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return ok({ imageId });
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private async handleImageUpdate(params: unknown): Promise<RpcResponse> {
|
|
281
|
+
const { imageId, updates } = params as {
|
|
282
|
+
imageId: string;
|
|
283
|
+
updates: { name?: string; description?: string; customData?: Record<string, unknown> };
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// Get existing image
|
|
287
|
+
const imageRecord = await this.runtime.platform.imageRepository.findImageById(imageId);
|
|
288
|
+
if (!imageRecord) {
|
|
289
|
+
return err(404, `Image not found: ${imageId}`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Update image record
|
|
293
|
+
const updatedRecord = {
|
|
294
|
+
...imageRecord,
|
|
295
|
+
...updates,
|
|
296
|
+
updatedAt: Date.now(),
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
await this.runtime.platform.imageRepository.saveImage(updatedRecord);
|
|
300
|
+
|
|
301
|
+
logger.info("Updated image", { imageId, updates });
|
|
302
|
+
|
|
303
|
+
return ok({ record: updatedRecord });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private async handleImageMessages(params: unknown): Promise<RpcResponse> {
|
|
307
|
+
const { imageId } = params as { imageId: string };
|
|
308
|
+
|
|
309
|
+
// Get image record to find sessionId
|
|
310
|
+
const imageRecord = await this.runtime.platform.imageRepository.findImageById(imageId);
|
|
311
|
+
if (!imageRecord) {
|
|
312
|
+
return err(404, `Image not found: ${imageId}`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Get messages from session
|
|
316
|
+
const messages = await this.runtime.platform.sessionRepository.getMessages(
|
|
317
|
+
imageRecord.sessionId
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
logger.debug("Got messages for image", { imageId, count: messages.length });
|
|
321
|
+
|
|
322
|
+
return ok({ imageId, messages });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ==================== Agent Commands ====================
|
|
326
|
+
|
|
327
|
+
private async handleAgentGet(params: unknown): Promise<RpcResponse> {
|
|
328
|
+
const { agentId } = params as { agentId: string };
|
|
329
|
+
const agent = this.runtime.getAgent(agentId);
|
|
330
|
+
|
|
331
|
+
return ok({
|
|
332
|
+
agent: agent
|
|
333
|
+
? {
|
|
334
|
+
agentId: agent.agentId,
|
|
335
|
+
imageId: agent.imageId,
|
|
336
|
+
containerId: agent.containerId,
|
|
337
|
+
sessionId: agent.sessionId,
|
|
338
|
+
lifecycle: agent.lifecycle,
|
|
339
|
+
}
|
|
340
|
+
: null,
|
|
341
|
+
exists: !!agent,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private async handleAgentList(params: unknown): Promise<RpcResponse> {
|
|
346
|
+
const { containerId } = params as { containerId?: string };
|
|
347
|
+
const agents = containerId
|
|
348
|
+
? this.runtime.getAgentsByContainer(containerId)
|
|
349
|
+
: this.runtime.getAgents();
|
|
350
|
+
|
|
351
|
+
return ok({
|
|
352
|
+
agents: agents.map((a) => ({
|
|
353
|
+
agentId: a.agentId,
|
|
354
|
+
imageId: a.imageId,
|
|
355
|
+
containerId: a.containerId,
|
|
356
|
+
sessionId: a.sessionId,
|
|
357
|
+
lifecycle: a.lifecycle,
|
|
358
|
+
})),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private async handleAgentDestroy(params: unknown): Promise<RpcResponse> {
|
|
363
|
+
const { agentId } = params as { agentId: string };
|
|
364
|
+
|
|
365
|
+
// Check if agent exists first
|
|
366
|
+
const agent = this.runtime.getAgent(agentId);
|
|
367
|
+
if (!agent) {
|
|
368
|
+
return ok({ agentId, success: false });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
await this.runtime.destroyAgent(agentId);
|
|
372
|
+
return ok({ agentId, success: true });
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
private async handleAgentDestroyAll(params: unknown): Promise<RpcResponse> {
|
|
376
|
+
const { containerId } = params as { containerId: string };
|
|
377
|
+
const agents = this.runtime.getAgentsByContainer(containerId);
|
|
378
|
+
for (const agent of agents) {
|
|
379
|
+
await this.runtime.destroyAgent(agent.agentId);
|
|
380
|
+
}
|
|
381
|
+
return ok({ containerId });
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private async handleAgentInterrupt(params: unknown): Promise<RpcResponse> {
|
|
385
|
+
const { agentId } = params as { agentId: string };
|
|
386
|
+
this.runtime.interrupt(agentId);
|
|
387
|
+
return ok({ agentId });
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// ==================== Message Commands ====================
|
|
391
|
+
|
|
392
|
+
private async handleMessageSend(params: unknown): Promise<RpcResponse> {
|
|
393
|
+
const { agentId, imageId, content } = params as {
|
|
394
|
+
agentId?: string;
|
|
395
|
+
imageId?: string;
|
|
396
|
+
content: string | UserContentPart[];
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
let targetAgentId: string;
|
|
400
|
+
|
|
401
|
+
if (agentId) {
|
|
402
|
+
// Direct agent reference
|
|
403
|
+
targetAgentId = agentId;
|
|
404
|
+
} else if (imageId) {
|
|
405
|
+
// Auto-activate image: find or create agent
|
|
406
|
+
const existingAgent = this.runtime
|
|
407
|
+
.getAgents()
|
|
408
|
+
.find((a) => a.imageId === imageId && a.lifecycle === "running");
|
|
409
|
+
|
|
410
|
+
if (existingAgent) {
|
|
411
|
+
targetAgentId = existingAgent.agentId;
|
|
412
|
+
logger.debug("Using existing agent for message", {
|
|
413
|
+
imageId,
|
|
414
|
+
agentId: targetAgentId,
|
|
415
|
+
});
|
|
416
|
+
} else {
|
|
417
|
+
// Create new agent for this image
|
|
418
|
+
const agent = await this.runtime.createAgent({ imageId });
|
|
419
|
+
targetAgentId = agent.agentId;
|
|
420
|
+
logger.info("Auto-created agent for message", {
|
|
421
|
+
imageId,
|
|
422
|
+
agentId: targetAgentId,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
return err(-32602, "Either agentId or imageId is required");
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
await this.runtime.receive(targetAgentId, content);
|
|
430
|
+
return ok({ agentId: targetAgentId, imageId });
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ==================== LLM Provider Commands ====================
|
|
434
|
+
|
|
435
|
+
private async handleLLMCreate(params: unknown): Promise<RpcResponse> {
|
|
436
|
+
const { containerId, name, vendor, protocol, apiKey, baseUrl, model } = params as {
|
|
437
|
+
containerId: string;
|
|
438
|
+
name: string;
|
|
439
|
+
vendor: string;
|
|
440
|
+
protocol: string;
|
|
441
|
+
apiKey: string;
|
|
442
|
+
baseUrl?: string;
|
|
443
|
+
model?: string;
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const repo = this.runtime.platform.llmProviderRepository;
|
|
447
|
+
if (!repo) {
|
|
448
|
+
return err(-32000, "LLM provider repository not available");
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const { generateId } = await import("@deepracticex/id");
|
|
452
|
+
const now = Date.now();
|
|
453
|
+
const record = {
|
|
454
|
+
id: generateId("llm"),
|
|
455
|
+
containerId,
|
|
456
|
+
name,
|
|
457
|
+
vendor,
|
|
458
|
+
protocol: protocol as "anthropic" | "openai",
|
|
459
|
+
apiKey,
|
|
460
|
+
baseUrl,
|
|
461
|
+
model,
|
|
462
|
+
isDefault: false,
|
|
463
|
+
createdAt: now,
|
|
464
|
+
updatedAt: now,
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
await repo.saveLLMProvider(record);
|
|
468
|
+
return ok({ record });
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private async handleLLMGet(params: unknown): Promise<RpcResponse> {
|
|
472
|
+
const { id } = params as { id: string };
|
|
473
|
+
const repo = this.runtime.platform.llmProviderRepository;
|
|
474
|
+
if (!repo) {
|
|
475
|
+
return err(-32000, "LLM provider repository not available");
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const record = await repo.findLLMProviderById(id);
|
|
479
|
+
return ok({ record });
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private async handleLLMList(params: unknown): Promise<RpcResponse> {
|
|
483
|
+
const { containerId } = params as { containerId: string };
|
|
484
|
+
const repo = this.runtime.platform.llmProviderRepository;
|
|
485
|
+
if (!repo) {
|
|
486
|
+
return err(-32000, "LLM provider repository not available");
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const records = await repo.findLLMProvidersByContainerId(containerId);
|
|
490
|
+
return ok({ records });
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
private async handleLLMUpdate(params: unknown): Promise<RpcResponse> {
|
|
494
|
+
const { id, updates } = params as {
|
|
495
|
+
id: string;
|
|
496
|
+
updates: Record<string, unknown>;
|
|
497
|
+
};
|
|
498
|
+
const repo = this.runtime.platform.llmProviderRepository;
|
|
499
|
+
if (!repo) {
|
|
500
|
+
return err(-32000, "LLM provider repository not available");
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const existing = await repo.findLLMProviderById(id);
|
|
504
|
+
if (!existing) {
|
|
505
|
+
return err(404, `LLM provider not found: ${id}`);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const updated = {
|
|
509
|
+
...existing,
|
|
510
|
+
...updates,
|
|
511
|
+
id: existing.id,
|
|
512
|
+
containerId: existing.containerId,
|
|
513
|
+
createdAt: existing.createdAt,
|
|
514
|
+
updatedAt: Date.now(),
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
await repo.saveLLMProvider(updated);
|
|
518
|
+
return ok({ record: updated });
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
private async handleLLMDelete(params: unknown): Promise<RpcResponse> {
|
|
522
|
+
const { id } = params as { id: string };
|
|
523
|
+
const repo = this.runtime.platform.llmProviderRepository;
|
|
524
|
+
if (!repo) {
|
|
525
|
+
return err(-32000, "LLM provider repository not available");
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
await repo.deleteLLMProvider(id);
|
|
529
|
+
return ok({ id });
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
private async handleLLMDefault(params: unknown): Promise<RpcResponse> {
|
|
533
|
+
const { id, containerId } = params as { id?: string; containerId?: string };
|
|
534
|
+
const repo = this.runtime.platform.llmProviderRepository;
|
|
535
|
+
if (!repo) {
|
|
536
|
+
return err(-32000, "LLM provider repository not available");
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (id) {
|
|
540
|
+
// Set default
|
|
541
|
+
await repo.setDefaultLLMProvider(id);
|
|
542
|
+
return ok({ id });
|
|
543
|
+
}
|
|
544
|
+
if (containerId) {
|
|
545
|
+
// Get default
|
|
546
|
+
const record = await repo.findDefaultLLMProvider(containerId);
|
|
547
|
+
return ok({ record });
|
|
548
|
+
}
|
|
549
|
+
return err(-32602, "Either id or containerId is required");
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// ==================== Lifecycle ====================
|
|
553
|
+
|
|
554
|
+
dispose(): void {
|
|
555
|
+
logger.debug("CommandHandler disposed");
|
|
556
|
+
}
|
|
557
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalClient - AgentX client for local mode
|
|
3
|
+
*
|
|
4
|
+
* Runs an embedded Runtime + Driver directly, without WebSocket.
|
|
5
|
+
* Implements the same AgentX interface as RemoteClient.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AgentXError } from "@agentxjs/core/error";
|
|
9
|
+
import type { BusEvent, BusEventHandler, EventBus, Unsubscribe } from "@agentxjs/core/event";
|
|
10
|
+
import type { RpcMethod } from "@agentxjs/core/network";
|
|
11
|
+
import type { AgentXRuntime } from "@agentxjs/core/runtime";
|
|
12
|
+
import { createLogger } from "commonxjs/logger";
|
|
13
|
+
import { CommandHandler } from "./CommandHandler";
|
|
14
|
+
import { createLocalAgents } from "./namespaces/agents";
|
|
15
|
+
import { createLocalContainers } from "./namespaces/containers";
|
|
16
|
+
import { createLocalImages } from "./namespaces/images";
|
|
17
|
+
import { createLocalLLM } from "./namespaces/llm";
|
|
18
|
+
import { createPresentations } from "./namespaces/presentations";
|
|
19
|
+
import { createLocalSessions } from "./namespaces/sessions";
|
|
20
|
+
import type {
|
|
21
|
+
AgentNamespace,
|
|
22
|
+
AgentX,
|
|
23
|
+
ContainerNamespace,
|
|
24
|
+
ImageNamespace,
|
|
25
|
+
LLMNamespace,
|
|
26
|
+
PresentationNamespace,
|
|
27
|
+
SessionNamespace,
|
|
28
|
+
} from "./types";
|
|
29
|
+
|
|
30
|
+
const logger = createLogger("agentx/LocalClient");
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* LocalClient - Embedded runtime implementation
|
|
34
|
+
*/
|
|
35
|
+
export class LocalClient implements AgentX {
|
|
36
|
+
private readonly runtime: AgentXRuntime;
|
|
37
|
+
private commandHandler: CommandHandler | null = null;
|
|
38
|
+
private isDisposed = false;
|
|
39
|
+
|
|
40
|
+
readonly container: ContainerNamespace;
|
|
41
|
+
readonly image: ImageNamespace;
|
|
42
|
+
readonly agent: AgentNamespace;
|
|
43
|
+
readonly session: SessionNamespace;
|
|
44
|
+
readonly presentation: PresentationNamespace;
|
|
45
|
+
readonly llm: LLMNamespace;
|
|
46
|
+
|
|
47
|
+
constructor(runtime: AgentXRuntime) {
|
|
48
|
+
this.runtime = runtime;
|
|
49
|
+
const platform = runtime.platform;
|
|
50
|
+
|
|
51
|
+
this.container = createLocalContainers(platform);
|
|
52
|
+
this.image = createLocalImages(platform);
|
|
53
|
+
this.agent = createLocalAgents(runtime);
|
|
54
|
+
this.session = createLocalSessions(runtime);
|
|
55
|
+
this.presentation = createPresentations(this);
|
|
56
|
+
this.llm = createLocalLLM(platform);
|
|
57
|
+
|
|
58
|
+
logger.info("LocalClient initialized");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ==================== Properties ====================
|
|
62
|
+
|
|
63
|
+
get connected(): boolean {
|
|
64
|
+
return !this.isDisposed;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get events(): EventBus {
|
|
68
|
+
return this.runtime.platform.eventBus;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ==================== Event Subscription ====================
|
|
72
|
+
|
|
73
|
+
on<T extends string>(type: T, handler: BusEventHandler<BusEvent & { type: T }>): Unsubscribe {
|
|
74
|
+
return this.runtime.platform.eventBus.on(type, handler);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
onAny(handler: BusEventHandler): Unsubscribe {
|
|
78
|
+
return this.runtime.platform.eventBus.onAny(handler);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
subscribe(_sessionId: string): void {
|
|
82
|
+
// No-op for local mode - already subscribed via eventBus
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ==================== Error Handling ====================
|
|
86
|
+
|
|
87
|
+
onError(handler: (error: AgentXError) => void): Unsubscribe {
|
|
88
|
+
return this.runtime.platform.eventBus.on("agentx_error", (event) => {
|
|
89
|
+
handler(event.data as AgentXError);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ==================== RPC ====================
|
|
94
|
+
|
|
95
|
+
async rpc<T = unknown>(method: string, params?: unknown): Promise<T> {
|
|
96
|
+
if (!this.commandHandler) {
|
|
97
|
+
this.commandHandler = new CommandHandler(this.runtime);
|
|
98
|
+
}
|
|
99
|
+
const result = await this.commandHandler.handle(method as RpcMethod, params);
|
|
100
|
+
if (result.success) {
|
|
101
|
+
return result.data as T;
|
|
102
|
+
}
|
|
103
|
+
throw new Error(result.message);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ==================== Lifecycle ====================
|
|
107
|
+
|
|
108
|
+
async disconnect(): Promise<void> {
|
|
109
|
+
// No-op for local mode
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async dispose(): Promise<void> {
|
|
113
|
+
if (this.isDisposed) return;
|
|
114
|
+
await this.runtime.shutdown();
|
|
115
|
+
this.isDisposed = true;
|
|
116
|
+
logger.info("LocalClient disposed");
|
|
117
|
+
}
|
|
118
|
+
}
|