agentlaunch 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +475 -0
- package/dist/__tests__/config.test.d.ts +20 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +155 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/commands/buy.d.ts +10 -0
- package/dist/commands/buy.d.ts.map +1 -0
- package/dist/commands/buy.js +126 -0
- package/dist/commands/buy.js.map +1 -0
- package/dist/commands/comments.d.ts +15 -0
- package/dist/commands/comments.d.ts.map +1 -0
- package/dist/commands/comments.js +137 -0
- package/dist/commands/comments.js.map +1 -0
- package/dist/commands/config.d.ts +10 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +63 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/create.d.ts +33 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +1177 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/deploy.d.ts +18 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +178 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/holders.d.ts +13 -0
- package/dist/commands/holders.d.ts.map +1 -0
- package/dist/commands/holders.js +114 -0
- package/dist/commands/holders.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +506 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +10 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +135 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/optimize.d.ts +12 -0
- package/dist/commands/optimize.d.ts.map +1 -0
- package/dist/commands/optimize.js +134 -0
- package/dist/commands/optimize.js.map +1 -0
- package/dist/commands/scaffold.d.ts +25 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/scaffold.js +123 -0
- package/dist/commands/scaffold.js.map +1 -0
- package/dist/commands/sell.d.ts +10 -0
- package/dist/commands/sell.d.ts.map +1 -0
- package/dist/commands/sell.js +109 -0
- package/dist/commands/sell.js.map +1 -0
- package/dist/commands/status.d.ts +10 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +124 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/tokenize.d.ts +16 -0
- package/dist/commands/tokenize.d.ts.map +1 -0
- package/dist/commands/tokenize.js +207 -0
- package/dist/commands/tokenize.js.map +1 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +137 -0
- package/dist/config.js.map +1 -0
- package/dist/http.d.ts +24 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +53 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,1177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-002 + CLI-006: create command
|
|
3
|
+
*
|
|
4
|
+
* agentlaunch create [--name "My Agent"] [--ticker MYAG] [--template custom]
|
|
5
|
+
* [--description "..."] [--chain 97] [--deploy] [--tokenize] [--json]
|
|
6
|
+
*
|
|
7
|
+
* Flagship one-command flow:
|
|
8
|
+
* 1. Scaffold agent project (always) — via agentlaunch-templates
|
|
9
|
+
* 2. Deploy to Agentverse (if --deploy) — via agentlaunch-sdk deployAgent
|
|
10
|
+
* 3. Tokenize on AgentLaunch (if --tokenize) — via agentlaunch-sdk client
|
|
11
|
+
* 4. Print handoff link
|
|
12
|
+
*
|
|
13
|
+
* When name/ticker are omitted, uses readline for interactive prompts.
|
|
14
|
+
*
|
|
15
|
+
* Platform constants (source of truth: deployed smart contracts):
|
|
16
|
+
* - Deploy fee: 120 FET (read dynamically, can change via multi-sig)
|
|
17
|
+
* - Graduation target: 30,000 FET -> auto DEX listing
|
|
18
|
+
* - Trading fee: 2% -> 100% to protocol treasury (NO creator fee)
|
|
19
|
+
*/
|
|
20
|
+
import fs from "node:fs";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import readline from "node:readline";
|
|
23
|
+
import { spawn } from "node:child_process";
|
|
24
|
+
import { deployAgent, getFrontendUrl } from "agentlaunch-sdk";
|
|
25
|
+
import { execSync } from "node:child_process";
|
|
26
|
+
import { generateFromTemplate, listTemplates, RULES, SKILLS, DOCS, EXAMPLES, buildPackageJson, CURSOR_MCP_CONFIG, CURSOR_RULES, buildSwarmClaudeMd, buildSwarmConfig, buildSwarmPackageJson, buildProjectSkills } from "agentlaunch-templates";
|
|
27
|
+
import { getClient } from "../http.js";
|
|
28
|
+
import { requireApiKey } from "../config.js";
|
|
29
|
+
const TEMPLATES = {};
|
|
30
|
+
// Populate from templates package at module load time
|
|
31
|
+
for (const t of listTemplates()) {
|
|
32
|
+
TEMPLATES[t.name] = { label: t.name, description: t.description };
|
|
33
|
+
}
|
|
34
|
+
// Legacy CLI type aliases -> template names
|
|
35
|
+
const LEGACY_ALIAS = {
|
|
36
|
+
faucet: "custom",
|
|
37
|
+
research: "research",
|
|
38
|
+
trading: "trading-bot",
|
|
39
|
+
data: "data-analyzer",
|
|
40
|
+
genesis: "swarm-starter", // Legacy alias for swarm-starter
|
|
41
|
+
};
|
|
42
|
+
// Genesis preset names for swarm mode
|
|
43
|
+
const GENESIS_PRESETS = [
|
|
44
|
+
{ name: "oracle", label: "Oracle", description: "Market data provider (0.001 FET/call)" },
|
|
45
|
+
{ name: "brain", label: "Brain", description: "LLM reasoning engine (0.01 FET/call)" },
|
|
46
|
+
{ name: "analyst", label: "Analyst", description: "Token scoring & evaluation (0.005 FET/call)" },
|
|
47
|
+
{ name: "coordinator", label: "Coordinator", description: "Query routing (0.0005 FET/call)" },
|
|
48
|
+
{ name: "sentinel", label: "Sentinel", description: "Real-time alerts (0.002 FET/call)" },
|
|
49
|
+
{ name: "launcher", label: "Launcher", description: "Autonomous agent creation (0.02 FET/call)" },
|
|
50
|
+
{ name: "scout", label: "Scout", description: "Agent & opportunity discovery (0.01 FET/call)" },
|
|
51
|
+
];
|
|
52
|
+
const TEMPLATE_LIST = listTemplates().map((t) => ({
|
|
53
|
+
id: t.name,
|
|
54
|
+
label: t.name,
|
|
55
|
+
description: t.description,
|
|
56
|
+
}));
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Helpers
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
function prompt(rl, question) {
|
|
61
|
+
return new Promise((resolve) => rl.question(question, resolve));
|
|
62
|
+
}
|
|
63
|
+
function sanitizeDirName(s) {
|
|
64
|
+
return s.replace(/[^a-zA-Z0-9_-]/g, "-").toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
/** Auto-generate a ticker from the agent name (e.g. "Price Oracle" → "PRICE") */
|
|
67
|
+
function autoTicker(name) {
|
|
68
|
+
const words = name.trim().split(/\s+/);
|
|
69
|
+
if (words.length === 1) {
|
|
70
|
+
return words[0].slice(0, 6).toUpperCase();
|
|
71
|
+
}
|
|
72
|
+
// Use first word if it's short enough, otherwise initials
|
|
73
|
+
const first = words[0].toUpperCase();
|
|
74
|
+
if (first.length >= 3 && first.length <= 6)
|
|
75
|
+
return first;
|
|
76
|
+
return words.map((w) => w[0]).join("").slice(0, 6).toUpperCase();
|
|
77
|
+
}
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// Workflow system prompt for Claude Code sessions
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
function buildWorkflowSystemPrompt(opts) {
|
|
82
|
+
return `You are guiding a developer through building a valuable AI agent on Fetch.ai Agentverse. This is not a tutorial — you are a collaborator helping them build something worth using and worth holding tokens for.
|
|
83
|
+
|
|
84
|
+
## Context
|
|
85
|
+
- Agent name: ${opts.agentName}
|
|
86
|
+
- Agent address: ${opts.agentAddress || "(not yet deployed)"}
|
|
87
|
+
- Description: ${opts.description}
|
|
88
|
+
- Auto-generated ticker: $${opts.ticker} (can change during tokenization)
|
|
89
|
+
- Status: ${opts.isDeployed ? "Deployed on Agentverse" : "Code scaffolded, not yet deployed"}
|
|
90
|
+
|
|
91
|
+
## Workflow — Follow These Steps In Order
|
|
92
|
+
|
|
93
|
+
### Step 1: Understand the Vision
|
|
94
|
+
Ask the developer what problem this agent solves. Probe with:
|
|
95
|
+
- Who would pay for this? Why can't they just use an API directly?
|
|
96
|
+
- What data or intelligence does this agent accumulate over time?
|
|
97
|
+
- What gets better the more people use it?
|
|
98
|
+
|
|
99
|
+
Teach the 3 pillars of agent value:
|
|
100
|
+
1. **Proprietary intelligence** — it knows something others don't (curated data, trained models, analyzed patterns)
|
|
101
|
+
2. **Network effects** — it gets better with more users/agents (shared state, cross-agent data, collective learning)
|
|
102
|
+
3. **Defensibility** — it's hard to replicate (unique data sources, accumulated history, agent relationships)
|
|
103
|
+
|
|
104
|
+
If the description is vague ("a helpful assistant"), push back. Help them find a specific, valuable niche.
|
|
105
|
+
|
|
106
|
+
### Step 2: Build the Agent Logic
|
|
107
|
+
Read agent.py and propose real code changes based on the vision from Step 1. This is where you write REAL business logic — not placeholder TODOs.
|
|
108
|
+
|
|
109
|
+
Available packages on Agentverse (most people only use 3 — use more):
|
|
110
|
+
- **AI**: anthropic, openai, langchain-anthropic, langchain-core, langchain-community, langchain-google-genai, transformers, sentence-transformers
|
|
111
|
+
- **Data**: pandas, numpy, scipy, scikit-learn, faiss-cpu
|
|
112
|
+
- **Web**: requests, aiohttp, beautifulsoup4, feedparser
|
|
113
|
+
- **Blockchain**: web3, eth-account, cosmpy
|
|
114
|
+
- **Storage**: pymongo, redis, sqlalchemy
|
|
115
|
+
- **Utils**: pydantic, pillow, matplotlib, networkx
|
|
116
|
+
|
|
117
|
+
Key patterns to use:
|
|
118
|
+
- **Chat Protocol** (on_message): Request/response interactions with users and other agents
|
|
119
|
+
- **ctx.storage**: Persistent state across restarts — this is how agents accumulate intelligence
|
|
120
|
+
- **on_interval**: Background tasks that run 24/7 — monitoring, data collection, analysis
|
|
121
|
+
- **External APIs**: Connect to real data sources (price feeds, news, on-chain data)
|
|
122
|
+
- **Payment Protocol**: Charge for premium services (RequestPayment → CommitPayment → CompletePayment)
|
|
123
|
+
|
|
124
|
+
Write production code. Explain each pattern as you introduce it. Pause and ask if the developer wants to adjust before continuing.
|
|
125
|
+
|
|
126
|
+
### Step 3: Deploy
|
|
127
|
+
${opts.isDeployed ? "The agent is already deployed. If you made code changes, push updated code to Agentverse using the deploy_to_agentverse MCP tool or 'npx agentlaunch deploy'." : "Deploy the agent to Agentverse using the deploy_to_agentverse MCP tool or 'npx agentlaunch deploy'. Explain what happens: code uploads, agent compiles, goes live in ~30 seconds."}
|
|
128
|
+
|
|
129
|
+
After deploy, verify it's running (check logs). Mention the agent takes 15-60s to compile.
|
|
130
|
+
|
|
131
|
+
### Step 4: Make It Beautiful
|
|
132
|
+
A well-presented agent gets more discovery, more interactions, more token holders. Do all of these:
|
|
133
|
+
|
|
134
|
+
1. **README** — Write a compelling README.md with:
|
|
135
|
+
- One-line value proposition (what problem it solves)
|
|
136
|
+
- Capabilities list (what it can actually do)
|
|
137
|
+
- Example interactions (show 2-3 real conversations)
|
|
138
|
+
- Pricing table (if it charges for services)
|
|
139
|
+
- Technical architecture (brief)
|
|
140
|
+
|
|
141
|
+
2. **Short description** — Write a 1-2 sentence description for the Agentverse directory
|
|
142
|
+
|
|
143
|
+
3. **Push metadata** — Use 'npx agentlaunch optimize <agent-address>' or the update_agent_metadata MCP tool
|
|
144
|
+
|
|
145
|
+
4. **Optimization checklist** (7 factors for Agentverse ranking):
|
|
146
|
+
- [ ] README is detailed and well-formatted
|
|
147
|
+
- [ ] Short description is clear and specific
|
|
148
|
+
- [ ] Chat Protocol is implemented (for DeltaV discovery)
|
|
149
|
+
- [ ] Agent has a custom avatar
|
|
150
|
+
- [ ] Agent has real interactions (test it!)
|
|
151
|
+
- [ ] Agent has a memorable handle
|
|
152
|
+
- [ ] publish_manifest=True is set
|
|
153
|
+
|
|
154
|
+
### Step 5: Tokenize
|
|
155
|
+
Help the developer tokenize their agent:
|
|
156
|
+
|
|
157
|
+
1. **Choose ticker** — Suggest a memorable ticker (2-6 chars). The auto-generated $${opts.ticker} is a starting point.
|
|
158
|
+
2. **Explain bonding curves** — The bonding curve is NOT a fundraiser. It's a continuous, real-time reputation system:
|
|
159
|
+
- Agent delivers value → people buy → price rises
|
|
160
|
+
- Agent stops delivering → people sell → price drops
|
|
161
|
+
- Every holder has skin in the game. The market doesn't lie.
|
|
162
|
+
3. **Create the token** — Use the create_token_record MCP tool or 'npx agentlaunch tokenize'
|
|
163
|
+
4. **Explain the handoff** — The link goes to a human who connects a wallet and pays the 120 FET deploy fee. Agents never hold private keys.
|
|
164
|
+
5. **Graduation** — At 30,000 FET liquidity, the token auto-lists on DEX. The 2% trading fee goes 100% to protocol treasury.
|
|
165
|
+
|
|
166
|
+
### Step 6: Share & Next Steps
|
|
167
|
+
Provide all links:
|
|
168
|
+
- Agentverse page: https://agentverse.ai/agents/details/<agent-address>
|
|
169
|
+
- Trade page: https://agent-launch.ai/token/<token-address> (after tokenization)
|
|
170
|
+
- Handoff link: (from tokenization step)
|
|
171
|
+
|
|
172
|
+
Suggest next steps:
|
|
173
|
+
- Test the agent by chatting with it on Agentverse
|
|
174
|
+
- Share the trade link to get early holders
|
|
175
|
+
- Build a complementary agent (data feeds, analysis, monitoring)
|
|
176
|
+
- Add cross-holdings (buy tokens of agents yours depends on)
|
|
177
|
+
|
|
178
|
+
## 6 Architecture Patterns for Inspiration
|
|
179
|
+
1. **Intelligence Agent** — Uses AI to analyze, summarize, or reason (Brain, Analyst)
|
|
180
|
+
2. **Sentinel Agent** — Monitors 24/7, alerts on conditions (price drops, anomalies)
|
|
181
|
+
3. **Research Agent** — Deep dives on demand (on-chain analysis, market research)
|
|
182
|
+
4. **Oracle Agent** — Serves live data feeds (prices, metrics, events)
|
|
183
|
+
5. **Coordinator Agent** — Routes queries to specialists, takes a routing fee
|
|
184
|
+
6. **Incubator Agent** — Finds gaps in the ecosystem, scaffolds new agents
|
|
185
|
+
|
|
186
|
+
## Style Guide
|
|
187
|
+
- Be educational but action-oriented. Explain WHY as you DO.
|
|
188
|
+
- Pause between major steps — don't dump everything at once.
|
|
189
|
+
- Never write placeholder code (# TODO: implement this). Write real logic or ask what to build.
|
|
190
|
+
- Use the MCP tools (deploy_to_agentverse, create_token_record, update_agent_metadata, etc.) when possible.
|
|
191
|
+
- Be encouraging but honest. If an idea won't create value, say so and suggest alternatives.
|
|
192
|
+
- Keep responses focused. Don't repeat information the developer already knows.`;
|
|
193
|
+
}
|
|
194
|
+
async function validateApiKey(apiKey) {
|
|
195
|
+
try {
|
|
196
|
+
const response = await fetch("https://agentverse.ai/v1/hosting/agents", {
|
|
197
|
+
method: "GET",
|
|
198
|
+
headers: {
|
|
199
|
+
Authorization: `Bearer ${apiKey}`,
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
if (response.ok) {
|
|
203
|
+
return { valid: true };
|
|
204
|
+
}
|
|
205
|
+
if (response.status === 401) {
|
|
206
|
+
return { valid: false, error: "Invalid API key" };
|
|
207
|
+
}
|
|
208
|
+
return { valid: false, error: `API returned ${response.status}` };
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
212
|
+
return { valid: false, error: msg };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Tokenize
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
async function tokenizeAgent(agentAddress, name, ticker, description, chainId, json) {
|
|
219
|
+
if (!json)
|
|
220
|
+
console.log("\nTokenizing agent on AgentLaunch...");
|
|
221
|
+
const client = getClient();
|
|
222
|
+
const result = await client.post("/agents/tokenize", {
|
|
223
|
+
agentAddress,
|
|
224
|
+
name,
|
|
225
|
+
symbol: ticker.toUpperCase(),
|
|
226
|
+
description,
|
|
227
|
+
chainId,
|
|
228
|
+
});
|
|
229
|
+
const tokenId = result.token_id ?? result.tokenId ?? result.data?.token_id;
|
|
230
|
+
const tokenAddress = result.token_address;
|
|
231
|
+
const frontendUrl = getFrontendUrl();
|
|
232
|
+
const handoffLink = result.handoff_link ??
|
|
233
|
+
result.data?.handoff_link ??
|
|
234
|
+
(tokenId !== undefined ? `${frontendUrl}/deploy/${tokenId}` : undefined);
|
|
235
|
+
return { tokenId, tokenAddress, handoffLink };
|
|
236
|
+
}
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
// Command registration
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
export function registerCreateCommand(program) {
|
|
241
|
+
program
|
|
242
|
+
.command("create")
|
|
243
|
+
.description("Flagship one-command flow: scaffold -> deploy -> tokenize an AI agent")
|
|
244
|
+
.option("--name <name>", "Agent name (prompted if omitted)")
|
|
245
|
+
.option("--ticker <ticker>", "Token ticker symbol e.g. MYAG (prompted if omitted)")
|
|
246
|
+
.option("--template <template>", "Agent template: chat-memory (default), swarm-starter, custom, research, trading-bot, data-analyzer, price-monitor, gifter")
|
|
247
|
+
.option("--description <desc>", "Token description (max 500 chars)")
|
|
248
|
+
.option("--chain <chainId>", "Chain ID: 97 (BSC testnet) or 56 (BSC mainnet) (default: 97)", "97")
|
|
249
|
+
.option("--deploy", "Deploy agent to Agentverse after scaffolding")
|
|
250
|
+
.option("--tokenize", "Create token record on AgentLaunch after deploy")
|
|
251
|
+
.option("--mode <mode>", "Build mode: quick (single agent), swarm (multi-agent), genesis (full 7-agent economy)")
|
|
252
|
+
.option("--preset <preset>", "Agent preset: oracle, brain, analyst, coordinator, sentinel, launcher, scout")
|
|
253
|
+
.option("--no-deploy", "Scaffold only, don't deploy to Agentverse")
|
|
254
|
+
.option("--json", "Output only JSON (machine-readable, disables prompts)")
|
|
255
|
+
.action(async (options) => {
|
|
256
|
+
const isJson = options.json === true;
|
|
257
|
+
// When --json is set we never prompt — fail fast with errors
|
|
258
|
+
let name = options.name?.trim() ?? "";
|
|
259
|
+
let ticker = options.ticker?.trim() ?? "";
|
|
260
|
+
// Resolve legacy aliases and raw template names
|
|
261
|
+
let templateRaw = options.template ?? "";
|
|
262
|
+
let template = LEGACY_ALIAS[templateRaw] ?? templateRaw;
|
|
263
|
+
let doDeploy = options.deploy === true;
|
|
264
|
+
let doTokenize = options.tokenize === true;
|
|
265
|
+
// Interactive prompts (only when not --json)
|
|
266
|
+
let description = options.description?.trim() ?? "";
|
|
267
|
+
let apiKey = process.env.AGENTVERSE_API_KEY ?? "";
|
|
268
|
+
if (!isJson) {
|
|
269
|
+
const rl = readline.createInterface({
|
|
270
|
+
input: process.stdin,
|
|
271
|
+
output: process.stdout,
|
|
272
|
+
});
|
|
273
|
+
if (!name) {
|
|
274
|
+
name = (await prompt(rl, "Agent name: ")).trim();
|
|
275
|
+
}
|
|
276
|
+
// Auto-generate ticker from name (user can refine during tokenization)
|
|
277
|
+
if (!ticker) {
|
|
278
|
+
ticker = autoTicker(name);
|
|
279
|
+
}
|
|
280
|
+
if (!description) {
|
|
281
|
+
description = (await prompt(rl, "Describe what your agent does: ")).trim();
|
|
282
|
+
}
|
|
283
|
+
// Always prompt for API key
|
|
284
|
+
console.log("\nGet your API key at: https://agentverse.ai/profile/api-keys\n");
|
|
285
|
+
const existingKey = apiKey ? ` [${apiKey.slice(0, 6)}...${apiKey.slice(-4)}]` : "";
|
|
286
|
+
const keyInput = (await prompt(rl, `Agentverse API key${existingKey}: `)).trim();
|
|
287
|
+
if (keyInput) {
|
|
288
|
+
apiKey = keyInput;
|
|
289
|
+
}
|
|
290
|
+
// Set API key in env for SDK calls
|
|
291
|
+
if (apiKey) {
|
|
292
|
+
process.env.AGENTVERSE_API_KEY = apiKey;
|
|
293
|
+
}
|
|
294
|
+
// Validate API key if we're going to deploy
|
|
295
|
+
// --no-deploy flag sets options.deploy to false
|
|
296
|
+
const skipDeploy = options.deploy === false;
|
|
297
|
+
if (!skipDeploy && apiKey) {
|
|
298
|
+
console.log("\n Validating API key...");
|
|
299
|
+
const validation = await validateApiKey(apiKey);
|
|
300
|
+
if (!validation.valid) {
|
|
301
|
+
console.error(` Error: ${validation.error ?? "Invalid API key"}`);
|
|
302
|
+
console.log(" Get your API key at: https://agentverse.ai/profile/api-keys");
|
|
303
|
+
rl.close();
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
console.log(" Valid.\n");
|
|
307
|
+
}
|
|
308
|
+
// Default to single agent mode; swarm reachable via --mode=swarm
|
|
309
|
+
const buildMode = options.mode ?? "single";
|
|
310
|
+
let selectedPresets;
|
|
311
|
+
if (buildMode === "swarm") {
|
|
312
|
+
// Show preset selection for swarm mode only
|
|
313
|
+
console.log("\n What kind of agents?\n");
|
|
314
|
+
console.log(" Recommended for earning fees:");
|
|
315
|
+
console.log(" 1) Oracle Sell market data (price feeds, OHLC) — 0.001 FET/call");
|
|
316
|
+
console.log(" 2) Brain Sell AI reasoning (analysis, summaries) — 0.01 FET/call");
|
|
317
|
+
console.log(" 3) Analyst Sell token scoring (quality, risk) — 0.005 FET/call");
|
|
318
|
+
console.log("");
|
|
319
|
+
console.log(" Infrastructure (other agents pay you):");
|
|
320
|
+
console.log(" 4) Coordinator Route queries to specialists — 0.0005 FET/call");
|
|
321
|
+
console.log(" 5) Sentinel Real-time monitoring & alerts — 0.002 FET/call");
|
|
322
|
+
console.log("");
|
|
323
|
+
console.log(" Advanced (autonomous growth):");
|
|
324
|
+
console.log(" 6) Launcher Find gaps, scaffold new agents — 0.02 FET/call");
|
|
325
|
+
console.log(" 7) Scout Discover & evaluate agents — 0.01 FET/call");
|
|
326
|
+
const pickInput = (await prompt(rl, "\n Pick agents (comma-separated, e.g. 1,2,4): ")).trim();
|
|
327
|
+
const picks = pickInput
|
|
328
|
+
.split(",")
|
|
329
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
330
|
+
.filter((n) => n >= 1 && n <= GENESIS_PRESETS.length);
|
|
331
|
+
if (picks.length === 0) {
|
|
332
|
+
// Default to oracle + brain if no valid input
|
|
333
|
+
selectedPresets = ["oracle", "brain"];
|
|
334
|
+
console.log(" Defaulting to Oracle + Brain.");
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
selectedPresets = picks.map((n) => GENESIS_PRESETS[n - 1].name);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
// Single agent mode - use "chat-memory" (the base template with LLM + memory)
|
|
342
|
+
selectedPresets = ["chat-memory"];
|
|
343
|
+
}
|
|
344
|
+
const isSingleAgent = buildMode === "single";
|
|
345
|
+
const baseName = name || (isSingleAgent ? selectedPresets[0] : "Swarm");
|
|
346
|
+
if (skipDeploy) {
|
|
347
|
+
console.log(`\n Scaffolding ${isSingleAgent ? baseName : selectedPresets.length + " agents"}...\n`);
|
|
348
|
+
}
|
|
349
|
+
else if (isSingleAgent) {
|
|
350
|
+
console.log(`\n Deploying ${baseName}...\n`);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
console.log(`\n Deploying ${selectedPresets.length} agents as "${baseName}"...\n`);
|
|
354
|
+
}
|
|
355
|
+
rl.close();
|
|
356
|
+
// Generate/deploy each agent in sequence
|
|
357
|
+
const deployResults = [];
|
|
358
|
+
const peerAddresses = {};
|
|
359
|
+
for (const presetName of selectedPresets) {
|
|
360
|
+
// For single agent, use baseName directly; for swarm, append preset
|
|
361
|
+
const agentName = isSingleAgent
|
|
362
|
+
? baseName
|
|
363
|
+
: `${baseName}-${presetName.charAt(0).toUpperCase() + presetName.slice(1)}`;
|
|
364
|
+
const presetInfo = GENESIS_PRESETS.find((p) => p.name === presetName);
|
|
365
|
+
const action = skipDeploy ? "Scaffolding" : "Deploying";
|
|
366
|
+
console.log(` [${deployResults.length + 1}/${selectedPresets.length}] ${action} ${agentName}...`);
|
|
367
|
+
try {
|
|
368
|
+
// Generate agent code — use chat-memory for single agents, swarm-starter for swarm presets
|
|
369
|
+
let agentCode;
|
|
370
|
+
let templateUsed;
|
|
371
|
+
if (isSingleAgent || presetName === "chat-memory") {
|
|
372
|
+
const generated = generateFromTemplate("chat-memory", {
|
|
373
|
+
agent_name: agentName,
|
|
374
|
+
description: description || presetInfo?.description || "",
|
|
375
|
+
});
|
|
376
|
+
agentCode = generated.code;
|
|
377
|
+
templateUsed = "chat-memory";
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
try {
|
|
381
|
+
const generated = generateFromTemplate("swarm-starter", {
|
|
382
|
+
agent_name: agentName,
|
|
383
|
+
description: presetInfo?.description ?? "",
|
|
384
|
+
preset: presetName,
|
|
385
|
+
});
|
|
386
|
+
agentCode = generated.code;
|
|
387
|
+
templateUsed = "swarm-starter";
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
const generated = generateFromTemplate("chat-memory", {
|
|
391
|
+
agent_name: agentName,
|
|
392
|
+
description: description || presetInfo?.description || "",
|
|
393
|
+
});
|
|
394
|
+
agentCode = generated.code;
|
|
395
|
+
templateUsed = "chat-memory";
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (skipDeploy) {
|
|
399
|
+
// Scaffold only - no deployment
|
|
400
|
+
deployResults.push({
|
|
401
|
+
name: agentName,
|
|
402
|
+
preset: presetName,
|
|
403
|
+
address: "",
|
|
404
|
+
status: "scaffolded",
|
|
405
|
+
code: agentCode,
|
|
406
|
+
});
|
|
407
|
+
console.log(" Scaffolded (not deployed)");
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
// Deploy to Agentverse
|
|
411
|
+
const deploySecrets = {
|
|
412
|
+
...peerAddresses,
|
|
413
|
+
AGENTVERSE_API_KEY: apiKey,
|
|
414
|
+
AGENTLAUNCH_API_KEY: apiKey,
|
|
415
|
+
};
|
|
416
|
+
// For chat-memory agents, set the default ASI1 LLM key
|
|
417
|
+
if (templateUsed === "chat-memory") {
|
|
418
|
+
deploySecrets.LLM_API_KEY = "sk_2a3c92a0b11e4f18b50708cca1a55179ab38a7c2fb7f4eee95fd68e1e28f860b";
|
|
419
|
+
}
|
|
420
|
+
const result = await deployAgent({
|
|
421
|
+
apiKey,
|
|
422
|
+
agentName,
|
|
423
|
+
sourceCode: agentCode,
|
|
424
|
+
secrets: deploySecrets,
|
|
425
|
+
});
|
|
426
|
+
peerAddresses[`${presetName.toUpperCase()}_ADDRESS`] = result.agentAddress;
|
|
427
|
+
deployResults.push({
|
|
428
|
+
name: agentName,
|
|
429
|
+
preset: presetName,
|
|
430
|
+
address: result.agentAddress,
|
|
431
|
+
status: result.status,
|
|
432
|
+
code: agentCode,
|
|
433
|
+
});
|
|
434
|
+
console.log(` Address: ${result.agentAddress}`);
|
|
435
|
+
console.log(` Status: ${result.status}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
catch (err) {
|
|
439
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
440
|
+
deployResults.push({
|
|
441
|
+
name: agentName,
|
|
442
|
+
preset: presetName,
|
|
443
|
+
address: "",
|
|
444
|
+
status: "failed",
|
|
445
|
+
error: errMsg,
|
|
446
|
+
});
|
|
447
|
+
console.error(` FAILED: ${errMsg}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// Summary
|
|
451
|
+
const successful = deployResults.filter((r) => r.status !== "failed");
|
|
452
|
+
const failed = deployResults.filter((r) => r.status === "failed");
|
|
453
|
+
// Create project directory
|
|
454
|
+
const dirName = sanitizeDirName(baseName);
|
|
455
|
+
const targetDir = path.resolve(process.cwd(), dirName);
|
|
456
|
+
if (!fs.existsSync(targetDir)) {
|
|
457
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
458
|
+
}
|
|
459
|
+
if (!isSingleAgent) {
|
|
460
|
+
fs.mkdirSync(path.join(targetDir, "agents"), { recursive: true });
|
|
461
|
+
}
|
|
462
|
+
fs.mkdirSync(path.join(targetDir, ".claude"), { recursive: true });
|
|
463
|
+
fs.mkdirSync(path.join(targetDir, ".claude", "rules"), { recursive: true });
|
|
464
|
+
fs.mkdirSync(path.join(targetDir, ".claude", "skills"), { recursive: true });
|
|
465
|
+
fs.mkdirSync(path.join(targetDir, ".cursor"), { recursive: true });
|
|
466
|
+
fs.mkdirSync(path.join(targetDir, "docs"), { recursive: true });
|
|
467
|
+
fs.mkdirSync(path.join(targetDir, "examples"), { recursive: true });
|
|
468
|
+
// Write agent code files
|
|
469
|
+
for (const agent of successful) {
|
|
470
|
+
if (agent.code) {
|
|
471
|
+
// Single agent: agent.py at root; Swarm: agents/<preset>.py
|
|
472
|
+
const codePath = isSingleAgent
|
|
473
|
+
? path.join(targetDir, "agent.py")
|
|
474
|
+
: path.join(targetDir, "agents", `${agent.preset}.py`);
|
|
475
|
+
fs.writeFileSync(codePath, agent.code, "utf8");
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
// Build context for CLAUDE.md (works for both single and swarm)
|
|
479
|
+
const swarmContext = {
|
|
480
|
+
swarmName: baseName,
|
|
481
|
+
agents: successful.map((a) => ({
|
|
482
|
+
name: a.name,
|
|
483
|
+
preset: a.preset,
|
|
484
|
+
address: a.address,
|
|
485
|
+
status: a.status,
|
|
486
|
+
})),
|
|
487
|
+
peerAddresses,
|
|
488
|
+
deployedAt: new Date().toISOString(),
|
|
489
|
+
};
|
|
490
|
+
// Write context files
|
|
491
|
+
fs.writeFileSync(path.join(targetDir, "CLAUDE.md"), buildSwarmClaudeMd(swarmContext), "utf8");
|
|
492
|
+
fs.writeFileSync(path.join(targetDir, "agentlaunch.config.json"), buildSwarmConfig(swarmContext), "utf8");
|
|
493
|
+
fs.writeFileSync(path.join(targetDir, "package.json"), buildSwarmPackageJson(baseName), "utf8");
|
|
494
|
+
// Write .env with API key
|
|
495
|
+
let envContent = `# AgentLaunch Environment Variables
|
|
496
|
+
AGENTVERSE_API_KEY=${apiKey}
|
|
497
|
+
AGENT_LAUNCH_API_URL=https://agent-launch.ai/api
|
|
498
|
+
`;
|
|
499
|
+
if (!isSingleAgent && Object.keys(peerAddresses).length > 0) {
|
|
500
|
+
envContent += `
|
|
501
|
+
# Peer addresses (for agent-to-agent communication)
|
|
502
|
+
${Object.entries(peerAddresses).map(([k, v]) => `${k}=${v}`).join("\n")}
|
|
503
|
+
`;
|
|
504
|
+
}
|
|
505
|
+
if (successful.length > 0) {
|
|
506
|
+
envContent += `
|
|
507
|
+
# Your agent address
|
|
508
|
+
AGENT_ADDRESS=${successful[0].address}
|
|
509
|
+
`;
|
|
510
|
+
}
|
|
511
|
+
fs.writeFileSync(path.join(targetDir, ".env"), envContent, "utf8");
|
|
512
|
+
// Write Claude rules
|
|
513
|
+
for (const [filename, content] of Object.entries(RULES)) {
|
|
514
|
+
fs.writeFileSync(path.join(targetDir, ".claude", "rules", filename), content, "utf8");
|
|
515
|
+
}
|
|
516
|
+
// Write Claude skills (generic)
|
|
517
|
+
for (const [filepath, content] of Object.entries(SKILLS)) {
|
|
518
|
+
const skillDir = path.dirname(filepath);
|
|
519
|
+
fs.mkdirSync(path.join(targetDir, ".claude", "skills", skillDir), { recursive: true });
|
|
520
|
+
fs.writeFileSync(path.join(targetDir, ".claude", "skills", filepath), content, "utf8");
|
|
521
|
+
}
|
|
522
|
+
// Write project-specific skills (with real agent addresses)
|
|
523
|
+
const agentSkillContexts = successful.map((a) => ({
|
|
524
|
+
name: a.name,
|
|
525
|
+
preset: a.preset,
|
|
526
|
+
address: a.address,
|
|
527
|
+
symbol: a.preset.slice(0, 4).toUpperCase(),
|
|
528
|
+
}));
|
|
529
|
+
const projectSkills = buildProjectSkills(agentSkillContexts, isSingleAgent);
|
|
530
|
+
for (const [filepath, content] of Object.entries(projectSkills)) {
|
|
531
|
+
const skillDir = path.dirname(filepath);
|
|
532
|
+
fs.mkdirSync(path.join(targetDir, ".claude", "skills", skillDir), { recursive: true });
|
|
533
|
+
fs.writeFileSync(path.join(targetDir, ".claude", "skills", filepath), content, "utf8");
|
|
534
|
+
}
|
|
535
|
+
// Write .claude/settings.json
|
|
536
|
+
const claudeSettings = JSON.stringify({
|
|
537
|
+
mcpServers: {
|
|
538
|
+
"agent-launch": {
|
|
539
|
+
command: "npx",
|
|
540
|
+
args: ["-y", "agent-launch-mcp"],
|
|
541
|
+
env: { AGENTVERSE_API_KEY: "${AGENTVERSE_API_KEY}" },
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
}, null, 2);
|
|
545
|
+
fs.writeFileSync(path.join(targetDir, ".claude", "settings.json"), claudeSettings, "utf8");
|
|
546
|
+
// Write MCP config
|
|
547
|
+
fs.writeFileSync(path.join(targetDir, ".mcp.json"), claudeSettings, "utf8");
|
|
548
|
+
// Write Cursor config
|
|
549
|
+
fs.writeFileSync(path.join(targetDir, ".cursor", "mcp.json"), CURSOR_MCP_CONFIG, "utf8");
|
|
550
|
+
fs.writeFileSync(path.join(targetDir, ".cursorrules"), CURSOR_RULES, "utf8");
|
|
551
|
+
// Write docs
|
|
552
|
+
for (const [filename, content] of Object.entries(DOCS)) {
|
|
553
|
+
fs.writeFileSync(path.join(targetDir, "docs", filename), content, "utf8");
|
|
554
|
+
}
|
|
555
|
+
// Write examples
|
|
556
|
+
for (const [filename, content] of Object.entries(EXAMPLES)) {
|
|
557
|
+
fs.writeFileSync(path.join(targetDir, "examples", filename), content, "utf8");
|
|
558
|
+
}
|
|
559
|
+
if (isJson) {
|
|
560
|
+
console.log(JSON.stringify({
|
|
561
|
+
mode: buildMode,
|
|
562
|
+
baseName,
|
|
563
|
+
directory: targetDir,
|
|
564
|
+
totalDeployed: successful.length,
|
|
565
|
+
totalFailed: failed.length,
|
|
566
|
+
agents: deployResults.map((a) => ({ name: a.name, preset: a.preset, address: a.address, status: a.status })),
|
|
567
|
+
peerAddresses,
|
|
568
|
+
}));
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const firstAgent = successful[0];
|
|
572
|
+
const agentAddr = firstAgent?.address ?? "";
|
|
573
|
+
const isDeployed = !skipDeploy && !!agentAddr;
|
|
574
|
+
if (failed.length > 0) {
|
|
575
|
+
console.log(`\n Failed (${failed.length}):`);
|
|
576
|
+
for (const agent of failed) {
|
|
577
|
+
console.log(` ${agent.preset}: ${agent.error}`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
// Install dependencies
|
|
581
|
+
console.log(`\n Installing dependencies...`);
|
|
582
|
+
try {
|
|
583
|
+
execSync("npm install --silent", { cwd: targetDir, stdio: "ignore" });
|
|
584
|
+
console.log(` Done.`);
|
|
585
|
+
}
|
|
586
|
+
catch {
|
|
587
|
+
console.log(` Warning: npm install failed. Run 'npm install' manually.`);
|
|
588
|
+
}
|
|
589
|
+
// ---------------------------------------------------------------
|
|
590
|
+
// Post-deployment summary
|
|
591
|
+
// ---------------------------------------------------------------
|
|
592
|
+
if (isSingleAgent && isDeployed) {
|
|
593
|
+
console.log(`\n ${"=".repeat(56)}`);
|
|
594
|
+
console.log(` Agent deployed!`);
|
|
595
|
+
console.log(` ${"=".repeat(56)}`);
|
|
596
|
+
console.log(` Name: ${baseName}`);
|
|
597
|
+
console.log(` Address: ${agentAddr}`);
|
|
598
|
+
console.log(` Directory: ${targetDir}`);
|
|
599
|
+
console.log(`\n Features installed:`);
|
|
600
|
+
console.log(` Conversation memory Remembers last 10 exchanges per user`);
|
|
601
|
+
console.log(` LLM (ASI1-mini) AI responses via api.asi1.ai`);
|
|
602
|
+
console.log(` Chat Protocol v0.3.0 Discoverable on Agentverse + DeltaV`);
|
|
603
|
+
console.log(` Agent wallet Fetch.ai wallet (auto-created)`);
|
|
604
|
+
console.log(` Session persistence State saved via ctx.storage`);
|
|
605
|
+
console.log(` Commands help, clear, status built in`);
|
|
606
|
+
console.log(`\n Links:`);
|
|
607
|
+
console.log(` Chat: https://agentverse.ai/agents/details/${agentAddr}`);
|
|
608
|
+
console.log(` Docs: https://docs.agentverse.ai`);
|
|
609
|
+
console.log(`\n Created files:`);
|
|
610
|
+
console.log(` agent.py Your agent code (edit this!)`);
|
|
611
|
+
console.log(` CLAUDE.md Context for Claude Code`);
|
|
612
|
+
console.log(` .claude/ Rules, skills, MCP config`);
|
|
613
|
+
console.log(` .cursor/ Cursor IDE config`);
|
|
614
|
+
}
|
|
615
|
+
else if (isSingleAgent && skipDeploy) {
|
|
616
|
+
console.log(`\n Agent scaffolded!`);
|
|
617
|
+
console.log(` Name: ${baseName}`);
|
|
618
|
+
console.log(` Directory: ${targetDir}`);
|
|
619
|
+
console.log(`\n To deploy: npx agentlaunch deploy --code agent.py`);
|
|
620
|
+
}
|
|
621
|
+
else if (!isSingleAgent) {
|
|
622
|
+
console.log(`\n Swarm deployed!`);
|
|
623
|
+
console.log(` Deployed: ${successful.length}/${deployResults.length} agents`);
|
|
624
|
+
if (successful.length > 0) {
|
|
625
|
+
console.log("\n Agent Addresses:");
|
|
626
|
+
for (const agent of successful) {
|
|
627
|
+
console.log(` ${agent.preset.padEnd(14)} ${agent.address}`);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
console.log(` Directory: ${targetDir}`);
|
|
631
|
+
}
|
|
632
|
+
// ---------------------------------------------------------------
|
|
633
|
+
// Editor selection
|
|
634
|
+
// ---------------------------------------------------------------
|
|
635
|
+
const rl2 = readline.createInterface({
|
|
636
|
+
input: process.stdin,
|
|
637
|
+
output: process.stdout,
|
|
638
|
+
});
|
|
639
|
+
console.log(`\n Open project in:\n`);
|
|
640
|
+
console.log(` 1) Claude Code (Recommended)`);
|
|
641
|
+
console.log(` 2) Cursor`);
|
|
642
|
+
console.log(` 3) Windsurf`);
|
|
643
|
+
console.log(` 4) Skip\n`);
|
|
644
|
+
const editorChoice = (await prompt(rl2, " Choice [1]: ")).trim() || "1";
|
|
645
|
+
rl2.close();
|
|
646
|
+
if (editorChoice === "4") {
|
|
647
|
+
console.log(`\n To open later:`);
|
|
648
|
+
console.log(` cd ${dirName} && claude\n`);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
if (editorChoice === "2") {
|
|
652
|
+
console.log(`\n Opening in Cursor...`);
|
|
653
|
+
const cursor = spawn("cursor", [targetDir], {
|
|
654
|
+
stdio: "inherit",
|
|
655
|
+
detached: true,
|
|
656
|
+
});
|
|
657
|
+
cursor.unref();
|
|
658
|
+
cursor.on("error", () => {
|
|
659
|
+
console.log(` Could not launch Cursor. Open manually:`);
|
|
660
|
+
console.log(` cursor ${targetDir}`);
|
|
661
|
+
});
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
if (editorChoice === "3") {
|
|
665
|
+
console.log(`\n Opening in Windsurf...`);
|
|
666
|
+
const windsurf = spawn("windsurf", [targetDir], {
|
|
667
|
+
stdio: "inherit",
|
|
668
|
+
detached: true,
|
|
669
|
+
});
|
|
670
|
+
windsurf.unref();
|
|
671
|
+
windsurf.on("error", () => {
|
|
672
|
+
console.log(` Could not launch Windsurf. Open manually:`);
|
|
673
|
+
console.log(` windsurf ${targetDir}`);
|
|
674
|
+
});
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
// Default: Claude Code
|
|
678
|
+
console.log(`\n Launching Claude Code...`);
|
|
679
|
+
let welcomePrompt;
|
|
680
|
+
if (isSingleAgent) {
|
|
681
|
+
welcomePrompt = isDeployed
|
|
682
|
+
? `I just created an agent called "${baseName}" and deployed it to Agentverse at ${agentAddr}.\n\nDescription: ${description}\n\nPlease start from Step 1 of the workflow.`
|
|
683
|
+
: `I just scaffolded an agent called "${baseName}". The code is in agent.py but it's NOT deployed yet.\n\nDescription: ${description}\n\nPlease start from Step 1 of the workflow.`;
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
const agentList = successful.map((a) => `${a.preset}: ${a.address}`).join(", ");
|
|
687
|
+
welcomePrompt = `I just deployed a ${successful.length}-agent swarm called "${baseName}" with: ${agentList}.\n\nDescription: ${description}\n\nPlease start from Step 1 of the workflow.`;
|
|
688
|
+
}
|
|
689
|
+
const systemPrompt = buildWorkflowSystemPrompt({
|
|
690
|
+
agentName: baseName,
|
|
691
|
+
agentAddress: agentAddr,
|
|
692
|
+
description,
|
|
693
|
+
ticker,
|
|
694
|
+
isDeployed,
|
|
695
|
+
});
|
|
696
|
+
const claudeArgs = [
|
|
697
|
+
welcomePrompt,
|
|
698
|
+
"--append-system-prompt", systemPrompt,
|
|
699
|
+
"--allowedTools", "Bash(npm run *),Bash(npx agentlaunch *),Bash(agentlaunch *),Edit,Read,Write,Glob,Grep",
|
|
700
|
+
];
|
|
701
|
+
const claude = spawn("claude", claudeArgs, {
|
|
702
|
+
cwd: targetDir,
|
|
703
|
+
stdio: "inherit",
|
|
704
|
+
});
|
|
705
|
+
claude.on("error", (err) => {
|
|
706
|
+
console.error(` Could not launch Claude Code: ${err.message}`);
|
|
707
|
+
console.log(`\n Run manually:`);
|
|
708
|
+
console.log(` cd ${dirName} && claude`);
|
|
709
|
+
});
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
// Validate inputs
|
|
713
|
+
const errors = [];
|
|
714
|
+
if (!name)
|
|
715
|
+
errors.push("--name is required");
|
|
716
|
+
else if (name.length > 32)
|
|
717
|
+
errors.push("--name must be 32 characters or fewer");
|
|
718
|
+
if (!ticker)
|
|
719
|
+
errors.push("--ticker is required");
|
|
720
|
+
else {
|
|
721
|
+
const t = ticker.toUpperCase();
|
|
722
|
+
if (t.length < 2 || t.length > 11) {
|
|
723
|
+
errors.push("--ticker must be 2-11 characters");
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
if (!template || !TEMPLATES[template]) {
|
|
727
|
+
template = "chat-memory";
|
|
728
|
+
}
|
|
729
|
+
const chainId = parseInt(options.chain, 10);
|
|
730
|
+
if (![56, 97].includes(chainId)) {
|
|
731
|
+
errors.push("--chain must be 97 (BSC testnet) or 56 (BSC mainnet)");
|
|
732
|
+
}
|
|
733
|
+
if (errors.length > 0) {
|
|
734
|
+
if (isJson) {
|
|
735
|
+
console.log(JSON.stringify({ error: errors.join("; ") }));
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
errors.forEach((e) => console.error(`Error: ${e}`));
|
|
739
|
+
}
|
|
740
|
+
process.exit(1);
|
|
741
|
+
}
|
|
742
|
+
const result = {
|
|
743
|
+
name,
|
|
744
|
+
ticker: ticker.toUpperCase(),
|
|
745
|
+
template,
|
|
746
|
+
};
|
|
747
|
+
// Step 1: Scaffold
|
|
748
|
+
const dirName = sanitizeDirName(name);
|
|
749
|
+
const targetDir = path.resolve(process.cwd(), dirName);
|
|
750
|
+
if (!isJson) {
|
|
751
|
+
console.log(`\nScaffolding agent: ${name} (${TEMPLATES[template]?.label ?? template})`);
|
|
752
|
+
console.log(`Directory: ${targetDir}`);
|
|
753
|
+
}
|
|
754
|
+
if (fs.existsSync(targetDir)) {
|
|
755
|
+
const msg = `Directory "${dirName}" already exists.`;
|
|
756
|
+
if (isJson) {
|
|
757
|
+
console.log(JSON.stringify({ error: msg }));
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
console.error(`Error: ${msg}`);
|
|
761
|
+
}
|
|
762
|
+
process.exit(1);
|
|
763
|
+
}
|
|
764
|
+
// Truncate description if too long
|
|
765
|
+
const presetInfo = options.preset ? GENESIS_PRESETS.find((p) => p.name === options.preset) : undefined;
|
|
766
|
+
const finalDescription = (description || presetInfo?.description || TEMPLATES[template]?.description || "").slice(0, 500);
|
|
767
|
+
// Generate files from templates package
|
|
768
|
+
// If a preset is specified, use swarm-starter template with the preset
|
|
769
|
+
const templateToUse = options.preset ? "swarm-starter" : template;
|
|
770
|
+
const templateVars = {
|
|
771
|
+
agent_name: name,
|
|
772
|
+
description: finalDescription,
|
|
773
|
+
};
|
|
774
|
+
if (options.preset) {
|
|
775
|
+
templateVars.preset = options.preset;
|
|
776
|
+
}
|
|
777
|
+
const generated = generateFromTemplate(templateToUse, templateVars);
|
|
778
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
779
|
+
fs.mkdirSync(path.join(targetDir, ".claude"), { recursive: true });
|
|
780
|
+
fs.mkdirSync(path.join(targetDir, ".claude", "rules"), { recursive: true });
|
|
781
|
+
fs.mkdirSync(path.join(targetDir, ".claude", "skills"), { recursive: true });
|
|
782
|
+
fs.mkdirSync(path.join(targetDir, ".cursor"), { recursive: true });
|
|
783
|
+
// Core files
|
|
784
|
+
fs.writeFileSync(path.join(targetDir, "agent.py"), generated.code, "utf8");
|
|
785
|
+
fs.writeFileSync(path.join(targetDir, "README.md"), generated.readme, "utf8");
|
|
786
|
+
fs.writeFileSync(path.join(targetDir, ".env.example"), generated.envExample, "utf8");
|
|
787
|
+
// Write .env with API key if provided
|
|
788
|
+
if (apiKey) {
|
|
789
|
+
const envContent = `# AgentLaunch Environment Variables
|
|
790
|
+
AGENTVERSE_API_KEY=${apiKey}
|
|
791
|
+
AGENT_LAUNCH_API_URL=https://agent-launch.ai/api
|
|
792
|
+
`;
|
|
793
|
+
fs.writeFileSync(path.join(targetDir, ".env"), envContent, "utf8");
|
|
794
|
+
}
|
|
795
|
+
fs.writeFileSync(path.join(targetDir, "CLAUDE.md"), generated.claudeMd, "utf8");
|
|
796
|
+
fs.writeFileSync(path.join(targetDir, "package.json"), buildPackageJson(name), "utf8");
|
|
797
|
+
fs.writeFileSync(path.join(targetDir, ".claude", "settings.json"), generated.claudeSettings, "utf8");
|
|
798
|
+
fs.writeFileSync(path.join(targetDir, "agentlaunch.config.json"), generated.agentlaunchConfig, "utf8");
|
|
799
|
+
// Claude rules (for AI context)
|
|
800
|
+
for (const [filename, content] of Object.entries(RULES)) {
|
|
801
|
+
fs.writeFileSync(path.join(targetDir, ".claude", "rules", filename), content, "utf8");
|
|
802
|
+
}
|
|
803
|
+
// Claude skills (slash commands for AI)
|
|
804
|
+
for (const [filepath, content] of Object.entries(SKILLS)) {
|
|
805
|
+
const skillDir = path.dirname(filepath);
|
|
806
|
+
fs.mkdirSync(path.join(targetDir, ".claude", "skills", skillDir), { recursive: true });
|
|
807
|
+
fs.writeFileSync(path.join(targetDir, ".claude", "skills", filepath), content, "utf8");
|
|
808
|
+
}
|
|
809
|
+
// MCP config (project root - recommended location)
|
|
810
|
+
fs.writeFileSync(path.join(targetDir, ".mcp.json"), generated.claudeSettings, "utf8");
|
|
811
|
+
// Cursor IDE config
|
|
812
|
+
fs.writeFileSync(path.join(targetDir, ".cursor", "mcp.json"), CURSOR_MCP_CONFIG, "utf8");
|
|
813
|
+
fs.writeFileSync(path.join(targetDir, ".cursorrules"), CURSOR_RULES, "utf8");
|
|
814
|
+
// Documentation
|
|
815
|
+
fs.mkdirSync(path.join(targetDir, "docs"), { recursive: true });
|
|
816
|
+
for (const [filename, content] of Object.entries(DOCS)) {
|
|
817
|
+
fs.writeFileSync(path.join(targetDir, "docs", filename), content, "utf8");
|
|
818
|
+
}
|
|
819
|
+
// Examples
|
|
820
|
+
fs.mkdirSync(path.join(targetDir, "examples"), { recursive: true });
|
|
821
|
+
for (const [filename, content] of Object.entries(EXAMPLES)) {
|
|
822
|
+
fs.writeFileSync(path.join(targetDir, "examples", filename), content, "utf8");
|
|
823
|
+
}
|
|
824
|
+
result.scaffoldDir = targetDir;
|
|
825
|
+
if (!isJson) {
|
|
826
|
+
console.log(` Created: agent.py`);
|
|
827
|
+
console.log(` Created: README.md`);
|
|
828
|
+
console.log(` Created: CLAUDE.md`);
|
|
829
|
+
console.log(` Created: package.json`);
|
|
830
|
+
console.log(` Created: .claude/ (settings, rules, skills)`);
|
|
831
|
+
console.log(` Created: .cursor/ (MCP config)`);
|
|
832
|
+
console.log(` Created: docs/ (${Object.keys(DOCS).length} guides)`);
|
|
833
|
+
console.log(` Created: examples/ (${Object.keys(EXAMPLES).length} samples)`);
|
|
834
|
+
}
|
|
835
|
+
// Install dependencies (quiet mode)
|
|
836
|
+
if (!isJson) {
|
|
837
|
+
console.log(`\nInstalling dependencies...`);
|
|
838
|
+
}
|
|
839
|
+
try {
|
|
840
|
+
execSync("npm install --silent", { cwd: targetDir, stdio: "ignore" });
|
|
841
|
+
if (!isJson) {
|
|
842
|
+
console.log(` Done.`);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
catch {
|
|
846
|
+
if (!isJson) {
|
|
847
|
+
console.log(` Warning: npm install failed. Run 'npm install' manually.`);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
// Step 2: Deploy (optional)
|
|
851
|
+
if (doDeploy) {
|
|
852
|
+
let apiKey;
|
|
853
|
+
try {
|
|
854
|
+
apiKey = requireApiKey();
|
|
855
|
+
}
|
|
856
|
+
catch (err) {
|
|
857
|
+
if (isJson) {
|
|
858
|
+
console.log(JSON.stringify({ error: err.message }));
|
|
859
|
+
}
|
|
860
|
+
else {
|
|
861
|
+
console.error(err.message);
|
|
862
|
+
}
|
|
863
|
+
process.exit(1);
|
|
864
|
+
}
|
|
865
|
+
if (!isJson)
|
|
866
|
+
console.log("\n[1/5] Creating agent on Agentverse...");
|
|
867
|
+
if (!isJson)
|
|
868
|
+
console.log("[2/5] Uploading code...");
|
|
869
|
+
if (!isJson)
|
|
870
|
+
console.log("[3/5] Setting secrets...");
|
|
871
|
+
if (!isJson)
|
|
872
|
+
console.log("[4/5] Starting agent...");
|
|
873
|
+
if (!isJson)
|
|
874
|
+
console.log("[5/5] Waiting for compilation...");
|
|
875
|
+
try {
|
|
876
|
+
const agentCode = fs.readFileSync(path.join(targetDir, "agent.py"), "utf8");
|
|
877
|
+
const deployed = await deployAgent({
|
|
878
|
+
apiKey,
|
|
879
|
+
agentName: name,
|
|
880
|
+
sourceCode: agentCode,
|
|
881
|
+
metadata: {
|
|
882
|
+
readme: generated.readme,
|
|
883
|
+
short_description: generated.shortDescription,
|
|
884
|
+
},
|
|
885
|
+
});
|
|
886
|
+
result.agentAddress = deployed.agentAddress;
|
|
887
|
+
result.walletAddress = deployed.walletAddress;
|
|
888
|
+
if (!isJson) {
|
|
889
|
+
console.log(` Address: ${deployed.agentAddress}`);
|
|
890
|
+
if (deployed.walletAddress) {
|
|
891
|
+
console.log(` Wallet: ${deployed.walletAddress}`);
|
|
892
|
+
}
|
|
893
|
+
if (deployed.status === "compiled" || deployed.status === "running") {
|
|
894
|
+
console.log(" Compiled.");
|
|
895
|
+
}
|
|
896
|
+
if (deployed.secretErrors?.length) {
|
|
897
|
+
for (const se of deployed.secretErrors) {
|
|
898
|
+
console.log(` Warning: ${se}`);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
if (deployed.status === "starting") {
|
|
903
|
+
const msg = "Compilation timeout — agent created but did not compile within 60 seconds.";
|
|
904
|
+
if (isJson) {
|
|
905
|
+
console.log(JSON.stringify({
|
|
906
|
+
error: msg,
|
|
907
|
+
agentAddress: deployed.agentAddress,
|
|
908
|
+
partial: result,
|
|
909
|
+
}));
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
console.error(`\nWarning: ${msg}`);
|
|
913
|
+
console.error(`Agent address: ${deployed.agentAddress}`);
|
|
914
|
+
console.error("Check at: https://agentverse.ai/agents");
|
|
915
|
+
}
|
|
916
|
+
process.exit(1);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
catch (err) {
|
|
920
|
+
if (isJson) {
|
|
921
|
+
console.log(JSON.stringify({
|
|
922
|
+
error: `Deploy failed: ${err.message}`,
|
|
923
|
+
partial: result,
|
|
924
|
+
}));
|
|
925
|
+
}
|
|
926
|
+
else {
|
|
927
|
+
console.error(`\nDeploy failed: ${err.message}`);
|
|
928
|
+
}
|
|
929
|
+
process.exit(1);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
// Step 3: Tokenize (optional)
|
|
933
|
+
if (doTokenize) {
|
|
934
|
+
if (!result.agentAddress) {
|
|
935
|
+
if (isJson) {
|
|
936
|
+
console.log(JSON.stringify({
|
|
937
|
+
error: "Cannot tokenize without deploying first. Add --deploy flag.",
|
|
938
|
+
partial: result,
|
|
939
|
+
}));
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
console.error("\nError: Cannot tokenize without deploying first. Add --deploy flag.");
|
|
943
|
+
}
|
|
944
|
+
process.exit(1);
|
|
945
|
+
}
|
|
946
|
+
try {
|
|
947
|
+
const tokenized = await tokenizeAgent(result.agentAddress, name, result.ticker, finalDescription, chainId, isJson);
|
|
948
|
+
result.tokenId = tokenized.tokenId;
|
|
949
|
+
result.tokenAddress = tokenized.tokenAddress;
|
|
950
|
+
result.handoffLink = tokenized.handoffLink;
|
|
951
|
+
}
|
|
952
|
+
catch (err) {
|
|
953
|
+
if (isJson) {
|
|
954
|
+
console.log(JSON.stringify({
|
|
955
|
+
error: `Tokenize failed: ${err.message}`,
|
|
956
|
+
partial: result,
|
|
957
|
+
}));
|
|
958
|
+
}
|
|
959
|
+
else {
|
|
960
|
+
console.error(`\nTokenize failed: ${err.message}`);
|
|
961
|
+
}
|
|
962
|
+
process.exit(1);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
// Output
|
|
966
|
+
if (isJson) {
|
|
967
|
+
console.log(JSON.stringify(result));
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
// Show deployment info if deployed/tokenized
|
|
971
|
+
if (result.agentAddress) {
|
|
972
|
+
console.log(`\nAgent: ${result.agentAddress}`);
|
|
973
|
+
}
|
|
974
|
+
if (result.handoffLink) {
|
|
975
|
+
console.log(`Handoff: ${result.handoffLink}`);
|
|
976
|
+
}
|
|
977
|
+
// Launch Claude Code in the new directory with workflow prompt
|
|
978
|
+
console.log(`\nLaunching Claude Code in ${dirName}...`);
|
|
979
|
+
const batchIsDeployed = !!result.agentAddress;
|
|
980
|
+
const batchWelcome = batchIsDeployed
|
|
981
|
+
? `I just created an agent called "${name}" and deployed it to Agentverse at ${result.agentAddress}.\n\nDescription: ${description || "No description provided."}\n\nPlease start from Step 1 of the workflow.`
|
|
982
|
+
: `I just scaffolded an agent called "${name}". The code is in agent.py but it's NOT deployed yet.\n\nDescription: ${description || "No description provided."}\n\nPlease start from Step 1 of the workflow.`;
|
|
983
|
+
const batchSystemPrompt = buildWorkflowSystemPrompt({
|
|
984
|
+
agentName: name,
|
|
985
|
+
agentAddress: result.agentAddress ?? "",
|
|
986
|
+
description: description || "No description provided.",
|
|
987
|
+
ticker: result.ticker,
|
|
988
|
+
isDeployed: batchIsDeployed,
|
|
989
|
+
});
|
|
990
|
+
const claudeArgs = [
|
|
991
|
+
batchWelcome,
|
|
992
|
+
"--append-system-prompt", batchSystemPrompt,
|
|
993
|
+
"--allowedTools", "Bash(npm run *),Bash(npx agentlaunch *),Bash(agentlaunch *),Edit,Read,Write,Glob,Grep",
|
|
994
|
+
];
|
|
995
|
+
const claude = spawn("claude", claudeArgs, {
|
|
996
|
+
cwd: targetDir,
|
|
997
|
+
stdio: "inherit",
|
|
998
|
+
});
|
|
999
|
+
claude.on("error", (err) => {
|
|
1000
|
+
console.error(`Could not launch Claude Code: ${err.message}`);
|
|
1001
|
+
console.log(`\nRun manually:`);
|
|
1002
|
+
console.log(` cd ${dirName} && claude`);
|
|
1003
|
+
});
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Simplified create flow for the default CLI command.
|
|
1008
|
+
* Called by index.ts when running `npx agentlaunch [name]`.
|
|
1009
|
+
*/
|
|
1010
|
+
export async function runCreate(options) {
|
|
1011
|
+
const isJson = options.json === true;
|
|
1012
|
+
const skipDeploy = options.skipDeploy === true;
|
|
1013
|
+
let name = options.name?.trim() ?? "";
|
|
1014
|
+
let description = options.description?.trim() ?? "";
|
|
1015
|
+
let apiKey = process.env.AGENTVERSE_API_KEY ?? "";
|
|
1016
|
+
// Interactive prompts (only when not --json)
|
|
1017
|
+
if (!isJson) {
|
|
1018
|
+
const rl = readline.createInterface({
|
|
1019
|
+
input: process.stdin,
|
|
1020
|
+
output: process.stdout,
|
|
1021
|
+
});
|
|
1022
|
+
console.log("\n agentlaunch — Build AI agents on Fetch.ai\n");
|
|
1023
|
+
if (!name) {
|
|
1024
|
+
name = (await prompt(rl, " Agent name: ")).trim();
|
|
1025
|
+
}
|
|
1026
|
+
if (!name) {
|
|
1027
|
+
console.error(" Error: Agent name is required.");
|
|
1028
|
+
rl.close();
|
|
1029
|
+
process.exit(1);
|
|
1030
|
+
}
|
|
1031
|
+
if (!description) {
|
|
1032
|
+
description = (await prompt(rl, " What does it do? ")).trim();
|
|
1033
|
+
}
|
|
1034
|
+
// Prompt for API key
|
|
1035
|
+
if (!skipDeploy) {
|
|
1036
|
+
console.log("\n Get your API key at: https://agentverse.ai/profile/api-keys\n");
|
|
1037
|
+
const existingKey = apiKey ? ` [${apiKey.slice(0, 6)}...${apiKey.slice(-4)}]` : "";
|
|
1038
|
+
const keyInput = (await prompt(rl, ` Agentverse API key${existingKey}: `)).trim();
|
|
1039
|
+
if (keyInput) {
|
|
1040
|
+
apiKey = keyInput;
|
|
1041
|
+
}
|
|
1042
|
+
if (!apiKey) {
|
|
1043
|
+
console.log("\n No API key provided. Scaffolding locally only.\n");
|
|
1044
|
+
}
|
|
1045
|
+
else {
|
|
1046
|
+
process.env.AGENTVERSE_API_KEY = apiKey;
|
|
1047
|
+
console.log("\n Validating API key...");
|
|
1048
|
+
const validation = await validateApiKey(apiKey);
|
|
1049
|
+
if (!validation.valid) {
|
|
1050
|
+
console.error(` Error: ${validation.error ?? "Invalid API key"}`);
|
|
1051
|
+
console.log(" Get your API key at: https://agentverse.ai/profile/api-keys");
|
|
1052
|
+
rl.close();
|
|
1053
|
+
process.exit(1);
|
|
1054
|
+
}
|
|
1055
|
+
console.log(" Valid.\n");
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
rl.close();
|
|
1059
|
+
}
|
|
1060
|
+
const shouldDeploy = !skipDeploy && !!apiKey;
|
|
1061
|
+
const baseName = name;
|
|
1062
|
+
const dirName = sanitizeDirName(baseName);
|
|
1063
|
+
const targetDir = path.resolve(process.cwd(), dirName);
|
|
1064
|
+
// Check if directory exists
|
|
1065
|
+
if (fs.existsSync(targetDir)) {
|
|
1066
|
+
if (isJson) {
|
|
1067
|
+
console.log(JSON.stringify({ error: `Directory "${dirName}" already exists.` }));
|
|
1068
|
+
}
|
|
1069
|
+
else {
|
|
1070
|
+
console.error(` Error: Directory "${dirName}" already exists.`);
|
|
1071
|
+
}
|
|
1072
|
+
process.exit(1);
|
|
1073
|
+
}
|
|
1074
|
+
// Generate agent code
|
|
1075
|
+
if (!isJson) {
|
|
1076
|
+
console.log(` ${shouldDeploy ? "Deploying" : "Scaffolding"} ${baseName}...`);
|
|
1077
|
+
}
|
|
1078
|
+
const generated = generateFromTemplate("chat-memory", {
|
|
1079
|
+
agent_name: baseName,
|
|
1080
|
+
description: description || "AI assistant with conversation memory",
|
|
1081
|
+
});
|
|
1082
|
+
// Create directory structure
|
|
1083
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
1084
|
+
fs.mkdirSync(path.join(targetDir, ".claude"), { recursive: true });
|
|
1085
|
+
fs.mkdirSync(path.join(targetDir, ".claude", "rules"), { recursive: true });
|
|
1086
|
+
fs.mkdirSync(path.join(targetDir, ".claude", "skills"), { recursive: true });
|
|
1087
|
+
fs.mkdirSync(path.join(targetDir, ".cursor"), { recursive: true });
|
|
1088
|
+
// Write files
|
|
1089
|
+
fs.writeFileSync(path.join(targetDir, "agent.py"), generated.code, "utf8");
|
|
1090
|
+
fs.writeFileSync(path.join(targetDir, "README.md"), generated.readme, "utf8");
|
|
1091
|
+
fs.writeFileSync(path.join(targetDir, ".env.example"), generated.envExample, "utf8");
|
|
1092
|
+
fs.writeFileSync(path.join(targetDir, "CLAUDE.md"), generated.claudeMd, "utf8");
|
|
1093
|
+
fs.writeFileSync(path.join(targetDir, ".claude", "settings.json"), generated.claudeSettings, "utf8");
|
|
1094
|
+
fs.writeFileSync(path.join(targetDir, "agentlaunch.config.json"), generated.agentlaunchConfig, "utf8");
|
|
1095
|
+
// Write .env with API key
|
|
1096
|
+
if (apiKey) {
|
|
1097
|
+
fs.writeFileSync(path.join(targetDir, ".env"), `AGENTVERSE_API_KEY=${apiKey}\nAGENT_LAUNCH_API_URL=https://agent-launch.ai/api\n`, "utf8");
|
|
1098
|
+
}
|
|
1099
|
+
// Write rules
|
|
1100
|
+
for (const [filename, content] of Object.entries(RULES)) {
|
|
1101
|
+
fs.writeFileSync(path.join(targetDir, ".claude", "rules", filename), content, "utf8");
|
|
1102
|
+
}
|
|
1103
|
+
// Write skills
|
|
1104
|
+
for (const [filepath, content] of Object.entries(SKILLS)) {
|
|
1105
|
+
const skillDir = path.dirname(filepath);
|
|
1106
|
+
fs.mkdirSync(path.join(targetDir, ".claude", "skills", skillDir), { recursive: true });
|
|
1107
|
+
fs.writeFileSync(path.join(targetDir, ".claude", "skills", filepath), content, "utf8");
|
|
1108
|
+
}
|
|
1109
|
+
// Write Cursor config
|
|
1110
|
+
fs.writeFileSync(path.join(targetDir, ".cursor", "mcp.json"), CURSOR_MCP_CONFIG, "utf8");
|
|
1111
|
+
fs.writeFileSync(path.join(targetDir, ".cursorrules"), CURSOR_RULES, "utf8");
|
|
1112
|
+
let agentAddress = "";
|
|
1113
|
+
// Deploy if API key provided
|
|
1114
|
+
if (shouldDeploy) {
|
|
1115
|
+
try {
|
|
1116
|
+
const deploySecrets = {
|
|
1117
|
+
AGENTVERSE_API_KEY: apiKey,
|
|
1118
|
+
AGENTLAUNCH_API_KEY: apiKey,
|
|
1119
|
+
LLM_API_KEY: "sk_2a3c92a0b11e4f18b50708cca1a55179ab38a7c2fb7f4eee95fd68e1e28f860b",
|
|
1120
|
+
};
|
|
1121
|
+
const result = await deployAgent({
|
|
1122
|
+
apiKey,
|
|
1123
|
+
agentName: baseName,
|
|
1124
|
+
sourceCode: generated.code,
|
|
1125
|
+
secrets: deploySecrets,
|
|
1126
|
+
metadata: {
|
|
1127
|
+
readme: generated.readme,
|
|
1128
|
+
short_description: generated.shortDescription,
|
|
1129
|
+
},
|
|
1130
|
+
});
|
|
1131
|
+
agentAddress = result.agentAddress;
|
|
1132
|
+
// Update .env with agent address
|
|
1133
|
+
fs.appendFileSync(path.join(targetDir, ".env"), `AGENT_ADDRESS=${agentAddress}\n`);
|
|
1134
|
+
if (!isJson) {
|
|
1135
|
+
console.log(` Address: ${agentAddress}`);
|
|
1136
|
+
if (result.status === "compiled" || result.status === "running") {
|
|
1137
|
+
console.log(" Status: Running");
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
catch (err) {
|
|
1142
|
+
if (isJson) {
|
|
1143
|
+
console.log(JSON.stringify({ error: `Deploy failed: ${err.message}` }));
|
|
1144
|
+
}
|
|
1145
|
+
else {
|
|
1146
|
+
console.error(`\n Deploy failed: ${err.message}`);
|
|
1147
|
+
console.log(` Your code is saved in ${targetDir}/agent.py`);
|
|
1148
|
+
console.log(" Run 'npx agentlaunch deploy' to try again.");
|
|
1149
|
+
}
|
|
1150
|
+
process.exit(1);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
// JSON output
|
|
1154
|
+
if (isJson) {
|
|
1155
|
+
console.log(JSON.stringify({
|
|
1156
|
+
name: baseName,
|
|
1157
|
+
directory: targetDir,
|
|
1158
|
+
agentAddress: agentAddress || undefined,
|
|
1159
|
+
deployed: shouldDeploy,
|
|
1160
|
+
}));
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
// Success output
|
|
1164
|
+
console.log(`\n ${"=".repeat(56)}`);
|
|
1165
|
+
console.log(` ${shouldDeploy ? "Agent deployed!" : "Agent created!"}`);
|
|
1166
|
+
console.log(` ${"=".repeat(56)}`);
|
|
1167
|
+
console.log(` Name: ${baseName}`);
|
|
1168
|
+
if (agentAddress) {
|
|
1169
|
+
console.log(` Address: ${agentAddress}`);
|
|
1170
|
+
}
|
|
1171
|
+
console.log(` Directory: ${targetDir}`);
|
|
1172
|
+
if (agentAddress) {
|
|
1173
|
+
console.log(`\n Chat: https://agentverse.ai/agents/details/${agentAddress}`);
|
|
1174
|
+
}
|
|
1175
|
+
console.log(`\n Next: cd ${dirName} && claude`);
|
|
1176
|
+
}
|
|
1177
|
+
//# sourceMappingURL=create.js.map
|