@mcp-use/cli 2.5.6 → 2.6.0-canary.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs DELETED
@@ -1,1976 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/index.ts
4
- import chalk3 from "chalk";
5
- import { Command } from "commander";
6
- import "dotenv/config";
7
- import { spawn } from "child_process";
8
- import { readFileSync } from "fs";
9
- import { access, mkdir, readFile, writeFile } from "fs/promises";
10
- import path3 from "path";
11
- import open3 from "open";
12
-
13
- // src/commands/auth.ts
14
- import chalk from "chalk";
15
- import {
16
- createServer
17
- } from "http";
18
- import open from "open";
19
-
20
- // src/utils/config.ts
21
- import { promises as fs } from "fs";
22
- import path from "path";
23
- import os from "os";
24
- var CONFIG_DIR = path.join(os.homedir(), ".mcp-use");
25
- var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
26
- var DEFAULT_API_URL = process.env.MCP_API_URL ? process.env.MCP_API_URL.replace(/\/api\/v1$/, "") + "/api/v1" : "https://cloud.mcp-use.com/api/v1";
27
- var DEFAULT_WEB_URL = process.env.MCP_WEB_URL ? process.env.MCP_WEB_URL : "https://mcp-use.com";
28
- async function ensureConfigDir() {
29
- try {
30
- await fs.mkdir(CONFIG_DIR, { recursive: true });
31
- } catch (error) {
32
- }
33
- }
34
- async function readConfig() {
35
- try {
36
- const content = await fs.readFile(CONFIG_FILE, "utf-8");
37
- return JSON.parse(content);
38
- } catch (error) {
39
- return {};
40
- }
41
- }
42
- async function writeConfig(config) {
43
- await ensureConfigDir();
44
- await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
45
- }
46
- async function deleteConfig() {
47
- try {
48
- await fs.unlink(CONFIG_FILE);
49
- } catch (error) {
50
- }
51
- }
52
- async function getApiUrl() {
53
- const config = await readConfig();
54
- return config.apiUrl || DEFAULT_API_URL;
55
- }
56
- async function getApiKey() {
57
- const config = await readConfig();
58
- return config.apiKey || null;
59
- }
60
- async function isLoggedIn() {
61
- const apiKey = await getApiKey();
62
- return !!apiKey;
63
- }
64
- async function getWebUrl() {
65
- return DEFAULT_WEB_URL;
66
- }
67
-
68
- // src/utils/api.ts
69
- var McpUseAPI = class _McpUseAPI {
70
- baseUrl;
71
- apiKey;
72
- constructor(baseUrl, apiKey) {
73
- this.baseUrl = baseUrl || "";
74
- this.apiKey = apiKey;
75
- }
76
- /**
77
- * Initialize API client with config
78
- */
79
- static async create() {
80
- const baseUrl = await getApiUrl();
81
- const apiKey = await getApiKey();
82
- return new _McpUseAPI(baseUrl, apiKey ?? void 0);
83
- }
84
- /**
85
- * Make authenticated request
86
- */
87
- async request(endpoint, options = {}) {
88
- const url = `${this.baseUrl}${endpoint}`;
89
- const headers = {
90
- "Content-Type": "application/json",
91
- ...options.headers || {}
92
- };
93
- if (this.apiKey) {
94
- headers["x-api-key"] = this.apiKey;
95
- }
96
- const response = await fetch(url, {
97
- ...options,
98
- headers
99
- });
100
- if (!response.ok) {
101
- const error = await response.text();
102
- throw new Error(`API request failed: ${response.status} ${error}`);
103
- }
104
- return response.json();
105
- }
106
- /**
107
- * Create API key using JWT token
108
- */
109
- async createApiKey(jwtToken, name = "CLI") {
110
- const url = `${this.baseUrl}/api-key`;
111
- const response = await fetch(url, {
112
- method: "POST",
113
- headers: {
114
- "Content-Type": "application/json",
115
- Authorization: `Bearer ${jwtToken}`
116
- },
117
- body: JSON.stringify({ name })
118
- });
119
- if (!response.ok) {
120
- const error = await response.text();
121
- throw new Error(`Failed to create API key: ${response.status} ${error}`);
122
- }
123
- return response.json();
124
- }
125
- /**
126
- * Test authentication
127
- */
128
- async testAuth() {
129
- return this.request("/test-auth");
130
- }
131
- /**
132
- * Create deployment
133
- */
134
- async createDeployment(request) {
135
- return this.request("/deployments", {
136
- method: "POST",
137
- body: JSON.stringify(request)
138
- });
139
- }
140
- /**
141
- * Get deployment by ID
142
- */
143
- async getDeployment(deploymentId) {
144
- return this.request(`/deployments/${deploymentId}`);
145
- }
146
- /**
147
- * Stream deployment logs
148
- */
149
- async *streamDeploymentLogs(deploymentId) {
150
- const url = `${this.baseUrl}/deployments/${deploymentId}/logs/stream`;
151
- const headers = {};
152
- if (this.apiKey) {
153
- headers["x-api-key"] = this.apiKey;
154
- }
155
- const response = await fetch(url, { headers });
156
- if (!response.ok) {
157
- throw new Error(`Failed to stream logs: ${response.status}`);
158
- }
159
- if (!response.body) {
160
- throw new Error("Response body is null");
161
- }
162
- const reader = response.body.getReader();
163
- const decoder = new TextDecoder();
164
- let buffer = "";
165
- try {
166
- while (true) {
167
- const { done, value } = await reader.read();
168
- if (done) break;
169
- buffer += decoder.decode(value, { stream: true });
170
- const lines = buffer.split("\n");
171
- buffer = lines.pop() || "";
172
- for (const line of lines) {
173
- if (line.startsWith("data: ")) {
174
- const data = line.slice(6);
175
- try {
176
- const parsed = JSON.parse(data);
177
- if (parsed.log) {
178
- yield parsed.log;
179
- } else if (parsed.error) {
180
- throw new Error(parsed.error);
181
- }
182
- } catch (e) {
183
- }
184
- }
185
- }
186
- }
187
- } finally {
188
- reader.releaseLock();
189
- }
190
- }
191
- /**
192
- * Create deployment with source code upload
193
- */
194
- async createDeploymentWithUpload(request, filePath) {
195
- const { readFile: readFile2 } = await import("fs/promises");
196
- const { basename } = await import("path");
197
- const { stat } = await import("fs/promises");
198
- const stats = await stat(filePath);
199
- const maxSize = 2 * 1024 * 1024;
200
- if (stats.size > maxSize) {
201
- throw new Error(
202
- `File size (${(stats.size / 1024 / 1024).toFixed(2)}MB) exceeds maximum of 2MB`
203
- );
204
- }
205
- const fileBuffer = await readFile2(filePath);
206
- const filename = basename(filePath);
207
- const formData = new FormData();
208
- const blob = new Blob([fileBuffer], { type: "application/gzip" });
209
- formData.append("source_file", blob, filename);
210
- formData.append("name", request.name);
211
- formData.append("source_type", "upload");
212
- if (request.source.type === "upload") {
213
- formData.append("runtime", request.source.runtime || "node");
214
- formData.append("port", String(request.source.port || 3e3));
215
- if (request.source.startCommand) {
216
- formData.append("startCommand", request.source.startCommand);
217
- }
218
- if (request.source.buildCommand) {
219
- formData.append("buildCommand", request.source.buildCommand);
220
- }
221
- if (request.source.env && Object.keys(request.source.env).length > 0) {
222
- formData.append("env", JSON.stringify(request.source.env));
223
- }
224
- }
225
- if (request.customDomain) {
226
- formData.append("customDomain", request.customDomain);
227
- }
228
- if (request.healthCheckPath) {
229
- formData.append("healthCheckPath", request.healthCheckPath);
230
- }
231
- const url = `${this.baseUrl}/deployments`;
232
- const headers = {};
233
- if (this.apiKey) {
234
- headers["x-api-key"] = this.apiKey;
235
- }
236
- const response = await fetch(url, {
237
- method: "POST",
238
- headers,
239
- body: formData
240
- });
241
- if (!response.ok) {
242
- const error = await response.text();
243
- throw new Error(`Deployment failed: ${error}`);
244
- }
245
- return response.json();
246
- }
247
- };
248
-
249
- // src/commands/auth.ts
250
- var LOGIN_TIMEOUT = 3e5;
251
- async function findAvailablePort(startPort = 8765) {
252
- for (let port = startPort; port < startPort + 100; port++) {
253
- try {
254
- await new Promise((resolve, reject) => {
255
- const server = createServer();
256
- server.once("error", reject);
257
- server.once("listening", () => {
258
- server.close();
259
- resolve();
260
- });
261
- server.listen(port);
262
- });
263
- return port;
264
- } catch {
265
- continue;
266
- }
267
- }
268
- throw new Error("No available ports found");
269
- }
270
- async function startCallbackServer(port) {
271
- return new Promise((resolve, reject) => {
272
- let tokenResolver = null;
273
- const tokenPromise = new Promise((res) => {
274
- tokenResolver = res;
275
- });
276
- const server = createServer((req, res) => {
277
- if (req.url?.startsWith("/callback")) {
278
- const url = new URL(req.url, `http://localhost:${port}`);
279
- const token = url.searchParams.get("token");
280
- if (token && tokenResolver) {
281
- res.writeHead(200, { "Content-Type": "text/html" });
282
- res.end(`
283
- <!DOCTYPE html>
284
- <html>
285
- <head>
286
- <title>Login Successful</title>
287
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
288
- <style>
289
- * {
290
- margin: 0;
291
- padding: 0;
292
- box-sizing: border-box;
293
- }
294
- body {
295
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
296
- display: flex;
297
- justify-content: center;
298
- align-items: center;
299
- min-height: 100vh;
300
- background: #000;
301
- padding: 1rem;
302
- }
303
- .container {
304
- width: 100%;
305
- max-width: 28rem;
306
- padding: 3rem;
307
- text-align: center;
308
- -webkit-backdrop-filter: blur(40px);
309
- border: 1px solid rgba(255, 255, 255, 0.2);
310
- border-radius: 1.5rem;
311
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
312
- }
313
- .icon-container {
314
- display: inline-flex;
315
- align-items: center;
316
- justify-content: center;
317
- width: 6rem;
318
- height: 6rem;
319
- margin-bottom: 2rem;
320
- background: rgba(255, 255, 255, 0.1);
321
- backdrop-filter: blur(10px);
322
- -webkit-backdrop-filter: blur(10px);
323
- border-radius: 50%;
324
- }
325
- .checkmark {
326
- font-size: 4rem;
327
- color: #fff;
328
- line-height: 1;
329
- animation: scaleIn 0.5s ease-out;
330
- }
331
- @keyframes scaleIn {
332
- from {
333
- transform: scale(0);
334
- opacity: 0;
335
- }
336
- to {
337
- transform: scale(1);
338
- opacity: 1;
339
- }
340
- }
341
- h1 {
342
- color: #fff;
343
- margin: 0 0 1rem 0;
344
- font-size: 2.5rem;
345
- font-weight: 700;
346
- letter-spacing: -0.025em;
347
- }
348
- p {
349
- color: rgba(255, 255, 255, 0.8);
350
- margin: 0 0 2rem 0;
351
- font-size: 1.125rem;
352
- line-height: 1.5;
353
- }
354
- .spinner {
355
- display: inline-block;
356
- width: 2rem;
357
- height: 2rem;
358
- border: 3px solid rgba(255, 255, 255, 0.3);
359
- border-top-color: #fff;
360
- border-radius: 50%;
361
- animation: spin 0.8s linear infinite;
362
- }
363
- @keyframes spin {
364
- to { transform: rotate(360deg); }
365
- }
366
- .footer {
367
- margin-top: 2rem;
368
- color: rgba(255, 255, 255, 0.6);
369
- font-size: 0.875rem;
370
- }
371
- </style>
372
- </head>
373
- <body>
374
- <div class="container">
375
- <h1>Authentication Successful!</h1>
376
- <p>You can now close this window and return to the CLI.</p>
377
- </div>
378
- </body>
379
- </html>
380
- `);
381
- tokenResolver(token);
382
- } else {
383
- res.writeHead(400, { "Content-Type": "text/html" });
384
- res.end(`
385
- <!DOCTYPE html>
386
- <html>
387
- <head>
388
- <title>Login Failed</title>
389
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
390
- <style>
391
- * {
392
- margin: 0;
393
- padding: 0;
394
- box-sizing: border-box;
395
- }
396
- body {
397
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
398
- display: flex;
399
- justify-content: center;
400
- align-items: center;
401
- min-height: 100vh;
402
- background: #000;
403
- padding: 1rem;
404
- }
405
- .container {
406
- width: 100%;
407
- max-width: 28rem;
408
- padding: 3rem;
409
- text-align: center;
410
- background: rgba(255, 255, 255, 0.1);
411
- backdrop-filter: blur(40px);
412
- -webkit-backdrop-filter: blur(40px);
413
- border: 1px solid rgba(255, 255, 255, 0.2);
414
- border-radius: 1.5rem;
415
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
416
- }
417
- .icon-container {
418
- display: inline-flex;
419
- align-items: center;
420
- justify-content: center;
421
- width: 6rem;
422
- height: 6rem;
423
- margin-bottom: 2rem;
424
- background: rgba(255, 255, 255, 0.1);
425
- backdrop-filter: blur(10px);
426
- -webkit-backdrop-filter: blur(10px);
427
- border-radius: 50%;
428
- }
429
- .cross {
430
- font-size: 4rem;
431
- color: #fff;
432
- line-height: 1;
433
- }
434
- h1 {
435
- color: #fff;
436
- margin: 0 0 1rem 0;
437
- font-size: 2.5rem;
438
- font-weight: 700;
439
- letter-spacing: -0.025em;
440
- }
441
- p {
442
- color: rgba(255, 255, 255, 0.8);
443
- margin: 0;
444
- font-size: 1.125rem;
445
- line-height: 1.5;
446
- }
447
- </style>
448
- </head>
449
- <body>
450
- <div class="container">
451
- <div class="icon-container">
452
- <div class="cross">\u2717</div>
453
- </div>
454
- <h1>Login Failed</h1>
455
- <p>No token received. Please try again.</p>
456
- </div>
457
- </body>
458
- </html>
459
- `);
460
- }
461
- }
462
- });
463
- server.listen(port, () => {
464
- resolve({ server, token: tokenPromise });
465
- });
466
- server.on("error", reject);
467
- });
468
- }
469
- async function loginCommand() {
470
- try {
471
- if (await isLoggedIn()) {
472
- console.log(
473
- chalk.yellow(
474
- "\u26A0\uFE0F You are already logged in. Run 'mcp-use logout' first if you want to login with a different account."
475
- )
476
- );
477
- return;
478
- }
479
- console.log(chalk.cyan.bold("\u{1F510} Logging in to mcp-use cloud...\n"));
480
- const port = await findAvailablePort();
481
- const redirectUri = `http://localhost:${port}/callback`;
482
- console.log(chalk.gray(`Starting local server on port ${port}...`));
483
- const { server, token } = await startCallbackServer(port);
484
- const webUrl = await getWebUrl();
485
- const loginUrl = `${webUrl}/auth/cli?redirect_uri=${encodeURIComponent(redirectUri)}`;
486
- console.log(chalk.gray(`Opening browser to ${webUrl}/auth/cli...
487
- `));
488
- console.log(
489
- chalk.white(
490
- "If the browser doesn't open automatically, please visit:\n" + chalk.cyan(loginUrl)
491
- )
492
- );
493
- await open(loginUrl);
494
- console.log(
495
- chalk.gray("\nWaiting for authentication... (this may take a moment)")
496
- );
497
- const jwtToken = await Promise.race([
498
- token,
499
- new Promise(
500
- (_, reject) => setTimeout(
501
- () => reject(new Error("Login timeout - please try again")),
502
- LOGIN_TIMEOUT
503
- )
504
- )
505
- ]);
506
- server.close();
507
- console.log(
508
- chalk.gray("Received authentication token, creating API key...")
509
- );
510
- const api = await McpUseAPI.create();
511
- const apiKeyResponse = await api.createApiKey(jwtToken, "CLI");
512
- await writeConfig({
513
- apiKey: apiKeyResponse.api_key
514
- });
515
- console.log(chalk.green.bold("\n\u2713 Successfully logged in!"));
516
- console.log(
517
- chalk.gray(
518
- `
519
- Your API key has been saved to ${chalk.white("~/.mcp-use/config.json")}`
520
- )
521
- );
522
- console.log(
523
- chalk.gray(
524
- "You can now deploy your MCP servers with " + chalk.white("mcp-use deploy")
525
- )
526
- );
527
- process.exit(0);
528
- } catch (error) {
529
- console.error(
530
- chalk.red.bold("\n\u2717 Login failed:"),
531
- chalk.red(error instanceof Error ? error.message : "Unknown error")
532
- );
533
- process.exit(1);
534
- }
535
- }
536
- async function logoutCommand() {
537
- try {
538
- if (!await isLoggedIn()) {
539
- console.log(chalk.yellow("\u26A0\uFE0F You are not logged in."));
540
- return;
541
- }
542
- console.log(chalk.cyan.bold("\u{1F513} Logging out...\n"));
543
- await deleteConfig();
544
- console.log(chalk.green.bold("\u2713 Successfully logged out!"));
545
- console.log(
546
- chalk.gray(
547
- "\nYour local config has been deleted. The API key will remain active until revoked from the web interface."
548
- )
549
- );
550
- } catch (error) {
551
- console.error(
552
- chalk.red.bold("\n\u2717 Logout failed:"),
553
- chalk.red(error instanceof Error ? error.message : "Unknown error")
554
- );
555
- process.exit(1);
556
- }
557
- }
558
- async function whoamiCommand() {
559
- try {
560
- if (!await isLoggedIn()) {
561
- console.log(chalk.yellow("\u26A0\uFE0F You are not logged in."));
562
- console.log(
563
- chalk.gray("Run " + chalk.white("mcp-use login") + " to get started.")
564
- );
565
- return;
566
- }
567
- console.log(chalk.cyan.bold("\u{1F464} Current user:\n"));
568
- const api = await McpUseAPI.create();
569
- const authInfo = await api.testAuth();
570
- console.log(chalk.white("Email: ") + chalk.cyan(authInfo.email));
571
- console.log(chalk.white("User ID: ") + chalk.gray(authInfo.user_id));
572
- const apiKey = await getApiKey();
573
- if (apiKey) {
574
- const masked = apiKey.substring(0, 8) + "..." + apiKey.substring(apiKey.length - 4);
575
- console.log(chalk.white("API Key: ") + chalk.gray(masked));
576
- }
577
- } catch (error) {
578
- console.error(
579
- chalk.red.bold("\n\u2717 Failed to get user info:"),
580
- chalk.red(error instanceof Error ? error.message : "Unknown error")
581
- );
582
- process.exit(1);
583
- }
584
- }
585
-
586
- // src/commands/deploy.ts
587
- import chalk2 from "chalk";
588
- import { promises as fs2 } from "fs";
589
- import path2 from "path";
590
- import os2 from "os";
591
- import { exec as exec2 } from "child_process";
592
- import { promisify as promisify2 } from "util";
593
-
594
- // src/utils/git.ts
595
- import { exec } from "child_process";
596
- import { promisify } from "util";
597
- var execAsync = promisify(exec);
598
- async function gitCommand(command, cwd = process.cwd()) {
599
- try {
600
- const { stdout } = await execAsync(command, { cwd });
601
- return stdout.trim();
602
- } catch (error) {
603
- return null;
604
- }
605
- }
606
- async function isGitRepo(cwd = process.cwd()) {
607
- const result = await gitCommand("git rev-parse --is-inside-work-tree", cwd);
608
- return result === "true";
609
- }
610
- async function getRemoteUrl(cwd = process.cwd()) {
611
- return gitCommand("git config --get remote.origin.url", cwd);
612
- }
613
- function parseGitHubUrl(url) {
614
- const sshMatch = url.match(/git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/);
615
- const httpsMatch = url.match(
616
- /https:\/\/github\.com\/([^/]+)\/(.+?)(?:\.git)?$/
617
- );
618
- const match = sshMatch || httpsMatch;
619
- if (!match) return null;
620
- return {
621
- owner: match[1],
622
- repo: match[2]
623
- };
624
- }
625
- async function getCurrentBranch(cwd = process.cwd()) {
626
- return gitCommand("git rev-parse --abbrev-ref HEAD", cwd);
627
- }
628
- async function getCommitSha(cwd = process.cwd()) {
629
- return gitCommand("git rev-parse HEAD", cwd);
630
- }
631
- async function getCommitMessage(cwd = process.cwd()) {
632
- return gitCommand("git log -1 --pretty=%B", cwd);
633
- }
634
- async function getGitInfo(cwd = process.cwd()) {
635
- const isRepo = await isGitRepo(cwd);
636
- if (!isRepo) {
637
- return { isGitRepo: false };
638
- }
639
- const remoteUrl = await getRemoteUrl(cwd);
640
- const branch = await getCurrentBranch(cwd);
641
- const commitSha = await getCommitSha(cwd);
642
- const commitMessage = await getCommitMessage(cwd);
643
- let owner;
644
- let repo;
645
- if (remoteUrl) {
646
- const parsed = parseGitHubUrl(remoteUrl);
647
- if (parsed) {
648
- owner = parsed.owner;
649
- repo = parsed.repo;
650
- }
651
- }
652
- return {
653
- isGitRepo: true,
654
- remoteUrl: remoteUrl || void 0,
655
- owner,
656
- repo,
657
- branch: branch || void 0,
658
- commitSha: commitSha || void 0,
659
- commitMessage: commitMessage || void 0
660
- };
661
- }
662
- function isGitHubUrl(url) {
663
- return url.includes("github.com");
664
- }
665
-
666
- // src/commands/deploy.ts
667
- import open2 from "open";
668
- var execAsync2 = promisify2(exec2);
669
- async function isMcpProject(cwd = process.cwd()) {
670
- try {
671
- const packageJsonPath = path2.join(cwd, "package.json");
672
- const content = await fs2.readFile(packageJsonPath, "utf-8");
673
- const packageJson2 = JSON.parse(content);
674
- const hasMcpDeps = packageJson2.dependencies?.["mcp-use"] || packageJson2.dependencies?.["@modelcontextprotocol/sdk"] || packageJson2.devDependencies?.["mcp-use"] || packageJson2.devDependencies?.["@modelcontextprotocol/sdk"];
675
- const hasMcpScripts = packageJson2.scripts?.mcp || packageJson2.scripts?.["mcp:dev"];
676
- return !!(hasMcpDeps || hasMcpScripts);
677
- } catch {
678
- return false;
679
- }
680
- }
681
- async function getProjectName(cwd = process.cwd()) {
682
- try {
683
- const packageJsonPath = path2.join(cwd, "package.json");
684
- const content = await fs2.readFile(packageJsonPath, "utf-8");
685
- const packageJson2 = JSON.parse(content);
686
- if (packageJson2.name) {
687
- return packageJson2.name;
688
- }
689
- } catch {
690
- }
691
- return path2.basename(cwd);
692
- }
693
- async function detectBuildCommand(cwd = process.cwd()) {
694
- try {
695
- const packageJsonPath = path2.join(cwd, "package.json");
696
- const content = await fs2.readFile(packageJsonPath, "utf-8");
697
- const packageJson2 = JSON.parse(content);
698
- if (packageJson2.scripts?.build) {
699
- return "npm run build";
700
- }
701
- } catch {
702
- }
703
- return void 0;
704
- }
705
- async function detectStartCommand(cwd = process.cwd()) {
706
- try {
707
- const packageJsonPath = path2.join(cwd, "package.json");
708
- const content = await fs2.readFile(packageJsonPath, "utf-8");
709
- const packageJson2 = JSON.parse(content);
710
- if (packageJson2.scripts?.start) {
711
- return "npm start";
712
- }
713
- if (packageJson2.main) {
714
- return `node ${packageJson2.main}`;
715
- }
716
- } catch {
717
- }
718
- return void 0;
719
- }
720
- async function detectRuntime(cwd = process.cwd()) {
721
- try {
722
- const pythonFiles = ["requirements.txt", "pyproject.toml", "setup.py"];
723
- for (const file of pythonFiles) {
724
- try {
725
- await fs2.access(path2.join(cwd, file));
726
- return "python";
727
- } catch {
728
- continue;
729
- }
730
- }
731
- try {
732
- await fs2.access(path2.join(cwd, "package.json"));
733
- return "node";
734
- } catch {
735
- }
736
- } catch {
737
- }
738
- return "node";
739
- }
740
- async function prompt(question, defaultValue = "n") {
741
- const readline = await import("readline");
742
- const rl = readline.createInterface({
743
- input: process.stdin,
744
- output: process.stdout
745
- });
746
- const defaultIndicator = defaultValue === "y" ? "Y/n" : "y/N";
747
- const questionWithDefault = question.replace(
748
- /(\(y\/n\):)/,
749
- `(${defaultIndicator}):`
750
- );
751
- return new Promise((resolve) => {
752
- rl.question(questionWithDefault, (answer) => {
753
- rl.close();
754
- const trimmedAnswer = answer.trim().toLowerCase();
755
- if (trimmedAnswer === "") {
756
- resolve(defaultValue === "y");
757
- } else {
758
- resolve(trimmedAnswer === "y" || trimmedAnswer === "yes");
759
- }
760
- });
761
- });
762
- }
763
- async function createTarball(cwd) {
764
- const tmpDir = os2.tmpdir();
765
- const tarballPath = path2.join(tmpDir, `mcp-deploy-${Date.now()}.tar.gz`);
766
- const excludePatterns = [
767
- "node_modules",
768
- ".git",
769
- "dist",
770
- "build",
771
- ".next",
772
- ".venv",
773
- "__pycache__",
774
- "*.pyc",
775
- ".DS_Store",
776
- "._*",
777
- // macOS resource fork files
778
- ".mcp-use",
779
- // Build artifacts directory
780
- ".env",
781
- ".env.local",
782
- "*.log"
783
- ];
784
- const excludeFlags = excludePatterns.map((pattern) => `--exclude=${pattern}`).join(" ");
785
- const command = `tar ${excludeFlags} -czf "${tarballPath}" -C "${cwd}" . 2>&1 || true`;
786
- try {
787
- await execAsync2(command);
788
- return tarballPath;
789
- } catch (error) {
790
- throw new Error(
791
- `Failed to create tarball: ${error instanceof Error ? error.message : "Unknown error"}`
792
- );
793
- }
794
- }
795
- function formatFileSize(bytes) {
796
- if (bytes === 0) return "0 B";
797
- const k = 1024;
798
- const sizes = ["B", "KB", "MB", "GB"];
799
- const i = Math.floor(Math.log(bytes) / Math.log(k));
800
- return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
801
- }
802
- async function displayDeploymentProgress(api, deployment) {
803
- const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
804
- let frameIndex = 0;
805
- let spinnerInterval = null;
806
- let lastStep = "";
807
- const startSpinner = (message) => {
808
- if (spinnerInterval) {
809
- clearInterval(spinnerInterval);
810
- }
811
- process.stdout.write("\r\x1B[K");
812
- spinnerInterval = setInterval(() => {
813
- const frame = frames[frameIndex];
814
- frameIndex = (frameIndex + 1) % frames.length;
815
- process.stdout.write(
816
- "\r" + chalk2.cyan(frame) + " " + chalk2.gray(message)
817
- );
818
- }, 80);
819
- };
820
- const stopSpinner = () => {
821
- if (spinnerInterval) {
822
- clearInterval(spinnerInterval);
823
- spinnerInterval = null;
824
- process.stdout.write("\r\x1B[K");
825
- }
826
- };
827
- console.log();
828
- startSpinner("Deploying...");
829
- try {
830
- for await (const log of api.streamDeploymentLogs(deployment.id)) {
831
- try {
832
- const logData = JSON.parse(log);
833
- if (logData.step && logData.step !== lastStep) {
834
- lastStep = logData.step;
835
- const stepMessages = {
836
- clone: "Preparing source code...",
837
- analyze: "Analyzing project...",
838
- build: "Building container image...",
839
- deploy: "Deploying to cloud..."
840
- };
841
- const message = stepMessages[logData.step] || "Deploying...";
842
- startSpinner(message);
843
- }
844
- if (logData.line) {
845
- stopSpinner();
846
- const levelColor = logData.level === "error" ? chalk2.red : logData.level === "warn" ? chalk2.yellow : chalk2.gray;
847
- const stepPrefix = logData.step ? chalk2.cyan(`[${logData.step}]`) + " " : "";
848
- console.log(stepPrefix + levelColor(logData.line));
849
- }
850
- } catch {
851
- }
852
- }
853
- } catch (error) {
854
- stopSpinner();
855
- }
856
- let checkCount = 0;
857
- const maxChecks = 60;
858
- let delay = 3e3;
859
- const maxDelay = 1e4;
860
- let lastDisplayedLogLength = 0;
861
- while (checkCount < maxChecks) {
862
- const currentDelay = delay;
863
- await new Promise((resolve) => setTimeout(resolve, currentDelay));
864
- const finalDeployment = await api.getDeployment(deployment.id);
865
- if (finalDeployment.buildLogs && finalDeployment.buildLogs.length > lastDisplayedLogLength) {
866
- const newLogs = finalDeployment.buildLogs.substring(
867
- lastDisplayedLogLength
868
- );
869
- const logLines = newLogs.split("\n").filter((l) => l.trim());
870
- for (const line of logLines) {
871
- try {
872
- const logData = JSON.parse(line);
873
- if (logData.line) {
874
- stopSpinner();
875
- const levelColor = logData.level === "error" ? chalk2.red : logData.level === "warn" ? chalk2.yellow : chalk2.gray;
876
- const stepPrefix = logData.step ? chalk2.cyan(`[${logData.step}]`) + " " : "";
877
- console.log(stepPrefix + levelColor(logData.line));
878
- }
879
- } catch {
880
- }
881
- }
882
- lastDisplayedLogLength = finalDeployment.buildLogs.length;
883
- }
884
- if (finalDeployment.status === "running") {
885
- const mcpUrl = `https://${finalDeployment.domain}/mcp`;
886
- const inspectorUrl = `https://inspector.mcp-use.com/inspector?autoConnect=${encodeURIComponent(mcpUrl)}`;
887
- console.log(chalk2.green.bold("\u2713 Deployment successful!\n"));
888
- console.log(chalk2.white("\u{1F310} MCP Server URL:"));
889
- console.log(chalk2.cyan.bold(` ${mcpUrl}
890
- `));
891
- console.log(chalk2.white("\u{1F50D} Inspector URL:"));
892
- console.log(chalk2.cyan.bold(` ${inspectorUrl}
893
- `));
894
- if (finalDeployment.customDomain) {
895
- const customMcpUrl = `https://${finalDeployment.customDomain}/mcp`;
896
- const customInspectorUrl = `https://inspector.mcp-use.com/inspect?autoConnect=${encodeURIComponent(customMcpUrl)}`;
897
- console.log(chalk2.white("\u{1F517} Custom Domain:"));
898
- console.log(chalk2.cyan.bold(` ${customMcpUrl}
899
- `));
900
- console.log(chalk2.white("\u{1F50D} Custom Inspector:"));
901
- console.log(chalk2.cyan.bold(` ${customInspectorUrl}
902
- `));
903
- }
904
- console.log(
905
- chalk2.gray("Deployment ID: ") + chalk2.white(finalDeployment.id)
906
- );
907
- return;
908
- } else if (finalDeployment.status === "failed") {
909
- console.log(chalk2.red.bold("\u2717 Deployment failed\n"));
910
- if (finalDeployment.error) {
911
- console.log(chalk2.red("Error: ") + finalDeployment.error);
912
- }
913
- if (finalDeployment.buildLogs) {
914
- console.log(chalk2.gray("\nBuild logs:"));
915
- try {
916
- const logs = finalDeployment.buildLogs.split("\n").filter((l) => l.trim());
917
- for (const log of logs) {
918
- try {
919
- const logData = JSON.parse(log);
920
- if (logData.line) {
921
- console.log(chalk2.gray(` ${logData.line}`));
922
- }
923
- } catch {
924
- console.log(chalk2.gray(` ${log}`));
925
- }
926
- }
927
- } catch {
928
- console.log(chalk2.gray(finalDeployment.buildLogs));
929
- }
930
- }
931
- process.exit(1);
932
- } else if (finalDeployment.status === "building") {
933
- startSpinner("Building and deploying...");
934
- checkCount++;
935
- delay = Math.min(delay * 1.2, maxDelay);
936
- } else {
937
- console.log(
938
- chalk2.yellow("\u26A0\uFE0F Deployment status: ") + finalDeployment.status
939
- );
940
- return;
941
- }
942
- }
943
- stopSpinner();
944
- console.log(chalk2.yellow("\u26A0\uFE0F Deployment is taking longer than expected."));
945
- console.log(
946
- chalk2.gray("Check status with: ") + chalk2.white(`mcp-use status ${deployment.id}`)
947
- );
948
- }
949
- async function deployCommand(options) {
950
- try {
951
- const cwd = process.cwd();
952
- if (!await isLoggedIn()) {
953
- console.log(chalk2.red("\u2717 You are not logged in."));
954
- console.log(
955
- chalk2.gray("Run " + chalk2.white("mcp-use login") + " to get started.")
956
- );
957
- process.exit(1);
958
- }
959
- console.log(chalk2.cyan.bold("\u{1F680} Deploying to mcp-use cloud...\n"));
960
- const isMcp = await isMcpProject(cwd);
961
- if (!isMcp) {
962
- console.log(
963
- chalk2.yellow(
964
- "\u26A0\uFE0F This doesn't appear to be an MCP server project (no mcp-use or @modelcontextprotocol/sdk dependency found)."
965
- )
966
- );
967
- const shouldContinue = await prompt(
968
- chalk2.white("Continue anyway? (y/n): ")
969
- );
970
- if (!shouldContinue) {
971
- console.log(chalk2.gray("Deployment cancelled."));
972
- process.exit(0);
973
- }
974
- console.log();
975
- }
976
- const gitInfo = await getGitInfo(cwd);
977
- if (!options.fromSource && gitInfo.isGitRepo && gitInfo.remoteUrl && isGitHubUrl(gitInfo.remoteUrl)) {
978
- if (!gitInfo.owner || !gitInfo.repo) {
979
- console.log(
980
- chalk2.red(
981
- "\u2717 Could not parse GitHub repository information from remote URL."
982
- )
983
- );
984
- process.exit(1);
985
- }
986
- console.log(chalk2.white("GitHub repository detected:"));
987
- console.log(
988
- chalk2.gray(` Repository: `) + chalk2.cyan(`${gitInfo.owner}/${gitInfo.repo}`)
989
- );
990
- console.log(
991
- chalk2.gray(` Branch: `) + chalk2.cyan(gitInfo.branch || "main")
992
- );
993
- if (gitInfo.commitSha) {
994
- console.log(
995
- chalk2.gray(` Commit: `) + chalk2.gray(gitInfo.commitSha.substring(0, 7))
996
- );
997
- }
998
- if (gitInfo.commitMessage) {
999
- console.log(
1000
- chalk2.gray(` Message: `) + chalk2.gray(gitInfo.commitMessage.split("\n")[0])
1001
- );
1002
- }
1003
- console.log();
1004
- const shouldDeploy = await prompt(
1005
- chalk2.white(
1006
- `Deploy from GitHub repository ${gitInfo.owner}/${gitInfo.repo}? (y/n): `
1007
- )
1008
- );
1009
- if (!shouldDeploy) {
1010
- console.log(chalk2.gray("Deployment cancelled."));
1011
- process.exit(0);
1012
- }
1013
- const projectName = options.name || await getProjectName(cwd);
1014
- const runtime = options.runtime || await detectRuntime(cwd);
1015
- const port = options.port || 3e3;
1016
- const buildCommand = await detectBuildCommand(cwd);
1017
- const startCommand = await detectStartCommand(cwd);
1018
- console.log();
1019
- console.log(chalk2.white("Deployment configuration:"));
1020
- console.log(chalk2.gray(` Name: `) + chalk2.cyan(projectName));
1021
- console.log(chalk2.gray(` Runtime: `) + chalk2.cyan(runtime));
1022
- console.log(chalk2.gray(` Port: `) + chalk2.cyan(port));
1023
- if (buildCommand) {
1024
- console.log(chalk2.gray(` Build command: `) + chalk2.cyan(buildCommand));
1025
- }
1026
- if (startCommand) {
1027
- console.log(chalk2.gray(` Start command: `) + chalk2.cyan(startCommand));
1028
- }
1029
- console.log();
1030
- const deploymentRequest = {
1031
- name: projectName,
1032
- source: {
1033
- type: "github",
1034
- repo: `${gitInfo.owner}/${gitInfo.repo}`,
1035
- branch: gitInfo.branch || "main",
1036
- runtime,
1037
- port,
1038
- buildCommand,
1039
- startCommand
1040
- },
1041
- healthCheckPath: "/healthz"
1042
- };
1043
- console.log(chalk2.gray("Creating deployment..."));
1044
- const api = await McpUseAPI.create();
1045
- const deployment = await api.createDeployment(deploymentRequest);
1046
- console.log(
1047
- chalk2.green("\u2713 Deployment created: ") + chalk2.gray(deployment.id)
1048
- );
1049
- await displayDeploymentProgress(api, deployment);
1050
- if (options.open && deployment.domain) {
1051
- console.log();
1052
- console.log(chalk2.gray("Opening deployment in browser..."));
1053
- await open2(`https://${deployment.domain}`);
1054
- }
1055
- } else {
1056
- if (options.fromSource) {
1057
- console.log(
1058
- chalk2.white("\u{1F4E6} Deploying from local source code (--from-source)...")
1059
- );
1060
- } else {
1061
- console.log(
1062
- chalk2.yellow(
1063
- "\u26A0\uFE0F This is not a GitHub repository or no remote is configured."
1064
- )
1065
- );
1066
- console.log(chalk2.white("Deploying from local source code instead..."));
1067
- }
1068
- console.log();
1069
- const projectName = options.name || await getProjectName(cwd);
1070
- const runtime = options.runtime || await detectRuntime(cwd);
1071
- const port = options.port || 3e3;
1072
- const buildCommand = await detectBuildCommand(cwd);
1073
- const startCommand = await detectStartCommand(cwd);
1074
- console.log(chalk2.white("Deployment configuration:"));
1075
- console.log(chalk2.gray(` Name: `) + chalk2.cyan(projectName));
1076
- console.log(chalk2.gray(` Runtime: `) + chalk2.cyan(runtime));
1077
- console.log(chalk2.gray(` Port: `) + chalk2.cyan(port));
1078
- if (buildCommand) {
1079
- console.log(chalk2.gray(` Build command: `) + chalk2.cyan(buildCommand));
1080
- }
1081
- if (startCommand) {
1082
- console.log(chalk2.gray(` Start command: `) + chalk2.cyan(startCommand));
1083
- }
1084
- console.log();
1085
- const shouldDeploy = await prompt(
1086
- chalk2.white("Deploy from local source? (y/n): "),
1087
- "y"
1088
- );
1089
- if (!shouldDeploy) {
1090
- console.log(chalk2.gray("Deployment cancelled."));
1091
- process.exit(0);
1092
- }
1093
- console.log();
1094
- console.log(chalk2.gray("Packaging source code..."));
1095
- const tarballPath = await createTarball(cwd);
1096
- const stats = await fs2.stat(tarballPath);
1097
- console.log(
1098
- chalk2.green("\u2713 Packaged: ") + chalk2.gray(formatFileSize(stats.size))
1099
- );
1100
- const maxSize = 2 * 1024 * 1024;
1101
- if (stats.size > maxSize) {
1102
- console.log(
1103
- chalk2.red(
1104
- `\u2717 File size (${formatFileSize(stats.size)}) exceeds maximum of 2MB`
1105
- )
1106
- );
1107
- await fs2.unlink(tarballPath);
1108
- process.exit(1);
1109
- }
1110
- const deploymentRequest = {
1111
- name: projectName,
1112
- source: {
1113
- type: "upload",
1114
- runtime,
1115
- port,
1116
- buildCommand,
1117
- startCommand
1118
- },
1119
- healthCheckPath: "/healthz"
1120
- };
1121
- console.log(chalk2.gray("Creating deployment..."));
1122
- const api = await McpUseAPI.create();
1123
- const deployment = await api.createDeploymentWithUpload(
1124
- deploymentRequest,
1125
- tarballPath
1126
- );
1127
- await fs2.unlink(tarballPath);
1128
- console.log(
1129
- chalk2.green("\u2713 Deployment created: ") + chalk2.gray(deployment.id)
1130
- );
1131
- await displayDeploymentProgress(api, deployment);
1132
- if (options.open && deployment.domain) {
1133
- console.log();
1134
- console.log(chalk2.gray("Opening deployment in browser..."));
1135
- await open2(`https://${deployment.domain}`);
1136
- }
1137
- }
1138
- } catch (error) {
1139
- console.error(
1140
- chalk2.red.bold("\n\u2717 Deployment failed:"),
1141
- chalk2.red(error instanceof Error ? error.message : "Unknown error")
1142
- );
1143
- process.exit(1);
1144
- }
1145
- }
1146
-
1147
- // src/index.ts
1148
- var program = new Command();
1149
- var packageContent = readFileSync(
1150
- path3.join(__dirname, "../package.json"),
1151
- "utf-8"
1152
- );
1153
- var packageJson = JSON.parse(packageContent);
1154
- var packageVersion = packageJson.version || "unknown";
1155
- program.name("mcp-use").description("Create and run MCP servers with ui resources widgets").version(packageVersion);
1156
- async function isPortAvailable(port, host = "localhost") {
1157
- try {
1158
- await fetch(`http://${host}:${port}`);
1159
- return false;
1160
- } catch {
1161
- return true;
1162
- }
1163
- }
1164
- async function findAvailablePort2(startPort, host = "localhost") {
1165
- for (let port = startPort; port < startPort + 100; port++) {
1166
- if (await isPortAvailable(port, host)) {
1167
- return port;
1168
- }
1169
- }
1170
- throw new Error("No available ports found");
1171
- }
1172
- async function waitForServer(port, host = "localhost", maxAttempts = 30) {
1173
- for (let i = 0; i < maxAttempts; i++) {
1174
- const controller = new AbortController();
1175
- try {
1176
- const response = await fetch(`http://${host}:${port}/inspector/health`, {
1177
- signal: controller.signal
1178
- });
1179
- if (response.ok) {
1180
- return true;
1181
- }
1182
- } catch {
1183
- } finally {
1184
- controller.abort();
1185
- }
1186
- await new Promise((resolve) => setTimeout(resolve, 1e3));
1187
- }
1188
- return false;
1189
- }
1190
- function runCommand(command, args, cwd, env, filterStderr = false) {
1191
- const proc = spawn(command, args, {
1192
- cwd,
1193
- stdio: filterStderr ? ["inherit", "inherit", "pipe"] : "inherit",
1194
- shell: false,
1195
- env: env ? { ...process.env, ...env } : process.env
1196
- });
1197
- if (filterStderr && proc.stderr) {
1198
- proc.stderr.on("data", (data) => {
1199
- const text = data.toString();
1200
- if (!text.includes("Previous process hasn't exited yet") && !text.includes("Force killing")) {
1201
- process.stderr.write(data);
1202
- }
1203
- });
1204
- }
1205
- const promise = new Promise((resolve, reject) => {
1206
- proc.on("error", reject);
1207
- proc.on("exit", (code) => {
1208
- if (code === 0 || code === 130 || code === 143) {
1209
- resolve();
1210
- } else {
1211
- reject(new Error(`Command failed with exit code ${code}`));
1212
- }
1213
- });
1214
- });
1215
- return { promise, process: proc };
1216
- }
1217
- async function startTunnel(port, subdomain) {
1218
- return new Promise((resolve, reject) => {
1219
- console.log(chalk3.gray(`Starting tunnel for port ${port}...`));
1220
- const tunnelArgs = ["--yes", "@mcp-use/tunnel", String(port)];
1221
- if (subdomain) {
1222
- tunnelArgs.push("--subdomain", subdomain);
1223
- }
1224
- const proc = spawn("npx", tunnelArgs, {
1225
- stdio: ["ignore", "pipe", "pipe"],
1226
- shell: false
1227
- });
1228
- let resolved = false;
1229
- let isShuttingDown = false;
1230
- proc.stdout?.on("data", (data) => {
1231
- const text = data.toString();
1232
- const isShutdownMessage = text.includes("Shutting down") || text.includes("\u{1F6D1}");
1233
- if (!isShuttingDown && !isShutdownMessage) {
1234
- process.stdout.write(text);
1235
- }
1236
- const urlMatch = text.match(/https?:\/\/([a-z0-9-]+\.[a-z0-9.-]+)/i);
1237
- if (urlMatch && !resolved) {
1238
- const url = urlMatch[0];
1239
- const fullDomain = urlMatch[1];
1240
- const subdomainMatch = fullDomain.match(/^([a-z0-9-]+)\./i);
1241
- let extractedSubdomain = subdomainMatch ? subdomainMatch[1] : fullDomain.split(".")[0];
1242
- if (!/^[a-z0-9-]+$/i.test(extractedSubdomain)) {
1243
- console.warn(
1244
- chalk3.yellow(
1245
- `Warning: Extracted subdomain "${extractedSubdomain}" does not match expected format.`
1246
- )
1247
- );
1248
- extractedSubdomain = "";
1249
- }
1250
- resolved = true;
1251
- clearTimeout(setupTimeout);
1252
- console.log(chalk3.green.bold(`\u2713 Tunnel established: ${url}/mcp`));
1253
- resolve({ url, subdomain: extractedSubdomain, process: proc });
1254
- }
1255
- });
1256
- proc.stderr?.on("data", (data) => {
1257
- const text = data.toString();
1258
- if (!isShuttingDown && !text.includes("INFO") && !text.includes("bore_cli") && !text.includes("Shutting down")) {
1259
- process.stderr.write(data);
1260
- }
1261
- });
1262
- proc.on("error", (error) => {
1263
- if (!resolved) {
1264
- clearTimeout(setupTimeout);
1265
- reject(new Error(`Failed to start tunnel: ${error.message}`));
1266
- }
1267
- });
1268
- proc.on("exit", (code) => {
1269
- if (code !== 0 && !resolved) {
1270
- clearTimeout(setupTimeout);
1271
- reject(new Error(`Tunnel process exited with code ${code}`));
1272
- }
1273
- });
1274
- proc.markShutdown = () => {
1275
- isShuttingDown = true;
1276
- };
1277
- const setupTimeout = setTimeout(() => {
1278
- if (!resolved) {
1279
- proc.kill();
1280
- reject(new Error("Tunnel setup timed out"));
1281
- }
1282
- }, 3e4);
1283
- });
1284
- }
1285
- async function findServerFile(projectPath) {
1286
- const candidates = ["index.ts", "src/index.ts", "server.ts", "src/server.ts"];
1287
- for (const candidate of candidates) {
1288
- try {
1289
- await access(path3.join(projectPath, candidate));
1290
- return candidate;
1291
- } catch {
1292
- continue;
1293
- }
1294
- }
1295
- throw new Error("No server file found");
1296
- }
1297
- async function buildWidgets(projectPath) {
1298
- const { promises: fs3 } = await import("fs");
1299
- const { build } = await import("vite");
1300
- const resourcesDir = path3.join(projectPath, "resources");
1301
- const mcpUrl = process.env.MCP_URL;
1302
- try {
1303
- await access(resourcesDir);
1304
- } catch {
1305
- console.log(
1306
- chalk3.gray("No resources/ directory found - skipping widget build")
1307
- );
1308
- return [];
1309
- }
1310
- const entries = [];
1311
- try {
1312
- const files = await fs3.readdir(resourcesDir, { withFileTypes: true });
1313
- for (const dirent of files) {
1314
- if (dirent.name.startsWith("._") || dirent.name.startsWith(".DS_Store")) {
1315
- continue;
1316
- }
1317
- if (dirent.isFile() && (dirent.name.endsWith(".tsx") || dirent.name.endsWith(".ts"))) {
1318
- entries.push({
1319
- name: dirent.name.replace(/\.tsx?$/, ""),
1320
- path: path3.join(resourcesDir, dirent.name)
1321
- });
1322
- } else if (dirent.isDirectory()) {
1323
- const widgetPath = path3.join(resourcesDir, dirent.name, "widget.tsx");
1324
- try {
1325
- await fs3.access(widgetPath);
1326
- entries.push({
1327
- name: dirent.name,
1328
- path: widgetPath
1329
- });
1330
- } catch {
1331
- }
1332
- }
1333
- }
1334
- } catch (error) {
1335
- console.log(chalk3.gray("No widgets found in resources/ directory"));
1336
- return [];
1337
- }
1338
- if (entries.length === 0) {
1339
- console.log(chalk3.gray("No widgets found in resources/ directory"));
1340
- return [];
1341
- }
1342
- console.log(chalk3.gray(`Building ${entries.length} widget(s)...`));
1343
- const react = (await import("@vitejs/plugin-react")).default;
1344
- const tailwindcss = (await import("@tailwindcss/vite")).default;
1345
- const builtWidgets = [];
1346
- for (const entry of entries) {
1347
- const widgetName = entry.name;
1348
- const entryPath = entry.path;
1349
- console.log(chalk3.gray(` - Building ${widgetName}...`));
1350
- const tempDir = path3.join(projectPath, ".mcp-use", widgetName);
1351
- await fs3.mkdir(tempDir, { recursive: true });
1352
- const relativeResourcesPath = path3.relative(tempDir, resourcesDir).replace(/\\/g, "/");
1353
- const cssContent = `@import "tailwindcss";
1354
-
1355
- /* Configure Tailwind to scan the resources directory */
1356
- @source "${relativeResourcesPath}";
1357
- `;
1358
- await fs3.writeFile(path3.join(tempDir, "styles.css"), cssContent, "utf8");
1359
- const entryContent = `import React from 'react'
1360
- import { createRoot } from 'react-dom/client'
1361
- import './styles.css'
1362
- import Component from '${entryPath}'
1363
-
1364
- const container = document.getElementById('widget-root')
1365
- if (container && Component) {
1366
- const root = createRoot(container)
1367
- root.render(<Component />)
1368
- }
1369
- `;
1370
- const htmlContent = `<!doctype html>
1371
- <html lang="en">
1372
- <head>
1373
- <meta charset="UTF-8" />
1374
- <meta name="viewport" content="width=device-width,initial-scale=1" />
1375
- <title>${widgetName} Widget</title>
1376
- </head>
1377
- <body>
1378
- <div id="widget-root"></div>
1379
- <script type="module" src="/entry.tsx"></script>
1380
- </body>
1381
- </html>`;
1382
- await fs3.writeFile(path3.join(tempDir, "entry.tsx"), entryContent, "utf8");
1383
- await fs3.writeFile(path3.join(tempDir, "index.html"), htmlContent, "utf8");
1384
- const outDir = path3.join(
1385
- projectPath,
1386
- "dist",
1387
- "resources",
1388
- "widgets",
1389
- widgetName
1390
- );
1391
- const baseUrl = mcpUrl ? `${mcpUrl}/${widgetName}/` : `/mcp-use/widgets/${widgetName}/`;
1392
- let widgetMetadata = {};
1393
- try {
1394
- const metadataTempDir = path3.join(
1395
- projectPath,
1396
- ".mcp-use",
1397
- `${widgetName}-metadata`
1398
- );
1399
- await fs3.mkdir(metadataTempDir, { recursive: true });
1400
- const { createServer: createServer2 } = await import("vite");
1401
- const nodeStubsPlugin = {
1402
- name: "node-stubs",
1403
- enforce: "pre",
1404
- resolveId(id) {
1405
- if (id === "posthog-node" || id.startsWith("posthog-node/")) {
1406
- return "\0virtual:posthog-node-stub";
1407
- }
1408
- return null;
1409
- },
1410
- load(id) {
1411
- if (id === "\0virtual:posthog-node-stub") {
1412
- return `
1413
- export class PostHog {
1414
- constructor() {}
1415
- capture() {}
1416
- identify() {}
1417
- alias() {}
1418
- flush() { return Promise.resolve(); }
1419
- shutdown() { return Promise.resolve(); }
1420
- }
1421
- export default PostHog;
1422
- `;
1423
- }
1424
- return null;
1425
- }
1426
- };
1427
- const metadataServer = await createServer2({
1428
- root: metadataTempDir,
1429
- cacheDir: path3.join(metadataTempDir, ".vite-cache"),
1430
- plugins: [nodeStubsPlugin, tailwindcss(), react()],
1431
- resolve: {
1432
- alias: {
1433
- "@": resourcesDir
1434
- }
1435
- },
1436
- server: {
1437
- middlewareMode: true
1438
- },
1439
- optimizeDeps: {
1440
- // Exclude Node.js-only packages from browser bundling
1441
- exclude: ["posthog-node"]
1442
- },
1443
- ssr: {
1444
- // Force Vite to transform these packages in SSR instead of using external requires
1445
- noExternal: ["@openai/apps-sdk-ui", "react-router"],
1446
- // Mark Node.js-only packages as external in SSR mode
1447
- external: ["posthog-node"]
1448
- },
1449
- define: {
1450
- // Define process.env for SSR context
1451
- "process.env.NODE_ENV": JSON.stringify(
1452
- process.env.NODE_ENV || "development"
1453
- ),
1454
- "import.meta.env.DEV": true,
1455
- "import.meta.env.PROD": false,
1456
- "import.meta.env.MODE": JSON.stringify("development"),
1457
- "import.meta.env.SSR": true
1458
- },
1459
- clearScreen: false,
1460
- logLevel: "silent",
1461
- customLogger: {
1462
- info: () => {
1463
- },
1464
- warn: () => {
1465
- },
1466
- error: () => {
1467
- },
1468
- clearScreen: () => {
1469
- },
1470
- hasErrorLogged: () => false,
1471
- hasWarned: false,
1472
- warnOnce: () => {
1473
- }
1474
- }
1475
- });
1476
- try {
1477
- const mod = await metadataServer.ssrLoadModule(entryPath);
1478
- if (mod.widgetMetadata) {
1479
- widgetMetadata = {
1480
- ...mod.widgetMetadata,
1481
- title: mod.widgetMetadata.title || widgetName,
1482
- description: mod.widgetMetadata.description,
1483
- inputs: mod.widgetMetadata.inputs?.shape || {}
1484
- };
1485
- }
1486
- await new Promise((resolve) => setTimeout(resolve, 50));
1487
- } catch (error) {
1488
- console.warn(
1489
- chalk3.yellow(` \u26A0 Could not extract metadata for ${widgetName}`)
1490
- );
1491
- } finally {
1492
- await metadataServer.close();
1493
- try {
1494
- await fs3.rm(metadataTempDir, { recursive: true, force: true });
1495
- } catch {
1496
- }
1497
- }
1498
- } catch (error) {
1499
- }
1500
- try {
1501
- const buildNodeStubsPlugin = {
1502
- name: "node-stubs-build",
1503
- enforce: "pre",
1504
- resolveId(id) {
1505
- if (id === "posthog-node" || id.startsWith("posthog-node/")) {
1506
- return "\0virtual:posthog-node-stub";
1507
- }
1508
- if (id === "path" || id === "node:path") {
1509
- return "\0virtual:path-stub";
1510
- }
1511
- return null;
1512
- },
1513
- load(id) {
1514
- if (id === "\0virtual:posthog-node-stub") {
1515
- return `
1516
- export class PostHog {
1517
- constructor() {}
1518
- capture() {}
1519
- identify() {}
1520
- alias() {}
1521
- flush() { return Promise.resolve(); }
1522
- shutdown() { return Promise.resolve(); }
1523
- }
1524
- export default PostHog;
1525
- `;
1526
- }
1527
- if (id === "\0virtual:path-stub") {
1528
- return `
1529
- export function join(...paths) {
1530
- return paths.filter(Boolean).join("/").replace(/\\/\\//g, "/").replace(/\\/$/, "");
1531
- }
1532
- export function resolve(...paths) {
1533
- return join(...paths);
1534
- }
1535
- export function dirname(filepath) {
1536
- const parts = filepath.split("/");
1537
- parts.pop();
1538
- return parts.join("/") || "/";
1539
- }
1540
- export function basename(filepath, ext) {
1541
- const parts = filepath.split("/");
1542
- let name = parts[parts.length - 1] || "";
1543
- if (ext && name.endsWith(ext)) {
1544
- name = name.slice(0, -ext.length);
1545
- }
1546
- return name;
1547
- }
1548
- export function extname(filepath) {
1549
- const name = basename(filepath);
1550
- const index = name.lastIndexOf(".");
1551
- return index > 0 ? name.slice(index) : "";
1552
- }
1553
- export function normalize(filepath) {
1554
- return filepath.replace(/\\/\\//g, "/");
1555
- }
1556
- export function isAbsolute(filepath) {
1557
- return filepath.startsWith("/");
1558
- }
1559
- export const sep = "/";
1560
- export const delimiter = ":";
1561
- export const posix = {
1562
- join,
1563
- resolve,
1564
- dirname,
1565
- basename,
1566
- extname,
1567
- normalize,
1568
- isAbsolute,
1569
- sep,
1570
- delimiter,
1571
- };
1572
- export default {
1573
- join,
1574
- resolve,
1575
- dirname,
1576
- basename,
1577
- extname,
1578
- normalize,
1579
- isAbsolute,
1580
- sep,
1581
- delimiter,
1582
- posix,
1583
- };
1584
- `;
1585
- }
1586
- return null;
1587
- }
1588
- };
1589
- await build({
1590
- root: tempDir,
1591
- base: baseUrl,
1592
- plugins: [buildNodeStubsPlugin, tailwindcss(), react()],
1593
- experimental: {
1594
- renderBuiltUrl: (filename, { hostType }) => {
1595
- if (["js", "css"].includes(hostType)) {
1596
- return {
1597
- runtime: `window.__getFile(${JSON.stringify(filename)})`
1598
- };
1599
- } else {
1600
- return { relative: true };
1601
- }
1602
- }
1603
- },
1604
- resolve: {
1605
- alias: {
1606
- "@": resourcesDir
1607
- }
1608
- },
1609
- optimizeDeps: {
1610
- // Exclude Node.js-only packages from browser bundling
1611
- exclude: ["posthog-node"]
1612
- },
1613
- build: {
1614
- outDir,
1615
- emptyOutDir: true,
1616
- rollupOptions: {
1617
- input: path3.join(tempDir, "index.html"),
1618
- external: (id) => {
1619
- return false;
1620
- }
1621
- }
1622
- }
1623
- });
1624
- const mcpServerUrl = process.env.MCP_SERVER_URL;
1625
- if (mcpServerUrl) {
1626
- try {
1627
- const htmlPath = path3.join(outDir, "index.html");
1628
- let html = await fs3.readFile(htmlPath, "utf8");
1629
- const injectionScript = `<script>window.__getFile = (filename) => { return "${mcpUrl}/${widgetName}/"+filename }; window.__mcpPublicUrl = "${mcpServerUrl}/mcp-use/public"; window.__mcpPublicAssetsUrl = "${mcpUrl}/public";</script>`;
1630
- if (!html.includes("window.__mcpPublicUrl")) {
1631
- html = html.replace(
1632
- /<head[^>]*>/i,
1633
- `<head>
1634
- ${injectionScript}`
1635
- );
1636
- }
1637
- if (/<base\s+[^>]*\/?>/i.test(html)) {
1638
- html = html.replace(
1639
- /<base\s+[^>]*\/?>/i,
1640
- `<base href="${mcpServerUrl}">`
1641
- );
1642
- } else {
1643
- html = html.replace(
1644
- injectionScript,
1645
- `${injectionScript}
1646
- <base href="${mcpServerUrl}">`
1647
- );
1648
- }
1649
- await fs3.writeFile(htmlPath, html, "utf8");
1650
- console.log(
1651
- chalk3.gray(` \u2192 Injected MCP_SERVER_URL into ${widgetName}`)
1652
- );
1653
- } catch (error) {
1654
- console.warn(
1655
- chalk3.yellow(
1656
- ` \u26A0 Failed to post-process HTML for ${widgetName}:`,
1657
- error
1658
- )
1659
- );
1660
- }
1661
- }
1662
- builtWidgets.push({
1663
- name: widgetName,
1664
- metadata: widgetMetadata
1665
- });
1666
- console.log(chalk3.green(` \u2713 Built ${widgetName}`));
1667
- } catch (error) {
1668
- console.error(chalk3.red(` \u2717 Failed to build ${widgetName}:`), error);
1669
- }
1670
- }
1671
- return builtWidgets;
1672
- }
1673
- program.command("build").description("Build TypeScript and MCP UI widgets").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--with-inspector", "Include inspector in production build").action(async (options) => {
1674
- try {
1675
- const projectPath = path3.resolve(options.path);
1676
- const { promises: fs3 } = await import("fs");
1677
- console.log(chalk3.cyan.bold(`mcp-use v${packageJson.version}`));
1678
- const builtWidgets = await buildWidgets(projectPath);
1679
- console.log(chalk3.gray("Building TypeScript..."));
1680
- await runCommand("npx", ["tsc"], projectPath);
1681
- console.log(chalk3.green("\u2713 TypeScript build complete!"));
1682
- const publicDir = path3.join(projectPath, "public");
1683
- try {
1684
- await fs3.access(publicDir);
1685
- console.log(chalk3.gray("Copying public assets..."));
1686
- await fs3.cp(publicDir, path3.join(projectPath, "dist", "public"), {
1687
- recursive: true
1688
- });
1689
- console.log(chalk3.green("\u2713 Public assets copied"));
1690
- } catch {
1691
- }
1692
- const manifestPath = path3.join(projectPath, "dist", "mcp-use.json");
1693
- let existingManifest = {};
1694
- try {
1695
- const existingContent = await fs3.readFile(manifestPath, "utf-8");
1696
- existingManifest = JSON.parse(existingContent);
1697
- } catch {
1698
- }
1699
- const widgetsData = {};
1700
- for (const widget of builtWidgets) {
1701
- widgetsData[widget.name] = widget.metadata;
1702
- }
1703
- const includeInspector = !!options.withInspector;
1704
- const buildTime = (/* @__PURE__ */ new Date()).toISOString();
1705
- const { createHash } = await import("crypto");
1706
- const buildId = createHash("sha256").update(buildTime + Math.random().toString()).digest("hex").substring(0, 16);
1707
- const manifest = {
1708
- ...existingManifest,
1709
- // Preserve existing fields like tunnel
1710
- includeInspector,
1711
- buildTime,
1712
- buildId,
1713
- widgets: widgetsData
1714
- };
1715
- await fs3.mkdir(path3.dirname(manifestPath), { recursive: true });
1716
- await fs3.writeFile(
1717
- manifestPath,
1718
- JSON.stringify(manifest, null, 2),
1719
- "utf8"
1720
- );
1721
- console.log(chalk3.green("\u2713 Build manifest created"));
1722
- console.log(chalk3.green.bold(`
1723
- \u2713 Build complete!`));
1724
- if (builtWidgets.length > 0) {
1725
- console.log(chalk3.gray(` ${builtWidgets.length} widget(s) built`));
1726
- }
1727
- if (options.withInspector) {
1728
- console.log(chalk3.gray(" Inspector included"));
1729
- }
1730
- } catch (error) {
1731
- console.error(chalk3.red("Build failed:"), error);
1732
- process.exit(1);
1733
- }
1734
- });
1735
- program.command("dev").description("Run development server with auto-reload and inspector").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--port <port>", "Server port", "3000").option("--host <host>", "Server host", "localhost").option("--no-open", "Do not auto-open inspector").action(async (options) => {
1736
- try {
1737
- const projectPath = path3.resolve(options.path);
1738
- let port = parseInt(options.port, 10);
1739
- const host = options.host;
1740
- console.log(chalk3.cyan.bold(`mcp-use v${packageJson.version}`));
1741
- if (!await isPortAvailable(port, host)) {
1742
- console.log(chalk3.yellow.bold(`\u26A0\uFE0F Port ${port} is already in use`));
1743
- const availablePort = await findAvailablePort2(port, host);
1744
- console.log(chalk3.green.bold(`\u2713 Using port ${availablePort} instead`));
1745
- port = availablePort;
1746
- }
1747
- const serverFile = await findServerFile(projectPath);
1748
- const processes = [];
1749
- const env = {
1750
- PORT: String(port),
1751
- HOST: host,
1752
- NODE_ENV: "development"
1753
- };
1754
- const serverCommand = runCommand(
1755
- "npx",
1756
- ["tsx", "watch", serverFile],
1757
- projectPath,
1758
- env,
1759
- true
1760
- );
1761
- processes.push(serverCommand.process);
1762
- if (options.open !== false) {
1763
- const startTime = Date.now();
1764
- const ready = await waitForServer(port, host);
1765
- if (ready) {
1766
- const mcpEndpoint = `http://${host}:${port}/mcp`;
1767
- const inspectorUrl = `http://${host}:${port}/inspector?autoConnect=${encodeURIComponent(mcpEndpoint)}`;
1768
- const readyTime = Date.now() - startTime;
1769
- console.log(chalk3.green.bold(`\u2713 Ready in ${readyTime}ms`));
1770
- console.log(chalk3.whiteBright(`Local: http://${host}:${port}`));
1771
- console.log(chalk3.whiteBright(`Network: http://${host}:${port}`));
1772
- console.log(chalk3.whiteBright(`MCP: ${mcpEndpoint}`));
1773
- console.log(chalk3.whiteBright(`Inspector: ${inspectorUrl}
1774
- `));
1775
- await open3(inspectorUrl);
1776
- }
1777
- }
1778
- const cleanup = () => {
1779
- console.log(chalk3.gray("\n\nShutting down..."));
1780
- const processesToKill = processes.length;
1781
- let killedCount = 0;
1782
- const checkAndExit = () => {
1783
- killedCount++;
1784
- if (killedCount >= processesToKill) {
1785
- process.exit(0);
1786
- }
1787
- };
1788
- processes.forEach((proc) => {
1789
- if (proc && typeof proc.kill === "function") {
1790
- proc.on("exit", checkAndExit);
1791
- proc.kill("SIGINT");
1792
- } else {
1793
- checkAndExit();
1794
- }
1795
- });
1796
- setTimeout(() => {
1797
- processes.forEach((proc) => {
1798
- if (proc && typeof proc.kill === "function" && proc.exitCode === null) {
1799
- proc.kill("SIGKILL");
1800
- }
1801
- });
1802
- process.exit(0);
1803
- }, 1e3);
1804
- };
1805
- process.on("SIGINT", cleanup);
1806
- process.on("SIGTERM", cleanup);
1807
- await new Promise(() => {
1808
- });
1809
- } catch (error) {
1810
- console.error(chalk3.red("Dev mode failed:"), error);
1811
- process.exit(1);
1812
- }
1813
- });
1814
- program.command("start").description("Start production server").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--port <port>", "Server port", "3000").option("--tunnel", "Expose server through a tunnel").action(async (options) => {
1815
- try {
1816
- const projectPath = path3.resolve(options.path);
1817
- const port = parseInt(options.port, 10);
1818
- console.log(
1819
- `\x1B[36m\x1B[1mmcp-use\x1B[0m \x1B[90mVersion: ${packageJson.version}\x1B[0m
1820
- `
1821
- );
1822
- let mcpUrl;
1823
- let tunnelProcess = void 0;
1824
- let tunnelSubdomain = void 0;
1825
- if (options.tunnel) {
1826
- try {
1827
- const manifestPath = path3.join(projectPath, "dist", "mcp-use.json");
1828
- let existingSubdomain;
1829
- try {
1830
- const manifestContent = await readFile(manifestPath, "utf-8");
1831
- const manifest = JSON.parse(manifestContent);
1832
- existingSubdomain = manifest.tunnel?.subdomain;
1833
- if (existingSubdomain) {
1834
- console.log(
1835
- chalk3.gray(`Found existing subdomain: ${existingSubdomain}`)
1836
- );
1837
- }
1838
- } catch (error) {
1839
- console.debug(
1840
- chalk3.gray(
1841
- `Debug: Failed to read or parse mcp-use.json: ${error instanceof Error ? error.message : String(error)}`
1842
- )
1843
- );
1844
- }
1845
- const tunnelInfo = await startTunnel(port, existingSubdomain);
1846
- mcpUrl = tunnelInfo.url;
1847
- tunnelProcess = tunnelInfo.process;
1848
- const subdomain = tunnelInfo.subdomain;
1849
- tunnelSubdomain = subdomain;
1850
- try {
1851
- let manifest = {};
1852
- try {
1853
- const manifestContent = await readFile(manifestPath, "utf-8");
1854
- manifest = JSON.parse(manifestContent);
1855
- } catch {
1856
- }
1857
- if (!manifest.tunnel) {
1858
- manifest.tunnel = {};
1859
- }
1860
- manifest.tunnel.subdomain = subdomain;
1861
- await mkdir(path3.dirname(manifestPath), { recursive: true });
1862
- await writeFile(
1863
- manifestPath,
1864
- JSON.stringify(manifest, null, 2),
1865
- "utf-8"
1866
- );
1867
- } catch (error) {
1868
- console.warn(
1869
- chalk3.yellow(
1870
- `\u26A0\uFE0F Failed to save subdomain to mcp-use.json: ${error instanceof Error ? error.message : "Unknown error"}`
1871
- )
1872
- );
1873
- }
1874
- } catch (error) {
1875
- console.error(chalk3.red("Failed to start tunnel:"), error);
1876
- process.exit(1);
1877
- }
1878
- }
1879
- let serverFile = "dist/index.js";
1880
- try {
1881
- await access(path3.join(projectPath, serverFile));
1882
- } catch {
1883
- serverFile = "dist/server.js";
1884
- }
1885
- console.log("Starting production server...");
1886
- const env = {
1887
- ...process.env,
1888
- PORT: String(port),
1889
- NODE_ENV: "production"
1890
- };
1891
- if (mcpUrl) {
1892
- env.MCP_URL = mcpUrl;
1893
- console.log(chalk3.whiteBright(`Tunnel: ${mcpUrl}/mcp`));
1894
- }
1895
- const serverProc = spawn("node", [serverFile], {
1896
- cwd: projectPath,
1897
- stdio: "inherit",
1898
- env
1899
- });
1900
- let cleanupInProgress = false;
1901
- const cleanup = async () => {
1902
- if (cleanupInProgress) {
1903
- return;
1904
- }
1905
- cleanupInProgress = true;
1906
- console.log(chalk3.gray("\n\nShutting down..."));
1907
- if (tunnelProcess && typeof tunnelProcess.markShutdown === "function") {
1908
- tunnelProcess.markShutdown();
1909
- }
1910
- if (tunnelSubdomain) {
1911
- try {
1912
- const apiBase = process.env.MCP_USE_API || "https://local.mcp-use.run";
1913
- await fetch(`${apiBase}/api/tunnels/${tunnelSubdomain}`, {
1914
- method: "DELETE"
1915
- });
1916
- } catch (err) {
1917
- }
1918
- }
1919
- const processesToKill = 1 + (tunnelProcess ? 1 : 0);
1920
- let killedCount = 0;
1921
- const checkAndExit = () => {
1922
- killedCount++;
1923
- if (killedCount >= processesToKill) {
1924
- process.exit(0);
1925
- }
1926
- };
1927
- serverProc.on("exit", checkAndExit);
1928
- serverProc.kill("SIGTERM");
1929
- if (tunnelProcess && typeof tunnelProcess.kill === "function") {
1930
- tunnelProcess.on("exit", checkAndExit);
1931
- tunnelProcess.kill("SIGINT");
1932
- } else {
1933
- checkAndExit();
1934
- }
1935
- setTimeout(() => {
1936
- if (serverProc.exitCode === null) {
1937
- serverProc.kill("SIGKILL");
1938
- }
1939
- if (tunnelProcess && tunnelProcess.exitCode === null) {
1940
- tunnelProcess.kill("SIGKILL");
1941
- }
1942
- process.exit(0);
1943
- }, 2e3);
1944
- };
1945
- process.on("SIGINT", cleanup);
1946
- process.on("SIGTERM", cleanup);
1947
- serverProc.on("exit", (code) => {
1948
- process.exit(code || 0);
1949
- });
1950
- } catch (error) {
1951
- console.error("Start failed:", error);
1952
- process.exit(1);
1953
- }
1954
- });
1955
- program.command("login").description("Login to mcp-use cloud").action(async () => {
1956
- await loginCommand();
1957
- });
1958
- program.command("logout").description("Logout from mcp-use cloud").action(async () => {
1959
- await logoutCommand();
1960
- });
1961
- program.command("whoami").description("Show current user information").action(async () => {
1962
- await whoamiCommand();
1963
- });
1964
- program.command("deploy").description("Deploy MCP server to mcp-use cloud").option("--open", "Open deployment in browser after successful deploy").option("--name <name>", "Custom deployment name").option("--port <port>", "Server port", "3000").option("--runtime <runtime>", "Runtime (node or python)").option(
1965
- "--from-source",
1966
- "Deploy from local source code (even for GitHub repos)"
1967
- ).action(async (options) => {
1968
- await deployCommand({
1969
- open: options.open,
1970
- name: options.name,
1971
- port: options.port ? parseInt(options.port, 10) : void 0,
1972
- runtime: options.runtime,
1973
- fromSource: options.fromSource
1974
- });
1975
- });
1976
- program.parse();