@t2000/cli 10.12.0 → 10.13.1

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/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  MAX_JOB_USDC,
9
9
  MIN_LP_USDC,
10
10
  T2000,
11
+ assertBuyerRequirements,
11
12
  buildPublishAgentCoinTx,
12
13
  buildTokenizeTx,
13
14
  clearLimits,
@@ -33,7 +34,7 @@ import {
33
34
  verifyJobForSeller,
34
35
  verifyReceipt,
35
36
  walletExists
36
- } from "./chunk-322SPE2Y.js";
37
+ } from "./chunk-I6UGZOTH.js";
37
38
  import "./chunk-634W6JCI.js";
38
39
  import "./chunk-3ZUWXUSN.js";
39
40
  import "./chunk-QSITA6GU.js";
@@ -22286,7 +22287,7 @@ async function runEstimate(url, opts) {
22286
22287
  let req = accepts.find((a) => a.scheme === "exact" && a.network?.startsWith("sui:")) ?? accepts[0];
22287
22288
  let dialect = "x402";
22288
22289
  if (!req) {
22289
- const { parseMppSuiChallenge } = await import("./dist-6X5CQWZV.js");
22290
+ const { parseMppSuiChallenge } = await import("./dist-G2XQHKA6.js");
22290
22291
  const challenge = await parseMppSuiChallenge(response);
22291
22292
  if (!challenge) {
22292
22293
  throw new Error(
@@ -23390,7 +23391,7 @@ function registerMcpStart(parent) {
23390
23391
  parent.command("start", { isDefault: true }).description("Start MCP server (stdio transport \u2014 for AI client integration)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").action(async (opts) => {
23391
23392
  let mod;
23392
23393
  try {
23393
- mod = await import("./dist-MUMCWXCF.js");
23394
+ mod = await import("./dist-GIJDTUKJ.js");
23394
23395
  } catch {
23395
23396
  console.error("MCP server not installed. Run:\n npm install -g @t2000/mcp");
23396
23397
  process.exit(1);
@@ -24833,15 +24834,33 @@ function parseDuration(input) {
24833
24834
  if (ms <= 0) throw new Error(`Duration must be positive (got "${input}").`);
24834
24835
  return Math.round(ms);
24835
24836
  }
24836
- async function resolveCommitment(input) {
24837
- if (/^0x[0-9a-fA-F]+$/.test(input) && input.length % 2 === 0) return input;
24837
+ var SHA256_HEX_RE = /^0x[0-9a-fA-F]{64}$/;
24838
+ var SPEC_STORE_MAX_BYTES = 16 * 1024;
24839
+ async function resolveSpecUpload(base, input) {
24840
+ const trimmed = input.trim();
24841
+ if (SHA256_HEX_RE.test(trimmed)) {
24842
+ return { hash: trimmed.toLowerCase(), uploaded: false };
24843
+ }
24838
24844
  let bytes;
24839
24845
  try {
24840
24846
  bytes = await readFile3(input);
24841
24847
  } catch {
24842
24848
  bytes = Buffer.from(input, "utf8");
24843
24849
  }
24844
- return `0x${createHash("sha256").update(bytes).digest("hex")}`;
24850
+ if (bytes.length > SPEC_STORE_MAX_BYTES) {
24851
+ throw new Error(
24852
+ `Content is ${bytes.length} bytes \u2014 the job-spec store caps at 16 KiB. Upload a short note that LINKS the artifact (URL / IPFS), or pin a precomputed commitment with --hash-only 0x<sha256>.`
24853
+ );
24854
+ }
24855
+ let text;
24856
+ try {
24857
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
24858
+ } catch {
24859
+ throw new Error(
24860
+ "Content is not UTF-8 text \u2014 the job-spec store holds text only. Upload a short note that LINKS the artifact, or pin a precomputed commitment with --hash-only 0x<sha256>."
24861
+ );
24862
+ }
24863
+ return { hash: `0x${await putJobSpec(base, text)}`, uploaded: true };
24845
24864
  }
24846
24865
  function stateColor(state) {
24847
24866
  if (state === "released") return import_picocolors15.default.green(state);
@@ -24915,7 +24934,7 @@ deadline). v1 caps jobs at ${MAX_JOB_USDC} USDC.
24915
24934
  Typical flow:
24916
24935
  buyer $ t2 job create 5 0xSELLER --spec brief.md --deadline 24h
24917
24936
  seller $ t2 job verify 0xJOB --price 5
24918
- seller $ t2 job deliver 0xJOB report.pdf
24937
+ seller $ t2 job deliver 0xJOB report.md
24919
24938
  buyer $ t2 job release 0xJOB (or: t2 job reject 0xJOB)
24920
24939
  either $ t2 job watch 0xJOB
24921
24940
  seller $ t2 job watch --mine (the provider inbox \u2014 all your jobs)
@@ -24927,7 +24946,7 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
24927
24946
  seller $ t2 job spec 0xJOB (read the buyer's requirements)
24928
24947
  `
24929
24948
  );
24930
- group.command("create").argument("[amount]", `USDC to escrow (max ${MAX_JOB_USDC}; omit when buying a --service)`).argument("[seller]", "The seller's Sui address (omit when buying a --service)").description("Create + fund an escrow job in one transaction (buyer)").option("--spec <file-or-text>", "Job spec \u2014 a file path or inline text (hashed on-chain), or a 0x\u2026 hash").option("--agent <address>", "Buy a service: the seller's agent address").option("--service <slug>", "The service slug (see t2 browse / t2 service list <agent>)").option("--requirements <file-or-json-or-text>", "Your requirements for the service (what the seller asked for)").option("--deadline <duration>", "Time the seller has to deliver (e.g. 30m, 24h, 7d)", "24h").option("--review <duration>", "Your accept/reject window after delivery", "24h").option("--split <bps>", "Your share in bps if you reject (0\u201310000)", String(DEFAULT_REJECT_SPLIT_BPS)).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(
24949
+ group.command("create").argument("[amount]", `USDC to escrow (max ${MAX_JOB_USDC}; omit when buying a --service)`).argument("[seller]", "The seller's Sui address (omit when buying a --service)").description("Create + fund an escrow job in one transaction (buyer)").option("--spec <file-or-text>", "Job spec \u2014 a file path or inline text (UPLOADED so the seller can read it; sha256 pinned on-chain), or a bare 0x\u2026 sha256 (confidential: pins without uploading)").option("--agent <address>", "Buy a service: the seller's agent address").option("--service <slug>", "The service slug (see t2 browse / t2 service list <agent>)").option("--requirements <file-or-json-or-text>", "What the seller asked buyers to provide \u2014 if the listing lists JSON keys, fill EVERY key (JSON object; extra keys OK)").option("--deadline <duration>", "Time the seller has to deliver (e.g. 30m, 24h, 7d)", "24h").option("--review <duration>", "Your accept/reject window after delivery", "24h").option("--split <bps>", "Your share in bps if you reject (0\u201310000)", String(DEFAULT_REJECT_SPLIT_BPS)).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(
24931
24950
  async (amountArg, sellerArg, opts) => {
24932
24951
  try {
24933
24952
  const base = opts.api ?? DEFAULT_API_BASE5;
@@ -24964,17 +24983,7 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
24964
24983
  requirements = text.trim();
24965
24984
  }
24966
24985
  }
24967
- if (service.requirements != null && requirements == null) {
24968
- const want = typeof service.requirements === "string" ? service.requirements : `JSON matching: ${JSON.stringify(service.requirements)}`;
24969
- throw new Error(
24970
- `This service needs --requirements. The seller asks for: ${want}`
24971
- );
24972
- }
24973
- if (service.requirements != null && typeof service.requirements === "object" && (typeof requirements !== "object" || requirements === null)) {
24974
- throw new Error(
24975
- `This service expects JSON requirements matching: ${JSON.stringify(service.requirements)}`
24976
- );
24977
- }
24986
+ assertBuyerRequirements(service.requirements, requirements);
24978
24987
  const buyer = (await withAgent({ keyPath: opts.key })).address();
24979
24988
  const spec = JSON.stringify({
24980
24989
  type: "t2-acp-job-spec@1",
@@ -25009,7 +25018,7 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
25009
25018
  throw new Error(`Amount must be a positive number (got "${amountArg}").`);
25010
25019
  }
25011
25020
  seller = validateAddress(sellerArg);
25012
- specHash = await resolveCommitment(opts.spec);
25021
+ ({ hash: specHash } = await resolveSpecUpload(base, opts.spec));
25013
25022
  deliverByMs = Date.now() + parseDuration(opts.deadline);
25014
25023
  reviewWindowMs = opts.review ? parseDuration(opts.review) : DEFAULT_REVIEW_WINDOW_MS;
25015
25024
  rejectSplitBps = Number.parseInt(opts.split, 10);
@@ -25085,23 +25094,39 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
25085
25094
  handleError(error);
25086
25095
  }
25087
25096
  });
25088
- group.command("deliver").argument("<jobId>", "The Job object id (0x\u2026)").argument("<proof>", "Delivery artifact \u2014 a file path or text (hashed on-chain), or a 0x\u2026 hash").description("Post your proof-of-delivery before the deadline (seller)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (jobId, proof, opts) => {
25097
+ group.command("deliver").argument("<jobId>", "The Job object id (0x\u2026)").argument("<proof>", "Delivery body \u2014 a file path or text (UPLOADED so the buyer can read it; sha256 pinned on-chain), or a bare 0x\u2026 sha256").description("Post your delivery before the deadline (seller) \u2014 the body uploads to the job-spec store so the buyer can read it, and its sha256 pins on-chain").option("--hash-only", "Pin <proof> as a precomputed 0x\u2026 sha256 WITHOUT uploading a body \u2014 the confidential / large-artifact path (the buyer can't read it on-platform; hand the artifact over out-of-band)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (jobId, proof, opts) => {
25089
25098
  try {
25090
- const deliveryHash = await resolveCommitment(proof);
25099
+ const base = opts.api ?? DEFAULT_API_BASE5;
25100
+ let deliveryHash;
25101
+ let uploaded = false;
25102
+ if (opts.hashOnly) {
25103
+ const hash = proof.trim().toLowerCase();
25104
+ if (!SHA256_HEX_RE.test(hash)) {
25105
+ throw new Error("--hash-only expects a 0x\u2026 64-char sha256 (e.g. from `shasum -a 256`).");
25106
+ }
25107
+ deliveryHash = hash;
25108
+ } else {
25109
+ ({ hash: deliveryHash, uploaded } = await resolveSpecUpload(base, proof));
25110
+ }
25091
25111
  const { digest } = await sponsoredJobVerb({
25092
- base: opts.api ?? DEFAULT_API_BASE5,
25112
+ base,
25093
25113
  keyPath: opts.key,
25094
25114
  action: "deliver",
25095
25115
  params: { jobId, deliveryHash }
25096
25116
  });
25097
25117
  if (isJsonMode()) {
25098
- printJson({ jobId, deliveryHash, digest });
25118
+ printJson({ jobId, deliveryHash, uploaded, digest });
25099
25119
  return;
25100
25120
  }
25101
25121
  printBlank();
25102
25122
  printSuccess("Delivery posted \u2014 the buyer's review window is now open.");
25103
25123
  printKeyValue("Delivery hash", deliveryHash);
25104
25124
  if (digest) printKeyValue("Tx", digest);
25125
+ if (uploaded) {
25126
+ printInfo("Body uploaded \u2014 the buyer reads it content-addressed (tamper-evident against the on-chain hash).");
25127
+ } else {
25128
+ printInfo("Hash-only commitment \u2014 the buyer cannot read the body on-platform; hand the artifact over out-of-band.");
25129
+ }
25105
25130
  printInfo("If the buyer neither accepts nor rejects before the window closes, anyone (including you) can run `t2 job release` to settle.");
25106
25131
  printBlank();
25107
25132
  } catch (error) {
@@ -25396,7 +25421,7 @@ Examples:
25396
25421
  );
25397
25422
  group.command("create").description("List a service under your Agent ID (re-run to update it)").requiredOption("--name <name>", "Service name (max 80 chars)").requiredOption("--price <usdc>", "Fixed price in USDC (0.01\u201350)").requiredOption("--sla <duration>", "Delivery SLA \u2014 e.g. 30m, 24h, 7d").requiredOption("--description <text>", "What this service is (max 2000 chars)").requiredOption("--deliverable <text>", "What the buyer receives (max 1000 chars)").option("--slug <slug>", "Machine name (default: derived from --name)").option(
25398
25423
  "--requirements <file-or-json-or-text>",
25399
- "What the buyer must provide \u2014 free text or a JSON schema (file path ok)"
25424
+ "What the buyer must provide \u2014 prefer a JSON object of required fields (keys enforced non-empty at hire); free text ok (file path ok)"
25400
25425
  ).option("--review <duration>", "Buyer's accept/reject window after delivery", "24h").option("--split <bps>", "Buyer's share in bps if they reject (0\u201310000)", "8000").option(
25401
25426
  "--category <category>",
25402
25427
  `Directory category for your listing: ${AGENT_CATEGORIES.join(" | ")} (required unless already set on your profile)`