@t2000/cli 10.27.0 → 10.27.2

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
@@ -30191,11 +30191,21 @@ var T2000 = class _T2000 extends import_index2.default {
30191
30191
  const rawAmount = BigInt(Math.floor(params.amount * 10 ** fromDecimals));
30192
30192
  let route;
30193
30193
  if (params.serializedRoute) {
30194
- const s = params.serializedRoute;
30194
+ let s = params.serializedRoute;
30195
+ const nested = s.serializedRoute;
30196
+ if (nested && typeof nested === "object" && typeof nested.fromCoinType === "string" && nested.routerData) {
30197
+ s = nested;
30198
+ }
30199
+ if (typeof s.fromCoinType !== "string" || typeof s.toCoinType !== "string") {
30200
+ throw new T2000Error(
30201
+ "SWAP_ROUTE_MISMATCH",
30202
+ "serializedRoute is not the object t2000 quoted \u2014 it is missing fromCoinType/toCoinType. Pass quote.serializedRoute exactly as the quote returned it (not the whole quote response), or omit serializedRoute to discover a fresh route."
30203
+ );
30204
+ }
30195
30205
  if (!verifyCetusRouteCoinMatch2(s, { fromCoinType: fromType, toCoinType: toType })) {
30196
30206
  throw new T2000Error(
30197
30207
  "SWAP_ROUTE_MISMATCH",
30198
- `The quoted route is for ${s.fromCoinType} -> ${s.toCoinType}, not ${params.from} -> ${params.to}. Re-quote and pass the new serializedRoute.`
30208
+ `The quoted route is for ${s.fromCoinType} -> ${s.toCoinType}, not ${fromType} -> ${toType}. Re-quote and pass the new serializedRoute.`
30199
30209
  );
30200
30210
  }
30201
30211
  if (s.byAmountIn !== byAmountIn || BigInt(s.amountIn) !== rawAmount) {
@@ -30545,6 +30555,8 @@ var A2A_ESCROW_FEE_CONFIG_VERSION = Number(
30545
30555
  );
30546
30556
  var MODULE2 = "escrow";
30547
30557
  var MAX_JOB_USDC = 50;
30558
+ var MAX_REVIEW_WINDOW_MS = 2592e6;
30559
+ var MAX_DELIVER_HORIZON_MS = 31536e6;
30548
30560
  var JOB_STATES = [
30549
30561
  "funded",
30550
30562
  "delivered",
@@ -30552,12 +30564,57 @@ var JOB_STATES = [
30552
30564
  "refunded",
30553
30565
  "rejected"
30554
30566
  ];
30567
+ function hexToBytes3(hex) {
30568
+ const clean4 = hex.startsWith("0x") ? hex.slice(2) : hex;
30569
+ if (clean4.length === 0 || clean4.length % 2 !== 0 || /[^0-9a-fA-F]/.test(clean4)) {
30570
+ throw new T2000Error(
30571
+ "INVALID_AMOUNT",
30572
+ `Expected a hex hash (0x\u2026), got "${hex.slice(0, 32)}"`
30573
+ );
30574
+ }
30575
+ const out = [];
30576
+ for (let i = 0; i < clean4.length; i += 2) {
30577
+ out.push(Number.parseInt(clean4.slice(i, i + 2), 16));
30578
+ }
30579
+ return out;
30580
+ }
30555
30581
  function bytesToHex3(bytes) {
30556
30582
  const arr = typeof bytes === "string" ? Array.from(atob(bytes), (c) => c.charCodeAt(0)) : bytes;
30557
30583
  let s = "0x";
30558
30584
  for (const b of arr) s += b.toString(16).padStart(2, "0");
30559
30585
  return s;
30560
30586
  }
30587
+ function preflightCreateJob(terms) {
30588
+ const addressCheck = checkSuiAddress(terms.seller);
30589
+ if (!addressCheck.valid) return addressCheck;
30590
+ if (!Number.isFinite(terms.amountUsdc) || terms.amountUsdc <= 0) {
30591
+ return preflightFail("INVALID_AMOUNT", `Amount must be positive. Got ${terms.amountUsdc}.`);
30592
+ }
30593
+ if (terms.amountUsdc > MAX_JOB_USDC) {
30594
+ return preflightFail(
30595
+ "INVALID_AMOUNT",
30596
+ `v1 caps escrow jobs at ${MAX_JOB_USDC} USDC (no-arbitration split only stays fair at small sizes). Got ${terms.amountUsdc}.`
30597
+ );
30598
+ }
30599
+ if (terms.deliverByMs <= Date.now()) {
30600
+ return preflightFail("INVALID_AMOUNT", "deliverByMs must be in the future.");
30601
+ }
30602
+ if (terms.deliverByMs > Date.now() + MAX_DELIVER_HORIZON_MS) {
30603
+ return preflightFail("INVALID_AMOUNT", "deliverByMs is more than 365 days out.");
30604
+ }
30605
+ if (terms.reviewWindowMs < 0 || terms.reviewWindowMs > MAX_REVIEW_WINDOW_MS) {
30606
+ return preflightFail("INVALID_AMOUNT", "reviewWindowMs must be 0\u201330 days.");
30607
+ }
30608
+ if (!Number.isInteger(terms.rejectSplitBps) || terms.rejectSplitBps < 0 || terms.rejectSplitBps > 1e4) {
30609
+ return preflightFail("INVALID_AMOUNT", "rejectSplitBps must be an integer 0\u201310000.");
30610
+ }
30611
+ try {
30612
+ hexToBytes3(terms.specHash);
30613
+ } catch (e) {
30614
+ return preflightFail("INVALID_AMOUNT", e.message);
30615
+ }
30616
+ return PREFLIGHT_OK;
30617
+ }
30561
30618
  async function getJob(client, jobId) {
30562
30619
  const resp = await client.core.getObject({ objectId: jobId, include: { json: true } }).catch((e) => {
30563
30620
  throw new T2000Error(
@@ -35874,6 +35931,40 @@ function recordSpendIfLanded(amountUsdc, digest) {
35874
35931
  }
35875
35932
  }
35876
35933
 
35934
+ // src/lib/settle-fee.ts
35935
+ var SERVICES_SETTLE_FEE_BPS = 500;
35936
+ var MICRO = 1e6;
35937
+ var TRAILING_ZEROS = /0+$/;
35938
+ function formatUsdMicro(micro) {
35939
+ if (!Number.isFinite(micro) || micro < 0) {
35940
+ return "\u2014";
35941
+ }
35942
+ const m = Math.floor(micro);
35943
+ const whole = Math.floor(m / MICRO);
35944
+ const frac = String(m % MICRO).padStart(6, "0");
35945
+ const dp = Math.max(2, frac.replace(TRAILING_ZEROS, "").length);
35946
+ return `$${whole}.${frac.slice(0, dp)}`;
35947
+ }
35948
+ function settlementSplit(priceUsdc, feeBps = SERVICES_SETTLE_FEE_BPS) {
35949
+ const escrowMicro = Math.floor(priceUsdc * MICRO);
35950
+ const feeMicro = Math.floor(escrowMicro * feeBps / 1e4);
35951
+ const payoutMicro = escrowMicro - feeMicro;
35952
+ return {
35953
+ escrowUsdc: priceUsdc,
35954
+ feeBps,
35955
+ feeMicro,
35956
+ payoutMicro,
35957
+ escrow: formatUsdMicro(escrowMicro),
35958
+ fee: formatUsdMicro(feeMicro),
35959
+ payout: formatUsdMicro(payoutMicro)
35960
+ };
35961
+ }
35962
+ function sellerReceivesLine(priceUsdc) {
35963
+ const s = settlementSplit(priceUsdc);
35964
+ return `${s.payout} after the ${s.feeBps / 100}% settle fee (${s.fee})`;
35965
+ }
35966
+ var SETTLE_FEE_NOTE = `Services settle at ${SERVICES_SETTLE_FEE_BPS / 100}% from the seller's payout \u2014 never added to the buyer's escrow. Refunds, cancels and declines are fee-free.`;
35967
+
35877
35968
  // src/lib/agent-ref.ts
35878
35969
  var HASH_ID_RE = /^#\d{1,10}$/;
35879
35970
  var BARE_DIGITS_RE = /^\d{1,10}$/;
@@ -36343,6 +36434,36 @@ no fund step; unclaimed openings refund fee-free):
36343
36434
  reviewWindowMs = opts.review ? parseDuration(opts.review) : DEFAULT_REVIEW_WINDOW_MS;
36344
36435
  rejectSplitBps = Number.parseInt(opts.split, 10);
36345
36436
  }
36437
+ const pre = preflightCreateJob({
36438
+ seller,
36439
+ amountUsdc,
36440
+ specHash,
36441
+ deliverByMs,
36442
+ reviewWindowMs,
36443
+ rejectSplitBps
36444
+ });
36445
+ if (!pre.valid) {
36446
+ throw new Error(pre.error);
36447
+ }
36448
+ const preAgent = await withAgent({ keyPath: opts.key });
36449
+ const bal = await preAgent.balance();
36450
+ const usdc = bal.stables.USDC ?? 0;
36451
+ if (usdc < amountUsdc) {
36452
+ throw new Error(
36453
+ `Insufficient USDC \u2014 need ${formatUsdMicro(Math.floor(amountUsdc * 1e6))}, wallet has ${formatUsdMicro(Math.floor(usdc * 1e6))}. Fund it: t2 fund`
36454
+ );
36455
+ }
36456
+ assertSpendAllowed(amountUsdc);
36457
+ const split4 = settlementSplit(amountUsdc);
36458
+ if (!isJsonMode()) {
36459
+ printBlank();
36460
+ printInfo("Hire preflight");
36461
+ printKeyValue(" Escrow", `${split4.escrow} USDC (leaves your wallet on sign)`);
36462
+ printKeyValue(" Seller", truncateAddress(seller));
36463
+ if (serviceSlug) printKeyValue(" Service", serviceSlug);
36464
+ printKeyValue(" Deliver by", new Date(deliverByMs).toISOString());
36465
+ printKeyValue(" Settle fee", `${split4.fee} from the seller's payout \u2014 not added to your escrow`);
36466
+ }
36346
36467
  const { address, digest } = await sponsoredJobVerb({
36347
36468
  base,
36348
36469
  keyPath: opts.key,
@@ -36368,7 +36489,7 @@ no fund step; unclaimed openings refund fee-free):
36368
36489
  }
36369
36490
  }
36370
36491
  if (isJsonMode()) {
36371
- printJson({ jobId, digest, buyer: address, seller, amountUsdc, specHash, deliverByMs, reviewWindowMs, rejectSplitBps, ...serviceSlug ? { service: serviceSlug } : {} });
36492
+ printJson({ jobId, digest, buyer: address, seller, amountUsdc, specHash, deliverByMs, reviewWindowMs, rejectSplitBps, feeBps: split4.feeBps, sellerReceiveUsdc: split4.payoutMicro / 1e6, ...serviceSlug ? { service: serviceSlug } : {} });
36372
36493
  return;
36373
36494
  }
36374
36495
  printBlank();
@@ -36376,6 +36497,7 @@ no fund step; unclaimed openings refund fee-free):
36376
36497
  if (jobId) printKeyValue("Job", jobId);
36377
36498
  printKeyValue("Spec hash", specHash);
36378
36499
  printKeyValue("Deliver by", new Date(deliverByMs).toISOString());
36500
+ printKeyValue("Seller receives", sellerReceivesLine(amountUsdc));
36379
36501
  if (digest) printKeyValue("Tx", digest);
36380
36502
  printBlank();
36381
36503
  if (jobId) {
@@ -36675,8 +36797,8 @@ no fund step; unclaimed openings refund fee-free):
36675
36797
  }
36676
36798
 
36677
36799
  // src/commands/service.ts
36678
- var import_picocolors14 = __toESM(require_picocolors(), 1);
36679
36800
  import { createHash as createHash2 } from "crypto";
36801
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
36680
36802
  import { readFile as readFile5 } from "fs/promises";
36681
36803
  var DEFAULT_API_BASE8 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
36682
36804
  async function signedServiceAction(opts) {
@@ -36733,7 +36855,10 @@ function formatSla(minutes) {
36733
36855
  function printService(o) {
36734
36856
  const flag = o.retired ? import_picocolors14.default.dim(" (retired)") : "";
36735
36857
  printLine(`${import_picocolors14.default.bold(o.name)} ${import_picocolors14.default.dim(`\xB7 ${o.slug}`)}${flag}`);
36736
- printKeyValue("Price", `$${o.priceUsdc.toFixed(2)} USDC`);
36858
+ printKeyValue(
36859
+ "Price",
36860
+ `$${o.priceUsdc.toFixed(2)} USDC ${import_picocolors14.default.dim(`\xB7 seller receives ${settlementSplit(o.priceUsdc).payout}`)}`
36861
+ );
36737
36862
  printKeyValue("Delivery", `within ${formatSla(o.slaMinutes)}`);
36738
36863
  printKeyValue(
36739
36864
  "Seller",
@@ -36813,15 +36938,22 @@ Examples:
36813
36938
  payload
36814
36939
  });
36815
36940
  if (isJsonMode()) {
36816
- printJson({ address, ...payload });
36941
+ printJson({
36942
+ address,
36943
+ ...payload,
36944
+ feeBps: SERVICES_SETTLE_FEE_BPS,
36945
+ sellerReceiveUsdc: settlementSplit(priceUsdc).payoutMicro / 1e6
36946
+ });
36817
36947
  return;
36818
36948
  }
36819
36949
  printBlank();
36820
36950
  printSuccess(`"${payload.name}" is listed \u2014 $${priceUsdc.toFixed(2)} USDC, delivery within ${formatSla(slaMinutes)}`);
36951
+ printKeyValue("You receive", sellerReceivesLine(priceUsdc));
36821
36952
  printKeyValue("Slug", slug);
36822
36953
  printKeyValue("Storefront", `https://t2000.ai/${address}`);
36823
36954
  printKeyValue("Buyers run", `t2 job hire --agent ${address} --service ${slug}`);
36824
36955
  printBlank();
36956
+ printInfo(SETTLE_FEE_NOTE);
36825
36957
  printInfo("Watch for incoming jobs with: t2 job watch --mine");
36826
36958
  printBlank();
36827
36959
  } catch (error) {