@t2000/cli 10.27.0 → 10.27.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
@@ -30545,6 +30545,8 @@ var A2A_ESCROW_FEE_CONFIG_VERSION = Number(
30545
30545
  );
30546
30546
  var MODULE2 = "escrow";
30547
30547
  var MAX_JOB_USDC = 50;
30548
+ var MAX_REVIEW_WINDOW_MS = 2592e6;
30549
+ var MAX_DELIVER_HORIZON_MS = 31536e6;
30548
30550
  var JOB_STATES = [
30549
30551
  "funded",
30550
30552
  "delivered",
@@ -30552,12 +30554,57 @@ var JOB_STATES = [
30552
30554
  "refunded",
30553
30555
  "rejected"
30554
30556
  ];
30557
+ function hexToBytes3(hex) {
30558
+ const clean4 = hex.startsWith("0x") ? hex.slice(2) : hex;
30559
+ if (clean4.length === 0 || clean4.length % 2 !== 0 || /[^0-9a-fA-F]/.test(clean4)) {
30560
+ throw new T2000Error(
30561
+ "INVALID_AMOUNT",
30562
+ `Expected a hex hash (0x\u2026), got "${hex.slice(0, 32)}"`
30563
+ );
30564
+ }
30565
+ const out = [];
30566
+ for (let i = 0; i < clean4.length; i += 2) {
30567
+ out.push(Number.parseInt(clean4.slice(i, i + 2), 16));
30568
+ }
30569
+ return out;
30570
+ }
30555
30571
  function bytesToHex3(bytes) {
30556
30572
  const arr = typeof bytes === "string" ? Array.from(atob(bytes), (c) => c.charCodeAt(0)) : bytes;
30557
30573
  let s = "0x";
30558
30574
  for (const b of arr) s += b.toString(16).padStart(2, "0");
30559
30575
  return s;
30560
30576
  }
30577
+ function preflightCreateJob(terms) {
30578
+ const addressCheck = checkSuiAddress(terms.seller);
30579
+ if (!addressCheck.valid) return addressCheck;
30580
+ if (!Number.isFinite(terms.amountUsdc) || terms.amountUsdc <= 0) {
30581
+ return preflightFail("INVALID_AMOUNT", `Amount must be positive. Got ${terms.amountUsdc}.`);
30582
+ }
30583
+ if (terms.amountUsdc > MAX_JOB_USDC) {
30584
+ return preflightFail(
30585
+ "INVALID_AMOUNT",
30586
+ `v1 caps escrow jobs at ${MAX_JOB_USDC} USDC (no-arbitration split only stays fair at small sizes). Got ${terms.amountUsdc}.`
30587
+ );
30588
+ }
30589
+ if (terms.deliverByMs <= Date.now()) {
30590
+ return preflightFail("INVALID_AMOUNT", "deliverByMs must be in the future.");
30591
+ }
30592
+ if (terms.deliverByMs > Date.now() + MAX_DELIVER_HORIZON_MS) {
30593
+ return preflightFail("INVALID_AMOUNT", "deliverByMs is more than 365 days out.");
30594
+ }
30595
+ if (terms.reviewWindowMs < 0 || terms.reviewWindowMs > MAX_REVIEW_WINDOW_MS) {
30596
+ return preflightFail("INVALID_AMOUNT", "reviewWindowMs must be 0\u201330 days.");
30597
+ }
30598
+ if (!Number.isInteger(terms.rejectSplitBps) || terms.rejectSplitBps < 0 || terms.rejectSplitBps > 1e4) {
30599
+ return preflightFail("INVALID_AMOUNT", "rejectSplitBps must be an integer 0\u201310000.");
30600
+ }
30601
+ try {
30602
+ hexToBytes3(terms.specHash);
30603
+ } catch (e) {
30604
+ return preflightFail("INVALID_AMOUNT", e.message);
30605
+ }
30606
+ return PREFLIGHT_OK;
30607
+ }
30561
30608
  async function getJob(client, jobId) {
30562
30609
  const resp = await client.core.getObject({ objectId: jobId, include: { json: true } }).catch((e) => {
30563
30610
  throw new T2000Error(
@@ -35874,6 +35921,40 @@ function recordSpendIfLanded(amountUsdc, digest) {
35874
35921
  }
35875
35922
  }
35876
35923
 
35924
+ // src/lib/settle-fee.ts
35925
+ var SERVICES_SETTLE_FEE_BPS = 500;
35926
+ var MICRO = 1e6;
35927
+ var TRAILING_ZEROS = /0+$/;
35928
+ function formatUsdMicro(micro) {
35929
+ if (!Number.isFinite(micro) || micro < 0) {
35930
+ return "\u2014";
35931
+ }
35932
+ const m = Math.floor(micro);
35933
+ const whole = Math.floor(m / MICRO);
35934
+ const frac = String(m % MICRO).padStart(6, "0");
35935
+ const dp = Math.max(2, frac.replace(TRAILING_ZEROS, "").length);
35936
+ return `$${whole}.${frac.slice(0, dp)}`;
35937
+ }
35938
+ function settlementSplit(priceUsdc, feeBps = SERVICES_SETTLE_FEE_BPS) {
35939
+ const escrowMicro = Math.floor(priceUsdc * MICRO);
35940
+ const feeMicro = Math.floor(escrowMicro * feeBps / 1e4);
35941
+ const payoutMicro = escrowMicro - feeMicro;
35942
+ return {
35943
+ escrowUsdc: priceUsdc,
35944
+ feeBps,
35945
+ feeMicro,
35946
+ payoutMicro,
35947
+ escrow: formatUsdMicro(escrowMicro),
35948
+ fee: formatUsdMicro(feeMicro),
35949
+ payout: formatUsdMicro(payoutMicro)
35950
+ };
35951
+ }
35952
+ function sellerReceivesLine(priceUsdc) {
35953
+ const s = settlementSplit(priceUsdc);
35954
+ return `${s.payout} after the ${s.feeBps / 100}% settle fee (${s.fee})`;
35955
+ }
35956
+ 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.`;
35957
+
35877
35958
  // src/lib/agent-ref.ts
35878
35959
  var HASH_ID_RE = /^#\d{1,10}$/;
35879
35960
  var BARE_DIGITS_RE = /^\d{1,10}$/;
@@ -36343,6 +36424,36 @@ no fund step; unclaimed openings refund fee-free):
36343
36424
  reviewWindowMs = opts.review ? parseDuration(opts.review) : DEFAULT_REVIEW_WINDOW_MS;
36344
36425
  rejectSplitBps = Number.parseInt(opts.split, 10);
36345
36426
  }
36427
+ const pre = preflightCreateJob({
36428
+ seller,
36429
+ amountUsdc,
36430
+ specHash,
36431
+ deliverByMs,
36432
+ reviewWindowMs,
36433
+ rejectSplitBps
36434
+ });
36435
+ if (!pre.valid) {
36436
+ throw new Error(pre.error);
36437
+ }
36438
+ const preAgent = await withAgent({ keyPath: opts.key });
36439
+ const bal = await preAgent.balance();
36440
+ const usdc = bal.stables.USDC ?? 0;
36441
+ if (usdc < amountUsdc) {
36442
+ throw new Error(
36443
+ `Insufficient USDC \u2014 need ${formatUsdMicro(Math.floor(amountUsdc * 1e6))}, wallet has ${formatUsdMicro(Math.floor(usdc * 1e6))}. Fund it: t2 fund`
36444
+ );
36445
+ }
36446
+ assertSpendAllowed(amountUsdc);
36447
+ const split4 = settlementSplit(amountUsdc);
36448
+ if (!isJsonMode()) {
36449
+ printBlank();
36450
+ printInfo("Hire preflight");
36451
+ printKeyValue(" Escrow", `${split4.escrow} USDC (leaves your wallet on sign)`);
36452
+ printKeyValue(" Seller", truncateAddress(seller));
36453
+ if (serviceSlug) printKeyValue(" Service", serviceSlug);
36454
+ printKeyValue(" Deliver by", new Date(deliverByMs).toISOString());
36455
+ printKeyValue(" Settle fee", `${split4.fee} from the seller's payout \u2014 not added to your escrow`);
36456
+ }
36346
36457
  const { address, digest } = await sponsoredJobVerb({
36347
36458
  base,
36348
36459
  keyPath: opts.key,
@@ -36368,7 +36479,7 @@ no fund step; unclaimed openings refund fee-free):
36368
36479
  }
36369
36480
  }
36370
36481
  if (isJsonMode()) {
36371
- printJson({ jobId, digest, buyer: address, seller, amountUsdc, specHash, deliverByMs, reviewWindowMs, rejectSplitBps, ...serviceSlug ? { service: serviceSlug } : {} });
36482
+ printJson({ jobId, digest, buyer: address, seller, amountUsdc, specHash, deliverByMs, reviewWindowMs, rejectSplitBps, feeBps: split4.feeBps, sellerReceiveUsdc: split4.payoutMicro / 1e6, ...serviceSlug ? { service: serviceSlug } : {} });
36372
36483
  return;
36373
36484
  }
36374
36485
  printBlank();
@@ -36376,6 +36487,7 @@ no fund step; unclaimed openings refund fee-free):
36376
36487
  if (jobId) printKeyValue("Job", jobId);
36377
36488
  printKeyValue("Spec hash", specHash);
36378
36489
  printKeyValue("Deliver by", new Date(deliverByMs).toISOString());
36490
+ printKeyValue("Seller receives", sellerReceivesLine(amountUsdc));
36379
36491
  if (digest) printKeyValue("Tx", digest);
36380
36492
  printBlank();
36381
36493
  if (jobId) {
@@ -36675,8 +36787,8 @@ no fund step; unclaimed openings refund fee-free):
36675
36787
  }
36676
36788
 
36677
36789
  // src/commands/service.ts
36678
- var import_picocolors14 = __toESM(require_picocolors(), 1);
36679
36790
  import { createHash as createHash2 } from "crypto";
36791
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
36680
36792
  import { readFile as readFile5 } from "fs/promises";
36681
36793
  var DEFAULT_API_BASE8 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
36682
36794
  async function signedServiceAction(opts) {
@@ -36733,7 +36845,10 @@ function formatSla(minutes) {
36733
36845
  function printService(o) {
36734
36846
  const flag = o.retired ? import_picocolors14.default.dim(" (retired)") : "";
36735
36847
  printLine(`${import_picocolors14.default.bold(o.name)} ${import_picocolors14.default.dim(`\xB7 ${o.slug}`)}${flag}`);
36736
- printKeyValue("Price", `$${o.priceUsdc.toFixed(2)} USDC`);
36848
+ printKeyValue(
36849
+ "Price",
36850
+ `$${o.priceUsdc.toFixed(2)} USDC ${import_picocolors14.default.dim(`\xB7 seller receives ${settlementSplit(o.priceUsdc).payout}`)}`
36851
+ );
36737
36852
  printKeyValue("Delivery", `within ${formatSla(o.slaMinutes)}`);
36738
36853
  printKeyValue(
36739
36854
  "Seller",
@@ -36813,15 +36928,22 @@ Examples:
36813
36928
  payload
36814
36929
  });
36815
36930
  if (isJsonMode()) {
36816
- printJson({ address, ...payload });
36931
+ printJson({
36932
+ address,
36933
+ ...payload,
36934
+ feeBps: SERVICES_SETTLE_FEE_BPS,
36935
+ sellerReceiveUsdc: settlementSplit(priceUsdc).payoutMicro / 1e6
36936
+ });
36817
36937
  return;
36818
36938
  }
36819
36939
  printBlank();
36820
36940
  printSuccess(`"${payload.name}" is listed \u2014 $${priceUsdc.toFixed(2)} USDC, delivery within ${formatSla(slaMinutes)}`);
36941
+ printKeyValue("You receive", sellerReceivesLine(priceUsdc));
36821
36942
  printKeyValue("Slug", slug);
36822
36943
  printKeyValue("Storefront", `https://t2000.ai/${address}`);
36823
36944
  printKeyValue("Buyers run", `t2 job hire --agent ${address} --service ${slug}`);
36824
36945
  printBlank();
36946
+ printInfo(SETTLE_FEE_NOTE);
36825
36947
  printInfo("Watch for incoming jobs with: t2 job watch --mine");
36826
36948
  printBlank();
36827
36949
  } catch (error) {