phantomx-tool-client 1.1.6 → 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,21 +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
- return withRepoLock(repoName, async () => {
754
- try {
755
- logger.info('Checking repository status', { repoName });
756
- // Combine all git commands into a single SSH call
757
- // Remove any stale .git/config.lock before touching remote config
758
- // (a crashed/interrupted previous run can leave this lock behind).
759
- // Also avoid sudo for git remote set-url so the lock file is never
760
- // created as root-owned, which would block subsequent cleanup.
761
- const combinedCommand = `
738
+ try {
739
+ logger.info('Checking repository status', { repoName });
740
+ // Combine all git commands into a single SSH call
741
+ const combinedCommand = `
762
742
  cd ${repoPath} &&
763
- rm -f .git/config.lock &&
764
743
  BRANCH=$(sudo git rev-parse --abbrev-ref HEAD) &&
765
- 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 &&
766
745
  sudo git fetch origin &&
767
- git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git &&
746
+ sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git &&
768
747
  BEHIND=$(sudo git rev-list --count HEAD..origin/$BRANCH 2>/dev/null || echo 0) &&
769
748
  AHEAD=$(sudo git rev-list --count origin/$BRANCH..HEAD 2>/dev/null || echo 0) &&
770
749
  STATUS=$(sudo git status --porcelain) &&
@@ -777,55 +756,54 @@ class GithubOperationsService {
777
756
  echo "CURRENT:$CURRENT_HASH" &&
778
757
  echo "REMOTE:$REMOTE_HASH"
779
758
  `.replace(/\n\s+/g, ' ');
780
- const result = await executeShell(combinedCommand);
781
- if (!result.success) {
782
- throw new Error('Failed to check repository status: ' + result.error);
783
- }
784
- // Parse the output
785
- const output = result.output;
786
- const branchMatch = output.match(/BRANCH:([^\n]+)/);
787
- const behindMatch = output.match(/BEHIND:(\d+)/);
788
- const aheadMatch = output.match(/AHEAD:(\d+)/);
789
- const statusMatch = output.match(/STATUS:([\s\S]*?)(?=CURRENT:|$)/);
790
- const currentMatch = output.match(/CURRENT:([a-f0-9]+)/);
791
- const remoteMatch = output.match(/REMOTE:([a-f0-9]+|unknown)/);
792
- const currentBranch = branchMatch ? branchMatch[1].trim() : 'unknown';
793
- const commitsBehind = behindMatch ? parseInt(behindMatch[1]) : 0;
794
- const commitsAhead = aheadMatch ? parseInt(aheadMatch[1]) : 0;
795
- const statusOutput = statusMatch ? statusMatch[1].trim() : '';
796
- const hasUncommittedChanges = statusOutput.length > 0;
797
- const currentCommitHash = currentMatch ? currentMatch[1].trim() : 'unknown';
798
- const remoteCommitHash = remoteMatch ? remoteMatch[1].trim() : 'unknown';
799
- const isUpToDate = commitsBehind === 0 && commitsAhead === 0;
800
- const needsPull = commitsBehind > 0;
801
- const needsPush = commitsAhead > 0;
802
- logger.info('Repository status checked', {
803
- repoName,
804
- commitsBehind,
805
- commitsAhead,
806
- hasUncommittedChanges,
807
- isUpToDate
808
- });
809
- // Check for merge conflicts
810
- const mergeConflicts = await this.getMergeConflicts(repoName);
811
- return {
812
- repoName,
813
- branchName: currentBranch,
814
- currentCommitHash,
815
- remoteCommitHash,
816
- commitsBehind,
817
- commitsAhead,
818
- hasUncommittedChanges,
819
- isUpToDate,
820
- needsPull,
821
- needsPush,
822
- mergeConflicts,
823
- status: isUpToDate ? 'up-to-date' : (needsPull && needsPush ? 'diverged' : needsPull ? 'behind' : 'ahead')
824
- };
825
- }
826
- finally {
759
+ const result = await executeShell(combinedCommand);
760
+ if (!result.success) {
761
+ throw new Error('Failed to check repository status');
827
762
  }
828
- }); // 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
+ }
829
807
  }
830
808
  /**
831
809
  * Get repository history
@@ -1071,186 +1049,183 @@ class GithubOperationsService {
1071
1049
  const repoPath = `${tool_server_1.folderPath}/${repoName}`;
1072
1050
  const installationToken = await fetchInstallationToken();
1073
1051
  const gitOrgName = await getGithubOrganizationName();
1074
- return withRepoLock(repoName, async () => {
1075
- try {
1076
- logger.info('Creating pull request', { repoName, targetBranch });
1077
- // Combine commands into a single SSH call, with remote URL set/unset
1078
- 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 = `
1079
1056
  cd ${repoPath} &&
1080
- rm -f .git/config.lock &&
1081
- 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 &&
1082
1058
  HEAD_BRANCH=$(sudo git rev-parse --abbrev-ref HEAD) &&
1083
1059
  sudo git fetch origin ${targetBranch} 2>/dev/null &&
1084
1060
  COMMITS_AHEAD=$(sudo git rev-list --count origin/${targetBranch}..$HEAD_BRANCH 2>/dev/null || echo 0) &&
1085
1061
  if [ "$COMMITS_AHEAD" = "0" ]; then
1086
1062
  echo "ERROR:NO_COMMITS:$HEAD_BRANCH";
1087
- git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git;
1063
+ sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git;
1088
1064
  exit 1;
1089
1065
  fi &&
1090
1066
  echo "HEAD_BRANCH:$HEAD_BRANCH" &&
1091
1067
  echo "COMMITS_AHEAD:$COMMITS_AHEAD" &&
1092
- git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git
1068
+ sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git
1093
1069
  `.replace(/\n\s+/g, ' ');
1094
- let result = await executeShell(combinedCommand);
1095
- const output = result.output || '';
1096
- const headBranchMatch = output.match(/HEAD_BRANCH:([^\n]+)/);
1097
- const commitsAheadMatch = output.match(/COMMITS_AHEAD:(\d+)/);
1098
- const errorMatch = output.match(/ERROR:NO_COMMITS:([^\n]+)/);
1099
- let headBranch = headBranchMatch ? headBranchMatch[1].trim() : 'unknown';
1100
- const commitsAhead = commitsAheadMatch ? parseInt(commitsAheadMatch[1]) : 0;
1101
- if (!result.success && errorMatch) {
1102
- headBranch = errorMatch[1].trim();
1103
- }
1104
- // using 'octokit' package (already installed, @octokit/rest is not installed)
1105
- const { Octokit } = require('octokit');
1106
- const octokit = new Octokit({
1107
- 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'
1108
1093
  });
1109
- // Always check for existing PR first even if there are no new commits
1110
- try {
1111
- const existingPRs = await octokit.rest.pulls.list({
1112
- owner: gitOrgName,
1113
- repo: repoName,
1114
- head: `${gitOrgName}:${headBranch}`,
1115
- base: targetBranch,
1116
- state: 'open'
1117
- });
1118
- if (existingPRs.data.length > 0) {
1119
- const existingPR = existingPRs.data[0];
1120
- logger.info('Pull request already exists', {
1121
- repoName,
1122
- prNumber: existingPR.number,
1123
- prUrl: existingPR.html_url
1124
- });
1125
- const resultData = {
1126
- repoName,
1127
- headBranch,
1128
- targetBranch,
1129
- prNumber: existingPR.number,
1130
- prUrl: existingPR.html_url,
1131
- prTitle: existingPR.title,
1132
- commitsAhead,
1133
- createdAt: existingPR.created_at,
1134
- alreadyExists: true
1135
- };
1136
- // Emit socket event to refresh PR status on client
1137
- if (socket) {
1138
- socket.emit('refresh_pr_status', { repoName, prNumber: existingPR.number });
1139
- }
1140
- // the socket server our main server will automatically update the pr metadata in db when i recvs the result of the given tool.
1141
- // this.updatePRMetadata(userId, resultData);
1142
- return resultData;
1143
- }
1144
- }
1145
- catch (fetchError) {
1146
- logger.error('Failed to check for existing PR', fetchError);
1147
- }
1148
- // No existing PR found - check if we have commits to create one
1149
- if (!result.success) {
1150
- if (errorMatch) {
1151
- throw new Error(`No commits to create PR. Branch '${headBranch}' is not ahead of '${targetBranch}'`);
1152
- }
1153
- throw new Error('Failed to prepare pull request');
1154
- }
1155
- // Create new PR
1156
- const prTitle = title || `Merge ${headBranch} into ${targetBranch}`;
1157
- const prBody = body || `This pull request merges changes from ${headBranch} into ${targetBranch}.\n\nCreated by AI-Playgrounds`;
1158
- try {
1159
- const prResponse = await octokit.rest.pulls.create({
1160
- owner: gitOrgName,
1161
- repo: repoName,
1162
- title: prTitle,
1163
- body: prBody,
1164
- head: headBranch,
1165
- base: targetBranch
1166
- });
1167
- const prUrl = prResponse.data.html_url;
1168
- const prNumber = prResponse.data.number;
1169
- 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', {
1170
1097
  repoName,
1171
- prNumber,
1172
- prUrl
1098
+ prNumber: existingPR.number,
1099
+ prUrl: existingPR.html_url
1173
1100
  });
1174
- // taskInfo.status = TaskStatus.Completed;
1175
- // taskInfo.nonRunningSince = new Date();
1176
- // //updating the task status in the db
1177
- // let dbService = await getDBService();
1178
- // let taskHanlder = dbService.getRepository<Task>(ds.UserInfo.get(userId as any)?.dbName, CollectionNames.TASKS);
1179
- // taskHanlder.updateOne({
1180
- // taskId: taskId
1181
- // },
1182
- // {
1183
- // "$set": {
1184
- // status: TaskStatus.Completed
1185
- // }
1186
- // });
1187
1101
  const resultData = {
1188
1102
  repoName,
1189
1103
  headBranch,
1190
1104
  targetBranch,
1191
- prNumber,
1192
- prUrl,
1193
- prTitle,
1105
+ prNumber: existingPR.number,
1106
+ prUrl: existingPR.html_url,
1107
+ prTitle: existingPR.title,
1194
1108
  commitsAhead,
1195
- createdAt: new Date().toISOString()
1109
+ createdAt: existingPR.created_at,
1110
+ alreadyExists: true
1196
1111
  };
1197
- // // Emit socket event to refresh PR status on client
1198
- // if (socket) {
1199
- // socket.emit('refresh_pr_status', { repoName, prNumber });
1200
- // }
1201
- // // Update database asynchronously (non-blocking)
1202
- // 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);
1203
1118
  return resultData;
1204
1119
  }
1205
- catch (prError) {
1206
- // Check if PR already exists
1207
- if (prError.status === 422) {
1208
- // Fetch existing PR
1209
- try {
1210
- const existingPRs = await octokit.rest.pulls.list({
1211
- owner: gitOrgName,
1212
- repo: repoName,
1213
- head: `${gitOrgName}:${headBranch}`,
1214
- base: targetBranch,
1215
- 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
1216
1199
  });
1217
- if (existingPRs.data.length > 0) {
1218
- const existingPR = existingPRs.data[0];
1219
- logger.info('Pull request already exists', {
1220
- repoName,
1221
- prNumber: existingPR.number,
1222
- prUrl: existingPR.html_url
1223
- });
1224
- const resultData = {
1225
- repoName,
1226
- headBranch,
1227
- targetBranch,
1228
- prNumber: existingPR.number,
1229
- prUrl: existingPR.html_url,
1230
- prTitle: existingPR.title,
1231
- commitsAhead,
1232
- createdAt: existingPR.created_at,
1233
- alreadyExists: true
1234
- };
1235
- // Emit socket event to refresh PR status on client
1236
- if (socket) {
1237
- socket.emit('refresh_pr_status', { repoName, prNumber: existingPR.number });
1238
- }
1239
- // Update database asynchronously (non-blocking)
1240
- // this.updatePRMetadata(userId, resultData);
1241
- 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 });
1242
1214
  }
1215
+ // Update database asynchronously (non-blocking)
1216
+ // this.updatePRMetadata(userId, resultData);
1217
+ return resultData;
1243
1218
  }
1244
- catch (fetchError) {
1245
- logger.error('Failed to fetch existing PR', fetchError);
1246
- }
1247
1219
  }
1248
- throw prError;
1220
+ catch (fetchError) {
1221
+ logger.error('Failed to fetch existing PR', fetchError);
1222
+ }
1249
1223
  }
1224
+ throw prError;
1250
1225
  }
1251
- finally {
1252
- }
1253
- }); // end withRepoLock
1226
+ }
1227
+ finally {
1228
+ }
1254
1229
  }
1255
1230
  /**
1256
1231
  * Check if pull request exists
@@ -1259,37 +1234,35 @@ class GithubOperationsService {
1259
1234
  const repoPath = `${tool_server_1.folderPath}/${repoName}`;
1260
1235
  const installationToken = await fetchInstallationToken();
1261
1236
  const gitOrgName = await getGithubOrganizationName();
1262
- return withRepoLock(repoName, async () => {
1263
- try {
1264
- const trimmedSourceBranch = (sourceBranch || '').trim();
1265
- const trimmedTargetBranch = (targetBranch || '').trim();
1266
- if (!trimmedSourceBranch || !trimmedTargetBranch) {
1267
- throw new Error('Both sourceBranch and targetBranch are required');
1268
- }
1269
- if (trimmedSourceBranch === trimmedTargetBranch) {
1270
- throw new Error('sourceBranch and targetBranch cannot be the same');
1271
- }
1272
- logger.info('Starting branch merge operation', {
1273
- repoName,
1274
- sourceBranch: trimmedSourceBranch,
1275
- targetBranch: trimmedTargetBranch,
1276
- pushToRemote
1277
- });
1278
- let result = await executeShell(`cd ${repoPath} && rm -f .git/config.lock && ` +
1279
- `git remote set-url origin https://x-access-token:${installationToken}@github.com/${gitOrgName}/${repoName}.git`);
1280
- if (!result.success) {
1281
- throw new Error(`Failed to set authenticated remote URL: ${result.error || result.output || 'Unknown error'}`);
1282
- }
1283
- const fetchResult = await executeShell(`cd ${repoPath} && sudo git fetch origin --prune`);
1284
- if (!fetchResult.success) {
1285
- throw new Error(`Failed to fetch remote branches: ${fetchResult.error || fetchResult.output || 'Unknown error'}`);
1286
- }
1287
- const resolvedSource = await this.resolveBranchReference(repoPath, repoName, trimmedSourceBranch);
1288
- const resolvedTarget = await this.resolveBranchReference(repoPath, repoName, trimmedTargetBranch);
1289
- const targetLocalBranch = resolvedTarget.displayBranch;
1290
- const safeTargetLocalBranch = this.escapeSingleQuotedShell(targetLocalBranch);
1291
- const safeSourceRef = this.escapeSingleQuotedShell(resolvedSource.resolvedRef);
1292
- 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 = `
1293
1266
  cd ${repoPath} &&
1294
1267
  TARGET_LOCAL='${safeTargetLocalBranch}' &&
1295
1268
  if sudo git show-ref --verify --quiet "refs/heads/$TARGET_LOCAL"; then
@@ -1304,178 +1277,174 @@ class GithubOperationsService {
1304
1277
  sudo git pull --ff-only origin "$TARGET_LOCAL" 2>/dev/null || true;
1305
1278
  fi
1306
1279
  `.replace(/\n\s+/g, ' ');
1307
- result = await executeShell(prepareBranchCommand);
1308
- if (!result.success) {
1309
- throw new Error(`Failed to prepare target branch for merge: ${result.error || result.output || 'Unknown error'}`);
1310
- }
1311
- const mergeCommand = `cd ${repoPath} && SOURCE_REF='${safeSourceRef}' && sudo git merge --no-ff --no-edit "$SOURCE_REF"`;
1312
- const mergeResult = await executeShell(mergeCommand);
1313
- if (!mergeResult.success) {
1314
- const conflictResult = await executeShell(`cd ${repoPath} && sudo git diff --name-only --diff-filter=U`);
1315
- const conflictFiles = (conflictResult.output || '')
1316
- .split('\n')
1317
- .map((line) => line.trim())
1318
- .filter((line) => line.length > 0);
1319
- if (conflictFiles.length > 0) {
1320
- logger.warn('Merge completed with conflicts', {
1321
- repoName,
1322
- sourceBranch: resolvedSource.displayBranch,
1323
- targetBranch: targetLocalBranch,
1324
- conflictFiles
1325
- });
1326
- return {
1327
- repoName,
1328
- sourceBranch: resolvedSource.displayBranch,
1329
- targetBranch: targetLocalBranch,
1330
- merged: false,
1331
- hasConflicts: true,
1332
- conflictFiles,
1333
- currentBranch: targetLocalBranch,
1334
- pushedToRemote: false,
1335
- mergedAt: new Date().toISOString(),
1336
- message: `Merge has conflicts. Resolve conflicts in target branch '${targetLocalBranch}'.`
1337
- };
1338
- }
1339
- throw new Error(`Failed to merge branches: ${mergeResult.error || mergeResult.output || 'Unknown error'}`);
1340
- }
1341
- 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"`);
1342
- if (!summaryResult.success) {
1343
- 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
+ };
1344
1311
  }
1345
- const summaryOutput = summaryResult.output || '';
1346
- const commitHashMatch = summaryOutput.match(/COMMIT_HASH:([^\n]+)/);
1347
- const currentBranchMatch = summaryOutput.match(/CURRENT_BRANCH:([^\n]+)/);
1348
- const commitHash = commitHashMatch ? commitHashMatch[1].trim() : '';
1349
- const currentBranch = currentBranchMatch ? currentBranchMatch[1].trim() : targetLocalBranch;
1350
- let pushedToRemote = false;
1351
- if (pushToRemote) {
1352
- const pushResult = await executeShell(`cd ${repoPath} && sudo git push origin '${safeTargetLocalBranch}'`);
1353
- if (!pushResult.success) {
1354
- throw new Error(`Merge succeeded but push failed: ${pushResult.error || pushResult.output || 'Unknown error'}`);
1355
- }
1356
- 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'}`);
1357
1328
  }
1358
- 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', {
1359
1363
  repoName,
1360
- sourceBranch: resolvedSource.displayBranch,
1361
- targetBranch: targetLocalBranch,
1362
- commitHash,
1363
- pushedToRemote
1364
+ error: restoreError instanceof Error ? restoreError.message : String(restoreError)
1364
1365
  });
1365
- if (socket) {
1366
- socket.emit('refresh_repo', { repoName });
1367
- }
1368
- return {
1369
- repoName,
1370
- sourceBranch: resolvedSource.displayBranch,
1371
- targetBranch: targetLocalBranch,
1372
- merged: true,
1373
- hasConflicts: false,
1374
- conflictFiles: [],
1375
- currentBranch,
1376
- commitHash,
1377
- pushedToRemote,
1378
- mergedAt: new Date().toISOString(),
1379
- message: pushedToRemote
1380
- ? `Merged '${resolvedSource.displayBranch}' into '${targetLocalBranch}' and pushed to remote.`
1381
- : `Merged '${resolvedSource.displayBranch}' into '${targetLocalBranch}' locally.`
1382
- };
1383
1366
  }
1384
- finally {
1385
- try {
1386
- await executeShell(`cd ${repoPath} && git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git`);
1387
- }
1388
- catch (restoreError) {
1389
- logger.error('Failed to restore unauthenticated remote URL after merge operation', {
1390
- repoName,
1391
- error: restoreError instanceof Error ? restoreError.message : String(restoreError)
1392
- });
1393
- }
1394
- }
1395
- }); // end withRepoLock
1367
+ }
1396
1368
  }
1397
1369
  async checkPullRequestExists(repoName, targetBranch) {
1398
1370
  const repoPath = `${tool_server_1.folderPath}/${repoName}`;
1399
1371
  const installationToken = await fetchInstallationToken();
1400
1372
  const gitOrgName = await getGithubOrganizationName();
1401
- return withRepoLock(repoName, async () => {
1402
- try {
1403
- logger.info('Checking if PR exists', { repoName, targetBranch });
1404
- // Get current branch and commits ahead
1405
- const combinedCommand = `
1373
+ try {
1374
+ logger.info('Checking if PR exists', { repoName, targetBranch });
1375
+ // Get current branch and commits ahead
1376
+ const combinedCommand = `
1406
1377
  cd ${repoPath} &&
1407
- rm -f .git/config.lock &&
1408
- 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 &&
1409
1379
  HEAD_BRANCH=$(sudo git rev-parse --abbrev-ref HEAD) &&
1410
1380
  sudo git fetch origin --prune 2>/dev/null || true &&
1411
1381
  sudo git fetch origin ${targetBranch} 2>/dev/null || true &&
1412
1382
  COMMITS_AHEAD=$(sudo git rev-list --count origin/${targetBranch}..$HEAD_BRANCH 2>/dev/null || echo 0) &&
1413
- git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git &&
1383
+ sudo git remote set-url origin https://github.com/${gitOrgName}/${repoName}.git &&
1414
1384
  echo "HEAD_BRANCH:$HEAD_BRANCH" &&
1415
1385
  echo "COMMITS_AHEAD:$COMMITS_AHEAD"
1416
1386
  `.replace(/\n\s+/g, ' ');
1417
- const result = await executeShell(combinedCommand);
1418
- // Check for command execution failure
1419
- if (!result.success) {
1420
- logger.error('Failed to check PR status', {
1421
- repoName,
1422
- error: result.error,
1423
- code: result.code,
1424
- output: result.output
1425
- });
1426
- throw new Error(`Failed to check PR status: ${result.error}`);
1427
- }
1428
- const output = result.output || '';
1429
- const headBranchMatch = output.match(/HEAD_BRANCH:([^\n]+)/);
1430
- const commitsAheadMatch = output.match(/COMMITS_AHEAD:(\d+)/);
1431
- const headBranch = headBranchMatch ? headBranchMatch[1].trim() : 'unknown';
1432
- const commitsAhead = commitsAheadMatch ? parseInt(commitsAheadMatch[1]) : 0;
1433
- logger.info('Branch info retrieved', { headBranch, commitsAhead });
1434
- // Check for existing PR - using 'octokit' package (already installed)
1435
- const { Octokit } = require('octokit');
1436
- const octokit = new Octokit({
1437
- 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
1438
1395
  });
1439
- const existingPRs = await octokit.rest.pulls.list({
1440
- owner: gitOrgName,
1441
- repo: repoName,
1442
- head: `${gitOrgName}:${headBranch}`,
1443
- base: targetBranch,
1444
- 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
1445
1422
  });
1446
- if (existingPRs.data.length > 0) {
1447
- const existingPR = existingPRs.data[0];
1448
- logger.info('Pull request exists', {
1449
- repoName,
1450
- prNumber: existingPR.number,
1451
- prUrl: existingPR.html_url
1452
- });
1453
- return {
1454
- repoName,
1455
- headBranch,
1456
- targetBranch,
1457
- prExists: true,
1458
- prNumber: existingPR.number,
1459
- prUrl: existingPR.html_url,
1460
- prTitle: existingPR.title,
1461
- commitsAhead,
1462
- createdAt: existingPR.created_at
1463
- };
1464
- }
1465
- else {
1466
- logger.info('No pull request found', { repoName, headBranch, targetBranch });
1467
- return {
1468
- repoName,
1469
- headBranch,
1470
- targetBranch,
1471
- prExists: false,
1472
- commitsAhead
1473
- };
1474
- }
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
+ };
1475
1434
  }
1476
- 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
+ };
1477
1444
  }
1478
- }); // end withRepoLock
1445
+ }
1446
+ finally {
1447
+ }
1479
1448
  }
1480
1449
  /**
1481
1450
  * Get commit list with commit IDs for a branch
@@ -1974,7 +1943,7 @@ class GithubOperationsService {
1974
1943
  /**
1975
1944
  * Helper method to get merge conflicts
1976
1945
  */
1977
- async getMergeConflicts(repoName) {
1946
+ async getMergeConflicts(socket, repoName) {
1978
1947
  return new Promise((resolve) => {
1979
1948
  this.getMergeConflictsImplementation(repoName, tool_server_1.folderPath).then((mergeData) => {
1980
1949
  resolve(mergeData);