@standardagents/builder 0.11.12 → 0.12.1

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/plugin.js CHANGED
@@ -1,6 +1,12 @@
1
- import fs2 from 'fs';
2
- import path3 from 'path';
1
+ import * as fs2 from 'fs';
2
+ import fs2__default from 'fs';
3
+ import * as path8 from 'path';
4
+ import path8__default from 'path';
3
5
  import { fileURLToPath } from 'url';
6
+ import * as ts from 'typescript';
7
+ import MagicString from 'magic-string';
8
+ import { exec } from 'child_process';
9
+ import { promisify } from 'util';
4
10
 
5
11
  // src/plugin.ts
6
12
  var TSCONFIG_CONTENT = `{
@@ -35,15 +41,15 @@ function extractSchemaFields(content) {
35
41
  }
36
42
  function scanPromptsWithSchemas(dir) {
37
43
  const results = [];
38
- if (!fs2.existsSync(dir)) {
44
+ if (!fs2__default.existsSync(dir)) {
39
45
  return results;
40
46
  }
41
47
  try {
42
- const entries = fs2.readdirSync(dir, { withFileTypes: true });
48
+ const entries = fs2__default.readdirSync(dir, { withFileTypes: true });
43
49
  for (const entry of entries) {
44
50
  if (entry.isFile() && entry.name.endsWith(".ts")) {
45
- const filePath = path3.join(dir, entry.name);
46
- const content = fs2.readFileSync(filePath, "utf-8");
51
+ const filePath = path8__default.join(dir, entry.name);
52
+ const content = fs2__default.readFileSync(filePath, "utf-8");
47
53
  const nameMatch = content.match(/name:\s*['"]([^'"]+)['"]/);
48
54
  if (nameMatch) {
49
55
  const name = nameMatch[1];
@@ -59,15 +65,15 @@ function scanPromptsWithSchemas(dir) {
59
65
  }
60
66
  function scanAgentsWithSideA(dir) {
61
67
  const results = [];
62
- if (!fs2.existsSync(dir)) {
68
+ if (!fs2__default.existsSync(dir)) {
63
69
  return results;
64
70
  }
65
71
  try {
66
- const entries = fs2.readdirSync(dir, { withFileTypes: true });
72
+ const entries = fs2__default.readdirSync(dir, { withFileTypes: true });
67
73
  for (const entry of entries) {
68
74
  if (entry.isFile() && entry.name.endsWith(".ts")) {
69
- const filePath = path3.join(dir, entry.name);
70
- const content = fs2.readFileSync(filePath, "utf-8");
75
+ const filePath = path8__default.join(dir, entry.name);
76
+ const content = fs2__default.readFileSync(filePath, "utf-8");
71
77
  const nameMatch = content.match(/name:\s*['"]([^'"]+)['"]/);
72
78
  if (nameMatch) {
73
79
  const name = nameMatch[1];
@@ -87,15 +93,15 @@ function scanAgentsWithSideA(dir) {
87
93
  }
88
94
  function scanForNames(dir, useFilename = false) {
89
95
  const names = [];
90
- if (!fs2.existsSync(dir)) {
96
+ if (!fs2__default.existsSync(dir)) {
91
97
  return names;
92
98
  }
93
99
  try {
94
- const entries = fs2.readdirSync(dir, { withFileTypes: true });
100
+ const entries = fs2__default.readdirSync(dir, { withFileTypes: true });
95
101
  for (const entry of entries) {
96
102
  if (entry.isFile() && entry.name.endsWith(".ts")) {
97
- const filePath = path3.join(dir, entry.name);
98
- const content = fs2.readFileSync(filePath, "utf-8");
103
+ const filePath = path8__default.join(dir, entry.name);
104
+ const content = fs2__default.readFileSync(filePath, "utf-8");
99
105
  if (useFilename) {
100
106
  const toolName = entry.name.replace(/\.ts$/, "");
101
107
  if (content.includes("defineTool")) {
@@ -114,6 +120,31 @@ function scanForNames(dir, useFilename = false) {
114
120
  }
115
121
  return names;
116
122
  }
123
+ function scanHooksForIds(dir) {
124
+ const hookIds = [];
125
+ if (!fs2__default.existsSync(dir)) {
126
+ return hookIds;
127
+ }
128
+ try {
129
+ const entries = fs2__default.readdirSync(dir, { withFileTypes: true });
130
+ for (const entry of entries) {
131
+ if (entry.isFile() && entry.name.endsWith(".ts")) {
132
+ if (entry.name === "index.ts") {
133
+ continue;
134
+ }
135
+ const filePath = path8__default.join(dir, entry.name);
136
+ const content = fs2__default.readFileSync(filePath, "utf-8");
137
+ const idMatch = content.match(/defineHook\s*\(\s*\{[^}]*id:\s*['"]([^'"]+)['"]/s);
138
+ if (idMatch) {
139
+ hookIds.push(idMatch[1]);
140
+ }
141
+ }
142
+ }
143
+ } catch (error) {
144
+ console.error(`Error scanning hooks directory ${dir}:`, error);
145
+ }
146
+ return hookIds;
147
+ }
117
148
  function generateRegistryProperties(names) {
118
149
  if (names.length === 0) {
119
150
  return "";
@@ -150,6 +181,7 @@ function generateTypesContent(config) {
150
181
  const promptResults = scanPromptsWithSchemas(config.promptsDir);
151
182
  const agentResults = scanAgentsWithSideA(config.agentsDir);
152
183
  const tools = scanForNames(config.toolsDir, true);
184
+ const hookIds = scanHooksForIds(config.hooksDir);
153
185
  const prompts = promptResults.map((p) => p.name);
154
186
  const agents = agentResults.map((a) => a.name);
155
187
  const callables = [...prompts, ...agents, ...tools];
@@ -157,11 +189,11 @@ function generateTypesContent(config) {
157
189
  // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
158
190
  //
159
191
  // This file augments the StandardAgentSpec namespace declared in @standardagents/spec
160
- // to provide type-safe references for your models, prompts, agents, and tools.
192
+ // to provide type-safe references for your models, prompts, agents, tools, and hooks.
161
193
 
162
194
  /**
163
195
  * Augment the global StandardAgentSpec namespace with your project's specific types.
164
- * This provides autocomplete and type checking for model, prompt, agent, and tool references.
196
+ * This provides autocomplete and type checking for model, prompt, agent, tool, and hook references.
165
197
  *
166
198
  * Uses interface declaration merging with property keys to create union types.
167
199
  * Example: interface ModelRegistry { 'gpt-4o': true; } gives type Models = 'gpt-4o'
@@ -193,6 +225,11 @@ declare global {
193
225
  ${generateRegistryProperties(callables)}
194
226
  }
195
227
 
228
+ /** Hook IDs from agents/hooks/ */
229
+ interface HookIdRegistry {
230
+ ${generateRegistryProperties(hookIds)}
231
+ }
232
+
196
233
  /** Schema field names for prompts and agents (used for initUserMessageProperty autocomplete) */
197
234
  interface PromptSchemaRegistry {
198
235
  ${generateSchemaRegistryProperties(promptResults, agentResults)}
@@ -643,35 +680,35 @@ declare module 'virtual:@standardagents/router' {
643
680
  `;
644
681
  }
645
682
  function ensureDir(dir) {
646
- if (!fs2.existsSync(dir)) {
647
- fs2.mkdirSync(dir, { recursive: true });
683
+ if (!fs2__default.existsSync(dir)) {
684
+ fs2__default.mkdirSync(dir, { recursive: true });
648
685
  }
649
686
  }
650
687
  function generateTypes(config) {
651
688
  ensureDir(config.outputDir);
652
689
  const typesContent = generateTypesContent(config);
653
- fs2.writeFileSync(path3.join(config.outputDir, "types.d.ts"), typesContent);
690
+ fs2__default.writeFileSync(path8__default.join(config.outputDir, "types.d.ts"), typesContent);
654
691
  const virtualModuleContent = generateVirtualModuleContent();
655
- fs2.writeFileSync(path3.join(config.outputDir, "virtual-module.d.ts"), virtualModuleContent);
656
- fs2.writeFileSync(path3.join(config.outputDir, "tsconfig.json"), TSCONFIG_CONTENT);
657
- fs2.writeFileSync(path3.join(config.outputDir, ".gitignore"), "*\n");
692
+ fs2__default.writeFileSync(path8__default.join(config.outputDir, "virtual-module.d.ts"), virtualModuleContent);
693
+ fs2__default.writeFileSync(path8__default.join(config.outputDir, "tsconfig.json"), TSCONFIG_CONTENT);
694
+ fs2__default.writeFileSync(path8__default.join(config.outputDir, ".gitignore"), "*\n");
658
695
  }
659
696
  function needsRegeneration(config) {
660
- const typesPath = path3.join(config.outputDir, "types.d.ts");
661
- if (!fs2.existsSync(typesPath)) {
697
+ const typesPath = path8__default.join(config.outputDir, "types.d.ts");
698
+ if (!fs2__default.existsSync(typesPath)) {
662
699
  return true;
663
700
  }
664
- const typesMtime = fs2.statSync(typesPath).mtime;
665
- const dirs = [config.modelsDir, config.promptsDir, config.agentsDir, config.toolsDir];
701
+ const typesMtime = fs2__default.statSync(typesPath).mtime;
702
+ const dirs = [config.modelsDir, config.promptsDir, config.agentsDir, config.toolsDir, config.hooksDir];
666
703
  for (const dir of dirs) {
667
- if (!fs2.existsSync(dir)) {
704
+ if (!fs2__default.existsSync(dir)) {
668
705
  continue;
669
706
  }
670
- const entries = fs2.readdirSync(dir, { withFileTypes: true });
707
+ const entries = fs2__default.readdirSync(dir, { withFileTypes: true });
671
708
  for (const entry of entries) {
672
709
  if (entry.isFile() && entry.name.endsWith(".ts")) {
673
- const filePath = path3.join(dir, entry.name);
674
- const fileMtime = fs2.statSync(filePath).mtime;
710
+ const filePath = path8__default.join(dir, entry.name);
711
+ const fileMtime = fs2__default.statSync(filePath).mtime;
675
712
  if (fileMtime > typesMtime) {
676
713
  return true;
677
714
  }
@@ -681,81 +718,6 @@ function needsRegeneration(config) {
681
718
  return false;
682
719
  }
683
720
 
684
- // src/utils/model-parser.ts
685
- function getName(content) {
686
- return content.match(/name:\s*['"]([^'"]+)['"]/)?.[1];
687
- }
688
- function getProvider(content) {
689
- const stringMatch = content.match(/provider:\s*['"]([^'"]+)['"]/)?.[1];
690
- if (stringMatch) return stringMatch;
691
- const refMatch = content.match(/provider:\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*[,\n}]/)?.[1];
692
- return refMatch || void 0;
693
- }
694
- function getModel(content) {
695
- return content.match(/model:\s*['"]([^'"]+)['"]/)?.[1];
696
- }
697
- function getInputPrice(content) {
698
- const match = content.match(/inputPrice:\s*([\d.]+)/);
699
- return match ? parseFloat(match[1]) : void 0;
700
- }
701
- function getOutputPrice(content) {
702
- const match = content.match(/outputPrice:\s*([\d.]+)/);
703
- return match ? parseFloat(match[1]) : void 0;
704
- }
705
- function getCachedPrice(content) {
706
- const match = content.match(/cachedPrice:\s*([\d.]+)/);
707
- return match ? parseFloat(match[1]) : void 0;
708
- }
709
- function getIncludedProviders(content) {
710
- const match = content.match(/includedProviders:\s*\[([^\]]*)\]/);
711
- if (!match) return void 0;
712
- const items = match[1].match(/['"]([^'"]+)['"]/g);
713
- return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
714
- }
715
- function getFallbacks(content) {
716
- const match = content.match(/fallbacks:\s*\[([^\]]*)\]/);
717
- if (!match) return [];
718
- const items = match[1].match(/['"]([^'"]+)['"]/g);
719
- return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
720
- }
721
- function getProviderTools(content) {
722
- const match = content.match(/providerTools:\s*\[([^\]]*)\]/);
723
- if (!match) return [];
724
- const items = match[1].match(/['"]([^'"]+)['"]/g);
725
- return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
726
- }
727
- function getCapabilities(content) {
728
- const match = content.match(/capabilities:\s*\{([^}]*)\}/s);
729
- if (!match) return void 0;
730
- const inner = match[1];
731
- const caps = {};
732
- const boolMatches = inner.matchAll(/(\w+):\s*(true|false)/g);
733
- for (const m of boolMatches) {
734
- caps[m[1]] = m[2] === "true";
735
- }
736
- const numMatches = inner.matchAll(/(\w+):\s*(\d+)/g);
737
- for (const m of numMatches) {
738
- caps[m[1]] = parseInt(m[2], 10);
739
- }
740
- return Object.keys(caps).length > 0 ? caps : void 0;
741
- }
742
- function parseModelFile(content) {
743
- const name = getName(content);
744
- if (!name) return null;
745
- return {
746
- name,
747
- provider: getProvider(content),
748
- model: getModel(content),
749
- inputPrice: getInputPrice(content),
750
- outputPrice: getOutputPrice(content),
751
- cachedPrice: getCachedPrice(content),
752
- includedProviders: getIncludedProviders(content),
753
- fallbacks: getFallbacks(content),
754
- providerTools: getProviderTools(content),
755
- capabilities: getCapabilities(content)
756
- };
757
- }
758
-
759
721
  // src/sdk/generators/generateModelFile.ts
760
722
  function generateModelFile(data, options) {
761
723
  const { providerName, providerPackage } = options;
@@ -1189,6 +1151,18 @@ function generateAgentFile(data) {
1189
1151
  `export default defineAgent({`,
1190
1152
  ` name: '${escapeString3(data.name)}',`
1191
1153
  ];
1154
+ if (data.packageName) {
1155
+ lines.push(` packageName: '${escapeString3(data.packageName)}',`);
1156
+ }
1157
+ if (data.version) {
1158
+ lines.push(` version: '${escapeString3(data.version)}',`);
1159
+ }
1160
+ if (data.author) {
1161
+ lines.push(` author: '${escapeString3(data.author)}',`);
1162
+ }
1163
+ if (data.license) {
1164
+ lines.push(` license: '${escapeString3(data.license)}',`);
1165
+ }
1192
1166
  if (data.title) {
1193
1167
  lines.push(` title: '${escapeString3(data.title)}',`);
1194
1168
  }
@@ -1236,7 +1210,7 @@ function formatSideConfig(config) {
1236
1210
  if (config.endSessionTool) {
1237
1211
  parts.push(` endSessionTool: '${escapeString3(config.endSessionTool)}',`);
1238
1212
  }
1239
- if (config.manualStopCondition !== void 0) {
1213
+ if (config.manualStopCondition) {
1240
1214
  parts.push(` manualStopCondition: ${config.manualStopCondition},`);
1241
1215
  }
1242
1216
  parts.push(" }");
@@ -1259,19 +1233,19 @@ function nameToFilename(name) {
1259
1233
  }
1260
1234
  function getModelFilePath(modelsDir, name) {
1261
1235
  const filename = nameToFilename(name);
1262
- return path3.join(modelsDir, `${filename}.ts`);
1236
+ return path8__default.join(modelsDir, `${filename}.ts`);
1263
1237
  }
1264
1238
  function modelExists(modelsDir, name) {
1265
1239
  const filePath = getModelFilePath(modelsDir, name);
1266
- return fs2.existsSync(filePath);
1240
+ return fs2__default.existsSync(filePath);
1267
1241
  }
1268
1242
  async function saveModel(modelsDir, data, overwrite = false) {
1269
1243
  try {
1270
- if (!fs2.existsSync(modelsDir)) {
1271
- fs2.mkdirSync(modelsDir, { recursive: true });
1244
+ if (!fs2__default.existsSync(modelsDir)) {
1245
+ fs2__default.mkdirSync(modelsDir, { recursive: true });
1272
1246
  }
1273
1247
  const filePath = getModelFilePath(modelsDir, data.name);
1274
- if (!overwrite && fs2.existsSync(filePath)) {
1248
+ if (!overwrite && fs2__default.existsSync(filePath)) {
1275
1249
  return {
1276
1250
  success: false,
1277
1251
  error: `Model file already exists: ${filePath}. Use update to modify existing models.`
@@ -1288,7 +1262,7 @@ async function saveModel(modelsDir, data, overwrite = false) {
1288
1262
  providerName: providerInfo.name,
1289
1263
  providerPackage: providerInfo.package
1290
1264
  });
1291
- await fs2.promises.writeFile(filePath, content, "utf-8");
1265
+ await fs2__default.promises.writeFile(filePath, content, "utf-8");
1292
1266
  return {
1293
1267
  success: true,
1294
1268
  filePath
@@ -1303,13 +1277,13 @@ async function saveModel(modelsDir, data, overwrite = false) {
1303
1277
  async function deleteModel(modelsDir, name) {
1304
1278
  try {
1305
1279
  const filePath = getModelFilePath(modelsDir, name);
1306
- if (!fs2.existsSync(filePath)) {
1280
+ if (!fs2__default.existsSync(filePath)) {
1307
1281
  return {
1308
1282
  success: false,
1309
1283
  error: `Model file not found: ${filePath}`
1310
1284
  };
1311
1285
  }
1312
- await fs2.promises.unlink(filePath);
1286
+ await fs2__default.promises.unlink(filePath);
1313
1287
  return {
1314
1288
  success: true,
1315
1289
  filePath
@@ -1348,6 +1322,9 @@ function validateModelData(data) {
1348
1322
  if (!data.name || typeof data.name !== "string") {
1349
1323
  return "Model name is required and must be a string";
1350
1324
  }
1325
+ if (data.name.includes("/")) {
1326
+ return "Model name cannot contain '/'. Reserved for namespace qualification.";
1327
+ }
1351
1328
  if (!data.provider || typeof data.provider !== "string") {
1352
1329
  return "Model provider is required and must be a string";
1353
1330
  }
@@ -1436,26 +1413,26 @@ function transformPromptData(data) {
1436
1413
  }
1437
1414
  function getPromptFilePath(promptsDir, name) {
1438
1415
  const filename = nameToFilename(name);
1439
- return path3.join(promptsDir, `${filename}.ts`);
1416
+ return path8__default.join(promptsDir, `${filename}.ts`);
1440
1417
  }
1441
1418
  function promptExists(promptsDir, name) {
1442
1419
  const filePath = getPromptFilePath(promptsDir, name);
1443
- return fs2.existsSync(filePath);
1420
+ return fs2__default.existsSync(filePath);
1444
1421
  }
1445
1422
  async function savePrompt(promptsDir, data, overwrite = false) {
1446
1423
  try {
1447
- if (!fs2.existsSync(promptsDir)) {
1448
- fs2.mkdirSync(promptsDir, { recursive: true });
1424
+ if (!fs2__default.existsSync(promptsDir)) {
1425
+ fs2__default.mkdirSync(promptsDir, { recursive: true });
1449
1426
  }
1450
1427
  const filePath = getPromptFilePath(promptsDir, data.name);
1451
- if (!overwrite && fs2.existsSync(filePath)) {
1428
+ if (!overwrite && fs2__default.existsSync(filePath)) {
1452
1429
  return {
1453
1430
  success: false,
1454
1431
  error: `Prompt file already exists: ${filePath}. Use update to modify existing prompts.`
1455
1432
  };
1456
1433
  }
1457
1434
  const content = generatePromptFile(data);
1458
- await fs2.promises.writeFile(filePath, content, "utf-8");
1435
+ await fs2__default.promises.writeFile(filePath, content, "utf-8");
1459
1436
  return {
1460
1437
  success: true,
1461
1438
  filePath
@@ -1470,13 +1447,13 @@ async function savePrompt(promptsDir, data, overwrite = false) {
1470
1447
  async function deletePrompt(promptsDir, name) {
1471
1448
  try {
1472
1449
  const filePath = getPromptFilePath(promptsDir, name);
1473
- if (!fs2.existsSync(filePath)) {
1450
+ if (!fs2__default.existsSync(filePath)) {
1474
1451
  return {
1475
1452
  success: false,
1476
1453
  error: `Prompt file not found: ${filePath}`
1477
1454
  };
1478
1455
  }
1479
- await fs2.promises.unlink(filePath);
1456
+ await fs2__default.promises.unlink(filePath);
1480
1457
  return {
1481
1458
  success: true,
1482
1459
  filePath
@@ -1492,25 +1469,25 @@ async function renamePrompt(promptsDir, oldName, newName) {
1492
1469
  try {
1493
1470
  const oldFilePath = getPromptFilePath(promptsDir, oldName);
1494
1471
  const newFilePath = getPromptFilePath(promptsDir, newName);
1495
- if (!fs2.existsSync(oldFilePath)) {
1472
+ if (!fs2__default.existsSync(oldFilePath)) {
1496
1473
  return {
1497
1474
  success: false,
1498
1475
  error: `Prompt file not found: ${oldFilePath}`
1499
1476
  };
1500
1477
  }
1501
- if (fs2.existsSync(newFilePath)) {
1478
+ if (fs2__default.existsSync(newFilePath)) {
1502
1479
  return {
1503
1480
  success: false,
1504
1481
  error: `Prompt file already exists: ${newFilePath}`
1505
1482
  };
1506
1483
  }
1507
- const content = await fs2.promises.readFile(oldFilePath, "utf-8");
1484
+ const content = await fs2__default.promises.readFile(oldFilePath, "utf-8");
1508
1485
  const updatedContent = content.replace(
1509
1486
  /name:\s*['"]([^'"]+)['"]/,
1510
1487
  `name: '${newName}'`
1511
1488
  );
1512
- await fs2.promises.writeFile(newFilePath, updatedContent, "utf-8");
1513
- await fs2.promises.unlink(oldFilePath);
1489
+ await fs2__default.promises.writeFile(newFilePath, updatedContent, "utf-8");
1490
+ await fs2__default.promises.unlink(oldFilePath);
1514
1491
  return {
1515
1492
  success: true,
1516
1493
  filePath: newFilePath
@@ -1526,6 +1503,9 @@ function validatePromptData(data) {
1526
1503
  if (!data.name || typeof data.name !== "string") {
1527
1504
  return "Prompt name is required and must be a string";
1528
1505
  }
1506
+ if (data.name.includes("/")) {
1507
+ return "Prompt name cannot contain '/'. Reserved for namespace qualification.";
1508
+ }
1529
1509
  if (!data.model || typeof data.model !== "string") {
1530
1510
  return "Prompt model is required and must be a string";
1531
1511
  }
@@ -1639,26 +1619,58 @@ function transformAgentData(data) {
1639
1619
  }
1640
1620
  function getAgentFilePath(agentsDir, name) {
1641
1621
  const filename = nameToFilename(name);
1642
- return path3.join(agentsDir, `${filename}.ts`);
1622
+ return path8__default.join(agentsDir, `${filename}.ts`);
1643
1623
  }
1644
1624
  function agentExists(agentsDir, name) {
1645
1625
  const filePath = getAgentFilePath(agentsDir, name);
1646
- return fs2.existsSync(filePath);
1626
+ return fs2__default.existsSync(filePath);
1627
+ }
1628
+ function extractAgentMetadata(filePath) {
1629
+ try {
1630
+ const content = fs2__default.readFileSync(filePath, "utf-8");
1631
+ const metadata = {};
1632
+ const packageNameMatch = content.match(/packageName:\s*['"]([^'"]+)['"]/);
1633
+ if (packageNameMatch) metadata.packageName = packageNameMatch[1];
1634
+ const versionMatch = content.match(/version:\s*['"]([^'"]+)['"]/);
1635
+ if (versionMatch) metadata.version = versionMatch[1];
1636
+ const authorMatch = content.match(/author:\s*['"]([^'"]+)['"]/);
1637
+ if (authorMatch) metadata.author = authorMatch[1];
1638
+ const licenseMatch = content.match(/license:\s*['"]([^'"]+)['"]/);
1639
+ if (licenseMatch) metadata.license = licenseMatch[1];
1640
+ return metadata;
1641
+ } catch {
1642
+ return {};
1643
+ }
1647
1644
  }
1648
1645
  async function saveAgent(agentsDir, data, overwrite = false) {
1649
1646
  try {
1650
- if (!fs2.existsSync(agentsDir)) {
1651
- fs2.mkdirSync(agentsDir, { recursive: true });
1647
+ if (!fs2__default.existsSync(agentsDir)) {
1648
+ fs2__default.mkdirSync(agentsDir, { recursive: true });
1652
1649
  }
1653
1650
  const filePath = getAgentFilePath(agentsDir, data.name);
1654
- if (!overwrite && fs2.existsSync(filePath)) {
1651
+ if (!overwrite && fs2__default.existsSync(filePath)) {
1655
1652
  return {
1656
1653
  success: false,
1657
1654
  error: `Agent file already exists: ${filePath}. Use update to modify existing agents.`
1658
1655
  };
1659
1656
  }
1657
+ if (overwrite && fs2__default.existsSync(filePath)) {
1658
+ const existingMetadata = extractAgentMetadata(filePath);
1659
+ if (existingMetadata.packageName && !data.packageName) {
1660
+ data.packageName = existingMetadata.packageName;
1661
+ }
1662
+ if (existingMetadata.version && !data.version) {
1663
+ data.version = existingMetadata.version;
1664
+ }
1665
+ if (existingMetadata.author && !data.author) {
1666
+ data.author = existingMetadata.author;
1667
+ }
1668
+ if (existingMetadata.license && !data.license) {
1669
+ data.license = existingMetadata.license;
1670
+ }
1671
+ }
1660
1672
  const content = generateAgentFile(data);
1661
- await fs2.promises.writeFile(filePath, content, "utf-8");
1673
+ await fs2__default.promises.writeFile(filePath, content, "utf-8");
1662
1674
  return {
1663
1675
  success: true,
1664
1676
  filePath
@@ -1673,13 +1685,13 @@ async function saveAgent(agentsDir, data, overwrite = false) {
1673
1685
  async function deleteAgent(agentsDir, name) {
1674
1686
  try {
1675
1687
  const filePath = getAgentFilePath(agentsDir, name);
1676
- if (!fs2.existsSync(filePath)) {
1688
+ if (!fs2__default.existsSync(filePath)) {
1677
1689
  return {
1678
1690
  success: false,
1679
1691
  error: `Agent file not found: ${filePath}`
1680
1692
  };
1681
1693
  }
1682
- await fs2.promises.unlink(filePath);
1694
+ await fs2__default.promises.unlink(filePath);
1683
1695
  return {
1684
1696
  success: true,
1685
1697
  filePath
@@ -1695,25 +1707,25 @@ async function renameModel(modelsDir, oldName, newName) {
1695
1707
  try {
1696
1708
  const oldFilePath = getModelFilePath(modelsDir, oldName);
1697
1709
  const newFilePath = getModelFilePath(modelsDir, newName);
1698
- if (!fs2.existsSync(oldFilePath)) {
1710
+ if (!fs2__default.existsSync(oldFilePath)) {
1699
1711
  return {
1700
1712
  success: false,
1701
1713
  error: `Model file not found: ${oldFilePath}`
1702
1714
  };
1703
1715
  }
1704
- if (fs2.existsSync(newFilePath)) {
1716
+ if (fs2__default.existsSync(newFilePath)) {
1705
1717
  return {
1706
1718
  success: false,
1707
1719
  error: `Model file already exists: ${newFilePath}`
1708
1720
  };
1709
1721
  }
1710
- const content = await fs2.promises.readFile(oldFilePath, "utf-8");
1722
+ const content = await fs2__default.promises.readFile(oldFilePath, "utf-8");
1711
1723
  const updatedContent = content.replace(
1712
1724
  /name:\s*['"]([^'"]+)['"]/,
1713
1725
  `name: '${newName}'`
1714
1726
  );
1715
- await fs2.promises.writeFile(newFilePath, updatedContent, "utf-8");
1716
- await fs2.promises.unlink(oldFilePath);
1727
+ await fs2__default.promises.writeFile(newFilePath, updatedContent, "utf-8");
1728
+ await fs2__default.promises.unlink(oldFilePath);
1717
1729
  return {
1718
1730
  success: true,
1719
1731
  filePath: newFilePath
@@ -1727,17 +1739,17 @@ async function renameModel(modelsDir, oldName, newName) {
1727
1739
  }
1728
1740
  async function updateModelReferencesInPrompts(promptsDir, oldModelName, newModelName) {
1729
1741
  const updatedFiles = [];
1730
- if (!fs2.existsSync(promptsDir)) {
1742
+ if (!fs2__default.existsSync(promptsDir)) {
1731
1743
  return updatedFiles;
1732
1744
  }
1733
- const files = fs2.readdirSync(promptsDir).filter((f) => f.endsWith(".ts"));
1745
+ const files = fs2__default.readdirSync(promptsDir).filter((f) => f.endsWith(".ts"));
1734
1746
  for (const file of files) {
1735
- const filePath = path3.join(promptsDir, file);
1736
- let content = await fs2.promises.readFile(filePath, "utf-8");
1747
+ const filePath = path8__default.join(promptsDir, file);
1748
+ let content = await fs2__default.promises.readFile(filePath, "utf-8");
1737
1749
  const modelRegex = new RegExp(`model:\\s*['"]${escapeRegExp(oldModelName)}['"]`, "g");
1738
1750
  if (modelRegex.test(content)) {
1739
1751
  content = content.replace(modelRegex, `model: '${newModelName}'`);
1740
- await fs2.promises.writeFile(filePath, content, "utf-8");
1752
+ await fs2__default.promises.writeFile(filePath, content, "utf-8");
1741
1753
  updatedFiles.push(filePath);
1742
1754
  }
1743
1755
  }
@@ -1745,13 +1757,13 @@ async function updateModelReferencesInPrompts(promptsDir, oldModelName, newModel
1745
1757
  }
1746
1758
  async function updatePromptReferencesInPrompts(promptsDir, oldPromptName, newPromptName) {
1747
1759
  const updatedFiles = [];
1748
- if (!fs2.existsSync(promptsDir)) {
1760
+ if (!fs2__default.existsSync(promptsDir)) {
1749
1761
  return updatedFiles;
1750
1762
  }
1751
- const files = fs2.readdirSync(promptsDir).filter((f) => f.endsWith(".ts"));
1763
+ const files = fs2__default.readdirSync(promptsDir).filter((f) => f.endsWith(".ts"));
1752
1764
  for (const file of files) {
1753
- const filePath = path3.join(promptsDir, file);
1754
- let content = await fs2.promises.readFile(filePath, "utf-8");
1765
+ const filePath = path8__default.join(promptsDir, file);
1766
+ let content = await fs2__default.promises.readFile(filePath, "utf-8");
1755
1767
  let modified = false;
1756
1768
  const toolsArrayRegex = /tools:\s*\[([^\]]*)\]/gs;
1757
1769
  const newContent = content.replace(toolsArrayRegex, (match) => {
@@ -1764,7 +1776,7 @@ async function updatePromptReferencesInPrompts(promptsDir, oldPromptName, newPro
1764
1776
  return replaced;
1765
1777
  });
1766
1778
  if (modified) {
1767
- await fs2.promises.writeFile(filePath, newContent, "utf-8");
1779
+ await fs2__default.promises.writeFile(filePath, newContent, "utf-8");
1768
1780
  updatedFiles.push(filePath);
1769
1781
  }
1770
1782
  }
@@ -1772,17 +1784,17 @@ async function updatePromptReferencesInPrompts(promptsDir, oldPromptName, newPro
1772
1784
  }
1773
1785
  async function updatePromptReferencesInAgents(agentsDir, oldPromptName, newPromptName) {
1774
1786
  const updatedFiles = [];
1775
- if (!fs2.existsSync(agentsDir)) {
1787
+ if (!fs2__default.existsSync(agentsDir)) {
1776
1788
  return updatedFiles;
1777
1789
  }
1778
- const files = fs2.readdirSync(agentsDir).filter((f) => f.endsWith(".ts"));
1790
+ const files = fs2__default.readdirSync(agentsDir).filter((f) => f.endsWith(".ts"));
1779
1791
  for (const file of files) {
1780
- const filePath = path3.join(agentsDir, file);
1781
- let content = await fs2.promises.readFile(filePath, "utf-8");
1792
+ const filePath = path8__default.join(agentsDir, file);
1793
+ let content = await fs2__default.promises.readFile(filePath, "utf-8");
1782
1794
  const promptRegex = new RegExp(`prompt:\\s*['"]${escapeRegExp(oldPromptName)}['"]`, "g");
1783
1795
  if (promptRegex.test(content)) {
1784
1796
  content = content.replace(promptRegex, `prompt: '${newPromptName}'`);
1785
- await fs2.promises.writeFile(filePath, content, "utf-8");
1797
+ await fs2__default.promises.writeFile(filePath, content, "utf-8");
1786
1798
  updatedFiles.push(filePath);
1787
1799
  }
1788
1800
  }
@@ -1795,6 +1807,9 @@ function validateAgentData(data) {
1795
1807
  if (!data.name || typeof data.name !== "string") {
1796
1808
  return "Agent name is required and must be a string";
1797
1809
  }
1810
+ if (data.name.includes("/")) {
1811
+ return "Agent name cannot contain '/'. Reserved for namespace qualification.";
1812
+ }
1798
1813
  if (data.title !== void 0 && typeof data.title !== "string") {
1799
1814
  return "Agent title must be a string if provided";
1800
1815
  }
@@ -1853,166 +1868,2528 @@ function validateAgentData(data) {
1853
1868
  }
1854
1869
  return null;
1855
1870
  }
1856
-
1857
- // src/plugin.ts
1858
- var VIRTUAL_TOOLS_ID = "virtual:@standardagents-tools";
1859
- var RESOLVED_VIRTUAL_TOOLS_ID = "\0" + VIRTUAL_TOOLS_ID;
1860
- var VIRTUAL_ROUTES_ID = "virtual:@standardagents-routes";
1861
- var RESOLVED_VIRTUAL_ROUTES_ID = "\0" + VIRTUAL_ROUTES_ID;
1862
- var VIRTUAL_ROUTER_ID = "virtual:@standardagents/router";
1863
- var RESOLVED_VIRTUAL_ROUTER_ID = "\0" + VIRTUAL_ROUTER_ID;
1864
- var VIRTUAL_HOOKS_ID = "virtual:@standardagents-hooks";
1865
- var RESOLVED_VIRTUAL_HOOKS_ID = "\0" + VIRTUAL_HOOKS_ID;
1866
- var VIRTUAL_CONFIG_ID = "virtual:@standardagents-config";
1867
- var RESOLVED_VIRTUAL_CONFIG_ID = "\0" + VIRTUAL_CONFIG_ID;
1868
- var VIRTUAL_THREAD_API_ID = "virtual:@standardagents-thread-api";
1869
- var RESOLVED_VIRTUAL_THREAD_API_ID = "\0" + VIRTUAL_THREAD_API_ID;
1870
- var VIRTUAL_MODELS_ID = "virtual:@standardagents-models";
1871
- var RESOLVED_VIRTUAL_MODELS_ID = "\0" + VIRTUAL_MODELS_ID;
1872
- var VIRTUAL_PROMPTS_ID = "virtual:@standardagents-prompts";
1873
- var RESOLVED_VIRTUAL_PROMPTS_ID = "\0" + VIRTUAL_PROMPTS_ID;
1874
- var VIRTUAL_AGENTS_ID = "virtual:@standardagents-agents";
1875
- var RESOLVED_VIRTUAL_AGENTS_ID = "\0" + VIRTUAL_AGENTS_ID;
1876
- var VIRTUAL_EFFECTS_ID = "virtual:@standardagents-effects";
1877
- var RESOLVED_VIRTUAL_EFFECTS_ID = "\0" + VIRTUAL_EFFECTS_ID;
1878
- var VIRTUAL_PROVIDERS_ID = "virtual:@standardagents-providers";
1879
- var RESOLVED_VIRTUAL_PROVIDERS_ID = "\0" + VIRTUAL_PROVIDERS_ID;
1880
- var VIRTUAL_BUILDER_ID = "virtual:@standardagents/builder";
1881
- var RESOLVED_VIRTUAL_BUILDER_ID = "\0" + VIRTUAL_BUILDER_ID;
1882
- function scanApiDirectory(dir, baseRoute = "") {
1883
- const routes = [];
1884
- if (!fs2.existsSync(dir)) {
1885
- return routes;
1871
+ var MetadataService = class {
1872
+ metadataDir;
1873
+ /**
1874
+ * Create a new MetadataService.
1875
+ *
1876
+ * @param agentsDir - Path to the agents/ directory
1877
+ */
1878
+ constructor(agentsDir) {
1879
+ this.metadataDir = path8.join(agentsDir, ".standardagent");
1886
1880
  }
1887
- try {
1888
- const entries = fs2.readdirSync(dir, { withFileTypes: true });
1889
- for (const entry of entries) {
1890
- const fullPath = path3.join(dir, entry.name);
1891
- if (entry.isDirectory()) {
1892
- const convertedDirName = entry.name.replace(/\[([^\]]+)\]/g, ":$1");
1893
- const subRoute = baseRoute + "/" + convertedDirName;
1894
- routes.push(...scanApiDirectory(fullPath, subRoute));
1895
- } else if (entry.isFile() && entry.name.endsWith(".ts")) {
1896
- const fileName = entry.name.replace(/\.ts$/, "");
1897
- let method = "";
1898
- let routePath = baseRoute;
1899
- if (fileName.includes(".")) {
1900
- const parts = fileName.split(".");
1901
- const methodPart = parts[parts.length - 1].toUpperCase();
1902
- if (["GET", "POST", "PUT", "DELETE", "PATCH"].includes(methodPart)) {
1903
- method = methodPart;
1904
- const pathPart = parts.slice(0, -1).join(".");
1905
- if (pathPart !== "index") {
1906
- const convertedPath = pathPart.replace(/\[([^\]]+)\]/g, ":$1");
1907
- routePath += "/" + convertedPath;
1908
- }
1909
- } else {
1910
- if (fileName !== "index") {
1911
- const convertedPath = fileName.replace(/\[([^\]]+)\]/g, ":$1");
1912
- routePath += "/" + convertedPath;
1881
+ /**
1882
+ * Read metadata for an agent.
1883
+ *
1884
+ * @param agentName - Name of the agent
1885
+ * @returns Metadata if found, null otherwise
1886
+ */
1887
+ async read(agentName) {
1888
+ const filePath = this.getMetadataPath(agentName);
1889
+ if (!fs2.existsSync(filePath)) {
1890
+ return null;
1891
+ }
1892
+ try {
1893
+ const content = fs2.readFileSync(filePath, "utf-8");
1894
+ return JSON.parse(content);
1895
+ } catch {
1896
+ return null;
1897
+ }
1898
+ }
1899
+ /**
1900
+ * Write metadata for an agent.
1901
+ *
1902
+ * @param agentName - Name of the agent
1903
+ * @param metadata - Metadata to write
1904
+ */
1905
+ async write(agentName, metadata) {
1906
+ if (!fs2.existsSync(this.metadataDir)) {
1907
+ fs2.mkdirSync(this.metadataDir, { recursive: true });
1908
+ }
1909
+ const filePath = this.getMetadataPath(agentName);
1910
+ fs2.writeFileSync(filePath, JSON.stringify(metadata, null, 2));
1911
+ }
1912
+ /**
1913
+ * Delete metadata for an agent.
1914
+ *
1915
+ * @param agentName - Name of the agent
1916
+ */
1917
+ async delete(agentName) {
1918
+ const filePath = this.getMetadataPath(agentName);
1919
+ if (fs2.existsSync(filePath)) {
1920
+ fs2.unlinkSync(filePath);
1921
+ }
1922
+ }
1923
+ /**
1924
+ * Get the next version by incrementing the patch number.
1925
+ *
1926
+ * @param currentVersion - Current semver version (e.g., "1.0.0")
1927
+ * @returns Incremented version (e.g., "1.0.1")
1928
+ */
1929
+ getNextVersion(currentVersion) {
1930
+ const parts = currentVersion.split(".");
1931
+ if (parts.length < 3) {
1932
+ return `${currentVersion}.1`;
1933
+ }
1934
+ const patchPart = parts[2];
1935
+ const prereleaseIndex = patchPart.indexOf("-");
1936
+ if (prereleaseIndex > -1) {
1937
+ const patch2 = parseInt(patchPart.substring(0, prereleaseIndex), 10);
1938
+ const prerelease = patchPart.substring(prereleaseIndex);
1939
+ return `${parts[0]}.${parts[1]}.${patch2 + 1}${prerelease}`;
1940
+ }
1941
+ const patch = parseInt(patchPart, 10);
1942
+ return `${parts[0]}.${parts[1]}.${patch + 1}`;
1943
+ }
1944
+ /**
1945
+ * Validate a semver version string.
1946
+ *
1947
+ * @param version - Version string to validate
1948
+ * @returns true if valid semver format
1949
+ */
1950
+ isValidVersion(version) {
1951
+ const semverPattern = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/;
1952
+ return semverPattern.test(version);
1953
+ }
1954
+ /**
1955
+ * Validate an npm package name.
1956
+ *
1957
+ * @param name - Package name to validate
1958
+ * @returns Object with valid flag and optional error message
1959
+ */
1960
+ validatePackageName(name) {
1961
+ if (!name) {
1962
+ return { valid: false, error: "Package name is required" };
1963
+ }
1964
+ if (name.length > 214) {
1965
+ return { valid: false, error: "Package name must be 214 characters or less" };
1966
+ }
1967
+ if (name.startsWith(".") || name.startsWith("_")) {
1968
+ return { valid: false, error: "Package name cannot start with . or _" };
1969
+ }
1970
+ if (name !== name.toLowerCase()) {
1971
+ return { valid: false, error: "Package name must be lowercase" };
1972
+ }
1973
+ const validPattern = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
1974
+ if (!validPattern.test(name)) {
1975
+ return {
1976
+ valid: false,
1977
+ error: "Package name can only contain lowercase letters, numbers, hyphens, underscores, and dots"
1978
+ };
1979
+ }
1980
+ return { valid: true };
1981
+ }
1982
+ /**
1983
+ * Generate a suggested package name from project ID and agent name.
1984
+ *
1985
+ * @param projectId - Project identifier (optional)
1986
+ * @param agentName - Agent name
1987
+ * @returns Suggested npm package name
1988
+ */
1989
+ suggestPackageName(projectId, agentName) {
1990
+ const normalizedAgent = agentName.toLowerCase().replace(/_/g, "-");
1991
+ if (projectId) {
1992
+ const normalizedProject = projectId.toLowerCase().replace(/_/g, "-");
1993
+ return `standardagent-${normalizedProject}-${normalizedAgent}`;
1994
+ }
1995
+ return `standardagent-${normalizedAgent}`;
1996
+ }
1997
+ /**
1998
+ * Get the file path for an agent's metadata.
1999
+ */
2000
+ getMetadataPath(agentName) {
2001
+ return path8.join(this.metadataDir, `${agentName}.json`);
2002
+ }
2003
+ };
2004
+ function extractToolUses(sourceCode) {
2005
+ const sourceFile = ts.createSourceFile(
2006
+ "tool.ts",
2007
+ sourceCode,
2008
+ ts.ScriptTarget.ESNext,
2009
+ true
2010
+ );
2011
+ const result = {
2012
+ uses: [],
2013
+ found: false
2014
+ };
2015
+ function visit(node) {
2016
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "defineTool" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
2017
+ const configObj = node.arguments[0];
2018
+ for (const prop of configObj.properties) {
2019
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === "uses" && ts.isArrayLiteralExpression(prop.initializer)) {
2020
+ result.found = true;
2021
+ result.line = sourceFile.getLineAndCharacterOfPosition(prop.getStart()).line + 1;
2022
+ for (const element of prop.initializer.elements) {
2023
+ if (ts.isStringLiteral(element)) {
2024
+ result.uses.push(element.text);
1913
2025
  }
1914
2026
  }
1915
- } else {
1916
- if (fileName !== "index") {
1917
- const convertedPath = fileName.replace(/\[([^\]]+)\]/g, ":$1");
1918
- routePath += "/" + convertedPath;
1919
- }
1920
2027
  }
1921
- const importPath = "./" + path3.relative(process.cwd(), fullPath).replace(/\\/g, "/");
1922
- routes.push({
1923
- method,
1924
- route: routePath || "/",
1925
- importPath
1926
- });
1927
2028
  }
1928
2029
  }
1929
- return routes;
1930
- } catch (error) {
1931
- console.error(`Error scanning API directory ${dir}:`, error);
1932
- return [];
2030
+ ts.forEachChild(node, visit);
1933
2031
  }
2032
+ visit(sourceFile);
2033
+ return result;
1934
2034
  }
1935
- function isSnakeCase(str) {
1936
- return /^[a-z][a-z0-9_]*[a-z0-9]$/.test(str) || /^[a-z]$/.test(str);
2035
+ function extractPromptTools(sourceCode) {
2036
+ const sourceFile = ts.createSourceFile(
2037
+ "prompt.ts",
2038
+ sourceCode,
2039
+ ts.ScriptTarget.ESNext,
2040
+ true
2041
+ );
2042
+ const result = {
2043
+ tools: [],
2044
+ hasSubpromptConfigs: false
2045
+ };
2046
+ function visit(node) {
2047
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "definePrompt" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
2048
+ const configObj = node.arguments[0];
2049
+ for (const prop of configObj.properties) {
2050
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === "tools" && ts.isArrayLiteralExpression(prop.initializer)) {
2051
+ for (const element of prop.initializer.elements) {
2052
+ if (ts.isStringLiteral(element)) {
2053
+ result.tools.push(element.text);
2054
+ } else if (ts.isObjectLiteralExpression(element)) {
2055
+ result.hasSubpromptConfigs = true;
2056
+ for (const objProp of element.properties) {
2057
+ if (ts.isPropertyAssignment(objProp) && ts.isIdentifier(objProp.name) && objProp.name.text === "name" && ts.isStringLiteral(objProp.initializer)) {
2058
+ result.tools.push(objProp.initializer.text);
2059
+ }
2060
+ }
2061
+ }
2062
+ }
2063
+ }
2064
+ }
2065
+ }
2066
+ ts.forEachChild(node, visit);
2067
+ }
2068
+ visit(sourceFile);
2069
+ return result;
1937
2070
  }
1938
- function validateToolFile(filePath, fileName) {
1939
- try {
1940
- const content = fs2.readFileSync(filePath, "utf-8");
1941
- const hasDefaultExport = /export\s+default\s+defineTool/.test(content);
1942
- if (!hasDefaultExport) {
1943
- return `Tool file '${fileName}.ts' must have a default export using defineTool()`;
2071
+ function extractPromptModel(sourceCode) {
2072
+ const sourceFile = ts.createSourceFile(
2073
+ "prompt.ts",
2074
+ sourceCode,
2075
+ ts.ScriptTarget.ESNext,
2076
+ true
2077
+ );
2078
+ let model = null;
2079
+ function visit(node) {
2080
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "definePrompt" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
2081
+ const configObj = node.arguments[0];
2082
+ for (const prop of configObj.properties) {
2083
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === "model" && ts.isStringLiteral(prop.initializer)) {
2084
+ model = prop.initializer.text;
2085
+ }
2086
+ }
1944
2087
  }
1945
- return null;
1946
- } catch (error) {
1947
- return `Failed to read tool file '${fileName}.ts'`;
2088
+ ts.forEachChild(node, visit);
1948
2089
  }
2090
+ visit(sourceFile);
2091
+ return model;
1949
2092
  }
1950
- async function scanToolsDirectory(dir) {
1951
- const tools = [];
1952
- if (!fs2.existsSync(dir)) {
1953
- return tools;
2093
+ function extractPromptIncludes(sourceCode) {
2094
+ const sourceFile = ts.createSourceFile(
2095
+ "prompt.ts",
2096
+ sourceCode,
2097
+ ts.ScriptTarget.ESNext,
2098
+ true
2099
+ );
2100
+ const includes = [];
2101
+ function extractFromStructuredPrompt(arr) {
2102
+ for (const element of arr.elements) {
2103
+ if (ts.isObjectLiteralExpression(element)) {
2104
+ let hasIncludeType = false;
2105
+ let includeName = null;
2106
+ for (const prop of element.properties) {
2107
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
2108
+ if (prop.name.text === "type" && ts.isStringLiteral(prop.initializer) && prop.initializer.text === "include") {
2109
+ hasIncludeType = true;
2110
+ }
2111
+ if (prop.name.text === "name" && ts.isStringLiteral(prop.initializer)) {
2112
+ includeName = prop.initializer.text;
2113
+ }
2114
+ }
2115
+ }
2116
+ if (hasIncludeType && includeName && !includes.includes(includeName)) {
2117
+ includes.push(includeName);
2118
+ }
2119
+ }
2120
+ }
1954
2121
  }
1955
- const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
1956
- for (const entry of entries) {
1957
- if (entry.isFile() && entry.name.endsWith(".ts")) {
1958
- const fileName = entry.name.replace(".ts", "");
1959
- const filePath = path3.join(dir, entry.name);
1960
- const importPath = "./" + path3.relative(process.cwd(), filePath).replace(/\\/g, "/");
1961
- let toolError;
1962
- const validationError = validateToolFile(filePath, fileName);
1963
- if (validationError) {
1964
- toolError = validationError;
1965
- console.error(`
1966
- \u274C Tool validation error: ${validationError}`);
2122
+ function visit(node) {
2123
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "definePrompt" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
2124
+ const configObj = node.arguments[0];
2125
+ for (const prop of configObj.properties) {
2126
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
2127
+ if (prop.name.text === "prompt" && ts.isArrayLiteralExpression(prop.initializer)) {
2128
+ extractFromStructuredPrompt(prop.initializer);
2129
+ }
2130
+ if (prop.name.text === "includes" && ts.isArrayLiteralExpression(prop.initializer)) {
2131
+ for (const element of prop.initializer.elements) {
2132
+ if (ts.isStringLiteral(element) && !includes.includes(element.text)) {
2133
+ includes.push(element.text);
2134
+ }
2135
+ }
2136
+ }
2137
+ }
1967
2138
  }
1968
- if (!isSnakeCase(fileName)) {
1969
- const warning = `Tool name should be in snake_case format (e.g., 'log_name', 'send_email')`;
1970
- if (toolError) {
1971
- toolError += ` | ${warning}`;
1972
- } else {
1973
- toolError = warning;
2139
+ }
2140
+ ts.forEachChild(node, visit);
2141
+ }
2142
+ visit(sourceFile);
2143
+ return includes;
2144
+ }
2145
+ function extractAgentPrompts(sourceCode) {
2146
+ const sourceFile = ts.createSourceFile(
2147
+ "agent.ts",
2148
+ sourceCode,
2149
+ ts.ScriptTarget.ESNext,
2150
+ true
2151
+ );
2152
+ const result = {};
2153
+ function extractPromptFromSide(obj) {
2154
+ for (const prop of obj.properties) {
2155
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === "prompt" && ts.isStringLiteral(prop.initializer)) {
2156
+ return prop.initializer.text;
2157
+ }
2158
+ }
2159
+ return void 0;
2160
+ }
2161
+ function visit(node) {
2162
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "defineAgent" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
2163
+ const configObj = node.arguments[0];
2164
+ for (const prop of configObj.properties) {
2165
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
2166
+ if (prop.name.text === "sideA" && ts.isObjectLiteralExpression(prop.initializer)) {
2167
+ result.sideA = extractPromptFromSide(prop.initializer);
2168
+ }
2169
+ if (prop.name.text === "sideB" && ts.isObjectLiteralExpression(prop.initializer)) {
2170
+ result.sideB = extractPromptFromSide(prop.initializer);
2171
+ }
1974
2172
  }
1975
- console.warn(
1976
- `
1977
- \u26A0\uFE0F Tool naming warning: '${fileName}' should be in snake_case format (e.g., 'log_name', 'send_email')`
1978
- );
1979
2173
  }
1980
- tools.push({ name: fileName, importPath, error: toolError });
1981
2174
  }
2175
+ ts.forEachChild(node, visit);
1982
2176
  }
1983
- return tools;
2177
+ visit(sourceFile);
2178
+ return result;
1984
2179
  }
1985
- async function scanHooksDirectory(dir) {
1986
- const hooks = [];
1987
- if (!fs2.existsSync(dir)) {
1988
- return hooks;
2180
+ function extractAgentDescription(sourceCode) {
2181
+ const sourceFile = ts.createSourceFile(
2182
+ "agent.ts",
2183
+ sourceCode,
2184
+ ts.ScriptTarget.ESNext,
2185
+ true
2186
+ );
2187
+ let description = null;
2188
+ function visit(node) {
2189
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "defineAgent" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
2190
+ const configObj = node.arguments[0];
2191
+ for (const prop of configObj.properties) {
2192
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === "description" && ts.isStringLiteral(prop.initializer)) {
2193
+ description = prop.initializer.text;
2194
+ }
2195
+ }
2196
+ }
2197
+ ts.forEachChild(node, visit);
1989
2198
  }
1990
- const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
1991
- for (const entry of entries) {
1992
- if (entry.isFile() && entry.name.endsWith(".ts")) {
1993
- const fileName = entry.name.replace(".ts", "");
1994
- if (fileName === "index") {
1995
- continue;
2199
+ visit(sourceFile);
2200
+ return description;
2201
+ }
2202
+ function extractDefinitionName(sourceCode) {
2203
+ const sourceFile = ts.createSourceFile(
2204
+ "def.ts",
2205
+ sourceCode,
2206
+ ts.ScriptTarget.ESNext,
2207
+ true
2208
+ );
2209
+ let name = null;
2210
+ function visit(node) {
2211
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && (node.expression.text === "defineTool" || node.expression.text === "definePrompt" || node.expression.text === "defineAgent" || node.expression.text === "defineModel" || node.expression.text === "defineHook") && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
2212
+ const configObj = node.arguments[0];
2213
+ for (const prop of configObj.properties) {
2214
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === "name" && ts.isStringLiteral(prop.initializer)) {
2215
+ name = prop.initializer.text;
2216
+ }
1996
2217
  }
1997
- const filePath = path3.join(dir, entry.name);
1998
- const importPath = "./" + path3.relative(process.cwd(), filePath).replace(/\\/g, "/");
1999
- hooks.push({ name: fileName, importPath });
2000
2218
  }
2219
+ ts.forEachChild(node, visit);
2001
2220
  }
2002
- return hooks;
2221
+ visit(sourceFile);
2222
+ return name;
2003
2223
  }
2004
- async function scanConfigDirectory(dir, definePattern) {
2005
- const items = [];
2006
- if (!fs2.existsSync(dir)) {
2007
- return items;
2224
+ function extractModelFallbacks(sourceCode) {
2225
+ const sourceFile = ts.createSourceFile(
2226
+ "model.ts",
2227
+ sourceCode,
2228
+ ts.ScriptTarget.ESNext,
2229
+ true
2230
+ );
2231
+ const fallbacks = [];
2232
+ function visit(node) {
2233
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "defineModel" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
2234
+ const configObj = node.arguments[0];
2235
+ for (const prop of configObj.properties) {
2236
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === "fallbacks" && ts.isArrayLiteralExpression(prop.initializer)) {
2237
+ for (const element of prop.initializer.elements) {
2238
+ if (ts.isStringLiteral(element)) {
2239
+ fallbacks.push(element.text);
2240
+ }
2241
+ }
2242
+ }
2243
+ }
2244
+ }
2245
+ ts.forEachChild(node, visit);
2008
2246
  }
2009
- const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
2010
- for (const entry of entries) {
2011
- if (entry.isFile() && entry.name.endsWith(".ts")) {
2012
- const filePath = path3.join(dir, entry.name);
2013
- const importPath = "./" + path3.relative(process.cwd(), filePath).replace(/\\/g, "/");
2247
+ visit(sourceFile);
2248
+ return fallbacks;
2249
+ }
2250
+ function transformBundledJs(code) {
2251
+ const s = new MagicString(code);
2252
+ const sourceFile = ts.createSourceFile(
2253
+ "file.js",
2254
+ code,
2255
+ ts.ScriptTarget.ESNext,
2256
+ true
2257
+ );
2258
+ let firstImportStart = -1;
2259
+ let varStatementInfo = null;
2260
+ function visit(node) {
2261
+ if (ts.isImportDeclaration(node) && firstImportStart === -1) {
2262
+ firstImportStart = node.getStart();
2263
+ }
2264
+ if (ts.isVariableStatement(node)) {
2265
+ const decl = node.declarationList.declarations[0];
2266
+ if (decl && decl.initializer && ts.isCallExpression(decl.initializer)) {
2267
+ const callExpr = decl.initializer.expression;
2268
+ if (ts.isIdentifier(callExpr) && (callExpr.text === "defineAgent" || callExpr.text === "definePrompt" || callExpr.text === "defineTool" || callExpr.text === "defineModel" || callExpr.text === "defineHook")) {
2269
+ varStatementInfo = {
2270
+ fullStart: node.getFullStart(),
2271
+ start: node.getStart(),
2272
+ varNameEnd: decl.initializer.getStart()
2273
+ };
2274
+ s.overwrite(varStatementInfo.start, varStatementInfo.varNameEnd, "export default ");
2275
+ }
2276
+ }
2277
+ }
2278
+ if (ts.isExportDeclaration(node)) {
2279
+ const exportClause = node.exportClause;
2280
+ if (exportClause && ts.isNamedExports(exportClause)) {
2281
+ for (const element of exportClause.elements) {
2282
+ if (element.propertyName && ts.isIdentifier(element.name) && element.name.text === "default") {
2283
+ s.remove(node.getFullStart(), node.getEnd());
2284
+ }
2285
+ }
2286
+ }
2287
+ }
2288
+ ts.forEachChild(node, visit);
2289
+ }
2290
+ visit(sourceFile);
2291
+ if (firstImportStart > 0) {
2292
+ s.remove(0, firstImportStart);
2293
+ }
2294
+ return s.toString().trim() + "\n";
2295
+ }
2296
+ function injectAgentMetadata(tsCode, metadata) {
2297
+ if (!metadata.packageName && !metadata.version && !metadata.author && !metadata.license) {
2298
+ return tsCode;
2299
+ }
2300
+ const s = new MagicString(tsCode);
2301
+ const sourceFile = ts.createSourceFile(
2302
+ "agent.ts",
2303
+ tsCode,
2304
+ ts.ScriptTarget.ESNext,
2305
+ true
2306
+ );
2307
+ function visit(node) {
2308
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "defineAgent" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
2309
+ const configObj = node.arguments[0];
2310
+ for (const prop of configObj.properties) {
2311
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === "name") {
2312
+ const metadataProps = [];
2313
+ if (metadata.packageName) {
2314
+ metadataProps.push(`packageName: ${JSON.stringify(metadata.packageName)}`);
2315
+ }
2316
+ if (metadata.version) {
2317
+ metadataProps.push(`version: ${JSON.stringify(metadata.version)}`);
2318
+ }
2319
+ if (metadata.author) {
2320
+ metadataProps.push(`author: ${JSON.stringify(metadata.author)}`);
2321
+ }
2322
+ if (metadata.license) {
2323
+ metadataProps.push(`license: ${JSON.stringify(metadata.license)}`);
2324
+ }
2325
+ if (metadataProps.length > 0) {
2326
+ const propEnd = prop.getEnd();
2327
+ const afterProp = tsCode.substring(propEnd, propEnd + 1);
2328
+ if (afterProp === ",") {
2329
+ const insertText = `
2330
+ ${metadataProps.join(",\n ")},`;
2331
+ s.appendRight(propEnd + 1, insertText);
2332
+ } else {
2333
+ const insertText = `,
2334
+ ${metadataProps.join(",\n ")}`;
2335
+ s.appendRight(propEnd, insertText);
2336
+ }
2337
+ }
2338
+ return;
2339
+ }
2340
+ }
2341
+ }
2342
+ ts.forEachChild(node, visit);
2343
+ }
2344
+ visit(sourceFile);
2345
+ return s.toString();
2346
+ }
2347
+
2348
+ // src/packing/packing-service.ts
2349
+ var PackingService = class {
2350
+ /**
2351
+ * Resolve the version of an npm package from node_modules.
2352
+ *
2353
+ * @param pkgName - Package name (e.g., '@standardagents/spec')
2354
+ * @param rootDir - Root directory of the project
2355
+ * @returns Version specifier (e.g., '^1.2.3') or '*' if not found
2356
+ */
2357
+ resolvePackageVersion(pkgName, rootDir) {
2358
+ const pkgJsonPath = path8.join(rootDir, "node_modules", pkgName, "package.json");
2359
+ try {
2360
+ const pkgJson = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
2361
+ return `^${pkgJson.version}`;
2362
+ } catch {
2363
+ return "*";
2364
+ }
2365
+ }
2366
+ /**
2367
+ * Analyze an agent to discover all its constituents.
2368
+ *
2369
+ * This performs static analysis on the agent definition and all
2370
+ * referenced prompts, tools, models, and hooks.
2371
+ *
2372
+ * @param agentName - Name of the agent to analyze
2373
+ * @param rootDir - Root directory of the agents workspace
2374
+ * @returns Analysis result with all discovered constituents
2375
+ */
2376
+ async analyzeAgent(agentName, rootDir) {
2377
+ const agentsDir = path8.join(rootDir, "agents");
2378
+ const analysis = {
2379
+ agent: agentName,
2380
+ primaryPrompt: "",
2381
+ constituents: {
2382
+ prompts: [],
2383
+ tools: [],
2384
+ models: [],
2385
+ hooks: [],
2386
+ agents: []
2387
+ },
2388
+ shared: {
2389
+ prompts: [],
2390
+ tools: [],
2391
+ models: [],
2392
+ hooks: []
2393
+ },
2394
+ warnings: [],
2395
+ errors: []
2396
+ };
2397
+ const agentFilePath = this.findFile(path8.join(agentsDir, "agents"), agentName);
2398
+ if (!agentFilePath) {
2399
+ analysis.errors.push(`Agent file not found: ${agentName}`);
2400
+ return analysis;
2401
+ }
2402
+ analysis.constituents.agents.push({
2403
+ name: agentName,
2404
+ filePath: agentFilePath,
2405
+ discoveredVia: "static",
2406
+ sharedWith: []
2407
+ });
2408
+ const agentSource = fs2.readFileSync(agentFilePath, "utf-8");
2409
+ const agentPrompts = extractAgentPrompts(agentSource);
2410
+ if (agentPrompts.sideA) {
2411
+ analysis.primaryPrompt = agentPrompts.sideA;
2412
+ await this.analyzePrompt(agentPrompts.sideA, agentsDir, analysis, /* @__PURE__ */ new Set(), `agent:${agentName}`);
2413
+ }
2414
+ if (agentPrompts.sideB) {
2415
+ await this.analyzePrompt(agentPrompts.sideB, agentsDir, analysis, /* @__PURE__ */ new Set(), `agent:${agentName}`);
2416
+ }
2417
+ await this.checkSharedItems(agentsDir, analysis);
2418
+ return analysis;
2419
+ }
2420
+ /**
2421
+ * Generate a README for an analyzed agent.
2422
+ *
2423
+ * This is used during analysis to provide a pre-populated README
2424
+ * that can be edited in the PackModal before packing.
2425
+ *
2426
+ * @param analysis - The packing analysis result
2427
+ * @param rootDir - Root directory of the project
2428
+ * @returns Object with generatedReadme and agentDescription
2429
+ */
2430
+ generateReadmeForAnalysis(analysis, rootDir) {
2431
+ const agentsDir = path8.join(rootDir, "agents");
2432
+ const metadataService = new MetadataService(agentsDir);
2433
+ let agentDescription;
2434
+ const agentItem = analysis.constituents.agents.find((a) => a.name === analysis.agent);
2435
+ if (agentItem?.filePath) {
2436
+ try {
2437
+ const agentSource = fs2.readFileSync(agentItem.filePath, "utf-8");
2438
+ agentDescription = extractAgentDescription(agentSource) || void 0;
2439
+ } catch {
2440
+ }
2441
+ }
2442
+ const suggestedPackageName = metadataService.suggestPackageName(void 0, analysis.agent);
2443
+ const generatedReadme = this.generateReadme(
2444
+ suggestedPackageName,
2445
+ analysis.agent,
2446
+ "1.0.0",
2447
+ // Default version, will be replaced in modal
2448
+ "MIT",
2449
+ // Default license
2450
+ agentDescription
2451
+ );
2452
+ return { generatedReadme, agentDescription };
2453
+ }
2454
+ /**
2455
+ * Recursively analyze a prompt and its dependencies.
2456
+ */
2457
+ async analyzePrompt(promptName, agentsDir, analysis, visited, parentKey) {
2458
+ const promptFilePath = this.findFile(path8.join(agentsDir, "prompts"), promptName);
2459
+ if (!promptFilePath) {
2460
+ analysis.warnings.push(`Prompt file not found: ${promptName}`);
2461
+ return;
2462
+ }
2463
+ const thisKey = `prompt:${promptName}`;
2464
+ if (!analysis.constituents.prompts.some((p) => p.name === promptName && p.parentKey === parentKey)) {
2465
+ analysis.constituents.prompts.push({
2466
+ name: promptName,
2467
+ filePath: promptFilePath,
2468
+ discoveredVia: "agent-prompt",
2469
+ sharedWith: [],
2470
+ parentKey
2471
+ });
2472
+ }
2473
+ if (visited.has(`prompt:${promptName}`)) {
2474
+ return;
2475
+ }
2476
+ visited.add(`prompt:${promptName}`);
2477
+ const promptSource = fs2.readFileSync(promptFilePath, "utf-8");
2478
+ const modelName = extractPromptModel(promptSource);
2479
+ if (modelName) {
2480
+ await this.analyzeModel(modelName, agentsDir, analysis, visited, thisKey);
2481
+ }
2482
+ const { tools: toolNames } = extractPromptTools(promptSource);
2483
+ for (const toolName of toolNames) {
2484
+ await this.analyzeTool(toolName, agentsDir, analysis, visited, "prompt-tools", thisKey);
2485
+ }
2486
+ const includedPrompts = extractPromptIncludes(promptSource);
2487
+ for (const includedPrompt of includedPrompts) {
2488
+ const item = analysis.constituents.prompts.find((p) => p.name === includedPrompt);
2489
+ if (item) {
2490
+ if (item.discoveredVia === "agent-prompt") {
2491
+ item.discoveredVia = "prompt-includes";
2492
+ }
2493
+ }
2494
+ await this.analyzePrompt(includedPrompt, agentsDir, analysis, visited, thisKey);
2495
+ }
2496
+ }
2497
+ /**
2498
+ * Analyze a tool and its dependencies.
2499
+ * This also handles prompts-as-tools and agents-as-tools (handoffs).
2500
+ */
2501
+ async analyzeTool(toolName, agentsDir, analysis, visited, discoveredVia, parentKey) {
2502
+ const thisKey = `tool:${toolName}`;
2503
+ const promptFilePath = this.findFile(path8.join(agentsDir, "prompts"), toolName);
2504
+ if (promptFilePath) {
2505
+ await this.analyzePrompt(toolName, agentsDir, analysis, visited, parentKey);
2506
+ return;
2507
+ }
2508
+ const agentFilePath = this.findFile(path8.join(agentsDir, "agents"), toolName);
2509
+ if (agentFilePath) {
2510
+ await this.analyzeNestedAgent(toolName, agentsDir, analysis, visited, parentKey);
2511
+ return;
2512
+ }
2513
+ const toolFilePath = this.findFile(path8.join(agentsDir, "tools"), toolName);
2514
+ if (!toolFilePath) {
2515
+ analysis.warnings.push(`Tool file not found: ${toolName}`);
2516
+ return;
2517
+ }
2518
+ if (!analysis.constituents.tools.some((t) => t.name === toolName && t.parentKey === parentKey)) {
2519
+ analysis.constituents.tools.push({
2520
+ name: toolName,
2521
+ filePath: toolFilePath,
2522
+ discoveredVia,
2523
+ sharedWith: [],
2524
+ parentKey
2525
+ });
2526
+ }
2527
+ if (visited.has(`tool:${toolName}`)) {
2528
+ return;
2529
+ }
2530
+ visited.add(`tool:${toolName}`);
2531
+ const toolSource = fs2.readFileSync(toolFilePath, "utf-8");
2532
+ const { uses } = extractToolUses(toolSource);
2533
+ for (const usedItem of uses) {
2534
+ await this.analyzeTool(usedItem, agentsDir, analysis, visited, "uses", thisKey);
2535
+ }
2536
+ }
2537
+ /**
2538
+ * Analyze a nested agent (used as a handoff target).
2539
+ */
2540
+ async analyzeNestedAgent(agentName, agentsDir, analysis, visited, parentKey) {
2541
+ const agentFilePath = this.findFile(path8.join(agentsDir, "agents"), agentName);
2542
+ if (!agentFilePath) {
2543
+ analysis.warnings.push(`Agent file not found: ${agentName}`);
2544
+ return;
2545
+ }
2546
+ const thisKey = `agent:${agentName}`;
2547
+ if (!analysis.constituents.agents.some((a) => a.name === agentName && a.parentKey === parentKey)) {
2548
+ analysis.constituents.agents.push({
2549
+ name: agentName,
2550
+ filePath: agentFilePath,
2551
+ discoveredVia: "uses",
2552
+ sharedWith: [],
2553
+ parentKey
2554
+ });
2555
+ }
2556
+ if (visited.has(`agent:${agentName}`)) {
2557
+ return;
2558
+ }
2559
+ visited.add(`agent:${agentName}`);
2560
+ const agentSource = fs2.readFileSync(agentFilePath, "utf-8");
2561
+ const agentPrompts = extractAgentPrompts(agentSource);
2562
+ if (agentPrompts.sideA) {
2563
+ await this.analyzePrompt(agentPrompts.sideA, agentsDir, analysis, visited, thisKey);
2564
+ }
2565
+ if (agentPrompts.sideB) {
2566
+ await this.analyzePrompt(agentPrompts.sideB, agentsDir, analysis, visited, thisKey);
2567
+ }
2568
+ }
2569
+ /**
2570
+ * Analyze a model and its fallbacks.
2571
+ */
2572
+ async analyzeModel(modelName, agentsDir, analysis, visited, parentKey) {
2573
+ const modelFilePath = this.findFile(path8.join(agentsDir, "models"), modelName);
2574
+ if (!modelFilePath) {
2575
+ analysis.warnings.push(`Model file not found: ${modelName}`);
2576
+ return;
2577
+ }
2578
+ const thisKey = `model:${modelName}`;
2579
+ if (!analysis.constituents.models.some((m) => m.name === modelName && m.parentKey === parentKey)) {
2580
+ analysis.constituents.models.push({
2581
+ name: modelName,
2582
+ filePath: modelFilePath,
2583
+ discoveredVia: "static",
2584
+ sharedWith: [],
2585
+ parentKey
2586
+ });
2587
+ }
2588
+ if (visited.has(`model:${modelName}`)) {
2589
+ return;
2590
+ }
2591
+ visited.add(`model:${modelName}`);
2592
+ const modelSource = fs2.readFileSync(modelFilePath, "utf-8");
2593
+ const fallbacks = extractModelFallbacks(modelSource);
2594
+ for (const fallbackName of fallbacks) {
2595
+ await this.analyzeModel(fallbackName, agentsDir, analysis, visited, thisKey);
2596
+ }
2597
+ }
2598
+ /**
2599
+ * Check which items are shared with other agents.
2600
+ */
2601
+ async checkSharedItems(agentsDir, analysis) {
2602
+ const agentsPath = path8.join(agentsDir, "agents");
2603
+ if (!fs2.existsSync(agentsPath)) return;
2604
+ const agentFiles = fs2.readdirSync(agentsPath).filter((f) => f.endsWith(".ts"));
2605
+ for (const agentFile of agentFiles) {
2606
+ const otherAgentName = agentFile.replace(".ts", "");
2607
+ if (otherAgentName === analysis.agent) continue;
2608
+ const otherAnalysis = await this.analyzeAgentLight(otherAgentName, path8.dirname(agentsDir));
2609
+ for (const prompt of analysis.constituents.prompts) {
2610
+ if (otherAnalysis.prompts.includes(prompt.name)) {
2611
+ prompt.sharedWith.push(otherAgentName);
2612
+ if (!analysis.shared.prompts.includes(prompt.name)) {
2613
+ analysis.shared.prompts.push(prompt.name);
2614
+ }
2615
+ }
2616
+ }
2617
+ for (const tool of analysis.constituents.tools) {
2618
+ if (otherAnalysis.tools.includes(tool.name)) {
2619
+ tool.sharedWith.push(otherAgentName);
2620
+ if (!analysis.shared.tools.includes(tool.name)) {
2621
+ analysis.shared.tools.push(tool.name);
2622
+ }
2623
+ }
2624
+ }
2625
+ for (const model of analysis.constituents.models) {
2626
+ if (otherAnalysis.models.includes(model.name)) {
2627
+ model.sharedWith.push(otherAgentName);
2628
+ if (!analysis.shared.models.includes(model.name)) {
2629
+ analysis.shared.models.push(model.name);
2630
+ }
2631
+ }
2632
+ }
2633
+ }
2634
+ }
2635
+ /**
2636
+ * Light analysis of an agent - just get the names of used items.
2637
+ */
2638
+ async analyzeAgentLight(agentName, rootDir) {
2639
+ const result = { prompts: [], tools: [], models: [] };
2640
+ const agentsDir = path8.join(rootDir, "agents");
2641
+ const visited = /* @__PURE__ */ new Set();
2642
+ const agentFilePath = this.findFile(path8.join(agentsDir, "agents"), agentName);
2643
+ if (!agentFilePath) return result;
2644
+ const agentSource = fs2.readFileSync(agentFilePath, "utf-8");
2645
+ const agentPrompts = extractAgentPrompts(agentSource);
2646
+ const analyzePromptLight = (promptName) => {
2647
+ if (visited.has(promptName)) return;
2648
+ visited.add(promptName);
2649
+ result.prompts.push(promptName);
2650
+ const promptFilePath = this.findFile(path8.join(agentsDir, "prompts"), promptName);
2651
+ if (!promptFilePath) return;
2652
+ const promptSource = fs2.readFileSync(promptFilePath, "utf-8");
2653
+ const modelName = extractPromptModel(promptSource);
2654
+ if (modelName && !result.models.includes(modelName)) {
2655
+ result.models.push(modelName);
2656
+ }
2657
+ const { tools } = extractPromptTools(promptSource);
2658
+ for (const tool of tools) {
2659
+ if (!result.tools.includes(tool)) {
2660
+ result.tools.push(tool);
2661
+ }
2662
+ }
2663
+ const includes = extractPromptIncludes(promptSource);
2664
+ for (const inc of includes) {
2665
+ analyzePromptLight(inc);
2666
+ }
2667
+ };
2668
+ if (agentPrompts.sideA) analyzePromptLight(agentPrompts.sideA);
2669
+ if (agentPrompts.sideB) analyzePromptLight(agentPrompts.sideB);
2670
+ return result;
2671
+ }
2672
+ /**
2673
+ * Pack an agent with all its dependencies using Rollup bundling.
2674
+ *
2675
+ * The packing format creates:
2676
+ * - dist/{type}/{name}.js - Individual bundled files per constituent
2677
+ * - dist/index.js - Re-exports and lazy loaders
2678
+ * - dist/index.d.ts - TypeScript type definitions
2679
+ * - package.json - With standardagent field and dependencies
2680
+ * - tsconfig.json - For building
2681
+ *
2682
+ * @param options - Packing options
2683
+ * @returns Packing result
2684
+ */
2685
+ async pack(options) {
2686
+ const {
2687
+ agentName,
2688
+ rootDir,
2689
+ outputDir,
2690
+ version = "1.0.0",
2691
+ removeOriginals = false,
2692
+ packageId = `standardagent-${agentName.replace(/_/g, "-")}`,
2693
+ packageName,
2694
+ license,
2695
+ licenseOwner,
2696
+ itemSelections,
2697
+ readme
2698
+ } = options;
2699
+ const finalPackageName = packageName || packageId;
2700
+ const packedAt = Date.now();
2701
+ const meta = {
2702
+ packageId,
2703
+ version,
2704
+ entryAgents: [agentName],
2705
+ packedAt
2706
+ };
2707
+ const result = {
2708
+ success: false,
2709
+ packageId,
2710
+ outputPath: "",
2711
+ meta,
2712
+ filesCreated: [],
2713
+ warnings: []
2714
+ };
2715
+ try {
2716
+ const analysis = await this.analyzeAgent(agentName, rootDir);
2717
+ if (analysis.errors.length > 0) {
2718
+ result.error = analysis.errors.join("\n");
2719
+ result.warnings = analysis.warnings;
2720
+ return result;
2721
+ }
2722
+ result.warnings = analysis.warnings;
2723
+ const pkgOutputDir = path8.join(outputDir, packageId);
2724
+ fs2.mkdirSync(path8.join(pkgOutputDir, "dist", "agents"), { recursive: true });
2725
+ fs2.mkdirSync(path8.join(pkgOutputDir, "dist", "prompts"), { recursive: true });
2726
+ fs2.mkdirSync(path8.join(pkgOutputDir, "dist", "tools"), { recursive: true });
2727
+ fs2.mkdirSync(path8.join(pkgOutputDir, "dist", "models"), { recursive: true });
2728
+ fs2.mkdirSync(path8.join(pkgOutputDir, "dist", "hooks"), { recursive: true });
2729
+ const seenItems = /* @__PURE__ */ new Set();
2730
+ const allItems = [];
2731
+ for (const a of analysis.constituents.agents) {
2732
+ const key = `agent:${a.name}`;
2733
+ if (!seenItems.has(key)) {
2734
+ seenItems.add(key);
2735
+ allItems.push({ ...a, type: "agent" });
2736
+ }
2737
+ }
2738
+ for (const p of analysis.constituents.prompts) {
2739
+ const key = `prompt:${p.name}`;
2740
+ if (!seenItems.has(key)) {
2741
+ seenItems.add(key);
2742
+ allItems.push({ ...p, type: "prompt" });
2743
+ }
2744
+ }
2745
+ for (const t of analysis.constituents.tools) {
2746
+ const key = `tool:${t.name}`;
2747
+ if (!seenItems.has(key)) {
2748
+ seenItems.add(key);
2749
+ allItems.push({ ...t, type: "tool" });
2750
+ }
2751
+ }
2752
+ for (const m of analysis.constituents.models) {
2753
+ const key = `model:${m.name}`;
2754
+ if (!seenItems.has(key)) {
2755
+ seenItems.add(key);
2756
+ allItems.push({ ...m, type: "model" });
2757
+ }
2758
+ }
2759
+ for (const h of analysis.constituents.hooks) {
2760
+ const key = `hook:${h.name}`;
2761
+ if (!seenItems.has(key)) {
2762
+ seenItems.add(key);
2763
+ allItems.push({ ...h, type: "hook" });
2764
+ }
2765
+ }
2766
+ const externalDeps = /* @__PURE__ */ new Map();
2767
+ for (const item of allItems) {
2768
+ const outputPath = path8.join(
2769
+ pkgOutputDir,
2770
+ "dist",
2771
+ this.getTypeDir(item.type),
2772
+ `${item.name}.js`
2773
+ );
2774
+ const deps = await this.bundleFile(item.filePath, outputPath, rootDir);
2775
+ for (const [depName, depVersion] of deps) {
2776
+ if (!externalDeps.has(depName)) {
2777
+ externalDeps.set(depName, depVersion);
2778
+ }
2779
+ }
2780
+ result.filesCreated.push(outputPath);
2781
+ }
2782
+ const indexJs = this.generateReExportIndex(analysis, meta);
2783
+ const indexJsPath = path8.join(pkgOutputDir, "dist", "index.js");
2784
+ fs2.writeFileSync(indexJsPath, indexJs);
2785
+ result.filesCreated.push(indexJsPath);
2786
+ const indexDts = this.generateIndexDts(analysis);
2787
+ const indexDtsPath = path8.join(pkgOutputDir, "dist", "index.d.ts");
2788
+ fs2.writeFileSync(indexDtsPath, indexDts);
2789
+ result.filesCreated.push(indexDtsPath);
2790
+ const pkgJson = this.generatePackageJson(
2791
+ finalPackageName,
2792
+ version,
2793
+ agentName,
2794
+ externalDeps,
2795
+ rootDir,
2796
+ license,
2797
+ licenseOwner
2798
+ );
2799
+ const pkgJsonPath = path8.join(pkgOutputDir, "package.json");
2800
+ fs2.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
2801
+ result.filesCreated.push(pkgJsonPath);
2802
+ if (license) {
2803
+ const licenseContent = this.generateLicenseFile(license, licenseOwner);
2804
+ const licensePath = path8.join(pkgOutputDir, "LICENSE");
2805
+ fs2.writeFileSync(licensePath, licenseContent);
2806
+ result.filesCreated.push(licensePath);
2807
+ }
2808
+ let readmeContent = readme;
2809
+ if (!readmeContent) {
2810
+ const agentItem = analysis.constituents.agents.find((a) => a.name === agentName);
2811
+ let agentDescription;
2812
+ if (agentItem?.filePath) {
2813
+ const agentSource = fs2.readFileSync(agentItem.filePath, "utf-8");
2814
+ agentDescription = extractAgentDescription(agentSource) || void 0;
2815
+ }
2816
+ readmeContent = this.generateReadme(
2817
+ finalPackageName,
2818
+ agentName,
2819
+ version,
2820
+ license,
2821
+ agentDescription
2822
+ );
2823
+ }
2824
+ const readmePath = path8.join(pkgOutputDir, "README.md");
2825
+ fs2.writeFileSync(readmePath, readmeContent);
2826
+ result.filesCreated.push(readmePath);
2827
+ if (removeOriginals || itemSelections) {
2828
+ result.filesRemoved = [];
2829
+ for (const item of allItems) {
2830
+ const selection = itemSelections?.find((s) => s.name === item.name && s.type === item.type);
2831
+ const shouldRemove = selection ? selection.mode === "extract" : removeOriginals && item.sharedWith.length === 0;
2832
+ if (shouldRemove && fs2.existsSync(item.filePath)) {
2833
+ fs2.unlinkSync(item.filePath);
2834
+ result.filesRemoved.push(item.filePath);
2835
+ }
2836
+ }
2837
+ }
2838
+ const agentsDir = path8.join(rootDir, "agents");
2839
+ const metadataService = new MetadataService(agentsDir);
2840
+ const metadata = {
2841
+ packageName: finalPackageName,
2842
+ version,
2843
+ license: license || "",
2844
+ licenseOwner: licenseOwner || "",
2845
+ lastPackedAt: (/* @__PURE__ */ new Date()).toISOString()
2846
+ };
2847
+ await metadataService.write(agentName, metadata);
2848
+ result.success = true;
2849
+ result.outputPath = pkgOutputDir;
2850
+ result.meta = meta;
2851
+ return result;
2852
+ } catch (error) {
2853
+ result.error = error instanceof Error ? error.message : String(error);
2854
+ return result;
2855
+ }
2856
+ }
2857
+ /**
2858
+ * Bundle a single source file with its local dependencies using Rollup.
2859
+ *
2860
+ * Local files (from the same agents/ directory) are inlined.
2861
+ * npm packages are kept as external dependencies.
2862
+ * Output is bundled JavaScript with all local deps inlined.
2863
+ *
2864
+ * @returns Map of external dependencies (name -> version specifier)
2865
+ */
2866
+ async bundleFile(inputPath, outputPath, rootDir) {
2867
+ const { rollup } = await import('rollup');
2868
+ const nodeResolve = (await import('@rollup/plugin-node-resolve')).default;
2869
+ const commonjs = (await import('@rollup/plugin-commonjs')).default;
2870
+ const esbuild = (await import('rollup-plugin-esbuild')).default;
2871
+ const externalDeps = /* @__PURE__ */ new Map();
2872
+ const agentsDir = path8.join(rootDir, "agents");
2873
+ const resolveVersion = this.resolvePackageVersion.bind(this);
2874
+ const trackExternalsPlugin = {
2875
+ name: "track-externals",
2876
+ resolveId(source, importer) {
2877
+ if (!importer) {
2878
+ return null;
2879
+ }
2880
+ if (source.startsWith("./") || source.startsWith("../")) {
2881
+ const resolved = path8.resolve(path8.dirname(importer), source);
2882
+ let tsResolved = resolved;
2883
+ if (!resolved.endsWith(".ts") && !resolved.endsWith(".js")) {
2884
+ if (fs2.existsSync(`${resolved}.ts`)) {
2885
+ tsResolved = `${resolved}.ts`;
2886
+ } else if (fs2.existsSync(`${resolved}.js`)) {
2887
+ tsResolved = `${resolved}.js`;
2888
+ } else if (fs2.existsSync(`${resolved}/index.ts`)) {
2889
+ tsResolved = `${resolved}/index.ts`;
2890
+ }
2891
+ }
2892
+ if (tsResolved.includes(agentsDir) || resolved.includes(agentsDir)) {
2893
+ return null;
2894
+ }
2895
+ return null;
2896
+ }
2897
+ if (source.startsWith("/") && source.includes(agentsDir)) {
2898
+ return null;
2899
+ }
2900
+ const pkgName = source.startsWith("@") ? source.split("/").slice(0, 2).join("/") : source.split("/")[0];
2901
+ if (pkgName.startsWith("@standardagents/")) {
2902
+ externalDeps.set(pkgName, resolveVersion(pkgName, rootDir));
2903
+ } else if (pkgName === "zod") {
2904
+ externalDeps.set(pkgName, "^4.0.0");
2905
+ } else {
2906
+ externalDeps.set(pkgName, "*");
2907
+ }
2908
+ return { id: source, external: true };
2909
+ }
2910
+ };
2911
+ const rollupOptions = {
2912
+ input: inputPath,
2913
+ plugins: [
2914
+ trackExternalsPlugin,
2915
+ nodeResolve({
2916
+ extensions: [".ts", ".js"]
2917
+ }),
2918
+ commonjs(),
2919
+ esbuild({
2920
+ target: "es2022",
2921
+ sourceMap: false
2922
+ })
2923
+ ],
2924
+ onwarn(warning, warn) {
2925
+ if (warning.code === "UNRESOLVED_IMPORT") return;
2926
+ if (warning.code === "CIRCULAR_DEPENDENCY") return;
2927
+ warn(warning);
2928
+ }
2929
+ };
2930
+ const bundle = await rollup(rollupOptions);
2931
+ try {
2932
+ const { output } = await bundle.generate({
2933
+ format: "esm",
2934
+ exports: "named"
2935
+ });
2936
+ const bundledCode = output[0].code;
2937
+ const header = `// Bundled from: ${path8.relative(rootDir, inputPath)}
2938
+ // Local dependencies have been inlined
2939
+
2940
+ `;
2941
+ fs2.writeFileSync(outputPath, header + bundledCode);
2942
+ await bundle.close();
2943
+ } catch (error) {
2944
+ await bundle.close();
2945
+ throw error;
2946
+ }
2947
+ return externalDeps;
2948
+ }
2949
+ /**
2950
+ * Get the directory name for a constituent type.
2951
+ */
2952
+ getTypeDir(type) {
2953
+ switch (type) {
2954
+ case "agent":
2955
+ return "agents";
2956
+ case "prompt":
2957
+ return "prompts";
2958
+ case "tool":
2959
+ return "tools";
2960
+ case "model":
2961
+ return "models";
2962
+ case "hook":
2963
+ return "hooks";
2964
+ }
2965
+ }
2966
+ /**
2967
+ * Deduplicate constituents by name (for packing, not for tree display).
2968
+ */
2969
+ deduplicateConstituents(analysis) {
2970
+ const dedupe = (items) => {
2971
+ const seen = /* @__PURE__ */ new Set();
2972
+ return items.filter((item) => {
2973
+ if (seen.has(item.name)) return false;
2974
+ seen.add(item.name);
2975
+ return true;
2976
+ });
2977
+ };
2978
+ return {
2979
+ agents: dedupe(analysis.constituents.agents),
2980
+ prompts: dedupe(analysis.constituents.prompts),
2981
+ tools: dedupe(analysis.constituents.tools),
2982
+ models: dedupe(analysis.constituents.models),
2983
+ hooks: dedupe(analysis.constituents.hooks)
2984
+ };
2985
+ }
2986
+ /**
2987
+ * Check if a name is a valid JavaScript/TypeScript identifier.
2988
+ */
2989
+ isValidIdentifier(name) {
2990
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
2991
+ }
2992
+ /**
2993
+ * Quote a property/export name if it contains non-identifier characters.
2994
+ * JavaScript identifiers must start with a letter, underscore, or dollar sign,
2995
+ * and contain only letters, digits, underscores, or dollar signs.
2996
+ */
2997
+ quoteName(name) {
2998
+ if (this.isValidIdentifier(name)) {
2999
+ return name;
3000
+ }
3001
+ return `'${name}'`;
3002
+ }
3003
+ /**
3004
+ * Generate the dist/index.js file with re-exports and lazy loaders.
3005
+ */
3006
+ generateReExportIndex(analysis, meta) {
3007
+ const constituents = this.deduplicateConstituents(analysis);
3008
+ const lines = [
3009
+ "// Packed agent: " + analysis.agent,
3010
+ "// Generated by @standardagents/builder",
3011
+ ""
3012
+ ];
3013
+ for (const agent of constituents.agents) {
3014
+ lines.push(`export { default as ${this.quoteName(agent.name)} } from './agents/${agent.name}.js';`);
3015
+ }
3016
+ for (const prompt of constituents.prompts) {
3017
+ lines.push(`export { default as ${this.quoteName(prompt.name)} } from './prompts/${prompt.name}.js';`);
3018
+ }
3019
+ for (const tool of constituents.tools) {
3020
+ lines.push(`export { default as ${this.quoteName(tool.name)} } from './tools/${tool.name}.js';`);
3021
+ }
3022
+ for (const model of constituents.models) {
3023
+ lines.push(`export { default as ${this.quoteName(model.name)} } from './models/${model.name}.js';`);
3024
+ }
3025
+ for (const hook of constituents.hooks) {
3026
+ lines.push(`export { default as ${this.quoteName(hook.name)} } from './hooks/${hook.name}.js';`);
3027
+ }
3028
+ lines.push("");
3029
+ lines.push("// Registry exports for lazy loading");
3030
+ lines.push("export const agents = {");
3031
+ for (const agent of constituents.agents) {
3032
+ lines.push(` ${this.quoteName(agent.name)}: async () => (await import('./agents/${agent.name}.js')).default,`);
3033
+ }
3034
+ lines.push("};");
3035
+ lines.push("");
3036
+ lines.push("export const prompts = {");
3037
+ for (const prompt of constituents.prompts) {
3038
+ lines.push(` ${this.quoteName(prompt.name)}: async () => (await import('./prompts/${prompt.name}.js')).default,`);
3039
+ }
3040
+ lines.push("};");
3041
+ lines.push("");
3042
+ lines.push("export const tools = {");
3043
+ for (const tool of constituents.tools) {
3044
+ lines.push(` ${this.quoteName(tool.name)}: async () => (await import('./tools/${tool.name}.js')).default,`);
3045
+ }
3046
+ lines.push("};");
3047
+ lines.push("");
3048
+ lines.push("export const models = {");
3049
+ for (const model of constituents.models) {
3050
+ lines.push(` ${this.quoteName(model.name)}: async () => (await import('./models/${model.name}.js')).default,`);
3051
+ }
3052
+ lines.push("};");
3053
+ lines.push("");
3054
+ lines.push("export const hooks = {");
3055
+ for (const hook of constituents.hooks) {
3056
+ lines.push(` ${this.quoteName(hook.name)}: async () => (await import('./hooks/${hook.name}.js')).default,`);
3057
+ }
3058
+ lines.push("};");
3059
+ lines.push("");
3060
+ lines.push("export const __meta = " + JSON.stringify(meta, null, 2) + ";");
3061
+ lines.push("");
3062
+ return lines.join("\n");
3063
+ }
3064
+ /**
3065
+ * Generate the dist/index.d.ts file with type definitions.
3066
+ */
3067
+ generateIndexDts(analysis) {
3068
+ const constituents = this.deduplicateConstituents(analysis);
3069
+ const lines = [
3070
+ "import type {",
3071
+ " AgentDefinition,",
3072
+ " PromptDefinition,",
3073
+ " ToolDefinition,",
3074
+ " ModelDefinition,",
3075
+ " PackedMeta,",
3076
+ "} from '@standardagents/spec';",
3077
+ "",
3078
+ "type DefinitionLoader<T> = () => Promise<T>;",
3079
+ ""
3080
+ ];
3081
+ for (const agent of constituents.agents) {
3082
+ if (this.isValidIdentifier(agent.name)) {
3083
+ lines.push(`export declare const ${agent.name}: AgentDefinition;`);
3084
+ }
3085
+ }
3086
+ for (const prompt of constituents.prompts) {
3087
+ if (this.isValidIdentifier(prompt.name)) {
3088
+ lines.push(`export declare const ${prompt.name}: PromptDefinition;`);
3089
+ }
3090
+ }
3091
+ for (const tool of constituents.tools) {
3092
+ if (this.isValidIdentifier(tool.name)) {
3093
+ lines.push(`export declare const ${tool.name}: ToolDefinition<unknown, any, any>;`);
3094
+ }
3095
+ }
3096
+ for (const model of constituents.models) {
3097
+ if (this.isValidIdentifier(model.name)) {
3098
+ lines.push(`export declare const ${model.name}: ModelDefinition;`);
3099
+ }
3100
+ }
3101
+ for (const hook of constituents.hooks) {
3102
+ if (this.isValidIdentifier(hook.name)) {
3103
+ lines.push(`export declare const ${hook.name}: unknown;`);
3104
+ }
3105
+ }
3106
+ lines.push("");
3107
+ lines.push("export declare const agents: {");
3108
+ for (const agent of constituents.agents) {
3109
+ lines.push(` readonly ${this.quoteName(agent.name)}: DefinitionLoader<AgentDefinition>;`);
3110
+ }
3111
+ lines.push("};");
3112
+ lines.push("");
3113
+ lines.push("export declare const prompts: {");
3114
+ for (const prompt of constituents.prompts) {
3115
+ lines.push(` readonly ${this.quoteName(prompt.name)}: DefinitionLoader<PromptDefinition>;`);
3116
+ }
3117
+ lines.push("};");
3118
+ lines.push("");
3119
+ lines.push("export declare const tools: {");
3120
+ for (const tool of constituents.tools) {
3121
+ lines.push(` readonly ${this.quoteName(tool.name)}: DefinitionLoader<ToolDefinition<unknown, any, any>>;`);
3122
+ }
3123
+ lines.push("};");
3124
+ lines.push("");
3125
+ lines.push("export declare const models: {");
3126
+ for (const model of constituents.models) {
3127
+ lines.push(` readonly ${this.quoteName(model.name)}: DefinitionLoader<ModelDefinition>;`);
3128
+ }
3129
+ lines.push("};");
3130
+ lines.push("");
3131
+ lines.push("export declare const hooks: {");
3132
+ for (const hook of constituents.hooks) {
3133
+ lines.push(` readonly ${this.quoteName(hook.name)}: DefinitionLoader<unknown>;`);
3134
+ }
3135
+ lines.push("};");
3136
+ lines.push("");
3137
+ lines.push("export declare const __meta: PackedMeta;");
3138
+ lines.push("");
3139
+ return lines.join("\n");
3140
+ }
3141
+ /**
3142
+ * Generate the package.json file with dependencies.
3143
+ */
3144
+ generatePackageJson(packageName, version, agentName, externalDeps, rootDir, license, licenseOwner) {
3145
+ const dependencies = {
3146
+ "@standardagents/spec": this.resolvePackageVersion("@standardagents/spec", rootDir)
3147
+ };
3148
+ for (const [name, ver] of externalDeps) {
3149
+ dependencies[name] = ver;
3150
+ }
3151
+ const pkgJson = {
3152
+ name: packageName,
3153
+ version,
3154
+ type: "module",
3155
+ main: "./dist/index.js",
3156
+ types: "./dist/index.d.ts",
3157
+ exports: {
3158
+ ".": {
3159
+ types: "./dist/index.d.ts",
3160
+ import: "./dist/index.js"
3161
+ }
3162
+ },
3163
+ keywords: ["standardagent"],
3164
+ standardagent: {
3165
+ entryAgents: [agentName]
3166
+ },
3167
+ files: license ? ["dist", "LICENSE", "README.md"] : ["dist", "README.md"],
3168
+ dependencies
3169
+ };
3170
+ if (license) {
3171
+ pkgJson.license = license;
3172
+ }
3173
+ if (licenseOwner) {
3174
+ pkgJson.author = licenseOwner;
3175
+ }
3176
+ return pkgJson;
3177
+ }
3178
+ /**
3179
+ * Generate LICENSE file content for common licenses.
3180
+ */
3181
+ generateLicenseFile(license, owner) {
3182
+ const year = (/* @__PURE__ */ new Date()).getFullYear();
3183
+ const copyrightHolder = owner || "[COPYRIGHT HOLDER]";
3184
+ switch (license) {
3185
+ case "MIT":
3186
+ return `MIT License
3187
+
3188
+ Copyright (c) ${year} ${copyrightHolder}
3189
+
3190
+ Permission is hereby granted, free of charge, to any person obtaining a copy
3191
+ of this software and associated documentation files (the "Software"), to deal
3192
+ in the Software without restriction, including without limitation the rights
3193
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3194
+ copies of the Software, and to permit persons to whom the Software is
3195
+ furnished to do so, subject to the following conditions:
3196
+
3197
+ The above copyright notice and this permission notice shall be included in all
3198
+ copies or substantial portions of the Software.
3199
+
3200
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3201
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3202
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3203
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3204
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3205
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3206
+ SOFTWARE.
3207
+ `;
3208
+ case "Apache-2.0":
3209
+ return ` Apache License
3210
+ Version 2.0, January 2004
3211
+ http://www.apache.org/licenses/
3212
+
3213
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
3214
+
3215
+ Copyright ${year} ${copyrightHolder}
3216
+
3217
+ Licensed under the Apache License, Version 2.0 (the "License");
3218
+ you may not use this file except in compliance with the License.
3219
+ You may obtain a copy of the License at
3220
+
3221
+ http://www.apache.org/licenses/LICENSE-2.0
3222
+
3223
+ Unless required by applicable law or agreed to in writing, software
3224
+ distributed under the License is distributed on an "AS IS" BASIS,
3225
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3226
+ See the License for the specific language governing permissions and
3227
+ limitations under the License.
3228
+ `;
3229
+ case "ISC":
3230
+ return `ISC License
3231
+
3232
+ Copyright (c) ${year} ${copyrightHolder}
3233
+
3234
+ Permission to use, copy, modify, and/or distribute this software for any
3235
+ purpose with or without fee is hereby granted, provided that the above
3236
+ copyright notice and this permission notice appear in all copies.
3237
+
3238
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
3239
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
3240
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
3241
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
3242
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
3243
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
3244
+ PERFORMANCE OF THIS SOFTWARE.
3245
+ `;
3246
+ case "GPL-3.0":
3247
+ return `GNU GENERAL PUBLIC LICENSE
3248
+ Version 3, 29 June 2007
3249
+
3250
+ Copyright (c) ${year} ${copyrightHolder}
3251
+
3252
+ This program is free software: you can redistribute it and/or modify
3253
+ it under the terms of the GNU General Public License as published by
3254
+ the Free Software Foundation, either version 3 of the License, or
3255
+ (at your option) any later version.
3256
+
3257
+ This program is distributed in the hope that it will be useful,
3258
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3259
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3260
+ GNU General Public License for more details.
3261
+
3262
+ You should have received a copy of the GNU General Public License
3263
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
3264
+ `;
3265
+ case "BSD-3-Clause":
3266
+ return `BSD 3-Clause License
3267
+
3268
+ Copyright (c) ${year} ${copyrightHolder}
3269
+
3270
+ Redistribution and use in source and binary forms, with or without
3271
+ modification, are permitted provided that the following conditions are met:
3272
+
3273
+ 1. Redistributions of source code must retain the above copyright notice, this
3274
+ list of conditions and the following disclaimer.
3275
+
3276
+ 2. Redistributions in binary form must reproduce the above copyright notice,
3277
+ this list of conditions and the following disclaimer in the documentation
3278
+ and/or other materials provided with the distribution.
3279
+
3280
+ 3. Neither the name of the copyright holder nor the names of its
3281
+ contributors may be used to endorse or promote products derived from
3282
+ this software without specific prior written permission.
3283
+
3284
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
3285
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3286
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
3287
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
3288
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3289
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
3290
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
3291
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3292
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3293
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3294
+ `;
3295
+ case "Unlicensed":
3296
+ case "UNLICENSED":
3297
+ return `Copyright (c) ${year} ${copyrightHolder}
3298
+
3299
+ All Rights Reserved.
3300
+
3301
+ This software and associated documentation files (the "Software") are
3302
+ proprietary and confidential. Unauthorized copying, modification, distribution,
3303
+ or use of the Software, via any medium, is strictly prohibited.
3304
+ `;
3305
+ default:
3306
+ return `${license}
3307
+
3308
+ Copyright (c) ${year} ${copyrightHolder}
3309
+ `;
3310
+ }
3311
+ }
3312
+ /**
3313
+ * Get information about a packed package.
3314
+ *
3315
+ * Reads the package.json and README.md from a packed package directory.
3316
+ * Used by the packed-info API endpoint to show package details before publishing.
3317
+ *
3318
+ * @param packageId - The package directory name (e.g., 'standardagent-my-agent')
3319
+ * @param rootDir - Root directory of the project
3320
+ * @returns Package info or null if not found
3321
+ */
3322
+ getPackedInfo(packageId, rootDir) {
3323
+ const packedDir = path8.join(rootDir, "agents", "packed");
3324
+ let packageDir = path8.join(packedDir, packageId);
3325
+ if (!fs2.existsSync(packageDir)) {
3326
+ if (!fs2.existsSync(packedDir)) {
3327
+ return null;
3328
+ }
3329
+ const dirs = fs2.readdirSync(packedDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
3330
+ const matchingDir = dirs.find(
3331
+ (d) => d === packageId || d === `standardagent-${packageId}` || d === `standardagent-${packageId.replace(/_/g, "-")}`
3332
+ );
3333
+ if (!matchingDir) {
3334
+ return null;
3335
+ }
3336
+ packageDir = path8.join(packedDir, matchingDir);
3337
+ }
3338
+ const pkgJsonPath = path8.join(packageDir, "package.json");
3339
+ if (!fs2.existsSync(pkgJsonPath)) {
3340
+ return null;
3341
+ }
3342
+ const pkgJson = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
3343
+ let readme;
3344
+ const readmePath = path8.join(packageDir, "README.md");
3345
+ if (fs2.existsSync(readmePath)) {
3346
+ readme = fs2.readFileSync(readmePath, "utf-8");
3347
+ }
3348
+ return {
3349
+ packageName: pkgJson.name,
3350
+ version: pkgJson.version,
3351
+ license: pkgJson.license,
3352
+ author: pkgJson.author,
3353
+ entryAgents: pkgJson.standardagent?.entryAgents || [],
3354
+ readme
3355
+ };
3356
+ }
3357
+ /**
3358
+ * List all packed packages in the workspace.
3359
+ *
3360
+ * @param rootDir - Root directory of the project
3361
+ * @returns Array of package directory names
3362
+ */
3363
+ listPackedPackages(rootDir) {
3364
+ const packedDir = path8.join(rootDir, "agents", "packed");
3365
+ if (!fs2.existsSync(packedDir)) {
3366
+ return [];
3367
+ }
3368
+ return fs2.readdirSync(packedDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
3369
+ }
3370
+ /**
3371
+ * Generate README.md content for a packed agent.
3372
+ *
3373
+ * @param packageName - The npm package name
3374
+ * @param agentName - The agent name
3375
+ * @param version - The package version
3376
+ * @param license - The license type
3377
+ * @param description - Optional description from the agent definition
3378
+ * @returns README markdown content
3379
+ */
3380
+ generateReadme(packageName, agentName, version, license, description) {
3381
+ const desc = description || "A Standard Agent package.";
3382
+ return `# ${packageName}
3383
+
3384
+ ${desc}
3385
+
3386
+ ## Installation
3387
+
3388
+ \`\`\`bash
3389
+ npm install ${packageName}
3390
+ \`\`\`
3391
+
3392
+ ## Usage
3393
+
3394
+ This is a [Standard Agent](https://standardagentbuilder.com) package. To use it:
3395
+
3396
+ 1. Install in your Standard Agents project
3397
+ 2. The agent will be automatically discovered and available
3398
+
3399
+ \`\`\`typescript
3400
+ // Create a thread with this agent
3401
+ const thread = await client.createThread({
3402
+ agent_id: '${packageName}/${agentName}'
3403
+ });
3404
+ \`\`\`
3405
+
3406
+ ## License
3407
+
3408
+ ${license || "See LICENSE file"}
3409
+ `;
3410
+ }
3411
+ /**
3412
+ * Find a file by name in a directory.
3413
+ */
3414
+ findFile(dir, name) {
3415
+ if (!fs2.existsSync(dir)) {
3416
+ return null;
3417
+ }
3418
+ const exactPath = path8.join(dir, `${name}.ts`);
3419
+ if (fs2.existsSync(exactPath)) {
3420
+ return exactPath;
3421
+ }
3422
+ const files = fs2.readdirSync(dir).filter((f) => f.endsWith(".ts"));
3423
+ for (const file of files) {
3424
+ const filePath = path8.join(dir, file);
3425
+ const source = fs2.readFileSync(filePath, "utf-8");
3426
+ const extractedName = extractDefinitionName(source);
3427
+ if (extractedName === name) {
3428
+ return filePath;
3429
+ }
3430
+ }
3431
+ return null;
3432
+ }
3433
+ /**
3434
+ * List all agents in the workspace.
3435
+ */
3436
+ listAgents(rootDir) {
3437
+ const agentsDir = path8.join(rootDir, "agents", "agents");
3438
+ if (!fs2.existsSync(agentsDir)) {
3439
+ return [];
3440
+ }
3441
+ const files = fs2.readdirSync(agentsDir).filter((f) => f.endsWith(".ts"));
3442
+ const agents = [];
3443
+ for (const file of files) {
3444
+ const filePath = path8.join(agentsDir, file);
3445
+ const source = fs2.readFileSync(filePath, "utf-8");
3446
+ const name = extractDefinitionName(source);
3447
+ if (name) {
3448
+ agents.push(name);
3449
+ } else {
3450
+ agents.push(file.replace(".ts", ""));
3451
+ }
3452
+ }
3453
+ return agents;
3454
+ }
3455
+ };
3456
+ var PackageDiscoveryService = class {
3457
+ config;
3458
+ constructor(config) {
3459
+ this.config = {
3460
+ rootDir: config.rootDir,
3461
+ packedDir: config.packedDir ?? path8.join(config.rootDir, "agents", "packed"),
3462
+ scanNpm: config.scanNpm ?? true,
3463
+ scanLocal: config.scanLocal ?? true
3464
+ };
3465
+ }
3466
+ /**
3467
+ * Discover all packed agent packages.
3468
+ *
3469
+ * @returns Array of discovered packages
3470
+ */
3471
+ async discoverPackages() {
3472
+ const packages = [];
3473
+ if (this.config.scanNpm) {
3474
+ const npmPackages = await this.discoverNpmPackages();
3475
+ packages.push(...npmPackages);
3476
+ }
3477
+ if (this.config.scanLocal) {
3478
+ const localPackages = await this.discoverLocalPackages();
3479
+ packages.push(...localPackages);
3480
+ }
3481
+ return packages;
3482
+ }
3483
+ /**
3484
+ * Discover npm packages that are Standard Agents packages.
3485
+ *
3486
+ * Looks for packages with:
3487
+ * - "standardagent" keyword in package.json
3488
+ * - "standardagent-*" prefix in name
3489
+ * - "@standardagents/*" scope
3490
+ * - "standardagent" field in package.json
3491
+ */
3492
+ async discoverNpmPackages() {
3493
+ const packages = [];
3494
+ const nodeModulesDir = path8.join(this.config.rootDir, "node_modules");
3495
+ if (!fs2.existsSync(nodeModulesDir)) {
3496
+ return packages;
3497
+ }
3498
+ const rootEntries = await this.scanDirectory(nodeModulesDir);
3499
+ for (const entry of rootEntries) {
3500
+ if (entry.startsWith(".")) continue;
3501
+ if (entry.startsWith("@")) {
3502
+ const scopeDir = path8.join(nodeModulesDir, entry);
3503
+ const scopeEntries = await this.scanDirectory(scopeDir);
3504
+ for (const scopedPkg of scopeEntries) {
3505
+ if (scopedPkg.startsWith(".")) continue;
3506
+ const pkgDir = path8.join(scopeDir, scopedPkg);
3507
+ const fullName = `${entry}/${scopedPkg}`;
3508
+ const pkg = await this.checkNpmPackage(pkgDir, fullName);
3509
+ if (pkg) {
3510
+ packages.push(pkg);
3511
+ }
3512
+ }
3513
+ } else {
3514
+ const pkgDir = path8.join(nodeModulesDir, entry);
3515
+ const pkg = await this.checkNpmPackage(pkgDir, entry);
3516
+ if (pkg) {
3517
+ packages.push(pkg);
3518
+ }
3519
+ }
3520
+ }
3521
+ return packages;
3522
+ }
3523
+ /**
3524
+ * Check if an npm package is a Standard Agent package.
3525
+ *
3526
+ * Detection is based on the `standardagent` field in package.json,
3527
+ * which must contain `entryAgents` array.
3528
+ */
3529
+ async checkNpmPackage(pkgDir, pkgName) {
3530
+ const pkgJsonPath = path8.join(pkgDir, "package.json");
3531
+ if (!fs2.existsSync(pkgJsonPath)) {
3532
+ return null;
3533
+ }
3534
+ try {
3535
+ const pkgJson = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
3536
+ const isStandardAgent = (
3537
+ // Has "standardagent" keyword
3538
+ pkgJson.keywords?.includes("standardagent") || // Has "standardagent-*" prefix
3539
+ pkgName.startsWith("standardagent-") || // Has "@standardagents/*" scope
3540
+ pkgName.startsWith("@standardagents/") || // Has "standardagent" field
3541
+ pkgJson.standardagent !== void 0
3542
+ );
3543
+ if (!isStandardAgent) {
3544
+ return null;
3545
+ }
3546
+ if (!pkgJson.standardagent?.entryAgents) {
3547
+ return null;
3548
+ }
3549
+ return {
3550
+ packageId: pkgJson.name || pkgName,
3551
+ version: pkgJson.version || "0.0.0",
3552
+ source: "npm",
3553
+ path: pkgDir,
3554
+ entryAgents: pkgJson.standardagent.entryAgents,
3555
+ description: pkgJson.description
3556
+ };
3557
+ } catch (error) {
3558
+ console.warn(`[PackageDiscovery] Error reading npm package ${pkgName}:`, error);
3559
+ return null;
3560
+ }
3561
+ }
3562
+ /**
3563
+ * Discover local packed agents from agents/packed/ directory.
3564
+ * Handles both regular packages and scoped packages (@scope/name).
3565
+ */
3566
+ async discoverLocalPackages() {
3567
+ const packages = [];
3568
+ if (!fs2.existsSync(this.config.packedDir)) {
3569
+ return packages;
3570
+ }
3571
+ const entries = await this.scanDirectory(this.config.packedDir);
3572
+ for (const entry of entries) {
3573
+ if (entry.startsWith(".")) continue;
3574
+ const pkgDir = path8.join(this.config.packedDir, entry);
3575
+ const stat = fs2.statSync(pkgDir);
3576
+ if (!stat.isDirectory()) continue;
3577
+ if (entry.startsWith("@")) {
3578
+ const scopeEntries = await this.scanDirectory(pkgDir);
3579
+ for (const scopedPkg of scopeEntries) {
3580
+ if (scopedPkg.startsWith(".")) continue;
3581
+ const scopedPkgDir = path8.join(pkgDir, scopedPkg);
3582
+ const scopedStat = fs2.statSync(scopedPkgDir);
3583
+ if (!scopedStat.isDirectory()) continue;
3584
+ const fullName = `${entry}/${scopedPkg}`;
3585
+ const pkg = await this.checkLocalPackage(scopedPkgDir, fullName);
3586
+ if (pkg) {
3587
+ packages.push(pkg);
3588
+ }
3589
+ }
3590
+ } else {
3591
+ const pkg = await this.checkLocalPackage(pkgDir, entry);
3592
+ if (pkg) {
3593
+ packages.push(pkg);
3594
+ }
3595
+ }
3596
+ }
3597
+ return packages;
3598
+ }
3599
+ /**
3600
+ * Check if a local directory is a packed agent package.
3601
+ *
3602
+ * Detection is based on the `standardagent` field in package.json.
3603
+ */
3604
+ async checkLocalPackage(pkgDir, dirName) {
3605
+ const pkgJsonPath = path8.join(pkgDir, "package.json");
3606
+ if (!fs2.existsSync(pkgJsonPath)) {
3607
+ return null;
3608
+ }
3609
+ try {
3610
+ const pkgJson = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
3611
+ if (!pkgJson.standardagent?.entryAgents) {
3612
+ return null;
3613
+ }
3614
+ return {
3615
+ packageId: pkgJson.name || dirName,
3616
+ version: pkgJson.version || "0.0.0",
3617
+ source: "local",
3618
+ path: pkgDir,
3619
+ entryAgents: pkgJson.standardagent.entryAgents,
3620
+ description: pkgJson.description
3621
+ };
3622
+ } catch (error) {
3623
+ console.warn(`[PackageDiscovery] Error reading local package ${dirName}:`, error);
3624
+ return null;
3625
+ }
3626
+ }
3627
+ /**
3628
+ * Scan a directory and return entry names.
3629
+ */
3630
+ async scanDirectory(dir) {
3631
+ if (!fs2.existsSync(dir)) {
3632
+ return [];
3633
+ }
3634
+ const entries = fs2.readdirSync(dir);
3635
+ return entries;
3636
+ }
3637
+ /**
3638
+ * Validate a standardagent field structure in package.json.
3639
+ */
3640
+ static validateStandardAgentField(field) {
3641
+ if (typeof field !== "object" || field === null) {
3642
+ return false;
3643
+ }
3644
+ const f = field;
3645
+ return Array.isArray(f.entryAgents) && f.entryAgents.length > 0 && f.entryAgents.every((e) => typeof e === "string");
3646
+ }
3647
+ /**
3648
+ * Get a package by ID.
3649
+ */
3650
+ async getPackage(packageId) {
3651
+ const packages = await this.discoverPackages();
3652
+ return packages.find((p) => p.packageId === packageId) ?? null;
3653
+ }
3654
+ /**
3655
+ * Create a package signature from a discovered package.
3656
+ *
3657
+ * Note: packedAt is set to current time since it's no longer
3658
+ * stored in a manifest file.
3659
+ */
3660
+ static createSignature(pkg) {
3661
+ return {
3662
+ packageId: pkg.packageId,
3663
+ version: pkg.version,
3664
+ source: pkg.source,
3665
+ packedAt: Date.now()
3666
+ };
3667
+ }
3668
+ };
3669
+
3670
+ // src/packing/unpacking-service.ts
3671
+ var TYPE_TO_DIR = {
3672
+ agent: "agents",
3673
+ prompt: "prompts",
3674
+ tool: "tools",
3675
+ model: "models",
3676
+ hook: "hooks"
3677
+ };
3678
+ var UnpackingService = class {
3679
+ /**
3680
+ * Analyze a package to discover what can be unpacked.
3681
+ *
3682
+ * @param packageId - Package ID to analyze
3683
+ * @param rootDir - Root directory of the agents workspace
3684
+ * @returns Analysis result with items that can be unpacked
3685
+ */
3686
+ async analyzeUnpack(packageId, rootDir) {
3687
+ const analysis = {
3688
+ packageId,
3689
+ version: "",
3690
+ source: "local",
3691
+ canUnpack: false,
3692
+ items: [],
3693
+ warnings: [],
3694
+ errors: []
3695
+ };
3696
+ try {
3697
+ const pkg = await this.resolvePackage(packageId, rootDir);
3698
+ if (!pkg) {
3699
+ analysis.errors.push(`Package not found: ${packageId}`);
3700
+ return analysis;
3701
+ }
3702
+ analysis.version = pkg.version;
3703
+ analysis.source = pkg.source;
3704
+ const indexPath = path8.join(pkg.path, "dist", "index.js");
3705
+ if (!fs2.existsSync(indexPath)) {
3706
+ analysis.errors.push(`Package index not found: ${indexPath}`);
3707
+ return analysis;
3708
+ }
3709
+ const indexContent = fs2.readFileSync(indexPath, "utf-8");
3710
+ const registryItems = this.parseIndexExports(indexContent);
3711
+ const availableItems = /* @__PURE__ */ new Map();
3712
+ for (const item of registryItems) {
3713
+ availableItems.set(`${item.type}:${item.name}`, item);
3714
+ }
3715
+ const agentsDir = path8.join(rootDir, "agents");
3716
+ const itemsWithRelations = [];
3717
+ for (const item of registryItems) {
3718
+ const targetPath = path8.join(agentsDir, TYPE_TO_DIR[item.type], `${item.name}.ts`);
3719
+ const sourcePath = path8.join(pkg.path, "dist", TYPE_TO_DIR[item.type], `${item.name}.js`);
3720
+ const existsGlobally = this.fileExists(targetPath);
3721
+ const sourceExists = fs2.existsSync(sourcePath);
3722
+ if (!sourceExists) {
3723
+ analysis.warnings.push(`Source file not found for ${item.type} "${item.name}": ${sourcePath}`);
3724
+ continue;
3725
+ }
3726
+ const bundledCode = fs2.readFileSync(sourcePath, "utf-8");
3727
+ const children = this.discoverRelationships(item.type, item.name, bundledCode, availableItems);
3728
+ itemsWithRelations.push({
3729
+ name: item.name,
3730
+ type: item.type,
3731
+ targetPath,
3732
+ existsGlobally,
3733
+ action: existsGlobally ? "skip" : "generate"
3734
+ });
3735
+ for (const child of children) {
3736
+ const childTargetPath = path8.join(agentsDir, TYPE_TO_DIR[child.type], `${child.name}.ts`);
3737
+ const childSourcePath = path8.join(pkg.path, "dist", TYPE_TO_DIR[child.type], `${child.name}.js`);
3738
+ const childExists = this.fileExists(childTargetPath);
3739
+ const childSourceExists = fs2.existsSync(childSourcePath);
3740
+ if (!childSourceExists) {
3741
+ continue;
3742
+ }
3743
+ itemsWithRelations.push({
3744
+ name: child.name,
3745
+ type: child.type,
3746
+ targetPath: childTargetPath,
3747
+ existsGlobally: childExists,
3748
+ action: childExists ? "skip" : "generate",
3749
+ parentKey: `${item.type}:${item.name}`
3750
+ });
3751
+ }
3752
+ }
3753
+ analysis.items = itemsWithRelations;
3754
+ const itemsToGenerate = analysis.items.filter((i) => i.action === "generate");
3755
+ if (itemsToGenerate.length === 0 && analysis.items.length > 0) {
3756
+ analysis.warnings.push("All items already exist in the global namespace. Nothing to unpack.");
3757
+ }
3758
+ analysis.canUnpack = itemsToGenerate.length > 0;
3759
+ } catch (error) {
3760
+ analysis.errors.push(error instanceof Error ? error.message : String(error));
3761
+ }
3762
+ return analysis;
3763
+ }
3764
+ /**
3765
+ * Discover relationships from a bundled file.
3766
+ * Returns child items that this item references.
3767
+ */
3768
+ discoverRelationships(type, name, bundledCode, availableItems) {
3769
+ const children = [];
3770
+ if (type === "agent") {
3771
+ const prompts = extractAgentPrompts(bundledCode);
3772
+ if (prompts.sideA && availableItems.has(`prompt:${prompts.sideA}`)) {
3773
+ children.push({ type: "prompt", name: prompts.sideA });
3774
+ }
3775
+ if (prompts.sideB && availableItems.has(`prompt:${prompts.sideB}`)) {
3776
+ children.push({ type: "prompt", name: prompts.sideB });
3777
+ }
3778
+ } else if (type === "prompt") {
3779
+ const toolsResult = extractPromptTools(bundledCode);
3780
+ for (const toolName of toolsResult.tools) {
3781
+ if (availableItems.has(`tool:${toolName}`)) {
3782
+ children.push({ type: "tool", name: toolName });
3783
+ } else if (availableItems.has(`agent:${toolName}`)) {
3784
+ children.push({ type: "agent", name: toolName });
3785
+ }
3786
+ }
3787
+ const model = extractPromptModel(bundledCode);
3788
+ if (model && availableItems.has(`model:${model}`)) {
3789
+ children.push({ type: "model", name: model });
3790
+ }
3791
+ } else if (type === "tool") {
3792
+ const usesResult = extractToolUses(bundledCode);
3793
+ for (const useName of usesResult.uses) {
3794
+ const colonIndex = useName.indexOf(":");
3795
+ if (colonIndex > 0) {
3796
+ const agentName = useName.substring(0, colonIndex);
3797
+ if (availableItems.has(`agent:${agentName}`)) {
3798
+ children.push({ type: "agent", name: agentName });
3799
+ }
3800
+ } else {
3801
+ if (availableItems.has(`tool:${useName}`)) {
3802
+ children.push({ type: "tool", name: useName });
3803
+ } else if (availableItems.has(`prompt:${useName}`)) {
3804
+ children.push({ type: "prompt", name: useName });
3805
+ } else if (availableItems.has(`agent:${useName}`)) {
3806
+ children.push({ type: "agent", name: useName });
3807
+ }
3808
+ }
3809
+ }
3810
+ }
3811
+ return children;
3812
+ }
3813
+ /**
3814
+ * Unpack a packed agent package by transforming bundled JS back to TypeScript.
3815
+ *
3816
+ * @param options - Unpacking options
3817
+ * @returns Result of the unpacking operation
3818
+ */
3819
+ async unpack(options) {
3820
+ const { packageId, rootDir, deletePackage = false, itemSelections } = options;
3821
+ const result = {
3822
+ success: false,
3823
+ packageId,
3824
+ filesGenerated: [],
3825
+ filesSkipped: [],
3826
+ packageDeleted: false,
3827
+ warnings: []
3828
+ };
3829
+ try {
3830
+ const analysis = await this.analyzeUnpack(packageId, rootDir);
3831
+ if (analysis.errors.length > 0) {
3832
+ result.error = analysis.errors.join("\n");
3833
+ return result;
3834
+ }
3835
+ result.warnings.push(...analysis.warnings);
3836
+ const pkg = await this.resolvePackage(packageId, rootDir);
3837
+ if (!pkg) {
3838
+ result.error = `Package not found: ${packageId}`;
3839
+ return result;
3840
+ }
3841
+ const itemsToProcess = this.applySelections(analysis.items, itemSelections);
3842
+ const pkgJsonPath = path8.join(pkg.path, "package.json");
3843
+ const pkgJson = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
3844
+ let author = "";
3845
+ if (typeof pkgJson.author === "string") {
3846
+ author = pkgJson.author;
3847
+ } else if (pkgJson.author?.name) {
3848
+ author = pkgJson.author.name;
3849
+ }
3850
+ const agentMetadata = {
3851
+ packageName: pkgJson.name || void 0,
3852
+ version: pkgJson.version || void 0,
3853
+ author: author || void 0,
3854
+ license: pkgJson.license || void 0
3855
+ };
3856
+ const processedItems = /* @__PURE__ */ new Set();
3857
+ for (const item of itemsToProcess) {
3858
+ const itemKey = `${item.type}:${item.name}`;
3859
+ if (processedItems.has(itemKey)) {
3860
+ continue;
3861
+ }
3862
+ processedItems.add(itemKey);
3863
+ if (item.action === "skip") {
3864
+ result.filesSkipped.push(item.targetPath);
3865
+ continue;
3866
+ }
3867
+ try {
3868
+ const sourcePath = path8.join(pkg.path, "dist", TYPE_TO_DIR[item.type], `${item.name}.js`);
3869
+ const bundledCode = fs2.readFileSync(sourcePath, "utf-8");
3870
+ let tsCode = transformBundledJs(bundledCode);
3871
+ if (item.type === "agent") {
3872
+ tsCode = injectAgentMetadata(tsCode, agentMetadata);
3873
+ }
3874
+ const dir = path8.dirname(item.targetPath);
3875
+ if (!fs2.existsSync(dir)) {
3876
+ fs2.mkdirSync(dir, { recursive: true });
3877
+ }
3878
+ fs2.writeFileSync(item.targetPath, tsCode);
3879
+ result.filesGenerated.push(item.targetPath);
3880
+ } catch (error) {
3881
+ result.warnings.push(
3882
+ `Failed to generate ${item.type} "${item.name}": ${error instanceof Error ? error.message : String(error)}`
3883
+ );
3884
+ }
3885
+ }
3886
+ if (deletePackage && pkg.source === "local") {
3887
+ try {
3888
+ this.deleteDirectory(pkg.path);
3889
+ result.packageDeleted = true;
3890
+ } catch (error) {
3891
+ result.warnings.push(
3892
+ `Failed to delete package: ${error instanceof Error ? error.message : String(error)}`
3893
+ );
3894
+ }
3895
+ } else if (deletePackage && pkg.source === "npm") {
3896
+ result.warnings.push("Cannot delete npm packages. Use npm uninstall instead.");
3897
+ }
3898
+ result.success = true;
3899
+ } catch (error) {
3900
+ result.error = error instanceof Error ? error.message : String(error);
3901
+ }
3902
+ return result;
3903
+ }
3904
+ /**
3905
+ * Parse the index.js file to extract registry items.
3906
+ *
3907
+ * Looks for patterns like:
3908
+ * ```javascript
3909
+ * export const agents = {
3910
+ * support_agent: async () => (await import('./agents/support_agent.js')).default,
3911
+ * };
3912
+ * ```
3913
+ */
3914
+ parseIndexExports(indexContent) {
3915
+ const sourceFile = ts.createSourceFile(
3916
+ "index.js",
3917
+ indexContent,
3918
+ ts.ScriptTarget.ESNext,
3919
+ true
3920
+ );
3921
+ const items = [];
3922
+ const registryTypes = ["agent", "prompt", "tool", "model", "hook"];
3923
+ function visit(node) {
3924
+ if (ts.isVariableStatement(node) && node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
3925
+ for (const decl of node.declarationList.declarations) {
3926
+ if (!ts.isIdentifier(decl.name)) continue;
3927
+ const varName = decl.name.text;
3928
+ let itemType = null;
3929
+ for (const type of registryTypes) {
3930
+ if (varName === TYPE_TO_DIR[type]) {
3931
+ itemType = type;
3932
+ break;
3933
+ }
3934
+ }
3935
+ if (itemType && decl.initializer && ts.isObjectLiteralExpression(decl.initializer)) {
3936
+ for (const prop of decl.initializer.properties) {
3937
+ if (ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop)) {
3938
+ let name;
3939
+ if (ts.isPropertyAssignment(prop)) {
3940
+ if (ts.isIdentifier(prop.name)) {
3941
+ name = prop.name.text;
3942
+ } else if (ts.isStringLiteral(prop.name)) {
3943
+ name = prop.name.text;
3944
+ }
3945
+ } else if (ts.isShorthandPropertyAssignment(prop)) {
3946
+ name = prop.name.text;
3947
+ }
3948
+ if (name) {
3949
+ items.push({ name, type: itemType });
3950
+ }
3951
+ }
3952
+ }
3953
+ }
3954
+ }
3955
+ }
3956
+ ts.forEachChild(node, visit);
3957
+ }
3958
+ visit(sourceFile);
3959
+ return items;
3960
+ }
3961
+ /**
3962
+ * Resolve a package identifier to a discovered package.
3963
+ */
3964
+ async resolvePackage(pkgIdentifier, rootDir) {
3965
+ if (pkgIdentifier.startsWith("/") || pkgIdentifier.startsWith("./")) {
3966
+ const absolutePath = path8.isAbsolute(pkgIdentifier) ? pkgIdentifier : path8.join(rootDir, pkgIdentifier);
3967
+ const pkgJsonPath2 = path8.join(absolutePath, "package.json");
3968
+ if (fs2.existsSync(pkgJsonPath2)) {
3969
+ try {
3970
+ const pkgJson = JSON.parse(fs2.readFileSync(pkgJsonPath2, "utf-8"));
3971
+ if (pkgJson.standardagent?.entryAgents) {
3972
+ return {
3973
+ packageId: pkgJson.name || path8.basename(absolutePath),
3974
+ version: pkgJson.version || "0.0.0",
3975
+ source: "local",
3976
+ path: absolutePath,
3977
+ entryAgents: pkgJson.standardagent.entryAgents
3978
+ };
3979
+ }
3980
+ } catch {
3981
+ }
3982
+ }
3983
+ return null;
3984
+ }
3985
+ const packedDir = path8.join(rootDir, "agents", "packed", pkgIdentifier);
3986
+ const pkgJsonPath = path8.join(packedDir, "package.json");
3987
+ if (fs2.existsSync(pkgJsonPath)) {
3988
+ try {
3989
+ const pkgJson = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
3990
+ if (pkgJson.standardagent?.entryAgents) {
3991
+ return {
3992
+ packageId: pkgJson.name || pkgIdentifier,
3993
+ version: pkgJson.version || "0.0.0",
3994
+ source: "local",
3995
+ path: packedDir,
3996
+ entryAgents: pkgJson.standardagent.entryAgents
3997
+ };
3998
+ }
3999
+ } catch {
4000
+ }
4001
+ }
4002
+ const discovery = new PackageDiscoveryService({ rootDir });
4003
+ const packages = await discovery.discoverPackages();
4004
+ const pkg = packages.find(
4005
+ (p) => p.packageId === pkgIdentifier || p.path.endsWith(`node_modules/${pkgIdentifier}`)
4006
+ );
4007
+ return pkg || null;
4008
+ }
4009
+ /**
4010
+ * Apply user selections to items.
4011
+ */
4012
+ applySelections(items, selections) {
4013
+ if (!selections || selections.length === 0) {
4014
+ return items;
4015
+ }
4016
+ return items.map((item) => {
4017
+ const selection = selections.find(
4018
+ (s) => s.name === item.name && s.type === item.type
4019
+ );
4020
+ if (selection) {
4021
+ return { ...item, action: selection.action };
4022
+ }
4023
+ return item;
4024
+ });
4025
+ }
4026
+ /**
4027
+ * Check if a file exists.
4028
+ */
4029
+ fileExists(filePath) {
4030
+ if (fs2.existsSync(filePath)) {
4031
+ return true;
4032
+ }
4033
+ const withoutExt = filePath.replace(/\.(ts|js)$/, "");
4034
+ return fs2.existsSync(`${withoutExt}.ts`) || fs2.existsSync(`${withoutExt}.js`);
4035
+ }
4036
+ /**
4037
+ * Recursively delete a directory.
4038
+ */
4039
+ deleteDirectory(dirPath) {
4040
+ if (fs2.existsSync(dirPath)) {
4041
+ fs2.rmSync(dirPath, { recursive: true, force: true });
4042
+ }
4043
+ }
4044
+ /**
4045
+ * List available packed packages.
4046
+ */
4047
+ async listPackages(rootDir) {
4048
+ const discovery = new PackageDiscoveryService({ rootDir });
4049
+ const packages = await discovery.discoverPackages();
4050
+ return packages.map((pkg) => ({
4051
+ packageId: pkg.packageId,
4052
+ version: pkg.version,
4053
+ source: pkg.source,
4054
+ entryAgents: pkg.entryAgents,
4055
+ path: pkg.path
4056
+ }));
4057
+ }
4058
+ };
4059
+ var execAsync = promisify(exec);
4060
+ var NpmPublishService = class {
4061
+ /**
4062
+ * Validate an npm auth token format.
4063
+ *
4064
+ * @param token - Token to validate
4065
+ * @returns true if token appears valid
4066
+ */
4067
+ validateToken(token) {
4068
+ if (!token || token.trim().length === 0) {
4069
+ return false;
4070
+ }
4071
+ return token.trim().length >= 10;
4072
+ }
4073
+ /**
4074
+ * Validate a registry URL.
4075
+ *
4076
+ * @param registry - Registry URL to validate
4077
+ * @returns true if URL is valid https URL
4078
+ */
4079
+ validateRegistry(registry) {
4080
+ try {
4081
+ const url = new URL(registry);
4082
+ return url.protocol === "https:";
4083
+ } catch {
4084
+ return false;
4085
+ }
4086
+ }
4087
+ /**
4088
+ * Publish a package to npm.
4089
+ *
4090
+ * Creates a temporary .npmrc with the token, runs npm publish,
4091
+ * and deletes the .npmrc immediately after.
4092
+ *
4093
+ * @param options - Publish options
4094
+ * @returns Publish result
4095
+ */
4096
+ async publish(options) {
4097
+ const {
4098
+ packageDir,
4099
+ token,
4100
+ registry = "https://registry.npmjs.org",
4101
+ dryRun = false
4102
+ } = options;
4103
+ if (!this.validateToken(token)) {
4104
+ return {
4105
+ success: false,
4106
+ output: "",
4107
+ error: "Invalid npm token format"
4108
+ };
4109
+ }
4110
+ if (!this.validateRegistry(registry)) {
4111
+ return {
4112
+ success: false,
4113
+ output: "",
4114
+ error: "Registry URL must use HTTPS"
4115
+ };
4116
+ }
4117
+ const pkgJsonPath = path8.join(packageDir, "package.json");
4118
+ if (!fs2.existsSync(pkgJsonPath)) {
4119
+ return {
4120
+ success: false,
4121
+ output: "",
4122
+ error: "package.json not found in package directory"
4123
+ };
4124
+ }
4125
+ let packageName;
4126
+ let version;
4127
+ try {
4128
+ const pkgJson = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
4129
+ packageName = pkgJson.name;
4130
+ version = pkgJson.version;
4131
+ } catch (e) {
4132
+ return {
4133
+ success: false,
4134
+ output: "",
4135
+ error: `Failed to read package.json: ${e instanceof Error ? e.message : String(e)}`
4136
+ };
4137
+ }
4138
+ const npmrcPath = path8.join(packageDir, ".npmrc");
4139
+ const registryHost = new URL(registry).host;
4140
+ const npmrcContent = `//${registryHost}/:_authToken=${token}
4141
+ registry=${registry}
4142
+ `;
4143
+ try {
4144
+ fs2.writeFileSync(npmrcPath, npmrcContent, { mode: 384 });
4145
+ const args = ["publish"];
4146
+ if (dryRun) {
4147
+ args.push("--dry-run");
4148
+ }
4149
+ args.push("--access", "public");
4150
+ const command = `npm ${args.join(" ")}`;
4151
+ const { stdout, stderr } = await execAsync(command, {
4152
+ cwd: packageDir,
4153
+ env: {
4154
+ ...process.env,
4155
+ // Ensure npm uses our .npmrc
4156
+ npm_config_userconfig: npmrcPath
4157
+ }
4158
+ });
4159
+ const output = [stdout, stderr].filter(Boolean).join("\n");
4160
+ return {
4161
+ success: true,
4162
+ output,
4163
+ packageName,
4164
+ version
4165
+ };
4166
+ } catch (e) {
4167
+ const error = e;
4168
+ const output = [error.stdout, error.stderr].filter(Boolean).join("\n");
4169
+ return {
4170
+ success: false,
4171
+ output,
4172
+ error: error.message || "npm publish failed"
4173
+ };
4174
+ } finally {
4175
+ try {
4176
+ if (fs2.existsSync(npmrcPath)) {
4177
+ fs2.unlinkSync(npmrcPath);
4178
+ }
4179
+ } catch {
4180
+ }
4181
+ }
4182
+ }
4183
+ /**
4184
+ * Check if a package version already exists on the registry.
4185
+ *
4186
+ * @param packageName - Package name
4187
+ * @param version - Version to check
4188
+ * @param registry - Registry URL
4189
+ * @returns true if version exists
4190
+ */
4191
+ async versionExists(packageName, version, registry = "https://registry.npmjs.org") {
4192
+ try {
4193
+ const url = `${registry}/${packageName}/${version}`;
4194
+ const response = await fetch(url);
4195
+ return response.status === 200;
4196
+ } catch {
4197
+ return false;
4198
+ }
4199
+ }
4200
+ /**
4201
+ * Get the npm package URL for a published package.
4202
+ *
4203
+ * @param packageName - Package name
4204
+ * @param registry - Registry URL
4205
+ * @returns URL to view the package
4206
+ */
4207
+ getPackageUrl(packageName, registry = "https://registry.npmjs.org") {
4208
+ if (registry === "https://registry.npmjs.org") {
4209
+ return `https://www.npmjs.com/package/${packageName}`;
4210
+ }
4211
+ return `${registry}/${packageName}`;
4212
+ }
4213
+ };
4214
+
4215
+ // src/plugin.ts
4216
+ var VIRTUAL_TOOLS_ID = "virtual:@standardagents-tools";
4217
+ var RESOLVED_VIRTUAL_TOOLS_ID = "\0" + VIRTUAL_TOOLS_ID;
4218
+ var VIRTUAL_ROUTES_ID = "virtual:@standardagents-routes";
4219
+ var RESOLVED_VIRTUAL_ROUTES_ID = "\0" + VIRTUAL_ROUTES_ID;
4220
+ var VIRTUAL_ROUTER_ID = "virtual:@standardagents/router";
4221
+ var RESOLVED_VIRTUAL_ROUTER_ID = "\0" + VIRTUAL_ROUTER_ID;
4222
+ var VIRTUAL_HOOKS_ID = "virtual:@standardagents-hooks";
4223
+ var RESOLVED_VIRTUAL_HOOKS_ID = "\0" + VIRTUAL_HOOKS_ID;
4224
+ var VIRTUAL_CONFIG_ID = "virtual:@standardagents-config";
4225
+ var RESOLVED_VIRTUAL_CONFIG_ID = "\0" + VIRTUAL_CONFIG_ID;
4226
+ var VIRTUAL_THREAD_API_ID = "virtual:@standardagents-thread-api";
4227
+ var RESOLVED_VIRTUAL_THREAD_API_ID = "\0" + VIRTUAL_THREAD_API_ID;
4228
+ var VIRTUAL_MODELS_ID = "virtual:@standardagents-models";
4229
+ var RESOLVED_VIRTUAL_MODELS_ID = "\0" + VIRTUAL_MODELS_ID;
4230
+ var VIRTUAL_PROMPTS_ID = "virtual:@standardagents-prompts";
4231
+ var RESOLVED_VIRTUAL_PROMPTS_ID = "\0" + VIRTUAL_PROMPTS_ID;
4232
+ var VIRTUAL_AGENTS_ID = "virtual:@standardagents-agents";
4233
+ var RESOLVED_VIRTUAL_AGENTS_ID = "\0" + VIRTUAL_AGENTS_ID;
4234
+ var VIRTUAL_EFFECTS_ID = "virtual:@standardagents-effects";
4235
+ var RESOLVED_VIRTUAL_EFFECTS_ID = "\0" + VIRTUAL_EFFECTS_ID;
4236
+ var VIRTUAL_PROVIDERS_ID = "virtual:@standardagents-providers";
4237
+ var RESOLVED_VIRTUAL_PROVIDERS_ID = "\0" + VIRTUAL_PROVIDERS_ID;
4238
+ var VIRTUAL_REGISTRY_ID = "virtual:@standardagents-registry";
4239
+ var RESOLVED_VIRTUAL_REGISTRY_ID = "\0" + VIRTUAL_REGISTRY_ID;
4240
+ var VIRTUAL_BUILDER_ID = "virtual:@standardagents/builder";
4241
+ var RESOLVED_VIRTUAL_BUILDER_ID = "\0" + VIRTUAL_BUILDER_ID;
4242
+ function scanApiDirectory(dir, baseRoute = "") {
4243
+ const routes = [];
4244
+ if (!fs2__default.existsSync(dir)) {
4245
+ return routes;
4246
+ }
4247
+ try {
4248
+ const entries = fs2__default.readdirSync(dir, { withFileTypes: true });
4249
+ for (const entry of entries) {
4250
+ const fullPath = path8__default.join(dir, entry.name);
4251
+ if (entry.isDirectory()) {
4252
+ const convertedDirName = entry.name.replace(/\[([^\]]+)\]/g, ":$1");
4253
+ const subRoute = baseRoute + "/" + convertedDirName;
4254
+ routes.push(...scanApiDirectory(fullPath, subRoute));
4255
+ } else if (entry.isFile() && entry.name.endsWith(".ts")) {
4256
+ const fileName = entry.name.replace(/\.ts$/, "");
4257
+ let method = "";
4258
+ let routePath = baseRoute;
4259
+ if (fileName.includes(".")) {
4260
+ const parts = fileName.split(".");
4261
+ const methodPart = parts[parts.length - 1].toUpperCase();
4262
+ if (["GET", "POST", "PUT", "DELETE", "PATCH"].includes(methodPart)) {
4263
+ method = methodPart;
4264
+ const pathPart = parts.slice(0, -1).join(".");
4265
+ if (pathPart !== "index") {
4266
+ const convertedPath = pathPart.replace(/\[([^\]]+)\]/g, ":$1");
4267
+ routePath += "/" + convertedPath;
4268
+ }
4269
+ } else {
4270
+ if (fileName !== "index") {
4271
+ const convertedPath = fileName.replace(/\[([^\]]+)\]/g, ":$1");
4272
+ routePath += "/" + convertedPath;
4273
+ }
4274
+ }
4275
+ } else {
4276
+ if (fileName !== "index") {
4277
+ const convertedPath = fileName.replace(/\[([^\]]+)\]/g, ":$1");
4278
+ routePath += "/" + convertedPath;
4279
+ }
4280
+ }
4281
+ const importPath = "./" + path8__default.relative(process.cwd(), fullPath).replace(/\\/g, "/");
4282
+ routes.push({
4283
+ method,
4284
+ route: routePath || "/",
4285
+ importPath
4286
+ });
4287
+ }
4288
+ }
4289
+ return routes;
4290
+ } catch (error) {
4291
+ console.error(`Error scanning API directory ${dir}:`, error);
4292
+ return [];
4293
+ }
4294
+ }
4295
+ function isSnakeCase(str) {
4296
+ return /^[a-z][a-z0-9_]*[a-z0-9]$/.test(str) || /^[a-z]$/.test(str);
4297
+ }
4298
+ function validateToolFile(filePath, fileName) {
4299
+ try {
4300
+ const content = fs2__default.readFileSync(filePath, "utf-8");
4301
+ const hasDefaultExport = /export\s+default\s+defineTool/.test(content);
4302
+ if (!hasDefaultExport) {
4303
+ return `Tool file '${fileName}.ts' must have a default export using defineTool()`;
4304
+ }
4305
+ return null;
4306
+ } catch (error) {
4307
+ return `Failed to read tool file '${fileName}.ts'`;
4308
+ }
4309
+ }
4310
+ async function scanToolsDirectory(dir) {
4311
+ const tools = [];
4312
+ if (!fs2__default.existsSync(dir)) {
4313
+ return tools;
4314
+ }
4315
+ const entries = await fs2__default.promises.readdir(dir, { withFileTypes: true });
4316
+ for (const entry of entries) {
4317
+ if (entry.isFile() && entry.name.endsWith(".ts")) {
4318
+ const fileName = entry.name.replace(".ts", "");
4319
+ const filePath = path8__default.join(dir, entry.name);
4320
+ const importPath = "./" + path8__default.relative(process.cwd(), filePath).replace(/\\/g, "/");
4321
+ let toolError;
4322
+ const validationError = validateToolFile(filePath, fileName);
4323
+ if (validationError) {
4324
+ toolError = validationError;
4325
+ console.error(`
4326
+ \u274C Tool validation error: ${validationError}`);
4327
+ }
4328
+ if (!isSnakeCase(fileName)) {
4329
+ const warning = `Tool name should be in snake_case format (e.g., 'log_name', 'send_email')`;
4330
+ if (toolError) {
4331
+ toolError += ` | ${warning}`;
4332
+ } else {
4333
+ toolError = warning;
4334
+ }
4335
+ console.warn(
4336
+ `
4337
+ \u26A0\uFE0F Tool naming warning: '${fileName}' should be in snake_case format (e.g., 'log_name', 'send_email')`
4338
+ );
4339
+ }
4340
+ tools.push({ name: fileName, importPath, error: toolError });
4341
+ }
4342
+ }
4343
+ return tools;
4344
+ }
4345
+ async function scanHooksDirectory(dir) {
4346
+ const hooks = [];
4347
+ if (!fs2__default.existsSync(dir)) {
4348
+ return hooks;
4349
+ }
4350
+ const entries = await fs2__default.promises.readdir(dir, { withFileTypes: true });
4351
+ for (const entry of entries) {
4352
+ if (entry.isFile() && entry.name.endsWith(".ts")) {
4353
+ const fileName = entry.name.replace(".ts", "");
4354
+ if (fileName === "index") {
4355
+ continue;
4356
+ }
4357
+ const filePath = path8__default.join(dir, entry.name);
4358
+ const importPath = "./" + path8__default.relative(process.cwd(), filePath).replace(/\\/g, "/");
4359
+ try {
4360
+ const content = fs2__default.readFileSync(filePath, "utf-8");
4361
+ const idMatch = content.match(/id:\s*['"]([^'"]+)['"]/);
4362
+ const hookMatch = content.match(/hook:\s*['"]([^'"]+)['"]/);
4363
+ if (idMatch && hookMatch) {
4364
+ hooks.push({
4365
+ id: idMatch[1],
4366
+ hook: hookMatch[1],
4367
+ importPath
4368
+ });
4369
+ } else {
4370
+ console.warn(
4371
+ `[vite-plugin-agent] Hook file '${entry.name}' is using deprecated format. Please update to use defineHook({ hook: '...', id: '...', execute: ... })`
4372
+ );
4373
+ }
4374
+ } catch (error) {
4375
+ console.error(`[vite-plugin-agent] Error reading hook file ${entry.name}:`, error);
4376
+ }
4377
+ }
4378
+ }
4379
+ return hooks;
4380
+ }
4381
+ async function scanConfigDirectory(dir, definePattern) {
4382
+ const items = [];
4383
+ if (!fs2__default.existsSync(dir)) {
4384
+ return items;
4385
+ }
4386
+ const entries = await fs2__default.promises.readdir(dir, { withFileTypes: true });
4387
+ for (const entry of entries) {
4388
+ if (entry.isFile() && entry.name.endsWith(".ts")) {
4389
+ const filePath = path8__default.join(dir, entry.name);
4390
+ const importPath = "./" + path8__default.relative(process.cwd(), filePath).replace(/\\/g, "/");
2014
4391
  try {
2015
- const content = fs2.readFileSync(filePath, "utf-8");
4392
+ const content = fs2__default.readFileSync(filePath, "utf-8");
2016
4393
  const hasDefaultExport = definePattern.test(content);
2017
4394
  if (!hasDefaultExport) {
2018
4395
  items.push({
@@ -2054,20 +4431,20 @@ async function scanAgentsDirectory(dir) {
2054
4431
  }
2055
4432
  async function scanEffectsDirectory(dir) {
2056
4433
  const effects = [];
2057
- if (!fs2.existsSync(dir)) {
4434
+ if (!fs2__default.existsSync(dir)) {
2058
4435
  return effects;
2059
4436
  }
2060
- const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
4437
+ const entries = await fs2__default.promises.readdir(dir, { withFileTypes: true });
2061
4438
  for (const entry of entries) {
2062
4439
  if (entry.isFile() && entry.name.endsWith(".ts")) {
2063
4440
  const fileName = entry.name.replace(".ts", "");
2064
- const filePath = path3.join(dir, entry.name);
2065
- const importPath = "./" + path3.relative(process.cwd(), filePath).replace(/\\/g, "/");
4441
+ const filePath = path8__default.join(dir, entry.name);
4442
+ const importPath = "./" + path8__default.relative(process.cwd(), filePath).replace(/\\/g, "/");
2066
4443
  if (fileName === "CLAUDE" || fileName.startsWith("_")) {
2067
4444
  continue;
2068
4445
  }
2069
4446
  try {
2070
- const content = fs2.readFileSync(filePath, "utf-8");
4447
+ const content = fs2__default.readFileSync(filePath, "utf-8");
2071
4448
  if (!content.includes("defineEffect")) {
2072
4449
  continue;
2073
4450
  }
@@ -2080,14 +4457,14 @@ async function scanEffectsDirectory(dir) {
2080
4457
  return effects;
2081
4458
  }
2082
4459
  function parseRequestBody(req) {
2083
- return new Promise((resolve, reject) => {
4460
+ return new Promise((resolve2, reject) => {
2084
4461
  let body = "";
2085
4462
  req.on("data", (chunk) => {
2086
4463
  body += chunk.toString();
2087
4464
  });
2088
4465
  req.on("end", () => {
2089
4466
  try {
2090
- resolve(body ? JSON.parse(body) : {});
4467
+ resolve2(body ? JSON.parse(body) : {});
2091
4468
  } catch (e) {
2092
4469
  reject(new Error("Invalid JSON body"));
2093
4470
  }
@@ -2103,19 +4480,20 @@ function agentbuilder(options = {}) {
2103
4480
  if (mountPoint.endsWith("/") && mountPoint.length > 1) {
2104
4481
  mountPoint = mountPoint.slice(0, -1);
2105
4482
  }
2106
- const toolsDir = options.toolsDir ? path3.resolve(process.cwd(), options.toolsDir) : path3.resolve(process.cwd(), "agents/tools");
2107
- const hooksDir = options.hooksDir ? path3.resolve(process.cwd(), options.hooksDir) : path3.resolve(process.cwd(), "agents/hooks");
2108
- const threadApiDir = options.apiDir ? path3.resolve(process.cwd(), options.apiDir) : path3.resolve(process.cwd(), "agents/api");
2109
- const modelsDir = options.modelsDir ? path3.resolve(process.cwd(), options.modelsDir) : path3.resolve(process.cwd(), "agents/models");
2110
- const promptsDir = options.promptsDir ? path3.resolve(process.cwd(), options.promptsDir) : path3.resolve(process.cwd(), "agents/prompts");
2111
- const agentsDir = options.agentsDir ? path3.resolve(process.cwd(), options.agentsDir) : path3.resolve(process.cwd(), "agents/agents");
2112
- const effectsDir = options.effectsDir ? path3.resolve(process.cwd(), options.effectsDir) : path3.resolve(process.cwd(), "agents/effects");
2113
- const outputDir = path3.resolve(process.cwd(), ".agents");
4483
+ const toolsDir = options.toolsDir ? path8__default.resolve(process.cwd(), options.toolsDir) : path8__default.resolve(process.cwd(), "agents/tools");
4484
+ const hooksDir = options.hooksDir ? path8__default.resolve(process.cwd(), options.hooksDir) : path8__default.resolve(process.cwd(), "agents/hooks");
4485
+ const threadApiDir = options.apiDir ? path8__default.resolve(process.cwd(), options.apiDir) : path8__default.resolve(process.cwd(), "agents/api");
4486
+ const modelsDir = options.modelsDir ? path8__default.resolve(process.cwd(), options.modelsDir) : path8__default.resolve(process.cwd(), "agents/models");
4487
+ const promptsDir = options.promptsDir ? path8__default.resolve(process.cwd(), options.promptsDir) : path8__default.resolve(process.cwd(), "agents/prompts");
4488
+ const agentsDir = options.agentsDir ? path8__default.resolve(process.cwd(), options.agentsDir) : path8__default.resolve(process.cwd(), "agents/agents");
4489
+ const effectsDir = options.effectsDir ? path8__default.resolve(process.cwd(), options.effectsDir) : path8__default.resolve(process.cwd(), "agents/effects");
4490
+ const outputDir = path8__default.resolve(process.cwd(), ".agents");
2114
4491
  const typeGenConfig = {
2115
4492
  modelsDir,
2116
4493
  promptsDir,
2117
4494
  agentsDir,
2118
4495
  toolsDir,
4496
+ hooksDir,
2119
4497
  outputDir
2120
4498
  };
2121
4499
  function regenerateTypes() {
@@ -2124,15 +4502,17 @@ function agentbuilder(options = {}) {
2124
4502
  }
2125
4503
  }
2126
4504
  const __filename = fileURLToPath(import.meta.url);
2127
- const __dirname = path3.dirname(__filename);
2128
- const rou3Path = path3.join(__dirname, "../dist/rou3.js");
4505
+ const __dirname = path8__default.dirname(__filename);
4506
+ const rou3Path = path8__default.join(__dirname, "../dist/rou3.js");
2129
4507
  let rou3Code = "";
2130
4508
  try {
2131
- rou3Code = fs2.readFileSync(rou3Path, "utf-8").replace(/^export \{[^}]+\};?\s*$/gm, "").replace(/\/\/# sourceMappingURL=.+$/gm, "").trim();
4509
+ rou3Code = fs2__default.readFileSync(rou3Path, "utf-8").replace(/^export \{[^}]+\};?\s*$/gm, "").replace(/\/\/# sourceMappingURL=.+$/gm, "").trim();
2132
4510
  } catch (err) {
2133
4511
  console.warn("[vite-plugin-agent] Could not read rou3.js for inlining:", err);
2134
4512
  }
2135
4513
  let routesVersion = Math.random().toString(36).substring(7);
4514
+ let agentsVersion = Math.random().toString(36).substring(7);
4515
+ let registryVersion = Math.random().toString(36).substring(7);
2136
4516
  function getWorkerEnvironment(server) {
2137
4517
  const envNames = Object.keys(server.environments);
2138
4518
  const workerEnvName = envNames.find((name) => name !== "client");
@@ -2142,120 +4522,269 @@ function agentbuilder(options = {}) {
2142
4522
  }
2143
4523
  return server.environments[workerEnvName];
2144
4524
  }
2145
- function reloadToolsModule(server) {
4525
+ function invalidateVirtualModule(server, virtualId, resolvedId) {
4526
+ const invalidatedEnvs = [];
4527
+ for (const [envName, env] of Object.entries(server.environments)) {
4528
+ let mod = env.moduleGraph.getModuleById(resolvedId);
4529
+ if (!mod) {
4530
+ mod = env.moduleGraph.urlToModuleMap.get(virtualId);
4531
+ }
4532
+ if (!mod) {
4533
+ mod = env.moduleGraph.urlToModuleMap.get(resolvedId);
4534
+ }
4535
+ if (mod) {
4536
+ console.log(`[HMR] Found ${virtualId} in ${envName} environment, invalidating`);
4537
+ env.moduleGraph.invalidateModule(mod);
4538
+ invalidatedEnvs.push(envName);
4539
+ for (const importer of mod.importers) {
4540
+ env.moduleGraph.invalidateModule(importer);
4541
+ console.log(`[HMR] Also invalidated importer: ${importer.url}`);
4542
+ }
4543
+ }
4544
+ }
4545
+ return {
4546
+ invalidatedAny: invalidatedEnvs.length > 0,
4547
+ environments: invalidatedEnvs
4548
+ };
4549
+ }
4550
+ async function reloadToolsModule(server) {
2146
4551
  const workerEnv = getWorkerEnvironment(server);
2147
4552
  if (!workerEnv) return false;
2148
- const toolsModule = workerEnv.moduleGraph.getModuleById(
2149
- RESOLVED_VIRTUAL_TOOLS_ID
2150
- );
2151
- if (toolsModule) {
2152
- workerEnv.reloadModule(toolsModule);
4553
+ try {
4554
+ const { invalidatedAny } = invalidateVirtualModule(
4555
+ server,
4556
+ VIRTUAL_TOOLS_ID,
4557
+ RESOLVED_VIRTUAL_TOOLS_ID
4558
+ );
4559
+ if (!invalidatedAny) {
4560
+ const toolsModule = await workerEnv.moduleGraph.ensureEntryFromUrl(
4561
+ VIRTUAL_TOOLS_ID
4562
+ );
4563
+ workerEnv.moduleGraph.invalidateModule(toolsModule);
4564
+ }
4565
+ workerEnv.hot.send({
4566
+ type: "full-reload",
4567
+ path: "*"
4568
+ });
2153
4569
  server.ws.send({
2154
4570
  type: "custom",
2155
4571
  event: "agent:tools-updated",
2156
4572
  data: { timestamp: Date.now() }
2157
4573
  });
2158
4574
  return true;
4575
+ } catch (err) {
4576
+ console.error("[HMR] reloadToolsModule error:", err);
4577
+ return false;
2159
4578
  }
2160
- return false;
2161
4579
  }
2162
- function reloadRoutesModule(server) {
4580
+ async function reloadRoutesModule(server) {
2163
4581
  const workerEnv = getWorkerEnvironment(server);
2164
4582
  if (!workerEnv) return false;
2165
- const routesModule = workerEnv.moduleGraph.getModuleById(
2166
- RESOLVED_VIRTUAL_ROUTES_ID
2167
- );
2168
- if (routesModule) {
4583
+ try {
2169
4584
  routesVersion = Math.random().toString(36).substring(7);
2170
- workerEnv.reloadModule(routesModule);
4585
+ const { invalidatedAny } = invalidateVirtualModule(
4586
+ server,
4587
+ VIRTUAL_ROUTES_ID,
4588
+ RESOLVED_VIRTUAL_ROUTES_ID
4589
+ );
4590
+ if (!invalidatedAny) {
4591
+ const routesModule = await workerEnv.moduleGraph.ensureEntryFromUrl(
4592
+ VIRTUAL_ROUTES_ID
4593
+ );
4594
+ workerEnv.moduleGraph.invalidateModule(routesModule);
4595
+ }
4596
+ workerEnv.hot.send({
4597
+ type: "full-reload",
4598
+ path: "*"
4599
+ });
2171
4600
  server.ws.send({
2172
4601
  type: "custom",
2173
4602
  event: "agent:routes-updated",
2174
4603
  data: { timestamp: Date.now() }
2175
4604
  });
2176
4605
  return true;
4606
+ } catch (err) {
4607
+ console.error("[HMR] reloadRoutesModule error:", err);
4608
+ return false;
2177
4609
  }
2178
- return false;
2179
4610
  }
2180
- function reloadHooksModule(server) {
4611
+ async function reloadHooksModule(server) {
2181
4612
  const workerEnv = getWorkerEnvironment(server);
2182
4613
  if (!workerEnv) return false;
2183
- const hooksModule = workerEnv.moduleGraph.getModuleById(
2184
- RESOLVED_VIRTUAL_HOOKS_ID
2185
- );
2186
- if (hooksModule) {
2187
- workerEnv.reloadModule(hooksModule);
4614
+ try {
4615
+ const { invalidatedAny } = invalidateVirtualModule(
4616
+ server,
4617
+ VIRTUAL_HOOKS_ID,
4618
+ RESOLVED_VIRTUAL_HOOKS_ID
4619
+ );
4620
+ if (!invalidatedAny) {
4621
+ const hooksModule = await workerEnv.moduleGraph.ensureEntryFromUrl(
4622
+ VIRTUAL_HOOKS_ID
4623
+ );
4624
+ workerEnv.moduleGraph.invalidateModule(hooksModule);
4625
+ }
4626
+ workerEnv.hot.send({
4627
+ type: "full-reload",
4628
+ path: "*"
4629
+ });
2188
4630
  server.ws.send({
2189
4631
  type: "custom",
2190
4632
  event: "agent:hooks-updated",
2191
4633
  data: { timestamp: Date.now() }
2192
4634
  });
2193
4635
  return true;
4636
+ } catch (err) {
4637
+ console.error("[HMR] reloadHooksModule error:", err);
4638
+ return false;
2194
4639
  }
2195
- return false;
2196
4640
  }
2197
4641
  async function reloadModelsModule(server) {
2198
4642
  const workerEnv = getWorkerEnvironment(server);
2199
4643
  if (!workerEnv) return false;
2200
- const modelsModule = workerEnv.moduleGraph.getModuleById(
2201
- RESOLVED_VIRTUAL_MODELS_ID
2202
- );
2203
- if (modelsModule) {
2204
- await workerEnv.reloadModule(modelsModule);
4644
+ try {
4645
+ const { invalidatedAny } = invalidateVirtualModule(
4646
+ server,
4647
+ VIRTUAL_MODELS_ID,
4648
+ RESOLVED_VIRTUAL_MODELS_ID
4649
+ );
4650
+ if (!invalidatedAny) {
4651
+ const modelsModule = await workerEnv.moduleGraph.ensureEntryFromUrl(
4652
+ VIRTUAL_MODELS_ID
4653
+ );
4654
+ workerEnv.moduleGraph.invalidateModule(modelsModule);
4655
+ }
4656
+ workerEnv.hot.send({
4657
+ type: "full-reload",
4658
+ path: "*"
4659
+ });
2205
4660
  server.ws.send({
2206
4661
  type: "custom",
2207
4662
  event: "agent:models-updated",
2208
4663
  data: { timestamp: Date.now() }
2209
4664
  });
2210
4665
  return true;
4666
+ } catch (err) {
4667
+ console.error("[HMR] reloadModelsModule error:", err);
4668
+ return false;
2211
4669
  }
2212
- return false;
2213
4670
  }
2214
4671
  async function reloadPromptsModule(server) {
2215
4672
  const workerEnv = getWorkerEnvironment(server);
2216
4673
  if (!workerEnv) return false;
2217
- const promptsModule = workerEnv.moduleGraph.getModuleById(
2218
- RESOLVED_VIRTUAL_PROMPTS_ID
2219
- );
2220
- if (promptsModule) {
2221
- await workerEnv.reloadModule(promptsModule);
4674
+ try {
4675
+ const { invalidatedAny } = invalidateVirtualModule(
4676
+ server,
4677
+ VIRTUAL_PROMPTS_ID,
4678
+ RESOLVED_VIRTUAL_PROMPTS_ID
4679
+ );
4680
+ if (!invalidatedAny) {
4681
+ const promptsModule = await workerEnv.moduleGraph.ensureEntryFromUrl(
4682
+ VIRTUAL_PROMPTS_ID
4683
+ );
4684
+ workerEnv.moduleGraph.invalidateModule(promptsModule);
4685
+ }
4686
+ workerEnv.hot.send({
4687
+ type: "full-reload",
4688
+ path: "*"
4689
+ });
2222
4690
  server.ws.send({
2223
4691
  type: "custom",
2224
4692
  event: "agent:prompts-updated",
2225
4693
  data: { timestamp: Date.now() }
2226
4694
  });
2227
4695
  return true;
4696
+ } catch (err) {
4697
+ console.error("[HMR] reloadPromptsModule error:", err);
4698
+ return false;
2228
4699
  }
2229
- return false;
2230
4700
  }
2231
4701
  async function reloadAgentsModule(server) {
2232
4702
  const workerEnv = getWorkerEnvironment(server);
2233
4703
  if (!workerEnv) return false;
2234
- const agentsModule = workerEnv.moduleGraph.getModuleById(
2235
- RESOLVED_VIRTUAL_AGENTS_ID
2236
- );
2237
- if (agentsModule) {
2238
- await workerEnv.reloadModule(agentsModule);
4704
+ try {
4705
+ agentsVersion = Math.random().toString(36).substring(7);
4706
+ const { invalidatedAny } = invalidateVirtualModule(
4707
+ server,
4708
+ VIRTUAL_AGENTS_ID,
4709
+ RESOLVED_VIRTUAL_AGENTS_ID
4710
+ );
4711
+ if (!invalidatedAny) {
4712
+ const agentsModule = await workerEnv.moduleGraph.ensureEntryFromUrl(
4713
+ VIRTUAL_AGENTS_ID
4714
+ );
4715
+ workerEnv.moduleGraph.invalidateModule(agentsModule);
4716
+ }
4717
+ workerEnv.hot.send({
4718
+ type: "full-reload",
4719
+ path: "*"
4720
+ });
2239
4721
  server.ws.send({
2240
4722
  type: "custom",
2241
4723
  event: "agent:agents-updated",
2242
4724
  data: { timestamp: Date.now() }
2243
4725
  });
2244
4726
  return true;
4727
+ } catch (err) {
4728
+ console.error("[HMR] reloadAgentsModule error:", err);
4729
+ return false;
2245
4730
  }
2246
- return false;
2247
4731
  }
2248
4732
  async function reloadRouterModule(server) {
2249
4733
  const workerEnv = getWorkerEnvironment(server);
2250
4734
  if (!workerEnv) return false;
2251
- const routerModule = workerEnv.moduleGraph.getModuleById(
2252
- RESOLVED_VIRTUAL_ROUTER_ID
2253
- );
2254
- if (routerModule) {
2255
- await workerEnv.reloadModule(routerModule);
4735
+ try {
4736
+ routesVersion = Math.random().toString(36).substring(7);
4737
+ const { invalidatedAny } = invalidateVirtualModule(
4738
+ server,
4739
+ VIRTUAL_ROUTES_ID,
4740
+ RESOLVED_VIRTUAL_ROUTES_ID
4741
+ );
4742
+ invalidateVirtualModule(
4743
+ server,
4744
+ VIRTUAL_ROUTER_ID,
4745
+ RESOLVED_VIRTUAL_ROUTER_ID
4746
+ );
4747
+ if (!invalidatedAny) {
4748
+ const routerModule = await workerEnv.moduleGraph.ensureEntryFromUrl(
4749
+ VIRTUAL_ROUTES_ID
4750
+ );
4751
+ workerEnv.moduleGraph.invalidateModule(routerModule);
4752
+ }
4753
+ workerEnv.hot.send({
4754
+ type: "full-reload",
4755
+ path: "*"
4756
+ });
4757
+ return true;
4758
+ } catch (err) {
4759
+ console.error("[HMR] reloadRouterModule error:", err);
4760
+ return false;
4761
+ }
4762
+ }
4763
+ async function reloadRegistryModule(server) {
4764
+ const workerEnv = getWorkerEnvironment(server);
4765
+ if (!workerEnv) return false;
4766
+ try {
4767
+ registryVersion = Math.random().toString(36).substring(7);
4768
+ const { invalidatedAny } = invalidateVirtualModule(
4769
+ server,
4770
+ VIRTUAL_REGISTRY_ID,
4771
+ RESOLVED_VIRTUAL_REGISTRY_ID
4772
+ );
4773
+ if (!invalidatedAny) {
4774
+ const registryModule = await workerEnv.moduleGraph.ensureEntryFromUrl(
4775
+ VIRTUAL_REGISTRY_ID
4776
+ );
4777
+ workerEnv.moduleGraph.invalidateModule(registryModule);
4778
+ }
4779
+ workerEnv.hot.send({
4780
+ type: "full-reload",
4781
+ path: "*"
4782
+ });
2256
4783
  return true;
4784
+ } catch (err) {
4785
+ console.error("[HMR] reloadRegistryModule error:", err);
4786
+ return false;
2257
4787
  }
2258
- return false;
2259
4788
  }
2260
4789
  function handleFileChange(file, server) {
2261
4790
  const isToolFile = file.startsWith(toolsDir) && file.endsWith(".ts");
@@ -2286,6 +4815,7 @@ function agentbuilder(options = {}) {
2286
4815
  }
2287
4816
  if (isHookFile) {
2288
4817
  reloadHooksModule(server);
4818
+ reloadRoutesModule(server);
2289
4819
  }
2290
4820
  }
2291
4821
  return {
@@ -2297,28 +4827,37 @@ function agentbuilder(options = {}) {
2297
4827
  "@standardagents/builder/runtime",
2298
4828
  "@standardagents/builder/built-in-routes",
2299
4829
  "@standardagents/builder/image-processing",
4830
+ "@standardagents/builder/packing",
2300
4831
  // WASM image processing deps - must be excluded to avoid pre-bundle cache issues
2301
4832
  "@cf-wasm/photon",
2302
4833
  "@cf-wasm/photon/workerd",
2303
4834
  "@standardagents/sip",
2304
4835
  // sip's jsquash dependencies (WASM-based decoders)
2305
4836
  "@jsquash/avif",
2306
- "@jsquash/webp"
4837
+ "@jsquash/webp",
4838
+ // Rollup and plugins - only used by CLI pack command, not in dev/production
4839
+ "rollup",
4840
+ "@rollup/plugin-commonjs",
4841
+ "@rollup/plugin-node-resolve",
4842
+ "rollup-plugin-esbuild",
4843
+ "fsevents",
4844
+ "typescript"
2307
4845
  ];
2308
4846
  const depsToInclude = [
2309
4847
  "zod",
2310
4848
  "openai",
4849
+ "magic-string",
2311
4850
  "@standardagents/spec"
2312
4851
  ];
2313
- const currentDir = path3.dirname(fileURLToPath(import.meta.url));
4852
+ const currentDir = path8__default.dirname(fileURLToPath(import.meta.url));
2314
4853
  const isInDist = currentDir.endsWith("dist");
2315
- const builderClientDir = path3.resolve(
4854
+ const builderClientDir = path8__default.resolve(
2316
4855
  currentDir,
2317
4856
  isInDist ? "./client" : "../dist/client"
2318
4857
  );
2319
4858
  return {
2320
4859
  // Set publicDir to builder's client assets so Cloudflare plugin preserves assets config
2321
- publicDir: fs2.existsSync(builderClientDir) ? builderClientDir : void 0,
4860
+ publicDir: fs2__default.existsSync(builderClientDir) ? builderClientDir : void 0,
2322
4861
  optimizeDeps: {
2323
4862
  // Exclude our packages from pre-bundling - they contain cloudflare:workers imports
2324
4863
  // that cannot be resolved during dependency optimization
@@ -2334,6 +4873,12 @@ function agentbuilder(options = {}) {
2334
4873
  "@standardagents/builder/runtime",
2335
4874
  "@standardagents/builder/built-in-routes"
2336
4875
  ]
4876
+ },
4877
+ // Suppress sourcemap warnings for packages without source maps (like typescript)
4878
+ server: {
4879
+ sourcemapIgnoreList: (sourcePath) => {
4880
+ return sourcePath.includes("node_modules/typescript") || sourcePath.includes("node_modules/.pnpm/typescript");
4881
+ }
2337
4882
  }
2338
4883
  };
2339
4884
  },
@@ -2344,13 +4889,21 @@ function agentbuilder(options = {}) {
2344
4889
  "@standardagents/builder/runtime",
2345
4890
  "@standardagents/builder/built-in-routes",
2346
4891
  "@standardagents/builder/image-processing",
4892
+ "@standardagents/builder/packing",
2347
4893
  // WASM image processing deps
2348
4894
  "@cf-wasm/photon",
2349
4895
  "@cf-wasm/photon/workerd",
2350
4896
  "@standardagents/sip",
2351
4897
  // sip's jsquash dependencies (WASM-based decoders)
2352
4898
  "@jsquash/avif",
2353
- "@jsquash/webp"
4899
+ "@jsquash/webp",
4900
+ // Rollup and plugins - only used by CLI pack command, not in dev/production
4901
+ "rollup",
4902
+ "@rollup/plugin-commonjs",
4903
+ "@rollup/plugin-node-resolve",
4904
+ "rollup-plugin-esbuild",
4905
+ "fsevents",
4906
+ "typescript"
2354
4907
  ];
2355
4908
  const depsToInclude = [
2356
4909
  "zod",
@@ -2358,6 +4911,7 @@ function agentbuilder(options = {}) {
2358
4911
  "zod/v4",
2359
4912
  "zod/v4/core",
2360
4913
  "openai",
4914
+ "magic-string",
2361
4915
  "@standardagents/spec"
2362
4916
  ];
2363
4917
  config.optimizeDeps = config.optimizeDeps || {};
@@ -2401,6 +4955,9 @@ function agentbuilder(options = {}) {
2401
4955
  if (id === VIRTUAL_PROVIDERS_ID) {
2402
4956
  return RESOLVED_VIRTUAL_PROVIDERS_ID;
2403
4957
  }
4958
+ if (id === VIRTUAL_REGISTRY_ID) {
4959
+ return RESOLVED_VIRTUAL_REGISTRY_ID;
4960
+ }
2404
4961
  if (id === VIRTUAL_BUILDER_ID) {
2405
4962
  return RESOLVED_VIRTUAL_BUILDER_ID;
2406
4963
  }
@@ -2440,7 +4997,7 @@ ${toolsCode}
2440
4997
  /* v${routesVersion} */ async () => (await import("${importPath}")).default
2441
4998
  );`;
2442
4999
  }).join("\n");
2443
- return `// Inline rou3 router code (no external imports)
5000
+ return `// Inline rou3 router code (no external imports) /* v${routesVersion} */
2444
5001
  ${rou3Code}
2445
5002
 
2446
5003
  import { registerBuiltInRoutes } from "@standardagents/builder/built-in-routes";
@@ -2450,6 +5007,7 @@ import { hooks } from "virtual:@standardagents-hooks";
2450
5007
  import { models, modelNames } from "virtual:@standardagents-models";
2451
5008
  import { prompts, promptNames } from "virtual:@standardagents-prompts";
2452
5009
  import { agents, agentNames } from "virtual:@standardagents-agents";
5010
+ import { registry } from "virtual:@standardagents-registry";
2453
5011
  import { requireAuth, createThreadEndpointHandler } from "@standardagents/builder/runtime";
2454
5012
  import { isThreadEndpoint } from "@standardagents/spec";
2455
5013
 
@@ -2462,7 +5020,8 @@ const PUBLIC_ROUTES = [
2462
5020
  '/api/auth/oauth/github',
2463
5021
  '/api/auth/oauth/google',
2464
5022
  '/api/auth/oauth/github/callback',
2465
- '/api/auth/oauth/google/callback'
5023
+ '/api/auth/oauth/google/callback',
5024
+ '/api/hooks' // Hook metadata is safe to expose publicly
2466
5025
  ];
2467
5026
 
2468
5027
  // Check if a route is public (no auth required)
@@ -2554,7 +5113,7 @@ export async function router(request, env) {
2554
5113
  const router = createRouter();
2555
5114
 
2556
5115
  // Register built-in API routes with runtime data
2557
- registerBuiltInRoutes(router, { config, tools, models, modelNames, prompts, promptNames, agents, agentNames });
5116
+ registerBuiltInRoutes(router, { config, tools, hooks, models, modelNames, prompts, promptNames, agents, agentNames, registry });
2558
5117
 
2559
5118
  // Register user thread API routes
2560
5119
  ${threadRouteCode}
@@ -2678,28 +5237,30 @@ async function serveUI(pathname, env) {
2678
5237
  }
2679
5238
  if (id === RESOLVED_VIRTUAL_HOOKS_ID) {
2680
5239
  const hooks = await scanHooksDirectory(hooksDir);
2681
- const hooksCode = hooks.map(({ name, importPath }) => {
2682
- return ` "${name}": async () => {
5240
+ const hooksCode = hooks.map(({ id: hookId, hook, importPath }) => {
5241
+ return ` "${hookId}": async () => {
2683
5242
  try {
2684
5243
  return (await import("${importPath}")).default;
2685
5244
  } catch (error) {
2686
- console.error('[Hooks] Failed to import hook ${name}:', error);
5245
+ console.error('[Hooks] Failed to import hook ${hookId}:', error);
2687
5246
  return null;
2688
5247
  }
2689
5248
  },`;
2690
5249
  }).join("\n");
2691
5250
  return `// Virtual agent hooks module
5251
+ // Hook registry keyed by hook ID
5252
+ // Each loader returns: { hook: string, id: string, execute: Function }
2692
5253
  export const hooks = {
2693
5254
  ${hooksCode}
2694
5255
  };`;
2695
5256
  }
2696
5257
  if (id === RESOLVED_VIRTUAL_CONFIG_ID) {
2697
- const relativeToolsDir = path3.relative(process.cwd(), toolsDir).replace(/\\/g, "/");
2698
- const relativeHooksDir = path3.relative(process.cwd(), hooksDir).replace(/\\/g, "/");
2699
- const relativeApiDir = path3.relative(process.cwd(), threadApiDir).replace(/\\/g, "/");
2700
- const relativeModelsDir = path3.relative(process.cwd(), modelsDir).replace(/\\/g, "/");
2701
- const relativePromptsDir = path3.relative(process.cwd(), promptsDir).replace(/\\/g, "/");
2702
- const relativeAgentsDir = path3.relative(process.cwd(), agentsDir).replace(/\\/g, "/");
5258
+ const relativeToolsDir = path8__default.relative(process.cwd(), toolsDir).replace(/\\/g, "/");
5259
+ const relativeHooksDir = path8__default.relative(process.cwd(), hooksDir).replace(/\\/g, "/");
5260
+ const relativeApiDir = path8__default.relative(process.cwd(), threadApiDir).replace(/\\/g, "/");
5261
+ const relativeModelsDir = path8__default.relative(process.cwd(), modelsDir).replace(/\\/g, "/");
5262
+ const relativePromptsDir = path8__default.relative(process.cwd(), promptsDir).replace(/\\/g, "/");
5263
+ const relativeAgentsDir = path8__default.relative(process.cwd(), agentsDir).replace(/\\/g, "/");
2703
5264
  return `// Virtual agent config module
2704
5265
  export const config = {
2705
5266
  toolsDir: "${relativeToolsDir}",
@@ -2762,7 +5323,9 @@ export const promptNames = ${JSON.stringify(prompts.filter((p) => !p.error).map(
2762
5323
  `;
2763
5324
  }
2764
5325
  if (id === RESOLVED_VIRTUAL_AGENTS_ID) {
5326
+ console.log(`[vite-plugin-agent] load() called for agents module (v${agentsVersion})`);
2765
5327
  const agents = await scanAgentsDirectory(agentsDir);
5328
+ console.log(`[vite-plugin-agent] Found ${agents.length} agents:`, agents.map((a) => a.name));
2766
5329
  const agentsCode = agents.map(({ name, importPath, error }) => {
2767
5330
  if (error) {
2768
5331
  const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
@@ -2778,7 +5341,7 @@ export const promptNames = ${JSON.stringify(prompts.filter((p) => !p.error).map(
2778
5341
  },`;
2779
5342
  }
2780
5343
  }).join("\n");
2781
- return `// Virtual agent agents module
5344
+ return `// Virtual agent agents module /* v${agentsVersion} */
2782
5345
  export const agents = {
2783
5346
  ${agentsCode}
2784
5347
  };
@@ -2803,36 +5366,192 @@ export const agentNames = ${JSON.stringify(agents.filter((a) => !a.error).map((a
2803
5366
  },`;
2804
5367
  }
2805
5368
  }).join("\n");
2806
- return `// Virtual agent effects module
2807
- export const effects = {
2808
- ${effectsCode}
2809
- };
5369
+ return `// Virtual agent effects module
5370
+ export const effects = {
5371
+ ${effectsCode}
5372
+ };
5373
+
5374
+ export const effectNames = ${JSON.stringify(effects.filter((e) => !e.error).map((e) => e.name))};
5375
+ `;
5376
+ }
5377
+ if (id === RESOLVED_VIRTUAL_PROVIDERS_ID) {
5378
+ const firstPartyProviders = [
5379
+ { name: "openai", package: "@standardagents/openai", label: "OpenAI", envKey: "OPENAI_API_KEY" },
5380
+ { name: "openrouter", package: "@standardagents/openrouter", label: "OpenRouter", envKey: "OPENROUTER_API_KEY" }
5381
+ ];
5382
+ const customProviders = (options.providers || []).map((pkg) => ({
5383
+ name: pkg.split("/").pop() || pkg,
5384
+ package: pkg,
5385
+ label: pkg.split("/").pop() || pkg,
5386
+ envKey: `${(pkg.split("/").pop() || pkg).toUpperCase().replace(/-/g, "_")}_API_KEY`,
5387
+ isCustom: true
5388
+ }));
5389
+ const allProviders = [...firstPartyProviders, ...customProviders];
5390
+ return `// Virtual providers module - lists available LLM provider packages
5391
+ export const providers = ${JSON.stringify(allProviders, null, 2)};
5392
+
5393
+ export const providerNames = ${JSON.stringify(allProviders.map((p) => p.name))};
5394
+
5395
+ // Provider factories - dynamic imports for code splitting
5396
+ export const providerFactories = {
5397
+ ${allProviders.map((p) => ` "${p.name}": async () => (await import("${p.package}")).${p.name},`).join("\n")}
5398
+ };
5399
+ `;
5400
+ }
5401
+ if (id === RESOLVED_VIRTUAL_REGISTRY_ID) {
5402
+ console.log(`[vite-plugin-agent] load() called for registry module (v${registryVersion})`);
5403
+ const tools = await scanToolsDirectory(toolsDir);
5404
+ const hooks = await scanHooksDirectory(hooksDir);
5405
+ const models = await scanModelsDirectory(modelsDir);
5406
+ const prompts = await scanPromptsDirectory(promptsDir);
5407
+ const agents = await scanAgentsDirectory(agentsDir);
5408
+ const toAbsolutePath = (relativePath) => {
5409
+ if (relativePath.startsWith("./")) {
5410
+ return path8__default.resolve(process.cwd(), relativePath).replace(/\\/g, "/");
5411
+ }
5412
+ return relativePath;
5413
+ };
5414
+ const globalToolsCode = tools.filter((t) => !t.error).map(({ name, importPath }) => {
5415
+ const absPath = toAbsolutePath(importPath);
5416
+ return ` "${name}": async () => (await import("${absPath}")).default,`;
5417
+ }).join("\n");
5418
+ const globalPromptsCode = prompts.filter((p) => !p.error).map(({ name, importPath }) => {
5419
+ const absPath = toAbsolutePath(importPath);
5420
+ return ` "${name}": async () => (await import("${absPath}")).default,`;
5421
+ }).join("\n");
5422
+ const globalModelsCode = models.filter((m) => !m.error).map(({ name, importPath }) => {
5423
+ const absPath = toAbsolutePath(importPath);
5424
+ return ` "${name}": async () => (await import("${absPath}")).default,`;
5425
+ }).join("\n");
5426
+ const globalAgentsCode = agents.filter((a) => !a.error).map(({ name, importPath }) => {
5427
+ const absPath = toAbsolutePath(importPath);
5428
+ return ` "${name}": async () => (await import("${absPath}")).default,`;
5429
+ }).join("\n");
5430
+ const globalHooksCode = hooks.map(({ id: id2, importPath }) => {
5431
+ const absPath = toAbsolutePath(importPath);
5432
+ return ` "${id2}": async () => (await import("${absPath}")).default,`;
5433
+ }).join("\n");
5434
+ const packedDir = path8__default.resolve(process.cwd(), "agents/packed");
5435
+ const packedPackages = [];
5436
+ if (fs2__default.existsSync(packedDir)) {
5437
+ console.log(`[vite-plugin-agent] Scanning packed directory: ${packedDir}`);
5438
+ const pkgDirs = fs2__default.readdirSync(packedDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
5439
+ console.log(`[vite-plugin-agent] Found package dirs: ${pkgDirs.join(", ")}`);
5440
+ for (const pkgDirName of pkgDirs) {
5441
+ const pkgPath = path8__default.join(packedDir, pkgDirName);
5442
+ const pkgJsonPath = path8__default.join(pkgPath, "package.json");
5443
+ if (fs2__default.existsSync(pkgJsonPath)) {
5444
+ try {
5445
+ const pkgJson = JSON.parse(fs2__default.readFileSync(pkgJsonPath, "utf-8"));
5446
+ console.log(`[vite-plugin-agent] Loaded ${pkgDirName} package.json, standardagent:`, pkgJson.standardagent);
5447
+ if (!pkgJson.standardagent) {
5448
+ continue;
5449
+ }
5450
+ const mainEntry = pkgJson.main || "./dist/index.js";
5451
+ const outputDir2 = path8__default.dirname(mainEntry).replace(/^\.\//, "");
5452
+ const scanPackedDir = async (subDir) => {
5453
+ const fullPath = path8__default.join(pkgPath, outputDir2, subDir);
5454
+ if (!fs2__default.existsSync(fullPath)) return [];
5455
+ const files = fs2__default.readdirSync(fullPath).filter((f) => f.endsWith(".js") || f.endsWith(".ts")).map((f) => ({
5456
+ name: f.replace(/\.(js|ts)$/, ""),
5457
+ path: path8__default.join(fullPath, f).replace(/\\/g, "/")
5458
+ }));
5459
+ return files;
5460
+ };
5461
+ const pkg = {
5462
+ packageId: pkgJson.name,
5463
+ version: pkgJson.version,
5464
+ entryAgents: pkgJson.standardagent.entryAgents || [],
5465
+ items: {
5466
+ agents: await scanPackedDir("agents"),
5467
+ prompts: await scanPackedDir("prompts"),
5468
+ tools: await scanPackedDir("tools"),
5469
+ models: await scanPackedDir("models"),
5470
+ hooks: await scanPackedDir("hooks")
5471
+ }
5472
+ };
5473
+ console.log(`[vite-plugin-agent] Scanned package ${pkgJson.name}:`, pkg.items.agents);
5474
+ packedPackages.push(pkg);
5475
+ } catch (err) {
5476
+ console.warn(`[vite-plugin-agent] Failed to load packed package ${pkgDirName}:`, err);
5477
+ }
5478
+ }
5479
+ }
5480
+ } else {
5481
+ console.log(`[vite-plugin-agent] Packed directory does not exist: ${packedDir}`);
5482
+ }
5483
+ console.log(`[vite-plugin-agent] Total packed packages found: ${packedPackages.length}`);
5484
+ const packagesCode = packedPackages.map((pkg) => {
5485
+ const signature = JSON.stringify({
5486
+ packageId: pkg.packageId,
5487
+ version: pkg.version,
5488
+ source: "local",
5489
+ packedAt: Date.now()
5490
+ });
5491
+ const genLoaders = (items) => items.map(
5492
+ (item) => ` "${item.name}": async () => {
5493
+ const mod = await import("${item.path}");
5494
+ return { ...mod.default, __package: ${signature} };
5495
+ },`
5496
+ ).join("\n");
5497
+ return ` "${pkg.packageId}": {
5498
+ agents: {
5499
+ ${genLoaders(pkg.items.agents)}
5500
+ },
5501
+ prompts: {
5502
+ ${genLoaders(pkg.items.prompts)}
5503
+ },
5504
+ tools: {
5505
+ ${genLoaders(pkg.items.tools)}
5506
+ },
5507
+ models: {
5508
+ ${genLoaders(pkg.items.models)}
5509
+ },
5510
+ hooks: {
5511
+ ${genLoaders(pkg.items.hooks)}
5512
+ },
5513
+ entryAgents: ${JSON.stringify(pkg.entryAgents)},
5514
+ signature: ${signature},
5515
+ },`;
5516
+ }).join("\n");
5517
+ return `// Virtual namespaced registry module /* v${registryVersion} */
5518
+ // Provides global namespace + packed package namespaces for namespace isolation
2810
5519
 
2811
- export const effectNames = ${JSON.stringify(effects.filter((e) => !e.error).map((e) => e.name))};
2812
- `;
2813
- }
2814
- if (id === RESOLVED_VIRTUAL_PROVIDERS_ID) {
2815
- const firstPartyProviders = [
2816
- { name: "openai", package: "@standardagents/openai", label: "OpenAI", envKey: "OPENAI_API_KEY" },
2817
- { name: "openrouter", package: "@standardagents/openrouter", label: "OpenRouter", envKey: "OPENROUTER_API_KEY" }
2818
- ];
2819
- const customProviders = (options.providers || []).map((pkg) => ({
2820
- name: pkg.split("/").pop() || pkg,
2821
- package: pkg,
2822
- label: pkg.split("/").pop() || pkg,
2823
- envKey: `${(pkg.split("/").pop() || pkg).toUpperCase().replace(/-/g, "_")}_API_KEY`,
2824
- isCustom: true
2825
- }));
2826
- const allProviders = [...firstPartyProviders, ...customProviders];
2827
- return `// Virtual providers module - lists available LLM provider packages
2828
- export const providers = ${JSON.stringify(allProviders, null, 2)};
5520
+ export const registry = {
5521
+ global: {
5522
+ agents: {
5523
+ ${globalAgentsCode}
5524
+ },
5525
+ prompts: {
5526
+ ${globalPromptsCode}
5527
+ },
5528
+ tools: {
5529
+ ${globalToolsCode}
5530
+ },
5531
+ models: {
5532
+ ${globalModelsCode}
5533
+ },
5534
+ hooks: {
5535
+ ${globalHooksCode}
5536
+ },
5537
+ },
5538
+ packages: {
5539
+ ${packagesCode}
5540
+ },
5541
+ };
2829
5542
 
2830
- export const providerNames = ${JSON.stringify(allProviders.map((p) => p.name))};
5543
+ // Helper to get all visible agent names (global + entry points)
5544
+ export function getVisibleAgentNames() {
5545
+ const globalAgents = Object.keys(registry.global.agents);
5546
+ const entryPoints = Object.values(registry.packages)
5547
+ .flatMap(pkg => pkg.entryAgents);
5548
+ return [...new Set([...globalAgents, ...entryPoints])];
5549
+ }
2831
5550
 
2832
- // Provider factories - dynamic imports for code splitting
2833
- export const providerFactories = {
2834
- ${allProviders.map((p) => ` "${p.name}": async () => (await import("${p.package}")).${p.name},`).join("\n")}
2835
- };
5551
+ // Helper to get all visible tool names (global only - packed tools are internal)
5552
+ export function getVisibleToolNames() {
5553
+ return Object.keys(registry.global.tools);
5554
+ }
2836
5555
  `;
2837
5556
  }
2838
5557
  if (id === RESOLVED_VIRTUAL_BUILDER_ID) {
@@ -2844,7 +5563,7 @@ ${allProviders.map((p) => ` "${p.name}": async () => (await import("${p.package
2844
5563
  const effects = await scanEffectsDirectory(effectsDir);
2845
5564
  const toAbsolutePath = (relativePath) => {
2846
5565
  if (relativePath.startsWith("./")) {
2847
- return path3.resolve(process.cwd(), relativePath).replace(/\\/g, "/");
5566
+ return path8__default.resolve(process.cwd(), relativePath).replace(/\\/g, "/");
2848
5567
  }
2849
5568
  return relativePath;
2850
5569
  };
@@ -2864,13 +5583,13 @@ ${allProviders.map((p) => ` "${p.name}": async () => (await import("${p.package
2864
5583
  },`;
2865
5584
  }
2866
5585
  }).join("\n");
2867
- const hooksCode = hooks.map(({ name, importPath }) => {
5586
+ const hooksCode = hooks.map(({ id: id2, importPath }) => {
2868
5587
  const absPath = toAbsolutePath(importPath);
2869
- return ` "${name}": async () => {
5588
+ return ` "${id2}": async () => {
2870
5589
  try {
2871
5590
  return (await import("${absPath}")).default;
2872
5591
  } catch (error) {
2873
- console.error('[Hooks] Failed to import hook ${name}:', error);
5592
+ console.error('[Hooks] Failed to import hook ${id2}:', error);
2874
5593
  return null;
2875
5594
  }
2876
5595
  },`;
@@ -2953,6 +5672,9 @@ import { initWithWasmModule as _initSipWasm } from '@standardagents/sip';
2953
5672
  // Re-export router from virtual:@standardagents-routes
2954
5673
  export { router } from 'virtual:@standardagents-routes';
2955
5674
 
5675
+ // Import namespaced registry for packed agent support
5676
+ import { registry as _registry } from 'virtual:@standardagents-registry';
5677
+
2956
5678
  // Registry objects
2957
5679
  const _tools = {
2958
5680
  ${toolsCode}
@@ -3016,6 +5738,10 @@ export class DurableThread extends _BaseDurableThread {
3016
5738
  effects() {
3017
5739
  return _effects;
3018
5740
  }
5741
+
5742
+ registry() {
5743
+ return _registry;
5744
+ }
3019
5745
  }
3020
5746
 
3021
5747
  /**
@@ -3046,6 +5772,10 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
3046
5772
  effects() {
3047
5773
  return _effects;
3048
5774
  }
5775
+
5776
+ registry() {
5777
+ return _registry;
5778
+ }
3049
5779
  }
3050
5780
  `;
3051
5781
  }
@@ -3078,53 +5808,6 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
3078
5808
  pathWithoutMount = "/" + pathWithoutMount;
3079
5809
  }
3080
5810
  const method = req.method?.toUpperCase();
3081
- if (pathWithoutMount === "/api/models" && method === "GET") {
3082
- try {
3083
- const fs4 = await import('fs');
3084
- const files = fs4.existsSync(modelsDir) ? fs4.readdirSync(modelsDir).filter((f) => f.endsWith(".ts")) : [];
3085
- const modelList = files.map((file) => {
3086
- try {
3087
- const filePath = path3.join(modelsDir, file);
3088
- const content = fs4.readFileSync(filePath, "utf-8");
3089
- const parsed = parseModelFile(content);
3090
- if (!parsed || !parsed.name) return null;
3091
- const fallbackObjects = parsed.fallbacks.map((fallbackName, index) => ({
3092
- id: fallbackName,
3093
- name: fallbackName,
3094
- order: index
3095
- }));
3096
- return {
3097
- id: parsed.name,
3098
- name: parsed.name,
3099
- provider: parsed.provider,
3100
- provider_id: parsed.provider,
3101
- model: parsed.model,
3102
- input_price: parsed.inputPrice,
3103
- output_price: parsed.outputPrice,
3104
- cached_price: parsed.cachedPrice,
3105
- included_providers: parsed.includedProviders,
3106
- fallbacks: fallbackObjects,
3107
- providerTools: parsed.providerTools,
3108
- capabilities: parsed.capabilities,
3109
- created_at: Math.floor(Date.now() / 1e3)
3110
- };
3111
- } catch (error) {
3112
- console.error(`Error loading model ${file}:`, error);
3113
- return null;
3114
- }
3115
- });
3116
- const validModels = modelList.filter(Boolean);
3117
- res.statusCode = 200;
3118
- res.setHeader("Content-Type", "application/json");
3119
- res.end(JSON.stringify({ models: validModels }));
3120
- return;
3121
- } catch (error) {
3122
- res.statusCode = 500;
3123
- res.setHeader("Content-Type", "application/json");
3124
- res.end(JSON.stringify({ error: error.message || "Failed to list models" }));
3125
- return;
3126
- }
3127
- }
3128
5811
  if (pathWithoutMount === "/api/models" && method === "POST") {
3129
5812
  try {
3130
5813
  const rawBody = await parseRequestBody(req);
@@ -3199,7 +5882,7 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
3199
5882
  await reloadPromptsModule(server);
3200
5883
  }
3201
5884
  await reloadRouterModule(server);
3202
- await new Promise((resolve) => setTimeout(resolve, 100));
5885
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
3203
5886
  res.statusCode = 200;
3204
5887
  res.setHeader("Content-Type", "application/json");
3205
5888
  res.end(JSON.stringify({
@@ -3249,90 +5932,6 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
3249
5932
  return;
3250
5933
  }
3251
5934
  }
3252
- if (pathWithoutMount === "/api/prompts" && method === "GET") {
3253
- try {
3254
- const fs4 = await import('fs');
3255
- const files = fs4.existsSync(promptsDir) ? fs4.readdirSync(promptsDir).filter((f) => f.endsWith(".ts")) : [];
3256
- const modelFiles = fs4.existsSync(modelsDir) ? fs4.readdirSync(modelsDir).filter((f) => f.endsWith(".ts")) : [];
3257
- const modelMap = {};
3258
- for (const file of modelFiles) {
3259
- try {
3260
- const filePath = path3.join(modelsDir, file);
3261
- const content = fs4.readFileSync(filePath, "utf-8");
3262
- const name = content.match(/name:\s*['"]([^'"]+)['"]/)?.[1];
3263
- const provider = content.match(/provider:\s*['"]([^'"]+)['"]/)?.[1];
3264
- if (name) {
3265
- modelMap[name] = { name, provider: provider || "unknown" };
3266
- }
3267
- } catch (error) {
3268
- }
3269
- }
3270
- const promptList = files.map((file) => {
3271
- try {
3272
- const filePath = path3.join(promptsDir, file);
3273
- const content = fs4.readFileSync(filePath, "utf-8");
3274
- const getName2 = (c) => c.match(/name:\s*['"]([^'"]+)['"]/)?.[1];
3275
- const getToolDescription = (c) => c.match(/toolDescription:\s*['"]([^'"]+)['"]/)?.[1];
3276
- const getModel2 = (c) => c.match(/model:\s*['"]([^'"]+)['"]/)?.[1];
3277
- const getIncludeChat = (c) => c.match(/includeChat:\s*(true|false)/)?.[1] === "true";
3278
- const getIncludePastTools = (c) => c.match(/includePastTools:\s*(true|false)/)?.[1] === "true";
3279
- const getParallelToolCalls = (c) => c.match(/parallelToolCalls:\s*(true|false)/)?.[1] === "true";
3280
- const getToolChoice = (c) => c.match(/toolChoice:\s*['"]([^'"]+)['"]/)?.[1];
3281
- const getTools = (c) => {
3282
- const match = c.match(/tools:\s*\[([^\]]*)\]/);
3283
- if (!match) return [];
3284
- const items = match[1].match(/['"]([^'"]+)['"]/g);
3285
- return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
3286
- };
3287
- const getPrompt = (c) => {
3288
- const backtickMatch = c.match(/prompt:\s*`([\s\S]*?)`/);
3289
- if (backtickMatch) return backtickMatch[1];
3290
- const quotedMatch = c.match(/prompt:\s*['"]([^'"]*)['"]/);
3291
- if (quotedMatch) return quotedMatch[1];
3292
- const arrayMatch = c.match(/prompt:\s*(\[[\s\S]*?\])/);
3293
- if (arrayMatch) return arrayMatch[1];
3294
- return "";
3295
- };
3296
- const name = getName2(content);
3297
- if (!name) return null;
3298
- const modelId = getModel2(content);
3299
- const modelDef = modelId ? modelMap[modelId] : null;
3300
- return {
3301
- id: name,
3302
- name,
3303
- tool_description: getToolDescription(content) || "",
3304
- prompt: getPrompt(content),
3305
- required_schema: null,
3306
- // Complex to parse, skip for now
3307
- model_id: modelId || "",
3308
- model_name: modelDef?.name || modelId || "",
3309
- model_provider: modelDef?.provider || "unknown",
3310
- include_chat: getIncludeChat(content),
3311
- include_past_tools: getIncludePastTools(content),
3312
- parallel_tool_calls: getParallelToolCalls(content),
3313
- tool_choice: getToolChoice(content) || "auto",
3314
- tools: getTools(content),
3315
- reasoning: null,
3316
- // Complex to parse
3317
- created_at: Math.floor(Date.now() / 1e3)
3318
- };
3319
- } catch (error) {
3320
- console.error(`Error loading prompt ${file}:`, error);
3321
- return null;
3322
- }
3323
- });
3324
- const validPrompts = promptList.filter(Boolean);
3325
- res.statusCode = 200;
3326
- res.setHeader("Content-Type", "application/json");
3327
- res.end(JSON.stringify({ prompts: validPrompts }));
3328
- return;
3329
- } catch (error) {
3330
- res.statusCode = 500;
3331
- res.setHeader("Content-Type", "application/json");
3332
- res.end(JSON.stringify({ error: error.message || "Failed to list prompts" }));
3333
- return;
3334
- }
3335
- }
3336
5935
  if (pathWithoutMount === "/api/prompts" && method === "POST") {
3337
5936
  try {
3338
5937
  const rawBody = await parseRequestBody(req);
@@ -3458,63 +6057,6 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
3458
6057
  return;
3459
6058
  }
3460
6059
  }
3461
- if (pathWithoutMount === "/api/agents" && method === "GET") {
3462
- try {
3463
- const fs4 = await import('fs');
3464
- const files = fs4.existsSync(agentsDir) ? fs4.readdirSync(agentsDir).filter((f) => f.endsWith(".ts")) : [];
3465
- const agentList = files.map((file) => {
3466
- try {
3467
- const filePath = path3.join(agentsDir, file);
3468
- const content = fs4.readFileSync(filePath, "utf-8");
3469
- const getName2 = (c) => c.match(/name:\s*['"]([^'"]+)['"]/)?.[1];
3470
- const getTitle = (c) => c.match(/title:\s*['"]([^'"]+)['"]/)?.[1];
3471
- const getType = (c) => c.match(/type:\s*['"]([^'"]+)['"]/)?.[1];
3472
- const getDefaultPrompt = (c) => c.match(/defaultPrompt:\s*['"]([^'"]+)['"]/)?.[1];
3473
- const getDefaultModel = (c) => c.match(/defaultModel:\s*['"]([^'"]+)['"]/)?.[1];
3474
- const getTools = (c) => {
3475
- const match = c.match(/tools:\s*\[([^\]]*)\]/);
3476
- if (!match) return [];
3477
- const items = match[1].match(/['"]([^'"]+)['"]/g);
3478
- return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
3479
- };
3480
- const getSidePrompt = (c, side) => {
3481
- const sideRegex = new RegExp(`${side}:\\s*\\{([^}]+)\\}`, "s");
3482
- const sideMatch = c.match(sideRegex);
3483
- if (!sideMatch) return null;
3484
- const promptMatch = sideMatch[1].match(/prompt:\s*['"]([^'"]+)['"]/);
3485
- return promptMatch ? promptMatch[1] : null;
3486
- };
3487
- const name = getName2(content);
3488
- if (!name) return null;
3489
- return {
3490
- id: name,
3491
- name,
3492
- title: getTitle(content) || name,
3493
- type: getType(content) || "ai_human",
3494
- default_prompt: getDefaultPrompt(content) || "",
3495
- default_model: getDefaultModel(content) || "",
3496
- tools: getTools(content),
3497
- side_a_agent_prompt: getSidePrompt(content, "sideA"),
3498
- side_b_agent_prompt: getSidePrompt(content, "sideB"),
3499
- created_at: Math.floor(Date.now() / 1e3)
3500
- };
3501
- } catch (error) {
3502
- console.error(`Error loading agent ${file}:`, error);
3503
- return null;
3504
- }
3505
- });
3506
- const validAgents = agentList.filter(Boolean);
3507
- res.statusCode = 200;
3508
- res.setHeader("Content-Type", "application/json");
3509
- res.end(JSON.stringify({ agents: validAgents }));
3510
- return;
3511
- } catch (error) {
3512
- res.statusCode = 500;
3513
- res.setHeader("Content-Type", "application/json");
3514
- res.end(JSON.stringify({ error: error.message || "Failed to list agents" }));
3515
- return;
3516
- }
3517
- }
3518
6060
  if (pathWithoutMount === "/api/agents" && method === "POST") {
3519
6061
  try {
3520
6062
  const rawBody = await parseRequestBody(req);
@@ -3622,6 +6164,333 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
3622
6164
  return;
3623
6165
  }
3624
6166
  }
6167
+ const packAnalyzeMatch = pathWithoutMount.match(/^\/api\/pack\/([^/]+)\/analyze$/);
6168
+ if (packAnalyzeMatch && method === "GET") {
6169
+ try {
6170
+ const agentName = decodeURIComponent(packAnalyzeMatch[1]);
6171
+ const packingService = new PackingService();
6172
+ const rootDir = process.cwd();
6173
+ const analysis = await packingService.analyzeAgent(agentName, rootDir);
6174
+ if (analysis.errors.length > 0) {
6175
+ res.statusCode = 400;
6176
+ res.setHeader("Content-Type", "application/json");
6177
+ res.end(JSON.stringify({
6178
+ error: analysis.errors.join("\n"),
6179
+ warnings: analysis.warnings
6180
+ }));
6181
+ return;
6182
+ }
6183
+ res.statusCode = 200;
6184
+ res.setHeader("Content-Type", "application/json");
6185
+ res.end(JSON.stringify(analysis));
6186
+ return;
6187
+ } catch (error) {
6188
+ res.statusCode = 500;
6189
+ res.setHeader("Content-Type", "application/json");
6190
+ res.end(JSON.stringify({ error: error.message || "Failed to analyze agent" }));
6191
+ return;
6192
+ }
6193
+ }
6194
+ const packMatch = pathWithoutMount.match(/^\/api\/pack\/([^/]+)$/);
6195
+ if (packMatch && method === "POST") {
6196
+ try {
6197
+ const agentName = decodeURIComponent(packMatch[1]);
6198
+ const body = await parseRequestBody(req);
6199
+ const packingService = new PackingService();
6200
+ const rootDir = process.cwd();
6201
+ const outputDir2 = path8__default.join(rootDir, "agents", "packed");
6202
+ const result = await packingService.pack({
6203
+ agentName,
6204
+ rootDir,
6205
+ outputDir: outputDir2,
6206
+ version: body.version,
6207
+ packageId: body.packageId,
6208
+ packageName: body.packageName,
6209
+ license: body.license,
6210
+ licenseOwner: body.licenseOwner,
6211
+ itemSelections: body.itemSelections
6212
+ });
6213
+ if (!result.success) {
6214
+ res.statusCode = 400;
6215
+ res.setHeader("Content-Type", "application/json");
6216
+ res.end(JSON.stringify({
6217
+ error: result.error,
6218
+ warnings: result.warnings
6219
+ }));
6220
+ return;
6221
+ }
6222
+ reloadToolsModule(server);
6223
+ reloadHooksModule(server);
6224
+ await reloadAgentsModule(server);
6225
+ await reloadPromptsModule(server);
6226
+ await reloadModelsModule(server);
6227
+ await reloadRegistryModule(server);
6228
+ await reloadRouterModule(server);
6229
+ res.statusCode = 200;
6230
+ res.setHeader("Content-Type", "application/json");
6231
+ res.end(JSON.stringify(result));
6232
+ return;
6233
+ } catch (error) {
6234
+ res.statusCode = 500;
6235
+ res.setHeader("Content-Type", "application/json");
6236
+ res.end(JSON.stringify({ error: error.message || "Failed to pack agent" }));
6237
+ return;
6238
+ }
6239
+ }
6240
+ const packDeleteMatch = pathWithoutMount.match(/^\/api\/pack\/([^/]+)$/);
6241
+ if (packDeleteMatch && method === "DELETE") {
6242
+ try {
6243
+ const packageId = decodeURIComponent(packDeleteMatch[1]);
6244
+ const unpackingService = new UnpackingService();
6245
+ const rootDir = process.cwd();
6246
+ const packages = await unpackingService.listPackages(rootDir);
6247
+ const pkg = packages.find((p) => p.packageId === packageId);
6248
+ if (!pkg) {
6249
+ res.statusCode = 404;
6250
+ res.setHeader("Content-Type", "application/json");
6251
+ res.end(JSON.stringify({ error: `Package not found: ${packageId}` }));
6252
+ return;
6253
+ }
6254
+ if (pkg.source !== "local") {
6255
+ res.statusCode = 400;
6256
+ res.setHeader("Content-Type", "application/json");
6257
+ res.end(JSON.stringify({ error: "Cannot delete npm packages. Use npm uninstall instead." }));
6258
+ return;
6259
+ }
6260
+ const fs9 = await import('fs');
6261
+ fs9.rmSync(pkg.path, { recursive: true, force: true });
6262
+ reloadToolsModule(server);
6263
+ reloadHooksModule(server);
6264
+ await reloadAgentsModule(server);
6265
+ await reloadPromptsModule(server);
6266
+ await reloadModelsModule(server);
6267
+ await reloadRegistryModule(server);
6268
+ await reloadRouterModule(server);
6269
+ res.statusCode = 200;
6270
+ res.setHeader("Content-Type", "application/json");
6271
+ res.end(JSON.stringify({ success: true, packageId }));
6272
+ return;
6273
+ } catch (error) {
6274
+ res.statusCode = 500;
6275
+ res.setHeader("Content-Type", "application/json");
6276
+ res.end(JSON.stringify({ error: error.message || "Failed to delete package" }));
6277
+ return;
6278
+ }
6279
+ }
6280
+ const packDownloadMatch = pathWithoutMount.match(/^\/api\/pack\/([^/]+)\/download$/);
6281
+ if (packDownloadMatch && method === "GET") {
6282
+ try {
6283
+ const packageId = decodeURIComponent(packDownloadMatch[1]);
6284
+ const unpackingService = new UnpackingService();
6285
+ const rootDir = process.cwd();
6286
+ const packages = await unpackingService.listPackages(rootDir);
6287
+ const pkg = packages.find((p) => p.packageId === packageId);
6288
+ if (!pkg) {
6289
+ res.statusCode = 404;
6290
+ res.setHeader("Content-Type", "application/json");
6291
+ res.end(JSON.stringify({ error: `Package not found: ${packageId}` }));
6292
+ return;
6293
+ }
6294
+ const { spawn } = await import('child_process');
6295
+ const parentDir = path8__default.dirname(pkg.path);
6296
+ const dirName = path8__default.basename(pkg.path);
6297
+ res.setHeader("Content-Type", "application/gzip");
6298
+ res.setHeader("Content-Disposition", `attachment; filename="${packageId}.tar.gz"`);
6299
+ const tar = spawn("tar", ["-czf", "-", "-C", parentDir, dirName]);
6300
+ tar.stdout.pipe(res);
6301
+ tar.stderr.on("data", (data) => console.error("tar error:", data.toString()));
6302
+ tar.on("error", (err) => {
6303
+ console.error("Failed to spawn tar:", err);
6304
+ if (!res.headersSent) {
6305
+ res.statusCode = 500;
6306
+ res.setHeader("Content-Type", "application/json");
6307
+ res.end(JSON.stringify({ error: "Failed to create tarball" }));
6308
+ }
6309
+ });
6310
+ return;
6311
+ } catch (error) {
6312
+ res.statusCode = 500;
6313
+ res.setHeader("Content-Type", "application/json");
6314
+ res.end(JSON.stringify({ error: error.message || "Failed to download package" }));
6315
+ return;
6316
+ }
6317
+ }
6318
+ const packInfoMatch = pathWithoutMount.match(/^\/api\/pack\/([^/]+)\/packed-info$/);
6319
+ if (packInfoMatch && method === "GET") {
6320
+ try {
6321
+ const packageId = decodeURIComponent(packInfoMatch[1]);
6322
+ const packingService = new PackingService();
6323
+ const rootDir = process.cwd();
6324
+ const info = packingService.getPackedInfo(packageId, rootDir);
6325
+ if (!info) {
6326
+ const availablePackages = packingService.listPackedPackages(rootDir);
6327
+ res.statusCode = 404;
6328
+ res.setHeader("Content-Type", "application/json");
6329
+ res.end(JSON.stringify({
6330
+ error: `Packed package not found: ${packageId}`,
6331
+ availablePackages
6332
+ }));
6333
+ return;
6334
+ }
6335
+ res.statusCode = 200;
6336
+ res.setHeader("Content-Type", "application/json");
6337
+ res.end(JSON.stringify(info));
6338
+ return;
6339
+ } catch (error) {
6340
+ res.statusCode = 500;
6341
+ res.setHeader("Content-Type", "application/json");
6342
+ res.end(JSON.stringify({ error: error.message || "Failed to read package info" }));
6343
+ return;
6344
+ }
6345
+ }
6346
+ const packPublishMatch = pathWithoutMount.match(/^\/api\/pack\/([^/]+)\/publish$/);
6347
+ if (packPublishMatch && method === "POST") {
6348
+ try {
6349
+ const packageId = decodeURIComponent(packPublishMatch[1]);
6350
+ const body = await parseRequestBody(req);
6351
+ const rootDir = process.cwd();
6352
+ const packedDir = path8__default.join(rootDir, "agents", "packed");
6353
+ let packageDir = path8__default.join(packedDir, packageId);
6354
+ if (!fs2__default.existsSync(packageDir)) {
6355
+ if (!fs2__default.existsSync(packedDir)) {
6356
+ res.statusCode = 400;
6357
+ res.setHeader("Content-Type", "application/json");
6358
+ res.end(JSON.stringify({ error: "No packed packages found. Pack the agent first." }));
6359
+ return;
6360
+ }
6361
+ const dirs = fs2__default.readdirSync(packedDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6362
+ const matchingDir = dirs.find(
6363
+ (d) => d === packageId || d === `standardagent-${packageId}` || d === `standardagent-${packageId.replace(/_/g, "-")}`
6364
+ );
6365
+ if (!matchingDir) {
6366
+ res.statusCode = 400;
6367
+ res.setHeader("Content-Type", "application/json");
6368
+ res.end(JSON.stringify({
6369
+ error: `Package not found: ${packageId}. Available packages: ${dirs.join(", ") || "none"}`
6370
+ }));
6371
+ return;
6372
+ }
6373
+ packageDir = path8__default.join(packedDir, matchingDir);
6374
+ }
6375
+ let token = body.token || process.env.NPM_TOKEN;
6376
+ let tokenSource = body.token ? "provided" : "environment";
6377
+ if (!token) {
6378
+ const devVarsPath = path8__default.join(rootDir, ".dev.vars");
6379
+ if (fs2__default.existsSync(devVarsPath)) {
6380
+ const devVars = fs2__default.readFileSync(devVarsPath, "utf-8");
6381
+ const match = devVars.match(/^NPM_TOKEN=(.+)$/m);
6382
+ if (match) {
6383
+ token = match[1].trim();
6384
+ tokenSource = "environment";
6385
+ }
6386
+ }
6387
+ }
6388
+ if (!token) {
6389
+ res.statusCode = 400;
6390
+ res.setHeader("Content-Type", "application/json");
6391
+ res.end(JSON.stringify({
6392
+ error: "npm token is required. Either provide a token in the request or set NPM_TOKEN in your .dev.vars or .env file."
6393
+ }));
6394
+ return;
6395
+ }
6396
+ const publishService = new NpmPublishService();
6397
+ const result = await publishService.publish({
6398
+ packageDir,
6399
+ token,
6400
+ registry: body.registry,
6401
+ dryRun: body.dryRun
6402
+ });
6403
+ if (!result.success) {
6404
+ res.statusCode = 500;
6405
+ res.setHeader("Content-Type", "application/json");
6406
+ res.end(JSON.stringify({ error: result.error, output: result.output }));
6407
+ return;
6408
+ }
6409
+ res.statusCode = 200;
6410
+ res.setHeader("Content-Type", "application/json");
6411
+ res.end(JSON.stringify({
6412
+ success: true,
6413
+ packageName: result.packageName,
6414
+ version: result.version,
6415
+ output: result.output,
6416
+ url: publishService.getPackageUrl(result.packageName, body.registry),
6417
+ tokenSource
6418
+ }));
6419
+ return;
6420
+ } catch (error) {
6421
+ res.statusCode = 500;
6422
+ res.setHeader("Content-Type", "application/json");
6423
+ res.end(JSON.stringify({ error: error.message || "Failed to publish package" }));
6424
+ return;
6425
+ }
6426
+ }
6427
+ const unpackAnalyzeMatch = pathWithoutMount.match(/^\/api\/unpack\/([^/]+)\/analyze$/);
6428
+ if (unpackAnalyzeMatch && method === "GET") {
6429
+ try {
6430
+ const packageId = decodeURIComponent(unpackAnalyzeMatch[1]);
6431
+ const unpackingService = new UnpackingService();
6432
+ const rootDir = process.cwd();
6433
+ const analysis = await unpackingService.analyzeUnpack(packageId, rootDir);
6434
+ if (analysis.errors.length > 0) {
6435
+ res.statusCode = 400;
6436
+ res.setHeader("Content-Type", "application/json");
6437
+ res.end(JSON.stringify({
6438
+ error: analysis.errors.join("\n"),
6439
+ warnings: analysis.warnings
6440
+ }));
6441
+ return;
6442
+ }
6443
+ res.statusCode = 200;
6444
+ res.setHeader("Content-Type", "application/json");
6445
+ res.end(JSON.stringify(analysis));
6446
+ return;
6447
+ } catch (error) {
6448
+ res.statusCode = 500;
6449
+ res.setHeader("Content-Type", "application/json");
6450
+ res.end(JSON.stringify({ error: error.message || "Failed to analyze package" }));
6451
+ return;
6452
+ }
6453
+ }
6454
+ const unpackMatch = pathWithoutMount.match(/^\/api\/unpack\/([^/]+)$/);
6455
+ if (unpackMatch && method === "POST") {
6456
+ try {
6457
+ const packageId = decodeURIComponent(unpackMatch[1]);
6458
+ const body = await parseRequestBody(req);
6459
+ const unpackingService = new UnpackingService();
6460
+ const rootDir = process.cwd();
6461
+ const result = await unpackingService.unpack({
6462
+ packageId,
6463
+ rootDir,
6464
+ deletePackage: body.deletePackage,
6465
+ itemSelections: body.itemSelections
6466
+ });
6467
+ if (!result.success) {
6468
+ res.statusCode = 400;
6469
+ res.setHeader("Content-Type", "application/json");
6470
+ res.end(JSON.stringify({
6471
+ error: result.error,
6472
+ warnings: result.warnings
6473
+ }));
6474
+ return;
6475
+ }
6476
+ reloadToolsModule(server);
6477
+ reloadHooksModule(server);
6478
+ await reloadAgentsModule(server);
6479
+ await reloadPromptsModule(server);
6480
+ await reloadModelsModule(server);
6481
+ await reloadRegistryModule(server);
6482
+ await reloadRouterModule(server);
6483
+ res.statusCode = 200;
6484
+ res.setHeader("Content-Type", "application/json");
6485
+ res.end(JSON.stringify(result));
6486
+ return;
6487
+ } catch (error) {
6488
+ res.statusCode = 500;
6489
+ res.setHeader("Content-Type", "application/json");
6490
+ res.end(JSON.stringify({ error: error.message || "Failed to unpack package" }));
6491
+ return;
6492
+ }
6493
+ }
3625
6494
  if (pathWithoutMount.startsWith("/api/")) {
3626
6495
  next();
3627
6496
  return;
@@ -3646,23 +6515,23 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
3646
6515
  }
3647
6516
  const isStaticAsset = pathWithoutMount.startsWith("/assets/") || pathWithoutMount.startsWith("/vendor.js") || pathWithoutMount.startsWith("/vue.js") || pathWithoutMount.startsWith("/monaco.js") || pathWithoutMount.startsWith("/index.js") || pathWithoutMount.startsWith("/index.css") || pathWithoutMount.match(/\.(js|css|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|ico)$/);
3648
6517
  {
3649
- const currentDir = path3.dirname(fileURLToPath(import.meta.url));
6518
+ const currentDir = path8__default.dirname(fileURLToPath(import.meta.url));
3650
6519
  const isInDist = currentDir.endsWith("dist");
3651
- const clientDir = path3.resolve(
6520
+ const clientDir = path8__default.resolve(
3652
6521
  currentDir,
3653
6522
  isInDist ? "./client" : "../dist/client"
3654
6523
  );
3655
6524
  let filePath;
3656
6525
  if (isStaticAsset) {
3657
6526
  const cleanUrl = pathWithoutMount.split("?")[0];
3658
- filePath = path3.join(clientDir, cleanUrl);
6527
+ filePath = path8__default.join(clientDir, cleanUrl);
3659
6528
  } else {
3660
- filePath = path3.join(clientDir, "index.html");
6529
+ filePath = path8__default.join(clientDir, "index.html");
3661
6530
  }
3662
6531
  try {
3663
- if (fs2.existsSync(filePath)) {
3664
- let content = fs2.readFileSync(filePath);
3665
- const ext = path3.extname(filePath).toLowerCase();
6532
+ if (fs2__default.existsSync(filePath)) {
6533
+ let content = fs2__default.readFileSync(filePath);
6534
+ const ext = path8__default.extname(filePath).toLowerCase();
3666
6535
  if (ext === ".html") {
3667
6536
  const configScript = `<script>window.__AGENTBUILDER_CONFIG__ = { mountPoint: "${mountPoint}" };</script>`;
3668
6537
  let htmlContent = content.toString();
@@ -3700,28 +6569,28 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
3700
6569
  writeBundle(options2, bundle) {
3701
6570
  const outDir = options2.dir || "dist";
3702
6571
  const mountPath = mountPoint.slice(1);
3703
- const mountDir = mountPath ? path3.join(outDir, "../client", mountPath) : path3.join(outDir, "../client");
3704
- const currentDir = path3.dirname(fileURLToPath(import.meta.url));
6572
+ const mountDir = mountPath ? path8__default.join(outDir, "../client", mountPath) : path8__default.join(outDir, "../client");
6573
+ const currentDir = path8__default.dirname(fileURLToPath(import.meta.url));
3705
6574
  const isInDist = currentDir.endsWith("dist");
3706
- const clientDir = path3.resolve(
6575
+ const clientDir = path8__default.resolve(
3707
6576
  currentDir,
3708
6577
  isInDist ? "./client" : "../dist/client"
3709
6578
  );
3710
- if (!fs2.existsSync(clientDir)) {
6579
+ if (!fs2__default.existsSync(clientDir)) {
3711
6580
  console.warn(`[agentbuilder] Client directory not found at ${clientDir}`);
3712
6581
  return;
3713
6582
  }
3714
- fs2.mkdirSync(mountDir, { recursive: true });
6583
+ fs2__default.mkdirSync(mountDir, { recursive: true });
3715
6584
  function copyRecursive(src, dest) {
3716
- const entries = fs2.readdirSync(src, { withFileTypes: true });
6585
+ const entries = fs2__default.readdirSync(src, { withFileTypes: true });
3717
6586
  for (const entry of entries) {
3718
- const srcPath = path3.join(src, entry.name);
3719
- const destPath = path3.join(dest, entry.name);
6587
+ const srcPath = path8__default.join(src, entry.name);
6588
+ const destPath = path8__default.join(dest, entry.name);
3720
6589
  if (entry.isDirectory()) {
3721
- fs2.mkdirSync(destPath, { recursive: true });
6590
+ fs2__default.mkdirSync(destPath, { recursive: true });
3722
6591
  copyRecursive(srcPath, destPath);
3723
6592
  } else {
3724
- let content = fs2.readFileSync(srcPath);
6593
+ let content = fs2__default.readFileSync(srcPath);
3725
6594
  if (entry.name === "index.html") {
3726
6595
  const configScript = `<script>window.__AGENTBUILDER_CONFIG__ = { mountPoint: "${mountPoint}" };</script>`;
3727
6596
  let htmlContent = content.toString();
@@ -3730,7 +6599,7 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
3730
6599
  htmlContent = htmlContent.replace("</head>", `${configScript}</head>`);
3731
6600
  content = Buffer.from(htmlContent);
3732
6601
  }
3733
- fs2.writeFileSync(destPath, content);
6602
+ fs2__default.writeFileSync(destPath, content);
3734
6603
  }
3735
6604
  }
3736
6605
  }