@botuyo/mcp 0.1.0 → 0.2.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/client.d.ts CHANGED
@@ -1,36 +1,31 @@
1
1
  /**
2
2
  * BotuyoApiClient - Thin HTTP wrapper around the BotUyo REST API
3
3
  * All MCP tools go through this client.
4
+ *
5
+ * Uses a JWT token directly (obtained via `npx @botuyo/mcp login`).
4
6
  */
5
7
  export interface BotuyoClientConfig {
6
8
  apiUrl: string;
7
- apiKey: string;
8
- }
9
- export interface AuthResult {
10
9
  token: string;
10
+ }
11
+ export interface AuthInfo {
11
12
  tenantId: string;
12
13
  tenantName: string;
13
14
  role: string;
14
- canWrite: boolean;
15
- channels: Array<{
16
- type: string;
17
- status: string;
18
- }>;
19
- adminPanelUrl: string;
15
+ email: string;
20
16
  }
21
17
  export declare class BotuyoApiClient {
22
18
  private config;
23
- private token;
24
- private authResult;
25
- private tokenExpiresAt;
19
+ private authInfo;
26
20
  constructor(config: BotuyoClientConfig);
27
- /** Exchange API key for a JWT and cache it for 55 minutes */
28
- authenticate(): Promise<AuthResult>;
21
+ /** Verify the stored JWT is still valid by calling GET /api/auth/me */
22
+ verify(): Promise<AuthInfo>;
29
23
  /** Make an authenticated GET request */
30
24
  get<T = any>(path: string): Promise<T>;
31
25
  /** Make an authenticated POST request */
32
26
  post<T = any>(path: string, data: unknown): Promise<T>;
33
27
  /** Make an authenticated PUT request */
34
28
  put<T = any>(path: string, data: unknown): Promise<T>;
29
+ private handleResponse;
35
30
  private parseJson;
36
31
  }
package/dist/client.js CHANGED
@@ -1,71 +1,74 @@
1
1
  /**
2
2
  * BotuyoApiClient - Thin HTTP wrapper around the BotUyo REST API
3
3
  * All MCP tools go through this client.
4
+ *
5
+ * Uses a JWT token directly (obtained via `npx @botuyo/mcp login`).
4
6
  */
5
7
  import fetch from 'node-fetch';
6
8
  export class BotuyoApiClient {
7
9
  config;
8
- token = null;
9
- authResult = null;
10
- tokenExpiresAt = 0;
10
+ authInfo = null;
11
11
  constructor(config) {
12
12
  this.config = config;
13
13
  }
14
- /** Exchange API key for a JWT and cache it for 55 minutes */
15
- async authenticate() {
16
- const now = Date.now();
17
- if (this.token && this.authResult && now < this.tokenExpiresAt) {
18
- return this.authResult;
19
- }
20
- const res = await fetch(`${this.config.apiUrl}/api/v1/mcp/auth`, {
21
- method: 'POST',
22
- headers: {
23
- 'x-api-key': this.config.apiKey,
24
- 'Content-Type': 'application/json'
25
- }
14
+ /** Verify the stored JWT is still valid by calling GET /api/auth/me */
15
+ async verify() {
16
+ if (this.authInfo)
17
+ return this.authInfo;
18
+ const res = await fetch(`${this.config.apiUrl}/api/auth/me`, {
19
+ headers: { Authorization: `Bearer ${this.config.token}` }
26
20
  });
27
21
  const body = await this.parseJson(res);
28
22
  if (!body.success) {
29
- throw new Error(`Authentication failed: ${body.error || 'Unknown error'}`);
23
+ throw new Error(`Session expired or invalid: ${body.error || 'Unknown error'}. Run: npx @botuyo/mcp login`);
30
24
  }
31
- this.token = body.data.token;
32
- this.authResult = body.data;
33
- this.tokenExpiresAt = now + 55 * 60 * 1000; // 55 min (token expires in 1h)
34
- return this.authResult;
25
+ const user = body.data.user;
26
+ this.authInfo = {
27
+ tenantId: user.tenantIds?.[0] || '',
28
+ tenantName: '', // Will be enriched from credentials
29
+ role: user.roles?.[0]?.role || 'member',
30
+ email: user.email
31
+ };
32
+ return this.authInfo;
35
33
  }
36
34
  /** Make an authenticated GET request */
37
35
  async get(path) {
38
- await this.authenticate();
39
36
  const res = await fetch(`${this.config.apiUrl}${path}`, {
40
- headers: { Authorization: `Bearer ${this.token}` }
37
+ headers: { Authorization: `Bearer ${this.config.token}` }
41
38
  });
42
- return this.parseJson(res);
39
+ return this.handleResponse(res);
43
40
  }
44
41
  /** Make an authenticated POST request */
45
42
  async post(path, data) {
46
- await this.authenticate();
47
43
  const res = await fetch(`${this.config.apiUrl}${path}`, {
48
44
  method: 'POST',
49
45
  headers: {
50
- Authorization: `Bearer ${this.token}`,
46
+ Authorization: `Bearer ${this.config.token}`,
51
47
  'Content-Type': 'application/json'
52
48
  },
53
49
  body: JSON.stringify(data)
54
50
  });
55
- return this.parseJson(res);
51
+ return this.handleResponse(res);
56
52
  }
57
53
  /** Make an authenticated PUT request */
58
54
  async put(path, data) {
59
- await this.authenticate();
60
55
  const res = await fetch(`${this.config.apiUrl}${path}`, {
61
56
  method: 'PUT',
62
57
  headers: {
63
- Authorization: `Bearer ${this.token}`,
58
+ Authorization: `Bearer ${this.config.token}`,
64
59
  'Content-Type': 'application/json'
65
60
  },
66
61
  body: JSON.stringify(data)
67
62
  });
68
- return this.parseJson(res);
63
+ return this.handleResponse(res);
64
+ }
65
+ async handleResponse(res) {
66
+ const json = await this.parseJson(res);
67
+ // Detect expired token
68
+ if (res.status === 401) {
69
+ throw new Error('Sesión expirada. Ejecutá: npx @botuyo/mcp login');
70
+ }
71
+ return json;
69
72
  }
70
73
  async parseJson(res) {
71
74
  const text = await res.text();
@@ -77,6 +80,8 @@ export class BotuyoApiClient {
77
80
  return json;
78
81
  }
79
82
  catch (e) {
83
+ if (e instanceof Error && e.message.includes('HTTP'))
84
+ throw e;
80
85
  throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
81
86
  }
82
87
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @botuyo/mcp auth command
4
+ *
5
+ * Usage: npx @botuyo/mcp auth
6
+ *
7
+ * Flow:
8
+ * 1. Starts a local HTTP server on a random port
9
+ * 2. Opens admin.botuyo.com/mcp-auth?redirect=http://localhost:PORT
10
+ * 3. The admin panel authenticates the user, lets them pick a tenant,
11
+ * calls POST /api/v1/mcp/oauth/authorize, then redirects to localhost with ?code=...
12
+ * 4. CLI exchanges the code for a permanent API key (POST /api/v1/mcp/oauth/token)
13
+ * 5. Saves the key to ~/.botuyo/credentials.json
14
+ */
15
+ export declare function runAuth(args: string[]): Promise<void>;
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @botuyo/mcp auth command
4
+ *
5
+ * Usage: npx @botuyo/mcp auth
6
+ *
7
+ * Flow:
8
+ * 1. Starts a local HTTP server on a random port
9
+ * 2. Opens admin.botuyo.com/mcp-auth?redirect=http://localhost:PORT
10
+ * 3. The admin panel authenticates the user, lets them pick a tenant,
11
+ * calls POST /api/v1/mcp/oauth/authorize, then redirects to localhost with ?code=...
12
+ * 4. CLI exchanges the code for a permanent API key (POST /api/v1/mcp/oauth/token)
13
+ * 5. Saves the key to ~/.botuyo/credentials.json
14
+ */
15
+ import http from 'http';
16
+ import { URL } from 'url';
17
+ import { execSync } from 'child_process';
18
+ import { readCredentials, saveCredentials } from './credentials.js';
19
+ import os from 'os';
20
+ const ADMIN_URL = process.env.BOTUYO_ADMIN_URL || 'https://admin.botuyo.com';
21
+ const API_URL = process.env.BOTUYO_API_URL || 'https://api.botuyo.com';
22
+ export async function runAuth(args) {
23
+ console.log('\n🤖 BotUyo MCP — Browser Authentication\n');
24
+ const existing = await readCredentials();
25
+ if (existing && !args.includes('--force')) {
26
+ console.log(`✅ Already authenticated as tenant: ${existing.tenantName} (${existing.role})`);
27
+ console.log(` Key: ${existing.key.slice(0, 14)}...`);
28
+ console.log('\n Run with --force to re-authenticate.\n');
29
+ return;
30
+ }
31
+ // Start local callback server
32
+ const { port, waitForCode } = await startCallbackServer();
33
+ const redirectUri = `http://localhost:${port}/callback`;
34
+ const deviceName = encodeURIComponent(`${os.hostname()} CLI`);
35
+ const authUrl = `${ADMIN_URL}/mcp-auth?redirect=${encodeURIComponent(redirectUri)}&device=${deviceName}`;
36
+ console.log(`📋 Opening browser to authorize...\n ${authUrl}\n`);
37
+ openBrowser(authUrl);
38
+ console.log('Waiting for authorization (10 minutes max)...\n');
39
+ const code = await waitForCode;
40
+ if (!code) {
41
+ console.error('❌ Authorization timed out or was cancelled.');
42
+ process.exit(1);
43
+ }
44
+ console.log('✅ Authorization code received! Exchanging for API key...');
45
+ // Exchange for permanent API key
46
+ const tokenRes = await fetch(`${API_URL}/api/v1/mcp/oauth/token`, {
47
+ method: 'POST',
48
+ headers: { 'Content-Type': 'application/json' },
49
+ body: JSON.stringify({ code, name: `${os.hostname()} CLI` })
50
+ });
51
+ const tokenData = (await tokenRes.json());
52
+ if (!tokenData.success) {
53
+ console.error(`❌ Token exchange failed: ${tokenData.error}`);
54
+ process.exit(1);
55
+ }
56
+ const creds = {
57
+ key: tokenData.data.key,
58
+ tenantId: tokenData.data.tenantId,
59
+ tenantName: tokenData.data.tenantName,
60
+ role: tokenData.data.role,
61
+ savedAt: new Date().toISOString()
62
+ };
63
+ await saveCredentials(creds);
64
+ console.log(`\n✅ Authenticated successfully!`);
65
+ console.log(` Tenant: ${creds.tenantName}`);
66
+ console.log(` Role: ${creds.role}`);
67
+ console.log(` Key: ${creds.key.slice(0, 14)}...`);
68
+ console.log(`\n🚀 You can now use the BotUyo MCP server.\n`);
69
+ }
70
+ // ── Local callback server ─────────────────────────────────────────────────────
71
+ function startCallbackServer() {
72
+ return new Promise((resolve) => {
73
+ let resolveCode;
74
+ const waitForCode = new Promise((res) => {
75
+ resolveCode = res;
76
+ });
77
+ const server = http.createServer((req, res) => {
78
+ const url = new URL(req.url || '/', `http://localhost`);
79
+ if (url.pathname === '/callback') {
80
+ const code = url.searchParams.get('code');
81
+ const html = code
82
+ ? `<html><body style="font-family:sans-serif;text-align:center;padding:80px">
83
+ <h2>✅ BotUyo CLI Authorized</h2>
84
+ <p>You can close this tab and return to the terminal.</p>
85
+ </body></html>`
86
+ : `<html><body style="font-family:sans-serif;text-align:center;padding:80px">
87
+ <h2>❌ Authorization failed</h2>
88
+ <p>No code received. Please try again.</p>
89
+ </body></html>`;
90
+ res.writeHead(200, { 'Content-Type': 'text/html' });
91
+ res.end(html);
92
+ server.close();
93
+ resolveCode(code);
94
+ }
95
+ else {
96
+ res.writeHead(404);
97
+ res.end();
98
+ }
99
+ });
100
+ server.listen(0, 'localhost', () => {
101
+ const port = server.address().port;
102
+ // Timeout after 10 minutes
103
+ setTimeout(() => { server.close(); resolveCode(null); }, 10 * 60 * 1000);
104
+ resolve({ port, waitForCode });
105
+ });
106
+ });
107
+ }
108
+ // ── Open browser cross-platform ───────────────────────────────────────────────
109
+ function openBrowser(url) {
110
+ const platform = process.platform;
111
+ try {
112
+ if (platform === 'win32')
113
+ execSync(`start "" "${url}"`, { stdio: 'ignore' });
114
+ else if (platform === 'darwin')
115
+ execSync(`open "${url}"`, { stdio: 'ignore' });
116
+ else
117
+ execSync(`xdg-open "${url}"`, { stdio: 'ignore' });
118
+ }
119
+ catch {
120
+ console.log(` Could not open browser automatically. Please visit:\n ${url}\n`);
121
+ }
122
+ }
123
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Credential storage for @botuyo/mcp
3
+ * Saves/reads JWT tokens to/from ~/.botuyo/credentials.json
4
+ */
5
+ export interface BotuyoCredentials {
6
+ token: string;
7
+ tenantId: string;
8
+ tenantName: string;
9
+ role: string;
10
+ email: string;
11
+ savedAt: string;
12
+ expiresAt: string;
13
+ }
14
+ export declare function readCredentials(): Promise<BotuyoCredentials | null>;
15
+ export declare function saveCredentials(creds: BotuyoCredentials): Promise<void>;
16
+ export declare function clearCredentials(): Promise<void>;
17
+ /**
18
+ * Resolve the JWT token to use, in priority order:
19
+ * 1. BOTUYO_TOKEN env var
20
+ * 2. ~/.botuyo/credentials.json
21
+ *
22
+ * Returns null if no token or if expired.
23
+ */
24
+ export declare function resolveToken(): Promise<string | null>;
25
+ /**
26
+ * Check if stored credentials are expired
27
+ */
28
+ export declare function isTokenExpired(): Promise<boolean>;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Credential storage for @botuyo/mcp
3
+ * Saves/reads JWT tokens to/from ~/.botuyo/credentials.json
4
+ */
5
+ import { readFile, writeFile, mkdir, unlink } from 'fs/promises';
6
+ import { join } from 'path';
7
+ import { homedir } from 'os';
8
+ const CONFIG_DIR = join(homedir(), '.botuyo');
9
+ const CREDENTIALS_FILE = join(CONFIG_DIR, 'credentials.json');
10
+ export async function readCredentials() {
11
+ try {
12
+ const raw = await readFile(CREDENTIALS_FILE, 'utf8');
13
+ return JSON.parse(raw);
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ export async function saveCredentials(creds) {
20
+ await mkdir(CONFIG_DIR, { recursive: true });
21
+ await writeFile(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), 'utf8');
22
+ }
23
+ export async function clearCredentials() {
24
+ try {
25
+ await unlink(CREDENTIALS_FILE);
26
+ }
27
+ catch {
28
+ // already gone
29
+ }
30
+ }
31
+ /**
32
+ * Resolve the JWT token to use, in priority order:
33
+ * 1. BOTUYO_TOKEN env var
34
+ * 2. ~/.botuyo/credentials.json
35
+ *
36
+ * Returns null if no token or if expired.
37
+ */
38
+ export async function resolveToken() {
39
+ if (process.env.BOTUYO_TOKEN)
40
+ return process.env.BOTUYO_TOKEN;
41
+ const creds = await readCredentials();
42
+ if (!creds?.token)
43
+ return null;
44
+ // Check if expired
45
+ if (creds.expiresAt && new Date(creds.expiresAt) < new Date()) {
46
+ return null; // expired
47
+ }
48
+ return creds.token;
49
+ }
50
+ /**
51
+ * Check if stored credentials are expired
52
+ */
53
+ export async function isTokenExpired() {
54
+ const creds = await readCredentials();
55
+ if (!creds?.expiresAt)
56
+ return true;
57
+ return new Date(creds.expiresAt) < new Date();
58
+ }
59
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/commands/credentials.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AAE5B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAA;AAC7C,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAA;AAY7D,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAA;QACpD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAsB,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAwB;IAC5D,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5C,MAAM,SAAS,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;AAC3E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAA;IAE7D,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK,EAAE,KAAK;QAAE,OAAO,IAAI,CAAA;IAE9B,mBAAmB;IACnB,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;QAC9D,OAAO,IAAI,CAAA,CAAC,UAAU;IACxB,CAAC;IAED,OAAO,KAAK,CAAC,KAAK,CAAA;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK,EAAE,SAAS;QAAE,OAAO,IAAI,CAAA;IAClC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,CAAA;AAC/C,CAAC"}
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @botuyo/mcp login command
4
+ *
5
+ * Usage: npx @botuyo/mcp login
6
+ *
7
+ * Flow:
8
+ * 1. Prompts for email and password in the terminal
9
+ * 2. Calls POST /api/auth/login to get a JWT
10
+ * 3. Calls GET /api/auth/me to get the user's tenants
11
+ * 4. If multiple tenants, asks the user to pick one
12
+ * 5. If needed, calls POST /api/auth/switch-tenant
13
+ * 6. Saves the credentials to ~/.botuyo/credentials.json
14
+ */
15
+ export declare function runLogin(args: string[]): Promise<void>;
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @botuyo/mcp login command
4
+ *
5
+ * Usage: npx @botuyo/mcp login
6
+ *
7
+ * Flow:
8
+ * 1. Prompts for email and password in the terminal
9
+ * 2. Calls POST /api/auth/login to get a JWT
10
+ * 3. Calls GET /api/auth/me to get the user's tenants
11
+ * 4. If multiple tenants, asks the user to pick one
12
+ * 5. If needed, calls POST /api/auth/switch-tenant
13
+ * 6. Saves the credentials to ~/.botuyo/credentials.json
14
+ */
15
+ import * as readline from 'readline';
16
+ import { readCredentials, saveCredentials } from './credentials.js';
17
+ const API_URL = process.env.BOTUYO_API_URL || 'https://api.botuyo.com';
18
+ export async function runLogin(args) {
19
+ console.log('\n🤖 BotUyo MCP — Login\n');
20
+ const existing = await readCredentials();
21
+ if (existing && !args.includes('--force')) {
22
+ const expired = existing.expiresAt && new Date(existing.expiresAt) < new Date();
23
+ if (!expired) {
24
+ console.log(`✅ Ya estás autenticado como: ${existing.email}`);
25
+ console.log(` Tenant: ${existing.tenantName} (${existing.role})`);
26
+ console.log('\n Usá --force para re-autenticarte.\n');
27
+ return;
28
+ }
29
+ console.log('⚠️ Tu sesión expiró. Vamos a re-autenticarte.\n');
30
+ }
31
+ // Prompt for email and password
32
+ const email = await prompt('📧 Email: ');
33
+ const password = await promptPassword('🔑 Password: ');
34
+ if (!email || !password) {
35
+ console.error('❌ Email y password son requeridos.');
36
+ process.exit(1);
37
+ }
38
+ console.log('\n⏳ Autenticando...');
39
+ // 1. Login
40
+ const loginRes = await fetch(`${API_URL}/api/auth/login`, {
41
+ method: 'POST',
42
+ headers: { 'Content-Type': 'application/json' },
43
+ body: JSON.stringify({ email: email.trim(), password })
44
+ });
45
+ const loginData = (await loginRes.json());
46
+ if (!loginData.success) {
47
+ console.error(`❌ Login fallido: ${loginData.error || 'Credenciales inválidas'}`);
48
+ process.exit(1);
49
+ }
50
+ let token = loginData.data.token;
51
+ let tenantId = loginData.data.tenantId;
52
+ // 2. Get user info and tenants
53
+ const meRes = await fetch(`${API_URL}/api/auth/me`, {
54
+ headers: { Authorization: `Bearer ${token}` }
55
+ });
56
+ const meData = (await meRes.json());
57
+ if (!meData.success) {
58
+ console.error(`❌ Error al obtener info del usuario: ${meData.error}`);
59
+ process.exit(1);
60
+ }
61
+ const user = meData.data.user;
62
+ const tenantIds = user.tenantIds || [];
63
+ const roles = user.roles || [];
64
+ // 3. If multiple tenants, ask user to pick
65
+ if (tenantIds.length > 1) {
66
+ console.log(`\n📋 Tenés ${tenantIds.length} tenants disponibles:\n`);
67
+ // Fetch tenant names for display
68
+ const tenantInfos = await Promise.all(tenantIds.map(async (tid, i) => {
69
+ const role = roles.find((r) => r.tenantId === tid)?.role || 'member';
70
+ const isActive = tid === tenantId;
71
+ return { id: tid, role, isActive, index: i + 1 };
72
+ }));
73
+ for (const t of tenantInfos) {
74
+ const marker = t.isActive ? ' ← actual' : '';
75
+ console.log(` ${t.index}. ${t.id} (${t.role})${marker}`);
76
+ }
77
+ const choice = await prompt('\n¿Qué tenant querés usar? (número, Enter para el actual): ');
78
+ const choiceNum = parseInt(choice, 10);
79
+ if (choice.trim() && choiceNum >= 1 && choiceNum <= tenantInfos.length) {
80
+ const selectedTenant = tenantInfos[choiceNum - 1];
81
+ if (selectedTenant.id !== tenantId) {
82
+ // Switch tenant
83
+ console.log(`\n⏳ Cambiando a tenant ${selectedTenant.id}...`);
84
+ const switchRes = await fetch(`${API_URL}/api/auth/switch-tenant`, {
85
+ method: 'POST',
86
+ headers: {
87
+ Authorization: `Bearer ${token}`,
88
+ 'Content-Type': 'application/json'
89
+ },
90
+ body: JSON.stringify({ tenantId: selectedTenant.id })
91
+ });
92
+ const switchData = (await switchRes.json());
93
+ if (!switchData.success) {
94
+ console.error(`❌ Error al cambiar de tenant: ${switchData.error}`);
95
+ process.exit(1);
96
+ }
97
+ token = switchData.data.token;
98
+ tenantId = selectedTenant.id;
99
+ }
100
+ }
101
+ }
102
+ // 4. Get the role for the active tenant
103
+ const activeRole = roles.find((r) => r.tenantId === tenantId)?.role || 'member';
104
+ // 5. Save credentials
105
+ // JWT from AuthService expires in 7 days
106
+ const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
107
+ const creds = {
108
+ token,
109
+ tenantId,
110
+ tenantName: tenantId, // We'll update this with actual name below
111
+ role: activeRole,
112
+ email: email.trim(),
113
+ savedAt: new Date().toISOString(),
114
+ expiresAt
115
+ };
116
+ // Try to get the tenant name
117
+ try {
118
+ const tenantRes = await fetch(`${API_URL}/api/v1/tenants/${tenantId}`, {
119
+ headers: { Authorization: `Bearer ${token}` }
120
+ });
121
+ const tenantData = (await tenantRes.json());
122
+ if (tenantData.success && tenantData.data?.name) {
123
+ creds.tenantName = tenantData.data.name;
124
+ }
125
+ else if (tenantData.data?.tenant?.name) {
126
+ creds.tenantName = tenantData.data.tenant.name;
127
+ }
128
+ }
129
+ catch {
130
+ // If we can't get the name, tenantId is fine
131
+ }
132
+ await saveCredentials(creds);
133
+ console.log(`\n✅ ¡Autenticado exitosamente!`);
134
+ console.log(` Email: ${creds.email}`);
135
+ console.log(` Tenant: ${creds.tenantName}`);
136
+ console.log(` Role: ${creds.role}`);
137
+ console.log(` Expira: ${new Date(creds.expiresAt).toLocaleDateString()}`);
138
+ console.log(`\n🚀 Ya podés usar el servidor MCP de BotUyo.\n`);
139
+ }
140
+ // ── Prompts ───────────────────────────────────────────────────────────────────
141
+ function prompt(question) {
142
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
143
+ return new Promise((resolve) => {
144
+ rl.question(question, (answer) => {
145
+ rl.close();
146
+ resolve(answer);
147
+ });
148
+ });
149
+ }
150
+ function promptPassword(question) {
151
+ return new Promise((resolve) => {
152
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
153
+ // Attempt to hide input on supported terminals
154
+ if (process.stdin.isTTY) {
155
+ process.stdout.write(question);
156
+ const stdin = process.stdin;
157
+ stdin.setRawMode(true);
158
+ stdin.resume();
159
+ stdin.setEncoding('utf8');
160
+ let password = '';
161
+ const onData = (ch) => {
162
+ const c = ch.toString();
163
+ switch (c) {
164
+ case '\n':
165
+ case '\r':
166
+ case '\u0004': // Ctrl+D
167
+ stdin.setRawMode(false);
168
+ stdin.pause();
169
+ stdin.removeListener('data', onData);
170
+ rl.close();
171
+ process.stdout.write('\n');
172
+ resolve(password);
173
+ break;
174
+ case '\u0003': // Ctrl+C
175
+ process.exit();
176
+ break;
177
+ case '\u007F': // Backspace
178
+ if (password.length > 0) {
179
+ password = password.slice(0, -1);
180
+ process.stdout.clearLine(0);
181
+ process.stdout.cursorTo(0);
182
+ process.stdout.write(question + '*'.repeat(password.length));
183
+ }
184
+ break;
185
+ default:
186
+ password += c;
187
+ process.stdout.write('*');
188
+ break;
189
+ }
190
+ };
191
+ stdin.on('data', onData);
192
+ }
193
+ else {
194
+ // Non-TTY fallback (piped input)
195
+ rl.question(question, (answer) => {
196
+ rl.close();
197
+ resolve(answer);
198
+ });
199
+ }
200
+ });
201
+ }
202
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}