struere 0.3.6 → 0.3.8
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/bin/struere.js
CHANGED
|
@@ -17480,6 +17480,332 @@ function getEnvLocal(deploymentUrl) {
|
|
|
17480
17480
|
return `STRUERE_DEPLOYMENT_URL=${deploymentUrl}
|
|
17481
17481
|
`;
|
|
17482
17482
|
}
|
|
17483
|
+
function getClaudeMd(name) {
|
|
17484
|
+
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
17485
|
+
return `# ${displayName} Agent
|
|
17486
|
+
|
|
17487
|
+
This is a Struere AI agent project. Struere is a framework for building production AI agents with built-in data management, event tracking, and job scheduling.
|
|
17488
|
+
|
|
17489
|
+
## Project Structure
|
|
17490
|
+
|
|
17491
|
+
\`\`\`
|
|
17492
|
+
src/
|
|
17493
|
+
\u251C\u2500\u2500 agent.ts # Agent definition (system prompt, model, tools)
|
|
17494
|
+
\u251C\u2500\u2500 context.ts # Dynamic context injection per request
|
|
17495
|
+
\u251C\u2500\u2500 tools.ts # Custom tool definitions
|
|
17496
|
+
\u2514\u2500\u2500 workflows/ # Multi-step workflow definitions
|
|
17497
|
+
tests/
|
|
17498
|
+
\u2514\u2500\u2500 *.test.yaml # YAML-based conversation tests
|
|
17499
|
+
struere.json # Project configuration (agentId, team, slug)
|
|
17500
|
+
struere.config.ts # Framework settings (port, CORS, logging)
|
|
17501
|
+
\`\`\`
|
|
17502
|
+
|
|
17503
|
+
## Agent Definition
|
|
17504
|
+
|
|
17505
|
+
Define your agent in \`src/agent.ts\`:
|
|
17506
|
+
|
|
17507
|
+
\`\`\`typescript
|
|
17508
|
+
import { defineAgent } from 'struere'
|
|
17509
|
+
import { tools } from './tools'
|
|
17510
|
+
import { context } from './context'
|
|
17511
|
+
|
|
17512
|
+
export default defineAgent({
|
|
17513
|
+
name: 'my-agent',
|
|
17514
|
+
version: '0.1.0',
|
|
17515
|
+
description: 'My AI Agent',
|
|
17516
|
+
model: {
|
|
17517
|
+
provider: 'anthropic',
|
|
17518
|
+
name: 'claude-sonnet-4-20250514',
|
|
17519
|
+
temperature: 0.7,
|
|
17520
|
+
maxTokens: 4096,
|
|
17521
|
+
},
|
|
17522
|
+
systemPrompt: \\\`You are a helpful assistant.
|
|
17523
|
+
|
|
17524
|
+
Current time: {{datetime}}
|
|
17525
|
+
Customer: {{entity.get({"id": "{{thread.metadata.customerId}}"})}}\\\`,
|
|
17526
|
+
tools,
|
|
17527
|
+
context,
|
|
17528
|
+
state: {
|
|
17529
|
+
storage: 'memory',
|
|
17530
|
+
ttl: 3600,
|
|
17531
|
+
},
|
|
17532
|
+
})
|
|
17533
|
+
\`\`\`
|
|
17534
|
+
|
|
17535
|
+
## System Prompt Templates
|
|
17536
|
+
|
|
17537
|
+
System prompts support dynamic \`{{...}}\` templates that are resolved at runtime before the LLM call.
|
|
17538
|
+
|
|
17539
|
+
### Available Variables
|
|
17540
|
+
|
|
17541
|
+
| Variable | Description |
|
|
17542
|
+
|----------|-------------|
|
|
17543
|
+
| \`{{organizationId}}\` | Current organization ID |
|
|
17544
|
+
| \`{{userId}}\` | Current user ID |
|
|
17545
|
+
| \`{{threadId}}\` | Conversation thread ID |
|
|
17546
|
+
| \`{{agentId}}\` | Agent ID |
|
|
17547
|
+
| \`{{agent.name}}\` | Agent name |
|
|
17548
|
+
| \`{{agent.slug}}\` | Agent slug |
|
|
17549
|
+
| \`{{thread.metadata.X}}\` | Thread metadata field X |
|
|
17550
|
+
| \`{{message}}\` | Current user message |
|
|
17551
|
+
| \`{{timestamp}}\` | Unix timestamp (ms) |
|
|
17552
|
+
| \`{{datetime}}\` | ISO 8601 datetime |
|
|
17553
|
+
|
|
17554
|
+
### Function Calls
|
|
17555
|
+
|
|
17556
|
+
Call any agent tool directly in the system prompt:
|
|
17557
|
+
|
|
17558
|
+
\`\`\`
|
|
17559
|
+
{{entity.get({"id": "ent_123"})}}
|
|
17560
|
+
{{entity.query({"type": "customer", "limit": 5})}}
|
|
17561
|
+
{{event.query({"entityId": "ent_123", "limit": 10})}}
|
|
17562
|
+
\`\`\`
|
|
17563
|
+
|
|
17564
|
+
### Nested Templates
|
|
17565
|
+
|
|
17566
|
+
Variables can be used inside function arguments:
|
|
17567
|
+
|
|
17568
|
+
\`\`\`
|
|
17569
|
+
{{entity.get({"id": "{{thread.metadata.customerId}}"})}}
|
|
17570
|
+
\`\`\`
|
|
17571
|
+
|
|
17572
|
+
### Error Handling
|
|
17573
|
+
|
|
17574
|
+
Failed templates are replaced with inline errors:
|
|
17575
|
+
\`\`\`
|
|
17576
|
+
[TEMPLATE_ERROR: variableName not found]
|
|
17577
|
+
[TEMPLATE_ERROR: toolName - error message]
|
|
17578
|
+
\`\`\`
|
|
17579
|
+
|
|
17580
|
+
## Custom Tools
|
|
17581
|
+
|
|
17582
|
+
Define tools in \`src/tools.ts\`:
|
|
17583
|
+
|
|
17584
|
+
\`\`\`typescript
|
|
17585
|
+
import { defineTools } from 'struere'
|
|
17586
|
+
|
|
17587
|
+
export const tools = defineTools([
|
|
17588
|
+
{
|
|
17589
|
+
name: 'search_products',
|
|
17590
|
+
description: 'Search the product catalog',
|
|
17591
|
+
parameters: {
|
|
17592
|
+
type: 'object',
|
|
17593
|
+
properties: {
|
|
17594
|
+
query: { type: 'string', description: 'Search query' },
|
|
17595
|
+
limit: { type: 'number', description: 'Max results' },
|
|
17596
|
+
},
|
|
17597
|
+
required: ['query'],
|
|
17598
|
+
},
|
|
17599
|
+
handler: async (params) => {
|
|
17600
|
+
const results = await searchProducts(params.query, params.limit ?? 10)
|
|
17601
|
+
return { products: results }
|
|
17602
|
+
},
|
|
17603
|
+
},
|
|
17604
|
+
])
|
|
17605
|
+
\`\`\`
|
|
17606
|
+
|
|
17607
|
+
Custom tool handlers are executed in a sandboxed Cloudflare Worker environment. They can make HTTP requests to allowlisted domains:
|
|
17608
|
+
- api.openai.com, api.anthropic.com, api.stripe.com
|
|
17609
|
+
- api.sendgrid.com, api.twilio.com, hooks.slack.com
|
|
17610
|
+
- discord.com, api.github.com
|
|
17611
|
+
|
|
17612
|
+
## Built-in Tools
|
|
17613
|
+
|
|
17614
|
+
Agents have access to these built-in tools for data management:
|
|
17615
|
+
|
|
17616
|
+
### Entity Tools
|
|
17617
|
+
|
|
17618
|
+
| Tool | Description |
|
|
17619
|
+
|------|-------------|
|
|
17620
|
+
| \`entity.create\` | Create a new entity |
|
|
17621
|
+
| \`entity.get\` | Get entity by ID |
|
|
17622
|
+
| \`entity.query\` | Query entities by type/filters |
|
|
17623
|
+
| \`entity.update\` | Update entity data |
|
|
17624
|
+
| \`entity.delete\` | Soft-delete entity |
|
|
17625
|
+
| \`entity.link\` | Create entity relation |
|
|
17626
|
+
| \`entity.unlink\` | Remove entity relation |
|
|
17627
|
+
|
|
17628
|
+
Example entity operations:
|
|
17629
|
+
\`\`\`json
|
|
17630
|
+
// entity.create
|
|
17631
|
+
{ "type": "customer", "data": { "name": "John", "email": "john@example.com" } }
|
|
17632
|
+
|
|
17633
|
+
// entity.query
|
|
17634
|
+
{ "type": "customer", "filters": { "status": "active" }, "limit": 10 }
|
|
17635
|
+
|
|
17636
|
+
// entity.update
|
|
17637
|
+
{ "id": "ent_123", "data": { "status": "vip" } }
|
|
17638
|
+
\`\`\`
|
|
17639
|
+
|
|
17640
|
+
### Event Tools
|
|
17641
|
+
|
|
17642
|
+
| Tool | Description |
|
|
17643
|
+
|------|-------------|
|
|
17644
|
+
| \`event.emit\` | Emit a custom event |
|
|
17645
|
+
| \`event.query\` | Query event history |
|
|
17646
|
+
|
|
17647
|
+
Example event operations:
|
|
17648
|
+
\`\`\`json
|
|
17649
|
+
// event.emit
|
|
17650
|
+
{ "entityId": "ent_123", "eventType": "order.placed", "payload": { "amount": 99.99 } }
|
|
17651
|
+
|
|
17652
|
+
// event.query
|
|
17653
|
+
{ "entityId": "ent_123", "eventType": "order.*", "limit": 20 }
|
|
17654
|
+
\`\`\`
|
|
17655
|
+
|
|
17656
|
+
### Job Tools
|
|
17657
|
+
|
|
17658
|
+
| Tool | Description |
|
|
17659
|
+
|------|-------------|
|
|
17660
|
+
| \`job.enqueue\` | Schedule a background job |
|
|
17661
|
+
| \`job.status\` | Get job status |
|
|
17662
|
+
|
|
17663
|
+
Example job operations:
|
|
17664
|
+
\`\`\`json
|
|
17665
|
+
// job.enqueue
|
|
17666
|
+
{ "jobType": "send_email", "payload": { "to": "user@example.com" }, "scheduledFor": 1706745600000 }
|
|
17667
|
+
|
|
17668
|
+
// job.status
|
|
17669
|
+
{ "id": "job_abc123" }
|
|
17670
|
+
\`\`\`
|
|
17671
|
+
|
|
17672
|
+
## Context Function
|
|
17673
|
+
|
|
17674
|
+
Inject dynamic context per request in \`src/context.ts\`:
|
|
17675
|
+
|
|
17676
|
+
\`\`\`typescript
|
|
17677
|
+
import { defineContext } from 'struere'
|
|
17678
|
+
|
|
17679
|
+
export const context = defineContext(async (request) => {
|
|
17680
|
+
const { conversationId, userId, channel, state } = request
|
|
17681
|
+
|
|
17682
|
+
const userProfile = await fetchUserProfile(userId)
|
|
17683
|
+
|
|
17684
|
+
return {
|
|
17685
|
+
additionalContext: \\\`
|
|
17686
|
+
User: \${userProfile.name} (\${userProfile.tier} tier)
|
|
17687
|
+
Conversation: \${conversationId}
|
|
17688
|
+
Channel: \${channel}
|
|
17689
|
+
\\\`,
|
|
17690
|
+
variables: {
|
|
17691
|
+
userId,
|
|
17692
|
+
userTier: userProfile.tier,
|
|
17693
|
+
timestamp: new Date().toISOString(),
|
|
17694
|
+
},
|
|
17695
|
+
}
|
|
17696
|
+
})
|
|
17697
|
+
\`\`\`
|
|
17698
|
+
|
|
17699
|
+
## Testing
|
|
17700
|
+
|
|
17701
|
+
Write YAML-based conversation tests in \`tests/\`:
|
|
17702
|
+
|
|
17703
|
+
\`\`\`yaml
|
|
17704
|
+
name: Order flow test
|
|
17705
|
+
description: Test the complete order flow
|
|
17706
|
+
|
|
17707
|
+
conversation:
|
|
17708
|
+
- role: user
|
|
17709
|
+
content: I want to order a pizza
|
|
17710
|
+
- role: assistant
|
|
17711
|
+
assertions:
|
|
17712
|
+
- type: contains
|
|
17713
|
+
value: size
|
|
17714
|
+
- type: toolCalled
|
|
17715
|
+
value: get_menu
|
|
17716
|
+
|
|
17717
|
+
- role: user
|
|
17718
|
+
content: Large pepperoni please
|
|
17719
|
+
- role: assistant
|
|
17720
|
+
assertions:
|
|
17721
|
+
- type: toolCalled
|
|
17722
|
+
value: entity.create
|
|
17723
|
+
\`\`\`
|
|
17724
|
+
|
|
17725
|
+
### Assertion Types
|
|
17726
|
+
|
|
17727
|
+
| Type | Description |
|
|
17728
|
+
|------|-------------|
|
|
17729
|
+
| \`contains\` | Response contains substring |
|
|
17730
|
+
| \`matches\` | Response matches regex |
|
|
17731
|
+
| \`toolCalled\` | Specific tool was called |
|
|
17732
|
+
| \`noToolCalled\` | No tools were called |
|
|
17733
|
+
|
|
17734
|
+
Run tests with:
|
|
17735
|
+
\`\`\`bash
|
|
17736
|
+
bun run test
|
|
17737
|
+
\`\`\`
|
|
17738
|
+
|
|
17739
|
+
## CLI Commands
|
|
17740
|
+
|
|
17741
|
+
| Command | Description |
|
|
17742
|
+
|---------|-------------|
|
|
17743
|
+
| \`struere dev\` | Start development mode (live sync to Convex) |
|
|
17744
|
+
| \`struere build\` | Validate agent configuration |
|
|
17745
|
+
| \`struere deploy\` | Deploy agent to production |
|
|
17746
|
+
| \`struere test\` | Run YAML conversation tests |
|
|
17747
|
+
| \`struere logs\` | View recent execution logs |
|
|
17748
|
+
| \`struere state\` | Inspect conversation thread state |
|
|
17749
|
+
|
|
17750
|
+
## Thread Metadata
|
|
17751
|
+
|
|
17752
|
+
Set thread metadata when creating conversations to provide context:
|
|
17753
|
+
|
|
17754
|
+
\`\`\`typescript
|
|
17755
|
+
// Via API
|
|
17756
|
+
POST /v1/chat
|
|
17757
|
+
{
|
|
17758
|
+
"agentId": "agent_123",
|
|
17759
|
+
"message": "Hello",
|
|
17760
|
+
"metadata": {
|
|
17761
|
+
"customerId": "ent_customer_456",
|
|
17762
|
+
"channel": "web",
|
|
17763
|
+
"language": "en"
|
|
17764
|
+
}
|
|
17765
|
+
}
|
|
17766
|
+
\`\`\`
|
|
17767
|
+
|
|
17768
|
+
Access in system prompt:
|
|
17769
|
+
\`\`\`
|
|
17770
|
+
Customer: {{entity.get({"id": "{{thread.metadata.customerId}}"})}}
|
|
17771
|
+
Channel: {{thread.metadata.channel}}
|
|
17772
|
+
\`\`\`
|
|
17773
|
+
|
|
17774
|
+
## Development Workflow
|
|
17775
|
+
|
|
17776
|
+
1. **Edit agent configuration** in \`src/agent.ts\`
|
|
17777
|
+
2. **Run \`bun run dev\`** to sync changes to Convex
|
|
17778
|
+
3. **Test via API** or dashboard chat interface
|
|
17779
|
+
4. **Write tests** in \`tests/*.test.yaml\`
|
|
17780
|
+
5. **Deploy** with \`bun run deploy\`
|
|
17781
|
+
|
|
17782
|
+
## API Endpoints
|
|
17783
|
+
|
|
17784
|
+
| Endpoint | Method | Description |
|
|
17785
|
+
|----------|--------|-------------|
|
|
17786
|
+
| \`/v1/chat\` | POST | Chat by agent ID |
|
|
17787
|
+
| \`/v1/agents/:slug/chat\` | POST | Chat by agent slug |
|
|
17788
|
+
|
|
17789
|
+
Authentication: Bearer token (API key from dashboard)
|
|
17790
|
+
|
|
17791
|
+
\`\`\`bash
|
|
17792
|
+
curl -X POST https://your-deployment.convex.cloud/v1/chat \\
|
|
17793
|
+
-H "Authorization: Bearer sk_live_..." \\
|
|
17794
|
+
-H "Content-Type: application/json" \\
|
|
17795
|
+
-d '{"agentId": "...", "message": "Hello"}'
|
|
17796
|
+
\`\`\`
|
|
17797
|
+
|
|
17798
|
+
## Best Practices
|
|
17799
|
+
|
|
17800
|
+
1. **System Prompts**: Use templates for dynamic data instead of hardcoding
|
|
17801
|
+
2. **Tools**: Keep tool handlers focused and stateless
|
|
17802
|
+
3. **Entities**: Model your domain data as entity types
|
|
17803
|
+
4. **Events**: Emit events for audit trails and analytics
|
|
17804
|
+
5. **Jobs**: Use jobs for async operations (emails, notifications)
|
|
17805
|
+
6. **Testing**: Write tests for critical conversation flows
|
|
17806
|
+
7. **Context**: Use context for user-specific personalization
|
|
17807
|
+
`;
|
|
17808
|
+
}
|
|
17483
17809
|
|
|
17484
17810
|
// src/cli/utils/scaffold.ts
|
|
17485
17811
|
function ensureDir(filePath) {
|
|
@@ -17519,7 +17845,8 @@ function scaffoldAgentFiles(cwd, projectName) {
|
|
|
17519
17845
|
"src/tools.ts": getToolsTs(),
|
|
17520
17846
|
"src/workflows/.gitkeep": "",
|
|
17521
17847
|
"tests/basic.test.yaml": getBasicTestYaml(),
|
|
17522
|
-
".env.example": getEnvExample()
|
|
17848
|
+
".env.example": getEnvExample(),
|
|
17849
|
+
".claude.md": getClaudeMd(projectName)
|
|
17523
17850
|
};
|
|
17524
17851
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
17525
17852
|
const fullPath = join3(cwd, relativePath);
|
|
@@ -17797,6 +18124,7 @@ function readLine() {
|
|
|
17797
18124
|
// src/cli/commands/dev.ts
|
|
17798
18125
|
var import_chokidar = __toESM(require_chokidar(), 1);
|
|
17799
18126
|
import { join as join7, basename as basename2 } from "path";
|
|
18127
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
17800
18128
|
|
|
17801
18129
|
// src/cli/utils/config.ts
|
|
17802
18130
|
import { join as join5 } from "path";
|
|
@@ -17896,14 +18224,21 @@ var devCommand = new Command("dev").description("Sync agent to development envir
|
|
|
17896
18224
|
spinner.start("Loading agent");
|
|
17897
18225
|
let agent = await loadAgent(cwd);
|
|
17898
18226
|
spinner.succeed(`Agent "${agent.name}" loaded`);
|
|
17899
|
-
const
|
|
18227
|
+
const claudeMdPath = join7(cwd, ".claude.md");
|
|
18228
|
+
if (!existsSync5(claudeMdPath)) {
|
|
18229
|
+
writeFileSync4(claudeMdPath, getClaudeMd(project.agent.slug));
|
|
18230
|
+
console.log(source_default.green("\u2713"), "Created .claude.md");
|
|
18231
|
+
}
|
|
18232
|
+
let credentials = loadCredentials();
|
|
17900
18233
|
const apiKey = getApiKey();
|
|
17901
18234
|
if (!credentials && !apiKey) {
|
|
17902
|
-
|
|
17903
|
-
console.log();
|
|
17904
|
-
console.log(source_default.gray("Run"), source_default.cyan("struere login"), source_default.gray("to authenticate"));
|
|
18235
|
+
console.log(source_default.gray("Authentication required"));
|
|
17905
18236
|
console.log();
|
|
17906
|
-
|
|
18237
|
+
credentials = await performLogin();
|
|
18238
|
+
if (!credentials) {
|
|
18239
|
+
console.log(source_default.red("Authentication failed"));
|
|
18240
|
+
process.exit(1);
|
|
18241
|
+
}
|
|
17907
18242
|
}
|
|
17908
18243
|
spinner.start("Syncing to Convex");
|
|
17909
18244
|
const performSync = async () => {
|
|
@@ -17918,13 +18253,38 @@ var devCommand = new Command("dev").description("Sync agent to development envir
|
|
|
17918
18253
|
throw error;
|
|
17919
18254
|
}
|
|
17920
18255
|
};
|
|
18256
|
+
const isAuthError = (error) => {
|
|
18257
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18258
|
+
return message.includes("Unauthenticated") || message.includes("OIDC") || message.includes("token") || message.includes("expired");
|
|
18259
|
+
};
|
|
17921
18260
|
try {
|
|
17922
18261
|
await performSync();
|
|
17923
18262
|
spinner.succeed("Synced to development");
|
|
17924
18263
|
} catch (error) {
|
|
17925
|
-
|
|
17926
|
-
|
|
17927
|
-
|
|
18264
|
+
if (isAuthError(error)) {
|
|
18265
|
+
spinner.fail("Session expired");
|
|
18266
|
+
console.log();
|
|
18267
|
+
console.log(source_default.gray("Re-authenticating..."));
|
|
18268
|
+
clearCredentials();
|
|
18269
|
+
credentials = await performLogin();
|
|
18270
|
+
if (!credentials) {
|
|
18271
|
+
console.log(source_default.red("Authentication failed"));
|
|
18272
|
+
process.exit(1);
|
|
18273
|
+
}
|
|
18274
|
+
spinner.start("Syncing to Convex");
|
|
18275
|
+
try {
|
|
18276
|
+
await performSync();
|
|
18277
|
+
spinner.succeed("Synced to development");
|
|
18278
|
+
} catch (retryError) {
|
|
18279
|
+
spinner.fail("Sync failed");
|
|
18280
|
+
console.log(source_default.red("Error:"), retryError instanceof Error ? retryError.message : String(retryError));
|
|
18281
|
+
process.exit(1);
|
|
18282
|
+
}
|
|
18283
|
+
} else {
|
|
18284
|
+
spinner.fail("Sync failed");
|
|
18285
|
+
console.log(source_default.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
18286
|
+
process.exit(1);
|
|
18287
|
+
}
|
|
17928
18288
|
}
|
|
17929
18289
|
const devUrl = `https://${project.agent.slug}-dev.struere.dev`;
|
|
17930
18290
|
console.log();
|
|
@@ -17945,8 +18305,28 @@ var devCommand = new Command("dev").description("Sync agent to development envir
|
|
|
17945
18305
|
await performSync();
|
|
17946
18306
|
syncSpinner.succeed("Synced");
|
|
17947
18307
|
} catch (error) {
|
|
17948
|
-
|
|
17949
|
-
|
|
18308
|
+
if (isAuthError(error)) {
|
|
18309
|
+
syncSpinner.fail("Session expired");
|
|
18310
|
+
console.log();
|
|
18311
|
+
console.log(source_default.gray("Re-authenticating..."));
|
|
18312
|
+
clearCredentials();
|
|
18313
|
+
const newCredentials = await performLogin();
|
|
18314
|
+
if (!newCredentials) {
|
|
18315
|
+
console.log(source_default.red("Authentication failed"));
|
|
18316
|
+
return;
|
|
18317
|
+
}
|
|
18318
|
+
const retrySyncSpinner = ora("Syncing...").start();
|
|
18319
|
+
try {
|
|
18320
|
+
await performSync();
|
|
18321
|
+
retrySyncSpinner.succeed("Synced");
|
|
18322
|
+
} catch (retryError) {
|
|
18323
|
+
retrySyncSpinner.fail("Sync failed");
|
|
18324
|
+
console.log(source_default.red("Error:"), retryError instanceof Error ? retryError.message : String(retryError));
|
|
18325
|
+
}
|
|
18326
|
+
} else {
|
|
18327
|
+
syncSpinner.fail("Sync failed");
|
|
18328
|
+
console.log(source_default.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
18329
|
+
}
|
|
17950
18330
|
}
|
|
17951
18331
|
});
|
|
17952
18332
|
process.on("SIGINT", () => {
|
|
@@ -18768,9 +19148,17 @@ var whoamiCommand = new Command("whoami").description("Show current logged in us
|
|
|
18768
19148
|
// package.json
|
|
18769
19149
|
var package_default = {
|
|
18770
19150
|
name: "struere",
|
|
18771
|
-
version: "0.3.
|
|
19151
|
+
version: "0.3.8",
|
|
18772
19152
|
description: "Build, test, and deploy AI agents",
|
|
18773
|
-
keywords: [
|
|
19153
|
+
keywords: [
|
|
19154
|
+
"ai",
|
|
19155
|
+
"agents",
|
|
19156
|
+
"llm",
|
|
19157
|
+
"anthropic",
|
|
19158
|
+
"openai",
|
|
19159
|
+
"framework",
|
|
19160
|
+
"cli"
|
|
19161
|
+
],
|
|
18774
19162
|
author: "struere",
|
|
18775
19163
|
license: "MIT",
|
|
18776
19164
|
publishConfig: {
|
|
@@ -18797,7 +19185,9 @@ var package_default = {
|
|
|
18797
19185
|
types: "./dist/index.d.ts"
|
|
18798
19186
|
}
|
|
18799
19187
|
},
|
|
18800
|
-
files: [
|
|
19188
|
+
files: [
|
|
19189
|
+
"dist"
|
|
19190
|
+
],
|
|
18801
19191
|
scripts: {
|
|
18802
19192
|
build: "bun build ./src/cli/index.ts --outdir ./dist/cli --target bun --external commander --external chalk --external ora --external chokidar --external yaml && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/bin/struere.ts --outdir ./dist/bin --target bun && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
|
|
18803
19193
|
dev: "tsc --watch",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAenC,eAAO,MAAM,UAAU,SAuKnB,CAAA"}
|
package/dist/cli/index.js
CHANGED
|
@@ -819,6 +819,332 @@ function getEnvLocal(deploymentUrl) {
|
|
|
819
819
|
return `STRUERE_DEPLOYMENT_URL=${deploymentUrl}
|
|
820
820
|
`;
|
|
821
821
|
}
|
|
822
|
+
function getClaudeMd(name) {
|
|
823
|
+
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
824
|
+
return `# ${displayName} Agent
|
|
825
|
+
|
|
826
|
+
This is a Struere AI agent project. Struere is a framework for building production AI agents with built-in data management, event tracking, and job scheduling.
|
|
827
|
+
|
|
828
|
+
## Project Structure
|
|
829
|
+
|
|
830
|
+
\`\`\`
|
|
831
|
+
src/
|
|
832
|
+
\u251C\u2500\u2500 agent.ts # Agent definition (system prompt, model, tools)
|
|
833
|
+
\u251C\u2500\u2500 context.ts # Dynamic context injection per request
|
|
834
|
+
\u251C\u2500\u2500 tools.ts # Custom tool definitions
|
|
835
|
+
\u2514\u2500\u2500 workflows/ # Multi-step workflow definitions
|
|
836
|
+
tests/
|
|
837
|
+
\u2514\u2500\u2500 *.test.yaml # YAML-based conversation tests
|
|
838
|
+
struere.json # Project configuration (agentId, team, slug)
|
|
839
|
+
struere.config.ts # Framework settings (port, CORS, logging)
|
|
840
|
+
\`\`\`
|
|
841
|
+
|
|
842
|
+
## Agent Definition
|
|
843
|
+
|
|
844
|
+
Define your agent in \`src/agent.ts\`:
|
|
845
|
+
|
|
846
|
+
\`\`\`typescript
|
|
847
|
+
import { defineAgent } from 'struere'
|
|
848
|
+
import { tools } from './tools'
|
|
849
|
+
import { context } from './context'
|
|
850
|
+
|
|
851
|
+
export default defineAgent({
|
|
852
|
+
name: 'my-agent',
|
|
853
|
+
version: '0.1.0',
|
|
854
|
+
description: 'My AI Agent',
|
|
855
|
+
model: {
|
|
856
|
+
provider: 'anthropic',
|
|
857
|
+
name: 'claude-sonnet-4-20250514',
|
|
858
|
+
temperature: 0.7,
|
|
859
|
+
maxTokens: 4096,
|
|
860
|
+
},
|
|
861
|
+
systemPrompt: \\\`You are a helpful assistant.
|
|
862
|
+
|
|
863
|
+
Current time: {{datetime}}
|
|
864
|
+
Customer: {{entity.get({"id": "{{thread.metadata.customerId}}"})}}\\\`,
|
|
865
|
+
tools,
|
|
866
|
+
context,
|
|
867
|
+
state: {
|
|
868
|
+
storage: 'memory',
|
|
869
|
+
ttl: 3600,
|
|
870
|
+
},
|
|
871
|
+
})
|
|
872
|
+
\`\`\`
|
|
873
|
+
|
|
874
|
+
## System Prompt Templates
|
|
875
|
+
|
|
876
|
+
System prompts support dynamic \`{{...}}\` templates that are resolved at runtime before the LLM call.
|
|
877
|
+
|
|
878
|
+
### Available Variables
|
|
879
|
+
|
|
880
|
+
| Variable | Description |
|
|
881
|
+
|----------|-------------|
|
|
882
|
+
| \`{{organizationId}}\` | Current organization ID |
|
|
883
|
+
| \`{{userId}}\` | Current user ID |
|
|
884
|
+
| \`{{threadId}}\` | Conversation thread ID |
|
|
885
|
+
| \`{{agentId}}\` | Agent ID |
|
|
886
|
+
| \`{{agent.name}}\` | Agent name |
|
|
887
|
+
| \`{{agent.slug}}\` | Agent slug |
|
|
888
|
+
| \`{{thread.metadata.X}}\` | Thread metadata field X |
|
|
889
|
+
| \`{{message}}\` | Current user message |
|
|
890
|
+
| \`{{timestamp}}\` | Unix timestamp (ms) |
|
|
891
|
+
| \`{{datetime}}\` | ISO 8601 datetime |
|
|
892
|
+
|
|
893
|
+
### Function Calls
|
|
894
|
+
|
|
895
|
+
Call any agent tool directly in the system prompt:
|
|
896
|
+
|
|
897
|
+
\`\`\`
|
|
898
|
+
{{entity.get({"id": "ent_123"})}}
|
|
899
|
+
{{entity.query({"type": "customer", "limit": 5})}}
|
|
900
|
+
{{event.query({"entityId": "ent_123", "limit": 10})}}
|
|
901
|
+
\`\`\`
|
|
902
|
+
|
|
903
|
+
### Nested Templates
|
|
904
|
+
|
|
905
|
+
Variables can be used inside function arguments:
|
|
906
|
+
|
|
907
|
+
\`\`\`
|
|
908
|
+
{{entity.get({"id": "{{thread.metadata.customerId}}"})}}
|
|
909
|
+
\`\`\`
|
|
910
|
+
|
|
911
|
+
### Error Handling
|
|
912
|
+
|
|
913
|
+
Failed templates are replaced with inline errors:
|
|
914
|
+
\`\`\`
|
|
915
|
+
[TEMPLATE_ERROR: variableName not found]
|
|
916
|
+
[TEMPLATE_ERROR: toolName - error message]
|
|
917
|
+
\`\`\`
|
|
918
|
+
|
|
919
|
+
## Custom Tools
|
|
920
|
+
|
|
921
|
+
Define tools in \`src/tools.ts\`:
|
|
922
|
+
|
|
923
|
+
\`\`\`typescript
|
|
924
|
+
import { defineTools } from 'struere'
|
|
925
|
+
|
|
926
|
+
export const tools = defineTools([
|
|
927
|
+
{
|
|
928
|
+
name: 'search_products',
|
|
929
|
+
description: 'Search the product catalog',
|
|
930
|
+
parameters: {
|
|
931
|
+
type: 'object',
|
|
932
|
+
properties: {
|
|
933
|
+
query: { type: 'string', description: 'Search query' },
|
|
934
|
+
limit: { type: 'number', description: 'Max results' },
|
|
935
|
+
},
|
|
936
|
+
required: ['query'],
|
|
937
|
+
},
|
|
938
|
+
handler: async (params) => {
|
|
939
|
+
const results = await searchProducts(params.query, params.limit ?? 10)
|
|
940
|
+
return { products: results }
|
|
941
|
+
},
|
|
942
|
+
},
|
|
943
|
+
])
|
|
944
|
+
\`\`\`
|
|
945
|
+
|
|
946
|
+
Custom tool handlers are executed in a sandboxed Cloudflare Worker environment. They can make HTTP requests to allowlisted domains:
|
|
947
|
+
- api.openai.com, api.anthropic.com, api.stripe.com
|
|
948
|
+
- api.sendgrid.com, api.twilio.com, hooks.slack.com
|
|
949
|
+
- discord.com, api.github.com
|
|
950
|
+
|
|
951
|
+
## Built-in Tools
|
|
952
|
+
|
|
953
|
+
Agents have access to these built-in tools for data management:
|
|
954
|
+
|
|
955
|
+
### Entity Tools
|
|
956
|
+
|
|
957
|
+
| Tool | Description |
|
|
958
|
+
|------|-------------|
|
|
959
|
+
| \`entity.create\` | Create a new entity |
|
|
960
|
+
| \`entity.get\` | Get entity by ID |
|
|
961
|
+
| \`entity.query\` | Query entities by type/filters |
|
|
962
|
+
| \`entity.update\` | Update entity data |
|
|
963
|
+
| \`entity.delete\` | Soft-delete entity |
|
|
964
|
+
| \`entity.link\` | Create entity relation |
|
|
965
|
+
| \`entity.unlink\` | Remove entity relation |
|
|
966
|
+
|
|
967
|
+
Example entity operations:
|
|
968
|
+
\`\`\`json
|
|
969
|
+
// entity.create
|
|
970
|
+
{ "type": "customer", "data": { "name": "John", "email": "john@example.com" } }
|
|
971
|
+
|
|
972
|
+
// entity.query
|
|
973
|
+
{ "type": "customer", "filters": { "status": "active" }, "limit": 10 }
|
|
974
|
+
|
|
975
|
+
// entity.update
|
|
976
|
+
{ "id": "ent_123", "data": { "status": "vip" } }
|
|
977
|
+
\`\`\`
|
|
978
|
+
|
|
979
|
+
### Event Tools
|
|
980
|
+
|
|
981
|
+
| Tool | Description |
|
|
982
|
+
|------|-------------|
|
|
983
|
+
| \`event.emit\` | Emit a custom event |
|
|
984
|
+
| \`event.query\` | Query event history |
|
|
985
|
+
|
|
986
|
+
Example event operations:
|
|
987
|
+
\`\`\`json
|
|
988
|
+
// event.emit
|
|
989
|
+
{ "entityId": "ent_123", "eventType": "order.placed", "payload": { "amount": 99.99 } }
|
|
990
|
+
|
|
991
|
+
// event.query
|
|
992
|
+
{ "entityId": "ent_123", "eventType": "order.*", "limit": 20 }
|
|
993
|
+
\`\`\`
|
|
994
|
+
|
|
995
|
+
### Job Tools
|
|
996
|
+
|
|
997
|
+
| Tool | Description |
|
|
998
|
+
|------|-------------|
|
|
999
|
+
| \`job.enqueue\` | Schedule a background job |
|
|
1000
|
+
| \`job.status\` | Get job status |
|
|
1001
|
+
|
|
1002
|
+
Example job operations:
|
|
1003
|
+
\`\`\`json
|
|
1004
|
+
// job.enqueue
|
|
1005
|
+
{ "jobType": "send_email", "payload": { "to": "user@example.com" }, "scheduledFor": 1706745600000 }
|
|
1006
|
+
|
|
1007
|
+
// job.status
|
|
1008
|
+
{ "id": "job_abc123" }
|
|
1009
|
+
\`\`\`
|
|
1010
|
+
|
|
1011
|
+
## Context Function
|
|
1012
|
+
|
|
1013
|
+
Inject dynamic context per request in \`src/context.ts\`:
|
|
1014
|
+
|
|
1015
|
+
\`\`\`typescript
|
|
1016
|
+
import { defineContext } from 'struere'
|
|
1017
|
+
|
|
1018
|
+
export const context = defineContext(async (request) => {
|
|
1019
|
+
const { conversationId, userId, channel, state } = request
|
|
1020
|
+
|
|
1021
|
+
const userProfile = await fetchUserProfile(userId)
|
|
1022
|
+
|
|
1023
|
+
return {
|
|
1024
|
+
additionalContext: \\\`
|
|
1025
|
+
User: \${userProfile.name} (\${userProfile.tier} tier)
|
|
1026
|
+
Conversation: \${conversationId}
|
|
1027
|
+
Channel: \${channel}
|
|
1028
|
+
\\\`,
|
|
1029
|
+
variables: {
|
|
1030
|
+
userId,
|
|
1031
|
+
userTier: userProfile.tier,
|
|
1032
|
+
timestamp: new Date().toISOString(),
|
|
1033
|
+
},
|
|
1034
|
+
}
|
|
1035
|
+
})
|
|
1036
|
+
\`\`\`
|
|
1037
|
+
|
|
1038
|
+
## Testing
|
|
1039
|
+
|
|
1040
|
+
Write YAML-based conversation tests in \`tests/\`:
|
|
1041
|
+
|
|
1042
|
+
\`\`\`yaml
|
|
1043
|
+
name: Order flow test
|
|
1044
|
+
description: Test the complete order flow
|
|
1045
|
+
|
|
1046
|
+
conversation:
|
|
1047
|
+
- role: user
|
|
1048
|
+
content: I want to order a pizza
|
|
1049
|
+
- role: assistant
|
|
1050
|
+
assertions:
|
|
1051
|
+
- type: contains
|
|
1052
|
+
value: size
|
|
1053
|
+
- type: toolCalled
|
|
1054
|
+
value: get_menu
|
|
1055
|
+
|
|
1056
|
+
- role: user
|
|
1057
|
+
content: Large pepperoni please
|
|
1058
|
+
- role: assistant
|
|
1059
|
+
assertions:
|
|
1060
|
+
- type: toolCalled
|
|
1061
|
+
value: entity.create
|
|
1062
|
+
\`\`\`
|
|
1063
|
+
|
|
1064
|
+
### Assertion Types
|
|
1065
|
+
|
|
1066
|
+
| Type | Description |
|
|
1067
|
+
|------|-------------|
|
|
1068
|
+
| \`contains\` | Response contains substring |
|
|
1069
|
+
| \`matches\` | Response matches regex |
|
|
1070
|
+
| \`toolCalled\` | Specific tool was called |
|
|
1071
|
+
| \`noToolCalled\` | No tools were called |
|
|
1072
|
+
|
|
1073
|
+
Run tests with:
|
|
1074
|
+
\`\`\`bash
|
|
1075
|
+
bun run test
|
|
1076
|
+
\`\`\`
|
|
1077
|
+
|
|
1078
|
+
## CLI Commands
|
|
1079
|
+
|
|
1080
|
+
| Command | Description |
|
|
1081
|
+
|---------|-------------|
|
|
1082
|
+
| \`struere dev\` | Start development mode (live sync to Convex) |
|
|
1083
|
+
| \`struere build\` | Validate agent configuration |
|
|
1084
|
+
| \`struere deploy\` | Deploy agent to production |
|
|
1085
|
+
| \`struere test\` | Run YAML conversation tests |
|
|
1086
|
+
| \`struere logs\` | View recent execution logs |
|
|
1087
|
+
| \`struere state\` | Inspect conversation thread state |
|
|
1088
|
+
|
|
1089
|
+
## Thread Metadata
|
|
1090
|
+
|
|
1091
|
+
Set thread metadata when creating conversations to provide context:
|
|
1092
|
+
|
|
1093
|
+
\`\`\`typescript
|
|
1094
|
+
// Via API
|
|
1095
|
+
POST /v1/chat
|
|
1096
|
+
{
|
|
1097
|
+
"agentId": "agent_123",
|
|
1098
|
+
"message": "Hello",
|
|
1099
|
+
"metadata": {
|
|
1100
|
+
"customerId": "ent_customer_456",
|
|
1101
|
+
"channel": "web",
|
|
1102
|
+
"language": "en"
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
\`\`\`
|
|
1106
|
+
|
|
1107
|
+
Access in system prompt:
|
|
1108
|
+
\`\`\`
|
|
1109
|
+
Customer: {{entity.get({"id": "{{thread.metadata.customerId}}"})}}
|
|
1110
|
+
Channel: {{thread.metadata.channel}}
|
|
1111
|
+
\`\`\`
|
|
1112
|
+
|
|
1113
|
+
## Development Workflow
|
|
1114
|
+
|
|
1115
|
+
1. **Edit agent configuration** in \`src/agent.ts\`
|
|
1116
|
+
2. **Run \`bun run dev\`** to sync changes to Convex
|
|
1117
|
+
3. **Test via API** or dashboard chat interface
|
|
1118
|
+
4. **Write tests** in \`tests/*.test.yaml\`
|
|
1119
|
+
5. **Deploy** with \`bun run deploy\`
|
|
1120
|
+
|
|
1121
|
+
## API Endpoints
|
|
1122
|
+
|
|
1123
|
+
| Endpoint | Method | Description |
|
|
1124
|
+
|----------|--------|-------------|
|
|
1125
|
+
| \`/v1/chat\` | POST | Chat by agent ID |
|
|
1126
|
+
| \`/v1/agents/:slug/chat\` | POST | Chat by agent slug |
|
|
1127
|
+
|
|
1128
|
+
Authentication: Bearer token (API key from dashboard)
|
|
1129
|
+
|
|
1130
|
+
\`\`\`bash
|
|
1131
|
+
curl -X POST https://your-deployment.convex.cloud/v1/chat \\
|
|
1132
|
+
-H "Authorization: Bearer sk_live_..." \\
|
|
1133
|
+
-H "Content-Type: application/json" \\
|
|
1134
|
+
-d '{"agentId": "...", "message": "Hello"}'
|
|
1135
|
+
\`\`\`
|
|
1136
|
+
|
|
1137
|
+
## Best Practices
|
|
1138
|
+
|
|
1139
|
+
1. **System Prompts**: Use templates for dynamic data instead of hardcoding
|
|
1140
|
+
2. **Tools**: Keep tool handlers focused and stateless
|
|
1141
|
+
3. **Entities**: Model your domain data as entity types
|
|
1142
|
+
4. **Events**: Emit events for audit trails and analytics
|
|
1143
|
+
5. **Jobs**: Use jobs for async operations (emails, notifications)
|
|
1144
|
+
6. **Testing**: Write tests for critical conversation flows
|
|
1145
|
+
7. **Context**: Use context for user-specific personalization
|
|
1146
|
+
`;
|
|
1147
|
+
}
|
|
822
1148
|
|
|
823
1149
|
// src/cli/utils/scaffold.ts
|
|
824
1150
|
function ensureDir(filePath) {
|
|
@@ -858,7 +1184,8 @@ function scaffoldAgentFiles(cwd, projectName) {
|
|
|
858
1184
|
"src/tools.ts": getToolsTs(),
|
|
859
1185
|
"src/workflows/.gitkeep": "",
|
|
860
1186
|
"tests/basic.test.yaml": getBasicTestYaml(),
|
|
861
|
-
".env.example": getEnvExample()
|
|
1187
|
+
".env.example": getEnvExample(),
|
|
1188
|
+
".claude.md": getClaudeMd(projectName)
|
|
862
1189
|
};
|
|
863
1190
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
864
1191
|
const fullPath = join3(cwd, relativePath);
|
|
@@ -1139,6 +1466,7 @@ import chalk3 from "chalk";
|
|
|
1139
1466
|
import ora3 from "ora";
|
|
1140
1467
|
import chokidar from "chokidar";
|
|
1141
1468
|
import { join as join7, basename as basename2 } from "path";
|
|
1469
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
1142
1470
|
|
|
1143
1471
|
// src/cli/utils/config.ts
|
|
1144
1472
|
import { join as join5 } from "path";
|
|
@@ -1238,14 +1566,21 @@ var devCommand = new Command3("dev").description("Sync agent to development envi
|
|
|
1238
1566
|
spinner.start("Loading agent");
|
|
1239
1567
|
let agent = await loadAgent(cwd);
|
|
1240
1568
|
spinner.succeed(`Agent "${agent.name}" loaded`);
|
|
1241
|
-
const
|
|
1569
|
+
const claudeMdPath = join7(cwd, ".claude.md");
|
|
1570
|
+
if (!existsSync5(claudeMdPath)) {
|
|
1571
|
+
writeFileSync4(claudeMdPath, getClaudeMd(project.agent.slug));
|
|
1572
|
+
console.log(chalk3.green("\u2713"), "Created .claude.md");
|
|
1573
|
+
}
|
|
1574
|
+
let credentials = loadCredentials();
|
|
1242
1575
|
const apiKey = getApiKey();
|
|
1243
1576
|
if (!credentials && !apiKey) {
|
|
1244
|
-
|
|
1245
|
-
console.log();
|
|
1246
|
-
console.log(chalk3.gray("Run"), chalk3.cyan("struere login"), chalk3.gray("to authenticate"));
|
|
1577
|
+
console.log(chalk3.gray("Authentication required"));
|
|
1247
1578
|
console.log();
|
|
1248
|
-
|
|
1579
|
+
credentials = await performLogin();
|
|
1580
|
+
if (!credentials) {
|
|
1581
|
+
console.log(chalk3.red("Authentication failed"));
|
|
1582
|
+
process.exit(1);
|
|
1583
|
+
}
|
|
1249
1584
|
}
|
|
1250
1585
|
spinner.start("Syncing to Convex");
|
|
1251
1586
|
const performSync = async () => {
|
|
@@ -1260,13 +1595,38 @@ var devCommand = new Command3("dev").description("Sync agent to development envi
|
|
|
1260
1595
|
throw error;
|
|
1261
1596
|
}
|
|
1262
1597
|
};
|
|
1598
|
+
const isAuthError = (error) => {
|
|
1599
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1600
|
+
return message.includes("Unauthenticated") || message.includes("OIDC") || message.includes("token") || message.includes("expired");
|
|
1601
|
+
};
|
|
1263
1602
|
try {
|
|
1264
1603
|
await performSync();
|
|
1265
1604
|
spinner.succeed("Synced to development");
|
|
1266
1605
|
} catch (error) {
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1606
|
+
if (isAuthError(error)) {
|
|
1607
|
+
spinner.fail("Session expired");
|
|
1608
|
+
console.log();
|
|
1609
|
+
console.log(chalk3.gray("Re-authenticating..."));
|
|
1610
|
+
clearCredentials();
|
|
1611
|
+
credentials = await performLogin();
|
|
1612
|
+
if (!credentials) {
|
|
1613
|
+
console.log(chalk3.red("Authentication failed"));
|
|
1614
|
+
process.exit(1);
|
|
1615
|
+
}
|
|
1616
|
+
spinner.start("Syncing to Convex");
|
|
1617
|
+
try {
|
|
1618
|
+
await performSync();
|
|
1619
|
+
spinner.succeed("Synced to development");
|
|
1620
|
+
} catch (retryError) {
|
|
1621
|
+
spinner.fail("Sync failed");
|
|
1622
|
+
console.log(chalk3.red("Error:"), retryError instanceof Error ? retryError.message : String(retryError));
|
|
1623
|
+
process.exit(1);
|
|
1624
|
+
}
|
|
1625
|
+
} else {
|
|
1626
|
+
spinner.fail("Sync failed");
|
|
1627
|
+
console.log(chalk3.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
1628
|
+
process.exit(1);
|
|
1629
|
+
}
|
|
1270
1630
|
}
|
|
1271
1631
|
const devUrl = `https://${project.agent.slug}-dev.struere.dev`;
|
|
1272
1632
|
console.log();
|
|
@@ -1287,8 +1647,28 @@ var devCommand = new Command3("dev").description("Sync agent to development envi
|
|
|
1287
1647
|
await performSync();
|
|
1288
1648
|
syncSpinner.succeed("Synced");
|
|
1289
1649
|
} catch (error) {
|
|
1290
|
-
|
|
1291
|
-
|
|
1650
|
+
if (isAuthError(error)) {
|
|
1651
|
+
syncSpinner.fail("Session expired");
|
|
1652
|
+
console.log();
|
|
1653
|
+
console.log(chalk3.gray("Re-authenticating..."));
|
|
1654
|
+
clearCredentials();
|
|
1655
|
+
const newCredentials = await performLogin();
|
|
1656
|
+
if (!newCredentials) {
|
|
1657
|
+
console.log(chalk3.red("Authentication failed"));
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
const retrySyncSpinner = ora3("Syncing...").start();
|
|
1661
|
+
try {
|
|
1662
|
+
await performSync();
|
|
1663
|
+
retrySyncSpinner.succeed("Synced");
|
|
1664
|
+
} catch (retryError) {
|
|
1665
|
+
retrySyncSpinner.fail("Sync failed");
|
|
1666
|
+
console.log(chalk3.red("Error:"), retryError instanceof Error ? retryError.message : String(retryError));
|
|
1667
|
+
}
|
|
1668
|
+
} else {
|
|
1669
|
+
syncSpinner.fail("Sync failed");
|
|
1670
|
+
console.log(chalk3.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
1671
|
+
}
|
|
1292
1672
|
}
|
|
1293
1673
|
});
|
|
1294
1674
|
process.on("SIGINT", () => {
|
|
@@ -2133,9 +2513,17 @@ var whoamiCommand = new Command11("whoami").description("Show current logged in
|
|
|
2133
2513
|
// package.json
|
|
2134
2514
|
var package_default = {
|
|
2135
2515
|
name: "struere",
|
|
2136
|
-
version: "0.3.
|
|
2516
|
+
version: "0.3.8",
|
|
2137
2517
|
description: "Build, test, and deploy AI agents",
|
|
2138
|
-
keywords: [
|
|
2518
|
+
keywords: [
|
|
2519
|
+
"ai",
|
|
2520
|
+
"agents",
|
|
2521
|
+
"llm",
|
|
2522
|
+
"anthropic",
|
|
2523
|
+
"openai",
|
|
2524
|
+
"framework",
|
|
2525
|
+
"cli"
|
|
2526
|
+
],
|
|
2139
2527
|
author: "struere",
|
|
2140
2528
|
license: "MIT",
|
|
2141
2529
|
publishConfig: {
|
|
@@ -2162,7 +2550,9 @@ var package_default = {
|
|
|
2162
2550
|
types: "./dist/index.d.ts"
|
|
2163
2551
|
}
|
|
2164
2552
|
},
|
|
2165
|
-
files: [
|
|
2553
|
+
files: [
|
|
2554
|
+
"dist"
|
|
2555
|
+
],
|
|
2166
2556
|
scripts: {
|
|
2167
2557
|
build: "bun build ./src/cli/index.ts --outdir ./dist/cli --target bun --external commander --external chalk --external ora --external chokidar --external yaml && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/bin/struere.ts --outdir ./dist/bin --target bun && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
|
|
2168
2558
|
dev: "tsc --watch",
|
|
@@ -9,4 +9,5 @@ export declare function getEnvExample(): string;
|
|
|
9
9
|
export declare function getGitignore(): string;
|
|
10
10
|
export declare function getStruereJson(agentId: string, team: string, slug: string, name: string): string;
|
|
11
11
|
export declare function getEnvLocal(deploymentUrl: string): string;
|
|
12
|
+
export declare function getClaudeMd(name: string): string;
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/index.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAuBnD;AAED,wBAAgB,WAAW,IAAI,MAAM,CAsBpC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAgBzC;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoC/C;AAED,wBAAgB,YAAY,IAAI,MAAM,CAkBrC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAoDnC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAmBzC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAatC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAgBrC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAahG;AAED,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAGzD"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/index.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAuBnD;AAED,wBAAgB,WAAW,IAAI,MAAM,CAsBpC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAgBzC;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoC/C;AAED,wBAAgB,YAAY,IAAI,MAAM,CAkBrC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAoDnC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAmBzC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAatC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAgBrC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAahG;AAED,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAyUhD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/scaffold.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/scaffold.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAeD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAexF;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAgCrF;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,CA+BnF;AAsBD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "struere",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "Build, test, and deploy AI agents",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"agents",
|
|
8
|
+
"llm",
|
|
9
|
+
"anthropic",
|
|
10
|
+
"openai",
|
|
11
|
+
"framework",
|
|
12
|
+
"cli"
|
|
13
|
+
],
|
|
6
14
|
"author": "struere",
|
|
7
15
|
"license": "MIT",
|
|
8
16
|
"publishConfig": {
|
|
@@ -29,7 +37,9 @@
|
|
|
29
37
|
"types": "./dist/index.d.ts"
|
|
30
38
|
}
|
|
31
39
|
},
|
|
32
|
-
"files": [
|
|
40
|
+
"files": [
|
|
41
|
+
"dist"
|
|
42
|
+
],
|
|
33
43
|
"scripts": {
|
|
34
44
|
"build": "bun build ./src/cli/index.ts --outdir ./dist/cli --target bun --external commander --external chalk --external ora --external chokidar --external yaml && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/bin/struere.ts --outdir ./dist/bin --target bun && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
|
|
35
45
|
"dev": "tsc --watch",
|