@dexto/agent-management 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/LICENSE +44 -0
  2. package/dist/AgentOrchestrator.cjs +263 -0
  3. package/dist/AgentOrchestrator.d.ts +191 -0
  4. package/dist/AgentOrchestrator.d.ts.map +1 -0
  5. package/dist/AgentOrchestrator.js +239 -0
  6. package/dist/config/config-enrichment.cjs +117 -0
  7. package/dist/config/config-enrichment.d.ts +31 -0
  8. package/dist/config/config-enrichment.d.ts.map +1 -0
  9. package/dist/config/config-enrichment.js +82 -0
  10. package/dist/config/config-manager.cjs +57 -0
  11. package/dist/config/config-manager.d.ts +51 -0
  12. package/dist/config/config-manager.d.ts.map +1 -0
  13. package/dist/config/config-manager.js +32 -0
  14. package/dist/config/error-codes.cjs +39 -0
  15. package/dist/config/error-codes.d.ts +16 -0
  16. package/dist/config/error-codes.d.ts.map +1 -0
  17. package/dist/config/error-codes.js +15 -0
  18. package/dist/config/errors.cjs +125 -0
  19. package/dist/config/errors.d.ts +34 -0
  20. package/dist/config/errors.d.ts.map +1 -0
  21. package/dist/config/errors.js +101 -0
  22. package/dist/config/index.cjs +44 -0
  23. package/dist/config/index.d.ts +6 -0
  24. package/dist/config/index.d.ts.map +1 -0
  25. package/dist/config/index.js +14 -0
  26. package/dist/config/loader.cjs +118 -0
  27. package/dist/config/loader.d.ts +18 -0
  28. package/dist/config/loader.d.ts.map +1 -0
  29. package/dist/config/loader.js +84 -0
  30. package/dist/index.cjs +132 -0
  31. package/dist/index.d.ts +20 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +104 -0
  34. package/dist/preferences/constants.cjs +28 -0
  35. package/dist/preferences/constants.d.ts +2 -0
  36. package/dist/preferences/constants.d.ts.map +1 -0
  37. package/dist/preferences/constants.js +4 -0
  38. package/dist/preferences/error-codes.cjs +35 -0
  39. package/dist/preferences/error-codes.d.ts +8 -0
  40. package/dist/preferences/error-codes.d.ts.map +1 -0
  41. package/dist/preferences/error-codes.js +11 -0
  42. package/dist/preferences/errors.cjs +74 -0
  43. package/dist/preferences/errors.d.ts +18 -0
  44. package/dist/preferences/errors.d.ts.map +1 -0
  45. package/dist/preferences/errors.js +49 -0
  46. package/dist/preferences/index.cjs +55 -0
  47. package/dist/preferences/index.d.ts +6 -0
  48. package/dist/preferences/index.d.ts.map +1 -0
  49. package/dist/preferences/index.js +31 -0
  50. package/dist/preferences/loader.cjs +138 -0
  51. package/dist/preferences/loader.d.ts +50 -0
  52. package/dist/preferences/loader.d.ts.map +1 -0
  53. package/dist/preferences/loader.js +109 -0
  54. package/dist/preferences/schemas.cjs +75 -0
  55. package/dist/preferences/schemas.d.ts +110 -0
  56. package/dist/preferences/schemas.d.ts.map +1 -0
  57. package/dist/preferences/schemas.js +48 -0
  58. package/dist/registry/error-codes.cjs +44 -0
  59. package/dist/registry/error-codes.d.ts +21 -0
  60. package/dist/registry/error-codes.d.ts.map +1 -0
  61. package/dist/registry/error-codes.js +20 -0
  62. package/dist/registry/errors.cjs +187 -0
  63. package/dist/registry/errors.d.ts +63 -0
  64. package/dist/registry/errors.d.ts.map +1 -0
  65. package/dist/registry/errors.js +163 -0
  66. package/dist/registry/registry.cjs +479 -0
  67. package/dist/registry/registry.d.ts +130 -0
  68. package/dist/registry/registry.d.ts.map +1 -0
  69. package/dist/registry/registry.js +452 -0
  70. package/dist/registry/types.cjs +74 -0
  71. package/dist/registry/types.d.ts +142 -0
  72. package/dist/registry/types.d.ts.map +1 -0
  73. package/dist/registry/types.js +47 -0
  74. package/dist/registry/user-registry.cjs +140 -0
  75. package/dist/registry/user-registry.d.ts +34 -0
  76. package/dist/registry/user-registry.d.ts.map +1 -0
  77. package/dist/registry/user-registry.js +104 -0
  78. package/dist/resolver.cjs +182 -0
  79. package/dist/resolver.d.ts +14 -0
  80. package/dist/resolver.d.ts.map +1 -0
  81. package/dist/resolver.js +151 -0
  82. package/dist/utils/api-key-resolver.cjs +62 -0
  83. package/dist/utils/api-key-resolver.d.ts +21 -0
  84. package/dist/utils/api-key-resolver.d.ts.map +1 -0
  85. package/dist/utils/api-key-resolver.js +36 -0
  86. package/dist/utils/api-key-store.cjs +56 -0
  87. package/dist/utils/api-key-store.d.ts +18 -0
  88. package/dist/utils/api-key-store.d.ts.map +1 -0
  89. package/dist/utils/api-key-store.js +30 -0
  90. package/dist/utils/env-file.cjs +118 -0
  91. package/dist/utils/env-file.d.ts +5 -0
  92. package/dist/utils/env-file.d.ts.map +1 -0
  93. package/dist/utils/env-file.js +84 -0
  94. package/dist/utils/execution-context.cjs +85 -0
  95. package/dist/utils/execution-context.d.ts +20 -0
  96. package/dist/utils/execution-context.d.ts.map +1 -0
  97. package/dist/utils/execution-context.js +49 -0
  98. package/dist/utils/fs-walk.cjs +52 -0
  99. package/dist/utils/fs-walk.d.ts +8 -0
  100. package/dist/utils/fs-walk.d.ts.map +1 -0
  101. package/dist/utils/fs-walk.js +18 -0
  102. package/dist/utils/path.cjs +205 -0
  103. package/dist/utils/path.d.ts +58 -0
  104. package/dist/utils/path.d.ts.map +1 -0
  105. package/dist/utils/path.js +167 -0
  106. package/dist/writer.cjs +182 -0
  107. package/dist/writer.d.ts +34 -0
  108. package/dist/writer.d.ts.map +1 -0
  109. package/dist/writer.js +146 -0
  110. package/package.json +41 -0
@@ -0,0 +1,452 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { promises as fs } from "fs";
3
+ import path from "path";
4
+ import { logger } from "@dexto/core";
5
+ import { resolveBundledScript, getDextoGlobalPath, copyDirectory } from "../utils/path.js";
6
+ import { loadGlobalPreferences } from "../preferences/loader.js";
7
+ import { writePreferencesToAgent } from "../writer.js";
8
+ import {
9
+ RegistrySchema,
10
+ normalizeRegistryJson
11
+ } from "./types.js";
12
+ import { RegistryError } from "./errors.js";
13
+ import {
14
+ loadUserRegistry,
15
+ mergeRegistries,
16
+ removeAgentFromUserRegistry,
17
+ addAgentToUserRegistry
18
+ } from "./user-registry.js";
19
+ let cachedRegistry = null;
20
+ class LocalAgentRegistry {
21
+ _registry = null;
22
+ /**
23
+ * Lazy load registry from JSON file
24
+ */
25
+ getRegistry() {
26
+ if (this._registry === null) {
27
+ this._registry = this.loadRegistry();
28
+ }
29
+ return this._registry;
30
+ }
31
+ /**
32
+ * Load and merge bundled + user registries
33
+ * Uses fail-fast approach - throws RegistryError for any loading issues
34
+ */
35
+ loadRegistry() {
36
+ let jsonPath;
37
+ try {
38
+ jsonPath = resolveBundledScript("agents/agent-registry.json");
39
+ } catch (error) {
40
+ throw RegistryError.registryNotFound(
41
+ "agents/agent-registry.json",
42
+ error instanceof Error ? error.message : String(error)
43
+ );
44
+ }
45
+ if (!existsSync(jsonPath)) {
46
+ throw RegistryError.registryNotFound(jsonPath, "File doesn't exist");
47
+ }
48
+ let bundledRegistry;
49
+ try {
50
+ const jsonData = readFileSync(jsonPath, "utf-8");
51
+ const rawRegistry = JSON.parse(jsonData);
52
+ bundledRegistry = RegistrySchema.parse(normalizeRegistryJson(rawRegistry));
53
+ } catch (error) {
54
+ throw RegistryError.registryParseError(
55
+ jsonPath,
56
+ error instanceof Error ? error.message : String(error)
57
+ );
58
+ }
59
+ const userRegistry = loadUserRegistry();
60
+ const merged = mergeRegistries(bundledRegistry, userRegistry);
61
+ logger.debug(
62
+ `Loaded registry: ${Object.keys(bundledRegistry.agents).length} bundled, ${Object.keys(userRegistry.agents).length} custom`
63
+ );
64
+ return merged;
65
+ }
66
+ /**
67
+ * Check if agent exists in registry by ID
68
+ */
69
+ hasAgent(agentId) {
70
+ const registry = this.getRegistry();
71
+ return agentId in registry.agents;
72
+ }
73
+ /**
74
+ * Get available agents with their metadata from registry
75
+ */
76
+ getAvailableAgents() {
77
+ const registry = this.getRegistry();
78
+ return registry.agents;
79
+ }
80
+ /**
81
+ * Validate custom agent ID doesn't conflict with bundled registry
82
+ * @throws RegistryError if ID conflicts with builtin agent
83
+ */
84
+ validateCustomAgentId(agentId) {
85
+ let jsonPath;
86
+ try {
87
+ jsonPath = resolveBundledScript("agents/agent-registry.json");
88
+ } catch (error) {
89
+ throw RegistryError.registryNotFound(
90
+ "agents/agent-registry.json",
91
+ error instanceof Error ? error.message : String(error)
92
+ );
93
+ }
94
+ try {
95
+ const jsonData = readFileSync(jsonPath, "utf-8");
96
+ const bundledRegistry = RegistrySchema.parse(
97
+ normalizeRegistryJson(JSON.parse(jsonData))
98
+ );
99
+ if (agentId in bundledRegistry.agents) {
100
+ throw RegistryError.customAgentNameConflict(agentId);
101
+ }
102
+ } catch (error) {
103
+ if (error instanceof Error && /name conflicts with builtin agent/.test(error.message)) {
104
+ throw error;
105
+ }
106
+ throw RegistryError.registryParseError(
107
+ jsonPath,
108
+ error instanceof Error ? error.message : String(error)
109
+ );
110
+ }
111
+ }
112
+ /**
113
+ * Resolve main config file for installed agent
114
+ * Handles both directory agents (with main field) and single-file agents
115
+ */
116
+ resolveMainConfig(agentDir, agentId) {
117
+ const registry = this.getRegistry();
118
+ const agentData = registry.agents[agentId];
119
+ if (!agentData) {
120
+ const available = Object.keys(registry.agents);
121
+ throw RegistryError.agentNotFound(agentId, available);
122
+ }
123
+ if (agentData.source.endsWith("/")) {
124
+ if (!agentData.main) {
125
+ throw RegistryError.agentInvalidEntry(
126
+ agentId,
127
+ "directory entry missing main field"
128
+ );
129
+ }
130
+ const mainConfigPath = path.join(agentDir, agentData.main);
131
+ if (!existsSync(mainConfigPath)) {
132
+ throw RegistryError.mainConfigMissing(agentId, mainConfigPath);
133
+ }
134
+ return mainConfigPath;
135
+ } else {
136
+ const filename = path.basename(agentData.source);
137
+ const configPath = path.join(agentDir, filename);
138
+ if (!existsSync(configPath)) {
139
+ throw RegistryError.configNotFound(configPath);
140
+ }
141
+ return configPath;
142
+ }
143
+ }
144
+ /**
145
+ * Install agent atomically using temp + rename pattern
146
+ * @param agentId ID of the agent to install
147
+ * @param injectPreferences Whether to inject global preferences into installed agent (default: true)
148
+ */
149
+ async installAgent(agentId, injectPreferences = true) {
150
+ logger.info(`Installing agent: ${agentId}`);
151
+ const registry = this.getRegistry();
152
+ const agentData = registry.agents[agentId];
153
+ if (!agentData) {
154
+ const available = Object.keys(registry.agents);
155
+ throw RegistryError.agentNotFound(agentId, available);
156
+ }
157
+ const globalAgentsDir = getDextoGlobalPath("agents");
158
+ const targetDir = path.resolve(globalAgentsDir, agentId);
159
+ const relTarget = path.relative(globalAgentsDir, targetDir);
160
+ if (relTarget.startsWith("..") || path.isAbsolute(relTarget)) {
161
+ throw RegistryError.installationFailed(
162
+ agentId,
163
+ "invalid agentId: path traversal detected"
164
+ );
165
+ }
166
+ if (existsSync(targetDir)) {
167
+ logger.info(`Agent '${agentId}' already installed`);
168
+ return this.resolveMainConfig(targetDir, agentId);
169
+ }
170
+ await fs.mkdir(globalAgentsDir, { recursive: true });
171
+ const sourcePath = resolveBundledScript(`agents/${agentData.source}`);
172
+ const tempDir = `${targetDir}.tmp.${Date.now()}`;
173
+ try {
174
+ if (agentData.source.endsWith("/")) {
175
+ await copyDirectory(sourcePath, tempDir);
176
+ } else {
177
+ await fs.mkdir(tempDir, { recursive: true });
178
+ const targetFile = path.join(tempDir, path.basename(sourcePath));
179
+ await fs.copyFile(sourcePath, targetFile);
180
+ }
181
+ const mainConfigPath = this.resolveMainConfig(tempDir, agentId);
182
+ if (!existsSync(mainConfigPath)) {
183
+ throw RegistryError.installationValidationFailed(agentId, mainConfigPath);
184
+ }
185
+ await fs.rename(tempDir, targetDir);
186
+ logger.info(`\u2713 Installed agent '${agentId}' to ${targetDir}`);
187
+ if (injectPreferences) {
188
+ try {
189
+ const preferences = await loadGlobalPreferences();
190
+ await writePreferencesToAgent(targetDir, preferences);
191
+ logger.info(`\u2713 Applied global preferences to installed agent '${agentId}'`);
192
+ } catch (error) {
193
+ logger.warn(
194
+ `Failed to inject preferences to '${agentId}': ${error instanceof Error ? error.message : String(error)}`
195
+ );
196
+ console.log(
197
+ `\u26A0\uFE0F Warning: Could not apply preferences to '${agentId}' - agent will use bundled settings`
198
+ );
199
+ }
200
+ } else {
201
+ logger.info(
202
+ `Skipped preference injection for '${agentId}' (injectPreferences=false)`
203
+ );
204
+ }
205
+ return this.resolveMainConfig(targetDir, agentId);
206
+ } catch (error) {
207
+ try {
208
+ if (existsSync(tempDir)) {
209
+ await fs.rm(tempDir, { recursive: true, force: true });
210
+ }
211
+ } catch (cleanupError) {
212
+ logger.error(
213
+ `Failed to clean up temp directory: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}. Skipping cleanup...`
214
+ );
215
+ }
216
+ throw RegistryError.installationFailed(
217
+ agentId,
218
+ error instanceof Error ? error.message : String(error)
219
+ );
220
+ }
221
+ }
222
+ /**
223
+ * Install a custom agent from a local file path
224
+ * @param agentId Unique identifier for the custom agent
225
+ * @param sourcePath Absolute path to agent YAML file or directory
226
+ * @param metadata Agent metadata (name for display, description, author, tags, main)
227
+ * @param injectPreferences Whether to inject global preferences (default: true)
228
+ * @returns Path to the installed agent's main config file
229
+ */
230
+ async installCustomAgentFromPath(agentId, sourcePath, metadata, injectPreferences = true) {
231
+ logger.info(`Installing custom agent '${agentId}' from ${sourcePath}`);
232
+ this.validateCustomAgentId(agentId);
233
+ if (!existsSync(sourcePath)) {
234
+ throw RegistryError.configNotFound(sourcePath);
235
+ }
236
+ const globalAgentsDir = getDextoGlobalPath("agents");
237
+ const targetDir = path.resolve(globalAgentsDir, agentId);
238
+ const relTarget = path.relative(globalAgentsDir, targetDir);
239
+ if (relTarget.startsWith("..") || path.isAbsolute(relTarget)) {
240
+ throw RegistryError.installationFailed(
241
+ agentId,
242
+ "invalid agentId: path traversal detected"
243
+ );
244
+ }
245
+ if (existsSync(targetDir)) {
246
+ throw RegistryError.agentAlreadyExists(agentId);
247
+ }
248
+ await fs.mkdir(globalAgentsDir, { recursive: true });
249
+ const stats = await fs.stat(sourcePath);
250
+ const isDirectory = stats.isDirectory();
251
+ const configFileName = isDirectory ? void 0 : `${agentId}.yml`;
252
+ if (!metadata.description) {
253
+ throw RegistryError.installationFailed(agentId, "description is required");
254
+ }
255
+ if (isDirectory && !metadata.main) {
256
+ throw RegistryError.installationFailed(
257
+ agentId,
258
+ "main field is required for directory-based agents"
259
+ );
260
+ }
261
+ const displayName = metadata.name || agentId.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
262
+ const registryEntry = {
263
+ id: agentId,
264
+ name: displayName,
265
+ description: metadata.description,
266
+ author: metadata.author,
267
+ tags: metadata.tags,
268
+ source: isDirectory ? `${agentId}/` : configFileName,
269
+ main: metadata.main
270
+ };
271
+ const tempDir = `${targetDir}.tmp.${Date.now()}`;
272
+ try {
273
+ if (isDirectory) {
274
+ await copyDirectory(sourcePath, tempDir);
275
+ } else {
276
+ await fs.mkdir(tempDir, { recursive: true });
277
+ const targetFile = path.join(tempDir, configFileName);
278
+ await fs.copyFile(sourcePath, targetFile);
279
+ }
280
+ const tempMainConfigPath = isDirectory ? path.join(tempDir, metadata.main) : path.join(tempDir, configFileName);
281
+ if (!existsSync(tempMainConfigPath)) {
282
+ throw RegistryError.installationValidationFailed(agentId, tempMainConfigPath);
283
+ }
284
+ await fs.rename(tempDir, targetDir);
285
+ logger.info(`\u2713 Installed custom agent '${agentId}' to ${targetDir}`);
286
+ const mainConfigPath = isDirectory && metadata.main ? path.join(targetDir, metadata.main) : path.join(targetDir, configFileName);
287
+ try {
288
+ await addAgentToUserRegistry(agentId, registryEntry);
289
+ logger.info(`\u2713 Added '${agentId}' to user registry`);
290
+ this._registry = null;
291
+ } catch (registryError) {
292
+ try {
293
+ if (existsSync(targetDir)) {
294
+ await fs.rm(targetDir, { recursive: true, force: true });
295
+ logger.info(`Rolled back installation: removed ${targetDir}`);
296
+ }
297
+ } catch (rollbackError) {
298
+ logger.error(
299
+ `Rollback failed for '${agentId}': ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`
300
+ );
301
+ }
302
+ throw registryError;
303
+ }
304
+ if (injectPreferences) {
305
+ try {
306
+ const preferences = await loadGlobalPreferences();
307
+ await writePreferencesToAgent(targetDir, preferences);
308
+ logger.info(`\u2713 Applied global preferences to custom agent '${agentId}'`);
309
+ } catch (error) {
310
+ logger.warn(
311
+ `Failed to inject preferences to '${agentId}': ${error instanceof Error ? error.message : String(error)}`
312
+ );
313
+ console.log(
314
+ `\u26A0\uFE0F Warning: Could not apply preferences to '${agentId}' - agent will use default settings`
315
+ );
316
+ }
317
+ }
318
+ return mainConfigPath;
319
+ } catch (error) {
320
+ try {
321
+ if (existsSync(tempDir)) {
322
+ await fs.rm(tempDir, { recursive: true, force: true });
323
+ }
324
+ } catch (cleanupError) {
325
+ logger.error(
326
+ `Failed to clean up temp directory: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}. Skipping cleanup...`
327
+ );
328
+ }
329
+ throw RegistryError.installationFailed(
330
+ agentId,
331
+ error instanceof Error ? error.message : String(error)
332
+ );
333
+ }
334
+ }
335
+ /**
336
+ * Resolve a registry agent ID to a config path
337
+ * NOTE: Only handles registry IDs, not file paths (routing done in loadAgentConfig)
338
+ * Handles installing agent if needed
339
+ * @param agentId ID of the agent to resolve
340
+ * @param autoInstall Whether to automatically install missing agents from registry (default: true)
341
+ * @param injectPreferences Whether to inject preferences during auto-installation (default: true)
342
+ */
343
+ async resolveAgent(agentId, autoInstall = true, injectPreferences = true) {
344
+ logger.debug(`Resolving registry agent: ${agentId}`);
345
+ const globalAgentsDir = getDextoGlobalPath("agents");
346
+ const installedPath = path.resolve(globalAgentsDir, agentId);
347
+ const relInstalled = path.relative(globalAgentsDir, installedPath);
348
+ if (relInstalled.startsWith("..") || path.isAbsolute(relInstalled)) {
349
+ throw RegistryError.agentNotFound(agentId, Object.keys(this.getRegistry().agents));
350
+ }
351
+ if (existsSync(installedPath)) {
352
+ const mainConfig = this.resolveMainConfig(installedPath, agentId);
353
+ logger.debug(`Resolved installed agent '${agentId}' to: ${mainConfig}`);
354
+ return mainConfig;
355
+ }
356
+ logger.debug(`Agent '${agentId}' not found in installed path: ${installedPath}`);
357
+ if (this.hasAgent(agentId)) {
358
+ if (autoInstall) {
359
+ logger.info(`Installing agent '${agentId}' from registry...`);
360
+ return await this.installAgent(agentId, injectPreferences);
361
+ } else {
362
+ const registry2 = this.getRegistry();
363
+ const available2 = Object.keys(registry2.agents);
364
+ throw RegistryError.agentNotInstalledAutoInstallDisabled(agentId, available2);
365
+ }
366
+ }
367
+ const registry = this.getRegistry();
368
+ const available = Object.keys(registry.agents);
369
+ throw RegistryError.agentNotFound(agentId, available);
370
+ }
371
+ /**
372
+ * Get list of currently installed agents
373
+ */
374
+ async getInstalledAgents() {
375
+ const globalAgentsDir = getDextoGlobalPath("agents");
376
+ if (!existsSync(globalAgentsDir)) {
377
+ return [];
378
+ }
379
+ try {
380
+ const entries = await fs.readdir(globalAgentsDir, { withFileTypes: true });
381
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => !name.startsWith(".tmp") && !name.includes(".tmp."));
382
+ } catch (error) {
383
+ logger.error(`Failed to read installed agents directory: ${error}`);
384
+ return [];
385
+ }
386
+ }
387
+ /**
388
+ * Check if an agent is safe to uninstall (not the default agent from preferences)
389
+ */
390
+ async isAgentSafeToUninstall(agentId) {
391
+ try {
392
+ const preferences = await loadGlobalPreferences();
393
+ const defaultAgent = preferences.defaults.defaultAgent;
394
+ return agentId !== defaultAgent;
395
+ } catch {
396
+ logger.warn("Could not load preferences, using fallback protection for default-agent");
397
+ return agentId !== "default-agent";
398
+ }
399
+ }
400
+ /**
401
+ * Uninstall an agent by removing its directory
402
+ * For custom agents: also removes from user registry
403
+ * For builtin agents: only removes from disk
404
+ * @param agentId ID of the agent to uninstall
405
+ * @param force Whether to force uninstall even if agent is protected (default: false)
406
+ */
407
+ async uninstallAgent(agentId, force = false) {
408
+ const globalAgentsDir = getDextoGlobalPath("agents");
409
+ const agentDir = path.resolve(globalAgentsDir, agentId);
410
+ const relAgent = path.relative(globalAgentsDir, agentDir);
411
+ if (relAgent.startsWith("..") || path.isAbsolute(relAgent)) {
412
+ throw RegistryError.uninstallationFailed(
413
+ agentId,
414
+ "invalid agentId: path traversal detected"
415
+ );
416
+ }
417
+ logger.info(`Uninstalling agent: ${agentId} from ${agentDir}`);
418
+ if (!existsSync(agentDir)) {
419
+ throw RegistryError.agentNotInstalled(agentId);
420
+ }
421
+ if (!force && !await this.isAgentSafeToUninstall(agentId)) {
422
+ throw RegistryError.agentProtected(agentId);
423
+ }
424
+ const registry = this.getRegistry();
425
+ const agentData = registry.agents[agentId];
426
+ const isCustomAgent = agentData?.type === "custom";
427
+ try {
428
+ await fs.rm(agentDir, { recursive: true, force: true });
429
+ logger.info(`\u2713 Removed agent '${agentId}' from ${agentDir}`);
430
+ if (isCustomAgent) {
431
+ await removeAgentFromUserRegistry(agentId);
432
+ logger.info(`\u2713 Removed custom agent '${agentId}' from user registry`);
433
+ this._registry = null;
434
+ }
435
+ } catch (error) {
436
+ throw RegistryError.uninstallationFailed(
437
+ agentId,
438
+ error instanceof Error ? error.message : String(error)
439
+ );
440
+ }
441
+ }
442
+ }
443
+ function getAgentRegistry() {
444
+ if (cachedRegistry === null) {
445
+ cachedRegistry = new LocalAgentRegistry();
446
+ }
447
+ return cachedRegistry;
448
+ }
449
+ export {
450
+ LocalAgentRegistry,
451
+ getAgentRegistry
452
+ };
@@ -0,0 +1,74 @@
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
+ var types_exports = {};
20
+ __export(types_exports, {
21
+ AgentRegistryEntrySchema: () => AgentRegistryEntrySchema,
22
+ RegistrySchema: () => RegistrySchema,
23
+ deriveDisplayName: () => deriveDisplayName,
24
+ normalizeRegistryJson: () => normalizeRegistryJson
25
+ });
26
+ module.exports = __toCommonJS(types_exports);
27
+ var import_zod = require("zod");
28
+ function deriveDisplayName(slug) {
29
+ return slug.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
30
+ }
31
+ const AgentRegistryEntrySchema = import_zod.z.object({
32
+ id: import_zod.z.string().describe("Unique identifier for the agent"),
33
+ name: import_zod.z.string().describe("Display name for the agent"),
34
+ description: import_zod.z.string(),
35
+ author: import_zod.z.string(),
36
+ tags: import_zod.z.array(import_zod.z.string()),
37
+ source: import_zod.z.string(),
38
+ main: import_zod.z.string().optional(),
39
+ type: import_zod.z.enum(["builtin", "custom"]).default("builtin").describe("Agent type")
40
+ }).strict();
41
+ const RegistrySchema = import_zod.z.object({
42
+ version: import_zod.z.string(),
43
+ agents: import_zod.z.record(import_zod.z.string(), AgentRegistryEntrySchema)
44
+ }).strict();
45
+ function normalizeRegistryJson(raw) {
46
+ if (!raw || typeof raw !== "object") {
47
+ return { version: "1.0.0", agents: {} };
48
+ }
49
+ const input = raw;
50
+ const normalizedAgents = {};
51
+ const agents = input.agents && typeof input.agents === "object" && input.agents !== null ? input.agents : {};
52
+ for (const [agentId, value] of Object.entries(agents)) {
53
+ if (!value || typeof value !== "object") continue;
54
+ const entry = { ...value };
55
+ if (!entry.id || typeof entry.id !== "string" || entry.id.trim() !== agentId) {
56
+ entry.id = agentId;
57
+ }
58
+ if (!entry.name || typeof entry.name !== "string" || !entry.name.trim()) {
59
+ entry.name = deriveDisplayName(agentId);
60
+ }
61
+ normalizedAgents[agentId] = entry;
62
+ }
63
+ return {
64
+ version: typeof input.version === "string" && input.version.trim().length > 0 ? input.version : "1.0.0",
65
+ agents: normalizedAgents
66
+ };
67
+ }
68
+ // Annotate the CommonJS export names for ESM import in node:
69
+ 0 && (module.exports = {
70
+ AgentRegistryEntrySchema,
71
+ RegistrySchema,
72
+ deriveDisplayName,
73
+ normalizeRegistryJson
74
+ });
@@ -0,0 +1,142 @@
1
+ import { z } from 'zod';
2
+ export declare function deriveDisplayName(slug: string): string;
3
+ /**
4
+ * Schema for agent data in registry JSON
5
+ */
6
+ export declare const AgentRegistryEntrySchema: z.ZodObject<{
7
+ id: z.ZodString;
8
+ name: z.ZodString;
9
+ description: z.ZodString;
10
+ author: z.ZodString;
11
+ tags: z.ZodArray<z.ZodString, "many">;
12
+ source: z.ZodString;
13
+ main: z.ZodOptional<z.ZodString>;
14
+ type: z.ZodDefault<z.ZodEnum<["builtin", "custom"]>>;
15
+ }, "strict", z.ZodTypeAny, {
16
+ name: string;
17
+ type: "custom" | "builtin";
18
+ id: string;
19
+ description: string;
20
+ author: string;
21
+ tags: string[];
22
+ source: string;
23
+ main?: string | undefined;
24
+ }, {
25
+ name: string;
26
+ id: string;
27
+ description: string;
28
+ author: string;
29
+ tags: string[];
30
+ source: string;
31
+ type?: "custom" | "builtin" | undefined;
32
+ main?: string | undefined;
33
+ }>;
34
+ export type AgentRegistryEntry = z.output<typeof AgentRegistryEntrySchema>;
35
+ /**
36
+ * Schema for complete registry JSON
37
+ */
38
+ export declare const RegistrySchema: z.ZodObject<{
39
+ version: z.ZodString;
40
+ agents: z.ZodRecord<z.ZodString, z.ZodObject<{
41
+ id: z.ZodString;
42
+ name: z.ZodString;
43
+ description: z.ZodString;
44
+ author: z.ZodString;
45
+ tags: z.ZodArray<z.ZodString, "many">;
46
+ source: z.ZodString;
47
+ main: z.ZodOptional<z.ZodString>;
48
+ type: z.ZodDefault<z.ZodEnum<["builtin", "custom"]>>;
49
+ }, "strict", z.ZodTypeAny, {
50
+ name: string;
51
+ type: "custom" | "builtin";
52
+ id: string;
53
+ description: string;
54
+ author: string;
55
+ tags: string[];
56
+ source: string;
57
+ main?: string | undefined;
58
+ }, {
59
+ name: string;
60
+ id: string;
61
+ description: string;
62
+ author: string;
63
+ tags: string[];
64
+ source: string;
65
+ type?: "custom" | "builtin" | undefined;
66
+ main?: string | undefined;
67
+ }>>;
68
+ }, "strict", z.ZodTypeAny, {
69
+ version: string;
70
+ agents: Record<string, {
71
+ name: string;
72
+ type: "custom" | "builtin";
73
+ id: string;
74
+ description: string;
75
+ author: string;
76
+ tags: string[];
77
+ source: string;
78
+ main?: string | undefined;
79
+ }>;
80
+ }, {
81
+ version: string;
82
+ agents: Record<string, {
83
+ name: string;
84
+ id: string;
85
+ description: string;
86
+ author: string;
87
+ tags: string[];
88
+ source: string;
89
+ type?: "custom" | "builtin" | undefined;
90
+ main?: string | undefined;
91
+ }>;
92
+ }>;
93
+ export type Registry = z.output<typeof RegistrySchema>;
94
+ type RawRegistry = {
95
+ version?: unknown;
96
+ agents?: Record<string, unknown> | unknown;
97
+ };
98
+ /**
99
+ * Normalize registry JSON data to ensure consistency.
100
+ * Validates that id field matches the registry key and derives display names if missing.
101
+ */
102
+ export declare function normalizeRegistryJson(raw: unknown): RawRegistry;
103
+ /**
104
+ * Agent registry interface
105
+ */
106
+ export interface AgentRegistry {
107
+ /**
108
+ * Returns true if the registry contains an agent with the provided ID
109
+ */
110
+ hasAgent(agentId: string): boolean;
111
+ /**
112
+ * Returns a map of available agent IDs to their registry entries
113
+ */
114
+ getAvailableAgents(): Record<string, AgentRegistryEntry>;
115
+ /**
116
+ * Installs an agent from the registry by ID
117
+ * @param agentId - Unique agent identifier
118
+ * @param injectPreferences - Whether to inject global preferences (default: true)
119
+ * @returns Path to the installed agent config
120
+ */
121
+ installAgent(agentId: string, injectPreferences?: boolean): Promise<string>;
122
+ /**
123
+ * Uninstalls an agent by ID
124
+ * @param agentId - Unique agent identifier
125
+ * @param force - Whether to force uninstall protected agents (default: false)
126
+ */
127
+ uninstallAgent(agentId: string, force?: boolean): Promise<void>;
128
+ /**
129
+ * Returns list of currently installed agent IDs
130
+ */
131
+ getInstalledAgents(): Promise<string[]>;
132
+ /**
133
+ * Resolves an agent ID or path and optionally auto-installs if needed
134
+ * @param idOrPath - Agent ID from registry or filesystem path
135
+ * @param autoInstall - Whether to auto-install from registry (default: true)
136
+ * @param injectPreferences - Whether to inject preferences during install (default: true)
137
+ * @returns Path to the agent config file
138
+ */
139
+ resolveAgent(idOrPath: string, autoInstall?: boolean, injectPreferences?: boolean): Promise<string>;
140
+ }
141
+ export {};
142
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/registry/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWxB,CAAC;AAEd,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE3E;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKd,CAAC;AAEd,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAEvD,KAAK,WAAW,GAAG;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;CAC9C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAsC/D;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC1B;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IACnC;;OAEG;IACH,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACzD;;;;;OAKG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5E;;;;OAIG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE;;OAEG;IACH,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACxC;;;;;;OAMG;IACH,YAAY,CACR,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,EACrB,iBAAiB,CAAC,EAAE,OAAO,GAC5B,OAAO,CAAC,MAAM,CAAC,CAAC;CACtB"}