@blindmarket/sdk 0.1.5 → 0.1.7

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/src/index.ts CHANGED
@@ -1,11 +1,24 @@
1
- import { Wallet, ethers } from 'ethers';
1
+ import { ethers } from 'ethers';
2
+ import type {
3
+ Address, Hex, HealthStatus, PlatformStats, OpenTask, TaskDetail,
4
+ CreateTaskTx, ExecutorProfile, RegisterExecutorInput,
5
+ DeployedAgentInfo, AgentWalletInfo, ReputationInfo, LeaderboardEntry,
6
+ StorageUploadResult, Message, AgentSearchResult, TaskTemplate,
7
+ VerifyTaskInput, A2ATaskState, AgentCapability,
8
+ CreateAgentParams, CreateAgentResult,
9
+ } from './types.js';
10
+
11
+ // ── Public config ───────────────────────────────────────────────────────────
2
12
 
3
13
  export interface BlindMarketConfig {
14
+ /** Backend API base URL (default: https://api.blindmarket.xyz) */
4
15
  apiBase?: string;
16
+ /** API key — shared AGENT_API_KEY or device-flow token */
5
17
  apiKey: string;
6
- rpcUrl?: string;
7
18
  }
8
19
 
20
+ // ── Agent deployment params ─────────────────────────────────────────────────
21
+
9
22
  export interface DeployAgentParams {
10
23
  name: string;
11
24
  instructions: string;
@@ -27,35 +40,176 @@ export interface DeployedAgent {
27
40
  status: string;
28
41
  }
29
42
 
43
+ // ── Helpers ─────────────────────────────────────────────────────────────────
44
+
45
+ class ApiError extends Error {
46
+ constructor(
47
+ public status: number,
48
+ message: string,
49
+ public body?: unknown,
50
+ ) {
51
+ super(message);
52
+ this.name = 'ApiError';
53
+ }
54
+ }
55
+
56
+ // ── Main client ─────────────────────────────────────────────────────────────
57
+
58
+ /**
59
+ * BlindMarket REST API client.
60
+ *
61
+ * Two usage modes:
62
+ * 1. **High-level (this class)** — talks to the BlindMarket backend over REST.
63
+ * Covers the full task lifecycle, agent management, A2A, and marketplace.
64
+ * 2. **Low-level primitives** — `Agent`, `Worker`, `PrivateKeySigner`, etc.
65
+ * for on-chain + crypto operations when you need direct chain access.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * const bb = new BlindMarket({ apiKey: process.env.BLINDMARKET_API_KEY! });
70
+ *
71
+ * // Deploy an agent
72
+ * const agent = await bb.deployAgent({ name: 'my-agent', ... });
73
+ *
74
+ * // Watch for status changes
75
+ * const unsub = bb.watchTask(42, (state) => console.log(state.status));
76
+ * ```
77
+ */
30
78
  export class BlindMarket {
31
79
  private apiBase: string;
32
80
  private apiKey: string;
33
81
 
34
82
  constructor(config: BlindMarketConfig) {
35
- this.apiBase = config.apiBase ?? 'http://localhost:3001';
83
+ this.apiBase = config.apiBase ?? 'https://api.blindmarket.xyz';
36
84
  this.apiKey = config.apiKey;
37
85
  }
38
86
 
87
+ // ── Tools ─────────────────────────────────────────────────────────────────
88
+
89
+ /**
90
+ * Tool definitions for AI agent frameworks. Access framework-specific formats
91
+ * via property — no need to remember adapter function names.
92
+ *
93
+ * IMPORTANT: due to ESM circular-dependency constraints this is a standalone
94
+ * function rather than an instance getter. You pass `bb` once and reach the
95
+ * format you need.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * import { BlindMarket, tools } from '@blindmarket/sdk';
100
+ * const bb = new BlindMarket({ apiKey });
101
+ *
102
+ * // OpenAI (default — also works with Vercel AI SDK)
103
+ * openai.chat.completions.create({ model: 'gpt-4', tools: tools(bb).definitions });
104
+ *
105
+ * // LangChain
106
+ * createReactAgent({ llm, tools: tools(bb).langchain });
107
+ *
108
+ * // Claude
109
+ * anthropic.messages.create({ model, tools: tools(bb).claude });
110
+ *
111
+ * // Vercel
112
+ * generateText({ model, tools: tools(bb).vercel });
113
+ * ```
114
+ */
115
+
39
116
  private async req<T>(method: string, path: string, body?: unknown): Promise<T> {
40
117
  const res = await fetch(`${this.apiBase}${path}`, {
41
118
  method,
42
- headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.apiKey}` },
119
+ headers: {
120
+ 'Content-Type': 'application/json',
121
+ Authorization: `Bearer ${this.apiKey}`,
122
+ },
43
123
  body: body ? JSON.stringify(body) : undefined,
44
124
  });
45
125
  const json = await res.json() as { success: boolean; data?: T; error?: { message: string } };
46
- if (!json.success) throw new Error(json.error?.message ?? `HTTP ${res.status}`);
126
+ if (!json.success) {
127
+ throw new ApiError(res.status, json.error?.message ?? `HTTP ${res.status}`, json);
128
+ }
47
129
  return json.data as T;
48
130
  }
49
131
 
50
- // ── Agent deployment ──────────────────────────────────────────────────────
132
+ // ── Health & Stats ──────────────────────────────────────────────────────
133
+
134
+ /** Backend liveness check. */
135
+ async health(): Promise<HealthStatus> {
136
+ return this.req<HealthStatus>('GET', '/health');
137
+ }
138
+
139
+ /** Live platform counts. */
140
+ async stats(): Promise<PlatformStats> {
141
+ return this.req<PlatformStats>('GET', '/api/v1/stats');
142
+ }
143
+
144
+ // ── Task lifecycle ──────────────────────────────────────────────────────
145
+
146
+ /** List open tasks (human-readable). */
147
+ async listTasks(limit = 20): Promise<OpenTask[]> {
148
+ const { tasks } = await this.req<{ tasks: OpenTask[] }>('GET', `/api/v1/tasks?limit=${limit}`);
149
+ return tasks;
150
+ }
151
+
152
+ /** Get full task details (on-chain + A2A state). */
153
+ async getTask(id: string): Promise<TaskDetail> {
154
+ return this.req<TaskDetail>('GET', `/api/v1/tasks/${id}`);
155
+ }
156
+
157
+ /**
158
+ * Build an unsigned `createTask` transaction.
159
+ * You must sign and broadcast it with your wallet.
160
+ */
161
+ async createTask(params: {
162
+ agent: Address;
163
+ amount: string;
164
+ token: Address;
165
+ category: string;
166
+ locationZone: string;
167
+ deadline: number;
168
+ }): Promise<CreateTaskTx> {
169
+ return this.req<CreateTaskTx>('POST', '/api/v1/tasks', params);
170
+ }
171
+
172
+ /**
173
+ * Build an unsigned `assignWorker` transaction.
174
+ */
175
+ async assignWorker(taskId: string, worker: Address): Promise<{ unsignedTx: object }> {
176
+ return this.req('POST', `/api/v1/tasks/${taskId}/assign`, { worker });
177
+ }
178
+
179
+ /**
180
+ * Build an unsigned `cancelTask` transaction.
181
+ */
182
+ async cancelTask(taskId: string): Promise<{ unsignedTx: object }> {
183
+ return this.req('POST', `/api/v1/tasks/${taskId}/cancel`);
184
+ }
185
+
186
+ /**
187
+ * Build an unsigned `claimTimeout` transaction.
188
+ */
189
+ async claimTimeout(taskId: string): Promise<{ unsignedTx: object }> {
190
+ return this.req('POST', `/api/v1/tasks/${taskId}/timeout`);
191
+ }
192
+
193
+ /**
194
+ * Build an unsigned `submitEvidence` transaction.
195
+ */
196
+ async submitEvidence(params: {
197
+ taskId: string;
198
+ evidenceHash: Hex;
199
+ }): Promise<{ unsignedTx: object }> {
200
+ return this.req('POST', '/api/v1/submissions/submit', params);
201
+ }
202
+
203
+ // ── Agent deployment & management ────────────────────────────────────────
51
204
 
52
205
  /**
53
- * Deploy a new agent. Generates a wallet, mints an INFT, returns the agent.
206
+ * Deploy a new agent. The backend generates a wallet, mints an INFT,
207
+ * and returns the agent descriptor.
54
208
  *
55
209
  * @example
56
210
  * const agent = await bb.deployAgent({
57
211
  * name: 'research-agent',
58
- * instructions: 'You research topics and post tasks for humans to verify.',
212
+ * instructions: 'You research topics and post tasks.',
59
213
  * provider: 'anthropic',
60
214
  * model: 'claude-sonnet-4-5',
61
215
  * apiKey: process.env.ANTHROPIC_API_KEY!,
@@ -68,81 +222,396 @@ export class BlindMarket {
68
222
  }
69
223
 
70
224
  /**
71
- * List all agents owned by an address.
225
+ * One-shot agent creation: generates a secp256k1 wallet, then registers the
226
+ * agent as an executor in the A2A marketplace with the generated wallet
227
+ * address and public key. Replaces the manual two-step flow of generating a
228
+ * wallet, then calling registerExecutor().
229
+ *
230
+ * The private key is returned **once** in the response — store it securely.
231
+ *
232
+ * @example
233
+ * const { executor, wallet } = await bb.createAgent({
234
+ * displayName: 'DataBot',
235
+ * capabilities: [AgentCap.DATA_PROCESSING, AgentCap.WEB_RESEARCH],
236
+ * minReward: '1000000000000000000', // 1 0G in wei
237
+ * });
238
+ * console.log(`Agent ${wallet.address} registered as ${executor.address}`);
72
239
  */
73
- async listAgents(ownerAddress: string): Promise<DeployedAgent[]> {
74
- return this.req<DeployedAgent[]>('GET', `/api/v1/agents?owner=${ownerAddress}`);
240
+ async createAgent(params: CreateAgentParams): Promise<CreateAgentResult> {
241
+ const wallet = ethers.Wallet.createRandom();
242
+ const executor: RegisterExecutorInput = {
243
+ address: wallet.address as Address,
244
+ displayName: params.displayName,
245
+ capabilities: params.capabilities,
246
+ publicKey: wallet.publicKey.slice(2), // strip 0x prefix — backend expects raw hex
247
+ agentCardUrl: params.agentCardUrl,
248
+ mcpEndpointUrl: params.mcpEndpointUrl,
249
+ minReward: params.minReward,
250
+ preferredCapabilities: params.preferredCapabilities,
251
+ };
252
+ const result = await this.registerExecutor(executor);
253
+ return {
254
+ executor: result.agent,
255
+ wallet: {
256
+ address: wallet.address as Address,
257
+ publicKey: wallet.publicKey,
258
+ privateKey: wallet.privateKey,
259
+ },
260
+ };
75
261
  }
76
262
 
77
- // ── Task lifecycle ────────────────────────────────────────────────────────
263
+ /** List deployed agents, optionally filtered by owner address. */
264
+ async listAgents(ownerAddress?: string): Promise<DeployedAgentInfo[]> {
265
+ const qs = ownerAddress ? `?owner=${ownerAddress}` : '';
266
+ return this.req<DeployedAgentInfo[]>('GET', `/api/v1/agents${qs}`);
267
+ }
268
+
269
+ /** Get a single deployed agent by ID. */
270
+ async getAgent(id: string): Promise<DeployedAgentInfo> {
271
+ return this.req<DeployedAgentInfo>('GET', `/api/v1/agents/${id}`);
272
+ }
273
+
274
+ /** Get an agent's on-chain wallet address. */
275
+ async getAgentWallet(id: string): Promise<AgentWalletInfo> {
276
+ return this.req<AgentWalletInfo>('GET', `/api/v1/agents/${id}/wallet`);
277
+ }
278
+
279
+ /** Start a deployed agent. Requires owner auth. */
280
+ async startAgent(id: string): Promise<DeployedAgentInfo> {
281
+ return this.req<DeployedAgentInfo>('POST', `/api/v1/agents/${id}/start`);
282
+ }
283
+
284
+ /** Stop a deployed agent. Requires owner auth. */
285
+ async stopAgent(id: string): Promise<DeployedAgentInfo> {
286
+ return this.req<DeployedAgentInfo>('POST', `/api/v1/agents/${id}/stop`);
287
+ }
288
+
289
+ /** Pause a deployed agent. Requires owner auth. */
290
+ async pauseAgent(id: string): Promise<DeployedAgentInfo> {
291
+ return this.req<DeployedAgentInfo>('POST', `/api/v1/agents/${id}/pause`);
292
+ }
293
+
294
+ /** Restart a deployed agent. Requires owner auth. */
295
+ async restartAgent(id: string): Promise<DeployedAgentInfo> {
296
+ return this.req<DeployedAgentInfo>('POST', `/api/v1/agents/${id}/restart`);
297
+ }
78
298
 
79
299
  /**
80
- * Assign a worker to a task. Returns an unsigned tx.
300
+ * Update a deployed agent's config (instructions, model, tools, etc.).
301
+ * Requires owner auth.
81
302
  */
82
- async assignWorker(taskId: string, worker: string): Promise<{ unsignedTx: object }> {
83
- return this.req('POST', `/api/v1/tasks/${taskId}/assign`, { worker });
303
+ async updateAgent(id: string, patch: Partial<{
304
+ instructions: string;
305
+ model: string;
306
+ capabilities: string[];
307
+ tools: object[];
308
+ minReward: string;
309
+ }>): Promise<DeployedAgentInfo> {
310
+ return this.req<DeployedAgentInfo>('PATCH', `/api/v1/agents/${id}`, patch);
311
+ }
312
+
313
+ // ── A2A executor registration ───────────────────────────────────────────
314
+
315
+ /** Register as an A2A agent executor (worker-side). */
316
+ async registerExecutor(params: RegisterExecutorInput): Promise<{ agent: ExecutorProfile }> {
317
+ return this.req('POST', '/api/v1/a2a/register', params);
318
+ }
319
+
320
+ /** List registered A2A executors, optionally filtered by capability. */
321
+ async listExecutors(capabilities?: string[]): Promise<{ executors: ExecutorProfile[] }> {
322
+ const qs = capabilities?.length ? `?capabilities=${capabilities.join(',')}` : '';
323
+ return this.req('GET', `/api/v1/a2a/executors${qs}`);
324
+ }
325
+
326
+ /** Get own executor profile with on-chain + decayed reputation. */
327
+ async getExecutorProfile(): Promise<{ agent: ExecutorProfile }> {
328
+ return this.req('GET', '/api/v1/a2a/profile');
329
+ }
330
+
331
+ // ── A2A task lifecycle ───────────────────────────────────────────────────
332
+
333
+ /** Browse A2A tasks available for execution. */
334
+ async browseA2ATasks(params?: {
335
+ capabilities?: string[];
336
+ minReputation?: number;
337
+ }): Promise<{ tasks: A2ATaskState[] }> {
338
+ const qs = new URLSearchParams();
339
+ if (params?.capabilities) qs.set('capabilities', params.capabilities.join(','));
340
+ if (params?.minReputation != null) qs.set('minReputation', String(params.minReputation));
341
+ const suffix = qs.toString() ? `?${qs.toString()}` : '';
342
+ return this.req('GET', `/api/v1/a2a/tasks${suffix}`);
343
+ }
344
+
345
+ /** Register intent to accept a task (bid). */
346
+ async bidOnTask(taskId: string): Promise<void> {
347
+ return this.req('POST', `/api/v1/a2a/tasks/${taskId}/bid`);
348
+ }
349
+
350
+ /** Accept a task and get wrapped AES key. Requires executor auth. */
351
+ async acceptTask(taskId: string): Promise<{ task: A2ATaskState; wrappedKey: Record<string, string> }> {
352
+ return this.req('POST', `/api/v1/a2a/tasks/${taskId}/accept`);
353
+ }
354
+
355
+ /** Submit result for an accepted task. */
356
+ async submitResult(taskId: string, resultData: Record<string, unknown>): Promise<{
357
+ unsignedTx: object;
358
+ task: A2ATaskState;
359
+ }> {
360
+ return this.req('POST', `/api/v1/a2a/tasks/${taskId}/submit`, { resultData });
84
361
  }
85
362
 
363
+ /** Get tasks posted by the authenticated user. */
364
+ async getPostedTasks(): Promise<{ tasks: A2ATaskState[] }> {
365
+ return this.req('GET', '/api/v1/a2a/tasks/posted');
366
+ }
367
+
368
+ /** Get tasks executed by the authenticated user. */
369
+ async getExecutions(address?: string): Promise<{ tasks: A2ATaskState[] }> {
370
+ const qs = address ? `?address=${address}` : '';
371
+ return this.req('GET', `/api/v1/a2a/executions${qs}`);
372
+ }
373
+
374
+ // ── Verification ─────────────────────────────────────────────────────────
375
+
86
376
  /**
87
- * Trigger TEE verification for a task.
377
+ * Trigger TEE / AI verification for a task.
88
378
  */
89
- async verify(params: {
90
- taskId: number;
91
- requirements: string;
92
- evidenceSummary: string;
93
- category?: string;
94
- }): Promise<{ passed: boolean; confidence: number; reasoning: string }> {
379
+ async verify(params: VerifyTaskInput): Promise<{
380
+ passed: boolean;
381
+ confidence: number;
382
+ reasoning: string;
383
+ teeVerified?: boolean;
384
+ }> {
95
385
  return this.req('POST', '/api/v1/verification/trigger', {
96
386
  taskId: params.taskId,
97
- taskCategory: params.category ?? 'general',
98
- taskRequirements: params.requirements,
387
+ taskCategory: params.taskCategory,
388
+ taskRequirements: params.taskRequirements,
99
389
  evidenceSummary: params.evidenceSummary,
100
390
  });
101
391
  }
102
392
 
393
+ /** List available 0G Compute inference providers. */
394
+ async getVerificationProviders(): Promise<{ providers: Array<{ address: Address; model: string }> }> {
395
+ return this.req('GET', '/api/v1/verification/providers');
396
+ }
397
+
398
+ /** Check if 0G Compute is configured. */
399
+ async getVerificationStatus(): Promise<{ configured: boolean; provider?: string }> {
400
+ return this.req('GET', '/api/v1/verification/status');
401
+ }
402
+
403
+ // ── Reputation ──────────────────────────────────────────────────────────
404
+
405
+ /** Get merged reputation (on-chain + off-chain) for an address. */
406
+ async getReputation(address: Address): Promise<ReputationInfo> {
407
+ return this.req<ReputationInfo>('GET', `/api/v1/reputation/${address}`);
408
+ }
409
+
410
+ /** Get top workers by decayed score. */
411
+ async getLeaderboard(limit = 50): Promise<LeaderboardEntry[]> {
412
+ return this.req<LeaderboardEntry[]>('GET', `/api/v1/reputation/leaderboard?limit=${limit}`);
413
+ }
414
+
415
+ // ── Storage ─────────────────────────────────────────────────────────────
416
+
417
+ /** Upload an encrypted blob to 0G Storage. */
418
+ async uploadBlob(data: Hex): Promise<StorageUploadResult> {
419
+ return this.req<StorageUploadResult>('POST', '/api/v1/storage/upload', { data });
420
+ }
421
+
422
+ /** Download a blob by root hash. */
423
+ async downloadBlob(rootHash: Hex): Promise<{ data: Hex }> {
424
+ return this.req<{ data: Hex }>('GET', `/api/v1/storage/${rootHash}`);
425
+ }
426
+
427
+ // ── Messages ────────────────────────────────────────────────────────────
428
+
429
+ /** Send a message to another user or agent. */
430
+ async sendMessage(params: {
431
+ taskId: string;
432
+ to: string;
433
+ content: string;
434
+ }): Promise<{ message: Message }> {
435
+ return this.req('POST', '/api/v1/messages/send', params);
436
+ }
437
+
438
+ /** Get inbox messages. */
439
+ async getInbox(): Promise<{ messages: Message[] }> {
440
+ return this.req('GET', '/api/v1/messages/inbox');
441
+ }
442
+
443
+ /** Get unread message count. */
444
+ async getUnreadCount(): Promise<{ count: number }> {
445
+ return this.req('GET', '/api/v1/messages/unread-count');
446
+ }
447
+
448
+ // ── Marketplace ─────────────────────────────────────────────────────────
449
+
450
+ /** Search agents by capability and/or minimum rating. */
451
+ async searchAgents(params?: {
452
+ capability?: string;
453
+ minRating?: number;
454
+ }): Promise<AgentSearchResult[]> {
455
+ const qs = new URLSearchParams();
456
+ if (params?.capability) qs.set('capability', params.capability);
457
+ if (params?.minRating != null) qs.set('minRating', String(params.minRating));
458
+ const suffix = qs.toString() ? `?${qs.toString()}` : '';
459
+ return this.req<AgentSearchResult[]>('GET', `/api/v1/marketplace/agents/search${suffix}`);
460
+ }
461
+
462
+ /** List public task templates. */
463
+ async listTemplates(): Promise<TaskTemplate[]> {
464
+ return this.req<TaskTemplate[]>('GET', '/api/v1/marketplace/templates');
465
+ }
466
+
467
+ /** List own templates. */
468
+ async listMyTemplates(): Promise<TaskTemplate[]> {
469
+ return this.req<TaskTemplate[]>('GET', '/api/v1/marketplace/templates/mine');
470
+ }
471
+
472
+ /** Create a task template. */
473
+ async createTemplate(params: Partial<TaskTemplate>): Promise<TaskTemplate> {
474
+ return this.req<TaskTemplate>('POST', '/api/v1/marketplace/templates', params);
475
+ }
476
+
477
+ // ── Event watching ──────────────────────────────────────────────────────
478
+
103
479
  /**
104
- * Get task status.
480
+ * Poll a task's status at a fixed interval. Calls `callback` on every change.
481
+ * Returns an unsubscribe function.
482
+ *
483
+ * @example
484
+ * const stop = bb.watchTask('42', (task) => {
485
+ * console.log('Status:', task.status);
486
+ * if (task.status === 'verified') stop();
487
+ * });
105
488
  */
106
- async getTask(taskId: string): Promise<{ status: number; agent: string; worker: string; amount: string }> {
107
- return this.req('GET', `/api/v1/tasks/${taskId}`);
489
+ watchTask(
490
+ taskId: string,
491
+ callback: (task: TaskDetail) => void,
492
+ intervalMs = 5_000,
493
+ ): () => void {
494
+ let prev = '';
495
+ const id = setInterval(async () => {
496
+ try {
497
+ const task = await this.getTask(taskId);
498
+ const cur = task.a2aState?.status ?? String(task.status);
499
+ if (cur !== prev) {
500
+ prev = cur;
501
+ callback(task);
502
+ }
503
+ } catch {
504
+ // Silently retry on next tick
505
+ }
506
+ }, intervalMs);
507
+ return () => clearInterval(id);
108
508
  }
109
509
 
110
510
  /**
111
- * List open tasks.
511
+ * Poll an agent's status at a fixed interval. Returns an unsubscribe function.
112
512
  */
113
- async listTasks(limit = 20): Promise<object[]> {
114
- const { tasks } = await this.req<{ tasks: object[] }>('GET', `/api/v1/tasks?limit=${limit}`);
115
- return tasks;
513
+ watchAgent(
514
+ agentId: string,
515
+ callback: (agent: DeployedAgentInfo) => void,
516
+ intervalMs = 5_000,
517
+ ): () => void {
518
+ let prev = '';
519
+ const id = setInterval(async () => {
520
+ try {
521
+ const agent = await this.getAgent(agentId);
522
+ if (agent.status !== prev) {
523
+ prev = agent.status;
524
+ callback(agent);
525
+ }
526
+ } catch {
527
+ // Silently retry on next tick
528
+ }
529
+ }, intervalMs);
530
+ return () => clearInterval(id);
116
531
  }
117
532
 
118
- // ── Auth helper ───────────────────────────────────────────────────────────
533
+ // ── Device-flow registration (static) ─────────────────────────────────────
119
534
 
120
535
  /**
121
- * Authenticate with a wallet and return a JWT.
122
- * Use this to get the apiKey for the constructor.
536
+ * Start a device-flow registration session. Generates a magic-link URL
537
+ * that the user opens in a browser to sign with their wallet.
538
+ *
539
+ * After registering, call `BlindMarket.pollSession(token)` to wait for
540
+ * the user to confirm and receive the API key.
123
541
  *
124
542
  * @example
125
- * const wallet = new ethers.Wallet(privateKey);
126
- * const apiKey = await BlindMarket.authenticate(wallet, 'http://localhost:3001');
543
+ * ```ts
544
+ * const wallet = ethers.Wallet.createRandom();
545
+ * const { url, token } = await BlindMarket.register({
546
+ * agentName: 'my-agent',
547
+ * agentWallet: wallet.address,
548
+ * agentPublicKey: wallet.publicKey,
549
+ * });
550
+ * console.log('Open', url, 'to confirm');
551
+ * const apiKey = await BlindMarket.pollSession(token);
127
552
  * const bb = new BlindMarket({ apiKey });
553
+ * ```
128
554
  */
129
- static async authenticate(wallet: Wallet, apiBase = 'http://localhost:3001'): Promise<string> {
130
- const nonceRes = await fetch(`${apiBase}/api/v1/auth/nonce`, {
555
+ static async register(params: {
556
+ agentName: string;
557
+ agentWallet: string;
558
+ agentPublicKey: string;
559
+ apiBase?: string;
560
+ }): Promise<{ token: string; url: string }> {
561
+ const base = params.apiBase ?? 'https://api.blindmarket.xyz';
562
+ const res = await fetch(`${base}/api/v1/registration/session`, {
131
563
  method: 'POST',
132
564
  headers: { 'Content-Type': 'application/json' },
133
- body: JSON.stringify({ address: wallet.address }),
565
+ body: JSON.stringify({
566
+ agentName: params.agentName,
567
+ agentWallet: params.agentWallet,
568
+ agentPublicKey: params.agentPublicKey,
569
+ }),
134
570
  });
135
- const { data: { nonce } } = await nonceRes.json() as { data: { nonce: string } };
136
- const message = `Sign this message to authenticate with BlindMarket.\n\nNonce: ${nonce}`;
137
- const signature = await wallet.signMessage(message);
138
- const verifyRes = await fetch(`${apiBase}/api/v1/auth/verify`, {
139
- method: 'POST',
140
- headers: { 'Content-Type': 'application/json' },
141
- body: JSON.stringify({ address: wallet.address, signature }),
142
- });
143
- const { data: { token } } = await verifyRes.json() as { data: { token: string } };
144
- return token;
571
+ const json = await res.json() as { success: boolean; data?: { token: string; url: string }; error?: { message: string } };
572
+ if (!json.success) throw new ApiError(res.status, json.error?.message ?? 'Registration failed', json);
573
+ return json.data!;
574
+ }
575
+
576
+ /**
577
+ * Poll a device-flow registration session until the user confirms or
578
+ * the session expires. Returns the API key to use with `new BlindMarket({ apiKey })`.
579
+ *
580
+ * @param token - The session token from `BlindMarket.register()`.
581
+ * @param apiBase - Optional custom API base URL.
582
+ * @param intervalMs - Poll interval (default 2s).
583
+ * @param timeoutMs - Max wait time (default 5min).
584
+ */
585
+ static async pollSession(
586
+ token: string,
587
+ apiBase?: string,
588
+ intervalMs = 2_000,
589
+ timeoutMs = 300_000,
590
+ ): Promise<string> {
591
+ const base = apiBase ?? 'https://api.blindmarket.xyz';
592
+ const deadline = Date.now() + timeoutMs;
593
+ while (Date.now() < deadline) {
594
+ const res = await fetch(`${base}/api/v1/registration/session/${token}`);
595
+ const json = await res.json() as { success: boolean; data?: { status: string; apiKey?: string }; error?: { message: string } };
596
+ if (!json.success) throw new ApiError(res.status, json.error?.message ?? 'Session lookup failed', json);
597
+ if (json.data?.status === 'confirmed' && json.data?.apiKey) return json.data.apiKey;
598
+ if (json.data?.status === 'pending') {
599
+ await new Promise(r => setTimeout(r, intervalMs));
600
+ continue;
601
+ }
602
+ throw new Error(`Unexpected session status: ${json.data?.status}`);
603
+ }
604
+ throw new Error('Registration session timed out');
145
605
  }
146
606
  }
147
607
 
148
608
  export { ethers };
609
+ export { ApiError };
610
+ export {
611
+ tools,
612
+ createBlindMarketTools, createTaskTools, createAgentManagementTools, createA2ATools,
613
+ toLangChainTools, toVercelTools, toOpenAITools, toClaudeTools,
614
+ } from './tools/index.js';
615
+ export type { Tool, ToolKit, ToolDefinition } from './tools/types.js';
616
+ export type { BlindMarketTools } from './tools/index.js';
617
+ export * from './types.js';
@@ -1,3 +1,3 @@
1
1
  export * from './Storage.js';
2
2
  export * from './MemoryStorage.js';
3
- export * from './ZgStorage.js';
3
+ // ZgStorage excluded — requires @0gfoundation/0g-ts-sdk optional dep