@lucid-agents/taskmarket 1.0.0 → 1.1.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 (2) hide show
  1. package/dist/index.js +132 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -638,9 +638,14 @@ var getCmd = new Command7("get").description("Get task details").argument("<task
638
638
  });
639
639
 
640
640
  // src/commands/task/submit.ts
641
- import { Command as Command8 } from "commander";
642
- import { promises as fs2 } from "fs";
641
+ import { Command as Command8, Option } from "commander";
642
+ import { promises as fsPromises } from "fs";
643
643
  import { basename, extname } from "path";
644
+ import { createHash } from "crypto";
645
+ import { Readable } from "stream";
646
+ import https from "https";
647
+ import http from "http";
648
+ import { keccak256 } from "viem";
644
649
  function collectFile(value, previous) {
645
650
  return [...previous, value];
646
651
  }
@@ -679,30 +684,98 @@ function mimeTypeForPath(filePath) {
679
684
  return "application/octet-stream";
680
685
  }
681
686
  }
682
- var submitCmd = new Command8("submit").description("Submit work for a task").argument("<taskId>", "Task ID (0x-prefixed hex)").option("--file <path>", "Path to a submission file (repeatable)", collectFile, []).action(async (taskId, opts) => {
687
+ function streamingPut(uploadUrl, data, mimeType, label) {
688
+ return new Promise((resolve, reject) => {
689
+ const url = new URL(uploadUrl);
690
+ const transport = url.protocol === "https:" ? https : http;
691
+ const req = transport.request(
692
+ url,
693
+ {
694
+ method: "PUT",
695
+ headers: {
696
+ "Content-Type": mimeType,
697
+ "Content-Length": String(data.length)
698
+ }
699
+ },
700
+ (res) => {
701
+ const chunks = [];
702
+ res.on("data", (chunk) => chunks.push(chunk));
703
+ res.on("end", () => {
704
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
705
+ process.stderr.write("\n");
706
+ resolve();
707
+ } else {
708
+ const body = Buffer.concat(chunks).toString("utf8").slice(0, 500);
709
+ reject(
710
+ new Error(
711
+ `Upload failed (${res.statusCode ?? "unknown"}): ${body || "no response body"}`
712
+ )
713
+ );
714
+ }
715
+ });
716
+ }
717
+ );
718
+ req.on("error", reject);
719
+ let uploaded = 0;
720
+ const stream = Readable.from([data]);
721
+ stream.on("data", (chunk) => {
722
+ uploaded += chunk.length;
723
+ const pct = Math.round(uploaded / data.length * 100);
724
+ process.stderr.write(`\r ${label}: ${pct}%`);
725
+ });
726
+ stream.on("error", reject);
727
+ stream.pipe(req);
728
+ });
729
+ }
730
+ var submitCmd = new Command8("submit").description("Submit work for a task").argument("<taskId>", "Task ID (0x-prefixed hex)").option("--file <path>", "Path to a submission file (repeatable)", collectFile, []).addOption(
731
+ new Option("--role <role>", "Artifact role applied to all files").choices(["preview", "source", "final", "attachment"]).default("attachment")
732
+ ).action(async (taskId, opts) => {
683
733
  try {
684
734
  if (!opts.file || opts.file.length === 0) {
685
735
  printError("--file is required");
686
736
  }
687
737
  const keystore = await loadKeystore();
688
738
  const signature = await signMessage(`taskmarket:submit:${taskId}`, keystore);
689
- const body = {
690
- workerAddress: keystore.walletAddress,
691
- signature,
692
- artifacts: await Promise.all(
693
- opts.file.map(async (filePath) => {
694
- const fileContent = await fs2.readFile(filePath);
695
- return {
739
+ const artifactInputs = await Promise.all(
740
+ opts.file.map(async (filePath) => {
741
+ const mimeType = mimeTypeForPath(filePath);
742
+ const data = await fsPromises.readFile(filePath);
743
+ if (data.length === 0) {
744
+ printError(`File is empty: ${basename(filePath)}`);
745
+ }
746
+ const { uploadUrl, artifactKey } = await apiPost(
747
+ `/api/tasks/${taskId}/submissions/request-upload-url`,
748
+ {
749
+ taskId,
750
+ workerAddress: keystore.walletAddress,
751
+ signature,
696
752
  fileName: basename(filePath),
697
- mimeType: mimeTypeForPath(filePath),
698
- role: "attachment",
699
- file: fileContent.toString("base64")
700
- };
701
- })
702
- )
703
- };
704
- const result = await apiPost(`/api/tasks/${taskId}/submissions`, {
705
- ...body
753
+ mimeType,
754
+ role: opts.role,
755
+ sizeBytes: data.length
756
+ }
757
+ );
758
+ const sha256Hash = createHash("sha256").update(data).digest("hex");
759
+ const keccak256Hash = keccak256(new Uint8Array(data));
760
+ process.stderr.write(` Uploading ${basename(filePath)}...
761
+ `);
762
+ await streamingPut(uploadUrl, data, mimeType, basename(filePath));
763
+ return {
764
+ artifactKey,
765
+ fileName: basename(filePath),
766
+ mimeType,
767
+ role: opts.role,
768
+ sizeBytes: data.length,
769
+ sha256Hash,
770
+ keccak256Hash
771
+ };
772
+ })
773
+ );
774
+ const result = await apiPost(`/api/tasks/${taskId}/submissions/from-keys`, {
775
+ taskId,
776
+ workerAddress: keystore.walletAddress,
777
+ artifacts: artifactInputs,
778
+ signature
706
779
  });
707
780
  printResult({ submissionId: result.submissionId });
708
781
  } finally {
@@ -831,10 +904,10 @@ var pitchCmd = new Command13("pitch").description("Submit a pitch for a task (co
831
904
 
832
905
  // src/commands/task/select-worker.ts
833
906
  import { Command as Command14 } from "commander";
834
- import { keccak256, toBytes } from "viem";
907
+ import { keccak256 as keccak2562, toBytes } from "viem";
835
908
  var selectWorkerCmd = new Command14("select-worker").description("Select a worker from pitches (requester only)").argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--pitch <pitchId>", "Pitch ID to select").requiredOption("--worker <address>", "Worker wallet address to assign").action(async (taskId, opts) => {
836
909
  const keystore = await loadKeystore();
837
- const hash = keccak256(toBytes(taskId + opts.pitch + opts.worker));
910
+ const hash = keccak2562(toBytes(taskId + opts.pitch + opts.worker));
838
911
  const signature = await signMessage(hash, keystore);
839
912
  await apiPost(`/api/tasks/${taskId}/pitches/select`, {
840
913
  taskId,
@@ -1510,11 +1583,11 @@ var withdrawCommand = new Command38("withdraw").description("Withdraw USDC to th
1510
1583
 
1511
1584
  // src/commands/encrypt.ts
1512
1585
  import { Command as Command39 } from "commander";
1513
- import { promises as fs3 } from "fs";
1586
+ import { promises as fs2 } from "fs";
1514
1587
  var encryptCommand = new Command39("encrypt").description("Encrypt a file with ECIES using wallet keys").argument("<file>", "Path to the file to encrypt").option("--recipient <address>", "Recipient wallet address (default: self)").option("--output <path>", "Output path (default: <file>.enc)").action(async (file, opts) => {
1515
1588
  let plaintext;
1516
1589
  try {
1517
- plaintext = await fs3.readFile(file);
1590
+ plaintext = await fs2.readFile(file);
1518
1591
  } catch {
1519
1592
  printError(`Cannot read file: ${file}`);
1520
1593
  }
@@ -1551,7 +1624,7 @@ var encryptCommand = new Command39("encrypt").description("Encrypt a file with E
1551
1624
  }
1552
1625
  const encrypted = encryptForRecipient(plaintext, recipientPubKey);
1553
1626
  const outputPath = opts.output ?? `${file}.enc`;
1554
- await fs3.writeFile(outputPath, encrypted);
1627
+ await fs2.writeFile(outputPath, encrypted);
1555
1628
  printResult({
1556
1629
  output: outputPath,
1557
1630
  bytes: encrypted.length,
@@ -1561,11 +1634,11 @@ var encryptCommand = new Command39("encrypt").description("Encrypt a file with E
1561
1634
 
1562
1635
  // src/commands/decrypt.ts
1563
1636
  import { Command as Command40 } from "commander";
1564
- import { promises as fs4 } from "fs";
1637
+ import { promises as fs3 } from "fs";
1565
1638
  var decryptCommand = new Command40("decrypt").description("Decrypt a file using your wallet key").argument("<file>", "Path to the encrypted file").option("--output <path>", "Output path (default: strips .enc, otherwise appends .dec)").action(async (file, opts) => {
1566
1639
  let ciphertext;
1567
1640
  try {
1568
- ciphertext = await fs4.readFile(file);
1641
+ ciphertext = await fs3.readFile(file);
1569
1642
  } catch {
1570
1643
  printError(`Cannot read file: ${file}`);
1571
1644
  }
@@ -1592,7 +1665,7 @@ var decryptCommand = new Command40("decrypt").description("Decrypt a file using
1592
1665
  } else {
1593
1666
  outputPath = `${file}.dec`;
1594
1667
  }
1595
- await fs4.writeFile(outputPath, plaintext);
1668
+ await fs3.writeFile(outputPath, plaintext);
1596
1669
  printResult({
1597
1670
  output: outputPath,
1598
1671
  bytes: plaintext.length
@@ -5806,9 +5879,9 @@ var UpdateTaskInputSchema = external_exports.object({
5806
5879
  pitchDeadline: external_exports.number().optional(),
5807
5880
  auctionFloorPrice: external_exports.string().optional(),
5808
5881
  auctionStartPrice: external_exports.string().optional(),
5809
- description: external_exports.string().optional(),
5810
- tags: external_exports.array(external_exports.string()).optional(),
5811
- metricDescription: external_exports.string().optional()
5882
+ description: external_exports.string().max(2e3, "Description is too long").optional(),
5883
+ tags: external_exports.array(external_exports.string()).max(10, "Maximum 10 tags allowed").optional(),
5884
+ metricDescription: external_exports.string().max(500).optional()
5812
5885
  });
5813
5886
 
5814
5887
  // ../../packages/shared/dist/schemas/submission.schemas.js
@@ -5878,6 +5951,35 @@ var DownloadResponseSchema = external_exports.object({
5878
5951
  keyBundle: external_exports.string().nullable().optional(),
5879
5952
  expiresAt: external_exports.string()
5880
5953
  });
5954
+ var MAX_ARTIFACT_BYTES = 500 * 1024 * 1024;
5955
+ var RequestUploadUrlInputSchema = external_exports.object({
5956
+ taskId: external_exports.string(),
5957
+ workerAddress: external_exports.string(),
5958
+ signature: external_exports.string(),
5959
+ fileName: external_exports.string().min(1, "File name is required").max(255, "File name is too long"),
5960
+ mimeType: external_exports.string().min(1, "MIME type is required").max(120, "MIME type is too long"),
5961
+ role: ArtifactRole.optional().default("attachment"),
5962
+ sizeBytes: external_exports.number().int().positive("Size must be positive").max(MAX_ARTIFACT_BYTES, "File must not exceed 500 MB")
5963
+ });
5964
+ var RequestUploadUrlOutputSchema = external_exports.object({
5965
+ uploadUrl: external_exports.string(),
5966
+ artifactKey: external_exports.string()
5967
+ });
5968
+ var ArtifactKeyInputSchema = external_exports.object({
5969
+ artifactKey: external_exports.string().min(1, "Artifact key is required"),
5970
+ fileName: external_exports.string().min(1, "File name is required").max(255, "File name is too long"),
5971
+ mimeType: external_exports.string().min(1, "MIME type is required").max(120, "MIME type is too long"),
5972
+ role: ArtifactRole.optional().default("attachment"),
5973
+ sizeBytes: external_exports.number().int().positive("Size must be positive").max(MAX_ARTIFACT_BYTES, "File must not exceed 500 MB"),
5974
+ sha256Hash: external_exports.string().regex(/^[0-9a-f]{64}$/, "sha256Hash must be 64 lowercase hex chars"),
5975
+ keccak256Hash: external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/, "keccak256Hash must be 0x followed by 64 hex chars")
5976
+ });
5977
+ var SubmissionCreateFromKeysSchema = external_exports.object({
5978
+ taskId: external_exports.string(),
5979
+ workerAddress: external_exports.string(),
5980
+ artifacts: external_exports.array(ArtifactKeyInputSchema).min(1).max(20),
5981
+ signature: external_exports.string()
5982
+ });
5881
5983
 
5882
5984
  // ../../packages/shared/dist/schemas/agent.schemas.js
5883
5985
  var ActorType = external_exports.enum(["agent", "human"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucid-agents/taskmarket",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "taskmarket": "./dist/index.js"