@standardagents/builder 0.11.0-next.af971ae → 0.11.0
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 +21823 -317
- package/dist/built-in-routes.js.map +1 -1
- package/dist/client/assets/index.css +1 -1
- package/dist/client/index.js +26 -23
- package/dist/client/vendor.js +1 -1
- package/dist/client/vue.js +1 -1
- package/dist/index.d.ts +333 -44
- package/dist/index.js +3347 -1112
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +7 -0
- package/dist/plugin.js +375 -67
- package/dist/plugin.js.map +1 -1
- package/dist/sip.wasm +0 -0
- package/package.json +8 -6
- package/dist/client/assets/img/meta.svg +0 -19
- package/dist/client/assets/img/moonshotai.svg +0 -4
- package/dist/client/assets/img/zai.svg +0 -219
package/dist/plugin.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs2 from 'fs';
|
|
2
2
|
import path3 from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
|
-
import { createRequire } from 'module';
|
|
5
4
|
|
|
6
5
|
// src/plugin.ts
|
|
7
6
|
var TSCONFIG_CONTENT = `{
|
|
@@ -62,18 +61,18 @@ function generateTypesContent(config) {
|
|
|
62
61
|
return `// Auto-generated by @standardagents/builder - DO NOT EDIT
|
|
63
62
|
// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
64
63
|
//
|
|
65
|
-
// This file augments the
|
|
64
|
+
// This file augments the StandardAgentSpec namespace declared in @standardagents/spec
|
|
66
65
|
// to provide type-safe references for your models, prompts, agents, and tools.
|
|
67
66
|
|
|
68
67
|
/**
|
|
69
|
-
* Augment the global
|
|
68
|
+
* Augment the global StandardAgentSpec namespace with your project's specific types.
|
|
70
69
|
* This provides autocomplete and type checking for model, prompt, agent, and tool references.
|
|
71
70
|
*
|
|
72
71
|
* Uses interface declaration merging with property keys to create union types.
|
|
73
72
|
* Example: interface ModelRegistry { 'gpt-4o': true; } gives type Models = 'gpt-4o'
|
|
74
73
|
*/
|
|
75
74
|
declare global {
|
|
76
|
-
namespace
|
|
75
|
+
namespace StandardAgentSpec {
|
|
77
76
|
/** Model names from agents/models/ */
|
|
78
77
|
interface ModelRegistry {
|
|
79
78
|
${generateRegistryProperties(models)}
|
|
@@ -398,6 +397,9 @@ declare module 'virtual:@standardagents/builder' {
|
|
|
398
397
|
listThreads(params?: {
|
|
399
398
|
agent_name?: string;
|
|
400
399
|
user_id?: string;
|
|
400
|
+
search?: string;
|
|
401
|
+
startDate?: number;
|
|
402
|
+
endDate?: number;
|
|
401
403
|
limit?: number;
|
|
402
404
|
offset?: number;
|
|
403
405
|
}): Promise<{ threads: ThreadRegistryEntry[]; total: number }>;
|
|
@@ -579,14 +581,91 @@ function needsRegeneration(config) {
|
|
|
579
581
|
return false;
|
|
580
582
|
}
|
|
581
583
|
|
|
584
|
+
// src/utils/model-parser.ts
|
|
585
|
+
function getName(content) {
|
|
586
|
+
return content.match(/name:\s*['"]([^'"]+)['"]/)?.[1];
|
|
587
|
+
}
|
|
588
|
+
function getProvider(content) {
|
|
589
|
+
const stringMatch = content.match(/provider:\s*['"]([^'"]+)['"]/)?.[1];
|
|
590
|
+
if (stringMatch) return stringMatch;
|
|
591
|
+
const refMatch = content.match(/provider:\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*[,\n}]/)?.[1];
|
|
592
|
+
return refMatch || void 0;
|
|
593
|
+
}
|
|
594
|
+
function getModel(content) {
|
|
595
|
+
return content.match(/model:\s*['"]([^'"]+)['"]/)?.[1];
|
|
596
|
+
}
|
|
597
|
+
function getInputPrice(content) {
|
|
598
|
+
const match = content.match(/inputPrice:\s*([\d.]+)/);
|
|
599
|
+
return match ? parseFloat(match[1]) : void 0;
|
|
600
|
+
}
|
|
601
|
+
function getOutputPrice(content) {
|
|
602
|
+
const match = content.match(/outputPrice:\s*([\d.]+)/);
|
|
603
|
+
return match ? parseFloat(match[1]) : void 0;
|
|
604
|
+
}
|
|
605
|
+
function getCachedPrice(content) {
|
|
606
|
+
const match = content.match(/cachedPrice:\s*([\d.]+)/);
|
|
607
|
+
return match ? parseFloat(match[1]) : void 0;
|
|
608
|
+
}
|
|
609
|
+
function getIncludedProviders(content) {
|
|
610
|
+
const match = content.match(/includedProviders:\s*\[([^\]]*)\]/);
|
|
611
|
+
if (!match) return void 0;
|
|
612
|
+
const items = match[1].match(/['"]([^'"]+)['"]/g);
|
|
613
|
+
return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
|
|
614
|
+
}
|
|
615
|
+
function getFallbacks(content) {
|
|
616
|
+
const match = content.match(/fallbacks:\s*\[([^\]]*)\]/);
|
|
617
|
+
if (!match) return [];
|
|
618
|
+
const items = match[1].match(/['"]([^'"]+)['"]/g);
|
|
619
|
+
return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
|
|
620
|
+
}
|
|
621
|
+
function getProviderTools(content) {
|
|
622
|
+
const match = content.match(/providerTools:\s*\[([^\]]*)\]/);
|
|
623
|
+
if (!match) return [];
|
|
624
|
+
const items = match[1].match(/['"]([^'"]+)['"]/g);
|
|
625
|
+
return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
|
|
626
|
+
}
|
|
627
|
+
function getCapabilities(content) {
|
|
628
|
+
const match = content.match(/capabilities:\s*\{([^}]*)\}/s);
|
|
629
|
+
if (!match) return void 0;
|
|
630
|
+
const inner = match[1];
|
|
631
|
+
const caps = {};
|
|
632
|
+
const boolMatches = inner.matchAll(/(\w+):\s*(true|false)/g);
|
|
633
|
+
for (const m of boolMatches) {
|
|
634
|
+
caps[m[1]] = m[2] === "true";
|
|
635
|
+
}
|
|
636
|
+
const numMatches = inner.matchAll(/(\w+):\s*(\d+)/g);
|
|
637
|
+
for (const m of numMatches) {
|
|
638
|
+
caps[m[1]] = parseInt(m[2], 10);
|
|
639
|
+
}
|
|
640
|
+
return Object.keys(caps).length > 0 ? caps : void 0;
|
|
641
|
+
}
|
|
642
|
+
function parseModelFile(content) {
|
|
643
|
+
const name = getName(content);
|
|
644
|
+
if (!name) return null;
|
|
645
|
+
return {
|
|
646
|
+
name,
|
|
647
|
+
provider: getProvider(content),
|
|
648
|
+
model: getModel(content),
|
|
649
|
+
inputPrice: getInputPrice(content),
|
|
650
|
+
outputPrice: getOutputPrice(content),
|
|
651
|
+
cachedPrice: getCachedPrice(content),
|
|
652
|
+
includedProviders: getIncludedProviders(content),
|
|
653
|
+
fallbacks: getFallbacks(content),
|
|
654
|
+
providerTools: getProviderTools(content),
|
|
655
|
+
capabilities: getCapabilities(content)
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
582
659
|
// src/sdk/generators/generateModelFile.ts
|
|
583
|
-
function generateModelFile(data) {
|
|
660
|
+
function generateModelFile(data, options) {
|
|
661
|
+
const { providerName, providerPackage } = options;
|
|
584
662
|
const lines = [
|
|
585
|
-
`import { defineModel } from '@standardagents/
|
|
663
|
+
`import { defineModel } from '@standardagents/spec';`,
|
|
664
|
+
`import { ${providerName} } from '${providerPackage}';`,
|
|
586
665
|
"",
|
|
587
666
|
`export default defineModel({`,
|
|
588
667
|
` name: '${escapeString(data.name)}',`,
|
|
589
|
-
` provider:
|
|
668
|
+
` provider: ${providerName},`,
|
|
590
669
|
` model: '${escapeString(data.model)}',`
|
|
591
670
|
];
|
|
592
671
|
if (data.includedProviders && data.includedProviders.length > 0) {
|
|
@@ -595,6 +674,9 @@ function generateModelFile(data) {
|
|
|
595
674
|
if (data.fallbacks && data.fallbacks.length > 0) {
|
|
596
675
|
lines.push(` fallbacks: ${JSON.stringify(data.fallbacks)},`);
|
|
597
676
|
}
|
|
677
|
+
if (data.providerTools && data.providerTools.length > 0) {
|
|
678
|
+
lines.push(` providerTools: ${JSON.stringify(data.providerTools)},`);
|
|
679
|
+
}
|
|
598
680
|
if (data.inputPrice !== void 0) {
|
|
599
681
|
lines.push(` inputPrice: ${data.inputPrice},`);
|
|
600
682
|
}
|
|
@@ -604,6 +686,36 @@ function generateModelFile(data) {
|
|
|
604
686
|
if (data.cachedPrice !== void 0) {
|
|
605
687
|
lines.push(` cachedPrice: ${data.cachedPrice},`);
|
|
606
688
|
}
|
|
689
|
+
if (data.capabilities && Object.keys(data.capabilities).length > 0) {
|
|
690
|
+
const caps = data.capabilities;
|
|
691
|
+
const capLines = [];
|
|
692
|
+
if (caps.supportsImages !== void 0) {
|
|
693
|
+
capLines.push(` supportsImages: ${caps.supportsImages},`);
|
|
694
|
+
}
|
|
695
|
+
if (caps.supportsToolCalls !== void 0) {
|
|
696
|
+
capLines.push(` supportsToolCalls: ${caps.supportsToolCalls},`);
|
|
697
|
+
}
|
|
698
|
+
if (caps.supportsStreaming !== void 0) {
|
|
699
|
+
capLines.push(` supportsStreaming: ${caps.supportsStreaming},`);
|
|
700
|
+
}
|
|
701
|
+
if (caps.supportsJsonMode !== void 0) {
|
|
702
|
+
capLines.push(` supportsJsonMode: ${caps.supportsJsonMode},`);
|
|
703
|
+
}
|
|
704
|
+
if (caps.maxContextTokens !== void 0) {
|
|
705
|
+
capLines.push(` maxContextTokens: ${caps.maxContextTokens},`);
|
|
706
|
+
}
|
|
707
|
+
if (caps.maxOutputTokens !== void 0) {
|
|
708
|
+
capLines.push(` maxOutputTokens: ${caps.maxOutputTokens},`);
|
|
709
|
+
}
|
|
710
|
+
if (caps.reasoningLevels !== void 0) {
|
|
711
|
+
capLines.push(` reasoningLevels: ${JSON.stringify(caps.reasoningLevels)},`);
|
|
712
|
+
}
|
|
713
|
+
if (capLines.length > 0) {
|
|
714
|
+
lines.push(` capabilities: {`);
|
|
715
|
+
lines.push(...capLines);
|
|
716
|
+
lines.push(` },`);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
607
719
|
lines.push(`});`);
|
|
608
720
|
lines.push("");
|
|
609
721
|
return lines.join("\n");
|
|
@@ -782,7 +894,7 @@ function isEmptySchema(schema) {
|
|
|
782
894
|
// src/sdk/generators/generatePromptFile.ts
|
|
783
895
|
function generatePromptFile(data) {
|
|
784
896
|
const hasSchema = data.requiredSchema && !isEmptySchema(data.requiredSchema);
|
|
785
|
-
const lines = [`import { definePrompt } from '@standardagents/
|
|
897
|
+
const lines = [`import { definePrompt } from '@standardagents/spec';`];
|
|
786
898
|
if (hasSchema) {
|
|
787
899
|
lines.push(`import { z } from 'zod';`);
|
|
788
900
|
}
|
|
@@ -858,6 +970,12 @@ function formatToolConfig(config) {
|
|
|
858
970
|
if (config.init_user_message_property !== void 0 && config.init_user_message_property !== null) {
|
|
859
971
|
parts.push(`initUserMessageProperty: '${escapeString2(config.init_user_message_property)}'`);
|
|
860
972
|
}
|
|
973
|
+
if (config.init_attachments_property !== void 0 && config.init_attachments_property !== null) {
|
|
974
|
+
parts.push(`initAttachmentsProperty: '${escapeString2(config.init_attachments_property)}'`);
|
|
975
|
+
}
|
|
976
|
+
if (config.tenvs && Object.keys(config.tenvs).length > 0) {
|
|
977
|
+
parts.push(`tenvs: ${JSON.stringify(config.tenvs)}`);
|
|
978
|
+
}
|
|
861
979
|
return `{ ${parts.join(", ")} }`;
|
|
862
980
|
}
|
|
863
981
|
function formatReasoningConfig(reasoning) {
|
|
@@ -966,7 +1084,7 @@ function normalizePart(part) {
|
|
|
966
1084
|
// src/sdk/generators/generateAgentFile.ts
|
|
967
1085
|
function generateAgentFile(data) {
|
|
968
1086
|
const lines = [
|
|
969
|
-
`import { defineAgent } from '@standardagents/
|
|
1087
|
+
`import { defineAgent } from '@standardagents/spec';`,
|
|
970
1088
|
"",
|
|
971
1089
|
`export default defineAgent({`,
|
|
972
1090
|
` name: '${escapeString3(data.name)}',`
|
|
@@ -990,6 +1108,9 @@ function generateAgentFile(data) {
|
|
|
990
1108
|
if (data.toolDescription) {
|
|
991
1109
|
lines.push(` toolDescription: '${escapeString3(data.toolDescription)}',`);
|
|
992
1110
|
}
|
|
1111
|
+
if (data.tenvs && Object.keys(data.tenvs).length > 0) {
|
|
1112
|
+
lines.push(` tenvs: ${JSON.stringify(data.tenvs)},`);
|
|
1113
|
+
}
|
|
993
1114
|
lines.push(`});`);
|
|
994
1115
|
lines.push("");
|
|
995
1116
|
return lines.join("\n");
|
|
@@ -1026,8 +1147,15 @@ function escapeString3(str) {
|
|
|
1026
1147
|
}
|
|
1027
1148
|
|
|
1028
1149
|
// src/sdk/persistence/index.ts
|
|
1150
|
+
var PROVIDER_PACKAGE_MAP = {
|
|
1151
|
+
openai: { name: "openai", package: "@standardagents/openai" },
|
|
1152
|
+
openrouter: { name: "openrouter", package: "@standardagents/openrouter" },
|
|
1153
|
+
anthropic: { name: "anthropic", package: "@standardagents/anthropic" },
|
|
1154
|
+
google: { name: "google", package: "@standardagents/google" },
|
|
1155
|
+
test: { name: "test", package: "@standardagents/builder/test" }
|
|
1156
|
+
};
|
|
1029
1157
|
function nameToFilename(name) {
|
|
1030
|
-
return name.replace(/[/\\]/g, "__").replace(/[:*?"
|
|
1158
|
+
return name.replace(/[/\\]/g, "__").replace(/[:*?"<>|.]/g, "_").replace(/-/g, "_");
|
|
1031
1159
|
}
|
|
1032
1160
|
function getModelFilePath(modelsDir, name) {
|
|
1033
1161
|
const filename = nameToFilename(name);
|
|
@@ -1049,7 +1177,17 @@ async function saveModel(modelsDir, data, overwrite = false) {
|
|
|
1049
1177
|
error: `Model file already exists: ${filePath}. Use update to modify existing models.`
|
|
1050
1178
|
};
|
|
1051
1179
|
}
|
|
1052
|
-
const
|
|
1180
|
+
const providerInfo = PROVIDER_PACKAGE_MAP[data.provider];
|
|
1181
|
+
if (!providerInfo) {
|
|
1182
|
+
return {
|
|
1183
|
+
success: false,
|
|
1184
|
+
error: `Unknown provider '${data.provider}'. Must be one of: ${Object.keys(PROVIDER_PACKAGE_MAP).join(", ")}`
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
const content = generateModelFile(data, {
|
|
1188
|
+
providerName: providerInfo.name,
|
|
1189
|
+
providerPackage: providerInfo.package
|
|
1190
|
+
});
|
|
1053
1191
|
await fs2.promises.writeFile(filePath, content, "utf-8");
|
|
1054
1192
|
return {
|
|
1055
1193
|
success: true,
|
|
@@ -1113,7 +1251,7 @@ function validateModelData(data) {
|
|
|
1113
1251
|
if (!data.provider || typeof data.provider !== "string") {
|
|
1114
1252
|
return "Model provider is required and must be a string";
|
|
1115
1253
|
}
|
|
1116
|
-
const validProviders =
|
|
1254
|
+
const validProviders = Object.keys(PROVIDER_PACKAGE_MAP);
|
|
1117
1255
|
if (!validProviders.includes(data.provider)) {
|
|
1118
1256
|
return `Invalid provider '${data.provider}'. Must be one of: ${validProviders.join(", ")}`;
|
|
1119
1257
|
}
|
|
@@ -1617,7 +1755,6 @@ function validateAgentData(data) {
|
|
|
1617
1755
|
}
|
|
1618
1756
|
|
|
1619
1757
|
// src/plugin.ts
|
|
1620
|
-
createRequire(import.meta.url);
|
|
1621
1758
|
var VIRTUAL_TOOLS_ID = "virtual:@standardagents-tools";
|
|
1622
1759
|
var RESOLVED_VIRTUAL_TOOLS_ID = "\0" + VIRTUAL_TOOLS_ID;
|
|
1623
1760
|
var VIRTUAL_ROUTES_ID = "virtual:@standardagents-routes";
|
|
@@ -1636,6 +1773,10 @@ var VIRTUAL_PROMPTS_ID = "virtual:@standardagents-prompts";
|
|
|
1636
1773
|
var RESOLVED_VIRTUAL_PROMPTS_ID = "\0" + VIRTUAL_PROMPTS_ID;
|
|
1637
1774
|
var VIRTUAL_AGENTS_ID = "virtual:@standardagents-agents";
|
|
1638
1775
|
var RESOLVED_VIRTUAL_AGENTS_ID = "\0" + VIRTUAL_AGENTS_ID;
|
|
1776
|
+
var VIRTUAL_EFFECTS_ID = "virtual:@standardagents-effects";
|
|
1777
|
+
var RESOLVED_VIRTUAL_EFFECTS_ID = "\0" + VIRTUAL_EFFECTS_ID;
|
|
1778
|
+
var VIRTUAL_PROVIDERS_ID = "virtual:@standardagents-providers";
|
|
1779
|
+
var RESOLVED_VIRTUAL_PROVIDERS_ID = "\0" + VIRTUAL_PROVIDERS_ID;
|
|
1639
1780
|
var VIRTUAL_BUILDER_ID = "virtual:@standardagents/builder";
|
|
1640
1781
|
var RESOLVED_VIRTUAL_BUILDER_ID = "\0" + VIRTUAL_BUILDER_ID;
|
|
1641
1782
|
function scanApiDirectory(dir, baseRoute = "") {
|
|
@@ -1811,6 +1952,33 @@ async function scanPromptsDirectory(dir) {
|
|
|
1811
1952
|
async function scanAgentsDirectory(dir) {
|
|
1812
1953
|
return scanConfigDirectory(dir, /export\s+default\s+defineAgent/);
|
|
1813
1954
|
}
|
|
1955
|
+
async function scanEffectsDirectory(dir) {
|
|
1956
|
+
const effects = [];
|
|
1957
|
+
if (!fs2.existsSync(dir)) {
|
|
1958
|
+
return effects;
|
|
1959
|
+
}
|
|
1960
|
+
const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
|
|
1961
|
+
for (const entry of entries) {
|
|
1962
|
+
if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
1963
|
+
const fileName = entry.name.replace(".ts", "");
|
|
1964
|
+
const filePath = path3.join(dir, entry.name);
|
|
1965
|
+
const importPath = "./" + path3.relative(process.cwd(), filePath).replace(/\\/g, "/");
|
|
1966
|
+
if (fileName === "CLAUDE" || fileName.startsWith("_")) {
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
try {
|
|
1970
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
1971
|
+
if (!content.includes("defineEffect")) {
|
|
1972
|
+
continue;
|
|
1973
|
+
}
|
|
1974
|
+
} catch {
|
|
1975
|
+
continue;
|
|
1976
|
+
}
|
|
1977
|
+
effects.push({ name: fileName, importPath });
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
return effects;
|
|
1981
|
+
}
|
|
1814
1982
|
function parseRequestBody(req) {
|
|
1815
1983
|
return new Promise((resolve, reject) => {
|
|
1816
1984
|
let body = "";
|
|
@@ -1841,6 +2009,7 @@ function agentbuilder(options = {}) {
|
|
|
1841
2009
|
const modelsDir = options.modelsDir ? path3.resolve(process.cwd(), options.modelsDir) : path3.resolve(process.cwd(), "agents/models");
|
|
1842
2010
|
const promptsDir = options.promptsDir ? path3.resolve(process.cwd(), options.promptsDir) : path3.resolve(process.cwd(), "agents/prompts");
|
|
1843
2011
|
const agentsDir = options.agentsDir ? path3.resolve(process.cwd(), options.agentsDir) : path3.resolve(process.cwd(), "agents/agents");
|
|
2012
|
+
const effectsDir = options.effectsDir ? path3.resolve(process.cwd(), options.effectsDir) : path3.resolve(process.cwd(), "agents/effects");
|
|
1844
2013
|
const outputDir = path3.resolve(process.cwd(), ".agents");
|
|
1845
2014
|
const typeGenConfig = {
|
|
1846
2015
|
modelsDir,
|
|
@@ -2040,7 +2209,15 @@ function agentbuilder(options = {}) {
|
|
|
2040
2209
|
"zod",
|
|
2041
2210
|
"openai"
|
|
2042
2211
|
];
|
|
2212
|
+
const currentDir = path3.dirname(fileURLToPath(import.meta.url));
|
|
2213
|
+
const isInDist = currentDir.endsWith("dist");
|
|
2214
|
+
const builderClientDir = path3.resolve(
|
|
2215
|
+
currentDir,
|
|
2216
|
+
isInDist ? "./client" : "../dist/client"
|
|
2217
|
+
);
|
|
2043
2218
|
return {
|
|
2219
|
+
// Set publicDir to builder's client assets so Cloudflare plugin preserves assets config
|
|
2220
|
+
publicDir: fs2.existsSync(builderClientDir) ? builderClientDir : void 0,
|
|
2044
2221
|
optimizeDeps: {
|
|
2045
2222
|
// Exclude our packages from pre-bundling - they contain cloudflare:workers imports
|
|
2046
2223
|
// that cannot be resolved during dependency optimization
|
|
@@ -2076,6 +2253,9 @@ function agentbuilder(options = {}) {
|
|
|
2076
2253
|
];
|
|
2077
2254
|
const depsToInclude = [
|
|
2078
2255
|
"zod",
|
|
2256
|
+
"zod/v3",
|
|
2257
|
+
"zod/v4",
|
|
2258
|
+
"zod/v4/core",
|
|
2079
2259
|
"openai"
|
|
2080
2260
|
];
|
|
2081
2261
|
config.optimizeDeps = config.optimizeDeps || {};
|
|
@@ -2113,6 +2293,12 @@ function agentbuilder(options = {}) {
|
|
|
2113
2293
|
if (id === VIRTUAL_AGENTS_ID) {
|
|
2114
2294
|
return RESOLVED_VIRTUAL_AGENTS_ID;
|
|
2115
2295
|
}
|
|
2296
|
+
if (id === VIRTUAL_EFFECTS_ID) {
|
|
2297
|
+
return RESOLVED_VIRTUAL_EFFECTS_ID;
|
|
2298
|
+
}
|
|
2299
|
+
if (id === VIRTUAL_PROVIDERS_ID) {
|
|
2300
|
+
return RESOLVED_VIRTUAL_PROVIDERS_ID;
|
|
2301
|
+
}
|
|
2116
2302
|
if (id === VIRTUAL_BUILDER_ID) {
|
|
2117
2303
|
return RESOLVED_VIRTUAL_BUILDER_ID;
|
|
2118
2304
|
}
|
|
@@ -2188,9 +2374,56 @@ function isPublicRoute(routePath) {
|
|
|
2188
2374
|
return true;
|
|
2189
2375
|
}
|
|
2190
2376
|
|
|
2377
|
+
// Provider icon routes are public (used by <img src>)
|
|
2378
|
+
if (routePath.startsWith('/api/providers/') && routePath.includes('/icon')) {
|
|
2379
|
+
return true;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2191
2382
|
return false;
|
|
2192
2383
|
}
|
|
2193
2384
|
|
|
2385
|
+
// CORS headers for API responses
|
|
2386
|
+
const CORS_HEADERS = {
|
|
2387
|
+
"Access-Control-Allow-Origin": "*",
|
|
2388
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
2389
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
|
|
2390
|
+
"Access-Control-Max-Age": "86400",
|
|
2391
|
+
};
|
|
2392
|
+
|
|
2393
|
+
// Helper to create headers with CORS
|
|
2394
|
+
function corsHeaders(contentType) {
|
|
2395
|
+
return {
|
|
2396
|
+
"Content-Type": contentType,
|
|
2397
|
+
...CORS_HEADERS,
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
// Helper to add CORS headers to any Response without touching the body
|
|
2402
|
+
function addCorsHeaders(response) {
|
|
2403
|
+
// Skip WebSocket upgrade responses - they can't be wrapped
|
|
2404
|
+
if (response.status === 101) {
|
|
2405
|
+
return response;
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
// Skip if already has CORS headers
|
|
2409
|
+
if (response.headers.has("Access-Control-Allow-Origin")) {
|
|
2410
|
+
return response;
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
// Create new headers with CORS added
|
|
2414
|
+
const newHeaders = new Headers(response.headers);
|
|
2415
|
+
for (const [key, value] of Object.entries(CORS_HEADERS)) {
|
|
2416
|
+
newHeaders.set(key, value);
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
// Return new Response with same body stream (not cloned, just transferred)
|
|
2420
|
+
return new Response(response.body, {
|
|
2421
|
+
status: response.status,
|
|
2422
|
+
statusText: response.statusText,
|
|
2423
|
+
headers: newHeaders,
|
|
2424
|
+
});
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2194
2427
|
export async function router(request, env) {
|
|
2195
2428
|
const url = new URL(request.url);
|
|
2196
2429
|
const pathname = url.pathname;
|
|
@@ -2200,6 +2433,14 @@ export async function router(request, env) {
|
|
|
2200
2433
|
return null;
|
|
2201
2434
|
}
|
|
2202
2435
|
|
|
2436
|
+
// Handle CORS preflight requests
|
|
2437
|
+
if (request.method === "OPTIONS") {
|
|
2438
|
+
return new Response(null, {
|
|
2439
|
+
status: 204,
|
|
2440
|
+
headers: CORS_HEADERS,
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2203
2444
|
// Strip mount point prefix for route matching, ensuring we keep the leading slash
|
|
2204
2445
|
let routePath = pathname.slice(MOUNT_POINT.length) || "/";
|
|
2205
2446
|
if (!routePath.startsWith('/')) {
|
|
@@ -2234,7 +2475,7 @@ ${threadRouteCode}
|
|
|
2234
2475
|
|
|
2235
2476
|
// If requireAuth returns a Response, it's an error (401)
|
|
2236
2477
|
if (authResult instanceof Response) {
|
|
2237
|
-
return authResult;
|
|
2478
|
+
return addCorsHeaders(authResult);
|
|
2238
2479
|
}
|
|
2239
2480
|
|
|
2240
2481
|
authContext = authResult;
|
|
@@ -2253,16 +2494,17 @@ ${threadRouteCode}
|
|
|
2253
2494
|
const result = await controller(context);
|
|
2254
2495
|
|
|
2255
2496
|
if (result instanceof Response) {
|
|
2256
|
-
return result;
|
|
2497
|
+
return addCorsHeaders(result);
|
|
2257
2498
|
}
|
|
2258
2499
|
if (typeof result === "string") {
|
|
2259
2500
|
return new Response(result, {
|
|
2260
|
-
headers:
|
|
2261
|
-
"Content-Type": "text/plain",
|
|
2262
|
-
},
|
|
2501
|
+
headers: corsHeaders("text/plain"),
|
|
2263
2502
|
});
|
|
2264
2503
|
}
|
|
2265
|
-
|
|
2504
|
+
// JSON responses get CORS headers
|
|
2505
|
+
return new Response(JSON.stringify(result), {
|
|
2506
|
+
headers: corsHeaders("application/json"),
|
|
2507
|
+
});
|
|
2266
2508
|
}
|
|
2267
2509
|
|
|
2268
2510
|
// Serve UI for all other routes (SPA fallback)
|
|
@@ -2277,13 +2519,15 @@ async function serveUI(pathname, env) {
|
|
|
2277
2519
|
// Create a proper request for the asset path
|
|
2278
2520
|
// Use a dummy origin since we only care about the path
|
|
2279
2521
|
// Re-add mount point since pathname was stripped by router
|
|
2280
|
-
|
|
2522
|
+
// Handle root mountPoint "/" specially to avoid double slashes
|
|
2523
|
+
const mountPrefix = MOUNT_POINT === "/" ? "" : MOUNT_POINT;
|
|
2524
|
+
const assetUrl = \`http://localhost\${mountPrefix}\${pathname}\`;
|
|
2281
2525
|
let response = await env.ASSETS.fetch(assetUrl);
|
|
2282
2526
|
|
|
2283
2527
|
// If not found, fall back to index.html for SPA routing
|
|
2284
2528
|
const isIndexHtml = response.status === 404 || pathname === "/" || !pathname.includes(".");
|
|
2285
2529
|
if (isIndexHtml) {
|
|
2286
|
-
response = await env.ASSETS.fetch(\`http://localhost\${
|
|
2530
|
+
response = await env.ASSETS.fetch(\`http://localhost\${mountPrefix}/index.html\`);
|
|
2287
2531
|
|
|
2288
2532
|
// Transform HTML to use configured mount point
|
|
2289
2533
|
if (response.status === 200) {
|
|
@@ -2430,6 +2674,55 @@ ${agentsCode}
|
|
|
2430
2674
|
};
|
|
2431
2675
|
|
|
2432
2676
|
export const agentNames = ${JSON.stringify(agents.filter((a) => !a.error).map((a) => a.name))};
|
|
2677
|
+
`;
|
|
2678
|
+
}
|
|
2679
|
+
if (id === RESOLVED_VIRTUAL_EFFECTS_ID) {
|
|
2680
|
+
const effects = await scanEffectsDirectory(effectsDir);
|
|
2681
|
+
const effectsCode = effects.map(({ name, importPath, error }) => {
|
|
2682
|
+
if (error) {
|
|
2683
|
+
const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
2684
|
+
return ` "${name}": async () => { throw new Error("${escapedError}"); },`;
|
|
2685
|
+
} else {
|
|
2686
|
+
return ` "${name}": async () => {
|
|
2687
|
+
try {
|
|
2688
|
+
return (await import("${importPath}")).default;
|
|
2689
|
+
} catch (error) {
|
|
2690
|
+
console.error('Failed to import effect ${name}:', error);
|
|
2691
|
+
throw error;
|
|
2692
|
+
}
|
|
2693
|
+
},`;
|
|
2694
|
+
}
|
|
2695
|
+
}).join("\n");
|
|
2696
|
+
return `// Virtual agent effects module
|
|
2697
|
+
export const effects = {
|
|
2698
|
+
${effectsCode}
|
|
2699
|
+
};
|
|
2700
|
+
|
|
2701
|
+
export const effectNames = ${JSON.stringify(effects.filter((e) => !e.error).map((e) => e.name))};
|
|
2702
|
+
`;
|
|
2703
|
+
}
|
|
2704
|
+
if (id === RESOLVED_VIRTUAL_PROVIDERS_ID) {
|
|
2705
|
+
const firstPartyProviders = [
|
|
2706
|
+
{ name: "openai", package: "@standardagents/openai", label: "OpenAI", envKey: "OPENAI_API_KEY" },
|
|
2707
|
+
{ name: "openrouter", package: "@standardagents/openrouter", label: "OpenRouter", envKey: "OPENROUTER_API_KEY" }
|
|
2708
|
+
];
|
|
2709
|
+
const customProviders = (options.providers || []).map((pkg) => ({
|
|
2710
|
+
name: pkg.split("/").pop() || pkg,
|
|
2711
|
+
package: pkg,
|
|
2712
|
+
label: pkg.split("/").pop() || pkg,
|
|
2713
|
+
envKey: `${(pkg.split("/").pop() || pkg).toUpperCase().replace(/-/g, "_")}_API_KEY`,
|
|
2714
|
+
isCustom: true
|
|
2715
|
+
}));
|
|
2716
|
+
const allProviders = [...firstPartyProviders, ...customProviders];
|
|
2717
|
+
return `// Virtual providers module - lists available LLM provider packages
|
|
2718
|
+
export const providers = ${JSON.stringify(allProviders, null, 2)};
|
|
2719
|
+
|
|
2720
|
+
export const providerNames = ${JSON.stringify(allProviders.map((p) => p.name))};
|
|
2721
|
+
|
|
2722
|
+
// Provider factories - dynamic imports for code splitting
|
|
2723
|
+
export const providerFactories = {
|
|
2724
|
+
${allProviders.map((p) => ` "${p.name}": async () => (await import("${p.package}")).${p.name},`).join("\n")}
|
|
2725
|
+
};
|
|
2433
2726
|
`;
|
|
2434
2727
|
}
|
|
2435
2728
|
if (id === RESOLVED_VIRTUAL_BUILDER_ID) {
|
|
@@ -2438,6 +2731,7 @@ export const agentNames = ${JSON.stringify(agents.filter((a) => !a.error).map((a
|
|
|
2438
2731
|
const models = await scanModelsDirectory(modelsDir);
|
|
2439
2732
|
const prompts = await scanPromptsDirectory(promptsDir);
|
|
2440
2733
|
const agents = await scanAgentsDirectory(agentsDir);
|
|
2734
|
+
const effects = await scanEffectsDirectory(effectsDir);
|
|
2441
2735
|
const toAbsolutePath = (relativePath) => {
|
|
2442
2736
|
if (relativePath.startsWith("./")) {
|
|
2443
2737
|
return path3.resolve(process.cwd(), relativePath).replace(/\\/g, "/");
|
|
@@ -2516,6 +2810,22 @@ export const agentNames = ${JSON.stringify(agents.filter((a) => !a.error).map((a
|
|
|
2516
2810
|
console.error('Failed to import agent ${name}:', error);
|
|
2517
2811
|
throw error;
|
|
2518
2812
|
}
|
|
2813
|
+
},`;
|
|
2814
|
+
}
|
|
2815
|
+
}).join("\n");
|
|
2816
|
+
const effectsCode = effects.map(({ name, importPath, error }) => {
|
|
2817
|
+
const absPath = toAbsolutePath(importPath);
|
|
2818
|
+
if (error) {
|
|
2819
|
+
const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
2820
|
+
return ` "${name}": async () => { throw new Error("${escapedError}"); },`;
|
|
2821
|
+
} else {
|
|
2822
|
+
return ` "${name}": async () => {
|
|
2823
|
+
try {
|
|
2824
|
+
return (await import("${absPath}")).default;
|
|
2825
|
+
} catch (error) {
|
|
2826
|
+
console.error('Failed to import effect ${name}:', error);
|
|
2827
|
+
throw error;
|
|
2828
|
+
}
|
|
2519
2829
|
},`;
|
|
2520
2830
|
}
|
|
2521
2831
|
}).join("\n");
|
|
@@ -2526,7 +2836,8 @@ import { DurableAgentBuilder as _BaseDurableAgentBuilder } from '@standardagents
|
|
|
2526
2836
|
|
|
2527
2837
|
// Import sip WASM module and initializer
|
|
2528
2838
|
// Static import allows workerd to pre-compile the WASM at bundle time
|
|
2529
|
-
|
|
2839
|
+
// WASM is bundled in builder's dist to avoid transitive dependency resolution issues
|
|
2840
|
+
import _sipWasm from '@standardagents/builder/dist/sip.wasm';
|
|
2530
2841
|
import { initWithWasmModule as _initSipWasm } from '@standardagents/sip';
|
|
2531
2842
|
|
|
2532
2843
|
// Re-export router from virtual:@standardagents-routes
|
|
@@ -2553,6 +2864,10 @@ const _agents = {
|
|
|
2553
2864
|
${agentsCode}
|
|
2554
2865
|
};
|
|
2555
2866
|
|
|
2867
|
+
const _effects = {
|
|
2868
|
+
${effectsCode}
|
|
2869
|
+
};
|
|
2870
|
+
|
|
2556
2871
|
/**
|
|
2557
2872
|
* DurableThread with all virtual module methods already implemented.
|
|
2558
2873
|
* Simply extend this class in your agents/Thread.ts file.
|
|
@@ -2587,6 +2902,10 @@ export class DurableThread extends _BaseDurableThread {
|
|
|
2587
2902
|
agents() {
|
|
2588
2903
|
return _agents;
|
|
2589
2904
|
}
|
|
2905
|
+
|
|
2906
|
+
effects() {
|
|
2907
|
+
return _effects;
|
|
2908
|
+
}
|
|
2590
2909
|
}
|
|
2591
2910
|
|
|
2592
2911
|
/**
|
|
@@ -2613,6 +2932,10 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
|
|
|
2613
2932
|
agents() {
|
|
2614
2933
|
return _agents;
|
|
2615
2934
|
}
|
|
2935
|
+
|
|
2936
|
+
effects() {
|
|
2937
|
+
return _effects;
|
|
2938
|
+
}
|
|
2616
2939
|
}
|
|
2617
2940
|
`;
|
|
2618
2941
|
}
|
|
@@ -2625,6 +2948,7 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
|
|
|
2625
2948
|
this.addWatchFile(modelsDir);
|
|
2626
2949
|
this.addWatchFile(promptsDir);
|
|
2627
2950
|
this.addWatchFile(agentsDir);
|
|
2951
|
+
this.addWatchFile(effectsDir);
|
|
2628
2952
|
},
|
|
2629
2953
|
configureServer(server) {
|
|
2630
2954
|
server.watcher.on("add", async (file) => {
|
|
@@ -2652,51 +2976,26 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
|
|
|
2652
2976
|
try {
|
|
2653
2977
|
const filePath = path3.join(modelsDir, file);
|
|
2654
2978
|
const content = fs4.readFileSync(filePath, "utf-8");
|
|
2655
|
-
const
|
|
2656
|
-
|
|
2657
|
-
const
|
|
2658
|
-
const getInputPrice = (c) => {
|
|
2659
|
-
const match = c.match(/inputPrice:\s*([\d.]+)/);
|
|
2660
|
-
return match ? parseFloat(match[1]) : void 0;
|
|
2661
|
-
};
|
|
2662
|
-
const getOutputPrice = (c) => {
|
|
2663
|
-
const match = c.match(/outputPrice:\s*([\d.]+)/);
|
|
2664
|
-
return match ? parseFloat(match[1]) : void 0;
|
|
2665
|
-
};
|
|
2666
|
-
const getCachedPrice = (c) => {
|
|
2667
|
-
const match = c.match(/cachedPrice:\s*([\d.]+)/);
|
|
2668
|
-
return match ? parseFloat(match[1]) : void 0;
|
|
2669
|
-
};
|
|
2670
|
-
const getIncludedProviders = (c) => {
|
|
2671
|
-
const match = c.match(/includedProviders:\s*\[([^\]]*)\]/);
|
|
2672
|
-
if (!match) return void 0;
|
|
2673
|
-
const items = match[1].match(/['"]([^'"]+)['"]/g);
|
|
2674
|
-
return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
|
|
2675
|
-
};
|
|
2676
|
-
const getFallbacks = (c) => {
|
|
2677
|
-
const match = c.match(/fallbacks:\s*\[([^\]]*)\]/);
|
|
2678
|
-
if (!match) return [];
|
|
2679
|
-
const items = match[1].match(/['"]([^'"]+)['"]/g);
|
|
2680
|
-
return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
|
|
2681
|
-
};
|
|
2682
|
-
const name = getName(content);
|
|
2683
|
-
if (!name) return null;
|
|
2684
|
-
const fallbacks = getFallbacks(content);
|
|
2685
|
-
const fallbackObjects = fallbacks.map((fallbackName, index) => ({
|
|
2979
|
+
const parsed = parseModelFile(content);
|
|
2980
|
+
if (!parsed || !parsed.name) return null;
|
|
2981
|
+
const fallbackObjects = parsed.fallbacks.map((fallbackName, index) => ({
|
|
2686
2982
|
id: fallbackName,
|
|
2687
2983
|
name: fallbackName,
|
|
2688
2984
|
order: index
|
|
2689
2985
|
}));
|
|
2690
2986
|
return {
|
|
2691
|
-
id: name,
|
|
2692
|
-
name,
|
|
2693
|
-
provider:
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2987
|
+
id: parsed.name,
|
|
2988
|
+
name: parsed.name,
|
|
2989
|
+
provider: parsed.provider,
|
|
2990
|
+
provider_id: parsed.provider,
|
|
2991
|
+
model: parsed.model,
|
|
2992
|
+
input_price: parsed.inputPrice,
|
|
2993
|
+
output_price: parsed.outputPrice,
|
|
2994
|
+
cached_price: parsed.cachedPrice,
|
|
2995
|
+
included_providers: parsed.includedProviders,
|
|
2699
2996
|
fallbacks: fallbackObjects,
|
|
2997
|
+
providerTools: parsed.providerTools,
|
|
2998
|
+
capabilities: parsed.capabilities,
|
|
2700
2999
|
created_at: Math.floor(Date.now() / 1e3)
|
|
2701
3000
|
};
|
|
2702
3001
|
} catch (error) {
|
|
@@ -2862,9 +3161,9 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
|
|
|
2862
3161
|
try {
|
|
2863
3162
|
const filePath = path3.join(promptsDir, file);
|
|
2864
3163
|
const content = fs4.readFileSync(filePath, "utf-8");
|
|
2865
|
-
const
|
|
3164
|
+
const getName2 = (c) => c.match(/name:\s*['"]([^'"]+)['"]/)?.[1];
|
|
2866
3165
|
const getToolDescription = (c) => c.match(/toolDescription:\s*['"]([^'"]+)['"]/)?.[1];
|
|
2867
|
-
const
|
|
3166
|
+
const getModel2 = (c) => c.match(/model:\s*['"]([^'"]+)['"]/)?.[1];
|
|
2868
3167
|
const getIncludeChat = (c) => c.match(/includeChat:\s*(true|false)/)?.[1] === "true";
|
|
2869
3168
|
const getIncludePastTools = (c) => c.match(/includePastTools:\s*(true|false)/)?.[1] === "true";
|
|
2870
3169
|
const getParallelToolCalls = (c) => c.match(/parallelToolCalls:\s*(true|false)/)?.[1] === "true";
|
|
@@ -2884,9 +3183,9 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
|
|
|
2884
3183
|
if (arrayMatch) return arrayMatch[1];
|
|
2885
3184
|
return "";
|
|
2886
3185
|
};
|
|
2887
|
-
const name =
|
|
3186
|
+
const name = getName2(content);
|
|
2888
3187
|
if (!name) return null;
|
|
2889
|
-
const modelId =
|
|
3188
|
+
const modelId = getModel2(content);
|
|
2890
3189
|
const modelDef = modelId ? modelMap[modelId] : null;
|
|
2891
3190
|
return {
|
|
2892
3191
|
id: name,
|
|
@@ -3057,7 +3356,7 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
|
|
|
3057
3356
|
try {
|
|
3058
3357
|
const filePath = path3.join(agentsDir, file);
|
|
3059
3358
|
const content = fs4.readFileSync(filePath, "utf-8");
|
|
3060
|
-
const
|
|
3359
|
+
const getName2 = (c) => c.match(/name:\s*['"]([^'"]+)['"]/)?.[1];
|
|
3061
3360
|
const getTitle = (c) => c.match(/title:\s*['"]([^'"]+)['"]/)?.[1];
|
|
3062
3361
|
const getType = (c) => c.match(/type:\s*['"]([^'"]+)['"]/)?.[1];
|
|
3063
3362
|
const getDefaultPrompt = (c) => c.match(/defaultPrompt:\s*['"]([^'"]+)['"]/)?.[1];
|
|
@@ -3068,7 +3367,14 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
|
|
|
3068
3367
|
const items = match[1].match(/['"]([^'"]+)['"]/g);
|
|
3069
3368
|
return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
|
|
3070
3369
|
};
|
|
3071
|
-
const
|
|
3370
|
+
const getSidePrompt = (c, side) => {
|
|
3371
|
+
const sideRegex = new RegExp(`${side}:\\s*\\{([^}]+)\\}`, "s");
|
|
3372
|
+
const sideMatch = c.match(sideRegex);
|
|
3373
|
+
if (!sideMatch) return null;
|
|
3374
|
+
const promptMatch = sideMatch[1].match(/prompt:\s*['"]([^'"]+)['"]/);
|
|
3375
|
+
return promptMatch ? promptMatch[1] : null;
|
|
3376
|
+
};
|
|
3377
|
+
const name = getName2(content);
|
|
3072
3378
|
if (!name) return null;
|
|
3073
3379
|
return {
|
|
3074
3380
|
id: name,
|
|
@@ -3078,6 +3384,8 @@ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
|
|
|
3078
3384
|
default_prompt: getDefaultPrompt(content) || "",
|
|
3079
3385
|
default_model: getDefaultModel(content) || "",
|
|
3080
3386
|
tools: getTools(content),
|
|
3387
|
+
side_a_agent_prompt: getSidePrompt(content, "sideA"),
|
|
3388
|
+
side_b_agent_prompt: getSidePrompt(content, "sideB"),
|
|
3081
3389
|
created_at: Math.floor(Date.now() / 1e3)
|
|
3082
3390
|
};
|
|
3083
3391
|
} catch (error) {
|