@blindmarket/sdk 0.1.7 → 0.4.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.
Files changed (188) hide show
  1. package/dist/chain/BlindEscrowClient.d.ts +2 -0
  2. package/dist/chain/BlindEscrowClient.js +14 -6
  3. package/dist/chain/BlindReputationClient.d.ts +3 -0
  4. package/dist/chain/BlindReputationClient.js +3 -1
  5. package/dist/chain/ChainClient.d.ts +5 -2
  6. package/dist/chain/ChainClient.js +5 -0
  7. package/dist/chain/IBlindMarketChain.d.ts +74 -0
  8. package/dist/chain/TaskRegistryClient.d.ts +2 -1
  9. package/dist/chain/TaskRegistryClient.js +15 -7
  10. package/dist/chain/createChainAdapter.d.ts +25 -0
  11. package/dist/chain/createChainAdapter.js +24 -0
  12. package/dist/chain/domain-types.d.ts +77 -0
  13. package/dist/chain/evm/EVMChainAdapter.d.ts +56 -0
  14. package/dist/chain/evm/EVMChainAdapter.js +161 -0
  15. package/dist/chain/evm/index.d.ts +1 -0
  16. package/dist/chain/evm/index.js +1 -0
  17. package/dist/chain/index.d.ts +4 -0
  18. package/dist/chain/index.js +5 -0
  19. package/dist/executor/WorkerRuntime.d.ts +134 -0
  20. package/dist/executor/WorkerRuntime.js +277 -0
  21. package/dist/executor/index.d.ts +1 -0
  22. package/dist/executor/index.js +1 -0
  23. package/dist/index.d.ts +82 -9
  24. package/dist/index.js +60 -6
  25. package/dist/network/presets.d.ts +33 -5
  26. package/dist/network/presets.js +35 -8
  27. package/dist/network/resolve.d.ts +12 -3
  28. package/dist/network/resolve.js +15 -7
  29. package/dist/network/types.d.ts +3 -16
  30. package/dist/types.d.ts +1 -1
  31. package/package.json +8 -1
  32. package/biome.json +0 -19
  33. package/dist/BlindEscrowClient-CyPuKMge.d.cts +0 -54
  34. package/dist/BlindEscrowClient-R7iGpE-n.d.ts +0 -54
  35. package/dist/KeyStore-Be_SSpCt.d.cts +0 -21
  36. package/dist/KeyStore-k1JgYPJY.d.ts +0 -21
  37. package/dist/Signer-BnqH_qew.d.cts +0 -38
  38. package/dist/Signer-LColE0Uo.d.ts +0 -38
  39. package/dist/Storage-4QOrh7rm.d.ts +0 -17
  40. package/dist/Storage-Cf4IJRMk.d.cts +0 -17
  41. package/dist/TaskRegistryClient-Ch6lYqM_.d.ts +0 -24
  42. package/dist/TaskRegistryClient-CrQjddDc.d.cts +0 -24
  43. package/dist/aes-J-7HrHze.d.cts +0 -17
  44. package/dist/aes-J-7HrHze.d.ts +0 -17
  45. package/dist/agent/index.cjs +0 -425
  46. package/dist/agent/index.cjs.map +0 -1
  47. package/dist/agent/index.d.cts +0 -63
  48. package/dist/agent/index.js.map +0 -1
  49. package/dist/api/index.cjs +0 -4
  50. package/dist/api/index.cjs.map +0 -1
  51. package/dist/api/index.d.cts +0 -2
  52. package/dist/api/index.js.map +0 -1
  53. package/dist/chain/index.cjs +0 -2484
  54. package/dist/chain/index.cjs.map +0 -1
  55. package/dist/chain/index.d.cts +0 -2305
  56. package/dist/chain/index.js.map +0 -1
  57. package/dist/compute/index.cjs +0 -4
  58. package/dist/compute/index.cjs.map +0 -1
  59. package/dist/compute/index.d.cts +0 -2
  60. package/dist/compute/index.js.map +0 -1
  61. package/dist/crypto/index.cjs +0 -424
  62. package/dist/crypto/index.cjs.map +0 -1
  63. package/dist/crypto/index.d.cts +0 -74
  64. package/dist/crypto/index.js.map +0 -1
  65. package/dist/errors/BlindBountyError.d.ts +0 -14
  66. package/dist/errors/BlindBountyError.js +0 -30
  67. package/dist/errors/index.cjs +0 -136
  68. package/dist/errors/index.cjs.map +0 -1
  69. package/dist/errors/index.d.cts +0 -60
  70. package/dist/errors/index.js.map +0 -1
  71. package/dist/events/index.cjs +0 -41
  72. package/dist/events/index.cjs.map +0 -1
  73. package/dist/events/index.d.cts +0 -61
  74. package/dist/events/index.js.map +0 -1
  75. package/dist/index.cjs +0 -4
  76. package/dist/index.cjs.map +0 -1
  77. package/dist/index.d.cts +0 -2
  78. package/dist/index.js.map +0 -1
  79. package/dist/keystore/index.cjs +0 -335
  80. package/dist/keystore/index.cjs.map +0 -1
  81. package/dist/keystore/index.d.cts +0 -23
  82. package/dist/keystore/index.js.map +0 -1
  83. package/dist/mcp.d.ts +0 -23
  84. package/dist/mcp.js +0 -101
  85. package/dist/network/index.cjs +0 -74
  86. package/dist/network/index.cjs.map +0 -1
  87. package/dist/network/index.d.cts +0 -36
  88. package/dist/network/index.js.map +0 -1
  89. package/dist/signer/index.cjs +0 -121
  90. package/dist/signer/index.cjs.map +0 -1
  91. package/dist/signer/index.d.cts +0 -40
  92. package/dist/signer/index.js.map +0 -1
  93. package/dist/storage/ZgStorage.d.ts +0 -24
  94. package/dist/storage/ZgStorage.js +0 -102
  95. package/dist/storage/index.cjs +0 -221
  96. package/dist/storage/index.cjs.map +0 -1
  97. package/dist/storage/index.d.cts +0 -44
  98. package/dist/storage/index.js.map +0 -1
  99. package/dist/testing/index.cjs +0 -4
  100. package/dist/testing/index.cjs.map +0 -1
  101. package/dist/testing/index.d.cts +0 -2
  102. package/dist/testing/index.js.map +0 -1
  103. package/dist/types-BOG5Ub1z.d.cts +0 -31
  104. package/dist/types-BOG5Ub1z.d.ts +0 -31
  105. package/dist/types-V7_ZmUb-.d.cts +0 -19
  106. package/dist/types-hAsITUQ7.d.ts +0 -19
  107. package/dist/verifier/index.cjs +0 -4
  108. package/dist/verifier/index.cjs.map +0 -1
  109. package/dist/verifier/index.d.cts +0 -2
  110. package/dist/verifier/index.js.map +0 -1
  111. package/dist/worker/index.cjs +0 -382
  112. package/dist/worker/index.cjs.map +0 -1
  113. package/dist/worker/index.d.cts +0 -58
  114. package/dist/worker/index.js.map +0 -1
  115. package/examples/agent-demo.ts +0 -121
  116. package/scripts/gen-crypto-fixtures.mjs +0 -72
  117. package/src/agent/Agent.ts +0 -122
  118. package/src/agent/index.ts +0 -1
  119. package/src/chain/BlindEscrowClient.ts +0 -187
  120. package/src/chain/BlindReputationClient.ts +0 -55
  121. package/src/chain/ChainClient.ts +0 -59
  122. package/src/chain/TaskRegistryClient.ts +0 -82
  123. package/src/chain/abi/BlindEscrow.json +0 -1113
  124. package/src/chain/abi/BlindReputation.json +0 -447
  125. package/src/chain/abi/TaskRegistry.json +0 -586
  126. package/src/chain/abi/index.ts +0 -5
  127. package/src/chain/errors.ts +0 -43
  128. package/src/chain/index.ts +0 -6
  129. package/src/crypto/aes.ts +0 -104
  130. package/src/crypto/constants.ts +0 -7
  131. package/src/crypto/ecies.ts +0 -60
  132. package/src/crypto/envelope.ts +0 -131
  133. package/src/crypto/hkdf.ts +0 -45
  134. package/src/crypto/index.ts +0 -6
  135. package/src/crypto/keys.ts +0 -56
  136. package/src/errors/BlindMarketError.ts +0 -38
  137. package/src/errors/codes.ts +0 -67
  138. package/src/errors/index.ts +0 -3
  139. package/src/errors/subclasses.ts +0 -59
  140. package/src/events/EventBus.ts +0 -45
  141. package/src/events/index.ts +0 -2
  142. package/src/events/types.ts +0 -13
  143. package/src/index.ts +0 -617
  144. package/src/keystore/InMemoryKeyStore.ts +0 -146
  145. package/src/keystore/KeyStore.ts +0 -19
  146. package/src/keystore/index.ts +0 -2
  147. package/src/network/index.ts +0 -3
  148. package/src/network/presets.ts +0 -27
  149. package/src/network/resolve.ts +0 -19
  150. package/src/network/types.ts +0 -18
  151. package/src/signer/EthersSigner.ts +0 -82
  152. package/src/signer/PrivateKeySigner.ts +0 -19
  153. package/src/signer/Signer.ts +0 -40
  154. package/src/signer/index.ts +0 -3
  155. package/src/storage/MemoryStorage.ts +0 -49
  156. package/src/storage/Storage.ts +0 -15
  157. package/src/storage/ZgStorage.ts +0 -124
  158. package/src/storage/index.ts +0 -3
  159. package/src/testing/index.ts +0 -1
  160. package/src/tools/adapters.ts +0 -177
  161. package/src/tools/helpers.ts +0 -199
  162. package/src/tools/index.ts +0 -59
  163. package/src/tools/types.ts +0 -47
  164. package/src/types.ts +0 -296
  165. package/src/verifier/index.ts +0 -1
  166. package/src/worker/Worker.ts +0 -98
  167. package/src/worker/index.ts +0 -1
  168. package/test/agent.test.ts +0 -193
  169. package/test/chain.test.ts +0 -145
  170. package/test/crypto.aes.test.ts +0 -81
  171. package/test/crypto.compat.test.ts +0 -33
  172. package/test/crypto.ecies.test.ts +0 -64
  173. package/test/envelope.test.ts +0 -82
  174. package/test/errors.test.ts +0 -64
  175. package/test/events.test.ts +0 -59
  176. package/test/fixtures/crypto.json +0 -16
  177. package/test/keystore.test.ts +0 -103
  178. package/test/network.test.ts +0 -53
  179. package/test/signer.test.ts +0 -66
  180. package/test/smoke.test.ts +0 -7
  181. package/test/storage.test.ts +0 -55
  182. package/test/worker.test.ts +0 -181
  183. package/tsconfig.build.json +0 -1
  184. package/tsconfig.json +0 -14
  185. package/tsup.config.ts +0 -32
  186. package/vitest.config.ts +0 -17
  187. /package/{src/api/index.ts → dist/chain/IBlindMarketChain.js} +0 -0
  188. /package/{src/compute/index.ts → dist/chain/domain-types.js} +0 -0
@@ -0,0 +1,134 @@
1
+ import { BlindMarket } from '../index.js';
2
+ import type { A2ATaskState, AgentCapability, ExecutorProfile, Message } from '../types.js';
3
+ export interface WorkerRuntimeConfig {
4
+ apiKey: string;
5
+ apiBase?: string;
6
+ displayName: string;
7
+ capabilities: AgentCapability[];
8
+ executeTask: ExecuteTaskHandler;
9
+ existingPrivateKey?: string;
10
+ existingAddress?: string;
11
+ existingPublicKey?: string;
12
+ minReward?: string;
13
+ preferredCapabilities?: AgentCapability[];
14
+ browseIntervalMs?: number;
15
+ watchIntervalMs?: number;
16
+ maxConcurrentTasks?: number;
17
+ /**
18
+ * RPC URL used to sign + broadcast `submitEvidence` after `submitResult()`.
19
+ * Defaults to the 0G testnet RPC (matches `backend/agents/worker.js`'s
20
+ * default). Point this at the RPC for whichever chain your tasks settle on.
21
+ */
22
+ rpcUrl?: string;
23
+ }
24
+ export type ExecuteTaskHandler = (ctx: TaskContext) => Promise<Record<string, unknown>>;
25
+ export interface TaskContext {
26
+ taskId: string;
27
+ task: A2ATaskState;
28
+ instructions: string;
29
+ }
30
+ export interface TaskExecutionInfo {
31
+ taskId: string;
32
+ status: 'bidding' | 'assigned' | 'working' | 'submitted' | 'completed' | 'failed';
33
+ task?: A2ATaskState;
34
+ error?: string;
35
+ startedAt: number;
36
+ }
37
+ export type WorkerRuntimeEvent = {
38
+ type: 'started';
39
+ } | {
40
+ type: 'stopped';
41
+ } | {
42
+ type: 'paused';
43
+ } | {
44
+ type: 'resumed';
45
+ } | {
46
+ type: 'registered';
47
+ profile: ExecutorProfile;
48
+ } | {
49
+ type: 'task_found';
50
+ taskId: string;
51
+ task: A2ATaskState;
52
+ } | {
53
+ type: 'task_bidded';
54
+ taskId: string;
55
+ } | {
56
+ type: 'task_assigned';
57
+ taskId: string;
58
+ } | {
59
+ type: 'task_accepted';
60
+ taskId: string;
61
+ } | {
62
+ type: 'task_working';
63
+ taskId: string;
64
+ } | {
65
+ type: 'task_executed';
66
+ taskId: string;
67
+ } | {
68
+ type: 'task_submitted';
69
+ taskId: string;
70
+ result: Record<string, unknown>;
71
+ } | {
72
+ type: 'task_finalized';
73
+ taskId: string;
74
+ finalize: Awaited<ReturnType<BlindMarket['finalize']>>;
75
+ } | {
76
+ type: 'task_failed';
77
+ taskId: string;
78
+ error: string;
79
+ } | {
80
+ type: 'message_received';
81
+ message: Message;
82
+ count: number;
83
+ } | {
84
+ type: 'browse_done';
85
+ found: number;
86
+ } | {
87
+ type: 'error';
88
+ error: string;
89
+ };
90
+ export declare class WorkerRuntime {
91
+ private bb;
92
+ private config;
93
+ private wallet?;
94
+ private profile?;
95
+ private running;
96
+ private paused;
97
+ private browseTimer?;
98
+ private watchTimers;
99
+ private executions;
100
+ private listeners;
101
+ constructor(config: WorkerRuntimeConfig);
102
+ get isRunning(): boolean;
103
+ get isPaused(): boolean;
104
+ get activeExecutions(): TaskExecutionInfo[];
105
+ /** Get own executor profile (available after start). */
106
+ get executorProfile(): ExecutorProfile | undefined;
107
+ /** Get own wallet info (available after start). */
108
+ get executorWallet(): {
109
+ address: string;
110
+ publicKey: string;
111
+ } | undefined;
112
+ on(listener: (event: WorkerRuntimeEvent) => void): () => void;
113
+ private emit;
114
+ start(): Promise<ExecutorProfile>;
115
+ stop(): void;
116
+ pause(): void;
117
+ resume(): void;
118
+ private startBrowseLoop;
119
+ private browse;
120
+ private watchForAssignment;
121
+ private executeTask;
122
+ /**
123
+ * Decode a wrapped-key hex string — acceptTask()'s wrappedKey field is a
124
+ * single hex string (this executor's ECIES-wrapped AES key), not a
125
+ * Record. The old code here called `Object.values` on that STRING
126
+ * argument (treating it as if it were a Record), which returned its
127
+ * first CHARACTER (e.g. "0" from "04a1b2…") — `clean.length / 2` was
128
+ * then 0 and every decrypt got an empty key.
129
+ */
130
+ private decodeWrappedKey;
131
+ private hexToBytes;
132
+ /** Decode a base64 string (e.g. downloadBlob()'s `blob` field) into bytes. */
133
+ private base64ToBytes;
134
+ }
@@ -0,0 +1,277 @@
1
+ import { ethers } from 'ethers';
2
+ import { BlindMarket } from '../index.js';
3
+ import { eciesDecrypt, aesDecrypt } from '../crypto/index.js';
4
+ // ── Defaults ────────────────────────────────────────────────────────────────
5
+ const DEFAULTS = {
6
+ browseIntervalMs: 15_000,
7
+ watchIntervalMs: 5_000,
8
+ maxConcurrentTasks: 3,
9
+ rpcUrl: 'https://evmrpc-testnet.0g.ai',
10
+ };
11
+ // ── WorkerRuntime ───────────────────────────────────────────────────────────
12
+ export class WorkerRuntime {
13
+ bb;
14
+ config;
15
+ wallet;
16
+ profile;
17
+ running = false;
18
+ paused = false;
19
+ browseTimer;
20
+ watchTimers = new Map();
21
+ executions = new Map();
22
+ listeners = new Set();
23
+ constructor(config) {
24
+ this.config = { ...DEFAULTS, ...config };
25
+ this.bb = new BlindMarket({ apiKey: config.apiKey, apiBase: config.apiBase });
26
+ }
27
+ // ── Status ──────────────────────────────────────────────────────────────
28
+ get isRunning() {
29
+ return this.running;
30
+ }
31
+ get isPaused() {
32
+ return this.paused;
33
+ }
34
+ get activeExecutions() {
35
+ return [...this.executions.values()];
36
+ }
37
+ /** Get own executor profile (available after start). */
38
+ get executorProfile() {
39
+ return this.profile;
40
+ }
41
+ /** Get own wallet info (available after start). */
42
+ get executorWallet() {
43
+ return this.wallet;
44
+ }
45
+ // ── Events ──────────────────────────────────────────────────────────────
46
+ on(listener) {
47
+ this.listeners.add(listener);
48
+ return () => this.listeners.delete(listener);
49
+ }
50
+ emit(event) {
51
+ for (const l of this.listeners)
52
+ l(event);
53
+ }
54
+ // ── Lifecycle ───────────────────────────────────────────────────────────
55
+ async start() {
56
+ if (this.running)
57
+ return this.profile;
58
+ // 1. Register or restore executor
59
+ if (this.config.existingPrivateKey && this.config.existingAddress && this.config.existingPublicKey) {
60
+ this.wallet = {
61
+ address: this.config.existingAddress,
62
+ privateKey: this.config.existingPrivateKey,
63
+ publicKey: this.config.existingPublicKey,
64
+ };
65
+ // Fetch existing profile — backend identifies by API key
66
+ const result = await this.bb.getExecutorProfile();
67
+ this.profile = result.agent;
68
+ }
69
+ else {
70
+ const result = await this.bb.createAgent({
71
+ displayName: this.config.displayName,
72
+ capabilities: this.config.capabilities,
73
+ minReward: this.config.minReward,
74
+ preferredCapabilities: this.config.preferredCapabilities,
75
+ });
76
+ this.wallet = {
77
+ address: result.wallet.address,
78
+ privateKey: result.wallet.privateKey,
79
+ publicKey: result.wallet.publicKey,
80
+ };
81
+ this.profile = result.executor;
82
+ }
83
+ this.emit({ type: 'registered', profile: this.profile });
84
+ // 2. Start the browse loop
85
+ this.running = true;
86
+ this.startBrowseLoop();
87
+ this.emit({ type: 'started' });
88
+ return this.profile;
89
+ }
90
+ stop() {
91
+ this.running = false;
92
+ if (this.browseTimer) {
93
+ clearInterval(this.browseTimer);
94
+ this.browseTimer = undefined;
95
+ }
96
+ for (const t of this.watchTimers.values())
97
+ clearInterval(t);
98
+ this.watchTimers.clear();
99
+ this.emit({ type: 'stopped' });
100
+ }
101
+ pause() {
102
+ if (!this.running || this.paused)
103
+ return;
104
+ this.paused = true;
105
+ this.emit({ type: 'paused' });
106
+ }
107
+ resume() {
108
+ if (!this.running || !this.paused)
109
+ return;
110
+ this.paused = false;
111
+ this.emit({ type: 'resumed' });
112
+ // Immediately browse instead of waiting for next interval
113
+ this.browse();
114
+ }
115
+ // ── Browse loop ─────────────────────────────────────────────────────────
116
+ startBrowseLoop() {
117
+ const ms = this.config.browseIntervalMs;
118
+ this.browseTimer = setInterval(() => this.browse(), ms);
119
+ this.browse();
120
+ }
121
+ async browse() {
122
+ if (this.paused)
123
+ return;
124
+ try {
125
+ const active = this.activeExecutions;
126
+ const inFlight = active.filter(e => e.status === 'bidding' || e.status === 'working').length;
127
+ if (inFlight >= this.config.maxConcurrentTasks)
128
+ return;
129
+ const result = await this.bb.browseA2ATasks({
130
+ capabilities: this.config.capabilities,
131
+ });
132
+ this.emit({ type: 'browse_done', found: result.tasks.length });
133
+ for (const task of result.tasks) {
134
+ if (this.executions.has(task.taskId))
135
+ continue;
136
+ if (task.status !== 'open' && task.status !== 'bidding')
137
+ continue;
138
+ if (task.executorAddress && task.executorAddress !== this.wallet?.address)
139
+ continue;
140
+ this.executions.set(task.taskId, {
141
+ taskId: task.taskId,
142
+ status: 'bidding',
143
+ task,
144
+ startedAt: Date.now(),
145
+ });
146
+ this.emit({ type: 'task_found', taskId: task.taskId, task });
147
+ await this.bb.bidOnTask(task.taskId);
148
+ this.emit({ type: 'task_bidded', taskId: task.taskId });
149
+ this.watchForAssignment(task.taskId);
150
+ }
151
+ }
152
+ catch (err) {
153
+ this.emit({ type: 'error', error: `Browse failed: ${err}` });
154
+ }
155
+ }
156
+ // ── Assignment watching ─────────────────────────────────────────────────
157
+ watchForAssignment(taskId) {
158
+ if (!this.running)
159
+ return;
160
+ const ms = this.config.watchIntervalMs;
161
+ const timer = setInterval(async () => {
162
+ if (!this.running || this.paused)
163
+ return;
164
+ try {
165
+ const detail = await this.bb.getTask(taskId);
166
+ const a2a = detail.a2aState;
167
+ if (!a2a)
168
+ return;
169
+ if (a2a.status === 'assigned') {
170
+ clearInterval(timer);
171
+ this.watchTimers.delete(taskId);
172
+ this.emit({ type: 'task_assigned', taskId });
173
+ await this.executeTask(taskId, a2a);
174
+ }
175
+ }
176
+ catch {
177
+ // Retry next tick
178
+ }
179
+ }, ms);
180
+ this.watchTimers.set(taskId, timer);
181
+ }
182
+ // ── Task execution ──────────────────────────────────────────────────────
183
+ async executeTask(taskId, a2a) {
184
+ const exec = this.executions.get(taskId);
185
+ if (!exec)
186
+ return;
187
+ try {
188
+ exec.status = 'assigned';
189
+ // Accept task — get the rootHash + this executor's ECIES-wrapped AES
190
+ // key. wrappedKey is a single hex string (this caller's slice), not a
191
+ // Record — see acceptTask()'s doc comment in ../index.ts.
192
+ const acceptResult = await this.bb.acceptTask(taskId);
193
+ exec.task = a2a;
194
+ this.emit({ type: 'task_accepted', taskId });
195
+ // Decrypt the brief. 'public' tasks carry no wrappedKey by design — the
196
+ // blob at rootHash is already plaintext, so skip ECIES/AES entirely.
197
+ // Download by acceptResult.rootHash (the 0G Storage pointer) — NOT the
198
+ // on-chain taskHash, which is a different value (sha256 of the
199
+ // ciphertext, used as the escrow's commitment) and isn't a valid
200
+ // storage lookup key.
201
+ let instructions = '';
202
+ if (acceptResult.rootHash) {
203
+ const storageResult = await this.bb.downloadBlob(acceptResult.rootHash);
204
+ const blobBytes = this.base64ToBytes(storageResult.blob);
205
+ if (acceptResult.privacy === 'public') {
206
+ instructions = new TextDecoder().decode(blobBytes);
207
+ }
208
+ else if (acceptResult.wrappedKey) {
209
+ const wrappedBytes = this.decodeWrappedKey(acceptResult.wrappedKey);
210
+ const aesKey = await eciesDecrypt(wrappedBytes, this.wallet.privateKey);
211
+ const plaintext = await aesDecrypt(blobBytes, aesKey);
212
+ instructions = new TextDecoder().decode(plaintext);
213
+ }
214
+ }
215
+ exec.status = 'working';
216
+ this.emit({ type: 'task_working', taskId });
217
+ // Call the user-provided execution handler with decrypted instructions
218
+ const result = await this.config.executeTask({
219
+ taskId,
220
+ task: a2a,
221
+ instructions,
222
+ });
223
+ exec.status = 'submitted';
224
+ this.emit({ type: 'task_executed', taskId });
225
+ // Submit result — the contract requires the assigned worker to sign
226
+ // submitEvidence personally (onlyWorker), so the backend hands back an
227
+ // unsigned tx instead of broadcasting it for us.
228
+ const submitResult = await this.bb.submitResult(taskId, result);
229
+ // Sign + broadcast submitEvidence with the runtime's own wallet, wait
230
+ // for confirmation, then tell the backend to proceed with
231
+ // verification. Without this the on-chain task never leaves
232
+ // 'Assigned' and completeVerification always reverts — the task only
233
+ // LOOKS complete.
234
+ if (submitResult.unsignedSubmitEvidence) {
235
+ const provider = new ethers.JsonRpcProvider(this.config.rpcUrl);
236
+ const signer = new ethers.Wallet(this.wallet.privateKey, provider);
237
+ const tx = await signer.sendTransaction(submitResult.unsignedSubmitEvidence);
238
+ await tx.wait();
239
+ }
240
+ const finalizeResult = await this.bb.finalize(taskId);
241
+ exec.status = 'completed';
242
+ this.emit({ type: 'task_submitted', taskId, result });
243
+ this.emit({ type: 'task_finalized', taskId, finalize: finalizeResult });
244
+ }
245
+ catch (err) {
246
+ exec.status = 'failed';
247
+ exec.error = String(err);
248
+ this.emit({ type: 'task_failed', taskId, error: String(err) });
249
+ }
250
+ }
251
+ // ── Helpers ─────────────────────────────────────────────────────────────
252
+ /**
253
+ * Decode a wrapped-key hex string — acceptTask()'s wrappedKey field is a
254
+ * single hex string (this executor's ECIES-wrapped AES key), not a
255
+ * Record. The old code here called `Object.values` on that STRING
256
+ * argument (treating it as if it were a Record), which returned its
257
+ * first CHARACTER (e.g. "0" from "04a1b2…") — `clean.length / 2` was
258
+ * then 0 and every decrypt got an empty key.
259
+ */
260
+ decodeWrappedKey(hex) {
261
+ if (!hex)
262
+ throw new Error('Empty wrapped key');
263
+ return this.hexToBytes(hex);
264
+ }
265
+ hexToBytes(hex) {
266
+ const clean = hex.startsWith('0x') ? hex.slice(2) : hex;
267
+ const bytes = new Uint8Array(clean.length / 2);
268
+ for (let i = 0; i < bytes.length; i++) {
269
+ bytes[i] = parseInt(clean.substring(i * 2, i * 2 + 2), 16);
270
+ }
271
+ return bytes;
272
+ }
273
+ /** Decode a base64 string (e.g. downloadBlob()'s `blob` field) into bytes. */
274
+ base64ToBytes(b64) {
275
+ return new Uint8Array(Buffer.from(b64, 'base64'));
276
+ }
277
+ }
@@ -0,0 +1 @@
1
+ export { WorkerRuntime, type WorkerRuntimeConfig, type ExecuteTaskHandler, type TaskContext, type TaskExecutionInfo, type WorkerRuntimeEvent, } from './WorkerRuntime.js';
@@ -0,0 +1 @@
1
+ export { WorkerRuntime, } from './WorkerRuntime.js';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ethers } from 'ethers';
2
- import type { Address, Hex, HealthStatus, PlatformStats, OpenTask, TaskDetail, CreateTaskTx, ExecutorProfile, RegisterExecutorInput, DeployedAgentInfo, AgentWalletInfo, ReputationInfo, LeaderboardEntry, StorageUploadResult, Message, AgentSearchResult, TaskTemplate, VerifyTaskInput, A2ATaskState, CreateAgentParams, CreateAgentResult } from './types.js';
2
+ import type { Address, Hex, RootHash, HealthStatus, PlatformStats, OpenTask, TaskDetail, CreateTaskTx, ExecutorProfile, RegisterExecutorInput, DeployedAgentInfo, AgentWalletInfo, ReputationInfo, LeaderboardEntry, StorageUploadResult, Message, AgentSearchResult, TaskTemplate, VerifyTaskInput, A2ATaskState, CreateAgentParams, CreateAgentResult } from './types.js';
3
3
  export interface BlindMarketConfig {
4
4
  /** Backend API base URL (default: https://api.blindmarket.xyz) */
5
5
  apiBase?: string;
@@ -207,15 +207,55 @@ export declare class BlindMarket {
207
207
  }>;
208
208
  /** Register intent to accept a task (bid). */
209
209
  bidOnTask(taskId: string): Promise<void>;
210
- /** Accept a task and get wrapped AES key. Requires executor auth. */
210
+ /**
211
+ * Accept a task and get the rootHash + ECIES-wrapped AES key for the
212
+ * caller's address. Requires executor auth.
213
+ *
214
+ * Note the shape here matches `backend/src/routes/a2a.ts`'s `/accept`
215
+ * handler exactly — there is no `task` field, and `wrappedKey` is a single
216
+ * hex string (this executor's slice), not a `Record`.
217
+ */
211
218
  acceptTask(taskId: string): Promise<{
212
- task: A2ATaskState;
213
- wrappedKey: Record<string, string>;
219
+ taskId: string;
220
+ status: string;
221
+ rootHash?: RootHash;
222
+ /** ECIES-wrapped AES key, hex-encoded with no 0x prefix. Absent on public tasks or legacy tasks with no brief. */
223
+ wrappedKey?: string;
224
+ /** 'public' means the blob at rootHash is plaintext — no wrappedKey by design. Absent means private/encrypted. */
225
+ privacy?: 'public' | 'private';
226
+ alreadySettled?: boolean;
227
+ assignTxHash?: Hex;
214
228
  }>;
215
- /** Submit result for an accepted task. */
229
+ /**
230
+ * Submit result for an accepted task. Returns an unsigned `submitEvidence`
231
+ * transaction — the contract requires the assigned worker to sign it
232
+ * personally (`onlyWorker`), so the caller must sign + broadcast
233
+ * `unsignedSubmitEvidence` and then call `finalize()`.
234
+ */
216
235
  submitResult(taskId: string, resultData: Record<string, unknown>): Promise<{
217
- unsignedTx: object;
218
- task: A2ATaskState;
236
+ taskId: string;
237
+ onChainTaskId?: string;
238
+ status: string;
239
+ evidenceHash?: Hex;
240
+ unsignedSubmitEvidence?: Record<string, unknown> | null;
241
+ }>;
242
+ /**
243
+ * Tell the backend your `submitEvidence` tx has confirmed on-chain so it
244
+ * can proceed with verification — auto-verify, or hand off to the poster
245
+ * / a designated verifier agent depending on the task's verificationMode.
246
+ * Call this after signing and broadcasting `unsignedSubmitEvidence` from
247
+ * `submitResult()`. Without this the escrow never settles.
248
+ */
249
+ finalize(taskId: string): Promise<{
250
+ taskId: string;
251
+ status: string;
252
+ verifier?: string;
253
+ awaitingPosterApproval?: boolean;
254
+ verificationResult?: {
255
+ passed: boolean;
256
+ reasons?: string[];
257
+ };
258
+ reconciled?: boolean;
219
259
  }>;
220
260
  /** Get tasks posted by the authenticated user. */
221
261
  getPostedTasks(): Promise<{
@@ -252,9 +292,15 @@ export declare class BlindMarket {
252
292
  getLeaderboard(limit?: number): Promise<LeaderboardEntry[]>;
253
293
  /** Upload an encrypted blob to 0G Storage. */
254
294
  uploadBlob(data: Hex): Promise<StorageUploadResult>;
255
- /** Download a blob by root hash. */
295
+ /**
296
+ * Download a blob by root hash. `blob` is base64-encoded raw bytes — the
297
+ * caller decodes (and, for encrypted tasks, decrypts) it client-side.
298
+ * Matches `backend/src/routes/storage.ts`'s `GET /:rootHash` handler,
299
+ * which returns `{ rootHash, blob }`, not `{ data }`.
300
+ */
256
301
  downloadBlob(rootHash: Hex): Promise<{
257
- data: Hex;
302
+ rootHash: Hex;
303
+ blob: string;
258
304
  }>;
259
305
  /** Send a message to another user or agent. */
260
306
  sendMessage(params: {
@@ -298,6 +344,14 @@ export declare class BlindMarket {
298
344
  * Poll an agent's status at a fixed interval. Returns an unsubscribe function.
299
345
  */
300
346
  watchAgent(agentId: string, callback: (agent: DeployedAgentInfo) => void, intervalMs?: number): () => void;
347
+ /**
348
+ * Canonical registration challenge signed by the agent wallet, proving
349
+ * control of `agentWallet`. Must stay byte-identical to
350
+ * `agentRegistrationMessage` in `backend/src/routes/registration.ts` — the
351
+ * SDK cannot import from the backend, so this is duplicated. A mismatch
352
+ * here silently breaks registration.
353
+ */
354
+ private static agentRegistrationMessage;
301
355
  /**
302
356
  * Start a device-flow registration session. Generates a magic-link URL
303
357
  * that the user opens in a browser to sign with their wallet.
@@ -305,6 +359,11 @@ export declare class BlindMarket {
305
359
  * After registering, call `BlindMarket.pollSession(token)` to wait for
306
360
  * the user to confirm and receive the API key.
307
361
  *
362
+ * Requires proof of control of `agentWallet`: pass either `agentSigner`
363
+ * (anything with a `signMessage`, e.g. an ethers `Wallet`) so the SDK signs
364
+ * the challenge itself, or a pre-computed `agentSignature` for callers
365
+ * holding a remote signer. Exactly one of the two is required.
366
+ *
308
367
  * @example
309
368
  * ```ts
310
369
  * const wallet = ethers.Wallet.createRandom();
@@ -312,6 +371,7 @@ export declare class BlindMarket {
312
371
  * agentName: 'my-agent',
313
372
  * agentWallet: wallet.address,
314
373
  * agentPublicKey: wallet.publicKey,
374
+ * agentSigner: wallet,
315
375
  * });
316
376
  * console.log('Open', url, 'to confirm');
317
377
  * const apiKey = await BlindMarket.pollSession(token);
@@ -322,6 +382,12 @@ export declare class BlindMarket {
322
382
  agentName: string;
323
383
  agentWallet: string;
324
384
  agentPublicKey: string;
385
+ /** Signs the registration challenge proving control of agentWallet. */
386
+ agentSigner?: {
387
+ signMessage(message: string): Promise<string>;
388
+ };
389
+ /** Pre-computed alternative to agentSigner, for callers holding a remote signer. */
390
+ agentSignature?: string;
325
391
  apiBase?: string;
326
392
  }): Promise<{
327
393
  token: string;
@@ -343,4 +409,11 @@ export { ApiError };
343
409
  export { tools, createBlindMarketTools, createTaskTools, createAgentManagementTools, createA2ATools, toLangChainTools, toVercelTools, toOpenAITools, toClaudeTools, } from './tools/index.js';
344
410
  export type { Tool, ToolKit, ToolDefinition } from './tools/types.js';
345
411
  export type { BlindMarketTools } from './tools/index.js';
412
+ export * from './executor/index.js';
346
413
  export * from './types.js';
414
+ export { EVMChainAdapter } from './chain/evm/index.js';
415
+ export { createChainAdapter } from './chain/createChainAdapter.js';
416
+ export type { CreateChainAdapterOptions } from './chain/createChainAdapter.js';
417
+ export type { IBlindMarketChain } from './chain/IBlindMarketChain.js';
418
+ export type { Network, EVMNetwork, BlockchainType, CreateTaskParams, DomainTask, DomainTaskMeta, DomainReputation, DomainTxReceipt, ChainSigner, } from './chain/domain-types.js';
419
+ export { isEVMNetwork } from './network/resolve.js';