@t2000/cli 0.14.0 → 0.15.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 CHANGED
@@ -90,14 +90,28 @@ t2000 init
90
90
  Portfolio: 105.26 SUI (avg $0.95)
91
91
  Tx: https://suiscan.xyz/mainnet/tx/...
92
92
 
93
+ ❯ t2000 invest buy 500 BTC
94
+ ✓ Bought 0.00512820 BTC at $97,500.00
95
+ Invested: $500.00
96
+ Portfolio: 0.00512820 BTC (avg $97,500.00)
97
+ Tx: https://suiscan.xyz/mainnet/tx/...
98
+
99
+ ❯ t2000 invest buy 200 ETH
100
+ ✓ Bought 0.10526316 ETH at $1,900.00
101
+ Invested: $200.00
102
+ Portfolio: 0.10526316 ETH (avg $1,900.00)
103
+ Tx: https://suiscan.xyz/mainnet/tx/...
104
+
93
105
  ❯ t2000 portfolio
94
106
  Investment Portfolio
95
107
  ─────────────────────────────────────────────────────
96
- SUI 105.26 Avg: $0.95 Now: $0.97 +$2.10 (+2.1%)
108
+ SUI 105.26000000 Avg: $0.95 Now: $0.97 APY: 2.10% +$2.10 (+2.1%)
109
+ BTC 0.00512820 Avg: $97,500 Now: $98,200 — +$3.59 (+0.7%)
110
+ ETH 0.10526316 Avg: $1,900 Now: $1,920 APY: 1.85% +$2.11 (+1.1%)
97
111
  ─────────────────────────────────────────────────────
98
- Total invested: $100.00
99
- Current value: $102.10
100
- Unrealized P&L: +$2.10 (+2.1%)
112
+ Total invested: $800.00
113
+ Current value: $807.80
114
+ Unrealized P&L: +$7.80 (+1.0%)
101
115
  ```
102
116
 
103
117
  30 seconds. Send → save → borrow → pay → repay → withdraw.
@@ -176,6 +190,18 @@ t2000 init
176
190
  | `t2000 contacts add <name> <address>` | Save a named contact |
177
191
  | `t2000 contacts remove <name>` | Remove a contact |
178
192
 
193
+ ### Investment
194
+
195
+ | Command | Description |
196
+ |---------|-------------|
197
+ | `t2000 invest buy <amount> <asset>` | Buy crypto with USDC (e.g. `t2000 invest buy 500 BTC`, `t2000 invest buy 200 ETH`) |
198
+ | `t2000 invest sell <amount\|all> <asset>` | Sell crypto back to USDC (auto-withdraws if earning) |
199
+ | `t2000 invest earn <asset>` | Deposit invested asset into best-rate lending for yield |
200
+ | `t2000 invest unearn <asset>` | Withdraw from lending, keep in portfolio |
201
+ | `t2000 portfolio` | View investment portfolio with cost-basis P&L (shows APY when earning) |
202
+
203
+ Supported assets: SUI, BTC, ETH. Dollar-denominated — `amount` is in USD.
204
+
179
205
  ### Safeguards
180
206
 
181
207
  | Command | Description |
@@ -20965,7 +20965,7 @@ var StdioServerTransport = class {
20965
20965
  import { readFile } from "fs/promises";
20966
20966
  import { resolve } from "path";
20967
20967
  import { homedir } from "os";
20968
- import { T2000, SafeguardError, T2000Error } from "@t2000/sdk";
20968
+ import { T2000, INVESTMENT_ASSETS, SafeguardError, T2000Error } from "@t2000/sdk";
20969
20969
  var SESSION_PATH = resolve(homedir(), ".t2000", ".session");
20970
20970
  async function resolvePin() {
20971
20971
  const envPin = process.env.T2000_PIN ?? process.env.T2000_PASSPHRASE;
@@ -21124,7 +21124,14 @@ function registerReadTools(server, agent) {
21124
21124
  async () => {
21125
21125
  try {
21126
21126
  const result = await agent.getPortfolio();
21127
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
21127
+ const enriched = {
21128
+ ...result,
21129
+ positions: result.positions.map((p) => ({
21130
+ ...p,
21131
+ ...p.currentPrice === 0 && p.totalAmount > 0 ? { note: "price unavailable" } : {}
21132
+ }))
21133
+ };
21134
+ return { content: [{ type: "text", text: JSON.stringify(enriched) }] };
21128
21135
  } catch (err) {
21129
21136
  return errorResult(err);
21130
21137
  }
@@ -21364,22 +21371,29 @@ function registerWriteTools(server, agent) {
21364
21371
  }
21365
21372
  }
21366
21373
  );
21374
+ const investAssets = Object.keys(INVESTMENT_ASSETS);
21367
21375
  server.tool(
21368
21376
  "t2000_invest",
21369
- "Buy or sell investment assets (spot). Amount is in USD. Asset is the crypto to buy/sell (e.g. SUI). Set dryRun: true to preview.",
21377
+ "Buy, sell, earn yield, or stop earning on investment assets. Actions: buy (invest USD), sell (convert to USDC), earn (deposit into best-rate lending for yield), unearn (withdraw from lending, keep in portfolio). Amount required for buy/sell only.",
21370
21378
  {
21371
- action: external_exports.enum(["buy", "sell"]).describe("'buy' to invest USD into asset, 'sell' to convert asset back to USDC"),
21372
- asset: external_exports.enum(["SUI"]).describe("Asset to invest in"),
21373
- amount: external_exports.union([external_exports.number(), external_exports.literal("all")]).describe('USD amount, or "all" to sell entire position'),
21379
+ action: external_exports.enum(["buy", "sell", "earn", "unearn"]).describe("'buy' to invest, 'sell' to liquidate, 'earn' to deposit into lending for yield, 'unearn' to withdraw from lending"),
21380
+ asset: external_exports.enum(investAssets).describe("Asset to invest in"),
21381
+ amount: external_exports.union([external_exports.number(), external_exports.literal("all")]).optional().describe("USD amount (required for buy/sell, ignored for earn/unearn)"),
21382
+ slippage: external_exports.number().optional().describe("Max slippage percent (default: 3, for buy/sell only)"),
21374
21383
  dryRun: external_exports.boolean().optional().describe("Preview without signing (default: false)")
21375
21384
  },
21376
- async ({ action, asset, amount, dryRun }) => {
21385
+ async ({ action, asset, amount, slippage, dryRun }) => {
21377
21386
  try {
21378
21387
  if (dryRun) {
21379
21388
  agent.enforcer.assertNotLocked();
21380
21389
  const balance = await agent.balance();
21381
21390
  const portfolio = await agent.getPortfolio();
21382
21391
  const position = portfolio.positions.find((p) => p.asset === asset);
21392
+ if (action === "sell" && amount === "all" && !position) {
21393
+ return {
21394
+ content: [{ type: "text", text: JSON.stringify({ preview: true, error: `No ${asset} position to sell` }) }]
21395
+ };
21396
+ }
21383
21397
  return {
21384
21398
  content: [{
21385
21399
  type: "text",
@@ -21387,20 +21401,30 @@ function registerWriteTools(server, agent) {
21387
21401
  preview: true,
21388
21402
  action,
21389
21403
  asset,
21390
- amount: amount === "all" ? position?.currentValue ?? 0 : amount,
21404
+ amount: amount === "all" ? position?.currentValue ?? 0 : amount ?? position?.totalAmount ?? 0,
21391
21405
  currentBalance: balance.available,
21392
- currentPosition: position ?? null
21406
+ currentPosition: position ?? null,
21407
+ earning: position?.earning ?? false,
21408
+ earningProtocol: position?.earningProtocol ?? null,
21409
+ earningApy: position?.earningApy ?? null
21393
21410
  })
21394
21411
  }]
21395
21412
  };
21396
21413
  }
21414
+ const maxSlippage = slippage ? slippage / 100 : void 0;
21397
21415
  if (action === "buy") {
21398
21416
  if (typeof amount !== "number") throw new Error("Buy amount must be a number");
21399
- const result = await mutex.run(() => agent.investBuy({ asset, usdAmount: amount }));
21417
+ const result = await mutex.run(() => agent.investBuy({ asset, usdAmount: amount, maxSlippage }));
21400
21418
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
21401
- } else {
21419
+ } else if (action === "sell") {
21402
21420
  const usdAmount = amount === "all" ? "all" : amount;
21403
- const result = await mutex.run(() => agent.investSell({ asset, usdAmount }));
21421
+ const result = await mutex.run(() => agent.investSell({ asset, usdAmount, maxSlippage }));
21422
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
21423
+ } else if (action === "earn") {
21424
+ const result = await mutex.run(() => agent.investEarn({ asset }));
21425
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
21426
+ } else {
21427
+ const result = await mutex.run(() => agent.investUnearn({ asset }));
21404
21428
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
21405
21429
  }
21406
21430
  } catch (err) {
@@ -21640,6 +21664,7 @@ ${context}
21640
21664
  "- Expected annual yield on the recommended amount",
21641
21665
  "- If they should rebalance existing savings (t2000_rebalance with dryRun: true)",
21642
21666
  "- Whether investing in SUI or other assets could complement their savings strategy",
21667
+ '- Note: investment assets (SUI, ETH) can also earn yield via t2000_invest action: "earn"',
21643
21668
  "",
21644
21669
  "If they want to proceed, use t2000_save to deposit. Always preview first."
21645
21670
  ].join("\n")
@@ -21667,9 +21692,13 @@ ${context}
21667
21692
  "- Portfolio allocation assessment (what % is in checking vs savings vs investment)",
21668
21693
  "- Whether current positions are performing well or need adjustment",
21669
21694
  "- If idle checking funds should be invested or saved for yield",
21695
+ '- Whether invested assets should earn yield (t2000_invest action: "earn") \u2014 this deposits the asset into the best lending protocol',
21696
+ "- If a position is earning, mention the APY and protocol",
21670
21697
  "- Risk assessment \u2014 concentration, unrealized losses, cost basis vs current price",
21671
21698
  "",
21672
- "If they want to invest, use t2000_invest with dryRun: true to preview first."
21699
+ "If they want to invest, use t2000_invest with dryRun: true to preview first.",
21700
+ 'If they want to earn yield on investments, use t2000_invest action: "earn".',
21701
+ 'If they want to stop earning, use t2000_invest action: "unearn".'
21673
21702
  ].join("\n")
21674
21703
  }
21675
21704
  }]
@@ -21695,4 +21724,4 @@ async function startMcpServer(opts) {
21695
21724
  export {
21696
21725
  startMcpServer
21697
21726
  };
21698
- //# sourceMappingURL=dist-4B5R5GWR.js.map
21727
+ //# sourceMappingURL=dist-TXXQC3NH.js.map