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