@t2000/cli 0.19.2 → 0.20.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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename);
3
- import "./chunk-YPWSCLE3.js";
3
+ import "./chunk-45YCL2KY.js";
4
4
 
5
5
  // src/program.ts
6
6
  import { Command } from "commander";
@@ -142,35 +142,53 @@ async function resolvePin(opts) {
142
142
 
143
143
  // src/commands/init.ts
144
144
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
145
- import { join } from "path";
145
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
146
+ import { join, dirname } from "path";
146
147
  import { homedir as homedir2, platform } from "os";
147
- import { exec } from "child_process";
148
- var LLM_KEY_URLS = {
149
- anthropic: "https://console.anthropic.com/settings/keys",
150
- openai: "https://platform.openai.com/api-keys"
151
- };
152
- function openBrowser(url) {
153
- const cmd = platform() === "darwin" ? "open" : platform() === "win32" ? "start" : "xdg-open";
154
- exec(`${cmd} "${url}"`, () => {
155
- });
156
- }
157
148
  var CONFIG_DIR = join(homedir2(), ".t2000");
158
149
  var CONFIG_PATH = join(CONFIG_DIR, "config.json");
159
- function loadConfig() {
160
- try {
161
- return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
162
- } catch {
163
- return {};
164
- }
150
+ function getMcpPlatforms() {
151
+ const home = homedir2();
152
+ const isMac = platform() === "darwin";
153
+ return [
154
+ {
155
+ name: "Claude Desktop",
156
+ path: isMac ? join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : join(home, "AppData", "Roaming", "Claude", "claude_desktop_config.json")
157
+ },
158
+ {
159
+ name: "Cursor",
160
+ path: join(home, ".cursor", "mcp.json")
161
+ },
162
+ {
163
+ name: "Windsurf",
164
+ path: join(home, ".codeium", "windsurf", "mcp_config.json")
165
+ }
166
+ ];
165
167
  }
166
- function saveConfig(config) {
167
- if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
168
- writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
168
+ async function installMcpForPlatforms(platforms) {
169
+ const mcpConfig = { command: "t2000", args: ["mcp"] };
170
+ for (const p of platforms) {
171
+ let config = {};
172
+ try {
173
+ config = JSON.parse(await readFile2(p.path, "utf-8"));
174
+ } catch {
175
+ }
176
+ const servers = config.mcpServers ?? {};
177
+ if (servers["t2000"]) {
178
+ printInfo(`${p.name} already configured`);
179
+ continue;
180
+ }
181
+ config.mcpServers = { ...servers, t2000: mcpConfig };
182
+ const dir = dirname(p.path);
183
+ if (!existsSync(dir)) await mkdir2(dir, { recursive: true });
184
+ await writeFile2(p.path, JSON.stringify(config, null, 2) + "\n", "utf-8");
185
+ printSuccess(`${p.name} configured`);
186
+ }
169
187
  }
170
188
  function registerInit(program2) {
171
- program2.command("init").description("Create a new agent bank account \u2014 guided setup with AI + Telegram + safeguards").option("--key <path>", "Key file path").option("--no-sponsor", "Skip gas sponsorship").action(async (opts) => {
189
+ program2.command("init").description("Create a new agent bank account \u2014 guided setup with MCP + safeguards").option("--key <path>", "Key file path").option("--no-sponsor", "Skip gas sponsorship").action(async (opts) => {
172
190
  try {
173
- const { select, input, password: password3, confirm: confirm2 } = await import("@inquirer/prompts");
191
+ const { checkbox, input, password: password3 } = await import("@inquirer/prompts");
174
192
  console.log("");
175
193
  console.log(` \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`);
176
194
  console.log(` \u2502 ${pc2.bold("Welcome to t2000")} \u2502`);
@@ -179,8 +197,10 @@ function registerInit(program2) {
179
197
  console.log("");
180
198
  const hasWallet = await walletExists(opts.key);
181
199
  let address = "";
182
- const skipWallet = hasWallet;
183
- if (skipWallet) {
200
+ const isReturning = hasWallet;
201
+ const totalSteps = isReturning ? 2 : 3;
202
+ let step = 1;
203
+ if (isReturning) {
184
204
  printSuccess("Existing wallet detected");
185
205
  const pin = await password3({ message: "Enter your PIN:" });
186
206
  if (!pin || pin.length < 4) throw new Error("PIN must be at least 4 characters");
@@ -189,7 +209,7 @@ function registerInit(program2) {
189
209
  await saveSession(pin);
190
210
  printSuccess(`Wallet unlocked (${address.slice(0, 6)}...${address.slice(-4)})`);
191
211
  } else {
192
- console.log(` ${pc2.bold("Step 1 of 5")} \u2014 Create wallet`);
212
+ console.log(` ${pc2.bold(`Step ${step} of ${totalSteps}`)} \u2014 Create wallet`);
193
213
  printBlank();
194
214
  const pin = await password3({ message: `Create PIN (min 4 chars):` });
195
215
  if (!pin || pin.length < 4) throw new Error("PIN must be at least 4 characters");
@@ -197,7 +217,7 @@ function registerInit(program2) {
197
217
  if (pin !== pinConfirm) throw new Error("PINs do not match");
198
218
  printBlank();
199
219
  printInfo("Creating agent wallet...");
200
- const { agent, address: addr, sponsored } = await T2000.init({ pin, keyPath: opts.key, sponsored: opts.sponsor });
220
+ const { address: addr, sponsored } = await T2000.init({ pin, keyPath: opts.key, sponsored: opts.sponsor });
201
221
  address = addr;
202
222
  await saveSession(pin);
203
223
  printSuccess("Keypair generated");
@@ -212,82 +232,31 @@ function registerInit(program2) {
212
232
  printLine(` \u{1F389} ${pc2.green("Bank account created")}`);
213
233
  printLine(` Address: ${pc2.yellow(address.slice(0, 6) + "..." + address.slice(-4))}`);
214
234
  printBlank();
215
- }
216
- const stepNum = skipWallet ? 1 : 3;
217
- console.log(` ${pc2.bold(`Step ${stepNum} of ${skipWallet ? 3 : 5}`)} \u2014 Connect AI`);
218
- printBlank();
219
- const llmProvider = await select({
220
- message: "Which LLM provider?",
221
- choices: [
222
- { name: "Claude (Anthropic)", value: "anthropic" },
223
- { name: "GPT (OpenAI)", value: "openai" },
224
- { name: "Skip (CLI only, no chat)", value: "skip" }
225
- ]
235
+ step++;
236
+ }
237
+ console.log(` ${pc2.bold(`Step ${step} of ${totalSteps}`)} \u2014 Connect AI platforms`);
238
+ printBlank();
239
+ const allPlatforms = getMcpPlatforms();
240
+ const selectedNames = await checkbox({
241
+ message: "Which AI platforms do you use? (space to select)",
242
+ choices: allPlatforms.map((p) => ({
243
+ name: p.name,
244
+ value: p.name,
245
+ checked: p.name !== "Windsurf"
246
+ }))
226
247
  });
227
- if (llmProvider !== "skip") {
228
- const providerName = llmProvider === "anthropic" ? "Anthropic" : "OpenAI";
229
- const keyUrl = LLM_KEY_URLS[llmProvider];
230
- printBlank();
231
- printInfo(`Opening ${providerName} API keys page in your browser...`);
232
- openBrowser(keyUrl);
233
- printLine(` ${pc2.dim(keyUrl)}`);
234
- printBlank();
235
- const apiKey = await password3({
236
- message: `Paste your ${providerName} API key:`
237
- });
238
- if (!apiKey) throw new Error("API key is required");
239
- const config = loadConfig();
240
- config.llm = { provider: llmProvider, apiKey };
241
- saveConfig(config);
242
- const modelName = llmProvider === "anthropic" ? "claude-sonnet-4-20250514" : "gpt-4o";
243
- printSuccess(`${providerName} connected \u2014 model: ${modelName}`);
244
- } else {
245
- printSuccess("Skipped \u2014 use CLI commands directly");
246
- }
247
- printBlank();
248
- const telegramStepNum = skipWallet ? 2 : 4;
249
- console.log(` ${pc2.bold(`Step ${telegramStepNum} of ${skipWallet ? 3 : 5}`)} \u2014 Connect Telegram ${pc2.dim("(optional)")}`);
248
+ const selectedPlatforms = allPlatforms.filter((p) => selectedNames.includes(p.name));
250
249
  printBlank();
251
- const wantsTelegram = await confirm2({
252
- message: "Want to chat with your agent on Telegram?",
253
- default: true
254
- });
255
- if (wantsTelegram) {
256
- printBlank();
257
- printInfo("Opening BotFather in Telegram...");
258
- openBrowser("https://t.me/BotFather");
259
- printBlank();
260
- printLine(`1. Send ${pc2.cyan("/newbot")} to BotFather`);
261
- printLine(`2. Pick a name (e.g. "My t2000 Agent")`);
262
- printLine(`3. Copy the bot token`);
263
- printBlank();
264
- const botToken = await input({ message: "Paste the bot token:" });
265
- if (!botToken) throw new Error("Bot token is required");
250
+ if (selectedPlatforms.length > 0) {
251
+ printInfo("Adding t2000 to your AI platforms...");
266
252
  printBlank();
267
- printInfo("Opening @userinfobot to get your Telegram user ID...");
268
- openBrowser("https://t.me/userinfobot");
269
- printBlank();
270
- printLine(`Send any message to ${pc2.cyan("@userinfobot")} \u2014 it will reply with your ID.`);
271
- printBlank();
272
- const userId = await input({ message: "Paste your Telegram user ID:" });
273
- const config = loadConfig();
274
- config.channels = {
275
- ...config.channels ?? {},
276
- telegram: {
277
- enabled: true,
278
- botToken,
279
- allowedUsers: userId ? [userId] : []
280
- },
281
- webchat: { enabled: true, port: 2e3 }
282
- };
283
- saveConfig(config);
284
- printSuccess("Telegram connected");
253
+ await installMcpForPlatforms(selectedPlatforms);
285
254
  } else {
286
- printSuccess("Skipped \u2014 you can add Telegram later");
255
+ printInfo("Skipped \u2014 you can add MCP later with: t2000 mcp install");
287
256
  }
288
257
  printBlank();
289
- const safeguardStepNum = skipWallet ? 3 : 5;
290
- console.log(` ${pc2.bold(`Step ${safeguardStepNum} of ${skipWallet ? 3 : 5}`)} \u2014 Set safeguards`);
258
+ step++;
259
+ console.log(` ${pc2.bold(`Step ${step} of ${totalSteps}`)} \u2014 Set safeguards`);
291
260
  printBlank();
292
261
  const maxPerTx = await input({
293
262
  message: "Max per transaction ($):",
@@ -303,18 +272,21 @@ function registerInit(program2) {
303
272
  enforcer.set("maxDailySend", Number(maxDaily));
304
273
  printSuccess("Safeguards configured");
305
274
  printBlank();
275
+ const platformList = selectedPlatforms.map((p) => p.name).join(" / ");
306
276
  console.log(` \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`);
307
277
  console.log(` \u2502 ${pc2.green("\u2713 You're all set")} \u2502`);
308
278
  console.log(` \u2502 \u2502`);
309
- if (llmProvider !== "skip") {
310
- console.log(` \u2502 Start your agent: \u2502`);
311
- console.log(` \u2502 ${pc2.cyan("t2000 gateway")} \u2502`);
279
+ if (selectedPlatforms.length > 0) {
280
+ console.log(` \u2502 ${pc2.bold("Next steps:")} \u2502`);
281
+ console.log(` \u2502 1. Restart ${platformList.length > 20 ? "your AI platform" : platformList}${" ".repeat(Math.max(0, 23 - Math.min(platformList.length, 20)))}\u2502`);
282
+ console.log(` \u2502 2. Ask: ${pc2.cyan(`"What's my t2000 balance?"`)} \u2502`);
283
+ console.log(` \u2502 \u2502`);
284
+ } else {
285
+ console.log(` \u2502 Use the CLI directly: \u2502`);
286
+ console.log(` \u2502 ${pc2.cyan("t2000 balance")} \u2502`);
287
+ console.log(` \u2502 ${pc2.cyan("t2000 invest buy 100 SUI")} \u2502`);
312
288
  console.log(` \u2502 \u2502`);
313
289
  }
314
- console.log(` \u2502 Or use the CLI directly: \u2502`);
315
- console.log(` \u2502 ${pc2.cyan("t2000 balance")} \u2502`);
316
- console.log(` \u2502 ${pc2.cyan("t2000 invest buy 100 SUI")} \u2502`);
317
- console.log(` \u2502 \u2502`);
318
290
  console.log(` \u2502 Deposit USDC to get started: \u2502`);
319
291
  console.log(` \u2502 ${pc2.yellow(address)} \u2502`);
320
292
  console.log(` \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518`);
@@ -551,7 +523,7 @@ function registerHistory(program2) {
551
523
  }
552
524
 
553
525
  // src/commands/exportKey.ts
554
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
526
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
555
527
  import { resolve as resolve2 } from "path";
556
528
  import { homedir as homedir3 } from "os";
557
529
  import { T2000 as T20007 } from "@t2000/sdk";
@@ -560,15 +532,15 @@ var MAX_ATTEMPTS = 5;
560
532
  var LOCKOUT_MS = 5 * 60 * 1e3;
561
533
  async function getLockState() {
562
534
  try {
563
- const data = JSON.parse(await readFile2(LOCKFILE, "utf-8"));
535
+ const data = JSON.parse(await readFile3(LOCKFILE, "utf-8"));
564
536
  return data;
565
537
  } catch {
566
538
  return { attempts: 0, lockedUntil: 0 };
567
539
  }
568
540
  }
569
541
  async function setLockState(state) {
570
- await mkdir2(resolve2(homedir3(), ".t2000"), { recursive: true });
571
- await writeFile2(LOCKFILE, JSON.stringify(state), { mode: 384 });
542
+ await mkdir3(resolve2(homedir3(), ".t2000"), { recursive: true });
543
+ await writeFile3(LOCKFILE, JSON.stringify(state), { mode: 384 });
572
544
  }
573
545
  function registerExport(program2) {
574
546
  program2.command("export").description("Export private key (raw Ed25519 hex)").option("--key <path>", "Key file path").option("--yes", "Skip confirmation").action(async (opts) => {
@@ -1040,14 +1012,14 @@ function setNestedValue(obj, path, value) {
1040
1012
  }
1041
1013
  current[parts[parts.length - 1]] = value;
1042
1014
  }
1043
- function loadConfig2() {
1015
+ function loadConfig() {
1044
1016
  try {
1045
1017
  return JSON.parse(readFileSync2(CONFIG_PATH2, "utf-8"));
1046
1018
  } catch {
1047
1019
  return {};
1048
1020
  }
1049
1021
  }
1050
- function saveConfig2(config) {
1022
+ function saveConfig(config) {
1051
1023
  if (!existsSync2(CONFIG_DIR2)) {
1052
1024
  mkdirSync2(CONFIG_DIR2, { recursive: true });
1053
1025
  }
@@ -1084,7 +1056,7 @@ function registerConfig(program2) {
1084
1056
  });
1085
1057
  configCmd.command("get").argument("[key]", "Config key to get, supports dot notation (e.g. llm.provider)").action((key) => {
1086
1058
  try {
1087
- const config = loadConfig2();
1059
+ const config = loadConfig();
1088
1060
  if (key) {
1089
1061
  const value = key.includes(".") ? getNestedValue(config, key) : config[key];
1090
1062
  if (isJsonMode()) {
@@ -1114,7 +1086,7 @@ function registerConfig(program2) {
1114
1086
  handleError(error);
1115
1087
  }
1116
1088
  });
1117
- configCmd.command("set").argument("<key>", "Config key, supports dot notation (e.g. llm.provider, channels.telegram.botToken)").argument("<value>", "Config value").action((key, value) => {
1089
+ configCmd.command("set").argument("<key>", "Config key, supports dot notation (e.g. llm.provider)").argument("<value>", "Config value").action((key, value) => {
1118
1090
  try {
1119
1091
  const leafKey = key.includes(".") ? key.split(".").pop() : key;
1120
1092
  if (SAFEGUARD_KEYS.has(leafKey) && !key.includes(".")) {
@@ -1134,7 +1106,7 @@ function registerConfig(program2) {
1134
1106
  printBlank();
1135
1107
  return;
1136
1108
  }
1137
- const config = loadConfig2();
1109
+ const config = loadConfig();
1138
1110
  let parsed = value;
1139
1111
  if (value === "true") parsed = true;
1140
1112
  else if (value === "false") parsed = false;
@@ -1150,7 +1122,7 @@ function registerConfig(program2) {
1150
1122
  } else {
1151
1123
  config[key] = parsed;
1152
1124
  }
1153
- saveConfig2(config);
1125
+ saveConfig(config);
1154
1126
  if (isJsonMode()) {
1155
1127
  printJson({ [key]: parsed });
1156
1128
  return;
@@ -1587,8 +1559,8 @@ function registerLock(program2) {
1587
1559
  if (!pin) {
1588
1560
  throw new Error("PIN required to unlock agent");
1589
1561
  }
1590
- const { T2000: T200028 } = await import("@t2000/sdk");
1591
- await T200028.create({ pin });
1562
+ const { T2000: T200027 } = await import("@t2000/sdk");
1563
+ await T200027.create({ pin });
1592
1564
  const enforcer = new SafeguardEnforcer3(CONFIG_DIR4);
1593
1565
  enforcer.load();
1594
1566
  enforcer.unlock();
@@ -2028,8 +2000,8 @@ function registerExchange(program2) {
2028
2000
  }
2029
2001
 
2030
2002
  // src/commands/mcp.ts
2031
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2032
- import { join as join5, dirname } from "path";
2003
+ import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
2004
+ import { join as join5, dirname as dirname2 } from "path";
2033
2005
  import { homedir as homedir7 } from "os";
2034
2006
  import { existsSync as existsSync4 } from "fs";
2035
2007
  var MCP_CONFIG = {
@@ -2044,32 +2016,36 @@ function getPlatformConfigs() {
2044
2016
  path: join5(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")
2045
2017
  },
2046
2018
  {
2047
- name: "Cursor (global)",
2019
+ name: "Cursor",
2048
2020
  path: join5(home, ".cursor", "mcp.json")
2021
+ },
2022
+ {
2023
+ name: "Windsurf",
2024
+ path: join5(home, ".codeium", "windsurf", "mcp_config.json")
2049
2025
  }
2050
2026
  ];
2051
2027
  }
2052
2028
  async function readJsonFile(path) {
2053
2029
  try {
2054
- const content = await readFile3(path, "utf-8");
2030
+ const content = await readFile4(path, "utf-8");
2055
2031
  return JSON.parse(content);
2056
2032
  } catch {
2057
2033
  return {};
2058
2034
  }
2059
2035
  }
2060
2036
  async function writeJsonFile(path, data) {
2061
- const dir = dirname(path);
2037
+ const dir = dirname2(path);
2062
2038
  if (!existsSync4(dir)) {
2063
- await mkdir3(dir, { recursive: true });
2039
+ await mkdir4(dir, { recursive: true });
2064
2040
  }
2065
- await writeFile3(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
2041
+ await writeFile4(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
2066
2042
  }
2067
2043
  function registerMcp(program2) {
2068
2044
  const mcp = program2.command("mcp").description("MCP server for AI platforms");
2069
2045
  mcp.command("start", { isDefault: true }).description("Start MCP server (stdio transport)").option("--key <path>", "Key file path").action(async (opts) => {
2070
2046
  let mod;
2071
2047
  try {
2072
- mod = await import("./dist-NNID5OEY.js");
2048
+ mod = await import("./dist-UJGH2XLF.js");
2073
2049
  } catch {
2074
2050
  console.error(
2075
2051
  "MCP server not installed. Run:\n npm install -g @t2000/mcp"
@@ -2078,22 +2054,22 @@ function registerMcp(program2) {
2078
2054
  }
2079
2055
  await mod.startMcpServer({ keyPath: opts.key });
2080
2056
  });
2081
- mcp.command("install").description("Auto-configure MCP in Claude Desktop and Cursor").action(async () => {
2057
+ mcp.command("install").description("Auto-configure MCP in Claude Desktop, Cursor, and Windsurf").action(async () => {
2082
2058
  try {
2083
2059
  const platforms = getPlatformConfigs();
2084
2060
  const results = [];
2085
- for (const platform3 of platforms) {
2086
- const config = await readJsonFile(platform3.path);
2061
+ for (const platform2 of platforms) {
2062
+ const config = await readJsonFile(platform2.path);
2087
2063
  if (config.mcpServers && config.mcpServers["t2000"]) {
2088
- results.push({ name: platform3.name, status: "exists" });
2064
+ results.push({ name: platform2.name, status: "exists" });
2089
2065
  continue;
2090
2066
  }
2091
2067
  config.mcpServers = {
2092
2068
  ...config.mcpServers ?? {},
2093
2069
  t2000: MCP_CONFIG
2094
2070
  };
2095
- await writeJsonFile(platform3.path, config);
2096
- results.push({ name: platform3.name, status: "added" });
2071
+ await writeJsonFile(platform2.path, config);
2072
+ results.push({ name: platform2.name, status: "added" });
2097
2073
  }
2098
2074
  if (isJsonMode()) {
2099
2075
  printJson({ installed: results });
@@ -2115,23 +2091,23 @@ function registerMcp(program2) {
2115
2091
  handleError(error);
2116
2092
  }
2117
2093
  });
2118
- mcp.command("uninstall").description("Remove t2000 MCP config from Claude Desktop and Cursor").action(async () => {
2094
+ mcp.command("uninstall").description("Remove t2000 MCP config from Claude Desktop, Cursor, and Windsurf").action(async () => {
2119
2095
  try {
2120
2096
  const platforms = getPlatformConfigs();
2121
2097
  const results = [];
2122
- for (const platform3 of platforms) {
2123
- if (!existsSync4(platform3.path)) {
2124
- results.push({ name: platform3.name, removed: false });
2098
+ for (const platform2 of platforms) {
2099
+ if (!existsSync4(platform2.path)) {
2100
+ results.push({ name: platform2.name, removed: false });
2125
2101
  continue;
2126
2102
  }
2127
- const config = await readJsonFile(platform3.path);
2103
+ const config = await readJsonFile(platform2.path);
2128
2104
  if (!config.mcpServers || !config.mcpServers["t2000"]) {
2129
- results.push({ name: platform3.name, removed: false });
2105
+ results.push({ name: platform2.name, removed: false });
2130
2106
  continue;
2131
2107
  }
2132
2108
  delete config.mcpServers["t2000"];
2133
- await writeJsonFile(platform3.path, config);
2134
- results.push({ name: platform3.name, removed: true });
2109
+ await writeJsonFile(platform2.path, config);
2110
+ results.push({ name: platform2.name, removed: true });
2135
2111
  }
2136
2112
  if (isJsonMode()) {
2137
2113
  printJson({ uninstalled: results });
@@ -2723,9 +2699,9 @@ function registerInvest(program2) {
2723
2699
  printBlank();
2724
2700
  if (result.executed.length > 0) {
2725
2701
  printSuccess(`Executed ${result.executed.length} auto-invest run(s)`);
2726
- for (const exec2 of result.executed) {
2727
- const target = exec2.strategy ?? exec2.asset ?? "?";
2728
- printKeyValue(target, formatUsd15(exec2.amount));
2702
+ for (const exec of result.executed) {
2703
+ const target = exec.strategy ?? exec.asset ?? "?";
2704
+ printKeyValue(target, formatUsd15(exec.amount));
2729
2705
  }
2730
2706
  }
2731
2707
  if (result.skipped.length > 0) {
@@ -2899,273 +2875,6 @@ function registerClaimRewards(program2) {
2899
2875
  });
2900
2876
  }
2901
2877
 
2902
- // src/commands/gateway.ts
2903
- import pc16 from "picocolors";
2904
- import { T2000 as T200027 } from "@t2000/sdk";
2905
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, unlinkSync } from "fs";
2906
- import { join as join6 } from "path";
2907
- import { homedir as homedir8, platform as platform2 } from "os";
2908
- import { execSync } from "child_process";
2909
- var PLIST_LABEL = "com.t2000.gateway";
2910
- var SYSTEMD_UNIT = "t2000-gateway";
2911
- function getLaunchAgentPath() {
2912
- return join6(homedir8(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
2913
- }
2914
- function getSystemdPath() {
2915
- return join6(homedir8(), ".config", "systemd", "user", `${SYSTEMD_UNIT}.service`);
2916
- }
2917
- function getLogDir() {
2918
- return join6(homedir8(), ".t2000", "logs");
2919
- }
2920
- function getT2000Bin() {
2921
- try {
2922
- return execSync("which t2000", { encoding: "utf-8" }).trim();
2923
- } catch {
2924
- return "npx t2000";
2925
- }
2926
- }
2927
- function registerGateway(program2) {
2928
- const gw = program2.command("gateway").description("Start AI financial advisor gateway");
2929
- gw.command("start", { isDefault: true }).description("Start the gateway (foreground)").option("--port <port>", "WebChat port", "2000").option("--no-telegram", "Skip Telegram channel").option("--no-heartbeat", "Skip heartbeat daemon").option("--verbose", "Debug logging").option("--key <path>", "Key file path").action(async (opts) => {
2930
- try {
2931
- const pin = await resolvePin();
2932
- const agent = await T200027.create({ pin, keyPath: opts.key });
2933
- console.log("");
2934
- console.log(` ${pc16.bold("t2000 gateway")}`);
2935
- console.log("");
2936
- const { Gateway } = await import("./dist-PJEEL7WQ.js");
2937
- const gateway = await Gateway.create({
2938
- agent,
2939
- port: parseInt(opts.port, 10),
2940
- noTelegram: !opts.telegram,
2941
- noHeartbeat: !opts.heartbeat,
2942
- verbose: opts.verbose
2943
- });
2944
- const info = await gateway.start();
2945
- console.log("");
2946
- const truncAddr = info.address.slice(0, 6) + "..." + info.address.slice(-4);
2947
- printSuccess(`Agent unlocked (${truncAddr})`);
2948
- printSuccess(`${info.llmProvider === "anthropic" ? "Claude" : "GPT"} connected (${info.llmModel})`);
2949
- if (info.webchatUrl) printSuccess(`WebChat at ${pc16.underline(info.webchatUrl)}`);
2950
- if (info.telegramConnected) {
2951
- printSuccess("Telegram connected");
2952
- } else if (opts.telegram) {
2953
- printWarning("Telegram not connected");
2954
- }
2955
- if (info.heartbeatTasks > 0) printSuccess(`Heartbeat started (${info.heartbeatTasks} tasks)`);
2956
- console.log("");
2957
- printSuccess(pc16.bold("Ready \u2014 talk to your agent"));
2958
- console.log("");
2959
- await new Promise(() => {
2960
- });
2961
- } catch (error) {
2962
- handleError(error);
2963
- }
2964
- });
2965
- gw.command("status").description("Check if gateway is running").option("--port <port>", "WebChat port to check", "2000").action(async (opts) => {
2966
- try {
2967
- const port = parseInt(opts.port, 10);
2968
- const response = await fetch(`http://127.0.0.1:${port}/health`);
2969
- if (response.ok) {
2970
- const data = await response.json();
2971
- printSuccess(`Gateway is running on port ${port} (${data.status})`);
2972
- }
2973
- } catch {
2974
- console.log(` ${pc16.yellow("\u25CB")} Gateway is not running on port ${opts.port}`);
2975
- }
2976
- });
2977
- gw.command("install").description("Install gateway as a background daemon").option("--port <port>", "WebChat port", "2000").action(async (opts) => {
2978
- try {
2979
- const os = platform2();
2980
- const bin = getT2000Bin();
2981
- if (os === "darwin") {
2982
- installLaunchd(bin, opts.port);
2983
- } else if (os === "linux") {
2984
- installSystemd(bin, opts.port);
2985
- } else {
2986
- throw new Error(`Unsupported platform: ${os}. Use macOS or Linux.`);
2987
- }
2988
- } catch (error) {
2989
- handleError(error);
2990
- }
2991
- });
2992
- gw.command("uninstall").description("Remove gateway daemon").action(async () => {
2993
- try {
2994
- const os = platform2();
2995
- if (os === "darwin") {
2996
- uninstallLaunchd();
2997
- } else if (os === "linux") {
2998
- uninstallSystemd();
2999
- } else {
3000
- throw new Error(`Unsupported platform: ${os}`);
3001
- }
3002
- } catch (error) {
3003
- handleError(error);
3004
- }
3005
- });
3006
- gw.command("logs").description("Tail gateway logs").option("-n <lines>", "Number of lines", "50").option("-f, --follow", "Follow log output").action(async (opts) => {
3007
- try {
3008
- const logPath = join6(getLogDir(), "gateway.log");
3009
- if (!existsSync5(logPath)) {
3010
- printWarning("No gateway logs found yet.");
3011
- printInfo(`Log path: ${logPath}`);
3012
- return;
3013
- }
3014
- printBlank();
3015
- printInfo(`Log file: ${logPath}`);
3016
- printBlank();
3017
- const content = readFileSync4(logPath, "utf-8");
3018
- const lines = content.trim().split("\n");
3019
- const tail = lines.slice(-parseInt(opts.n, 10));
3020
- for (const line of tail) {
3021
- try {
3022
- const entry = JSON.parse(line);
3023
- const time = new Date(entry.ts).toLocaleTimeString();
3024
- const levelColor = entry.level === "error" ? pc16.red : entry.level === "warn" ? pc16.yellow : pc16.dim;
3025
- printLine(`${pc16.dim(time)} ${levelColor(entry.level.padEnd(5))} ${entry.msg}`);
3026
- } catch {
3027
- printLine(line);
3028
- }
3029
- }
3030
- if (opts.follow) {
3031
- printBlank();
3032
- printInfo("Following... (Ctrl+C to stop)");
3033
- const { spawn } = await import("child_process");
3034
- const child = spawn("tail", ["-f", logPath], { stdio: "inherit" });
3035
- process.on("SIGINT", () => {
3036
- child.kill();
3037
- process.exit(0);
3038
- });
3039
- await new Promise(() => {
3040
- });
3041
- }
3042
- printBlank();
3043
- } catch (error) {
3044
- handleError(error);
3045
- }
3046
- });
3047
- }
3048
- function installLaunchd(bin, port) {
3049
- const logDir = getLogDir();
3050
- const plistPath = getLaunchAgentPath();
3051
- const plist = `<?xml version="1.0" encoding="UTF-8"?>
3052
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3053
- <plist version="1.0">
3054
- <dict>
3055
- <key>Label</key>
3056
- <string>${PLIST_LABEL}</string>
3057
- <key>ProgramArguments</key>
3058
- <array>
3059
- <string>${bin}</string>
3060
- <string>gateway</string>
3061
- <string>start</string>
3062
- <string>--port</string>
3063
- <string>${port}</string>
3064
- </array>
3065
- <key>RunAtLoad</key>
3066
- <true/>
3067
- <key>KeepAlive</key>
3068
- <true/>
3069
- <key>StandardOutPath</key>
3070
- <string>${logDir}/gateway-stdout.log</string>
3071
- <key>StandardErrorPath</key>
3072
- <string>${logDir}/gateway-stderr.log</string>
3073
- <key>EnvironmentVariables</key>
3074
- <dict>
3075
- <key>PATH</key>
3076
- <string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
3077
- </dict>
3078
- </dict>
3079
- </plist>`;
3080
- if (existsSync5(plistPath)) {
3081
- try {
3082
- execSync(`launchctl unload "${plistPath}" 2>/dev/null`);
3083
- } catch {
3084
- }
3085
- }
3086
- writeFileSync4(plistPath, plist);
3087
- execSync(`launchctl load "${plistPath}"`);
3088
- printBlank();
3089
- printSuccess("Gateway daemon installed");
3090
- printSuccess(`Starts on boot \u2014 runs in background`);
3091
- printLine(` ${pc16.dim("Logs:")} ${logDir}/gateway.log`);
3092
- printLine(` ${pc16.dim("Stop:")} t2000 gateway uninstall`);
3093
- printBlank();
3094
- }
3095
- function uninstallLaunchd() {
3096
- const plistPath = getLaunchAgentPath();
3097
- if (!existsSync5(plistPath)) {
3098
- printWarning("Gateway daemon is not installed.");
3099
- return;
3100
- }
3101
- try {
3102
- execSync(`launchctl unload "${plistPath}" 2>/dev/null`);
3103
- } catch {
3104
- }
3105
- unlinkSync(plistPath);
3106
- printBlank();
3107
- printSuccess("Gateway daemon removed");
3108
- printBlank();
3109
- }
3110
- function installSystemd(bin, port) {
3111
- const logDir = getLogDir();
3112
- const unitPath = getSystemdPath();
3113
- const unitDir = join6(homedir8(), ".config", "systemd", "user");
3114
- const unit = `[Unit]
3115
- Description=t2000 Gateway \u2014 AI Financial Advisor
3116
- After=network-online.target
3117
- Wants=network-online.target
3118
-
3119
- [Service]
3120
- Type=simple
3121
- ExecStart=${bin} gateway start --port ${port}
3122
- Restart=on-failure
3123
- RestartSec=10
3124
- StandardOutput=append:${logDir}/gateway-stdout.log
3125
- StandardError=append:${logDir}/gateway-stderr.log
3126
-
3127
- [Install]
3128
- WantedBy=default.target
3129
- `;
3130
- if (!existsSync5(unitDir)) {
3131
- execSync(`mkdir -p "${unitDir}"`);
3132
- }
3133
- writeFileSync4(unitPath, unit);
3134
- execSync("systemctl --user daemon-reload");
3135
- execSync(`systemctl --user enable ${SYSTEMD_UNIT}`);
3136
- execSync(`systemctl --user start ${SYSTEMD_UNIT}`);
3137
- printBlank();
3138
- printSuccess("Gateway daemon installed (systemd)");
3139
- printSuccess("Starts on login \u2014 runs in background");
3140
- printLine(` ${pc16.dim("Logs:")} ${logDir}/gateway.log`);
3141
- printLine(` ${pc16.dim("Status:")} systemctl --user status ${SYSTEMD_UNIT}`);
3142
- printLine(` ${pc16.dim("Stop:")} t2000 gateway uninstall`);
3143
- printBlank();
3144
- }
3145
- function uninstallSystemd() {
3146
- const unitPath = getSystemdPath();
3147
- if (!existsSync5(unitPath)) {
3148
- printWarning("Gateway daemon is not installed.");
3149
- return;
3150
- }
3151
- try {
3152
- execSync(`systemctl --user stop ${SYSTEMD_UNIT} 2>/dev/null`);
3153
- } catch {
3154
- }
3155
- try {
3156
- execSync(`systemctl --user disable ${SYSTEMD_UNIT} 2>/dev/null`);
3157
- } catch {
3158
- }
3159
- unlinkSync(unitPath);
3160
- try {
3161
- execSync("systemctl --user daemon-reload");
3162
- } catch {
3163
- }
3164
- printBlank();
3165
- printSuccess("Gateway daemon removed");
3166
- printBlank();
3167
- }
3168
-
3169
2878
  // src/program.ts
3170
2879
  var require2 = createRequire(import.meta.url);
3171
2880
  var { version: CLI_VERSION } = require2("../package.json");
@@ -3205,7 +2914,6 @@ function createProgram() {
3205
2914
  registerInvest(program2);
3206
2915
  registerPortfolio(program2);
3207
2916
  registerClaimRewards(program2);
3208
- registerGateway(program2);
3209
2917
  return program2;
3210
2918
  }
3211
2919