@t2000/cli 10.30.2 → 10.30.3
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 +241 -27
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -30670,7 +30670,6 @@ function jobActionsFor(job, caller, nowMs = Date.now()) {
|
|
|
30670
30670
|
const actions = [];
|
|
30671
30671
|
if (job.state === "funded") {
|
|
30672
30672
|
if (isSeller && nowMs <= job.deliverByMs) actions.push("deliver");
|
|
30673
|
-
if (isBuyer) actions.push("release");
|
|
30674
30673
|
if (nowMs > job.deliverByMs) actions.push("refund");
|
|
30675
30674
|
} else if (job.state === "delivered") {
|
|
30676
30675
|
const windowClosesMs = (job.deliveredAtMs ?? 0) + job.reviewWindowMs;
|
|
@@ -36404,6 +36403,10 @@ function printJob(job, me) {
|
|
|
36404
36403
|
if (job.deliveryHash) printKeyValue("Delivery hash", job.deliveryHash);
|
|
36405
36404
|
printKeyValue("Reject split", `${job.rejectSplitBps / 100}% buyer / ${(1e4 - job.rejectSplitBps) / 100}% seller`);
|
|
36406
36405
|
}
|
|
36406
|
+
async function fetchBuyerJobs(base, buyer) {
|
|
36407
|
+
const json = await fetchJson4(`${base}/jobs?buyer=${encodeURIComponent(buyer)}&limit=100`);
|
|
36408
|
+
return json.jobs ?? [];
|
|
36409
|
+
}
|
|
36407
36410
|
async function fetchSellerJobs(base, seller) {
|
|
36408
36411
|
const json = await fetchJson4(`${base}/jobs?seller=${encodeURIComponent(seller)}&limit=100`);
|
|
36409
36412
|
return json.jobs ?? [];
|
|
@@ -36463,7 +36466,7 @@ function mergeIndexedJobFromChain(row, chain) {
|
|
|
36463
36466
|
updatedAtMs: chain.state === "delivered" && chain.deliveredAtMs != null ? chain.deliveredAtMs : row.updatedAtMs
|
|
36464
36467
|
};
|
|
36465
36468
|
}
|
|
36466
|
-
async function
|
|
36469
|
+
async function hydrateJobsFromChain(rows, getJobById, opts) {
|
|
36467
36470
|
const max = opts?.maxHydrate ?? HYDRATE_MAX;
|
|
36468
36471
|
const out = [...rows];
|
|
36469
36472
|
const targets = [];
|
|
@@ -36482,6 +36485,43 @@ async function hydrateSellerJobsFromChain(rows, getJobById, opts) {
|
|
|
36482
36485
|
}
|
|
36483
36486
|
return out;
|
|
36484
36487
|
}
|
|
36488
|
+
var hydrateSellerJobsFromChain = hydrateJobsFromChain;
|
|
36489
|
+
function bucketBuyerJob(job, nowMs) {
|
|
36490
|
+
if (TERMINAL_STATES.has(job.state)) {
|
|
36491
|
+
return "terminal";
|
|
36492
|
+
}
|
|
36493
|
+
if (job.state === "delivered") {
|
|
36494
|
+
return "needsYou";
|
|
36495
|
+
}
|
|
36496
|
+
return nowMs > job.deliverByMs ? "refundable" : "waiting";
|
|
36497
|
+
}
|
|
36498
|
+
function summarizeBuyerInbox(jobs, nowMs) {
|
|
36499
|
+
const buckets = {
|
|
36500
|
+
counts: { total: jobs.length, needsYou: 0, refundable: 0, waiting: 0, terminal: 0 },
|
|
36501
|
+
needsYou: [],
|
|
36502
|
+
refundable: [],
|
|
36503
|
+
waiting: [],
|
|
36504
|
+
terminal: []
|
|
36505
|
+
};
|
|
36506
|
+
for (const job of jobs) {
|
|
36507
|
+
const bucket = bucketBuyerJob(job, nowMs);
|
|
36508
|
+
buckets[bucket].push(job);
|
|
36509
|
+
buckets.counts[bucket] += 1;
|
|
36510
|
+
}
|
|
36511
|
+
return buckets;
|
|
36512
|
+
}
|
|
36513
|
+
function buyerInboxHint(job, bucket) {
|
|
36514
|
+
switch (bucket) {
|
|
36515
|
+
case "needsYou":
|
|
36516
|
+
return `delivered \u2014 grade it: t2 job release ${job.jobId} \xB7 t2 job reject ${job.jobId}`;
|
|
36517
|
+
case "refundable":
|
|
36518
|
+
return `deadline passed, no delivery \u2014 t2 job refund ${job.jobId} (fee-free, anyone may crank it)`;
|
|
36519
|
+
case "waiting":
|
|
36520
|
+
return `waiting on the seller \u2014 t2 job watch ${job.jobId}`;
|
|
36521
|
+
default:
|
|
36522
|
+
return "";
|
|
36523
|
+
}
|
|
36524
|
+
}
|
|
36485
36525
|
function inboxHint(job, bucket) {
|
|
36486
36526
|
switch (bucket) {
|
|
36487
36527
|
case "needsYou":
|
|
@@ -36499,12 +36539,17 @@ function inboxHint(job, bucket) {
|
|
|
36499
36539
|
}
|
|
36500
36540
|
}
|
|
36501
36541
|
function printInboxRow(job, bucket) {
|
|
36542
|
+
printInboxRowWithHint(job, inboxHint(job, bucket), "from", job.buyer);
|
|
36543
|
+
}
|
|
36544
|
+
function printBuyerInboxRow(job, bucket) {
|
|
36545
|
+
printInboxRowWithHint(job, buyerInboxHint(job, bucket), "seller", job.seller);
|
|
36546
|
+
}
|
|
36547
|
+
function printInboxRowWithHint(job, hint, partyLabel, party) {
|
|
36502
36548
|
const deadline = job.state === "funded" ? ` \xB7 deliver by ${new Date(job.deliverByMs).toISOString()}` : "";
|
|
36503
36549
|
printLine(
|
|
36504
|
-
` ${stateColor(job.state)} $${job.amountUsdc.toFixed(2)} USDC \xB7
|
|
36550
|
+
` ${stateColor(job.state)} $${job.amountUsdc.toFixed(2)} USDC \xB7 ${partyLabel} ${truncateAddress(party)}${deadline}`
|
|
36505
36551
|
);
|
|
36506
36552
|
printLine(` ${import_picocolors13.default.dim(job.jobId)}`);
|
|
36507
|
-
const hint = inboxHint(job, bucket);
|
|
36508
36553
|
if (hint) printLine(` ${import_picocolors13.default.dim("\u2192")} ${hint}`);
|
|
36509
36554
|
}
|
|
36510
36555
|
function deliverPreflightError(state, deliverByMs, nowMs) {
|
|
@@ -36549,8 +36594,11 @@ function registerJob(program3) {
|
|
|
36549
36594
|
The escrow is a Sui object, not a company: funds lock inside the Job object at
|
|
36550
36595
|
create; release/refund are pure functions of (state, clock, caller). A ghosting
|
|
36551
36596
|
buyer can't strand a delivering seller (anyone may release after the review
|
|
36552
|
-
window) and a no-show seller
|
|
36553
|
-
deadline
|
|
36597
|
+
window), and a no-show seller cannot keep funds AS LONG AS the buyer uses the
|
|
36598
|
+
escrow's protections \u2014 wait out the deadline and refund (anyone may crank it),
|
|
36599
|
+
or reject a bad delivery in-window. Releasing early without a delivery
|
|
36600
|
+
(--pay-without-delivery) voluntarily waives that protection. v1 caps jobs at
|
|
36601
|
+
${MAX_JOB_USDC} USDC.
|
|
36554
36602
|
|
|
36555
36603
|
Typical flow:
|
|
36556
36604
|
buyer $ t2 job hire 5 0xSELLER --spec brief.md --deadline 24h
|
|
@@ -36558,7 +36606,8 @@ Typical flow:
|
|
|
36558
36606
|
seller $ t2 job deliver 0xJOB report.md
|
|
36559
36607
|
buyer $ t2 job release 0xJOB (or: t2 job reject 0xJOB)
|
|
36560
36608
|
either $ t2 job watch 0xJOB
|
|
36561
|
-
seller $ t2 job watch --mine (the provider inbox \u2014 all your
|
|
36609
|
+
seller $ t2 job watch --mine (the provider inbox \u2014 all your sells)
|
|
36610
|
+
buyer $ t2 job watch --buying (the buyer inbox \u2014 every job you funded)
|
|
36562
36611
|
|
|
36563
36612
|
Hiring a LISTING (t2 ACP) \u2014 price + terms come from the listing:
|
|
36564
36613
|
buyer $ t2 services "market report"
|
|
@@ -36817,12 +36866,45 @@ no fund step; unclaimed openings refund fee-free):
|
|
|
36817
36866
|
handleError(error);
|
|
36818
36867
|
}
|
|
36819
36868
|
});
|
|
36869
|
+
group.command("release").argument("<jobId>", "The Job object id (0x\u2026)").description("Accept delivery \u2014 funds go to the seller (buyer; or anyone once the review window lapses). On a FUNDED job with no delivery this pays the full escrow to the seller, terminally \u2014 refused unless --pay-without-delivery.").option("--pay-without-delivery", "DELIBERATE goodwill: release the full escrow on a funded job with NO delivery (off-band delivery only \u2014 the seller keeps everything, no refund path)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE7})`).action(async (jobId, opts) => {
|
|
36870
|
+
try {
|
|
36871
|
+
const existing = await getJob(getSuiClient(), jobId).catch(() => null);
|
|
36872
|
+
const undelivered = existing?.state === "funded" && !existing.deliveryHash;
|
|
36873
|
+
if (undelivered && !opts.payWithoutDelivery) {
|
|
36874
|
+
throw new Error(
|
|
36875
|
+
"This job is FUNDED with no delivery \u2014 releasing now pays the full escrow to the seller with no recovery path. Wait for the delivery, refund after the deadline (t2 job refund), or \u2014 only if the work arrived off-band \u2014 re-run with --pay-without-delivery."
|
|
36876
|
+
);
|
|
36877
|
+
}
|
|
36878
|
+
if (undelivered && opts.payWithoutDelivery) {
|
|
36879
|
+
printWarning(
|
|
36880
|
+
"Paying WITHOUT an on-chain delivery: the full escrow goes to the seller, terminally. No refund path exists after this."
|
|
36881
|
+
);
|
|
36882
|
+
}
|
|
36883
|
+
const { digest } = await sponsoredJobVerb({
|
|
36884
|
+
base: opts.api ?? DEFAULT_API_BASE7,
|
|
36885
|
+
keyPath: opts.key,
|
|
36886
|
+
action: "release",
|
|
36887
|
+
params: {
|
|
36888
|
+
jobId,
|
|
36889
|
+
...opts.payWithoutDelivery ? { payWithoutDelivery: true } : {}
|
|
36890
|
+
}
|
|
36891
|
+
});
|
|
36892
|
+
if (isJsonMode()) {
|
|
36893
|
+
printJson({ jobId, action: "release", digest, ...undelivered ? { paidWithoutDelivery: true } : {} });
|
|
36894
|
+
return;
|
|
36895
|
+
}
|
|
36896
|
+
printBlank();
|
|
36897
|
+
printSuccess("Funds released to the seller.");
|
|
36898
|
+
if (digest) printKeyValue("Tx", digest);
|
|
36899
|
+
if (existing?.deliveryHash) {
|
|
36900
|
+
printInfo(`Rate the work: t2 job review ${jobId} --stars <1-5>`);
|
|
36901
|
+
}
|
|
36902
|
+
printBlank();
|
|
36903
|
+
} catch (error) {
|
|
36904
|
+
handleError(error);
|
|
36905
|
+
}
|
|
36906
|
+
});
|
|
36820
36907
|
for (const [verb, description, note] of [
|
|
36821
|
-
[
|
|
36822
|
-
"release",
|
|
36823
|
-
"Accept delivery \u2014 funds go to the seller (buyer; or anyone once the review window lapses)",
|
|
36824
|
-
"Funds released to the seller."
|
|
36825
|
-
],
|
|
36826
36908
|
[
|
|
36827
36909
|
"reject",
|
|
36828
36910
|
"Reject a delivery within the review window \u2014 funds split per the create terms (buyer)",
|
|
@@ -36854,9 +36936,6 @@ no fund step; unclaimed openings refund fee-free):
|
|
|
36854
36936
|
printBlank();
|
|
36855
36937
|
printSuccess(note);
|
|
36856
36938
|
if (digest) printKeyValue("Tx", digest);
|
|
36857
|
-
if (verb === "release") {
|
|
36858
|
-
printInfo(`Rate the work (builds the seller's on-chain-backed reputation): t2 job review ${jobId} --stars 5`);
|
|
36859
|
-
}
|
|
36860
36939
|
printBlank();
|
|
36861
36940
|
} catch (error) {
|
|
36862
36941
|
handleError(error);
|
|
@@ -36869,6 +36948,12 @@ no fund step; unclaimed openings refund fee-free):
|
|
|
36869
36948
|
if (!Number.isInteger(stars) || stars < 1 || stars > 5) {
|
|
36870
36949
|
throw new Error(`--stars must be an integer 1\u20135 (got "${opts.stars}").`);
|
|
36871
36950
|
}
|
|
36951
|
+
const reviewedJob = await getJob(getSuiClient(), jobId).catch(() => null);
|
|
36952
|
+
if (reviewedJob && !reviewedJob.deliveryHash) {
|
|
36953
|
+
throw new Error(
|
|
36954
|
+
"This job has no on-chain delivery \u2014 there is no work to rate. Reviews attach only to jobs the seller actually delivered."
|
|
36955
|
+
);
|
|
36956
|
+
}
|
|
36872
36957
|
const base = opts.api ?? DEFAULT_API_BASE7;
|
|
36873
36958
|
const agent = await withAgent({ keyPath: opts.key });
|
|
36874
36959
|
const address = agent.address();
|
|
@@ -36938,11 +37023,84 @@ no fund step; unclaimed openings refund fee-free):
|
|
|
36938
37023
|
handleError(error);
|
|
36939
37024
|
}
|
|
36940
37025
|
});
|
|
36941
|
-
group.command("watch").argument("[jobId]", "The Job object id (0x\u2026) \u2014 omit with --mine").description("Poll a job \u2014 or
|
|
37026
|
+
group.command("watch").argument("[jobId]", "The Job object id (0x\u2026) \u2014 omit with --mine or --buying").description("Poll a job \u2014 or an inbox: --mine (every job selling to you) / --buying (every job you funded)").option("--mine", "Watch ALL jobs where this wallet is the seller (the provider inbox)").option("--buying", "Watch ALL jobs where this wallet is the BUYER \u2014 recovers job ids the hire line printed once (S.1016)").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_BASE7})`).action(async (jobId, opts) => {
|
|
36942
37027
|
try {
|
|
37028
|
+
if (opts.mine && opts.buying) {
|
|
37029
|
+
throw new Error("Pick one seat: --mine (selling) or --buying (funding) \u2014 not both.");
|
|
37030
|
+
}
|
|
37031
|
+
if (jobId && (opts.mine || opts.buying)) {
|
|
37032
|
+
throw new Error("Pick one mode: a job id OR an inbox flag (--mine / --buying).");
|
|
37033
|
+
}
|
|
36943
37034
|
const agent = await withAgent({ keyPath: opts.key });
|
|
36944
37035
|
const me = agent.address();
|
|
36945
37036
|
const intervalMs = Math.max(5, Number.parseInt(opts.interval, 10) || 15) * 1e3;
|
|
37037
|
+
if (opts.buying) {
|
|
37038
|
+
const base = opts.api ?? DEFAULT_API_BASE7;
|
|
37039
|
+
const seen = /* @__PURE__ */ new Map();
|
|
37040
|
+
const buyClient = getSuiClient();
|
|
37041
|
+
const loadInbox = async () => hydrateJobsFromChain(await fetchBuyerJobs(base, me), (id) => getJob(buyClient, id));
|
|
37042
|
+
const jobs = await loadInbox();
|
|
37043
|
+
const inbox = summarizeBuyerInbox(jobs, Date.now());
|
|
37044
|
+
if (isJsonMode()) {
|
|
37045
|
+
printJson({
|
|
37046
|
+
buyer: me,
|
|
37047
|
+
counts: inbox.counts,
|
|
37048
|
+
needsYou: inbox.needsYou,
|
|
37049
|
+
refundable: inbox.refundable,
|
|
37050
|
+
waiting: inbox.waiting,
|
|
37051
|
+
terminal: inbox.terminal,
|
|
37052
|
+
jobs
|
|
37053
|
+
});
|
|
37054
|
+
return;
|
|
37055
|
+
}
|
|
37056
|
+
printBlank();
|
|
37057
|
+
printInfo(
|
|
37058
|
+
`Buyer inbox for ${truncateAddress(me)} \u2014 ${jobs.length} job(s) \xB7 ${inbox.counts.needsYou} need you \xB7 ${inbox.counts.refundable} refundable \xB7 ${inbox.counts.waiting} waiting`
|
|
37059
|
+
);
|
|
37060
|
+
printBlank();
|
|
37061
|
+
for (const [bucket, rows] of [
|
|
37062
|
+
["needsYou", inbox.needsYou],
|
|
37063
|
+
["refundable", inbox.refundable],
|
|
37064
|
+
["waiting", inbox.waiting]
|
|
37065
|
+
]) {
|
|
37066
|
+
for (const job of rows) {
|
|
37067
|
+
printBuyerInboxRow(job, bucket);
|
|
37068
|
+
printBlank();
|
|
37069
|
+
}
|
|
37070
|
+
}
|
|
37071
|
+
if (jobs.length - inbox.counts.terminal === 0) {
|
|
37072
|
+
printInfo("No open buys. Hire a listing or post an Open job and it lands here.");
|
|
37073
|
+
printBlank();
|
|
37074
|
+
}
|
|
37075
|
+
for (const job of jobs) seen.set(job.jobId, bucketBuyerJob(job, Date.now()));
|
|
37076
|
+
if (opts.once) return;
|
|
37077
|
+
for (; ; ) {
|
|
37078
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
37079
|
+
let latest;
|
|
37080
|
+
try {
|
|
37081
|
+
latest = await loadInbox();
|
|
37082
|
+
} catch {
|
|
37083
|
+
continue;
|
|
37084
|
+
}
|
|
37085
|
+
const nowMs = Date.now();
|
|
37086
|
+
for (const job of latest) {
|
|
37087
|
+
const bucket = bucketBuyerJob(job, nowMs);
|
|
37088
|
+
const prev = seen.get(job.jobId);
|
|
37089
|
+
if (prev === bucket) continue;
|
|
37090
|
+
seen.set(job.jobId, bucket);
|
|
37091
|
+
printBlank();
|
|
37092
|
+
if (bucket === "needsYou") {
|
|
37093
|
+
printSuccess(`Delivered \u2014 grade it: t2 job release ${job.jobId} \xB7 t2 job reject ${job.jobId}`);
|
|
37094
|
+
} else if (bucket === "refundable") {
|
|
37095
|
+
printSuccess(`Refundable \u2014 t2 job refund ${job.jobId} (deadline passed, no delivery)`);
|
|
37096
|
+
} else {
|
|
37097
|
+
printInfo(`Job ${truncateAddress(job.jobId)}: ${prev ?? "new"} \u2192 ${job.state}`);
|
|
37098
|
+
}
|
|
37099
|
+
printBuyerInboxRow(job, bucket);
|
|
37100
|
+
printBlank();
|
|
37101
|
+
}
|
|
37102
|
+
}
|
|
37103
|
+
}
|
|
36946
37104
|
if (opts.mine) {
|
|
36947
37105
|
const base = opts.api ?? DEFAULT_API_BASE7;
|
|
36948
37106
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -37017,7 +37175,7 @@ no fund step; unclaimed openings refund fee-free):
|
|
|
37017
37175
|
}
|
|
37018
37176
|
}
|
|
37019
37177
|
if (!jobId) {
|
|
37020
|
-
printError("Provide a job id \u2014 or
|
|
37178
|
+
printError("Provide a job id \u2014 or an inbox: --mine (selling) / --buying (funding).");
|
|
37021
37179
|
process.exitCode = 1;
|
|
37022
37180
|
return;
|
|
37023
37181
|
}
|
|
@@ -37127,10 +37285,13 @@ function printService(o) {
|
|
|
37127
37285
|
`$${o.priceUsdc.toFixed(2)} USDC ${import_picocolors14.default.dim(`\xB7 seller receives ${settlementSplit(o.priceUsdc).payout}`)}`
|
|
37128
37286
|
);
|
|
37129
37287
|
printKeyValue("Delivery", `within ${formatSla(o.slaMinutes)}`);
|
|
37130
|
-
|
|
37131
|
-
"
|
|
37132
|
-
|
|
37133
|
-
|
|
37288
|
+
{
|
|
37289
|
+
const id = o.agentNumericId != null ? ` #${o.agentNumericId}` : "";
|
|
37290
|
+
printKeyValue(
|
|
37291
|
+
"Seller",
|
|
37292
|
+
`${o.agentName ?? "unnamed"}${import_picocolors14.default.bold(id)} ${import_picocolors14.default.dim(truncateAddress(o.agent))}`
|
|
37293
|
+
);
|
|
37294
|
+
}
|
|
37134
37295
|
printKeyValue("You get", o.deliverable);
|
|
37135
37296
|
if (o.requirements != null) {
|
|
37136
37297
|
printKeyValue(
|
|
@@ -37280,15 +37441,55 @@ Examples:
|
|
|
37280
37441
|
}
|
|
37281
37442
|
});
|
|
37282
37443
|
}
|
|
37444
|
+
async function resolveServicesQuery(base, query) {
|
|
37445
|
+
const q = query?.trim();
|
|
37446
|
+
if (!q) {
|
|
37447
|
+
return { url: `${base}/services`, scope: { kind: "all" } };
|
|
37448
|
+
}
|
|
37449
|
+
if (q.startsWith("0x")) {
|
|
37450
|
+
try {
|
|
37451
|
+
const agent = validateAddress(q);
|
|
37452
|
+
return {
|
|
37453
|
+
url: `${base}/services?agent=${encodeURIComponent(agent)}`,
|
|
37454
|
+
scope: { kind: "agent", agent }
|
|
37455
|
+
};
|
|
37456
|
+
} catch {
|
|
37457
|
+
}
|
|
37458
|
+
} else if (looksLikeAgentRefValue(q)) {
|
|
37459
|
+
const ref = await resolveAgentRef(base, q);
|
|
37460
|
+
return {
|
|
37461
|
+
url: `${base}/services?agent=${encodeURIComponent(ref.address)}`,
|
|
37462
|
+
scope: { kind: "agent", agent: ref.address, numericId: ref.numericId }
|
|
37463
|
+
};
|
|
37464
|
+
}
|
|
37465
|
+
return {
|
|
37466
|
+
url: `${base}/services?q=${encodeURIComponent(q)}`,
|
|
37467
|
+
scope: { kind: "search", q }
|
|
37468
|
+
};
|
|
37469
|
+
}
|
|
37470
|
+
function scopeLabel(scope) {
|
|
37471
|
+
if (scope.kind !== "agent") {
|
|
37472
|
+
return "";
|
|
37473
|
+
}
|
|
37474
|
+
return scope.numericId != null ? `#${scope.numericId} (${truncateAddress(scope.agent)})` : truncateAddress(scope.agent);
|
|
37475
|
+
}
|
|
37283
37476
|
function registerDiscovery(command, opts) {
|
|
37284
|
-
command.argument("[query]", "What you need \u2014 free
|
|
37477
|
+
command.argument("[query]", "What you need \u2014 free text, or a SELLER scope: 0x\u2026 address, #id, or @handle (empty = everything)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE8})`).action(async (query, cmdOpts) => {
|
|
37285
37478
|
try {
|
|
37286
37479
|
const base = cmdOpts.api ?? DEFAULT_API_BASE8;
|
|
37287
|
-
const
|
|
37288
|
-
const json = await fetchJson4(
|
|
37289
|
-
const
|
|
37480
|
+
const { url, scope } = await resolveServicesQuery(base, query);
|
|
37481
|
+
const json = await fetchJson4(url);
|
|
37482
|
+
const all = json.services ?? [];
|
|
37483
|
+
const rows = scope.kind === "agent" ? all.filter((o) => !o.retired) : all;
|
|
37484
|
+
const retiredHidden = all.length - rows.length;
|
|
37290
37485
|
if (isJsonMode()) {
|
|
37291
|
-
printJson({
|
|
37486
|
+
printJson({
|
|
37487
|
+
query: query ?? null,
|
|
37488
|
+
scope,
|
|
37489
|
+
total: scope.kind === "agent" ? rows.length : json.total ?? rows.length,
|
|
37490
|
+
...retiredHidden > 0 ? { retiredHidden } : {},
|
|
37491
|
+
services: rows
|
|
37492
|
+
});
|
|
37292
37493
|
return;
|
|
37293
37494
|
}
|
|
37294
37495
|
printBlank();
|
|
@@ -37297,7 +37498,9 @@ function registerDiscovery(command, opts) {
|
|
|
37297
37498
|
printBlank();
|
|
37298
37499
|
}
|
|
37299
37500
|
if (rows.length === 0) {
|
|
37300
|
-
printInfo(
|
|
37501
|
+
printInfo(
|
|
37502
|
+
scope.kind === "agent" ? `No active services for ${scopeLabel(scope)}.` : scope.kind === "search" ? `No services match "${scope.q}".` : "No services listed yet."
|
|
37503
|
+
);
|
|
37301
37504
|
printBlank();
|
|
37302
37505
|
return;
|
|
37303
37506
|
}
|
|
@@ -37305,6 +37508,17 @@ function registerDiscovery(command, opts) {
|
|
|
37305
37508
|
printService(o);
|
|
37306
37509
|
printBlank();
|
|
37307
37510
|
}
|
|
37511
|
+
if (retiredHidden > 0) {
|
|
37512
|
+
printInfo(import_picocolors14.default.dim(`${retiredHidden} retired omitted \u2014 t2 service list ${scope.kind === "agent" ? scope.agent : ""}`));
|
|
37513
|
+
printBlank();
|
|
37514
|
+
}
|
|
37515
|
+
if (scope.kind === "search") {
|
|
37516
|
+
const sellers = new Set(rows.map((o) => o.agent)).size;
|
|
37517
|
+
if (sellers >= 2) {
|
|
37518
|
+
printInfo(import_picocolors14.default.dim(`Results span ${sellers} sellers \u2014 scope to one with t2 services 0x\u2026 or #id.`));
|
|
37519
|
+
printBlank();
|
|
37520
|
+
}
|
|
37521
|
+
}
|
|
37308
37522
|
} catch (error) {
|
|
37309
37523
|
handleError(error);
|
|
37310
37524
|
}
|