@t2000/cli 5.6.1 → 5.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -28900,6 +28900,7 @@ init_errors();
|
|
|
28900
28900
|
init_swap_quote();
|
|
28901
28901
|
init_cetus_swap();
|
|
28902
28902
|
init_token_registry();
|
|
28903
|
+
var AGENT_ID_PARENT_NFT_ID = process.env.AGENT_ID_PARENT_NFT_ID ?? "0xc8c13f5b5a6d4c47c04877014794f65e67e2745d3bfa089b736eb54b0ebd5d1f";
|
|
28903
28904
|
init_preflight();
|
|
28904
28905
|
|
|
28905
28906
|
// ../../node_modules/.pnpm/@inquirer+core@10.3.2_@types+node@20.19.37/node_modules/@inquirer/core/dist/esm/lib/key.js
|
|
@@ -31312,7 +31313,49 @@ async function askHidden(message) {
|
|
|
31312
31313
|
return esm_default9({ message });
|
|
31313
31314
|
}
|
|
31314
31315
|
|
|
31316
|
+
// src/lib/agent-register.ts
|
|
31317
|
+
async function postJson(url, body) {
|
|
31318
|
+
const res = await fetch(url, {
|
|
31319
|
+
method: "POST",
|
|
31320
|
+
headers: { "Content-Type": "application/json" },
|
|
31321
|
+
body: JSON.stringify(body)
|
|
31322
|
+
});
|
|
31323
|
+
const json = await res.json().catch(() => ({}));
|
|
31324
|
+
if (!res.ok) {
|
|
31325
|
+
const err = json.error;
|
|
31326
|
+
const msg = typeof err === "string" ? err : err?.message ?? `HTTP ${res.status}`;
|
|
31327
|
+
throw new Error(msg);
|
|
31328
|
+
}
|
|
31329
|
+
return json;
|
|
31330
|
+
}
|
|
31331
|
+
async function registerWallet(opts) {
|
|
31332
|
+
const prep = await postJson(`${opts.base}/agent/register/prepare`, {
|
|
31333
|
+
address: opts.address
|
|
31334
|
+
});
|
|
31335
|
+
if (prep.alreadyRegistered === true) {
|
|
31336
|
+
return { alreadyRegistered: true };
|
|
31337
|
+
}
|
|
31338
|
+
const regNonce = prep.regNonce;
|
|
31339
|
+
const txBytes = prep.txBytes;
|
|
31340
|
+
if (!(regNonce && txBytes)) {
|
|
31341
|
+
throw new Error("Failed to prepare registration.");
|
|
31342
|
+
}
|
|
31343
|
+
const bytes = new Uint8Array(Buffer.from(txBytes, "base64"));
|
|
31344
|
+
const { signature } = await opts.keypair.signTransaction(bytes);
|
|
31345
|
+
const res = await postJson(`${opts.base}/agent/register/submit`, {
|
|
31346
|
+
regNonce,
|
|
31347
|
+
address: opts.address,
|
|
31348
|
+
agentSignature: signature
|
|
31349
|
+
});
|
|
31350
|
+
return {
|
|
31351
|
+
digest: res.digest,
|
|
31352
|
+
alreadyRegistered: Boolean(res.alreadyRegistered)
|
|
31353
|
+
};
|
|
31354
|
+
}
|
|
31355
|
+
|
|
31315
31356
|
// src/commands/init.ts
|
|
31357
|
+
var DEFAULT_API_BASE = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
|
|
31358
|
+
var REGISTER_TIMEOUT_MS = 1e4;
|
|
31316
31359
|
var DEFAULT_PER_TX_USD = 25;
|
|
31317
31360
|
var DEFAULT_DAILY_USD = 100;
|
|
31318
31361
|
var limitsFooter = (perTx, daily) => `Spending limits ON: $${perTx}/tx, $${daily}/day (cumulative). Change with \`t2 limit set\`, or override a single call with --force.`;
|
|
@@ -31320,6 +31363,9 @@ function registerInit(program3) {
|
|
|
31320
31363
|
program3.command("init").description("Create a new Agent Wallet (no PIN; plain Bech32 key file with 0o600 perms)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option(
|
|
31321
31364
|
"--import [secret]",
|
|
31322
31365
|
"Import an existing wallet via Bech32 secret. Omit the value for an interactive hidden-input prompt."
|
|
31366
|
+
).option(
|
|
31367
|
+
"--no-register",
|
|
31368
|
+
"Skip the best-effort on-chain Agent ID registration (offline/CI)."
|
|
31323
31369
|
).action(async (opts) => {
|
|
31324
31370
|
try {
|
|
31325
31371
|
if (await walletExists(opts.key)) {
|
|
@@ -31329,18 +31375,35 @@ function registerInit(program3) {
|
|
|
31329
31375
|
}
|
|
31330
31376
|
let address;
|
|
31331
31377
|
let imported = false;
|
|
31378
|
+
let signingKeypair;
|
|
31332
31379
|
if (opts.import !== void 0) {
|
|
31333
31380
|
const secret = typeof opts.import === "string" && opts.import.length > 0 ? opts.import : await askHidden("Paste your suiprivkey1... secret:");
|
|
31334
31381
|
if (typeof opts.import === "string" && opts.import.length > 0) {
|
|
31335
31382
|
printWarning("Private key passed as a CLI flag will be in shell history. Prefer the interactive prompt: `t2 init --import` (no value).");
|
|
31336
31383
|
}
|
|
31384
|
+
const kp = keypairFromPrivateKey(secret);
|
|
31337
31385
|
await saveBech32(secret, opts.key);
|
|
31338
|
-
address = getAddress(
|
|
31386
|
+
address = getAddress(kp);
|
|
31387
|
+
signingKeypair = kp;
|
|
31339
31388
|
imported = true;
|
|
31340
31389
|
} else {
|
|
31341
31390
|
const keypair = generateKeypair();
|
|
31342
31391
|
await saveKey(keypair, void 0, opts.key);
|
|
31343
31392
|
address = getAddress(keypair);
|
|
31393
|
+
signingKeypair = keypair;
|
|
31394
|
+
}
|
|
31395
|
+
let registered = false;
|
|
31396
|
+
if (opts.register !== false) {
|
|
31397
|
+
try {
|
|
31398
|
+
await Promise.race([
|
|
31399
|
+
registerWallet({ keypair: signingKeypair, address, base: DEFAULT_API_BASE }),
|
|
31400
|
+
new Promise(
|
|
31401
|
+
(_, reject) => setTimeout(() => reject(new Error("timeout")), REGISTER_TIMEOUT_MS)
|
|
31402
|
+
)
|
|
31403
|
+
]);
|
|
31404
|
+
registered = true;
|
|
31405
|
+
} catch {
|
|
31406
|
+
}
|
|
31344
31407
|
}
|
|
31345
31408
|
if (!hasLimits()) {
|
|
31346
31409
|
setLimits({ perTxUsd: DEFAULT_PER_TX_USD, dailyUsd: DEFAULT_DAILY_USD });
|
|
@@ -31349,6 +31412,7 @@ function registerInit(program3) {
|
|
|
31349
31412
|
printJson({
|
|
31350
31413
|
address,
|
|
31351
31414
|
imported,
|
|
31415
|
+
registered,
|
|
31352
31416
|
configPath: opts.key ?? "~/.t2000/wallet.key",
|
|
31353
31417
|
limits: { perTxUsd: DEFAULT_PER_TX_USD, dailyUsd: DEFAULT_DAILY_USD }
|
|
31354
31418
|
});
|
|
@@ -31358,6 +31422,12 @@ function registerInit(program3) {
|
|
|
31358
31422
|
printSuccess(imported ? "Wallet imported" : "Wallet created");
|
|
31359
31423
|
printKeyValue("Address", address);
|
|
31360
31424
|
printKeyValue("Path", opts.key ?? "~/.t2000/wallet.key");
|
|
31425
|
+
if (opts.register !== false) {
|
|
31426
|
+
printKeyValue(
|
|
31427
|
+
"Agent ID",
|
|
31428
|
+
registered ? "registered" : "pending (run: t2 agent register)"
|
|
31429
|
+
);
|
|
31430
|
+
}
|
|
31361
31431
|
printBlank();
|
|
31362
31432
|
printLine(`\u26A0 ${limitsFooter(DEFAULT_PER_TX_USD, DEFAULT_DAILY_USD)}`);
|
|
31363
31433
|
printBlank();
|
|
@@ -32494,7 +32564,7 @@ function registerMcpStart(parent) {
|
|
|
32494
32564
|
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) => {
|
|
32495
32565
|
let mod2;
|
|
32496
32566
|
try {
|
|
32497
|
-
mod2 = await import("./dist-
|
|
32567
|
+
mod2 = await import("./dist-IBS23LR5.js");
|
|
32498
32568
|
} catch {
|
|
32499
32569
|
console.error("MCP server not installed. Run:\n npm install -g @t2000/mcp");
|
|
32500
32570
|
process.exit(1);
|
|
@@ -32833,6 +32903,214 @@ For MCP-aware clients (Claude Desktop, Cursor, Windsurf), prefer
|
|
|
32833
32903
|
registerSkillsUninstall(group);
|
|
32834
32904
|
}
|
|
32835
32905
|
|
|
32906
|
+
// src/commands/agent/index.ts
|
|
32907
|
+
var DEFAULT_API_BASE2 = process.env.T2000_API_URL ?? "https://api.t2000.ai/v1";
|
|
32908
|
+
function normalizeTopupAsset(input) {
|
|
32909
|
+
return input?.toLowerCase() === "usdsui" ? "USDsui" : "USDC";
|
|
32910
|
+
}
|
|
32911
|
+
async function fundCredit(agent, base, amountStr, assetOpt) {
|
|
32912
|
+
const amount = Number.parseFloat(amountStr);
|
|
32913
|
+
if (Number.isNaN(amount) || amount <= 0) {
|
|
32914
|
+
throw new Error(`amount must be a positive number (got "${amountStr}").`);
|
|
32915
|
+
}
|
|
32916
|
+
const asset = normalizeTopupAsset(assetOpt);
|
|
32917
|
+
const cfg = await fetchJson(`${base}/agent/topup`, { method: "GET" });
|
|
32918
|
+
const treasury = cfg.treasury;
|
|
32919
|
+
if (!treasury) {
|
|
32920
|
+
throw new Error("Could not resolve the t2000 treasury address.");
|
|
32921
|
+
}
|
|
32922
|
+
const sent = await agent.send({ to: treasury, amount, asset });
|
|
32923
|
+
const topup = await fetchJson(`${base}/agent/topup`, {
|
|
32924
|
+
method: "POST",
|
|
32925
|
+
body: { address: agent.address(), digest: sent.tx }
|
|
32926
|
+
});
|
|
32927
|
+
return { amount, asset, balanceUsd: topup.balanceUsd };
|
|
32928
|
+
}
|
|
32929
|
+
async function fetchJson(url, init) {
|
|
32930
|
+
const res = await fetch(url, {
|
|
32931
|
+
method: init.method,
|
|
32932
|
+
headers: init.body ? { "Content-Type": "application/json" } : void 0,
|
|
32933
|
+
body: init.body ? JSON.stringify(init.body) : void 0
|
|
32934
|
+
});
|
|
32935
|
+
const json = await res.json().catch(() => ({}));
|
|
32936
|
+
if (!res.ok) {
|
|
32937
|
+
const err = json.error;
|
|
32938
|
+
const msg = typeof err === "string" ? err : err?.message ?? `HTTP ${res.status}`;
|
|
32939
|
+
throw new Error(msg);
|
|
32940
|
+
}
|
|
32941
|
+
return json;
|
|
32942
|
+
}
|
|
32943
|
+
function registerAgent(program3) {
|
|
32944
|
+
const group = program3.command("agent").description("Agent ID \u2014 onboard this wallet to the t2000 API (api.t2000.ai)").addHelpText(
|
|
32945
|
+
"after",
|
|
32946
|
+
`
|
|
32947
|
+
Subcommands:
|
|
32948
|
+
$ t2 agent onboard --fund 5 Fund 5 USDC \u2192 mint an API key (ready to call)
|
|
32949
|
+
$ t2 agent onboard --fund 5 --asset USDsui
|
|
32950
|
+
$ t2 agent onboard Already funded \u2192 just mint a key
|
|
32951
|
+
`
|
|
32952
|
+
);
|
|
32953
|
+
group.command("onboard").description("Fund credit (gasless USDC/USDsui) + mint an API key for this wallet.").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_BASE2})`).action(
|
|
32954
|
+
async (opts) => {
|
|
32955
|
+
try {
|
|
32956
|
+
const base = opts.api ?? DEFAULT_API_BASE2;
|
|
32957
|
+
const agent = await withAgent({ keyPath: opts.key });
|
|
32958
|
+
const address = agent.address();
|
|
32959
|
+
if (opts.fund !== void 0) {
|
|
32960
|
+
const funded = await fundCredit(agent, base, opts.fund, opts.asset);
|
|
32961
|
+
if (!isJsonMode()) {
|
|
32962
|
+
printSuccess(
|
|
32963
|
+
`Funded ${formatUsd(funded.amount)} ${funded.asset} \u2192 credit $${funded.balanceUsd}`
|
|
32964
|
+
);
|
|
32965
|
+
}
|
|
32966
|
+
}
|
|
32967
|
+
const challenge = await fetchJson(`${base}/agent/challenge`, {
|
|
32968
|
+
method: "POST",
|
|
32969
|
+
body: { address }
|
|
32970
|
+
});
|
|
32971
|
+
const nonce = challenge.nonce;
|
|
32972
|
+
if (!nonce) {
|
|
32973
|
+
throw new Error("Failed to get a challenge nonce.");
|
|
32974
|
+
}
|
|
32975
|
+
const message = new TextEncoder().encode(`t2000-agent-keys:${nonce}`);
|
|
32976
|
+
const { signature } = await agent.keypair.signPersonalMessage(message);
|
|
32977
|
+
const minted = await fetchJson(`${base}/agent/keys`, {
|
|
32978
|
+
method: "POST",
|
|
32979
|
+
body: { address, nonce, signature }
|
|
32980
|
+
});
|
|
32981
|
+
const key = minted.key;
|
|
32982
|
+
if (!key) {
|
|
32983
|
+
throw new Error("Failed to mint an API key.");
|
|
32984
|
+
}
|
|
32985
|
+
let registered = false;
|
|
32986
|
+
try {
|
|
32987
|
+
await registerWallet({ keypair: agent.keypair, address, base });
|
|
32988
|
+
registered = true;
|
|
32989
|
+
} catch {
|
|
32990
|
+
}
|
|
32991
|
+
if (isJsonMode()) {
|
|
32992
|
+
printJson({ address, apiKey: key, baseUrl: base, registered });
|
|
32993
|
+
return;
|
|
32994
|
+
}
|
|
32995
|
+
printBlank();
|
|
32996
|
+
printSuccess("Agent onboarded \u2014 API key minted (shown once, store it now)");
|
|
32997
|
+
printKeyValue("Address", truncateAddress(address));
|
|
32998
|
+
printKeyValue("API key", key);
|
|
32999
|
+
printKeyValue(
|
|
33000
|
+
"Agent ID",
|
|
33001
|
+
registered ? "registered" : "pending (retry: t2 agent register)"
|
|
33002
|
+
);
|
|
33003
|
+
printKeyValue("Base URL", base);
|
|
33004
|
+
printBlank();
|
|
33005
|
+
printInfo(`export OPENAI_BASE_URL=${base} OPENAI_API_KEY=${key}`);
|
|
33006
|
+
printBlank();
|
|
33007
|
+
} catch (error) {
|
|
33008
|
+
handleError(error);
|
|
33009
|
+
}
|
|
33010
|
+
}
|
|
33011
|
+
);
|
|
33012
|
+
group.command("topup").argument("<amount>", "Stablecoin amount to deposit as credit").description(
|
|
33013
|
+
"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."
|
|
33014
|
+
).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_BASE2})`).action(
|
|
33015
|
+
async (amount, opts) => {
|
|
33016
|
+
try {
|
|
33017
|
+
const base = opts.api ?? DEFAULT_API_BASE2;
|
|
33018
|
+
const agent = await withAgent({ keyPath: opts.key });
|
|
33019
|
+
const funded = await fundCredit(agent, base, amount, opts.asset);
|
|
33020
|
+
if (isJsonMode()) {
|
|
33021
|
+
printJson({
|
|
33022
|
+
address: agent.address(),
|
|
33023
|
+
funded: funded.amount,
|
|
33024
|
+
asset: funded.asset,
|
|
33025
|
+
balanceUsd: funded.balanceUsd
|
|
33026
|
+
});
|
|
33027
|
+
return;
|
|
33028
|
+
}
|
|
33029
|
+
printBlank();
|
|
33030
|
+
printSuccess(
|
|
33031
|
+
`Topped up ${formatUsd(funded.amount)} ${funded.asset} \u2192 credit $${funded.balanceUsd}`
|
|
33032
|
+
);
|
|
33033
|
+
printBlank();
|
|
33034
|
+
} catch (error) {
|
|
33035
|
+
handleError(error);
|
|
33036
|
+
}
|
|
33037
|
+
}
|
|
33038
|
+
);
|
|
33039
|
+
group.command("register").description(
|
|
33040
|
+
"Register this wallet on-chain as an Agent ID (sponsored, gasless). Idempotent \u2014 safe to re-run."
|
|
33041
|
+
).option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option("--api <url>", `API base URL (default ${DEFAULT_API_BASE2})`).action(async (opts) => {
|
|
33042
|
+
try {
|
|
33043
|
+
const base = opts.api ?? DEFAULT_API_BASE2;
|
|
33044
|
+
const agent = await withAgent({ keyPath: opts.key });
|
|
33045
|
+
const address = agent.address();
|
|
33046
|
+
const reg = await registerWallet({ keypair: agent.keypair, address, base });
|
|
33047
|
+
if (isJsonMode()) {
|
|
33048
|
+
printJson({
|
|
33049
|
+
address,
|
|
33050
|
+
registered: true,
|
|
33051
|
+
alreadyRegistered: reg.alreadyRegistered,
|
|
33052
|
+
digest: reg.digest
|
|
33053
|
+
});
|
|
33054
|
+
return;
|
|
33055
|
+
}
|
|
33056
|
+
printBlank();
|
|
33057
|
+
printSuccess(
|
|
33058
|
+
reg.alreadyRegistered ? "Already registered as an Agent ID" : "Registered as an Agent ID"
|
|
33059
|
+
);
|
|
33060
|
+
printKeyValue("Address", truncateAddress(address));
|
|
33061
|
+
if (reg.digest) {
|
|
33062
|
+
printKeyValue("Tx", reg.digest);
|
|
33063
|
+
}
|
|
33064
|
+
printBlank();
|
|
33065
|
+
} catch (error) {
|
|
33066
|
+
handleError(error);
|
|
33067
|
+
}
|
|
33068
|
+
});
|
|
33069
|
+
group.command("handle").argument("<label>", "Handle label (3\u201320 chars: lowercase a\u2013z, 0\u20139, hyphens)").description(
|
|
33070
|
+
"Claim <label>.agent-id.sui \u2192 this wallet (custody-minted, gasless). Use --release to give it up."
|
|
33071
|
+
).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_BASE2})`).action(
|
|
33072
|
+
async (label, opts) => {
|
|
33073
|
+
try {
|
|
33074
|
+
const base = opts.api ?? DEFAULT_API_BASE2;
|
|
33075
|
+
const agent = await withAgent({ keyPath: opts.key });
|
|
33076
|
+
const address = agent.address();
|
|
33077
|
+
const challenge = await fetchJson(`${base}/agent/challenge`, {
|
|
33078
|
+
method: "POST",
|
|
33079
|
+
body: { address }
|
|
33080
|
+
});
|
|
33081
|
+
const nonce = challenge.nonce;
|
|
33082
|
+
if (!nonce) {
|
|
33083
|
+
throw new Error("Failed to get a challenge nonce.");
|
|
33084
|
+
}
|
|
33085
|
+
const action = opts.release ? "t2000-agent-handle-release" : "t2000-agent-handle";
|
|
33086
|
+
const message = new TextEncoder().encode(`${action}:${nonce}:${label}`);
|
|
33087
|
+
const { signature } = await agent.keypair.signPersonalMessage(message);
|
|
33088
|
+
const path2 = opts.release ? "/agent/handle/release" : "/agent/handle";
|
|
33089
|
+
const res = await fetchJson(`${base}${path2}`, {
|
|
33090
|
+
method: "POST",
|
|
33091
|
+
body: { address, label, nonce, signature }
|
|
33092
|
+
});
|
|
33093
|
+
if (isJsonMode()) {
|
|
33094
|
+
printJson({ address, ...res });
|
|
33095
|
+
return;
|
|
33096
|
+
}
|
|
33097
|
+
printBlank();
|
|
33098
|
+
if (opts.release) {
|
|
33099
|
+
printSuccess(`Handle released: ${String(res.handle)}`);
|
|
33100
|
+
} else {
|
|
33101
|
+
printSuccess(`Handle claimed: ${String(res.display)}`);
|
|
33102
|
+
printKeyValue("Handle", String(res.handle));
|
|
33103
|
+
}
|
|
33104
|
+
printKeyValue("Address", truncateAddress(address));
|
|
33105
|
+
printKeyValue("Tx", String(res.digest));
|
|
33106
|
+
printBlank();
|
|
33107
|
+
} catch (error) {
|
|
33108
|
+
handleError(error);
|
|
33109
|
+
}
|
|
33110
|
+
}
|
|
33111
|
+
);
|
|
33112
|
+
}
|
|
33113
|
+
|
|
32836
33114
|
// src/program.ts
|
|
32837
33115
|
var require3 = createRequire2(import.meta.url);
|
|
32838
33116
|
var { version: CLI_VERSION2 } = require3("../package.json");
|
|
@@ -32868,6 +33146,7 @@ Examples:
|
|
|
32868
33146
|
registerLimit(program3);
|
|
32869
33147
|
registerMcp(program3);
|
|
32870
33148
|
registerSkills(program3);
|
|
33149
|
+
registerAgent(program3);
|
|
32871
33150
|
return program3;
|
|
32872
33151
|
}
|
|
32873
33152
|
|