mobbdev 1.1.39 → 1.1.41

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.
@@ -12,7 +12,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
12
12
 
13
13
  // src/features/analysis/scm/env.ts
14
14
  import { z as z16 } from "zod";
15
- var EnvVariablesZod, GITLAB_API_TOKEN, GITHUB_API_TOKEN, GIT_PROXY_HOST, MAX_UPLOAD_FILE_SIZE_MB;
15
+ var EnvVariablesZod, GITLAB_API_TOKEN, GITHUB_API_TOKEN, GIT_PROXY_HOST, MAX_UPLOAD_FILE_SIZE_MB, GITHUB_API_CONCURRENCY;
16
16
  var init_env = __esm({
17
17
  "src/features/analysis/scm/env.ts"() {
18
18
  "use strict";
@@ -20,13 +20,15 @@ var init_env = __esm({
20
20
  GITLAB_API_TOKEN: z16.string().optional(),
21
21
  GITHUB_API_TOKEN: z16.string().optional(),
22
22
  GIT_PROXY_HOST: z16.string().optional().default("http://tinyproxy:8888"),
23
- MAX_UPLOAD_FILE_SIZE_MB: z16.coerce.number().gt(0).default(5)
23
+ MAX_UPLOAD_FILE_SIZE_MB: z16.coerce.number().gt(0).default(5),
24
+ GITHUB_API_CONCURRENCY: z16.coerce.number().gt(0).optional().default(10)
24
25
  });
25
26
  ({
26
27
  GITLAB_API_TOKEN,
27
28
  GITHUB_API_TOKEN,
28
29
  GIT_PROXY_HOST,
29
- MAX_UPLOAD_FILE_SIZE_MB
30
+ MAX_UPLOAD_FILE_SIZE_MB,
31
+ GITHUB_API_CONCURRENCY
30
32
  } = EnvVariablesZod.parse(process.env));
31
33
  }
32
34
  });
@@ -1369,15 +1371,141 @@ import { withFile } from "tmp-promise";
1369
1371
  import z26 from "zod";
1370
1372
 
1371
1373
  // src/commands/handleMobbLogin.ts
1372
- import crypto from "crypto";
1373
- import os from "os";
1374
1374
  import chalk2 from "chalk";
1375
1375
  import Debug6 from "debug";
1376
- import open from "open";
1377
1376
 
1378
- // src/constants.ts
1377
+ // src/utils/dirname.ts
1378
+ import fs from "fs";
1379
+ import path from "path";
1380
+ import { fileURLToPath } from "url";
1381
+ function getModuleRootDir() {
1382
+ let manifestDir = getDirName();
1383
+ for (let i = 0; i < 10; i++) {
1384
+ const manifestPath = path.join(manifestDir, "package.json");
1385
+ if (fs.existsSync(manifestPath)) {
1386
+ return manifestDir;
1387
+ }
1388
+ manifestDir = path.join(manifestDir, "..");
1389
+ }
1390
+ throw new Error("Cannot locate package.json file");
1391
+ }
1392
+ function getDirName() {
1393
+ if (typeof __filename !== "undefined") {
1394
+ return path.dirname(__filename);
1395
+ } else {
1396
+ try {
1397
+ const getImportMetaUrl = new Function("return import.meta.url");
1398
+ const importMetaUrl = getImportMetaUrl();
1399
+ return path.dirname(fileURLToPath(importMetaUrl));
1400
+ } catch (e) {
1401
+ try {
1402
+ const err = new Error();
1403
+ const stack = err.stack || "";
1404
+ const match = stack.match(/file:\/\/[^\s)]+/);
1405
+ if (match) {
1406
+ const fileUrl = match[0];
1407
+ return path.dirname(fileURLToPath(fileUrl));
1408
+ }
1409
+ } catch {
1410
+ }
1411
+ throw new Error("Unable to determine directory name in this environment");
1412
+ }
1413
+ }
1414
+ }
1415
+
1416
+ // src/utils/keypress.ts
1417
+ import readline from "readline";
1418
+ async function keypress() {
1419
+ const rl = readline.createInterface({
1420
+ input: process.stdin,
1421
+ output: process.stdout
1422
+ });
1423
+ return new Promise((resolve) => {
1424
+ rl.question("", (answer) => {
1425
+ rl.close();
1426
+ process.stderr.moveCursor(0, -1);
1427
+ process.stderr.clearLine(1);
1428
+ resolve(answer);
1429
+ });
1430
+ });
1431
+ }
1432
+
1433
+ // src/utils/spinner.ts
1434
+ import {
1435
+ createSpinner as _createSpinner
1436
+ } from "nanospinner";
1437
+ function printToStdError(opts) {
1438
+ if (opts?.text) console.error(opts.text);
1439
+ }
1440
+ var mockSpinner = {
1441
+ success: (opts) => {
1442
+ printToStdError(opts);
1443
+ return mockSpinner;
1444
+ },
1445
+ error: (opts) => {
1446
+ printToStdError(opts);
1447
+ return mockSpinner;
1448
+ },
1449
+ warn: (opts) => {
1450
+ printToStdError(opts);
1451
+ return mockSpinner;
1452
+ },
1453
+ stop: (opts) => {
1454
+ printToStdError(opts);
1455
+ return mockSpinner;
1456
+ },
1457
+ start: (opts) => {
1458
+ printToStdError(opts);
1459
+ return mockSpinner;
1460
+ },
1461
+ update: (opts) => {
1462
+ printToStdError(opts);
1463
+ return mockSpinner;
1464
+ },
1465
+ reset: () => mockSpinner,
1466
+ clear: () => mockSpinner,
1467
+ spin: () => mockSpinner
1468
+ };
1469
+ function Spinner({ ci = false } = {}) {
1470
+ return {
1471
+ createSpinner: (text, options) => ci ? mockSpinner : _createSpinner(text, options)
1472
+ };
1473
+ }
1474
+
1475
+ // src/utils/check_node_version.ts
1379
1476
  import fs2 from "fs";
1380
1477
  import path2 from "path";
1478
+ import semver from "semver";
1479
+ function getPackageJson() {
1480
+ return JSON.parse(
1481
+ fs2.readFileSync(path2.join(getModuleRootDir(), "package.json"), "utf8")
1482
+ );
1483
+ }
1484
+ var packageJson = getPackageJson();
1485
+ if (!semver.satisfies(process.version, packageJson.engines.node)) {
1486
+ console.error(
1487
+ `
1488
+ \u26A0\uFE0F ${packageJson.name} requires node version ${packageJson.engines.node}, but running ${process.version}.`
1489
+ );
1490
+ process.exit(1);
1491
+ }
1492
+
1493
+ // src/utils/gitUtils.ts
1494
+ import simpleGit from "simple-git";
1495
+
1496
+ // src/utils/index.ts
1497
+ var sleep = (ms = 2e3) => new Promise((r) => setTimeout(r, ms));
1498
+ var CliError = class extends Error {
1499
+ };
1500
+
1501
+ // src/commands/AuthManager.ts
1502
+ import crypto from "crypto";
1503
+ import os from "os";
1504
+ import open from "open";
1505
+
1506
+ // src/constants.ts
1507
+ import fs3 from "fs";
1508
+ import path3 from "path";
1381
1509
  import chalk from "chalk";
1382
1510
  import Debug from "debug";
1383
1511
  import * as dotenv from "dotenv";
@@ -3479,56 +3607,17 @@ var ScmType = /* @__PURE__ */ ((ScmType2) => {
3479
3607
  return ScmType2;
3480
3608
  })(ScmType || {});
3481
3609
 
3482
- // src/utils/dirname.ts
3483
- import fs from "fs";
3484
- import path from "path";
3485
- import { fileURLToPath } from "url";
3486
- function getModuleRootDir() {
3487
- let manifestDir = getDirName();
3488
- for (let i = 0; i < 10; i++) {
3489
- const manifestPath = path.join(manifestDir, "package.json");
3490
- if (fs.existsSync(manifestPath)) {
3491
- return manifestDir;
3492
- }
3493
- manifestDir = path.join(manifestDir, "..");
3494
- }
3495
- throw new Error("Cannot locate package.json file");
3496
- }
3497
- function getDirName() {
3498
- if (typeof __filename !== "undefined") {
3499
- return path.dirname(__filename);
3500
- } else {
3501
- try {
3502
- const getImportMetaUrl = new Function("return import.meta.url");
3503
- const importMetaUrl = getImportMetaUrl();
3504
- return path.dirname(fileURLToPath(importMetaUrl));
3505
- } catch (e) {
3506
- try {
3507
- const err = new Error();
3508
- const stack = err.stack || "";
3509
- const match = stack.match(/file:\/\/[^\s)]+/);
3510
- if (match) {
3511
- const fileUrl = match[0];
3512
- return path.dirname(fileURLToPath(fileUrl));
3513
- }
3514
- } catch {
3515
- }
3516
- throw new Error("Unable to determine directory name in this environment");
3517
- }
3518
- }
3519
- }
3520
-
3521
3610
  // src/constants.ts
3522
3611
  var debug = Debug("mobbdev:constants");
3523
- var runtimeConfigPath = path2.join(
3612
+ var runtimeConfigPath = path3.join(
3524
3613
  getModuleRootDir(),
3525
3614
  "out",
3526
3615
  "runtime.config.json"
3527
3616
  );
3528
- if (fs2.existsSync(runtimeConfigPath)) {
3617
+ if (fs3.existsSync(runtimeConfigPath)) {
3529
3618
  try {
3530
3619
  const runtimeConfig = JSON.parse(
3531
- fs2.readFileSync(runtimeConfigPath, "utf-8")
3620
+ fs3.readFileSync(runtimeConfigPath, "utf-8")
3532
3621
  );
3533
3622
  Object.assign(process.env, runtimeConfig);
3534
3623
  debug("Loaded runtime config from %s", runtimeConfigPath);
@@ -3536,7 +3625,7 @@ if (fs2.existsSync(runtimeConfigPath)) {
3536
3625
  debug("Failed to load runtime config: %o", e);
3537
3626
  }
3538
3627
  }
3539
- dotenv.config({ path: path2.join(getModuleRootDir(), ".env") });
3628
+ dotenv.config({ path: path3.join(getModuleRootDir(), ".env") });
3540
3629
  var DEFAULT_API_URL = "https://api.mobb.ai/v1/graphql";
3541
3630
  var DEFAULT_WEB_APP_URL = "https://app.mobb.ai";
3542
3631
  var scmFriendlyText = {
@@ -3633,91 +3722,6 @@ var ScanContext = {
3633
3722
  BUGSY: "BUGSY"
3634
3723
  };
3635
3724
 
3636
- // src/utils/keypress.ts
3637
- import readline from "readline";
3638
- async function keypress() {
3639
- const rl = readline.createInterface({
3640
- input: process.stdin,
3641
- output: process.stdout
3642
- });
3643
- return new Promise((resolve) => {
3644
- rl.question("", (answer) => {
3645
- rl.close();
3646
- process.stderr.moveCursor(0, -1);
3647
- process.stderr.clearLine(1);
3648
- resolve(answer);
3649
- });
3650
- });
3651
- }
3652
-
3653
- // src/utils/spinner.ts
3654
- import {
3655
- createSpinner as _createSpinner
3656
- } from "nanospinner";
3657
- function printToStdError(opts) {
3658
- if (opts?.text) console.error(opts.text);
3659
- }
3660
- var mockSpinner = {
3661
- success: (opts) => {
3662
- printToStdError(opts);
3663
- return mockSpinner;
3664
- },
3665
- error: (opts) => {
3666
- printToStdError(opts);
3667
- return mockSpinner;
3668
- },
3669
- warn: (opts) => {
3670
- printToStdError(opts);
3671
- return mockSpinner;
3672
- },
3673
- stop: (opts) => {
3674
- printToStdError(opts);
3675
- return mockSpinner;
3676
- },
3677
- start: (opts) => {
3678
- printToStdError(opts);
3679
- return mockSpinner;
3680
- },
3681
- update: (opts) => {
3682
- printToStdError(opts);
3683
- return mockSpinner;
3684
- },
3685
- reset: () => mockSpinner,
3686
- clear: () => mockSpinner,
3687
- spin: () => mockSpinner
3688
- };
3689
- function Spinner({ ci = false } = {}) {
3690
- return {
3691
- createSpinner: (text, options) => ci ? mockSpinner : _createSpinner(text, options)
3692
- };
3693
- }
3694
-
3695
- // src/utils/check_node_version.ts
3696
- import fs3 from "fs";
3697
- import path3 from "path";
3698
- import semver from "semver";
3699
- function getPackageJson() {
3700
- return JSON.parse(
3701
- fs3.readFileSync(path3.join(getModuleRootDir(), "package.json"), "utf8")
3702
- );
3703
- }
3704
- var packageJson = getPackageJson();
3705
- if (!semver.satisfies(process.version, packageJson.engines.node)) {
3706
- console.error(
3707
- `
3708
- \u26A0\uFE0F ${packageJson.name} requires node version ${packageJson.engines.node}, but running ${process.version}.`
3709
- );
3710
- process.exit(1);
3711
- }
3712
-
3713
- // src/utils/gitUtils.ts
3714
- import simpleGit from "simple-git";
3715
-
3716
- // src/utils/index.ts
3717
- var sleep = (ms = 2e3) => new Promise((r) => setTimeout(r, ms));
3718
- var CliError = class extends Error {
3719
- };
3720
-
3721
3725
  // src/utils/subscribe/subscribe.ts
3722
3726
  import { createClient } from "graphql-ws";
3723
3727
  import WebsocketNode from "isomorphic-ws";
@@ -5915,11 +5919,17 @@ var REPORT_DEFAULT_FILE_NAME = "report.json";
5915
5919
  init_env();
5916
5920
 
5917
5921
  // src/features/analysis/scm/github/GithubSCMLib.ts
5918
- import pLimit from "p-limit";
5922
+ init_env();
5923
+ import pLimit2 from "p-limit";
5919
5924
  import { z as z22 } from "zod";
5920
5925
 
5921
5926
  // src/features/analysis/scm/github/github.ts
5922
5927
  import { RequestError } from "@octokit/request-error";
5928
+ import pLimit from "p-limit";
5929
+
5930
+ // src/utils/contextLogger.ts
5931
+ import debugModule from "debug";
5932
+ var debug4 = debugModule("mobb:shared");
5923
5933
 
5924
5934
  // src/features/analysis/scm/github/utils/encrypt_secret.ts
5925
5935
  import sodium from "libsodium-wrappers";
@@ -5928,9 +5938,6 @@ import sodium from "libsodium-wrappers";
5928
5938
  import { Octokit } from "octokit";
5929
5939
  import { fetch as fetch2, ProxyAgent } from "undici";
5930
5940
 
5931
- // src/features/analysis/scm/github/GithubSCMLib.ts
5932
- var GITHUB_COMMIT_FETCH_CONCURRENCY = parseInt(process.env["GITHUB_COMMIT_CONCURRENCY"] || "10", 10) || 10;
5933
-
5934
5941
  // src/features/analysis/scm/gitlab/gitlab.ts
5935
5942
  import querystring3 from "querystring";
5936
5943
  import {
@@ -5946,12 +5953,6 @@ import {
5946
5953
  fetch as undiciFetch,
5947
5954
  ProxyAgent as ProxyAgent2
5948
5955
  } from "undici";
5949
-
5950
- // src/utils/contextLogger.ts
5951
- import debugModule from "debug";
5952
- var debug4 = debugModule("mobb:shared");
5953
-
5954
- // src/features/analysis/scm/gitlab/gitlab.ts
5955
5956
  init_env();
5956
5957
 
5957
5958
  // src/features/analysis/scm/gitlab/types.ts
@@ -6494,10 +6495,174 @@ function getConfigStore() {
6494
6495
  }
6495
6496
  var configStore = getConfigStore();
6496
6497
 
6497
- // src/commands/handleMobbLogin.ts
6498
- var debug7 = Debug6("mobbdev:commands");
6498
+ // src/commands/AuthManager.ts
6499
6499
  var LOGIN_MAX_WAIT = 10 * 60 * 1e3;
6500
6500
  var LOGIN_CHECK_DELAY = 5 * 1e3;
6501
+ var AuthManager = class {
6502
+ constructor(webAppUrl, apiUrl) {
6503
+ __publicField(this, "publicKey");
6504
+ __publicField(this, "privateKey");
6505
+ __publicField(this, "loginId");
6506
+ __publicField(this, "gqlClient");
6507
+ __publicField(this, "currentBrowserUrl");
6508
+ __publicField(this, "authenticated", null);
6509
+ __publicField(this, "resolvedWebAppUrl");
6510
+ __publicField(this, "resolvedApiUrl");
6511
+ this.resolvedWebAppUrl = webAppUrl || WEB_APP_URL;
6512
+ this.resolvedApiUrl = apiUrl || API_URL;
6513
+ }
6514
+ openUrlInBrowser() {
6515
+ if (this.currentBrowserUrl) {
6516
+ open(this.currentBrowserUrl);
6517
+ return true;
6518
+ }
6519
+ return false;
6520
+ }
6521
+ async waitForAuthentication() {
6522
+ let newApiToken = null;
6523
+ for (let i = 0; i < LOGIN_MAX_WAIT / LOGIN_CHECK_DELAY; i++) {
6524
+ newApiToken = await this.getApiToken();
6525
+ if (newApiToken) {
6526
+ break;
6527
+ }
6528
+ await sleep(LOGIN_CHECK_DELAY);
6529
+ }
6530
+ if (!newApiToken) {
6531
+ return false;
6532
+ }
6533
+ this.gqlClient = new GQLClient({
6534
+ apiKey: newApiToken,
6535
+ type: "apiKey",
6536
+ apiUrl: this.resolvedApiUrl
6537
+ });
6538
+ const loginSuccess = await this.gqlClient.validateUserToken();
6539
+ if (loginSuccess) {
6540
+ configStore.set("apiToken", newApiToken);
6541
+ this.authenticated = true;
6542
+ return true;
6543
+ }
6544
+ return false;
6545
+ }
6546
+ /**
6547
+ * Checks if the user is already authenticated
6548
+ */
6549
+ async isAuthenticated() {
6550
+ if (this.authenticated === null) {
6551
+ const result = await this.checkAuthentication();
6552
+ this.authenticated = result.isAuthenticated;
6553
+ }
6554
+ return this.authenticated;
6555
+ }
6556
+ /**
6557
+ * Private function to check if the user is authenticated with the server
6558
+ */
6559
+ async checkAuthentication(apiKey) {
6560
+ try {
6561
+ if (!this.gqlClient) {
6562
+ this.gqlClient = this.getGQLClient(apiKey);
6563
+ }
6564
+ const isConnected = await this.gqlClient.verifyApiConnection();
6565
+ if (!isConnected) {
6566
+ return {
6567
+ isAuthenticated: false,
6568
+ message: "Failed to connect to Mobb server"
6569
+ };
6570
+ }
6571
+ const userVerify = await this.gqlClient.validateUserToken();
6572
+ if (!userVerify) {
6573
+ return {
6574
+ isAuthenticated: false,
6575
+ message: "User token validation failed"
6576
+ };
6577
+ }
6578
+ } catch (error) {
6579
+ return {
6580
+ isAuthenticated: false,
6581
+ message: error instanceof Error ? error.message : "Unknown authentication error"
6582
+ };
6583
+ }
6584
+ return { isAuthenticated: true, message: "Successfully authenticated" };
6585
+ }
6586
+ /**
6587
+ * Generates a login URL for manual authentication
6588
+ */
6589
+ async generateLoginUrl(loginContext) {
6590
+ try {
6591
+ if (!this.gqlClient) {
6592
+ this.gqlClient = this.getGQLClient();
6593
+ }
6594
+ const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
6595
+ modulusLength: 2048
6596
+ });
6597
+ this.publicKey = publicKey;
6598
+ this.privateKey = privateKey;
6599
+ this.loginId = await this.gqlClient.createCliLogin({
6600
+ publicKey: this.publicKey.export({ format: "pem", type: "pkcs1" }).toString()
6601
+ });
6602
+ const webLoginUrl = `${this.resolvedWebAppUrl}/cli-login`;
6603
+ const browserUrl = loginContext ? buildLoginUrl(webLoginUrl, this.loginId, os.hostname(), loginContext) : `${webLoginUrl}/${this.loginId}?hostname=${os.hostname()}`;
6604
+ this.currentBrowserUrl = browserUrl;
6605
+ return browserUrl;
6606
+ } catch (error) {
6607
+ console.error("Failed to generate login URL:", error);
6608
+ return null;
6609
+ }
6610
+ }
6611
+ /**
6612
+ * Retrieves and decrypts the API token after authentication
6613
+ */
6614
+ async getApiToken() {
6615
+ if (!this.gqlClient || !this.loginId || !this.privateKey) {
6616
+ return null;
6617
+ }
6618
+ const encryptedApiToken = await this.gqlClient.getEncryptedApiToken({
6619
+ loginId: this.loginId
6620
+ });
6621
+ if (encryptedApiToken) {
6622
+ return crypto.privateDecrypt(
6623
+ this.privateKey,
6624
+ Buffer.from(encryptedApiToken, "base64")
6625
+ ).toString("utf-8");
6626
+ }
6627
+ return null;
6628
+ }
6629
+ /**
6630
+ * Gets the current GQL client (if authenticated)
6631
+ */
6632
+ getGQLClient(inputApiKey) {
6633
+ if (this.gqlClient === void 0) {
6634
+ this.gqlClient = new GQLClient({
6635
+ apiKey: inputApiKey || configStore.get("apiToken") || "",
6636
+ type: "apiKey",
6637
+ apiUrl: this.resolvedApiUrl
6638
+ });
6639
+ }
6640
+ return this.gqlClient;
6641
+ }
6642
+ /**
6643
+ * Assigns a GQL client instance to the AuthManager, and resets auth state
6644
+ * @param gqlClient The GQL client instance to set
6645
+ */
6646
+ setGQLClient(gqlClient) {
6647
+ this.gqlClient = gqlClient;
6648
+ this.cleanup();
6649
+ }
6650
+ /**
6651
+ * Cleans up any active login session
6652
+ */
6653
+ cleanup() {
6654
+ this.publicKey = void 0;
6655
+ this.privateKey = void 0;
6656
+ this.loginId = void 0;
6657
+ this.authenticated = null;
6658
+ this.currentBrowserUrl = null;
6659
+ }
6660
+ };
6661
+
6662
+ // src/commands/handleMobbLogin.ts
6663
+ var debug7 = Debug6("mobbdev:commands");
6664
+ var LOGIN_MAX_WAIT2 = 10 * 60 * 1e3;
6665
+ var LOGIN_CHECK_DELAY2 = 5 * 1e3;
6501
6666
  var MOBB_LOGIN_REQUIRED_MSG = `\u{1F513} Login to Mobb is Required, you will be redirected to our login page, once the authorization is complete return to this prompt, ${chalk2.bgBlue(
6502
6667
  "press any key to continue"
6503
6668
  )};`;
@@ -6512,11 +6677,8 @@ async function getAuthenticatedGQLClient({
6512
6677
  apiUrl || "undefined",
6513
6678
  webAppUrl || "undefined"
6514
6679
  );
6515
- let gqlClient = new GQLClient({
6516
- apiKey: inputApiKey || configStore.get("apiToken") || "",
6517
- type: "apiKey",
6518
- apiUrl
6519
- });
6680
+ const authManager = new AuthManager(webAppUrl, apiUrl);
6681
+ let gqlClient = authManager.getGQLClient(inputApiKey);
6520
6682
  gqlClient = await handleMobbLogin({
6521
6683
  inGqlClient: gqlClient,
6522
6684
  skipPrompts: isSkipPrompts,
@@ -6533,35 +6695,28 @@ async function handleMobbLogin({
6533
6695
  webAppUrl,
6534
6696
  loginContext
6535
6697
  }) {
6536
- const resolvedWebAppUrl = webAppUrl || WEB_APP_URL;
6537
- const resolvedApiUrl = apiUrl || API_URL;
6538
6698
  debug7(
6539
6699
  "handleMobbLogin: resolved URLs - apiUrl=%s (from param: %s), webAppUrl=%s (from param: %s)",
6540
- resolvedApiUrl,
6541
6700
  apiUrl || "fallback",
6542
- resolvedWebAppUrl,
6701
+ apiUrl || "fallback",
6702
+ webAppUrl || "fallback",
6543
6703
  webAppUrl || "fallback"
6544
6704
  );
6545
6705
  const { createSpinner } = Spinner({ ci: skipPrompts });
6546
- const isConnected = await inGqlClient.verifyApiConnection();
6547
- if (!isConnected) {
6548
- createSpinner().start().error({
6549
- text: "\u{1F513} Connection to Mobb: failed to connect to the Mobb server"
6550
- });
6551
- throw new CliError(
6552
- "Connection to Mobb: failed to connect to the Mobb server"
6553
- );
6706
+ const authManager = new AuthManager(webAppUrl, apiUrl);
6707
+ authManager.setGQLClient(inGqlClient);
6708
+ try {
6709
+ const isAuthenticated = await authManager.isAuthenticated();
6710
+ if (isAuthenticated) {
6711
+ createSpinner().start().success({
6712
+ text: `\u{1F513} Login to Mobb succeeded. Already authenticated`
6713
+ });
6714
+ return authManager.getGQLClient();
6715
+ }
6716
+ } catch (error) {
6717
+ debug7("Authentication check failed:", error);
6554
6718
  }
6555
- createSpinner().start().success({
6556
- text: `\u{1F513} Connection to Mobb: succeeded`
6557
- });
6558
- const userVerify = await inGqlClient.validateUserToken();
6559
- if (userVerify) {
6560
- createSpinner().start().success({
6561
- text: `\u{1F513} Login to Mobb succeeded. ${typeof userVerify === "string" ? `Logged in as ${userVerify}` : ""}`
6562
- });
6563
- return inGqlClient;
6564
- } else if (apiKey) {
6719
+ if (apiKey) {
6565
6720
  createSpinner().start().error({
6566
6721
  text: "\u{1F513} Login to Mobb failed: The provided API key does not match any configured API key on the system"
6567
6722
  });
@@ -6577,57 +6732,32 @@ async function handleMobbLogin({
6577
6732
  loginSpinner.update({
6578
6733
  text: "\u{1F513} Waiting for Mobb login..."
6579
6734
  });
6580
- const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
6581
- modulusLength: 2048
6582
- });
6583
- const loginId = await inGqlClient.createCliLogin({
6584
- publicKey: publicKey.export({ format: "pem", type: "pkcs1" }).toString()
6585
- });
6586
- const webLoginUrl = `${resolvedWebAppUrl}/cli-login`;
6587
- const browserUrl = loginContext ? buildLoginUrl(webLoginUrl, loginId, os.hostname(), loginContext) : `${webLoginUrl}/${loginId}?hostname=${os.hostname()}`;
6588
- !skipPrompts && console.log(
6589
- `If the page does not open automatically, kindly access it through ${browserUrl}.`
6590
- );
6591
- await open(browserUrl);
6592
- let newApiToken = null;
6593
- for (let i = 0; i < LOGIN_MAX_WAIT / LOGIN_CHECK_DELAY; i++) {
6594
- const encryptedApiToken = await inGqlClient.getEncryptedApiToken({
6595
- loginId
6596
- });
6597
- loginSpinner.spin();
6598
- if (encryptedApiToken) {
6599
- debug7("encrypted API token received %s", encryptedApiToken);
6600
- newApiToken = crypto.privateDecrypt(privateKey, Buffer.from(encryptedApiToken, "base64")).toString("utf-8");
6601
- debug7("API token decrypted");
6602
- break;
6735
+ try {
6736
+ const loginUrl = await authManager.generateLoginUrl(loginContext);
6737
+ if (!loginUrl) {
6738
+ loginSpinner.error({
6739
+ text: "Failed to generate login URL"
6740
+ });
6741
+ throw new CliError("Failed to generate login URL");
6742
+ }
6743
+ !skipPrompts && console.log(
6744
+ `If the page does not open automatically, kindly access it through ${loginUrl}.`
6745
+ );
6746
+ authManager.openUrlInBrowser();
6747
+ const authSuccess = await authManager.waitForAuthentication();
6748
+ if (!authSuccess) {
6749
+ loginSpinner.error({
6750
+ text: "Login timeout error"
6751
+ });
6752
+ throw new CliError("Login timeout error");
6603
6753
  }
6604
- await sleep(LOGIN_CHECK_DELAY);
6605
- }
6606
- if (!newApiToken) {
6607
- loginSpinner.error({
6608
- text: "Login timeout error"
6609
- });
6610
- throw new CliError();
6611
- }
6612
- const newGqlClient = new GQLClient({
6613
- apiKey: newApiToken,
6614
- type: "apiKey",
6615
- apiUrl: resolvedApiUrl
6616
- });
6617
- const loginSuccess = await newGqlClient.validateUserToken();
6618
- if (loginSuccess) {
6619
- debug7(`set api token ${newApiToken}`);
6620
- configStore.set("apiToken", newApiToken);
6621
6754
  loginSpinner.success({
6622
- text: `\u{1F513} Login to Mobb successful! ${typeof loginSpinner === "string" ? `Logged in as ${loginSuccess}` : ""}`
6623
- });
6624
- } else {
6625
- loginSpinner.error({
6626
- text: "Something went wrong, API token is invalid."
6755
+ text: `\u{1F513} Login to Mobb successful!`
6627
6756
  });
6628
- throw new CliError();
6757
+ return authManager.getGQLClient();
6758
+ } finally {
6759
+ authManager.cleanup();
6629
6760
  }
6630
- return newGqlClient;
6631
6761
  }
6632
6762
 
6633
6763
  // src/args/commands/upload_ai_blame.ts