@radaros/admin 0.3.20 → 0.3.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1030 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ConfigStore: () => ConfigStore,
24
+ EntityFactory: () => EntityFactory,
25
+ ToolkitManager: () => ToolkitManager,
26
+ createAdminGateway: () => createAdminGateway,
27
+ createAdminRouter: () => createAdminRouter
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/admin-gateway.ts
32
+ var import_core3 = require("@radaros/core");
33
+
34
+ // src/config-store.ts
35
+ var NS_AGENTS = "radaros:admin:agents";
36
+ var NS_TEAMS = "radaros:admin:teams";
37
+ var NS_WORKFLOWS = "radaros:admin:workflows";
38
+ var ConfigStore = class {
39
+ constructor(storage) {
40
+ this.storage = storage;
41
+ }
42
+ async initialize() {
43
+ await this.storage.initialize?.();
44
+ }
45
+ // ── Agents ────────────────────────────────────────────────────────────
46
+ async saveAgent(blueprint) {
47
+ await this.storage.set(NS_AGENTS, blueprint.name, blueprint);
48
+ }
49
+ async loadAgent(name) {
50
+ return this.storage.get(NS_AGENTS, name);
51
+ }
52
+ async deleteAgent(name) {
53
+ await this.storage.delete(NS_AGENTS, name);
54
+ }
55
+ async listAgents() {
56
+ const entries = await this.storage.list(NS_AGENTS);
57
+ return entries.map((e) => e.value);
58
+ }
59
+ // ── Teams ─────────────────────────────────────────────────────────────
60
+ async saveTeam(blueprint) {
61
+ await this.storage.set(NS_TEAMS, blueprint.name, blueprint);
62
+ }
63
+ async loadTeam(name) {
64
+ return this.storage.get(NS_TEAMS, name);
65
+ }
66
+ async deleteTeam(name) {
67
+ await this.storage.delete(NS_TEAMS, name);
68
+ }
69
+ async listTeams() {
70
+ const entries = await this.storage.list(NS_TEAMS);
71
+ return entries.map((e) => e.value);
72
+ }
73
+ // ── Workflows ─────────────────────────────────────────────────────────
74
+ async saveWorkflow(blueprint) {
75
+ await this.storage.set(NS_WORKFLOWS, blueprint.name, blueprint);
76
+ }
77
+ async loadWorkflow(name) {
78
+ return this.storage.get(NS_WORKFLOWS, name);
79
+ }
80
+ async deleteWorkflow(name) {
81
+ await this.storage.delete(NS_WORKFLOWS, name);
82
+ }
83
+ async listWorkflows() {
84
+ const entries = await this.storage.list(NS_WORKFLOWS);
85
+ return entries.map((e) => e.value);
86
+ }
87
+ };
88
+
89
+ // src/entity-factory.ts
90
+ var import_core = require("@radaros/core");
91
+ var EntityFactory = class {
92
+ toolSource;
93
+ constructor(toolSource = {}) {
94
+ this.toolSource = toolSource;
95
+ }
96
+ get toolLibrary() {
97
+ return typeof this.toolSource === "function" ? this.toolSource() : this.toolSource;
98
+ }
99
+ createAgent(blueprint) {
100
+ this.validateProvider(blueprint.provider);
101
+ const tools = this.resolveTools(blueprint.tools ?? []);
102
+ const model = import_core.modelRegistry.resolve(blueprint.provider, blueprint.model, blueprint.providerConfig);
103
+ return new import_core.Agent({
104
+ name: blueprint.name,
105
+ model,
106
+ instructions: blueprint.instructions,
107
+ tools: tools.length > 0 ? tools : void 0,
108
+ temperature: blueprint.temperature
109
+ });
110
+ }
111
+ createTeam(blueprint) {
112
+ this.validateProvider(blueprint.provider);
113
+ const members = this.resolveMembers(blueprint.members);
114
+ const mode = this.resolveTeamMode(blueprint.mode);
115
+ const model = import_core.modelRegistry.resolve(blueprint.provider, blueprint.model, blueprint.providerConfig);
116
+ return new import_core.Team({
117
+ name: blueprint.name,
118
+ mode,
119
+ model,
120
+ members,
121
+ instructions: blueprint.instructions
122
+ });
123
+ }
124
+ /**
125
+ * Destroy a live entity by removing it from the global registry.
126
+ * Returns true if found and removed.
127
+ */
128
+ destroyAgent(name) {
129
+ const agent = import_core.registry.getAgent(name);
130
+ if (!agent) return false;
131
+ return import_core.registry.remove(agent);
132
+ }
133
+ destroyTeam(name) {
134
+ const team = import_core.registry.getTeam(name);
135
+ if (!team) return false;
136
+ return import_core.registry.remove(team);
137
+ }
138
+ destroyWorkflow(name) {
139
+ const workflow = import_core.registry.getWorkflow(name);
140
+ if (!workflow) return false;
141
+ return import_core.registry.remove(workflow);
142
+ }
143
+ validateProvider(provider) {
144
+ if (!import_core.modelRegistry.has(provider)) {
145
+ throw new Error(
146
+ `Unknown model provider "${provider}". Registered providers can be checked via modelRegistry.has().`
147
+ );
148
+ }
149
+ }
150
+ resolveTools(toolNames) {
151
+ const resolved = [];
152
+ for (const name of toolNames) {
153
+ const tool = this.toolLibrary[name];
154
+ if (!tool) {
155
+ const available = Object.keys(this.toolLibrary).join(", ") || "(none)";
156
+ throw new Error(`Tool "${name}" not found in toolLibrary. Available: ${available}`);
157
+ }
158
+ resolved.push(tool);
159
+ }
160
+ return resolved;
161
+ }
162
+ resolveMembers(memberNames) {
163
+ const members = [];
164
+ for (const name of memberNames) {
165
+ const agent = import_core.registry.getAgent(name);
166
+ if (!agent) {
167
+ const available = import_core.registry.list().agents.join(", ") || "(none)";
168
+ throw new Error(`Agent "${name}" not found in registry. Available agents: ${available}`);
169
+ }
170
+ members.push(agent);
171
+ }
172
+ return members;
173
+ }
174
+ resolveTeamMode(mode) {
175
+ const normalized = mode.toLowerCase();
176
+ const modeMap = {
177
+ coordinate: import_core.TeamMode.Coordinate,
178
+ route: import_core.TeamMode.Route,
179
+ broadcast: import_core.TeamMode.Broadcast,
180
+ collaborate: import_core.TeamMode.Collaborate,
181
+ handoff: import_core.TeamMode.Handoff
182
+ };
183
+ const resolved = modeMap[normalized];
184
+ if (!resolved) {
185
+ throw new Error(`Unknown team mode "${mode}". Expected: ${Object.keys(modeMap).join(", ")}`);
186
+ }
187
+ return resolved;
188
+ }
189
+ };
190
+
191
+ // src/toolkit-manager.ts
192
+ var import_core2 = require("@radaros/core");
193
+ var NS_TOOLKIT_CONFIGS = "radaros:admin:toolkit-configs";
194
+ function getSecretFields(toolkitId) {
195
+ const meta = import_core2.toolkitCatalog.get(toolkitId);
196
+ if (!meta) return /* @__PURE__ */ new Set();
197
+ const fields = /* @__PURE__ */ new Set();
198
+ for (const field of meta.configFields) {
199
+ if (field.secret) fields.add(field.name);
200
+ }
201
+ return fields;
202
+ }
203
+ function maskSecrets(cfg) {
204
+ const secretFields = getSecretFields(cfg.toolkitId);
205
+ const masked = {};
206
+ for (const [key, value] of Object.entries(cfg.config)) {
207
+ if (secretFields.has(key) && typeof value === "string" && value.length > 0) {
208
+ masked[key] = value.length > 8 ? `${value.slice(0, 4)}${"*".repeat(Math.min(value.length - 4, 20))}` : "********";
209
+ } else {
210
+ masked[key] = value;
211
+ }
212
+ }
213
+ return { ...cfg, config: masked };
214
+ }
215
+ var ToolkitManager = class {
216
+ storage;
217
+ liveToolkits = /* @__PURE__ */ new Map();
218
+ mergedToolLibrary = {};
219
+ constructor(storage) {
220
+ this.storage = storage;
221
+ }
222
+ /* ── Catalog ──────────────────────────────────────────────────── */
223
+ /** List all available toolkit types from the catalog. */
224
+ listCatalog() {
225
+ return import_core2.toolkitCatalog.list();
226
+ }
227
+ /** Get a single catalog entry. */
228
+ getCatalogEntry(id) {
229
+ return import_core2.toolkitCatalog.get(id);
230
+ }
231
+ /* ── Config CRUD ──────────────────────────────────────────────── */
232
+ /** Save a toolkit config and optionally instantiate it. */
233
+ async saveConfig(cfg) {
234
+ if (!import_core2.toolkitCatalog.has(cfg.toolkitId)) {
235
+ throw new Error(`Unknown toolkit type "${cfg.toolkitId}"`);
236
+ }
237
+ const now = (/* @__PURE__ */ new Date()).toISOString();
238
+ const toSave = {
239
+ ...cfg,
240
+ createdAt: cfg.createdAt ?? now,
241
+ updatedAt: now
242
+ };
243
+ await this.storage.set(NS_TOOLKIT_CONFIGS, toSave.instanceName, toSave);
244
+ if (toSave.enabled) {
245
+ this.instantiate(toSave);
246
+ } else {
247
+ this.deactivate(toSave.instanceName);
248
+ }
249
+ return maskSecrets(toSave);
250
+ }
251
+ /** Load a toolkit config (masked). */
252
+ async loadConfig(instanceName) {
253
+ const raw = await this.storage.get(NS_TOOLKIT_CONFIGS, instanceName);
254
+ if (!raw) return null;
255
+ return maskSecrets(raw);
256
+ }
257
+ /** Load a toolkit config with raw (unmasked) values — internal use only. */
258
+ async loadConfigRaw(instanceName) {
259
+ return this.storage.get(NS_TOOLKIT_CONFIGS, instanceName);
260
+ }
261
+ /** List all saved toolkit configs (masked). */
262
+ async listConfigs() {
263
+ const entries = await this.storage.list(NS_TOOLKIT_CONFIGS);
264
+ return entries.map((e) => maskSecrets(e.value));
265
+ }
266
+ /** Update a toolkit config (partial). Secret fields are merged, not replaced when omitted. */
267
+ async updateConfig(instanceName, updates) {
268
+ const existing = await this.loadConfigRaw(instanceName);
269
+ if (!existing) {
270
+ throw new Error(`Toolkit config "${instanceName}" not found`);
271
+ }
272
+ const secretFields = getSecretFields(existing.toolkitId);
273
+ const mergedConfig = { ...existing.config };
274
+ if (updates.config) {
275
+ for (const [key, value] of Object.entries(updates.config)) {
276
+ if (secretFields.has(key) && typeof value === "string" && value.includes("*")) {
277
+ continue;
278
+ }
279
+ mergedConfig[key] = value;
280
+ }
281
+ }
282
+ const updated = {
283
+ ...existing,
284
+ config: mergedConfig,
285
+ enabled: updates.enabled ?? existing.enabled,
286
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
287
+ };
288
+ return this.saveConfig(updated);
289
+ }
290
+ /** Delete a toolkit config and remove its tools. */
291
+ async deleteConfig(instanceName) {
292
+ const existing = await this.loadConfigRaw(instanceName);
293
+ if (!existing) return false;
294
+ this.deactivate(instanceName);
295
+ await this.storage.delete(NS_TOOLKIT_CONFIGS, instanceName);
296
+ return true;
297
+ }
298
+ /* ── Instantiation ────────────────────────────────────────────── */
299
+ /** Create a live toolkit instance from a config and add its tools. */
300
+ instantiate(cfg) {
301
+ try {
302
+ const tk = import_core2.toolkitCatalog.create(cfg.toolkitId, cfg.config);
303
+ this.liveToolkits.set(cfg.instanceName, tk);
304
+ this.rebuildToolLibrary();
305
+ } catch (error) {
306
+ throw new Error(`Failed to instantiate toolkit "${cfg.instanceName}": ${error.message}`);
307
+ }
308
+ }
309
+ /** Remove a live toolkit and its tools. */
310
+ deactivate(instanceName) {
311
+ if (this.liveToolkits.delete(instanceName)) {
312
+ this.rebuildToolLibrary();
313
+ }
314
+ }
315
+ /** Rebuild the merged tool library from all active toolkits. */
316
+ rebuildToolLibrary() {
317
+ this.mergedToolLibrary = (0, import_core2.collectToolkitTools)(Array.from(this.liveToolkits.values()));
318
+ }
319
+ /** Get the current tool library (all tools from active toolkits). */
320
+ getToolLibrary() {
321
+ return this.mergedToolLibrary;
322
+ }
323
+ /** Get all live toolkit instances. */
324
+ getLiveToolkits() {
325
+ return Array.from(this.liveToolkits.values());
326
+ }
327
+ /* ── Hydration ────────────────────────────────────────────────── */
328
+ /**
329
+ * Load all saved configs from storage and instantiate enabled ones.
330
+ * Call once at startup.
331
+ */
332
+ async hydrate() {
333
+ const entries = await this.storage.list(NS_TOOLKIT_CONFIGS);
334
+ let active = 0;
335
+ const failed = [];
336
+ for (const entry of entries) {
337
+ if (entry.value.enabled) {
338
+ try {
339
+ this.instantiate(entry.value);
340
+ active++;
341
+ } catch {
342
+ failed.push(entry.value.instanceName);
343
+ }
344
+ }
345
+ }
346
+ return { total: entries.length, active, failed };
347
+ }
348
+ };
349
+
350
+ // src/admin-gateway.ts
351
+ function buildStaticToolLibrary(opts) {
352
+ const fromToolkits = opts.toolkits ? (0, import_core3.collectToolkitTools)(opts.toolkits) : {};
353
+ return { ...fromToolkits, ...opts.toolLibrary ?? {} };
354
+ }
355
+ function createAdminGateway(opts) {
356
+ const staticToolLibrary = buildStaticToolLibrary(opts);
357
+ const store = new ConfigStore(opts.storage);
358
+ const tkManager = new ToolkitManager(opts.storage);
359
+ const getToolLibrary = () => ({
360
+ ...tkManager.getToolLibrary(),
361
+ ...staticToolLibrary
362
+ });
363
+ const factory = new EntityFactory(getToolLibrary);
364
+ const ns = opts.io.of(opts.namespace ?? "/radaros-admin");
365
+ if (opts.authMiddleware) {
366
+ ns.use(opts.authMiddleware);
367
+ }
368
+ ns.on("connection", (socket) => {
369
+ socket.on("admin.agent.create", async (data, ack) => {
370
+ try {
371
+ if (!data.name || !data.provider || !data.model) {
372
+ return ack?.({ ok: false, error: "name, provider, and model are required" });
373
+ }
374
+ const existing = await store.loadAgent(data.name);
375
+ if (existing) {
376
+ return ack?.({ ok: false, error: `Agent "${data.name}" already exists` });
377
+ }
378
+ const blueprint = {
379
+ name: data.name,
380
+ provider: data.provider,
381
+ model: data.model,
382
+ instructions: data.instructions,
383
+ tools: data.tools,
384
+ temperature: data.temperature,
385
+ providerConfig: data.providerConfig,
386
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
387
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
388
+ };
389
+ factory.createAgent(blueprint);
390
+ await store.saveAgent(blueprint);
391
+ ns.emit("admin.agent.created", blueprint);
392
+ ack?.({ ok: true, data: blueprint });
393
+ } catch (error) {
394
+ ack?.({ ok: false, error: error.message });
395
+ }
396
+ });
397
+ socket.on("admin.agent.list", async (_data, ack) => {
398
+ try {
399
+ const agents = await store.listAgents();
400
+ ack?.({ ok: true, data: agents });
401
+ } catch (error) {
402
+ ack?.({ ok: false, error: error.message });
403
+ }
404
+ });
405
+ socket.on("admin.agent.get", async (data, ack) => {
406
+ try {
407
+ const blueprint = await store.loadAgent(data.name);
408
+ if (!blueprint) return ack?.({ ok: false, error: `Agent "${data.name}" not found` });
409
+ ack?.({ ok: true, data: blueprint });
410
+ } catch (error) {
411
+ ack?.({ ok: false, error: error.message });
412
+ }
413
+ });
414
+ socket.on("admin.agent.update", async (data, ack) => {
415
+ try {
416
+ const existing = await store.loadAgent(data.name);
417
+ if (!existing) return ack?.({ ok: false, error: `Agent "${data.name}" not found` });
418
+ const updated = {
419
+ ...existing,
420
+ ...data,
421
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
422
+ };
423
+ factory.destroyAgent(data.name);
424
+ factory.createAgent(updated);
425
+ await store.saveAgent(updated);
426
+ ns.emit("admin.agent.updated", updated);
427
+ ack?.({ ok: true, data: updated });
428
+ } catch (error) {
429
+ ack?.({ ok: false, error: error.message });
430
+ }
431
+ });
432
+ socket.on("admin.agent.delete", async (data, ack) => {
433
+ try {
434
+ const existing = await store.loadAgent(data.name);
435
+ if (!existing) return ack?.({ ok: false, error: `Agent "${data.name}" not found` });
436
+ factory.destroyAgent(data.name);
437
+ await store.deleteAgent(data.name);
438
+ ns.emit("admin.agent.deleted", { name: data.name });
439
+ ack?.({ ok: true, data: { deleted: data.name } });
440
+ } catch (error) {
441
+ ack?.({ ok: false, error: error.message });
442
+ }
443
+ });
444
+ socket.on("admin.team.create", async (data, ack) => {
445
+ try {
446
+ if (!data.name || !data.mode || !data.provider || !data.model || !data.members?.length) {
447
+ return ack?.({ ok: false, error: "name, mode, provider, model, and members[] are required" });
448
+ }
449
+ const existing = await store.loadTeam(data.name);
450
+ if (existing) {
451
+ return ack?.({ ok: false, error: `Team "${data.name}" already exists` });
452
+ }
453
+ const blueprint = {
454
+ name: data.name,
455
+ mode: data.mode,
456
+ provider: data.provider,
457
+ model: data.model,
458
+ members: data.members,
459
+ instructions: data.instructions,
460
+ providerConfig: data.providerConfig,
461
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
462
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
463
+ };
464
+ factory.createTeam(blueprint);
465
+ await store.saveTeam(blueprint);
466
+ ns.emit("admin.team.created", blueprint);
467
+ ack?.({ ok: true, data: blueprint });
468
+ } catch (error) {
469
+ ack?.({ ok: false, error: error.message });
470
+ }
471
+ });
472
+ socket.on("admin.team.list", async (_data, ack) => {
473
+ try {
474
+ const teams = await store.listTeams();
475
+ ack?.({ ok: true, data: teams });
476
+ } catch (error) {
477
+ ack?.({ ok: false, error: error.message });
478
+ }
479
+ });
480
+ socket.on("admin.team.get", async (data, ack) => {
481
+ try {
482
+ const blueprint = await store.loadTeam(data.name);
483
+ if (!blueprint) return ack?.({ ok: false, error: `Team "${data.name}" not found` });
484
+ ack?.({ ok: true, data: blueprint });
485
+ } catch (error) {
486
+ ack?.({ ok: false, error: error.message });
487
+ }
488
+ });
489
+ socket.on("admin.team.update", async (data, ack) => {
490
+ try {
491
+ const existing = await store.loadTeam(data.name);
492
+ if (!existing) return ack?.({ ok: false, error: `Team "${data.name}" not found` });
493
+ const updated = {
494
+ ...existing,
495
+ ...data,
496
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
497
+ };
498
+ factory.destroyTeam(data.name);
499
+ factory.createTeam(updated);
500
+ await store.saveTeam(updated);
501
+ ns.emit("admin.team.updated", updated);
502
+ ack?.({ ok: true, data: updated });
503
+ } catch (error) {
504
+ ack?.({ ok: false, error: error.message });
505
+ }
506
+ });
507
+ socket.on("admin.team.delete", async (data, ack) => {
508
+ try {
509
+ const existing = await store.loadTeam(data.name);
510
+ if (!existing) return ack?.({ ok: false, error: `Team "${data.name}" not found` });
511
+ factory.destroyTeam(data.name);
512
+ await store.deleteTeam(data.name);
513
+ ns.emit("admin.team.deleted", { name: data.name });
514
+ ack?.({ ok: true, data: { deleted: data.name } });
515
+ } catch (error) {
516
+ ack?.({ ok: false, error: error.message });
517
+ }
518
+ });
519
+ socket.on("admin.workflow.create", async (data, ack) => {
520
+ try {
521
+ if (!data.name) return ack?.({ ok: false, error: "name is required" });
522
+ const existing = await store.loadWorkflow(data.name);
523
+ if (existing) return ack?.({ ok: false, error: `Workflow "${data.name}" already exists` });
524
+ const blueprint = {
525
+ name: data.name,
526
+ description: data.description,
527
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
528
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
529
+ };
530
+ await store.saveWorkflow(blueprint);
531
+ ns.emit("admin.workflow.created", blueprint);
532
+ ack?.({ ok: true, data: blueprint });
533
+ } catch (error) {
534
+ ack?.({ ok: false, error: error.message });
535
+ }
536
+ });
537
+ socket.on("admin.workflow.list", async (_data, ack) => {
538
+ try {
539
+ const workflows = await store.listWorkflows();
540
+ ack?.({ ok: true, data: workflows });
541
+ } catch (error) {
542
+ ack?.({ ok: false, error: error.message });
543
+ }
544
+ });
545
+ socket.on("admin.workflow.delete", async (data, ack) => {
546
+ try {
547
+ const existing = await store.loadWorkflow(data.name);
548
+ if (!existing) return ack?.({ ok: false, error: `Workflow "${data.name}" not found` });
549
+ factory.destroyWorkflow(data.name);
550
+ await store.deleteWorkflow(data.name);
551
+ ns.emit("admin.workflow.deleted", { name: data.name });
552
+ ack?.({ ok: true, data: { deleted: data.name } });
553
+ } catch (error) {
554
+ ack?.({ ok: false, error: error.message });
555
+ }
556
+ });
557
+ socket.on("admin.registry.list", async (_data, ack) => {
558
+ ack?.({ ok: true, data: import_core3.registry.list() });
559
+ });
560
+ socket.on("admin.tools.list", async (_data, ack) => {
561
+ ack?.({ ok: true, data: (0, import_core3.describeToolLibrary)(getToolLibrary()) });
562
+ });
563
+ socket.on("admin.tools.get", async (data, ack) => {
564
+ const lib = getToolLibrary();
565
+ const tool = lib[data.name];
566
+ if (!tool) return ack?.({ ok: false, error: `Tool "${data.name}" not found` });
567
+ ack?.({
568
+ ok: true,
569
+ data: {
570
+ name: tool.name,
571
+ description: tool.description,
572
+ parameters: Object.keys(tool.parameters.shape ?? {})
573
+ }
574
+ });
575
+ });
576
+ socket.on("admin.toolkit-catalog.list", async (_data, ack) => {
577
+ ack?.({ ok: true, data: tkManager.listCatalog() });
578
+ });
579
+ socket.on("admin.toolkit-catalog.get", async (data, ack) => {
580
+ const entry = tkManager.getCatalogEntry(data.id);
581
+ if (!entry) return ack?.({ ok: false, error: `Toolkit type "${data.id}" not found` });
582
+ ack?.({ ok: true, data: entry });
583
+ });
584
+ socket.on("admin.toolkit-config.create", async (data, ack) => {
585
+ try {
586
+ if (!data.toolkitId || !data.instanceName) {
587
+ return ack?.({ ok: false, error: "toolkitId and instanceName are required" });
588
+ }
589
+ const existing = await tkManager.loadConfig(data.instanceName);
590
+ if (existing) {
591
+ return ack?.({ ok: false, error: `Toolkit config "${data.instanceName}" already exists` });
592
+ }
593
+ const config = {
594
+ toolkitId: data.toolkitId,
595
+ instanceName: data.instanceName,
596
+ config: data.config ?? {},
597
+ enabled: data.enabled !== false
598
+ };
599
+ const masked = await tkManager.saveConfig(config);
600
+ ns.emit("admin.toolkit-config.created", masked);
601
+ ack?.({ ok: true, data: masked });
602
+ } catch (error) {
603
+ ack?.({ ok: false, error: error.message });
604
+ }
605
+ });
606
+ socket.on("admin.toolkit-config.list", async (_data, ack) => {
607
+ try {
608
+ const configs = await tkManager.listConfigs();
609
+ ack?.({ ok: true, data: configs });
610
+ } catch (error) {
611
+ ack?.({ ok: false, error: error.message });
612
+ }
613
+ });
614
+ socket.on("admin.toolkit-config.get", async (data, ack) => {
615
+ try {
616
+ const cfg = await tkManager.loadConfig(data.instanceName);
617
+ if (!cfg) return ack?.({ ok: false, error: `Toolkit config "${data.instanceName}" not found` });
618
+ ack?.({ ok: true, data: cfg });
619
+ } catch (error) {
620
+ ack?.({ ok: false, error: error.message });
621
+ }
622
+ });
623
+ socket.on(
624
+ "admin.toolkit-config.update",
625
+ async (data, ack) => {
626
+ try {
627
+ const masked = await tkManager.updateConfig(data.instanceName, {
628
+ config: data.config,
629
+ enabled: data.enabled
630
+ });
631
+ ns.emit("admin.toolkit-config.updated", masked);
632
+ ack?.({ ok: true, data: masked });
633
+ } catch (error) {
634
+ ack?.({ ok: false, error: error.message });
635
+ }
636
+ }
637
+ );
638
+ socket.on("admin.toolkit-config.delete", async (data, ack) => {
639
+ try {
640
+ const deleted = await tkManager.deleteConfig(data.instanceName);
641
+ if (!deleted) return ack?.({ ok: false, error: `Toolkit config "${data.instanceName}" not found` });
642
+ ns.emit("admin.toolkit-config.deleted", { instanceName: data.instanceName });
643
+ ack?.({ ok: true, data: { deleted: data.instanceName } });
644
+ } catch (error) {
645
+ ack?.({ ok: false, error: error.message });
646
+ }
647
+ });
648
+ });
649
+ async function hydrate() {
650
+ await store.initialize();
651
+ const tkResult = await tkManager.hydrate();
652
+ const agentBlueprints = await store.listAgents();
653
+ for (const bp of agentBlueprints) {
654
+ if (!import_core3.registry.getAgent(bp.name)) {
655
+ factory.createAgent(bp);
656
+ }
657
+ }
658
+ const teamBlueprints = await store.listTeams();
659
+ for (const bp of teamBlueprints) {
660
+ if (!import_core3.registry.getTeam(bp.name)) {
661
+ factory.createTeam(bp);
662
+ }
663
+ }
664
+ return {
665
+ agents: agentBlueprints.length,
666
+ teams: teamBlueprints.length,
667
+ workflows: (await store.listWorkflows()).length,
668
+ toolkits: tkResult
669
+ };
670
+ }
671
+ return { hydrate };
672
+ }
673
+
674
+ // src/admin-router.ts
675
+ var import_core4 = require("@radaros/core");
676
+ var import_express = require("express");
677
+ function buildStaticToolLibrary2(opts) {
678
+ const fromToolkits = opts.toolkits ? (0, import_core4.collectToolkitTools)(opts.toolkits) : {};
679
+ return { ...fromToolkits, ...opts.toolLibrary ?? {} };
680
+ }
681
+ function createAdminRouter(opts) {
682
+ const staticToolLibrary = buildStaticToolLibrary2(opts);
683
+ const store = new ConfigStore(opts.storage);
684
+ const tkManager = new ToolkitManager(opts.storage);
685
+ const getToolLibrary = () => ({
686
+ ...tkManager.getToolLibrary(),
687
+ ...staticToolLibrary
688
+ });
689
+ const factory = new EntityFactory(getToolLibrary);
690
+ const router = (0, import_express.Router)();
691
+ if (opts.middleware) {
692
+ for (const mw of opts.middleware) {
693
+ router.use(mw);
694
+ }
695
+ }
696
+ router.post("/agents", async (req, res) => {
697
+ try {
698
+ const body = req.body;
699
+ if (!body.name || !body.provider || !body.model) {
700
+ return res.status(400).json({ error: "name, provider, and model are required" });
701
+ }
702
+ const existing = await store.loadAgent(body.name);
703
+ if (existing) {
704
+ return res.status(409).json({ error: `Agent "${body.name}" already exists` });
705
+ }
706
+ const blueprint = {
707
+ name: body.name,
708
+ provider: body.provider,
709
+ model: body.model,
710
+ instructions: body.instructions,
711
+ tools: body.tools,
712
+ temperature: body.temperature,
713
+ providerConfig: body.providerConfig,
714
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
715
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
716
+ };
717
+ factory.createAgent(blueprint);
718
+ await store.saveAgent(blueprint);
719
+ res.status(201).json(blueprint);
720
+ } catch (error) {
721
+ res.status(422).json({ error: error.message });
722
+ }
723
+ });
724
+ router.get("/agents", async (_req, res) => {
725
+ try {
726
+ const agents = await store.listAgents();
727
+ res.json(agents);
728
+ } catch (error) {
729
+ res.status(500).json({ error: error.message });
730
+ }
731
+ });
732
+ router.get("/agents/:name", async (req, res) => {
733
+ try {
734
+ const blueprint = await store.loadAgent(req.params.name);
735
+ if (!blueprint) {
736
+ return res.status(404).json({ error: `Agent "${req.params.name}" not found` });
737
+ }
738
+ res.json(blueprint);
739
+ } catch (error) {
740
+ res.status(500).json({ error: error.message });
741
+ }
742
+ });
743
+ router.put("/agents/:name", async (req, res) => {
744
+ try {
745
+ const name = req.params.name;
746
+ const existing = await store.loadAgent(name);
747
+ if (!existing) {
748
+ return res.status(404).json({ error: `Agent "${name}" not found` });
749
+ }
750
+ const body = req.body;
751
+ const updated = {
752
+ ...existing,
753
+ ...body,
754
+ name,
755
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
756
+ };
757
+ factory.destroyAgent(name);
758
+ factory.createAgent(updated);
759
+ await store.saveAgent(updated);
760
+ res.json(updated);
761
+ } catch (error) {
762
+ res.status(422).json({ error: error.message });
763
+ }
764
+ });
765
+ router.delete("/agents/:name", async (req, res) => {
766
+ try {
767
+ const name = req.params.name;
768
+ const existing = await store.loadAgent(name);
769
+ if (!existing) {
770
+ return res.status(404).json({ error: `Agent "${name}" not found` });
771
+ }
772
+ factory.destroyAgent(name);
773
+ await store.deleteAgent(name);
774
+ res.json({ deleted: name });
775
+ } catch (error) {
776
+ res.status(500).json({ error: error.message });
777
+ }
778
+ });
779
+ router.post("/teams", async (req, res) => {
780
+ try {
781
+ const body = req.body;
782
+ if (!body.name || !body.mode || !body.provider || !body.model || !body.members?.length) {
783
+ return res.status(400).json({ error: "name, mode, provider, model, and members[] are required" });
784
+ }
785
+ const existing = await store.loadTeam(body.name);
786
+ if (existing) {
787
+ return res.status(409).json({ error: `Team "${body.name}" already exists` });
788
+ }
789
+ const blueprint = {
790
+ name: body.name,
791
+ mode: body.mode,
792
+ provider: body.provider,
793
+ model: body.model,
794
+ members: body.members,
795
+ instructions: body.instructions,
796
+ providerConfig: body.providerConfig,
797
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
798
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
799
+ };
800
+ factory.createTeam(blueprint);
801
+ await store.saveTeam(blueprint);
802
+ res.status(201).json(blueprint);
803
+ } catch (error) {
804
+ res.status(422).json({ error: error.message });
805
+ }
806
+ });
807
+ router.get("/teams", async (_req, res) => {
808
+ try {
809
+ const teams = await store.listTeams();
810
+ res.json(teams);
811
+ } catch (error) {
812
+ res.status(500).json({ error: error.message });
813
+ }
814
+ });
815
+ router.get("/teams/:name", async (req, res) => {
816
+ try {
817
+ const blueprint = await store.loadTeam(req.params.name);
818
+ if (!blueprint) {
819
+ return res.status(404).json({ error: `Team "${req.params.name}" not found` });
820
+ }
821
+ res.json(blueprint);
822
+ } catch (error) {
823
+ res.status(500).json({ error: error.message });
824
+ }
825
+ });
826
+ router.put("/teams/:name", async (req, res) => {
827
+ try {
828
+ const name = req.params.name;
829
+ const existing = await store.loadTeam(name);
830
+ if (!existing) {
831
+ return res.status(404).json({ error: `Team "${name}" not found` });
832
+ }
833
+ const body = req.body;
834
+ const updated = {
835
+ ...existing,
836
+ ...body,
837
+ name,
838
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
839
+ };
840
+ factory.destroyTeam(name);
841
+ factory.createTeam(updated);
842
+ await store.saveTeam(updated);
843
+ res.json(updated);
844
+ } catch (error) {
845
+ res.status(422).json({ error: error.message });
846
+ }
847
+ });
848
+ router.delete("/teams/:name", async (req, res) => {
849
+ try {
850
+ const name = req.params.name;
851
+ const existing = await store.loadTeam(name);
852
+ if (!existing) {
853
+ return res.status(404).json({ error: `Team "${name}" not found` });
854
+ }
855
+ factory.destroyTeam(name);
856
+ await store.deleteTeam(name);
857
+ res.json({ deleted: name });
858
+ } catch (error) {
859
+ res.status(500).json({ error: error.message });
860
+ }
861
+ });
862
+ router.post("/workflows", async (req, res) => {
863
+ try {
864
+ const body = req.body;
865
+ if (!body.name) {
866
+ return res.status(400).json({ error: "name is required" });
867
+ }
868
+ const existing = await store.loadWorkflow(body.name);
869
+ if (existing) {
870
+ return res.status(409).json({ error: `Workflow "${body.name}" already exists` });
871
+ }
872
+ const blueprint = {
873
+ name: body.name,
874
+ description: body.description,
875
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
876
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
877
+ };
878
+ await store.saveWorkflow(blueprint);
879
+ res.status(201).json(blueprint);
880
+ } catch (error) {
881
+ res.status(422).json({ error: error.message });
882
+ }
883
+ });
884
+ router.get("/workflows", async (_req, res) => {
885
+ try {
886
+ const workflows = await store.listWorkflows();
887
+ res.json(workflows);
888
+ } catch (error) {
889
+ res.status(500).json({ error: error.message });
890
+ }
891
+ });
892
+ router.get("/workflows/:name", async (req, res) => {
893
+ try {
894
+ const blueprint = await store.loadWorkflow(req.params.name);
895
+ if (!blueprint) {
896
+ return res.status(404).json({ error: `Workflow "${req.params.name}" not found` });
897
+ }
898
+ res.json(blueprint);
899
+ } catch (error) {
900
+ res.status(500).json({ error: error.message });
901
+ }
902
+ });
903
+ router.delete("/workflows/:name", async (req, res) => {
904
+ try {
905
+ const name = req.params.name;
906
+ const existing = await store.loadWorkflow(name);
907
+ if (!existing) {
908
+ return res.status(404).json({ error: `Workflow "${name}" not found` });
909
+ }
910
+ factory.destroyWorkflow(name);
911
+ await store.deleteWorkflow(name);
912
+ res.json({ deleted: name });
913
+ } catch (error) {
914
+ res.status(500).json({ error: error.message });
915
+ }
916
+ });
917
+ router.get("/tools", (_req, res) => {
918
+ res.json((0, import_core4.describeToolLibrary)(getToolLibrary()));
919
+ });
920
+ router.get("/tools/:name", (req, res) => {
921
+ const lib = getToolLibrary();
922
+ const tool = lib[req.params.name];
923
+ if (!tool) return res.status(404).json({ error: `Tool "${req.params.name}" not found` });
924
+ res.json({
925
+ name: tool.name,
926
+ description: tool.description,
927
+ parameters: Object.keys(tool.parameters.shape ?? {})
928
+ });
929
+ });
930
+ router.get("/toolkit-catalog", (_req, res) => {
931
+ res.json(tkManager.listCatalog());
932
+ });
933
+ router.get("/toolkit-catalog/:id", (req, res) => {
934
+ const entry = tkManager.getCatalogEntry(req.params.id);
935
+ if (!entry) return res.status(404).json({ error: `Toolkit type "${req.params.id}" not found` });
936
+ res.json(entry);
937
+ });
938
+ router.post("/toolkit-configs", async (req, res) => {
939
+ try {
940
+ const body = req.body;
941
+ if (!body.toolkitId || !body.instanceName) {
942
+ return res.status(400).json({ error: "toolkitId and instanceName are required" });
943
+ }
944
+ const existing = await tkManager.loadConfig(body.instanceName);
945
+ if (existing) {
946
+ return res.status(409).json({ error: `Toolkit config "${body.instanceName}" already exists` });
947
+ }
948
+ const config = {
949
+ toolkitId: body.toolkitId,
950
+ instanceName: body.instanceName,
951
+ config: body.config ?? {},
952
+ enabled: body.enabled !== false
953
+ };
954
+ const masked = await tkManager.saveConfig(config);
955
+ res.status(201).json(masked);
956
+ } catch (error) {
957
+ res.status(422).json({ error: error.message });
958
+ }
959
+ });
960
+ router.get("/toolkit-configs", async (_req, res) => {
961
+ try {
962
+ const configs = await tkManager.listConfigs();
963
+ res.json(configs);
964
+ } catch (error) {
965
+ res.status(500).json({ error: error.message });
966
+ }
967
+ });
968
+ router.get("/toolkit-configs/:name", async (req, res) => {
969
+ try {
970
+ const cfg = await tkManager.loadConfig(req.params.name);
971
+ if (!cfg) return res.status(404).json({ error: `Toolkit config "${req.params.name}" not found` });
972
+ res.json(cfg);
973
+ } catch (error) {
974
+ res.status(500).json({ error: error.message });
975
+ }
976
+ });
977
+ router.put("/toolkit-configs/:name", async (req, res) => {
978
+ try {
979
+ const body = req.body;
980
+ const masked = await tkManager.updateConfig(req.params.name, body);
981
+ res.json(masked);
982
+ } catch (error) {
983
+ if (error.message?.includes("not found")) {
984
+ res.status(404).json({ error: error.message });
985
+ } else {
986
+ res.status(422).json({ error: error.message });
987
+ }
988
+ }
989
+ });
990
+ router.delete("/toolkit-configs/:name", async (req, res) => {
991
+ try {
992
+ const deleted = await tkManager.deleteConfig(req.params.name);
993
+ if (!deleted) return res.status(404).json({ error: `Toolkit config "${req.params.name}" not found` });
994
+ res.json({ deleted: req.params.name });
995
+ } catch (error) {
996
+ res.status(500).json({ error: error.message });
997
+ }
998
+ });
999
+ async function hydrate() {
1000
+ await store.initialize();
1001
+ const tkResult = await tkManager.hydrate();
1002
+ const agentBlueprints = await store.listAgents();
1003
+ for (const bp of agentBlueprints) {
1004
+ if (!import_core4.registry.getAgent(bp.name)) {
1005
+ factory.createAgent(bp);
1006
+ }
1007
+ }
1008
+ const teamBlueprints = await store.listTeams();
1009
+ for (const bp of teamBlueprints) {
1010
+ if (!import_core4.registry.getTeam(bp.name)) {
1011
+ factory.createTeam(bp);
1012
+ }
1013
+ }
1014
+ return {
1015
+ agents: agentBlueprints.length,
1016
+ teams: teamBlueprints.length,
1017
+ workflows: (await store.listWorkflows()).length,
1018
+ toolkits: tkResult
1019
+ };
1020
+ }
1021
+ return { router, toolLibrary: staticToolLibrary, toolkitManager: tkManager, hydrate };
1022
+ }
1023
+ // Annotate the CommonJS export names for ESM import in node:
1024
+ 0 && (module.exports = {
1025
+ ConfigStore,
1026
+ EntityFactory,
1027
+ ToolkitManager,
1028
+ createAdminGateway,
1029
+ createAdminRouter
1030
+ });
@@ -0,0 +1,227 @@
1
+ import { StorageDriver, ToolDef, Toolkit, ToolkitMeta, Agent, Team } from '@radaros/core';
2
+ import { Router } from 'express';
3
+
4
+ /**
5
+ * Serializable agent configuration — no class instances or functions.
6
+ * Persisted to storage and resolved into a live Agent via EntityFactory.
7
+ */
8
+ interface AgentBlueprint {
9
+ name: string;
10
+ /** Model provider id, e.g. "openai", "anthropic", "google", "ollama" */
11
+ provider: string;
12
+ /** Model id, e.g. "gpt-4o", "claude-sonnet-4-20250514" */
13
+ model: string;
14
+ instructions?: string;
15
+ /** Tool names resolved from the toolLibrary at creation time. */
16
+ tools?: string[];
17
+ temperature?: number;
18
+ /** Provider-specific config (apiKey, baseURL, etc.) — passed to modelRegistry.resolve(). */
19
+ providerConfig?: Record<string, unknown>;
20
+ createdAt?: string;
21
+ updatedAt?: string;
22
+ }
23
+ /**
24
+ * Serializable team configuration.
25
+ * Members are agent names resolved from the registry at creation time.
26
+ */
27
+ interface TeamBlueprint {
28
+ name: string;
29
+ /** One of: "coordinate", "route", "broadcast", "collaborate", "handoff" */
30
+ mode: string;
31
+ provider: string;
32
+ model: string;
33
+ /** Agent names — must already exist in the registry. */
34
+ members: string[];
35
+ instructions?: string;
36
+ providerConfig?: Record<string, unknown>;
37
+ createdAt?: string;
38
+ updatedAt?: string;
39
+ }
40
+ /**
41
+ * Serializable workflow configuration.
42
+ * Workflows with typed state and function steps cannot be fully serialized,
43
+ * so this is a lightweight placeholder for name-based registration.
44
+ */
45
+ interface WorkflowBlueprint {
46
+ name: string;
47
+ description?: string;
48
+ createdAt?: string;
49
+ updatedAt?: string;
50
+ }
51
+ interface AdminOptions {
52
+ /** Storage driver for persisting blueprints across restarts. */
53
+ storage: StorageDriver;
54
+ /** Named tools available for agent creation. Users reference tools by key. */
55
+ toolLibrary?: Record<string, ToolDef>;
56
+ /**
57
+ * Toolkit instances whose tools are automatically added to the tool library.
58
+ * Tools from toolkits are merged with `toolLibrary` (explicit entries take precedence).
59
+ */
60
+ toolkits?: Toolkit[];
61
+ /** Express middleware applied to all admin routes (e.g., auth). */
62
+ middleware?: any[];
63
+ }
64
+
65
+ interface AdminGatewayOptions extends AdminOptions {
66
+ /** Socket.IO server instance. */
67
+ io: any;
68
+ /** Socket.IO namespace for admin events. Default: "/radaros-admin" */
69
+ namespace?: string;
70
+ /** Socket.IO middleware for authentication. */
71
+ authMiddleware?: (socket: any, next: (err?: Error) => void) => void;
72
+ }
73
+ /**
74
+ * Attaches admin CRUD event handlers to a Socket.IO server.
75
+ */
76
+ declare function createAdminGateway(opts: AdminGatewayOptions): {
77
+ hydrate: () => Promise<{
78
+ agents: number;
79
+ teams: number;
80
+ workflows: number;
81
+ toolkits: {
82
+ total: number;
83
+ active: number;
84
+ failed: string[];
85
+ };
86
+ }>;
87
+ };
88
+
89
+ /** Persisted toolkit configuration. */
90
+ interface ToolkitConfig {
91
+ /** Toolkit id from the catalog (e.g. "github", "slack"). */
92
+ toolkitId: string;
93
+ /** User-chosen instance name — allows multiple configs of same type. */
94
+ instanceName: string;
95
+ /** Config values (API keys, connection strings, etc.). */
96
+ config: Record<string, unknown>;
97
+ /** Whether this toolkit is active (instantiated). */
98
+ enabled: boolean;
99
+ createdAt?: string;
100
+ updatedAt?: string;
101
+ }
102
+ /** Toolkit config with secret fields masked for API responses. */
103
+ type MaskedToolkitConfig = Omit<ToolkitConfig, "config"> & {
104
+ config: Record<string, unknown>;
105
+ };
106
+ /**
107
+ * Manages toolkit configurations: persists credentials, instantiates
108
+ * toolkits, and maintains a live tool library that agents can use.
109
+ *
110
+ * Secret fields (API keys, tokens) are stored as-is in the storage driver
111
+ * but are always masked in API responses.
112
+ */
113
+ declare class ToolkitManager {
114
+ private readonly storage;
115
+ private readonly liveToolkits;
116
+ private mergedToolLibrary;
117
+ constructor(storage: StorageDriver);
118
+ /** List all available toolkit types from the catalog. */
119
+ listCatalog(): Omit<ToolkitMeta, "factory">[];
120
+ /** Get a single catalog entry. */
121
+ getCatalogEntry(id: string): Omit<ToolkitMeta, "factory"> | undefined;
122
+ /** Save a toolkit config and optionally instantiate it. */
123
+ saveConfig(cfg: ToolkitConfig): Promise<MaskedToolkitConfig>;
124
+ /** Load a toolkit config (masked). */
125
+ loadConfig(instanceName: string): Promise<MaskedToolkitConfig | null>;
126
+ /** Load a toolkit config with raw (unmasked) values — internal use only. */
127
+ private loadConfigRaw;
128
+ /** List all saved toolkit configs (masked). */
129
+ listConfigs(): Promise<MaskedToolkitConfig[]>;
130
+ /** Update a toolkit config (partial). Secret fields are merged, not replaced when omitted. */
131
+ updateConfig(instanceName: string, updates: Partial<Pick<ToolkitConfig, "config" | "enabled">>): Promise<MaskedToolkitConfig>;
132
+ /** Delete a toolkit config and remove its tools. */
133
+ deleteConfig(instanceName: string): Promise<boolean>;
134
+ /** Create a live toolkit instance from a config and add its tools. */
135
+ private instantiate;
136
+ /** Remove a live toolkit and its tools. */
137
+ private deactivate;
138
+ /** Rebuild the merged tool library from all active toolkits. */
139
+ private rebuildToolLibrary;
140
+ /** Get the current tool library (all tools from active toolkits). */
141
+ getToolLibrary(): Record<string, ToolDef>;
142
+ /** Get all live toolkit instances. */
143
+ getLiveToolkits(): Toolkit[];
144
+ /**
145
+ * Load all saved configs from storage and instantiate enabled ones.
146
+ * Call once at startup.
147
+ */
148
+ hydrate(): Promise<{
149
+ total: number;
150
+ active: number;
151
+ failed: string[];
152
+ }>;
153
+ }
154
+
155
+ interface AdminRouterResult {
156
+ /** Express router — mount at your chosen path (e.g., `app.use("/admin", router)`) */
157
+ router: Router;
158
+ /** The merged tool library (toolkit tools + explicit toolLibrary + dynamic toolkit configs). */
159
+ toolLibrary: Record<string, ToolDef>;
160
+ /** Toolkit manager for programmatic access. */
161
+ toolkitManager: ToolkitManager;
162
+ /**
163
+ * Re-create all persisted entities and toolkit configs into the live registry.
164
+ * Call once at startup before accepting requests.
165
+ */
166
+ hydrate: () => Promise<{
167
+ agents: number;
168
+ teams: number;
169
+ workflows: number;
170
+ toolkits: {
171
+ total: number;
172
+ active: number;
173
+ failed: string[];
174
+ };
175
+ }>;
176
+ }
177
+ declare function createAdminRouter(opts: AdminOptions): AdminRouterResult;
178
+
179
+ /**
180
+ * Persists serializable blueprints to a StorageDriver.
181
+ * All values are plain JSON — no class instances.
182
+ */
183
+ declare class ConfigStore {
184
+ private readonly storage;
185
+ constructor(storage: StorageDriver);
186
+ initialize(): Promise<void>;
187
+ saveAgent(blueprint: AgentBlueprint): Promise<void>;
188
+ loadAgent(name: string): Promise<AgentBlueprint | null>;
189
+ deleteAgent(name: string): Promise<void>;
190
+ listAgents(): Promise<AgentBlueprint[]>;
191
+ saveTeam(blueprint: TeamBlueprint): Promise<void>;
192
+ loadTeam(name: string): Promise<TeamBlueprint | null>;
193
+ deleteTeam(name: string): Promise<void>;
194
+ listTeams(): Promise<TeamBlueprint[]>;
195
+ saveWorkflow(blueprint: WorkflowBlueprint): Promise<void>;
196
+ loadWorkflow(name: string): Promise<WorkflowBlueprint | null>;
197
+ deleteWorkflow(name: string): Promise<void>;
198
+ listWorkflows(): Promise<WorkflowBlueprint[]>;
199
+ }
200
+
201
+ /**
202
+ * Resolves serializable blueprints into live Agent/Team instances
203
+ * that auto-register into the global registry.
204
+ *
205
+ * The `toolSource` can be a static record or a function that returns one,
206
+ * enabling dynamic tool libraries that grow as toolkit configs are added.
207
+ */
208
+ declare class EntityFactory {
209
+ private readonly toolSource;
210
+ constructor(toolSource?: (() => Record<string, ToolDef>) | Record<string, ToolDef>);
211
+ private get toolLibrary();
212
+ createAgent(blueprint: AgentBlueprint): Agent;
213
+ createTeam(blueprint: TeamBlueprint): Team;
214
+ /**
215
+ * Destroy a live entity by removing it from the global registry.
216
+ * Returns true if found and removed.
217
+ */
218
+ destroyAgent(name: string): boolean;
219
+ destroyTeam(name: string): boolean;
220
+ destroyWorkflow(name: string): boolean;
221
+ private validateProvider;
222
+ private resolveTools;
223
+ private resolveMembers;
224
+ private resolveTeamMode;
225
+ }
226
+
227
+ export { type AdminGatewayOptions, type AdminOptions, type AdminRouterResult, type AgentBlueprint, ConfigStore, EntityFactory, type MaskedToolkitConfig, type TeamBlueprint, type ToolkitConfig, ToolkitManager, type WorkflowBlueprint, createAdminGateway, createAdminRouter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radaros/admin",
3
- "version": "0.3.20",
3
+ "version": "0.3.22",
4
4
  "description": "Admin CRUD API for dynamically creating, updating, and deleting RadarOS agents, teams, and workflows at runtime",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -17,20 +17,23 @@
17
17
  "radaros"
18
18
  ],
19
19
  "type": "module",
20
- "main": "./dist/index.js",
20
+ "main": "./dist/index.cjs",
21
+ "module": "./dist/index.js",
21
22
  "types": "./dist/index.d.ts",
22
23
  "exports": {
23
24
  ".": {
25
+ "types": "./dist/index.d.ts",
24
26
  "import": "./dist/index.js",
25
- "types": "./dist/index.d.ts"
27
+ "require": "./dist/index.cjs",
28
+ "default": "./dist/index.js"
26
29
  }
27
30
  },
28
31
  "files": [
29
32
  "dist"
30
33
  ],
31
34
  "scripts": {
32
- "build": "tsup src/index.ts --format esm --dts --clean",
33
- "dev": "tsup src/index.ts --format esm --dts --watch",
35
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
36
+ "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
34
37
  "prepublishOnly": "npm run build"
35
38
  },
36
39
  "devDependencies": {
@@ -39,7 +42,7 @@
39
42
  "typescript": "^5.6.0"
40
43
  },
41
44
  "peerDependencies": {
42
- "@radaros/core": "^0.3.20",
45
+ "@radaros/core": "^0.3.22",
43
46
  "@types/express": "^4.0.0 || ^5.0.0",
44
47
  "express": "^4.0.0 || ^5.0.0",
45
48
  "socket.io": "^4.0.0"