@t2000/cli 10.29.1 → 10.30.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 +183 -58
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -30548,8 +30548,9 @@ init_coinSelection();
|
|
|
30548
30548
|
init_errors();
|
|
30549
30549
|
init_token_registry();
|
|
30550
30550
|
init_coinSelection();
|
|
30551
|
-
var
|
|
30552
|
-
var
|
|
30551
|
+
var MAINNET_A2A_ESCROW_LATEST_PACKAGE_ID = "0x83f02c59575b7daa4576eeccc22542f8a5679520e4edd02feeeee2daae16b819";
|
|
30552
|
+
var MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID = MAINNET_A2A_ESCROW_LATEST_PACKAGE_ID;
|
|
30553
|
+
var A2A_ESCROW_LATEST_PACKAGE_ID = process.env.A2A_ESCROW_OPENING_PACKAGE_ID ?? process.env.A2A_ESCROW_PACKAGE_ID ?? MAINNET_A2A_ESCROW_LATEST_PACKAGE_ID;
|
|
30553
30554
|
var MAINNET_A2A_ESCROW_PACKAGE_ID = "0x358a819c1c016e2cc84ef5fbea81cba90c31f7f8a62bf45cb5e5276acf198bdd";
|
|
30554
30555
|
var A2A_ESCROW_PACKAGE_ID = process.env.A2A_ESCROW_PACKAGE_ID ?? MAINNET_A2A_ESCROW_PACKAGE_ID;
|
|
30555
30556
|
var A2A_ESCROW_FEE_CONFIG_ID = process.env.A2A_ESCROW_FEE_CONFIG_ID ?? "0xddaa25570950b7484dbf20797ebcf75707be9c87cd67bef2a06ed2e81d2c494b";
|
|
@@ -30558,6 +30559,7 @@ var A2A_ESCROW_FEE_CONFIG_VERSION = Number(
|
|
|
30558
30559
|
);
|
|
30559
30560
|
var MODULE2 = "escrow";
|
|
30560
30561
|
var MAX_JOB_USDC = 50;
|
|
30562
|
+
var MIN_JOB_USDC = 0.05;
|
|
30561
30563
|
var MAX_REVIEW_WINDOW_MS = 2592e6;
|
|
30562
30564
|
var MAX_DELIVER_HORIZON_MS = 31536e6;
|
|
30563
30565
|
var JOB_STATES = [
|
|
@@ -30593,6 +30595,12 @@ function preflightCreateJob(terms) {
|
|
|
30593
30595
|
if (!Number.isFinite(terms.amountUsdc) || terms.amountUsdc <= 0) {
|
|
30594
30596
|
return preflightFail("INVALID_AMOUNT", `Amount must be positive. Got ${terms.amountUsdc}.`);
|
|
30595
30597
|
}
|
|
30598
|
+
if (terms.amountUsdc < MIN_JOB_USDC) {
|
|
30599
|
+
return preflightFail(
|
|
30600
|
+
"INVALID_AMOUNT",
|
|
30601
|
+
`Escrow jobs start at ${MIN_JOB_USDC} USDC (contract-enforced minimum). Got ${terms.amountUsdc}.`
|
|
30602
|
+
);
|
|
30603
|
+
}
|
|
30596
30604
|
if (terms.amountUsdc > MAX_JOB_USDC) {
|
|
30597
30605
|
return preflightFail(
|
|
30598
30606
|
"INVALID_AMOUNT",
|
|
@@ -30946,56 +30954,57 @@ init_preflight();
|
|
|
30946
30954
|
var CANONICAL_API_ORIGIN = "https://api.t2000.ai";
|
|
30947
30955
|
var ALLOW_UNTRUSTED_FLAG = "--allow-untrusted-api";
|
|
30948
30956
|
var ACTION_TARGETS = {
|
|
30949
|
-
//
|
|
30957
|
+
// Escrow verbs — emitted at the original id (pre-S.981 SDK) or the latest
|
|
30958
|
+
// id (S.981+ SDK, the version-gated path).
|
|
30950
30959
|
create: {
|
|
30951
|
-
|
|
30960
|
+
pkgs: [MAINNET_A2A_ESCROW_PACKAGE_ID, MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID],
|
|
30952
30961
|
module: "escrow",
|
|
30953
30962
|
functions: ["create"]
|
|
30954
30963
|
},
|
|
30955
30964
|
deliver: {
|
|
30956
|
-
|
|
30965
|
+
pkgs: [MAINNET_A2A_ESCROW_PACKAGE_ID, MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID],
|
|
30957
30966
|
module: "escrow",
|
|
30958
30967
|
functions: ["deliver"]
|
|
30959
30968
|
},
|
|
30960
30969
|
release: {
|
|
30961
|
-
|
|
30970
|
+
pkgs: [MAINNET_A2A_ESCROW_PACKAGE_ID, MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID],
|
|
30962
30971
|
module: "escrow",
|
|
30963
30972
|
functions: ["release"]
|
|
30964
30973
|
},
|
|
30965
30974
|
reject: {
|
|
30966
|
-
|
|
30975
|
+
pkgs: [MAINNET_A2A_ESCROW_PACKAGE_ID, MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID],
|
|
30967
30976
|
module: "escrow",
|
|
30968
30977
|
functions: ["reject"]
|
|
30969
30978
|
},
|
|
30970
30979
|
refund: {
|
|
30971
|
-
|
|
30980
|
+
pkgs: [MAINNET_A2A_ESCROW_PACKAGE_ID, MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID],
|
|
30972
30981
|
module: "escrow",
|
|
30973
30982
|
functions: ["refund"]
|
|
30974
30983
|
},
|
|
30975
30984
|
// v3 opening package — note the `escrow` module.
|
|
30976
30985
|
decline: {
|
|
30977
|
-
|
|
30986
|
+
pkgs: [MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID],
|
|
30978
30987
|
module: "escrow",
|
|
30979
30988
|
functions: ["decline"]
|
|
30980
30989
|
},
|
|
30981
30990
|
// Open board.
|
|
30982
30991
|
"open-create": {
|
|
30983
|
-
|
|
30992
|
+
pkgs: [MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID],
|
|
30984
30993
|
module: "opening",
|
|
30985
30994
|
functions: ["create_open"]
|
|
30986
30995
|
},
|
|
30987
30996
|
"open-claim": {
|
|
30988
|
-
|
|
30997
|
+
pkgs: [MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID],
|
|
30989
30998
|
module: "opening",
|
|
30990
30999
|
functions: ["claim"]
|
|
30991
31000
|
},
|
|
30992
31001
|
"open-cancel": {
|
|
30993
|
-
|
|
31002
|
+
pkgs: [MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID],
|
|
30994
31003
|
module: "opening",
|
|
30995
31004
|
functions: ["cancel_open"]
|
|
30996
31005
|
},
|
|
30997
31006
|
"open-refund": {
|
|
30998
|
-
|
|
31007
|
+
pkgs: [MAINNET_A2A_ESCROW_OPENING_PACKAGE_ID],
|
|
30999
31008
|
module: "opening",
|
|
31000
31009
|
functions: ["refund_unclaimed"]
|
|
31001
31010
|
}
|
|
@@ -31081,10 +31090,10 @@ function assertTxMatchesIntent(txBytes, intent, opts = {}) {
|
|
|
31081
31090
|
"Refusing to sign: the prepared transaction contains no Move calls."
|
|
31082
31091
|
);
|
|
31083
31092
|
}
|
|
31084
|
-
const
|
|
31093
|
+
const expectedPkgs = expected.pkgs.map((p) => normalizeSuiAddress(p));
|
|
31085
31094
|
let foundIntent = false;
|
|
31086
31095
|
for (const call of calls) {
|
|
31087
|
-
if (normalizeSuiAddress(call.pkg)
|
|
31096
|
+
if (expectedPkgs.includes(normalizeSuiAddress(call.pkg))) {
|
|
31088
31097
|
if (call.module !== expected.module) {
|
|
31089
31098
|
throw new IntentMismatchError(
|
|
31090
31099
|
`Refusing to sign: "${intent.action}" should call ${expected.module}, but the transaction calls ${call.module}.`
|
|
@@ -31102,7 +31111,7 @@ function assertTxMatchesIntent(txBytes, intent, opts = {}) {
|
|
|
31102
31111
|
continue;
|
|
31103
31112
|
}
|
|
31104
31113
|
throw new IntentMismatchError(
|
|
31105
|
-
`Refusing to sign: "${intent.action}" targets package ${expected.
|
|
31114
|
+
`Refusing to sign: "${intent.action}" targets package ${expected.pkgs.join(" | ")}, but the transaction calls ${call.pkg}::${call.module}::${call.fn}.`
|
|
31106
31115
|
);
|
|
31107
31116
|
}
|
|
31108
31117
|
if (!foundIntent) {
|
|
@@ -34359,7 +34368,7 @@ var import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
|
34359
34368
|
function registerPay(program3) {
|
|
34360
34369
|
program3.command("pay <url>").description("Pay an x402 Service (USDC on Sui) \u2014 the seller's own endpoint").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--method <method>", "HTTP method (GET, POST, PUT)", "GET").option("--data <json>", "Request body for POST/PUT (auto-promotes --method to POST)").option("--header <key=value>", "Additional HTTP header (repeatable)", collectHeaders, {}).option("--max-price <amount>", "Max USDC price to auto-approve", "1.00").option(
|
|
34361
34370
|
"--estimate",
|
|
34362
|
-
|
|
34371
|
+
'Preview the price + service info (no signing, no payment). Exits 0 ONLY if the service answers a payable 402 x402 challenge; any other response (including 2xx "no payment required") exits 1.'
|
|
34363
34372
|
).option("--force", "Override spending limits for this call (see `t2 limit`)").addHelpText(
|
|
34364
34373
|
"after",
|
|
34365
34374
|
`
|
|
@@ -34430,6 +34439,23 @@ Examples:
|
|
|
34430
34439
|
}
|
|
34431
34440
|
});
|
|
34432
34441
|
}
|
|
34442
|
+
var ESTIMATE_PREVIEW_MAX = 512;
|
|
34443
|
+
function truncatePreview(body2, max = ESTIMATE_PREVIEW_MAX) {
|
|
34444
|
+
if (body2.length <= max) {
|
|
34445
|
+
return body2;
|
|
34446
|
+
}
|
|
34447
|
+
return `${body2.slice(0, max)}\u2026 (truncated, ${body2.length} total bytes)`;
|
|
34448
|
+
}
|
|
34449
|
+
var EstimateFailure = class extends Error {
|
|
34450
|
+
constructor(message, payload) {
|
|
34451
|
+
super(message);
|
|
34452
|
+
this.payload = payload;
|
|
34453
|
+
this.name = "EstimateFailure";
|
|
34454
|
+
}
|
|
34455
|
+
toJSON() {
|
|
34456
|
+
return this.payload;
|
|
34457
|
+
}
|
|
34458
|
+
};
|
|
34433
34459
|
async function runEstimate(url, opts) {
|
|
34434
34460
|
const method = opts.data && opts.method === "GET" ? "POST" : opts.method;
|
|
34435
34461
|
const canHaveBody = method !== "GET" && method !== "HEAD";
|
|
@@ -34452,28 +34478,33 @@ async function runEstimate(url, opts) {
|
|
|
34452
34478
|
});
|
|
34453
34479
|
if (response.status !== 402) {
|
|
34454
34480
|
const body2 = await response.text().catch(() => "");
|
|
34455
|
-
|
|
34456
|
-
|
|
34481
|
+
const preview = truncatePreview(body2);
|
|
34482
|
+
const open = response.status >= 200 && response.status < 300;
|
|
34483
|
+
const note = open ? `Endpoint responded ${response.status} without a 402 challenge \u2014 no payment required.` : `Endpoint responded ${response.status} (not a 402 payment challenge).`;
|
|
34484
|
+
if (!isJsonMode()) {
|
|
34485
|
+
if (open) {
|
|
34486
|
+
printInfo(`No payment required (status ${response.status}).`);
|
|
34487
|
+
} else {
|
|
34488
|
+
printInfo(`Status ${response.status} \u2014 not a 402 payment challenge.`);
|
|
34489
|
+
}
|
|
34490
|
+
if (preview) {
|
|
34491
|
+
printBlank();
|
|
34492
|
+
console.log(preview);
|
|
34493
|
+
printBlank();
|
|
34494
|
+
}
|
|
34495
|
+
}
|
|
34496
|
+
throw new EstimateFailure(
|
|
34497
|
+
`${note} --estimate exits 0 only for a payable 402 x402 challenge.`,
|
|
34498
|
+
{
|
|
34457
34499
|
url,
|
|
34458
34500
|
method,
|
|
34459
34501
|
status: response.status,
|
|
34502
|
+
ok: false,
|
|
34460
34503
|
estimate: null,
|
|
34461
|
-
note
|
|
34462
|
-
|
|
34463
|
-
}
|
|
34464
|
-
|
|
34465
|
-
}
|
|
34466
|
-
if (response.status >= 200 && response.status < 300) {
|
|
34467
|
-
printSuccess(`No payment required (status ${response.status}).`);
|
|
34468
|
-
} else {
|
|
34469
|
-
printInfo(`Status ${response.status} \u2014 not a 402 payment challenge.`);
|
|
34470
|
-
}
|
|
34471
|
-
if (body2) {
|
|
34472
|
-
printBlank();
|
|
34473
|
-
console.log(body2);
|
|
34474
|
-
printBlank();
|
|
34475
|
-
}
|
|
34476
|
-
return;
|
|
34504
|
+
note,
|
|
34505
|
+
...preview ? { bodyPreview: preview } : {}
|
|
34506
|
+
}
|
|
34507
|
+
);
|
|
34477
34508
|
}
|
|
34478
34509
|
let accepts = [];
|
|
34479
34510
|
try {
|
|
@@ -36378,24 +36409,76 @@ async function fetchSellerJobs(base, seller) {
|
|
|
36378
36409
|
return json.jobs ?? [];
|
|
36379
36410
|
}
|
|
36380
36411
|
var TERMINAL_STATES = /* @__PURE__ */ new Set(["released", "rejected", "refunded"]);
|
|
36381
|
-
function
|
|
36382
|
-
|
|
36383
|
-
|
|
36412
|
+
function reviewClosesMs(job) {
|
|
36413
|
+
const anchor = job.deliveredAtMs ?? job.updatedAtMs;
|
|
36414
|
+
if (!(typeof anchor === "number" && anchor > 0 && typeof job.reviewWindowMs === "number" && job.reviewWindowMs > 0)) {
|
|
36415
|
+
return null;
|
|
36416
|
+
}
|
|
36417
|
+
return anchor + job.reviewWindowMs;
|
|
36418
|
+
}
|
|
36419
|
+
function bucketSellerJob(job, nowMs) {
|
|
36420
|
+
if (TERMINAL_STATES.has(job.state)) {
|
|
36421
|
+
return "terminal";
|
|
36384
36422
|
}
|
|
36385
|
-
if (job.state === "
|
|
36386
|
-
return
|
|
36423
|
+
if (job.state === "funded") {
|
|
36424
|
+
return nowMs <= job.deliverByMs ? "needsYou" : "fundedLate";
|
|
36425
|
+
}
|
|
36426
|
+
const closes = reviewClosesMs(job);
|
|
36427
|
+
return closes !== null && nowMs > closes ? "releasable" : "awaitingBuyer";
|
|
36428
|
+
}
|
|
36429
|
+
function summarizeSellerInbox(jobs, nowMs) {
|
|
36430
|
+
const buckets = {
|
|
36431
|
+
counts: { total: jobs.length, needsYou: 0, fundedLate: 0, awaitingBuyer: 0, releasable: 0, terminal: 0 },
|
|
36432
|
+
needsYou: [],
|
|
36433
|
+
fundedLate: [],
|
|
36434
|
+
awaitingBuyer: [],
|
|
36435
|
+
releasable: [],
|
|
36436
|
+
terminal: []
|
|
36437
|
+
};
|
|
36438
|
+
for (const job of jobs) {
|
|
36439
|
+
const bucket = bucketSellerJob(job, nowMs);
|
|
36440
|
+
buckets[bucket].push(job);
|
|
36441
|
+
buckets.counts[bucket] += 1;
|
|
36442
|
+
}
|
|
36443
|
+
return buckets;
|
|
36444
|
+
}
|
|
36445
|
+
function inboxHint(job, bucket) {
|
|
36446
|
+
switch (bucket) {
|
|
36447
|
+
case "needsYou":
|
|
36448
|
+
return `t2 job spec ${job.jobId} \u2192 do the work \u2192 t2 job deliver ${job.jobId} <file>`;
|
|
36449
|
+
case "releasable":
|
|
36450
|
+
return `releasable now \u2014 t2 job release ${job.jobId}`;
|
|
36451
|
+
case "awaitingBuyer": {
|
|
36452
|
+
const closes = reviewClosesMs(job);
|
|
36453
|
+
return closes !== null ? `waiting on the buyer's review \u2014 release becomes permissionless after ${new Date(closes).toISOString()}` : `waiting on the buyer's review \u2014 anyone can \`t2 job release\` once it lapses`;
|
|
36454
|
+
}
|
|
36455
|
+
case "fundedLate":
|
|
36456
|
+
return `deliver deadline passed \u2014 the chain rejects late delivers; the buyer (or anyone) may t2 job refund ${job.jobId}`;
|
|
36457
|
+
default:
|
|
36458
|
+
return "";
|
|
36387
36459
|
}
|
|
36388
|
-
return "";
|
|
36389
36460
|
}
|
|
36390
|
-
function printInboxRow(job) {
|
|
36461
|
+
function printInboxRow(job, bucket) {
|
|
36391
36462
|
const deadline = job.state === "funded" ? ` \xB7 deliver by ${new Date(job.deliverByMs).toISOString()}` : "";
|
|
36392
36463
|
printLine(
|
|
36393
36464
|
` ${stateColor(job.state)} $${job.amountUsdc.toFixed(2)} USDC \xB7 from ${truncateAddress(job.buyer)}${deadline}`
|
|
36394
36465
|
);
|
|
36395
36466
|
printLine(` ${import_picocolors13.default.dim(job.jobId)}`);
|
|
36396
|
-
const hint = inboxHint(job);
|
|
36467
|
+
const hint = inboxHint(job, bucket);
|
|
36397
36468
|
if (hint) printLine(` ${import_picocolors13.default.dim("\u2192")} ${hint}`);
|
|
36398
36469
|
}
|
|
36470
|
+
function deliverPreflightError(state, deliverByMs, nowMs) {
|
|
36471
|
+
if (state === "delivered") {
|
|
36472
|
+
return "This job is already delivered. The delivery hash is permanent and cannot be replaced \u2014 wait for the buyer's review, or once the window closes anyone may run `t2 job release <jobId>`. Fixes travel via buyer reject (inside the window) or out-of-band, never a second deliver.";
|
|
36473
|
+
}
|
|
36474
|
+
if (state === "released" || state === "rejected" || state === "refunded") {
|
|
36475
|
+
return `This job is ${state} \u2014 nothing can be delivered.`;
|
|
36476
|
+
}
|
|
36477
|
+
if (state === "funded" && nowMs > deliverByMs) {
|
|
36478
|
+
return `The deliver deadline (${new Date(deliverByMs).toISOString()}) has passed \u2014 the chain rejects late delivers, so nothing was uploaded or signed. The refund path is open: anyone may run \`t2 job refund <jobId>\`.`;
|
|
36479
|
+
}
|
|
36480
|
+
return null;
|
|
36481
|
+
}
|
|
36399
36482
|
async function sponsoredJobVerb(opts) {
|
|
36400
36483
|
const agent = await withAgent({ keyPath: opts.keyPath });
|
|
36401
36484
|
const address = agent.address();
|
|
@@ -36644,9 +36727,16 @@ no fund step; unclaimed openings refund fee-free):
|
|
|
36644
36727
|
handleError(error);
|
|
36645
36728
|
}
|
|
36646
36729
|
});
|
|
36647
|
-
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
|
|
36730
|
+
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 ONE SHOT: the sha256 pins on-chain permanently and cannot be replaced. Fix mistakes via buyer reject (inside the review window) or out-of-band \u2014 never a second deliver. Decline is only possible BEFORE delivery.").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_BASE7})`).action(async (jobId, proof, opts) => {
|
|
36648
36731
|
try {
|
|
36649
36732
|
const base = opts.api ?? DEFAULT_API_BASE7;
|
|
36733
|
+
const existing = await getJob(getSuiClient(), jobId).catch(() => null);
|
|
36734
|
+
if (existing) {
|
|
36735
|
+
const preflightError = deliverPreflightError(existing.state, existing.deliverByMs, Date.now());
|
|
36736
|
+
if (preflightError) {
|
|
36737
|
+
throw new Error(preflightError.replaceAll("<jobId>", jobId));
|
|
36738
|
+
}
|
|
36739
|
+
}
|
|
36650
36740
|
let deliveryHash;
|
|
36651
36741
|
let uploaded = false;
|
|
36652
36742
|
if (opts.hashOnly) {
|
|
@@ -36658,6 +36748,9 @@ no fund step; unclaimed openings refund fee-free):
|
|
|
36658
36748
|
} else {
|
|
36659
36749
|
({ hash: deliveryHash, uploaded } = await resolveSpecUpload(base, proof));
|
|
36660
36750
|
}
|
|
36751
|
+
if (!isJsonMode()) {
|
|
36752
|
+
printInfo("Delivery pins once. The buyer review window starts after this succeeds; you cannot replace the hash.");
|
|
36753
|
+
}
|
|
36661
36754
|
const { digest } = await sponsoredJobVerb({
|
|
36662
36755
|
base,
|
|
36663
36756
|
keyPath: opts.key,
|
|
@@ -36665,11 +36758,12 @@ no fund step; unclaimed openings refund fee-free):
|
|
|
36665
36758
|
params: { jobId, deliveryHash }
|
|
36666
36759
|
});
|
|
36667
36760
|
if (isJsonMode()) {
|
|
36668
|
-
printJson({ jobId, deliveryHash, uploaded, digest });
|
|
36761
|
+
printJson({ jobId, deliveryHash, uploaded, digest, onceOnly: true });
|
|
36669
36762
|
return;
|
|
36670
36763
|
}
|
|
36671
36764
|
printBlank();
|
|
36672
36765
|
printSuccess("Delivery posted \u2014 the buyer's review window is now open.");
|
|
36766
|
+
printInfo("This delivery cannot be amended \u2014 further `t2 job deliver` calls on this job will fail.");
|
|
36673
36767
|
printKeyValue("Delivery hash", deliveryHash);
|
|
36674
36768
|
if (digest) printKeyValue("Tx", digest);
|
|
36675
36769
|
if (uploaded) {
|
|
@@ -36813,23 +36907,45 @@ no fund step; unclaimed openings refund fee-free):
|
|
|
36813
36907
|
const base = opts.api ?? DEFAULT_API_BASE7;
|
|
36814
36908
|
const seen = /* @__PURE__ */ new Map();
|
|
36815
36909
|
const jobs = await fetchSellerJobs(base, me);
|
|
36910
|
+
const inbox = summarizeSellerInbox(jobs, Date.now());
|
|
36816
36911
|
if (isJsonMode()) {
|
|
36817
|
-
printJson({
|
|
36912
|
+
printJson({
|
|
36913
|
+
seller: me,
|
|
36914
|
+
counts: inbox.counts,
|
|
36915
|
+
needsYou: inbox.needsYou,
|
|
36916
|
+
fundedLate: inbox.fundedLate,
|
|
36917
|
+
awaitingBuyer: inbox.awaitingBuyer,
|
|
36918
|
+
releasable: inbox.releasable,
|
|
36919
|
+
terminal: inbox.terminal,
|
|
36920
|
+
jobs
|
|
36921
|
+
});
|
|
36818
36922
|
return;
|
|
36819
36923
|
}
|
|
36820
|
-
const open = jobs.filter((j) => !TERMINAL_STATES.has(j.state));
|
|
36821
36924
|
printBlank();
|
|
36822
|
-
printInfo(
|
|
36925
|
+
printInfo(
|
|
36926
|
+
`Provider inbox for ${truncateAddress(me)} \u2014 ${jobs.length} job(s) \xB7 ${inbox.counts.needsYou} need you \xB7 ${inbox.counts.awaitingBuyer} awaiting buyer \xB7 ${inbox.counts.releasable} releasable now`
|
|
36927
|
+
);
|
|
36928
|
+
if (inbox.counts.fundedLate > 0) {
|
|
36929
|
+
printLine(import_picocolors13.default.dim(` ${inbox.counts.fundedLate} past deliver deadline (not deliverable \u2014 refund path is open to others)`));
|
|
36930
|
+
}
|
|
36823
36931
|
printBlank();
|
|
36824
|
-
for (const
|
|
36825
|
-
|
|
36826
|
-
|
|
36932
|
+
for (const [bucket, rows] of [
|
|
36933
|
+
["needsYou", inbox.needsYou],
|
|
36934
|
+
["releasable", inbox.releasable],
|
|
36935
|
+
["awaitingBuyer", inbox.awaitingBuyer],
|
|
36936
|
+
["fundedLate", inbox.fundedLate]
|
|
36937
|
+
]) {
|
|
36938
|
+
for (const job of rows) {
|
|
36939
|
+
printInboxRow(job, bucket);
|
|
36940
|
+
printBlank();
|
|
36941
|
+
}
|
|
36827
36942
|
}
|
|
36828
|
-
|
|
36943
|
+
const openCount = jobs.length - inbox.counts.terminal;
|
|
36944
|
+
if (openCount === 0) {
|
|
36829
36945
|
printInfo("No open jobs. New hires appear here the moment the escrow funds.");
|
|
36830
36946
|
printBlank();
|
|
36831
36947
|
}
|
|
36832
|
-
for (const job of jobs) seen.set(job.jobId, job.
|
|
36948
|
+
for (const job of jobs) seen.set(job.jobId, bucketSellerJob(job, Date.now()));
|
|
36833
36949
|
if (opts.once) return;
|
|
36834
36950
|
for (; ; ) {
|
|
36835
36951
|
await new Promise((r) => setTimeout(r, intervalMs));
|
|
@@ -36839,17 +36955,21 @@ no fund step; unclaimed openings refund fee-free):
|
|
|
36839
36955
|
} catch {
|
|
36840
36956
|
continue;
|
|
36841
36957
|
}
|
|
36958
|
+
const nowMs = Date.now();
|
|
36842
36959
|
for (const job of latest) {
|
|
36960
|
+
const bucket = bucketSellerJob(job, nowMs);
|
|
36843
36961
|
const prev = seen.get(job.jobId);
|
|
36844
|
-
if (prev ===
|
|
36845
|
-
seen.set(job.jobId,
|
|
36962
|
+
if (prev === bucket) continue;
|
|
36963
|
+
seen.set(job.jobId, bucket);
|
|
36846
36964
|
printBlank();
|
|
36847
|
-
if (prev === void 0 &&
|
|
36965
|
+
if (prev === void 0 && bucket === "needsYou") {
|
|
36848
36966
|
printSuccess(`New job \u2014 $${job.amountUsdc.toFixed(2)} USDC escrowed for you.`);
|
|
36967
|
+
} else if (bucket === "releasable") {
|
|
36968
|
+
printSuccess(`Releasable now \u2014 t2 job release ${job.jobId}`);
|
|
36849
36969
|
} else {
|
|
36850
36970
|
printInfo(`Job ${truncateAddress(job.jobId)}: ${prev ?? "new"} \u2192 ${job.state}`);
|
|
36851
36971
|
}
|
|
36852
|
-
printInboxRow(job);
|
|
36972
|
+
printInboxRow(job, bucket);
|
|
36853
36973
|
printBlank();
|
|
36854
36974
|
}
|
|
36855
36975
|
}
|
|
@@ -37000,7 +37120,7 @@ Examples:
|
|
|
37000
37120
|
$ t2 service retire sui-market-report
|
|
37001
37121
|
`
|
|
37002
37122
|
);
|
|
37003
|
-
group.command("create").description("List a service under your Agent ID (re-run to update it)").requiredOption("--name <name>", "Service name (max 80 chars)").requiredOption("--price <usdc>", "Fixed price in USDC (0.
|
|
37123
|
+
group.command("create").description("List a service under your Agent ID (re-run to update it)").requiredOption("--name <name>", "Service name (max 80 chars)").requiredOption("--price <usdc>", "Fixed price in USDC (0.05\u201350)").requiredOption("--sla <duration>", "Delivery SLA \u2014 e.g. 30m, 24h, 7d").requiredOption("--description <text>", "What this service is (max 2000 chars)").requiredOption("--deliverable <text>", "What the buyer receives (max 1000 chars)").option("--slug <slug>", "Machine name (default: derived from --name)").requiredOption(
|
|
37004
37124
|
"--requirements <file-or-json-or-text>",
|
|
37005
37125
|
"The questions buyers answer at hire (REQUIRED \u2014 the API rejects listings that ask nothing): free text, or a JSON object of field names \u2192 hints (keys enforced non-empty at hire; file path ok)"
|
|
37006
37126
|
).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(
|
|
@@ -37013,6 +37133,11 @@ Examples:
|
|
|
37013
37133
|
if (!Number.isFinite(priceUsdc) || priceUsdc <= 0) {
|
|
37014
37134
|
throw new Error(`--price must be a positive number (got "${opts.price}").`);
|
|
37015
37135
|
}
|
|
37136
|
+
if (priceUsdc < MIN_JOB_USDC) {
|
|
37137
|
+
throw new Error(
|
|
37138
|
+
`--price must be at least ${MIN_JOB_USDC} USDC \u2014 escrow jobs start there (contract-enforced minimum).`
|
|
37139
|
+
);
|
|
37140
|
+
}
|
|
37016
37141
|
const slaMinutes = Math.round(parseDuration(opts.sla) / 6e4);
|
|
37017
37142
|
const reviewWindowMinutes = Math.round(parseDuration(opts.review) / 6e4);
|
|
37018
37143
|
const rejectSplitBps = Number.parseInt(opts.split, 10);
|