@t2000/cli 9.6.0 → 9.7.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.
@@ -1,5 +1,6 @@
1
1
  import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename);
2
2
  import {
3
+ A2A_ESCROW_PACKAGE_ID,
3
4
  AGENT_ID_PARENT,
4
5
  AGENT_ID_PARENT_NAME,
5
6
  AGENT_ID_PARENT_NFT_ID,
@@ -18,6 +19,7 @@ import {
18
19
  GAS_RESERVE_MIN,
19
20
  IKA_TYPE,
20
21
  InvalidAddressError,
22
+ JOB_STATES,
21
23
  KNOWN_TARGETS,
22
24
  KeypairSigner,
23
25
  LABEL_PATTERNS,
@@ -25,6 +27,7 @@ import {
25
27
  LimitEnforcer,
26
28
  LimitExceededError,
27
29
  MANIFEST_TYPE,
30
+ MAX_JOB_USDC,
28
31
  MIST_PER_SUI,
29
32
  NAVX_TYPE,
30
33
  OPERATION_ASSETS,
@@ -61,6 +64,11 @@ import {
61
64
  assertAllowedAsset,
62
65
  assertLimitConfig,
63
66
  buildAddLeafTx,
67
+ buildCreateJobTx,
68
+ buildDeliverJobTx,
69
+ buildRefundJobTx,
70
+ buildRejectJobTx,
71
+ buildReleaseJobTx,
64
72
  buildRevokeLeafTx,
65
73
  buildSendTx,
66
74
  buildSwapTx,
@@ -95,6 +103,7 @@ import {
95
103
  getCoinMeta,
96
104
  getDecimals,
97
105
  getDecimalsForCoinType,
106
+ getJob,
98
107
  getLimits,
99
108
  getSponsoredSwapProviders,
100
109
  getSuiClient,
@@ -104,6 +113,7 @@ import {
104
113
  isAllowedAsset,
105
114
  isCetusRouteFresh,
106
115
  isInRegistry,
116
+ jobActionsFor,
107
117
  keypairFromPrivateKey,
108
118
  listModels,
109
119
  loadKey,
@@ -117,6 +127,7 @@ import {
117
127
  parseMppSuiChallenge,
118
128
  parseSuiRpcTx,
119
129
  payWithMpp,
130
+ preflightCreateJob,
120
131
  preflightFail,
121
132
  preflightPay,
122
133
  preflightSend,
@@ -148,10 +159,11 @@ import {
148
159
  validateAddress,
149
160
  validateLabel,
150
161
  verifyCetusRouteCoinMatch,
162
+ verifyJobForSeller,
151
163
  verifyReceipt,
152
164
  walletExists,
153
165
  writeLimitsFile
154
- } from "./chunk-UGQY67E3.js";
166
+ } from "./chunk-SXB4BX4Y.js";
155
167
  import "./chunk-634W6JCI.js";
156
168
  import "./chunk-BHQGGFE6.js";
157
169
  import "./chunk-QHLVOCU6.js";
@@ -165,6 +177,7 @@ import "./chunk-SUSL7H4U.js";
165
177
  import "./chunk-PDKJSPAS.js";
166
178
  import "./chunk-YPWSCLE3.js";
167
179
  export {
180
+ A2A_ESCROW_PACKAGE_ID,
168
181
  AGENT_ID_PARENT,
169
182
  AGENT_ID_PARENT_NAME,
170
183
  AGENT_ID_PARENT_NFT_ID,
@@ -183,6 +196,7 @@ export {
183
196
  GAS_RESERVE_MIN,
184
197
  IKA_TYPE,
185
198
  InvalidAddressError,
199
+ JOB_STATES,
186
200
  KNOWN_TARGETS,
187
201
  KeypairSigner,
188
202
  LABEL_PATTERNS,
@@ -190,6 +204,7 @@ export {
190
204
  LimitEnforcer,
191
205
  LimitExceededError,
192
206
  MANIFEST_TYPE,
207
+ MAX_JOB_USDC,
193
208
  MIST_PER_SUI,
194
209
  NAVX_TYPE,
195
210
  OPERATION_ASSETS,
@@ -226,6 +241,11 @@ export {
226
241
  assertAllowedAsset,
227
242
  assertLimitConfig,
228
243
  buildAddLeafTx,
244
+ buildCreateJobTx,
245
+ buildDeliverJobTx,
246
+ buildRefundJobTx,
247
+ buildRejectJobTx,
248
+ buildReleaseJobTx,
229
249
  buildRevokeLeafTx,
230
250
  buildSendTx,
231
251
  buildSwapTx,
@@ -260,6 +280,7 @@ export {
260
280
  getCoinMeta,
261
281
  getDecimals,
262
282
  getDecimalsForCoinType,
283
+ getJob,
263
284
  getLimits,
264
285
  getSponsoredSwapProviders,
265
286
  getSuiClient,
@@ -269,6 +290,7 @@ export {
269
290
  isAllowedAsset,
270
291
  isCetusRouteFresh,
271
292
  isInRegistry,
293
+ jobActionsFor,
272
294
  keypairFromPrivateKey,
273
295
  listModels,
274
296
  loadKey,
@@ -282,6 +304,7 @@ export {
282
304
  parseMppSuiChallenge,
283
305
  parseSuiRpcTx,
284
306
  payWithMpp,
307
+ preflightCreateJob,
285
308
  preflightFail,
286
309
  preflightPay,
287
310
  preflightSend,
@@ -313,8 +336,9 @@ export {
313
336
  validateAddress,
314
337
  validateLabel,
315
338
  verifyCetusRouteCoinMatch,
339
+ verifyJobForSeller,
316
340
  verifyReceipt,
317
341
  walletExists,
318
342
  writeLimitsFile
319
343
  };
320
- //# sourceMappingURL=dist-WHAWAYBP.js.map
344
+ //# sourceMappingURL=dist-DHOTKMC2.js.map
package/dist/index.js CHANGED
@@ -4,22 +4,28 @@ import {
4
4
  require_safer
5
5
  } from "./chunk-FNQMIHYC.js";
6
6
  import {
7
+ MAX_JOB_USDC,
7
8
  T2000,
8
9
  clearLimits,
9
10
  formatUsd,
10
11
  generateKeypair,
11
12
  getAddress,
13
+ getJob,
12
14
  getLimits,
15
+ getSuiClient,
13
16
  hasLimits,
17
+ jobActionsFor,
14
18
  keypairFromPrivateKey,
15
19
  listModels,
16
20
  saveBech32,
17
21
  saveKey,
18
22
  setLimits,
19
23
  truncateAddress,
24
+ validateAddress,
25
+ verifyJobForSeller,
20
26
  verifyReceipt,
21
27
  walletExists
22
- } from "./chunk-UGQY67E3.js";
28
+ } from "./chunk-SXB4BX4Y.js";
23
29
  import "./chunk-634W6JCI.js";
24
30
  import "./chunk-BHQGGFE6.js";
25
31
  import "./chunk-QHLVOCU6.js";
@@ -22272,7 +22278,7 @@ async function runEstimate(url, opts) {
22272
22278
  let req = accepts.find((a) => a.scheme === "exact" && a.network?.startsWith("sui:")) ?? accepts[0];
22273
22279
  let dialect = "x402";
22274
22280
  if (!req) {
22275
- const { parseMppSuiChallenge } = await import("./dist-WHAWAYBP.js");
22281
+ const { parseMppSuiChallenge } = await import("./dist-DHOTKMC2.js");
22276
22282
  const challenge = await parseMppSuiChallenge(response);
22277
22283
  if (!challenge) {
22278
22284
  throw new Error(
@@ -23364,7 +23370,7 @@ function registerMcpStart(parent) {
23364
23370
  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) => {
23365
23371
  let mod;
23366
23372
  try {
23367
- mod = await import("./dist-VTNYUSEM.js");
23373
+ mod = await import("./dist-7NJYMC53.js");
23368
23374
  } catch {
23369
23375
  console.error("MCP server not installed. Run:\n npm install -g @t2000/mcp");
23370
23376
  process.exit(1);
@@ -24438,6 +24444,329 @@ function registerAgents(program3) {
24438
24444
  );
24439
24445
  }
24440
24446
 
24447
+ // src/commands/check.ts
24448
+ var DEFAULT_GATEWAY_BASE2 = process.env.T2000_GATEWAY_URL ?? "https://mpp.t2000.ai";
24449
+ function registerCheck(program3) {
24450
+ program3.command("check").argument("<url>", "Your paid API endpoint (https, answers 402)").description(
24451
+ "Check a paid API against the t2 Agents listing gates (payable 402 \xB7 x402 envelope \xB7 price cap) \u2014 dry run, nothing is paid or listed. Add --list to list it. No account needed: the 402 challenge\u2019s payTo wallet is the seller identity."
24452
+ ).option("--list", "List it in the catalog when the checks pass").option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY_BASE2})`).action(async (url, opts) => {
24453
+ try {
24454
+ const base = opts.gateway ?? DEFAULT_GATEWAY_BASE2;
24455
+ const route = opts.list ? "submit" : "preview";
24456
+ const res = await fetch(`${base}/api/catalog/${route}`, {
24457
+ method: "POST",
24458
+ headers: { "content-type": "application/json" },
24459
+ body: JSON.stringify({ url })
24460
+ });
24461
+ const out = await res.json().catch(() => ({}));
24462
+ if (out.error && !out.gates) {
24463
+ throw new Error(out.error);
24464
+ }
24465
+ if (isJsonMode()) {
24466
+ printJson(out);
24467
+ if (!out.ok) process.exitCode = 1;
24468
+ return;
24469
+ }
24470
+ printBlank();
24471
+ for (const gate of out.gates ?? []) {
24472
+ printInfo(`${gate.ok ? "\u2713" : "\u2717"} ${gate.gate}: ${gate.detail}`);
24473
+ }
24474
+ printBlank();
24475
+ if (!out.ok) {
24476
+ printInfo("Not listable yet \u2014 fix the failing gate above and re-run.");
24477
+ printInfo(`Seller guide: ${base}/sellers.md`);
24478
+ printBlank();
24479
+ process.exitCode = 1;
24480
+ return;
24481
+ }
24482
+ if (opts.list) {
24483
+ printSuccess("Listed.");
24484
+ if (out.url) printKeyValue("Catalog", out.url);
24485
+ if (out.storeUrl) printKeyValue("Store page", out.storeUrl);
24486
+ } else {
24487
+ printHeader(out.service?.name ?? url);
24488
+ if (out.payTo) printKeyValue("Pays", out.payTo);
24489
+ for (const ep of out.service?.endpoints ?? []) {
24490
+ printLine(` ${ep.method} ${ep.path} \u2014 $${ep.price}${ep.description ? ` \u2014 ${ep.description}` : ""}`);
24491
+ }
24492
+ printBlank();
24493
+ printInfo("All gates pass. List it with: t2 check <url> --list");
24494
+ }
24495
+ const warnings = out.warnings ?? [];
24496
+ if (warnings.length > 0) {
24497
+ printBlank();
24498
+ printHeader(`Listing quality \u2014 ${warnings.length} improvement(s)`);
24499
+ for (const w of warnings) {
24500
+ printInfo(`\u2022 ${w.message}`);
24501
+ printLine(" Paste this into your coding agent to fix it:");
24502
+ printLine(` ${w.prompt}`);
24503
+ printBlank();
24504
+ }
24505
+ }
24506
+ printBlank();
24507
+ } catch (error) {
24508
+ handleError(error);
24509
+ }
24510
+ });
24511
+ }
24512
+
24513
+ // src/commands/job.ts
24514
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
24515
+ import { createHash } from "crypto";
24516
+ import { readFile as readFile3 } from "fs/promises";
24517
+ var DEFAULT_API_BASE5 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
24518
+ var DEFAULT_REVIEW_WINDOW_MS = 24 * 60 * 60 * 1e3;
24519
+ var DEFAULT_REJECT_SPLIT_BPS = 8e3;
24520
+ function parseDuration(input) {
24521
+ const m = /^(\d+(?:\.\d+)?)([mhd]?)$/.exec(input.trim());
24522
+ if (!m) {
24523
+ throw new Error(`Invalid duration "${input}". Use e.g. 30m, 24h, 7d.`);
24524
+ }
24525
+ const n = Number(m[1]);
24526
+ const unit = m[2] || "m";
24527
+ const ms = unit === "d" ? n * 864e5 : unit === "h" ? n * 36e5 : n * 6e4;
24528
+ if (ms <= 0) throw new Error(`Duration must be positive (got "${input}").`);
24529
+ return Math.round(ms);
24530
+ }
24531
+ async function resolveCommitment(input) {
24532
+ if (/^0x[0-9a-fA-F]+$/.test(input) && input.length % 2 === 0) return input;
24533
+ let bytes;
24534
+ try {
24535
+ bytes = await readFile3(input);
24536
+ } catch {
24537
+ bytes = Buffer.from(input, "utf8");
24538
+ }
24539
+ return `0x${createHash("sha256").update(bytes).digest("hex")}`;
24540
+ }
24541
+ function stateColor(state) {
24542
+ if (state === "released") return import_picocolors15.default.green(state);
24543
+ if (state === "refunded" || state === "rejected") return import_picocolors15.default.yellow(state);
24544
+ return import_picocolors15.default.cyan(state);
24545
+ }
24546
+ function printJob(job, me) {
24547
+ printKeyValue("Job", job.id);
24548
+ printKeyValue("State", stateColor(job.state));
24549
+ printKeyValue("Buyer", truncateAddress(job.buyer) + (me === job.buyer ? import_picocolors15.default.dim(" (you)") : ""));
24550
+ printKeyValue("Seller", truncateAddress(job.seller) + (me === job.seller ? import_picocolors15.default.dim(" (you)") : ""));
24551
+ printKeyValue("Amount", `$${job.amountUsdc.toFixed(2)} USDC`);
24552
+ printKeyValue("Deliver by", new Date(job.deliverByMs).toISOString());
24553
+ if (job.deliveredAtMs) {
24554
+ printKeyValue("Delivered", new Date(job.deliveredAtMs).toISOString());
24555
+ printKeyValue(
24556
+ "Review closes",
24557
+ new Date(job.deliveredAtMs + job.reviewWindowMs).toISOString()
24558
+ );
24559
+ }
24560
+ if (job.deliveryHash) printKeyValue("Delivery hash", job.deliveryHash);
24561
+ printKeyValue("Reject split", `${job.rejectSplitBps / 100}% buyer / ${(1e4 - job.rejectSplitBps) / 100}% seller`);
24562
+ }
24563
+ async function sponsoredJobVerb(opts) {
24564
+ const agent = await withAgent({ keyPath: opts.keyPath });
24565
+ const address = agent.address();
24566
+ const { digest } = await runSponsoredTx({
24567
+ keypair: agent.keypair,
24568
+ actor: address,
24569
+ prepareUrl: `${opts.base}/job/prepare`,
24570
+ prepareBody: { address, action: opts.action, params: opts.params },
24571
+ submitUrl: `${opts.base}/job/submit`
24572
+ });
24573
+ return { address, digest };
24574
+ }
24575
+ function registerJob(program3) {
24576
+ const group = program3.command("job").description(
24577
+ "A2A escrow jobs \u2014 USDC locked in a shared Move object, released on delivery (no platform custody)"
24578
+ ).addHelpText(
24579
+ "after",
24580
+ `
24581
+ The escrow is a Sui object, not a company: funds lock inside the Job object at
24582
+ create; release/refund are pure functions of (state, clock, caller). A ghosting
24583
+ buyer can't strand a delivering seller (anyone may release after the review
24584
+ window) and a no-show seller can never keep funds (anyone may refund after the
24585
+ deadline). v1 caps jobs at ${MAX_JOB_USDC} USDC.
24586
+
24587
+ Typical flow:
24588
+ buyer $ t2 job create 5 0xSELLER --spec brief.md --deadline 24h
24589
+ seller $ t2 job verify 0xJOB --price 5
24590
+ seller $ t2 job deliver 0xJOB report.pdf
24591
+ buyer $ t2 job release 0xJOB (or: t2 job reject 0xJOB)
24592
+ either $ t2 job watch 0xJOB
24593
+ `
24594
+ );
24595
+ group.command("create").argument("<amount>", `USDC to escrow (max ${MAX_JOB_USDC})`).argument("<seller>", "The seller's Sui address (their listing's payTo wallet)").description("Create + fund an escrow job in one transaction (buyer)").requiredOption("--spec <file-or-text>", "Job spec \u2014 a file path or inline text (hashed on-chain), or a 0x\u2026 hash").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(
24596
+ async (amountArg, sellerArg, opts) => {
24597
+ try {
24598
+ const amountUsdc = Number.parseFloat(amountArg);
24599
+ if (!Number.isFinite(amountUsdc) || amountUsdc <= 0) {
24600
+ throw new Error(`Amount must be a positive number (got "${amountArg}").`);
24601
+ }
24602
+ const seller = validateAddress(sellerArg);
24603
+ const specHash = await resolveCommitment(opts.spec);
24604
+ const deliverByMs = Date.now() + parseDuration(opts.deadline);
24605
+ const reviewWindowMs = opts.review ? parseDuration(opts.review) : DEFAULT_REVIEW_WINDOW_MS;
24606
+ const rejectSplitBps = Number.parseInt(opts.split, 10);
24607
+ const base = opts.api ?? DEFAULT_API_BASE5;
24608
+ const { address, digest } = await sponsoredJobVerb({
24609
+ base,
24610
+ keyPath: opts.key,
24611
+ action: "create",
24612
+ params: { seller, amountUsdc, specHash, deliverByMs, reviewWindowMs, rejectSplitBps }
24613
+ });
24614
+ const client = getSuiClient();
24615
+ let jobId;
24616
+ if (digest) {
24617
+ try {
24618
+ const result = await client.core.waitForTransaction({
24619
+ digest,
24620
+ include: { objectTypes: true },
24621
+ timeout: 15e3
24622
+ });
24623
+ const txn = result.$kind === "Transaction" ? result.Transaction : result.FailedTransaction;
24624
+ const types = txn.objectTypes ?? {};
24625
+ jobId = Object.keys(types).find((id) => types[id]?.includes("::escrow::Job<"));
24626
+ } catch {
24627
+ }
24628
+ }
24629
+ if (isJsonMode()) {
24630
+ printJson({ jobId, digest, buyer: address, seller, amountUsdc, specHash, deliverByMs, reviewWindowMs, rejectSplitBps });
24631
+ return;
24632
+ }
24633
+ printBlank();
24634
+ printSuccess(`Escrowed $${amountUsdc.toFixed(2)} USDC \u2192 job for ${truncateAddress(seller)}`);
24635
+ if (jobId) printKeyValue("Job", jobId);
24636
+ printKeyValue("Spec hash", specHash);
24637
+ printKeyValue("Deliver by", new Date(deliverByMs).toISOString());
24638
+ if (digest) printKeyValue("Tx", digest);
24639
+ printBlank();
24640
+ if (jobId) {
24641
+ printInfo(`Hand the seller the job id \u2014 they verify it with: t2 job verify ${jobId} --price ${amountUsdc}`);
24642
+ }
24643
+ printBlank();
24644
+ } catch (error) {
24645
+ handleError(error);
24646
+ }
24647
+ }
24648
+ );
24649
+ group.command("verify").argument("<jobId>", "The Job object id (0x\u2026)").description("Seller-side check: the job is funded, pays YOU, and covers the price").requiredOption("--price <usdc>", "Your price for this job class").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").action(async (jobId, opts) => {
24650
+ try {
24651
+ const agent = await withAgent({ keyPath: opts.key });
24652
+ const client = getSuiClient();
24653
+ const result = await verifyJobForSeller({
24654
+ client,
24655
+ jobId,
24656
+ seller: agent.address(),
24657
+ minAmountUsdc: Number.parseFloat(opts.price)
24658
+ });
24659
+ if (isJsonMode()) {
24660
+ printJson({ ok: result.ok, problems: result.problems, job: result.job });
24661
+ if (!result.ok) process.exitCode = 1;
24662
+ return;
24663
+ }
24664
+ printBlank();
24665
+ if (result.ok) {
24666
+ printSuccess("Escrow verified \u2014 funded, pays this wallet, covers the price. Safe to start work.");
24667
+ } else {
24668
+ printError("Do NOT start work on this job:");
24669
+ for (const p of result.problems) printWarning(` ${p}`);
24670
+ process.exitCode = 1;
24671
+ }
24672
+ printBlank();
24673
+ printJob(result.job, agent.address());
24674
+ printBlank();
24675
+ } catch (error) {
24676
+ handleError(error);
24677
+ }
24678
+ });
24679
+ 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) => {
24680
+ try {
24681
+ const deliveryHash = await resolveCommitment(proof);
24682
+ const { digest } = await sponsoredJobVerb({
24683
+ base: opts.api ?? DEFAULT_API_BASE5,
24684
+ keyPath: opts.key,
24685
+ action: "deliver",
24686
+ params: { jobId, deliveryHash }
24687
+ });
24688
+ if (isJsonMode()) {
24689
+ printJson({ jobId, deliveryHash, digest });
24690
+ return;
24691
+ }
24692
+ printBlank();
24693
+ printSuccess("Delivery posted \u2014 the buyer's review window is now open.");
24694
+ printKeyValue("Delivery hash", deliveryHash);
24695
+ if (digest) printKeyValue("Tx", digest);
24696
+ printInfo("If the buyer neither accepts nor rejects before the window closes, anyone (including you) can run `t2 job release` to settle.");
24697
+ printBlank();
24698
+ } catch (error) {
24699
+ handleError(error);
24700
+ }
24701
+ });
24702
+ for (const [verb, description, note] of [
24703
+ [
24704
+ "release",
24705
+ "Accept delivery \u2014 funds go to the seller (buyer; or anyone once the review window lapses)",
24706
+ "Funds released to the seller."
24707
+ ],
24708
+ [
24709
+ "reject",
24710
+ "Reject a delivery within the review window \u2014 funds split per the create terms (buyer)",
24711
+ "Delivery rejected \u2014 funds split per the terms agreed at create."
24712
+ ],
24713
+ [
24714
+ "refund",
24715
+ "Reclaim funds after the deadline passed with no delivery (anyone may crank this)",
24716
+ "Escrow refunded to the buyer."
24717
+ ]
24718
+ ]) {
24719
+ 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) => {
24720
+ try {
24721
+ const { digest } = await sponsoredJobVerb({
24722
+ base: opts.api ?? DEFAULT_API_BASE5,
24723
+ keyPath: opts.key,
24724
+ action: verb,
24725
+ params: { jobId }
24726
+ });
24727
+ if (isJsonMode()) {
24728
+ printJson({ jobId, action: verb, digest });
24729
+ return;
24730
+ }
24731
+ printBlank();
24732
+ printSuccess(note);
24733
+ if (digest) printKeyValue("Tx", digest);
24734
+ printBlank();
24735
+ } catch (error) {
24736
+ handleError(error);
24737
+ }
24738
+ });
24739
+ }
24740
+ group.command("watch").argument("<jobId>", "The Job object id (0x\u2026)").description("Poll the job \u2014 state, timers, and what YOU can do right now").option("--interval <seconds>", "Poll interval", "15").option("--once", "Print the current state and exit").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").action(async (jobId, opts) => {
24741
+ try {
24742
+ const agent = await withAgent({ keyPath: opts.key });
24743
+ const me = agent.address();
24744
+ const client = getSuiClient();
24745
+ const intervalMs = Math.max(5, Number.parseInt(opts.interval, 10) || 15) * 1e3;
24746
+ for (; ; ) {
24747
+ const job = await getJob(client, jobId);
24748
+ const actions = jobActionsFor(job, me);
24749
+ const terminal = job.state === "released" || job.state === "refunded" || job.state === "rejected";
24750
+ if (isJsonMode()) {
24751
+ printJson({ job, yourActions: actions, terminal });
24752
+ } else {
24753
+ printBlank();
24754
+ printJob(job, me);
24755
+ if (actions.length > 0) {
24756
+ printInfo(`You can now: ${actions.map((a) => `t2 job ${a} ${truncateAddress(jobId)}`).join(" \xB7 ")}`);
24757
+ } else if (!terminal) {
24758
+ printInfo("Nothing for you to do yet \u2014 waiting on the counterparty / clock.");
24759
+ }
24760
+ }
24761
+ if (terminal || opts.once || isJsonMode()) return;
24762
+ await new Promise((r) => setTimeout(r, intervalMs));
24763
+ }
24764
+ } catch (error) {
24765
+ handleError(error);
24766
+ }
24767
+ });
24768
+ }
24769
+
24441
24770
  // src/program.ts
24442
24771
  var require3 = createRequire2(import.meta.url);
24443
24772
  var { version: CLI_VERSION2 } = require3("../package.json");
@@ -24459,6 +24788,8 @@ Examples:
24459
24788
  $ t2 connect t2code --key sk-... Point a coding tool at Private Inference
24460
24789
  $ t2 pay <url> --estimate Preview an x402 service's price + input schema (no payment)
24461
24790
  $ t2 services search "image" Discover x402 services in the gateway catalog
24791
+ $ t2 check <url> Validate your paid API against the listing gates (add --list to sell it)
24792
+ $ t2 job create 5 0xSELLER --spec brief.md --deadline 24h Escrow USDC for deliverable work (A2A)
24462
24793
  $ t2 agents Look up the agent directory (agents.t2000.ai)
24463
24794
  $ t2 limit set --daily 100 Change the daily spend cap (default $100/day)
24464
24795
  $ t2 mcp install Connect Claude / Cursor / Windsurf
@@ -24481,6 +24812,8 @@ Examples:
24481
24812
  registerSkills(program3);
24482
24813
  registerAgent(program3);
24483
24814
  registerAgents(program3);
24815
+ registerCheck(program3);
24816
+ registerJob(program3);
24484
24817
  return program3;
24485
24818
  }
24486
24819