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.
- package/dist/proxy-server.cjs +296 -12
- 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.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
|
-
|
|
141040
|
-
if (binaryContent !== null) {
|
|
141041
|
-
content = binaryContent;
|
|
141135
|
+
if (action === "delete") {
|
|
141042
141136
|
} else {
|
|
141043
|
-
|
|
141044
|
-
|
|
141045
|
-
|
|
141046
|
-
|
|
141047
|
-
|
|
141048
|
-
|
|
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
|
|
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 (
|
|
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(
|
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.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.
|
|
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",
|