@myvtp/mcp 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 myvtp
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # @myvtp/mcp
2
+
3
+ MCP (Model Context Protocol) server for VTP - deploy apps via Claude Code.
4
+
5
+ ## Installation
6
+
7
+ Add to your Claude Code configuration (`~/.claude.json`):
8
+
9
+ ```json
10
+ {
11
+ "mcpServers": {
12
+ "vtp": {
13
+ "command": "npx",
14
+ "args": ["-y", "@myvtp/mcp"]
15
+ }
16
+ }
17
+ }
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ Once configured, simply ask Claude Code to deploy your app:
23
+
24
+ ```
25
+ Deploy this app to VTP
26
+ ```
27
+
28
+ Claude will analyse your project, create the necessary configuration, and deploy it.
29
+
30
+ ### Available Tools
31
+
32
+ | Tool | Description |
33
+ |------|-------------|
34
+ | `deploy` | Deploy an app from a vtp.yaml config |
35
+ | `list` | List all deployed apps |
36
+ | `list_app_types` | Show supported app types |
37
+ | `get_deployment_guide` | Get detailed deployment instructions |
38
+
39
+ ## Configuration
40
+
41
+ The MCP server connects to the VTP API. By default it uses `https://api.myvtp.app`.
42
+
43
+ To use a different API URL (e.g., for local development):
44
+
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "vtp": {
49
+ "command": "npx",
50
+ "args": ["-y", "@myvtp/mcp"],
51
+ "env": {
52
+ "VTP_API_URL": "https://api.myvtp.dev"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ## Authentication
60
+
61
+ On first use, the MCP server will open your browser for authentication. Your credentials are stored locally at `~/.vtp/credentials.json`.
62
+
63
+ ## License
64
+
65
+ MIT
@@ -0,0 +1,46 @@
1
+ export interface App {
2
+ id: string;
3
+ name: string;
4
+ description?: string;
5
+ type: string;
6
+ status: string;
7
+ url: string;
8
+ containerId: string;
9
+ createdAt: string;
10
+ imageName: string;
11
+ }
12
+ export interface DeployResult {
13
+ app?: App;
14
+ replaced?: boolean;
15
+ error?: string;
16
+ message?: string;
17
+ existingApp?: App;
18
+ }
19
+ export interface AppTypeInfo {
20
+ type: string;
21
+ displayName: string;
22
+ description: string;
23
+ aliases: string[];
24
+ apiType: 'static' | 'node';
25
+ }
26
+ export interface DeploymentGuide extends AppTypeInfo {
27
+ content: string;
28
+ }
29
+ /**
30
+ * List all deployed apps.
31
+ */
32
+ export declare function listApps(): Promise<App[]>;
33
+ /**
34
+ * List all supported app types.
35
+ */
36
+ export declare function listAppTypes(): Promise<AppTypeInfo[]>;
37
+ /**
38
+ * Get deployment guide for a specific app type.
39
+ */
40
+ export declare function getDeploymentGuide(type: string): Promise<DeploymentGuide>;
41
+ /**
42
+ * Deploy an app from a local directory.
43
+ * Reads vtp.yaml for all config including deploy path, creates tar.gz archive, and POSTs to the API.
44
+ */
45
+ export declare function deploy(configPath?: string, force?: boolean): Promise<DeployResult>;
46
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAwCA,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,GAAG,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AAyTD;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAE/C;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAG3D;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAE/E;AAuDD;;;GAGG;AACH,wBAAsB,MAAM,CAC1B,UAAU,GAAE,MAAqB,EACjC,KAAK,GAAE,OAAe,GACrB,OAAO,CAAC,YAAY,CAAC,CA6CvB"}
package/dist/client.js ADDED
@@ -0,0 +1,430 @@
1
+ import { createReadStream, existsSync, chmodSync } from 'fs';
2
+ import { unlink, copyFile, readFile, writeFile, mkdir } from 'fs/promises';
3
+ import { tmpdir, homedir } from 'os';
4
+ import { join, dirname, resolve } from 'path';
5
+ import { exec } from 'child_process';
6
+ import { promisify } from 'util';
7
+ import FormData from 'form-data';
8
+ import { parse as parseYaml } from 'yaml';
9
+ import * as tar from 'tar';
10
+ import { getFilesToInclude } from './utils/ignoreFilter.js';
11
+ const execAsync = promisify(exec);
12
+ const API_BASE = process.env.VTP_API_URL || 'https://api.myvtp.app';
13
+ const CREDENTIALS_PATH = join(homedir(), '.vtp', 'credentials.json');
14
+ // =============================================================================
15
+ // Authentication Functions
16
+ // =============================================================================
17
+ /**
18
+ * Load credentials from disk.
19
+ */
20
+ async function loadCredentials() {
21
+ try {
22
+ if (!existsSync(CREDENTIALS_PATH)) {
23
+ return null;
24
+ }
25
+ const content = await readFile(CREDENTIALS_PATH, 'utf-8');
26
+ return JSON.parse(content);
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ /**
33
+ * Save credentials to disk with restricted permissions.
34
+ */
35
+ async function saveCredentials(credentials) {
36
+ const dir = dirname(CREDENTIALS_PATH);
37
+ await mkdir(dir, { recursive: true });
38
+ await writeFile(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2));
39
+ // Set file permissions to 0600 (owner read/write only)
40
+ chmodSync(CREDENTIALS_PATH, 0o600);
41
+ }
42
+ /**
43
+ * Delete credentials from disk.
44
+ */
45
+ async function deleteCredentials() {
46
+ try {
47
+ await unlink(CREDENTIALS_PATH);
48
+ }
49
+ catch {
50
+ // Ignore if file doesn't exist
51
+ }
52
+ }
53
+ /**
54
+ * Check if credentials are expired (with 5 minute buffer).
55
+ */
56
+ function isTokenExpired(credentials) {
57
+ const bufferMs = 5 * 60 * 1000; // 5 minutes
58
+ return Date.now() >= (credentials.expiresAt - bufferMs);
59
+ }
60
+ /**
61
+ * Refresh the access token using the refresh token.
62
+ */
63
+ async function refreshAccessToken(refreshToken) {
64
+ try {
65
+ const response = await fetch(`${API_BASE}/auth/refresh`, {
66
+ method: 'POST',
67
+ headers: { 'Content-Type': 'application/json' },
68
+ body: JSON.stringify({ refreshToken }),
69
+ });
70
+ if (!response.ok) {
71
+ return null;
72
+ }
73
+ const data = await response.json();
74
+ const expiresAt = Date.now() + (24 * 60 * 60 * 1000); // 24 hours
75
+ return {
76
+ accessToken: data.accessToken,
77
+ refreshToken,
78
+ expiresAt,
79
+ };
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ }
85
+ /**
86
+ * Start the device authorization flow.
87
+ */
88
+ async function startDeviceFlow() {
89
+ const response = await fetch(`${API_BASE}/auth/device/start`, {
90
+ method: 'POST',
91
+ });
92
+ if (!response.ok) {
93
+ throw new Error('Failed to start device flow');
94
+ }
95
+ return response.json();
96
+ }
97
+ /**
98
+ * Poll for device authorization completion.
99
+ */
100
+ async function pollDeviceFlow(deviceCode) {
101
+ const response = await fetch(`${API_BASE}/auth/device/poll`, {
102
+ method: 'POST',
103
+ headers: { 'Content-Type': 'application/json' },
104
+ body: JSON.stringify({ device_code: deviceCode }),
105
+ });
106
+ if (response.status === 428) {
107
+ return 'pending';
108
+ }
109
+ if (response.status === 403) {
110
+ return 'denied';
111
+ }
112
+ if (response.status === 410) {
113
+ return 'expired';
114
+ }
115
+ if (!response.ok) {
116
+ throw new Error('Device flow poll failed');
117
+ }
118
+ return response.json();
119
+ }
120
+ /**
121
+ * Open a URL in the default browser.
122
+ */
123
+ async function openBrowser(url) {
124
+ const platform = process.platform;
125
+ let cmd;
126
+ if (platform === 'darwin') {
127
+ cmd = `open "${url}"`;
128
+ }
129
+ else if (platform === 'win32') {
130
+ cmd = `start "" "${url}"`;
131
+ }
132
+ else {
133
+ cmd = `xdg-open "${url}"`;
134
+ }
135
+ await execAsync(cmd);
136
+ }
137
+ /**
138
+ * Perform device flow authentication.
139
+ * Returns credentials on success, throws on failure.
140
+ */
141
+ async function performDeviceFlow() {
142
+ // Start the device flow
143
+ const deviceFlow = await startDeviceFlow();
144
+ // Notify user and open browser
145
+ console.error(`\n┌─────────────────────────────────────────────────┐`);
146
+ console.error(`│ VTP Authentication Required │`);
147
+ console.error(`├─────────────────────────────────────────────────┤`);
148
+ console.error(`│ Opening browser to complete authentication... │`);
149
+ console.error(`│ │`);
150
+ console.error(`│ If the browser doesn't open, visit: │`);
151
+ console.error(`│ ${deviceFlow.verification_uri.padEnd(41)} │`);
152
+ console.error(`│ │`);
153
+ console.error(`│ And enter code: ${deviceFlow.user_code.padEnd(28)} │`);
154
+ console.error(`└─────────────────────────────────────────────────┘\n`);
155
+ // Open browser
156
+ try {
157
+ await openBrowser(deviceFlow.verification_uri_complete);
158
+ }
159
+ catch {
160
+ // Browser failed to open, user will need to manually visit the URL
161
+ }
162
+ // Poll for completion
163
+ const pollIntervalMs = deviceFlow.interval * 1000;
164
+ const expiresAt = Date.now() + (deviceFlow.expires_in * 1000);
165
+ while (Date.now() < expiresAt) {
166
+ await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
167
+ const result = await pollDeviceFlow(deviceFlow.device_code);
168
+ if (result === 'pending') {
169
+ continue;
170
+ }
171
+ if (result === 'denied') {
172
+ throw new Error('Authorization denied by user');
173
+ }
174
+ if (result === 'expired') {
175
+ throw new Error('Device code expired. Please try again.');
176
+ }
177
+ // Success - we have tokens
178
+ const credentials = {
179
+ accessToken: result.access_token,
180
+ refreshToken: result.refresh_token,
181
+ expiresAt: Date.now() + (result.expires_in * 1000),
182
+ };
183
+ // Save credentials
184
+ await saveCredentials(credentials);
185
+ console.error(`\n✓ Authentication successful!\n`);
186
+ return credentials;
187
+ }
188
+ throw new Error('Device code expired. Please try again.');
189
+ }
190
+ /**
191
+ * Ensure we have valid credentials.
192
+ * Will refresh token if expired, or start device flow if no valid credentials.
193
+ */
194
+ async function ensureAuthenticated() {
195
+ let credentials = await loadCredentials();
196
+ // No credentials - need to authenticate
197
+ if (!credentials) {
198
+ credentials = await performDeviceFlow();
199
+ return credentials.accessToken;
200
+ }
201
+ // Token expired - try to refresh
202
+ if (isTokenExpired(credentials)) {
203
+ const refreshed = await refreshAccessToken(credentials.refreshToken);
204
+ if (refreshed) {
205
+ await saveCredentials(refreshed);
206
+ return refreshed.accessToken;
207
+ }
208
+ // Refresh failed - need to re-authenticate
209
+ await deleteCredentials();
210
+ credentials = await performDeviceFlow();
211
+ return credentials.accessToken;
212
+ }
213
+ return credentials.accessToken;
214
+ }
215
+ // =============================================================================
216
+ // API Request Helper
217
+ // =============================================================================
218
+ /**
219
+ * Generic HTTP request helper with authentication and connection error handling.
220
+ */
221
+ async function apiRequest(method, path, body, retried = false) {
222
+ const url = `${API_BASE}${path}`;
223
+ // Get valid access token
224
+ const accessToken = await ensureAuthenticated();
225
+ try {
226
+ const headers = {
227
+ 'Authorization': `Bearer ${accessToken}`,
228
+ };
229
+ if (body) {
230
+ headers['Content-Type'] = 'application/json';
231
+ }
232
+ const options = {
233
+ method,
234
+ headers,
235
+ body: body ? JSON.stringify(body) : undefined,
236
+ };
237
+ const response = await fetch(url, options);
238
+ // Handle 401 - token might have been invalidated, try re-auth once
239
+ if (response.status === 401) {
240
+ await deleteCredentials();
241
+ if (!retried) {
242
+ return apiRequest(method, path, body, true);
243
+ }
244
+ throw new Error('Authentication failed. Please check your credentials.');
245
+ }
246
+ if (!response.ok) {
247
+ const error = await response.json().catch(() => ({ error: `HTTP ${response.status}` }));
248
+ throw new Error(error.message || error.error || `HTTP ${response.status}`);
249
+ }
250
+ return response.json();
251
+ }
252
+ catch (error) {
253
+ // Handle connection errors
254
+ if (error instanceof TypeError && error.message.includes('fetch failed')) {
255
+ throw new Error(`Cannot connect to VTP API at ${API_BASE}. ` +
256
+ `Make sure the API server is running (pnpm dev:api).`);
257
+ }
258
+ throw error;
259
+ }
260
+ }
261
+ /**
262
+ * List all deployed apps.
263
+ */
264
+ export async function listApps() {
265
+ return apiRequest('GET', '/apps');
266
+ }
267
+ /**
268
+ * List all supported app types.
269
+ */
270
+ export async function listAppTypes() {
271
+ const response = await apiRequest('GET', '/guides');
272
+ return response.guides;
273
+ }
274
+ /**
275
+ * Get deployment guide for a specific app type.
276
+ */
277
+ export async function getDeploymentGuide(type) {
278
+ return apiRequest('GET', `/guides/${encodeURIComponent(type)}`);
279
+ }
280
+ /**
281
+ * Create a tar.gz archive of a directory.
282
+ * Copies the config file (vtp.yaml) into the archive.
283
+ * Respects .gitignore, vtp.yaml ignore patterns, and filters out sensitive files.
284
+ * Returns the path to the temporary tar.gz file.
285
+ */
286
+ async function createTarGz(sourcePath, configPath, ignorePatterns) {
287
+ const tarPath = join(tmpdir(), `vtp-deploy-${Date.now()}.tar.gz`);
288
+ const destVtpYaml = join(sourcePath, 'vtp.yaml');
289
+ let copiedVtpYaml = false;
290
+ // Copy vtp.yaml into source directory if not already there
291
+ if (!existsSync(destVtpYaml)) {
292
+ await copyFile(configPath, destVtpYaml);
293
+ copiedVtpYaml = true;
294
+ }
295
+ try {
296
+ // Get filtered list of files respecting vtp.yaml ignore, .gitignore, and security defaults
297
+ const filesToInclude = await getFilesToInclude(sourcePath, ignorePatterns);
298
+ // Always include vtp.yaml
299
+ if (!filesToInclude.includes('vtp.yaml')) {
300
+ filesToInclude.push('vtp.yaml');
301
+ }
302
+ // Create tar.gz archive with only the filtered files
303
+ await tar.create({
304
+ gzip: true,
305
+ file: tarPath,
306
+ cwd: sourcePath,
307
+ }, filesToInclude);
308
+ }
309
+ finally {
310
+ // Clean up the copied vtp.yaml
311
+ if (copiedVtpYaml) {
312
+ try {
313
+ await unlink(destVtpYaml);
314
+ }
315
+ catch {
316
+ // Ignore cleanup errors
317
+ }
318
+ }
319
+ }
320
+ return tarPath;
321
+ }
322
+ /**
323
+ * Deploy an app from a local directory.
324
+ * Reads vtp.yaml for all config including deploy path, creates tar.gz archive, and POSTs to the API.
325
+ */
326
+ export async function deploy(configPath = './vtp.yaml', force = false) {
327
+ // Read and parse vtp.yaml
328
+ const yamlContent = await readFile(configPath, 'utf-8');
329
+ const config = parseYaml(yamlContent);
330
+ // Validate required fields
331
+ if (!config.name) {
332
+ throw new Error('vtp.yaml: "name" is required');
333
+ }
334
+ if (!config.type) {
335
+ throw new Error('vtp.yaml: "type" is required (static or node)');
336
+ }
337
+ if (!config.path) {
338
+ throw new Error('vtp.yaml: "path" is required (folder to deploy)');
339
+ }
340
+ // Resolve path relative to vtp.yaml location
341
+ const configDir = dirname(resolve(configPath));
342
+ // Run predeploy commands if specified
343
+ if (config.predeploy) {
344
+ const commands = Array.isArray(config.predeploy)
345
+ ? config.predeploy
346
+ : [config.predeploy];
347
+ for (const cmd of commands) {
348
+ await execAsync(cmd, { cwd: configDir });
349
+ }
350
+ }
351
+ const sourcePath = resolve(configDir, config.path);
352
+ if (!existsSync(sourcePath)) {
353
+ throw new Error(`Deploy path does not exist: ${sourcePath}`);
354
+ }
355
+ // Create tar.gz of the directory, including vtp.yaml
356
+ const tarPath = await createTarGz(sourcePath, configPath, config.ignore);
357
+ try {
358
+ return await postDeploy(tarPath, config.name, config.type, force);
359
+ }
360
+ finally {
361
+ // Clean up temp tar file
362
+ await unlink(tarPath).catch(() => { });
363
+ }
364
+ }
365
+ /**
366
+ * POST to the /deploy endpoint with tar.gz archive.
367
+ * Uses form-data's submit() method for proper streaming support.
368
+ */
369
+ async function postDeploy(tarPath, name, type, force, retried = false) {
370
+ // Ensure we have valid authentication
371
+ const accessToken = await ensureAuthenticated();
372
+ return new Promise((resolve, reject) => {
373
+ const form = new FormData();
374
+ form.append('archive', createReadStream(tarPath), {
375
+ filename: 'app.tar.gz',
376
+ contentType: 'application/gzip',
377
+ });
378
+ form.append('name', name);
379
+ form.append('type', type);
380
+ form.append('force', String(force));
381
+ // Parse API_BASE URL
382
+ const url = new URL(`${API_BASE}/deploy`);
383
+ form.submit({
384
+ host: url.hostname,
385
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
386
+ path: url.pathname,
387
+ protocol: url.protocol,
388
+ headers: {
389
+ 'Authorization': `Bearer ${accessToken}`,
390
+ },
391
+ }, (err, res) => {
392
+ if (err) {
393
+ // Handle connection errors
394
+ if (err.message.includes('ECONNREFUSED')) {
395
+ reject(new Error(`Cannot connect to VTP API at ${API_BASE}. ` +
396
+ `Make sure the API server is running (pnpm dev:api).`));
397
+ return;
398
+ }
399
+ reject(err);
400
+ return;
401
+ }
402
+ // Handle 401 - try re-auth once
403
+ if (res.statusCode === 401 && !retried) {
404
+ deleteCredentials()
405
+ .then(() => postDeploy(tarPath, name, type, force, true))
406
+ .then(resolve)
407
+ .catch(reject);
408
+ return;
409
+ }
410
+ if (res.statusCode === 401) {
411
+ reject(new Error('Authentication failed. Please check your credentials.'));
412
+ return;
413
+ }
414
+ let body = '';
415
+ res.on('data', (chunk) => {
416
+ body += chunk;
417
+ });
418
+ res.on('end', () => {
419
+ try {
420
+ resolve(JSON.parse(body));
421
+ }
422
+ catch {
423
+ reject(new Error(`Invalid JSON response: ${body}`));
424
+ }
425
+ });
426
+ res.on('error', reject);
427
+ });
428
+ });
429
+ }
430
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAElC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,uBAAuB,CAAC;AACpE,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC;AAwErE,gFAAgF;AAChF,2BAA2B;AAC3B,gFAAgF;AAEhF;;GAEG;AACH,KAAK,UAAU,eAAe;IAC5B,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAgB,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,eAAe,CAAC,WAAwB;IACrD,MAAM,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,uDAAuD;IACvD,SAAS,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,iBAAiB;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,WAAwB;IAC9C,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;IAC5C,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,kBAAkB,CAAC,YAAoB;IACpD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,eAAe,EAAE;YACvD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;SACvC,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA6B,CAAC;QAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,WAAW;QAEjE,OAAO;YACL,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY;YACZ,SAAS;SACV,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,eAAe;IAC5B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,oBAAoB,EAAE;QAC5D,MAAM,EAAE,MAAM;KACf,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,cAAc,CAAC,UAAkB;IAC9C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,mBAAmB,EAAE;QAC3D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;KAClD,CAAC,CAAC;IAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,WAAW,CAAC,GAAW;IACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,GAAW,CAAC;IAEhB,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,GAAG,GAAG,SAAS,GAAG,GAAG,CAAC;IACxB,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,GAAG,GAAG,aAAa,GAAG,GAAG,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,aAAa,GAAG,GAAG,CAAC;IAC5B,CAAC;IAED,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,iBAAiB;IAC9B,wBAAwB;IACxB,MAAM,UAAU,GAAG,MAAM,eAAe,EAAE,CAAC;IAE3C,+BAA+B;IAC/B,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACvE,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACrE,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACrE,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACrE,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACrE,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACrE,OAAO,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;IACnE,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACrE,OAAO,CAAC,KAAK,CAAC,sBAAsB,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;IAC1E,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAEvE,eAAe;IACf,IAAI,CAAC;QACH,MAAM,WAAW,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;IACrE,CAAC;IAED,sBAAsB;IACtB,MAAM,cAAc,GAAG,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC;IAClD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAE9D,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;QAElE,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QAE5D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,SAAS;QACX,CAAC;QAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QAED,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC5D,CAAC;QAED,2BAA2B;QAC3B,MAAM,WAAW,GAAgB;YAC/B,WAAW,EAAE,MAAM,CAAC,YAAY;YAChC,YAAY,EAAE,MAAM,CAAC,aAAa;YAClC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;SACnD,CAAC;QAEF,mBAAmB;QACnB,MAAM,eAAe,CAAC,WAAW,CAAC,CAAC;QAEnC,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAClD,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,mBAAmB;IAChC,IAAI,WAAW,GAAG,MAAM,eAAe,EAAE,CAAC;IAE1C,wCAAwC;IACxC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,WAAW,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACxC,OAAO,WAAW,CAAC,WAAW,CAAC;IACjC,CAAC;IAED,iCAAiC;IACjC,IAAI,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QAErE,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;YACjC,OAAO,SAAS,CAAC,WAAW,CAAC;QAC/B,CAAC;QAED,2CAA2C;QAC3C,MAAM,iBAAiB,EAAE,CAAC;QAC1B,WAAW,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACxC,OAAO,WAAW,CAAC,WAAW,CAAC;IACjC,CAAC;IAED,OAAO,WAAW,CAAC,WAAW,CAAC;AACjC,CAAC;AAED,gFAAgF;AAChF,qBAAqB;AACrB,gFAAgF;AAEhF;;GAEG;AACH,KAAK,UAAU,UAAU,CACvB,MAAiC,EACjC,IAAY,EACZ,IAAc,EACd,UAAmB,KAAK;IAExB,MAAM,GAAG,GAAG,GAAG,QAAQ,GAAG,IAAI,EAAE,CAAC;IAEjC,yBAAyB;IACzB,MAAM,WAAW,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAEhD,IAAI,CAAC;QACH,MAAM,OAAO,GAA2B;YACtC,eAAe,EAAE,UAAU,WAAW,EAAE;SACzC,CAAC;QAEF,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC/C,CAAC;QAED,MAAM,OAAO,GAAgB;YAC3B,MAAM;YACN,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SAC9C,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAE3C,mEAAmE;QACnE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,iBAAiB,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;YAC9C,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC3E,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;YACxF,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,IAAI,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,2BAA2B;QAC3B,IAAI,KAAK,YAAY,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YACzE,MAAM,IAAI,KAAK,CACb,gCAAgC,QAAQ,IAAI;gBAC5C,qDAAqD,CACtD,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,OAAO,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,QAAQ,GAAG,MAAM,UAAU,CAA4B,KAAK,EAAE,SAAS,CAAC,CAAC;IAC/E,OAAO,QAAQ,CAAC,MAAM,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY;IACnD,OAAO,UAAU,CAAC,KAAK,EAAE,WAAW,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAClE,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,WAAW,CACxB,UAAkB,EAClB,UAAkB,EAClB,cAAyB;IAEzB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAClE,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACjD,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,2DAA2D;IAC3D,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,MAAM,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QACxC,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,IAAI,CAAC;QACH,2FAA2F;QAC3F,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QAE3E,0BAA0B;QAC1B,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACzC,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC;QAED,qDAAqD;QACrD,MAAM,GAAG,CAAC,MAAM,CACd;YACE,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,OAAO;YACb,GAAG,EAAE,UAAU;SAChB,EACD,cAAc,CACf,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,+BAA+B;QAC/B,IAAI,aAAa,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,wBAAwB;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,aAAqB,YAAY,EACjC,QAAiB,KAAK;IAEtB,0BAA0B;IAC1B,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,CAAc,CAAC;IAEnD,2BAA2B;IAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,6CAA6C;IAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAE/C,sCAAsC;IACtC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;YAC9C,CAAC,CAAC,MAAM,CAAC,SAAS;YAClB,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAEvB,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,MAAM,SAAS,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAEnD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,+BAA+B,UAAU,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,qDAAqD;IACrD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAEzE,IAAI,CAAC;QACH,OAAO,MAAM,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACpE,CAAC;YAAS,CAAC;QACT,yBAAyB;QACzB,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,UAAU,CACvB,OAAe,EACf,IAAY,EACZ,IAAY,EACZ,KAAc,EACd,UAAmB,KAAK;IAExB,sCAAsC;IACtC,MAAM,WAAW,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAEhD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE;YAChD,QAAQ,EAAE,YAAY;YACtB,WAAW,EAAE,kBAAkB;SAChC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAEpC,qBAAqB;QACrB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,QAAQ,SAAS,CAAC,CAAC;QAE1C,IAAI,CAAC,MAAM,CACT;YACE,IAAI,EAAE,GAAG,CAAC,QAAQ;YAClB,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,IAAI,EAAE,GAAG,CAAC,QAAQ;YAClB,QAAQ,EAAE,GAAG,CAAC,QAA8B;YAC5C,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,WAAW,EAAE;aACzC;SACF,EACD,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACX,IAAI,GAAG,EAAE,CAAC;gBACR,2BAA2B;gBAC3B,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;oBACzC,MAAM,CAAC,IAAI,KAAK,CACd,gCAAgC,QAAQ,IAAI;wBAC5C,qDAAqD,CACtD,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;gBACD,MAAM,CAAC,GAAG,CAAC,CAAC;gBACZ,OAAO;YACT,CAAC;YAED,gCAAgC;YAChC,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;gBACvC,iBAAiB,EAAE;qBAChB,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;qBACxD,IAAI,CAAC,OAAO,CAAC;qBACb,KAAK,CAAC,MAAM,CAAC,CAAC;gBACjB,OAAO;YACT,CAAC;YACD,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC,CAAC;gBAC3E,OAAO;YACT,CAAC;YAED,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;gBACvB,IAAI,IAAI,KAAK,CAAC;YAChB,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,IAAI,CAAC;oBACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC5B,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,CAAC,IAAI,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC,CAAC;gBACtD,CAAC;YACH,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1B,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { existsSync } from 'fs';
6
+ import * as client from './client.js';
7
+ import { DeploySchema, GuideTypeSchema, toolDefinitions } from './tools.js';
8
+ const server = new Server({
9
+ name: 'vtp',
10
+ version: '0.7.0',
11
+ }, {
12
+ capabilities: {
13
+ tools: {},
14
+ },
15
+ });
16
+ // List available tools
17
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
18
+ tools: toolDefinitions,
19
+ }));
20
+ // Handle tool calls - all delegate to HTTP API via client
21
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
22
+ const { name, arguments: args } = request.params;
23
+ try {
24
+ switch (name) {
25
+ case 'list_app_types': {
26
+ const types = await client.listAppTypes();
27
+ const typeList = types
28
+ .map(t => `- **${t.displayName}** (${t.type})\n ${t.description}`)
29
+ .join('\n');
30
+ return {
31
+ content: [{
32
+ type: 'text',
33
+ text: `Supported app types:\n\n${typeList}\n\n` +
34
+ `Use get_deployment_guide with a type to see detailed instructions.`,
35
+ }],
36
+ };
37
+ }
38
+ case 'get_deployment_guide': {
39
+ const { type: guideType } = GuideTypeSchema.parse(args);
40
+ try {
41
+ const guide = await client.getDeploymentGuide(guideType);
42
+ return {
43
+ content: [{
44
+ type: 'text',
45
+ text: guide.content,
46
+ }],
47
+ };
48
+ }
49
+ catch (error) {
50
+ if (error instanceof Error && error.message.includes('guide_not_found')) {
51
+ const types = await client.listAppTypes();
52
+ const available = types.map(t => t.type).join(', ');
53
+ return {
54
+ content: [{
55
+ type: 'text',
56
+ text: `Unknown app type: "${guideType}"\n\nAvailable types: ${available}`,
57
+ }],
58
+ isError: true,
59
+ };
60
+ }
61
+ throw error;
62
+ }
63
+ }
64
+ case 'deploy': {
65
+ const { config, force } = DeploySchema.parse(args);
66
+ const configPath = config || './vtp.yaml';
67
+ // Validate config exists
68
+ if (!existsSync(configPath)) {
69
+ return {
70
+ content: [{
71
+ type: 'text',
72
+ text: `Error: Config file not found: ${configPath}\n\n` +
73
+ `Create a vtp.yaml file with:\n` +
74
+ ` name: My App Name # Display name\n` +
75
+ ` id: my-app # Optional: URL slug\n` +
76
+ ` type: static # or "node"\n` +
77
+ ` path: ./dist # folder to deploy\n\n` +
78
+ `Use list_app_types and get_deployment_guide for help.`,
79
+ }],
80
+ isError: true,
81
+ };
82
+ }
83
+ // Deploy via HTTP API
84
+ const result = await client.deploy(configPath, force ?? false);
85
+ // Handle conflict
86
+ if (result.error === 'conflict') {
87
+ const existingApp = result.existingApp;
88
+ return {
89
+ content: [{
90
+ type: 'text',
91
+ text: `App '${existingApp?.id}' already exists at ${existingApp?.url || 'unknown URL'}\n` +
92
+ `Name: ${existingApp?.name || 'unknown'}\n` +
93
+ `Status: ${existingApp?.status || 'unknown'}\n\n` +
94
+ `Use force: true to replace it.`,
95
+ }],
96
+ };
97
+ }
98
+ // Handle other errors
99
+ if (result.error) {
100
+ return {
101
+ content: [{
102
+ type: 'text',
103
+ text: `Error: ${result.message || result.error}`,
104
+ }],
105
+ isError: true,
106
+ };
107
+ }
108
+ // Success
109
+ const app = result.app;
110
+ const prefix = result.replaced ? 'Replaced' : 'Deployed';
111
+ return {
112
+ content: [{
113
+ type: 'text',
114
+ text: `${prefix} ${app.name} (@${app.id})\n` +
115
+ ` URL: ${app.url}\n` +
116
+ ` Type: ${app.type}\n` +
117
+ ` Status: ${app.status}`,
118
+ }],
119
+ };
120
+ }
121
+ case 'list': {
122
+ const apps = await client.listApps();
123
+ if (apps.length === 0) {
124
+ return {
125
+ content: [{
126
+ type: 'text',
127
+ text: 'No apps deployed yet.',
128
+ }],
129
+ };
130
+ }
131
+ const appList = apps
132
+ .map(app => {
133
+ const desc = app.description ? `\n ${app.description}` : '';
134
+ return `- ${app.name} (@${app.id}) - ${app.type} - ${app.status}${desc}\n ${app.url}`;
135
+ })
136
+ .join('\n');
137
+ return {
138
+ content: [{
139
+ type: 'text',
140
+ text: `Deployed apps:\n${appList}`,
141
+ }],
142
+ };
143
+ }
144
+ default:
145
+ return {
146
+ content: [{
147
+ type: 'text',
148
+ text: `Unknown tool: ${name}`,
149
+ }],
150
+ isError: true,
151
+ };
152
+ }
153
+ }
154
+ catch (error) {
155
+ const message = error instanceof Error ? error.message : String(error);
156
+ return {
157
+ content: [{
158
+ type: 'text',
159
+ text: `Error: ${message}`,
160
+ }],
161
+ isError: true,
162
+ };
163
+ }
164
+ });
165
+ // Start the server
166
+ async function main() {
167
+ const transport = new StdioServerTransport();
168
+ await server.connect(transport);
169
+ console.error('VTP MCP server running on stdio');
170
+ }
171
+ main().catch(console.error);
172
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { z } from 'zod';
2
+ export declare const DeploySchema: z.ZodObject<{
3
+ config: z.ZodOptional<z.ZodString>;
4
+ force: z.ZodOptional<z.ZodBoolean>;
5
+ }, "strip", z.ZodTypeAny, {
6
+ force?: boolean | undefined;
7
+ config?: string | undefined;
8
+ }, {
9
+ force?: boolean | undefined;
10
+ config?: string | undefined;
11
+ }>;
12
+ export declare const GuideTypeSchema: z.ZodObject<{
13
+ type: z.ZodString;
14
+ }, "strip", z.ZodTypeAny, {
15
+ type: string;
16
+ }, {
17
+ type: string;
18
+ }>;
19
+ export declare const toolDefinitions: ({
20
+ name: string;
21
+ description: string;
22
+ inputSchema: {
23
+ type: string;
24
+ properties: {
25
+ type?: undefined;
26
+ config?: undefined;
27
+ force?: undefined;
28
+ };
29
+ required?: undefined;
30
+ };
31
+ } | {
32
+ name: string;
33
+ description: string;
34
+ inputSchema: {
35
+ type: string;
36
+ properties: {
37
+ type: {
38
+ type: string;
39
+ description: string;
40
+ };
41
+ config?: undefined;
42
+ force?: undefined;
43
+ };
44
+ required: string[];
45
+ };
46
+ } | {
47
+ name: string;
48
+ description: string;
49
+ inputSchema: {
50
+ type: string;
51
+ properties: {
52
+ config: {
53
+ type: string;
54
+ description: string;
55
+ };
56
+ force: {
57
+ type: string;
58
+ description: string;
59
+ };
60
+ type?: undefined;
61
+ };
62
+ required: never[];
63
+ };
64
+ })[];
65
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,YAAY;;;;;;;;;EAGvB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;EAE1B,CAAC;AAGH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAwG3B,CAAC"}
package/dist/tools.js ADDED
@@ -0,0 +1,116 @@
1
+ import { z } from 'zod';
2
+ // Tool parameter schemas
3
+ export const DeploySchema = z.object({
4
+ config: z.string().optional().describe('Path to vtp.yaml (default: ./vtp.yaml)'),
5
+ force: z.boolean().optional().describe('Replace existing app with same name'),
6
+ });
7
+ export const GuideTypeSchema = z.object({
8
+ type: z.string().describe('App type from list_app_types (e.g., "nextjs", "spa", "node")'),
9
+ });
10
+ // Tool definitions for MCP
11
+ export const toolDefinitions = [
12
+ {
13
+ name: 'list_app_types',
14
+ description: `List supported app types for deployment.
15
+
16
+ DEPLOYMENT WORKFLOW (all steps required):
17
+ 1. list_app_types → identify your app type
18
+ 2. get_deployment_guide → ALWAYS call this before deploy, even if vtp.yaml exists
19
+ 3. list → check if app already exists (redeploy vs new)
20
+ 4. Review files in deploy path, add 'ignore' to vtp.yaml for files not needed at runtime
21
+ 5. deploy → deploy the app
22
+
23
+ After identifying the type, call get_deployment_guide to get the vtp.yaml template.`,
24
+ inputSchema: {
25
+ type: 'object',
26
+ properties: {},
27
+ },
28
+ },
29
+ {
30
+ name: 'get_deployment_guide',
31
+ description: `Get deployment instructions and vtp.yaml template for an app type.
32
+
33
+ MANDATORY: Call this before EVERY deployment - even if vtp.yaml already exists.
34
+ This ensures your configuration matches the latest requirements.
35
+
36
+ Returns a vtp.yaml template with 'predeploy' commands that automatically handle:
37
+ - Building the app (npm run build, etc.)
38
+ - Copying assets (for Next.js standalone, etc.)
39
+
40
+ CRITICAL FOR NODE APPS: Before creating vtp.yaml, check if the app uses SQLite or writes files:
41
+ - Look for: better-sqlite3, sql.js, sqlite3, prisma with SQLite, fs.writeFile to data files
42
+ - If found: You MUST add 'volumes' config AND update the app to use the volume path
43
+ - Without volumes, all data is lost on every redeployment
44
+
45
+ Copy the vtp.yaml template, adjust the app name, then call deploy.`,
46
+ inputSchema: {
47
+ type: 'object',
48
+ properties: {
49
+ type: {
50
+ type: 'string',
51
+ description: 'App type from list_app_types (e.g., "nextjs", "spa", "node")',
52
+ },
53
+ },
54
+ required: ['type'],
55
+ },
56
+ },
57
+ {
58
+ name: 'deploy',
59
+ description: `PREREQUISITES (complete ALL before deploying):
60
+ 1. Call get_deployment_guide to verify vtp.yaml config (even if it already exists)
61
+ 2. Call list to check if this app already exists (redeploy vs new deployment)
62
+ 3. Review files in deploy path - add 'ignore' to vtp.yaml for files not needed at runtime
63
+
64
+ Deploy a web app to VTP.
65
+
66
+ The vtp.yaml 'predeploy' commands run automatically before packaging.
67
+ You do NOT need to manually run build commands - predeploy handles it.
68
+
69
+ REQUIRED: vtp.yaml with:
70
+ name: My App Name # Display name (shown in dashboard)
71
+ id: my-app # Optional: URL slug (auto-generated from name if omitted)
72
+ description: Brief description of the app
73
+ type: static|node
74
+ path: ./dist
75
+ predeploy: npm run build # Runs automatically!
76
+ ignore: # Exclude unnecessary files
77
+ - node_modules # ALWAYS exclude - reinstalled in container
78
+ - src # Source files if deploying compiled output
79
+
80
+ NAME vs ID:
81
+ - name: Human-friendly display name (e.g., "My Budget Tracker")
82
+ - id: URL-safe identifier used for: https://{id}.{user}.myvtp.dev, container name, volumes
83
+ If omitted, auto-generated from name: "My Budget Tracker" → "my-budget-tracker"
84
+ Rules: lowercase, letters/numbers/hyphens only, max 63 chars
85
+
86
+ CRITICAL - FOR APPS WITH DATABASES OR FILE STORAGE:
87
+ You MUST add volumes or data will be lost on redeploy:
88
+ volumes:
89
+ data: /app/data
90
+ AND update the app code to write to the volume path (e.g., /app/data/app.db)
91
+
92
+ FILE EXCLUSION:
93
+ - .env files and .git are excluded automatically (security)
94
+ - .gitignore patterns are respected if the file exists
95
+ - Add 'ignore' in vtp.yaml for anything else not needed at runtime`,
96
+ inputSchema: {
97
+ type: 'object',
98
+ properties: {
99
+ config: { type: 'string', description: 'Path to vtp.yaml (default: ./vtp.yaml)' },
100
+ force: { type: 'boolean', description: 'Replace existing app with same id' },
101
+ },
102
+ required: [],
103
+ },
104
+ },
105
+ {
106
+ name: 'list',
107
+ description: `List all deployed apps with their status and URLs.
108
+
109
+ Call this before deploying to check if the app already exists (redeploy requires force flag).`,
110
+ inputSchema: {
111
+ type: 'object',
112
+ properties: {},
113
+ },
114
+ },
115
+ ];
116
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,yBAAyB;AACzB,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;IAChF,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;CAC9E,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8DAA8D,CAAC;CAC1F,CAAC,CAAC;AAEH,2BAA2B;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B;QACE,IAAI,EAAE,gBAAgB;QACtB,WAAW,EAAE;;;;;;;;;oFASmE;QAChF,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,EAAE;SACf;KACF;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE;;;;;;;;;;;;;;mEAckD;QAC/D,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,8DAA8D;iBAC5E;aACF;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;SACnB;KACF;IACD;QACE,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAoCkD;QAC/D,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,wCAAwC,EAAE;gBACjF,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,mCAAmC,EAAE;aAC7E;YACD,QAAQ,EAAE,EAAE;SACb;KACF;IACD;QACE,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE;;8FAE6E;QAC1F,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,EAAE;SACf;KACF;CACF,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { Ignore } from 'ignore';
2
+ /**
3
+ * Create an ignore filter for a directory.
4
+ * Priority: security defaults > vtp.yaml ignore > .gitignore
5
+ */
6
+ export declare function createIgnoreFilter(rootPath: string, configIgnore?: string[]): Promise<Ignore>;
7
+ /**
8
+ * Get all files to include in the archive, respecting ignore patterns.
9
+ * Returns relative paths from the root.
10
+ */
11
+ export declare function getFilesToInclude(rootPath: string, configIgnore?: string[], ig?: Ignore, basePath?: string): Promise<string[]>;
12
+ //# sourceMappingURL=ignoreFilter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ignoreFilter.d.ts","sourceRoot":"","sources":["../../src/utils/ignoreFilter.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAqBxC;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,EAAE,EACvB,EAAE,CAAC,EAAE,MAAM,EACX,QAAQ,GAAE,MAAW,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAmCnB"}
@@ -0,0 +1,74 @@
1
+ import ignore from 'ignore';
2
+ import { readFile, readdir, stat } from 'fs/promises';
3
+ import { join } from 'path';
4
+ /**
5
+ * Patterns that are ALWAYS excluded for security.
6
+ * These apply even if no .gitignore exists.
7
+ *
8
+ * Keep this list minimal - users control other exclusions via .gitignore.
9
+ */
10
+ const ALWAYS_IGNORED = [
11
+ // Environment files (secrets) - ALWAYS excluded
12
+ '.env',
13
+ '.env.*',
14
+ '.env.local',
15
+ '.env.*.local',
16
+ // Version control - never needed in deployment
17
+ '.git',
18
+ ];
19
+ /**
20
+ * Create an ignore filter for a directory.
21
+ * Priority: security defaults > vtp.yaml ignore > .gitignore
22
+ */
23
+ export async function createIgnoreFilter(rootPath, configIgnore) {
24
+ const ig = ignore().add(ALWAYS_IGNORED);
25
+ // Add patterns from vtp.yaml ignore field
26
+ if (configIgnore && configIgnore.length > 0) {
27
+ ig.add(configIgnore);
28
+ }
29
+ // Load .gitignore if present
30
+ try {
31
+ const gitignorePath = join(rootPath, '.gitignore');
32
+ const content = await readFile(gitignorePath, 'utf-8');
33
+ ig.add(content);
34
+ }
35
+ catch {
36
+ // No .gitignore file, that's fine
37
+ }
38
+ return ig;
39
+ }
40
+ /**
41
+ * Get all files to include in the archive, respecting ignore patterns.
42
+ * Returns relative paths from the root.
43
+ */
44
+ export async function getFilesToInclude(rootPath, configIgnore, ig, basePath = '') {
45
+ // Initialise ignore filter at root level
46
+ if (!ig) {
47
+ ig = await createIgnoreFilter(rootPath, configIgnore);
48
+ }
49
+ const files = [];
50
+ const entries = await readdir(join(rootPath, basePath));
51
+ for (const entry of entries) {
52
+ const relativePath = basePath ? `${basePath}/${entry}` : entry;
53
+ const fullPath = join(rootPath, relativePath);
54
+ // Check if this path should be ignored
55
+ if (ig.ignores(relativePath)) {
56
+ continue;
57
+ }
58
+ const entryStat = await stat(fullPath);
59
+ if (entryStat.isDirectory()) {
60
+ // Check if directory itself is ignored (with trailing slash)
61
+ if (ig.ignores(relativePath + '/')) {
62
+ continue;
63
+ }
64
+ // Recurse into directory
65
+ const subFiles = await getFilesToInclude(rootPath, configIgnore, ig, relativePath);
66
+ files.push(...subFiles);
67
+ }
68
+ else {
69
+ files.push(relativePath);
70
+ }
71
+ }
72
+ return files;
73
+ }
74
+ //# sourceMappingURL=ignoreFilter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ignoreFilter.js","sourceRoot":"","sources":["../../src/utils/ignoreFilter.ts"],"names":[],"mappings":"AAAA,OAAO,MAAkB,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B;;;;;GAKG;AACH,MAAM,cAAc,GAAG;IACrB,gDAAgD;IAChD,MAAM;IACN,QAAQ;IACR,YAAY;IACZ,cAAc;IAEd,+CAA+C;IAC/C,MAAM;CACP,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,QAAgB,EAChB,YAAuB;IAEvB,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAExC,0CAA0C;IAC1C,IAAI,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACvB,CAAC;IAED,6BAA6B;IAC7B,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QACvD,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,kCAAkC;IACpC,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAgB,EAChB,YAAuB,EACvB,EAAW,EACX,WAAmB,EAAE;IAErB,yCAAyC;IACzC,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,EAAE,GAAG,MAAM,kBAAkB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAExD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAE9C,uCAAuC;QACvC,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,SAAS;QACX,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEvC,IAAI,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC;YAC5B,6DAA6D;YAC7D,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,GAAG,GAAG,CAAC,EAAE,CAAC;gBACnC,SAAS;YACX,CAAC;YAED,yBAAyB;YACzB,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,CAAC,CAAC;YACnF,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@myvtp/mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for VTP - deploy apps via Claude Code",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "myvtp-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsx watch src/index.ts",
16
+ "start": "node dist/index.js"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/myvtp/mcp.git"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "claude",
25
+ "deployment",
26
+ "vtp"
27
+ ],
28
+ "author": "myvtp",
29
+ "license": "MIT",
30
+ "bugs": {
31
+ "url": "https://github.com/myvtp/mcp/issues"
32
+ },
33
+ "homepage": "https://github.com/myvtp/mcp#readme",
34
+ "dependencies": {
35
+ "@modelcontextprotocol/sdk": "^1.0.0",
36
+ "form-data": "^4.0.0",
37
+ "ignore": "^7.0.5",
38
+ "tar": "^7.5.7",
39
+ "yaml": "^2.8.2",
40
+ "zod": "^3.22.4"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.10.0",
44
+ "tsx": "^4.7.0",
45
+ "typescript": "^5.3.3"
46
+ }
47
+ }