@standardagents/cli 0.12.8 → 0.13.0-next.c55f029
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/index.js +432 -22
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import path3 from 'path';
|
|
|
4
4
|
import fs3 from 'fs';
|
|
5
5
|
import crypto from 'crypto';
|
|
6
6
|
import readline from 'readline';
|
|
7
|
-
import { spawn } from 'child_process';
|
|
7
|
+
import { spawn, execSync } from 'child_process';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { parse, modify, applyEdits } from 'jsonc-parser';
|
|
10
10
|
import { loadFile, writeFile, generateCode } from 'magicast';
|
|
@@ -127,16 +127,37 @@ interface ThreadState {
|
|
|
127
127
|
agentId: string; // Agent identifier
|
|
128
128
|
userId: string | null; // User identifier (if set)
|
|
129
129
|
createdAt: number; // Creation timestamp
|
|
130
|
+
children: SubagentRegistryEntry[]; // Resumable child registry
|
|
131
|
+
terminated: number | null; // Soft-termination timestamp
|
|
130
132
|
|
|
131
133
|
// Methods
|
|
132
134
|
getMessages(options?): Promise<MessagesResult>; // Get conversation history
|
|
133
135
|
injectMessage(input): Promise<Message>; // Add message to thread
|
|
136
|
+
queueMessage(input): Promise<void>; // Queue message for next step
|
|
134
137
|
loadModel(name): Promise<ModelConfig>; // Load model definition
|
|
135
138
|
loadPrompt(name): Promise<PromptConfig>; // Load prompt definition
|
|
136
139
|
loadAgent(name): Promise<AgentConfig>; // Load agent definition
|
|
140
|
+
env(propertyName): Promise<string>; // Resolve env value for this thread
|
|
141
|
+
setEnv(propertyName, value): Promise<void>; // Set thread env + propagate to active descendants
|
|
142
|
+
notifyParent(content): Promise<void>; // Explicitly queue a silent message to the parent
|
|
143
|
+
setStatus(status): Promise<void>; // Update this child in the parent registry
|
|
144
|
+
getChildThread(referenceId): Promise<ThreadState | null>; // Resolve child thread
|
|
145
|
+
getParentThread(): Promise<ThreadState | null>; // Resolve parent thread
|
|
146
|
+
terminate(): Promise<void>; // Soft-terminate thread
|
|
137
147
|
}
|
|
138
148
|
\`\`\`
|
|
139
149
|
|
|
150
|
+
## Subagents
|
|
151
|
+
|
|
152
|
+
\`dual_ai\` agents can be used as subagents from prompt tool configs:
|
|
153
|
+
|
|
154
|
+
- Each subagent runs in its own thread (\`DurableThread\`)
|
|
155
|
+
- Parent/child linkage is tracked in thread metadata
|
|
156
|
+
- Resumable children are visible in \`state.children\`
|
|
157
|
+
- Parent threads create/message resumable children via built-ins (\`subagent_create\`, \`subagent_message\`)
|
|
158
|
+
- Child completion results are queued back to the parent when \`parentCommunication\` is \`implicit\`
|
|
159
|
+
- Explicit relationships can stay idle until code calls \`state.notifyParent()\` or \`state.setStatus()\`
|
|
160
|
+
|
|
140
161
|
Access in tools:
|
|
141
162
|
\`\`\`typescript
|
|
142
163
|
export default defineTool('...', schema, async (state, args) => {
|
|
@@ -297,12 +318,14 @@ Prompts define system instructions, model selection, and available tools for LLM
|
|
|
297
318
|
| \`toolDescription\` | \`string\` | Yes | Description shown when used as a tool |
|
|
298
319
|
| \`prompt\` | \`string \\| StructuredPrompt\` | Yes | System instructions |
|
|
299
320
|
| \`model\` | \`string\` | Yes | Model name to use (references \`agents/models/\`) |
|
|
300
|
-
| \`tools\` | \`(string \\| ToolConfig)[]\` | No | Available tools (include ai_human agent names for handoffs) |
|
|
321
|
+
| \`tools\` | \`(string \\| ToolConfig \\| SubagentToolConfig)[]\` | No | Available tools (include ai_human agent names for handoffs and dual_ai subagent configs) |
|
|
301
322
|
| \`includeChat\` | \`boolean\` | No | Include full conversation history |
|
|
302
323
|
| \`includePastTools\` | \`boolean\` | No | Include previous tool results |
|
|
303
324
|
| \`parallelToolCalls\` | \`boolean\` | No | Allow multiple concurrent tool calls |
|
|
304
325
|
| \`toolChoice\` | \`'auto' \\| 'none' \\| 'required'\` | No | Tool calling strategy |
|
|
305
326
|
| \`requiredSchema\` | \`z.ZodObject\` | No | Input validation when called as tool |
|
|
327
|
+
| \`variables\` | \`VariableDefinition[]\` | No | Variable declarations (\`name\`, \`type\`, \`required\`, \`scoped\`, \`description\`) |
|
|
328
|
+
| \`env\` | \`Record<string, string>\` | No | Prompt-level default values for variables |
|
|
306
329
|
| \`hooks\` | \`string[]\` | No | Hook IDs to run for this prompt (overrides agent hooks) |
|
|
307
330
|
| \`reasoning\` | \`ReasoningConfig\` | No | Extended thinking configuration |
|
|
308
331
|
|
|
@@ -335,11 +358,14 @@ export default definePrompt({
|
|
|
335
358
|
prompt: [
|
|
336
359
|
{ type: 'text', content: 'You are a helpful assistant.\\n\\n' },
|
|
337
360
|
{ type: 'include', prompt: 'common_rules' }, // Includes another prompt
|
|
361
|
+
{ type: 'env', property: 'ASSISTANT_TONE' }, // Inject variable value
|
|
338
362
|
{ type: 'text', content: '\\n\\nBe concise.' },
|
|
339
363
|
],
|
|
340
364
|
});
|
|
341
365
|
\`\`\`
|
|
342
366
|
|
|
367
|
+
\`type: 'env'\` inserts a resolved thread variable value. Use this only for non-secret values that are safe to expose to the LLM.
|
|
368
|
+
|
|
343
369
|
## Tool Configuration
|
|
344
370
|
|
|
345
371
|
Tools can be simple names or detailed configs:
|
|
@@ -357,6 +383,48 @@ tools: [
|
|
|
357
383
|
],
|
|
358
384
|
\`\`\`
|
|
359
385
|
|
|
386
|
+
### Subagent Tool Configuration
|
|
387
|
+
|
|
388
|
+
Use \`SubagentToolConfig\` when the tool is a \`dual_ai\` agent:
|
|
389
|
+
|
|
390
|
+
\`\`\`typescript
|
|
391
|
+
tools: [
|
|
392
|
+
// Blocking, non-resumable (default)
|
|
393
|
+
{ name: 'data_processor_agent' },
|
|
394
|
+
|
|
395
|
+
// Non-blocking, resumable with a single instance
|
|
396
|
+
{
|
|
397
|
+
name: 'browser_agent',
|
|
398
|
+
blocking: false,
|
|
399
|
+
immediate: {
|
|
400
|
+
nameEnv: 'BROWSER_AGENT_NAME',
|
|
401
|
+
descriptionEnv: 'BROWSER_AGENT_DESCRIPTION',
|
|
402
|
+
scopedEnv: ['BROWSER_AGENT_API_KEY'],
|
|
403
|
+
},
|
|
404
|
+
optional: 'ENABLE_BROWSER_AGENT',
|
|
405
|
+
initAgentNameProperty: 'name',
|
|
406
|
+
resumable: {
|
|
407
|
+
receives_messages: 'side_a',
|
|
408
|
+
maxInstances: 1,
|
|
409
|
+
parentCommunication: 'explicit',
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
]
|
|
413
|
+
\`\`\`
|
|
414
|
+
|
|
415
|
+
When prompt-configured resumable dual_ai subagents exist, the runtime auto-injects:
|
|
416
|
+
- \`subagent_create\` for spawning new child instances
|
|
417
|
+
- \`subagent_message\` for messaging active resumable instances
|
|
418
|
+
|
|
419
|
+
\`subagent_create\` supports optional \`name\` for human-friendly child thread labels.
|
|
420
|
+
Pair it with \`initAgentNameProperty\` to map that argument into a \`name:<value>\` thread tag.
|
|
421
|
+
|
|
422
|
+
Set \`immediate: true\` to run a tool automatically when the prompt activates.
|
|
423
|
+
Use \`immediate: { nameEnv, descriptionEnv, scopedEnv }\` when you want safe per-instance bootstrap hints while keeping copied scoped env runtime-only.
|
|
424
|
+
Set \`optional: 'ENV_NAME'\` to enable a subagent branch only when env resolves to \`true\`, \`1\`, or \`yes\`.
|
|
425
|
+
Set \`resumable.parentCommunication: 'explicit'\` when the child should stay quiet until its own tools or hooks call \`state.notifyParent()\` or \`state.setStatus()\`.
|
|
426
|
+
If required scoped vars are missing, the runtime returns \`error_code: "subagent_env_required"\` with a temporary \`GET/POST /api/threads/:id/variables/:requestId\` endpoint in \`error_data.endpoint\`.
|
|
427
|
+
|
|
360
428
|
## Input Validation
|
|
361
429
|
|
|
362
430
|
Validate inputs when prompt is called as a tool:
|
|
@@ -447,6 +515,7 @@ Agents orchestrate conversations by defining prompts, stop conditions, and turn
|
|
|
447
515
|
| \`maxSessionTurns\` | \`number\` | No | Max total turns across both sides |
|
|
448
516
|
| \`exposeAsTool\` | \`boolean\` | No | Allow other prompts to hand off to this agent |
|
|
449
517
|
| \`toolDescription\` | \`string\` | No | Description when used as tool (required if exposeAsTool) |
|
|
518
|
+
| \`env\` | \`Record<string, string>\` | No | Agent-level default variable values |
|
|
450
519
|
| \`hooks\` | \`string[]\` | No | Hook IDs to run for this agent (fallback if prompt has no hooks) |
|
|
451
520
|
| \`tags\` | \`string[]\` | No | Tags for categorization and filtering |
|
|
452
521
|
|
|
@@ -460,6 +529,8 @@ Agents orchestrate conversations by defining prompts, stop conditions, and turn
|
|
|
460
529
|
| \`stopTool\` | \`string\` | No | Tool that stops this side's turn |
|
|
461
530
|
| \`stopToolResponseProperty\` | \`string\` | No | Property to extract from stop tool result |
|
|
462
531
|
| \`endConversationTool\` | \`string\` | No | Tool that ends the entire conversation |
|
|
532
|
+
| \`failSessionTool\` | \`string\` | No | Tool that fails and ends the entire conversation |
|
|
533
|
+
| \`statusTool\` | \`string\` | No | Tool that reports subagent status to a parent registry |
|
|
463
534
|
| \`maxTurns\` | \`number\` | No | Maximum turns for this side |
|
|
464
535
|
| \`manualStopCondition\` | \`boolean\` | No | Enable custom stop handling via hooks |
|
|
465
536
|
|
|
@@ -504,6 +575,34 @@ export default defineAgent({
|
|
|
504
575
|
});
|
|
505
576
|
\`\`\`
|
|
506
577
|
|
|
578
|
+
## Dual-AI Subagent Pattern
|
|
579
|
+
|
|
580
|
+
When a \`dual_ai\` agent is used as a subagent tool, a common split is:
|
|
581
|
+
- **sideA**: performs work (tools, data gathering, execution)
|
|
582
|
+
- **sideB**: reflection, verification, and quality checks
|
|
583
|
+
|
|
584
|
+
\`\`\`typescript
|
|
585
|
+
export default defineAgent({
|
|
586
|
+
name: 'research_subagent',
|
|
587
|
+
type: 'dual_ai',
|
|
588
|
+
exposeAsTool: true,
|
|
589
|
+
toolDescription: 'Researches and validates findings autonomously',
|
|
590
|
+
maxSessionTurns: 16,
|
|
591
|
+
sideA: {
|
|
592
|
+
label: 'Worker',
|
|
593
|
+
prompt: 'research_worker',
|
|
594
|
+
statusTool: 'set_subagent_status',
|
|
595
|
+
failSessionTool: 'fail_research_subagent',
|
|
596
|
+
endConversationTool: 'finish_subagent',
|
|
597
|
+
},
|
|
598
|
+
sideB: {
|
|
599
|
+
label: 'Reviewer',
|
|
600
|
+
prompt: 'research_reviewer',
|
|
601
|
+
stopOnResponse: true,
|
|
602
|
+
},
|
|
603
|
+
});
|
|
604
|
+
\`\`\`
|
|
605
|
+
|
|
507
606
|
## Agent Handoffs
|
|
508
607
|
|
|
509
608
|
Expose an agent for handoffs from other prompts:
|
|
@@ -524,11 +623,12 @@ Other prompts can then include the agent name in their \`tools\` array to enable
|
|
|
524
623
|
|
|
525
624
|
## Stop Conditions (Priority Order)
|
|
526
625
|
|
|
527
|
-
1. **
|
|
528
|
-
2. **
|
|
529
|
-
3. **
|
|
530
|
-
4. **
|
|
531
|
-
5. **
|
|
626
|
+
1. **failSessionTool** - Ends entire conversation with a terminal failure
|
|
627
|
+
2. **endConversationTool** - Ends entire conversation successfully
|
|
628
|
+
3. **stopTool** - Ends current side's turn
|
|
629
|
+
4. **maxTurns** - Ends side's participation when reached
|
|
630
|
+
5. **stopOnResponse** - Ends turn when LLM returns text
|
|
631
|
+
6. **maxSessionTurns** - Ends conversation (hard limit: 250)
|
|
532
632
|
|
|
533
633
|
## Hooks
|
|
534
634
|
|
|
@@ -578,7 +678,7 @@ defineTool({
|
|
|
578
678
|
description: string, // What the tool does (shown to LLM)
|
|
579
679
|
args?: z.ZodObject, // Input validation schema (optional)
|
|
580
680
|
execute: (state, args) => ToolResult, // Implementation
|
|
581
|
-
|
|
681
|
+
variables?: VariableDefinition[], // Declared variables (optional)
|
|
582
682
|
})
|
|
583
683
|
\`\`\`
|
|
584
684
|
|
|
@@ -611,6 +711,8 @@ export default defineTool({
|
|
|
611
711
|
| \`status\` | \`'success' \\| 'error'\` | Whether the tool succeeded |
|
|
612
712
|
| \`result\` | \`string\` | Tool output (required for success) |
|
|
613
713
|
| \`error\` | \`string\` | Error message (for error status) |
|
|
714
|
+
| \`error_code\` | \`string\` | Machine-readable error code |
|
|
715
|
+
| \`error_data\` | \`Record<string, unknown>\` | Structured error payload |
|
|
614
716
|
| \`attachments\` | \`ToolAttachment[]\` | Files to attach (images, etc.) |
|
|
615
717
|
|
|
616
718
|
## ThreadState Access
|
|
@@ -630,8 +732,9 @@ execute: async (state, args) => {
|
|
|
630
732
|
// Message history
|
|
631
733
|
const messages = state.messages;
|
|
632
734
|
|
|
633
|
-
//
|
|
634
|
-
const
|
|
735
|
+
// Thread variable resolution
|
|
736
|
+
const apiKey = await state.env('GOOGLE_API_KEY');
|
|
737
|
+
await state.setEnv('LAST_TOOL_RUN_AT', new Date().toISOString());
|
|
635
738
|
|
|
636
739
|
// Parent state (for sub-prompts)
|
|
637
740
|
const root = state.rootState;
|
|
@@ -718,25 +821,33 @@ export default defineTool({
|
|
|
718
821
|
});
|
|
719
822
|
\`\`\`
|
|
720
823
|
|
|
721
|
-
##
|
|
824
|
+
## Variables and Env
|
|
722
825
|
|
|
723
|
-
Tools can declare required
|
|
826
|
+
Tools can declare variable requirements with \`variables\`, including \`required\` and \`description\` metadata:
|
|
724
827
|
|
|
725
828
|
\`\`\`typescript
|
|
726
829
|
export default defineTool({
|
|
727
830
|
description: 'Search uploaded documents',
|
|
728
831
|
args: z.object({ query: z.string() }),
|
|
729
832
|
execute: async (state, args) => {
|
|
730
|
-
const vectorStoreId = state.
|
|
833
|
+
const vectorStoreId = await state.env('VECTOR_STORE_ID');
|
|
731
834
|
const results = await searchVectorStore(vectorStoreId, args.query);
|
|
732
835
|
return { status: 'success', result: JSON.stringify(results) };
|
|
733
836
|
},
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
837
|
+
variables: [
|
|
838
|
+
{
|
|
839
|
+
name: 'VECTOR_STORE_ID',
|
|
840
|
+
type: 'text',
|
|
841
|
+
required: true,
|
|
842
|
+
scoped: false,
|
|
843
|
+
description: 'OpenAI Vector Store ID',
|
|
844
|
+
},
|
|
845
|
+
],
|
|
737
846
|
});
|
|
738
847
|
\`\`\`
|
|
739
848
|
|
|
849
|
+
Use \`scoped: true\` for per-instance values that should not inherit from parent thread env (for example per-account OAuth tokens on subagents).
|
|
850
|
+
|
|
740
851
|
## Returning Attachments
|
|
741
852
|
|
|
742
853
|
Tools can return file attachments (e.g., generated images):
|
|
@@ -1056,6 +1167,10 @@ export default defineThreadEndpoint(async (req, state) => {
|
|
|
1056
1167
|
order: 'desc',
|
|
1057
1168
|
});
|
|
1058
1169
|
|
|
1170
|
+
// Thread env (thread scope + active descendants)
|
|
1171
|
+
const approvalGate = await state.env('TOPDOWN_APPROVAL_GATE');
|
|
1172
|
+
await state.setEnv('TOPDOWN_APPROVAL_GATE', approvalGate || 'open');
|
|
1173
|
+
|
|
1059
1174
|
// File system operations
|
|
1060
1175
|
const files = await state.findFiles('**/*.json');
|
|
1061
1176
|
const data = await state.readFile('/data/config.json');
|
|
@@ -1702,7 +1817,7 @@ function getPackageVersion() {
|
|
|
1702
1817
|
if (process.env.STANDARDAGENTS_WORKSPACE === "1") {
|
|
1703
1818
|
return "workspace:*";
|
|
1704
1819
|
}
|
|
1705
|
-
return "0.
|
|
1820
|
+
return "0.13.0-next.c55f029";
|
|
1706
1821
|
}
|
|
1707
1822
|
async function selectPackageManager(detected) {
|
|
1708
1823
|
const options = ["npm", "pnpm", "yarn", "bun"];
|
|
@@ -1799,21 +1914,25 @@ async function init(projectNameArg, options = {}) {
|
|
|
1799
1914
|
switch (pm) {
|
|
1800
1915
|
case "pnpm":
|
|
1801
1916
|
createCmd = "pnpm";
|
|
1802
|
-
createArgs = ["create", "vite@latest", projectName, "--template", template, "--
|
|
1917
|
+
createArgs = ["create", "vite@latest", projectName, "--template", template, "--yes"];
|
|
1803
1918
|
break;
|
|
1804
1919
|
case "yarn":
|
|
1805
1920
|
createCmd = "yarn";
|
|
1806
|
-
createArgs = ["create", "vite@latest", projectName, "--template", template, "--
|
|
1921
|
+
createArgs = ["create", "vite@latest", projectName, "--template", template, "--yes"];
|
|
1807
1922
|
break;
|
|
1808
1923
|
case "bun":
|
|
1809
1924
|
createCmd = "bun";
|
|
1810
|
-
createArgs = ["create", "vite@latest", projectName, "--template", template, "--
|
|
1925
|
+
createArgs = ["create", "vite@latest", projectName, "--template", template, "--yes"];
|
|
1811
1926
|
break;
|
|
1812
1927
|
default:
|
|
1813
1928
|
createCmd = "npm";
|
|
1814
|
-
createArgs = ["create", "vite@latest", projectName, "--", "--template", template, "--
|
|
1929
|
+
createArgs = ["create", "vite@latest", projectName, "--", "--template", template, "--yes"];
|
|
1815
1930
|
}
|
|
1816
1931
|
await runCommand(createCmd, createArgs, cwd);
|
|
1932
|
+
const createdPackageJsonPath = path3.join(projectPath, "package.json");
|
|
1933
|
+
if (!fs3.existsSync(createdPackageJsonPath)) {
|
|
1934
|
+
throw new Error("Vite project scaffolding did not create package.json");
|
|
1935
|
+
}
|
|
1817
1936
|
} catch (error) {
|
|
1818
1937
|
logger.error("Failed to create Vite project");
|
|
1819
1938
|
process.exit(1);
|
|
@@ -2916,16 +3035,307 @@ async function listPacked() {
|
|
|
2916
3035
|
console.log("");
|
|
2917
3036
|
}
|
|
2918
3037
|
}
|
|
3038
|
+
var DEFAULT_ENDPOINT = "https://api.standardagents.ai";
|
|
3039
|
+
var FREE_TIER_BUNDLE_LIMIT = 5 * 1024 * 1024;
|
|
3040
|
+
function parseEnvFile(content) {
|
|
3041
|
+
const result = {};
|
|
3042
|
+
for (const line of content.split("\n")) {
|
|
3043
|
+
const trimmed = line.trim();
|
|
3044
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
3045
|
+
const eqIndex = trimmed.indexOf("=");
|
|
3046
|
+
if (eqIndex === -1) continue;
|
|
3047
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
3048
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
3049
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
3050
|
+
value = value.slice(1, -1).replace(/\\"/g, '"');
|
|
3051
|
+
} else if (value.startsWith("'") && value.endsWith("'")) {
|
|
3052
|
+
value = value.slice(1, -1);
|
|
3053
|
+
}
|
|
3054
|
+
result[key] = value;
|
|
3055
|
+
}
|
|
3056
|
+
return result;
|
|
3057
|
+
}
|
|
3058
|
+
function compareVersions(a, b) {
|
|
3059
|
+
const [coreA, preA] = a.replace(/^v/, "").split("-", 2);
|
|
3060
|
+
const [coreB, preB] = b.replace(/^v/, "").split("-", 2);
|
|
3061
|
+
const partsA = coreA.split(".").map(Number);
|
|
3062
|
+
const partsB = coreB.split(".").map(Number);
|
|
3063
|
+
for (let i = 0; i < 3; i++) {
|
|
3064
|
+
const va = partsA[i] ?? 0;
|
|
3065
|
+
const vb = partsB[i] ?? 0;
|
|
3066
|
+
if (va < vb) return -1;
|
|
3067
|
+
if (va > vb) return 1;
|
|
3068
|
+
}
|
|
3069
|
+
if (preA && !preB) return -1;
|
|
3070
|
+
if (!preA && preB) return 1;
|
|
3071
|
+
return 0;
|
|
3072
|
+
}
|
|
3073
|
+
function validateBundleSize(sizeBytes) {
|
|
3074
|
+
if (sizeBytes > FREE_TIER_BUNDLE_LIMIT) {
|
|
3075
|
+
return `Bundle size (${formatBytes(sizeBytes)}) exceeds the free tier limit (${formatBytes(FREE_TIER_BUNDLE_LIMIT)}). A paid plan may be required.`;
|
|
3076
|
+
}
|
|
3077
|
+
return null;
|
|
3078
|
+
}
|
|
3079
|
+
function createManifest(agents, builderVersion) {
|
|
3080
|
+
return {
|
|
3081
|
+
agents,
|
|
3082
|
+
builder_version: builderVersion
|
|
3083
|
+
};
|
|
3084
|
+
}
|
|
3085
|
+
function formatBytes(bytes) {
|
|
3086
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
3087
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
3088
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3089
|
+
}
|
|
3090
|
+
function formatDeployError(response) {
|
|
3091
|
+
if (response.error) {
|
|
3092
|
+
return `${response.error.code}: ${response.error.message}`;
|
|
3093
|
+
}
|
|
3094
|
+
if (response.errors && response.errors.length > 0) {
|
|
3095
|
+
return response.errors.join("\n");
|
|
3096
|
+
}
|
|
3097
|
+
return "Unknown error";
|
|
3098
|
+
}
|
|
3099
|
+
function detectProject() {
|
|
3100
|
+
const hasWranglerJsonc = fs3.existsSync("wrangler.jsonc") || fs3.existsSync("wrangler.json");
|
|
3101
|
+
const hasAgentsDir = fs3.existsSync("agents/agents");
|
|
3102
|
+
return hasWranglerJsonc || hasAgentsDir;
|
|
3103
|
+
}
|
|
3104
|
+
function loadConfig(options) {
|
|
3105
|
+
const envFileVars = fs3.existsSync(".env") ? parseEnvFile(fs3.readFileSync(".env", "utf-8")) : {};
|
|
3106
|
+
const apiKey = options.apiKey || process.env.PLATFORM_API_KEY || envFileVars.PLATFORM_API_KEY;
|
|
3107
|
+
if (!apiKey) {
|
|
3108
|
+
throw new Error(
|
|
3109
|
+
"No API key found. Provide one with --api-key, set PLATFORM_API_KEY env var, or add it to .env"
|
|
3110
|
+
);
|
|
3111
|
+
}
|
|
3112
|
+
const endpoint = options.endpoint || process.env.PLATFORM_ENDPOINT || envFileVars.PLATFORM_ENDPOINT || DEFAULT_ENDPOINT;
|
|
3113
|
+
return { apiKey, endpoint };
|
|
3114
|
+
}
|
|
3115
|
+
function discoverAgents() {
|
|
3116
|
+
const agentsDir = "agents/agents";
|
|
3117
|
+
if (!fs3.existsSync(agentsDir)) return [];
|
|
3118
|
+
const scriptExtensions = /* @__PURE__ */ new Set([".ts", ".js", ".mts", ".mjs"]);
|
|
3119
|
+
const files = fs3.readdirSync(agentsDir).filter((f) => scriptExtensions.has(path3.extname(f)));
|
|
3120
|
+
return files.map((file) => {
|
|
3121
|
+
const filePath = path3.join(agentsDir, file);
|
|
3122
|
+
const realPath = fs3.realpathSync(filePath);
|
|
3123
|
+
if (!realPath.startsWith(fs3.realpathSync("."))) {
|
|
3124
|
+
return path3.basename(file, path3.extname(file));
|
|
3125
|
+
}
|
|
3126
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
3127
|
+
const nameMatch = content.match(/defineAgent\s*\(\s*\{[\s\S]*?\bname\s*:\s*['"]([^'"]+)['"]/);
|
|
3128
|
+
return nameMatch ? nameMatch[1] : path3.basename(file, path3.extname(file));
|
|
3129
|
+
});
|
|
3130
|
+
}
|
|
3131
|
+
function getBuilderVersion() {
|
|
3132
|
+
const searchPaths = [
|
|
3133
|
+
"node_modules/@standardagents/builder/package.json",
|
|
3134
|
+
"../node_modules/@standardagents/builder/package.json"
|
|
3135
|
+
];
|
|
3136
|
+
for (const p of searchPaths) {
|
|
3137
|
+
if (fs3.existsSync(p)) {
|
|
3138
|
+
const pkg = JSON.parse(fs3.readFileSync(p, "utf-8"));
|
|
3139
|
+
return pkg.version;
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
throw new Error("Could not find @standardagents/builder. Is it installed?");
|
|
3143
|
+
}
|
|
3144
|
+
function detectPackageManager3() {
|
|
3145
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
3146
|
+
if (userAgent) {
|
|
3147
|
+
if (userAgent.includes("pnpm")) return "pnpm";
|
|
3148
|
+
if (userAgent.includes("yarn")) return "yarn";
|
|
3149
|
+
if (userAgent.includes("bun")) return "bun";
|
|
3150
|
+
}
|
|
3151
|
+
if (fs3.existsSync("pnpm-lock.yaml")) return "pnpm";
|
|
3152
|
+
if (fs3.existsSync("yarn.lock")) return "yarn";
|
|
3153
|
+
if (fs3.existsSync("bun.lockb") || fs3.existsSync("bun.lock")) return "bun";
|
|
3154
|
+
return "npm";
|
|
3155
|
+
}
|
|
3156
|
+
function buildProject(endpoint) {
|
|
3157
|
+
const pm = detectPackageManager3();
|
|
3158
|
+
const buildCmd = pm === "npm" ? "npx vite build" : `${pm} exec vite build`;
|
|
3159
|
+
execSync(buildCmd, {
|
|
3160
|
+
stdio: "inherit",
|
|
3161
|
+
env: {
|
|
3162
|
+
...process.env,
|
|
3163
|
+
PLATFORM_ENDPOINT: endpoint
|
|
3164
|
+
}
|
|
3165
|
+
});
|
|
3166
|
+
}
|
|
3167
|
+
function findWorkerBundle() {
|
|
3168
|
+
const searchDirs = [".wrangler/tmp", "dist"];
|
|
3169
|
+
for (const dir of searchDirs) {
|
|
3170
|
+
if (!fs3.existsSync(dir)) continue;
|
|
3171
|
+
const bundle = findBundleInDir(dir);
|
|
3172
|
+
if (bundle) return bundle;
|
|
3173
|
+
}
|
|
3174
|
+
throw new Error(
|
|
3175
|
+
"Could not find Worker bundle in build output.\nExpected build output in .wrangler/tmp/ or dist/.\nMake sure `vite build` completed successfully."
|
|
3176
|
+
);
|
|
3177
|
+
}
|
|
3178
|
+
function findBundleInDir(dir) {
|
|
3179
|
+
const entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
3180
|
+
const jsFiles = entries.filter((e) => e.isFile() && (e.name.endsWith(".js") || e.name.endsWith(".mjs")));
|
|
3181
|
+
jsFiles.sort((a, b) => {
|
|
3182
|
+
const aIsIndex = a.name === "index.js" || a.name === "index.mjs" ? 0 : 1;
|
|
3183
|
+
const bIsIndex = b.name === "index.js" || b.name === "index.mjs" ? 0 : 1;
|
|
3184
|
+
if (aIsIndex !== bIsIndex) return aIsIndex - bIsIndex;
|
|
3185
|
+
const aSize = fs3.statSync(path3.join(dir, a.name)).size;
|
|
3186
|
+
const bSize = fs3.statSync(path3.join(dir, b.name)).size;
|
|
3187
|
+
return bSize - aSize;
|
|
3188
|
+
});
|
|
3189
|
+
for (const entry of jsFiles) {
|
|
3190
|
+
const fullPath = path3.join(dir, entry.name);
|
|
3191
|
+
const content = fs3.readFileSync(fullPath);
|
|
3192
|
+
if (content.length > 0 && content.toString("utf-8", 0, 1e4).includes("export")) {
|
|
3193
|
+
return { bundlePath: fullPath, bundleContent: content };
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
for (const entry of entries) {
|
|
3197
|
+
if (entry.isDirectory() && entry.name !== "client" && entry.name !== "node_modules") {
|
|
3198
|
+
const result = findBundleInDir(path3.join(dir, entry.name));
|
|
3199
|
+
if (result) return result;
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
return null;
|
|
3203
|
+
}
|
|
3204
|
+
async function checkPlatformVersion(endpoint, builderVersion) {
|
|
3205
|
+
const res = await fetch(`${endpoint}/manifest`);
|
|
3206
|
+
if (!res.ok) {
|
|
3207
|
+
throw new Error(`Failed to check platform version: ${res.status} ${res.statusText}`);
|
|
3208
|
+
}
|
|
3209
|
+
const manifest = await res.json();
|
|
3210
|
+
if (compareVersions(builderVersion, manifest.minimum_builder_version) < 0) {
|
|
3211
|
+
throw new Error(
|
|
3212
|
+
`Builder version ${builderVersion} is below the platform minimum (${manifest.minimum_builder_version}).
|
|
3213
|
+
Please upgrade @standardagents/builder:
|
|
3214
|
+
npm install @standardagents/builder@latest`
|
|
3215
|
+
);
|
|
3216
|
+
}
|
|
3217
|
+
return manifest;
|
|
3218
|
+
}
|
|
3219
|
+
async function uploadDeploy(endpoint, apiKey, bundleContent, manifest, builderVersion) {
|
|
3220
|
+
const formData = new FormData();
|
|
3221
|
+
formData.append("bundle", new Blob([new Uint8Array(bundleContent)], { type: "application/javascript" }), "index.js");
|
|
3222
|
+
formData.append("manifest", new Blob([JSON.stringify(manifest)], { type: "application/json" }), "manifest.json");
|
|
3223
|
+
formData.append("builder_version", builderVersion);
|
|
3224
|
+
formData.append("agent_count", String(manifest.agents.length));
|
|
3225
|
+
const res = await fetch(`${endpoint}/deploys`, {
|
|
3226
|
+
method: "POST",
|
|
3227
|
+
headers: {
|
|
3228
|
+
"Authorization": `Bearer ${apiKey}`
|
|
3229
|
+
},
|
|
3230
|
+
body: formData
|
|
3231
|
+
});
|
|
3232
|
+
let body;
|
|
3233
|
+
try {
|
|
3234
|
+
body = await res.json();
|
|
3235
|
+
} catch {
|
|
3236
|
+
throw new Error(`Deploy failed (${res.status}): Invalid response from server`);
|
|
3237
|
+
}
|
|
3238
|
+
if (!res.ok) {
|
|
3239
|
+
const errorMsg = formatDeployError(body);
|
|
3240
|
+
throw new Error(`Deploy failed (${res.status}): ${errorMsg}`);
|
|
3241
|
+
}
|
|
3242
|
+
return body;
|
|
3243
|
+
}
|
|
3244
|
+
async function deploy(options) {
|
|
3245
|
+
console.log("");
|
|
3246
|
+
logger.info(`${chalk.bold("Standard Agents Deploy")}
|
|
3247
|
+
`);
|
|
3248
|
+
if (!detectProject()) {
|
|
3249
|
+
logger.error("This does not appear to be a Standard Agents project.");
|
|
3250
|
+
logger.info("Run this command from a directory with wrangler.jsonc and agents/agents/.");
|
|
3251
|
+
process.exit(1);
|
|
3252
|
+
}
|
|
3253
|
+
let apiKey;
|
|
3254
|
+
let endpoint;
|
|
3255
|
+
try {
|
|
3256
|
+
({ apiKey, endpoint } = loadConfig(options));
|
|
3257
|
+
} catch (err) {
|
|
3258
|
+
logger.error(err.message);
|
|
3259
|
+
process.exit(1);
|
|
3260
|
+
}
|
|
3261
|
+
logger.success("Configuration loaded");
|
|
3262
|
+
const agents = discoverAgents();
|
|
3263
|
+
if (agents.length === 0) {
|
|
3264
|
+
logger.error("No agents found in agents/agents/.");
|
|
3265
|
+
logger.info("Create at least one agent with defineAgent() before deploying.");
|
|
3266
|
+
process.exit(1);
|
|
3267
|
+
}
|
|
3268
|
+
logger.success(`Found ${agents.length} agent${agents.length === 1 ? "" : "s"}: ${agents.join(", ")}`);
|
|
3269
|
+
let builderVersion;
|
|
3270
|
+
try {
|
|
3271
|
+
builderVersion = getBuilderVersion();
|
|
3272
|
+
} catch (err) {
|
|
3273
|
+
logger.error(err.message);
|
|
3274
|
+
process.exit(1);
|
|
3275
|
+
}
|
|
3276
|
+
logger.info(`Builder version: ${builderVersion}`);
|
|
3277
|
+
try {
|
|
3278
|
+
const platformManifest = await checkPlatformVersion(endpoint, builderVersion);
|
|
3279
|
+
if (compareVersions(builderVersion, platformManifest.latest_builder_version) < 0) {
|
|
3280
|
+
logger.warning(
|
|
3281
|
+
`A newer builder version is available (${platformManifest.latest_builder_version}). Consider upgrading.`
|
|
3282
|
+
);
|
|
3283
|
+
}
|
|
3284
|
+
} catch (err) {
|
|
3285
|
+
logger.error(err.message);
|
|
3286
|
+
process.exit(1);
|
|
3287
|
+
}
|
|
3288
|
+
logger.info("Building project...\n");
|
|
3289
|
+
try {
|
|
3290
|
+
buildProject(endpoint);
|
|
3291
|
+
} catch {
|
|
3292
|
+
console.log("");
|
|
3293
|
+
logger.error("Build failed. See output above for details.");
|
|
3294
|
+
process.exit(1);
|
|
3295
|
+
}
|
|
3296
|
+
console.log("");
|
|
3297
|
+
logger.success("Build complete");
|
|
3298
|
+
let bundlePath;
|
|
3299
|
+
let bundleContent;
|
|
3300
|
+
try {
|
|
3301
|
+
({ bundlePath, bundleContent } = findWorkerBundle());
|
|
3302
|
+
} catch (err) {
|
|
3303
|
+
logger.error(err.message);
|
|
3304
|
+
process.exit(1);
|
|
3305
|
+
}
|
|
3306
|
+
const manifest = createManifest(agents, builderVersion);
|
|
3307
|
+
logger.success(`Bundle: ${bundlePath} (${formatBytes(bundleContent.length)})`);
|
|
3308
|
+
const sizeWarning = validateBundleSize(bundleContent.length);
|
|
3309
|
+
if (sizeWarning) {
|
|
3310
|
+
logger.warning(sizeWarning);
|
|
3311
|
+
}
|
|
3312
|
+
logger.info("Uploading...");
|
|
3313
|
+
try {
|
|
3314
|
+
const result = await uploadDeploy(endpoint, apiKey, bundleContent, manifest, builderVersion);
|
|
3315
|
+
console.log("");
|
|
3316
|
+
logger.success(chalk.bold("Deploy successful!"));
|
|
3317
|
+
console.log(` Deploy ID: ${result.deploy.id}`);
|
|
3318
|
+
console.log(` Status: ${result.deploy.status}`);
|
|
3319
|
+
if (result.deploy.account_id) {
|
|
3320
|
+
console.log(` URL: https://${result.deploy.account_id}.standardagents.ai`);
|
|
3321
|
+
}
|
|
3322
|
+
console.log("");
|
|
3323
|
+
} catch (err) {
|
|
3324
|
+
logger.error(err.message);
|
|
3325
|
+
process.exit(1);
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
2919
3328
|
|
|
2920
3329
|
// src/index.ts
|
|
2921
3330
|
var program = new Command();
|
|
2922
|
-
program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version("0.
|
|
3331
|
+
program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version("0.13.0-next.c55f029");
|
|
2923
3332
|
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);
|
|
2924
3333
|
program.command("scaffold").description("Add Standard Agents to an existing Vite project").option("--force", "Overwrite existing configuration").action(scaffold);
|
|
2925
3334
|
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);
|
|
2926
3335
|
program.command("pack [agent-name]").description("Pack an agent with all its dependencies").option("--output <dir>", "Output directory (default: agents/packed)").option("--npm", "Generate npm-ready package structure").option("--version <version>", "Package version (default: 1.0.0)").option("--no-remove", "Keep original files after packing").option("--no-interactive", "Skip interactive item mode selector").action(pack);
|
|
2927
3336
|
program.command("unpack [package]").description("Unpack a packed agent to the local agents directory").option("--no-signatures", "Remove package signatures (fully editable)").action(unpack);
|
|
2928
3337
|
program.command("list-packed").description("List all discovered packed agents").action(listPacked);
|
|
3338
|
+
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);
|
|
2929
3339
|
program.parse();
|
|
2930
3340
|
//# sourceMappingURL=index.js.map
|
|
2931
3341
|
//# sourceMappingURL=index.js.map
|