@lucid-agents/taskmarket 1.17.2 → 1.18.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,447 @@
1
+ import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
2
+ import {
3
+ x402FeePost
4
+ } from "./chunk-W7CXEYST.js";
5
+ import {
6
+ createWalletAccountFromKeystore,
7
+ signMessage
8
+ } from "./chunk-S4A6V3J3.js";
9
+ import {
10
+ READ_AUTH_ADDRESS_HEADER,
11
+ READ_AUTH_SIGNATURE_HEADER,
12
+ TASK_ACCESS_GRANT_HEADER,
13
+ apiGet,
14
+ apiPost,
15
+ buildClaimMessage,
16
+ buildReadAuthMessage,
17
+ buildSubmitMessage,
18
+ getTaskmarketHome,
19
+ isRecordingPendingResponse,
20
+ loadKeystore
21
+ } from "./chunk-75LS6XS6.js";
22
+
23
+ // src/lib/read-auth.ts
24
+ async function signReadAuth() {
25
+ let keystore;
26
+ try {
27
+ keystore = await loadKeystore();
28
+ } catch {
29
+ return null;
30
+ }
31
+ try {
32
+ const account = await createWalletAccountFromKeystore(keystore);
33
+ const signature = await account.signMessage({
34
+ message: buildReadAuthMessage(keystore.walletAddress)
35
+ });
36
+ return {
37
+ walletAddress: keystore.walletAddress,
38
+ headers: {
39
+ [READ_AUTH_ADDRESS_HEADER]: keystore.walletAddress,
40
+ [READ_AUTH_SIGNATURE_HEADER]: signature
41
+ }
42
+ };
43
+ } catch {
44
+ return { walletAddress: keystore.walletAddress, headers: {} };
45
+ }
46
+ }
47
+
48
+ // src/lib/task-access-grants.ts
49
+ import { promises as fs } from "fs";
50
+ import path from "path";
51
+ function getGrantsPath() {
52
+ return path.join(getTaskmarketHome(), "task-access-grants.json");
53
+ }
54
+ async function readGrantsFile() {
55
+ try {
56
+ const raw = await fs.readFile(getGrantsPath(), "utf-8");
57
+ return JSON.parse(raw);
58
+ } catch {
59
+ return {};
60
+ }
61
+ }
62
+ async function loadTaskAccessGrant(taskId) {
63
+ const grants = await readGrantsFile();
64
+ const record = grants[taskId];
65
+ if (!record) return null;
66
+ if (new Date(record.expiresAt).getTime() <= Date.now()) return null;
67
+ return record.grant;
68
+ }
69
+ async function taskAccessGrantHeaders(taskId) {
70
+ const grant = await loadTaskAccessGrant(taskId);
71
+ return grant ? { [TASK_ACCESS_GRANT_HEADER]: grant } : {};
72
+ }
73
+ async function saveTaskAccessGrant(taskId, grant, expiresAt) {
74
+ const grants = await readGrantsFile();
75
+ grants[taskId] = { grant, expiresAt };
76
+ const dir = path.dirname(getGrantsPath());
77
+ await fs.mkdir(dir, { recursive: true });
78
+ await fs.writeFile(getGrantsPath(), JSON.stringify(grants, null, 2), {
79
+ mode: 384
80
+ });
81
+ }
82
+
83
+ // src/sdk/artifact-upload.ts
84
+ import { extname } from "path";
85
+ import { Readable } from "stream";
86
+ import https from "https";
87
+ import http from "http";
88
+ function mimeTypeForPath(filePath) {
89
+ switch (extname(filePath).toLowerCase()) {
90
+ case ".png":
91
+ return "image/png";
92
+ case ".jpg":
93
+ case ".jpeg":
94
+ return "image/jpeg";
95
+ case ".webp":
96
+ return "image/webp";
97
+ case ".gif":
98
+ return "image/gif";
99
+ case ".svg":
100
+ return "image/svg+xml";
101
+ case ".pdf":
102
+ return "application/pdf";
103
+ case ".mp4":
104
+ return "video/mp4";
105
+ case ".webm":
106
+ return "video/webm";
107
+ case ".mp3":
108
+ return "audio/mpeg";
109
+ case ".wav":
110
+ return "audio/wav";
111
+ case ".md":
112
+ return "text/markdown";
113
+ case ".txt":
114
+ return "text/plain";
115
+ case ".json":
116
+ return "application/json";
117
+ case ".zip":
118
+ return "application/zip";
119
+ default:
120
+ return "application/octet-stream";
121
+ }
122
+ }
123
+ function putArtifactBytes(uploadUrl, data, mimeType, onProgress) {
124
+ return new Promise((resolve, reject) => {
125
+ const url = new URL(uploadUrl);
126
+ const transport = url.protocol === "https:" ? https : http;
127
+ const req = transport.request(
128
+ url,
129
+ {
130
+ method: "PUT",
131
+ headers: {
132
+ "Content-Type": mimeType,
133
+ "Content-Length": String(data.length)
134
+ }
135
+ },
136
+ (res) => {
137
+ const chunks = [];
138
+ res.on("data", (chunk) => chunks.push(chunk));
139
+ res.on("end", () => {
140
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
141
+ resolve();
142
+ } else {
143
+ const body = Buffer.concat(chunks).toString("utf8").slice(0, 500);
144
+ reject(
145
+ new Error(
146
+ `Upload failed (${res.statusCode ?? "unknown"}): ${body || "no response body"}`
147
+ )
148
+ );
149
+ }
150
+ });
151
+ }
152
+ );
153
+ req.on("error", reject);
154
+ let uploaded = 0;
155
+ const stream = Readable.from([data]);
156
+ stream.on("data", (chunk) => {
157
+ uploaded += chunk.length;
158
+ onProgress?.(Math.round(uploaded / data.length * 100));
159
+ });
160
+ stream.on("error", reject);
161
+ stream.pipe(req);
162
+ });
163
+ }
164
+
165
+ // src/lib/task-sdk.ts
166
+ async function putWithProgress(upload) {
167
+ process.stderr.write(` Uploading ${upload.fileName}...
168
+ `);
169
+ await putArtifactBytes(upload.uploadUrl, upload.data, upload.mimeType, (percent) => {
170
+ process.stderr.write(`\r ${upload.fileName}: ${percent}%`);
171
+ });
172
+ process.stderr.write("\n");
173
+ }
174
+ var cliTaskTransport = {
175
+ get: (path2, headers) => apiGet(path2, { headers }),
176
+ // A write the caller gave no key is sent exactly as before the SDK existed, with no options,
177
+ // so `apiPost` resolves its key itself (explicit, then TASKMARKET_IDEMPOTENCY_KEY, then fresh).
178
+ post: (path2, body, options) => options === void 0 ? apiPost(path2, body) : apiPost(path2, body, options),
179
+ feePost: (path2, body, options) => x402FeePost(path2, body, options),
180
+ put: putWithProgress
181
+ };
182
+ function keystoreTaskSigner(keystore) {
183
+ return {
184
+ address: keystore.walletAddress,
185
+ signMessage: (message) => signMessage(message, keystore)
186
+ };
187
+ }
188
+ function cliTaskReadContext() {
189
+ return {
190
+ transport: cliTaskTransport,
191
+ readAuth: signReadAuth,
192
+ accessGrantHeaders: taskAccessGrantHeaders
193
+ };
194
+ }
195
+ function cliTaskWriteContext(keystore) {
196
+ return { ...cliTaskReadContext(), signer: keystoreTaskSigner(keystore) };
197
+ }
198
+
199
+ // src/sdk/tasks.ts
200
+ import { promises as fsPromises } from "fs";
201
+ import { basename } from "path";
202
+ import { createHash } from "crypto";
203
+ import { keccak256 } from "viem";
204
+ var TaskOperationError = class extends Error {
205
+ code;
206
+ constructor(code, message) {
207
+ super(message);
208
+ this.name = "TaskOperationError";
209
+ this.code = code;
210
+ }
211
+ };
212
+ function withChainQuery(path2, chainId) {
213
+ if (chainId === void 0) return path2;
214
+ return `${path2}${path2.includes("?") ? "&" : "?"}chainId=${chainId}`;
215
+ }
216
+ function withChainBody(body, chainId) {
217
+ return chainId === void 0 ? body : { ...body, chainId };
218
+ }
219
+ function writeOptions(idempotencyKey) {
220
+ return idempotencyKey === void 0 ? void 0 : { idempotencyKey };
221
+ }
222
+ async function readAuthHeaders(ctx) {
223
+ return (await ctx.readAuth?.())?.headers ?? {};
224
+ }
225
+ async function getTask(ctx, ref) {
226
+ const headers = {
227
+ ...await readAuthHeaders(ctx),
228
+ ...await ctx.accessGrantHeaders?.(ref.taskId) ?? {}
229
+ };
230
+ const task = await ctx.transport.get(
231
+ withChainQuery(`/api/tasks/${ref.taskId}`, ref.chainId),
232
+ headers
233
+ );
234
+ return task ?? null;
235
+ }
236
+ async function getTaskTerms(ctx, ref) {
237
+ const headers = {
238
+ ...await readAuthHeaders(ctx),
239
+ ...await ctx.accessGrantHeaders?.(ref.taskId) ?? {}
240
+ };
241
+ const detail = await ctx.transport.get(
242
+ withChainQuery(`/api/tasks/${ref.taskId}/terms`, ref.chainId),
243
+ headers
244
+ );
245
+ return detail ?? null;
246
+ }
247
+ async function listTasks(ctx, filter = {}) {
248
+ const params = new URLSearchParams();
249
+ if (filter.chainId !== void 0) params.set("chainId", String(filter.chainId));
250
+ if (filter.q) params.set("q", filter.q);
251
+ if (filter.sort) params.set("sort", filter.sort);
252
+ if (filter.status) params.set("status", filter.status);
253
+ if (filter.phase) params.set("phase", filter.phase);
254
+ if (filter.mode) params.set("mode", filter.mode);
255
+ if (filter.auctionType) params.set("auctionType", filter.auctionType);
256
+ if (filter.tags) params.set("tags", filter.tags);
257
+ if (filter.minReward) params.set("minReward", filter.minReward);
258
+ if (filter.maxReward) params.set("maxReward", filter.maxReward);
259
+ if (filter.deadlineHours) params.set("deadlineHours", filter.deadlineHours);
260
+ if (filter.limit) params.set("limit", filter.limit);
261
+ if (filter.cursor) params.set("cursor", filter.cursor);
262
+ const result = await ctx.transport.get(
263
+ `/api/tasks?${params.toString()}`,
264
+ await readAuthHeaders(ctx)
265
+ );
266
+ return { tasks: result.tasks, hasMore: result.hasMore, nextCursor: result.nextCursor };
267
+ }
268
+ async function listTaskSubmissions(ctx, ref) {
269
+ const headers = await readAuthHeaders(ctx);
270
+ return await ctx.transport.get(
271
+ withChainQuery(`/api/tasks/${ref.taskId}/submissions`, ref.chainId),
272
+ headers
273
+ );
274
+ }
275
+ async function listWorkerSubmissions(ctx, options = {}) {
276
+ const auth = await ctx.readAuth?.();
277
+ const address = options.address ?? auth?.walletAddress;
278
+ if (!address) {
279
+ throw new TaskOperationError(
280
+ "no_address",
281
+ "No worker address was given and there is no reading wallet to default to"
282
+ );
283
+ }
284
+ return ctx.transport.get(`/api/submissions/mine?workerAddress=${encodeURIComponent(address)}`, {
285
+ ...auth?.headers ?? {}
286
+ });
287
+ }
288
+ async function getWorkerInbox(ctx) {
289
+ const auth = await ctx.readAuth?.();
290
+ if (!auth?.walletAddress) {
291
+ throw new TaskOperationError("no_address", "There is no reading wallet to ask about");
292
+ }
293
+ const raw = await ctx.transport.get(
294
+ `/api/agents/inbox?address=${encodeURIComponent(auth.walletAddress)}`,
295
+ auth.headers
296
+ );
297
+ return {
298
+ address: auth.walletAddress,
299
+ proven: Object.keys(auth.headers).length > 0,
300
+ inbox: {
301
+ asRequester: raw?.asRequester ?? [],
302
+ asWorker: raw?.asWorker ?? [],
303
+ invitedPrivateTasks: raw?.invitedPrivateTasks ?? []
304
+ }
305
+ };
306
+ }
307
+ async function getAgentStats(ctx, options) {
308
+ return await ctx.transport.get(
309
+ withChainQuery(
310
+ `/api/agents/stats?address=${encodeURIComponent(options.address)}`,
311
+ options.chainId
312
+ ),
313
+ {}
314
+ );
315
+ }
316
+ async function claimTask(ctx, ref, options = {}) {
317
+ const signature = await ctx.signer.signMessage(buildClaimMessage(ref.taskId));
318
+ const { data, idempotencyKey } = await ctx.transport.post(
319
+ `/api/tasks/${ref.taskId}/claim`,
320
+ withChainBody({ workerAddress: ctx.signer.address, signature }, ref.chainId),
321
+ writeOptions(options.idempotencyKey)
322
+ );
323
+ return { claimId: data.claimId, idempotencyKey };
324
+ }
325
+ async function authorizeUploads(ctx, ref) {
326
+ return { signature: await ctx.signer.signMessage(buildSubmitMessage(ref.taskId)) };
327
+ }
328
+ async function readArtifactFile(file) {
329
+ const fileName = file.fileName ?? basename(file.path);
330
+ const data = await fsPromises.readFile(file.path);
331
+ if (data.length === 0) {
332
+ throw new TaskOperationError("empty_artifact", `File is empty: ${fileName}`);
333
+ }
334
+ return {
335
+ data,
336
+ artifact: {
337
+ fileName,
338
+ mimeType: file.mimeType ?? mimeTypeForPath(file.path),
339
+ role: file.role,
340
+ sizeBytes: data.length,
341
+ sha256Hash: createHash("sha256").update(data).digest("hex"),
342
+ keccak256Hash: keccak256(new Uint8Array(data))
343
+ }
344
+ };
345
+ }
346
+ async function requestArtifactUpload(ctx, ref, artifact, authorization) {
347
+ const {
348
+ data: { uploadUrl, artifactKey }
349
+ } = await ctx.transport.post(
350
+ `/api/tasks/${ref.taskId}/submissions/request-upload-url`,
351
+ withChainBody(
352
+ {
353
+ taskId: ref.taskId,
354
+ workerAddress: ctx.signer.address,
355
+ signature: authorization.signature,
356
+ fileName: artifact.fileName,
357
+ mimeType: artifact.mimeType,
358
+ role: artifact.role,
359
+ sizeBytes: artifact.sizeBytes
360
+ },
361
+ ref.chainId
362
+ )
363
+ );
364
+ return { uploadUrl, artifactKey };
365
+ }
366
+ async function uploadArtifact(ctx, ref, file, authorization) {
367
+ const { data, artifact } = await readArtifactFile(file);
368
+ const { uploadUrl, artifactKey } = await requestArtifactUpload(ctx, ref, artifact, authorization);
369
+ await ctx.transport.put({
370
+ uploadUrl,
371
+ data,
372
+ mimeType: artifact.mimeType,
373
+ fileName: artifact.fileName
374
+ });
375
+ return { artifactKey, ...artifact };
376
+ }
377
+ async function submitUploadedArtifacts(ctx, ref, artifacts, options = {}) {
378
+ if (artifacts.length === 0) {
379
+ throw new TaskOperationError("no_artifacts", "At least one artifact is required");
380
+ }
381
+ const signature = await ctx.signer.signMessage(
382
+ buildSubmitMessage(
383
+ ref.taskId,
384
+ artifacts.map((artifact) => artifact.artifactKey)
385
+ )
386
+ );
387
+ const { data, idempotencyKey } = await ctx.transport.feePost(
388
+ `/api/tasks/${ref.taskId}/submissions/from-keys`,
389
+ withChainBody(
390
+ {
391
+ taskId: ref.taskId,
392
+ workerAddress: ctx.signer.address,
393
+ artifacts,
394
+ signature
395
+ },
396
+ ref.chainId
397
+ ),
398
+ options
399
+ );
400
+ if (isRecordingPendingResponse(data)) {
401
+ return {
402
+ submissionId: data.submissionId,
403
+ idempotencyKey,
404
+ recordingPending: {
405
+ ...typeof data.intentId === "string" ? { intentId: data.intentId } : {},
406
+ ...typeof data.txHash === "string" ? { txHash: data.txHash } : {}
407
+ }
408
+ };
409
+ }
410
+ return { submissionId: data.submissionId, idempotencyKey };
411
+ }
412
+ async function submitTaskFiles(ctx, ref, files, options = {}) {
413
+ if (files.length === 0) {
414
+ throw new TaskOperationError("no_artifacts", "At least one file is required");
415
+ }
416
+ const authorization = await authorizeUploads(ctx, ref);
417
+ const artifacts = await Promise.all(
418
+ files.map((file) => uploadArtifact(ctx, ref, file, authorization))
419
+ );
420
+ return submitUploadedArtifacts(ctx, ref, artifacts, options);
421
+ }
422
+
423
+ export {
424
+ signReadAuth,
425
+ taskAccessGrantHeaders,
426
+ saveTaskAccessGrant,
427
+ mimeTypeForPath,
428
+ putArtifactBytes,
429
+ cliTaskTransport,
430
+ keystoreTaskSigner,
431
+ cliTaskReadContext,
432
+ cliTaskWriteContext,
433
+ TaskOperationError,
434
+ getTask,
435
+ getTaskTerms,
436
+ listTasks,
437
+ listTaskSubmissions,
438
+ listWorkerSubmissions,
439
+ getWorkerInbox,
440
+ getAgentStats,
441
+ claimTask,
442
+ authorizeUploads,
443
+ readArtifactFile,
444
+ requestArtifactUpload,
445
+ submitUploadedArtifacts,
446
+ submitTaskFiles
447
+ };