create-polyclaw-agent 0.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.
@@ -0,0 +1,543 @@
1
+ import { writeFileSync, mkdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ /**
4
+ * Convert agent name to kebab-case directory name
5
+ */
6
+ export function nameToDir(name) {
7
+ return name.toLowerCase().replace(/\s+/g, '-');
8
+ }
9
+ export function scaffoldProject(options) {
10
+ const projectDir = join(process.cwd(), options.dirName);
11
+ mkdirSync(projectDir, { recursive: true });
12
+ // Write .env file with credentials
13
+ const aiKeyLine = options.aiProvider === 'anthropic'
14
+ ? `\nANTHROPIC_API_KEY=${options.aiApiKey || 'your-anthropic-api-key'}\n`
15
+ : options.aiProvider === 'openai'
16
+ ? `\nOPENAI_API_KEY=${options.aiApiKey || 'your-openai-api-key'}\n`
17
+ : '';
18
+ const envContent = `# Polyclaw Agent: ${options.name}
19
+ # Generated by create-polyclaw-agent
20
+
21
+ POLYCLAW_API_KEY=${options.apiKey}
22
+ POLYCLAW_API_SECRET=${options.apiSecret}
23
+ POLYCLAW_API_URL=https://api.polyclaw.co
24
+ ${aiKeyLine}`;
25
+ writeFileSync(join(projectDir, '.env'), envContent);
26
+ if (options.language === 'python') {
27
+ scaffoldPython(projectDir, options);
28
+ }
29
+ else {
30
+ scaffoldTypeScript(projectDir, options);
31
+ }
32
+ return projectDir;
33
+ }
34
+ function scaffoldPython(dir, options) {
35
+ // AI-specific imports and analysis function
36
+ const aiImport = options.aiProvider === 'anthropic'
37
+ ? `import anthropic`
38
+ : options.aiProvider === 'openai'
39
+ ? `import openai`
40
+ : '';
41
+ const aiKeyEnv = options.aiProvider === 'anthropic'
42
+ ? `AI_API_KEY = os.getenv("ANTHROPIC_API_KEY")`
43
+ : options.aiProvider === 'openai'
44
+ ? `AI_API_KEY = os.getenv("OPENAI_API_KEY")`
45
+ : '';
46
+ const aiAnalyzeFunc = options.aiProvider === 'anthropic' ? `
47
+ def analyze_markets(markets):
48
+ """Use Claude to analyze prediction markets and pick the best bets."""
49
+ client = anthropic.Anthropic(api_key=AI_API_KEY)
50
+
51
+ # Format markets for the AI
52
+ market_summaries = []
53
+ for i, m in enumerate(markets):
54
+ prices = m.get("outcomePrices", [0.5, 0.5])
55
+ yes_price = float(prices[0]) if prices else 0.5
56
+ no_price = float(prices[1]) if len(prices) > 1 else 1 - yes_price
57
+ market_summaries.append(
58
+ f"{i+1}. {m.get('question', 'Unknown')}\\n"
59
+ f" Yes: {yes_price:.0%} | No: {no_price:.0%} | "
60
+ f"Volume: \${float(m.get('volume', 0)):,.0f} | "
61
+ f"ID: {m.get('id', '')}"
62
+ )
63
+
64
+ markets_text = "\\n".join(market_summaries)
65
+
66
+ response = client.messages.create(
67
+ model="claude-sonnet-4-20250514",
68
+ max_tokens=1024,
69
+ messages=[{
70
+ "role": "user",
71
+ "content": f"""You are a prediction market analyst. Analyze these Polymarket markets and pick up to 3 best betting opportunities.
72
+
73
+ Markets:
74
+ {markets_text}
75
+
76
+ For each pick, respond in this exact JSON format (no markdown, just raw JSON array):
77
+ [
78
+ {{
79
+ "market_index": 1,
80
+ "outcome": "Yes" or "No",
81
+ "side": "BUY",
82
+ "confidence": 0.0 to 1.0,
83
+ "recommended_amount": 5 to 50,
84
+ "reasoning": "One sentence explaining why this is a good bet"
85
+ }}
86
+ ]
87
+
88
+ Rules:
89
+ - Pick 1-3 markets that look like good opportunities
90
+ - Consider the current price, volume, and whether the market seems mispriced
91
+ - Be diverse -- don't pick markets that are all about the same topic
92
+ - Higher confidence = stronger conviction (0.5 = coin flip, 0.9 = very confident)
93
+ - Recommended amount should reflect your confidence (higher confidence = more)
94
+ """
95
+ }]
96
+ )
97
+
98
+ # Parse the AI response
99
+ try:
100
+ picks = json.loads(response.content[0].text)
101
+ model_name = response.model
102
+ return picks, model_name
103
+ except (json.JSONDecodeError, IndexError):
104
+ print(f" AI response: {response.content[0].text}")
105
+ return [], response.model
106
+ ` : options.aiProvider === 'openai' ? `
107
+ def analyze_markets(markets):
108
+ """Use GPT to analyze prediction markets and pick the best bets."""
109
+ client = openai.OpenAI(api_key=AI_API_KEY)
110
+
111
+ # Format markets for the AI
112
+ market_summaries = []
113
+ for i, m in enumerate(markets):
114
+ prices = m.get("outcomePrices", [0.5, 0.5])
115
+ yes_price = float(prices[0]) if prices else 0.5
116
+ no_price = float(prices[1]) if len(prices) > 1 else 1 - yes_price
117
+ market_summaries.append(
118
+ f"{i+1}. {m.get('question', 'Unknown')}\\n"
119
+ f" Yes: {yes_price:.0%} | No: {no_price:.0%} | "
120
+ f"Volume: \${float(m.get('volume', 0)):,.0f} | "
121
+ f"ID: {m.get('id', '')}"
122
+ )
123
+
124
+ markets_text = "\\n".join(market_summaries)
125
+
126
+ response = client.chat.completions.create(
127
+ model="gpt-4o",
128
+ messages=[{
129
+ "role": "user",
130
+ "content": f"""You are a prediction market analyst. Analyze these Polymarket markets and pick up to 3 best betting opportunities.
131
+
132
+ Markets:
133
+ {markets_text}
134
+
135
+ For each pick, respond in this exact JSON format (no markdown, just raw JSON array):
136
+ [
137
+ {{
138
+ "market_index": 1,
139
+ "outcome": "Yes" or "No",
140
+ "side": "BUY",
141
+ "confidence": 0.0 to 1.0,
142
+ "recommended_amount": 5 to 50,
143
+ "reasoning": "One sentence explaining why this is a good bet"
144
+ }}
145
+ ]
146
+
147
+ Rules:
148
+ - Pick 1-3 markets that look like good opportunities
149
+ - Consider the current price, volume, and whether the market seems mispriced
150
+ - Be diverse -- don't pick markets that are all about the same topic
151
+ - Higher confidence = stronger conviction (0.5 = coin flip, 0.9 = very confident)
152
+ - Recommended amount should reflect your confidence (higher confidence = more)
153
+ """
154
+ }],
155
+ )
156
+
157
+ # Parse the AI response
158
+ try:
159
+ picks = json.loads(response.choices[0].message.content)
160
+ model_name = response.model
161
+ return picks, model_name
162
+ except (json.JSONDecodeError, IndexError):
163
+ print(f" AI response: {response.choices[0].message.content}")
164
+ return [], response.model
165
+ ` : `
166
+ def analyze_markets(markets):
167
+ """Placeholder: Add your own AI logic here to analyze markets."""
168
+ # This is where you connect your AI model.
169
+ # Return a list of picks in this format:
170
+ # [{"market_index": 1, "outcome": "Yes", "side": "BUY",
171
+ # "confidence": 0.7, "recommended_amount": 10,
172
+ # "reasoning": "Your analysis here"}]
173
+ #
174
+ # Example with OpenAI:
175
+ # client = openai.OpenAI()
176
+ # response = client.chat.completions.create(model="gpt-4o", ...)
177
+ #
178
+ # Example with Anthropic:
179
+ # client = anthropic.Anthropic()
180
+ # response = client.messages.create(model="claude-sonnet-4-20250514", ...)
181
+
182
+ print(" No AI configured. Add your API key to .env and update bot.py")
183
+ print(" See https://polyclaw.co/docs for examples.")
184
+ return [], "none"
185
+ `;
186
+ const botPy = `"""
187
+ ${options.name} - Polyclaw AI Trading Agent
188
+
189
+ This bot connects to Polyclaw, fetches prediction markets from Polymarket,
190
+ uses AI to analyze them, and submits trade proposals for you to review.
191
+
192
+ Usage:
193
+ python3 -m venv venv && source venv/bin/activate
194
+ pip install -r requirements.txt
195
+ python bot.py
196
+ """
197
+
198
+ import os
199
+ import json
200
+ import time
201
+ import hmac
202
+ import hashlib
203
+ import requests
204
+ from dotenv import load_dotenv
205
+ ${aiImport}
206
+
207
+ load_dotenv()
208
+
209
+ API_KEY = os.getenv("POLYCLAW_API_KEY")
210
+ API_SECRET = os.getenv("POLYCLAW_API_SECRET")
211
+ BASE_URL = os.getenv("POLYCLAW_API_URL", "https://api.polyclaw.co")
212
+ ${aiKeyEnv}
213
+
214
+
215
+ class PolyclawClient:
216
+ def __init__(self, api_key: str, api_secret: str, base_url: str):
217
+ self.api_key = api_key
218
+ self.base_url = base_url
219
+ self.signing_key = hmac.new(
220
+ b"polyclaw-secret-salt", api_secret.encode(), hashlib.sha256
221
+ ).hexdigest()
222
+
223
+ def _sign(self, timestamp: str, method: str, path: str, body: str = "") -> str:
224
+ message = f"{timestamp}{method.upper()}{path}{body}"
225
+ return hmac.new(
226
+ self.signing_key.encode(), message.encode(), hashlib.sha256
227
+ ).hexdigest()
228
+
229
+ def _request(self, method: str, path: str, body: dict = None):
230
+ timestamp = str(int(time.time() * 1000))
231
+ body_str = json.dumps(body) if body else ""
232
+ signature = self._sign(timestamp, method, path, body_str)
233
+ headers = {
234
+ "Content-Type": "application/json",
235
+ "X-Polyclaw-Api-Key": self.api_key,
236
+ "X-Polyclaw-Signature": signature,
237
+ "X-Polyclaw-Timestamp": timestamp,
238
+ }
239
+ url = f"{self.base_url}{path}"
240
+ response = requests.request(method, url, headers=headers, json=body)
241
+ return response.json()
242
+
243
+ def get_markets(self, page: int = 1, limit: int = 20):
244
+ return self._request("GET", f"/api/markets?page={page}&limit={limit}")
245
+
246
+ def submit_proposal(self, market_id, outcome, side, recommended_amount, confidence, reasoning=None, model=None):
247
+ body = {"marketId": market_id, "outcome": outcome, "side": side,
248
+ "recommendedAmount": recommended_amount, "confidence": confidence}
249
+ if reasoning: body["reasoning"] = reasoning
250
+ if model: body["model"] = model
251
+ return self._request("POST", "/api/proposals", body)
252
+
253
+ ${aiAnalyzeFunc}
254
+
255
+ def main():
256
+ if not API_KEY or not API_SECRET:
257
+ print("Error: POLYCLAW_API_KEY and POLYCLAW_API_SECRET must be set in .env")
258
+ return
259
+ ${options.aiProvider !== 'none' ? `
260
+ ai_key_name = "${options.aiProvider === 'anthropic' ? 'ANTHROPIC_API_KEY' : 'OPENAI_API_KEY'}"
261
+ if not AI_API_KEY:
262
+ print(f"Error: {ai_key_name} must be set in .env")
263
+ return
264
+ ` : ''}
265
+ client = PolyclawClient(API_KEY, API_SECRET, BASE_URL)
266
+ print(f"${options.name} is online!")
267
+ print()
268
+
269
+ # Fetch markets
270
+ print("Fetching markets from Polymarket...")
271
+ result = client.get_markets(limit=20)
272
+
273
+ if not result.get("success"):
274
+ print(f"Error: {result.get('error', 'Unknown error')}")
275
+ return
276
+
277
+ markets = result.get("data", [])
278
+ print(f"Found {len(markets)} markets")
279
+ print()
280
+
281
+ # Ask AI to analyze and pick the best bets
282
+ print("Asking AI to analyze markets...\\n")
283
+ picks, model_name = analyze_markets(markets)
284
+
285
+ if not picks:
286
+ print("No picks from AI. Try again later.")
287
+ return
288
+
289
+ print(f" AI picked {len(picks)} market(s) (model: {model_name})\\n")
290
+
291
+ # Submit each pick as a proposal
292
+ for pick in picks:
293
+ idx = pick.get("market_index", 1) - 1
294
+ if idx < 0 or idx >= len(markets):
295
+ continue
296
+
297
+ market = markets[idx]
298
+ print(f" -> {market.get('question', 'Unknown')}")
299
+ print(f" {pick['side']} {pick['outcome']} | Confidence: {pick['confidence']:.0%} | Amount: \${pick['recommended_amount']}")
300
+ print(f" Reasoning: {pick.get('reasoning', 'N/A')}")
301
+
302
+ proposal = client.submit_proposal(
303
+ market_id=market["id"],
304
+ outcome=pick["outcome"],
305
+ side=pick["side"],
306
+ recommended_amount=pick["recommended_amount"],
307
+ confidence=pick["confidence"],
308
+ reasoning=pick.get("reasoning"),
309
+ model=model_name,
310
+ )
311
+
312
+ if proposal.get("success"):
313
+ print(f" Submitted!\\n")
314
+ else:
315
+ print(f" Error: {proposal.get('error', 'Unknown')}\\n")
316
+
317
+ print("Done! Check your dashboard to review proposals.")
318
+
319
+
320
+ if __name__ == "__main__":
321
+ main()
322
+ `;
323
+ const aiDep = options.aiProvider === 'anthropic'
324
+ ? 'anthropic>=0.40.0\n'
325
+ : options.aiProvider === 'openai'
326
+ ? 'openai>=1.50.0\n'
327
+ : '';
328
+ const requirements = `requests>=2.31.0
329
+ python-dotenv>=1.0.0
330
+ ${aiDep}`;
331
+ const gitignore = `.env
332
+ __pycache__/
333
+ *.pyc
334
+ .venv/
335
+ `;
336
+ writeFileSync(join(dir, 'bot.py'), botPy);
337
+ writeFileSync(join(dir, 'requirements.txt'), requirements);
338
+ writeFileSync(join(dir, '.gitignore'), gitignore);
339
+ }
340
+ function scaffoldTypeScript(dir, options) {
341
+ const botTs = `/**
342
+ * ${options.name} - Polyclaw AI Trading Agent
343
+ *
344
+ * This bot connects to Polyclaw to browse prediction markets and submit
345
+ * trade proposals. A human reviews and approves proposals before execution.
346
+ *
347
+ * Usage:
348
+ * npm install
349
+ * npx tsx bot.ts
350
+ */
351
+
352
+ import { createHmac } from 'crypto';
353
+ import 'dotenv/config';
354
+
355
+ const API_KEY = process.env.POLYCLAW_API_KEY!;
356
+ const API_SECRET = process.env.POLYCLAW_API_SECRET!;
357
+ const BASE_URL = process.env.POLYCLAW_API_URL || 'https://api.polyclaw.co';
358
+
359
+ class PolyclawClient {
360
+ private apiKey: string;
361
+ private signingKey: string;
362
+ private baseUrl: string;
363
+
364
+ constructor(config: { apiKey: string; apiSecret: string; baseUrl: string }) {
365
+ this.apiKey = config.apiKey;
366
+ this.baseUrl = config.baseUrl;
367
+ this.signingKey = createHmac('sha256', 'polyclaw-secret-salt')
368
+ .update(config.apiSecret)
369
+ .digest('hex');
370
+ }
371
+
372
+ private sign(timestamp: string, method: string, path: string, body?: string): string {
373
+ const message = \`\${timestamp}\${method.toUpperCase()}\${path}\${body || ''}\`;
374
+ return createHmac('sha256', this.signingKey).update(message).digest('hex');
375
+ }
376
+
377
+ private async request<T>(method: string, path: string, body?: object): Promise<T> {
378
+ const timestamp = Date.now().toString();
379
+ const bodyStr = body ? JSON.stringify(body) : '';
380
+ const signature = this.sign(timestamp, method, path, bodyStr);
381
+ const response = await fetch(\`\${this.baseUrl}\${path}\`, {
382
+ method,
383
+ headers: {
384
+ 'Content-Type': 'application/json',
385
+ 'X-Polyclaw-Api-Key': this.apiKey,
386
+ 'X-Polyclaw-Signature': signature,
387
+ 'X-Polyclaw-Timestamp': timestamp,
388
+ },
389
+ body: body ? bodyStr : undefined,
390
+ });
391
+ return response.json() as T;
392
+ }
393
+
394
+ async getMarkets(page = 1, limit = 20) {
395
+ return this.request<any>('GET', \`/api/markets?page=\${page}&limit=\${limit}\`);
396
+ }
397
+
398
+ async submitProposal(proposal: {
399
+ marketId: string;
400
+ outcome: string;
401
+ side: string;
402
+ recommendedAmount: number;
403
+ confidence: number;
404
+ reasoning?: string;
405
+ model?: string;
406
+ }) {
407
+ return this.request<any>('POST', '/api/proposals', proposal);
408
+ }
409
+ }
410
+
411
+ interface MarketPlay {
412
+ market: any;
413
+ outcome: string;
414
+ side: string;
415
+ confidence: number;
416
+ reasoning: string;
417
+ score: number;
418
+ }
419
+
420
+ function findBestPlay(markets: any[]): MarketPlay | null {
421
+ // Analyze markets and find the best opportunity.
422
+ // Strategy: Look for markets with strong price signals and high volume.
423
+ const scored: MarketPlay[] = [];
424
+
425
+ for (const market of markets) {
426
+ const yesPrice = parseFloat(market.outcomePrices?.[0] ?? '0.5');
427
+ const volume = parseFloat(market.volume ?? '0');
428
+
429
+ // Skip markets that are basically decided
430
+ if (yesPrice < 0.05 || yesPrice > 0.95) continue;
431
+
432
+ const edge = Math.abs(yesPrice - 0.5);
433
+ const liquidityScore = Math.min(volume / 500000, 1.0);
434
+ const score = edge * 0.6 + liquidityScore * 0.4;
435
+
436
+ let outcome: string, side: string, confidence: number, reasoning: string;
437
+
438
+ if (yesPrice < 0.40) {
439
+ outcome = 'Yes'; side = 'BUY';
440
+ confidence = Math.round((0.5 + edge) * 100) / 100;
441
+ reasoning = \`Yes is priced at \${(yesPrice * 100).toFixed(0)}% -- looks undervalued with $\${volume.toLocaleString()} volume\`;
442
+ } else if (yesPrice > 0.60) {
443
+ outcome = 'Yes'; side = 'BUY';
444
+ confidence = Math.round((0.5 + edge * 0.8) * 100) / 100;
445
+ reasoning = \`Market strongly favors Yes at \${(yesPrice * 100).toFixed(0)}% -- riding the momentum\`;
446
+ } else {
447
+ outcome = 'Yes'; side = 'BUY';
448
+ confidence = Math.round((0.5 + edge * 0.5) * 100) / 100;
449
+ reasoning = \`Market is contested at \${(yesPrice * 100).toFixed(0)}% -- taking a position\`;
450
+ }
451
+
452
+ scored.push({ market, outcome, side, confidence: Math.min(confidence, 0.95), reasoning, score });
453
+ }
454
+
455
+ scored.sort((a, b) => b.score - a.score);
456
+ return scored[0] || null;
457
+ }
458
+
459
+ async function main() {
460
+ if (!API_KEY || !API_SECRET) {
461
+ console.error('Error: POLYCLAW_API_KEY and POLYCLAW_API_SECRET must be set in .env');
462
+ return;
463
+ }
464
+
465
+ const client = new PolyclawClient({ apiKey: API_KEY, apiSecret: API_SECRET, baseUrl: BASE_URL });
466
+ console.log('${options.name} is online!');
467
+ console.log();
468
+
469
+ // Fetch markets
470
+ console.log('Scanning markets...');
471
+ const result = await client.getMarkets(1, 20);
472
+
473
+ if (!result.success) {
474
+ console.error(\`Error: \${result.error || 'Unknown error'}\`);
475
+ return;
476
+ }
477
+
478
+ const markets = result.data || [];
479
+ console.log(\`Analyzing \${markets.length} markets...\\n\`);
480
+
481
+ // Find the best play
482
+ const best = findBestPlay(markets);
483
+
484
+ if (!best) {
485
+ console.log('No good opportunities found right now.');
486
+ return;
487
+ }
488
+
489
+ const yesPrice = parseFloat(best.market.outcomePrices?.[0] ?? '0.5');
490
+ console.log(' Best opportunity found:');
491
+ console.log(\` \${best.market.question}\`);
492
+ console.log(\` Yes: \${(yesPrice * 100).toFixed(0)}% | Volume: $\${parseFloat(best.market.volume || '0').toLocaleString()}\`);
493
+ console.log(\` Play: \${best.side} \${best.outcome} | Confidence: \${(best.confidence * 100).toFixed(0)}%\`);
494
+ console.log(\` Reasoning: \${best.reasoning}\`);
495
+ console.log();
496
+
497
+ // Submit proposal
498
+ // Tip: Replace this logic with your own AI model for smarter predictions!
499
+ // const aiResponse = await openai.chat.completions.create({ model: 'gpt-4o', ... });
500
+ // const modelName = aiResponse.model; // e.g. "gpt-4o-2025-01-15"
501
+ console.log('Submitting proposal...');
502
+ const proposal = await client.submitProposal({
503
+ marketId: best.market.id,
504
+ outcome: best.outcome,
505
+ side: best.side,
506
+ recommendedAmount: 10.0,
507
+ confidence: best.confidence,
508
+ reasoning: best.reasoning,
509
+ model: 'your-model-here', // Replace with your AI model name
510
+ });
511
+
512
+ if (proposal.success) {
513
+ console.log('Proposal submitted! Check your dashboard to approve it.');
514
+ } else {
515
+ console.error(\`Proposal error: \${proposal.error || 'Unknown'}\`);
516
+ }
517
+ }
518
+
519
+ main();
520
+ `;
521
+ const packageJson = `{
522
+ "name": "${options.dirName}",
523
+ "version": "0.1.0",
524
+ "private": true,
525
+ "type": "module",
526
+ "scripts": {
527
+ "start": "npx tsx bot.ts"
528
+ },
529
+ "dependencies": {
530
+ "dotenv": "^16.4.0",
531
+ "tsx": "^4.7.0"
532
+ }
533
+ }
534
+ `;
535
+ const gitignore = `.env
536
+ node_modules/
537
+ dist/
538
+ `;
539
+ writeFileSync(join(dir, 'bot.ts'), botTs);
540
+ writeFileSync(join(dir, 'package.json'), packageJson);
541
+ writeFileSync(join(dir, '.gitignore'), gitignore);
542
+ }
543
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "create-polyclaw-agent",
3
+ "version": "0.1.0",
4
+ "description": "Create a Polyclaw AI trading agent in seconds",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-polyclaw-agent": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js"
14
+ },
15
+ "dependencies": {
16
+ "chalk": "^5.3.0",
17
+ "prompts": "^2.4.2"
18
+ },
19
+ "devDependencies": {
20
+ "@types/prompts": "^2.4.9",
21
+ "typescript": "^5.4.0"
22
+ },
23
+ "keywords": ["polyclaw", "polymarket", "ai-agent", "prediction-markets"],
24
+ "license": "MIT"
25
+ }
package/src/api.ts ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * API client for the Polyclaw registration flow.
3
+ * Calls the Polyclaw API to generate names and register agents.
4
+ */
5
+
6
+ const DEFAULT_API_URL = 'https://api.polyclaw.co';
7
+
8
+ function getApiUrl(): string {
9
+ return process.env.POLYCLAW_API_URL || DEFAULT_API_URL;
10
+ }
11
+
12
+ interface ApiResponse<T> {
13
+ success: boolean;
14
+ data?: T;
15
+ error?: string;
16
+ message?: string;
17
+ }
18
+
19
+ export async function generateName(): Promise<string> {
20
+ const url = `${getApiUrl()}/api/agents/generate-name`;
21
+ const res = await fetch(url);
22
+ const json = (await res.json()) as ApiResponse<{ name: string }>;
23
+
24
+ if (!json.success || !json.data) {
25
+ throw new Error(json.message || json.error || 'Failed to generate name');
26
+ }
27
+
28
+ return json.data.name;
29
+ }
30
+
31
+ export interface RegisterResult {
32
+ apiKey: string;
33
+ apiSecret: string;
34
+ linkToken: string;
35
+ }
36
+
37
+ export async function registerAgent(name: string, options?: { ownerName?: string }): Promise<RegisterResult> {
38
+ const url = `${getApiUrl()}/api/agents/register`;
39
+ const body: Record<string, string> = { name };
40
+ if (options?.ownerName) {
41
+ body.ownerName = options.ownerName;
42
+ }
43
+ const res = await fetch(url, {
44
+ method: 'POST',
45
+ headers: { 'Content-Type': 'application/json' },
46
+ body: JSON.stringify(body),
47
+ });
48
+
49
+ const json = (await res.json()) as ApiResponse<RegisterResult>;
50
+
51
+ if (!json.success || !json.data) {
52
+ throw new Error(json.message || json.error || 'Registration failed');
53
+ }
54
+
55
+ return json.data;
56
+ }
57
+
58
+ export function getLinkUrl(linkToken: string): string {
59
+ const webUrl = process.env.POLYCLAW_WEB_URL || 'https://polyclaw.co';
60
+ return `${webUrl}/link/${linkToken}`;
61
+ }