@standardagents/cli 0.13.1 → 0.14.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/README.md +53 -1
- package/chat/vite/vite.config.ts +3 -0
- package/dist/index.js +818 -44
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -10,7 +10,9 @@ import { parse, modify, applyEdits } from 'jsonc-parser';
|
|
|
10
10
|
import { loadFile, writeFile, generateCode } from 'magicast';
|
|
11
11
|
import { addVitePlugin } from 'magicast/helpers';
|
|
12
12
|
import net from 'net';
|
|
13
|
-
import { fileURLToPath } from 'url';
|
|
13
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
14
|
+
import os from 'os';
|
|
15
|
+
import { copySkill, readSkillFile } from '@standardagents/skill';
|
|
14
16
|
|
|
15
17
|
var logger = {
|
|
16
18
|
success: (message) => {
|
|
@@ -227,7 +229,7 @@ Models define which LLM provider and model to use for prompts.
|
|
|
227
229
|
| Property | Type | Required | Description |
|
|
228
230
|
|----------|------|----------|-------------|
|
|
229
231
|
| \`name\` | \`string\` | Yes | Unique identifier for this model configuration |
|
|
230
|
-
| \`provider\` | \`'openai' \\| 'anthropic' \\| 'openrouter' \\| 'google' \\| 'cerebras'\` | Yes | LLM provider |
|
|
232
|
+
| \`provider\` | \`'openai' \\| 'anthropic' \\| 'openrouter' \\| 'google' \\| 'cerebras' \\| 'groq' \\| 'xai'\` | Yes | LLM provider |
|
|
231
233
|
| \`model\` | \`string\` | Yes | Model ID sent to provider API |
|
|
232
234
|
| \`fallbacks\` | \`string[]\` | No | Fallback model names to try if primary fails |
|
|
233
235
|
| \`inputPrice\` | \`number\` | No | Cost per 1M input tokens (USD) |
|
|
@@ -266,10 +268,12 @@ Set these environment variables in \`.dev.vars\` (local) or Cloudflare secrets (
|
|
|
266
268
|
| Provider | Environment Variable |
|
|
267
269
|
|----------|---------------------|
|
|
268
270
|
| Cerebras | \`CEREBRAS_API_KEY\` |
|
|
271
|
+
| Groq | \`GROQ_API_KEY\` |
|
|
269
272
|
| OpenAI | \`OPENAI_API_KEY\` |
|
|
270
273
|
| Anthropic | \`ANTHROPIC_API_KEY\` |
|
|
271
274
|
| OpenRouter | \`OPENROUTER_API_KEY\` |
|
|
272
275
|
| Google | \`GOOGLE_API_KEY\` |
|
|
276
|
+
| xAI | \`XAI_API_KEY\` |
|
|
273
277
|
|
|
274
278
|
## Fallback Strategy
|
|
275
279
|
|
|
@@ -298,7 +302,8 @@ export default defineModel({
|
|
|
298
302
|
|
|
299
303
|
- **Name by use case**, not model ID (e.g., \`fast\`, \`default\`, \`heavy\`)
|
|
300
304
|
- **Configure fallbacks** for production reliability
|
|
301
|
-
- **Set pricing** for cost tracking in logs
|
|
305
|
+
- **Set pricing** for cost tracking in logs when your provider or runtime does not supply it automatically
|
|
306
|
+
- **Cerebras note:** documented Cerebras models have built-in fallback pricing for request logs, but custom/private Cerebras models should still set \`inputPrice\` and \`outputPrice\`
|
|
302
307
|
- **Use environment variables** for API keys, never hardcode
|
|
303
308
|
|
|
304
309
|
## Documentation
|
|
@@ -529,10 +534,10 @@ Agents orchestrate conversations by defining prompts, stop conditions, and turn
|
|
|
529
534
|
| \`stopOnResponse\` | \`boolean\` | No | Stop turn when LLM returns text (default: true) |
|
|
530
535
|
| \`stopTool\` | \`string\` | No | Tool that stops this side's turn |
|
|
531
536
|
| \`stopToolResponseProperty\` | \`string\` | No | Property to extract from stop tool result |
|
|
532
|
-
| \`
|
|
533
|
-
| \`
|
|
534
|
-
| \`
|
|
535
|
-
| \`
|
|
537
|
+
| \`maxSteps\` | \`number\` | No | Maximum step budget for this side |
|
|
538
|
+
| \`sessionStop\` | \`string \\| SessionToolBinding\` | No | Terminal success lifecycle binding |
|
|
539
|
+
| \`sessionFail\` | \`string \\| SessionToolBinding\` | No | Terminal failure lifecycle binding |
|
|
540
|
+
| \`sessionStatus\` | \`string \\| SessionToolBinding\` | No | Non-terminal status lifecycle binding |
|
|
536
541
|
| \`manualStopCondition\` | \`boolean\` | No | Enable custom stop handling via hooks |
|
|
537
542
|
|
|
538
543
|
## Basic Example (AI-Human)
|
|
@@ -547,8 +552,8 @@ export default defineAgent({
|
|
|
547
552
|
label: 'Support',
|
|
548
553
|
prompt: 'customer_support',
|
|
549
554
|
stopOnResponse: true,
|
|
550
|
-
|
|
551
|
-
|
|
555
|
+
sessionStop: 'close_ticket',
|
|
556
|
+
maxSteps: 50,
|
|
552
557
|
},
|
|
553
558
|
tags: ['support', 'tier-1'],
|
|
554
559
|
});
|
|
@@ -592,9 +597,9 @@ export default defineAgent({
|
|
|
592
597
|
sideA: {
|
|
593
598
|
label: 'Worker',
|
|
594
599
|
prompt: 'research_worker',
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
600
|
+
sessionStatus: 'set_subagent_status',
|
|
601
|
+
sessionFail: 'fail_research_subagent',
|
|
602
|
+
sessionStop: 'finish_subagent',
|
|
598
603
|
},
|
|
599
604
|
sideB: {
|
|
600
605
|
label: 'Reviewer',
|
|
@@ -624,10 +629,10 @@ Other prompts can then include the agent name in their \`tools\` array to enable
|
|
|
624
629
|
|
|
625
630
|
## Stop Conditions (Priority Order)
|
|
626
631
|
|
|
627
|
-
1. **
|
|
628
|
-
2. **
|
|
632
|
+
1. **sessionFail** - Ends entire conversation with a terminal failure
|
|
633
|
+
2. **sessionStop** - Ends entire conversation successfully
|
|
629
634
|
3. **stopTool** - Ends current side's turn
|
|
630
|
-
4. **
|
|
635
|
+
4. **maxSteps** - Ends side execution when the safety budget is reached
|
|
631
636
|
5. **stopOnResponse** - Ends turn when LLM returns text
|
|
632
637
|
6. **maxSessionTurns** - Ends conversation (hard limit: 250)
|
|
633
638
|
|
|
@@ -657,7 +662,7 @@ This convention is enforced by the builder UI and makes agents easily identifiab
|
|
|
657
662
|
|
|
658
663
|
- **Use descriptive names** (\`customer_support_agent\` not \`agent1\`)
|
|
659
664
|
- **Always use the _agent suffix** - names like \`support_agent\`, \`research_agent\`
|
|
660
|
-
- **Always set
|
|
665
|
+
- **Always set maxSteps or maxSessionTurns** as safety limits
|
|
661
666
|
- **Match stop conditions to use case** - chat apps use stopOnResponse, workflows use stopTool
|
|
662
667
|
- **Use labels** for clarity in logs and UI
|
|
663
668
|
- **Organize with tags** for filtering
|
|
@@ -1273,13 +1278,15 @@ Full reference: https://docs.standardagentbuilder.com/core-concepts/api
|
|
|
1273
1278
|
`;
|
|
1274
1279
|
|
|
1275
1280
|
// src/commands/scaffold.ts
|
|
1281
|
+
function getScaffoldCompatibilityDate() {
|
|
1282
|
+
return "2025-08-13";
|
|
1283
|
+
}
|
|
1276
1284
|
var WRANGLER_TEMPLATE = (name) => {
|
|
1277
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1278
1285
|
return `{
|
|
1279
1286
|
"$schema": "node_modules/wrangler/config-schema.json",
|
|
1280
1287
|
"name": "${name}",
|
|
1281
1288
|
"main": "worker/index.ts",
|
|
1282
|
-
"compatibility_date": "${
|
|
1289
|
+
"compatibility_date": "${getScaffoldCompatibilityDate()}",
|
|
1283
1290
|
"compatibility_flags": ["nodejs_compat"],
|
|
1284
1291
|
"observability": {
|
|
1285
1292
|
"enabled": true
|
|
@@ -1802,6 +1809,33 @@ function runCommand(command, args, cwd, env) {
|
|
|
1802
1809
|
function toKebabCase(str) {
|
|
1803
1810
|
return str.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1804
1811
|
}
|
|
1812
|
+
function findWorkspaceRoot(startDir) {
|
|
1813
|
+
let currentDir = startDir;
|
|
1814
|
+
while (true) {
|
|
1815
|
+
if (fs3.existsSync(path3.join(currentDir, "pnpm-workspace.yaml"))) {
|
|
1816
|
+
return currentDir;
|
|
1817
|
+
}
|
|
1818
|
+
const parentDir = path3.dirname(currentDir);
|
|
1819
|
+
if (parentDir === currentDir) {
|
|
1820
|
+
return null;
|
|
1821
|
+
}
|
|
1822
|
+
currentDir = parentDir;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
function getPnpmWorkspaceFilterTarget(projectPath) {
|
|
1826
|
+
if (process.env.STANDARDAGENTS_WORKSPACE !== "1") {
|
|
1827
|
+
return null;
|
|
1828
|
+
}
|
|
1829
|
+
const workspaceRoot = findWorkspaceRoot(projectPath);
|
|
1830
|
+
if (!workspaceRoot) {
|
|
1831
|
+
return null;
|
|
1832
|
+
}
|
|
1833
|
+
const relativeProjectPath = path3.relative(workspaceRoot, projectPath).split(path3.sep).join("/");
|
|
1834
|
+
return {
|
|
1835
|
+
cwd: workspaceRoot,
|
|
1836
|
+
filterTarget: relativeProjectPath.startsWith(".") ? relativeProjectPath : `./${relativeProjectPath}`
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1805
1839
|
function detectPackageManager() {
|
|
1806
1840
|
const userAgent = process.env.npm_config_user_agent;
|
|
1807
1841
|
if (userAgent) {
|
|
@@ -1815,11 +1849,61 @@ function detectPackageManager() {
|
|
|
1815
1849
|
if (fs3.existsSync("package-lock.json")) return "npm";
|
|
1816
1850
|
return "npm";
|
|
1817
1851
|
}
|
|
1818
|
-
function
|
|
1852
|
+
function normalizeScaffoldedPackageJson(packageJson, projectName, versions) {
|
|
1853
|
+
const normalized = { ...packageJson };
|
|
1854
|
+
normalized.name = toKebabCase(projectName);
|
|
1855
|
+
normalized.devDependencies = {
|
|
1856
|
+
...normalized.devDependencies || {},
|
|
1857
|
+
vite: versions.vite,
|
|
1858
|
+
typescript: versions.typescript,
|
|
1859
|
+
"@cloudflare/vite-plugin": versions.cloudflareVitePlugin,
|
|
1860
|
+
wrangler: versions.wrangler
|
|
1861
|
+
};
|
|
1862
|
+
return normalized;
|
|
1863
|
+
}
|
|
1864
|
+
function applyPackageSpecifiers(packageJson, key, specifiers) {
|
|
1865
|
+
packageJson[key] = {
|
|
1866
|
+
...packageJson[key] || {}
|
|
1867
|
+
};
|
|
1868
|
+
for (const specifier of specifiers) {
|
|
1869
|
+
const atIndex = specifier.lastIndexOf("@");
|
|
1870
|
+
if (atIndex <= 0) {
|
|
1871
|
+
packageJson[key][specifier] = "^4.0.0";
|
|
1872
|
+
continue;
|
|
1873
|
+
}
|
|
1874
|
+
const name = specifier.slice(0, atIndex);
|
|
1875
|
+
const version = specifier.slice(atIndex + 1);
|
|
1876
|
+
packageJson[key][name] = version;
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
function getWorkspacePackageVersion() {
|
|
1819
1880
|
if (process.env.STANDARDAGENTS_WORKSPACE === "1") {
|
|
1820
1881
|
return "workspace:*";
|
|
1821
1882
|
}
|
|
1822
|
-
return "0.
|
|
1883
|
+
return "0.14.0";
|
|
1884
|
+
}
|
|
1885
|
+
function getSipPackageVersion() {
|
|
1886
|
+
return "1.0.1";
|
|
1887
|
+
}
|
|
1888
|
+
function getScaffoldToolchainVersions() {
|
|
1889
|
+
return {
|
|
1890
|
+
vite: "^7.0.6",
|
|
1891
|
+
typescript: "^5.9.0",
|
|
1892
|
+
cloudflareVitePlugin: "^1.17.0",
|
|
1893
|
+
wrangler: "^4.54.0"
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
async function runPackageBinary(packageManager, binary, binaryArgs, cwd, env) {
|
|
1897
|
+
switch (packageManager) {
|
|
1898
|
+
case "pnpm":
|
|
1899
|
+
return runCommand("pnpm", ["exec", binary, ...binaryArgs], cwd, env);
|
|
1900
|
+
case "yarn":
|
|
1901
|
+
return runCommand("yarn", ["exec", binary, ...binaryArgs], cwd, env);
|
|
1902
|
+
case "bun":
|
|
1903
|
+
return runCommand("bunx", [binary, ...binaryArgs], cwd, env);
|
|
1904
|
+
default:
|
|
1905
|
+
return runCommand("npx", [binary, ...binaryArgs], cwd, env);
|
|
1906
|
+
}
|
|
1823
1907
|
}
|
|
1824
1908
|
async function selectPackageManager(detected) {
|
|
1825
1909
|
const options = ["npm", "pnpm", "yarn", "bun"];
|
|
@@ -1953,27 +2037,40 @@ export default defineConfig({
|
|
|
1953
2037
|
const packageJsonPath = path3.join(projectPath, "package.json");
|
|
1954
2038
|
if (fs3.existsSync(packageJsonPath)) {
|
|
1955
2039
|
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
|
|
1956
|
-
const
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2040
|
+
const normalizedPackageJson = normalizeScaffoldedPackageJson(
|
|
2041
|
+
packageJson,
|
|
2042
|
+
projectName,
|
|
2043
|
+
getScaffoldToolchainVersions()
|
|
2044
|
+
);
|
|
2045
|
+
fs3.writeFileSync(packageJsonPath, JSON.stringify(normalizedPackageJson, null, 2) + "\n", "utf-8");
|
|
1961
2046
|
}
|
|
1962
2047
|
logger.log("");
|
|
1963
2048
|
logger.info("Step 2: Configuring environment variables...");
|
|
1964
2049
|
logger.log("");
|
|
1965
2050
|
const encryptionKey = crypto.randomBytes(32).toString("hex");
|
|
2051
|
+
let cloudflareToken = "";
|
|
2052
|
+
let cloudflareAccountId = "";
|
|
1966
2053
|
let cerebrasKey = "";
|
|
2054
|
+
let googleKey = "";
|
|
2055
|
+
let groqKey = "";
|
|
1967
2056
|
let openrouterKey = "";
|
|
1968
2057
|
let openaiKey = "";
|
|
2058
|
+
let xaiKey = "";
|
|
1969
2059
|
let adminPassword = "";
|
|
1970
2060
|
if (!options.yes) {
|
|
1971
2061
|
logger.log("API keys are optional but required to use AI models.");
|
|
1972
2062
|
logger.log("You can add them later by editing .dev.vars");
|
|
1973
2063
|
logger.log("");
|
|
2064
|
+
cloudflareToken = await prompt("Cloudflare Workers AI API token (optional, press Enter to skip): ");
|
|
2065
|
+
if (cloudflareToken) {
|
|
2066
|
+
cloudflareAccountId = await prompt("Cloudflare Account ID: ");
|
|
2067
|
+
}
|
|
1974
2068
|
cerebrasKey = await prompt("Cerebras API key (optional, press Enter to skip): ");
|
|
2069
|
+
googleKey = await prompt("Google Gemini API key (optional, press Enter to skip): ");
|
|
2070
|
+
groqKey = await prompt("Groq API key (optional, press Enter to skip): ");
|
|
1975
2071
|
openrouterKey = await prompt("OpenRouter API key (optional, press Enter to skip): ");
|
|
1976
2072
|
openaiKey = await prompt("OpenAI API key (optional, press Enter to skip): ");
|
|
2073
|
+
xaiKey = await prompt("xAI API key (optional, press Enter to skip): ");
|
|
1977
2074
|
logger.log("");
|
|
1978
2075
|
logger.log("Set a temporary admin password for development access.");
|
|
1979
2076
|
logger.log('Press Enter to use the default password "admin".');
|
|
@@ -2003,29 +2100,78 @@ export default defineConfig({
|
|
|
2003
2100
|
try {
|
|
2004
2101
|
const installCmd = pm === "npm" ? "install" : "add";
|
|
2005
2102
|
const devFlag = pm === "npm" ? "--save-dev" : "-D";
|
|
2006
|
-
const
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
"
|
|
2012
|
-
|
|
2103
|
+
const workspacePackageVersion = getWorkspacePackageVersion();
|
|
2104
|
+
const sipPackageVersion = getSipPackageVersion();
|
|
2105
|
+
const isWorkspaceMode = pm === "pnpm" && process.env.STANDARDAGENTS_WORKSPACE === "1";
|
|
2106
|
+
const pnpmWorkspaceFilter = pm === "pnpm" ? getPnpmWorkspaceFilterTarget(projectPath) : null;
|
|
2107
|
+
const installPackages = async (packages, { dev = false } = {}) => {
|
|
2108
|
+
if (pm === "pnpm" && pnpmWorkspaceFilter) {
|
|
2109
|
+
await runCommand(
|
|
2110
|
+
pm,
|
|
2111
|
+
[
|
|
2112
|
+
installCmd,
|
|
2113
|
+
...dev ? [devFlag] : [],
|
|
2114
|
+
...packages,
|
|
2115
|
+
"--filter",
|
|
2116
|
+
pnpmWorkspaceFilter.filterTarget
|
|
2117
|
+
],
|
|
2118
|
+
pnpmWorkspaceFilter.cwd
|
|
2119
|
+
);
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
await runCommand(
|
|
2123
|
+
pm,
|
|
2124
|
+
[
|
|
2125
|
+
installCmd,
|
|
2126
|
+
...dev ? [devFlag] : [],
|
|
2127
|
+
...packages
|
|
2128
|
+
],
|
|
2129
|
+
projectPath
|
|
2130
|
+
);
|
|
2131
|
+
};
|
|
2013
2132
|
const deps = [
|
|
2014
|
-
`@standardagents/builder@${
|
|
2015
|
-
`@standardagents/spec@${
|
|
2016
|
-
`@standardagents/sip@${
|
|
2133
|
+
`@standardagents/builder@${workspacePackageVersion}`,
|
|
2134
|
+
`@standardagents/spec@${workspacePackageVersion}`,
|
|
2135
|
+
`@standardagents/sip@${sipPackageVersion}`,
|
|
2017
2136
|
"zod"
|
|
2018
2137
|
];
|
|
2138
|
+
if (cloudflareToken) {
|
|
2139
|
+
deps.push(`@standardagents/cloudflare@${workspacePackageVersion}`);
|
|
2140
|
+
}
|
|
2019
2141
|
if (cerebrasKey) {
|
|
2020
|
-
deps.push(`@standardagents/cerebras@${
|
|
2142
|
+
deps.push(`@standardagents/cerebras@${workspacePackageVersion}`);
|
|
2143
|
+
}
|
|
2144
|
+
if (googleKey) {
|
|
2145
|
+
deps.push(`@standardagents/google@${workspacePackageVersion}`);
|
|
2146
|
+
}
|
|
2147
|
+
if (groqKey) {
|
|
2148
|
+
deps.push(`@standardagents/groq@${workspacePackageVersion}`);
|
|
2021
2149
|
}
|
|
2022
2150
|
if (openaiKey) {
|
|
2023
|
-
deps.push(`@standardagents/openai@${
|
|
2151
|
+
deps.push(`@standardagents/openai@${workspacePackageVersion}`);
|
|
2024
2152
|
}
|
|
2025
2153
|
if (openrouterKey) {
|
|
2026
|
-
deps.push(`@standardagents/openrouter@${
|
|
2154
|
+
deps.push(`@standardagents/openrouter@${workspacePackageVersion}`);
|
|
2155
|
+
}
|
|
2156
|
+
if (xaiKey) {
|
|
2157
|
+
deps.push(`@standardagents/xai@${workspacePackageVersion}`);
|
|
2158
|
+
}
|
|
2159
|
+
if (isWorkspaceMode) {
|
|
2160
|
+
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
|
|
2161
|
+
applyPackageSpecifiers(packageJson, "devDependencies", [
|
|
2162
|
+
`@cloudflare/vite-plugin@${getScaffoldToolchainVersions().cloudflareVitePlugin}`,
|
|
2163
|
+
`wrangler@${getScaffoldToolchainVersions().wrangler}`
|
|
2164
|
+
]);
|
|
2165
|
+
applyPackageSpecifiers(packageJson, "dependencies", deps);
|
|
2166
|
+
fs3.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
|
|
2167
|
+
logger.info("Workspace mode detected: wrote dependency specs to package.json and deferred install.");
|
|
2168
|
+
} else {
|
|
2169
|
+
await installPackages([
|
|
2170
|
+
`@cloudflare/vite-plugin@${getScaffoldToolchainVersions().cloudflareVitePlugin}`,
|
|
2171
|
+
`wrangler@${getScaffoldToolchainVersions().wrangler}`
|
|
2172
|
+
], { dev: true });
|
|
2173
|
+
await installPackages(deps);
|
|
2027
2174
|
}
|
|
2028
|
-
await runCommand(pm, [installCmd, ...deps], projectPath);
|
|
2029
2175
|
} catch (error) {
|
|
2030
2176
|
logger.error("Failed to install dependencies");
|
|
2031
2177
|
process.exit(1);
|
|
@@ -2052,11 +2198,28 @@ export default defineConfig({
|
|
|
2052
2198
|
"# AI Provider API Keys",
|
|
2053
2199
|
"# Uncomment and add your keys as needed"
|
|
2054
2200
|
];
|
|
2201
|
+
if (cloudflareToken) {
|
|
2202
|
+
devVarsLines.push(`CLOUDFLARE_API_TOKEN=${cloudflareToken}`);
|
|
2203
|
+
devVarsLines.push(`CLOUDFLARE_ACCOUNT_ID=${cloudflareAccountId}`);
|
|
2204
|
+
} else {
|
|
2205
|
+
devVarsLines.push("# CLOUDFLARE_API_TOKEN=your-cloudflare-workers-ai-token");
|
|
2206
|
+
devVarsLines.push("# CLOUDFLARE_ACCOUNT_ID=your-cloudflare-account-id");
|
|
2207
|
+
}
|
|
2055
2208
|
if (cerebrasKey) {
|
|
2056
2209
|
devVarsLines.push(`CEREBRAS_API_KEY=${cerebrasKey}`);
|
|
2057
2210
|
} else {
|
|
2058
2211
|
devVarsLines.push("# CEREBRAS_API_KEY=your-cerebras-key");
|
|
2059
2212
|
}
|
|
2213
|
+
if (googleKey) {
|
|
2214
|
+
devVarsLines.push(`GOOGLE_API_KEY=${googleKey}`);
|
|
2215
|
+
} else {
|
|
2216
|
+
devVarsLines.push("# GOOGLE_API_KEY=your-google-gemini-key");
|
|
2217
|
+
}
|
|
2218
|
+
if (groqKey) {
|
|
2219
|
+
devVarsLines.push(`GROQ_API_KEY=${groqKey}`);
|
|
2220
|
+
} else {
|
|
2221
|
+
devVarsLines.push("# GROQ_API_KEY=your-groq-key");
|
|
2222
|
+
}
|
|
2060
2223
|
if (openrouterKey) {
|
|
2061
2224
|
devVarsLines.push(`OPENROUTER_API_KEY=${openrouterKey}`);
|
|
2062
2225
|
} else {
|
|
@@ -2067,6 +2230,11 @@ export default defineConfig({
|
|
|
2067
2230
|
} else {
|
|
2068
2231
|
devVarsLines.push("# OPENAI_API_KEY=your-openai-key");
|
|
2069
2232
|
}
|
|
2233
|
+
if (xaiKey) {
|
|
2234
|
+
devVarsLines.push(`XAI_API_KEY=${xaiKey}`);
|
|
2235
|
+
} else {
|
|
2236
|
+
devVarsLines.push("# XAI_API_KEY=your-xai-key");
|
|
2237
|
+
}
|
|
2070
2238
|
devVarsLines.push("");
|
|
2071
2239
|
const devVarsPath = path3.join(projectPath, ".dev.vars");
|
|
2072
2240
|
fs3.writeFileSync(devVarsPath, devVarsLines.join("\n"), "utf-8");
|
|
@@ -2082,10 +2250,14 @@ export default defineConfig({
|
|
|
2082
2250
|
logger.log("");
|
|
2083
2251
|
logger.info("Step 5: Generating Cloudflare types...");
|
|
2084
2252
|
logger.log("");
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2253
|
+
if (pm === "pnpm" && process.env.STANDARDAGENTS_WORKSPACE === "1") {
|
|
2254
|
+
logger.info("Workspace mode detected: skipping Wrangler type generation until dependencies are installed.");
|
|
2255
|
+
} else {
|
|
2256
|
+
try {
|
|
2257
|
+
await runPackageBinary(pm, "wrangler", ["types"], projectPath);
|
|
2258
|
+
} catch (error) {
|
|
2259
|
+
logger.warning("Could not generate types automatically. Run your package manager's Wrangler types command manually.");
|
|
2260
|
+
}
|
|
2089
2261
|
}
|
|
2090
2262
|
logger.log("");
|
|
2091
2263
|
logger.success("Project created successfully!");
|
|
@@ -3337,10 +3509,609 @@ async function deploy(options) {
|
|
|
3337
3509
|
process.exit(1);
|
|
3338
3510
|
}
|
|
3339
3511
|
}
|
|
3512
|
+
var DEFAULT_SKILL_NAME = "agentbuilder";
|
|
3513
|
+
function resolveCodexHome(env = process.env, homeDir = os.homedir()) {
|
|
3514
|
+
return env.CODEX_HOME || path3.join(homeDir, ".codex");
|
|
3515
|
+
}
|
|
3516
|
+
function resolveClaudeHome(homeDir = os.homedir()) {
|
|
3517
|
+
return path3.join(homeDir, ".claude");
|
|
3518
|
+
}
|
|
3519
|
+
function expandHomePath(inputPath, homeDir = os.homedir()) {
|
|
3520
|
+
if (inputPath === "~") {
|
|
3521
|
+
return homeDir;
|
|
3522
|
+
}
|
|
3523
|
+
if (inputPath.startsWith("~/")) {
|
|
3524
|
+
return path3.join(homeDir, inputPath.slice(2));
|
|
3525
|
+
}
|
|
3526
|
+
return inputPath;
|
|
3527
|
+
}
|
|
3528
|
+
function buildInstallTargets(options = {}, env = process.env, homeDir = os.homedir()) {
|
|
3529
|
+
const customRoot = options.dir ? path3.resolve(expandHomePath(options.dir, homeDir)) : null;
|
|
3530
|
+
const codexHome = resolveCodexHome(env, homeDir);
|
|
3531
|
+
const claudeHome = resolveClaudeHome(homeDir);
|
|
3532
|
+
return [
|
|
3533
|
+
{
|
|
3534
|
+
id: "codex",
|
|
3535
|
+
label: "Codex",
|
|
3536
|
+
summary: "Install as a Codex skill folder",
|
|
3537
|
+
detected: fs3.existsSync(codexHome),
|
|
3538
|
+
installPath: customRoot ?? path3.join(codexHome, "skills")
|
|
3539
|
+
},
|
|
3540
|
+
{
|
|
3541
|
+
id: "claude",
|
|
3542
|
+
label: "Claude Code",
|
|
3543
|
+
summary: "Install as a Claude Code user subagent",
|
|
3544
|
+
detected: fs3.existsSync(claudeHome),
|
|
3545
|
+
installPath: customRoot ?? path3.join(claudeHome, "agents")
|
|
3546
|
+
}
|
|
3547
|
+
];
|
|
3548
|
+
}
|
|
3549
|
+
function resolveSelectedTarget(targetId, options = {}, env = process.env, homeDir = os.homedir()) {
|
|
3550
|
+
const target = buildInstallTargets(options, env, homeDir).find(
|
|
3551
|
+
(candidate) => candidate.id === targetId
|
|
3552
|
+
);
|
|
3553
|
+
if (!target) {
|
|
3554
|
+
throw new Error(`Unknown install target "${targetId}".`);
|
|
3555
|
+
}
|
|
3556
|
+
return target;
|
|
3557
|
+
}
|
|
3558
|
+
function buildClaudeSubagentMarkdown(skillName, description, body) {
|
|
3559
|
+
const normalizedBody = body.replace(/^\uFEFF/, "").trim();
|
|
3560
|
+
return [
|
|
3561
|
+
"---",
|
|
3562
|
+
`name: ${skillName}`,
|
|
3563
|
+
`description: ${description}`,
|
|
3564
|
+
"---",
|
|
3565
|
+
"",
|
|
3566
|
+
normalizedBody,
|
|
3567
|
+
""
|
|
3568
|
+
].join("\n");
|
|
3569
|
+
}
|
|
3570
|
+
async function selectInstallTarget(targets) {
|
|
3571
|
+
return new Promise((resolve) => {
|
|
3572
|
+
const stdin = process.stdin;
|
|
3573
|
+
const stdout = process.stdout;
|
|
3574
|
+
let selectedIndex = 0;
|
|
3575
|
+
const render = (initial = false) => {
|
|
3576
|
+
const lines = [];
|
|
3577
|
+
lines.push("Choose where to install the AgentBuilder skill:");
|
|
3578
|
+
lines.push("");
|
|
3579
|
+
for (let i = 0; i < targets.length; i++) {
|
|
3580
|
+
const target = targets[i];
|
|
3581
|
+
const isSelected = i === selectedIndex;
|
|
3582
|
+
const cursor = isSelected ? "\x1B[36m\u276F\x1B[0m" : " ";
|
|
3583
|
+
const title = isSelected ? "\x1B[36m" : "\x1B[90m";
|
|
3584
|
+
const detection = target.detected ? "\x1B[32m(detected)\x1B[0m" : "\x1B[90m(not found yet)\x1B[0m";
|
|
3585
|
+
lines.push(`${cursor} ${title}${target.label}\x1B[0m ${detection}`);
|
|
3586
|
+
lines.push(` ${target.summary}`);
|
|
3587
|
+
lines.push(` ${target.installPath}`);
|
|
3588
|
+
lines.push("");
|
|
3589
|
+
}
|
|
3590
|
+
lines.push("Use arrows to move, Enter to install, Ctrl+C to cancel.");
|
|
3591
|
+
if (!initial) {
|
|
3592
|
+
stdout.write("\x1B[?25l");
|
|
3593
|
+
stdout.write(`\x1B[${lines.length}A`);
|
|
3594
|
+
stdout.write("\x1B[0J");
|
|
3595
|
+
}
|
|
3596
|
+
stdout.write(lines.join("\n") + "\n");
|
|
3597
|
+
};
|
|
3598
|
+
render(true);
|
|
3599
|
+
const wasRaw = stdin.isRaw;
|
|
3600
|
+
if (stdin.isTTY) {
|
|
3601
|
+
stdin.setRawMode(true);
|
|
3602
|
+
}
|
|
3603
|
+
stdin.resume();
|
|
3604
|
+
stdin.setEncoding("utf8");
|
|
3605
|
+
const cleanup = () => {
|
|
3606
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
3607
|
+
stdin.pause();
|
|
3608
|
+
stdin.removeListener("data", onData);
|
|
3609
|
+
stdout.write("\x1B[?25h");
|
|
3610
|
+
};
|
|
3611
|
+
const onData = (key) => {
|
|
3612
|
+
if (key === "\x1B[A" || key === "k") {
|
|
3613
|
+
selectedIndex = (selectedIndex - 1 + targets.length) % targets.length;
|
|
3614
|
+
render();
|
|
3615
|
+
} else if (key === "\x1B[B" || key === "j") {
|
|
3616
|
+
selectedIndex = (selectedIndex + 1) % targets.length;
|
|
3617
|
+
render();
|
|
3618
|
+
} else if (key === "\r" || key === "\n") {
|
|
3619
|
+
cleanup();
|
|
3620
|
+
resolve(targets[selectedIndex]);
|
|
3621
|
+
} else if (key === "") {
|
|
3622
|
+
cleanup();
|
|
3623
|
+
stdout.write("\n");
|
|
3624
|
+
process.exit(1);
|
|
3625
|
+
}
|
|
3626
|
+
};
|
|
3627
|
+
stdin.on("data", onData);
|
|
3628
|
+
});
|
|
3629
|
+
}
|
|
3630
|
+
async function installBundledSkill(targetId, skillName = DEFAULT_SKILL_NAME, options = {}) {
|
|
3631
|
+
const target = resolveSelectedTarget(targetId, options);
|
|
3632
|
+
if (target.id === "codex") {
|
|
3633
|
+
const targetDir = path3.join(target.installPath, skillName);
|
|
3634
|
+
if (fs3.existsSync(targetDir) && !options.force) {
|
|
3635
|
+
throw new Error(
|
|
3636
|
+
`Skill already exists at ${targetDir}. Re-run with --force to overwrite it.`
|
|
3637
|
+
);
|
|
3638
|
+
}
|
|
3639
|
+
return copySkill(skillName, target.installPath, {
|
|
3640
|
+
overwrite: options.force ?? false
|
|
3641
|
+
});
|
|
3642
|
+
}
|
|
3643
|
+
const targetFile = path3.join(target.installPath, `${skillName}.md`);
|
|
3644
|
+
if (fs3.existsSync(targetFile) && !options.force) {
|
|
3645
|
+
throw new Error(
|
|
3646
|
+
`Claude subagent already exists at ${targetFile}. Re-run with --force to overwrite it.`
|
|
3647
|
+
);
|
|
3648
|
+
}
|
|
3649
|
+
const skillBody = await readSkillFile(skillName);
|
|
3650
|
+
const subagentMarkdown = buildClaudeSubagentMarkdown(
|
|
3651
|
+
skillName,
|
|
3652
|
+
"Use for Standard Agents and AgentBuilder architecture, composition, subagent design, and implementation work.",
|
|
3653
|
+
skillBody
|
|
3654
|
+
);
|
|
3655
|
+
fs3.mkdirSync(target.installPath, { recursive: true });
|
|
3656
|
+
fs3.writeFileSync(targetFile, subagentMarkdown, "utf8");
|
|
3657
|
+
return targetFile;
|
|
3658
|
+
}
|
|
3659
|
+
function isInteractiveTerminal() {
|
|
3660
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
3661
|
+
}
|
|
3662
|
+
async function skill(options = {}) {
|
|
3663
|
+
try {
|
|
3664
|
+
const selectedTarget = options.agent ? resolveSelectedTarget(options.agent, options) : isInteractiveTerminal() ? await selectInstallTarget(buildInstallTargets(options)) : resolveSelectedTarget("codex", options);
|
|
3665
|
+
logger.log("");
|
|
3666
|
+
logger.info(`Installing AgentBuilder skill into ${selectedTarget.label}...`);
|
|
3667
|
+
const installedPath = await installBundledSkill(
|
|
3668
|
+
selectedTarget.id,
|
|
3669
|
+
DEFAULT_SKILL_NAME,
|
|
3670
|
+
options
|
|
3671
|
+
);
|
|
3672
|
+
logger.success(`Installed ${DEFAULT_SKILL_NAME} into ${selectedTarget.label}`);
|
|
3673
|
+
logger.log("");
|
|
3674
|
+
logger.log(`Path: ${installedPath}`);
|
|
3675
|
+
logger.log("");
|
|
3676
|
+
logger.log("Next steps:");
|
|
3677
|
+
if (selectedTarget.id === "codex") {
|
|
3678
|
+
logger.log(" Restart Codex if it is already running");
|
|
3679
|
+
logger.log(` Edit ${path3.join(installedPath, "SKILL.md")} to continue customizing the skill`);
|
|
3680
|
+
} else {
|
|
3681
|
+
logger.log(" Restart Claude Code if it is already running");
|
|
3682
|
+
logger.log(` Edit ${installedPath} to continue customizing the subagent`);
|
|
3683
|
+
}
|
|
3684
|
+
} catch (error) {
|
|
3685
|
+
logger.log("");
|
|
3686
|
+
logger.error(error instanceof Error ? error.message : "Failed to install skill");
|
|
3687
|
+
process.exit(1);
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
var UUID_LIKE_MODEL_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
3691
|
+
var CLOUDFLARE_PROVIDER_MODEL_ID = /^@cf\//i;
|
|
3692
|
+
var PAGE_SIZE = 100;
|
|
3693
|
+
var MAX_PAGES = 1e3;
|
|
3694
|
+
var CURRENT_MODELS_URLS = [
|
|
3695
|
+
"https://standardagentspec.org/models.md",
|
|
3696
|
+
"https://standardagentspec.com/models.md"
|
|
3697
|
+
];
|
|
3698
|
+
var PROVIDER_DEFINITIONS = [
|
|
3699
|
+
{
|
|
3700
|
+
name: "cloudflare",
|
|
3701
|
+
packageName: "@standardagents/cloudflare",
|
|
3702
|
+
exportName: "cloudflare",
|
|
3703
|
+
label: "Cloudflare Workers AI",
|
|
3704
|
+
envKeys: ["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID"]
|
|
3705
|
+
},
|
|
3706
|
+
{
|
|
3707
|
+
name: "cerebras",
|
|
3708
|
+
packageName: "@standardagents/cerebras",
|
|
3709
|
+
exportName: "cerebras",
|
|
3710
|
+
label: "Cerebras",
|
|
3711
|
+
envKeys: ["CEREBRAS_API_KEY"]
|
|
3712
|
+
},
|
|
3713
|
+
{
|
|
3714
|
+
name: "google",
|
|
3715
|
+
packageName: "@standardagents/google",
|
|
3716
|
+
exportName: "google",
|
|
3717
|
+
label: "Google Gemini",
|
|
3718
|
+
envKeys: ["GOOGLE_API_KEY"]
|
|
3719
|
+
},
|
|
3720
|
+
{
|
|
3721
|
+
name: "groq",
|
|
3722
|
+
packageName: "@standardagents/groq",
|
|
3723
|
+
exportName: "groq",
|
|
3724
|
+
label: "Groq",
|
|
3725
|
+
envKeys: ["GROQ_API_KEY"]
|
|
3726
|
+
},
|
|
3727
|
+
{
|
|
3728
|
+
name: "openai",
|
|
3729
|
+
packageName: "@standardagents/openai",
|
|
3730
|
+
exportName: "openai",
|
|
3731
|
+
label: "OpenAI",
|
|
3732
|
+
envKeys: ["OPENAI_API_KEY"]
|
|
3733
|
+
},
|
|
3734
|
+
{
|
|
3735
|
+
name: "openrouter",
|
|
3736
|
+
packageName: "@standardagents/openrouter",
|
|
3737
|
+
exportName: "openrouter",
|
|
3738
|
+
label: "OpenRouter",
|
|
3739
|
+
envKeys: ["OPENROUTER_API_KEY"]
|
|
3740
|
+
},
|
|
3741
|
+
{
|
|
3742
|
+
name: "xai",
|
|
3743
|
+
packageName: "@standardagents/xai",
|
|
3744
|
+
exportName: "xai",
|
|
3745
|
+
label: "xai",
|
|
3746
|
+
envKeys: ["XAI_API_KEY"]
|
|
3747
|
+
}
|
|
3748
|
+
];
|
|
3749
|
+
function isOpaqueModelId(modelId) {
|
|
3750
|
+
if (!modelId) return true;
|
|
3751
|
+
return UUID_LIKE_MODEL_ID.test(modelId) || CLOUDFLARE_PROVIDER_MODEL_ID.test(modelId);
|
|
3752
|
+
}
|
|
3753
|
+
function getProviderDefinition(name) {
|
|
3754
|
+
return PROVIDER_DEFINITIONS.find((provider) => provider.name === name);
|
|
3755
|
+
}
|
|
3756
|
+
function parseEnvFile2(content) {
|
|
3757
|
+
const result = {};
|
|
3758
|
+
for (const line of content.split("\n")) {
|
|
3759
|
+
const trimmed = line.trim();
|
|
3760
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
3761
|
+
const withoutExport = trimmed.startsWith("export ") ? trimmed.slice("export ".length).trim() : trimmed;
|
|
3762
|
+
const eqIndex = withoutExport.indexOf("=");
|
|
3763
|
+
if (eqIndex === -1) continue;
|
|
3764
|
+
const key = withoutExport.slice(0, eqIndex).trim();
|
|
3765
|
+
let value = withoutExport.slice(eqIndex + 1).trim();
|
|
3766
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
3767
|
+
value = value.slice(1, -1).replace(/\\"/g, '"');
|
|
3768
|
+
} else if (value.startsWith("'") && value.endsWith("'")) {
|
|
3769
|
+
value = value.slice(1, -1);
|
|
3770
|
+
}
|
|
3771
|
+
result[key] = value;
|
|
3772
|
+
}
|
|
3773
|
+
return result;
|
|
3774
|
+
}
|
|
3775
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
3776
|
+
let currentDir = path3.resolve(startDir);
|
|
3777
|
+
while (true) {
|
|
3778
|
+
if (fs3.existsSync(path3.join(currentDir, "package.json"))) {
|
|
3779
|
+
return currentDir;
|
|
3780
|
+
}
|
|
3781
|
+
const parentDir = path3.dirname(currentDir);
|
|
3782
|
+
if (parentDir === currentDir) {
|
|
3783
|
+
return path3.resolve(startDir);
|
|
3784
|
+
}
|
|
3785
|
+
currentDir = parentDir;
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
function readProjectPackageJson(projectRoot) {
|
|
3789
|
+
const packagePath = path3.join(projectRoot, "package.json");
|
|
3790
|
+
if (!fs3.existsSync(packagePath)) {
|
|
3791
|
+
return {};
|
|
3792
|
+
}
|
|
3793
|
+
return JSON.parse(fs3.readFileSync(packagePath, "utf8"));
|
|
3794
|
+
}
|
|
3795
|
+
function getDeclaredProviderPackages(projectRoot) {
|
|
3796
|
+
const pkg = readProjectPackageJson(projectRoot);
|
|
3797
|
+
const combined = {
|
|
3798
|
+
...pkg.dependencies ?? {},
|
|
3799
|
+
...pkg.devDependencies ?? {},
|
|
3800
|
+
...pkg.optionalDependencies ?? {},
|
|
3801
|
+
...pkg.peerDependencies ?? {}
|
|
3802
|
+
};
|
|
3803
|
+
return PROVIDER_DEFINITIONS.map((provider) => provider.packageName).filter((packageName) => typeof combined[packageName] === "string");
|
|
3804
|
+
}
|
|
3805
|
+
function resolvePackageEntry(packageName, projectRoot) {
|
|
3806
|
+
const packageDir = findInstalledPackageDir(packageName, projectRoot);
|
|
3807
|
+
if (!packageDir) {
|
|
3808
|
+
return null;
|
|
3809
|
+
}
|
|
3810
|
+
const packageJsonPath = path3.join(packageDir, "package.json");
|
|
3811
|
+
try {
|
|
3812
|
+
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf8"));
|
|
3813
|
+
const exportPath = resolvePackageExportPath(packageJson.exports) || packageJson.main || "index.js";
|
|
3814
|
+
return path3.join(packageDir, exportPath);
|
|
3815
|
+
} catch {
|
|
3816
|
+
return null;
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
function findInstalledPackageDir(packageName, projectRoot) {
|
|
3820
|
+
let currentDir = path3.resolve(projectRoot);
|
|
3821
|
+
while (true) {
|
|
3822
|
+
const candidate = path3.join(currentDir, "node_modules", ...packageName.split("/"));
|
|
3823
|
+
if (fs3.existsSync(candidate)) {
|
|
3824
|
+
return candidate;
|
|
3825
|
+
}
|
|
3826
|
+
const parentDir = path3.dirname(currentDir);
|
|
3827
|
+
if (parentDir === currentDir) {
|
|
3828
|
+
return null;
|
|
3829
|
+
}
|
|
3830
|
+
currentDir = parentDir;
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
function resolvePackageExportPath(exportsField) {
|
|
3834
|
+
if (typeof exportsField === "string") {
|
|
3835
|
+
return exportsField;
|
|
3836
|
+
}
|
|
3837
|
+
if (!exportsField || typeof exportsField !== "object") {
|
|
3838
|
+
return null;
|
|
3839
|
+
}
|
|
3840
|
+
const rootExport = exportsField["."];
|
|
3841
|
+
if (typeof rootExport === "string") {
|
|
3842
|
+
return rootExport;
|
|
3843
|
+
}
|
|
3844
|
+
if (!rootExport || typeof rootExport !== "object") {
|
|
3845
|
+
return null;
|
|
3846
|
+
}
|
|
3847
|
+
const rootExportRecord = rootExport;
|
|
3848
|
+
const candidate = rootExportRecord.import || rootExportRecord.default || rootExportRecord.require;
|
|
3849
|
+
return typeof candidate === "string" ? candidate : null;
|
|
3850
|
+
}
|
|
3851
|
+
function isProviderPackageInstalled(packageName, projectRoot) {
|
|
3852
|
+
return resolvePackageEntry(packageName, projectRoot) !== null;
|
|
3853
|
+
}
|
|
3854
|
+
function loadProjectEnv(projectRoot, processEnv = process.env) {
|
|
3855
|
+
const result = {};
|
|
3856
|
+
const envFiles = [".env", ".env.local", ".dev.vars", ".dev.vars.local"];
|
|
3857
|
+
for (const fileName of envFiles) {
|
|
3858
|
+
const filePath = path3.join(projectRoot, fileName);
|
|
3859
|
+
if (!fs3.existsSync(filePath)) continue;
|
|
3860
|
+
Object.assign(result, parseEnvFile2(fs3.readFileSync(filePath, "utf8")));
|
|
3861
|
+
}
|
|
3862
|
+
for (const [key, value] of Object.entries(processEnv)) {
|
|
3863
|
+
if (typeof value === "string") {
|
|
3864
|
+
result[key] = value;
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
return result;
|
|
3868
|
+
}
|
|
3869
|
+
function detectProviders(projectRoot, env = loadProjectEnv(projectRoot)) {
|
|
3870
|
+
const declaredPackages = new Set(getDeclaredProviderPackages(projectRoot));
|
|
3871
|
+
return PROVIDER_DEFINITIONS.filter((provider) => declaredPackages.has(provider.packageName)).map((provider) => {
|
|
3872
|
+
const installed = isProviderPackageInstalled(provider.packageName, projectRoot);
|
|
3873
|
+
const missingEnvKeys = provider.envKeys.filter((envKey) => !env[envKey]);
|
|
3874
|
+
return {
|
|
3875
|
+
...provider,
|
|
3876
|
+
declared: true,
|
|
3877
|
+
installed,
|
|
3878
|
+
configured: installed && missingEnvKeys.length === 0,
|
|
3879
|
+
missingEnvKeys
|
|
3880
|
+
};
|
|
3881
|
+
});
|
|
3882
|
+
}
|
|
3883
|
+
async function loadProviderFactory(provider, projectRoot) {
|
|
3884
|
+
const entryPath = resolvePackageEntry(provider.packageName, projectRoot);
|
|
3885
|
+
if (!entryPath) {
|
|
3886
|
+
throw new Error(`Provider package ${provider.packageName} is not installed in ${projectRoot}.`);
|
|
3887
|
+
}
|
|
3888
|
+
const moduleUrl = entryPath.startsWith("file:") ? entryPath : pathToFileURL(entryPath).href;
|
|
3889
|
+
const providerModule = await import(moduleUrl);
|
|
3890
|
+
const factory = providerModule[provider.exportName];
|
|
3891
|
+
if (typeof factory !== "function") {
|
|
3892
|
+
throw new Error(`Provider package ${provider.packageName} does not export ${provider.exportName}().`);
|
|
3893
|
+
}
|
|
3894
|
+
return factory;
|
|
3895
|
+
}
|
|
3896
|
+
function buildProviderConfig(provider, env) {
|
|
3897
|
+
const apiKey = env[provider.envKeys[0]] || "";
|
|
3898
|
+
if (provider.name === "cloudflare") {
|
|
3899
|
+
const accountId = env.CLOUDFLARE_ACCOUNT_ID;
|
|
3900
|
+
return {
|
|
3901
|
+
apiKey,
|
|
3902
|
+
accountId,
|
|
3903
|
+
baseUrl: accountId ? `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/v1` : void 0
|
|
3904
|
+
};
|
|
3905
|
+
}
|
|
3906
|
+
return { apiKey };
|
|
3907
|
+
}
|
|
3908
|
+
async function collectProviderModels(providerName, providerInstance) {
|
|
3909
|
+
const rawModels = [];
|
|
3910
|
+
if (providerInstance.getModelsPage) {
|
|
3911
|
+
for (let page = 1; page <= MAX_PAGES; page += 1) {
|
|
3912
|
+
const result = await providerInstance.getModelsPage({ page, perPage: PAGE_SIZE });
|
|
3913
|
+
rawModels.push(...result.models);
|
|
3914
|
+
if (!result.hasNextPage) {
|
|
3915
|
+
return normalizeProviderModels(providerName, rawModels);
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
throw new Error(`Provider ${providerName} exceeded ${MAX_PAGES} model pages without terminating pagination.`);
|
|
3919
|
+
}
|
|
3920
|
+
if (providerInstance.getModels) {
|
|
3921
|
+
rawModels.push(...await providerInstance.getModels());
|
|
3922
|
+
return normalizeProviderModels(providerName, rawModels);
|
|
3923
|
+
}
|
|
3924
|
+
throw new Error(`Provider ${providerName} does not support model discovery.`);
|
|
3925
|
+
}
|
|
3926
|
+
async function fetchAvailableModelsForProvider(providerName, projectRoot, env, factoryLoader = loadProviderFactory) {
|
|
3927
|
+
const provider = getProviderDefinition(providerName);
|
|
3928
|
+
if (!provider) {
|
|
3929
|
+
throw new Error(`Unknown provider "${providerName}".`);
|
|
3930
|
+
}
|
|
3931
|
+
const factory = await factoryLoader(provider, projectRoot);
|
|
3932
|
+
const providerInstance = factory(buildProviderConfig(provider, env));
|
|
3933
|
+
return collectProviderModels(provider.name, providerInstance);
|
|
3934
|
+
}
|
|
3935
|
+
async function fetchCurrentModelsMarkdown(urls = CURRENT_MODELS_URLS) {
|
|
3936
|
+
let lastError = null;
|
|
3937
|
+
for (const url of urls) {
|
|
3938
|
+
try {
|
|
3939
|
+
const response = await fetch(url);
|
|
3940
|
+
if (!response.ok) {
|
|
3941
|
+
throw new Error(`HTTP ${response.status} ${response.statusText}`.trim());
|
|
3942
|
+
}
|
|
3943
|
+
return {
|
|
3944
|
+
url,
|
|
3945
|
+
content: await response.text()
|
|
3946
|
+
};
|
|
3947
|
+
} catch (error) {
|
|
3948
|
+
lastError = error instanceof Error ? new Error(`Failed to fetch ${url}: ${error.message}`) : new Error(`Failed to fetch ${url}.`);
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
throw lastError ?? new Error("No current models URLs configured.");
|
|
3952
|
+
}
|
|
3953
|
+
function buildProviderSelectionHelp(providers) {
|
|
3954
|
+
const providerNames = providers.map((provider) => provider.name).join(", ");
|
|
3955
|
+
const examples = providers.map((provider) => ` agents available-models --provider=${provider.name}`).join("\n");
|
|
3956
|
+
return [
|
|
3957
|
+
`Multiple configured providers were found: ${providerNames}`,
|
|
3958
|
+
"Re-run with a specific provider to fetch all model IDs:",
|
|
3959
|
+
examples
|
|
3960
|
+
].join("\n");
|
|
3961
|
+
}
|
|
3962
|
+
function describeProviderAvailability(provider) {
|
|
3963
|
+
if (!provider.installed) {
|
|
3964
|
+
return `${provider.name}: not installed`;
|
|
3965
|
+
}
|
|
3966
|
+
if (provider.missingEnvKeys.length > 0) {
|
|
3967
|
+
return `${provider.name}: missing ${provider.missingEnvKeys.join(", ")}`;
|
|
3968
|
+
}
|
|
3969
|
+
return `${provider.name}: unavailable`;
|
|
3970
|
+
}
|
|
3971
|
+
function formatAvailableModelsOutput(provider, models) {
|
|
3972
|
+
const lines = [
|
|
3973
|
+
`# ${provider.label} models`,
|
|
3974
|
+
"",
|
|
3975
|
+
"Use the `model` values exactly as shown in `defineModel({ model: ... })`.",
|
|
3976
|
+
"",
|
|
3977
|
+
"| model | label | context | notes |",
|
|
3978
|
+
"| --- | --- | ---: | --- |"
|
|
3979
|
+
];
|
|
3980
|
+
for (const model of models) {
|
|
3981
|
+
const context = model.contextLength ? model.contextLength.toLocaleString("en-US") : "";
|
|
3982
|
+
lines.push(
|
|
3983
|
+
`| \`${escapeTableCell(model.value)}\` | ${escapeTableCell(model.label)} | ${context} | ${escapeTableCell(model.description)} |`
|
|
3984
|
+
);
|
|
3985
|
+
}
|
|
3986
|
+
return `${lines.join("\n")}
|
|
3987
|
+
`;
|
|
3988
|
+
}
|
|
3989
|
+
function normalizeProviderModels(providerName, rawModels) {
|
|
3990
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
3991
|
+
for (const model of rawModels) {
|
|
3992
|
+
const value = getExecutableModelId(providerName, model);
|
|
3993
|
+
if (!value) continue;
|
|
3994
|
+
if (!deduped.has(value)) {
|
|
3995
|
+
deduped.set(value, {
|
|
3996
|
+
value,
|
|
3997
|
+
label: model.name || value,
|
|
3998
|
+
description: model.description || "",
|
|
3999
|
+
contextLength: model.contextLength,
|
|
4000
|
+
slug: model.slug
|
|
4001
|
+
});
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
return [...deduped.values()].sort((left, right) => left.value.localeCompare(right.value));
|
|
4005
|
+
}
|
|
4006
|
+
function getExecutableModelId(providerName, model) {
|
|
4007
|
+
if (model.id && !isOpaqueModelId(model.id)) {
|
|
4008
|
+
return model.id;
|
|
4009
|
+
}
|
|
4010
|
+
if (providerName === "cloudflare") {
|
|
4011
|
+
const publicCloudflareId = [model.name, model.slug].find((value) => typeof value === "string" && value.includes("/"));
|
|
4012
|
+
if (publicCloudflareId) {
|
|
4013
|
+
return publicCloudflareId.startsWith("@cf/") ? publicCloudflareId : `@cf/${publicCloudflareId.replace(/^@cf\//, "")}`;
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
return model.slug || model.name || model.id;
|
|
4017
|
+
}
|
|
4018
|
+
function escapeTableCell(value) {
|
|
4019
|
+
return value.replace(/\|/g, "\\|").replace(/\n/g, "<br />");
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
// src/commands/current-models.ts
|
|
4023
|
+
async function currentModels(options = {}) {
|
|
4024
|
+
try {
|
|
4025
|
+
const { content } = await fetchCurrentModelsMarkdown(
|
|
4026
|
+
options.url ? [options.url] : void 0
|
|
4027
|
+
);
|
|
4028
|
+
process.stdout.write(content.endsWith("\n") ? content : `${content}
|
|
4029
|
+
`);
|
|
4030
|
+
} catch (error) {
|
|
4031
|
+
const message = error instanceof Error ? error.message : "Failed to fetch current models.";
|
|
4032
|
+
process.stderr.write(`${message}
|
|
4033
|
+
`);
|
|
4034
|
+
process.exit(1);
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
|
|
4038
|
+
// src/commands/available-models.ts
|
|
4039
|
+
function writeError(message) {
|
|
4040
|
+
process.stderr.write(`${message}
|
|
4041
|
+
`);
|
|
4042
|
+
process.exit(1);
|
|
4043
|
+
}
|
|
4044
|
+
async function availableModels(options = {}) {
|
|
4045
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
4046
|
+
const env = loadProjectEnv(projectRoot);
|
|
4047
|
+
const detectedProviders = detectProviders(projectRoot, env);
|
|
4048
|
+
if (detectedProviders.length === 0) {
|
|
4049
|
+
writeError(
|
|
4050
|
+
[
|
|
4051
|
+
`No installed provider packages were found in ${projectRoot}.`,
|
|
4052
|
+
"Add a provider package such as `@standardagents/cerebras`, then run this command again."
|
|
4053
|
+
].join("\n")
|
|
4054
|
+
);
|
|
4055
|
+
}
|
|
4056
|
+
const providerName = options.provider?.trim();
|
|
4057
|
+
if (providerName) {
|
|
4058
|
+
const provider2 = detectedProviders.find((candidate) => candidate.name === providerName);
|
|
4059
|
+
if (!getProviderDefinition(providerName)) {
|
|
4060
|
+
writeError(
|
|
4061
|
+
`Unknown provider "${providerName}". Supported providers: ${detectedProviders.map((candidate) => candidate.name).join(", ")}.`
|
|
4062
|
+
);
|
|
4063
|
+
}
|
|
4064
|
+
if (!provider2) {
|
|
4065
|
+
writeError(
|
|
4066
|
+
[
|
|
4067
|
+
`Provider "${providerName}" is not installed in ${projectRoot}.`,
|
|
4068
|
+
`Install ${getProviderDefinition(providerName)?.packageName || providerName} and try again.`
|
|
4069
|
+
].join("\n")
|
|
4070
|
+
);
|
|
4071
|
+
}
|
|
4072
|
+
if (!provider2.configured) {
|
|
4073
|
+
writeError(
|
|
4074
|
+
[
|
|
4075
|
+
`Provider "${providerName}" is missing required environment variables.`,
|
|
4076
|
+
`Missing: ${provider2.missingEnvKeys.join(", ")}`
|
|
4077
|
+
].join("\n")
|
|
4078
|
+
);
|
|
4079
|
+
}
|
|
4080
|
+
try {
|
|
4081
|
+
const models = await fetchAvailableModelsForProvider(provider2.name, projectRoot, env);
|
|
4082
|
+
process.stdout.write(formatAvailableModelsOutput(provider2, models));
|
|
4083
|
+
return;
|
|
4084
|
+
} catch (error) {
|
|
4085
|
+
const message = error instanceof Error ? error.message : `Failed to fetch models for ${providerName}.`;
|
|
4086
|
+
writeError(message);
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
4089
|
+
const configuredProviders = detectedProviders.filter((provider2) => provider2.configured);
|
|
4090
|
+
if (configuredProviders.length === 0) {
|
|
4091
|
+
const missingSummary = detectedProviders.map((provider2) => describeProviderAvailability(provider2)).join("\n");
|
|
4092
|
+
writeError(
|
|
4093
|
+
[
|
|
4094
|
+
"No installed providers are fully configured yet.",
|
|
4095
|
+
missingSummary
|
|
4096
|
+
].join("\n")
|
|
4097
|
+
);
|
|
4098
|
+
}
|
|
4099
|
+
if (configuredProviders.length > 1) {
|
|
4100
|
+
writeError(buildProviderSelectionHelp(configuredProviders));
|
|
4101
|
+
}
|
|
4102
|
+
const provider = configuredProviders[0];
|
|
4103
|
+
try {
|
|
4104
|
+
const models = await fetchAvailableModelsForProvider(provider.name, projectRoot, env);
|
|
4105
|
+
process.stdout.write(formatAvailableModelsOutput(provider, models));
|
|
4106
|
+
} catch (error) {
|
|
4107
|
+
const message = error instanceof Error ? error.message : `Failed to fetch models for ${provider.name}.`;
|
|
4108
|
+
writeError(message);
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
3340
4111
|
|
|
3341
4112
|
// src/index.ts
|
|
3342
4113
|
var program = new Command();
|
|
3343
|
-
program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version("0.
|
|
4114
|
+
program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version("0.14.0");
|
|
3344
4115
|
program.command("init [project-name]").description("Create a new Standard Agents project (runs create vite, then scaffold)").option("-y, --yes", "Skip prompts and use defaults").option("--template <template>", "Vite template to use", "vanilla-ts").action(init);
|
|
3345
4116
|
program.command("scaffold").description("Add Standard Agents to an existing Vite project").option("--force", "Overwrite existing configuration").action(scaffold);
|
|
3346
4117
|
program.command("init-chat [project-name]").description("Scaffold a frontend chat application").option("-y, --yes", "Skip prompts and use defaults").option("--framework <framework>", "Framework to use (vite or nextjs)").action(initChat);
|
|
@@ -3348,6 +4119,9 @@ program.command("pack [agent-name]").description("Pack an agent with all its dep
|
|
|
3348
4119
|
program.command("unpack [package]").description("Unpack a packed agent to the local agents directory").option("--no-signatures", "Remove package signatures (fully editable)").action(unpack);
|
|
3349
4120
|
program.command("list-packed").description("List all discovered packed agents").action(listPacked);
|
|
3350
4121
|
program.command("deploy").description("Build and deploy your project to the Standard Agents platform").option("--api-key <key>", "Platform API key (overrides PLATFORM_API_KEY env var)").option("--endpoint <url>", "Platform API endpoint (default: https://api.standardagents.ai)").action(deploy);
|
|
4122
|
+
program.command("skill").description("Install the bundled AgentBuilder guidance into a supported coding agent").option("--agent <target>", "Install target: codex or claude").option("--dir <path>", "Install directory override for the selected target").option("--force", "Overwrite an existing installed skill").action(skill);
|
|
4123
|
+
program.command("current-models").description("Print the live curated model guide from standardagentspec.com").option("--url <url>", "Override the models markdown URL").action(currentModels);
|
|
4124
|
+
program.command("available-models").description("List every available model for one configured provider in the current project").option("--provider <provider>", "Provider to query, for example cerebras or openai").action(availableModels);
|
|
3351
4125
|
program.parse();
|
|
3352
4126
|
//# sourceMappingURL=index.js.map
|
|
3353
4127
|
//# sourceMappingURL=index.js.map
|