@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/built-in-routes.js +3498 -498
- package/dist/built-in-routes.js.map +1 -1
- package/dist/client/assets/index.css +1 -1
- package/dist/client/index.js +56 -25
- package/dist/client/vendor.js +78 -5
- package/dist/client/vue.js +1 -1
- package/dist/discovery-CpMs68Yx.d.ts +97 -0
- package/dist/index-DkbUJ4MM.d.ts +1043 -0
- package/dist/index.d.ts +205 -999
- package/dist/index.js +4563 -956
- package/dist/index.js.map +1 -1
- package/dist/packing.d.ts +266 -0
- package/dist/packing.js +2202 -0
- package/dist/packing.js.map +1 -0
- package/dist/plugin.js +3477 -608
- package/dist/plugin.js.map +1 -1
- package/dist/test.d.ts +256 -0
- package/dist/test.js +391 -0
- package/dist/test.js.map +1 -0
- package/dist/types-pLkJx8vg.d.ts +306 -0
- package/package.json +25 -4
package/dist/plugin.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import fs2 from 'fs';
|
|
2
|
-
import
|
|
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 (!
|
|
44
|
+
if (!fs2__default.existsSync(dir)) {
|
|
39
45
|
return results;
|
|
40
46
|
}
|
|
41
47
|
try {
|
|
42
|
-
const entries =
|
|
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 =
|
|
46
|
-
const content =
|
|
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 (!
|
|
68
|
+
if (!fs2__default.existsSync(dir)) {
|
|
63
69
|
return results;
|
|
64
70
|
}
|
|
65
71
|
try {
|
|
66
|
-
const entries =
|
|
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 =
|
|
70
|
-
const content =
|
|
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 (!
|
|
96
|
+
if (!fs2__default.existsSync(dir)) {
|
|
91
97
|
return names;
|
|
92
98
|
}
|
|
93
99
|
try {
|
|
94
|
-
const entries =
|
|
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 =
|
|
98
|
-
const content =
|
|
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
|
|
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
|
|
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 (!
|
|
647
|
-
|
|
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
|
-
|
|
690
|
+
fs2__default.writeFileSync(path8__default.join(config.outputDir, "types.d.ts"), typesContent);
|
|
654
691
|
const virtualModuleContent = generateVirtualModuleContent();
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
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 =
|
|
661
|
-
if (!
|
|
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 =
|
|
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 (!
|
|
704
|
+
if (!fs2__default.existsSync(dir)) {
|
|
668
705
|
continue;
|
|
669
706
|
}
|
|
670
|
-
const entries =
|
|
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 =
|
|
674
|
-
const fileMtime =
|
|
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
|
|
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
|
|
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
|
|
1240
|
+
return fs2__default.existsSync(filePath);
|
|
1267
1241
|
}
|
|
1268
1242
|
async function saveModel(modelsDir, data, overwrite = false) {
|
|
1269
1243
|
try {
|
|
1270
|
-
if (!
|
|
1271
|
-
|
|
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 &&
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
1420
|
+
return fs2__default.existsSync(filePath);
|
|
1444
1421
|
}
|
|
1445
1422
|
async function savePrompt(promptsDir, data, overwrite = false) {
|
|
1446
1423
|
try {
|
|
1447
|
-
if (!
|
|
1448
|
-
|
|
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 &&
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (
|
|
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
|
|
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
|
|
1513
|
-
await
|
|
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
|
|
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
|
|
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 (!
|
|
1651
|
-
|
|
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 &&
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (
|
|
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
|
|
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
|
|
1716
|
-
await
|
|
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 (!
|
|
1742
|
+
if (!fs2__default.existsSync(promptsDir)) {
|
|
1731
1743
|
return updatedFiles;
|
|
1732
1744
|
}
|
|
1733
|
-
const files =
|
|
1745
|
+
const files = fs2__default.readdirSync(promptsDir).filter((f) => f.endsWith(".ts"));
|
|
1734
1746
|
for (const file of files) {
|
|
1735
|
-
const filePath =
|
|
1736
|
-
let content = await
|
|
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
|
|
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 (!
|
|
1760
|
+
if (!fs2__default.existsSync(promptsDir)) {
|
|
1749
1761
|
return updatedFiles;
|
|
1750
1762
|
}
|
|
1751
|
-
const files =
|
|
1763
|
+
const files = fs2__default.readdirSync(promptsDir).filter((f) => f.endsWith(".ts"));
|
|
1752
1764
|
for (const file of files) {
|
|
1753
|
-
const filePath =
|
|
1754
|
-
let content = await
|
|
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
|
|
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 (!
|
|
1787
|
+
if (!fs2__default.existsSync(agentsDir)) {
|
|
1776
1788
|
return updatedFiles;
|
|
1777
1789
|
}
|
|
1778
|
-
const files =
|
|
1790
|
+
const files = fs2__default.readdirSync(agentsDir).filter((f) => f.endsWith(".ts"));
|
|
1779
1791
|
for (const file of files) {
|
|
1780
|
-
const filePath =
|
|
1781
|
-
let content = await
|
|
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
|
|
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
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
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
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
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
|
-
|
|
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
|
|
1936
|
-
|
|
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
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1951
|
-
const
|
|
1952
|
-
|
|
1953
|
-
|
|
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
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
const
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
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
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
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
|
-
|
|
2177
|
+
visit(sourceFile);
|
|
2178
|
+
return result;
|
|
1984
2179
|
}
|
|
1985
|
-
|
|
1986
|
-
const
|
|
1987
|
-
|
|
1988
|
-
|
|
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
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
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
|
-
|
|
2221
|
+
visit(sourceFile);
|
|
2222
|
+
return name;
|
|
2003
2223
|
}
|
|
2004
|
-
|
|
2005
|
-
const
|
|
2006
|
-
|
|
2007
|
-
|
|
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
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
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 =
|
|
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 (!
|
|
4434
|
+
if (!fs2__default.existsSync(dir)) {
|
|
2058
4435
|
return effects;
|
|
2059
4436
|
}
|
|
2060
|
-
const entries = await
|
|
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 =
|
|
2065
|
-
const importPath = "./" +
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
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 ?
|
|
2107
|
-
const hooksDir = options.hooksDir ?
|
|
2108
|
-
const threadApiDir = options.apiDir ?
|
|
2109
|
-
const modelsDir = options.modelsDir ?
|
|
2110
|
-
const promptsDir = options.promptsDir ?
|
|
2111
|
-
const agentsDir = options.agentsDir ?
|
|
2112
|
-
const effectsDir = options.effectsDir ?
|
|
2113
|
-
const outputDir =
|
|
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 =
|
|
2128
|
-
const rou3Path =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
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
|
-
|
|
2166
|
-
RESOLVED_VIRTUAL_ROUTES_ID
|
|
2167
|
-
);
|
|
2168
|
-
if (routesModule) {
|
|
4583
|
+
try {
|
|
2169
4584
|
routesVersion = Math.random().toString(36).substring(7);
|
|
2170
|
-
|
|
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
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
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
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
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
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
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
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
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
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
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 =
|
|
4852
|
+
const currentDir = path8__default.dirname(fileURLToPath(import.meta.url));
|
|
2314
4853
|
const isInDist = currentDir.endsWith("dist");
|
|
2315
|
-
const builderClientDir =
|
|
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:
|
|
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(({
|
|
2682
|
-
return ` "${
|
|
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 ${
|
|
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 =
|
|
2698
|
-
const relativeHooksDir =
|
|
2699
|
-
const relativeApiDir =
|
|
2700
|
-
const relativeModelsDir =
|
|
2701
|
-
const relativePromptsDir =
|
|
2702
|
-
const relativeAgentsDir =
|
|
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
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
2833
|
-
export
|
|
2834
|
-
|
|
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
|
|
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(({
|
|
5586
|
+
const hooksCode = hooks.map(({ id: id2, importPath }) => {
|
|
2868
5587
|
const absPath = toAbsolutePath(importPath);
|
|
2869
|
-
return ` "${
|
|
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 ${
|
|
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((
|
|
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 =
|
|
6518
|
+
const currentDir = path8__default.dirname(fileURLToPath(import.meta.url));
|
|
3650
6519
|
const isInDist = currentDir.endsWith("dist");
|
|
3651
|
-
const clientDir =
|
|
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 =
|
|
6527
|
+
filePath = path8__default.join(clientDir, cleanUrl);
|
|
3659
6528
|
} else {
|
|
3660
|
-
filePath =
|
|
6529
|
+
filePath = path8__default.join(clientDir, "index.html");
|
|
3661
6530
|
}
|
|
3662
6531
|
try {
|
|
3663
|
-
if (
|
|
3664
|
-
let content =
|
|
3665
|
-
const ext =
|
|
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 ?
|
|
3704
|
-
const currentDir =
|
|
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 =
|
|
6575
|
+
const clientDir = path8__default.resolve(
|
|
3707
6576
|
currentDir,
|
|
3708
6577
|
isInDist ? "./client" : "../dist/client"
|
|
3709
6578
|
);
|
|
3710
|
-
if (!
|
|
6579
|
+
if (!fs2__default.existsSync(clientDir)) {
|
|
3711
6580
|
console.warn(`[agentbuilder] Client directory not found at ${clientDir}`);
|
|
3712
6581
|
return;
|
|
3713
6582
|
}
|
|
3714
|
-
|
|
6583
|
+
fs2__default.mkdirSync(mountDir, { recursive: true });
|
|
3715
6584
|
function copyRecursive(src, dest) {
|
|
3716
|
-
const entries =
|
|
6585
|
+
const entries = fs2__default.readdirSync(src, { withFileTypes: true });
|
|
3717
6586
|
for (const entry of entries) {
|
|
3718
|
-
const srcPath =
|
|
3719
|
-
const destPath =
|
|
6587
|
+
const srcPath = path8__default.join(src, entry.name);
|
|
6588
|
+
const destPath = path8__default.join(dest, entry.name);
|
|
3720
6589
|
if (entry.isDirectory()) {
|
|
3721
|
-
|
|
6590
|
+
fs2__default.mkdirSync(destPath, { recursive: true });
|
|
3722
6591
|
copyRecursive(srcPath, destPath);
|
|
3723
6592
|
} else {
|
|
3724
|
-
let content =
|
|
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
|
-
|
|
6602
|
+
fs2__default.writeFileSync(destPath, content);
|
|
3734
6603
|
}
|
|
3735
6604
|
}
|
|
3736
6605
|
}
|