@t2000/cli 9.12.0 → 9.13.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.
@@ -6,7 +6,7 @@ import { createRequire } from "module";
6
6
  import Url, { fileURLToPath } from "url";
7
7
  import { dirname, join as join$1, resolve as resolve$1 } from "path";
8
8
  import * as nc from "crypto";
9
- import nc__default from "crypto";
9
+ import nc__default, { createHash } from "crypto";
10
10
  import stream3, { Readable } from "stream";
11
11
  import util3 from "util";
12
12
  import http5 from "http";
@@ -122325,7 +122325,7 @@ var require_crypto_compat = __commonJS({
122325
122325
  } else {
122326
122326
  X509Certificate = crypto4.X509Certificate;
122327
122327
  }
122328
- var createHash = function(algorithm) {
122328
+ var createHash2 = function(algorithm) {
122329
122329
  if (isBrowser) {
122330
122330
  const algo = algorithm.toLowerCase();
122331
122331
  if (hashJs[algo]) {
@@ -122530,7 +122530,7 @@ ${b64.match(/.{1,64}/g).join("\n")}
122530
122530
  module.exports = {
122531
122531
  ...crypto4,
122532
122532
  X509Certificate,
122533
- createHash,
122533
+ createHash: createHash2,
122534
122534
  createVerify,
122535
122535
  createPublicKey,
122536
122536
  isBrowser
@@ -143032,6 +143032,7 @@ var init_swap_quote = __esm2({
143032
143032
  });
143033
143033
  init_errors7();
143034
143034
  var MIST_PER_SUI2 = 1000000000n;
143035
+ var USDC_DECIMALS = 6;
143035
143036
  var SUPPORTED_ASSETS = {
143036
143037
  USDC: {
143037
143038
  type: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC",
@@ -143695,6 +143696,18 @@ async function buildSendTx({
143695
143696
  required: amount2
143696
143697
  });
143697
143698
  }
143699
+ if (asset === "USDC" || asset === "USDsui") {
143700
+ const rawFloor = displayToRaw(GASLESS_MIN_STABLE_AMOUNT, assetInfo.decimals);
143701
+ const remainder = totalBalance - rawAmount;
143702
+ if (remainder > 0n && remainder < rawFloor) {
143703
+ const total = Number(totalBalance) / 10 ** assetInfo.decimals;
143704
+ throw new T2000Error(
143705
+ "INVALID_AMOUNT",
143706
+ `Gasless ${asset} transfers must send the entire balance or leave at least ${GASLESS_MIN_STABLE_AMOUNT} ${asset}. Sending ${amount2} of ${total} leaves ${(total - amount2).toFixed(assetInfo.decimals)}. Send ${total} (everything) or at most ${(total - GASLESS_MIN_STABLE_AMOUNT).toFixed(assetInfo.decimals)}.`,
143707
+ { available: total, required: amount2 }
143708
+ );
143709
+ }
143710
+ }
143698
143711
  if (asset === "SUI") {
143699
143712
  const [sendCoin] = tx.splitCoins(tx.gas, [rawAmount]);
143700
143713
  tx.transferObjects([sendCoin], recipient);
@@ -145191,6 +145204,146 @@ process.env.A2A_ESCROW_FEE_CONFIG_ID ?? "0xddaa25570950b7484dbf20797ebcf75707be9
145191
145204
  Number(
145192
145205
  process.env.A2A_ESCROW_FEE_CONFIG_VERSION ?? 931861903
145193
145206
  );
145207
+ var MODULE = "escrow";
145208
+ var MAX_JOB_USDC = 50;
145209
+ var JOB_STATES = [
145210
+ "funded",
145211
+ "delivered",
145212
+ "released",
145213
+ "refunded",
145214
+ "rejected"
145215
+ ];
145216
+ function bytesToHex22(bytes) {
145217
+ const arr = typeof bytes === "string" ? Array.from(atob(bytes), (c) => c.charCodeAt(0)) : bytes;
145218
+ let s = "0x";
145219
+ for (const b of arr) s += b.toString(16).padStart(2, "0");
145220
+ return s;
145221
+ }
145222
+ async function getJob(client, jobId) {
145223
+ const resp = await client.core.getObject({ objectId: jobId, include: { json: true } }).catch((e) => {
145224
+ throw new T2000Error(
145225
+ "RPC_ERROR",
145226
+ `Job ${jobId} not found: ${e instanceof Error ? e.message : String(e)}`
145227
+ );
145228
+ });
145229
+ const objType = resp.object?.type ?? "";
145230
+ const json2 = resp.object?.json;
145231
+ if (!json2 || !objType.includes(`::${MODULE}::Job<`)) {
145232
+ throw new T2000Error("RPC_ERROR", `Object ${jobId} is not an a2a_escrow Job.`);
145233
+ }
145234
+ const stateNum = Number(json2.state ?? -1);
145235
+ const state = JOB_STATES[stateNum];
145236
+ if (!state) {
145237
+ throw new T2000Error("RPC_ERROR", `Job ${jobId} has unknown state ${stateNum}.`);
145238
+ }
145239
+ const deliveredAtMs = Number(json2.delivered_at_ms ?? 0);
145240
+ const deliveryBytes = json2.delivery_hash ?? [];
145241
+ const hasDelivery = deliveredAtMs > 0;
145242
+ return {
145243
+ id: jobId,
145244
+ buyer: String(json2.buyer),
145245
+ seller: String(json2.seller),
145246
+ amountUsdc: Number(json2.amount) / 10 ** USDC_DECIMALS,
145247
+ escrowUsdc: Number(json2.escrow) / 10 ** USDC_DECIMALS,
145248
+ feeBps: Number(json2.fee_bps ?? 0),
145249
+ specHash: bytesToHex22(json2.spec_hash ?? []),
145250
+ deliverByMs: Number(json2.deliver_by_ms),
145251
+ reviewWindowMs: Number(json2.review_window_ms),
145252
+ rejectSplitBps: Number(json2.reject_split_bps),
145253
+ state,
145254
+ deliveryHash: hasDelivery ? bytesToHex22(deliveryBytes) : null,
145255
+ deliveredAtMs: hasDelivery ? deliveredAtMs : null,
145256
+ createdAtMs: Number(json2.created_at_ms)
145257
+ };
145258
+ }
145259
+ function jobActionsFor(job, caller, nowMs = Date.now()) {
145260
+ const me = validateAddress(caller);
145261
+ const isBuyer = me === job.buyer;
145262
+ const isSeller = me === job.seller;
145263
+ const actions = [];
145264
+ if (job.state === "funded") {
145265
+ if (isSeller && nowMs <= job.deliverByMs) actions.push("deliver");
145266
+ if (isBuyer) actions.push("release");
145267
+ if (nowMs > job.deliverByMs) actions.push("refund");
145268
+ } else if (job.state === "delivered") {
145269
+ const windowClosesMs = (job.deliveredAtMs ?? 0) + job.reviewWindowMs;
145270
+ if (isBuyer && nowMs <= windowClosesMs) actions.push("release", "reject");
145271
+ if (nowMs > windowClosesMs) actions.push("release");
145272
+ }
145273
+ return actions;
145274
+ }
145275
+ async function commerceFetchJson(url3, init) {
145276
+ const res = await fetch(url3, {
145277
+ method: init?.method ?? "GET",
145278
+ headers: init?.body ? { "Content-Type": "application/json" } : void 0,
145279
+ body: init?.body ? JSON.stringify(init.body) : void 0
145280
+ });
145281
+ const json2 = await res.json().catch(() => ({}));
145282
+ if (!res.ok) {
145283
+ const err = json2.error;
145284
+ const msg = typeof err === "string" ? err : err?.message ?? `HTTP ${res.status}`;
145285
+ throw new Error(msg);
145286
+ }
145287
+ return json2;
145288
+ }
145289
+ async function listOfferings(base, filter2 = {}) {
145290
+ const params = new URLSearchParams();
145291
+ if (filter2.agent) params.set("agent", filter2.agent);
145292
+ if (filter2.query) params.set("q", filter2.query);
145293
+ const qs = params.size > 0 ? `?${params.toString()}` : "";
145294
+ const json2 = await commerceFetchJson(`${base}/offerings${qs}`);
145295
+ const offerings = json2.offerings ?? [];
145296
+ return { total: json2.total ?? offerings.length, offerings };
145297
+ }
145298
+ async function fetchOffering(base, agent, slug) {
145299
+ const { offerings: rows } = await listOfferings(base, { agent });
145300
+ const match = rows.find((o) => o.slug === slug.trim().toLowerCase());
145301
+ if (!match) {
145302
+ const live = rows.filter((o) => !o.retired).map((o) => o.slug);
145303
+ throw new Error(
145304
+ `Agent ${truncateAddress(agent)} has no offering "${slug}".` + (live.length > 0 ? ` Live offerings: ${live.join(", ")}` : "")
145305
+ );
145306
+ }
145307
+ if (match.retired) {
145308
+ throw new Error(
145309
+ `Offering "${slug}" is retired \u2014 the seller no longer sells it.`
145310
+ );
145311
+ }
145312
+ return match;
145313
+ }
145314
+ async function sha256Hex(content) {
145315
+ const digest = await crypto.subtle.digest(
145316
+ "SHA-256",
145317
+ new TextEncoder().encode(content)
145318
+ );
145319
+ return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
145320
+ }
145321
+ async function putJobSpec(base, content) {
145322
+ const json2 = await commerceFetchJson(`${base}/job/spec`, {
145323
+ method: "POST",
145324
+ body: { content }
145325
+ });
145326
+ const hash7 = json2.hash;
145327
+ if (!hash7) {
145328
+ throw new Error("Failed to store the job spec.");
145329
+ }
145330
+ return hash7;
145331
+ }
145332
+ async function getJobSpec(base, hash7) {
145333
+ const clean4 = hash7.trim().toLowerCase().replace(/^0x/, "");
145334
+ const json2 = await commerceFetchJson(`${base}/job/spec/${clean4}`);
145335
+ const content = json2.content;
145336
+ if (content === void 0) {
145337
+ throw new Error("No spec stored for this hash.");
145338
+ }
145339
+ const actual = await sha256Hex(content);
145340
+ if (actual !== clean4) {
145341
+ throw new Error(
145342
+ "Spec content does NOT match its hash \u2014 the store returned tampered data. Do not trust it."
145343
+ );
145344
+ }
145345
+ return content;
145346
+ }
145194
145347
  init_coinSelection();
145195
145348
  init_cetus_swap();
145196
145349
  init_coinSelection();
@@ -145207,6 +145360,23 @@ async function createAgent(keyPath) {
145207
145360
  return T2000.create({ keyPath });
145208
145361
  }
145209
145362
  init_zod();
145363
+ var TxMutex = class {
145364
+ queue = Promise.resolve();
145365
+ async run(fn) {
145366
+ let release;
145367
+ const next = new Promise((r) => {
145368
+ release = r;
145369
+ });
145370
+ const prev = this.queue;
145371
+ this.queue = next;
145372
+ await prev;
145373
+ try {
145374
+ return await fn();
145375
+ } finally {
145376
+ release();
145377
+ }
145378
+ }
145379
+ };
145210
145380
  function mapError(err) {
145211
145381
  if (err instanceof LimitExceededError2) {
145212
145382
  return {
@@ -145242,6 +145412,416 @@ function errorResult(err) {
145242
145412
  isError: true
145243
145413
  };
145244
145414
  }
145415
+ var API_BASE = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
145416
+ async function signedMutation(opts) {
145417
+ const address2 = opts.agent.address();
145418
+ const chRes = await fetch(`${API_BASE}/agent/challenge`, {
145419
+ method: "POST",
145420
+ headers: { "Content-Type": "application/json" },
145421
+ body: JSON.stringify({ address: address2 })
145422
+ });
145423
+ const challenge2 = await chRes.json().catch(() => ({}));
145424
+ if (!challenge2.nonce) throw new Error("Failed to get a challenge nonce.");
145425
+ const payloadHash = createHash("sha256").update(JSON.stringify(opts.payload), "utf8").digest("hex");
145426
+ const message = new TextEncoder().encode(
145427
+ `t2000-${opts.kind}:${challenge2.nonce}:${payloadHash}`
145428
+ );
145429
+ const { signature: signature2 } = await opts.agent.signer.signPersonalMessage(message);
145430
+ const res = await fetch(opts.url, {
145431
+ method: "POST",
145432
+ headers: { "Content-Type": "application/json" },
145433
+ body: JSON.stringify(opts.body(challenge2.nonce, signature2, opts.payload))
145434
+ });
145435
+ const json2 = await res.json().catch(() => ({}));
145436
+ if (!res.ok) {
145437
+ const err = json2.error;
145438
+ throw new Error(
145439
+ typeof err === "string" ? err : err?.message ?? `HTTP ${res.status}`
145440
+ );
145441
+ }
145442
+ return json2;
145443
+ }
145444
+ async function sponsoredJobVerb(agent, action, params) {
145445
+ const address2 = agent.address();
145446
+ const prepRes = await fetch(`${API_BASE}/job/prepare`, {
145447
+ method: "POST",
145448
+ headers: { "Content-Type": "application/json" },
145449
+ body: JSON.stringify({ address: address2, action, params })
145450
+ });
145451
+ const prep = await prepRes.json().catch(() => ({}));
145452
+ if (!prepRes.ok) {
145453
+ const msg = typeof prep.error === "string" ? prep.error : prep.error?.message ?? `HTTP ${prepRes.status}`;
145454
+ throw new Error(msg);
145455
+ }
145456
+ if (!(prep.nonce && prep.txBytes)) throw new Error("Failed to prepare the transaction.");
145457
+ const bytes = new Uint8Array(Buffer.from(prep.txBytes, "base64"));
145458
+ const { signature: signature2 } = await agent.signer.signTransaction(bytes);
145459
+ const subRes = await fetch(`${API_BASE}/job/submit`, {
145460
+ method: "POST",
145461
+ headers: { "Content-Type": "application/json" },
145462
+ body: JSON.stringify({ nonce: prep.nonce, address: address2, signature: signature2 })
145463
+ });
145464
+ const sub2 = await subRes.json().catch(() => ({}));
145465
+ if (!subRes.ok) {
145466
+ const msg = typeof sub2.error === "string" ? sub2.error : sub2.error?.message ?? `HTTP ${subRes.status}`;
145467
+ throw new Error(msg);
145468
+ }
145469
+ return { digest: sub2.digest };
145470
+ }
145471
+ function slugify2(name) {
145472
+ return name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "").slice(0, 48);
145473
+ }
145474
+ function parseRequirements(input) {
145475
+ if (input === void 0) return null;
145476
+ try {
145477
+ const parsed = JSON.parse(input);
145478
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed;
145479
+ } catch {
145480
+ }
145481
+ return input.trim();
145482
+ }
145483
+ async function resolveJobId(digest) {
145484
+ if (!digest) return void 0;
145485
+ try {
145486
+ const client = getSuiClient();
145487
+ const result = await client.core.waitForTransaction({
145488
+ digest,
145489
+ include: { objectTypes: true },
145490
+ timeout: 15e3
145491
+ });
145492
+ const txn = result.$kind === "Transaction" ? result.Transaction : result.FailedTransaction;
145493
+ const types3 = txn.objectTypes ?? {};
145494
+ return Object.keys(types3).find((id) => types3[id]?.includes("::escrow::Job<"));
145495
+ } catch {
145496
+ return void 0;
145497
+ }
145498
+ }
145499
+ function registerCommerceTools(server, agent) {
145500
+ const mutex = new TxMutex();
145501
+ server.tool(
145502
+ "t2000_offering_create",
145503
+ "List (or update) an OFFERING under this wallet's Agent ID \u2014 a structured, fixed-price unit of deliverable work (name, USDC price, delivery SLA, what the buyer provides, what they get back). Buyers browse offerings and fund an on-chain USDC escrow Job against one; you deliver with t2000_job_deliver and the escrow settles to you (2.5% protocol fee). NO server or endpoint needed to sell. Re-run with the same slug to update. Requires an on-chain Agent ID (`t2 agent register`). Free \u2014 one signed message, no funds spent. Mirrors `t2 offering create`.",
145504
+ {
145505
+ name: external_exports.string().max(80).describe('Offering name, e.g. "Sui market report" (max 80 chars)'),
145506
+ priceUsdc: external_exports.number().min(0.01).max(50).describe("Fixed price in USDC (0.01\u201350)"),
145507
+ slaMinutes: external_exports.number().int().positive().describe("Delivery SLA in minutes (e.g. 1440 = 24h) \u2014 the escrow refunds the buyer if you miss it"),
145508
+ description: external_exports.string().max(2e3).describe("What this offering is \u2014 buyers see it on your profile (max 2000 chars)"),
145509
+ deliverable: external_exports.string().max(1e3).describe('What the buyer receives, e.g. "Markdown report, sources cited" (max 1000 chars)'),
145510
+ slug: external_exports.string().optional().describe("Machine name (default: derived from name)"),
145511
+ requirements: external_exports.string().optional().describe("What the buyer must provide \u2014 free text or a JSON schema string"),
145512
+ reviewWindowMinutes: external_exports.number().int().positive().optional().describe("Buyer's accept/reject window after delivery (default 1440 = 24h)"),
145513
+ rejectSplitBps: external_exports.number().int().min(0).max(1e4).optional().describe("Buyer's share in bps if they reject (default 8000 = 80/20 buyer-favored)")
145514
+ },
145515
+ async ({ name, priceUsdc, slaMinutes, description, deliverable, slug, requirements, reviewWindowMinutes, rejectSplitBps }) => {
145516
+ try {
145517
+ const payload = {
145518
+ slug: (slug ?? slugify2(name)).trim().toLowerCase(),
145519
+ name: name.trim(),
145520
+ description: description.trim(),
145521
+ priceUsdc,
145522
+ slaMinutes,
145523
+ reviewWindowMinutes: reviewWindowMinutes ?? 1440,
145524
+ rejectSplitBps: rejectSplitBps ?? 8e3,
145525
+ requirements: parseRequirements(requirements),
145526
+ deliverable: deliverable.trim()
145527
+ };
145528
+ await signedMutation({
145529
+ agent,
145530
+ kind: "agent-offering",
145531
+ url: `${API_BASE}/agent/offering`,
145532
+ payload,
145533
+ body: (nonce2, signature2, p) => ({ address: agent.address(), nonce: nonce2, signature: signature2, action: "upsert", payload: p })
145534
+ });
145535
+ return {
145536
+ content: [{
145537
+ type: "text",
145538
+ text: JSON.stringify({
145539
+ ok: true,
145540
+ ...payload,
145541
+ storefront: `https://agents.t2000.ai/${agent.address()}`,
145542
+ buyersRun: `t2 job create --agent ${agent.address()} --offering ${payload.slug}`,
145543
+ watchInbox: "Use t2000_jobs (role: seller) to see incoming jobs."
145544
+ })
145545
+ }]
145546
+ };
145547
+ } catch (err) {
145548
+ return errorResult(err);
145549
+ }
145550
+ }
145551
+ );
145552
+ server.tool(
145553
+ "t2000_offering_retire",
145554
+ "Take one of your offerings off the board (soft-delete \u2014 already-funded jobs still settle on-chain). Re-create with the same slug to relist. Mirrors `t2 offering retire <slug>`.",
145555
+ { slug: external_exports.string().describe("The offering slug to retire (see t2000_offerings with your address)") },
145556
+ async ({ slug }) => {
145557
+ try {
145558
+ const payload = { slug: slug.trim().toLowerCase() };
145559
+ await signedMutation({
145560
+ agent,
145561
+ kind: "agent-offering",
145562
+ url: `${API_BASE}/agent/offering`,
145563
+ payload,
145564
+ body: (nonce2, signature2, p) => ({ address: agent.address(), nonce: nonce2, signature: signature2, action: "retire", payload: p })
145565
+ });
145566
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true, retired: payload.slug }) }] };
145567
+ } catch (err) {
145568
+ return errorResult(err);
145569
+ }
145570
+ }
145571
+ );
145572
+ server.tool(
145573
+ "t2000_offerings",
145574
+ "Browse OFFERINGS across the t2 agent economy \u2014 structured, fixed-price work other agents sell (hire them with t2000_job_create), or one agent's full catalog. No arguments = everything live. This is how you FIND WORK TO BUY; distinct from t2000_services (per-call MPP APIs). Mirrors `t2 browse` / `t2 offering list`.",
145575
+ {
145576
+ query: external_exports.string().optional().describe("Free-text search across offering names/descriptions (omit for all)"),
145577
+ agent: external_exports.string().optional().describe("One agent's Sui address \u2014 their catalog, retired included (e.g. your own to check your listings)")
145578
+ },
145579
+ async ({ query, agent: agentAddr }) => {
145580
+ try {
145581
+ const result = await listOfferings(API_BASE, {
145582
+ agent: agentAddr ? validateAddress(agentAddr) : void 0,
145583
+ query
145584
+ });
145585
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
145586
+ } catch (err) {
145587
+ return errorResult(err);
145588
+ }
145589
+ }
145590
+ );
145591
+ server.tool(
145592
+ "t2000_job_create",
145593
+ `HIRE an agent: create + fund an on-chain USDC escrow Job in one sponsored transaction (buyer side). THIS SPENDS FUNDS \u2014 the price is locked in the Job object until settlement. Two modes:
145594
+ 1. OFFERING mode (preferred): pass agent + offering (a slug from t2000_offerings) + requirements. Price/SLA/terms come from the listing.
145595
+ 2. DIRECT mode: pass seller + amountUsdc + spec (your brief; stored content-addressed, sha256 pinned on-chain) + optional deadline/review/split terms.
145596
+ The escrow protects both sides: no delivery by the deadline \u2192 anyone can refund the buyer; delivery + lapsed review window \u2192 anyone can release to the seller. Max ${MAX_JOB_USDC} USDC. Mirrors \`t2 job create\`.`,
145597
+ {
145598
+ agent: external_exports.string().optional().describe("OFFERING mode: the seller's agent address"),
145599
+ offering: external_exports.string().optional().describe("OFFERING mode: the offering slug"),
145600
+ requirements: external_exports.string().optional().describe("OFFERING mode: what the seller asked buyers to provide \u2014 JSON string or free text"),
145601
+ seller: external_exports.string().optional().describe("DIRECT mode: the seller's Sui address"),
145602
+ amountUsdc: external_exports.number().positive().max(MAX_JOB_USDC).optional().describe("DIRECT mode: USDC to escrow"),
145603
+ spec: external_exports.string().optional().describe("DIRECT mode: the job brief (stored content-addressed; its sha256 goes on-chain)"),
145604
+ deadlineMinutes: external_exports.number().int().positive().optional().describe("DIRECT mode: time the seller has to deliver (default 1440 = 24h)"),
145605
+ reviewWindowMinutes: external_exports.number().int().positive().optional().describe("DIRECT mode: your accept/reject window after delivery (default 1440)"),
145606
+ rejectSplitBps: external_exports.number().int().min(0).max(1e4).optional().describe("DIRECT mode: your share in bps if you reject (default 8000)")
145607
+ },
145608
+ async (input) => {
145609
+ try {
145610
+ const buyer = agent.address();
145611
+ let params;
145612
+ let offeringSlug;
145613
+ if (input.offering || input.agent) {
145614
+ if (!(input.offering && input.agent)) {
145615
+ throw new Error("agent and offering go together (offering mode).");
145616
+ }
145617
+ const sellerAgent = validateAddress(input.agent);
145618
+ const offering = await fetchOffering(API_BASE, sellerAgent, input.offering);
145619
+ offeringSlug = offering.slug;
145620
+ const requirements = parseRequirements(input.requirements);
145621
+ if (offering.requirements != null && requirements == null) {
145622
+ const want = typeof offering.requirements === "string" ? offering.requirements : `JSON matching: ${JSON.stringify(offering.requirements)}`;
145623
+ throw new Error(`This offering needs requirements. The seller asks for: ${want}`);
145624
+ }
145625
+ const spec = JSON.stringify({
145626
+ type: "t2-acp-job-spec@1",
145627
+ offering: {
145628
+ agent: offering.agent,
145629
+ slug: offering.slug,
145630
+ name: offering.name,
145631
+ priceUsdc: offering.priceUsdc,
145632
+ deliverable: offering.deliverable
145633
+ },
145634
+ requirements,
145635
+ buyer,
145636
+ createdAtMs: Date.now()
145637
+ });
145638
+ params = {
145639
+ seller: offering.agent,
145640
+ amountUsdc: offering.priceUsdc,
145641
+ specHash: `0x${await putJobSpec(API_BASE, spec)}`,
145642
+ deliverByMs: Date.now() + offering.slaMinutes * 6e4,
145643
+ reviewWindowMs: offering.reviewWindowMinutes * 6e4,
145644
+ rejectSplitBps: offering.rejectSplitBps
145645
+ };
145646
+ } else {
145647
+ if (!(input.seller && input.amountUsdc && input.spec)) {
145648
+ throw new Error("Provide seller + amountUsdc + spec (direct mode) or agent + offering (buy a listing).");
145649
+ }
145650
+ params = {
145651
+ seller: validateAddress(input.seller),
145652
+ amountUsdc: input.amountUsdc,
145653
+ specHash: `0x${await putJobSpec(API_BASE, input.spec)}`,
145654
+ deliverByMs: Date.now() + (input.deadlineMinutes ?? 1440) * 6e4,
145655
+ reviewWindowMs: (input.reviewWindowMinutes ?? 1440) * 6e4,
145656
+ rejectSplitBps: input.rejectSplitBps ?? 8e3
145657
+ };
145658
+ }
145659
+ const { digest } = await mutex.run(() => sponsoredJobVerb(agent, "create", params));
145660
+ const jobId = await resolveJobId(digest);
145661
+ return {
145662
+ content: [{
145663
+ type: "text",
145664
+ text: JSON.stringify({
145665
+ ok: true,
145666
+ jobId,
145667
+ digest,
145668
+ buyer,
145669
+ ...params,
145670
+ ...offeringSlug ? { offering: offeringSlug } : {},
145671
+ next: "Track it with t2000_jobs. When the seller delivers, accept with t2000_job_settle (release) or reject within the review window."
145672
+ })
145673
+ }]
145674
+ };
145675
+ } catch (err) {
145676
+ return errorResult(err);
145677
+ }
145678
+ }
145679
+ );
145680
+ server.tool(
145681
+ "t2000_jobs",
145682
+ "Escrow-job status. With jobId \u2192 the on-chain Job (state, parties, amount, deadlines), the actions THIS wallet can take right now, the buyer's spec/requirements (content-verified against the on-chain hash), and \u2014 once delivered \u2014 the delivery content. Without jobId \u2192 this wallet's job inbox from the indexer (role: seller = jobs you were hired for, buyer = jobs you funded). Read-only. Mirrors `t2 job watch [--mine]` + `t2 job spec`.",
145683
+ {
145684
+ jobId: external_exports.string().optional().describe("A Job object id (0x\u2026) for full detail (omit to list your jobs)"),
145685
+ role: external_exports.enum(["seller", "buyer"]).optional().describe("Inbox mode: which side of the table (default seller)")
145686
+ },
145687
+ async ({ jobId, role }) => {
145688
+ try {
145689
+ const me = agent.address();
145690
+ if (jobId) {
145691
+ const client = getSuiClient();
145692
+ const job = await getJob(client, validateAddress(jobId));
145693
+ const actions = jobActionsFor(job, me);
145694
+ let spec = null;
145695
+ try {
145696
+ spec = await getJobSpec(API_BASE, job.specHash);
145697
+ } catch {
145698
+ spec = null;
145699
+ }
145700
+ let delivery = null;
145701
+ if (job.deliveryHash) {
145702
+ try {
145703
+ delivery = await getJobSpec(API_BASE, job.deliveryHash);
145704
+ } catch {
145705
+ delivery = null;
145706
+ }
145707
+ }
145708
+ return {
145709
+ content: [{
145710
+ type: "text",
145711
+ text: JSON.stringify({ job, yourAddress: me, yourActions: actions, spec, delivery })
145712
+ }]
145713
+ };
145714
+ }
145715
+ const key = role === "buyer" ? "buyer" : "seller";
145716
+ const res = await fetch(`${API_BASE}/jobs?${key}=${encodeURIComponent(me)}&limit=100`);
145717
+ if (!res.ok) throw new Error(`Job index lookup failed (${res.status})`);
145718
+ const data = await res.json();
145719
+ const jobs = data.jobs ?? [];
145720
+ const open = jobs.filter((j) => !["released", "rejected", "refunded"].includes(j.state));
145721
+ return {
145722
+ content: [{
145723
+ type: "text",
145724
+ text: JSON.stringify({ role: key, address: me, total: jobs.length, open: open.length, jobs })
145725
+ }]
145726
+ };
145727
+ } catch (err) {
145728
+ return errorResult(err);
145729
+ }
145730
+ }
145731
+ );
145732
+ server.tool(
145733
+ "t2000_job_deliver",
145734
+ "Post your DELIVERY on a funded job you're selling (seller side, before the deadline). The delivery content is stored content-addressed and its sha256 is pinned to the Job object on-chain \u2014 the buyer verifies what they read is exactly what you delivered. Opens the buyer's review window. Sponsored (no gas). Mirrors `t2 job deliver`.",
145735
+ {
145736
+ jobId: external_exports.string().describe("The Job object id (0x\u2026) \u2014 see t2000_jobs (role: seller)"),
145737
+ delivery: external_exports.string().describe("The delivery content itself (e.g. the report markdown). Stored + hash-pinned on-chain.")
145738
+ },
145739
+ async ({ jobId, delivery }) => {
145740
+ try {
145741
+ const deliveryHash = `0x${await putJobSpec(API_BASE, delivery)}`;
145742
+ const { digest } = await mutex.run(
145743
+ () => sponsoredJobVerb(agent, "deliver", { jobId: validateAddress(jobId), deliveryHash })
145744
+ );
145745
+ return {
145746
+ content: [{
145747
+ type: "text",
145748
+ text: JSON.stringify({
145749
+ ok: true,
145750
+ jobId,
145751
+ deliveryHash,
145752
+ digest,
145753
+ next: "The buyer's review window is open. If they neither accept nor reject before it closes, anyone (including you) can settle with t2000_job_settle (release)."
145754
+ })
145755
+ }]
145756
+ };
145757
+ } catch (err) {
145758
+ return errorResult(err);
145759
+ }
145760
+ }
145761
+ );
145762
+ server.tool(
145763
+ "t2000_job_settle",
145764
+ `Settle an escrow job \u2014 MOVES THE ESCROWED FUNDS:
145765
+ - release: accept the delivery \u2192 funds to the seller (buyer; or ANYONE once the review window lapses \u2014 the anti-ghosting crank)
145766
+ - reject: within the review window \u2192 funds split per the terms agreed at create (buyer only)
145767
+ - refund: no delivery by the deadline \u2192 funds back to the buyer (anyone may crank)
145768
+ Check t2000_jobs first \u2014 it tells you which of these THIS wallet can run right now. Sponsored (no gas). Mirrors \`t2 job release|reject|refund\`.`,
145769
+ {
145770
+ jobId: external_exports.string().describe("The Job object id (0x\u2026)"),
145771
+ action: external_exports.enum(["release", "reject", "refund"]).describe("Which settlement to run")
145772
+ },
145773
+ async ({ jobId, action }) => {
145774
+ try {
145775
+ const { digest } = await mutex.run(
145776
+ () => sponsoredJobVerb(agent, action, { jobId: validateAddress(jobId) })
145777
+ );
145778
+ return {
145779
+ content: [{
145780
+ type: "text",
145781
+ text: JSON.stringify({
145782
+ ok: true,
145783
+ jobId,
145784
+ action,
145785
+ digest,
145786
+ ...action === "release" ? { next: "Rate the work with t2000_job_review \u2014 it builds the seller's on-chain-backed reputation." } : {}
145787
+ })
145788
+ }]
145789
+ };
145790
+ } catch (err) {
145791
+ return errorResult(err);
145792
+ }
145793
+ }
145794
+ );
145795
+ server.tool(
145796
+ "t2000_job_review",
145797
+ "Rate a RELEASED job you paid for, 1\u20135 stars \u2014 receipt-bound to the Job object, shown on the seller's public profile (agents.t2000.ai). Re-run to edit. Free \u2014 one signed message, no funds spent. Mirrors `t2 job review`.",
145798
+ {
145799
+ jobId: external_exports.string().describe("The Job object id (0x\u2026) of a released job this wallet funded"),
145800
+ stars: external_exports.number().int().min(1).max(5).describe("1 (poor) to 5 (excellent)"),
145801
+ text: external_exports.string().max(400).optional().describe("Optional short review (max 400 chars)")
145802
+ },
145803
+ async ({ jobId, stars, text }) => {
145804
+ try {
145805
+ const payload = {
145806
+ jobId: validateAddress(jobId),
145807
+ stars,
145808
+ text: text?.trim() || null
145809
+ };
145810
+ const response = await signedMutation({
145811
+ agent,
145812
+ kind: "job-review",
145813
+ url: `${API_BASE}/job/review`,
145814
+ payload,
145815
+ body: (nonce2, signature2, p) => ({ address: agent.address(), nonce: nonce2, signature: signature2, payload: p })
145816
+ });
145817
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true, ...response }) }] };
145818
+ } catch (err) {
145819
+ return errorResult(err);
145820
+ }
145821
+ }
145822
+ );
145823
+ }
145824
+ init_zod();
145245
145825
  function registerReadTools(server, agent) {
145246
145826
  server.tool(
145247
145827
  "t2000_balance",
@@ -145364,23 +145944,6 @@ No address \u2192 the registered-agent list (filter with category/limit). With a
145364
145944
  );
145365
145945
  }
145366
145946
  init_zod();
145367
- var TxMutex = class {
145368
- queue = Promise.resolve();
145369
- async run(fn) {
145370
- let release;
145371
- const next = new Promise((r) => {
145372
- release = r;
145373
- });
145374
- const prev = this.queue;
145375
- this.queue = next;
145376
- await prev;
145377
- try {
145378
- return await fn();
145379
- } finally {
145380
- release();
145381
- }
145382
- }
145383
- };
145384
145947
  function extractImageUrls(data) {
145385
145948
  const urls = [];
145386
145949
  const urlPattern = /^https?:\/\/.+\.(png|jpg|jpeg|webp|gif)/i;
@@ -145781,8 +146344,10 @@ Through this wallet you can reach essentially any major external API, billed to
145781
146344
 
145782
146345
  CRITICAL: When the user asks to use any external or paid API, names a provider (e.g. "via fal.ai", "with ElevenLabs"), or requests a capability one of the services above provides, DO NOT say you cannot reach that service, that it isn't on an allowlist, or that there's no connector \u2014 and do NOT fall back to writing a script for the user to run. You CAN do it directly through this wallet. Use t2000_services to discover the endpoint and request shape, then t2000_pay to execute, then show the user the result (display image/audio URLs returned in the response).
145783
146346
 
145784
- Spending is the user's own USDC and every t2000_pay call is bounded by maxPrice. For larger or multi-step spends, state the estimated cost first and proceed once the user is happy. Use t2000_balance to check funds. The v4 wallet is payments-only \u2014 there is no savings / lending surface.`;
145785
- var PKG_VERSION = "9.12.0";
146347
+ The wallet also trades on the t2 AGENT ECONOMY (agents.t2000.ai). It can HIRE other agents: browse structured fixed-price offerings with t2000_offerings, fund an on-chain USDC escrow job with t2000_job_create, track it with t2000_jobs, settle with t2000_job_settle, and rate the seller with t2000_job_review (escrow protects both sides \u2014 no delivery means an automatic refund path). It can EARN too: list what THIS agent sells with t2000_offering_create (no server or endpoint needed), watch incoming jobs with t2000_jobs (role: seller), and deliver with t2000_job_deliver \u2014 the escrow pays this wallet on release.
146348
+
146349
+ Spending is the user's own USDC and every t2000_pay call is bounded by maxPrice; t2000_job_create locks the offering price in escrow. For larger or multi-step spends, state the estimated cost first and proceed once the user is happy. Use t2000_balance to check funds. The v4 wallet is payments-only \u2014 there is no savings / lending surface.`;
146350
+ var PKG_VERSION = "9.13.0";
145786
146351
  console.log = (...args) => console.error("[log]", ...args);
145787
146352
  console.warn = (...args) => console.error("[warn]", ...args);
145788
146353
  async function startMcpServer(opts) {
@@ -145793,6 +146358,7 @@ async function startMcpServer(opts) {
145793
146358
  );
145794
146359
  registerReadTools(server, agent);
145795
146360
  registerWriteTools(server, agent);
146361
+ registerCommerceTools(server, agent);
145796
146362
  registerLimitTool(server);
145797
146363
  registerChatTools(server);
145798
146364
  registerSkillPrompts(server);
@@ -145867,4 +146433,4 @@ mime-types/index.js:
145867
146433
  @scure/bip39/index.js:
145868
146434
  (*! scure-bip39 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) *)
145869
146435
  */
145870
- //# sourceMappingURL=dist-PAYZWJCN.js.map
146436
+ //# sourceMappingURL=dist-WU4EKM5J.js.map