@mcp-use/cli 3.0.1 → 3.0.2-canary.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1202,6 +1202,13 @@ var GitHubAuthRequiredError = class extends Error {
1202
1202
  this.authorizeUrl = authorizeUrl;
1203
1203
  }
1204
1204
  };
1205
+ var ApiUnauthorizedError = class extends Error {
1206
+ status = 401;
1207
+ constructor(message = "Your session has expired or your API key is invalid.") {
1208
+ super(message);
1209
+ this.name = "ApiUnauthorizedError";
1210
+ }
1211
+ };
1205
1212
  var McpUseAPI = class _McpUseAPI {
1206
1213
  baseUrl;
1207
1214
  apiKey;
@@ -1243,11 +1250,7 @@ var McpUseAPI = class _McpUseAPI {
1243
1250
  });
1244
1251
  clearTimeout(timeoutId);
1245
1252
  if (response.status === 401) {
1246
- const err = new Error(
1247
- "Your session has expired or your API key is invalid."
1248
- );
1249
- err.status = 401;
1250
- throw err;
1253
+ throw new ApiUnauthorizedError();
1251
1254
  }
1252
1255
  if (!response.ok) {
1253
1256
  const errorText = await response.text();
@@ -2803,23 +2806,54 @@ import { promises as fs9 } from "fs";
2803
2806
  import path5 from "path";
2804
2807
 
2805
2808
  // src/utils/git.ts
2806
- import { exec } from "child_process";
2809
+ import { execFile as execFile7 } from "child_process";
2807
2810
  import { promisify as promisify7 } from "util";
2808
- var execAsync = promisify7(exec);
2809
- async function gitCommand(command, cwd = process.cwd()) {
2811
+ var execFileAsync5 = promisify7(execFile7);
2812
+ async function gitCommand(args, cwd = process.cwd()) {
2810
2813
  try {
2811
- const { stdout } = await execAsync(command, { cwd });
2814
+ const { stdout } = await execFileAsync5("git", args, { cwd });
2812
2815
  return stdout.trim();
2813
2816
  } catch (error) {
2814
2817
  return null;
2815
2818
  }
2816
2819
  }
2820
+ var GitCommandError = class extends Error {
2821
+ command;
2822
+ stderr;
2823
+ stdout;
2824
+ exitCode;
2825
+ constructor(opts) {
2826
+ const trimmed = opts.stderr.trim() || opts.stdout.trim() || "unknown error";
2827
+ super(`git command failed: \`${opts.command}\`
2828
+ ${trimmed}`);
2829
+ this.name = "GitCommandError";
2830
+ this.command = opts.command;
2831
+ this.stderr = opts.stderr;
2832
+ this.stdout = opts.stdout;
2833
+ this.exitCode = opts.exitCode;
2834
+ }
2835
+ };
2836
+ async function gitCommandOrThrow(args, cwd = process.cwd()) {
2837
+ const command = `git ${args.join(" ")}`;
2838
+ try {
2839
+ const { stdout } = await execFileAsync5("git", args, { cwd });
2840
+ return stdout.trim();
2841
+ } catch (error) {
2842
+ const e = error;
2843
+ throw new GitCommandError({
2844
+ command,
2845
+ stderr: (e.stderr ?? "").toString(),
2846
+ stdout: (e.stdout ?? "").toString(),
2847
+ exitCode: typeof e.code === "number" ? e.code : null
2848
+ });
2849
+ }
2850
+ }
2817
2851
  async function isGitRepo(cwd = process.cwd()) {
2818
- const result = await gitCommand("git rev-parse --is-inside-work-tree", cwd);
2852
+ const result = await gitCommand(["rev-parse", "--is-inside-work-tree"], cwd);
2819
2853
  return result === "true";
2820
2854
  }
2821
2855
  async function getRemoteUrl(cwd = process.cwd()) {
2822
- return gitCommand("git config --get remote.origin.url", cwd);
2856
+ return gitCommand(["config", "--get", "remote.origin.url"], cwd);
2823
2857
  }
2824
2858
  function parseGitHubUrl(url) {
2825
2859
  const sshMatch = url.match(/git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/);
@@ -2834,16 +2868,16 @@ function parseGitHubUrl(url) {
2834
2868
  };
2835
2869
  }
2836
2870
  async function getCurrentBranch(cwd = process.cwd()) {
2837
- return gitCommand("git rev-parse --abbrev-ref HEAD", cwd);
2871
+ return gitCommand(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
2838
2872
  }
2839
2873
  async function getCommitSha(cwd = process.cwd()) {
2840
- return gitCommand("git rev-parse HEAD", cwd);
2874
+ return gitCommand(["rev-parse", "HEAD"], cwd);
2841
2875
  }
2842
2876
  async function getCommitMessage(cwd = process.cwd()) {
2843
- return gitCommand("git log -1 --pretty=%B", cwd);
2877
+ return gitCommand(["log", "-1", "--pretty=%B"], cwd);
2844
2878
  }
2845
2879
  async function hasUncommittedChanges(cwd = process.cwd()) {
2846
- const result = await gitCommand("git status --porcelain", cwd);
2880
+ const result = await gitCommand(["status", "--porcelain"], cwd);
2847
2881
  return result !== null && result.length > 0;
2848
2882
  }
2849
2883
  async function getGitInfo(cwd = process.cwd()) {
@@ -2877,18 +2911,20 @@ async function getGitInfo(cwd = process.cwd()) {
2877
2911
  };
2878
2912
  }
2879
2913
  async function gitInit(cwd, message = "Initial commit") {
2880
- await gitCommand("git init", cwd);
2881
- await gitCommand("git add .", cwd);
2882
- await gitCommand(`git commit -m "${message}"`, cwd);
2914
+ await gitCommandOrThrow(["init"], cwd);
2915
+ await gitCommandOrThrow(["add", "."], cwd);
2916
+ await gitCommandOrThrow(["commit", "-m", message], cwd);
2917
+ await gitCommandOrThrow(["branch", "-M", "main"], cwd);
2918
+ await gitCommandOrThrow(["rev-parse", "HEAD"], cwd);
2883
2919
  }
2884
2920
  async function gitAddRemoteAndPush(cwd, cloneUrl, branch = "main") {
2885
- await gitCommand(`git remote add origin ${cloneUrl}`, cwd);
2886
- await gitCommand(`git push -u origin ${branch}`, cwd);
2921
+ await gitCommandOrThrow(["remote", "add", "origin", cloneUrl], cwd);
2922
+ await gitCommandOrThrow(["push", "-u", "origin", branch], cwd);
2887
2923
  }
2888
2924
  async function gitCommitAndPush(cwd, message, branch = "main") {
2889
- await gitCommand("git add .", cwd);
2890
- await gitCommand(`git commit -m "${message}"`, cwd);
2891
- await gitCommand(`git push origin ${branch}`, cwd);
2925
+ await gitCommandOrThrow(["add", "."], cwd);
2926
+ await gitCommandOrThrow(["commit", "-m", message], cwd);
2927
+ await gitCommandOrThrow(["push", "origin", branch], cwd);
2892
2928
  }
2893
2929
  function isGitHubUrl(url) {
2894
2930
  try {
@@ -3289,6 +3325,58 @@ async function displayDeploymentProgress(api, deploymentId, progressOptions) {
3289
3325
  source_default.gray("Check status with: ") + source_default.white(`mcp-use deployments get ${deploymentId}`)
3290
3326
  );
3291
3327
  }
3328
+ async function promptReauthenticateOn401(options, orgIdToRestore) {
3329
+ console.log(source_default.red("\n\u2717 Session expired or API key invalid."));
3330
+ if (options.yes) {
3331
+ console.log(
3332
+ source_default.gray(" Run mcp-use login to re-authenticate, then retry.")
3333
+ );
3334
+ process.exit(1);
3335
+ }
3336
+ const should = await prompt(source_default.white("Log in again? (Y/n): "), "y");
3337
+ if (!should) {
3338
+ process.exit(1);
3339
+ }
3340
+ await loginCommand({ silent: false });
3341
+ if (!await isLoggedIn()) {
3342
+ console.log(source_default.red("\u2717 Login failed. Please try again."));
3343
+ process.exit(1);
3344
+ }
3345
+ const fresh = await McpUseAPI.create();
3346
+ if (orgIdToRestore) {
3347
+ fresh.setOrgId(orgIdToRestore);
3348
+ }
3349
+ return fresh;
3350
+ }
3351
+ async function ensureApiSessionForDeploy(api, options, orgIdToRestore) {
3352
+ let client = api;
3353
+ for (; ; ) {
3354
+ try {
3355
+ await client.testAuth();
3356
+ return client;
3357
+ } catch (e) {
3358
+ if (!(e instanceof ApiUnauthorizedError)) throw e;
3359
+ client = await promptReauthenticateOn401(options, orgIdToRestore);
3360
+ }
3361
+ }
3362
+ }
3363
+ async function getGitHubConnectionStatusWith401Retry(api, options, orgIdToRestore) {
3364
+ let client = api;
3365
+ for (let attempt = 0; attempt < 2; attempt++) {
3366
+ try {
3367
+ const status = await client.getGitHubConnectionStatus();
3368
+ return { api: client, status };
3369
+ } catch (e) {
3370
+ if (e instanceof ApiUnauthorizedError && attempt === 0) {
3371
+ client = await promptReauthenticateOn401(options, orgIdToRestore);
3372
+ await client.testAuth();
3373
+ continue;
3374
+ }
3375
+ throw e;
3376
+ }
3377
+ }
3378
+ throw new Error("Unreachable");
3379
+ }
3292
3380
  async function checkRepoAccess(api, owner, repo) {
3293
3381
  try {
3294
3382
  const resp = await api.getGitHubRepos(true);
@@ -3299,6 +3387,7 @@ async function checkRepoAccess(api, owner, repo) {
3299
3387
  }
3300
3388
  async function promptGitHubInstallation(api, reason, repoName, opts) {
3301
3389
  const yes = !!opts?.yes;
3390
+ const reauth = opts?.reauth;
3302
3391
  console.log();
3303
3392
  if (reason === "not_connected") {
3304
3393
  console.log(source_default.yellow("\u26A0\uFE0F GitHub account not connected"));
@@ -3322,9 +3411,23 @@ async function promptGitHubInstallation(api, reason, repoName, opts) {
3322
3411
  ),
3323
3412
  "y"
3324
3413
  );
3325
- if (!shouldInstall) return false;
3414
+ if (!shouldInstall) return { ok: false, api };
3415
+ let client = api;
3326
3416
  try {
3327
- const appName = await api.getGitHubAppName();
3417
+ let appName;
3418
+ for (; ; ) {
3419
+ try {
3420
+ appName = await client.getGitHubAppName();
3421
+ break;
3422
+ } catch (e) {
3423
+ if (e instanceof ApiUnauthorizedError && reauth) {
3424
+ client = await reauth();
3425
+ await client.testAuth();
3426
+ continue;
3427
+ }
3428
+ throw e;
3429
+ }
3430
+ }
3328
3431
  const installUrl = `https://github.com/apps/${appName}/installations/new`;
3329
3432
  console.log(source_default.cyan(`
3330
3433
  Opening browser...`));
@@ -3348,23 +3451,34 @@ Opening browser...`));
3348
3451
  while (Date.now() < deadline) {
3349
3452
  await new Promise((r) => setTimeout(r, 2e3));
3350
3453
  try {
3351
- const status = await api.getGitHubConnectionStatus();
3454
+ const status = await client.getGitHubConnectionStatus();
3352
3455
  if (status.is_connected) {
3353
- if (!repoName) return true;
3456
+ if (!repoName) return { ok: true, api: client };
3354
3457
  const [o, r] = repoName.split("/");
3355
- if (o && r && await checkRepoAccess(api, o, r)) return true;
3458
+ if (o && r && await checkRepoAccess(client, o, r)) {
3459
+ return { ok: true, api: client };
3460
+ }
3461
+ }
3462
+ } catch (e) {
3463
+ if (e instanceof ApiUnauthorizedError && reauth) {
3464
+ client = await reauth();
3465
+ await client.testAuth();
3466
+ continue;
3356
3467
  }
3357
- } catch {
3358
3468
  }
3359
3469
  }
3360
3470
  }
3361
- return true;
3362
- } catch {
3471
+ return { ok: true, api: client };
3472
+ } catch (e) {
3473
+ if (e instanceof ApiUnauthorizedError) {
3474
+ console.log(source_default.red("\n\u2717 Session expired or API key invalid."));
3475
+ process.exit(1);
3476
+ }
3363
3477
  console.log(source_default.yellow("\n\u26A0\uFE0F Unable to open browser automatically"));
3364
3478
  console.log(
3365
3479
  source_default.white("Please visit: ") + source_default.cyan("https://manufact.com/cloud/settings")
3366
3480
  );
3367
- return false;
3481
+ return { ok: false, api: client };
3368
3482
  }
3369
3483
  }
3370
3484
  async function deployCommand(options) {
@@ -3407,7 +3521,8 @@ async function deployCommand(options) {
3407
3521
  process.exit(1);
3408
3522
  }
3409
3523
  }
3410
- const api = await McpUseAPI.create();
3524
+ let api = await McpUseAPI.create();
3525
+ let resolvedOrgId;
3411
3526
  if (options.org) {
3412
3527
  const authInfo = await api.testAuth();
3413
3528
  const match = (authInfo.orgs ?? []).find(
@@ -3415,6 +3530,7 @@ async function deployCommand(options) {
3415
3530
  );
3416
3531
  if (match) {
3417
3532
  api.setOrgId(match.id);
3533
+ resolvedOrgId = match.id;
3418
3534
  const slug = match.slug ? source_default.gray(` (${match.slug})`) : "";
3419
3535
  console.log(
3420
3536
  source_default.gray("Organization: ") + source_default.cyan(match.name) + slug
@@ -3453,6 +3569,7 @@ async function deployCommand(options) {
3453
3569
  process.exit(1);
3454
3570
  }
3455
3571
  api.setOrgId(selectedOrg.id);
3572
+ resolvedOrgId = selectedOrg.id;
3456
3573
  await writeConfig({
3457
3574
  ...config,
3458
3575
  orgId: selectedOrg.id,
@@ -3463,6 +3580,8 @@ async function deployCommand(options) {
3463
3580
  source_default.gray("Organization: ") + source_default.cyan(selectedOrg.name)
3464
3581
  );
3465
3582
  } else {
3583
+ resolvedOrgId = config.orgId;
3584
+ api.setOrgId(config.orgId);
3466
3585
  if (config.orgName) {
3467
3586
  const slug = config.orgSlug ? source_default.gray(` (${config.orgSlug})`) : "";
3468
3587
  console.log(
@@ -3471,21 +3590,39 @@ async function deployCommand(options) {
3471
3590
  }
3472
3591
  }
3473
3592
  }
3593
+ api = await ensureApiSessionForDeploy(api, options, resolvedOrgId);
3474
3594
  console.log(source_default.cyan.bold("\n\u{1F680} Deploying to Manufact cloud...\n"));
3475
- let connectionStatus = await api.getGitHubConnectionStatus().catch(() => null);
3476
- if (!connectionStatus?.is_connected) {
3595
+ const reauth = () => promptReauthenticateOn401(options, resolvedOrgId);
3596
+ let ghConn = await getGitHubConnectionStatusWith401Retry(
3597
+ api,
3598
+ options,
3599
+ resolvedOrgId
3600
+ );
3601
+ api = ghConn.api;
3602
+ let connectionStatus = ghConn.status;
3603
+ if (!connectionStatus.is_connected) {
3477
3604
  const installed = await promptGitHubInstallation(
3478
3605
  api,
3479
3606
  "not_connected",
3480
3607
  void 0,
3481
- { yes: options.yes }
3608
+ {
3609
+ yes: options.yes,
3610
+ reauth
3611
+ }
3482
3612
  );
3483
- if (!installed) {
3613
+ if (!installed.ok) {
3484
3614
  console.log(source_default.gray("Deployment cancelled."));
3485
3615
  process.exit(0);
3486
3616
  }
3487
- connectionStatus = await api.getGitHubConnectionStatus().catch(() => null);
3488
- if (!connectionStatus?.is_connected) {
3617
+ api = installed.api;
3618
+ ghConn = await getGitHubConnectionStatusWith401Retry(
3619
+ api,
3620
+ options,
3621
+ resolvedOrgId
3622
+ );
3623
+ api = ghConn.api;
3624
+ connectionStatus = ghConn.status;
3625
+ if (!connectionStatus.is_connected) {
3489
3626
  console.log(source_default.red("\n\u2717 GitHub connection could not be verified."));
3490
3627
  console.log(
3491
3628
  source_default.cyan(
@@ -3652,27 +3789,56 @@ async function deployCommand(options) {
3652
3789
  }
3653
3790
  }
3654
3791
  console.log(source_default.green(`\u2713 Created ${source_default.cyan(repoResult.fullName)}`));
3655
- if (!gitInfo.isGitRepo) {
3656
- await ensureGitignore(cwd);
3657
- console.log(source_default.gray("Initializing git..."));
3658
- await gitInit(cwd, "Initial commit");
3659
- console.log(source_default.gray("Pushing to GitHub..."));
3660
- await gitAddRemoteAndPush(cwd, repoResult.cloneUrl, "main");
3661
- } else {
3662
- if (await hasUncommittedChanges(cwd)) {
3663
- console.log(
3664
- source_default.red(
3665
- "\u2717 You have uncommitted changes. Commit and push before deploying."
3666
- )
3792
+ try {
3793
+ if (!gitInfo.isGitRepo) {
3794
+ await ensureGitignore(cwd);
3795
+ console.log(source_default.gray("Initializing git..."));
3796
+ await gitInit(cwd, "Initial commit");
3797
+ console.log(source_default.gray("Pushing to GitHub..."));
3798
+ await gitAddRemoteAndPush(cwd, repoResult.cloneUrl, "main");
3799
+ } else {
3800
+ if (await hasUncommittedChanges(cwd)) {
3801
+ console.log(
3802
+ source_default.red(
3803
+ "\u2717 You have uncommitted changes. Commit and push before deploying."
3804
+ )
3805
+ );
3806
+ process.exit(1);
3807
+ }
3808
+ console.log(source_default.gray("Adding remote and pushing..."));
3809
+ await gitAddRemoteAndPush(
3810
+ cwd,
3811
+ repoResult.cloneUrl,
3812
+ gitInfo.branch || "main"
3667
3813
  );
3814
+ }
3815
+ } catch (err) {
3816
+ if (err instanceof GitCommandError) {
3817
+ console.log(source_default.red(`
3818
+ \u2717 Git step failed: \`${err.command}\``));
3819
+ const stderrTrimmed = (err.stderr || err.stdout).trim();
3820
+ if (stderrTrimmed) {
3821
+ console.log(source_default.gray(stderrTrimmed));
3822
+ }
3823
+ if (/tell me who you are|user\.email|user\.name/i.test(err.stderr)) {
3824
+ console.log(
3825
+ source_default.yellow(
3826
+ `
3827
+ Set your git identity for this project and retry:
3828
+ git -C ${JSON.stringify(cwd)} config user.email "you@example.com"
3829
+ git -C ${JSON.stringify(cwd)} config user.name "Your Name"`
3830
+ )
3831
+ );
3832
+ } else if (/non-fast-forward|rejected|unrelated histories/i.test(err.stderr)) {
3833
+ console.log(
3834
+ source_default.yellow(
3835
+ "\n The remote branch already has commits. Either delete the empty GitHub repo and retry, or reconcile manually:\n git pull --rebase origin main --allow-unrelated-histories\n git push -u origin main"
3836
+ )
3837
+ );
3838
+ }
3668
3839
  process.exit(1);
3669
3840
  }
3670
- console.log(source_default.gray("Adding remote and pushing..."));
3671
- await gitAddRemoteAndPush(
3672
- cwd,
3673
- repoResult.cloneUrl,
3674
- gitInfo.branch || "main"
3675
- );
3841
+ throw err;
3676
3842
  }
3677
3843
  console.log(source_default.green("\u2713 Code pushed to GitHub\n"));
3678
3844
  gitInfo = await getGitInfo(cwd);
@@ -3735,11 +3901,16 @@ async function deployCommand(options) {
3735
3901
  api,
3736
3902
  "no_access",
3737
3903
  repoFullName,
3738
- { yes: options.yes, installationId: githubInstallationId }
3904
+ {
3905
+ yes: options.yes,
3906
+ installationId: githubInstallationId,
3907
+ reauth: () => promptReauthenticateOn401(options, resolvedOrgId)
3908
+ }
3739
3909
  );
3740
- if (!configured) {
3910
+ if (!configured.ok) {
3741
3911
  process.exit(0);
3742
3912
  }
3913
+ api = configured.api;
3743
3914
  const retry = await checkRepoAccess(api, gitInfo.owner, gitInfo.repo);
3744
3915
  if (!retry) {
3745
3916
  const appName = await api.getGitHubAppName();