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.
- package/dist/proxy-server.cjs +280 -3
- package/dist/staklink-cli.cjs +1 -1
- package/package.json +2 -1
package/dist/proxy-server.cjs
CHANGED
|
@@ -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.
|
|
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
|
|
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 (
|
|
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(
|
package/dist/staklink-cli.cjs
CHANGED
|
@@ -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.
|
|
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.
|
|
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",
|