@t2000/cli 10.13.4 → 10.14.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.
package/dist/index.js CHANGED
@@ -11,9 +11,13 @@ import {
11
11
  assertBuyerRequirements,
12
12
  buildPublishAgentCoinTx,
13
13
  buildTokenizeTx,
14
+ cancelOpenJob,
15
+ claimOpenJob,
14
16
  clearLimits,
17
+ createOpenJob,
15
18
  fetchService,
16
19
  formatUsd,
20
+ fundOpenJob,
17
21
  generateKeypair,
18
22
  getAddress,
19
23
  getJob,
@@ -24,17 +28,19 @@ import {
24
28
  jobActionsFor,
25
29
  keypairFromPrivateKey,
26
30
  listModels,
31
+ listOpenJobs,
27
32
  putJobSpec,
28
33
  saveBech32,
29
34
  saveKey,
30
35
  setLimits,
31
36
  truncateAddress,
37
+ unclaimOpenJob,
32
38
  validateAddress,
33
39
  validateAgentCoinParams,
34
40
  verifyJobForSeller,
35
41
  verifyReceipt,
36
42
  walletExists
37
- } from "./chunk-M3VP3LAY.js";
43
+ } from "./chunk-3ZC3KARI.js";
38
44
  import "./chunk-634W6JCI.js";
39
45
  import "./chunk-3ZUWXUSN.js";
40
46
  import "./chunk-QSITA6GU.js";
@@ -22287,7 +22293,7 @@ async function runEstimate(url, opts) {
22287
22293
  let req = accepts.find((a) => a.scheme === "exact" && a.network?.startsWith("sui:")) ?? accepts[0];
22288
22294
  let dialect = "x402";
22289
22295
  if (!req) {
22290
- const { parseMppSuiChallenge } = await import("./dist-4BBYOHH2.js");
22296
+ const { parseMppSuiChallenge } = await import("./dist-K6ZK2ZFY.js");
22291
22297
  const challenge = await parseMppSuiChallenge(response);
22292
22298
  if (!challenge) {
22293
22299
  throw new Error(
@@ -23391,7 +23397,7 @@ function registerMcpStart(parent) {
23391
23397
  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) => {
23392
23398
  let mod;
23393
23399
  try {
23394
- mod = await import("./dist-DTKT6EKV.js");
23400
+ mod = await import("./dist-ZATHFBVY.js");
23395
23401
  } catch {
23396
23402
  console.error("MCP server not installed. Run:\n npm install -g @t2000/mcp");
23397
23403
  process.exit(1);
@@ -24799,9 +24805,218 @@ function registerCheck(program3) {
24799
24805
  }
24800
24806
 
24801
24807
  // src/commands/job.ts
24802
- var import_picocolors15 = __toESM(require_picocolors(), 1);
24808
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
24803
24809
  import { createHash } from "crypto";
24810
+ import { readFile as readFile4 } from "fs/promises";
24811
+
24812
+ // src/commands/open.ts
24813
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
24804
24814
  import { readFile as readFile3 } from "fs/promises";
24815
+ var DEFAULT_API_BASE5 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
24816
+ var MAX_BRIEF_BYTES = 16 * 1024;
24817
+ async function resolveBrief(input) {
24818
+ let bytes;
24819
+ try {
24820
+ bytes = await readFile3(input);
24821
+ } catch {
24822
+ bytes = Buffer.from(input, "utf8");
24823
+ }
24824
+ if (bytes.length > MAX_BRIEF_BYTES) {
24825
+ throw new Error(
24826
+ `Brief is ${bytes.length} bytes \u2014 open-job briefs cap at 16 KiB. Keep it short and link out for more.`
24827
+ );
24828
+ }
24829
+ try {
24830
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes).trim();
24831
+ } catch {
24832
+ throw new Error("Brief is not UTF-8 text \u2014 the board holds text only.");
24833
+ }
24834
+ }
24835
+ function statusColor(status) {
24836
+ if (status === "open") return import_picocolors15.default.green(status);
24837
+ if (status === "claimed") return import_picocolors15.default.cyan(status);
24838
+ if (status === "funded") return import_picocolors15.default.dim(status);
24839
+ return import_picocolors15.default.yellow(status);
24840
+ }
24841
+ function fmtLeft(ms) {
24842
+ if (ms == null) return "";
24843
+ const left = ms - Date.now();
24844
+ if (left <= 0) return "now";
24845
+ const hours = Math.floor(left / 36e5);
24846
+ if (hours >= 48) return `${Math.floor(hours / 24)}d`;
24847
+ if (hours >= 1) return `${hours}h`;
24848
+ return `${Math.max(1, Math.floor(left / 6e4))}m`;
24849
+ }
24850
+ function printOpenJob(row) {
24851
+ printKeyValue("Open job", row.id);
24852
+ printKeyValue("Status", statusColor(row.status));
24853
+ printKeyValue("Title", row.title);
24854
+ printKeyValue("Budget", `$${row.maxUsdc.toFixed(2)} USDC`);
24855
+ printKeyValue("Deliver in", `${row.slaMinutes} min (once funded)`);
24856
+ if (row.status === "open") {
24857
+ printKeyValue("Open for", fmtLeft(row.openUntilMs));
24858
+ }
24859
+ if (row.buyerAgent) {
24860
+ printKeyValue("Buyer", `${row.buyerAgent.name} (#${row.buyerAgent.agentId})`);
24861
+ }
24862
+ if (row.seller) {
24863
+ printKeyValue(
24864
+ "Claimed by",
24865
+ row.sellerAgent ? `${row.sellerAgent.name} (${row.seller})` : row.seller
24866
+ );
24867
+ if (row.status === "claimed") {
24868
+ printKeyValue("Claim lapses", fmtLeft(row.claimExpiresAtMs));
24869
+ }
24870
+ }
24871
+ if (row.jobId) {
24872
+ printKeyValue("Job", row.jobId);
24873
+ }
24874
+ }
24875
+ function registerOpenVerbs(group) {
24876
+ group.command("open").description("Open \u2014 post the job to the public board with no ASP picked (buyer); holds no USDC, first claim wins").requiredOption("--title <text>", "The job's public name (up to 80 chars)").requiredOption("--brief <file-or-text>", "What you want delivered \u2014 PUBLIC, every ASP on the board reads it").requiredOption("--max <usdc>", `Budget escrowed at fund time (max ${MAX_JOB_USDC})`).option("--sla <duration>", "Delivery window once funded (e.g. 30m, 24h, 7d)", "24h").option("--open-for <duration>", "How long the posting stays claimable", "24h").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(
24877
+ async (opts) => {
24878
+ try {
24879
+ const base = opts.api ?? DEFAULT_API_BASE5;
24880
+ const maxUsdc = Number(opts.max);
24881
+ if (!Number.isFinite(maxUsdc) || maxUsdc <= 0 || maxUsdc > MAX_JOB_USDC) {
24882
+ throw new Error(`--max must be between 0.01 and ${MAX_JOB_USDC} USDC.`);
24883
+ }
24884
+ const brief = await resolveBrief(opts.brief);
24885
+ const agent = await withAgent({ keyPath: opts.key });
24886
+ const row = await createOpenJob(base, agent.signer, {
24887
+ title: opts.title.trim(),
24888
+ brief,
24889
+ maxUsdc,
24890
+ slaMinutes: Math.round(parseDuration(opts.sla) / 6e4),
24891
+ openHours: parseDuration(opts.openFor) / 36e5
24892
+ });
24893
+ if (isJsonMode()) {
24894
+ printJson({ openJob: row });
24895
+ return;
24896
+ }
24897
+ printBlank();
24898
+ printSuccess("Posted \u2014 your open job is on the board (no USDC moved).");
24899
+ printBlank();
24900
+ printOpenJob(row);
24901
+ printBlank();
24902
+ printInfo("When an ASP claims it: t2 job fund " + row.id);
24903
+ printBlank();
24904
+ } catch (error) {
24905
+ handleError(error);
24906
+ }
24907
+ }
24908
+ );
24909
+ group.command("board").argument("[query]", "Free-text filter across titles + briefs").description("Read the open board \u2014 claimable postings first (public, no wallet)").option("--status <status>", "open | claimed | funded | expired | cancelled", "open").option("--limit <n>", "Max rows (default 24)", "24").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (query, opts) => {
24910
+ try {
24911
+ const base = opts.api ?? DEFAULT_API_BASE5;
24912
+ const rows = await listOpenJobs(base, {
24913
+ status: opts.status,
24914
+ query,
24915
+ limit: Number(opts.limit)
24916
+ });
24917
+ if (isJsonMode()) {
24918
+ printJson({ total: rows.length, openJobs: rows });
24919
+ return;
24920
+ }
24921
+ printBlank();
24922
+ if (rows.length === 0) {
24923
+ printInfo(
24924
+ `No ${opts.status} jobs on the board` + (query ? ` matching "${query}"` : "") + '. Post one: t2 job open --title "\u2026" --brief "\u2026" --max 5'
24925
+ );
24926
+ printBlank();
24927
+ return;
24928
+ }
24929
+ for (const row of rows) {
24930
+ printLine(
24931
+ ` ${import_picocolors15.default.bold(row.title)} ${import_picocolors15.default.dim(`$${row.maxUsdc.toFixed(2)}`)} ${statusColor(row.status)}` + (row.status === "open" ? import_picocolors15.default.dim(` ${fmtLeft(row.openUntilMs)} left`) : "")
24932
+ );
24933
+ const brief = row.brief.replace(/\s+/g, " ");
24934
+ printLine(` ${import_picocolors15.default.dim(brief.length > 100 ? `${brief.slice(0, 100)}\u2026` : brief)}`);
24935
+ printLine(` ${import_picocolors15.default.dim(row.id)}`);
24936
+ printBlank();
24937
+ }
24938
+ printInfo("Claim one: t2 job claim <id> \u2014 no USDC, 2h to get funded.");
24939
+ printBlank();
24940
+ } catch (error) {
24941
+ handleError(error);
24942
+ }
24943
+ });
24944
+ group.command("claim").argument("<id>", "The open-job id (from t2 job board)").description("Claim an open job (ASP) \u2014 first claim wins; no USDC, 2h to get funded").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (id, opts) => {
24945
+ try {
24946
+ const base = opts.api ?? DEFAULT_API_BASE5;
24947
+ const agent = await withAgent({ keyPath: opts.key });
24948
+ const row = await claimOpenJob(base, agent.signer, id.trim());
24949
+ if (isJsonMode()) {
24950
+ printJson({ claimed: true, openJob: row });
24951
+ return;
24952
+ }
24953
+ printBlank();
24954
+ printSuccess("Claimed \u2014 the buyer has 2 hours to fund before it reopens.");
24955
+ printBlank();
24956
+ if (row) printOpenJob(row);
24957
+ printBlank();
24958
+ printInfo("Funded jobs land in your inbox: t2 job watch --mine");
24959
+ printBlank();
24960
+ } catch (error) {
24961
+ handleError(error);
24962
+ }
24963
+ });
24964
+ group.command("unclaim").argument("<id>", "The open-job id you claimed").description("Hand a claim back early (ASP) \u2014 the job reopens immediately").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (id, opts) => {
24965
+ try {
24966
+ const base = opts.api ?? DEFAULT_API_BASE5;
24967
+ const agent = await withAgent({ keyPath: opts.key });
24968
+ await unclaimOpenJob(base, agent.signer, id.trim());
24969
+ if (isJsonMode()) {
24970
+ printJson({ unclaimed: true });
24971
+ return;
24972
+ }
24973
+ printBlank();
24974
+ printSuccess("Unclaimed \u2014 the job is back on the board.");
24975
+ printBlank();
24976
+ } catch (error) {
24977
+ handleError(error);
24978
+ }
24979
+ });
24980
+ group.command("cancel").argument("<id>", "Your open-job id").description("Withdraw your own open posting (buyer) \u2014 only while still unclaimed").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (id, opts) => {
24981
+ try {
24982
+ const base = opts.api ?? DEFAULT_API_BASE5;
24983
+ const agent = await withAgent({ keyPath: opts.key });
24984
+ await cancelOpenJob(base, agent.signer, id.trim());
24985
+ if (isJsonMode()) {
24986
+ printJson({ cancelled: true });
24987
+ return;
24988
+ }
24989
+ printBlank();
24990
+ printSuccess("Cancelled \u2014 the posting is off the board.");
24991
+ printBlank();
24992
+ } catch (error) {
24993
+ handleError(error);
24994
+ }
24995
+ });
24996
+ group.command("fund").argument("<id>", "Your claimed open-job id").description("Fund a claimed opening (buyer) \u2014 escrows the budget into a normal Job, gasless").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (id, opts) => {
24997
+ try {
24998
+ const base = opts.api ?? DEFAULT_API_BASE5;
24999
+ const agent = await withAgent({ keyPath: opts.key });
25000
+ const { digest, jobId } = await fundOpenJob(base, agent.signer, id.trim());
25001
+ if (isJsonMode()) {
25002
+ printJson({ digest, jobId });
25003
+ return;
25004
+ }
25005
+ printBlank();
25006
+ printSuccess("Funded \u2014 the budget is escrowed in an on-chain Job.");
25007
+ printBlank();
25008
+ if (jobId) printKeyValue("Job", jobId);
25009
+ printKeyValue("Tx", digest);
25010
+ printBlank();
25011
+ printInfo(
25012
+ jobId ? `Track it: t2 job watch ${jobId}` : "Track it: t2 job watch --mine"
25013
+ );
25014
+ printBlank();
25015
+ } catch (error) {
25016
+ handleError(error);
25017
+ }
25018
+ });
25019
+ }
24805
25020
 
24806
25021
  // src/lib/services.ts
24807
25022
  async function fetchJson3(url, init) {
@@ -24820,7 +25035,7 @@ async function fetchJson3(url, init) {
24820
25035
  }
24821
25036
 
24822
25037
  // src/commands/job.ts
24823
- var DEFAULT_API_BASE5 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
25038
+ var DEFAULT_API_BASE6 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
24824
25039
  var DEFAULT_REVIEW_WINDOW_MS = 24 * 60 * 60 * 1e3;
24825
25040
  var DEFAULT_REJECT_SPLIT_BPS = 8e3;
24826
25041
  function parseDuration(input) {
@@ -24843,7 +25058,7 @@ async function resolveSpecUpload(base, input) {
24843
25058
  }
24844
25059
  let bytes;
24845
25060
  try {
24846
- bytes = await readFile3(input);
25061
+ bytes = await readFile4(input);
24847
25062
  } catch {
24848
25063
  bytes = Buffer.from(input, "utf8");
24849
25064
  }
@@ -24863,15 +25078,15 @@ async function resolveSpecUpload(base, input) {
24863
25078
  return { hash: `0x${await putJobSpec(base, text)}`, uploaded: true };
24864
25079
  }
24865
25080
  function stateColor(state) {
24866
- if (state === "released") return import_picocolors15.default.green(state);
24867
- if (state === "refunded" || state === "rejected") return import_picocolors15.default.yellow(state);
24868
- return import_picocolors15.default.cyan(state);
25081
+ if (state === "released") return import_picocolors16.default.green(state);
25082
+ if (state === "refunded" || state === "rejected") return import_picocolors16.default.yellow(state);
25083
+ return import_picocolors16.default.cyan(state);
24869
25084
  }
24870
25085
  function printJob(job, me) {
24871
25086
  printKeyValue("Job", job.id);
24872
25087
  printKeyValue("State", stateColor(job.state));
24873
- printKeyValue("Buyer", truncateAddress(job.buyer) + (me === job.buyer ? import_picocolors15.default.dim(" (you)") : ""));
24874
- printKeyValue("Seller", truncateAddress(job.seller) + (me === job.seller ? import_picocolors15.default.dim(" (you)") : ""));
25088
+ printKeyValue("Buyer", truncateAddress(job.buyer) + (me === job.buyer ? import_picocolors16.default.dim(" (you)") : ""));
25089
+ printKeyValue("Seller", truncateAddress(job.seller) + (me === job.seller ? import_picocolors16.default.dim(" (you)") : ""));
24875
25090
  printKeyValue("Amount", `$${job.amountUsdc.toFixed(2)} USDC`);
24876
25091
  printKeyValue("Deliver by", new Date(job.deliverByMs).toISOString());
24877
25092
  if (job.deliveredAtMs) {
@@ -24903,9 +25118,9 @@ function printInboxRow(job) {
24903
25118
  printLine(
24904
25119
  ` ${stateColor(job.state)} $${job.amountUsdc.toFixed(2)} USDC \xB7 from ${truncateAddress(job.buyer)}${deadline}`
24905
25120
  );
24906
- printLine(` ${import_picocolors15.default.dim(job.jobId)}`);
25121
+ printLine(` ${import_picocolors16.default.dim(job.jobId)}`);
24907
25122
  const hint = inboxHint(job);
24908
- if (hint) printLine(` ${import_picocolors15.default.dim("\u2192")} ${hint}`);
25123
+ if (hint) printLine(` ${import_picocolors16.default.dim("\u2192")} ${hint}`);
24909
25124
  }
24910
25125
  async function sponsoredJobVerb(opts) {
24911
25126
  const agent = await withAgent({ keyPath: opts.keyPath });
@@ -24932,24 +25147,29 @@ window) and a no-show seller can never keep funds (anyone may refund after the
24932
25147
  deadline). v1 caps jobs at ${MAX_JOB_USDC} USDC.
24933
25148
 
24934
25149
  Typical flow:
24935
- buyer $ t2 job create 5 0xSELLER --spec brief.md --deadline 24h
25150
+ buyer $ t2 job hire 5 0xSELLER --spec brief.md --deadline 24h
24936
25151
  seller $ t2 job verify 0xJOB --price 5
24937
25152
  seller $ t2 job deliver 0xJOB report.md
24938
25153
  buyer $ t2 job release 0xJOB (or: t2 job reject 0xJOB)
24939
25154
  either $ t2 job watch 0xJOB
24940
25155
  seller $ t2 job watch --mine (the provider inbox \u2014 all your jobs)
24941
25156
 
24942
- Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
25157
+ Hiring a LISTING (t2 ACP) \u2014 price + terms come from the listing:
24943
25158
  buyer $ t2 browse "market report"
24944
- buyer $ t2 job create --agent 0xSELLER --service sui-market-report \\
25159
+ buyer $ t2 job hire --agent 0xSELLER --service sui-market-report \\
24945
25160
  --requirements '{"token":"DEEP"}'
24946
25161
  seller $ t2 job spec 0xJOB (read the buyer's requirements)
25162
+
25163
+ No ASP picked at all? Open the job to the board instead:
25164
+ buyer $ t2 job open --title "Logo sketch" --brief brief.md --max 5
25165
+ ASP $ t2 job board \xB7 t2 job claim <id>
25166
+ buyer $ t2 job fund <id> (escrows into a normal job)
24947
25167
  `
24948
25168
  );
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(
25169
+ group.command("hire").alias("create").argument("[amount]", `USDC to escrow (max ${MAX_JOB_USDC}; omit when hiring a --service listing)`).argument("[seller]", "The ASP's Sui address (omit when hiring a --service listing)").description("Hire \u2014 fund an escrow job in one transaction (buyer): a listing (--agent + --service) or your own terms (amount + seller + --spec)").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>", "Hire a listing: the ASP'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_BASE6})`).action(
24950
25170
  async (amountArg, sellerArg, opts) => {
24951
25171
  try {
24952
- const base = opts.api ?? DEFAULT_API_BASE5;
25172
+ const base = opts.api ?? DEFAULT_API_BASE6;
24953
25173
  let amountUsdc;
24954
25174
  let seller;
24955
25175
  let specHash;
@@ -24974,7 +25194,7 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
24974
25194
  if (opts.requirements) {
24975
25195
  let text = opts.requirements;
24976
25196
  try {
24977
- text = await readFile3(opts.requirements, "utf8");
25197
+ text = await readFile4(opts.requirements, "utf8");
24978
25198
  } catch {
24979
25199
  }
24980
25200
  try {
@@ -25094,9 +25314,9 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
25094
25314
  handleError(error);
25095
25315
  }
25096
25316
  });
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) => {
25317
+ 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_BASE6})`).action(async (jobId, proof, opts) => {
25098
25318
  try {
25099
- const base = opts.api ?? DEFAULT_API_BASE5;
25319
+ const base = opts.api ?? DEFAULT_API_BASE6;
25100
25320
  let deliveryHash;
25101
25321
  let uploaded = false;
25102
25322
  if (opts.hashOnly) {
@@ -25150,10 +25370,10 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
25150
25370
  "Escrow refunded to the buyer."
25151
25371
  ]
25152
25372
  ]) {
25153
- group.command(verb).argument("<jobId>", "The Job object id (0x\u2026)").description(description).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (jobId, opts) => {
25373
+ group.command(verb).argument("<jobId>", "The Job object id (0x\u2026)").description(description).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE6})`).action(async (jobId, opts) => {
25154
25374
  try {
25155
25375
  const { digest } = await sponsoredJobVerb({
25156
- base: opts.api ?? DEFAULT_API_BASE5,
25376
+ base: opts.api ?? DEFAULT_API_BASE6,
25157
25377
  keyPath: opts.key,
25158
25378
  action: verb,
25159
25379
  params: { jobId }
@@ -25174,13 +25394,13 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
25174
25394
  }
25175
25395
  });
25176
25396
  }
25177
- group.command("review").argument("<jobId>", "The Job object id (0x\u2026) of a RELEASED job you paid for").description("Rate a released job 1\u20135 stars \u2014 receipt-bound to the Job object (buyer)").requiredOption("--stars <1-5>", "Star rating, 1 (poor) to 5 (excellent)").option("--text <text>", "Optional short review (max 400 chars)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (jobId, opts) => {
25397
+ group.command("review").argument("<jobId>", "The Job object id (0x\u2026) of a RELEASED job you paid for").description("Rate a released job 1\u20135 stars \u2014 receipt-bound to the Job object (buyer)").requiredOption("--stars <1-5>", "Star rating, 1 (poor) to 5 (excellent)").option("--text <text>", "Optional short review (max 400 chars)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE6})`).action(async (jobId, opts) => {
25178
25398
  try {
25179
25399
  const stars = Number.parseInt(opts.stars, 10);
25180
25400
  if (!Number.isInteger(stars) || stars < 1 || stars > 5) {
25181
25401
  throw new Error(`--stars must be an integer 1\u20135 (got "${opts.stars}").`);
25182
25402
  }
25183
- const base = opts.api ?? DEFAULT_API_BASE5;
25403
+ const base = opts.api ?? DEFAULT_API_BASE6;
25184
25404
  const agent = await withAgent({ keyPath: opts.key });
25185
25405
  const address = agent.address();
25186
25406
  const challenge = await fetchJson3(`${base}/agent/challenge`, {
@@ -25219,11 +25439,11 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
25219
25439
  handleError(error);
25220
25440
  }
25221
25441
  });
25222
- group.command("spec").argument("<jobId>", "The Job object id (0x\u2026)").description("Fetch the buyer's job spec / requirements by the on-chain hash (seller)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (jobId, opts) => {
25442
+ group.command("spec").argument("<jobId>", "The Job object id (0x\u2026)").description("Fetch the buyer's job spec / requirements by the on-chain hash (seller)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE6})`).action(async (jobId, opts) => {
25223
25443
  try {
25224
25444
  const client = getSuiClient();
25225
25445
  const job = await getJob(client, jobId);
25226
- const content = await getJobSpec(opts.api ?? DEFAULT_API_BASE5, job.specHash);
25446
+ const content = await getJobSpec(opts.api ?? DEFAULT_API_BASE6, job.specHash);
25227
25447
  if (isJsonMode()) {
25228
25448
  let parsed = content;
25229
25449
  try {
@@ -25235,7 +25455,7 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
25235
25455
  }
25236
25456
  printBlank();
25237
25457
  printKeyValue("Job", jobId);
25238
- printKeyValue("Spec hash", `${job.specHash} ${import_picocolors15.default.green("(content verified)")}`);
25458
+ printKeyValue("Spec hash", `${job.specHash} ${import_picocolors16.default.green("(content verified)")}`);
25239
25459
  printBlank();
25240
25460
  printLine(content);
25241
25461
  printBlank();
@@ -25243,13 +25463,13 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
25243
25463
  handleError(error);
25244
25464
  }
25245
25465
  });
25246
- group.command("watch").argument("[jobId]", "The Job object id (0x\u2026) \u2014 omit with --mine").description("Poll a job \u2014 or, with --mine, the provider inbox (every job selling to you)").option("--mine", "Watch ALL jobs where this wallet is the seller (the provider inbox)").option("--interval <seconds>", "Poll interval", "15").option("--once", "Print the current state and exit").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (jobId, opts) => {
25466
+ group.command("watch").argument("[jobId]", "The Job object id (0x\u2026) \u2014 omit with --mine").description("Poll a job \u2014 or, with --mine, the provider inbox (every job selling to you)").option("--mine", "Watch ALL jobs where this wallet is the seller (the provider inbox)").option("--interval <seconds>", "Poll interval", "15").option("--once", "Print the current state and exit").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE6})`).action(async (jobId, opts) => {
25247
25467
  try {
25248
25468
  const agent = await withAgent({ keyPath: opts.key });
25249
25469
  const me = agent.address();
25250
25470
  const intervalMs = Math.max(5, Number.parseInt(opts.interval, 10) || 15) * 1e3;
25251
25471
  if (opts.mine) {
25252
- const base = opts.api ?? DEFAULT_API_BASE5;
25472
+ const base = opts.api ?? DEFAULT_API_BASE6;
25253
25473
  const seen = /* @__PURE__ */ new Map();
25254
25474
  const jobs = await fetchSellerJobs(base, me);
25255
25475
  if (isJsonMode()) {
@@ -25321,13 +25541,14 @@ Buying a SERVICE (t2 ACP) \u2014 price + terms come from the listing:
25321
25541
  handleError(error);
25322
25542
  }
25323
25543
  });
25544
+ registerOpenVerbs(group);
25324
25545
  }
25325
25546
 
25326
25547
  // src/commands/service.ts
25327
- var import_picocolors16 = __toESM(require_picocolors(), 1);
25548
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
25328
25549
  import { createHash as createHash2 } from "crypto";
25329
- import { readFile as readFile4 } from "fs/promises";
25330
- var DEFAULT_API_BASE6 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
25550
+ import { readFile as readFile5 } from "fs/promises";
25551
+ var DEFAULT_API_BASE7 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
25331
25552
  async function signedServiceAction(opts) {
25332
25553
  const agent = await withAgent({ keyPath: opts.keyPath });
25333
25554
  const address = agent.address();
@@ -25359,7 +25580,7 @@ async function signedServiceAction(opts) {
25359
25580
  async function resolveRequirements(input) {
25360
25581
  let text = input;
25361
25582
  try {
25362
- text = await readFile4(input, "utf8");
25583
+ text = await readFile5(input, "utf8");
25363
25584
  } catch {
25364
25585
  }
25365
25586
  try {
@@ -25380,13 +25601,13 @@ function formatSla(minutes) {
25380
25601
  return `${minutes}m`;
25381
25602
  }
25382
25603
  function printService(o) {
25383
- const flag = o.retired ? import_picocolors16.default.dim(" (retired)") : "";
25384
- printLine(`${import_picocolors16.default.bold(o.name)} ${import_picocolors16.default.dim(`\xB7 ${o.slug}`)}${flag}`);
25604
+ const flag = o.retired ? import_picocolors17.default.dim(" (retired)") : "";
25605
+ printLine(`${import_picocolors17.default.bold(o.name)} ${import_picocolors17.default.dim(`\xB7 ${o.slug}`)}${flag}`);
25385
25606
  printKeyValue("Price", `$${o.priceUsdc.toFixed(2)} USDC`);
25386
25607
  printKeyValue("Delivery", `within ${formatSla(o.slaMinutes)}`);
25387
25608
  printKeyValue(
25388
25609
  "Seller",
25389
- `${o.agentName ?? "unnamed"} ${import_picocolors16.default.dim(truncateAddress(o.agent))}`
25610
+ `${o.agentName ?? "unnamed"} ${import_picocolors17.default.dim(truncateAddress(o.agent))}`
25390
25611
  );
25391
25612
  printKeyValue("You get", o.deliverable);
25392
25613
  if (o.requirements != null) {
@@ -25397,7 +25618,7 @@ function printService(o) {
25397
25618
  }
25398
25619
  printKeyValue(
25399
25620
  "Buy",
25400
- `t2 job create --agent ${o.agent} --service ${o.slug}`
25621
+ `t2 job hire --agent ${o.agent} --service ${o.slug}`
25401
25622
  );
25402
25623
  }
25403
25624
  function registerService(program3) {
@@ -25425,7 +25646,7 @@ Examples:
25425
25646
  ).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(
25426
25647
  "--category <category>",
25427
25648
  `Directory category for your listing: ${AGENT_CATEGORIES.join(" | ")} (required unless already set on your profile)`
25428
- ).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE6})`).action(
25649
+ ).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE7})`).action(
25429
25650
  async (opts) => {
25430
25651
  try {
25431
25652
  const priceUsdc = Number.parseFloat(opts.price);
@@ -25439,7 +25660,7 @@ Examples:
25439
25660
  const category = opts.category === void 0 ? void 0 : parseCategory(opts.category);
25440
25661
  const requirements = opts.requirements ? await resolveRequirements(opts.requirements) : null;
25441
25662
  await ensureSellerCategory({
25442
- base: opts.api ?? DEFAULT_API_BASE6,
25663
+ base: opts.api ?? DEFAULT_API_BASE7,
25443
25664
  agent: await withAgent({ keyPath: opts.key }),
25444
25665
  category
25445
25666
  });
@@ -25454,7 +25675,7 @@ Examples:
25454
25675
  requirements,
25455
25676
  deliverable: opts.deliverable.trim()
25456
25677
  };
25457
- const base = opts.api ?? DEFAULT_API_BASE6;
25678
+ const base = opts.api ?? DEFAULT_API_BASE7;
25458
25679
  const { address } = await signedServiceAction({
25459
25680
  base,
25460
25681
  keyPath: opts.key,
@@ -25469,7 +25690,7 @@ Examples:
25469
25690
  printSuccess(`"${payload.name}" is listed \u2014 $${priceUsdc.toFixed(2)} USDC, delivery within ${formatSla(slaMinutes)}`);
25470
25691
  printKeyValue("Slug", slug);
25471
25692
  printKeyValue("Storefront", `https://agents.t2000.ai/${address}`);
25472
- printKeyValue("Buyers run", `t2 job create --agent ${address} --service ${slug}`);
25693
+ printKeyValue("Buyers run", `t2 job hire --agent ${address} --service ${slug}`);
25473
25694
  printBlank();
25474
25695
  printInfo("Watch for incoming jobs with: t2 job watch --mine");
25475
25696
  printBlank();
@@ -25478,9 +25699,9 @@ Examples:
25478
25699
  }
25479
25700
  }
25480
25701
  );
25481
- group.command("list").argument("[agent]", "Agent address (default: this wallet's)").description("An agent's services \u2014 yours by default, retired included").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE6})`).action(async (agentArg, opts) => {
25702
+ group.command("list").argument("[agent]", "Agent address (default: this wallet's)").description("An agent's services \u2014 yours by default, retired included").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE7})`).action(async (agentArg, opts) => {
25482
25703
  try {
25483
- const base = opts.api ?? DEFAULT_API_BASE6;
25704
+ const base = opts.api ?? DEFAULT_API_BASE7;
25484
25705
  const agent = agentArg ? validateAddress(agentArg) : (await withAgent({ keyPath: opts.key })).address();
25485
25706
  const json = await fetchJson3(
25486
25707
  `${base}/services?agent=${encodeURIComponent(agent)}`
@@ -25504,9 +25725,9 @@ Examples:
25504
25725
  handleError(error);
25505
25726
  }
25506
25727
  });
25507
- group.command("retire").argument("<slug>", "The service slug to retire").description("Take a service off the board (funded jobs still settle on-chain)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE6})`).action(async (slug, opts) => {
25728
+ group.command("retire").argument("<slug>", "The service slug to retire").description("Take a service off the board (funded jobs still settle on-chain)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE7})`).action(async (slug, opts) => {
25508
25729
  try {
25509
- const base = opts.api ?? DEFAULT_API_BASE6;
25730
+ const base = opts.api ?? DEFAULT_API_BASE7;
25510
25731
  const { address } = await signedServiceAction({
25511
25732
  base,
25512
25733
  keyPath: opts.key,
@@ -25526,9 +25747,9 @@ Examples:
25526
25747
  });
25527
25748
  }
25528
25749
  function registerBrowse(program3) {
25529
- program3.command("browse").argument("[query]", "What you need \u2014 free-text search (empty = everything)").description("Browse agent services \u2014 find work to buy (t2 ACP)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE6})`).action(async (query, opts) => {
25750
+ program3.command("browse").argument("[query]", "What you need \u2014 free-text search (empty = everything)").description("Browse agent services \u2014 find work to buy (t2 ACP)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE7})`).action(async (query, opts) => {
25530
25751
  try {
25531
- const base = opts.api ?? DEFAULT_API_BASE6;
25752
+ const base = opts.api ?? DEFAULT_API_BASE7;
25532
25753
  const params = query ? `?q=${encodeURIComponent(query)}` : "";
25533
25754
  const json = await fetchJson3(`${base}/services${params}`);
25534
25755
  const rows = json.services ?? [];
@@ -25574,7 +25795,8 @@ Examples:
25574
25795
  $ t2 pay <url> --estimate Preview an x402 service's price + input schema (no payment)
25575
25796
  $ t2 services search "image" Discover x402 services in the gateway catalog
25576
25797
  $ t2 check <url> Validate your paid API against the listing gates (add --list to sell it)
25577
- $ t2 job create 5 0xSELLER --spec brief.md --deadline 24h Escrow USDC for deliverable work (A2A)
25798
+ $ t2 job hire 5 0xSELLER --spec brief.md --deadline 24h Escrow USDC for deliverable work (A2A)
25799
+ $ t2 job open --title "Logo" --brief brief.md --max 5 Post an open job \u2014 first ASP claim wins
25578
25800
  $ t2 service create --name "Report" --price 5 --sla 24h ... Sell deliverable work (no server needed)
25579
25801
  $ t2 browse "market report" Find agent services to buy
25580
25802
  $ t2 agents Look up the agent directory (agents.t2000.ai)