staklink 0.4.10 → 0.4.12

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.12";
60818
60914
 
60819
60915
  // node_modules/uuid/dist/esm/stringify.js
60820
60916
  var byteToHex = [];
@@ -141036,16 +141132,23 @@ async function handleBranchDiff(req, res) {
141036
141132
  action = "modify";
141037
141133
  }
141038
141134
  let content = "";
141039
- const binaryContent = await binaryDiffContent(repo, filePath, action);
141040
- if (binaryContent !== null) {
141041
- content = binaryContent;
141135
+ if (action === "delete") {
141042
141136
  } else {
141043
- try {
141044
- content = await repo.execCommand(
141045
- `git diff ${diffBase} -- "${filePath}"`
141046
- );
141047
- } catch (error88) {
141048
- warn(`Error getting branch diff for ${filePath}:`, error88);
141137
+ const binaryContent = await binaryDiffContent(
141138
+ repo,
141139
+ filePath,
141140
+ action
141141
+ );
141142
+ if (binaryContent !== null) {
141143
+ content = binaryContent;
141144
+ } else {
141145
+ try {
141146
+ content = await repo.execCommand(
141147
+ `git diff ${diffBase} -- "${filePath}"`
141148
+ );
141149
+ } catch (error88) {
141150
+ warn(`Error getting branch diff for ${filePath}:`, error88);
141151
+ }
141049
141152
  }
141050
141153
  }
141051
141154
  results.push({
@@ -141412,10 +141515,12 @@ ${diff.trim()}`);
141412
141515
  }
141413
141516
  }
141414
141517
  }
141415
- const response = { success: true, commits, branches };
141518
+ const hasPrErrors = Object.keys(prErrors).length > 0;
141519
+ const success3 = !(createPR && hasPrErrors);
141520
+ const response = { success: success3, commits, branches };
141416
141521
  if (createPR) {
141417
141522
  response.prs = prs;
141418
- if (Object.keys(prErrors).length > 0) {
141523
+ if (hasPrErrors) {
141419
141524
  response.prErrors = prErrors;
141420
141525
  }
141421
141526
  }
@@ -141525,6 +141630,152 @@ async function handleCreateRepo(req, res) {
141525
141630
  });
141526
141631
  }
141527
141632
  }
141633
+ function buildPushPrompt(opts) {
141634
+ const lines = [
141635
+ `You are in the git repo at: ${opts.repoPath}`,
141636
+ "",
141637
+ "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.",
141638
+ "",
141639
+ "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.",
141640
+ "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.",
141641
+ "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."
141642
+ ];
141643
+ if (opts.createPR) {
141644
+ let step = 4;
141645
+ lines.push(
141646
+ `${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.`
141647
+ );
141648
+ if (opts.label) {
141649
+ step++;
141650
+ lines.push(
141651
+ `${step}. Add the label "${opts.label}" to the PR.`
141652
+ );
141653
+ }
141654
+ step++;
141655
+ lines.push(
141656
+ `${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.`
141657
+ );
141658
+ if (opts.autoMerge) {
141659
+ step++;
141660
+ lines.push(
141661
+ `${step}. Enable auto-merge (squash) on the PR using: gh pr merge <number> --auto --squash --delete-branch`
141662
+ );
141663
+ }
141664
+ }
141665
+ lines.push(
141666
+ "",
141667
+ "IMPORTANT:",
141668
+ "- Do NOT ask for confirmation. Just do it.",
141669
+ "- If push fails, try pulling with rebase first. If that fails with conflicts, resolve them and continue.",
141670
+ "- Output the PR URL if you created one.",
141671
+ "- Output the branch name you ended up on.",
141672
+ "- Output the commit hash after pushing."
141673
+ );
141674
+ return lines.join("\n");
141675
+ }
141676
+ async function handleAgentPush(req, res) {
141677
+ const request_id = startReq();
141678
+ try {
141679
+ const code = req.body;
141680
+ const repos = getReposWithChangedFiles(code);
141681
+ const createPR = req.query.pr === "true";
141682
+ const autoMerge = req.query.automerge === "true";
141683
+ const label = req.query.label;
141684
+ if (!repos.length) {
141685
+ finishReq(request_id, {
141686
+ success: true,
141687
+ commits: [],
141688
+ branches: {}
141689
+ });
141690
+ res.json({ request_id, status: "pending" });
141691
+ return;
141692
+ }
141693
+ if (!code.git_credentials?.auth_data?.token) {
141694
+ failReq(request_id, new Error("git_credentials with token required"));
141695
+ res.json({ request_id, status: "pending" });
141696
+ return;
141697
+ }
141698
+ const apiKey = code.apiKey || process.env.ANTHROPIC_API_KEY || "";
141699
+ (async () => {
141700
+ const results = { commits: [], prs: {}, branches: {} };
141701
+ for (const r of repos) {
141702
+ const repo = await NewRepo(r.url);
141703
+ const repoPath = repo.printcwd();
141704
+ const repoName = getRepoNameFromUrl(r.url);
141705
+ const baseBranch = r.base_branch || "main";
141706
+ const { env: credEnv, teardown } = await setupGlobalGitCredentials(
141707
+ repoPath,
141708
+ code.git_credentials
141709
+ );
141710
+ try {
141711
+ const prompt = buildPushPrompt({
141712
+ repoPath,
141713
+ createPR,
141714
+ autoMerge,
141715
+ label,
141716
+ baseBranch
141717
+ });
141718
+ log(`=> agent/push prompt for ${repoName}:
141719
+ ${prompt}`);
141720
+ const prevGhToken = process.env.GH_TOKEN;
141721
+ const prevGithubToken = process.env.GITHUB_TOKEN;
141722
+ process.env.GH_TOKEN = credEnv.GH_TOKEN;
141723
+ process.env.GITHUB_TOKEN = credEnv.GITHUB_TOKEN;
141724
+ const agentFn = chooseAgent("goose");
141725
+ let result;
141726
+ try {
141727
+ result = await agentFn({
141728
+ prompt,
141729
+ apiKey,
141730
+ cwd: repoPath,
141731
+ session: code.session,
141732
+ model: code.model
141733
+ });
141734
+ } finally {
141735
+ if (prevGhToken !== void 0) {
141736
+ process.env.GH_TOKEN = prevGhToken;
141737
+ } else {
141738
+ delete process.env.GH_TOKEN;
141739
+ }
141740
+ if (prevGithubToken !== void 0) {
141741
+ process.env.GITHUB_TOKEN = prevGithubToken;
141742
+ } else {
141743
+ delete process.env.GITHUB_TOKEN;
141744
+ }
141745
+ }
141746
+ const output = typeof result === "string" ? result : result.output;
141747
+ results.commits.push(output);
141748
+ const branchMatch = output.match(
141749
+ /(?:on branch|switched to|branch)\s+[`'"]?([^\s`'"]+)/i
141750
+ );
141751
+ if (branchMatch) {
141752
+ results.branches[repoName] = branchMatch[1];
141753
+ }
141754
+ const prMatch = output.match(
141755
+ /https:\/\/github\.com\/[^\s]+\/pull\/\d+/
141756
+ );
141757
+ if (prMatch) {
141758
+ results.prs[repoName] = prMatch[0];
141759
+ }
141760
+ } finally {
141761
+ await teardown();
141762
+ }
141763
+ }
141764
+ finishReq(request_id, {
141765
+ success: true,
141766
+ ...results
141767
+ });
141768
+ })().catch((error88) => {
141769
+ error("agent/push error:", error88);
141770
+ failReq(request_id, error88);
141771
+ });
141772
+ res.json({ request_id, status: "pending" });
141773
+ } catch (e) {
141774
+ error("agent/push setup error:", e);
141775
+ failReq(request_id, e);
141776
+ res.json({ request_id, status: "pending" });
141777
+ }
141778
+ }
141528
141779
 
141529
141780
  // src/proxy/code_actions.ts
141530
141781
  var fs14 = __toESM(require("fs/promises"), 1);
@@ -142263,6 +142514,35 @@ async function startProxyServer() {
142263
142514
  }
142264
142515
  clearInterval(keepAliveInterval);
142265
142516
  });
142517
+ app.get("/ide-auth", async (req, res) => {
142518
+ const fallback = () => res.redirect(302, "/");
142519
+ try {
142520
+ const { token, expires } = req.query;
142521
+ const password = process.env.CODE_SERVER_PASSWORD;
142522
+ const port = process.env.CODE_SERVER_PORT || "8444";
142523
+ if (!token || !expires || !password) return fallback();
142524
+ const expiresNum = parseInt(expires, 10);
142525
+ if (isNaN(expiresNum) || Math.floor(Date.now() / 1e3) > expiresNum) return fallback();
142526
+ const crypto3 = await import("crypto");
142527
+ const expected = crypto3.createHmac("sha256", password).update(`ide-auth:${expires}`).digest("hex");
142528
+ const tA = Buffer.from(token);
142529
+ const tB = Buffer.from(expected);
142530
+ if (tA.length !== tB.length || !crypto3.timingSafeEqual(tA, tB)) return fallback();
142531
+ const loginRes = await fetch(`http://localhost:${port}/login`, {
142532
+ method: "POST",
142533
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
142534
+ body: new URLSearchParams({ password }).toString(),
142535
+ redirect: "manual"
142536
+ });
142537
+ const setCookieHeader = loginRes.headers.get("set-cookie") || "";
142538
+ const keyMatch = setCookieHeader.match(/(?:^|,)\s*key=([^;,]+)/);
142539
+ if (!keyMatch) return fallback();
142540
+ res.setHeader("Set-Cookie", `key=${keyMatch[1]}; Path=/; SameSite=Lax; HttpOnly`);
142541
+ res.redirect(302, "/");
142542
+ } catch {
142543
+ fallback();
142544
+ }
142545
+ });
142266
142546
  app.use(requireAuth);
142267
142547
  app.post("/session", async (req, res) => {
142268
142548
  const { sessionId, webhookUrl, apiKey, searchApiKey, model, agent } = req.body;
@@ -142319,6 +142599,10 @@ async function startProxyServer() {
142319
142599
  system: req.body.system
142320
142600
  }))
142321
142601
  );
142602
+ app.post("/agent/push", async (req, res) => {
142603
+ log("===> POST /agent/push");
142604
+ await handleAgentPush(req, res);
142605
+ });
142322
142606
  app.post(
142323
142607
  "/repair",
142324
142608
  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.12";
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.12",
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",