staklink 0.4.10 → 0.4.11

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.
@@ -60768,6 +60768,102 @@ async function NewRepo(url3) {
60768
60768
  }
60769
60769
  return new Repo(repoLocation);
60770
60770
  }
60771
+ function buildAuthenticatedUrl(remoteUrl, credentials) {
60772
+ let url3 = remoteUrl;
60773
+ switch (credentials.provider) {
60774
+ case "github":
60775
+ url3 = url3.replace(
60776
+ "https://github.com/",
60777
+ `https://${credentials.auth_data.token}@github.com/`
60778
+ ).replace(
60779
+ /https:\/\/.*@github\.com\//,
60780
+ `https://${credentials.auth_data.token}@github.com/`
60781
+ );
60782
+ if (url3.startsWith("git@github.com:")) {
60783
+ const repoPath = url3.replace("git@github.com:", "").replace(/\.git$/, "");
60784
+ url3 = `https://${credentials.auth_data.token}@github.com/${repoPath}.git`;
60785
+ }
60786
+ break;
60787
+ case "gitlab":
60788
+ url3 = url3.replace(
60789
+ "https://gitlab.com/",
60790
+ `https://oauth2:${credentials.auth_data.token}@gitlab.com/`
60791
+ ).replace(
60792
+ /https:\/\/.*@gitlab\.com\//,
60793
+ `https://oauth2:${credentials.auth_data.token}@gitlab.com/`
60794
+ );
60795
+ if (url3.startsWith("git@gitlab.com:")) {
60796
+ const repoPath = url3.replace("git@gitlab.com:", "").replace(/\.git$/, "");
60797
+ url3 = `https://oauth2:${credentials.auth_data.token}@gitlab.com/${repoPath}.git`;
60798
+ }
60799
+ break;
60800
+ case "bitbucket":
60801
+ url3 = url3.replace(
60802
+ "https://bitbucket.org/",
60803
+ `https://${credentials.auth_data.username}:${credentials.auth_data.token}@bitbucket.org/`
60804
+ ).replace(
60805
+ /https:\/\/.*@bitbucket\.org\//,
60806
+ `https://${credentials.auth_data.username}:${credentials.auth_data.token}@bitbucket.org/`
60807
+ );
60808
+ if (url3.startsWith("git@bitbucket.org:")) {
60809
+ const repoPath = url3.replace("git@bitbucket.org:", "").replace(/\.git$/, "");
60810
+ url3 = `https://${credentials.auth_data.username}:${credentials.auth_data.token}@bitbucket.org/${repoPath}.git`;
60811
+ }
60812
+ break;
60813
+ }
60814
+ return url3;
60815
+ }
60816
+ async function setupGlobalGitCredentials(repoCwd, credentials) {
60817
+ const run = (cmd) => exec4(cmd, {
60818
+ cwd: repoCwd,
60819
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
60820
+ });
60821
+ const { stdout: origUrl } = await run(
60822
+ "git config --get remote.origin.url"
60823
+ );
60824
+ let origName = null;
60825
+ let origEmail = null;
60826
+ try {
60827
+ origName = (await run("git config --global user.name")).stdout.trim();
60828
+ } catch {
60829
+ }
60830
+ try {
60831
+ origEmail = (await run("git config --global user.email")).stdout.trim();
60832
+ } catch {
60833
+ }
60834
+ const authUrl = buildAuthenticatedUrl(origUrl.trim(), credentials);
60835
+ await run(`git remote set-url origin "${authUrl}"`);
60836
+ await run(
60837
+ `git config --global user.name "${credentials.auth_data.username}"`
60838
+ );
60839
+ await run(
60840
+ `git config --global user.email "${credentials.auth_data.username}@users.noreply.github.com"`
60841
+ );
60842
+ const env2 = {
60843
+ GH_TOKEN: credentials.auth_data.token,
60844
+ GITHUB_TOKEN: credentials.auth_data.token
60845
+ };
60846
+ const teardown = async () => {
60847
+ try {
60848
+ await run(`git remote set-url origin "${origUrl.trim()}"`);
60849
+ if (origName) {
60850
+ await run(`git config --global user.name "${origName}"`);
60851
+ } else {
60852
+ await run("git config --global --unset user.name").catch(() => {
60853
+ });
60854
+ }
60855
+ if (origEmail) {
60856
+ await run(`git config --global user.email "${origEmail}"`);
60857
+ } else {
60858
+ await run("git config --global --unset user.email").catch(() => {
60859
+ });
60860
+ }
60861
+ } catch (e) {
60862
+ error("Error tearing down global git credentials:", e);
60863
+ }
60864
+ };
60865
+ return { env: env2, teardown };
60866
+ }
60771
60867
 
60772
60868
  // src/proxy/sse.ts
60773
60869
  var SSEManager = class {
@@ -60814,7 +60910,7 @@ var SSEManager = class {
60814
60910
  var sseManager = new SSEManager();
60815
60911
 
60816
60912
  // src/proxy/version.ts
60817
- var VERSION = "0.4.10";
60913
+ var VERSION = "0.4.11";
60818
60914
 
60819
60915
  // node_modules/uuid/dist/esm/stringify.js
60820
60916
  var byteToHex = [];
@@ -141412,10 +141508,12 @@ ${diff.trim()}`);
141412
141508
  }
141413
141509
  }
141414
141510
  }
141415
- const response = { success: true, commits, branches };
141511
+ const hasPrErrors = Object.keys(prErrors).length > 0;
141512
+ const success3 = !(createPR && hasPrErrors);
141513
+ const response = { success: success3, commits, branches };
141416
141514
  if (createPR) {
141417
141515
  response.prs = prs;
141418
- if (Object.keys(prErrors).length > 0) {
141516
+ if (hasPrErrors) {
141419
141517
  response.prErrors = prErrors;
141420
141518
  }
141421
141519
  }
@@ -141525,6 +141623,152 @@ async function handleCreateRepo(req, res) {
141525
141623
  });
141526
141624
  }
141527
141625
  }
141626
+ function buildPushPrompt(opts) {
141627
+ const lines = [
141628
+ `You are in the git repo at: ${opts.repoPath}`,
141629
+ "",
141630
+ "Perform the following git operations. If any step fails, diagnose the problem and fix it (resolve merge conflicts, pull and rebase, etc). Be resilient - the goal is to get code pushed successfully.",
141631
+ "",
141632
+ "1. Check the current branch. If you are on main or master, create a new feature branch and switch to it. Otherwise stay on the current branch.",
141633
+ "2. Check git status. If there are uncommitted changes, stage them (git add .) and commit with a short descriptive message. If everything is already committed, skip this step.",
141634
+ "3. Push the current branch to origin. If the push is rejected (non-fast-forward, remote has new work, etc), pull with rebase and try again. If there are merge conflicts during rebase, resolve them sensibly and continue."
141635
+ ];
141636
+ if (opts.createPR) {
141637
+ let step = 4;
141638
+ lines.push(
141639
+ `${step}. Create a pull request from the current branch to "${opts.baseBranch}" using the gh CLI. The PR title should be derived from the commit message. If a PR already exists for this branch, skip creation.`
141640
+ );
141641
+ if (opts.label) {
141642
+ step++;
141643
+ lines.push(
141644
+ `${step}. Add the label "${opts.label}" to the PR.`
141645
+ );
141646
+ }
141647
+ step++;
141648
+ lines.push(
141649
+ `${step}. Ensure the PR branch is up to date with "${opts.baseBranch}". Run: git fetch origin ${opts.baseBranch} && git merge origin/${opts.baseBranch}. If there are conflicts, resolve them, commit, and push again. This is equivalent to GitHub's "Update branch" button.`
141650
+ );
141651
+ if (opts.autoMerge) {
141652
+ step++;
141653
+ lines.push(
141654
+ `${step}. Enable auto-merge (squash) on the PR using: gh pr merge <number> --auto --squash --delete-branch`
141655
+ );
141656
+ }
141657
+ }
141658
+ lines.push(
141659
+ "",
141660
+ "IMPORTANT:",
141661
+ "- Do NOT ask for confirmation. Just do it.",
141662
+ "- If push fails, try pulling with rebase first. If that fails with conflicts, resolve them and continue.",
141663
+ "- Output the PR URL if you created one.",
141664
+ "- Output the branch name you ended up on.",
141665
+ "- Output the commit hash after pushing."
141666
+ );
141667
+ return lines.join("\n");
141668
+ }
141669
+ async function handleAgentPush(req, res) {
141670
+ const request_id = startReq();
141671
+ try {
141672
+ const code = req.body;
141673
+ const repos = getReposWithChangedFiles(code);
141674
+ const createPR = req.query.pr === "true";
141675
+ const autoMerge = req.query.automerge === "true";
141676
+ const label = req.query.label;
141677
+ if (!repos.length) {
141678
+ finishReq(request_id, {
141679
+ success: true,
141680
+ commits: [],
141681
+ branches: {}
141682
+ });
141683
+ res.json({ request_id, status: "pending" });
141684
+ return;
141685
+ }
141686
+ if (!code.git_credentials?.auth_data?.token) {
141687
+ failReq(request_id, new Error("git_credentials with token required"));
141688
+ res.json({ request_id, status: "pending" });
141689
+ return;
141690
+ }
141691
+ const apiKey = code.apiKey || process.env.ANTHROPIC_API_KEY || "";
141692
+ (async () => {
141693
+ const results = { commits: [], prs: {}, branches: {} };
141694
+ for (const r of repos) {
141695
+ const repo = await NewRepo(r.url);
141696
+ const repoPath = repo.printcwd();
141697
+ const repoName = getRepoNameFromUrl(r.url);
141698
+ const baseBranch = r.base_branch || "main";
141699
+ const { env: credEnv, teardown } = await setupGlobalGitCredentials(
141700
+ repoPath,
141701
+ code.git_credentials
141702
+ );
141703
+ try {
141704
+ const prompt = buildPushPrompt({
141705
+ repoPath,
141706
+ createPR,
141707
+ autoMerge,
141708
+ label,
141709
+ baseBranch
141710
+ });
141711
+ log(`=> agent/push prompt for ${repoName}:
141712
+ ${prompt}`);
141713
+ const prevGhToken = process.env.GH_TOKEN;
141714
+ const prevGithubToken = process.env.GITHUB_TOKEN;
141715
+ process.env.GH_TOKEN = credEnv.GH_TOKEN;
141716
+ process.env.GITHUB_TOKEN = credEnv.GITHUB_TOKEN;
141717
+ const agentFn = chooseAgent("goose");
141718
+ let result;
141719
+ try {
141720
+ result = await agentFn({
141721
+ prompt,
141722
+ apiKey,
141723
+ cwd: repoPath,
141724
+ session: code.session,
141725
+ model: code.model
141726
+ });
141727
+ } finally {
141728
+ if (prevGhToken !== void 0) {
141729
+ process.env.GH_TOKEN = prevGhToken;
141730
+ } else {
141731
+ delete process.env.GH_TOKEN;
141732
+ }
141733
+ if (prevGithubToken !== void 0) {
141734
+ process.env.GITHUB_TOKEN = prevGithubToken;
141735
+ } else {
141736
+ delete process.env.GITHUB_TOKEN;
141737
+ }
141738
+ }
141739
+ const output = typeof result === "string" ? result : result.output;
141740
+ results.commits.push(output);
141741
+ const branchMatch = output.match(
141742
+ /(?:on branch|switched to|branch)\s+[`'"]?([^\s`'"]+)/i
141743
+ );
141744
+ if (branchMatch) {
141745
+ results.branches[repoName] = branchMatch[1];
141746
+ }
141747
+ const prMatch = output.match(
141748
+ /https:\/\/github\.com\/[^\s]+\/pull\/\d+/
141749
+ );
141750
+ if (prMatch) {
141751
+ results.prs[repoName] = prMatch[0];
141752
+ }
141753
+ } finally {
141754
+ await teardown();
141755
+ }
141756
+ }
141757
+ finishReq(request_id, {
141758
+ success: true,
141759
+ ...results
141760
+ });
141761
+ })().catch((error88) => {
141762
+ error("agent/push error:", error88);
141763
+ failReq(request_id, error88);
141764
+ });
141765
+ res.json({ request_id, status: "pending" });
141766
+ } catch (e) {
141767
+ error("agent/push setup error:", e);
141768
+ failReq(request_id, e);
141769
+ res.json({ request_id, status: "pending" });
141770
+ }
141771
+ }
141528
141772
 
141529
141773
  // src/proxy/code_actions.ts
141530
141774
  var fs14 = __toESM(require("fs/promises"), 1);
@@ -142263,6 +142507,35 @@ async function startProxyServer() {
142263
142507
  }
142264
142508
  clearInterval(keepAliveInterval);
142265
142509
  });
142510
+ app.get("/ide-auth", async (req, res) => {
142511
+ const fallback = () => res.redirect(302, "/");
142512
+ try {
142513
+ const { token, expires } = req.query;
142514
+ const password = process.env.CODE_SERVER_PASSWORD;
142515
+ const port = process.env.CODE_SERVER_PORT || "8444";
142516
+ if (!token || !expires || !password) return fallback();
142517
+ const expiresNum = parseInt(expires, 10);
142518
+ if (isNaN(expiresNum) || Math.floor(Date.now() / 1e3) > expiresNum) return fallback();
142519
+ const crypto3 = await import("crypto");
142520
+ const expected = crypto3.createHmac("sha256", password).update(`ide-auth:${expires}`).digest("hex");
142521
+ const tA = Buffer.from(token);
142522
+ const tB = Buffer.from(expected);
142523
+ if (tA.length !== tB.length || !crypto3.timingSafeEqual(tA, tB)) return fallback();
142524
+ const loginRes = await fetch(`http://localhost:${port}/login`, {
142525
+ method: "POST",
142526
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
142527
+ body: new URLSearchParams({ password }).toString(),
142528
+ redirect: "manual"
142529
+ });
142530
+ const setCookieHeader = loginRes.headers.get("set-cookie") || "";
142531
+ const keyMatch = setCookieHeader.match(/(?:^|,)\s*key=([^;,]+)/);
142532
+ if (!keyMatch) return fallback();
142533
+ res.setHeader("Set-Cookie", `key=${keyMatch[1]}; Path=/; SameSite=Lax; HttpOnly`);
142534
+ res.redirect(302, "/");
142535
+ } catch {
142536
+ fallback();
142537
+ }
142538
+ });
142266
142539
  app.use(requireAuth);
142267
142540
  app.post("/session", async (req, res) => {
142268
142541
  const { sessionId, webhookUrl, apiKey, searchApiKey, model, agent } = req.body;
@@ -142319,6 +142592,10 @@ async function startProxyServer() {
142319
142592
  system: req.body.system
142320
142593
  }))
142321
142594
  );
142595
+ app.post("/agent/push", async (req, res) => {
142596
+ log("===> POST /agent/push");
142597
+ await handleAgentPush(req, res);
142598
+ });
142322
142599
  app.post(
142323
142600
  "/repair",
142324
142601
  createAsyncAgentHandler(
@@ -10987,7 +10987,7 @@ var glob = Object.assign(glob_, {
10987
10987
  glob.glob = glob;
10988
10988
 
10989
10989
  // src/proxy/version.ts
10990
- var VERSION = "0.4.10";
10990
+ var VERSION = "0.4.11";
10991
10991
 
10992
10992
  // src/deps.ts
10993
10993
  var import_child_process = require("child_process");
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.4.10",
5
+ "version": "0.4.11",
6
6
  "type": "module",
7
7
  "publisher": "stakwork",
8
8
  "engines": {
@@ -108,6 +108,7 @@
108
108
  "ask-gemini": "ts-node ./src/scripts/ask-gemini.ts",
109
109
  "test-git": "ts-node ./src/test/test-git.ts",
110
110
  "test-pm2": "ts-node ./src/test/test-pm2.ts",
111
+ "test-ide-auth": "npx tsx ./src/test/test-ide-auth.ts",
111
112
  "try-parse": "ts-node ./src/scripts/try-parse.ts",
112
113
  "try-gemini": "ts-node ./src/try/try-gemini.ts",
113
114
  "try-cc": "ts-node ./src/try/try-cc.ts",