staklink 0.3.4 → 0.3.6

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.
@@ -25719,7 +25719,16 @@ var PM2Manager = class {
25719
25719
  async execCommand(command, env) {
25720
25720
  const execOpts = {
25721
25721
  cwd: this.cwd,
25722
- ...env && { env: { ...process.env, ...env } }
25722
+ shell: "/bin/bash",
25723
+ // Use bash explicitly since /bin/sh might have issues
25724
+ env: {
25725
+ ...process.env,
25726
+ // Important: inherit ALL environment
25727
+ PATH: process.env.PATH || "/usr/local/bin:/usr/bin:/bin",
25728
+ HOME: process.env.HOME || "/root",
25729
+ ...env
25730
+ // Then override with custom env
25731
+ }
25723
25732
  };
25724
25733
  const { stdout, stderr } = await exec2(command, execOpts);
25725
25734
  return stdout || stderr;
@@ -33892,7 +33901,7 @@ var SSEManager = class {
33892
33901
  var sseManager = new SSEManager();
33893
33902
 
33894
33903
  // src/proxy/version.ts
33895
- var VERSION = "0.3.4";
33904
+ var VERSION = "0.3.6";
33896
33905
 
33897
33906
  // node_modules/uuid/dist/esm/stringify.js
33898
33907
  var byteToHex = [];
@@ -79570,6 +79579,387 @@ function gitleaksProtect() {
79570
79579
  }
79571
79580
  }
79572
79581
 
79582
+ // src/proxy/gh.ts
79583
+ var import_child_process4 = require("child_process");
79584
+ var import_util10 = require("util");
79585
+ var execAsync = (0, import_util10.promisify)(import_child_process4.exec);
79586
+ var GitHubCLI = class {
79587
+ token;
79588
+ branch;
79589
+ owner;
79590
+ repo;
79591
+ constructor(token, branch) {
79592
+ this.token = token;
79593
+ this.branch = branch;
79594
+ }
79595
+ /**
79596
+ * Initialize and get repo info from git
79597
+ */
79598
+ async init() {
79599
+ try {
79600
+ const { stdout } = await this.execGH("repo view --json owner,name");
79601
+ const data = JSON.parse(stdout);
79602
+ this.owner = data.owner.login;
79603
+ this.repo = data.name;
79604
+ } catch (error82) {
79605
+ throw new Error(`Failed to initialize: ${error82}`);
79606
+ }
79607
+ }
79608
+ /**
79609
+ * Execute gh CLI command with authentication
79610
+ */
79611
+ async execGH(command) {
79612
+ const fullCommand = `gh ${command}`;
79613
+ try {
79614
+ return await execAsync(fullCommand, {
79615
+ env: {
79616
+ ...process.env,
79617
+ GH_TOKEN: this.token,
79618
+ GITHUB_TOKEN: this.token
79619
+ }
79620
+ });
79621
+ } catch (error82) {
79622
+ throw new Error(
79623
+ `gh command failed: ${error82.message}
79624
+ Stderr: ${error82.stderr}`
79625
+ );
79626
+ }
79627
+ }
79628
+ /**
79629
+ * Sync version of execGH for simple checks
79630
+ */
79631
+ execGHSync(command) {
79632
+ const fullCommand = `gh ${command}`;
79633
+ try {
79634
+ return (0, import_child_process4.execSync)(fullCommand, {
79635
+ encoding: "utf-8",
79636
+ env: {
79637
+ ...process.env,
79638
+ GH_TOKEN: this.token,
79639
+ GITHUB_TOKEN: this.token
79640
+ }
79641
+ });
79642
+ } catch (error82) {
79643
+ throw new Error(
79644
+ `gh command failed: ${error82.message}
79645
+ Stderr: ${error82.stderr}`
79646
+ );
79647
+ }
79648
+ }
79649
+ /**
79650
+ * Check if the branch exists
79651
+ */
79652
+ async branchExists() {
79653
+ try {
79654
+ const { stdout } = await execAsync(
79655
+ "git rev-parse --verify --quiet " + this.branch
79656
+ );
79657
+ return true;
79658
+ } catch {
79659
+ return false;
79660
+ }
79661
+ }
79662
+ /**
79663
+ * Get current branch name
79664
+ */
79665
+ async getCurrentBranch() {
79666
+ const { stdout } = await execAsync("git rev-parse --abbrev-ref HEAD");
79667
+ return stdout.trim();
79668
+ }
79669
+ /**
79670
+ * Checkout the branch
79671
+ */
79672
+ async checkoutBranch() {
79673
+ try {
79674
+ await execAsync(`git checkout ${this.branch}`);
79675
+ } catch (error82) {
79676
+ throw new Error(`Failed to checkout branch ${this.branch}: ${error82}`);
79677
+ }
79678
+ }
79679
+ /**
79680
+ * Create a new PR from the branch
79681
+ */
79682
+ async createPR(options) {
79683
+ const args = [
79684
+ "pr",
79685
+ "create",
79686
+ "--head",
79687
+ this.branch,
79688
+ "--title",
79689
+ `"${options.title}"`
79690
+ ];
79691
+ if (options.body) {
79692
+ args.push("--body", `"${options.body}"`);
79693
+ }
79694
+ if (options.base) {
79695
+ args.push("--base", options.base);
79696
+ }
79697
+ if (options.draft) {
79698
+ args.push("--draft");
79699
+ }
79700
+ if (options.assignees?.length) {
79701
+ args.push("--assignee", options.assignees.join(","));
79702
+ }
79703
+ if (options.reviewers?.length) {
79704
+ args.push("--reviewer", options.reviewers.join(","));
79705
+ }
79706
+ if (options.labels?.length) {
79707
+ args.push("--label", options.labels.join(","));
79708
+ }
79709
+ try {
79710
+ const { stdout } = await this.execGH(args.join(" "));
79711
+ const prUrl = stdout.trim();
79712
+ const prNumber = parseInt(prUrl.split("/").pop() || "0");
79713
+ return await this.getPR(prNumber);
79714
+ } catch (error82) {
79715
+ throw new Error(`Failed to create PR: ${error82}`);
79716
+ }
79717
+ }
79718
+ /**
79719
+ * Find PR by branch name
79720
+ */
79721
+ async findPRByBranch() {
79722
+ try {
79723
+ const { stdout } = await this.execGH(
79724
+ `pr list --head ${this.branch} --json number,title,body,state,url,headRefName,baseRefName,mergeable,mergeStateStatus,isDraft,author,createdAt,updatedAt --limit 1`
79725
+ );
79726
+ const prs = JSON.parse(stdout);
79727
+ if (prs.length === 0) {
79728
+ return null;
79729
+ }
79730
+ const pr = prs[0];
79731
+ return {
79732
+ number: pr.number,
79733
+ title: pr.title,
79734
+ body: pr.body,
79735
+ state: pr.state,
79736
+ url: pr.url,
79737
+ headRefName: pr.headRefName,
79738
+ baseRefName: pr.baseRefName,
79739
+ mergeable: pr.mergeable,
79740
+ mergeStateStatus: pr.mergeStateStatus,
79741
+ isDraft: pr.isDraft,
79742
+ author: pr.author.login,
79743
+ createdAt: pr.createdAt,
79744
+ updatedAt: pr.updatedAt
79745
+ };
79746
+ } catch (error82) {
79747
+ throw new Error(`Failed to find PR: ${error82}`);
79748
+ }
79749
+ }
79750
+ /**
79751
+ * Get PR details by number
79752
+ */
79753
+ async getPR(prNumber) {
79754
+ try {
79755
+ const { stdout } = await this.execGH(
79756
+ `pr view ${prNumber} --json number,title,body,state,url,headRefName,baseRefName,mergeable,mergeStateStatus,isDraft,author,createdAt,updatedAt`
79757
+ );
79758
+ const pr = JSON.parse(stdout);
79759
+ return {
79760
+ number: pr.number,
79761
+ title: pr.title,
79762
+ body: pr.body,
79763
+ state: pr.state,
79764
+ url: pr.url,
79765
+ headRefName: pr.headRefName,
79766
+ baseRefName: pr.baseRefName,
79767
+ mergeable: pr.mergeable,
79768
+ mergeStateStatus: pr.mergeStateStatus,
79769
+ isDraft: pr.isDraft,
79770
+ author: pr.author.login,
79771
+ createdAt: pr.createdAt,
79772
+ updatedAt: pr.updatedAt
79773
+ };
79774
+ } catch (error82) {
79775
+ throw new Error(`Failed to get PR #${prNumber}: ${error82}`);
79776
+ }
79777
+ }
79778
+ /**
79779
+ * Check if PR is auto-mergeable
79780
+ */
79781
+ async isPRMergeable(prNumber) {
79782
+ const pr = await this.getPR(prNumber);
79783
+ const isMergeable = pr.mergeable === "MERGEABLE" && pr.mergeStateStatus === "CLEAN" && pr.state === "OPEN" && !pr.isDraft;
79784
+ let reason;
79785
+ if (!isMergeable) {
79786
+ if (pr.isDraft) {
79787
+ reason = "PR is in draft state";
79788
+ } else if (pr.mergeable === "CONFLICTING") {
79789
+ reason = "PR has merge conflicts";
79790
+ } else if (pr.mergeStateStatus === "BLOCKED") {
79791
+ reason = "PR is blocked by required checks";
79792
+ } else if (pr.mergeStateStatus === "BEHIND") {
79793
+ reason = "Branch is behind base branch";
79794
+ } else if (pr.mergeStateStatus === "UNSTABLE") {
79795
+ reason = "Some checks are failing";
79796
+ } else if (pr.mergeable === "UNKNOWN") {
79797
+ reason = "Mergeability is still being calculated";
79798
+ } else {
79799
+ reason = `Status: ${pr.mergeStateStatus}`;
79800
+ }
79801
+ }
79802
+ return { mergeable: isMergeable, reason };
79803
+ }
79804
+ /**
79805
+ * Merge a PR
79806
+ */
79807
+ async mergePR(prNumber, options) {
79808
+ const {
79809
+ method = "squash",
79810
+ deleteBranch = true,
79811
+ auto = false,
79812
+ commitTitle,
79813
+ commitBody
79814
+ } = options || {};
79815
+ const args = ["pr", "merge", prNumber.toString()];
79816
+ if (auto) {
79817
+ args.push("--auto");
79818
+ }
79819
+ switch (method) {
79820
+ case "squash":
79821
+ args.push("--squash");
79822
+ break;
79823
+ case "rebase":
79824
+ args.push("--rebase");
79825
+ break;
79826
+ case "merge":
79827
+ args.push("--merge");
79828
+ break;
79829
+ }
79830
+ if (deleteBranch) {
79831
+ args.push("--delete-branch");
79832
+ }
79833
+ if (commitTitle) {
79834
+ args.push("--subject", `"${commitTitle}"`);
79835
+ }
79836
+ if (commitBody) {
79837
+ args.push("--body", `"${commitBody}"`);
79838
+ }
79839
+ try {
79840
+ const { stdout } = await this.execGH(args.join(" "));
79841
+ console.log(stdout);
79842
+ } catch (error82) {
79843
+ throw new Error(`Failed to merge PR #${prNumber}: ${error82}`);
79844
+ }
79845
+ }
79846
+ /**
79847
+ * Check if PR is mergeable and merge if ready
79848
+ */
79849
+ async checkAndMerge(prNumber, options) {
79850
+ const { mergeable, reason } = await this.isPRMergeable(prNumber);
79851
+ if (!mergeable) {
79852
+ console.log(`PR #${prNumber} is not mergeable: ${reason}`);
79853
+ return false;
79854
+ }
79855
+ await this.mergePR(prNumber, options);
79856
+ return true;
79857
+ }
79858
+ /**
79859
+ * Wait for PR to be mergeable and then merge
79860
+ */
79861
+ async waitAndMerge(prNumber, options) {
79862
+ const {
79863
+ maxWaitTime = 3e5,
79864
+ // 5 minutes
79865
+ checkInterval = 1e4,
79866
+ // 10 seconds
79867
+ ...mergeOptions
79868
+ } = options || {};
79869
+ const startTime = Date.now();
79870
+ while (Date.now() - startTime < maxWaitTime) {
79871
+ const { mergeable, reason } = await this.isPRMergeable(prNumber);
79872
+ if (mergeable) {
79873
+ await this.mergePR(prNumber, mergeOptions);
79874
+ return true;
79875
+ }
79876
+ if (reason?.includes("conflict")) {
79877
+ throw new Error(`PR #${prNumber} has merge conflicts`);
79878
+ }
79879
+ console.log(`Waiting for PR #${prNumber} to be mergeable... (${reason})`);
79880
+ await new Promise((resolve3) => setTimeout(resolve3, checkInterval));
79881
+ }
79882
+ throw new Error(
79883
+ `Timeout: PR #${prNumber} did not become mergeable within ${maxWaitTime}ms`
79884
+ );
79885
+ }
79886
+ /**
79887
+ * Add comment to PR
79888
+ */
79889
+ async addComment(prNumber, comment) {
79890
+ try {
79891
+ await this.execGH(`pr comment ${prNumber} --body "${comment}"`);
79892
+ } catch (error82) {
79893
+ throw new Error(`Failed to add comment to PR #${prNumber}: ${error82}`);
79894
+ }
79895
+ }
79896
+ /**
79897
+ * Update PR title and/or body
79898
+ */
79899
+ async updatePR(prNumber, updates) {
79900
+ const args = ["pr", "edit", prNumber.toString()];
79901
+ if (updates.title) {
79902
+ args.push("--title", `"${updates.title}"`);
79903
+ }
79904
+ if (updates.body) {
79905
+ args.push("--body", `"${updates.body}"`);
79906
+ }
79907
+ try {
79908
+ await this.execGH(args.join(" "));
79909
+ } catch (error82) {
79910
+ throw new Error(`Failed to update PR #${prNumber}: ${error82}`);
79911
+ }
79912
+ }
79913
+ /**
79914
+ * Close PR
79915
+ */
79916
+ async closePR(prNumber, comment) {
79917
+ try {
79918
+ if (comment) {
79919
+ await this.addComment(prNumber, comment);
79920
+ }
79921
+ await this.execGH(`pr close ${prNumber}`);
79922
+ } catch (error82) {
79923
+ throw new Error(`Failed to close PR #${prNumber}: ${error82}`);
79924
+ }
79925
+ }
79926
+ /**
79927
+ * Reopen PR
79928
+ */
79929
+ async reopenPR(prNumber) {
79930
+ try {
79931
+ await this.execGH(`pr reopen ${prNumber}`);
79932
+ } catch (error82) {
79933
+ throw new Error(`Failed to reopen PR #${prNumber}: ${error82}`);
79934
+ }
79935
+ }
79936
+ /**
79937
+ * Get PR checks status
79938
+ */
79939
+ async getPRChecks(prNumber) {
79940
+ try {
79941
+ const { stdout } = await this.execGH(
79942
+ `pr checks ${prNumber} --json name,state,conclusion`
79943
+ );
79944
+ return JSON.parse(stdout);
79945
+ } catch (error82) {
79946
+ throw new Error(`Failed to get PR checks for #${prNumber}: ${error82}`);
79947
+ }
79948
+ }
79949
+ /**
79950
+ * Get repo info
79951
+ */
79952
+ getRepoInfo() {
79953
+ return { owner: this.owner, repo: this.repo };
79954
+ }
79955
+ /**
79956
+ * Get branch name
79957
+ */
79958
+ getBranch() {
79959
+ return this.branch;
79960
+ }
79961
+ };
79962
+
79573
79963
  // src/proxy/server.ts
79574
79964
  var PORT = parseInt(process.env.STAKLINK_PORT || "15552") || 15552;
79575
79965
  var VSCODE_EXTENSION_URL = `http://localhost:${PORT + 1}`;
@@ -79889,7 +80279,9 @@ async function startProxyServer() {
79889
80279
  console.log(`===> POST /push`);
79890
80280
  const code = req.body;
79891
80281
  const repos = getReposWithChangedFiles(code);
80282
+ const createPR = req.query.pr === "true";
79892
80283
  const commits = [];
80284
+ const prs = {};
79893
80285
  for (const r of repos) {
79894
80286
  console.log(`=> PUSH to ${r.url}`);
79895
80287
  const repo = await NewRepo(r.url);
@@ -79900,8 +80292,41 @@ async function startProxyServer() {
79900
80292
  }
79901
80293
  const link = await repo.getLatestCommitLink();
79902
80294
  commits.push(link);
80295
+ if (createPR && code.git_credentials?.auth_data?.token) {
80296
+ try {
80297
+ console.log(`=> Creating PR for ${r.url}`);
80298
+ const repoName = getRepoNameFromUrl(r.url);
80299
+ const gh = new GitHubCLI(
80300
+ code.git_credentials.auth_data.token,
80301
+ r.branch_name
80302
+ );
80303
+ await gh.init();
80304
+ const existingPR = await gh.findPRByBranch();
80305
+ if (existingPR) {
80306
+ console.log(`=> PR already exists: ${existingPR.url}`);
80307
+ prs[repoName] = existingPR.url;
80308
+ } else {
80309
+ const pr = await gh.createPR({
80310
+ title: r.commit_name || `Changes from ${r.branch_name}`,
80311
+ body: `Automated PR from branch ${r.branch_name}`,
80312
+ base: r.base_branch || "main",
80313
+ draft: false
80314
+ });
80315
+ console.log(`=> PR created: ${pr.url}`);
80316
+ prs[repoName] = pr.url;
80317
+ }
80318
+ } catch (prError) {
80319
+ console.error(`=> Failed to create PR for ${r.url}:`, prError);
80320
+ const repoName = getRepoNameFromUrl(r.url);
80321
+ prs[repoName] = `Error: ${prError instanceof Error ? prError.message : String(prError)}`;
80322
+ }
80323
+ }
80324
+ }
80325
+ const response = { success: true, commits };
80326
+ if (createPR) {
80327
+ response.prs = prs;
79903
80328
  }
79904
- res.json({ success: true, commits });
80329
+ res.json(response);
79905
80330
  } catch (e) {
79906
80331
  fail(res, e);
79907
80332
  }
@@ -3693,7 +3693,16 @@ var PM2Manager = class {
3693
3693
  async execCommand(command, env) {
3694
3694
  const execOpts = {
3695
3695
  cwd: this.cwd,
3696
- ...env && { env: { ...process.env, ...env } }
3696
+ shell: "/bin/bash",
3697
+ // Use bash explicitly since /bin/sh might have issues
3698
+ env: {
3699
+ ...process.env,
3700
+ // Important: inherit ALL environment
3701
+ PATH: process.env.PATH || "/usr/local/bin:/usr/bin:/bin",
3702
+ HOME: process.env.HOME || "/root",
3703
+ ...env
3704
+ // Then override with custom env
3705
+ }
3697
3706
  };
3698
3707
  const { stdout, stderr } = await exec2(command, execOpts);
3699
3708
  return stdout || stderr;
@@ -10896,7 +10905,7 @@ var glob = Object.assign(glob_, {
10896
10905
  glob.glob = glob;
10897
10906
 
10898
10907
  // src/proxy/version.ts
10899
- var VERSION = "0.3.2";
10908
+ var VERSION = "0.3.6";
10900
10909
 
10901
10910
  // src/cli.ts
10902
10911
  var STAKLINK_PROXY = "staklink-proxy";
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "staklink",
3
3
  "displayName": "staklink",
4
4
  "description": "staklink process manager",
5
- "version": "0.3.4",
5
+ "version": "0.3.6",
6
6
  "type": "module",
7
7
  "publisher": "stakwork",
8
8
  "engines": {