@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 +9 -14
- package/dist/client.js +34 -29
- package/dist/client.js.map +1 -1
- package/dist/commands/auth.d.ts +15 -0
- package/dist/commands/auth.js +123 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/credentials.d.ts +28 -0
- package/dist/commands/credentials.js +59 -0
- package/dist/commands/credentials.js.map +1 -0
- package/dist/commands/login.d.ts +15 -0
- package/dist/commands/login.js +202 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/setup.d.ts +8 -0
- package/dist/commands/setup.js +95 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/switch_tenant.d.ts +10 -0
- package/dist/commands/switch_tenant.js +117 -0
- package/dist/commands/switch_tenant.js.map +1 -0
- package/dist/commands/tenants.d.ts +9 -0
- package/dist/commands/tenants.js +48 -0
- package/dist/commands/tenants.js.map +1 -0
- package/dist/index.d.ts +13 -14
- package/dist/index.js +120 -57
- package/dist/index.js.map +1 -1
- package/package.json +12 -3
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
|
-
|
|
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
|
|
24
|
-
private authResult;
|
|
25
|
-
private tokenExpiresAt;
|
|
19
|
+
private authInfo;
|
|
26
20
|
constructor(config: BotuyoClientConfig);
|
|
27
|
-
/**
|
|
28
|
-
|
|
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
|
-
|
|
9
|
-
authResult = null;
|
|
10
|
-
tokenExpiresAt = 0;
|
|
10
|
+
authInfo = null;
|
|
11
11
|
constructor(config) {
|
|
12
12
|
this.config = config;
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
async
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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(`
|
|
23
|
+
throw new Error(`Session expired or invalid: ${body.error || 'Unknown error'}. Run: npx @botuyo/mcp login`);
|
|
30
24
|
}
|
|
31
|
-
|
|
32
|
-
this.
|
|
33
|
-
|
|
34
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
}
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA
|
|
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":";AACA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAqB,MAAM,kBAAkB,CAAA;AAEtF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,wBAAwB,CAAA;AAEtE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAc;IAC3C,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;IAExC,MAAM,QAAQ,GAAG,MAAM,eAAe,EAAE,CAAA;IACxC,IAAI,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,CAAA;QAC/E,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,GAAG,CAAC,gCAAgC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;YAC7D,OAAO,CAAC,GAAG,CAAC,cAAc,QAAQ,CAAC,UAAU,KAAK,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAA;YACnE,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAA;YACvD,OAAM;QACR,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAA;IACjE,CAAC;IAED,gCAAgC;IAChC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAA;IACxC,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,eAAe,CAAC,CAAA;IAEtD,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAA;QACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;IAElC,WAAW;IACX,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,iBAAiB,EAAE;QACxD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC;KACxD,CAAC,CAAA;IAEF,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAQ,CAAA;IAChD,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,oBAAoB,SAAS,CAAC,KAAK,IAAI,wBAAwB,EAAE,CAAC,CAAA;QAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,IAAI,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAA;IAChC,IAAI,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAA;IAEtC,+BAA+B;IAC/B,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,cAAc,EAAE;QAClD,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;KAC9C,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAQ,CAAA;IAE1C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,wCAAwC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAA;IAC7B,MAAM,SAAS,GAAa,IAAI,CAAC,SAAS,IAAI,EAAE,CAAA;IAChD,MAAM,KAAK,GAA8C,IAAI,CAAC,KAAK,IAAI,EAAE,CAAA;IAEzE,2CAA2C;IAC3C,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,cAAc,SAAS,CAAC,MAAM,yBAAyB,CAAC,CAAA;QAEpE,iCAAiC;QACjC,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CACnC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAW,EAAE,CAAS,EAAE,EAAE;YAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,GAAG,CAAC,EAAE,IAAI,IAAI,QAAQ,CAAA;YACzE,MAAM,QAAQ,GAAG,GAAG,KAAK,QAAQ,CAAA;YACjC,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAA;QAClD,CAAC,CAAC,CACH,CAAA;QAED,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;YAC5C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC,CAAA;QAC5D,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,6DAA6D,CAAC,CAAA;QAC1F,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QAEtC,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;YACvE,MAAM,cAAc,GAAG,WAAW,CAAC,SAAS,GAAG,CAAC,CAAC,CAAA;YACjD,IAAI,cAAc,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;gBACnC,gBAAgB;gBAChB,OAAO,CAAC,GAAG,CAAC,0BAA0B,cAAc,CAAC,EAAE,KAAK,CAAC,CAAA;gBAC7D,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,yBAAyB,EAAE;oBACjE,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE;wBACP,aAAa,EAAE,UAAU,KAAK,EAAE;wBAChC,cAAc,EAAE,kBAAkB;qBACnC;oBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,cAAc,CAAC,EAAE,EAAE,CAAC;iBACtD,CAAC,CAAA;gBACF,MAAM,UAAU,GAAG,CAAC,MAAM,SAAS,CAAC,IAAI,EAAE,CAAQ,CAAA;gBAClD,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;oBACxB,OAAO,CAAC,KAAK,CAAC,iCAAiC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAA;oBAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;gBACjB,CAAC;gBACD,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAA;gBAC7B,QAAQ,GAAG,cAAc,CAAC,EAAE,CAAA;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAED,wCAAwC;IACxC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,EAAE,IAAI,IAAI,QAAQ,CAAA;IAEpF,sBAAsB;IACtB,yCAAyC;IACzC,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAA;IAE9E,MAAM,KAAK,GAAsB;QAC/B,KAAK;QACL,QAAQ;QACR,UAAU,EAAE,QAAQ,EAAE,2CAA2C;QACjE,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE;QACnB,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACjC,SAAS;KACV,CAAA;IAED,6BAA6B;IAC7B,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,mBAAmB,QAAQ,EAAE,EAAE;YACrE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;SAC9C,CAAC,CAAA;QACF,MAAM,UAAU,GAAG,CAAC,MAAM,SAAS,CAAC,IAAI,EAAE,CAAQ,CAAA;QAClD,IAAI,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;YAChD,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAA;QACzC,CAAC;aAAM,IAAI,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YACzC,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAA;QAChD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6CAA6C;IAC/C,CAAC;IAED,MAAM,eAAe,CAAC,KAAK,CAAC,CAAA;IAE5B,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAA;IAC7C,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA;IACzC,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,UAAU,EAAE,CAAC,CAAA;IAC9C,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IACxC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAA;IAC5E,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAA;AAChE,CAAC;AAED,iFAAiF;AAEjF,SAAS,MAAM,CAAC,QAAgB;IAC9B,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACrF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;YAC/B,EAAE,CAAC,KAAK,EAAE,CAAA;YACV,OAAO,CAAC,MAAM,CAAC,CAAA;QACjB,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,QAAgB;IACtC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QAErF,+CAA+C;QAC/C,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;YAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAA;YAC3B,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;YACtB,KAAK,CAAC,MAAM,EAAE,CAAA;YACd,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAEzB,IAAI,QAAQ,GAAG,EAAE,CAAA;YACjB,MAAM,MAAM,GAAG,CAAC,EAAU,EAAE,EAAE;gBAC5B,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAA;gBACvB,QAAQ,CAAC,EAAE,CAAC;oBACV,KAAK,IAAI,CAAC;oBACV,KAAK,IAAI,CAAC;oBACV,KAAK,QAAQ,EAAE,SAAS;wBACtB,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;wBACvB,KAAK,CAAC,KAAK,EAAE,CAAA;wBACb,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;wBACpC,EAAE,CAAC,KAAK,EAAE,CAAA;wBACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;wBAC1B,OAAO,CAAC,QAAQ,CAAC,CAAA;wBACjB,MAAK;oBACP,KAAK,QAAQ,EAAE,SAAS;wBACtB,OAAO,CAAC,IAAI,EAAE,CAAA;wBACd,MAAK;oBACP,KAAK,QAAQ,EAAE,YAAY;wBACzB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;4BACxB,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;4BAChC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;4BAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;4BAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAA;wBAC9D,CAAC;wBACD,MAAK;oBACP;wBACE,QAAQ,IAAI,CAAC,CAAA;wBACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;wBACzB,MAAK;gBACT,CAAC;YACH,CAAC,CAAA;YACD,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC1B,CAAC;aAAM,CAAC;YACN,iCAAiC;YACjC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;gBAC/B,EAAE,CAAC,KAAK,EAAE,CAAA;gBACV,OAAO,CAAC,MAAM,CAAC,CAAA;YACjB,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
|