@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(keypairFromPrivateKey(secret));
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-5TKR5HBZ.js");
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