@standardagents/cli 0.10.1-next.bbd142a → 0.11.0-next.14455ae
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/LICENSE.txt +48 -0
- package/chat/next/app/layout.tsx +24 -0
- package/chat/next/app/page.tsx +21 -0
- package/chat/next/next-env.d.ts +6 -0
- package/chat/next/next.config.ts +15 -0
- package/chat/next/postcss.config.mjs +5 -0
- package/chat/next/tsconfig.json +27 -0
- package/chat/package.json +32 -0
- package/chat/src/App.tsx +130 -0
- package/chat/src/components/AgentSelector.tsx +102 -0
- package/chat/src/components/Chat.tsx +134 -0
- package/chat/src/components/EmptyState.tsx +437 -0
- package/chat/src/components/Login.tsx +139 -0
- package/chat/src/components/Logo.tsx +39 -0
- package/chat/src/components/Markdown.tsx +222 -0
- package/chat/src/components/MessageInput.tsx +197 -0
- package/chat/src/components/MessageList.tsx +850 -0
- package/chat/src/components/Sidebar.tsx +253 -0
- package/chat/src/hooks/useAuth.tsx +118 -0
- package/chat/src/hooks/useTheme.tsx +55 -0
- package/chat/src/hooks/useThreads.ts +131 -0
- package/chat/src/index.css +168 -0
- package/chat/tsconfig.json +24 -0
- package/chat/vite/favicon.svg +3 -0
- package/chat/vite/index.html +17 -0
- package/chat/vite/main.tsx +25 -0
- package/chat/vite/vite.config.ts +23 -0
- package/dist/index.js +827 -203
- package/dist/index.js.map +1 -1
- package/package.json +13 -9
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import fs3, { readFileSync } from 'fs';
|
|
4
|
+
import path3, { dirname, resolve } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import crypto from 'crypto';
|
|
7
7
|
import readline from 'readline';
|
|
@@ -10,6 +10,7 @@ import chalk from 'chalk';
|
|
|
10
10
|
import { parse, modify, applyEdits } from 'jsonc-parser';
|
|
11
11
|
import { loadFile, writeFile, generateCode } from 'magicast';
|
|
12
12
|
import { addVitePlugin } from 'magicast/helpers';
|
|
13
|
+
import net from 'net';
|
|
13
14
|
|
|
14
15
|
var logger = {
|
|
15
16
|
success: (message) => {
|
|
@@ -115,30 +116,32 @@ agents/
|
|
|
115
116
|
|
|
116
117
|
Files are **auto-discovered** at runtime. No manual registration needed.
|
|
117
118
|
|
|
118
|
-
##
|
|
119
|
+
## ThreadState
|
|
119
120
|
|
|
120
|
-
\`
|
|
121
|
+
\`ThreadState\` is the state object available in tools and hooks:
|
|
121
122
|
|
|
122
123
|
\`\`\`typescript
|
|
123
|
-
interface
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
interface ThreadState {
|
|
125
|
+
// Identity (readonly)
|
|
126
|
+
threadId: string; // Thread identifier
|
|
127
|
+
agentId: string; // Agent identifier
|
|
128
|
+
userId: string | null; // User identifier (if set)
|
|
129
|
+
createdAt: number; // Creation timestamp
|
|
130
|
+
|
|
131
|
+
// Methods
|
|
132
|
+
getMessages(options?): Promise<MessagesResult>; // Get conversation history
|
|
133
|
+
injectMessage(input): Promise<Message>; // Add message to thread
|
|
134
|
+
loadModel(name): Promise<ModelConfig>; // Load model definition
|
|
135
|
+
loadPrompt(name): Promise<PromptConfig>; // Load prompt definition
|
|
136
|
+
loadAgent(name): Promise<AgentConfig>; // Load agent definition
|
|
134
137
|
}
|
|
135
138
|
\`\`\`
|
|
136
139
|
|
|
137
140
|
Access in tools:
|
|
138
141
|
\`\`\`typescript
|
|
139
142
|
export default defineTool('...', schema, async (flow, args) => {
|
|
140
|
-
const threadId = flow.
|
|
141
|
-
const messages = flow.
|
|
143
|
+
const threadId = flow.threadId;
|
|
144
|
+
const { messages } = await flow.getMessages();
|
|
142
145
|
// ...
|
|
143
146
|
});
|
|
144
147
|
\`\`\`
|
|
@@ -205,15 +208,24 @@ Models define which LLM provider and model to use for prompts.
|
|
|
205
208
|
import { defineModel } from '@standardagents/builder';
|
|
206
209
|
|
|
207
210
|
export default defineModel({
|
|
208
|
-
name: '
|
|
209
|
-
provider: '
|
|
210
|
-
model: '
|
|
211
|
-
fallbacks: ['
|
|
212
|
-
inputPrice: 2.5,
|
|
213
|
-
outputPrice: 10,
|
|
211
|
+
name: 'default',
|
|
212
|
+
provider: 'openrouter',
|
|
213
|
+
model: 'google/gemini-3-flash-preview',
|
|
214
|
+
fallbacks: ['fast', 'cheap-heavy'],
|
|
214
215
|
});
|
|
215
216
|
\`\`\`
|
|
216
217
|
|
|
218
|
+
## Recommended Models (OpenRouter)
|
|
219
|
+
|
|
220
|
+
| Use Case | Model ID | Description |
|
|
221
|
+
|----------|----------|-------------|
|
|
222
|
+
| Fast/Cheap | \`z-ai/glm-4.5-air\` | Quick responses, low cost |
|
|
223
|
+
| Mid-tier | \`google/gemini-3-flash-preview\` | Good balance of speed and quality |
|
|
224
|
+
| Cheap Heavy | \`z-ai/glm-4.7\` | More capable, still affordable |
|
|
225
|
+
| Heavy | \`google/gemini-3-pro-preview\` | Most capable, for complex tasks |
|
|
226
|
+
|
|
227
|
+
**\u26A0\uFE0F Google Models**: When using Google models (\`google/gemini-*\`), you must enable \`reasoning\` in your prompt configuration or tool calls will fail. See the prompts CLAUDE.md for details.
|
|
228
|
+
|
|
217
229
|
## Provider API Keys
|
|
218
230
|
|
|
219
231
|
Set these environment variables in \`.dev.vars\` (local) or Cloudflare secrets (production):
|
|
@@ -241,16 +253,16 @@ For OpenRouter, use the full model path:
|
|
|
241
253
|
|
|
242
254
|
\`\`\`typescript
|
|
243
255
|
export default defineModel({
|
|
244
|
-
name: '
|
|
256
|
+
name: 'heavy',
|
|
245
257
|
provider: 'openrouter',
|
|
246
|
-
model: '
|
|
247
|
-
includedProviders: ['
|
|
258
|
+
model: 'google/gemini-3-pro-preview',
|
|
259
|
+
includedProviders: ['google'], // Prefer Google's servers
|
|
248
260
|
});
|
|
249
261
|
\`\`\`
|
|
250
262
|
|
|
251
263
|
## Best Practices
|
|
252
264
|
|
|
253
|
-
- **Name by use case**, not model ID (e.g., \`
|
|
265
|
+
- **Name by use case**, not model ID (e.g., \`fast\`, \`default\`, \`heavy\`)
|
|
254
266
|
- **Configure fallbacks** for production reliability
|
|
255
267
|
- **Set pricing** for cost tracking in logs
|
|
256
268
|
- **Use environment variables** for API keys, never hardcode
|
|
@@ -273,15 +285,12 @@ Prompts define system instructions, model selection, and available tools for LLM
|
|
|
273
285
|
| \`toolDescription\` | \`string\` | Yes | Description shown when used as a tool |
|
|
274
286
|
| \`prompt\` | \`string \\| StructuredPrompt\` | Yes | System instructions |
|
|
275
287
|
| \`model\` | \`string\` | Yes | Model name to use (references \`agents/models/\`) |
|
|
276
|
-
| \`tools\` | \`(string \\| ToolConfig)[]\` | No | Available tools for
|
|
277
|
-
| \`handoffAgents\` | \`string[]\` | No | Agents this prompt can hand off to |
|
|
288
|
+
| \`tools\` | \`(string \\| ToolConfig)[]\` | No | Available tools (include ai_human agent names for handoffs) |
|
|
278
289
|
| \`includeChat\` | \`boolean\` | No | Include full conversation history |
|
|
279
290
|
| \`includePastTools\` | \`boolean\` | No | Include previous tool results |
|
|
280
291
|
| \`parallelToolCalls\` | \`boolean\` | No | Allow multiple concurrent tool calls |
|
|
281
292
|
| \`toolChoice\` | \`'auto' \\| 'none' \\| 'required'\` | No | Tool calling strategy |
|
|
282
293
|
| \`requiredSchema\` | \`z.ZodObject\` | No | Input validation when called as tool |
|
|
283
|
-
| \`beforeTool\` | \`string\` | No | Tool to run before LLM request |
|
|
284
|
-
| \`afterTool\` | \`string\` | No | Tool to run after LLM response |
|
|
285
294
|
| \`reasoning\` | \`ReasoningConfig\` | No | Extended thinking configuration |
|
|
286
295
|
|
|
287
296
|
## Basic Example
|
|
@@ -292,7 +301,7 @@ import { definePrompt } from '@standardagents/builder';
|
|
|
292
301
|
export default definePrompt({
|
|
293
302
|
name: 'customer_support',
|
|
294
303
|
toolDescription: 'Handle customer support inquiries',
|
|
295
|
-
model: '
|
|
304
|
+
model: 'default',
|
|
296
305
|
prompt: \`You are a helpful customer support agent.
|
|
297
306
|
Always be polite and professional.
|
|
298
307
|
If you cannot help, escalate to a human.\`,
|
|
@@ -309,7 +318,7 @@ Include other prompts for reusable instruction blocks:
|
|
|
309
318
|
export default definePrompt({
|
|
310
319
|
name: 'main_assistant',
|
|
311
320
|
toolDescription: 'Primary assistant',
|
|
312
|
-
model: '
|
|
321
|
+
model: 'default',
|
|
313
322
|
prompt: [
|
|
314
323
|
{ type: 'text', content: 'You are a helpful assistant.\\n\\n' },
|
|
315
324
|
{ type: 'include', prompt: 'common_rules' }, // Includes another prompt
|
|
@@ -345,7 +354,7 @@ import { z } from 'zod';
|
|
|
345
354
|
export default definePrompt({
|
|
346
355
|
name: 'data_extractor',
|
|
347
356
|
toolDescription: 'Extract structured data from text',
|
|
348
|
-
model: '
|
|
357
|
+
model: 'default',
|
|
349
358
|
prompt: 'Extract the requested data from the input.',
|
|
350
359
|
requiredSchema: z.object({
|
|
351
360
|
text: z.string().describe('Text to extract from'),
|
|
@@ -362,7 +371,7 @@ Enable for complex reasoning tasks:
|
|
|
362
371
|
export default definePrompt({
|
|
363
372
|
name: 'code_reviewer',
|
|
364
373
|
toolDescription: 'Review code for issues',
|
|
365
|
-
model: '
|
|
374
|
+
model: 'heavy',
|
|
366
375
|
prompt: 'Review the code thoroughly...',
|
|
367
376
|
reasoning: {
|
|
368
377
|
effort: 'high', // 'low' | 'medium' | 'high'
|
|
@@ -372,6 +381,14 @@ export default definePrompt({
|
|
|
372
381
|
});
|
|
373
382
|
\`\`\`
|
|
374
383
|
|
|
384
|
+
**\u26A0\uFE0F Google Models Require Reasoning**: When using Google models (\`google/gemini-*\`) via OpenRouter, you **must** include \`reasoning\` configuration or tool calls will fail. At minimum:
|
|
385
|
+
|
|
386
|
+
\`\`\`typescript
|
|
387
|
+
reasoning: {
|
|
388
|
+
effort: 'low', // Can be 'low', 'medium', or 'high'
|
|
389
|
+
},
|
|
390
|
+
\`\`\`
|
|
391
|
+
|
|
375
392
|
## Best Practices
|
|
376
393
|
|
|
377
394
|
- **Write clear instructions** - Structure with headers and bullet points
|
|
@@ -473,7 +490,7 @@ export default defineAgent({
|
|
|
473
490
|
});
|
|
474
491
|
\`\`\`
|
|
475
492
|
|
|
476
|
-
Other prompts can then include
|
|
493
|
+
Other prompts can then include the agent name in their \`tools\` array to enable handoffs.
|
|
477
494
|
|
|
478
495
|
## Stop Conditions (Priority Order)
|
|
479
496
|
|
|
@@ -483,9 +500,15 @@ Other prompts can then include it in their \`handoffAgents\` array.
|
|
|
483
500
|
4. **stopOnResponse** - Ends turn when LLM returns text
|
|
484
501
|
5. **maxSessionTurns** - Ends conversation (hard limit: 250)
|
|
485
502
|
|
|
503
|
+
## Naming Convention
|
|
504
|
+
|
|
505
|
+
Agent names **must** end with the \`_agent\` suffix (e.g., \`support_agent\`, \`research_agent\`).
|
|
506
|
+
This convention is enforced by the builder UI and makes agents easily identifiable in logs and code.
|
|
507
|
+
|
|
486
508
|
## Best Practices
|
|
487
509
|
|
|
488
510
|
- **Use descriptive names** (\`customer_support_agent\` not \`agent1\`)
|
|
511
|
+
- **Always use the _agent suffix** - names like \`support_agent\`, \`research_agent\`
|
|
489
512
|
- **Always set maxTurns** as a safety limit
|
|
490
513
|
- **Match stop conditions to use case** - chat apps use stopOnResponse, workflows use stopTool
|
|
491
514
|
- **Use labels** for clarity in logs and UI
|
|
@@ -504,12 +527,12 @@ Tools extend agent capabilities beyond text generation. They allow LLMs to fetch
|
|
|
504
527
|
## Function Signature
|
|
505
528
|
|
|
506
529
|
\`\`\`typescript
|
|
507
|
-
defineTool(
|
|
530
|
+
defineTool({
|
|
508
531
|
description: string, // What the tool does (shown to LLM)
|
|
509
|
-
args
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
)
|
|
532
|
+
args?: z.ZodObject, // Input validation schema (optional)
|
|
533
|
+
execute: (state, args) => ToolResult, // Implementation
|
|
534
|
+
tenvs?: z.ZodObject, // Thread environment variables (optional)
|
|
535
|
+
})
|
|
513
536
|
\`\`\`
|
|
514
537
|
|
|
515
538
|
## Basic Example
|
|
@@ -518,20 +541,20 @@ defineTool(
|
|
|
518
541
|
import { defineTool } from '@standardagents/builder';
|
|
519
542
|
import { z } from 'zod';
|
|
520
543
|
|
|
521
|
-
export default defineTool(
|
|
522
|
-
'Search the knowledge base for articles matching a query',
|
|
523
|
-
z.object({
|
|
544
|
+
export default defineTool({
|
|
545
|
+
description: 'Search the knowledge base for articles matching a query',
|
|
546
|
+
args: z.object({
|
|
524
547
|
query: z.string().describe('Search query'),
|
|
525
548
|
limit: z.number().optional().default(10).describe('Max results'),
|
|
526
549
|
}),
|
|
527
|
-
async (
|
|
550
|
+
execute: async (state, args) => {
|
|
528
551
|
const results = await searchKB(args.query, args.limit);
|
|
529
552
|
return {
|
|
530
553
|
status: 'success',
|
|
531
554
|
result: JSON.stringify(results),
|
|
532
555
|
};
|
|
533
|
-
}
|
|
534
|
-
);
|
|
556
|
+
},
|
|
557
|
+
});
|
|
535
558
|
\`\`\`
|
|
536
559
|
|
|
537
560
|
## ToolResult Interface
|
|
@@ -541,44 +564,45 @@ export default defineTool(
|
|
|
541
564
|
| \`status\` | \`'success' \\| 'error'\` | Whether the tool succeeded |
|
|
542
565
|
| \`result\` | \`string\` | Tool output (required for success) |
|
|
543
566
|
| \`error\` | \`string\` | Error message (for error status) |
|
|
567
|
+
| \`attachments\` | \`ToolAttachment[]\` | Files to attach (images, etc.) |
|
|
544
568
|
|
|
545
|
-
##
|
|
569
|
+
## ThreadState Access
|
|
546
570
|
|
|
547
|
-
The \`
|
|
571
|
+
The \`state\` parameter provides access to:
|
|
548
572
|
|
|
549
573
|
\`\`\`typescript
|
|
550
|
-
async (
|
|
574
|
+
execute: async (state, args) => {
|
|
551
575
|
// Thread information
|
|
552
|
-
const threadId =
|
|
553
|
-
const thread =
|
|
576
|
+
const threadId = state.thread.id;
|
|
577
|
+
const thread = state.thread.instance;
|
|
554
578
|
|
|
555
579
|
// Configuration
|
|
556
|
-
const agentName =
|
|
557
|
-
const modelName =
|
|
580
|
+
const agentName = state.agent.name;
|
|
581
|
+
const modelName = state.model.name;
|
|
558
582
|
|
|
559
583
|
// Message history
|
|
560
|
-
const messages =
|
|
584
|
+
const messages = state.messages;
|
|
561
585
|
|
|
562
586
|
// Environment bindings
|
|
563
|
-
const env =
|
|
587
|
+
const env = state.env;
|
|
564
588
|
|
|
565
589
|
// Parent state (for sub-prompts)
|
|
566
|
-
const root =
|
|
590
|
+
const root = state.rootState;
|
|
567
591
|
}
|
|
568
592
|
\`\`\`
|
|
569
593
|
|
|
570
594
|
## Tool Without Arguments
|
|
571
595
|
|
|
572
596
|
\`\`\`typescript
|
|
573
|
-
export default defineTool(
|
|
574
|
-
'Get current server time',
|
|
575
|
-
async (
|
|
597
|
+
export default defineTool({
|
|
598
|
+
description: 'Get current server time',
|
|
599
|
+
execute: async (state) => {
|
|
576
600
|
return {
|
|
577
601
|
status: 'success',
|
|
578
602
|
result: new Date().toISOString(),
|
|
579
603
|
};
|
|
580
|
-
}
|
|
581
|
-
);
|
|
604
|
+
},
|
|
605
|
+
});
|
|
582
606
|
\`\`\`
|
|
583
607
|
|
|
584
608
|
## Error Handling
|
|
@@ -586,10 +610,10 @@ export default defineTool(
|
|
|
586
610
|
Return errors gracefully instead of throwing:
|
|
587
611
|
|
|
588
612
|
\`\`\`typescript
|
|
589
|
-
export default defineTool(
|
|
590
|
-
'Fetch user data',
|
|
591
|
-
z.object({ userId: z.string() }),
|
|
592
|
-
async (
|
|
613
|
+
export default defineTool({
|
|
614
|
+
description: 'Fetch user data',
|
|
615
|
+
args: z.object({ userId: z.string() }),
|
|
616
|
+
execute: async (state, args) => {
|
|
593
617
|
try {
|
|
594
618
|
const user = await fetchUser(args.userId);
|
|
595
619
|
return { status: 'success', result: JSON.stringify(user) };
|
|
@@ -599,8 +623,8 @@ export default defineTool(
|
|
|
599
623
|
error: \`Failed to fetch user: \${error.message}\`,
|
|
600
624
|
};
|
|
601
625
|
}
|
|
602
|
-
}
|
|
603
|
-
);
|
|
626
|
+
},
|
|
627
|
+
});
|
|
604
628
|
\`\`\`
|
|
605
629
|
|
|
606
630
|
## Queueing Additional Tools
|
|
@@ -610,21 +634,21 @@ Queue another tool to run after the current one:
|
|
|
610
634
|
\`\`\`typescript
|
|
611
635
|
import { queueTool } from '@standardagents/builder';
|
|
612
636
|
|
|
613
|
-
export default defineTool(
|
|
614
|
-
'Create order and send confirmation',
|
|
615
|
-
z.object({ items: z.array(z.string()) }),
|
|
616
|
-
async (
|
|
637
|
+
export default defineTool({
|
|
638
|
+
description: 'Create order and send confirmation',
|
|
639
|
+
args: z.object({ items: z.array(z.string()) }),
|
|
640
|
+
execute: async (state, args) => {
|
|
617
641
|
const order = await createOrder(args.items);
|
|
618
642
|
|
|
619
643
|
// Queue email tool to run next
|
|
620
|
-
queueTool(
|
|
644
|
+
queueTool(state, 'send_confirmation_email', {
|
|
621
645
|
orderId: order.id,
|
|
622
|
-
email:
|
|
646
|
+
email: state.thread.metadata.userEmail,
|
|
623
647
|
});
|
|
624
648
|
|
|
625
649
|
return { status: 'success', result: JSON.stringify(order) };
|
|
626
|
-
}
|
|
627
|
-
);
|
|
650
|
+
},
|
|
651
|
+
});
|
|
628
652
|
\`\`\`
|
|
629
653
|
|
|
630
654
|
## Injecting Messages
|
|
@@ -634,17 +658,61 @@ Add messages without triggering re-execution:
|
|
|
634
658
|
\`\`\`typescript
|
|
635
659
|
import { injectMessage } from '@standardagents/builder';
|
|
636
660
|
|
|
637
|
-
export default defineTool(
|
|
638
|
-
'Log audit event',
|
|
639
|
-
z.object({ event: z.string() }),
|
|
640
|
-
async (
|
|
641
|
-
await injectMessage(
|
|
661
|
+
export default defineTool({
|
|
662
|
+
description: 'Log audit event',
|
|
663
|
+
args: z.object({ event: z.string() }),
|
|
664
|
+
execute: async (state, args) => {
|
|
665
|
+
await injectMessage(state, {
|
|
642
666
|
role: 'system',
|
|
643
667
|
content: \`[AUDIT] \${args.event}\`,
|
|
644
668
|
});
|
|
645
669
|
return { status: 'success', result: 'Logged' };
|
|
646
|
-
}
|
|
647
|
-
);
|
|
670
|
+
},
|
|
671
|
+
});
|
|
672
|
+
\`\`\`
|
|
673
|
+
|
|
674
|
+
## Thread Environment Variables (tenvs)
|
|
675
|
+
|
|
676
|
+
Tools can declare required thread environment variables:
|
|
677
|
+
|
|
678
|
+
\`\`\`typescript
|
|
679
|
+
export default defineTool({
|
|
680
|
+
description: 'Search uploaded documents',
|
|
681
|
+
args: z.object({ query: z.string() }),
|
|
682
|
+
execute: async (state, args) => {
|
|
683
|
+
const vectorStoreId = state.tenvs.vectorStoreId;
|
|
684
|
+
const results = await searchVectorStore(vectorStoreId, args.query);
|
|
685
|
+
return { status: 'success', result: JSON.stringify(results) };
|
|
686
|
+
},
|
|
687
|
+
tenvs: z.object({
|
|
688
|
+
vectorStoreId: z.string().describe('OpenAI Vector Store ID'),
|
|
689
|
+
}),
|
|
690
|
+
});
|
|
691
|
+
\`\`\`
|
|
692
|
+
|
|
693
|
+
## Returning Attachments
|
|
694
|
+
|
|
695
|
+
Tools can return file attachments (e.g., generated images):
|
|
696
|
+
|
|
697
|
+
\`\`\`typescript
|
|
698
|
+
export default defineTool({
|
|
699
|
+
description: 'Generate a chart from data',
|
|
700
|
+
args: z.object({ data: z.array(z.number()) }),
|
|
701
|
+
execute: async (state, args) => {
|
|
702
|
+
const chartImage = await generateChart(args.data);
|
|
703
|
+
return {
|
|
704
|
+
status: 'success',
|
|
705
|
+
result: 'Chart generated successfully',
|
|
706
|
+
attachments: [{
|
|
707
|
+
name: 'chart.png',
|
|
708
|
+
mimeType: 'image/png',
|
|
709
|
+
data: chartImage.base64,
|
|
710
|
+
width: 800,
|
|
711
|
+
height: 600,
|
|
712
|
+
}],
|
|
713
|
+
};
|
|
714
|
+
},
|
|
715
|
+
});
|
|
648
716
|
\`\`\`
|
|
649
717
|
|
|
650
718
|
## Best Practices
|
|
@@ -654,7 +722,7 @@ export default defineTool(
|
|
|
654
722
|
- **Handle errors gracefully** - return error status, don't throw
|
|
655
723
|
- **Use snake_case** for file names (\`search_knowledge_base.ts\`)
|
|
656
724
|
- **Keep tools focused** - one task per tool
|
|
657
|
-
- **Use \`
|
|
725
|
+
- **Use \`state.rootState\`** when queueing from sub-prompts
|
|
658
726
|
|
|
659
727
|
## Supported Zod Types
|
|
660
728
|
|
|
@@ -958,17 +1026,19 @@ Full reference: https://docs.standardagentbuilder.com/core-concepts/api
|
|
|
958
1026
|
`;
|
|
959
1027
|
|
|
960
1028
|
// src/commands/scaffold.ts
|
|
961
|
-
var WRANGLER_TEMPLATE = (name) =>
|
|
1029
|
+
var WRANGLER_TEMPLATE = (name) => {
|
|
1030
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1031
|
+
return `{
|
|
962
1032
|
"$schema": "node_modules/wrangler/config-schema.json",
|
|
963
1033
|
"name": "${name}",
|
|
964
1034
|
"main": "worker/index.ts",
|
|
965
|
-
"compatibility_date": "
|
|
1035
|
+
"compatibility_date": "${today}",
|
|
966
1036
|
"compatibility_flags": ["nodejs_compat"],
|
|
967
1037
|
"observability": {
|
|
968
1038
|
"enabled": true
|
|
969
1039
|
},
|
|
970
1040
|
"assets": {
|
|
971
|
-
"directory": "dist",
|
|
1041
|
+
"directory": "dist/client",
|
|
972
1042
|
"not_found_handling": "single-page-application",
|
|
973
1043
|
"binding": "ASSETS",
|
|
974
1044
|
"run_worker_first": ["/**"]
|
|
@@ -997,6 +1067,7 @@ var WRANGLER_TEMPLATE = (name) => `{
|
|
|
997
1067
|
]
|
|
998
1068
|
}
|
|
999
1069
|
`;
|
|
1070
|
+
};
|
|
1000
1071
|
var THREAD_TS = `import { DurableThread } from 'virtual:@standardagents/builder'
|
|
1001
1072
|
|
|
1002
1073
|
export default class Thread extends DurableThread {}
|
|
@@ -1019,23 +1090,23 @@ export default {
|
|
|
1019
1090
|
export { DurableThread, DurableAgentBuilder }
|
|
1020
1091
|
`;
|
|
1021
1092
|
function getProjectName(cwd) {
|
|
1022
|
-
const packageJsonPath =
|
|
1023
|
-
if (
|
|
1093
|
+
const packageJsonPath = path3.join(cwd, "package.json");
|
|
1094
|
+
if (fs3.existsSync(packageJsonPath)) {
|
|
1024
1095
|
try {
|
|
1025
|
-
const pkg2 = JSON.parse(
|
|
1096
|
+
const pkg2 = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
|
|
1026
1097
|
if (pkg2.name) {
|
|
1027
1098
|
return pkg2.name.replace(/^@[^/]+\//, "");
|
|
1028
1099
|
}
|
|
1029
1100
|
} catch {
|
|
1030
1101
|
}
|
|
1031
1102
|
}
|
|
1032
|
-
return
|
|
1103
|
+
return path3.basename(cwd);
|
|
1033
1104
|
}
|
|
1034
1105
|
function findViteConfig(cwd) {
|
|
1035
1106
|
const candidates = ["vite.config.ts", "vite.config.js", "vite.config.mts", "vite.config.mjs"];
|
|
1036
1107
|
for (const candidate of candidates) {
|
|
1037
|
-
const configPath =
|
|
1038
|
-
if (
|
|
1108
|
+
const configPath = path3.join(cwd, candidate);
|
|
1109
|
+
if (fs3.existsSync(configPath)) {
|
|
1039
1110
|
return configPath;
|
|
1040
1111
|
}
|
|
1041
1112
|
}
|
|
@@ -1044,8 +1115,8 @@ function findViteConfig(cwd) {
|
|
|
1044
1115
|
function findWranglerConfig(cwd) {
|
|
1045
1116
|
const candidates = ["wrangler.jsonc", "wrangler.json"];
|
|
1046
1117
|
for (const candidate of candidates) {
|
|
1047
|
-
const configPath =
|
|
1048
|
-
if (
|
|
1118
|
+
const configPath = path3.join(cwd, candidate);
|
|
1119
|
+
if (fs3.existsSync(configPath)) {
|
|
1049
1120
|
return configPath;
|
|
1050
1121
|
}
|
|
1051
1122
|
}
|
|
@@ -1054,21 +1125,13 @@ function findWranglerConfig(cwd) {
|
|
|
1054
1125
|
async function modifyViteConfig(configPath, force) {
|
|
1055
1126
|
try {
|
|
1056
1127
|
const mod = await loadFile(configPath);
|
|
1057
|
-
const code =
|
|
1128
|
+
const code = fs3.readFileSync(configPath, "utf-8");
|
|
1058
1129
|
const hasCloudflare = code.includes("@cloudflare/vite-plugin") || code.includes("cloudflare()");
|
|
1059
1130
|
const hasAgentBuilder = code.includes("@standardagents/builder") || code.includes("agentbuilder()");
|
|
1060
1131
|
if (hasCloudflare && hasAgentBuilder && !force) {
|
|
1061
1132
|
logger.info("Vite config already includes Standard Agents plugins");
|
|
1062
1133
|
return true;
|
|
1063
1134
|
}
|
|
1064
|
-
if (!hasCloudflare || force) {
|
|
1065
|
-
addVitePlugin(mod, {
|
|
1066
|
-
from: "@cloudflare/vite-plugin",
|
|
1067
|
-
imported: "cloudflare",
|
|
1068
|
-
constructor: "cloudflare"
|
|
1069
|
-
});
|
|
1070
|
-
logger.success("Added cloudflare plugin to vite.config");
|
|
1071
|
-
}
|
|
1072
1135
|
if (!hasAgentBuilder || force) {
|
|
1073
1136
|
addVitePlugin(mod, {
|
|
1074
1137
|
from: "@standardagents/builder",
|
|
@@ -1078,6 +1141,14 @@ async function modifyViteConfig(configPath, force) {
|
|
|
1078
1141
|
});
|
|
1079
1142
|
logger.success("Added agentbuilder plugin to vite.config");
|
|
1080
1143
|
}
|
|
1144
|
+
if (!hasCloudflare || force) {
|
|
1145
|
+
addVitePlugin(mod, {
|
|
1146
|
+
from: "@cloudflare/vite-plugin",
|
|
1147
|
+
imported: "cloudflare",
|
|
1148
|
+
constructor: "cloudflare"
|
|
1149
|
+
});
|
|
1150
|
+
logger.success("Added cloudflare plugin to vite.config");
|
|
1151
|
+
}
|
|
1081
1152
|
await writeFile(mod, configPath);
|
|
1082
1153
|
return true;
|
|
1083
1154
|
} catch (error) {
|
|
@@ -1085,10 +1156,10 @@ async function modifyViteConfig(configPath, force) {
|
|
|
1085
1156
|
logger.log("");
|
|
1086
1157
|
logger.log("Please manually add the following to your vite.config.ts:");
|
|
1087
1158
|
logger.log("");
|
|
1088
|
-
logger.log(` import { cloudflare } from '@cloudflare/vite-plugin'`);
|
|
1089
1159
|
logger.log(` import { agentbuilder } from '@standardagents/builder'`);
|
|
1160
|
+
logger.log(` import { cloudflare } from '@cloudflare/vite-plugin'`);
|
|
1090
1161
|
logger.log("");
|
|
1091
|
-
logger.log(` plugins: [
|
|
1162
|
+
logger.log(` plugins: [agentbuilder({ mountPoint: '/' }), cloudflare()]`);
|
|
1092
1163
|
logger.log("");
|
|
1093
1164
|
return false;
|
|
1094
1165
|
}
|
|
@@ -1097,7 +1168,7 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
|
|
|
1097
1168
|
const existingConfig = findWranglerConfig(cwd);
|
|
1098
1169
|
if (existingConfig && !force) {
|
|
1099
1170
|
try {
|
|
1100
|
-
const text =
|
|
1171
|
+
const text = fs3.readFileSync(existingConfig, "utf-8");
|
|
1101
1172
|
const config = parse(text);
|
|
1102
1173
|
const hasBindings = config.durable_objects?.bindings?.some(
|
|
1103
1174
|
(b) => b.name === "AGENT_BUILDER_THREAD" || b.name === "AGENT_BUILDER"
|
|
@@ -1137,14 +1208,14 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
|
|
|
1137
1208
|
}
|
|
1138
1209
|
if (!config.assets) {
|
|
1139
1210
|
const edits = modify(result, ["assets"], {
|
|
1140
|
-
directory: "dist",
|
|
1211
|
+
directory: "dist/client",
|
|
1141
1212
|
not_found_handling: "single-page-application",
|
|
1142
1213
|
binding: "ASSETS",
|
|
1143
1214
|
run_worker_first: ["/**"]
|
|
1144
1215
|
}, {});
|
|
1145
1216
|
result = applyEdits(result, edits);
|
|
1146
1217
|
}
|
|
1147
|
-
|
|
1218
|
+
fs3.writeFileSync(existingConfig, result, "utf-8");
|
|
1148
1219
|
logger.success("Updated wrangler.jsonc with Standard Agents configuration");
|
|
1149
1220
|
return true;
|
|
1150
1221
|
} catch (error) {
|
|
@@ -1152,9 +1223,9 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
|
|
|
1152
1223
|
return false;
|
|
1153
1224
|
}
|
|
1154
1225
|
}
|
|
1155
|
-
const wranglerPath =
|
|
1226
|
+
const wranglerPath = path3.join(cwd, "wrangler.jsonc");
|
|
1156
1227
|
const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
1157
|
-
|
|
1228
|
+
fs3.writeFileSync(wranglerPath, WRANGLER_TEMPLATE(sanitizedName), "utf-8");
|
|
1158
1229
|
logger.success("Created wrangler.jsonc");
|
|
1159
1230
|
return true;
|
|
1160
1231
|
}
|
|
@@ -1162,33 +1233,33 @@ function getWorkerEntryPoint(cwd) {
|
|
|
1162
1233
|
const wranglerConfig = findWranglerConfig(cwd);
|
|
1163
1234
|
if (wranglerConfig) {
|
|
1164
1235
|
try {
|
|
1165
|
-
const text =
|
|
1236
|
+
const text = fs3.readFileSync(wranglerConfig, "utf-8");
|
|
1166
1237
|
const config = parse(text);
|
|
1167
1238
|
if (config.main) {
|
|
1168
|
-
return
|
|
1239
|
+
return path3.join(cwd, config.main);
|
|
1169
1240
|
}
|
|
1170
1241
|
} catch {
|
|
1171
1242
|
}
|
|
1172
1243
|
}
|
|
1173
|
-
return
|
|
1244
|
+
return path3.join(cwd, "worker", "index.ts");
|
|
1174
1245
|
}
|
|
1175
1246
|
async function createOrUpdateWorkerEntry(cwd, force) {
|
|
1176
1247
|
const entryPath = getWorkerEntryPoint(cwd);
|
|
1177
|
-
const entryDir =
|
|
1178
|
-
if (!
|
|
1179
|
-
|
|
1180
|
-
logger.success(`Created ${
|
|
1248
|
+
const entryDir = path3.dirname(entryPath);
|
|
1249
|
+
if (!fs3.existsSync(entryDir)) {
|
|
1250
|
+
fs3.mkdirSync(entryDir, { recursive: true });
|
|
1251
|
+
logger.success(`Created ${path3.relative(cwd, entryDir)} directory`);
|
|
1181
1252
|
}
|
|
1182
|
-
if (!
|
|
1183
|
-
|
|
1184
|
-
logger.success(`Created ${
|
|
1253
|
+
if (!fs3.existsSync(entryPath)) {
|
|
1254
|
+
fs3.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
|
|
1255
|
+
logger.success(`Created ${path3.relative(cwd, entryPath)}`);
|
|
1185
1256
|
return true;
|
|
1186
1257
|
}
|
|
1187
|
-
const content =
|
|
1258
|
+
const content = fs3.readFileSync(entryPath, "utf-8");
|
|
1188
1259
|
const hasRouter = content.includes("virtual:@standardagents/builder") && content.includes("router");
|
|
1189
1260
|
const hasDurableExports = content.includes("DurableThread") && content.includes("DurableAgentBuilder");
|
|
1190
1261
|
if (hasRouter && hasDurableExports && !force) {
|
|
1191
|
-
logger.info(`${
|
|
1262
|
+
logger.info(`${path3.relative(cwd, entryPath)} already configured`);
|
|
1192
1263
|
return true;
|
|
1193
1264
|
}
|
|
1194
1265
|
try {
|
|
@@ -1216,17 +1287,17 @@ async function createOrUpdateWorkerEntry(cwd, force) {
|
|
|
1216
1287
|
}
|
|
1217
1288
|
const { code } = generateCode(mod);
|
|
1218
1289
|
if (force || !hasRouter) {
|
|
1219
|
-
|
|
1220
|
-
logger.success(`Updated ${
|
|
1290
|
+
fs3.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
|
|
1291
|
+
logger.success(`Updated ${path3.relative(cwd, entryPath)} with Standard Agents router`);
|
|
1221
1292
|
}
|
|
1222
1293
|
return true;
|
|
1223
1294
|
} catch (error) {
|
|
1224
1295
|
if (force) {
|
|
1225
|
-
|
|
1226
|
-
logger.success(`Created ${
|
|
1296
|
+
fs3.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
|
|
1297
|
+
logger.success(`Created ${path3.relative(cwd, entryPath)}`);
|
|
1227
1298
|
return true;
|
|
1228
1299
|
}
|
|
1229
|
-
logger.warning(`Could not automatically modify ${
|
|
1300
|
+
logger.warning(`Could not automatically modify ${path3.relative(cwd, entryPath)}`);
|
|
1230
1301
|
logger.log("");
|
|
1231
1302
|
logger.log("Please ensure your worker entry point includes:");
|
|
1232
1303
|
logger.log("");
|
|
@@ -1245,31 +1316,31 @@ async function createOrUpdateWorkerEntry(cwd, force) {
|
|
|
1245
1316
|
}
|
|
1246
1317
|
}
|
|
1247
1318
|
function createDurableObjects(cwd) {
|
|
1248
|
-
const agentsDir =
|
|
1249
|
-
if (!
|
|
1250
|
-
|
|
1319
|
+
const agentsDir = path3.join(cwd, "agents");
|
|
1320
|
+
if (!fs3.existsSync(agentsDir)) {
|
|
1321
|
+
fs3.mkdirSync(agentsDir, { recursive: true });
|
|
1251
1322
|
}
|
|
1252
|
-
const threadPath =
|
|
1253
|
-
if (!
|
|
1254
|
-
|
|
1323
|
+
const threadPath = path3.join(agentsDir, "Thread.ts");
|
|
1324
|
+
if (!fs3.existsSync(threadPath)) {
|
|
1325
|
+
fs3.writeFileSync(threadPath, THREAD_TS, "utf-8");
|
|
1255
1326
|
logger.success("Created agents/Thread.ts");
|
|
1256
1327
|
} else {
|
|
1257
1328
|
logger.info("agents/Thread.ts already exists");
|
|
1258
1329
|
}
|
|
1259
|
-
const agentBuilderPath =
|
|
1260
|
-
if (!
|
|
1261
|
-
|
|
1330
|
+
const agentBuilderPath = path3.join(agentsDir, "AgentBuilder.ts");
|
|
1331
|
+
if (!fs3.existsSync(agentBuilderPath)) {
|
|
1332
|
+
fs3.writeFileSync(agentBuilderPath, AGENT_BUILDER_TS, "utf-8");
|
|
1262
1333
|
logger.success("Created agents/AgentBuilder.ts");
|
|
1263
1334
|
} else {
|
|
1264
1335
|
logger.info("agents/AgentBuilder.ts already exists");
|
|
1265
1336
|
}
|
|
1266
1337
|
}
|
|
1267
1338
|
function createDirectoriesAndDocs(cwd) {
|
|
1268
|
-
|
|
1269
|
-
const rootDocPath =
|
|
1270
|
-
if (!
|
|
1271
|
-
|
|
1272
|
-
logger.success("Created
|
|
1339
|
+
path3.join(cwd, "agents");
|
|
1340
|
+
const rootDocPath = path3.join(cwd, "CLAUDE.md");
|
|
1341
|
+
if (!fs3.existsSync(rootDocPath)) {
|
|
1342
|
+
fs3.writeFileSync(rootDocPath, ROOT_CLAUDE_MD, "utf-8");
|
|
1343
|
+
logger.success("Created CLAUDE.md");
|
|
1273
1344
|
}
|
|
1274
1345
|
const directories = [
|
|
1275
1346
|
{ path: "agents/agents", doc: AGENTS_CLAUDE_MD },
|
|
@@ -1280,26 +1351,26 @@ function createDirectoriesAndDocs(cwd) {
|
|
|
1280
1351
|
{ path: "agents/api", doc: API_CLAUDE_MD }
|
|
1281
1352
|
];
|
|
1282
1353
|
for (const dir of directories) {
|
|
1283
|
-
const dirPath =
|
|
1284
|
-
const docPath =
|
|
1285
|
-
if (!
|
|
1286
|
-
|
|
1354
|
+
const dirPath = path3.join(cwd, dir.path);
|
|
1355
|
+
const docPath = path3.join(dirPath, "CLAUDE.md");
|
|
1356
|
+
if (!fs3.existsSync(dirPath)) {
|
|
1357
|
+
fs3.mkdirSync(dirPath, { recursive: true });
|
|
1287
1358
|
logger.success(`Created ${dir.path}`);
|
|
1288
1359
|
}
|
|
1289
|
-
if (!
|
|
1290
|
-
|
|
1360
|
+
if (!fs3.existsSync(docPath)) {
|
|
1361
|
+
fs3.writeFileSync(docPath, dir.doc, "utf-8");
|
|
1291
1362
|
logger.success(`Created ${dir.path}/CLAUDE.md`);
|
|
1292
1363
|
}
|
|
1293
1364
|
}
|
|
1294
1365
|
}
|
|
1295
1366
|
function updateTsConfig(cwd) {
|
|
1296
|
-
const tsconfigPath =
|
|
1297
|
-
if (!
|
|
1367
|
+
const tsconfigPath = path3.join(cwd, "tsconfig.json");
|
|
1368
|
+
if (!fs3.existsSync(tsconfigPath)) {
|
|
1298
1369
|
logger.info("No tsconfig.json found, skipping TypeScript configuration");
|
|
1299
1370
|
return;
|
|
1300
1371
|
}
|
|
1301
1372
|
try {
|
|
1302
|
-
const text =
|
|
1373
|
+
const text = fs3.readFileSync(tsconfigPath, "utf-8");
|
|
1303
1374
|
const config = parse(text);
|
|
1304
1375
|
let result = text;
|
|
1305
1376
|
const types = config.compilerOptions?.types || [];
|
|
@@ -1321,7 +1392,7 @@ function updateTsConfig(cwd) {
|
|
|
1321
1392
|
result = applyEdits(result, edits);
|
|
1322
1393
|
}
|
|
1323
1394
|
if (newTypes.length > 0 || newIncludes.length > 0) {
|
|
1324
|
-
|
|
1395
|
+
fs3.writeFileSync(tsconfigPath, result, "utf-8");
|
|
1325
1396
|
logger.success("Updated tsconfig.json with Standard Agents types");
|
|
1326
1397
|
} else {
|
|
1327
1398
|
logger.info("tsconfig.json already configured");
|
|
@@ -1342,21 +1413,21 @@ function cleanupViteDefaults(cwd) {
|
|
|
1342
1413
|
];
|
|
1343
1414
|
const dirsToRemove = ["src", "public"];
|
|
1344
1415
|
for (const file of filesToRemove) {
|
|
1345
|
-
const filePath =
|
|
1346
|
-
if (
|
|
1416
|
+
const filePath = path3.join(cwd, file);
|
|
1417
|
+
if (fs3.existsSync(filePath)) {
|
|
1347
1418
|
try {
|
|
1348
|
-
|
|
1419
|
+
fs3.unlinkSync(filePath);
|
|
1349
1420
|
} catch {
|
|
1350
1421
|
}
|
|
1351
1422
|
}
|
|
1352
1423
|
}
|
|
1353
1424
|
for (const dir of dirsToRemove) {
|
|
1354
|
-
const dirPath =
|
|
1355
|
-
if (
|
|
1425
|
+
const dirPath = path3.join(cwd, dir);
|
|
1426
|
+
if (fs3.existsSync(dirPath)) {
|
|
1356
1427
|
try {
|
|
1357
|
-
const files =
|
|
1428
|
+
const files = fs3.readdirSync(dirPath);
|
|
1358
1429
|
if (files.length === 0) {
|
|
1359
|
-
|
|
1430
|
+
fs3.rmdirSync(dirPath);
|
|
1360
1431
|
}
|
|
1361
1432
|
} catch {
|
|
1362
1433
|
}
|
|
@@ -1388,8 +1459,8 @@ async function scaffold(options = {}) {
|
|
|
1388
1459
|
logger.log("");
|
|
1389
1460
|
logger.log("Your project structure:");
|
|
1390
1461
|
logger.log("");
|
|
1462
|
+
logger.log(" CLAUDE.md # Architecture documentation");
|
|
1391
1463
|
logger.log(" agents/");
|
|
1392
|
-
logger.log(" \u251C\u2500\u2500 CLAUDE.md # Architecture documentation");
|
|
1393
1464
|
logger.log(" \u251C\u2500\u2500 Thread.ts # Durable Object for threads");
|
|
1394
1465
|
logger.log(" \u251C\u2500\u2500 AgentBuilder.ts # Durable Object for agent state");
|
|
1395
1466
|
logger.log(" \u251C\u2500\u2500 agents/ # Agent definitions");
|
|
@@ -1490,12 +1561,22 @@ function detectPackageManager() {
|
|
|
1490
1561
|
if (userAgent.includes("yarn")) return "yarn";
|
|
1491
1562
|
if (userAgent.includes("bun")) return "bun";
|
|
1492
1563
|
}
|
|
1493
|
-
if (
|
|
1494
|
-
if (
|
|
1495
|
-
if (
|
|
1496
|
-
if (
|
|
1564
|
+
if (fs3.existsSync("pnpm-lock.yaml")) return "pnpm";
|
|
1565
|
+
if (fs3.existsSync("yarn.lock")) return "yarn";
|
|
1566
|
+
if (fs3.existsSync("bun.lockb")) return "bun";
|
|
1567
|
+
if (fs3.existsSync("package-lock.json")) return "npm";
|
|
1497
1568
|
return "npm";
|
|
1498
1569
|
}
|
|
1570
|
+
function getCliVersion() {
|
|
1571
|
+
try {
|
|
1572
|
+
const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
|
|
1573
|
+
const pkgPath = path3.resolve(__dirname2, "../../package.json");
|
|
1574
|
+
const pkg2 = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
1575
|
+
return pkg2.version;
|
|
1576
|
+
} catch {
|
|
1577
|
+
return "latest";
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1499
1580
|
async function selectPackageManager(detected) {
|
|
1500
1581
|
const options = ["npm", "pnpm", "yarn", "bun"];
|
|
1501
1582
|
const detectedIndex = options.indexOf(detected);
|
|
@@ -1577,8 +1658,8 @@ async function init(projectNameArg, options = {}) {
|
|
|
1577
1658
|
logger.error("Project name is required");
|
|
1578
1659
|
process.exit(1);
|
|
1579
1660
|
}
|
|
1580
|
-
const projectPath =
|
|
1581
|
-
if (
|
|
1661
|
+
const projectPath = path3.join(cwd, projectName);
|
|
1662
|
+
if (fs3.existsSync(projectPath)) {
|
|
1582
1663
|
logger.error(`Directory "${projectName}" already exists`);
|
|
1583
1664
|
process.exit(1);
|
|
1584
1665
|
}
|
|
@@ -1610,51 +1691,28 @@ async function init(projectNameArg, options = {}) {
|
|
|
1610
1691
|
logger.error("Failed to create Vite project");
|
|
1611
1692
|
process.exit(1);
|
|
1612
1693
|
}
|
|
1613
|
-
const viteConfigPath =
|
|
1614
|
-
if (!
|
|
1694
|
+
const viteConfigPath = path3.join(projectPath, "vite.config.ts");
|
|
1695
|
+
if (!fs3.existsSync(viteConfigPath)) {
|
|
1615
1696
|
const viteConfigContent = `import { defineConfig } from 'vite'
|
|
1616
1697
|
|
|
1617
1698
|
export default defineConfig({
|
|
1618
1699
|
plugins: [],
|
|
1619
1700
|
})
|
|
1620
1701
|
`;
|
|
1621
|
-
|
|
1702
|
+
fs3.writeFileSync(viteConfigPath, viteConfigContent, "utf-8");
|
|
1622
1703
|
logger.success("Created vite.config.ts");
|
|
1623
1704
|
}
|
|
1624
|
-
const packageJsonPath =
|
|
1625
|
-
if (
|
|
1626
|
-
const packageJson = JSON.parse(
|
|
1705
|
+
const packageJsonPath = path3.join(projectPath, "package.json");
|
|
1706
|
+
if (fs3.existsSync(packageJsonPath)) {
|
|
1707
|
+
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
|
|
1627
1708
|
const kebabName = toKebabCase(projectName);
|
|
1628
1709
|
if (packageJson.name !== kebabName) {
|
|
1629
1710
|
packageJson.name = kebabName;
|
|
1630
|
-
|
|
1711
|
+
fs3.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
|
|
1631
1712
|
}
|
|
1632
1713
|
}
|
|
1633
1714
|
logger.log("");
|
|
1634
|
-
logger.info("Step 2:
|
|
1635
|
-
logger.log("");
|
|
1636
|
-
try {
|
|
1637
|
-
const installCmd = pm === "npm" ? "install" : "add";
|
|
1638
|
-
const devFlag = pm === "npm" ? "--save-dev" : "-D";
|
|
1639
|
-
await runCommand(pm, [
|
|
1640
|
-
installCmd,
|
|
1641
|
-
devFlag,
|
|
1642
|
-
"@cloudflare/vite-plugin",
|
|
1643
|
-
"@standardagents/builder",
|
|
1644
|
-
"wrangler",
|
|
1645
|
-
"zod"
|
|
1646
|
-
], projectPath);
|
|
1647
|
-
} catch (error) {
|
|
1648
|
-
logger.error("Failed to install dependencies");
|
|
1649
|
-
process.exit(1);
|
|
1650
|
-
}
|
|
1651
|
-
logger.log("");
|
|
1652
|
-
logger.info("Step 3: Configuring Standard Agents...");
|
|
1653
|
-
logger.log("");
|
|
1654
|
-
process.chdir(projectPath);
|
|
1655
|
-
await scaffold({ force: true });
|
|
1656
|
-
logger.log("");
|
|
1657
|
-
logger.info("Step 4: Configuring environment variables...");
|
|
1715
|
+
logger.info("Step 2: Configuring environment variables...");
|
|
1658
1716
|
logger.log("");
|
|
1659
1717
|
const encryptionKey = crypto.randomBytes(32).toString("hex");
|
|
1660
1718
|
let openrouterKey = "";
|
|
@@ -1689,6 +1747,42 @@ export default defineConfig({
|
|
|
1689
1747
|
if (!adminPassword) {
|
|
1690
1748
|
adminPassword = "admin";
|
|
1691
1749
|
}
|
|
1750
|
+
logger.log("");
|
|
1751
|
+
logger.info("Step 3: Installing Standard Agents dependencies...");
|
|
1752
|
+
logger.log("");
|
|
1753
|
+
try {
|
|
1754
|
+
const installCmd = pm === "npm" ? "install" : "add";
|
|
1755
|
+
const devFlag = pm === "npm" ? "--save-dev" : "-D";
|
|
1756
|
+
const cliVersion = getCliVersion();
|
|
1757
|
+
await runCommand(pm, [
|
|
1758
|
+
installCmd,
|
|
1759
|
+
devFlag,
|
|
1760
|
+
"@cloudflare/vite-plugin",
|
|
1761
|
+
"wrangler"
|
|
1762
|
+
], projectPath);
|
|
1763
|
+
const deps = [
|
|
1764
|
+
`@standardagents/builder@${cliVersion}`,
|
|
1765
|
+
"zod"
|
|
1766
|
+
];
|
|
1767
|
+
if (openaiKey) {
|
|
1768
|
+
deps.push(`@standardagents/openai@${cliVersion}`);
|
|
1769
|
+
}
|
|
1770
|
+
if (openrouterKey) {
|
|
1771
|
+
deps.push(`@standardagents/openrouter@${cliVersion}`);
|
|
1772
|
+
}
|
|
1773
|
+
await runCommand(pm, [installCmd, ...deps], projectPath);
|
|
1774
|
+
} catch (error) {
|
|
1775
|
+
logger.error("Failed to install dependencies");
|
|
1776
|
+
process.exit(1);
|
|
1777
|
+
}
|
|
1778
|
+
logger.log("");
|
|
1779
|
+
logger.info("Step 4: Configuring Standard Agents...");
|
|
1780
|
+
logger.log("");
|
|
1781
|
+
process.chdir(projectPath);
|
|
1782
|
+
await scaffold({ force: true });
|
|
1783
|
+
logger.log("");
|
|
1784
|
+
logger.info("Writing environment configuration...");
|
|
1785
|
+
logger.log("");
|
|
1692
1786
|
const devVarsLines = [
|
|
1693
1787
|
"# Standard Agents Environment Variables",
|
|
1694
1788
|
"# This file contains secrets for local development only",
|
|
@@ -1714,14 +1808,14 @@ export default defineConfig({
|
|
|
1714
1808
|
devVarsLines.push("# OPENAI_API_KEY=your-openai-key");
|
|
1715
1809
|
}
|
|
1716
1810
|
devVarsLines.push("");
|
|
1717
|
-
const devVarsPath =
|
|
1718
|
-
|
|
1811
|
+
const devVarsPath = path3.join(projectPath, ".dev.vars");
|
|
1812
|
+
fs3.writeFileSync(devVarsPath, devVarsLines.join("\n"), "utf-8");
|
|
1719
1813
|
logger.success("Created .dev.vars with encryption key");
|
|
1720
|
-
const gitignorePath =
|
|
1721
|
-
if (
|
|
1722
|
-
const gitignoreContent =
|
|
1814
|
+
const gitignorePath = path3.join(projectPath, ".gitignore");
|
|
1815
|
+
if (fs3.existsSync(gitignorePath)) {
|
|
1816
|
+
const gitignoreContent = fs3.readFileSync(gitignorePath, "utf-8");
|
|
1723
1817
|
if (!gitignoreContent.includes(".dev.vars")) {
|
|
1724
|
-
|
|
1818
|
+
fs3.appendFileSync(gitignorePath, "\n# Local environment variables\n.dev.vars\n");
|
|
1725
1819
|
logger.success("Added .dev.vars to .gitignore");
|
|
1726
1820
|
}
|
|
1727
1821
|
}
|
|
@@ -1743,6 +1837,535 @@ export default defineConfig({
|
|
|
1743
1837
|
logger.log("");
|
|
1744
1838
|
logger.log("For more information, visit: https://standardagents.ai/docs");
|
|
1745
1839
|
}
|
|
1840
|
+
function getReactPackageVersion() {
|
|
1841
|
+
try {
|
|
1842
|
+
const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
|
|
1843
|
+
const pkgPath = path3.resolve(__dirname2, "../package.json");
|
|
1844
|
+
const pkg2 = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
1845
|
+
const version = pkg2.version;
|
|
1846
|
+
const prereleaseMatch = version.match(/-([a-z]+)\./i);
|
|
1847
|
+
if (prereleaseMatch) {
|
|
1848
|
+
return prereleaseMatch[1];
|
|
1849
|
+
}
|
|
1850
|
+
return `^${version}`;
|
|
1851
|
+
} catch {
|
|
1852
|
+
return "latest";
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
function getChatSourceDir() {
|
|
1856
|
+
const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
|
|
1857
|
+
return path3.resolve(__dirname2, "../chat");
|
|
1858
|
+
}
|
|
1859
|
+
async function prompt2(question) {
|
|
1860
|
+
const rl = readline.createInterface({
|
|
1861
|
+
input: process.stdin,
|
|
1862
|
+
output: process.stdout
|
|
1863
|
+
});
|
|
1864
|
+
return new Promise((resolve2) => {
|
|
1865
|
+
rl.question(question, (answer) => {
|
|
1866
|
+
rl.close();
|
|
1867
|
+
resolve2(answer.trim());
|
|
1868
|
+
});
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
function runCommand2(command, args, cwd) {
|
|
1872
|
+
return new Promise((resolve2, reject) => {
|
|
1873
|
+
const child = spawn(command, args, {
|
|
1874
|
+
cwd,
|
|
1875
|
+
stdio: "inherit",
|
|
1876
|
+
shell: false
|
|
1877
|
+
});
|
|
1878
|
+
child.on("close", (code) => {
|
|
1879
|
+
if (code === 0) {
|
|
1880
|
+
resolve2();
|
|
1881
|
+
} else {
|
|
1882
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
child.on("error", reject);
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
function detectPackageManager2() {
|
|
1889
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
1890
|
+
if (userAgent) {
|
|
1891
|
+
if (userAgent.includes("pnpm")) return "pnpm";
|
|
1892
|
+
if (userAgent.includes("yarn")) return "yarn";
|
|
1893
|
+
if (userAgent.includes("bun")) return "bun";
|
|
1894
|
+
}
|
|
1895
|
+
if (fs3.existsSync("pnpm-lock.yaml")) return "pnpm";
|
|
1896
|
+
if (fs3.existsSync("yarn.lock")) return "yarn";
|
|
1897
|
+
if (fs3.existsSync("bun.lockb")) return "bun";
|
|
1898
|
+
if (fs3.existsSync("package-lock.json")) return "npm";
|
|
1899
|
+
return "npm";
|
|
1900
|
+
}
|
|
1901
|
+
async function selectPackageManager2(detected) {
|
|
1902
|
+
const options = ["npm", "pnpm", "yarn", "bun"];
|
|
1903
|
+
const detectedIndex = options.indexOf(detected);
|
|
1904
|
+
return new Promise((resolve2) => {
|
|
1905
|
+
const stdin = process.stdin;
|
|
1906
|
+
const stdout = process.stdout;
|
|
1907
|
+
let selectedIndex = detectedIndex;
|
|
1908
|
+
const renderOptions = () => {
|
|
1909
|
+
stdout.write("\x1B[?25l");
|
|
1910
|
+
stdout.write(`\x1B[${options.length + 1}A`);
|
|
1911
|
+
stdout.write("\x1B[0J");
|
|
1912
|
+
stdout.write("Select a package manager:\n");
|
|
1913
|
+
options.forEach((opt, i) => {
|
|
1914
|
+
const marker = i === detectedIndex ? " (detected)" : "";
|
|
1915
|
+
const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
|
|
1916
|
+
const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
|
|
1917
|
+
stdout.write(`${prefix} ${highlight}${opt}${marker}\x1B[0m
|
|
1918
|
+
`);
|
|
1919
|
+
});
|
|
1920
|
+
};
|
|
1921
|
+
stdout.write("Select a package manager:\n");
|
|
1922
|
+
options.forEach((opt, i) => {
|
|
1923
|
+
const marker = i === detectedIndex ? " (detected)" : "";
|
|
1924
|
+
const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
|
|
1925
|
+
const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
|
|
1926
|
+
stdout.write(`${prefix} ${highlight}${opt}${marker}\x1B[0m
|
|
1927
|
+
`);
|
|
1928
|
+
});
|
|
1929
|
+
const wasRaw = stdin.isRaw;
|
|
1930
|
+
if (stdin.isTTY) {
|
|
1931
|
+
stdin.setRawMode(true);
|
|
1932
|
+
}
|
|
1933
|
+
stdin.resume();
|
|
1934
|
+
stdin.setEncoding("utf8");
|
|
1935
|
+
const cleanup = () => {
|
|
1936
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
1937
|
+
stdin.pause();
|
|
1938
|
+
stdin.removeListener("data", onData);
|
|
1939
|
+
stdout.write("\x1B[?25h");
|
|
1940
|
+
};
|
|
1941
|
+
const onData = (key) => {
|
|
1942
|
+
if (key === "\x1B[A" || key === "k") {
|
|
1943
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
1944
|
+
renderOptions();
|
|
1945
|
+
} else if (key === "\x1B[B" || key === "j") {
|
|
1946
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
1947
|
+
renderOptions();
|
|
1948
|
+
} else if (key === "\r" || key === "\n") {
|
|
1949
|
+
cleanup();
|
|
1950
|
+
resolve2(options[selectedIndex]);
|
|
1951
|
+
} else if (key === "") {
|
|
1952
|
+
cleanup();
|
|
1953
|
+
stdout.write("\n");
|
|
1954
|
+
process.exit(1);
|
|
1955
|
+
}
|
|
1956
|
+
};
|
|
1957
|
+
stdin.on("data", onData);
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
async function selectFramework() {
|
|
1961
|
+
const options = [
|
|
1962
|
+
{ value: "vite", label: "Vite (React + TypeScript)" },
|
|
1963
|
+
{ value: "nextjs", label: "Next.js (App Router)" }
|
|
1964
|
+
];
|
|
1965
|
+
return new Promise((resolve2) => {
|
|
1966
|
+
const stdin = process.stdin;
|
|
1967
|
+
const stdout = process.stdout;
|
|
1968
|
+
let selectedIndex = 0;
|
|
1969
|
+
const renderOptions = () => {
|
|
1970
|
+
stdout.write("\x1B[?25l");
|
|
1971
|
+
stdout.write(`\x1B[${options.length + 1}A`);
|
|
1972
|
+
stdout.write("\x1B[0J");
|
|
1973
|
+
stdout.write("Select a framework:\n");
|
|
1974
|
+
options.forEach((opt, i) => {
|
|
1975
|
+
const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
|
|
1976
|
+
const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
|
|
1977
|
+
stdout.write(`${prefix} ${highlight}${opt.label}\x1B[0m
|
|
1978
|
+
`);
|
|
1979
|
+
});
|
|
1980
|
+
};
|
|
1981
|
+
stdout.write("Select a framework:\n");
|
|
1982
|
+
options.forEach((opt, i) => {
|
|
1983
|
+
const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
|
|
1984
|
+
const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
|
|
1985
|
+
stdout.write(`${prefix} ${highlight}${opt.label}\x1B[0m
|
|
1986
|
+
`);
|
|
1987
|
+
});
|
|
1988
|
+
const wasRaw = stdin.isRaw;
|
|
1989
|
+
if (stdin.isTTY) {
|
|
1990
|
+
stdin.setRawMode(true);
|
|
1991
|
+
}
|
|
1992
|
+
stdin.resume();
|
|
1993
|
+
stdin.setEncoding("utf8");
|
|
1994
|
+
const cleanup = () => {
|
|
1995
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
1996
|
+
stdin.pause();
|
|
1997
|
+
stdin.removeListener("data", onData);
|
|
1998
|
+
stdout.write("\x1B[?25h");
|
|
1999
|
+
};
|
|
2000
|
+
const onData = (key) => {
|
|
2001
|
+
if (key === "\x1B[A" || key === "k") {
|
|
2002
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
2003
|
+
renderOptions();
|
|
2004
|
+
} else if (key === "\x1B[B" || key === "j") {
|
|
2005
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
2006
|
+
renderOptions();
|
|
2007
|
+
} else if (key === "\r" || key === "\n") {
|
|
2008
|
+
cleanup();
|
|
2009
|
+
resolve2(options[selectedIndex].value);
|
|
2010
|
+
} else if (key === "") {
|
|
2011
|
+
cleanup();
|
|
2012
|
+
stdout.write("\n");
|
|
2013
|
+
process.exit(1);
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
2016
|
+
stdin.on("data", onData);
|
|
2017
|
+
});
|
|
2018
|
+
}
|
|
2019
|
+
function isValidUrl(url) {
|
|
2020
|
+
try {
|
|
2021
|
+
new URL(url);
|
|
2022
|
+
return true;
|
|
2023
|
+
} catch {
|
|
2024
|
+
return false;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
async function isPortOpen(port) {
|
|
2028
|
+
const tryConnect = (host) => {
|
|
2029
|
+
return new Promise((resolve2) => {
|
|
2030
|
+
const socket = new net.Socket();
|
|
2031
|
+
socket.setTimeout(200);
|
|
2032
|
+
socket.on("connect", () => {
|
|
2033
|
+
socket.destroy();
|
|
2034
|
+
resolve2(true);
|
|
2035
|
+
});
|
|
2036
|
+
socket.on("timeout", () => {
|
|
2037
|
+
socket.destroy();
|
|
2038
|
+
resolve2(false);
|
|
2039
|
+
});
|
|
2040
|
+
socket.on("error", () => {
|
|
2041
|
+
socket.destroy();
|
|
2042
|
+
resolve2(false);
|
|
2043
|
+
});
|
|
2044
|
+
socket.connect(port, host);
|
|
2045
|
+
});
|
|
2046
|
+
};
|
|
2047
|
+
if (await tryConnect("127.0.0.1")) return true;
|
|
2048
|
+
if (await tryConnect("::1")) return true;
|
|
2049
|
+
return false;
|
|
2050
|
+
}
|
|
2051
|
+
async function isAgentbuilderInstance(url) {
|
|
2052
|
+
try {
|
|
2053
|
+
const controller = new AbortController();
|
|
2054
|
+
const timeout = setTimeout(() => controller.abort(), 1e3);
|
|
2055
|
+
const response = await fetch(`${url}/api/agents`, {
|
|
2056
|
+
signal: controller.signal,
|
|
2057
|
+
headers: { "Accept": "application/json" }
|
|
2058
|
+
});
|
|
2059
|
+
clearTimeout(timeout);
|
|
2060
|
+
if (response.status === 401) {
|
|
2061
|
+
return true;
|
|
2062
|
+
}
|
|
2063
|
+
if (response.ok) {
|
|
2064
|
+
const data = await response.json();
|
|
2065
|
+
return Array.isArray(data.agents);
|
|
2066
|
+
}
|
|
2067
|
+
return false;
|
|
2068
|
+
} catch {
|
|
2069
|
+
return false;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
async function detectAgentbuilderInstance() {
|
|
2073
|
+
const portsToScan = [
|
|
2074
|
+
5173,
|
|
2075
|
+
5174,
|
|
2076
|
+
5175,
|
|
2077
|
+
5176,
|
|
2078
|
+
5177,
|
|
2079
|
+
5178,
|
|
2080
|
+
5179,
|
|
2081
|
+
5180,
|
|
2082
|
+
3e3,
|
|
2083
|
+
3001,
|
|
2084
|
+
3002,
|
|
2085
|
+
3003,
|
|
2086
|
+
8080,
|
|
2087
|
+
8e3,
|
|
2088
|
+
4e3
|
|
2089
|
+
];
|
|
2090
|
+
logger.log("\x1B[90mScanning for running agentbuilder instance...\x1B[0m");
|
|
2091
|
+
const openPorts = [];
|
|
2092
|
+
await Promise.all(
|
|
2093
|
+
portsToScan.map(async (port) => {
|
|
2094
|
+
if (await isPortOpen(port)) {
|
|
2095
|
+
openPorts.push(port);
|
|
2096
|
+
}
|
|
2097
|
+
})
|
|
2098
|
+
);
|
|
2099
|
+
for (const port of openPorts.sort((a, b) => a - b)) {
|
|
2100
|
+
const url = `http://localhost:${port}`;
|
|
2101
|
+
if (await isAgentbuilderInstance(url)) {
|
|
2102
|
+
return url;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
return null;
|
|
2106
|
+
}
|
|
2107
|
+
function copyDir(src, dest, skip = []) {
|
|
2108
|
+
if (!fs3.existsSync(dest)) {
|
|
2109
|
+
fs3.mkdirSync(dest, { recursive: true });
|
|
2110
|
+
}
|
|
2111
|
+
const entries = fs3.readdirSync(src, { withFileTypes: true });
|
|
2112
|
+
for (const entry of entries) {
|
|
2113
|
+
if (skip.includes(entry.name)) continue;
|
|
2114
|
+
const srcPath = path3.join(src, entry.name);
|
|
2115
|
+
const destPath = path3.join(dest, entry.name);
|
|
2116
|
+
if (entry.isDirectory()) {
|
|
2117
|
+
copyDir(srcPath, destPath, skip);
|
|
2118
|
+
} else {
|
|
2119
|
+
fs3.copyFileSync(srcPath, destPath);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
async function initChat(projectNameArg, options = {}) {
|
|
2124
|
+
const cwd = process.cwd();
|
|
2125
|
+
const chatSourceDir = getChatSourceDir();
|
|
2126
|
+
if (!fs3.existsSync(chatSourceDir)) {
|
|
2127
|
+
logger.error("Chat source files not found. This CLI installation may be corrupted.");
|
|
2128
|
+
process.exit(1);
|
|
2129
|
+
}
|
|
2130
|
+
logger.log("");
|
|
2131
|
+
logger.info("Scaffolding a frontend chat application...");
|
|
2132
|
+
logger.log("");
|
|
2133
|
+
let projectName = projectNameArg;
|
|
2134
|
+
if (!projectName && !options.yes) {
|
|
2135
|
+
projectName = await prompt2("Project name: ");
|
|
2136
|
+
}
|
|
2137
|
+
if (!projectName) {
|
|
2138
|
+
logger.error("Project name is required");
|
|
2139
|
+
process.exit(1);
|
|
2140
|
+
}
|
|
2141
|
+
const projectPath = path3.join(cwd, projectName);
|
|
2142
|
+
if (fs3.existsSync(projectPath)) {
|
|
2143
|
+
logger.error(`Directory "${projectName}" already exists`);
|
|
2144
|
+
process.exit(1);
|
|
2145
|
+
}
|
|
2146
|
+
let framework;
|
|
2147
|
+
if (options.framework) {
|
|
2148
|
+
framework = options.framework;
|
|
2149
|
+
} else if (options.yes) {
|
|
2150
|
+
framework = "vite";
|
|
2151
|
+
} else {
|
|
2152
|
+
framework = await selectFramework();
|
|
2153
|
+
logger.log("");
|
|
2154
|
+
}
|
|
2155
|
+
let serverUrl;
|
|
2156
|
+
if (options.yes) {
|
|
2157
|
+
serverUrl = "http://localhost:5173";
|
|
2158
|
+
} else {
|
|
2159
|
+
const detectedUrl = await detectAgentbuilderInstance();
|
|
2160
|
+
if (detectedUrl) {
|
|
2161
|
+
logger.success(`Found agentbuilder at ${detectedUrl}`);
|
|
2162
|
+
const urlInput = await prompt2(`Agentbuilder dev server URL [${detectedUrl}]: `);
|
|
2163
|
+
serverUrl = urlInput || detectedUrl;
|
|
2164
|
+
} else {
|
|
2165
|
+
logger.log("\x1B[90mNo running agentbuilder instance found\x1B[0m");
|
|
2166
|
+
const urlInput = await prompt2("Agentbuilder dev server URL [http://localhost:5173]: ");
|
|
2167
|
+
serverUrl = urlInput || "http://localhost:5173";
|
|
2168
|
+
}
|
|
2169
|
+
if (!isValidUrl(serverUrl)) {
|
|
2170
|
+
logger.error("Invalid URL format");
|
|
2171
|
+
process.exit(1);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
const detectedPm = detectPackageManager2();
|
|
2175
|
+
let pm;
|
|
2176
|
+
if (options.yes) {
|
|
2177
|
+
pm = detectedPm;
|
|
2178
|
+
} else {
|
|
2179
|
+
pm = await selectPackageManager2(detectedPm);
|
|
2180
|
+
logger.log("");
|
|
2181
|
+
}
|
|
2182
|
+
logger.log("");
|
|
2183
|
+
logger.info(`Creating ${framework === "vite" ? "Vite" : "Next.js"} project...`);
|
|
2184
|
+
logger.log("");
|
|
2185
|
+
fs3.mkdirSync(projectPath, { recursive: true });
|
|
2186
|
+
const reactVersion = getReactPackageVersion();
|
|
2187
|
+
if (framework === "vite") {
|
|
2188
|
+
await scaffoldVite(projectPath, chatSourceDir, projectName, serverUrl, reactVersion);
|
|
2189
|
+
} else {
|
|
2190
|
+
await scaffoldNextjs(projectPath, chatSourceDir, projectName, serverUrl, reactVersion);
|
|
2191
|
+
}
|
|
2192
|
+
logger.log("");
|
|
2193
|
+
logger.info("Installing dependencies...");
|
|
2194
|
+
logger.log("");
|
|
2195
|
+
try {
|
|
2196
|
+
await runCommand2(pm, pm === "npm" ? ["install"] : ["install"], projectPath);
|
|
2197
|
+
} catch (error) {
|
|
2198
|
+
logger.error("Failed to install dependencies");
|
|
2199
|
+
process.exit(1);
|
|
2200
|
+
}
|
|
2201
|
+
logger.log("");
|
|
2202
|
+
logger.success("Chat UI scaffolded successfully!");
|
|
2203
|
+
logger.log("");
|
|
2204
|
+
logger.info("Starting development server...");
|
|
2205
|
+
logger.log("");
|
|
2206
|
+
const devArgs = pm === "npm" ? ["run", "dev"] : ["dev"];
|
|
2207
|
+
const devProcess = spawn(pm, devArgs, {
|
|
2208
|
+
cwd: projectPath,
|
|
2209
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
2210
|
+
shell: false
|
|
2211
|
+
});
|
|
2212
|
+
let browserOpened = false;
|
|
2213
|
+
const openBrowser = (url) => {
|
|
2214
|
+
if (browserOpened) return;
|
|
2215
|
+
browserOpened = true;
|
|
2216
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
2217
|
+
spawn(openCmd, [url], { stdio: "ignore", detached: true }).unref();
|
|
2218
|
+
};
|
|
2219
|
+
devProcess.stdout?.on("data", (data) => {
|
|
2220
|
+
const text = data.toString();
|
|
2221
|
+
process.stdout.write(data);
|
|
2222
|
+
const urlMatch = text.match(/Local:\s+(https?:\/\/localhost:\d+\/?)/);
|
|
2223
|
+
if (urlMatch && !browserOpened) {
|
|
2224
|
+
openBrowser(urlMatch[1]);
|
|
2225
|
+
}
|
|
2226
|
+
});
|
|
2227
|
+
devProcess.stderr?.on("data", (data) => {
|
|
2228
|
+
process.stderr.write(data);
|
|
2229
|
+
});
|
|
2230
|
+
devProcess.on("error", (error) => {
|
|
2231
|
+
logger.error(`Failed to start dev server: ${error.message}`);
|
|
2232
|
+
process.exit(1);
|
|
2233
|
+
});
|
|
2234
|
+
}
|
|
2235
|
+
async function scaffoldVite(projectPath, chatSourceDir, projectName, serverUrl, reactVersion) {
|
|
2236
|
+
copyDir(path3.join(chatSourceDir, "src"), path3.join(projectPath, "src"), []);
|
|
2237
|
+
logger.success("Copied src/");
|
|
2238
|
+
const viteDir = path3.join(chatSourceDir, "vite");
|
|
2239
|
+
let indexHtml = fs3.readFileSync(path3.join(viteDir, "index.html"), "utf-8");
|
|
2240
|
+
indexHtml = indexHtml.replace('src="/main.tsx"', 'src="/src/main.tsx"');
|
|
2241
|
+
fs3.writeFileSync(path3.join(projectPath, "index.html"), indexHtml, "utf-8");
|
|
2242
|
+
logger.success("Created index.html");
|
|
2243
|
+
fs3.copyFileSync(path3.join(viteDir, "main.tsx"), path3.join(projectPath, "src", "main.tsx"));
|
|
2244
|
+
logger.success("Created src/main.tsx");
|
|
2245
|
+
let viteConfig = fs3.readFileSync(path3.join(viteDir, "vite.config.ts"), "utf-8");
|
|
2246
|
+
viteConfig = `import { defineConfig } from 'vite'
|
|
2247
|
+
import react from '@vitejs/plugin-react'
|
|
2248
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
2249
|
+
|
|
2250
|
+
export default defineConfig({
|
|
2251
|
+
plugins: [
|
|
2252
|
+
react(),
|
|
2253
|
+
tailwindcss(),
|
|
2254
|
+
],
|
|
2255
|
+
})
|
|
2256
|
+
`;
|
|
2257
|
+
fs3.writeFileSync(path3.join(projectPath, "vite.config.ts"), viteConfig, "utf-8");
|
|
2258
|
+
logger.success("Created vite.config.ts");
|
|
2259
|
+
fs3.copyFileSync(path3.join(chatSourceDir, "tsconfig.json"), path3.join(projectPath, "tsconfig.json"));
|
|
2260
|
+
const tsconfig = JSON.parse(fs3.readFileSync(path3.join(projectPath, "tsconfig.json"), "utf-8"));
|
|
2261
|
+
tsconfig.include = ["src"];
|
|
2262
|
+
delete tsconfig.compilerOptions?.paths;
|
|
2263
|
+
fs3.writeFileSync(path3.join(projectPath, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
|
|
2264
|
+
logger.success("Created tsconfig.json");
|
|
2265
|
+
fs3.copyFileSync(path3.join(chatSourceDir, "package.json"), path3.join(projectPath, "package.json"));
|
|
2266
|
+
const pkg2 = JSON.parse(fs3.readFileSync(path3.join(projectPath, "package.json"), "utf-8"));
|
|
2267
|
+
pkg2.name = projectName;
|
|
2268
|
+
pkg2.scripts = {
|
|
2269
|
+
dev: "vite",
|
|
2270
|
+
build: "vite build",
|
|
2271
|
+
preview: "vite preview"
|
|
2272
|
+
};
|
|
2273
|
+
delete pkg2.private;
|
|
2274
|
+
if (pkg2.dependencies?.["@standardagents/react"]) {
|
|
2275
|
+
pkg2.dependencies["@standardagents/react"] = reactVersion;
|
|
2276
|
+
}
|
|
2277
|
+
delete pkg2.dependencies?.["next"];
|
|
2278
|
+
delete pkg2.devDependencies?.["@tailwindcss/postcss"];
|
|
2279
|
+
delete pkg2.devDependencies?.["postcss"];
|
|
2280
|
+
fs3.writeFileSync(path3.join(projectPath, "package.json"), JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
2281
|
+
logger.success("Created package.json");
|
|
2282
|
+
const envContent = `# Agentbuilder connection
|
|
2283
|
+
VITE_AGENTBUILDER_URL=${serverUrl}
|
|
2284
|
+
`;
|
|
2285
|
+
fs3.writeFileSync(path3.join(projectPath, ".env"), envContent, "utf-8");
|
|
2286
|
+
logger.success("Created .env");
|
|
2287
|
+
fs3.writeFileSync(path3.join(projectPath, ".gitignore"), `node_modules
|
|
2288
|
+
dist
|
|
2289
|
+
.env
|
|
2290
|
+
`, "utf-8");
|
|
2291
|
+
logger.success("Created .gitignore");
|
|
2292
|
+
let mainTsx = fs3.readFileSync(path3.join(projectPath, "src", "main.tsx"), "utf-8");
|
|
2293
|
+
mainTsx = mainTsx.replace(/from '\.\.\/src\//g, "from './").replace(/import '\.\.\/src\//g, "import './");
|
|
2294
|
+
fs3.writeFileSync(path3.join(projectPath, "src", "main.tsx"), mainTsx, "utf-8");
|
|
2295
|
+
if (fs3.existsSync(path3.join(viteDir, "favicon.svg"))) {
|
|
2296
|
+
fs3.copyFileSync(path3.join(viteDir, "favicon.svg"), path3.join(projectPath, "favicon.svg"));
|
|
2297
|
+
logger.success("Created favicon.svg");
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
async function scaffoldNextjs(projectPath, chatSourceDir, projectName, serverUrl, reactVersion) {
|
|
2301
|
+
copyDir(path3.join(chatSourceDir, "src"), path3.join(projectPath, "src"), []);
|
|
2302
|
+
logger.success("Copied src/");
|
|
2303
|
+
const nextDir = path3.join(chatSourceDir, "next");
|
|
2304
|
+
copyDir(path3.join(nextDir, "app"), path3.join(projectPath, "app"), []);
|
|
2305
|
+
logger.success("Copied app/");
|
|
2306
|
+
fs3.copyFileSync(path3.join(nextDir, "next.config.ts"), path3.join(projectPath, "next.config.ts"));
|
|
2307
|
+
logger.success("Created next.config.ts");
|
|
2308
|
+
fs3.copyFileSync(path3.join(nextDir, "postcss.config.mjs"), path3.join(projectPath, "postcss.config.mjs"));
|
|
2309
|
+
logger.success("Created postcss.config.mjs");
|
|
2310
|
+
const tsconfig = {
|
|
2311
|
+
compilerOptions: {
|
|
2312
|
+
target: "ES2017",
|
|
2313
|
+
lib: ["dom", "dom.iterable", "esnext"],
|
|
2314
|
+
allowJs: true,
|
|
2315
|
+
skipLibCheck: true,
|
|
2316
|
+
strict: true,
|
|
2317
|
+
noEmit: true,
|
|
2318
|
+
esModuleInterop: true,
|
|
2319
|
+
module: "esnext",
|
|
2320
|
+
moduleResolution: "bundler",
|
|
2321
|
+
resolveJsonModule: true,
|
|
2322
|
+
isolatedModules: true,
|
|
2323
|
+
jsx: "preserve",
|
|
2324
|
+
incremental: true,
|
|
2325
|
+
plugins: [{ name: "next" }],
|
|
2326
|
+
paths: {
|
|
2327
|
+
"@/*": ["./src/*"]
|
|
2328
|
+
}
|
|
2329
|
+
},
|
|
2330
|
+
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
2331
|
+
exclude: ["node_modules"]
|
|
2332
|
+
};
|
|
2333
|
+
fs3.writeFileSync(path3.join(projectPath, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
|
|
2334
|
+
logger.success("Created tsconfig.json");
|
|
2335
|
+
const pkg2 = JSON.parse(fs3.readFileSync(path3.join(chatSourceDir, "package.json"), "utf-8"));
|
|
2336
|
+
pkg2.name = projectName;
|
|
2337
|
+
pkg2.scripts = {
|
|
2338
|
+
dev: "next dev",
|
|
2339
|
+
build: "next build",
|
|
2340
|
+
start: "next start"
|
|
2341
|
+
};
|
|
2342
|
+
delete pkg2.private;
|
|
2343
|
+
if (pkg2.dependencies?.["@standardagents/react"]) {
|
|
2344
|
+
pkg2.dependencies["@standardagents/react"] = reactVersion;
|
|
2345
|
+
}
|
|
2346
|
+
delete pkg2.devDependencies?.["@tailwindcss/vite"];
|
|
2347
|
+
delete pkg2.devDependencies?.["@vitejs/plugin-react"];
|
|
2348
|
+
delete pkg2.devDependencies?.["vite"];
|
|
2349
|
+
fs3.writeFileSync(path3.join(projectPath, "package.json"), JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
2350
|
+
logger.success("Created package.json");
|
|
2351
|
+
const envContent = `# Agentbuilder connection
|
|
2352
|
+
NEXT_PUBLIC_AGENTBUILDER_URL=${serverUrl}
|
|
2353
|
+
`;
|
|
2354
|
+
fs3.writeFileSync(path3.join(projectPath, ".env.local"), envContent, "utf-8");
|
|
2355
|
+
logger.success("Created .env.local");
|
|
2356
|
+
fs3.writeFileSync(path3.join(projectPath, ".gitignore"), `node_modules
|
|
2357
|
+
.next
|
|
2358
|
+
out
|
|
2359
|
+
.env.local
|
|
2360
|
+
`, "utf-8");
|
|
2361
|
+
logger.success("Created .gitignore");
|
|
2362
|
+
let layoutTsx = fs3.readFileSync(path3.join(projectPath, "app", "layout.tsx"), "utf-8");
|
|
2363
|
+
layoutTsx = layoutTsx.replace(/from '\.\.\/\.\.\/src\//g, "from '../src/");
|
|
2364
|
+
fs3.writeFileSync(path3.join(projectPath, "app", "layout.tsx"), layoutTsx, "utf-8");
|
|
2365
|
+
let pageTsx = fs3.readFileSync(path3.join(projectPath, "app", "page.tsx"), "utf-8");
|
|
2366
|
+
pageTsx = pageTsx.replace(/from '\.\.\/\.\.\/src\//g, "from '../src/");
|
|
2367
|
+
fs3.writeFileSync(path3.join(projectPath, "app", "page.tsx"), pageTsx, "utf-8");
|
|
2368
|
+
}
|
|
1746
2369
|
|
|
1747
2370
|
// src/index.ts
|
|
1748
2371
|
var __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
@@ -1751,6 +2374,7 @@ var program = new Command();
|
|
|
1751
2374
|
program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version(pkg.version);
|
|
1752
2375
|
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);
|
|
1753
2376
|
program.command("scaffold").description("Add Standard Agents to an existing Vite project").option("--force", "Overwrite existing configuration").action(scaffold);
|
|
2377
|
+
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);
|
|
1754
2378
|
program.parse();
|
|
1755
2379
|
//# sourceMappingURL=index.js.map
|
|
1756
2380
|
//# sourceMappingURL=index.js.map
|