@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/README.md +242 -29
- package/dist/chain/BlindEscrowClient.js +5 -1
- package/dist/chain/ChainClient.d.ts +1 -1
- package/dist/chain/ChainClient.js +1 -1
- package/dist/crypto/constants.js +1 -1
- package/dist/errors/BlindMarketError.d.ts +14 -0
- package/dist/errors/BlindMarketError.js +30 -0
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/errors/subclasses.d.ts +9 -9
- package/dist/errors/subclasses.js +9 -9
- package/dist/index.d.ts +282 -29
- package/dist/index.js +399 -41
- package/dist/keystore/InMemoryKeyStore.js +2 -2
- package/dist/mcp.d.ts +23 -0
- package/dist/mcp.js +101 -0
- package/dist/signer/EthersSigner.js +1 -1
- package/dist/storage/index.d.ts +0 -1
- package/dist/storage/index.js +1 -1
- package/dist/tools/adapters.d.ts +99 -0
- package/dist/tools/adapters.js +129 -0
- package/dist/tools/helpers.d.ts +9 -0
- package/dist/tools/helpers.js +169 -0
- package/dist/tools/index.d.ts +36 -0
- package/dist/tools/index.js +32 -0
- package/dist/tools/types.d.ts +44 -0
- package/dist/tools/types.js +1 -0
- package/dist/types.d.ts +201 -0
- package/dist/types.js +24 -1
- package/package.json +13 -2
- package/src/chain/BlindEscrowClient.ts +5 -0
- package/src/index.ts +518 -49
- package/src/storage/index.ts +1 -1
- package/src/tools/adapters.ts +177 -0
- package/src/tools/helpers.ts +199 -0
- package/src/tools/index.ts +59 -0
- package/src/tools/types.ts +47 -0
- package/src/types.ts +220 -0
- package/tsconfig.json +2 -1
package/src/index.ts
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import {
|
|
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 ?? '
|
|
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: {
|
|
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)
|
|
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
|
-
// ──
|
|
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.
|
|
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
|
|
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
|
-
*
|
|
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
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
300
|
+
* Update a deployed agent's config (instructions, model, tools, etc.).
|
|
301
|
+
* Requires owner auth.
|
|
81
302
|
*/
|
|
82
|
-
async
|
|
83
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
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.
|
|
98
|
-
taskRequirements: params.
|
|
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
|
-
*
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
*
|
|
511
|
+
* Poll an agent's status at a fixed interval. Returns an unsubscribe function.
|
|
112
512
|
*/
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
// ──
|
|
533
|
+
// ── Device-flow registration (static) ─────────────────────────────────────
|
|
119
534
|
|
|
120
535
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
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
|
-
*
|
|
126
|
-
* const
|
|
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
|
|
130
|
-
|
|
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({
|
|
565
|
+
body: JSON.stringify({
|
|
566
|
+
agentName: params.agentName,
|
|
567
|
+
agentWallet: params.agentWallet,
|
|
568
|
+
agentPublicKey: params.agentPublicKey,
|
|
569
|
+
}),
|
|
134
570
|
});
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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';
|
package/src/storage/index.ts
CHANGED