@t2000/cli 9.10.0 → 9.12.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.
package/dist/index.js
CHANGED
|
@@ -23370,7 +23370,7 @@ function registerMcpStart(parent) {
|
|
|
23370
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) => {
|
|
23371
23371
|
let mod;
|
|
23372
23372
|
try {
|
|
23373
|
-
mod = await import("./dist-
|
|
23373
|
+
mod = await import("./dist-PAYZWJCN.js");
|
|
23374
23374
|
} catch {
|
|
23375
23375
|
console.error("MCP server not installed. Run:\n npm install -g @t2000/mcp");
|
|
23376
23376
|
process.exit(1);
|
|
@@ -24624,6 +24624,29 @@ function printJob(job, me) {
|
|
|
24624
24624
|
if (job.deliveryHash) printKeyValue("Delivery hash", job.deliveryHash);
|
|
24625
24625
|
printKeyValue("Reject split", `${job.rejectSplitBps / 100}% buyer / ${(1e4 - job.rejectSplitBps) / 100}% seller`);
|
|
24626
24626
|
}
|
|
24627
|
+
async function fetchSellerJobs(base, seller) {
|
|
24628
|
+
const json = await fetchJson3(`${base}/jobs?seller=${encodeURIComponent(seller)}&limit=100`);
|
|
24629
|
+
return json.jobs ?? [];
|
|
24630
|
+
}
|
|
24631
|
+
var TERMINAL_STATES = /* @__PURE__ */ new Set(["released", "rejected", "refunded"]);
|
|
24632
|
+
function inboxHint(job) {
|
|
24633
|
+
if (job.state === "funded") {
|
|
24634
|
+
return `t2 job spec ${truncateAddress(job.jobId)} \u2192 do the work \u2192 t2 job deliver ${truncateAddress(job.jobId)} <file>`;
|
|
24635
|
+
}
|
|
24636
|
+
if (job.state === "delivered") {
|
|
24637
|
+
return `waiting on the buyer's review \u2014 anyone can \`t2 job release\` once it lapses`;
|
|
24638
|
+
}
|
|
24639
|
+
return "";
|
|
24640
|
+
}
|
|
24641
|
+
function printInboxRow(job) {
|
|
24642
|
+
const deadline = job.state === "funded" ? ` \xB7 deliver by ${new Date(job.deliverByMs).toISOString()}` : "";
|
|
24643
|
+
printLine(
|
|
24644
|
+
` ${stateColor(job.state)} $${job.amountUsdc.toFixed(2)} USDC \xB7 from ${truncateAddress(job.buyer)}${deadline}`
|
|
24645
|
+
);
|
|
24646
|
+
printLine(` ${import_picocolors15.default.dim(job.jobId)}`);
|
|
24647
|
+
const hint = inboxHint(job);
|
|
24648
|
+
if (hint) printLine(` ${import_picocolors15.default.dim("\u2192")} ${hint}`);
|
|
24649
|
+
}
|
|
24627
24650
|
async function sponsoredJobVerb(opts) {
|
|
24628
24651
|
const agent = await withAgent({ keyPath: opts.keyPath });
|
|
24629
24652
|
const address = agent.address();
|
|
@@ -24654,6 +24677,7 @@ Typical flow:
|
|
|
24654
24677
|
seller $ t2 job deliver 0xJOB report.pdf
|
|
24655
24678
|
buyer $ t2 job release 0xJOB (or: t2 job reject 0xJOB)
|
|
24656
24679
|
either $ t2 job watch 0xJOB
|
|
24680
|
+
seller $ t2 job watch --mine (the provider inbox \u2014 all your jobs)
|
|
24657
24681
|
|
|
24658
24682
|
Buying an OFFERING (t2 ACP) \u2014 price + terms come from the listing:
|
|
24659
24683
|
buyer $ t2 browse "market report"
|
|
@@ -24874,12 +24898,60 @@ Buying an OFFERING (t2 ACP) \u2014 price + terms come from the listing:
|
|
|
24874
24898
|
printBlank();
|
|
24875
24899
|
printSuccess(note);
|
|
24876
24900
|
if (digest) printKeyValue("Tx", digest);
|
|
24901
|
+
if (verb === "release") {
|
|
24902
|
+
printInfo(`Rate the work (builds the seller's on-chain-backed reputation): t2 job review ${truncateAddress(jobId)} --stars 5`);
|
|
24903
|
+
}
|
|
24877
24904
|
printBlank();
|
|
24878
24905
|
} catch (error) {
|
|
24879
24906
|
handleError(error);
|
|
24880
24907
|
}
|
|
24881
24908
|
});
|
|
24882
24909
|
}
|
|
24910
|
+
group.command("review").argument("<jobId>", "The Job object id (0x\u2026) of a RELEASED job you paid for").description("Rate a released job 1\u20135 stars \u2014 receipt-bound to the Job object (buyer)").requiredOption("--stars <1-5>", "Star rating, 1 (poor) to 5 (excellent)").option("--text <text>", "Optional short review (max 400 chars)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (jobId, opts) => {
|
|
24911
|
+
try {
|
|
24912
|
+
const stars = Number.parseInt(opts.stars, 10);
|
|
24913
|
+
if (!Number.isInteger(stars) || stars < 1 || stars > 5) {
|
|
24914
|
+
throw new Error(`--stars must be an integer 1\u20135 (got "${opts.stars}").`);
|
|
24915
|
+
}
|
|
24916
|
+
const base = opts.api ?? DEFAULT_API_BASE5;
|
|
24917
|
+
const agent = await withAgent({ keyPath: opts.key });
|
|
24918
|
+
const address = agent.address();
|
|
24919
|
+
const challenge = await fetchJson3(`${base}/agent/challenge`, {
|
|
24920
|
+
method: "POST",
|
|
24921
|
+
body: { address }
|
|
24922
|
+
});
|
|
24923
|
+
const nonce = challenge.nonce;
|
|
24924
|
+
if (!nonce) throw new Error("Failed to get a challenge nonce.");
|
|
24925
|
+
const payload = {
|
|
24926
|
+
jobId: validateAddress(jobId),
|
|
24927
|
+
stars,
|
|
24928
|
+
text: opts.text?.trim() || null
|
|
24929
|
+
};
|
|
24930
|
+
const payloadHash = createHash2("sha256").update(JSON.stringify(payload), "utf8").digest("hex");
|
|
24931
|
+
const message = new TextEncoder().encode(
|
|
24932
|
+
`t2000-job-review:${nonce}:${payloadHash}`
|
|
24933
|
+
);
|
|
24934
|
+
const { signature } = await agent.keypair.signPersonalMessage(message);
|
|
24935
|
+
const response = await fetchJson3(`${base}/job/review`, {
|
|
24936
|
+
method: "POST",
|
|
24937
|
+
body: { address, nonce, signature, payload }
|
|
24938
|
+
});
|
|
24939
|
+
if (isJsonMode()) {
|
|
24940
|
+
printJson(response);
|
|
24941
|
+
return;
|
|
24942
|
+
}
|
|
24943
|
+
printBlank();
|
|
24944
|
+
printSuccess(`Review saved \u2014 ${"\u2605".repeat(stars)}${"\u2606".repeat(5 - stars)} on job ${truncateAddress(payload.jobId)}.`);
|
|
24945
|
+
const review = response.review;
|
|
24946
|
+
if (review?.seller) {
|
|
24947
|
+
printKeyValue("Seller page", `https://agents.t2000.ai/${review.seller}`);
|
|
24948
|
+
}
|
|
24949
|
+
printInfo("Re-run with different --stars/--text to edit your review.");
|
|
24950
|
+
printBlank();
|
|
24951
|
+
} catch (error) {
|
|
24952
|
+
handleError(error);
|
|
24953
|
+
}
|
|
24954
|
+
});
|
|
24883
24955
|
group.command("spec").argument("<jobId>", "The Job object id (0x\u2026)").description("Fetch the buyer's job spec / requirements by the on-chain hash (seller)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (jobId, opts) => {
|
|
24884
24956
|
try {
|
|
24885
24957
|
const client = getSuiClient();
|
|
@@ -24904,12 +24976,62 @@ Buying an OFFERING (t2 ACP) \u2014 price + terms come from the listing:
|
|
|
24904
24976
|
handleError(error);
|
|
24905
24977
|
}
|
|
24906
24978
|
});
|
|
24907
|
-
group.command("watch").argument("
|
|
24979
|
+
group.command("watch").argument("[jobId]", "The Job object id (0x\u2026) \u2014 omit with --mine").description("Poll a job \u2014 or, with --mine, the provider inbox (every job selling to you)").option("--mine", "Watch ALL jobs where this wallet is the seller (the provider inbox)").option("--interval <seconds>", "Poll interval", "15").option("--once", "Print the current state and exit").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(async (jobId, opts) => {
|
|
24908
24980
|
try {
|
|
24909
24981
|
const agent = await withAgent({ keyPath: opts.key });
|
|
24910
24982
|
const me = agent.address();
|
|
24911
|
-
const client = getSuiClient();
|
|
24912
24983
|
const intervalMs = Math.max(5, Number.parseInt(opts.interval, 10) || 15) * 1e3;
|
|
24984
|
+
if (opts.mine) {
|
|
24985
|
+
const base = opts.api ?? DEFAULT_API_BASE5;
|
|
24986
|
+
const seen = /* @__PURE__ */ new Map();
|
|
24987
|
+
const jobs = await fetchSellerJobs(base, me);
|
|
24988
|
+
if (isJsonMode()) {
|
|
24989
|
+
printJson({ seller: me, jobs });
|
|
24990
|
+
return;
|
|
24991
|
+
}
|
|
24992
|
+
const open = jobs.filter((j) => !TERMINAL_STATES.has(j.state));
|
|
24993
|
+
printBlank();
|
|
24994
|
+
printInfo(`Provider inbox for ${truncateAddress(me)} \u2014 ${jobs.length} job(s), ${open.length} open.`);
|
|
24995
|
+
printBlank();
|
|
24996
|
+
for (const job of open) {
|
|
24997
|
+
printInboxRow(job);
|
|
24998
|
+
printBlank();
|
|
24999
|
+
}
|
|
25000
|
+
if (open.length === 0) {
|
|
25001
|
+
printInfo("No open jobs. New hires appear here the moment the escrow funds.");
|
|
25002
|
+
printBlank();
|
|
25003
|
+
}
|
|
25004
|
+
for (const job of jobs) seen.set(job.jobId, job.state);
|
|
25005
|
+
if (opts.once) return;
|
|
25006
|
+
for (; ; ) {
|
|
25007
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
25008
|
+
let latest;
|
|
25009
|
+
try {
|
|
25010
|
+
latest = await fetchSellerJobs(base, me);
|
|
25011
|
+
} catch {
|
|
25012
|
+
continue;
|
|
25013
|
+
}
|
|
25014
|
+
for (const job of latest) {
|
|
25015
|
+
const prev = seen.get(job.jobId);
|
|
25016
|
+
if (prev === job.state) continue;
|
|
25017
|
+
seen.set(job.jobId, job.state);
|
|
25018
|
+
printBlank();
|
|
25019
|
+
if (prev === void 0 && job.state === "funded") {
|
|
25020
|
+
printSuccess(`New job \u2014 $${job.amountUsdc.toFixed(2)} USDC escrowed for you.`);
|
|
25021
|
+
} else {
|
|
25022
|
+
printInfo(`Job ${truncateAddress(job.jobId)}: ${prev ?? "new"} \u2192 ${job.state}`);
|
|
25023
|
+
}
|
|
25024
|
+
printInboxRow(job);
|
|
25025
|
+
printBlank();
|
|
25026
|
+
}
|
|
25027
|
+
}
|
|
25028
|
+
}
|
|
25029
|
+
if (!jobId) {
|
|
25030
|
+
printError("Provide a job id \u2014 or use --mine for the provider inbox.");
|
|
25031
|
+
process.exitCode = 1;
|
|
25032
|
+
return;
|
|
25033
|
+
}
|
|
25034
|
+
const client = getSuiClient();
|
|
24913
25035
|
for (; ; ) {
|
|
24914
25036
|
const job = await getJob(client, jobId);
|
|
24915
25037
|
const actions = jobActionsFor(job, me);
|