@openagentmarket/create-agent 1.0.0 → 1.1.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 ADDED
@@ -0,0 +1,33 @@
1
+ # @openagentmarket/create-agent
2
+
3
+ The quickest way to start building an OpenAgent. This CLI tool scaffolds a comprehensive TypeScript project with all necessary configurations and dependencies.
4
+
5
+ ## Usage
6
+
7
+ Run the following command to create a new agent:
8
+
9
+ ```bash
10
+ npm create @openagentmarket/agent@latest
11
+ # or
12
+ npx @openagentmarket/create-agent@latest
13
+ ```
14
+
15
+ Follow the interactive prompts to name your agent.
16
+
17
+ ## What is included?
18
+
19
+ The scaffolded project includes:
20
+ - **TypeScript** configuration for modern Node.js development.
21
+ - **ESLint & Prettier** setup (implied standards).
22
+ - **@openagentmarket/nodejs** SDK integration.
23
+ - **Example Agent** code (`index.ts`) demonstrating the basic structure.
24
+ - **Environment Configuration** (`.env`) template.
25
+
26
+ ## Getting Started with your new Agent
27
+
28
+ Once created:
29
+
30
+ 1. Navigate to your project directory: `cd <your-agent-name>`
31
+ 2. Install dependencies: `pnpm install` (or `npm install`)
32
+ 3. Configure your `.env` file with a valid mnemonic.
33
+ 4. Start dev mode: `pnpm dev`
package/dist/index.js CHANGED
@@ -2,70 +2,373 @@
2
2
  import fs from 'fs-extra';
3
3
  import path from 'node:path';
4
4
  import prompts from 'prompts';
5
- import { fileURLToPath } from 'node:url';
6
5
  import kleur from 'kleur';
7
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ import { Wallet } from 'ethers';
8
7
  async function main() {
9
- console.log(kleur.bold().cyan('\nšŸš€ Create OpenAgent Project\n'));
8
+ console.log(kleur.bold().cyan('\nšŸš€ OpenAgent Market — Project Scaffolder\n'));
9
+ // Step 1: Choose role
10
+ const { role } = await prompts({
11
+ type: 'select',
12
+ name: 'role',
13
+ message: 'What do you want to do?',
14
+ choices: [
15
+ { title: 'šŸ”Ø Worker — Build an agent that handles tasks', value: 'worker' },
16
+ { title: 'šŸ’¼ Hirer — Hire agents to do work for you', value: 'hirer' }
17
+ ]
18
+ });
19
+ if (!role) {
20
+ console.log(kleur.red('Cancelled.'));
21
+ process.exit(1);
22
+ }
23
+ if (role === 'hirer') {
24
+ await scaffoldHirer();
25
+ }
26
+ else {
27
+ await scaffoldWorker();
28
+ }
29
+ }
30
+ // ── Hirer Flow (zero-config) ─────────────────────────────────────────────────
31
+ async function scaffoldHirer() {
32
+ const targetDir = process.cwd();
33
+ console.log(kleur.dim('\nScaffolding hirer project in current directory...\n'));
34
+ // Auto-generate wallet
35
+ const wallet = Wallet.createRandom();
36
+ const mnemonic = wallet.mnemonic.phrase;
37
+ // 1. package.json
38
+ const packageJson = {
39
+ name: "openagent-hirer",
40
+ version: "1.0.0",
41
+ description: "Hire agents on OpenAgent Market",
42
+ type: "module",
43
+ scripts: {
44
+ start: "tsx index.ts",
45
+ dev: "tsx watch index.ts"
46
+ },
47
+ dependencies: {
48
+ "@openagentmarket/nodejs": "^1.0.0",
49
+ ethers: "^6.10.0",
50
+ dotenv: "^16.4.0"
51
+ },
52
+ devDependencies: {
53
+ tsx: "^4.7.0",
54
+ typescript: "^5.3.3",
55
+ "@types/node": "^20.11.0"
56
+ }
57
+ };
58
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2));
59
+ // 2. tsconfig.json
60
+ writeSharedTsConfig(targetDir);
61
+ // 3. .env (with auto-generated mnemonic)
62
+ fs.writeFileSync(path.join(targetDir, '.env'), `MNEMONIC="${mnemonic}"\n`);
63
+ // 4. .gitignore
64
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\ndist\n');
65
+ // 5. index.ts — persistent XMTP chat CLI
66
+ const indexTs = [
67
+ `import { OpenAgentClient } from '@openagentmarket/nodejs';`,
68
+ `import * as readline from 'node:readline';`,
69
+ `import 'dotenv/config';`,
70
+ ``,
71
+ `const DISCOVER_URL = "https://openagent.market/discover?protocol=openagentmarket";`,
72
+ ``,
73
+ `async function main() {`,
74
+ ` const client = await OpenAgentClient.create({`,
75
+ ` mnemonic: process.env.MNEMONIC,`,
76
+ ` env: "production"`,
77
+ ` });`,
78
+ ``,
79
+ ` console.log("");`,
80
+ ` console.log("āœ… Connected to XMTP");`,
81
+ ` console.log(" Wallet: " + client.getAddress());`,
82
+ ` console.log("");`,
83
+ ` printHelp();`,
84
+ ``,
85
+ ` // ── Start REPL ──`,
86
+ ` const rl = readline.createInterface({`,
87
+ ` input: process.stdin,`,
88
+ ` output: process.stdout,`,
89
+ ` prompt: "\\n> "`,
90
+ ` });`,
91
+ ``,
92
+ ` rl.prompt();`,
93
+ ``,
94
+ ` rl.on("line", async (line: string) => {`,
95
+ ` const input = line.trim();`,
96
+ ` if (!input) { rl.prompt(); return; }`,
97
+ ``,
98
+ ` try {`,
99
+ ` if (input === "/help") {`,
100
+ ` printHelp();`,
101
+ ` }`,
102
+ ` else if (input === "/discover") {`,
103
+ ` await discoverAgents();`,
104
+ ` }`,
105
+ ` else if (input.startsWith("/chat ")) {`,
106
+ ` const parts = input.slice(6).trim().split(" ");`,
107
+ ` const address = parts[0];`,
108
+ ` const message = parts.slice(1).join(" ");`,
109
+ ` if (!address || !message) {`,
110
+ ` console.log("Usage: /chat <agent-address> <message>");`,
111
+ ` } else {`,
112
+ ` console.log("šŸ“¤ Sending to " + address + "...");`,
113
+ ` const reply = await client.chat(address, message);`,
114
+ ` console.log("šŸ“© Reply:", reply.success ? reply.result : reply.error);`,
115
+ ` }`,
116
+ ` }`,
117
+ ` else if (input.startsWith("/task ")) {`,
118
+ ` const parts = input.slice(6).trim().split(" ");`,
119
+ ` const address = parts[0];`,
120
+ ` const method = parts[1];`,
121
+ ` const paramsStr = parts.slice(2).join(" ") || "{}";`,
122
+ ` if (!address || !method) {`,
123
+ ` console.log("Usage: /task <agent-address> <method> [json-params]");`,
124
+ ` } else {`,
125
+ ` let params: any = {};`,
126
+ ` try { params = JSON.parse(paramsStr); } catch { params = { input: paramsStr }; }`,
127
+ ` console.log("šŸ“¤ Sending task '" + method + "' to " + address + "...");`,
128
+ ` const result = await client.sendTask(address, method, params);`,
129
+ ` if (result.paymentRequired) {`,
130
+ ` console.log("šŸ’° Payment required:", result.paymentRequired);`,
131
+ ` } else if (result.success) {`,
132
+ ` console.log("āœ… Result:", JSON.stringify(result.result, null, 2));`,
133
+ ` } else {`,
134
+ ` console.log("āŒ Error:", result.error);`,
135
+ ` }`,
136
+ ` }`,
137
+ ` }`,
138
+ ` else if (input === "/quit" || input === "/exit") {`,
139
+ ` console.log("Bye! šŸ‘‹");`,
140
+ ` process.exit(0);`,
141
+ ` }`,
142
+ ` else {`,
143
+ ` console.log("Unknown command. Type /help for available commands.");`,
144
+ ` }`,
145
+ ` } catch (err: any) {`,
146
+ ` console.error("āŒ Error:", err.message || err);`,
147
+ ` }`,
148
+ ``,
149
+ ` rl.prompt();`,
150
+ ` });`,
151
+ ``,
152
+ ` rl.on("close", () => {`,
153
+ ` console.log("\\nBye! šŸ‘‹");`,
154
+ ` process.exit(0);`,
155
+ ` });`,
156
+ `}`,
157
+ ``,
158
+ `function printHelp() {`,
159
+ ` console.log("šŸ“– Commands:");`,
160
+ ` console.log(" /discover — Browse available agents");`,
161
+ ` console.log(" /chat <agent-address> <message> — Send a chat message");`,
162
+ ` console.log(" /task <agent-address> <method> [params] — Send a task (params as JSON)");`,
163
+ ` console.log(" /help — Show this help");`,
164
+ ` console.log(" /quit — Exit");`,
165
+ `}`,
166
+ ``,
167
+ `async function discoverAgents() {`,
168
+ ` console.log("šŸ” Fetching agents from OpenAgent Market...\\n");`,
169
+ ` try {`,
170
+ ` const res = await fetch(DISCOVER_URL);`,
171
+ ` const data = await res.json() as any;`,
172
+ ` if (!data.success || !data.items?.length) {`,
173
+ ` console.log("No agents found.");`,
174
+ ` return;`,
175
+ ` }`,
176
+ ` for (const item of data.items) {`,
177
+ ` const reg = item.registrationFile || {};`,
178
+ ` const name = reg.name || "Unknown";`,
179
+ ` const desc = reg.description || "";`,
180
+ ` // Find xmtpAddress from metadata array`,
181
+ ` const meta = item.metadata || [];`,
182
+ ` let addr = item.owner || "?";`,
183
+ ` const walletMeta = meta.find((m: any) => m.key === "agentWallet");`,
184
+ ` if (walletMeta) addr = walletMeta.value;`,
185
+ ` const agentId = item.agentId || "";`,
186
+ ` console.log(" šŸ¤– " + name + (agentId ? " (#" + agentId + ")" : ""));`,
187
+ ` if (desc) console.log(" " + desc.slice(0, 120) + (desc.length > 120 ? "..." : ""));`,
188
+ ` console.log(" Address: " + addr);`,
189
+ ` console.log("");`,
190
+ ` }`,
191
+ ` console.log("Total: " + data.items.length + " agent(s)");`,
192
+ ` if (data.hasMore) console.log("(more agents available)");`,
193
+ ` } catch (err: any) {`,
194
+ ` console.error("Failed to fetch agents:", err.message);`,
195
+ ` }`,
196
+ `}`,
197
+ ``,
198
+ `main().catch(console.error);`,
199
+ ].join('\n');
200
+ fs.writeFileSync(path.join(targetDir, 'index.ts'), indexTs);
201
+ // 6. README.md
202
+ const readmeLines = [
203
+ `# OpenAgent Hirer CLI`,
204
+ ``,
205
+ `A persistent XMTP chat CLI for hiring agents on [OpenAgent Market](https://openagent.market).`,
206
+ ``,
207
+ `## Getting Started`,
208
+ ``,
209
+ '```bash',
210
+ `npm install`,
211
+ `npm start`,
212
+ '```',
213
+ ``,
214
+ `## Commands`,
215
+ ``,
216
+ `| Command | Description |`,
217
+ `|---------|-------------|`,
218
+ '| `/discover` | Browse available agents from the marketplace |',
219
+ '| `/chat <address> <message>` | Send a plain text message to an agent |',
220
+ '| `/task <address> <method> [json]` | Send a JSON-RPC task to an agent |',
221
+ '| `/help` | Show available commands |',
222
+ '| `/quit` | Exit the CLI |',
223
+ ``,
224
+ `## Examples`,
225
+ ``,
226
+ '```',
227
+ `> /discover`,
228
+ `> /chat 0xAgentAddr What can you do?`,
229
+ `> /task 0xAgentAddr say_hello {"name": "World"}`,
230
+ '```',
231
+ ``,
232
+ `## Environment Variables`,
233
+ ``,
234
+ `| Variable | Description |`,
235
+ `|----------|-------------|`,
236
+ '| `MNEMONIC` | Wallet seed phrase (auto-generated for you) |',
237
+ ``,
238
+ `## Resources`,
239
+ ``,
240
+ `- [Discover API](https://openagent.market/discover?protocol=openagentmarket)`,
241
+ `- [SDK Docs](https://www.npmjs.com/package/@openagentmarket/nodejs)`,
242
+ `- [Explorer](https://8004agents.ai)`,
243
+ ].join('\n');
244
+ fs.writeFileSync(path.join(targetDir, 'README.md'), readmeLines);
245
+ // Print success
246
+ console.log(kleur.green('āœ… Hirer CLI project created!\n'));
247
+ console.log(` ${kleur.bold('Wallet address:')} ${kleur.cyan(wallet.address)}`);
248
+ console.log(` ${kleur.bold('Mnemonic:')} saved to ${kleur.yellow('.env')}\n`);
249
+ console.log(kleur.bold('Next steps:'));
250
+ console.log(' npm install');
251
+ console.log(' npm start');
252
+ console.log(' # Then type /discover to find agents\n');
253
+ }
254
+ // ── Worker Flow (interactive) ────────────────────────────────────────────────
255
+ async function scaffoldWorker() {
10
256
  const response = await prompts([
11
257
  {
12
258
  type: 'text',
13
- name: 'projectName',
14
- message: 'What is the name of your agent?',
259
+ name: 'agentName',
260
+ message: 'What is your agent\'s name?',
15
261
  initial: 'my-agent'
262
+ },
263
+ {
264
+ type: 'text',
265
+ name: 'description',
266
+ message: 'Describe your agent:',
267
+ initial: 'A freshly created OpenAgent.'
268
+ },
269
+ {
270
+ type: 'text',
271
+ name: 'skills',
272
+ message: 'Skills (comma-separated):',
273
+ initial: 'say_hello'
274
+ },
275
+ {
276
+ type: 'confirm',
277
+ name: 'includeRegistration',
278
+ message: 'Include on-chain registration (ERC-8004)?',
279
+ initial: false
280
+ },
281
+ {
282
+ type: 'confirm',
283
+ name: 'includePayments',
284
+ message: 'Include x402 payments?',
285
+ initial: false
16
286
  }
17
287
  ]);
18
- const { projectName } = response;
19
- if (!projectName) {
288
+ const { agentName, description, skills, includeRegistration, includePayments } = response;
289
+ if (!agentName) {
20
290
  console.log(kleur.red('Cancelled.'));
21
291
  process.exit(1);
22
292
  }
23
- const targetDir = path.join(process.cwd(), projectName);
293
+ const skillList = skills.split(',').map((s) => s.trim()).filter(Boolean);
294
+ const dirName = agentName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
295
+ const targetDir = path.join(process.cwd(), dirName);
24
296
  if (fs.existsSync(targetDir)) {
25
- console.log(kleur.red(`Error: Directory ${projectName} already exists!`));
297
+ console.log(kleur.red(`Error: Directory ${dirName} already exists!`));
26
298
  process.exit(1);
27
299
  }
28
- console.log(`\nCreating project in ${kleur.green(targetDir)}...\n`);
300
+ console.log(`\nCreating agent in ${kleur.green(targetDir)}...\n`);
29
301
  fs.mkdirSync(targetDir, { recursive: true });
30
302
  // 1. package.json
31
303
  const packageJson = {
32
- name: projectName,
304
+ name: dirName,
33
305
  version: "1.0.0",
34
- description: "An OpenAgent bot",
306
+ description: description,
35
307
  type: "module",
36
308
  scripts: {
37
- "start": "tsx index.ts",
38
- "dev": "tsx watch index.ts"
309
+ start: "tsx index.ts",
310
+ dev: "tsx watch index.ts"
39
311
  },
40
312
  dependencies: {
41
313
  "@openagentmarket/nodejs": "^1.0.0",
42
- "ethers": "^6.10.0",
43
- "dotenv": "^16.4.0"
314
+ ethers: "^6.10.0",
315
+ dotenv: "^16.4.0"
44
316
  },
45
317
  devDependencies: {
46
- "tsx": "^4.7.0",
47
- "typescript": "^5.3.3",
318
+ tsx: "^4.7.0",
319
+ typescript: "^5.3.3",
48
320
  "@types/node": "^20.11.0"
49
321
  }
50
322
  };
51
323
  fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2));
52
324
  // 2. tsconfig.json
53
- const tsConfig = {
54
- "compilerOptions": {
55
- "target": "ES2022",
56
- "module": "NodeNext",
57
- "moduleResolution": "NodeNext",
58
- "strict": true,
59
- "esModuleInterop": true,
60
- "skipLibCheck": true,
61
- "forceConsistentCasingInFileNames": true
62
- }
63
- };
64
- fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
325
+ writeSharedTsConfig(targetDir);
65
326
  // 3. .env
66
- fs.writeFileSync(path.join(targetDir, '.env'), 'MNEMONIC="your twelve word mnemonic phrase here"\n');
67
- fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\n');
68
- // 4. index.ts (Template)
327
+ let envContent = 'MNEMONIC="your twelve word mnemonic phrase here"\n';
328
+ if (includeRegistration) {
329
+ envContent += 'REGISTRATION_PRIVATE_KEY=""\n';
330
+ envContent += 'PINATA_JWT=""\n';
331
+ }
332
+ fs.writeFileSync(path.join(targetDir, '.env'), envContent);
333
+ // 4. .gitignore
334
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\ndist\n');
335
+ // 5. index.ts
336
+ const taskHandlers = skillList.map((skill) => `
337
+ agent.onTask("${skill}", async (input) => {
338
+ return { message: \`Handled ${skill} with \${JSON.stringify(input)}\` };
339
+ });`).join('\n');
340
+ const paymentConfig = includePayments ? `
341
+ payment: {
342
+ amount: 5,
343
+ currency: "USDC",
344
+ recipientAddress: "0x0000000000000000000000000000000000000000" // Update this!
345
+ },` : '';
346
+ const registrationBlock = includeRegistration ? `
347
+
348
+ // ── Register on-chain (optional) ──
349
+ async function registerAgent(agent: any) {
350
+ const result = await agent.register(
351
+ {
352
+ name: "${agentName}",
353
+ description: "${description}",
354
+ image: "https://example.com/avatar.png",
355
+ metadata: {
356
+ skills: ${JSON.stringify(skillList)},
357
+ pricing: { amount: "5.0", currency: "USDC", chain: "base" },
358
+ category: "utility",
359
+ tags: ["openagent"]
360
+ }
361
+ },
362
+ {
363
+ privateKey: process.env.REGISTRATION_PRIVATE_KEY!,
364
+ pinataJwt: process.env.PINATA_JWT!,
365
+ }
366
+ );
367
+ console.log("Registered:", result);
368
+ }` : '';
369
+ const registrationCall = includeRegistration
370
+ ? '\n // Uncomment to register on-chain:\n // await registerAgent(agent);\n'
371
+ : '';
69
372
  const indexTs = `import { OpenAgent } from '@openagentmarket/nodejs';
70
373
  import 'dotenv/config';
71
374
 
@@ -80,33 +383,91 @@ async function main() {
80
383
  mnemonic,
81
384
  env: "production",
82
385
  card: {
83
- name: "${projectName}",
84
- description: "A freshly created OpenAgent.",
85
- skills: ["say_hello"]
86
- },
87
- payment: {
88
- amount: 0.001, // 0.001 ETH
89
- currency: "ETH",
90
- recipientAddress: "0x0000000000000000000000000000000000000000" // Update this!
91
- }
92
- });
93
-
94
- agent.onTask("say_hello", async (input) => {
95
- return { message: \`Hello \${input.name || "World"}!\` };
386
+ name: "${agentName}",
387
+ description: "${description}",
388
+ skills: ${JSON.stringify(skillList)}
389
+ },${paymentConfig}
96
390
  });
391
+ ${taskHandlers}
97
392
 
98
- await agent.start();
393
+ await agent.start();${registrationCall}
99
394
  }
100
-
395
+ ${registrationBlock}
101
396
  main().catch(console.error);
102
397
  `;
103
398
  fs.writeFileSync(path.join(targetDir, 'index.ts'), indexTs);
104
- console.log(kleur.green('Success! Project created.'));
105
- console.log('\nNext steps:');
106
- console.log(` cd ${projectName}`);
107
- console.log(' pnpm install');
108
- console.log(' # Update .env with your mnemonic');
109
- console.log(' pnpm start\n');
399
+ // 6. README.md
400
+ const readmeLines = [
401
+ `# ${agentName}`,
402
+ ``,
403
+ description,
404
+ ``,
405
+ `## Getting Started`,
406
+ ``,
407
+ '```bash',
408
+ `cd ${dirName}`,
409
+ `npm install`,
410
+ '```',
411
+ ``,
412
+ `### Configuration`,
413
+ ``,
414
+ `Edit \`.env\` and set your 12-word mnemonic phrase.`,
415
+ includeRegistration ? `\nFor on-chain registration, also set \`REGISTRATION_PRIVATE_KEY\` and \`PINATA_JWT\`.\n` : '',
416
+ `### Running`,
417
+ ``,
418
+ '```bash',
419
+ `# Development mode with hot-reload`,
420
+ `npm run dev`,
421
+ ``,
422
+ `# Production`,
423
+ `npm start`,
424
+ '```',
425
+ ``,
426
+ `## Agent Details`,
427
+ ``,
428
+ `| Property | Value |`,
429
+ `|----------|-------|`,
430
+ `| **Name** | ${agentName} |`,
431
+ `| **Skills** | ${skillList.join(', ')} |`,
432
+ `| **Payments** | ${includePayments ? 'Enabled (x402)' : 'Disabled'} |`,
433
+ `| **Registration** | ${includeRegistration ? 'Included' : 'Not included'} |`,
434
+ ``,
435
+ `## Environment Variables`,
436
+ ``,
437
+ `| Variable | Required | Description |`,
438
+ `|----------|----------|-------------|`,
439
+ '| `MNEMONIC` | Yes | Agent wallet seed phrase |',
440
+ includeRegistration ? '| `REGISTRATION_PRIVATE_KEY` | For registration | Wallet paying gas |\n| `PINATA_JWT` | For registration | IPFS metadata upload |' : '',
441
+ ``,
442
+ `## Resources`,
443
+ ``,
444
+ `- [SDK Docs](https://www.npmjs.com/package/@openagentmarket/nodejs)`,
445
+ `- [Explorer](https://8004agents.ai)`,
446
+ `- [GitHub](https://github.com/openagentmarket)`,
447
+ ].join('\n');
448
+ fs.writeFileSync(path.join(targetDir, 'README.md'), readmeLines);
449
+ // Print success
450
+ console.log(kleur.green('āœ… Agent project created!\n'));
451
+ console.log(kleur.bold('Next steps:'));
452
+ console.log(` cd ${dirName}`);
453
+ console.log(' npm install');
454
+ console.log(' # Set your MNEMONIC in .env');
455
+ console.log(' npm start\n');
456
+ }
457
+ // ── Shared helpers ───────────────────────────────────────────────────────────
458
+ function writeSharedTsConfig(targetDir) {
459
+ const tsConfig = {
460
+ compilerOptions: {
461
+ target: "ES2022",
462
+ module: "NodeNext",
463
+ moduleResolution: "NodeNext",
464
+ strict: true,
465
+ esModuleInterop: true,
466
+ skipLibCheck: true,
467
+ forceConsistentCasingInFileNames: true
468
+ }
469
+ };
470
+ fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
110
471
  }
111
472
  main().catch((err) => {
112
473
  console.error(err);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openagentmarket/create-agent",
3
- "version": "1.0.0",
4
- "description": "CLI to scaffold a new OpenAgent project",
3
+ "version": "1.1.0",
4
+ "description": "CLI to scaffold a new OpenAgent project (hirer or worker)",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create-openagent": "./dist/index.js"
@@ -14,7 +14,8 @@
14
14
  "dependencies": {
15
15
  "prompts": "^2.4.2",
16
16
  "kleur": "^4.1.5",
17
- "fs-extra": "^11.2.0"
17
+ "fs-extra": "^11.2.0",
18
+ "ethers": "^6.10.0"
18
19
  },
19
20
  "devDependencies": {
20
21
  "@types/prompts": "^2.4.9",
package/src/index.ts CHANGED
@@ -2,81 +2,402 @@
2
2
  import fs from 'fs-extra';
3
3
  import path from 'node:path';
4
4
  import prompts from 'prompts';
5
- import { fileURLToPath } from 'node:url';
6
5
  import kleur from 'kleur';
7
-
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ import { Wallet } from 'ethers';
9
7
 
10
8
  async function main() {
11
- console.log(kleur.bold().cyan('\nšŸš€ Create OpenAgent Project\n'));
9
+ console.log(kleur.bold().cyan('\nšŸš€ OpenAgent Market — Project Scaffolder\n'));
10
+
11
+ // Step 1: Choose role
12
+ const { role } = await prompts({
13
+ type: 'select',
14
+ name: 'role',
15
+ message: 'What do you want to do?',
16
+ choices: [
17
+ { title: 'šŸ”Ø Worker — Build an agent that handles tasks', value: 'worker' },
18
+ { title: 'šŸ’¼ Hirer — Hire agents to do work for you', value: 'hirer' }
19
+ ]
20
+ });
21
+
22
+ if (!role) {
23
+ console.log(kleur.red('Cancelled.'));
24
+ process.exit(1);
25
+ }
26
+
27
+ if (role === 'hirer') {
28
+ await scaffoldHirer();
29
+ } else {
30
+ await scaffoldWorker();
31
+ }
32
+ }
33
+
34
+ // ── Hirer Flow (zero-config) ─────────────────────────────────────────────────
12
35
 
36
+ async function scaffoldHirer() {
37
+ const targetDir = process.cwd();
38
+
39
+ console.log(kleur.dim('\nScaffolding hirer project in current directory...\n'));
40
+
41
+ // Auto-generate wallet
42
+ const wallet = Wallet.createRandom();
43
+ const mnemonic = wallet.mnemonic!.phrase;
44
+
45
+ // 1. package.json
46
+ const packageJson = {
47
+ name: "openagent-hirer",
48
+ version: "1.0.0",
49
+ description: "Hire agents on OpenAgent Market",
50
+ type: "module",
51
+ scripts: {
52
+ start: "tsx index.ts",
53
+ dev: "tsx watch index.ts"
54
+ },
55
+ dependencies: {
56
+ "@openagentmarket/nodejs": "^1.0.0",
57
+ ethers: "^6.10.0",
58
+ dotenv: "^16.4.0"
59
+ },
60
+ devDependencies: {
61
+ tsx: "^4.7.0",
62
+ typescript: "^5.3.3",
63
+ "@types/node": "^20.11.0"
64
+ }
65
+ };
66
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2));
67
+
68
+ // 2. tsconfig.json
69
+ writeSharedTsConfig(targetDir);
70
+
71
+ // 3. .env (with auto-generated mnemonic)
72
+ fs.writeFileSync(path.join(targetDir, '.env'), `MNEMONIC="${mnemonic}"\n`);
73
+
74
+ // 4. .gitignore
75
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\ndist\n');
76
+
77
+ // 5. index.ts — persistent XMTP chat CLI
78
+ const indexTs = [
79
+ `import { OpenAgentClient } from '@openagentmarket/nodejs';`,
80
+ `import * as readline from 'node:readline';`,
81
+ `import 'dotenv/config';`,
82
+ ``,
83
+ `const DISCOVER_URL = "https://openagent.market/discover?protocol=openagentmarket";`,
84
+ ``,
85
+ `async function main() {`,
86
+ ` const client = await OpenAgentClient.create({`,
87
+ ` mnemonic: process.env.MNEMONIC,`,
88
+ ` env: "production"`,
89
+ ` });`,
90
+ ``,
91
+ ` console.log("");`,
92
+ ` console.log("āœ… Connected to XMTP");`,
93
+ ` console.log(" Wallet: " + client.getAddress());`,
94
+ ` console.log("");`,
95
+ ` printHelp();`,
96
+ ``,
97
+ ` // ── Start REPL ──`,
98
+ ` const rl = readline.createInterface({`,
99
+ ` input: process.stdin,`,
100
+ ` output: process.stdout,`,
101
+ ` prompt: "\\n> "`,
102
+ ` });`,
103
+ ``,
104
+ ` rl.prompt();`,
105
+ ``,
106
+ ` rl.on("line", async (line: string) => {`,
107
+ ` const input = line.trim();`,
108
+ ` if (!input) { rl.prompt(); return; }`,
109
+ ``,
110
+ ` try {`,
111
+ ` if (input === "/help") {`,
112
+ ` printHelp();`,
113
+ ` }`,
114
+ ` else if (input === "/discover") {`,
115
+ ` await discoverAgents();`,
116
+ ` }`,
117
+ ` else if (input.startsWith("/chat ")) {`,
118
+ ` const parts = input.slice(6).trim().split(" ");`,
119
+ ` const address = parts[0];`,
120
+ ` const message = parts.slice(1).join(" ");`,
121
+ ` if (!address || !message) {`,
122
+ ` console.log("Usage: /chat <agent-address> <message>");`,
123
+ ` } else {`,
124
+ ` console.log("šŸ“¤ Sending to " + address + "...");`,
125
+ ` const reply = await client.chat(address, message);`,
126
+ ` console.log("šŸ“© Reply:", reply.success ? reply.result : reply.error);`,
127
+ ` }`,
128
+ ` }`,
129
+ ` else if (input.startsWith("/task ")) {`,
130
+ ` const parts = input.slice(6).trim().split(" ");`,
131
+ ` const address = parts[0];`,
132
+ ` const method = parts[1];`,
133
+ ` const paramsStr = parts.slice(2).join(" ") || "{}";`,
134
+ ` if (!address || !method) {`,
135
+ ` console.log("Usage: /task <agent-address> <method> [json-params]");`,
136
+ ` } else {`,
137
+ ` let params: any = {};`,
138
+ ` try { params = JSON.parse(paramsStr); } catch { params = { input: paramsStr }; }`,
139
+ ` console.log("šŸ“¤ Sending task '" + method + "' to " + address + "...");`,
140
+ ` const result = await client.sendTask(address, method, params);`,
141
+ ` if (result.paymentRequired) {`,
142
+ ` console.log("šŸ’° Payment required:", result.paymentRequired);`,
143
+ ` } else if (result.success) {`,
144
+ ` console.log("āœ… Result:", JSON.stringify(result.result, null, 2));`,
145
+ ` } else {`,
146
+ ` console.log("āŒ Error:", result.error);`,
147
+ ` }`,
148
+ ` }`,
149
+ ` }`,
150
+ ` else if (input === "/quit" || input === "/exit") {`,
151
+ ` console.log("Bye! šŸ‘‹");`,
152
+ ` process.exit(0);`,
153
+ ` }`,
154
+ ` else {`,
155
+ ` console.log("Unknown command. Type /help for available commands.");`,
156
+ ` }`,
157
+ ` } catch (err: any) {`,
158
+ ` console.error("āŒ Error:", err.message || err);`,
159
+ ` }`,
160
+ ``,
161
+ ` rl.prompt();`,
162
+ ` });`,
163
+ ``,
164
+ ` rl.on("close", () => {`,
165
+ ` console.log("\\nBye! šŸ‘‹");`,
166
+ ` process.exit(0);`,
167
+ ` });`,
168
+ `}`,
169
+ ``,
170
+ `function printHelp() {`,
171
+ ` console.log("šŸ“– Commands:");`,
172
+ ` console.log(" /discover — Browse available agents");`,
173
+ ` console.log(" /chat <agent-address> <message> — Send a chat message");`,
174
+ ` console.log(" /task <agent-address> <method> [params] — Send a task (params as JSON)");`,
175
+ ` console.log(" /help — Show this help");`,
176
+ ` console.log(" /quit — Exit");`,
177
+ `}`,
178
+ ``,
179
+ `async function discoverAgents() {`,
180
+ ` console.log("šŸ” Fetching agents from OpenAgent Market...\\n");`,
181
+ ` try {`,
182
+ ` const res = await fetch(DISCOVER_URL);`,
183
+ ` const data = await res.json() as any;`,
184
+ ` if (!data.success || !data.items?.length) {`,
185
+ ` console.log("No agents found.");`,
186
+ ` return;`,
187
+ ` }`,
188
+ ` for (const item of data.items) {`,
189
+ ` const reg = item.registrationFile || {};`,
190
+ ` const name = reg.name || "Unknown";`,
191
+ ` const desc = reg.description || "";`,
192
+ ` // Find xmtpAddress from metadata array`,
193
+ ` const meta = item.metadata || [];`,
194
+ ` let addr = item.owner || "?";`,
195
+ ` const walletMeta = meta.find((m: any) => m.key === "agentWallet");`,
196
+ ` if (walletMeta) addr = walletMeta.value;`,
197
+ ` const agentId = item.agentId || "";`,
198
+ ` console.log(" šŸ¤– " + name + (agentId ? " (#" + agentId + ")" : ""));`,
199
+ ` if (desc) console.log(" " + desc.slice(0, 120) + (desc.length > 120 ? "..." : ""));`,
200
+ ` console.log(" Address: " + addr);`,
201
+ ` console.log("");`,
202
+ ` }`,
203
+ ` console.log("Total: " + data.items.length + " agent(s)");`,
204
+ ` if (data.hasMore) console.log("(more agents available)");`,
205
+ ` } catch (err: any) {`,
206
+ ` console.error("Failed to fetch agents:", err.message);`,
207
+ ` }`,
208
+ `}`,
209
+ ``,
210
+ `main().catch(console.error);`,
211
+ ].join('\n');
212
+ fs.writeFileSync(path.join(targetDir, 'index.ts'), indexTs);
213
+
214
+ // 6. README.md
215
+ const readmeLines = [
216
+ `# OpenAgent Hirer CLI`,
217
+ ``,
218
+ `A persistent XMTP chat CLI for hiring agents on [OpenAgent Market](https://openagent.market).`,
219
+ ``,
220
+ `## Getting Started`,
221
+ ``,
222
+ '```bash',
223
+ `npm install`,
224
+ `npm start`,
225
+ '```',
226
+ ``,
227
+ `## Commands`,
228
+ ``,
229
+ `| Command | Description |`,
230
+ `|---------|-------------|`,
231
+ '| `/discover` | Browse available agents from the marketplace |',
232
+ '| `/chat <address> <message>` | Send a plain text message to an agent |',
233
+ '| `/task <address> <method> [json]` | Send a JSON-RPC task to an agent |',
234
+ '| `/help` | Show available commands |',
235
+ '| `/quit` | Exit the CLI |',
236
+ ``,
237
+ `## Examples`,
238
+ ``,
239
+ '```',
240
+ `> /discover`,
241
+ `> /chat 0xAgentAddr What can you do?`,
242
+ `> /task 0xAgentAddr say_hello {"name": "World"}`,
243
+ '```',
244
+ ``,
245
+ `## Environment Variables`,
246
+ ``,
247
+ `| Variable | Description |`,
248
+ `|----------|-------------|`,
249
+ '| `MNEMONIC` | Wallet seed phrase (auto-generated for you) |',
250
+ ``,
251
+ `## Resources`,
252
+ ``,
253
+ `- [Discover API](https://openagent.market/discover?protocol=openagentmarket)`,
254
+ `- [SDK Docs](https://www.npmjs.com/package/@openagentmarket/nodejs)`,
255
+ `- [Explorer](https://8004agents.ai)`,
256
+ ].join('\n');
257
+ fs.writeFileSync(path.join(targetDir, 'README.md'), readmeLines);
258
+
259
+ // Print success
260
+ console.log(kleur.green('āœ… Hirer CLI project created!\n'));
261
+ console.log(` ${kleur.bold('Wallet address:')} ${kleur.cyan(wallet.address)}`);
262
+ console.log(` ${kleur.bold('Mnemonic:')} saved to ${kleur.yellow('.env')}\n`);
263
+ console.log(kleur.bold('Next steps:'));
264
+ console.log(' npm install');
265
+ console.log(' npm start');
266
+ console.log(' # Then type /discover to find agents\n');
267
+ }
268
+
269
+ // ── Worker Flow (interactive) ────────────────────────────────────────────────
270
+
271
+ async function scaffoldWorker() {
13
272
  const response = await prompts([
14
273
  {
15
274
  type: 'text',
16
- name: 'projectName',
17
- message: 'What is the name of your agent?',
275
+ name: 'agentName',
276
+ message: 'What is your agent\'s name?',
18
277
  initial: 'my-agent'
278
+ },
279
+ {
280
+ type: 'text',
281
+ name: 'description',
282
+ message: 'Describe your agent:',
283
+ initial: 'A freshly created OpenAgent.'
284
+ },
285
+ {
286
+ type: 'text',
287
+ name: 'skills',
288
+ message: 'Skills (comma-separated):',
289
+ initial: 'say_hello'
290
+ },
291
+ {
292
+ type: 'confirm',
293
+ name: 'includeRegistration',
294
+ message: 'Include on-chain registration (ERC-8004)?',
295
+ initial: false
296
+ },
297
+ {
298
+ type: 'confirm',
299
+ name: 'includePayments',
300
+ message: 'Include x402 payments?',
301
+ initial: false
19
302
  }
20
303
  ]);
21
304
 
22
- const { projectName } = response;
23
- if (!projectName) {
305
+ const { agentName, description, skills, includeRegistration, includePayments } = response;
306
+ if (!agentName) {
24
307
  console.log(kleur.red('Cancelled.'));
25
308
  process.exit(1);
26
309
  }
27
310
 
28
- const targetDir = path.join(process.cwd(), projectName);
311
+ const skillList = skills.split(',').map((s: string) => s.trim()).filter(Boolean);
312
+ const dirName = agentName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
313
+ const targetDir = path.join(process.cwd(), dirName);
29
314
 
30
315
  if (fs.existsSync(targetDir)) {
31
- console.log(kleur.red(`Error: Directory ${projectName} already exists!`));
316
+ console.log(kleur.red(`Error: Directory ${dirName} already exists!`));
32
317
  process.exit(1);
33
318
  }
34
319
 
35
- console.log(`\nCreating project in ${kleur.green(targetDir)}...\n`);
320
+ console.log(`\nCreating agent in ${kleur.green(targetDir)}...\n`);
36
321
  fs.mkdirSync(targetDir, { recursive: true });
37
322
 
38
323
  // 1. package.json
39
324
  const packageJson = {
40
- name: projectName,
325
+ name: dirName,
41
326
  version: "1.0.0",
42
- description: "An OpenAgent bot",
327
+ description: description,
43
328
  type: "module",
44
329
  scripts: {
45
- "start": "tsx index.ts",
46
- "dev": "tsx watch index.ts"
330
+ start: "tsx index.ts",
331
+ dev: "tsx watch index.ts"
47
332
  },
48
333
  dependencies: {
49
334
  "@openagentmarket/nodejs": "^1.0.0",
50
- "ethers": "^6.10.0",
51
- "dotenv": "^16.4.0"
335
+ ethers: "^6.10.0",
336
+ dotenv: "^16.4.0"
52
337
  },
53
338
  devDependencies: {
54
- "tsx": "^4.7.0",
55
- "typescript": "^5.3.3",
339
+ tsx: "^4.7.0",
340
+ typescript: "^5.3.3",
56
341
  "@types/node": "^20.11.0"
57
342
  }
58
343
  };
59
344
  fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2));
60
345
 
61
346
  // 2. tsconfig.json
62
- const tsConfig = {
63
- "compilerOptions": {
64
- "target": "ES2022",
65
- "module": "NodeNext",
66
- "moduleResolution": "NodeNext",
67
- "strict": true,
68
- "esModuleInterop": true,
69
- "skipLibCheck": true,
70
- "forceConsistentCasingInFileNames": true
71
- }
72
- };
73
- fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
347
+ writeSharedTsConfig(targetDir);
74
348
 
75
349
  // 3. .env
76
- fs.writeFileSync(path.join(targetDir, '.env'), 'MNEMONIC="your twelve word mnemonic phrase here"\n');
77
- fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\n');
350
+ let envContent = 'MNEMONIC="your twelve word mnemonic phrase here"\n';
351
+ if (includeRegistration) {
352
+ envContent += 'REGISTRATION_PRIVATE_KEY=""\n';
353
+ envContent += 'PINATA_JWT=""\n';
354
+ }
355
+ fs.writeFileSync(path.join(targetDir, '.env'), envContent);
356
+
357
+ // 4. .gitignore
358
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\ndist\n');
359
+
360
+ // 5. index.ts
361
+ const taskHandlers = skillList.map((skill: string) => `
362
+ agent.onTask("${skill}", async (input) => {
363
+ return { message: \`Handled ${skill} with \${JSON.stringify(input)}\` };
364
+ });`).join('\n');
365
+
366
+ const paymentConfig = includePayments ? `
367
+ payment: {
368
+ amount: 5,
369
+ currency: "USDC",
370
+ recipientAddress: "0x0000000000000000000000000000000000000000" // Update this!
371
+ },` : '';
372
+
373
+ const registrationBlock = includeRegistration ? `
374
+
375
+ // ── Register on-chain (optional) ──
376
+ async function registerAgent(agent: any) {
377
+ const result = await agent.register(
378
+ {
379
+ name: "${agentName}",
380
+ description: "${description}",
381
+ image: "https://example.com/avatar.png",
382
+ metadata: {
383
+ skills: ${JSON.stringify(skillList)},
384
+ pricing: { amount: "5.0", currency: "USDC", chain: "base" },
385
+ category: "utility",
386
+ tags: ["openagent"]
387
+ }
388
+ },
389
+ {
390
+ privateKey: process.env.REGISTRATION_PRIVATE_KEY!,
391
+ pinataJwt: process.env.PINATA_JWT!,
392
+ }
393
+ );
394
+ console.log("Registered:", result);
395
+ }` : '';
396
+
397
+ const registrationCall = includeRegistration
398
+ ? '\n // Uncomment to register on-chain:\n // await registerAgent(agent);\n'
399
+ : '';
78
400
 
79
- // 4. index.ts (Template)
80
401
  const indexTs = `import { OpenAgent } from '@openagentmarket/nodejs';
81
402
  import 'dotenv/config';
82
403
 
@@ -91,34 +412,95 @@ async function main() {
91
412
  mnemonic,
92
413
  env: "production",
93
414
  card: {
94
- name: "${projectName}",
95
- description: "A freshly created OpenAgent.",
96
- skills: ["say_hello"]
97
- },
98
- payment: {
99
- amount: 0.001, // 0.001 ETH
100
- currency: "ETH",
101
- recipientAddress: "0x0000000000000000000000000000000000000000" // Update this!
102
- }
103
- });
104
-
105
- agent.onTask("say_hello", async (input) => {
106
- return { message: \`Hello \${input.name || "World"}!\` };
415
+ name: "${agentName}",
416
+ description: "${description}",
417
+ skills: ${JSON.stringify(skillList)}
418
+ },${paymentConfig}
107
419
  });
420
+ ${taskHandlers}
108
421
 
109
- await agent.start();
422
+ await agent.start();${registrationCall}
110
423
  }
111
-
424
+ ${registrationBlock}
112
425
  main().catch(console.error);
113
426
  `;
114
427
  fs.writeFileSync(path.join(targetDir, 'index.ts'), indexTs);
115
428
 
116
- console.log(kleur.green('Success! Project created.'));
117
- console.log('\nNext steps:');
118
- console.log(` cd ${projectName}`);
119
- console.log(' pnpm install');
120
- console.log(' # Update .env with your mnemonic');
121
- console.log(' pnpm start\n');
429
+ // 6. README.md
430
+ const readmeLines = [
431
+ `# ${agentName}`,
432
+ ``,
433
+ description,
434
+ ``,
435
+ `## Getting Started`,
436
+ ``,
437
+ '```bash',
438
+ `cd ${dirName}`,
439
+ `npm install`,
440
+ '```',
441
+ ``,
442
+ `### Configuration`,
443
+ ``,
444
+ `Edit \`.env\` and set your 12-word mnemonic phrase.`,
445
+ includeRegistration ? `\nFor on-chain registration, also set \`REGISTRATION_PRIVATE_KEY\` and \`PINATA_JWT\`.\n` : '',
446
+ `### Running`,
447
+ ``,
448
+ '```bash',
449
+ `# Development mode with hot-reload`,
450
+ `npm run dev`,
451
+ ``,
452
+ `# Production`,
453
+ `npm start`,
454
+ '```',
455
+ ``,
456
+ `## Agent Details`,
457
+ ``,
458
+ `| Property | Value |`,
459
+ `|----------|-------|`,
460
+ `| **Name** | ${agentName} |`,
461
+ `| **Skills** | ${skillList.join(', ')} |`,
462
+ `| **Payments** | ${includePayments ? 'Enabled (x402)' : 'Disabled'} |`,
463
+ `| **Registration** | ${includeRegistration ? 'Included' : 'Not included'} |`,
464
+ ``,
465
+ `## Environment Variables`,
466
+ ``,
467
+ `| Variable | Required | Description |`,
468
+ `|----------|----------|-------------|`,
469
+ '| `MNEMONIC` | Yes | Agent wallet seed phrase |',
470
+ includeRegistration ? '| `REGISTRATION_PRIVATE_KEY` | For registration | Wallet paying gas |\n| `PINATA_JWT` | For registration | IPFS metadata upload |' : '',
471
+ ``,
472
+ `## Resources`,
473
+ ``,
474
+ `- [SDK Docs](https://www.npmjs.com/package/@openagentmarket/nodejs)`,
475
+ `- [Explorer](https://8004agents.ai)`,
476
+ `- [GitHub](https://github.com/openagentmarket)`,
477
+ ].join('\n');
478
+ fs.writeFileSync(path.join(targetDir, 'README.md'), readmeLines);
479
+
480
+ // Print success
481
+ console.log(kleur.green('āœ… Agent project created!\n'));
482
+ console.log(kleur.bold('Next steps:'));
483
+ console.log(` cd ${dirName}`);
484
+ console.log(' npm install');
485
+ console.log(' # Set your MNEMONIC in .env');
486
+ console.log(' npm start\n');
487
+ }
488
+
489
+ // ── Shared helpers ───────────────────────────────────────────────────────────
490
+
491
+ function writeSharedTsConfig(targetDir: string) {
492
+ const tsConfig = {
493
+ compilerOptions: {
494
+ target: "ES2022",
495
+ module: "NodeNext",
496
+ moduleResolution: "NodeNext",
497
+ strict: true,
498
+ esModuleInterop: true,
499
+ skipLibCheck: true,
500
+ forceConsistentCasingInFileNames: true
501
+ }
502
+ };
503
+ fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
122
504
  }
123
505
 
124
506
  main().catch((err) => {