phantomx-tool-client 1.1.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -64,19 +64,6 @@ function executeShell(command) {
64
64
  });
65
65
  });
66
66
  }
67
- // ---------------------------------------------------------------------------
68
- // Per-repo async mutex — serialises all git remote set-url operations so that
69
- // concurrent tool calls never race on .git/config.lock for the same repo.
70
- // ---------------------------------------------------------------------------
71
- const repoLocks = new Map();
72
- function withRepoLock(repoName, fn) {
73
- var _a;
74
- const prev = (_a = repoLocks.get(repoName)) !== null && _a !== void 0 ? _a : Promise.resolve();
75
- let releaseLock;
76
- const lockAcquired = new Promise(res => { releaseLock = res; });
77
- repoLocks.set(repoName, prev.then(() => lockAcquired));
78
- return prev.then(() => fn()).finally(() => releaseLock());
79
- }
80
67
  // Use the official 'octokit' package import via require to avoid missing types for '@octokit/rest'
81
68
  const { Octokit } = require('octokit');
82
69
  const logger = (0, Logger_1.createLogger)('GithubOperationsService');
@@ -610,26 +597,25 @@ class GithubOperationsService {
610
597
  const repoPath = `${tool_server_1.folderPath}/${repoName}`;
611
598
  const installationToken = await fetchInstallationToken();
612
599
  const gitOrgName = await getGithubOrganizationName();
613
- return withRepoLock(repoName, async () => {
614
- try {
615
- logger.info('Getting repository origin/default branch', { repoName });
616
- // Set token URL, query origin, restore URL single pipeline to prevent lock races.
617
- const result = await executeShell(`cd ${repoPath} && rm -f .git/config.lock && ` +
618
- `git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git && ` +
619
- `sudo git remote show origin | grep 'HEAD branch' | awk '{print $NF}' && ` +
620
- `git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git`);
621
- if (!result.success || !result.output.trim()) {
622
- throw new Error(`Failed to get origin branch for repo '${repoName}': ${result.error || result.output}`);
623
- }
624
- const originBranch = result.output.trim();
625
- logger.success('Repository origin branch retrieved', { repoName, originBranch });
626
- return {
627
- repoName,
628
- originBranch
629
- };
630
- }
631
- finally { }
632
- }); // end withRepoLock
600
+ try {
601
+ logger.info('Getting repository origin/default branch', { repoName });
602
+ // Set authenticated remote URL
603
+ await executeShell(`cd ${repoPath} && sudo git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git`);
604
+ // Fetch remote HEAD to find the default branch
605
+ const result = await executeShell(`cd ${repoPath} && sudo git remote show origin | grep 'HEAD branch' | awk '{print $NF}'`);
606
+ // Restore unauthenticated remote URL
607
+ await executeShell(`cd ${repoPath} && sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git`);
608
+ if (!result.success || !result.output.trim()) {
609
+ throw new Error(`Failed to get origin branch for repo '${repoName}': ${result.error || result.output}`);
610
+ }
611
+ const originBranch = result.output.trim();
612
+ logger.success('Repository origin branch retrieved', { repoName, originBranch });
613
+ return {
614
+ repoName,
615
+ originBranch
616
+ };
617
+ }
618
+ finally { }
633
619
  }
634
620
  /**
635
621
  * List branches for a repository
@@ -650,38 +636,40 @@ class GithubOperationsService {
650
636
  const repoPath = `${tool_server_1.folderPath}/${repoName}`;
651
637
  const installationToken = await fetchInstallationToken();
652
638
  const gitOrgName = await getGithubOrganizationName();
653
- return withRepoLock(repoName, async () => {
654
- try {
655
- // Get current local branch name
656
- let result = await executeShell(`cd ${repoPath} && sudo git rev-parse --abbrev-ref HEAD`);
657
- if (!result.success) {
658
- throw new Error(`Failed to get current branch: ${result.error}`);
659
- }
660
- const currentBranch = result.output.trim();
661
- logger.info('starting git pull operation', { repoName, currentBranch });
662
- // Token-URL, fetch, pull, restore URL — all in one pipeline to prevent
663
- // concurrent operations from racing on .git/config.lock.
664
- result = await executeShell(`cd ${repoPath} && rm -f .git/config.lock && ` +
665
- `git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git && ` +
666
- `sudo git fetch origin && ` +
667
- `sudo git pull --no-rebase origin ${currentBranch} ; ` +
668
- `git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git`);
669
- if (!result.success && result.code !== 128) {
670
- throw new Error(`Failed to pull branch ${currentBranch}: ${result.error}`);
671
- }
672
- logger.success('Git pull completed successfully', { repoName, currentBranch });
673
- // Get merge conflicts
674
- const mergeConflicts = await this.getMergeConflicts(repoName);
675
- return {
676
- repoName,
677
- branchName: currentBranch,
678
- lastCommit: result.output.trim(),
679
- mergeConflicts
680
- };
639
+ try {
640
+ // Get current local branch name
641
+ let result = await executeShell(`cd ${repoPath} && sudo git rev-parse --abbrev-ref HEAD`);
642
+ if (!result.success) {
643
+ throw new Error(`Failed to get current branch: ${result.error}`);
681
644
  }
682
- finally {
645
+ const currentBranch = result.output.trim();
646
+ logger.info('starting git pull operation', { repoName, currentBranch });
647
+ // Set remote url with token
648
+ result = await executeShell(`cd ${repoPath} && sudo git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git`);
649
+ if (!result.success) {
650
+ throw new Error(`Failed to set remote URL: ${result.error}`);
683
651
  }
684
- }); // end withRepoLock
652
+ result = await executeShell(`cd ${repoPath} && sudo git fetch origin`);
653
+ if (!result.success) {
654
+ throw new Error(`Failed to fetch from remote: ${result.error}`);
655
+ }
656
+ result = await executeShell(`cd ${repoPath} && sudo git pull --no-rebase origin ${currentBranch}`);
657
+ if (!result.success && result.code !== 128) {
658
+ throw new Error(`Failed to pull branch ${currentBranch}: ${result.error}`);
659
+ }
660
+ await executeShell(`cd ${repoPath} && sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git`);
661
+ logger.success('Git pull completed successfully', { repoName, currentBranch });
662
+ // Get merge conflicts
663
+ const mergeConflicts = await this.getMergeConflicts(socket, repoName);
664
+ return {
665
+ repoName,
666
+ branchName: currentBranch,
667
+ lastCommit: result.output.trim(),
668
+ mergeConflicts
669
+ };
670
+ }
671
+ finally {
672
+ }
685
673
  }
686
674
  /**
687
675
  * Push repository to remote
@@ -690,58 +678,55 @@ class GithubOperationsService {
690
678
  const repoPath = `${tool_server_1.folderPath}/${repoName}`;
691
679
  const installationToken = await fetchInstallationToken();
692
680
  const gitOrgName = await getGithubOrganizationName();
693
- return withRepoLock(repoName, async () => {
694
- try {
695
- logger.info('Starting git push operation', { repoName });
696
- // Combine all commands into a single SSH call
697
- const combinedCommand = `
681
+ try {
682
+ logger.info('Starting git push operation', { repoName });
683
+ // Combine all commands into a single SSH call
684
+ const combinedCommand = `
698
685
  cd ${repoPath} &&
699
- rm -f .git/config.lock &&
700
686
  BRANCH=$(sudo git rev-parse --abbrev-ref HEAD) &&
701
687
  COMMITS_AHEAD=$(sudo git rev-list --count origin/$BRANCH..HEAD 2>/dev/null || echo 0) &&
702
688
  if [ "$COMMITS_AHEAD" = "0" ]; then
703
689
  echo "ERROR:NO_COMMITS";
704
690
  exit 1;
705
691
  fi &&
706
- git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git &&
692
+ sudo git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git &&
707
693
  sudo git push origin $BRANCH &&
708
- git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git &&
694
+ sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git &&
709
695
  COMMIT_HASH=$(sudo git rev-parse HEAD) &&
710
696
  echo "BRANCH:$BRANCH" &&
711
697
  echo "COMMITS_AHEAD:$COMMITS_AHEAD" &&
712
698
  echo "COMMIT_HASH:$COMMIT_HASH"
713
699
  `.replace(/\n\s+/g, ' ');
714
- const result = await executeShell(combinedCommand);
715
- if (!result.success) {
716
- if (result.output && result.output.includes('ERROR:NO_COMMITS')) {
717
- throw new Error('No commits to push');
718
- }
719
- throw new Error('Failed to push repository');
720
- }
721
- // Parse output
722
- const output = result.output;
723
- const branchMatch = output.match(/BRANCH:([^\n]+)/);
724
- const commitsAheadMatch = output.match(/COMMITS_AHEAD:(\d+)/);
725
- const commitHashMatch = output.match(/COMMIT_HASH:([a-f0-9]+)/);
726
- const currentBranch = branchMatch ? branchMatch[1].trim() : 'unknown';
727
- const commitsAhead = commitsAheadMatch ? parseInt(commitsAheadMatch[1]) : 0;
728
- const commitHash = commitHashMatch ? commitHashMatch[1].trim() : 'unknown';
729
- logger.success('Git push completed successfully', { repoName, currentBranch, commitHash, commitsAhead });
730
- // Emit socket event to refresh repo on client (only when called by AI agent)
731
- if (socket) {
732
- socket.emit('refresh_repo', { repoName });
700
+ const result = await executeShell(combinedCommand);
701
+ if (!result.success) {
702
+ if (result.output && result.output.includes('ERROR:NO_COMMITS')) {
703
+ throw new Error('No commits to push');
733
704
  }
734
- return {
735
- repoName,
736
- branchName: currentBranch,
737
- commitHash,
738
- commitsAhead,
739
- pushedAt: new Date().toISOString()
740
- };
705
+ throw new Error('Failed to push repository');
741
706
  }
742
- finally {
707
+ // Parse output
708
+ const output = result.output;
709
+ const branchMatch = output.match(/BRANCH:([^\n]+)/);
710
+ const commitsAheadMatch = output.match(/COMMITS_AHEAD:(\d+)/);
711
+ const commitHashMatch = output.match(/COMMIT_HASH:([a-f0-9]+)/);
712
+ const currentBranch = branchMatch ? branchMatch[1].trim() : 'unknown';
713
+ const commitsAhead = commitsAheadMatch ? parseInt(commitsAheadMatch[1]) : 0;
714
+ const commitHash = commitHashMatch ? commitHashMatch[1].trim() : 'unknown';
715
+ logger.success('Git push completed successfully', { repoName, currentBranch, commitHash, commitsAhead });
716
+ // Emit socket event to refresh repo on client (only when called by AI agent)
717
+ if (socket) {
718
+ socket.emit('refresh_repo', { repoName });
743
719
  }
744
- }); // end withRepoLock
720
+ return {
721
+ repoName,
722
+ branchName: currentBranch,
723
+ commitHash,
724
+ commitsAhead,
725
+ pushedAt: new Date().toISOString()
726
+ };
727
+ }
728
+ finally {
729
+ }
745
730
  }
746
731
  /**
747
732
  * Check repository status
@@ -750,24 +735,15 @@ class GithubOperationsService {
750
735
  const repoPath = `${tool_server_1.folderPath}/${repoName}`;
751
736
  const installationToken = await fetchInstallationToken();
752
737
  const gitOrgName = await getGithubOrganizationName();
753
- // const repoPath = 'mnt/f/PhantomX-workspace/93943752-2576-44ee-9b57-4b6fb3130fe4/AI_CODER_REMOTE'
754
- // const installationToken = "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";
755
- // const gitOrgName = "AI-PLAYGROUNDS";
756
- return withRepoLock(repoName, async () => {
757
- try {
758
- logger.info('Checking repository status', { repoName });
759
- // Combine all git commands into a single SSH call
760
- // Remove any stale .git/config.lock before touching remote config
761
- // (a crashed/interrupted previous run can leave this lock behind).
762
- // Also avoid sudo for git remote set-url so the lock file is never
763
- // created as root-owned, which would block subsequent cleanup.
764
- const combinedCommand = `
738
+ try {
739
+ logger.info('Checking repository status', { repoName });
740
+ // Combine all git commands into a single SSH call
741
+ const combinedCommand = `
765
742
  cd ${repoPath} &&
766
- rm -f .git/config.lock &&
767
743
  BRANCH=$(sudo git rev-parse --abbrev-ref HEAD) &&
768
- git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git &&
744
+ sudo git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git &&
769
745
  sudo git fetch origin &&
770
- git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git &&
746
+ sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git &&
771
747
  BEHIND=$(sudo git rev-list --count HEAD..origin/$BRANCH 2>/dev/null || echo 0) &&
772
748
  AHEAD=$(sudo git rev-list --count origin/$BRANCH..HEAD 2>/dev/null || echo 0) &&
773
749
  STATUS=$(sudo git status --porcelain) &&
@@ -780,55 +756,54 @@ class GithubOperationsService {
780
756
  echo "CURRENT:$CURRENT_HASH" &&
781
757
  echo "REMOTE:$REMOTE_HASH"
782
758
  `.replace(/\n\s+/g, ' ');
783
- const result = await executeShell(combinedCommand);
784
- if (!result.success) {
785
- throw new Error('Failed to check repository status: ' + result.error);
786
- }
787
- // Parse the output
788
- const output = result.output;
789
- const branchMatch = output.match(/BRANCH:([^\n]+)/);
790
- const behindMatch = output.match(/BEHIND:(\d+)/);
791
- const aheadMatch = output.match(/AHEAD:(\d+)/);
792
- const statusMatch = output.match(/STATUS:([\s\S]*?)(?=CURRENT:|$)/);
793
- const currentMatch = output.match(/CURRENT:([a-f0-9]+)/);
794
- const remoteMatch = output.match(/REMOTE:([a-f0-9]+|unknown)/);
795
- const currentBranch = branchMatch ? branchMatch[1].trim() : 'unknown';
796
- const commitsBehind = behindMatch ? parseInt(behindMatch[1]) : 0;
797
- const commitsAhead = aheadMatch ? parseInt(aheadMatch[1]) : 0;
798
- const statusOutput = statusMatch ? statusMatch[1].trim() : '';
799
- const hasUncommittedChanges = statusOutput.length > 0;
800
- const currentCommitHash = currentMatch ? currentMatch[1].trim() : 'unknown';
801
- const remoteCommitHash = remoteMatch ? remoteMatch[1].trim() : 'unknown';
802
- const isUpToDate = commitsBehind === 0 && commitsAhead === 0;
803
- const needsPull = commitsBehind > 0;
804
- const needsPush = commitsAhead > 0;
805
- logger.info('Repository status checked', {
806
- repoName,
807
- commitsBehind,
808
- commitsAhead,
809
- hasUncommittedChanges,
810
- isUpToDate
811
- });
812
- // Check for merge conflicts
813
- const mergeConflicts = await this.getMergeConflicts(repoName);
814
- return {
815
- repoName,
816
- branchName: currentBranch,
817
- currentCommitHash,
818
- remoteCommitHash,
819
- commitsBehind,
820
- commitsAhead,
821
- hasUncommittedChanges,
822
- isUpToDate,
823
- needsPull,
824
- needsPush,
825
- mergeConflicts,
826
- status: isUpToDate ? 'up-to-date' : (needsPull && needsPush ? 'diverged' : needsPull ? 'behind' : 'ahead')
827
- };
828
- }
829
- finally {
759
+ const result = await executeShell(combinedCommand);
760
+ if (!result.success) {
761
+ throw new Error('Failed to check repository status');
830
762
  }
831
- }); // end withRepoLock
763
+ // Parse the output
764
+ const output = result.output;
765
+ const branchMatch = output.match(/BRANCH:([^\n]+)/);
766
+ const behindMatch = output.match(/BEHIND:(\d+)/);
767
+ const aheadMatch = output.match(/AHEAD:(\d+)/);
768
+ const statusMatch = output.match(/STATUS:([\s\S]*?)(?=CURRENT:|$)/);
769
+ const currentMatch = output.match(/CURRENT:([a-f0-9]+)/);
770
+ const remoteMatch = output.match(/REMOTE:([a-f0-9]+|unknown)/);
771
+ const currentBranch = branchMatch ? branchMatch[1].trim() : 'unknown';
772
+ const commitsBehind = behindMatch ? parseInt(behindMatch[1]) : 0;
773
+ const commitsAhead = aheadMatch ? parseInt(aheadMatch[1]) : 0;
774
+ const statusOutput = statusMatch ? statusMatch[1].trim() : '';
775
+ const hasUncommittedChanges = statusOutput.length > 0;
776
+ const currentCommitHash = currentMatch ? currentMatch[1].trim() : 'unknown';
777
+ const remoteCommitHash = remoteMatch ? remoteMatch[1].trim() : 'unknown';
778
+ const isUpToDate = commitsBehind === 0 && commitsAhead === 0;
779
+ const needsPull = commitsBehind > 0;
780
+ const needsPush = commitsAhead > 0;
781
+ logger.info('Repository status checked', {
782
+ repoName,
783
+ commitsBehind,
784
+ commitsAhead,
785
+ hasUncommittedChanges,
786
+ isUpToDate
787
+ });
788
+ // Check for merge conflicts
789
+ const mergeConflicts = await this.getMergeConflicts(socket, repoName);
790
+ return {
791
+ repoName,
792
+ branchName: currentBranch,
793
+ currentCommitHash,
794
+ remoteCommitHash,
795
+ commitsBehind,
796
+ commitsAhead,
797
+ hasUncommittedChanges,
798
+ isUpToDate,
799
+ needsPull,
800
+ needsPush,
801
+ mergeConflicts,
802
+ status: isUpToDate ? 'up-to-date' : (needsPull && needsPush ? 'diverged' : needsPull ? 'behind' : 'ahead')
803
+ };
804
+ }
805
+ finally {
806
+ }
832
807
  }
833
808
  /**
834
809
  * Get repository history
@@ -1074,186 +1049,183 @@ class GithubOperationsService {
1074
1049
  const repoPath = `${tool_server_1.folderPath}/${repoName}`;
1075
1050
  const installationToken = await fetchInstallationToken();
1076
1051
  const gitOrgName = await getGithubOrganizationName();
1077
- return withRepoLock(repoName, async () => {
1078
- try {
1079
- logger.info('Creating pull request', { repoName, targetBranch });
1080
- // Combine commands into a single SSH call, with remote URL set/unset
1081
- const combinedCommand = `
1052
+ try {
1053
+ logger.info('Creating pull request', { repoName, targetBranch });
1054
+ // Combine commands into a single SSH call, with remote URL set/unset
1055
+ const combinedCommand = `
1082
1056
  cd ${repoPath} &&
1083
- rm -f .git/config.lock &&
1084
- git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git &&
1057
+ sudo git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git &&
1085
1058
  HEAD_BRANCH=$(sudo git rev-parse --abbrev-ref HEAD) &&
1086
1059
  sudo git fetch origin ${targetBranch} 2>/dev/null &&
1087
1060
  COMMITS_AHEAD=$(sudo git rev-list --count origin/${targetBranch}..$HEAD_BRANCH 2>/dev/null || echo 0) &&
1088
1061
  if [ "$COMMITS_AHEAD" = "0" ]; then
1089
1062
  echo "ERROR:NO_COMMITS:$HEAD_BRANCH";
1090
- git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git;
1063
+ sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git;
1091
1064
  exit 1;
1092
1065
  fi &&
1093
1066
  echo "HEAD_BRANCH:$HEAD_BRANCH" &&
1094
1067
  echo "COMMITS_AHEAD:$COMMITS_AHEAD" &&
1095
- git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git
1068
+ sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git
1096
1069
  `.replace(/\n\s+/g, ' ');
1097
- let result = await executeShell(combinedCommand);
1098
- const output = result.output || '';
1099
- const headBranchMatch = output.match(/HEAD_BRANCH:([^\n]+)/);
1100
- const commitsAheadMatch = output.match(/COMMITS_AHEAD:(\d+)/);
1101
- const errorMatch = output.match(/ERROR:NO_COMMITS:([^\n]+)/);
1102
- let headBranch = headBranchMatch ? headBranchMatch[1].trim() : 'unknown';
1103
- const commitsAhead = commitsAheadMatch ? parseInt(commitsAheadMatch[1]) : 0;
1104
- if (!result.success && errorMatch) {
1105
- headBranch = errorMatch[1].trim();
1106
- }
1107
- // using 'octokit' package (already installed, @octokit/rest is not installed)
1108
- const { Octokit } = require('octokit');
1109
- const octokit = new Octokit({
1110
- auth: installationToken
1070
+ let result = await executeShell(combinedCommand);
1071
+ const output = result.output || '';
1072
+ const headBranchMatch = output.match(/HEAD_BRANCH:([^\n]+)/);
1073
+ const commitsAheadMatch = output.match(/COMMITS_AHEAD:(\d+)/);
1074
+ const errorMatch = output.match(/ERROR:NO_COMMITS:([^\n]+)/);
1075
+ let headBranch = headBranchMatch ? headBranchMatch[1].trim() : 'unknown';
1076
+ const commitsAhead = commitsAheadMatch ? parseInt(commitsAheadMatch[1]) : 0;
1077
+ if (!result.success && errorMatch) {
1078
+ headBranch = errorMatch[1].trim();
1079
+ }
1080
+ // using 'octokit' package (already installed, @octokit/rest is not installed)
1081
+ const { Octokit } = require('octokit');
1082
+ const octokit = new Octokit({
1083
+ auth: installationToken
1084
+ });
1085
+ // Always check for existing PR first even if there are no new commits
1086
+ try {
1087
+ const existingPRs = await octokit.rest.pulls.list({
1088
+ owner: gitOrgName,
1089
+ repo: repoName,
1090
+ head: `${gitOrgName}:${headBranch}`,
1091
+ base: targetBranch,
1092
+ state: 'open'
1111
1093
  });
1112
- // Always check for existing PR first even if there are no new commits
1113
- try {
1114
- const existingPRs = await octokit.rest.pulls.list({
1115
- owner: gitOrgName,
1116
- repo: repoName,
1117
- head: `${gitOrgName}:${headBranch}`,
1118
- base: targetBranch,
1119
- state: 'open'
1120
- });
1121
- if (existingPRs.data.length > 0) {
1122
- const existingPR = existingPRs.data[0];
1123
- logger.info('Pull request already exists', {
1124
- repoName,
1125
- prNumber: existingPR.number,
1126
- prUrl: existingPR.html_url
1127
- });
1128
- const resultData = {
1129
- repoName,
1130
- headBranch,
1131
- targetBranch,
1132
- prNumber: existingPR.number,
1133
- prUrl: existingPR.html_url,
1134
- prTitle: existingPR.title,
1135
- commitsAhead,
1136
- createdAt: existingPR.created_at,
1137
- alreadyExists: true
1138
- };
1139
- // Emit socket event to refresh PR status on client
1140
- if (socket) {
1141
- socket.emit('refresh_pr_status', { repoName, prNumber: existingPR.number });
1142
- }
1143
- // the socket server our main server will automatically update the pr metadata in db when i recvs the result of the given tool.
1144
- // this.updatePRMetadata(userId, resultData);
1145
- return resultData;
1146
- }
1147
- }
1148
- catch (fetchError) {
1149
- logger.error('Failed to check for existing PR', fetchError);
1150
- }
1151
- // No existing PR found - check if we have commits to create one
1152
- if (!result.success) {
1153
- if (errorMatch) {
1154
- throw new Error(`No commits to create PR. Branch '${headBranch}' is not ahead of '${targetBranch}'`);
1155
- }
1156
- throw new Error('Failed to prepare pull request');
1157
- }
1158
- // Create new PR
1159
- const prTitle = title || `Merge ${headBranch} into ${targetBranch}`;
1160
- const prBody = body || `This pull request merges changes from ${headBranch} into ${targetBranch}.\n\nCreated by AI-Playgrounds`;
1161
- try {
1162
- const prResponse = await octokit.rest.pulls.create({
1163
- owner: gitOrgName,
1164
- repo: repoName,
1165
- title: prTitle,
1166
- body: prBody,
1167
- head: headBranch,
1168
- base: targetBranch
1169
- });
1170
- const prUrl = prResponse.data.html_url;
1171
- const prNumber = prResponse.data.number;
1172
- logger.success('Pull request created successfully', {
1094
+ if (existingPRs.data.length > 0) {
1095
+ const existingPR = existingPRs.data[0];
1096
+ logger.info('Pull request already exists', {
1173
1097
  repoName,
1174
- prNumber,
1175
- prUrl
1098
+ prNumber: existingPR.number,
1099
+ prUrl: existingPR.html_url
1176
1100
  });
1177
- // taskInfo.status = TaskStatus.Completed;
1178
- // taskInfo.nonRunningSince = new Date();
1179
- // //updating the task status in the db
1180
- // let dbService = await getDBService();
1181
- // let taskHanlder = dbService.getRepository<Task>(ds.UserInfo.get(userId as any)?.dbName, CollectionNames.TASKS);
1182
- // taskHanlder.updateOne({
1183
- // taskId: taskId
1184
- // },
1185
- // {
1186
- // "$set": {
1187
- // status: TaskStatus.Completed
1188
- // }
1189
- // });
1190
1101
  const resultData = {
1191
1102
  repoName,
1192
1103
  headBranch,
1193
1104
  targetBranch,
1194
- prNumber,
1195
- prUrl,
1196
- prTitle,
1105
+ prNumber: existingPR.number,
1106
+ prUrl: existingPR.html_url,
1107
+ prTitle: existingPR.title,
1197
1108
  commitsAhead,
1198
- createdAt: new Date().toISOString()
1109
+ createdAt: existingPR.created_at,
1110
+ alreadyExists: true
1199
1111
  };
1200
- // // Emit socket event to refresh PR status on client
1201
- // if (socket) {
1202
- // socket.emit('refresh_pr_status', { repoName, prNumber });
1203
- // }
1204
- // // Update database asynchronously (non-blocking)
1205
- // this.updatePRMetadata(userId, resultData);
1112
+ // Emit socket event to refresh PR status on client
1113
+ if (socket) {
1114
+ socket.emit('refresh_pr_status', { repoName, prNumber: existingPR.number });
1115
+ }
1116
+ // the socket server our main server will automatically update the pr metadata in db when i recvs the result of the given tool.
1117
+ // this.updatePRMetadata(userId, resultData);
1206
1118
  return resultData;
1207
1119
  }
1208
- catch (prError) {
1209
- // Check if PR already exists
1210
- if (prError.status === 422) {
1211
- // Fetch existing PR
1212
- try {
1213
- const existingPRs = await octokit.rest.pulls.list({
1214
- owner: gitOrgName,
1215
- repo: repoName,
1216
- head: `${gitOrgName}:${headBranch}`,
1217
- base: targetBranch,
1218
- state: 'open'
1120
+ }
1121
+ catch (fetchError) {
1122
+ logger.error('Failed to check for existing PR', fetchError);
1123
+ }
1124
+ // No existing PR found - check if we have commits to create one
1125
+ if (!result.success) {
1126
+ if (errorMatch) {
1127
+ throw new Error(`No commits to create PR. Branch '${headBranch}' is not ahead of '${targetBranch}'`);
1128
+ }
1129
+ throw new Error('Failed to prepare pull request');
1130
+ }
1131
+ // Create new PR
1132
+ const prTitle = title || `Merge ${headBranch} into ${targetBranch}`;
1133
+ const prBody = body || `This pull request merges changes from ${headBranch} into ${targetBranch}.\n\nCreated by AI-Playgrounds`;
1134
+ try {
1135
+ const prResponse = await octokit.rest.pulls.create({
1136
+ owner: gitOrgName,
1137
+ repo: repoName,
1138
+ title: prTitle,
1139
+ body: prBody,
1140
+ head: headBranch,
1141
+ base: targetBranch
1142
+ });
1143
+ const prUrl = prResponse.data.html_url;
1144
+ const prNumber = prResponse.data.number;
1145
+ logger.success('Pull request created successfully', {
1146
+ repoName,
1147
+ prNumber,
1148
+ prUrl
1149
+ });
1150
+ // taskInfo.status = TaskStatus.Completed;
1151
+ // taskInfo.nonRunningSince = new Date();
1152
+ // //updating the task status in the db
1153
+ // let dbService = await getDBService();
1154
+ // let taskHanlder = dbService.getRepository<Task>(ds.UserInfo.get(userId as any)?.dbName, CollectionNames.TASKS);
1155
+ // taskHanlder.updateOne({
1156
+ // taskId: taskId
1157
+ // },
1158
+ // {
1159
+ // "$set": {
1160
+ // status: TaskStatus.Completed
1161
+ // }
1162
+ // });
1163
+ const resultData = {
1164
+ repoName,
1165
+ headBranch,
1166
+ targetBranch,
1167
+ prNumber,
1168
+ prUrl,
1169
+ prTitle,
1170
+ commitsAhead,
1171
+ createdAt: new Date().toISOString()
1172
+ };
1173
+ // // Emit socket event to refresh PR status on client
1174
+ // if (socket) {
1175
+ // socket.emit('refresh_pr_status', { repoName, prNumber });
1176
+ // }
1177
+ // // Update database asynchronously (non-blocking)
1178
+ // this.updatePRMetadata(userId, resultData);
1179
+ return resultData;
1180
+ }
1181
+ catch (prError) {
1182
+ // Check if PR already exists
1183
+ if (prError.status === 422) {
1184
+ // Fetch existing PR
1185
+ try {
1186
+ const existingPRs = await octokit.rest.pulls.list({
1187
+ owner: gitOrgName,
1188
+ repo: repoName,
1189
+ head: `${gitOrgName}:${headBranch}`,
1190
+ base: targetBranch,
1191
+ state: 'open'
1192
+ });
1193
+ if (existingPRs.data.length > 0) {
1194
+ const existingPR = existingPRs.data[0];
1195
+ logger.info('Pull request already exists', {
1196
+ repoName,
1197
+ prNumber: existingPR.number,
1198
+ prUrl: existingPR.html_url
1219
1199
  });
1220
- if (existingPRs.data.length > 0) {
1221
- const existingPR = existingPRs.data[0];
1222
- logger.info('Pull request already exists', {
1223
- repoName,
1224
- prNumber: existingPR.number,
1225
- prUrl: existingPR.html_url
1226
- });
1227
- const resultData = {
1228
- repoName,
1229
- headBranch,
1230
- targetBranch,
1231
- prNumber: existingPR.number,
1232
- prUrl: existingPR.html_url,
1233
- prTitle: existingPR.title,
1234
- commitsAhead,
1235
- createdAt: existingPR.created_at,
1236
- alreadyExists: true
1237
- };
1238
- // Emit socket event to refresh PR status on client
1239
- if (socket) {
1240
- socket.emit('refresh_pr_status', { repoName, prNumber: existingPR.number });
1241
- }
1242
- // Update database asynchronously (non-blocking)
1243
- // this.updatePRMetadata(userId, resultData);
1244
- return resultData;
1200
+ const resultData = {
1201
+ repoName,
1202
+ headBranch,
1203
+ targetBranch,
1204
+ prNumber: existingPR.number,
1205
+ prUrl: existingPR.html_url,
1206
+ prTitle: existingPR.title,
1207
+ commitsAhead,
1208
+ createdAt: existingPR.created_at,
1209
+ alreadyExists: true
1210
+ };
1211
+ // Emit socket event to refresh PR status on client
1212
+ if (socket) {
1213
+ socket.emit('refresh_pr_status', { repoName, prNumber: existingPR.number });
1245
1214
  }
1215
+ // Update database asynchronously (non-blocking)
1216
+ // this.updatePRMetadata(userId, resultData);
1217
+ return resultData;
1246
1218
  }
1247
- catch (fetchError) {
1248
- logger.error('Failed to fetch existing PR', fetchError);
1249
- }
1250
1219
  }
1251
- throw prError;
1220
+ catch (fetchError) {
1221
+ logger.error('Failed to fetch existing PR', fetchError);
1222
+ }
1252
1223
  }
1224
+ throw prError;
1253
1225
  }
1254
- finally {
1255
- }
1256
- }); // end withRepoLock
1226
+ }
1227
+ finally {
1228
+ }
1257
1229
  }
1258
1230
  /**
1259
1231
  * Check if pull request exists
@@ -1262,37 +1234,35 @@ class GithubOperationsService {
1262
1234
  const repoPath = `${tool_server_1.folderPath}/${repoName}`;
1263
1235
  const installationToken = await fetchInstallationToken();
1264
1236
  const gitOrgName = await getGithubOrganizationName();
1265
- return withRepoLock(repoName, async () => {
1266
- try {
1267
- const trimmedSourceBranch = (sourceBranch || '').trim();
1268
- const trimmedTargetBranch = (targetBranch || '').trim();
1269
- if (!trimmedSourceBranch || !trimmedTargetBranch) {
1270
- throw new Error('Both sourceBranch and targetBranch are required');
1271
- }
1272
- if (trimmedSourceBranch === trimmedTargetBranch) {
1273
- throw new Error('sourceBranch and targetBranch cannot be the same');
1274
- }
1275
- logger.info('Starting branch merge operation', {
1276
- repoName,
1277
- sourceBranch: trimmedSourceBranch,
1278
- targetBranch: trimmedTargetBranch,
1279
- pushToRemote
1280
- });
1281
- let result = await executeShell(`cd ${repoPath} && rm -f .git/config.lock && ` +
1282
- `git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git`);
1283
- if (!result.success) {
1284
- throw new Error(`Failed to set authenticated remote URL: ${result.error || result.output || 'Unknown error'}`);
1285
- }
1286
- const fetchResult = await executeShell(`cd ${repoPath} && sudo git fetch origin --prune`);
1287
- if (!fetchResult.success) {
1288
- throw new Error(`Failed to fetch remote branches: ${fetchResult.error || fetchResult.output || 'Unknown error'}`);
1289
- }
1290
- const resolvedSource = await this.resolveBranchReference(repoPath, repoName, trimmedSourceBranch);
1291
- const resolvedTarget = await this.resolveBranchReference(repoPath, repoName, trimmedTargetBranch);
1292
- const targetLocalBranch = resolvedTarget.displayBranch;
1293
- const safeTargetLocalBranch = this.escapeSingleQuotedShell(targetLocalBranch);
1294
- const safeSourceRef = this.escapeSingleQuotedShell(resolvedSource.resolvedRef);
1295
- const prepareBranchCommand = `
1237
+ try {
1238
+ const trimmedSourceBranch = (sourceBranch || '').trim();
1239
+ const trimmedTargetBranch = (targetBranch || '').trim();
1240
+ if (!trimmedSourceBranch || !trimmedTargetBranch) {
1241
+ throw new Error('Both sourceBranch and targetBranch are required');
1242
+ }
1243
+ if (trimmedSourceBranch === trimmedTargetBranch) {
1244
+ throw new Error('sourceBranch and targetBranch cannot be the same');
1245
+ }
1246
+ logger.info('Starting branch merge operation', {
1247
+ repoName,
1248
+ sourceBranch: trimmedSourceBranch,
1249
+ targetBranch: trimmedTargetBranch,
1250
+ pushToRemote
1251
+ });
1252
+ let result = await executeShell(`cd ${repoPath} && sudo git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git`);
1253
+ if (!result.success) {
1254
+ throw new Error(`Failed to set authenticated remote URL: ${result.error || result.output || 'Unknown error'}`);
1255
+ }
1256
+ const fetchResult = await executeShell(`cd ${repoPath} && sudo git fetch origin --prune`);
1257
+ if (!fetchResult.success) {
1258
+ throw new Error(`Failed to fetch remote branches: ${fetchResult.error || fetchResult.output || 'Unknown error'}`);
1259
+ }
1260
+ const resolvedSource = await this.resolveBranchReference(repoPath, repoName, trimmedSourceBranch);
1261
+ const resolvedTarget = await this.resolveBranchReference(repoPath, repoName, trimmedTargetBranch);
1262
+ const targetLocalBranch = resolvedTarget.displayBranch;
1263
+ const safeTargetLocalBranch = this.escapeSingleQuotedShell(targetLocalBranch);
1264
+ const safeSourceRef = this.escapeSingleQuotedShell(resolvedSource.resolvedRef);
1265
+ const prepareBranchCommand = `
1296
1266
  cd ${repoPath} &&
1297
1267
  TARGET_LOCAL='${safeTargetLocalBranch}' &&
1298
1268
  if sudo git show-ref --verify --quiet "refs/heads/$TARGET_LOCAL"; then
@@ -1307,178 +1277,174 @@ class GithubOperationsService {
1307
1277
  sudo git pull --ff-only origin "$TARGET_LOCAL" 2>/dev/null || true;
1308
1278
  fi
1309
1279
  `.replace(/\n\s+/g, ' ');
1310
- result = await executeShell(prepareBranchCommand);
1311
- if (!result.success) {
1312
- throw new Error(`Failed to prepare target branch for merge: ${result.error || result.output || 'Unknown error'}`);
1313
- }
1314
- const mergeCommand = `cd ${repoPath} && SOURCE_REF='${safeSourceRef}' && sudo git merge --no-ff --no-edit "$SOURCE_REF"`;
1315
- const mergeResult = await executeShell(mergeCommand);
1316
- if (!mergeResult.success) {
1317
- const conflictResult = await executeShell(`cd ${repoPath} && sudo git diff --name-only --diff-filter=U`);
1318
- const conflictFiles = (conflictResult.output || '')
1319
- .split('\n')
1320
- .map((line) => line.trim())
1321
- .filter((line) => line.length > 0);
1322
- if (conflictFiles.length > 0) {
1323
- logger.warn('Merge completed with conflicts', {
1324
- repoName,
1325
- sourceBranch: resolvedSource.displayBranch,
1326
- targetBranch: targetLocalBranch,
1327
- conflictFiles
1328
- });
1329
- return {
1330
- repoName,
1331
- sourceBranch: resolvedSource.displayBranch,
1332
- targetBranch: targetLocalBranch,
1333
- merged: false,
1334
- hasConflicts: true,
1335
- conflictFiles,
1336
- currentBranch: targetLocalBranch,
1337
- pushedToRemote: false,
1338
- mergedAt: new Date().toISOString(),
1339
- message: `Merge has conflicts. Resolve conflicts in target branch '${targetLocalBranch}'.`
1340
- };
1341
- }
1342
- throw new Error(`Failed to merge branches: ${mergeResult.error || mergeResult.output || 'Unknown error'}`);
1343
- }
1344
- const summaryResult = await executeShell(`cd ${repoPath} && COMMIT_HASH=$(sudo git rev-parse HEAD) && CURRENT_BRANCH=$(sudo git rev-parse --abbrev-ref HEAD) && echo "COMMIT_HASH:$COMMIT_HASH" && echo "CURRENT_BRANCH:$CURRENT_BRANCH"`);
1345
- if (!summaryResult.success) {
1346
- throw new Error(`Merge succeeded but failed to fetch merge summary: ${summaryResult.error || summaryResult.output || 'Unknown error'}`);
1280
+ result = await executeShell(prepareBranchCommand);
1281
+ if (!result.success) {
1282
+ throw new Error(`Failed to prepare target branch for merge: ${result.error || result.output || 'Unknown error'}`);
1283
+ }
1284
+ const mergeCommand = `cd ${repoPath} && SOURCE_REF='${safeSourceRef}' && sudo git merge --no-ff --no-edit "$SOURCE_REF"`;
1285
+ const mergeResult = await executeShell(mergeCommand);
1286
+ if (!mergeResult.success) {
1287
+ const conflictResult = await executeShell(`cd ${repoPath} && sudo git diff --name-only --diff-filter=U`);
1288
+ const conflictFiles = (conflictResult.output || '')
1289
+ .split('\n')
1290
+ .map((line) => line.trim())
1291
+ .filter((line) => line.length > 0);
1292
+ if (conflictFiles.length > 0) {
1293
+ logger.warn('Merge completed with conflicts', {
1294
+ repoName,
1295
+ sourceBranch: resolvedSource.displayBranch,
1296
+ targetBranch: targetLocalBranch,
1297
+ conflictFiles
1298
+ });
1299
+ return {
1300
+ repoName,
1301
+ sourceBranch: resolvedSource.displayBranch,
1302
+ targetBranch: targetLocalBranch,
1303
+ merged: false,
1304
+ hasConflicts: true,
1305
+ conflictFiles,
1306
+ currentBranch: targetLocalBranch,
1307
+ pushedToRemote: false,
1308
+ mergedAt: new Date().toISOString(),
1309
+ message: `Merge has conflicts. Resolve conflicts in target branch '${targetLocalBranch}'.`
1310
+ };
1347
1311
  }
1348
- const summaryOutput = summaryResult.output || '';
1349
- const commitHashMatch = summaryOutput.match(/COMMIT_HASH:([^\n]+)/);
1350
- const currentBranchMatch = summaryOutput.match(/CURRENT_BRANCH:([^\n]+)/);
1351
- const commitHash = commitHashMatch ? commitHashMatch[1].trim() : '';
1352
- const currentBranch = currentBranchMatch ? currentBranchMatch[1].trim() : targetLocalBranch;
1353
- let pushedToRemote = false;
1354
- if (pushToRemote) {
1355
- const pushResult = await executeShell(`cd ${repoPath} && sudo git push origin '${safeTargetLocalBranch}'`);
1356
- if (!pushResult.success) {
1357
- throw new Error(`Merge succeeded but push failed: ${pushResult.error || pushResult.output || 'Unknown error'}`);
1358
- }
1359
- pushedToRemote = true;
1312
+ throw new Error(`Failed to merge branches: ${mergeResult.error || mergeResult.output || 'Unknown error'}`);
1313
+ }
1314
+ const summaryResult = await executeShell(`cd ${repoPath} && COMMIT_HASH=$(sudo git rev-parse HEAD) && CURRENT_BRANCH=$(sudo git rev-parse --abbrev-ref HEAD) && echo "COMMIT_HASH:$COMMIT_HASH" && echo "CURRENT_BRANCH:$CURRENT_BRANCH"`);
1315
+ if (!summaryResult.success) {
1316
+ throw new Error(`Merge succeeded but failed to fetch merge summary: ${summaryResult.error || summaryResult.output || 'Unknown error'}`);
1317
+ }
1318
+ const summaryOutput = summaryResult.output || '';
1319
+ const commitHashMatch = summaryOutput.match(/COMMIT_HASH:([^\n]+)/);
1320
+ const currentBranchMatch = summaryOutput.match(/CURRENT_BRANCH:([^\n]+)/);
1321
+ const commitHash = commitHashMatch ? commitHashMatch[1].trim() : '';
1322
+ const currentBranch = currentBranchMatch ? currentBranchMatch[1].trim() : targetLocalBranch;
1323
+ let pushedToRemote = false;
1324
+ if (pushToRemote) {
1325
+ const pushResult = await executeShell(`cd ${repoPath} && sudo git push origin '${safeTargetLocalBranch}'`);
1326
+ if (!pushResult.success) {
1327
+ throw new Error(`Merge succeeded but push failed: ${pushResult.error || pushResult.output || 'Unknown error'}`);
1360
1328
  }
1361
- logger.success('Branch merge completed successfully', {
1329
+ pushedToRemote = true;
1330
+ }
1331
+ logger.success('Branch merge completed successfully', {
1332
+ repoName,
1333
+ sourceBranch: resolvedSource.displayBranch,
1334
+ targetBranch: targetLocalBranch,
1335
+ commitHash,
1336
+ pushedToRemote
1337
+ });
1338
+ if (socket) {
1339
+ socket.emit('refresh_repo', { repoName });
1340
+ }
1341
+ return {
1342
+ repoName,
1343
+ sourceBranch: resolvedSource.displayBranch,
1344
+ targetBranch: targetLocalBranch,
1345
+ merged: true,
1346
+ hasConflicts: false,
1347
+ conflictFiles: [],
1348
+ currentBranch,
1349
+ commitHash,
1350
+ pushedToRemote,
1351
+ mergedAt: new Date().toISOString(),
1352
+ message: pushedToRemote
1353
+ ? `Merged '${resolvedSource.displayBranch}' into '${targetLocalBranch}' and pushed to remote.`
1354
+ : `Merged '${resolvedSource.displayBranch}' into '${targetLocalBranch}' locally.`
1355
+ };
1356
+ }
1357
+ finally {
1358
+ try {
1359
+ await executeShell(`cd ${repoPath} && sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git`);
1360
+ }
1361
+ catch (restoreError) {
1362
+ logger.error('Failed to restore unauthenticated remote URL after merge operation', {
1362
1363
  repoName,
1363
- sourceBranch: resolvedSource.displayBranch,
1364
- targetBranch: targetLocalBranch,
1365
- commitHash,
1366
- pushedToRemote
1364
+ error: restoreError instanceof Error ? restoreError.message : String(restoreError)
1367
1365
  });
1368
- if (socket) {
1369
- socket.emit('refresh_repo', { repoName });
1370
- }
1371
- return {
1372
- repoName,
1373
- sourceBranch: resolvedSource.displayBranch,
1374
- targetBranch: targetLocalBranch,
1375
- merged: true,
1376
- hasConflicts: false,
1377
- conflictFiles: [],
1378
- currentBranch,
1379
- commitHash,
1380
- pushedToRemote,
1381
- mergedAt: new Date().toISOString(),
1382
- message: pushedToRemote
1383
- ? `Merged '${resolvedSource.displayBranch}' into '${targetLocalBranch}' and pushed to remote.`
1384
- : `Merged '${resolvedSource.displayBranch}' into '${targetLocalBranch}' locally.`
1385
- };
1386
1366
  }
1387
- finally {
1388
- try {
1389
- await executeShell(`cd ${repoPath} && git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git`);
1390
- }
1391
- catch (restoreError) {
1392
- logger.error('Failed to restore unauthenticated remote URL after merge operation', {
1393
- repoName,
1394
- error: restoreError instanceof Error ? restoreError.message : String(restoreError)
1395
- });
1396
- }
1397
- }
1398
- }); // end withRepoLock
1367
+ }
1399
1368
  }
1400
1369
  async checkPullRequestExists(repoName, targetBranch) {
1401
1370
  const repoPath = `${tool_server_1.folderPath}/${repoName}`;
1402
1371
  const installationToken = await fetchInstallationToken();
1403
1372
  const gitOrgName = await getGithubOrganizationName();
1404
- return withRepoLock(repoName, async () => {
1405
- try {
1406
- logger.info('Checking if PR exists', { repoName, targetBranch });
1407
- // Get current branch and commits ahead
1408
- const combinedCommand = `
1373
+ try {
1374
+ logger.info('Checking if PR exists', { repoName, targetBranch });
1375
+ // Get current branch and commits ahead
1376
+ const combinedCommand = `
1409
1377
  cd ${repoPath} &&
1410
- rm -f .git/config.lock &&
1411
- git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git &&
1378
+ sudo git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git &&
1412
1379
  HEAD_BRANCH=$(sudo git rev-parse --abbrev-ref HEAD) &&
1413
1380
  sudo git fetch origin --prune 2>/dev/null || true &&
1414
1381
  sudo git fetch origin ${targetBranch} 2>/dev/null || true &&
1415
1382
  COMMITS_AHEAD=$(sudo git rev-list --count origin/${targetBranch}..$HEAD_BRANCH 2>/dev/null || echo 0) &&
1416
- git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git &&
1383
+ sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git &&
1417
1384
  echo "HEAD_BRANCH:$HEAD_BRANCH" &&
1418
1385
  echo "COMMITS_AHEAD:$COMMITS_AHEAD"
1419
1386
  `.replace(/\n\s+/g, ' ');
1420
- const result = await executeShell(combinedCommand);
1421
- // Check for command execution failure
1422
- if (!result.success) {
1423
- logger.error('Failed to check PR status', {
1424
- repoName,
1425
- error: result.error,
1426
- code: result.code,
1427
- output: result.output
1428
- });
1429
- throw new Error(`Failed to check PR status: ${result.error}`);
1430
- }
1431
- const output = result.output || '';
1432
- const headBranchMatch = output.match(/HEAD_BRANCH:([^\n]+)/);
1433
- const commitsAheadMatch = output.match(/COMMITS_AHEAD:(\d+)/);
1434
- const headBranch = headBranchMatch ? headBranchMatch[1].trim() : 'unknown';
1435
- const commitsAhead = commitsAheadMatch ? parseInt(commitsAheadMatch[1]) : 0;
1436
- logger.info('Branch info retrieved', { headBranch, commitsAhead });
1437
- // Check for existing PR - using 'octokit' package (already installed)
1438
- const { Octokit } = require('octokit');
1439
- const octokit = new Octokit({
1440
- auth: installationToken
1387
+ const result = await executeShell(combinedCommand);
1388
+ // Check for command execution failure
1389
+ if (!result.success) {
1390
+ logger.error('Failed to check PR status', {
1391
+ repoName,
1392
+ error: result.error,
1393
+ code: result.code,
1394
+ output: result.output
1441
1395
  });
1442
- const existingPRs = await octokit.rest.pulls.list({
1443
- owner: gitOrgName,
1444
- repo: repoName,
1445
- head: `${gitOrgName}:${headBranch}`,
1446
- base: targetBranch,
1447
- state: 'open'
1396
+ throw new Error(`Failed to check PR status: ${result.error}`);
1397
+ }
1398
+ const output = result.output || '';
1399
+ const headBranchMatch = output.match(/HEAD_BRANCH:([^\n]+)/);
1400
+ const commitsAheadMatch = output.match(/COMMITS_AHEAD:(\d+)/);
1401
+ const headBranch = headBranchMatch ? headBranchMatch[1].trim() : 'unknown';
1402
+ const commitsAhead = commitsAheadMatch ? parseInt(commitsAheadMatch[1]) : 0;
1403
+ logger.info('Branch info retrieved', { headBranch, commitsAhead });
1404
+ // Check for existing PR - using 'octokit' package (already installed)
1405
+ const { Octokit } = require('octokit');
1406
+ const octokit = new Octokit({
1407
+ auth: installationToken
1408
+ });
1409
+ const existingPRs = await octokit.rest.pulls.list({
1410
+ owner: gitOrgName,
1411
+ repo: repoName,
1412
+ head: `${gitOrgName}:${headBranch}`,
1413
+ base: targetBranch,
1414
+ state: 'open'
1415
+ });
1416
+ if (existingPRs.data.length > 0) {
1417
+ const existingPR = existingPRs.data[0];
1418
+ logger.info('Pull request exists', {
1419
+ repoName,
1420
+ prNumber: existingPR.number,
1421
+ prUrl: existingPR.html_url
1448
1422
  });
1449
- if (existingPRs.data.length > 0) {
1450
- const existingPR = existingPRs.data[0];
1451
- logger.info('Pull request exists', {
1452
- repoName,
1453
- prNumber: existingPR.number,
1454
- prUrl: existingPR.html_url
1455
- });
1456
- return {
1457
- repoName,
1458
- headBranch,
1459
- targetBranch,
1460
- prExists: true,
1461
- prNumber: existingPR.number,
1462
- prUrl: existingPR.html_url,
1463
- prTitle: existingPR.title,
1464
- commitsAhead,
1465
- createdAt: existingPR.created_at
1466
- };
1467
- }
1468
- else {
1469
- logger.info('No pull request found', { repoName, headBranch, targetBranch });
1470
- return {
1471
- repoName,
1472
- headBranch,
1473
- targetBranch,
1474
- prExists: false,
1475
- commitsAhead
1476
- };
1477
- }
1423
+ return {
1424
+ repoName,
1425
+ headBranch,
1426
+ targetBranch,
1427
+ prExists: true,
1428
+ prNumber: existingPR.number,
1429
+ prUrl: existingPR.html_url,
1430
+ prTitle: existingPR.title,
1431
+ commitsAhead,
1432
+ createdAt: existingPR.created_at
1433
+ };
1478
1434
  }
1479
- finally {
1435
+ else {
1436
+ logger.info('No pull request found', { repoName, headBranch, targetBranch });
1437
+ return {
1438
+ repoName,
1439
+ headBranch,
1440
+ targetBranch,
1441
+ prExists: false,
1442
+ commitsAhead
1443
+ };
1480
1444
  }
1481
- }); // end withRepoLock
1445
+ }
1446
+ finally {
1447
+ }
1482
1448
  }
1483
1449
  /**
1484
1450
  * Get commit list with commit IDs for a branch
@@ -1977,7 +1943,7 @@ class GithubOperationsService {
1977
1943
  /**
1978
1944
  * Helper method to get merge conflicts
1979
1945
  */
1980
- async getMergeConflicts(repoName) {
1946
+ async getMergeConflicts(socket, repoName) {
1981
1947
  return new Promise((resolve) => {
1982
1948
  this.getMergeConflictsImplementation(repoName, tool_server_1.folderPath).then((mergeData) => {
1983
1949
  resolve(mergeData);