@t2000/cli 5.32.0 → 7.0.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/README.md +1 -2
- package/dist/{dist-VB42REWC.js → dist-Q2GGV3W4.js} +11 -156
- package/dist/{dist-VB42REWC.js.map → dist-Q2GGV3W4.js.map} +1 -1
- package/dist/index.js +86 -1307
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -10784,7 +10784,7 @@ var require_lib2 = __commonJS({
|
|
|
10784
10784
|
return matches;
|
|
10785
10785
|
};
|
|
10786
10786
|
exports.analyse = analyse;
|
|
10787
|
-
var detectFile = (filepath, opts = {}) => new Promise((
|
|
10787
|
+
var detectFile = (filepath, opts = {}) => new Promise((resolve2, reject) => {
|
|
10788
10788
|
let fd;
|
|
10789
10789
|
const fs = (0, node_1.default)();
|
|
10790
10790
|
const handler = (err, buffer) => {
|
|
@@ -10794,7 +10794,7 @@ var require_lib2 = __commonJS({
|
|
|
10794
10794
|
if (err) {
|
|
10795
10795
|
reject(err);
|
|
10796
10796
|
} else if (buffer) {
|
|
10797
|
-
|
|
10797
|
+
resolve2((0, exports.detect)(buffer));
|
|
10798
10798
|
} else {
|
|
10799
10799
|
reject(new Error("No error and no buffer received"));
|
|
10800
10800
|
}
|
|
@@ -18970,10 +18970,10 @@ var require_browser = __commonJS({
|
|
|
18970
18970
|
text = canvas;
|
|
18971
18971
|
canvas = void 0;
|
|
18972
18972
|
}
|
|
18973
|
-
return new Promise(function(
|
|
18973
|
+
return new Promise(function(resolve2, reject) {
|
|
18974
18974
|
try {
|
|
18975
18975
|
const data = QRCode.create(text, opts);
|
|
18976
|
-
|
|
18976
|
+
resolve2(renderFunc(data, canvas, opts));
|
|
18977
18977
|
} catch (e) {
|
|
18978
18978
|
reject(e);
|
|
18979
18979
|
}
|
|
@@ -19055,11 +19055,11 @@ var require_server = __commonJS({
|
|
|
19055
19055
|
}
|
|
19056
19056
|
function render(renderFunc, text, params) {
|
|
19057
19057
|
if (!params.cb) {
|
|
19058
|
-
return new Promise(function(
|
|
19058
|
+
return new Promise(function(resolve2, reject) {
|
|
19059
19059
|
try {
|
|
19060
19060
|
const data = QRCode.create(text, params.opts);
|
|
19061
19061
|
return renderFunc(data, params.opts, function(err, data2) {
|
|
19062
|
-
return err ? reject(err) :
|
|
19062
|
+
return err ? reject(err) : resolve2(data2);
|
|
19063
19063
|
});
|
|
19064
19064
|
} catch (e) {
|
|
19065
19065
|
reject(e);
|
|
@@ -30298,13 +30298,13 @@ var PromisePolyfill = class extends Promise {
|
|
|
30298
30298
|
// Available starting from Node 22
|
|
30299
30299
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
|
|
30300
30300
|
static withResolver() {
|
|
30301
|
-
let
|
|
30301
|
+
let resolve2;
|
|
30302
30302
|
let reject;
|
|
30303
30303
|
const promise = new Promise((res, rej) => {
|
|
30304
|
-
|
|
30304
|
+
resolve2 = res;
|
|
30305
30305
|
reject = rej;
|
|
30306
30306
|
});
|
|
30307
|
-
return { promise, resolve:
|
|
30307
|
+
return { promise, resolve: resolve2, reject };
|
|
30308
30308
|
}
|
|
30309
30309
|
};
|
|
30310
30310
|
|
|
@@ -30338,7 +30338,7 @@ function createPrompt(view) {
|
|
|
30338
30338
|
output
|
|
30339
30339
|
});
|
|
30340
30340
|
const screen = new ScreenManager(rl);
|
|
30341
|
-
const { promise, resolve:
|
|
30341
|
+
const { promise, resolve: resolve2, reject } = PromisePolyfill.withResolver();
|
|
30342
30342
|
const cancel = () => reject(new CancelPromptError());
|
|
30343
30343
|
if (signal) {
|
|
30344
30344
|
const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
|
|
@@ -30365,7 +30365,7 @@ function createPrompt(view) {
|
|
|
30365
30365
|
cycle(() => {
|
|
30366
30366
|
try {
|
|
30367
30367
|
const nextView = view(config, (value) => {
|
|
30368
|
-
setImmediate(() =>
|
|
30368
|
+
setImmediate(() => resolve2(value));
|
|
30369
30369
|
});
|
|
30370
30370
|
if (nextView === void 0) {
|
|
30371
30371
|
const callerFilename = callSites[1]?.getFileName();
|
|
@@ -33122,7 +33122,7 @@ function registerMcpStart(parent) {
|
|
|
33122
33122
|
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) => {
|
|
33123
33123
|
let mod3;
|
|
33124
33124
|
try {
|
|
33125
|
-
mod3 = await import("./dist-
|
|
33125
|
+
mod3 = await import("./dist-Q2GGV3W4.js");
|
|
33126
33126
|
} catch {
|
|
33127
33127
|
console.error("MCP server not installed. Run:\n npm install -g @t2000/mcp");
|
|
33128
33128
|
process.exit(1);
|
|
@@ -33594,9 +33594,6 @@ For MCP-aware clients (Claude Desktop, Cursor, Windsurf), prefer
|
|
|
33594
33594
|
registerSkillsUninstall(group);
|
|
33595
33595
|
}
|
|
33596
33596
|
|
|
33597
|
-
// src/commands/agent/index.ts
|
|
33598
|
-
import { createHash as createHash4 } from "crypto";
|
|
33599
|
-
|
|
33600
33597
|
// src/commands/agent/create.ts
|
|
33601
33598
|
var DEFAULT_API_BASE3 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
|
|
33602
33599
|
var STORE_BASE = "https://agents.t2000.ai";
|
|
@@ -33715,868 +33712,47 @@ function registerAgentCreate(group) {
|
|
|
33715
33712
|
}
|
|
33716
33713
|
const storeUrl = `${STORE_BASE}/${address}`;
|
|
33717
33714
|
if (isJsonMode()) {
|
|
33718
|
-
printJson({
|
|
33719
|
-
address,
|
|
33720
|
-
walletCreated: created,
|
|
33721
|
-
registered: true,
|
|
33722
|
-
alreadyRegistered: reg.alreadyRegistered,
|
|
33723
|
-
name,
|
|
33724
|
-
...category ? { category } : {},
|
|
33725
|
-
...ownerProposed ? { ownerProposed: owner } : {},
|
|
33726
|
-
storeUrl,
|
|
33727
|
-
keyPath: opts.key ?? "~/.t2000/wallet.key"
|
|
33728
|
-
});
|
|
33729
|
-
return;
|
|
33730
|
-
}
|
|
33731
|
-
printBlank();
|
|
33732
|
-
printSuccess(`${name} is live`);
|
|
33733
|
-
printKeyValue("Address", address);
|
|
33734
|
-
printKeyValue("Store", storeUrl);
|
|
33735
|
-
printKeyValue(
|
|
33736
|
-
"Wallet",
|
|
33737
|
-
created ? `created at ${opts.key ?? "~/.t2000/wallet.key"}` : `reused ${opts.key ?? "~/.t2000/wallet.key"}`
|
|
33738
|
-
);
|
|
33739
|
-
if (ownerProposed) {
|
|
33740
|
-
printKeyValue(
|
|
33741
|
-
"Owner",
|
|
33742
|
-
`proposed ${owner} \u2014 confirm at ${STORE_BASE}/manage/agents`
|
|
33743
|
-
);
|
|
33744
|
-
}
|
|
33745
|
-
printBlank();
|
|
33746
|
-
printLine("Next:");
|
|
33747
|
-
printLine(" t2 fund # add USDC (QR / card link)");
|
|
33748
|
-
printLine(" t2 agent services add ... # list something to sell");
|
|
33749
|
-
printLine(" t2 agent deploy ... # wrap an API behind x402");
|
|
33750
|
-
printBlank();
|
|
33751
|
-
} catch (error) {
|
|
33752
|
-
handleError(error);
|
|
33753
|
-
}
|
|
33754
|
-
});
|
|
33755
|
-
}
|
|
33756
|
-
|
|
33757
|
-
// src/commands/agent/review.ts
|
|
33758
|
-
import { createHash } from "crypto";
|
|
33759
|
-
var DEFAULT_GATEWAY = "https://x402.t2000.ai";
|
|
33760
|
-
async function fetchJson2(url, init) {
|
|
33761
|
-
const res = await fetch(url, {
|
|
33762
|
-
method: init?.method ?? "GET",
|
|
33763
|
-
headers: init?.body ? { "Content-Type": "application/json" } : void 0,
|
|
33764
|
-
body: init?.body ? JSON.stringify(init.body) : void 0
|
|
33765
|
-
});
|
|
33766
|
-
const json = await res.json().catch(() => ({}));
|
|
33767
|
-
if (!res.ok) {
|
|
33768
|
-
const err = json.error;
|
|
33769
|
-
throw new Error(typeof err === "string" ? err : `HTTP ${res.status}`);
|
|
33770
|
-
}
|
|
33771
|
-
return json;
|
|
33772
|
-
}
|
|
33773
|
-
function reviewMessage(digest, stars, text, timestamp) {
|
|
33774
|
-
const hash = createHash("sha256").update(text, "utf8").digest("hex");
|
|
33775
|
-
return `t2000-review:${digest}:${stars}:${hash}:${timestamp}`;
|
|
33776
|
-
}
|
|
33777
|
-
function registerAgentReview(agent) {
|
|
33778
|
-
agent.command("review").description(
|
|
33779
|
-
"Review a purchase (1-5 stars + optional text). Binds to your latest settled receipt with the seller, or --digest for a specific one. Re-run to edit. [Store v2]"
|
|
33780
|
-
).argument("<seller>", "Seller address (0x\u2026)").requiredOption("--stars <n>", "Rating 1-5").option("--text <text>", "Review text (\u2264400 chars)").option("--digest <digest>", "Collect digest of the specific purchase").option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY})`).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").action(
|
|
33781
|
-
async (seller, opts) => {
|
|
33782
|
-
try {
|
|
33783
|
-
const stars = Number(opts.stars);
|
|
33784
|
-
if (!Number.isInteger(stars) || stars < 1 || stars > 5) {
|
|
33785
|
-
throw new Error("--stars must be an integer 1-5.");
|
|
33786
|
-
}
|
|
33787
|
-
const text = (opts.text ?? "").trim();
|
|
33788
|
-
if (text.length > 400) {
|
|
33789
|
-
throw new Error("--text must be \u2264 400 chars.");
|
|
33790
|
-
}
|
|
33791
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY;
|
|
33792
|
-
const agentW = await withAgent({ keyPath: opts.key });
|
|
33793
|
-
const buyer = agentW.address();
|
|
33794
|
-
let digest = (opts.digest ?? "").trim();
|
|
33795
|
-
if (!digest) {
|
|
33796
|
-
const res = await fetchJson2(
|
|
33797
|
-
`${gateway}/commerce/review?buyer=${buyer}&seller=${encodeURIComponent(seller)}`
|
|
33798
|
-
);
|
|
33799
|
-
const reviewable = res.reviewable ?? [];
|
|
33800
|
-
if (reviewable.length === 0) {
|
|
33801
|
-
throw new Error(
|
|
33802
|
-
"No settled purchase from this wallet to that seller \u2014 buy first (`t2 agent pay`), then review."
|
|
33803
|
-
);
|
|
33804
|
-
}
|
|
33805
|
-
digest = reviewable[0].digest;
|
|
33806
|
-
}
|
|
33807
|
-
const timestamp = Date.now();
|
|
33808
|
-
const message = new TextEncoder().encode(
|
|
33809
|
-
reviewMessage(digest, stars, text, timestamp)
|
|
33810
|
-
);
|
|
33811
|
-
const { signature } = await agentW.keypair.signPersonalMessage(message);
|
|
33812
|
-
await fetchJson2(`${gateway}/commerce/review`, {
|
|
33813
|
-
method: "POST",
|
|
33814
|
-
body: { digest, stars, text, timestamp, signature }
|
|
33815
|
-
});
|
|
33816
|
-
if (isJsonMode()) {
|
|
33817
|
-
printJson({ ok: true, digest, stars, text: text || null });
|
|
33818
|
-
return;
|
|
33819
|
-
}
|
|
33820
|
-
printBlank();
|
|
33821
|
-
printSuccess(
|
|
33822
|
-
`Review posted \u2014 ${"\u2605".repeat(stars)}${"\u2606".repeat(5 - stars)}${text ? ` "${text}"` : ""}`
|
|
33823
|
-
);
|
|
33824
|
-
printKeyValue("Receipt", digest);
|
|
33825
|
-
printKeyValue(
|
|
33826
|
-
"Listing",
|
|
33827
|
-
`https://agents.t2000.ai/${seller.startsWith("0x") ? seller : ""}`
|
|
33828
|
-
);
|
|
33829
|
-
printBlank();
|
|
33830
|
-
} catch (e) {
|
|
33831
|
-
handleError(e);
|
|
33832
|
-
}
|
|
33833
|
-
}
|
|
33834
|
-
);
|
|
33835
|
-
}
|
|
33836
|
-
|
|
33837
|
-
// src/commands/agent/serve.ts
|
|
33838
|
-
import { createHash as createHash3 } from "crypto";
|
|
33839
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
33840
|
-
import { createServer } from "http";
|
|
33841
|
-
import { join as join8, resolve as resolve2 } from "path";
|
|
33842
|
-
import { pathToFileURL } from "url";
|
|
33843
|
-
|
|
33844
|
-
// src/commands/agent/services.ts
|
|
33845
|
-
import { createHash as createHash2 } from "crypto";
|
|
33846
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
33847
|
-
var SLUG_RE = /^[a-z0-9][a-z0-9-]{1,39}$/;
|
|
33848
|
-
async function fetchJson3(url, init) {
|
|
33849
|
-
const res = await fetch(url, {
|
|
33850
|
-
method: init?.method ?? "GET",
|
|
33851
|
-
headers: init?.body ? { "Content-Type": "application/json" } : void 0,
|
|
33852
|
-
body: init?.body ? JSON.stringify(init.body) : void 0
|
|
33853
|
-
});
|
|
33854
|
-
const json = await res.json().catch(() => ({}));
|
|
33855
|
-
if (!res.ok) {
|
|
33856
|
-
const err = json.error;
|
|
33857
|
-
const msg = typeof err === "string" ? err : err?.message ?? `HTTP ${res.status}`;
|
|
33858
|
-
throw new Error(msg);
|
|
33859
|
-
}
|
|
33860
|
-
return json;
|
|
33861
|
-
}
|
|
33862
|
-
function canonicalServicesJson(services) {
|
|
33863
|
-
return JSON.stringify(
|
|
33864
|
-
services.map(
|
|
33865
|
-
(s) => Object.fromEntries(
|
|
33866
|
-
Object.entries(s).sort(([a], [b]) => a.localeCompare(b))
|
|
33867
|
-
)
|
|
33868
|
-
)
|
|
33869
|
-
);
|
|
33870
|
-
}
|
|
33871
|
-
async function getCatalog(base, address) {
|
|
33872
|
-
const res = await fetchJson3(
|
|
33873
|
-
`${base}/agent/services?address=${encodeURIComponent(address)}`
|
|
33874
|
-
);
|
|
33875
|
-
return Array.isArray(res.services) ? res.services : [];
|
|
33876
|
-
}
|
|
33877
|
-
async function putCatalog(opts) {
|
|
33878
|
-
const agent = await withAgent({ keyPath: opts.keyPath });
|
|
33879
|
-
const address = agent.address();
|
|
33880
|
-
const challenge = await fetchJson3(`${opts.base}/agent/challenge`, {
|
|
33881
|
-
method: "POST",
|
|
33882
|
-
body: { address }
|
|
33883
|
-
});
|
|
33884
|
-
const nonce = challenge.nonce;
|
|
33885
|
-
if (!nonce) {
|
|
33886
|
-
throw new Error("Failed to get a challenge nonce.");
|
|
33887
|
-
}
|
|
33888
|
-
const digest = createHash2("sha256").update(canonicalServicesJson(opts.services)).digest("hex");
|
|
33889
|
-
const message = new TextEncoder().encode(
|
|
33890
|
-
`t2000-agent-services:${nonce}:${digest}`
|
|
33891
|
-
);
|
|
33892
|
-
const { signature } = await agent.keypair.signPersonalMessage(message);
|
|
33893
|
-
const res = await fetchJson3(`${opts.base}/agent/services`, {
|
|
33894
|
-
method: "POST",
|
|
33895
|
-
body: { address, nonce, signature, services: opts.services }
|
|
33896
|
-
});
|
|
33897
|
-
return { address, count: Number(res.count ?? opts.services.length) };
|
|
33898
|
-
}
|
|
33899
|
-
function entryFromFlags(opts) {
|
|
33900
|
-
const slug = String(opts.slug ?? "").trim().toLowerCase();
|
|
33901
|
-
if (!SLUG_RE.test(slug)) {
|
|
33902
|
-
throw new Error("--slug is required: [a-z0-9-], 2-40 chars.");
|
|
33903
|
-
}
|
|
33904
|
-
const out = { slug };
|
|
33905
|
-
if (opts.title !== void 0) {
|
|
33906
|
-
out.title = opts.title;
|
|
33907
|
-
}
|
|
33908
|
-
if (opts.description !== void 0) {
|
|
33909
|
-
out.description = opts.description;
|
|
33910
|
-
}
|
|
33911
|
-
if (opts.price !== void 0) {
|
|
33912
|
-
out.priceUsdc = opts.price;
|
|
33913
|
-
}
|
|
33914
|
-
if (opts.input !== void 0) {
|
|
33915
|
-
out.input = opts.input || null;
|
|
33916
|
-
}
|
|
33917
|
-
if (opts.endpoint !== void 0) {
|
|
33918
|
-
out.endpoint = opts.endpoint || null;
|
|
33919
|
-
}
|
|
33920
|
-
if (opts.method !== void 0) {
|
|
33921
|
-
out.method = opts.method.toUpperCase() === "GET" ? "GET" : "POST";
|
|
33922
|
-
}
|
|
33923
|
-
return out;
|
|
33924
|
-
}
|
|
33925
|
-
function registerAgentServices(agentGroup, defaults) {
|
|
33926
|
-
const group = agentGroup.command("services").description(
|
|
33927
|
-
"Manage this agent's service CATALOG (one agent, many services). Buy URLs: commerce/pay/<agent>/<slug>. [Store v2]"
|
|
33928
|
-
);
|
|
33929
|
-
group.command("list").argument("[address]", "Agent address (default: your wallet)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${defaults.apiBase})`).description("List the catalog (public read).").action(async (addressArg, opts) => {
|
|
33930
|
-
try {
|
|
33931
|
-
const base = opts.api ?? defaults.apiBase;
|
|
33932
|
-
const address = addressArg ?? (await withAgent({ keyPath: opts.key })).address();
|
|
33933
|
-
const services = await getCatalog(base, address);
|
|
33934
|
-
if (isJsonMode()) {
|
|
33935
|
-
printJson({ address, services });
|
|
33936
|
-
return;
|
|
33937
|
-
}
|
|
33938
|
-
printBlank();
|
|
33939
|
-
if (services.length === 0) {
|
|
33940
|
-
printLine("No services in the catalog. Add one: t2 agent services add --slug <slug> --title \u2026 --description \u2026 --price \u2026");
|
|
33941
|
-
printBlank();
|
|
33942
|
-
return;
|
|
33943
|
-
}
|
|
33944
|
-
for (const s of services) {
|
|
33945
|
-
printKeyValue(
|
|
33946
|
-
s.slug,
|
|
33947
|
-
`$${s.priceUsdc} \u2014 ${s.title}${s.active === false ? " (inactive)" : ""}`
|
|
33948
|
-
);
|
|
33949
|
-
}
|
|
33950
|
-
printBlank();
|
|
33951
|
-
} catch (error) {
|
|
33952
|
-
handleError(error);
|
|
33953
|
-
}
|
|
33954
|
-
});
|
|
33955
|
-
group.command("add").description("Add one service to the catalog.").requiredOption("--slug <slug>", "Service slug ([a-z0-9-], 2-40 chars)").requiredOption("--title <title>", "Service title (\u226480 chars)").requiredOption("--description <text>", "Listing copy (\u2264480 chars)").requiredOption("--price <usdc>", "Price per call in USDC (e.g. 0.02)").option("--input <hint>", 'Input hint, e.g. "Provide: 1. address 2. chain"').option("--endpoint <url>", "Self-hosted https endpoint (omit for wrap/payment-only)").option("--method <method>", "Wrap delivery method: GET or POST").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${defaults.apiBase})`).action(async (opts) => {
|
|
33956
|
-
try {
|
|
33957
|
-
const base = opts.api ?? defaults.apiBase;
|
|
33958
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
33959
|
-
const current = await getCatalog(base, agent.address());
|
|
33960
|
-
const entry = entryFromFlags(opts);
|
|
33961
|
-
if (current.some((s) => s.slug === entry.slug)) {
|
|
33962
|
-
throw new Error(
|
|
33963
|
-
`Slug "${entry.slug}" already exists \u2014 use: t2 agent services update --slug ${entry.slug}`
|
|
33964
|
-
);
|
|
33965
|
-
}
|
|
33966
|
-
const next = {
|
|
33967
|
-
title: "",
|
|
33968
|
-
description: "",
|
|
33969
|
-
priceUsdc: "0",
|
|
33970
|
-
active: true,
|
|
33971
|
-
...entry
|
|
33972
|
-
};
|
|
33973
|
-
const { count } = await putCatalog({
|
|
33974
|
-
base,
|
|
33975
|
-
keyPath: opts.key,
|
|
33976
|
-
services: [...current, next]
|
|
33977
|
-
});
|
|
33978
|
-
if (isJsonMode()) {
|
|
33979
|
-
printJson({ added: entry.slug, count });
|
|
33980
|
-
return;
|
|
33981
|
-
}
|
|
33982
|
-
printBlank();
|
|
33983
|
-
printSuccess(`Service "${entry.slug}" added \u2014 catalog now ${count} service${count === 1 ? "" : "s"}.`);
|
|
33984
|
-
printLine(`Buy URL: https://x402.t2000.ai/commerce/pay/${agent.address()}/${entry.slug}`);
|
|
33985
|
-
printBlank();
|
|
33986
|
-
} catch (error) {
|
|
33987
|
-
handleError(error);
|
|
33988
|
-
}
|
|
33989
|
-
});
|
|
33990
|
-
group.command("update").description("Update one service (only the provided fields change).").requiredOption("--slug <slug>", "Service slug to update").option("--title <title>", "Service title (\u226480 chars)").option("--description <text>", "Listing copy (\u2264480 chars)").option("--price <usdc>", "Price per call in USDC").option("--input <hint>", 'Input hint ("" to clear)').option("--endpoint <url>", 'Self-hosted https endpoint ("" to clear \u2192 wrap mode)').option("--method <method>", "Wrap delivery method: GET or POST").option("--active <bool>", "true | false \u2014 per-service kill switch").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${defaults.apiBase})`).action(async (opts) => {
|
|
33991
|
-
try {
|
|
33992
|
-
const base = opts.api ?? defaults.apiBase;
|
|
33993
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
33994
|
-
const current = await getCatalog(base, agent.address());
|
|
33995
|
-
const entry = entryFromFlags(opts);
|
|
33996
|
-
const idx = current.findIndex((s) => s.slug === entry.slug);
|
|
33997
|
-
if (idx === -1) {
|
|
33998
|
-
throw new Error(`No service "${entry.slug}" in the catalog.`);
|
|
33999
|
-
}
|
|
34000
|
-
const merged = { ...current[idx], ...entry };
|
|
34001
|
-
if (opts.active !== void 0) {
|
|
34002
|
-
merged.active = String(opts.active).toLowerCase() !== "false";
|
|
34003
|
-
}
|
|
34004
|
-
const services = [...current];
|
|
34005
|
-
services[idx] = merged;
|
|
34006
|
-
const { count } = await putCatalog({ base, keyPath: opts.key, services });
|
|
34007
|
-
if (isJsonMode()) {
|
|
34008
|
-
printJson({ updated: entry.slug, count });
|
|
34009
|
-
return;
|
|
34010
|
-
}
|
|
34011
|
-
printBlank();
|
|
34012
|
-
printSuccess(`Service "${entry.slug}" updated.`);
|
|
34013
|
-
printBlank();
|
|
34014
|
-
} catch (error) {
|
|
34015
|
-
handleError(error);
|
|
34016
|
-
}
|
|
34017
|
-
});
|
|
34018
|
-
group.command("remove").description("Remove one service from the catalog.").requiredOption("--slug <slug>", "Service slug to remove").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${defaults.apiBase})`).action(async (opts) => {
|
|
34019
|
-
try {
|
|
34020
|
-
const base = opts.api ?? defaults.apiBase;
|
|
34021
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
34022
|
-
const slug = String(opts.slug ?? "").trim().toLowerCase();
|
|
34023
|
-
const current = await getCatalog(base, agent.address());
|
|
34024
|
-
const services = current.filter((s) => s.slug !== slug);
|
|
34025
|
-
if (services.length === current.length) {
|
|
34026
|
-
throw new Error(`No service "${slug}" in the catalog.`);
|
|
34027
|
-
}
|
|
34028
|
-
const { count } = await putCatalog({ base, keyPath: opts.key, services });
|
|
34029
|
-
if (isJsonMode()) {
|
|
34030
|
-
printJson({ removed: slug, count });
|
|
34031
|
-
return;
|
|
34032
|
-
}
|
|
34033
|
-
printBlank();
|
|
34034
|
-
printSuccess(`Service "${slug}" removed \u2014 catalog now ${count} service${count === 1 ? "" : "s"}.`);
|
|
34035
|
-
printBlank();
|
|
34036
|
-
} catch (error) {
|
|
34037
|
-
handleError(error);
|
|
34038
|
-
}
|
|
34039
|
-
});
|
|
34040
|
-
group.command("sync").description(
|
|
34041
|
-
"Declarative catalog sync \u2014 the manifest file IS the catalog (adds/updates/removes to match). The catalog-scale path."
|
|
34042
|
-
).argument("<file>", "Path to a JSON manifest: [{ slug, title, description, priceUsdc, input?, endpoint?, method?, active? }]").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${defaults.apiBase})`).action(async (file, opts) => {
|
|
34043
|
-
try {
|
|
34044
|
-
const base = opts.api ?? defaults.apiBase;
|
|
34045
|
-
const raw = JSON.parse(readFileSync3(file, "utf8"));
|
|
34046
|
-
const list = Array.isArray(raw) ? raw : raw?.services ?? null;
|
|
34047
|
-
if (!Array.isArray(list)) {
|
|
34048
|
-
throw new Error(
|
|
34049
|
-
'Manifest must be a JSON array of services (or { "services": [...] }).'
|
|
34050
|
-
);
|
|
34051
|
-
}
|
|
34052
|
-
const services = list.map((s) => {
|
|
34053
|
-
const entry = s;
|
|
34054
|
-
return { ...entry, active: entry.active !== false };
|
|
34055
|
-
});
|
|
34056
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
34057
|
-
const before = await getCatalog(base, agent.address());
|
|
34058
|
-
const { count } = await putCatalog({ base, keyPath: opts.key, services });
|
|
34059
|
-
if (isJsonMode()) {
|
|
34060
|
-
printJson({ synced: count, before: before.length });
|
|
34061
|
-
return;
|
|
34062
|
-
}
|
|
34063
|
-
printBlank();
|
|
34064
|
-
printSuccess(`Catalog synced \u2014 ${before.length} \u2192 ${count} services.`);
|
|
34065
|
-
printBlank();
|
|
34066
|
-
} catch (error) {
|
|
34067
|
-
handleError(error);
|
|
34068
|
-
}
|
|
34069
|
-
});
|
|
34070
|
-
}
|
|
34071
|
-
|
|
34072
|
-
// src/commands/agent/serve.ts
|
|
34073
|
-
var DEFAULT_API_BASE4 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
|
|
34074
|
-
var DEFAULT_GATEWAY2 = process.env.T2000_GATEWAY_URL ?? "https://mpp.t2000.ai";
|
|
34075
|
-
var SLUG_RE2 = /^[a-z0-9][a-z0-9-]{1,39}$/;
|
|
34076
|
-
var MANIFEST = "t2serve.json";
|
|
34077
|
-
var HANDLER = "handler.mjs";
|
|
34078
|
-
var HANDLER_TEMPLATE = `// t2000 hosted handler (R1). Deploys with: t2 agent serve deploy
|
|
34079
|
-
//
|
|
34080
|
-
// Contract: one self-contained ES module (no imports \u2014 fetch/crypto/URL are
|
|
34081
|
-
// available as Workers globals). The gateway calls this ONLY for paid,
|
|
34082
|
-
// escrowed deliveries; on a 2xx your wallet is paid, on failure the buyer is
|
|
34083
|
-
// auto-refunded. Keep it fast (15s delivery timeout, 5s CPU cap).
|
|
34084
|
-
//
|
|
34085
|
-
// input \u2014 the buyer's request body (parsed JSON, or a raw string)
|
|
34086
|
-
// ctx \u2014 { agent, slug, buyer, secrets }
|
|
34087
|
-
// secrets = your vault (t2 agent serve secrets set KEY=value)
|
|
34088
|
-
export default async function handle(input, ctx) {
|
|
34089
|
-
// Example: compose public data and return a result.
|
|
34090
|
-
// const res = await fetch('https://api.example.com/data');
|
|
34091
|
-
// const data = await res.json();
|
|
34092
|
-
return {
|
|
34093
|
-
echo: input,
|
|
34094
|
-
from: ctx.slug,
|
|
34095
|
-
note: 'Replace this with your service logic.',
|
|
34096
|
-
};
|
|
34097
|
-
}
|
|
34098
|
-
`;
|
|
34099
|
-
var PROXY_TEMPLATE = `// t2000 hosted handler \u2014 "Proxy an API" template. You hold a key to an
|
|
34100
|
-
// API; this resells calls to it per-call, with your key never leaving the
|
|
34101
|
-
// t2000 vault. Setup:
|
|
34102
|
-
// 1. Edit UPSTREAM below.
|
|
34103
|
-
// 2. t2 agent serve secrets set UPSTREAM_KEY=<your api key>
|
|
34104
|
-
// 3. t2 agent serve deploy
|
|
34105
|
-
const UPSTREAM = 'https://api.example.com/v1/endpoint';
|
|
34106
|
-
|
|
34107
|
-
export default async function handle(input, ctx) {
|
|
34108
|
-
const res = await fetch(UPSTREAM, {
|
|
34109
|
-
method: 'POST',
|
|
34110
|
-
headers: {
|
|
34111
|
-
'content-type': 'application/json',
|
|
34112
|
-
// Adjust the auth scheme to your upstream (x-api-key, basic, \u2026).
|
|
34113
|
-
...(ctx.secrets?.UPSTREAM_KEY
|
|
34114
|
-
? { authorization: \`Bearer \${ctx.secrets.UPSTREAM_KEY}\` }
|
|
34115
|
-
: {}),
|
|
34116
|
-
},
|
|
34117
|
-
body: JSON.stringify(input ?? {}),
|
|
34118
|
-
});
|
|
34119
|
-
if (!res.ok) {
|
|
34120
|
-
// Throwing fails the delivery \u2192 the buyer is auto-refunded.
|
|
34121
|
-
throw new Error(\`Upstream error \${res.status}\`);
|
|
34122
|
-
}
|
|
34123
|
-
return await res.json();
|
|
34124
|
-
}
|
|
34125
|
-
`;
|
|
34126
|
-
var TEMPLATES = {
|
|
34127
|
-
default: HANDLER_TEMPLATE,
|
|
34128
|
-
proxy: PROXY_TEMPLATE
|
|
34129
|
-
};
|
|
34130
|
-
function readManifest(dir) {
|
|
34131
|
-
const p = join8(dir, MANIFEST);
|
|
34132
|
-
if (!existsSync4(p)) {
|
|
34133
|
-
throw new Error(
|
|
34134
|
-
`No ${MANIFEST} here \u2014 run \`t2 agent serve init\` first (or --dir <path>).`
|
|
34135
|
-
);
|
|
34136
|
-
}
|
|
34137
|
-
const m = JSON.parse(readFileSync4(p, "utf8"));
|
|
34138
|
-
const slug = String(m.slug ?? "").trim().toLowerCase();
|
|
34139
|
-
if (!SLUG_RE2.test(slug)) {
|
|
34140
|
-
throw new Error(`${MANIFEST}: slug must match [a-z0-9-], 2-40 chars.`);
|
|
34141
|
-
}
|
|
34142
|
-
if (!m.title?.trim() || !m.description?.trim()) {
|
|
34143
|
-
throw new Error(`${MANIFEST}: title and description are required.`);
|
|
34144
|
-
}
|
|
34145
|
-
const price = Number(m.price);
|
|
34146
|
-
if (!Number.isFinite(price) || price <= 0) {
|
|
34147
|
-
throw new Error(`${MANIFEST}: price must be a positive USDC amount.`);
|
|
34148
|
-
}
|
|
34149
|
-
return { ...m, slug };
|
|
34150
|
-
}
|
|
34151
|
-
function readHandler(dir) {
|
|
34152
|
-
const p = join8(dir, HANDLER);
|
|
34153
|
-
if (!existsSync4(p)) {
|
|
34154
|
-
throw new Error(`No ${HANDLER} here \u2014 run \`t2 agent serve init\` first.`);
|
|
34155
|
-
}
|
|
34156
|
-
const src = readFileSync4(p, "utf8");
|
|
34157
|
-
if (!src.trim()) {
|
|
34158
|
-
throw new Error(`${HANDLER} is empty.`);
|
|
34159
|
-
}
|
|
34160
|
-
if (/^\s*import\s.+from\s/m.test(src)) {
|
|
34161
|
-
throw new Error(
|
|
34162
|
-
`${HANDLER} must be self-contained (no imports) \u2014 fetch, crypto, and URL are available as globals.`
|
|
34163
|
-
);
|
|
34164
|
-
}
|
|
34165
|
-
return src;
|
|
34166
|
-
}
|
|
34167
|
-
async function fetchJson4(url, init) {
|
|
34168
|
-
const res = await fetch(url, {
|
|
34169
|
-
method: init?.method ?? "GET",
|
|
34170
|
-
headers: init?.body ? { "Content-Type": "application/json" } : void 0,
|
|
34171
|
-
body: init?.body ? JSON.stringify(init.body) : void 0
|
|
34172
|
-
});
|
|
34173
|
-
const json = await res.json().catch(() => ({}));
|
|
34174
|
-
if (!res.ok) {
|
|
34175
|
-
const err = json.error;
|
|
34176
|
-
const msg = typeof err === "string" ? err : err?.message ?? `HTTP ${res.status}`;
|
|
34177
|
-
throw new Error(msg);
|
|
34178
|
-
}
|
|
34179
|
-
return json;
|
|
34180
|
-
}
|
|
34181
|
-
function registerAgentServe(agentGroup) {
|
|
34182
|
-
const group = agentGroup.command("serve").description(
|
|
34183
|
-
"Hosted handlers \u2014 deploy a function to t2000 compute and sell it per call. No server, no wrap. [R1]"
|
|
34184
|
-
);
|
|
34185
|
-
group.command("init").description(`Scaffold ${HANDLER} + ${MANIFEST} in the current directory.`).option("--slug <slug>", "Service slug (in the buy URL)", "my-service").option(
|
|
34186
|
-
"--template <name>",
|
|
34187
|
-
`Handler template: ${Object.keys(TEMPLATES).join(" | ")} (proxy = resell a keyed API)`,
|
|
34188
|
-
"default"
|
|
34189
|
-
).option("--dir <path>", "Target directory (default: cwd)").action((opts) => {
|
|
34190
|
-
try {
|
|
34191
|
-
const dir = resolve2(opts.dir ?? ".");
|
|
34192
|
-
mkdirSync2(dir, { recursive: true });
|
|
34193
|
-
const slug = opts.slug.trim().toLowerCase();
|
|
34194
|
-
if (!SLUG_RE2.test(slug)) {
|
|
34195
|
-
throw new Error("--slug must match [a-z0-9-], 2-40 chars.");
|
|
34196
|
-
}
|
|
34197
|
-
const template = TEMPLATES[opts.template];
|
|
34198
|
-
if (!template) {
|
|
34199
|
-
throw new Error(
|
|
34200
|
-
`Unknown --template "${opts.template}" \u2014 use: ${Object.keys(TEMPLATES).join(", ")}.`
|
|
34201
|
-
);
|
|
34202
|
-
}
|
|
34203
|
-
const manifestPath = join8(dir, MANIFEST);
|
|
34204
|
-
const handlerPath = join8(dir, HANDLER);
|
|
34205
|
-
if (existsSync4(manifestPath) || existsSync4(handlerPath)) {
|
|
34206
|
-
throw new Error(`${MANIFEST} or ${HANDLER} already exists here.`);
|
|
34207
|
-
}
|
|
34208
|
-
writeFileSync3(
|
|
34209
|
-
manifestPath,
|
|
34210
|
-
`${JSON.stringify(
|
|
34211
|
-
{
|
|
34212
|
-
slug,
|
|
34213
|
-
title: "My service",
|
|
34214
|
-
description: "What the buyer gets, in one or two sentences. Provide: 1. \u2026",
|
|
34215
|
-
price: "0.02",
|
|
34216
|
-
input: "JSON body with your parameters"
|
|
34217
|
-
},
|
|
34218
|
-
null,
|
|
34219
|
-
2
|
|
34220
|
-
)}
|
|
34221
|
-
`
|
|
34222
|
-
);
|
|
34223
|
-
writeFileSync3(handlerPath, template);
|
|
34224
|
-
if (isJsonMode()) {
|
|
34225
|
-
printJson({ dir, files: [MANIFEST, HANDLER] });
|
|
34226
|
-
return;
|
|
34227
|
-
}
|
|
34228
|
-
printBlank();
|
|
34229
|
-
printSuccess("Scaffolded a hosted handler");
|
|
34230
|
-
printKeyValue("Handler", handlerPath);
|
|
34231
|
-
printKeyValue("Manifest", manifestPath);
|
|
34232
|
-
printBlank();
|
|
34233
|
-
printLine("Next:");
|
|
34234
|
-
printLine(" t2 agent serve dev # try it locally");
|
|
34235
|
-
printLine(" t2 agent serve deploy # deploy + list it for sale");
|
|
34236
|
-
printBlank();
|
|
34237
|
-
} catch (error) {
|
|
34238
|
-
handleError(error);
|
|
34239
|
-
}
|
|
34240
|
-
});
|
|
34241
|
-
group.command("dev").description(
|
|
34242
|
-
"Run the handler locally against the delivery contract (POST {input} to :8787)."
|
|
34243
|
-
).option("--dir <path>", "Handler directory (default: cwd)").option("--port <port>", "Port (default 8787)", "8787").option("--input <json>", "One-shot: invoke once with this input and exit").action(async (opts) => {
|
|
34244
|
-
try {
|
|
34245
|
-
const dir = resolve2(opts.dir ?? ".");
|
|
34246
|
-
const manifest = readManifest(dir);
|
|
34247
|
-
readHandler(dir);
|
|
34248
|
-
const mod3 = await import(pathToFileURL(join8(dir, HANDLER)).href);
|
|
34249
|
-
const fn = mod3.default;
|
|
34250
|
-
if (typeof fn !== "function") {
|
|
34251
|
-
throw new Error(`${HANDLER} must default-export a function.`);
|
|
34252
|
-
}
|
|
34253
|
-
const ctx = {
|
|
34254
|
-
agent: "0xLOCAL",
|
|
34255
|
-
slug: manifest.slug,
|
|
34256
|
-
buyer: "0xBUYER"
|
|
34257
|
-
};
|
|
34258
|
-
if (opts.input !== void 0) {
|
|
34259
|
-
let input = opts.input;
|
|
34260
|
-
try {
|
|
34261
|
-
input = JSON.parse(opts.input);
|
|
34262
|
-
} catch {
|
|
34263
|
-
}
|
|
34264
|
-
const out = await fn(input, ctx);
|
|
34265
|
-
printJson({ ok: true, output: out });
|
|
34266
|
-
return;
|
|
34267
|
-
}
|
|
34268
|
-
const port = Number(opts.port) || 8787;
|
|
34269
|
-
const server = createServer(async (req, res) => {
|
|
34270
|
-
const chunks = [];
|
|
34271
|
-
for await (const c of req) {
|
|
34272
|
-
chunks.push(c);
|
|
34273
|
-
}
|
|
34274
|
-
const raw = Buffer.concat(chunks).toString("utf8");
|
|
34275
|
-
let input = raw;
|
|
34276
|
-
try {
|
|
34277
|
-
input = raw ? JSON.parse(raw) : {};
|
|
34278
|
-
} catch {
|
|
34279
|
-
}
|
|
34280
|
-
try {
|
|
34281
|
-
const out = await fn(input, ctx);
|
|
34282
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
34283
|
-
res.end(JSON.stringify(out));
|
|
34284
|
-
} catch (e) {
|
|
34285
|
-
res.writeHead(500, { "content-type": "application/json" });
|
|
34286
|
-
res.end(
|
|
34287
|
-
JSON.stringify({ error: e instanceof Error ? e.message : String(e) })
|
|
34288
|
-
);
|
|
34289
|
-
}
|
|
34290
|
-
});
|
|
34291
|
-
server.listen(port, () => {
|
|
34292
|
-
printBlank();
|
|
34293
|
-
printSuccess(`Local handler running \u2014 slug "${manifest.slug}"`);
|
|
34294
|
-
printLine(` curl -X POST http://localhost:${port} -d '{"hello":"world"}'`);
|
|
34295
|
-
printLine(" Ctrl-C to stop.");
|
|
34296
|
-
printBlank();
|
|
34297
|
-
});
|
|
34298
|
-
} catch (error) {
|
|
34299
|
-
handleError(error);
|
|
34300
|
-
}
|
|
34301
|
-
});
|
|
34302
|
-
group.command("deploy").description(
|
|
34303
|
-
"Deploy the handler to t2000 compute + list the SKU in your catalog. Sponsored listing, per-call earnings to your wallet."
|
|
34304
|
-
).option("--dir <path>", "Handler directory (default: cwd)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE4})`).option("--gateway <url>", `Gateway URL (default ${DEFAULT_GATEWAY2})`).action(
|
|
34305
|
-
async (opts) => {
|
|
34306
|
-
try {
|
|
34307
|
-
const dir = resolve2(opts.dir ?? ".");
|
|
34308
|
-
const base = opts.api ?? DEFAULT_API_BASE4;
|
|
34309
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY2;
|
|
34310
|
-
const manifest = readManifest(dir);
|
|
34311
|
-
const script = readHandler(dir);
|
|
34312
|
-
const scriptB64 = Buffer.from(script, "utf8").toString("base64");
|
|
34313
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
34314
|
-
const address = agent.address();
|
|
34315
|
-
const ts = Date.now();
|
|
34316
|
-
const bodyHash = createHash3("sha256").update(`${manifest.slug}|${scriptB64}`).digest("hex");
|
|
34317
|
-
const message = new TextEncoder().encode(
|
|
34318
|
-
`t2000-serve:${ts}:${bodyHash}`
|
|
34319
|
-
);
|
|
34320
|
-
const { signature } = await agent.keypair.signPersonalMessage(message);
|
|
34321
|
-
const deployed = await fetchJson4(`${gateway}/serve/deploy`, {
|
|
34322
|
-
method: "POST",
|
|
34323
|
-
body: {
|
|
34324
|
-
address,
|
|
34325
|
-
slug: manifest.slug,
|
|
34326
|
-
script: scriptB64,
|
|
34327
|
-
timestamp: ts,
|
|
34328
|
-
signature
|
|
34329
|
-
}
|
|
34330
|
-
});
|
|
34331
|
-
const catalog = await getCatalog(base, address);
|
|
34332
|
-
const entry = {
|
|
34333
|
-
slug: manifest.slug,
|
|
34334
|
-
title: manifest.title,
|
|
34335
|
-
description: manifest.description,
|
|
34336
|
-
priceUsdc: String(manifest.price),
|
|
34337
|
-
input: manifest.input ?? null,
|
|
34338
|
-
endpoint: null,
|
|
34339
|
-
method: "POST",
|
|
34340
|
-
active: true
|
|
34341
|
-
};
|
|
34342
|
-
const next = [
|
|
34343
|
-
...catalog.filter((s) => s.slug !== manifest.slug),
|
|
34344
|
-
entry
|
|
34345
|
-
];
|
|
34346
|
-
await putCatalog({ base, keyPath: opts.key, services: next });
|
|
34347
|
-
const buyUrl = String(
|
|
34348
|
-
deployed.buyUrl ?? `https://x402.t2000.ai/commerce/pay/${address}/${manifest.slug}`
|
|
34349
|
-
);
|
|
34350
|
-
if (isJsonMode()) {
|
|
34351
|
-
printJson({
|
|
34352
|
-
address,
|
|
34353
|
-
slug: manifest.slug,
|
|
34354
|
-
sizeBytes: deployed.sizeBytes,
|
|
34355
|
-
listed: true,
|
|
34356
|
-
buyUrl
|
|
34357
|
-
});
|
|
34358
|
-
return;
|
|
34359
|
-
}
|
|
34360
|
-
printBlank();
|
|
34361
|
-
printSuccess(`Deployed "${manifest.slug}" to t2000 compute`);
|
|
34362
|
-
printKeyValue("Buy URL", buyUrl);
|
|
34363
|
-
printKeyValue("Price", `$${manifest.price} per call`);
|
|
34364
|
-
printKeyValue("Store", `https://agents.t2000.ai/${address}`);
|
|
34365
|
-
printBlank();
|
|
34366
|
-
printLine(" t2 agent serve logs # invocations");
|
|
34367
|
-
printLine(" t2 agent serve undeploy --slug " + manifest.slug);
|
|
34368
|
-
printBlank();
|
|
34369
|
-
} catch (error) {
|
|
34370
|
-
handleError(error);
|
|
34371
|
-
}
|
|
34372
|
-
}
|
|
34373
|
-
);
|
|
34374
|
-
const secrets = group.command("secrets").description(
|
|
34375
|
-
"This agent's handler secrets vault \u2014 encrypted at t2000, injected as ctx.secrets on paid deliveries only. Values are write-only."
|
|
34376
|
-
);
|
|
34377
|
-
secrets.command("set").argument("<pairs...>", "NAME=value pairs (e.g. UPSTREAM_KEY=sk-\u2026)").description("Set (or overwrite) vault secrets. NAME: A-Z, 0-9, _.").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--gateway <url>", `Gateway URL (default ${DEFAULT_GATEWAY2})`).action(
|
|
34378
|
-
async (pairs, opts) => {
|
|
34379
|
-
try {
|
|
34380
|
-
const updates = {};
|
|
34381
|
-
for (const pair of pairs) {
|
|
34382
|
-
const eq = pair.indexOf("=");
|
|
34383
|
-
if (eq <= 0) {
|
|
34384
|
-
throw new Error(`"${pair}" is not NAME=value.`);
|
|
34385
|
-
}
|
|
34386
|
-
updates[pair.slice(0, eq).trim()] = pair.slice(eq + 1);
|
|
34387
|
-
}
|
|
34388
|
-
const res = await postSecrets(opts, "set", updates);
|
|
34389
|
-
if (isJsonMode()) {
|
|
34390
|
-
printJson({ names: res.names });
|
|
34391
|
-
return;
|
|
34392
|
-
}
|
|
34393
|
-
printBlank();
|
|
34394
|
-
printSuccess("Vault updated");
|
|
34395
|
-
printKeyValue("Secrets", res.names.join(", ") || "(none)");
|
|
34396
|
-
printInfo("Handlers read them as ctx.secrets.NAME on paid deliveries.");
|
|
34397
|
-
printBlank();
|
|
34398
|
-
} catch (error) {
|
|
34399
|
-
handleError(error);
|
|
34400
|
-
}
|
|
34401
|
-
}
|
|
34402
|
-
);
|
|
34403
|
-
secrets.command("unset").argument("<names...>", "Secret names to delete").description("Delete vault secrets.").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--gateway <url>", `Gateway URL (default ${DEFAULT_GATEWAY2})`).action(
|
|
34404
|
-
async (names, opts) => {
|
|
34405
|
-
try {
|
|
34406
|
-
const updates = {};
|
|
34407
|
-
for (const n of names) {
|
|
34408
|
-
updates[n.trim()] = "";
|
|
34409
|
-
}
|
|
34410
|
-
const res = await postSecrets(opts, "set", updates);
|
|
34411
|
-
if (isJsonMode()) {
|
|
34412
|
-
printJson({ names: res.names });
|
|
34413
|
-
return;
|
|
34414
|
-
}
|
|
34415
|
-
printBlank();
|
|
34416
|
-
printSuccess("Vault updated");
|
|
34417
|
-
printKeyValue("Secrets", res.names.join(", ") || "(none)");
|
|
34418
|
-
printBlank();
|
|
34419
|
-
} catch (error) {
|
|
34420
|
-
handleError(error);
|
|
34421
|
-
}
|
|
34422
|
-
}
|
|
34423
|
-
);
|
|
34424
|
-
secrets.command("list").description("List vault secret NAMES (values never leave the gateway).").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--gateway <url>", `Gateway URL (default ${DEFAULT_GATEWAY2})`).action(async (opts) => {
|
|
34425
|
-
try {
|
|
34426
|
-
const res = await postSecrets(opts, "list");
|
|
34427
|
-
if (isJsonMode()) {
|
|
34428
|
-
printJson({ names: res.names });
|
|
34429
|
-
return;
|
|
34430
|
-
}
|
|
34431
|
-
const names = res.names;
|
|
34432
|
-
printBlank();
|
|
34433
|
-
if (names.length === 0) {
|
|
34434
|
-
printLine("Vault is empty. Set one: t2 agent serve secrets set NAME=value");
|
|
34435
|
-
} else {
|
|
34436
|
-
for (const n of names) {
|
|
34437
|
-
printLine(` ${n}`);
|
|
34438
|
-
}
|
|
34439
|
-
}
|
|
34440
|
-
printBlank();
|
|
34441
|
-
} catch (error) {
|
|
34442
|
-
handleError(error);
|
|
34443
|
-
}
|
|
34444
|
-
});
|
|
34445
|
-
async function postSecrets(opts, op, updates) {
|
|
34446
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY2;
|
|
34447
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
34448
|
-
const address = agent.address();
|
|
34449
|
-
const ts = Date.now();
|
|
34450
|
-
const payload = op === "list" ? "list" : JSON.stringify(
|
|
34451
|
-
Object.fromEntries(
|
|
34452
|
-
Object.entries(updates ?? {}).sort(
|
|
34453
|
-
([a], [b]) => a.localeCompare(b)
|
|
34454
|
-
)
|
|
34455
|
-
)
|
|
34456
|
-
);
|
|
34457
|
-
const bodyHash = createHash3("sha256").update(payload).digest("hex");
|
|
34458
|
-
const message = new TextEncoder().encode(
|
|
34459
|
-
`t2000-serve-secrets:${ts}:${bodyHash}`
|
|
34460
|
-
);
|
|
34461
|
-
const { signature } = await agent.keypair.signPersonalMessage(message);
|
|
34462
|
-
return await fetchJson4(`${gateway}/serve/secrets`, {
|
|
34463
|
-
method: "POST",
|
|
34464
|
-
body: {
|
|
34465
|
-
address,
|
|
34466
|
-
op,
|
|
34467
|
-
...op === "set" ? { updates } : {},
|
|
34468
|
-
timestamp: ts,
|
|
34469
|
-
signature
|
|
34470
|
-
}
|
|
34471
|
-
});
|
|
34472
|
-
}
|
|
34473
|
-
group.command("status").description("Deployed handlers + invocation stats.").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--gateway <url>", `Gateway URL (default ${DEFAULT_GATEWAY2})`).action(async (opts) => {
|
|
34474
|
-
try {
|
|
34475
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY2;
|
|
34476
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
34477
|
-
const address = agent.address();
|
|
34478
|
-
const res = await fetchJson4(
|
|
34479
|
-
`${gateway}/serve/status?address=${encodeURIComponent(address)}`
|
|
34480
|
-
);
|
|
34481
|
-
if (isJsonMode()) {
|
|
34482
|
-
printJson(res);
|
|
34483
|
-
return;
|
|
34484
|
-
}
|
|
34485
|
-
const handlers = res.handlers ?? [];
|
|
34486
|
-
printBlank();
|
|
34487
|
-
if (handlers.length === 0) {
|
|
34488
|
-
printLine("No hosted handlers. Start one: t2 agent serve init");
|
|
34489
|
-
printBlank();
|
|
34490
|
-
return;
|
|
34491
|
-
}
|
|
34492
|
-
for (const h of handlers) {
|
|
34493
|
-
printLine(
|
|
34494
|
-
` ${h.slug} ${h.active ? "live" : "undeployed"} \xB7 ${(h.sizeBytes / 1024).toFixed(1)} KB \xB7 ${h.invocations} invocation${h.invocations === 1 ? "" : "s"}${h.lastInvocation ? ` \xB7 last ${h.lastInvocation.status} @ ${h.lastInvocation.at}` : ""}`
|
|
34495
|
-
);
|
|
34496
|
-
}
|
|
34497
|
-
printBlank();
|
|
34498
|
-
} catch (error) {
|
|
34499
|
-
handleError(error);
|
|
34500
|
-
}
|
|
34501
|
-
});
|
|
34502
|
-
group.command("logs").description("Recent invocations (status \xB7 duration \xB7 error).").option("--slug <slug>", "Filter to one handler").option("--limit <n>", "Rows (default 50)", "50").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--gateway <url>", `Gateway URL (default ${DEFAULT_GATEWAY2})`).action(
|
|
34503
|
-
async (opts) => {
|
|
34504
|
-
try {
|
|
34505
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY2;
|
|
34506
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
34507
|
-
const address = agent.address();
|
|
34508
|
-
const params = new URLSearchParams({
|
|
34509
|
-
address,
|
|
34510
|
-
limit: opts.limit
|
|
34511
|
-
});
|
|
34512
|
-
if (opts.slug) {
|
|
34513
|
-
params.set("slug", opts.slug);
|
|
34514
|
-
}
|
|
34515
|
-
const res = await fetchJson4(`${gateway}/serve/logs?${params}`);
|
|
34516
|
-
if (isJsonMode()) {
|
|
34517
|
-
printJson(res);
|
|
34518
|
-
return;
|
|
34519
|
-
}
|
|
34520
|
-
const rows = res.invocations ?? [];
|
|
34521
|
-
printBlank();
|
|
34522
|
-
if (rows.length === 0) {
|
|
34523
|
-
printLine("No invocations yet.");
|
|
34524
|
-
printBlank();
|
|
34525
|
-
return;
|
|
34526
|
-
}
|
|
34527
|
-
for (const r of rows) {
|
|
34528
|
-
printLine(
|
|
34529
|
-
` ${r.at} ${r.slug} ${r.status} ${r.durationMs}ms${r.error ? ` ${r.error}` : ""}`
|
|
34530
|
-
);
|
|
34531
|
-
}
|
|
34532
|
-
printBlank();
|
|
34533
|
-
} catch (error) {
|
|
34534
|
-
handleError(error);
|
|
34535
|
-
}
|
|
34536
|
-
}
|
|
34537
|
-
);
|
|
34538
|
-
group.command("undeploy").description(
|
|
34539
|
-
"Remove a hosted handler (the SKU stays in the catalog \u2014 deactivate it with `t2 agent services update --slug \u2026 --inactive` if wanted)."
|
|
34540
|
-
).requiredOption("--slug <slug>", "Handler slug to remove").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--gateway <url>", `Gateway URL (default ${DEFAULT_GATEWAY2})`).action(
|
|
34541
|
-
async (opts) => {
|
|
34542
|
-
try {
|
|
34543
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY2;
|
|
34544
|
-
const slug = opts.slug.trim().toLowerCase();
|
|
34545
|
-
if (!SLUG_RE2.test(slug)) {
|
|
34546
|
-
throw new Error("--slug must match [a-z0-9-], 2-40 chars.");
|
|
34547
|
-
}
|
|
34548
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
34549
|
-
const address = agent.address();
|
|
34550
|
-
const ts = Date.now();
|
|
34551
|
-
const message = new TextEncoder().encode(
|
|
34552
|
-
`t2000-serve-remove:${ts}:${slug}`
|
|
34553
|
-
);
|
|
34554
|
-
const { signature } = await agent.keypair.signPersonalMessage(message);
|
|
34555
|
-
await fetchJson4(`${gateway}/serve/undeploy`, {
|
|
34556
|
-
method: "POST",
|
|
34557
|
-
body: { address, slug, timestamp: ts, signature }
|
|
33715
|
+
printJson({
|
|
33716
|
+
address,
|
|
33717
|
+
walletCreated: created,
|
|
33718
|
+
registered: true,
|
|
33719
|
+
alreadyRegistered: reg.alreadyRegistered,
|
|
33720
|
+
name,
|
|
33721
|
+
...category ? { category } : {},
|
|
33722
|
+
...ownerProposed ? { ownerProposed: owner } : {},
|
|
33723
|
+
storeUrl,
|
|
33724
|
+
keyPath: opts.key ?? "~/.t2000/wallet.key"
|
|
34558
33725
|
});
|
|
34559
|
-
|
|
34560
|
-
|
|
34561
|
-
|
|
34562
|
-
|
|
34563
|
-
|
|
34564
|
-
|
|
34565
|
-
|
|
34566
|
-
|
|
33726
|
+
return;
|
|
33727
|
+
}
|
|
33728
|
+
printBlank();
|
|
33729
|
+
printSuccess(`${name} is live`);
|
|
33730
|
+
printKeyValue("Address", address);
|
|
33731
|
+
printKeyValue("Store", storeUrl);
|
|
33732
|
+
printKeyValue(
|
|
33733
|
+
"Wallet",
|
|
33734
|
+
created ? `created at ${opts.key ?? "~/.t2000/wallet.key"}` : `reused ${opts.key ?? "~/.t2000/wallet.key"}`
|
|
33735
|
+
);
|
|
33736
|
+
if (ownerProposed) {
|
|
33737
|
+
printKeyValue(
|
|
33738
|
+
"Owner",
|
|
33739
|
+
`proposed ${owner} \u2014 confirm at ${STORE_BASE}/manage/agents`
|
|
34567
33740
|
);
|
|
34568
|
-
printBlank();
|
|
34569
|
-
} catch (error) {
|
|
34570
|
-
handleError(error);
|
|
34571
33741
|
}
|
|
33742
|
+
printBlank();
|
|
33743
|
+
printLine("Next:");
|
|
33744
|
+
printLine(" t2 fund # add USDC (QR / card link)");
|
|
33745
|
+
printLine(" t2 agent service ... # declare a paid endpoint (sell per call)");
|
|
33746
|
+
printBlank();
|
|
33747
|
+
} catch (error) {
|
|
33748
|
+
handleError(error);
|
|
34572
33749
|
}
|
|
34573
|
-
);
|
|
33750
|
+
});
|
|
34574
33751
|
}
|
|
34575
33752
|
|
|
34576
33753
|
// src/commands/agent/index.ts
|
|
34577
|
-
var
|
|
34578
|
-
var
|
|
34579
|
-
var DEFAULT_RAIL = process.env.T2000_RAIL_URL ?? "https://x402.t2000.ai";
|
|
33754
|
+
var DEFAULT_API_BASE4 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
|
|
33755
|
+
var DEFAULT_GATEWAY = process.env.T2000_GATEWAY_URL ?? "https://mpp.t2000.ai";
|
|
34580
33756
|
var AGENT_CATEGORIES2 = [
|
|
34581
33757
|
"ai-models",
|
|
34582
33758
|
"data-feeds",
|
|
@@ -34598,13 +33774,6 @@ function normalizeCategory(input) {
|
|
|
34598
33774
|
}
|
|
34599
33775
|
return c;
|
|
34600
33776
|
}
|
|
34601
|
-
function collectHeader(value, previous) {
|
|
34602
|
-
const [key, ...rest] = value.split("=");
|
|
34603
|
-
if (key && rest.length > 0) {
|
|
34604
|
-
previous[key.trim()] = rest.join("=").trim();
|
|
34605
|
-
}
|
|
34606
|
-
return previous;
|
|
34607
|
-
}
|
|
34608
33777
|
function normalizeTopupAsset(input) {
|
|
34609
33778
|
return input?.toLowerCase() === "usdsui" ? "USDsui" : "USDC";
|
|
34610
33779
|
}
|
|
@@ -34614,19 +33783,19 @@ async function fundCredit(agent, base, amountStr, assetOpt) {
|
|
|
34614
33783
|
throw new Error(`amount must be a positive number (got "${amountStr}").`);
|
|
34615
33784
|
}
|
|
34616
33785
|
const asset = normalizeTopupAsset(assetOpt);
|
|
34617
|
-
const cfg = await
|
|
33786
|
+
const cfg = await fetchJson2(`${base}/agent/topup`, { method: "GET" });
|
|
34618
33787
|
const treasury = cfg.treasury;
|
|
34619
33788
|
if (!treasury) {
|
|
34620
33789
|
throw new Error("Could not resolve the t2000 treasury address.");
|
|
34621
33790
|
}
|
|
34622
33791
|
const sent = await agent.send({ to: treasury, amount, asset });
|
|
34623
|
-
const topup = await
|
|
33792
|
+
const topup = await fetchJson2(`${base}/agent/topup`, {
|
|
34624
33793
|
method: "POST",
|
|
34625
33794
|
body: { address: agent.address(), digest: sent.tx }
|
|
34626
33795
|
});
|
|
34627
33796
|
return { amount, asset, balanceUsd: topup.balanceUsd };
|
|
34628
33797
|
}
|
|
34629
|
-
async function
|
|
33798
|
+
async function fetchJson2(url, init) {
|
|
34630
33799
|
const res = await fetch(url, {
|
|
34631
33800
|
method: init.method,
|
|
34632
33801
|
headers: init.body ? { "Content-Type": "application/json" } : void 0,
|
|
@@ -34649,20 +33818,16 @@ Subcommands:
|
|
|
34649
33818
|
$ t2 agent onboard --fund 5 Fund 5 USDC \u2192 mint an API key (ready to call)
|
|
34650
33819
|
$ t2 agent onboard --fund 5 --asset USDsui
|
|
34651
33820
|
$ t2 agent onboard Already funded \u2192 just mint a key
|
|
34652
|
-
$ t2 agent
|
|
34653
|
-
$ t2 agent services sync ./services.json Manifest IS the catalog (catalog-scale sellers)
|
|
33821
|
+
$ t2 agent pay <address> Pay another agent's declared service
|
|
34654
33822
|
`
|
|
34655
33823
|
);
|
|
34656
33824
|
registerAgentCreate(group);
|
|
34657
|
-
registerAgentServices(group, { apiBase: DEFAULT_API_BASE5 });
|
|
34658
|
-
registerAgentServe(group);
|
|
34659
|
-
registerAgentReview(group);
|
|
34660
33825
|
group.command("onboard").description(
|
|
34661
33826
|
"Buy-side setup: fund credit (gasless USDC/USDsui) + mint a Private API key. Registering an Agent ID is free and separate \u2014 `t2 init` / `t2 agent register`."
|
|
34662
|
-
).option("--fund <amount>", "Stablecoin amount to deposit as credit (omit if already funded)").option("--asset <asset>", "USDC (default) or USDsui", "USDC").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${
|
|
33827
|
+
).option("--fund <amount>", "Stablecoin amount to deposit as credit (omit if already funded)").option("--asset <asset>", "USDC (default) or USDsui", "USDC").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE4})`).action(
|
|
34663
33828
|
async (opts) => {
|
|
34664
33829
|
try {
|
|
34665
|
-
const base = opts.api ??
|
|
33830
|
+
const base = opts.api ?? DEFAULT_API_BASE4;
|
|
34666
33831
|
const agent = await withAgent({ keyPath: opts.key });
|
|
34667
33832
|
const address = agent.address();
|
|
34668
33833
|
if (opts.fund !== void 0) {
|
|
@@ -34673,7 +33838,7 @@ Subcommands:
|
|
|
34673
33838
|
);
|
|
34674
33839
|
}
|
|
34675
33840
|
}
|
|
34676
|
-
const challenge = await
|
|
33841
|
+
const challenge = await fetchJson2(`${base}/agent/challenge`, {
|
|
34677
33842
|
method: "POST",
|
|
34678
33843
|
body: { address }
|
|
34679
33844
|
});
|
|
@@ -34683,7 +33848,7 @@ Subcommands:
|
|
|
34683
33848
|
}
|
|
34684
33849
|
const message = new TextEncoder().encode(`t2000-agent-keys:${nonce}`);
|
|
34685
33850
|
const { signature } = await agent.keypair.signPersonalMessage(message);
|
|
34686
|
-
const minted = await
|
|
33851
|
+
const minted = await fetchJson2(`${base}/agent/keys`, {
|
|
34687
33852
|
method: "POST",
|
|
34688
33853
|
body: { address, nonce, signature }
|
|
34689
33854
|
});
|
|
@@ -34720,10 +33885,10 @@ Subcommands:
|
|
|
34720
33885
|
);
|
|
34721
33886
|
group.command("topup").argument("<amount>", "Stablecoin amount to deposit as credit").description(
|
|
34722
33887
|
"Top up this wallet's t2000 credit with gasless USDC/USDsui (no new key). The 'never runs dry' primitive \u2014 call it on a 402 or a schedule."
|
|
34723
|
-
).option("--asset <asset>", "USDC (default) or USDsui", "USDC").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${
|
|
33888
|
+
).option("--asset <asset>", "USDC (default) or USDsui", "USDC").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE4})`).action(
|
|
34724
33889
|
async (amount, opts) => {
|
|
34725
33890
|
try {
|
|
34726
|
-
const base = opts.api ??
|
|
33891
|
+
const base = opts.api ?? DEFAULT_API_BASE4;
|
|
34727
33892
|
const agent = await withAgent({ keyPath: opts.key });
|
|
34728
33893
|
const funded = await fundCredit(agent, base, amount, opts.asset);
|
|
34729
33894
|
if (isJsonMode()) {
|
|
@@ -34747,9 +33912,9 @@ Subcommands:
|
|
|
34747
33912
|
);
|
|
34748
33913
|
group.command("register").description(
|
|
34749
33914
|
"Register this wallet on-chain as an Agent ID (sponsored, gasless). Idempotent \u2014 safe to re-run."
|
|
34750
|
-
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${
|
|
33915
|
+
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE4})`).action(async (opts) => {
|
|
34751
33916
|
try {
|
|
34752
|
-
const base = opts.api ??
|
|
33917
|
+
const base = opts.api ?? DEFAULT_API_BASE4;
|
|
34753
33918
|
const agent = await withAgent({ keyPath: opts.key });
|
|
34754
33919
|
const address = agent.address();
|
|
34755
33920
|
const reg = await registerWallet({ keypair: agent.keypair, address, base });
|
|
@@ -34777,9 +33942,9 @@ Subcommands:
|
|
|
34777
33942
|
});
|
|
34778
33943
|
group.command("link").argument("<owner>", "The owner's Sui address (Passport) to propose").description(
|
|
34779
33944
|
"Propose an owner for this agent (two-sided \u2014 the owner must then confirm). Sponsored, gasless."
|
|
34780
|
-
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${
|
|
33945
|
+
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE4})`).action(async (owner, opts) => {
|
|
34781
33946
|
try {
|
|
34782
|
-
const base = opts.api ??
|
|
33947
|
+
const base = opts.api ?? DEFAULT_API_BASE4;
|
|
34783
33948
|
const agent = await withAgent({ keyPath: opts.key });
|
|
34784
33949
|
const address = agent.address();
|
|
34785
33950
|
if (!isValidSuiAddress(owner)) {
|
|
@@ -34816,9 +33981,9 @@ Subcommands:
|
|
|
34816
33981
|
});
|
|
34817
33982
|
group.command("confirm").argument("<agent>", "The agent Sui address to confirm ownership of").description(
|
|
34818
33983
|
"Confirm ownership of an agent that proposed you as its owner. Sponsored, gasless."
|
|
34819
|
-
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${
|
|
33984
|
+
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE4})`).action(async (agentAddress, opts) => {
|
|
34820
33985
|
try {
|
|
34821
|
-
const base = opts.api ??
|
|
33986
|
+
const base = opts.api ?? DEFAULT_API_BASE4;
|
|
34822
33987
|
const owner = await withAgent({ keyPath: opts.key });
|
|
34823
33988
|
const address = owner.address();
|
|
34824
33989
|
const { digest } = await runSponsoredTx({
|
|
@@ -34842,9 +34007,9 @@ Subcommands:
|
|
|
34842
34007
|
});
|
|
34843
34008
|
group.command("unlink").argument("<agent>", "The agent Sui address to renounce ownership of").description(
|
|
34844
34009
|
"Renounce ownership of an agent you own \u2014 the record returns to autonomous (public, on-chain). Sponsored, gasless. Re-link = the agent proposes again."
|
|
34845
|
-
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${
|
|
34010
|
+
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE4})`).action(async (agentAddress, opts) => {
|
|
34846
34011
|
try {
|
|
34847
|
-
const base = opts.api ??
|
|
34012
|
+
const base = opts.api ?? DEFAULT_API_BASE4;
|
|
34848
34013
|
const owner = await withAgent({ keyPath: opts.key });
|
|
34849
34014
|
const address = owner.address();
|
|
34850
34015
|
const { digest } = await runSponsoredTx({
|
|
@@ -34869,7 +34034,7 @@ Subcommands:
|
|
|
34869
34034
|
});
|
|
34870
34035
|
group.command("profile").description(
|
|
34871
34036
|
"Set this agent's public profile (name \xB7 image \xB7 description \xB7 links). Signed, no gas \u2014 shows in the directory."
|
|
34872
|
-
).option("--name <name>", "Display name").option("--image <url>", "Image URL (https)").option("--description <text>", "Short description").option("--website <url>", "Website link (https)").option("--twitter <url>", "X / Twitter link (https)").option("--github <url>", "GitHub link (https)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${
|
|
34037
|
+
).option("--name <name>", "Display name").option("--image <url>", "Image URL (https)").option("--description <text>", "Short description").option("--website <url>", "Website link (https)").option("--twitter <url>", "X / Twitter link (https)").option("--github <url>", "GitHub link (https)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE4})`).action(
|
|
34873
34038
|
async (opts) => {
|
|
34874
34039
|
try {
|
|
34875
34040
|
if (!(opts.name || opts.image || opts.description || opts.website || opts.twitter || opts.github)) {
|
|
@@ -34877,10 +34042,10 @@ Subcommands:
|
|
|
34877
34042
|
"Provide at least one of --name, --image, --description, --website, --twitter, --github."
|
|
34878
34043
|
);
|
|
34879
34044
|
}
|
|
34880
|
-
const base = opts.api ??
|
|
34045
|
+
const base = opts.api ?? DEFAULT_API_BASE4;
|
|
34881
34046
|
const agent = await withAgent({ keyPath: opts.key });
|
|
34882
34047
|
const address = agent.address();
|
|
34883
|
-
const challenge = await
|
|
34048
|
+
const challenge = await fetchJson2(`${base}/agent/challenge`, {
|
|
34884
34049
|
method: "POST",
|
|
34885
34050
|
body: { address }
|
|
34886
34051
|
});
|
|
@@ -34890,7 +34055,7 @@ Subcommands:
|
|
|
34890
34055
|
}
|
|
34891
34056
|
const message = new TextEncoder().encode(`t2000-agent-profile:${nonce}`);
|
|
34892
34057
|
const { signature } = await agent.keypair.signPersonalMessage(message);
|
|
34893
|
-
await
|
|
34058
|
+
await fetchJson2(`${base}/agent/profile`, {
|
|
34894
34059
|
method: "POST",
|
|
34895
34060
|
body: {
|
|
34896
34061
|
address,
|
|
@@ -34924,7 +34089,7 @@ Subcommands:
|
|
|
34924
34089
|
).option("--price <usdc>", "Price per call in USDC (e.g. 0.02) \u2014 buyers pay this").option(
|
|
34925
34090
|
"--category <category>",
|
|
34926
34091
|
`Storefront category: ${AGENT_CATEGORIES2.join(" | ")}`
|
|
34927
|
-
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${
|
|
34092
|
+
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE4})`).action(
|
|
34928
34093
|
async (opts) => {
|
|
34929
34094
|
try {
|
|
34930
34095
|
if (opts.mcpEndpoint === void 0 && opts.paymentMethods === void 0 && opts.price === void 0 && opts.category === void 0) {
|
|
@@ -34939,7 +34104,7 @@ Subcommands:
|
|
|
34939
34104
|
}
|
|
34940
34105
|
}
|
|
34941
34106
|
const category = normalizeCategory(opts.category);
|
|
34942
|
-
const base = opts.api ??
|
|
34107
|
+
const base = opts.api ?? DEFAULT_API_BASE4;
|
|
34943
34108
|
const agent = await withAgent({ keyPath: opts.key });
|
|
34944
34109
|
const address = agent.address();
|
|
34945
34110
|
const prepareBody = { address };
|
|
@@ -34987,143 +34152,11 @@ Subcommands:
|
|
|
34987
34152
|
}
|
|
34988
34153
|
}
|
|
34989
34154
|
);
|
|
34990
|
-
group.command("deploy").description(
|
|
34991
|
-
"Deploy a paid service by wrapping any HTTP API \u2014 t2000 hosts the proxy (your key stays server-side, encrypted), lists it, and settles payments. No server needed. Use --remove to take it down. [Agent Commerce]"
|
|
34992
|
-
).option("--upstream <url>", "The upstream API URL to wrap (https)").option(
|
|
34993
|
-
"--header <k=v>",
|
|
34994
|
-
"Header to inject into upstream calls (repeatable; e.g. your API key)",
|
|
34995
|
-
collectHeader,
|
|
34996
|
-
{}
|
|
34997
|
-
).option("--method <method>", "Upstream method: GET or POST (default POST)").option("--price <usdc>", "Price per call in USDC (e.g. 0.02)").option(
|
|
34998
|
-
"--category <category>",
|
|
34999
|
-
`Storefront category: ${AGENT_CATEGORIES2.join(" | ")}`
|
|
35000
|
-
).option("--remove", "Take down the deployed service").option(
|
|
35001
|
-
"--service <slug>",
|
|
35002
|
-
"Catalog service slug \u2014 wrap config for ONE SKU (Store v2; omit = the default service)"
|
|
35003
|
-
).option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY3})`).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(
|
|
35004
|
-
async (opts) => {
|
|
35005
|
-
try {
|
|
35006
|
-
const base = opts.api ?? DEFAULT_API_BASE5;
|
|
35007
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY3;
|
|
35008
|
-
const category = normalizeCategory(opts.category);
|
|
35009
|
-
const slug = opts.service?.trim().toLowerCase() || void 0;
|
|
35010
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
35011
|
-
const address = agent.address();
|
|
35012
|
-
if (opts.remove) {
|
|
35013
|
-
const ts2 = Date.now();
|
|
35014
|
-
const msg2 = `t2000-deploy-remove:${ts2}${slug ? `:${slug}` : ""}`;
|
|
35015
|
-
const { signature: signature2 } = await agent.keypair.signPersonalMessage(
|
|
35016
|
-
new TextEncoder().encode(msg2)
|
|
35017
|
-
);
|
|
35018
|
-
await fetchJson5(`${gateway}/deploy/config`, {
|
|
35019
|
-
method: "DELETE",
|
|
35020
|
-
body: { address, timestamp: ts2, signature: signature2, ...slug ? { slug } : {} }
|
|
35021
|
-
});
|
|
35022
|
-
if (!slug) {
|
|
35023
|
-
await runSponsoredTx({
|
|
35024
|
-
keypair: agent.keypair,
|
|
35025
|
-
actor: address,
|
|
35026
|
-
prepareUrl: `${base}/agent/service/prepare`,
|
|
35027
|
-
prepareBody: { address, mcpEndpoint: "" },
|
|
35028
|
-
submitUrl: `${base}/agent/service/submit`
|
|
35029
|
-
}).catch(() => void 0);
|
|
35030
|
-
}
|
|
35031
|
-
if (isJsonMode()) {
|
|
35032
|
-
printJson({ address, removed: true });
|
|
35033
|
-
return;
|
|
35034
|
-
}
|
|
35035
|
-
printBlank();
|
|
35036
|
-
printSuccess("Service taken down.");
|
|
35037
|
-
printBlank();
|
|
35038
|
-
return;
|
|
35039
|
-
}
|
|
35040
|
-
if (!(opts.upstream && opts.price)) {
|
|
35041
|
-
throw new Error("Both --upstream and --price are required (or use --remove).");
|
|
35042
|
-
}
|
|
35043
|
-
const price = Number.parseFloat(opts.price);
|
|
35044
|
-
if (Number.isNaN(price) || price <= 0) {
|
|
35045
|
-
throw new Error(`--price must be a positive number (got "${opts.price}").`);
|
|
35046
|
-
}
|
|
35047
|
-
const method = (opts.method ?? "POST").toUpperCase() === "GET" ? "GET" : "POST";
|
|
35048
|
-
const headers = opts.header ?? {};
|
|
35049
|
-
const ts = Date.now();
|
|
35050
|
-
const bodyHash = createHash4("sha256").update(
|
|
35051
|
-
`${opts.upstream}|${method}|${JSON.stringify(headers)}${slug ? `|${slug}` : ""}`
|
|
35052
|
-
).digest("hex");
|
|
35053
|
-
const msg = `t2000-deploy:${ts}:${bodyHash}`;
|
|
35054
|
-
const { signature } = await agent.keypair.signPersonalMessage(
|
|
35055
|
-
new TextEncoder().encode(msg)
|
|
35056
|
-
);
|
|
35057
|
-
await fetchJson5(`${gateway}/deploy/config`, {
|
|
35058
|
-
method: "POST",
|
|
35059
|
-
body: {
|
|
35060
|
-
address,
|
|
35061
|
-
timestamp: ts,
|
|
35062
|
-
signature,
|
|
35063
|
-
upstreamUrl: opts.upstream,
|
|
35064
|
-
method,
|
|
35065
|
-
headers,
|
|
35066
|
-
...slug ? { slug } : {}
|
|
35067
|
-
}
|
|
35068
|
-
});
|
|
35069
|
-
if (slug) {
|
|
35070
|
-
if (isJsonMode()) {
|
|
35071
|
-
printJson({ address, slug, upstream: opts.upstream, price });
|
|
35072
|
-
return;
|
|
35073
|
-
}
|
|
35074
|
-
printBlank();
|
|
35075
|
-
printSuccess(`Wrap config stored for service "${slug}".`);
|
|
35076
|
-
printKeyValue("Wraps", opts.upstream);
|
|
35077
|
-
printKeyValue("Buy URL", `${DEFAULT_RAIL}/commerce/pay/${address}/${slug}`);
|
|
35078
|
-
printInfo(
|
|
35079
|
-
`List it in the catalog: t2 agent services add --slug ${slug} --title \u2026 --description \u2026 --price ${opts.price}`
|
|
35080
|
-
);
|
|
35081
|
-
printBlank();
|
|
35082
|
-
return;
|
|
35083
|
-
}
|
|
35084
|
-
const { digest } = await runSponsoredTx({
|
|
35085
|
-
keypair: agent.keypair,
|
|
35086
|
-
actor: address,
|
|
35087
|
-
prepareUrl: `${base}/agent/service/prepare`,
|
|
35088
|
-
prepareBody: {
|
|
35089
|
-
address,
|
|
35090
|
-
// The real, x402-callable buy endpoint (GET → 402 + requirements,
|
|
35091
|
-
// pay → collect/deliver/forward). `/deploy/<addr>` was a phantom
|
|
35092
|
-
// (no route → 404). Any x402 client can hit this URL.
|
|
35093
|
-
mcpEndpoint: `${DEFAULT_RAIL}/commerce/pay/${address}`,
|
|
35094
|
-
paymentMethods: ["x402"],
|
|
35095
|
-
priceUsdc: opts.price,
|
|
35096
|
-
...category ? { category } : {}
|
|
35097
|
-
},
|
|
35098
|
-
submitUrl: `${base}/agent/service/submit`
|
|
35099
|
-
});
|
|
35100
|
-
if (isJsonMode()) {
|
|
35101
|
-
printJson({ address, upstream: opts.upstream, price, digest });
|
|
35102
|
-
return;
|
|
35103
|
-
}
|
|
35104
|
-
printBlank();
|
|
35105
|
-
printSuccess("Service deployed \u2014 live + listed in the directory.");
|
|
35106
|
-
printKeyValue("Wraps", opts.upstream);
|
|
35107
|
-
printKeyValue("Price", `$${opts.price} USDC`);
|
|
35108
|
-
if (category) {
|
|
35109
|
-
printKeyValue("Category", category);
|
|
35110
|
-
}
|
|
35111
|
-
printKeyValue("Tx", String(digest));
|
|
35112
|
-
printInfo(`Buyers: t2 agent pay ${truncateAddress(address)}`);
|
|
35113
|
-
printBlank();
|
|
35114
|
-
} catch (error) {
|
|
35115
|
-
handleError(error);
|
|
35116
|
-
}
|
|
35117
|
-
}
|
|
35118
|
-
);
|
|
35119
34155
|
group.command("pay").argument("<seller>", "The seller agent's Sui address").description(
|
|
35120
34156
|
"Pay a seller agent for a service (gateway-mediated, USDC). t2000 collects, keeps a small fee, and forwards the rest to the seller \u2014 with a receipt. [Agent Commerce]"
|
|
35121
34157
|
).option("--amount <usdc>", "Override the price (default: the seller's declared price)").option("--data <json>", "Service input forwarded to the seller's endpoint").option("--max-price <usdc>", "Max USDC to auto-approve (default 1.00, or --amount)").option(
|
|
35122
|
-
"--service <slug>",
|
|
35123
|
-
"Catalog service slug \u2014 buys ONE SKU of the seller's catalog (Store v2)"
|
|
35124
|
-
).option(
|
|
35125
34158
|
"--gateway <url>",
|
|
35126
|
-
`Gateway base URL (default ${
|
|
34159
|
+
`Gateway base URL (default ${DEFAULT_GATEWAY})`
|
|
35127
34160
|
).option("--force", "Override spending limits for this call (see `t2 limit`)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").action(
|
|
35128
34161
|
async (seller, opts) => {
|
|
35129
34162
|
try {
|
|
@@ -35134,11 +34167,10 @@ Subcommands:
|
|
|
35134
34167
|
}
|
|
35135
34168
|
}
|
|
35136
34169
|
const maxPrice = opts.maxPrice ? Number.parseFloat(opts.maxPrice) : opts.amount ? Number.parseFloat(opts.amount) : 1;
|
|
35137
|
-
const gateway = opts.gateway ??
|
|
34170
|
+
const gateway = opts.gateway ?? DEFAULT_GATEWAY;
|
|
35138
34171
|
const agent = await withAgent({ keyPath: opts.key });
|
|
35139
34172
|
const resolvedSeller = seller.startsWith("0x") ? seller : (await agent.resolveRecipient(seller)).address;
|
|
35140
|
-
const
|
|
35141
|
-
const url = opts.amount ? `${gateway}/commerce/pay/${resolvedSeller}${slugPath}?amount=${encodeURIComponent(opts.amount)}` : `${gateway}/commerce/pay/${resolvedSeller}${slugPath}`;
|
|
34173
|
+
const url = opts.amount ? `${gateway}/commerce/pay/${resolvedSeller}?amount=${encodeURIComponent(opts.amount)}` : `${gateway}/commerce/pay/${resolvedSeller}`;
|
|
35142
34174
|
const result = await agent.pay({
|
|
35143
34175
|
url,
|
|
35144
34176
|
method: "POST",
|
|
@@ -35202,12 +34234,12 @@ Subcommands:
|
|
|
35202
34234
|
);
|
|
35203
34235
|
group.command("earnings").description(
|
|
35204
34236
|
"Your sales as a seller \u2014 count, USDC earned (net), and unique buyers, from the on-chain settlement ledger. [Agent Commerce]"
|
|
35205
|
-
).option("--gateway <url>", `Gateway base URL (default ${
|
|
34237
|
+
).option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY})`).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").action(async (opts) => {
|
|
35206
34238
|
try {
|
|
35207
|
-
const gateway = opts.gateway ??
|
|
34239
|
+
const gateway = opts.gateway ?? DEFAULT_GATEWAY;
|
|
35208
34240
|
const agent = await withAgent({ keyPath: opts.key });
|
|
35209
34241
|
const address = agent.address();
|
|
35210
|
-
const stats = await
|
|
34242
|
+
const stats = await fetchJson2(
|
|
35211
34243
|
`${gateway}/commerce/stats/${address}`,
|
|
35212
34244
|
{ method: "GET" }
|
|
35213
34245
|
);
|
|
@@ -35230,13 +34262,13 @@ Subcommands:
|
|
|
35230
34262
|
});
|
|
35231
34263
|
group.command("handle").argument("<label>", "Handle label (3\u201320 chars: lowercase a\u2013z, 0\u20139, hyphens)").description(
|
|
35232
34264
|
"Claim <label>.agent-id.sui \u2192 this wallet (custody-minted, gasless). Use --release to give it up."
|
|
35233
|
-
).option("--release", "Release (revoke) this handle instead of claiming it").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${
|
|
34265
|
+
).option("--release", "Release (revoke) this handle instead of claiming it").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE4})`).action(
|
|
35234
34266
|
async (label, opts) => {
|
|
35235
34267
|
try {
|
|
35236
|
-
const base = opts.api ??
|
|
34268
|
+
const base = opts.api ?? DEFAULT_API_BASE4;
|
|
35237
34269
|
const agent = await withAgent({ keyPath: opts.key });
|
|
35238
34270
|
const address = agent.address();
|
|
35239
|
-
const challenge = await
|
|
34271
|
+
const challenge = await fetchJson2(`${base}/agent/challenge`, {
|
|
35240
34272
|
method: "POST",
|
|
35241
34273
|
body: { address }
|
|
35242
34274
|
});
|
|
@@ -35248,7 +34280,7 @@ Subcommands:
|
|
|
35248
34280
|
const message = new TextEncoder().encode(`${action}:${nonce}:${label}`);
|
|
35249
34281
|
const { signature } = await agent.keypair.signPersonalMessage(message);
|
|
35250
34282
|
const path2 = opts.release ? "/agent/handle/release" : "/agent/handle";
|
|
35251
|
-
const res = await
|
|
34283
|
+
const res = await fetchJson2(`${base}${path2}`, {
|
|
35252
34284
|
method: "POST",
|
|
35253
34285
|
body: { address, label, nonce, signature }
|
|
35254
34286
|
});
|
|
@@ -35274,7 +34306,7 @@ Subcommands:
|
|
|
35274
34306
|
}
|
|
35275
34307
|
|
|
35276
34308
|
// src/commands/agents.ts
|
|
35277
|
-
var
|
|
34309
|
+
var DEFAULT_API_BASE5 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
|
|
35278
34310
|
async function getJson(url) {
|
|
35279
34311
|
const res = await fetch(url, { headers: { accept: "application/json" } });
|
|
35280
34312
|
if (!res.ok) {
|
|
@@ -35288,11 +34320,11 @@ function firstLine(text, max = 76) {
|
|
|
35288
34320
|
}
|
|
35289
34321
|
function registerAgents(program3) {
|
|
35290
34322
|
program3.command("agents").argument("[address]", "Show one agent\u2019s full listing (profile + reputation)").description(
|
|
35291
|
-
"
|
|
35292
|
-
).option("--category <category>", "Filter the list by
|
|
34323
|
+
"Look up the agent directory (agents.t2000.ai): priced services from the live registry, or one agent\u2019s full listing. Buy with `t2 agent pay <address>`. [Agent Commerce]"
|
|
34324
|
+
).option("--category <category>", "Filter the list by category").option("--all", "Include agents without a priced service").option("--limit <n>", "Max rows (default: all)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE5})`).action(
|
|
35293
34325
|
async (address, opts) => {
|
|
35294
34326
|
try {
|
|
35295
|
-
const base = opts.api ??
|
|
34327
|
+
const base = opts.api ?? DEFAULT_API_BASE5;
|
|
35296
34328
|
if (address) {
|
|
35297
34329
|
const profile = await getJson(`${base}/agents/${address}`);
|
|
35298
34330
|
if (isJsonMode()) {
|
|
@@ -35345,7 +34377,7 @@ function registerAgents(program3) {
|
|
|
35345
34377
|
}
|
|
35346
34378
|
printBlank();
|
|
35347
34379
|
printHeader(
|
|
35348
|
-
`Agent
|
|
34380
|
+
`Agent directory \u2014 ${agents.length} ${opts.all ? "agents" : "priced services"}${opts.category ? ` in ${opts.category}` : ""}`
|
|
35349
34381
|
);
|
|
35350
34382
|
for (const a of agents) {
|
|
35351
34383
|
const price = a.priceUsdc ? formatUsd(Number.parseFloat(a.priceUsdc)).padStart(6) : " \u2014";
|
|
@@ -35364,256 +34396,6 @@ function registerAgents(program3) {
|
|
|
35364
34396
|
);
|
|
35365
34397
|
}
|
|
35366
34398
|
|
|
35367
|
-
// src/commands/task/index.ts
|
|
35368
|
-
var DEFAULT_GATEWAY4 = process.env.T2000_GATEWAY_URL ?? "https://mpp.t2000.ai";
|
|
35369
|
-
async function getJson2(url) {
|
|
35370
|
-
const res = await fetch(url, { headers: { accept: "application/json" } });
|
|
35371
|
-
const json = await res.json().catch(() => ({}));
|
|
35372
|
-
if (!res.ok) {
|
|
35373
|
-
throw new Error(json.error ?? `Request failed (${res.status}).`);
|
|
35374
|
-
}
|
|
35375
|
-
return json;
|
|
35376
|
-
}
|
|
35377
|
-
async function postJson2(url, body2) {
|
|
35378
|
-
const res = await fetch(url, {
|
|
35379
|
-
method: "POST",
|
|
35380
|
-
headers: { "content-type": "application/json" },
|
|
35381
|
-
body: JSON.stringify(body2)
|
|
35382
|
-
});
|
|
35383
|
-
const json = await res.json().catch(() => ({}));
|
|
35384
|
-
if (!res.ok) {
|
|
35385
|
-
throw new Error(json.error ?? `Request failed (${res.status}).`);
|
|
35386
|
-
}
|
|
35387
|
-
return json;
|
|
35388
|
-
}
|
|
35389
|
-
function registerTask(program3) {
|
|
35390
|
-
const group = program3.command("task").description(
|
|
35391
|
-
"Earn from t2000 reward tasks and work (or post) community board tasks \u2014 all paid through the rail. [Agent Commerce]"
|
|
35392
|
-
);
|
|
35393
|
-
group.command("list").description("Live tasks: t2000 rewards (auto-verified) + the community board (poster-approved)").option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY4})`).action(async (opts) => {
|
|
35394
|
-
try {
|
|
35395
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY4;
|
|
35396
|
-
const [rewards, board] = await Promise.all([
|
|
35397
|
-
getJson2(`${gateway}/tasks/stats`),
|
|
35398
|
-
getJson2(`${gateway}/tasks/board`)
|
|
35399
|
-
]);
|
|
35400
|
-
if (isJsonMode()) {
|
|
35401
|
-
printJson({ rewards: rewards.tasks, board: board.tasks });
|
|
35402
|
-
return;
|
|
35403
|
-
}
|
|
35404
|
-
printBlank();
|
|
35405
|
-
printHeader("t2000 reward tasks (auto-verified, one per wallet)");
|
|
35406
|
-
for (const t of rewards.tasks) {
|
|
35407
|
-
printLine(
|
|
35408
|
-
` ${formatUsd(t.rewardNetUsd).padStart(6)} ${t.id.padEnd(20)} ${t.kind.padEnd(8)} ${t.status === "live" ? "live" : "budget spent"}`
|
|
35409
|
-
);
|
|
35410
|
-
}
|
|
35411
|
-
printBlank();
|
|
35412
|
-
printHeader(`Community board (${board.tasks.length} live, poster approves)`);
|
|
35413
|
-
if (board.tasks.length === 0) {
|
|
35414
|
-
printLine(" (none live \u2014 post one: t2 task post --help)");
|
|
35415
|
-
}
|
|
35416
|
-
for (const t of board.tasks) {
|
|
35417
|
-
const days = Math.max(0, Math.ceil((Date.parse(t.expiresAt) - Date.now()) / 864e5));
|
|
35418
|
-
printLine(
|
|
35419
|
-
` ${formatUsd(t.rewardUsd).padStart(6)} ${t.title.slice(0, 44).padEnd(44)} ${t.remainingCompletions}/${t.maxCompletions} spots \xB7 ${days}d ${t.id}`
|
|
35420
|
-
);
|
|
35421
|
-
}
|
|
35422
|
-
printBlank();
|
|
35423
|
-
printInfo("Claim a reward: t2 task claim <task> \xB7 Work the board: t2 task submit <taskId>");
|
|
35424
|
-
printBlank();
|
|
35425
|
-
} catch (error) {
|
|
35426
|
-
handleError(error);
|
|
35427
|
-
}
|
|
35428
|
-
});
|
|
35429
|
-
group.command("claim").argument("<task>", "Reward task id (e.g. buy-sui, share-a-read) \u2014 see `t2 task list`").description("Claim a t2000 reward task (verified in one request; also retries automated tasks)").option("--tx <digest>", "Swap tx digest (buy-manifest / buy-sui)").option("--post <url>", "Your X post URL (X-proof tasks)").option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY4})`).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").action(
|
|
35430
|
-
async (task, opts) => {
|
|
35431
|
-
try {
|
|
35432
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY4;
|
|
35433
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
35434
|
-
const result = await postJson2(`${gateway}/tasks/claim`, {
|
|
35435
|
-
task,
|
|
35436
|
-
address: agent.address(),
|
|
35437
|
-
...opts.tx ? { txDigest: opts.tx } : {},
|
|
35438
|
-
...opts.post ? { postUrl: opts.post } : {}
|
|
35439
|
-
});
|
|
35440
|
-
if (isJsonMode()) {
|
|
35441
|
-
printJson(result);
|
|
35442
|
-
return;
|
|
35443
|
-
}
|
|
35444
|
-
printBlank();
|
|
35445
|
-
if (result.paid) {
|
|
35446
|
-
printSuccess(`Paid ${formatUsd(result.netUsd ?? 0)} to your agent.`);
|
|
35447
|
-
if (result.suiscan) {
|
|
35448
|
-
printKeyValue("Receipt", result.suiscan);
|
|
35449
|
-
}
|
|
35450
|
-
} else {
|
|
35451
|
-
printWarning(result.note ?? "Not paid.");
|
|
35452
|
-
}
|
|
35453
|
-
printBlank();
|
|
35454
|
-
} catch (error) {
|
|
35455
|
-
handleError(error);
|
|
35456
|
-
}
|
|
35457
|
-
}
|
|
35458
|
-
);
|
|
35459
|
-
group.command("post").description(
|
|
35460
|
-
"Post a community task \u2014 pays the FULL budget (reward \xD7 completions) into escrow via x402; auto-moderated at post time. SAVE the returned manageKey."
|
|
35461
|
-
).requiredOption("--title <text>", "What needs doing (8+ chars)").requiredOption("--description <text>", "Exactly what the worker must deliver + what proof (30+ chars)").requiredOption("--reward <usdc>", "Reward per approved completion ($0.01\u2013$50)").option("--completions <n>", "Max completions (default 1)", "1").option("--expiry-days <n>", "Days until unspent budget auto-refunds (default 7)", "7").option("--category <category>", "research | data | marketing | dev | creative | other", "other").option("--notify-email <email>", "Email me when submissions arrive + when the refund settles (per-task, one-click stop in every mail)").option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY4})`).option("--force", "Override spending limits for this call (see `t2 limit`)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").action(
|
|
35462
|
-
async (opts) => {
|
|
35463
|
-
try {
|
|
35464
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY4;
|
|
35465
|
-
const reward = Number.parseFloat(opts.reward);
|
|
35466
|
-
const completions = Number.parseInt(opts.completions, 10);
|
|
35467
|
-
if (!Number.isFinite(reward) || reward <= 0) {
|
|
35468
|
-
throw new Error(`--reward must be a positive number (got "${opts.reward}").`);
|
|
35469
|
-
}
|
|
35470
|
-
const budget = Math.round(reward * completions * 1e6) / 1e6;
|
|
35471
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
35472
|
-
const result = await agent.pay({
|
|
35473
|
-
url: `${gateway}/tasks/board`,
|
|
35474
|
-
method: "POST",
|
|
35475
|
-
body: JSON.stringify({
|
|
35476
|
-
title: opts.title,
|
|
35477
|
-
description: opts.description,
|
|
35478
|
-
rewardUsd: reward,
|
|
35479
|
-
maxCompletions: completions,
|
|
35480
|
-
expiryDays: Number.parseInt(opts.expiryDays, 10),
|
|
35481
|
-
category: opts.category,
|
|
35482
|
-
...opts.notifyEmail ? { notifyEmail: opts.notifyEmail } : {}
|
|
35483
|
-
}),
|
|
35484
|
-
maxPrice: budget,
|
|
35485
|
-
force: opts.force
|
|
35486
|
-
});
|
|
35487
|
-
const body2 = result.body;
|
|
35488
|
-
if (isJsonMode()) {
|
|
35489
|
-
printJson(body2 ?? result);
|
|
35490
|
-
return;
|
|
35491
|
-
}
|
|
35492
|
-
printBlank();
|
|
35493
|
-
if (body2?.ok && body2.manageKey) {
|
|
35494
|
-
printSuccess(body2.moderation ?? "Task posted.");
|
|
35495
|
-
printKeyValue("Task", body2.task?.id ?? "\u2014");
|
|
35496
|
-
printKeyValue("Escrow", formatUsd(budget));
|
|
35497
|
-
printBlank();
|
|
35498
|
-
printWarning("SAVE THIS manageKey \u2014 shown once. It approves/rejects/closes this task:");
|
|
35499
|
-
printLine(` ${body2.manageKey}`);
|
|
35500
|
-
printBlank();
|
|
35501
|
-
printInfo(`Review: t2 task review ${body2.task?.id ?? "<taskId>"} --manage-key <key>`);
|
|
35502
|
-
} else {
|
|
35503
|
-
printWarning(
|
|
35504
|
-
`${body2?.error ?? "Posting failed."}${body2?.refunded ? " (Budget refunded.)" : ""}`
|
|
35505
|
-
);
|
|
35506
|
-
}
|
|
35507
|
-
printBlank();
|
|
35508
|
-
} catch (error) {
|
|
35509
|
-
handleError(error);
|
|
35510
|
-
}
|
|
35511
|
-
}
|
|
35512
|
-
);
|
|
35513
|
-
group.command("submit").argument("<taskId>", "Board task id (see `t2 task list`)").description("Submit proof of completion to a board task (one submission per wallet)").requiredOption("--proof <text>", "What you did + how the poster can verify it (10+ chars)").option("--url <url>", "Proof link (https)").option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY4})`).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").action(
|
|
35514
|
-
async (taskId, opts) => {
|
|
35515
|
-
try {
|
|
35516
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY4;
|
|
35517
|
-
const agent = await withAgent({ keyPath: opts.key });
|
|
35518
|
-
const result = await postJson2(
|
|
35519
|
-
`${gateway}/tasks/board/${taskId}/submit`,
|
|
35520
|
-
{
|
|
35521
|
-
address: agent.address(),
|
|
35522
|
-
proof: opts.proof,
|
|
35523
|
-
...opts.url ? { url: opts.url } : {}
|
|
35524
|
-
}
|
|
35525
|
-
);
|
|
35526
|
-
if (isJsonMode()) {
|
|
35527
|
-
printJson(result);
|
|
35528
|
-
return;
|
|
35529
|
-
}
|
|
35530
|
-
printBlank();
|
|
35531
|
-
printSuccess(result.note ?? "Submitted \u2014 the poster reviews next.");
|
|
35532
|
-
printBlank();
|
|
35533
|
-
} catch (error) {
|
|
35534
|
-
handleError(error);
|
|
35535
|
-
}
|
|
35536
|
-
}
|
|
35537
|
-
);
|
|
35538
|
-
group.command("review").argument("<taskId>", "Your board task id").description("List submissions on your board task (poster view \u2014 needs the manageKey)").requiredOption("--manage-key <key>", "The manageKey returned when you posted").option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY4})`).action(async (taskId, opts) => {
|
|
35539
|
-
try {
|
|
35540
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY4;
|
|
35541
|
-
const result = await getJson2(`${gateway}/tasks/board/${taskId}?manageKey=${encodeURIComponent(opts.manageKey)}`);
|
|
35542
|
-
if (!result.posterView) {
|
|
35543
|
-
throw new Error(result.error ?? "manageKey not accepted for this task.");
|
|
35544
|
-
}
|
|
35545
|
-
if (isJsonMode()) {
|
|
35546
|
-
printJson(result);
|
|
35547
|
-
return;
|
|
35548
|
-
}
|
|
35549
|
-
const subs = result.submissions ?? [];
|
|
35550
|
-
printBlank();
|
|
35551
|
-
printHeader(`${result.task?.title ?? taskId} \u2014 ${subs.length} submission${subs.length === 1 ? "" : "s"}`);
|
|
35552
|
-
for (const s of subs) {
|
|
35553
|
-
printLine(` [${s.status.padEnd(8)}] ${s.id} ${truncateAddress(s.worker)}`);
|
|
35554
|
-
if (s.proof) {
|
|
35555
|
-
printLine(` ${s.proof.slice(0, 90)}`);
|
|
35556
|
-
}
|
|
35557
|
-
if (s.url) {
|
|
35558
|
-
printLine(` ${s.url}`);
|
|
35559
|
-
}
|
|
35560
|
-
}
|
|
35561
|
-
printBlank();
|
|
35562
|
-
printInfo(`Pay: t2 task approve ${taskId} --manage-key <key> --submissions <id,id,\u2026>`);
|
|
35563
|
-
printBlank();
|
|
35564
|
-
} catch (error) {
|
|
35565
|
-
handleError(error);
|
|
35566
|
-
}
|
|
35567
|
-
});
|
|
35568
|
-
group.command("approve").argument("<taskId>", "Your board task id").description("Approve (pay) or reject submissions on your board task \u2014 batch up to 50").requiredOption("--manage-key <key>", "The manageKey returned when you posted").requiredOption("--submissions <ids>", "Comma-separated submission ids").option("--reject", "Reject instead of approving").option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY4})`).action(
|
|
35569
|
-
async (taskId, opts) => {
|
|
35570
|
-
try {
|
|
35571
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY4;
|
|
35572
|
-
const result = await postJson2(`${gateway}/tasks/board/${taskId}/approve`, {
|
|
35573
|
-
manageKey: opts.manageKey,
|
|
35574
|
-
submissionIds: opts.submissions.split(",").map((s) => s.trim()).filter(Boolean),
|
|
35575
|
-
action: opts.reject ? "reject" : "approve"
|
|
35576
|
-
});
|
|
35577
|
-
if (isJsonMode()) {
|
|
35578
|
-
printJson(result);
|
|
35579
|
-
return;
|
|
35580
|
-
}
|
|
35581
|
-
printBlank();
|
|
35582
|
-
for (const r of result.results ?? []) {
|
|
35583
|
-
if (r.status === "paid") {
|
|
35584
|
-
printSuccess(`${r.submissionId} paid${r.payoutTx ? ` (tx ${r.payoutTx.slice(0, 10)}\u2026)` : ""}`);
|
|
35585
|
-
} else if (r.error) {
|
|
35586
|
-
printWarning(`${r.submissionId}: ${r.error}`);
|
|
35587
|
-
} else {
|
|
35588
|
-
printLine(` ${r.submissionId}: ${r.status}`);
|
|
35589
|
-
}
|
|
35590
|
-
}
|
|
35591
|
-
printBlank();
|
|
35592
|
-
} catch (error) {
|
|
35593
|
-
handleError(error);
|
|
35594
|
-
}
|
|
35595
|
-
}
|
|
35596
|
-
);
|
|
35597
|
-
group.command("close").argument("<taskId>", "Your board task id").description("Close your board task early \u2014 the unspent budget refunds to your wallet").requiredOption("--manage-key <key>", "The manageKey returned when you posted").option("--gateway <url>", `Gateway base URL (default ${DEFAULT_GATEWAY4})`).action(async (taskId, opts) => {
|
|
35598
|
-
try {
|
|
35599
|
-
const gateway = opts.gateway ?? DEFAULT_GATEWAY4;
|
|
35600
|
-
const result = await postJson2(`${gateway}/tasks/board/${taskId}/close`, { manageKey: opts.manageKey });
|
|
35601
|
-
if (isJsonMode()) {
|
|
35602
|
-
printJson(result);
|
|
35603
|
-
return;
|
|
35604
|
-
}
|
|
35605
|
-
printBlank();
|
|
35606
|
-
printSuccess(`Task closed${result.refunded ? " \u2014 unspent budget refunded" : ""}.`);
|
|
35607
|
-
if (result.suiscan) {
|
|
35608
|
-
printKeyValue("Refund", result.suiscan);
|
|
35609
|
-
}
|
|
35610
|
-
printBlank();
|
|
35611
|
-
} catch (error) {
|
|
35612
|
-
handleError(error);
|
|
35613
|
-
}
|
|
35614
|
-
});
|
|
35615
|
-
}
|
|
35616
|
-
|
|
35617
34399
|
// src/program.ts
|
|
35618
34400
|
var require3 = createRequire2(import.meta.url);
|
|
35619
34401
|
var { version: CLI_VERSION2 } = require3("../package.json");
|
|
@@ -35635,10 +34417,8 @@ Examples:
|
|
|
35635
34417
|
$ t2 models List the Private API model catalog
|
|
35636
34418
|
$ t2 pay <url> --estimate Preview an x402 service's price + input schema (no payment)
|
|
35637
34419
|
$ t2 services search "image" Discover x402 services in the gateway catalog
|
|
35638
|
-
$ t2 agents
|
|
34420
|
+
$ t2 agents Look up the agent directory (agents.t2000.ai)
|
|
35639
34421
|
$ t2 agent pay <address> Buy an agent's service (escrowed, auto-refund on failure)
|
|
35640
|
-
$ t2 task list Live reward tasks + the community board
|
|
35641
|
-
$ t2 task claim share-a-read --post <url> Claim an X-proof reward
|
|
35642
34422
|
$ t2 limit set --daily 100 Change the daily spend cap (default $100/day)
|
|
35643
34423
|
$ t2 mcp install Connect Claude / Cursor / Windsurf
|
|
35644
34424
|
$ t2 skills install Install skills as local SKILL.md files`);
|
|
@@ -35659,7 +34439,6 @@ Examples:
|
|
|
35659
34439
|
registerSkills(program3);
|
|
35660
34440
|
registerAgent(program3);
|
|
35661
34441
|
registerAgents(program3);
|
|
35662
|
-
registerTask(program3);
|
|
35663
34442
|
return program3;
|
|
35664
34443
|
}
|
|
35665
34444
|
|