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); import { x402FeePost } from "./chunk-5YR4AZAD.js"; import { signReadAuth } from "./chunk-E7HXLN2A.js"; import { taskAccessGrantHeaders } from "./chunk-MSTGI2YG.js"; import { apiGet, apiPost, loadKeystore, runWorkerAction, signMessage, taskRequiresTap } from "./chunk-HET5ZDJT.js"; import { TaskAwardSchema, buildClaimMessage, buildSubmitMessage, isRecordingPendingResponse } from "./chunk-RY4U7U45.js"; // src/sdk/artifact-upload.ts import { extname } from "path"; import { Readable } from "stream"; import https from "https"; import http from "http"; function mimeTypeForPath(filePath) { switch (extname(filePath).toLowerCase()) { case ".png": return "image/png"; case ".jpg": case ".jpeg": return "image/jpeg"; case ".webp": return "image/webp"; case ".gif": return "image/gif"; case ".svg": return "image/svg+xml"; case ".pdf": return "application/pdf"; case ".mp4": return "video/mp4"; case ".webm": return "video/webm"; case ".mp3": return "audio/mpeg"; case ".wav": return "audio/wav"; case ".md": return "text/markdown"; case ".txt": return "text/plain"; case ".json": return "application/json"; case ".zip": return "application/zip"; default: return "application/octet-stream"; } } function putArtifactBytes(uploadUrl, data, mimeType, onProgress) { return new Promise((resolve, reject) => { const url = new URL(uploadUrl); const transport = url.protocol === "https:" ? https : http; const req = transport.request( url, { method: "PUT", headers: { "Content-Type": mimeType, "Content-Length": String(data.length) } }, (res) => { const chunks = []; res.on("data", (chunk) => chunks.push(chunk)); res.on("end", () => { if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { resolve(); } else { const body = Buffer.concat(chunks).toString("utf8").slice(0, 500); reject( new Error( `Upload failed (${res.statusCode ?? "unknown"}): ${body || "no response body"}` ) ); } }); } ); req.on("error", reject); let uploaded = 0; const stream = Readable.from([data]); stream.on("data", (chunk) => { uploaded += chunk.length; onProgress?.(Math.round(uploaded / data.length * 100)); }); stream.on("error", reject); stream.pipe(req); }); } // src/lib/worker-attestation.ts var requirementCache = /* @__PURE__ */ new Map(); async function defaultGet(path) { const { apiGet: apiGet2 } = await import("./api-JLEPMDTE.js"); const { signReadAuth: signReadAuth2 } = await import("./read-auth-GIBB7BSS.js"); const { taskAccessGrantHeaders: taskAccessGrantHeaders2 } = await import("./task-access-grants-FS7OSOE2.js"); const taskId = /^\/api\/tasks\/([^/?]+)/.exec(path)?.[1] ?? ""; return apiGet2(path, { headers: { ...(await signReadAuth2())?.headers ?? {}, ...await taskAccessGrantHeaders2(taskId) ?? {} } }); } var REQUIREMENT_CACHE_MS = 6e4; function taskRequirementsOf(taskId, get = defaultGet, now = Date.now) { const key = taskId.toLowerCase(); const cached = requirementCache.get(key); if (cached && cached.until > now()) return cached.pending; const entry = { pending: Promise.resolve(null), until: 0 }; entry.pending = get(`/api/tasks/${taskId}`).then((task) => task ?? null).catch(() => { if (requirementCache.get(key) === entry) requirementCache.delete(key); return null; }); entry.until = now() + REQUIREMENT_CACHE_MS; requirementCache.set(key, entry); return entry.pending; } async function attestedWorkerAction(taskId, keystore, run, task) { if (!keystore.tapKey) return run(void 0); return runWorkerAction({ taskId, task: task === void 0 ? await taskRequirementsOf(taskId) : task, keystore, run }); } var GATED_WRITE_PATH = /^\/api\/tasks\/([^/?]+)\/(?:claim|pitches|bids|proofs|submissions|submissions\/from-keys)(?:\?.*)?$/; function gatedWriteTaskId(path) { return GATED_WRITE_PATH.exec(path)?.[1]; } async function maybeAttestedWrite(path, send, get) { const taskId = gatedWriteTaskId(path); if (!taskId) return send(void 0); const keystore = await loadKeystore().catch(() => void 0); if (!keystore?.tapKey) return send(void 0); const task = await taskRequirementsOf(taskId, get); if (!taskRequiresTap(task)) return send(void 0); return runWorkerAction({ taskId, task, keystore, run: send }); } // src/lib/task-sdk.ts async function putWithProgress(upload) { process.stderr.write(` Uploading ${upload.fileName}... `); await putArtifactBytes(upload.uploadUrl, upload.data, upload.mimeType, (percent) => { process.stderr.write(`\r ${upload.fileName}: ${percent}%`); }); process.stderr.write("\n"); } var cliTaskTransport = { get: (path, headers) => apiGet(path, { headers }), // A write the caller gave no key is sent exactly as before the SDK existed, with no options, // so `apiPost` resolves its key itself (explicit, then TASKMARKET_IDEMPOTENCY_KEY, then fresh). // A gated worker action on a task that requires `tap` is TAP-signed (lib/worker-attestation.ts). post: (path, body, options) => maybeAttestedWrite( path, (tap) => tap ? apiPost(path, body, { ...options, tap }) : options === void 0 ? apiPost(path, body) : apiPost(path, body, options) ), feePost: (path, body, options) => maybeAttestedWrite(path, (tap) => x402FeePost(path, body, tap ? { ...options, tap } : options)), put: putWithProgress }; function keystoreTaskSigner(keystore) { return { address: keystore.walletAddress, signMessage: (message) => signMessage(message, keystore) }; } function cliTaskReadContext() { return { transport: cliTaskTransport, readAuth: signReadAuth, accessGrantHeaders: taskAccessGrantHeaders }; } function cliTaskWriteContext(keystore) { return { ...cliTaskReadContext(), signer: keystoreTaskSigner(keystore) }; } // src/sdk/tasks.ts import { promises as fsPromises } from "fs"; import { basename } from "path"; import { createHash } from "crypto"; import { keccak256 } from "viem"; var TaskOperationError = class extends Error { code; constructor(code, message) { super(message); this.name = "TaskOperationError"; this.code = code; } }; function withChainQuery(path, chainId) { if (chainId === void 0) return path; return `${path}${path.includes("?") ? "&" : "?"}chainId=${chainId}`; } function withChainBody(body, chainId) { return chainId === void 0 ? body : { ...body, chainId }; } function writeOptions(idempotencyKey) { return idempotencyKey === void 0 ? void 0 : { idempotencyKey }; } async function readAuthHeaders(ctx) { return (await ctx.readAuth?.())?.headers ?? {}; } async function getTask(ctx, ref) { const headers = { ...await readAuthHeaders(ctx), ...await ctx.accessGrantHeaders?.(ref.taskId) ?? {} }; const task = await ctx.transport.get( withChainQuery(`/api/tasks/${ref.taskId}`, ref.chainId), headers ); return task ?? null; } async function getTaskTerms(ctx, ref) { const headers = { ...await readAuthHeaders(ctx), ...await ctx.accessGrantHeaders?.(ref.taskId) ?? {} }; const detail = await ctx.transport.get( withChainQuery(`/api/tasks/${ref.taskId}/terms`, ref.chainId), headers ); return detail ?? null; } async function listTasks(ctx, filter = {}) { const params = new URLSearchParams(); if (filter.chainId !== void 0) params.set("chainId", String(filter.chainId)); if (filter.q) params.set("q", filter.q); if (filter.sort) params.set("sort", filter.sort); if (filter.status) params.set("status", filter.status); if (filter.phase) params.set("phase", filter.phase); if (filter.mode) params.set("mode", filter.mode); if (filter.auctionType) params.set("auctionType", filter.auctionType); if (filter.tags) params.set("tags", filter.tags); if (filter.capabilities) params.set("capabilities", filter.capabilities); if (filter.minReward) params.set("minReward", filter.minReward); if (filter.maxReward) params.set("maxReward", filter.maxReward); if (filter.minTotalReward) params.set("minTotalReward", filter.minTotalReward); if (filter.deadlineHours) params.set("deadlineHours", filter.deadlineHours); if (filter.sponsored) params.set("sponsored", "true"); if (filter.identity) params.set("identity", filter.identity); if (filter.verifiedBy) params.set("verifiedBy", filter.verifiedBy); if (filter.requester) params.set("requester", filter.requester); if (filter.hire !== void 0) params.set("hire", String(filter.hire)); if (filter.hireKind) params.set("hireKind", filter.hireKind); if (filter.hireState) params.set("hireState", filter.hireState); if (filter.hireServiceId) params.set("hireServiceId", filter.hireServiceId); if (filter.reservedWorker) params.set("reservedWorker", filter.reservedWorker); if (filter.limit) params.set("limit", filter.limit); if (filter.cursor) params.set("cursor", filter.cursor); const result = await ctx.transport.get( `/api/tasks?${params.toString()}`, await readAuthHeaders(ctx) ); return { tasks: result.tasks, hasMore: result.hasMore, nextCursor: result.nextCursor }; } async function listTaskSubmissions(ctx, ref, filter = {}) { const headers = await readAuthHeaders(ctx); const query = filter.passedCheck ? `?${new URLSearchParams({ passedCheck: filter.passedCheck }).toString()}` : ""; return await ctx.transport.get( withChainQuery(`/api/tasks/${ref.taskId}/submissions${query}`, ref.chainId), headers ); } async function listWorkerSubmissions(ctx, options = {}) { const auth = await ctx.readAuth?.(); const address = options.address ?? auth?.walletAddress; if (!address) { throw new TaskOperationError( "no_address", "No worker address was given and there is no reading wallet to default to" ); } return ctx.transport.get(`/api/submissions/mine?workerAddress=${encodeURIComponent(address)}`, { ...auth?.headers ?? {} }); } async function getWorkerInbox(ctx) { const auth = await ctx.readAuth?.(); if (!auth?.walletAddress) { throw new TaskOperationError("no_address", "There is no reading wallet to ask about"); } const raw = await ctx.transport.get( `/api/agents/inbox?address=${encodeURIComponent(auth.walletAddress)}`, auth.headers ); return { address: auth.walletAddress, proven: Object.keys(auth.headers).length > 0, inbox: { asRequester: raw?.asRequester ?? [], asWorker: raw?.asWorker ?? [], invitedPrivateTasks: raw?.invitedPrivateTasks ?? [] } }; } async function getAgentStats(ctx, options) { return await ctx.transport.get( withChainQuery( `/api/agents/stats?address=${encodeURIComponent(options.address)}`, options.chainId ), {} ); } function taskAwardsOf(task) { const parsed = TaskAwardSchema.array().safeParse(task.awards ?? []); if (!parsed.success) { throw new TaskOperationError( "malformed_response", `Task ${task.id} returned awards that do not match the award schema` ); } return parsed.data; } async function claimTask(ctx, ref, options = {}) { const signature = await ctx.signer.signMessage(buildClaimMessage(ref.taskId)); const { data, idempotencyKey } = await ctx.transport.post( `/api/tasks/${ref.taskId}/claim`, withChainBody({ workerAddress: ctx.signer.address, signature }, ref.chainId), writeOptions(options.idempotencyKey) ); return { claimId: data.claimId, idempotencyKey }; } async function authorizeUploads(ctx, ref) { return { signature: await ctx.signer.signMessage(buildSubmitMessage(ref.taskId)) }; } async function readArtifactFile(file) { const fileName = file.fileName ?? basename(file.path); const data = await fsPromises.readFile(file.path); if (data.length === 0) { throw new TaskOperationError("empty_artifact", `File is empty: ${fileName}`); } return { data, artifact: { fileName, mimeType: file.mimeType ?? mimeTypeForPath(file.path), role: file.role, sizeBytes: data.length, sha256Hash: createHash("sha256").update(data).digest("hex"), keccak256Hash: keccak256(new Uint8Array(data)) } }; } async function requestArtifactUpload(ctx, ref, artifact, authorization) { const { data: { uploadUrl, artifactKey } } = await ctx.transport.post( `/api/tasks/${ref.taskId}/submissions/request-upload-url`, withChainBody( { taskId: ref.taskId, workerAddress: ctx.signer.address, signature: authorization.signature, fileName: artifact.fileName, mimeType: artifact.mimeType, role: artifact.role, sizeBytes: artifact.sizeBytes }, ref.chainId ) ); return { uploadUrl, artifactKey }; } async function uploadArtifact(ctx, ref, file, authorization) { const { data, artifact } = await readArtifactFile(file); const { uploadUrl, artifactKey } = await requestArtifactUpload(ctx, ref, artifact, authorization); await ctx.transport.put({ uploadUrl, data, mimeType: artifact.mimeType, fileName: artifact.fileName }); return { artifactKey, ...artifact }; } async function submitUploadedArtifacts(ctx, ref, artifacts, options = {}) { if (artifacts.length === 0) { throw new TaskOperationError("no_artifacts", "At least one artifact is required"); } const signature = await ctx.signer.signMessage( buildSubmitMessage( ref.taskId, artifacts.map((artifact) => artifact.artifactKey) ) ); const { data, idempotencyKey } = await ctx.transport.feePost( `/api/tasks/${ref.taskId}/submissions/from-keys`, withChainBody( { taskId: ref.taskId, workerAddress: ctx.signer.address, artifacts, signature }, ref.chainId ), options ); if (isRecordingPendingResponse(data)) { return { submissionId: data.submissionId, idempotencyKey, recordingPending: { ...typeof data.intentId === "string" ? { intentId: data.intentId } : {}, ...typeof data.txHash === "string" ? { txHash: data.txHash } : {} } }; } return { submissionId: data.submissionId, idempotencyKey }; } async function submitTaskFiles(ctx, ref, files, options = {}) { if (files.length === 0) { throw new TaskOperationError("no_artifacts", "At least one file is required"); } const authorization = await authorizeUploads(ctx, ref); const artifacts = await Promise.all( files.map((file) => uploadArtifact(ctx, ref, file, authorization)) ); return submitUploadedArtifacts(ctx, ref, artifacts, options); } export { taskRequirementsOf, attestedWorkerAction, mimeTypeForPath, putArtifactBytes, cliTaskTransport, keystoreTaskSigner, cliTaskReadContext, cliTaskWriteContext, TaskOperationError, getTask, getTaskTerms, listTasks, listTaskSubmissions, listWorkerSubmissions, getWorkerInbox, getAgentStats, taskAwardsOf, claimTask, authorizeUploads, readArtifactFile, requestArtifactUpload, submitUploadedArtifacts, submitTaskFiles };