@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.
- package/dist/{chunk-UGQY67E3.js → chunk-SXB4BX4Y.js} +213 -1
- package/dist/chunk-SXB4BX4Y.js.map +1 -0
- package/dist/{dist-VTNYUSEM.js → dist-7NJYMC53.js} +8 -3
- package/dist/{dist-VTNYUSEM.js.map → dist-7NJYMC53.js.map} +1 -1
- package/dist/{dist-WHAWAYBP.js → dist-DHOTKMC2.js} +26 -2
- package/dist/index.js +336 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-UGQY67E3.js.map +0 -1
- /package/dist/{dist-WHAWAYBP.js.map → dist-DHOTKMC2.js.map} +0 -0
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|