staklink 0.3.74 → 0.3.76
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/1.extension.cjs +6 -6
- package/dist/1.extension.cjs.map +1 -1
- package/dist/2.extension.cjs +9 -9
- package/dist/2.extension.cjs.map +1 -1
- package/dist/proxy-server.cjs +1340 -1204
- package/dist/staklink-cli.cjs +1 -1
- package/package.json +1 -1
package/dist/proxy-server.cjs
CHANGED
|
@@ -1289,8 +1289,8 @@ var require_node = __commonJS({
|
|
|
1289
1289
|
}
|
|
1290
1290
|
break;
|
|
1291
1291
|
case "FILE":
|
|
1292
|
-
var
|
|
1293
|
-
stream3 = new
|
|
1292
|
+
var fs14 = require("fs");
|
|
1293
|
+
stream3 = new fs14.SyncWriteStream(fd2, { autoClose: false });
|
|
1294
1294
|
stream3._type = "fs";
|
|
1295
1295
|
break;
|
|
1296
1296
|
case "PIPE":
|
|
@@ -17508,8 +17508,8 @@ var require_node2 = __commonJS({
|
|
|
17508
17508
|
}
|
|
17509
17509
|
break;
|
|
17510
17510
|
case "FILE":
|
|
17511
|
-
var
|
|
17512
|
-
stream3 = new
|
|
17511
|
+
var fs14 = require("fs");
|
|
17512
|
+
stream3 = new fs14.SyncWriteStream(fd2, { autoClose: false });
|
|
17513
17513
|
stream3._type = "fs";
|
|
17514
17514
|
break;
|
|
17515
17515
|
case "PIPE":
|
|
@@ -18227,8 +18227,8 @@ var require_node3 = __commonJS({
|
|
|
18227
18227
|
}
|
|
18228
18228
|
break;
|
|
18229
18229
|
case "FILE":
|
|
18230
|
-
var
|
|
18231
|
-
stream3 = new
|
|
18230
|
+
var fs14 = require("fs");
|
|
18231
|
+
stream3 = new fs14.SyncWriteStream(fd2, { autoClose: false });
|
|
18232
18232
|
stream3._type = "fs";
|
|
18233
18233
|
break;
|
|
18234
18234
|
case "PIPE":
|
|
@@ -19120,7 +19120,7 @@ var require_view = __commonJS({
|
|
|
19120
19120
|
"use strict";
|
|
19121
19121
|
var debug = require_src3()("express:view");
|
|
19122
19122
|
var path11 = require("path");
|
|
19123
|
-
var
|
|
19123
|
+
var fs14 = require("fs");
|
|
19124
19124
|
var dirname2 = path11.dirname;
|
|
19125
19125
|
var basename2 = path11.basename;
|
|
19126
19126
|
var extname = path11.extname;
|
|
@@ -19186,7 +19186,7 @@ var require_view = __commonJS({
|
|
|
19186
19186
|
function tryStat(path12) {
|
|
19187
19187
|
debug('stat "%s"', path12);
|
|
19188
19188
|
try {
|
|
19189
|
-
return
|
|
19189
|
+
return fs14.statSync(path12);
|
|
19190
19190
|
} catch (e) {
|
|
19191
19191
|
return void 0;
|
|
19192
19192
|
}
|
|
@@ -19791,8 +19791,8 @@ var require_node4 = __commonJS({
|
|
|
19791
19791
|
}
|
|
19792
19792
|
break;
|
|
19793
19793
|
case "FILE":
|
|
19794
|
-
var
|
|
19795
|
-
stream3 = new
|
|
19794
|
+
var fs14 = require("fs");
|
|
19795
|
+
stream3 = new fs14.SyncWriteStream(fd2, { autoClose: false });
|
|
19796
19796
|
stream3._type = "fs";
|
|
19797
19797
|
break;
|
|
19798
19798
|
case "PIPE":
|
|
@@ -19979,7 +19979,7 @@ var require_types = __commonJS({
|
|
|
19979
19979
|
var require_mime = __commonJS({
|
|
19980
19980
|
"node_modules/mime/mime.js"(exports2, module2) {
|
|
19981
19981
|
var path11 = require("path");
|
|
19982
|
-
var
|
|
19982
|
+
var fs14 = require("fs");
|
|
19983
19983
|
function Mime() {
|
|
19984
19984
|
this.types = /* @__PURE__ */ Object.create(null);
|
|
19985
19985
|
this.extensions = /* @__PURE__ */ Object.create(null);
|
|
@@ -20000,7 +20000,7 @@ var require_mime = __commonJS({
|
|
|
20000
20000
|
};
|
|
20001
20001
|
Mime.prototype.load = function(file3) {
|
|
20002
20002
|
this._loading = file3;
|
|
20003
|
-
var map3 = {}, content =
|
|
20003
|
+
var map3 = {}, content = fs14.readFileSync(file3, "ascii"), lines = content.split(/[\r\n]+/);
|
|
20004
20004
|
lines.forEach(function(line) {
|
|
20005
20005
|
var fields = line.replace(/\s*#.*|^\s*|\s*$/g, "").split(/\s+/);
|
|
20006
20006
|
map3[fields.shift()] = fields;
|
|
@@ -20238,7 +20238,7 @@ var require_send = __commonJS({
|
|
|
20238
20238
|
var escapeHtml = require_escape_html();
|
|
20239
20239
|
var etag = require_etag();
|
|
20240
20240
|
var fresh = require_fresh();
|
|
20241
|
-
var
|
|
20241
|
+
var fs14 = require("fs");
|
|
20242
20242
|
var mime = require_mime();
|
|
20243
20243
|
var ms = require_ms5();
|
|
20244
20244
|
var onFinished = require_on_finished();
|
|
@@ -20571,7 +20571,7 @@ var require_send = __commonJS({
|
|
|
20571
20571
|
var i = 0;
|
|
20572
20572
|
var self = this;
|
|
20573
20573
|
debug('stat "%s"', path12);
|
|
20574
|
-
|
|
20574
|
+
fs14.stat(path12, function onstat(err, stat4) {
|
|
20575
20575
|
if (err && err.code === "ENOENT" && !extname(path12) && path12[path12.length - 1] !== sep2) {
|
|
20576
20576
|
return next(err);
|
|
20577
20577
|
}
|
|
@@ -20586,7 +20586,7 @@ var require_send = __commonJS({
|
|
|
20586
20586
|
}
|
|
20587
20587
|
var p = path12 + "." + self._extensions[i++];
|
|
20588
20588
|
debug('stat "%s"', p);
|
|
20589
|
-
|
|
20589
|
+
fs14.stat(p, function(err2, stat4) {
|
|
20590
20590
|
if (err2) return next(err2);
|
|
20591
20591
|
if (stat4.isDirectory()) return next();
|
|
20592
20592
|
self.emit("file", p, stat4);
|
|
@@ -20604,7 +20604,7 @@ var require_send = __commonJS({
|
|
|
20604
20604
|
}
|
|
20605
20605
|
var p = join7(path12, self._index[i]);
|
|
20606
20606
|
debug('stat "%s"', p);
|
|
20607
|
-
|
|
20607
|
+
fs14.stat(p, function(err2, stat4) {
|
|
20608
20608
|
if (err2) return next(err2);
|
|
20609
20609
|
if (stat4.isDirectory()) return next();
|
|
20610
20610
|
self.emit("file", p, stat4);
|
|
@@ -20616,7 +20616,7 @@ var require_send = __commonJS({
|
|
|
20616
20616
|
SendStream.prototype.stream = function stream2(path12, options) {
|
|
20617
20617
|
var self = this;
|
|
20618
20618
|
var res = this.res;
|
|
20619
|
-
var stream3 =
|
|
20619
|
+
var stream3 = fs14.createReadStream(path12, options);
|
|
20620
20620
|
this.emit("stream", stream3);
|
|
20621
20621
|
stream3.pipe(res);
|
|
20622
20622
|
function cleanup2() {
|
|
@@ -24146,7 +24146,7 @@ var require_token_util = __commonJS({
|
|
|
24146
24146
|
});
|
|
24147
24147
|
module2.exports = __toCommonJS(token_util_exports);
|
|
24148
24148
|
var path11 = __toESM2(require("path"));
|
|
24149
|
-
var
|
|
24149
|
+
var fs14 = __toESM2(require("fs"));
|
|
24150
24150
|
var import_token_error = require_token_error();
|
|
24151
24151
|
var import_token_io = require_token_io();
|
|
24152
24152
|
function getVercelDataDir() {
|
|
@@ -24163,10 +24163,10 @@ var require_token_util = __commonJS({
|
|
|
24163
24163
|
return null;
|
|
24164
24164
|
}
|
|
24165
24165
|
const tokenPath = path11.join(dataDir, "auth.json");
|
|
24166
|
-
if (!
|
|
24166
|
+
if (!fs14.existsSync(tokenPath)) {
|
|
24167
24167
|
return null;
|
|
24168
24168
|
}
|
|
24169
|
-
const token =
|
|
24169
|
+
const token = fs14.readFileSync(tokenPath, "utf8");
|
|
24170
24170
|
if (!token) {
|
|
24171
24171
|
return null;
|
|
24172
24172
|
}
|
|
@@ -24208,10 +24208,10 @@ var require_token_util = __commonJS({
|
|
|
24208
24208
|
}
|
|
24209
24209
|
try {
|
|
24210
24210
|
const prjPath = path11.join(dir, ".vercel", "project.json");
|
|
24211
|
-
if (!
|
|
24211
|
+
if (!fs14.existsSync(prjPath)) {
|
|
24212
24212
|
throw new import_token_error.VercelOidcTokenError("project.json not found");
|
|
24213
24213
|
}
|
|
24214
|
-
const prj = JSON.parse(
|
|
24214
|
+
const prj = JSON.parse(fs14.readFileSync(prjPath, "utf8"));
|
|
24215
24215
|
if (typeof prj.projectId !== "string" && typeof prj.orgId !== "string") {
|
|
24216
24216
|
throw new TypeError("Expected a string-valued projectId property");
|
|
24217
24217
|
}
|
|
@@ -24228,9 +24228,9 @@ var require_token_util = __commonJS({
|
|
|
24228
24228
|
}
|
|
24229
24229
|
const tokenPath = path11.join(dir, "com.vercel.token", `${projectId}.json`);
|
|
24230
24230
|
const tokenJson = JSON.stringify(token);
|
|
24231
|
-
|
|
24232
|
-
|
|
24233
|
-
|
|
24231
|
+
fs14.mkdirSync(path11.dirname(tokenPath), { mode: 504, recursive: true });
|
|
24232
|
+
fs14.writeFileSync(tokenPath, tokenJson);
|
|
24233
|
+
fs14.chmodSync(tokenPath, 432);
|
|
24234
24234
|
return;
|
|
24235
24235
|
} catch (e) {
|
|
24236
24236
|
throw new import_token_error.VercelOidcTokenError(`Failed to save token`, e);
|
|
@@ -24243,10 +24243,10 @@ var require_token_util = __commonJS({
|
|
|
24243
24243
|
return null;
|
|
24244
24244
|
}
|
|
24245
24245
|
const tokenPath = path11.join(dir, "com.vercel.token", `${projectId}.json`);
|
|
24246
|
-
if (!
|
|
24246
|
+
if (!fs14.existsSync(tokenPath)) {
|
|
24247
24247
|
return null;
|
|
24248
24248
|
}
|
|
24249
|
-
const token = JSON.parse(
|
|
24249
|
+
const token = JSON.parse(fs14.readFileSync(tokenPath, "utf8"));
|
|
24250
24250
|
assertVercelOidcTokenResponse(token);
|
|
24251
24251
|
return token;
|
|
24252
24252
|
} catch (e) {
|
|
@@ -26278,7 +26278,7 @@ var require_auth_config = __commonJS({
|
|
|
26278
26278
|
writeAuthConfig: () => writeAuthConfig
|
|
26279
26279
|
});
|
|
26280
26280
|
module2.exports = __toCommonJS(auth_config_exports);
|
|
26281
|
-
var
|
|
26281
|
+
var fs14 = __toESM2(require("fs"));
|
|
26282
26282
|
var path11 = __toESM2(require("path"));
|
|
26283
26283
|
var import_token_util = require_token_util2();
|
|
26284
26284
|
function getAuthConfigPath() {
|
|
@@ -26293,10 +26293,10 @@ var require_auth_config = __commonJS({
|
|
|
26293
26293
|
function readAuthConfig() {
|
|
26294
26294
|
try {
|
|
26295
26295
|
const authPath = getAuthConfigPath();
|
|
26296
|
-
if (!
|
|
26296
|
+
if (!fs14.existsSync(authPath)) {
|
|
26297
26297
|
return null;
|
|
26298
26298
|
}
|
|
26299
|
-
const content =
|
|
26299
|
+
const content = fs14.readFileSync(authPath, "utf8");
|
|
26300
26300
|
if (!content) {
|
|
26301
26301
|
return null;
|
|
26302
26302
|
}
|
|
@@ -26308,10 +26308,10 @@ var require_auth_config = __commonJS({
|
|
|
26308
26308
|
function writeAuthConfig(config4) {
|
|
26309
26309
|
const authPath = getAuthConfigPath();
|
|
26310
26310
|
const authDir = path11.dirname(authPath);
|
|
26311
|
-
if (!
|
|
26312
|
-
|
|
26311
|
+
if (!fs14.existsSync(authDir)) {
|
|
26312
|
+
fs14.mkdirSync(authDir, { mode: 504, recursive: true });
|
|
26313
26313
|
}
|
|
26314
|
-
|
|
26314
|
+
fs14.writeFileSync(authPath, JSON.stringify(config4, null, 2), { mode: 384 });
|
|
26315
26315
|
}
|
|
26316
26316
|
function isValidAccessToken(authConfig) {
|
|
26317
26317
|
if (!authConfig.token)
|
|
@@ -26458,7 +26458,7 @@ var require_token_util2 = __commonJS({
|
|
|
26458
26458
|
});
|
|
26459
26459
|
module2.exports = __toCommonJS(token_util_exports);
|
|
26460
26460
|
var path11 = __toESM2(require("path"));
|
|
26461
|
-
var
|
|
26461
|
+
var fs14 = __toESM2(require("fs"));
|
|
26462
26462
|
var import_token_error = require_token_error2();
|
|
26463
26463
|
var import_token_io = require_token_io2();
|
|
26464
26464
|
var import_auth_config = require_auth_config();
|
|
@@ -26543,12 +26543,12 @@ var require_token_util2 = __commonJS({
|
|
|
26543
26543
|
);
|
|
26544
26544
|
}
|
|
26545
26545
|
const prjPath = path11.join(dir, ".vercel", "project.json");
|
|
26546
|
-
if (!
|
|
26546
|
+
if (!fs14.existsSync(prjPath)) {
|
|
26547
26547
|
throw new import_token_error.VercelOidcTokenError(
|
|
26548
26548
|
"project.json not found, have you linked your project with `vc link?`"
|
|
26549
26549
|
);
|
|
26550
26550
|
}
|
|
26551
|
-
const prj = JSON.parse(
|
|
26551
|
+
const prj = JSON.parse(fs14.readFileSync(prjPath, "utf8"));
|
|
26552
26552
|
if (typeof prj.projectId !== "string" && typeof prj.orgId !== "string") {
|
|
26553
26553
|
throw new TypeError(
|
|
26554
26554
|
"Expected a string-valued projectId property. Try running `vc link` to re-link your project."
|
|
@@ -26565,9 +26565,9 @@ var require_token_util2 = __commonJS({
|
|
|
26565
26565
|
}
|
|
26566
26566
|
const tokenPath = path11.join(dir, "com.vercel.token", `${projectId}.json`);
|
|
26567
26567
|
const tokenJson = JSON.stringify(token);
|
|
26568
|
-
|
|
26569
|
-
|
|
26570
|
-
|
|
26568
|
+
fs14.mkdirSync(path11.dirname(tokenPath), { mode: 504, recursive: true });
|
|
26569
|
+
fs14.writeFileSync(tokenPath, tokenJson);
|
|
26570
|
+
fs14.chmodSync(tokenPath, 432);
|
|
26571
26571
|
return;
|
|
26572
26572
|
}
|
|
26573
26573
|
function loadToken(projectId) {
|
|
@@ -26578,10 +26578,10 @@ var require_token_util2 = __commonJS({
|
|
|
26578
26578
|
);
|
|
26579
26579
|
}
|
|
26580
26580
|
const tokenPath = path11.join(dir, "com.vercel.token", `${projectId}.json`);
|
|
26581
|
-
if (!
|
|
26581
|
+
if (!fs14.existsSync(tokenPath)) {
|
|
26582
26582
|
return null;
|
|
26583
26583
|
}
|
|
26584
|
-
const token = JSON.parse(
|
|
26584
|
+
const token = JSON.parse(fs14.readFileSync(tokenPath, "utf8"));
|
|
26585
26585
|
assertVercelOidcTokenResponse(token);
|
|
26586
26586
|
return token;
|
|
26587
26587
|
}
|
|
@@ -52826,6 +52826,14 @@ var require_undici = __commonJS({
|
|
|
52826
52826
|
// src/proxy/server.ts
|
|
52827
52827
|
var import_express = __toESM(require_express2(), 1);
|
|
52828
52828
|
|
|
52829
|
+
// src/proxy/utils.ts
|
|
52830
|
+
var fs5 = __toESM(require("fs/promises"), 1);
|
|
52831
|
+
|
|
52832
|
+
// src/proxy/pm2.ts
|
|
52833
|
+
var import_util = require("util");
|
|
52834
|
+
var proc = __toESM(require("child_process"), 1);
|
|
52835
|
+
var fs = __toESM(require("fs"), 1);
|
|
52836
|
+
|
|
52829
52837
|
// src/proxy/logger.ts
|
|
52830
52838
|
function formatTimestamp() {
|
|
52831
52839
|
const now3 = /* @__PURE__ */ new Date();
|
|
@@ -52843,110 +52851,7 @@ function warn(...args) {
|
|
|
52843
52851
|
console.warn(formatTimestamp(), ...args);
|
|
52844
52852
|
}
|
|
52845
52853
|
|
|
52846
|
-
// src/proxy/parse.ts
|
|
52847
|
-
function parseXmlResponse(xmlString) {
|
|
52848
|
-
function extractBetweenTags(xml, tag) {
|
|
52849
|
-
const startTag = `<${tag}>`;
|
|
52850
|
-
const endTag = `</${tag}>`;
|
|
52851
|
-
const startIndex = xml.indexOf(startTag) + startTag.length;
|
|
52852
|
-
const endIndex = xml.indexOf(endTag);
|
|
52853
|
-
if (startIndex === -1 + startTag.length || endIndex === -1) {
|
|
52854
|
-
return "";
|
|
52855
|
-
}
|
|
52856
|
-
return xml.slice(startIndex, endIndex);
|
|
52857
|
-
}
|
|
52858
|
-
function extractFiles(xml) {
|
|
52859
|
-
const files2 = [];
|
|
52860
|
-
let remainingXml = xml;
|
|
52861
|
-
while (remainingXml.includes("<file")) {
|
|
52862
|
-
const fileStart = remainingXml.indexOf("<file");
|
|
52863
|
-
const fileEndTag = remainingXml.indexOf("</file>");
|
|
52864
|
-
if (fileEndTag === -1) {
|
|
52865
|
-
break;
|
|
52866
|
-
}
|
|
52867
|
-
const fileEnd = fileEndTag + 7;
|
|
52868
|
-
const fileXml = remainingXml.slice(fileStart, fileEnd);
|
|
52869
|
-
const pathMatch = fileXml.match(/path="([^"]*)"/);
|
|
52870
|
-
const actionMatch = fileXml.match(/action="([^"]*)"/);
|
|
52871
|
-
const path11 = pathMatch ? pathMatch[1] : "";
|
|
52872
|
-
const action = actionMatch ? actionMatch[1] : "modify";
|
|
52873
|
-
const changes = [];
|
|
52874
|
-
let changeXml = fileXml;
|
|
52875
|
-
while (changeXml.includes("<change>")) {
|
|
52876
|
-
const changeStart = changeXml.indexOf("<change>");
|
|
52877
|
-
const changeEndTag = changeXml.indexOf("</change>");
|
|
52878
|
-
if (changeEndTag === -1) {
|
|
52879
|
-
break;
|
|
52880
|
-
}
|
|
52881
|
-
const changeEnd = changeEndTag + 8;
|
|
52882
|
-
const currentChange = changeXml.slice(changeStart, changeEnd);
|
|
52883
|
-
const description = extractBetweenTags(currentChange, "description");
|
|
52884
|
-
let search = void 0;
|
|
52885
|
-
if (currentChange.includes("<search>") && currentChange.includes("</search>")) {
|
|
52886
|
-
const searchText = extractBetweenTags(currentChange, "search");
|
|
52887
|
-
try {
|
|
52888
|
-
if (searchText) {
|
|
52889
|
-
search = extractContent(searchText);
|
|
52890
|
-
}
|
|
52891
|
-
} catch (e) {
|
|
52892
|
-
}
|
|
52893
|
-
}
|
|
52894
|
-
let content = "";
|
|
52895
|
-
if (currentChange.includes("<content>") && currentChange.includes("</content>")) {
|
|
52896
|
-
const contentText = extractBetweenTags(currentChange, "content");
|
|
52897
|
-
try {
|
|
52898
|
-
if (contentText) {
|
|
52899
|
-
content = extractContent(contentText);
|
|
52900
|
-
} else {
|
|
52901
|
-
changeXml = changeXml.slice(changeEnd);
|
|
52902
|
-
continue;
|
|
52903
|
-
}
|
|
52904
|
-
} catch (e) {
|
|
52905
|
-
changeXml = changeXml.slice(changeEnd);
|
|
52906
|
-
continue;
|
|
52907
|
-
}
|
|
52908
|
-
} else {
|
|
52909
|
-
changeXml = changeXml.slice(changeEnd);
|
|
52910
|
-
continue;
|
|
52911
|
-
}
|
|
52912
|
-
changes.push({
|
|
52913
|
-
description,
|
|
52914
|
-
search: search || void 0,
|
|
52915
|
-
content
|
|
52916
|
-
});
|
|
52917
|
-
changeXml = changeXml.slice(changeEnd);
|
|
52918
|
-
}
|
|
52919
|
-
if (changes.length > 0) {
|
|
52920
|
-
files2.push({ path: path11, action, changes });
|
|
52921
|
-
}
|
|
52922
|
-
remainingXml = remainingXml.slice(fileEnd);
|
|
52923
|
-
}
|
|
52924
|
-
return files2;
|
|
52925
|
-
}
|
|
52926
|
-
const plan = extractBetweenTags(xmlString, "Plan").trim();
|
|
52927
|
-
const files = extractFiles(xmlString);
|
|
52928
|
-
return { plan, files };
|
|
52929
|
-
}
|
|
52930
|
-
function extractContent(text3) {
|
|
52931
|
-
const marker40 = "\n===\n";
|
|
52932
|
-
const firstIndex = text3.indexOf(marker40);
|
|
52933
|
-
const lastIndex = text3.lastIndexOf(marker40);
|
|
52934
|
-
if (firstIndex === -1 || lastIndex === -1 || firstIndex === lastIndex) {
|
|
52935
|
-
log("==============> ERROR no \\n===\\n", JSON.stringify(text3));
|
|
52936
|
-
throw new Error(
|
|
52937
|
-
"Input string must include at least two '\\n===\\n' markers"
|
|
52938
|
-
);
|
|
52939
|
-
}
|
|
52940
|
-
return text3.substring(firstIndex + marker40.length, lastIndex);
|
|
52941
|
-
}
|
|
52942
|
-
|
|
52943
|
-
// src/proxy/utils.ts
|
|
52944
|
-
var fs5 = __toESM(require("fs/promises"), 1);
|
|
52945
|
-
|
|
52946
52854
|
// src/proxy/pm2.ts
|
|
52947
|
-
var import_util = require("util");
|
|
52948
|
-
var proc = __toESM(require("child_process"), 1);
|
|
52949
|
-
var fs = __toESM(require("fs"), 1);
|
|
52950
52855
|
var exec2 = (0, import_util.promisify)(proc.exec);
|
|
52951
52856
|
var PM2Manager = class {
|
|
52952
52857
|
cwd;
|
|
@@ -53189,9 +53094,7 @@ async function startup() {
|
|
|
53189
53094
|
try {
|
|
53190
53095
|
await runner.runRebuildCommands(config4);
|
|
53191
53096
|
await runner.runInstallCommands(config4);
|
|
53192
|
-
log(
|
|
53193
|
-
`\u2705 Install commands executed successfully in ${workspaceRoot2}`
|
|
53194
|
-
);
|
|
53097
|
+
log(`\u2705 Install commands executed successfully in ${workspaceRoot2}`);
|
|
53195
53098
|
} catch (error87) {
|
|
53196
53099
|
log(`\u274C Failed to run install/rebuild commands: ${error87}`);
|
|
53197
53100
|
}
|
|
@@ -53942,62 +53845,112 @@ async function getChildDirectories(dirPath) {
|
|
|
53942
53845
|
}
|
|
53943
53846
|
}
|
|
53944
53847
|
|
|
53945
|
-
// src/proxy/
|
|
53946
|
-
|
|
53947
|
-
|
|
53948
|
-
|
|
53949
|
-
|
|
53950
|
-
|
|
53951
|
-
|
|
53952
|
-
|
|
53953
|
-
|
|
53954
|
-
if (await dirExists(repodir1)) {
|
|
53955
|
-
log(`Found repo in /workspaces: ${repodir1}`);
|
|
53956
|
-
return repodir1;
|
|
53957
|
-
}
|
|
53958
|
-
const repodir2 = `${process.cwd()}/${repoName}`;
|
|
53959
|
-
if (await dirExists(repodir2)) {
|
|
53960
|
-
log(`Found repo in current directory: ${repodir2}`);
|
|
53961
|
-
return repodir2;
|
|
53848
|
+
// src/proxy/parse.ts
|
|
53849
|
+
function parseXmlResponse(xmlString) {
|
|
53850
|
+
function extractBetweenTags(xml, tag) {
|
|
53851
|
+
const startTag = `<${tag}>`;
|
|
53852
|
+
const endTag = `</${tag}>`;
|
|
53853
|
+
const startIndex = xml.indexOf(startTag) + startTag.length;
|
|
53854
|
+
const endIndex = xml.indexOf(endTag);
|
|
53855
|
+
if (startIndex === -1 + startTag.length || endIndex === -1) {
|
|
53856
|
+
return "";
|
|
53962
53857
|
}
|
|
53963
|
-
|
|
53964
|
-
|
|
53965
|
-
|
|
53966
|
-
|
|
53967
|
-
|
|
53968
|
-
|
|
53969
|
-
|
|
53858
|
+
return xml.slice(startIndex, endIndex);
|
|
53859
|
+
}
|
|
53860
|
+
function extractFiles(xml) {
|
|
53861
|
+
const files2 = [];
|
|
53862
|
+
let remainingXml = xml;
|
|
53863
|
+
while (remainingXml.includes("<file")) {
|
|
53864
|
+
const fileStart = remainingXml.indexOf("<file");
|
|
53865
|
+
const fileEndTag = remainingXml.indexOf("</file>");
|
|
53866
|
+
if (fileEndTag === -1) {
|
|
53867
|
+
break;
|
|
53970
53868
|
}
|
|
53971
|
-
const
|
|
53972
|
-
|
|
53973
|
-
|
|
53869
|
+
const fileEnd = fileEndTag + 7;
|
|
53870
|
+
const fileXml = remainingXml.slice(fileStart, fileEnd);
|
|
53871
|
+
const pathMatch = fileXml.match(/path="([^"]*)"/);
|
|
53872
|
+
const actionMatch = fileXml.match(/action="([^"]*)"/);
|
|
53873
|
+
const path11 = pathMatch ? pathMatch[1] : "";
|
|
53874
|
+
const action = actionMatch ? actionMatch[1] : "modify";
|
|
53875
|
+
const changes = [];
|
|
53876
|
+
let changeXml = fileXml;
|
|
53877
|
+
while (changeXml.includes("<change>")) {
|
|
53878
|
+
const changeStart = changeXml.indexOf("<change>");
|
|
53879
|
+
const changeEndTag = changeXml.indexOf("</change>");
|
|
53880
|
+
if (changeEndTag === -1) {
|
|
53881
|
+
break;
|
|
53882
|
+
}
|
|
53883
|
+
const changeEnd = changeEndTag + 8;
|
|
53884
|
+
const currentChange = changeXml.slice(changeStart, changeEnd);
|
|
53885
|
+
const description = extractBetweenTags(currentChange, "description");
|
|
53886
|
+
let search = void 0;
|
|
53887
|
+
if (currentChange.includes("<search>") && currentChange.includes("</search>")) {
|
|
53888
|
+
const searchText = extractBetweenTags(currentChange, "search");
|
|
53889
|
+
try {
|
|
53890
|
+
if (searchText) {
|
|
53891
|
+
search = extractContent(searchText);
|
|
53892
|
+
}
|
|
53893
|
+
} catch (e) {
|
|
53894
|
+
}
|
|
53895
|
+
}
|
|
53896
|
+
let content = "";
|
|
53897
|
+
if (currentChange.includes("<content>") && currentChange.includes("</content>")) {
|
|
53898
|
+
const contentText = extractBetweenTags(currentChange, "content");
|
|
53899
|
+
try {
|
|
53900
|
+
if (contentText) {
|
|
53901
|
+
content = extractContent(contentText);
|
|
53902
|
+
} else {
|
|
53903
|
+
changeXml = changeXml.slice(changeEnd);
|
|
53904
|
+
continue;
|
|
53905
|
+
}
|
|
53906
|
+
} catch (e) {
|
|
53907
|
+
changeXml = changeXml.slice(changeEnd);
|
|
53908
|
+
continue;
|
|
53909
|
+
}
|
|
53910
|
+
} else {
|
|
53911
|
+
changeXml = changeXml.slice(changeEnd);
|
|
53912
|
+
continue;
|
|
53913
|
+
}
|
|
53914
|
+
changes.push({
|
|
53915
|
+
description,
|
|
53916
|
+
search: search || void 0,
|
|
53917
|
+
content
|
|
53918
|
+
});
|
|
53919
|
+
changeXml = changeXml.slice(changeEnd);
|
|
53974
53920
|
}
|
|
53975
|
-
|
|
53976
|
-
|
|
53977
|
-
|
|
53921
|
+
if (changes.length > 0) {
|
|
53922
|
+
files2.push({ path: path11, action, changes });
|
|
53923
|
+
}
|
|
53924
|
+
remainingXml = remainingXml.slice(fileEnd);
|
|
53978
53925
|
}
|
|
53979
|
-
|
|
53980
|
-
error("Error in findRepoLocation:", error87);
|
|
53981
|
-
return null;
|
|
53926
|
+
return files2;
|
|
53982
53927
|
}
|
|
53928
|
+
const plan = extractBetweenTags(xmlString, "Plan").trim();
|
|
53929
|
+
const files = extractFiles(xmlString);
|
|
53930
|
+
return { plan, files };
|
|
53983
53931
|
}
|
|
53984
|
-
|
|
53985
|
-
|
|
53986
|
-
|
|
53987
|
-
|
|
53988
|
-
|
|
53989
|
-
|
|
53990
|
-
|
|
53991
|
-
|
|
53932
|
+
function extractContent(text3) {
|
|
53933
|
+
const marker40 = "\n===\n";
|
|
53934
|
+
const firstIndex = text3.indexOf(marker40);
|
|
53935
|
+
const lastIndex = text3.lastIndexOf(marker40);
|
|
53936
|
+
if (firstIndex === -1 || lastIndex === -1 || firstIndex === lastIndex) {
|
|
53937
|
+
log("==============> ERROR no \\n===\\n", JSON.stringify(text3));
|
|
53938
|
+
throw new Error(
|
|
53939
|
+
"Input string must include at least two '\\n===\\n' markers"
|
|
53940
|
+
);
|
|
53992
53941
|
}
|
|
53942
|
+
return text3.substring(firstIndex + marker40.length, lastIndex);
|
|
53993
53943
|
}
|
|
53994
|
-
|
|
53995
|
-
|
|
53996
|
-
|
|
53997
|
-
|
|
53998
|
-
|
|
53999
|
-
|
|
54000
|
-
|
|
53944
|
+
|
|
53945
|
+
// src/proxy/utils.ts
|
|
53946
|
+
function getReposWithChangedFiles(code) {
|
|
53947
|
+
const repoNames = returnRepoNamesForCodeObject(code);
|
|
53948
|
+
log("Repo names to filter:", repoNames);
|
|
53949
|
+
return code.repos.filter((r) => {
|
|
53950
|
+
const rname = getRepoNameFromUrl(r.url);
|
|
53951
|
+
log("Checking repo:", rname);
|
|
53952
|
+
return repoNames.includes(rname);
|
|
53953
|
+
});
|
|
54001
53954
|
}
|
|
54002
53955
|
function returnRepoNamesForCodeObject(code) {
|
|
54003
53956
|
const repoNames = [];
|
|
@@ -54025,19 +53978,66 @@ function returnRepoNamesForCodeObject(code) {
|
|
|
54025
53978
|
}
|
|
54026
53979
|
return repoNames;
|
|
54027
53980
|
}
|
|
53981
|
+
async function findRepoLocation(repoFullName) {
|
|
53982
|
+
try {
|
|
53983
|
+
log(`Finding location for repo: ${repoFullName}`);
|
|
53984
|
+
if (repoFullName.startsWith("/") && await dirExists(repoFullName)) {
|
|
53985
|
+
return repoFullName;
|
|
53986
|
+
}
|
|
53987
|
+
const repoName = repoFullName.split("/").pop() || "";
|
|
53988
|
+
const repodir1 = `/workspaces/${repoName}`;
|
|
53989
|
+
if (await dirExists(repodir1)) {
|
|
53990
|
+
log(`Found repo in /workspaces: ${repodir1}`);
|
|
53991
|
+
return repodir1;
|
|
53992
|
+
}
|
|
53993
|
+
const repodir2 = `${process.cwd()}/${repoName}`;
|
|
53994
|
+
if (await dirExists(repodir2)) {
|
|
53995
|
+
log(`Found repo in current directory: ${repodir2}`);
|
|
53996
|
+
return repodir2;
|
|
53997
|
+
}
|
|
53998
|
+
try {
|
|
53999
|
+
const repodirs = (await fs5.readdir("/workspaces")).filter(
|
|
54000
|
+
(dir) => !dir.startsWith(".")
|
|
54001
|
+
);
|
|
54002
|
+
if (repodirs.length === 0) {
|
|
54003
|
+
warn("No workspace folders found");
|
|
54004
|
+
return null;
|
|
54005
|
+
}
|
|
54006
|
+
const repodir3 = `/workspaces/${repodirs[0]}`;
|
|
54007
|
+
if (await dirExists(repodir3)) {
|
|
54008
|
+
return repodir3;
|
|
54009
|
+
}
|
|
54010
|
+
return null;
|
|
54011
|
+
} catch (readdirError) {
|
|
54012
|
+
return null;
|
|
54013
|
+
}
|
|
54014
|
+
} catch (error87) {
|
|
54015
|
+
error("Error in findRepoLocation:", error87);
|
|
54016
|
+
return null;
|
|
54017
|
+
}
|
|
54018
|
+
}
|
|
54019
|
+
async function hasOnlyOneRepo() {
|
|
54020
|
+
try {
|
|
54021
|
+
const basePath = process.cwd().startsWith("/workspaces") ? "/workspaces" : process.cwd();
|
|
54022
|
+
const dirs = (await fs5.readdir(basePath, { withFileTypes: true })).filter((dirent) => dirent.isDirectory() && !dirent.name.startsWith(".")).map((dirent) => dirent.name);
|
|
54023
|
+
return dirs.length === 1;
|
|
54024
|
+
} catch (error87) {
|
|
54025
|
+
error("Error checking workspace directories:", error87);
|
|
54026
|
+
return false;
|
|
54027
|
+
}
|
|
54028
|
+
}
|
|
54029
|
+
function getRepoNameFromUrl(url3) {
|
|
54030
|
+
const matches = url3.match(/github\.com\/([^\/]+\/[^\/\.]+)(\.git)?$/);
|
|
54031
|
+
if (matches && matches[1]) {
|
|
54032
|
+
return matches[1];
|
|
54033
|
+
}
|
|
54034
|
+
const repoName = url3.split("/").pop()?.replace(".git", "") || "";
|
|
54035
|
+
return repoName;
|
|
54036
|
+
}
|
|
54028
54037
|
function is_repo_file(file3, reponame) {
|
|
54029
54038
|
const pathy = file3.path.replaceAll("ast/examples", "stakwork");
|
|
54030
54039
|
return pathy.startsWith(reponame + "/");
|
|
54031
54040
|
}
|
|
54032
|
-
function getReposWithChangedFiles(code) {
|
|
54033
|
-
const repoNames = returnRepoNamesForCodeObject(code);
|
|
54034
|
-
log("Repo names to filter:", repoNames);
|
|
54035
|
-
return code.repos.filter((r) => {
|
|
54036
|
-
const rname = getRepoNameFromUrl(r.url);
|
|
54037
|
-
log("Checking repo:", rname);
|
|
54038
|
-
return repoNames.includes(rname);
|
|
54039
|
-
});
|
|
54040
|
-
}
|
|
54041
54041
|
var RULES_PATTERNS = [
|
|
54042
54042
|
"**/.windsurfrules",
|
|
54043
54043
|
"**/.cursorrules",
|
|
@@ -54053,98 +54053,60 @@ function clean_file(file3, reponame) {
|
|
|
54053
54053
|
changes: file3.changes
|
|
54054
54054
|
};
|
|
54055
54055
|
}
|
|
54056
|
-
|
|
54057
|
-
|
|
54058
|
-
|
|
54059
|
-
|
|
54060
|
-
|
|
54061
|
-
|
|
54062
|
-
case "create":
|
|
54063
|
-
await createFile(filePath, file3.changes[0].content);
|
|
54064
|
-
break;
|
|
54065
|
-
case "rewrite":
|
|
54066
|
-
await rewriteFile(filePath, file3.changes[0].content);
|
|
54067
|
-
break;
|
|
54068
|
-
case "modify":
|
|
54069
|
-
await modifyFile(filePath, file3.changes);
|
|
54070
|
-
break;
|
|
54071
|
-
case "delete":
|
|
54072
|
-
await deleteFile(filePath);
|
|
54073
|
-
break;
|
|
54074
|
-
default:
|
|
54075
|
-
throw new Error(`Unknown action: ${file3.action}`);
|
|
54076
|
-
}
|
|
54056
|
+
function fail(res, e) {
|
|
54057
|
+
error(e);
|
|
54058
|
+
res.status(200).json({
|
|
54059
|
+
error: "Internal server error",
|
|
54060
|
+
message: e instanceof Error ? e.message : String(e)
|
|
54061
|
+
});
|
|
54077
54062
|
}
|
|
54078
|
-
async function
|
|
54063
|
+
async function handleRequest(req, res, endpoint, fallbackHandler, vscodeExtensionUrl) {
|
|
54079
54064
|
try {
|
|
54080
|
-
|
|
54081
|
-
|
|
54082
|
-
|
|
54083
|
-
|
|
54084
|
-
throw e;
|
|
54065
|
+
let url3 = `${vscodeExtensionUrl}/${endpoint}`;
|
|
54066
|
+
if (req.method === "GET" && Object.keys(req.query).length > 0) {
|
|
54067
|
+
const params = new URLSearchParams(req.query);
|
|
54068
|
+
url3 += `?${params.toString()}`;
|
|
54085
54069
|
}
|
|
54086
|
-
|
|
54087
|
-
|
|
54088
|
-
|
|
54089
|
-
|
|
54090
|
-
|
|
54091
|
-
}
|
|
54092
|
-
await fs6.writeFile(filePath, content, "utf8");
|
|
54093
|
-
}
|
|
54094
|
-
async function rewriteFile(filePath, content) {
|
|
54095
|
-
try {
|
|
54096
|
-
await fs6.stat(filePath);
|
|
54097
|
-
await fs6.writeFile(filePath, content, "utf8");
|
|
54098
|
-
} catch (e) {
|
|
54099
|
-
if (e.code === "ENOENT") {
|
|
54100
|
-
await createFile(filePath, content);
|
|
54101
|
-
} else {
|
|
54102
|
-
throw e;
|
|
54070
|
+
const forwardHeaders = {
|
|
54071
|
+
"Content-Type": "application/json"
|
|
54072
|
+
};
|
|
54073
|
+
if (req.headers.authorization) {
|
|
54074
|
+
forwardHeaders.authorization = req.headers.authorization;
|
|
54103
54075
|
}
|
|
54104
|
-
|
|
54105
|
-
|
|
54106
|
-
|
|
54107
|
-
|
|
54108
|
-
|
|
54109
|
-
|
|
54110
|
-
|
|
54111
|
-
|
|
54112
|
-
|
|
54113
|
-
|
|
54114
|
-
|
|
54115
|
-
|
|
54116
|
-
|
|
54117
|
-
const emsg = `Search pattern not found in ${filePath} ${finalChange.search}`;
|
|
54118
|
-
error(emsg);
|
|
54119
|
-
throw new Error(emsg);
|
|
54120
|
-
}
|
|
54076
|
+
const controller = new AbortController();
|
|
54077
|
+
const timeoutId = setTimeout(() => controller.abort(), 2e4);
|
|
54078
|
+
const response = await fetch(url3, {
|
|
54079
|
+
method: req.method,
|
|
54080
|
+
headers: forwardHeaders,
|
|
54081
|
+
body: req.method !== "GET" ? JSON.stringify(req.body) : void 0,
|
|
54082
|
+
signal: controller.signal
|
|
54083
|
+
});
|
|
54084
|
+
clearTimeout(timeoutId);
|
|
54085
|
+
if (!response.ok) {
|
|
54086
|
+
throw new Error(
|
|
54087
|
+
`VSCode extension server responded with status ${response.status}`
|
|
54088
|
+
);
|
|
54121
54089
|
}
|
|
54122
|
-
|
|
54123
|
-
|
|
54124
|
-
|
|
54125
|
-
|
|
54126
|
-
|
|
54127
|
-
|
|
54128
|
-
|
|
54129
|
-
|
|
54090
|
+
const data = await response.json();
|
|
54091
|
+
res.status(response.status).json(data);
|
|
54092
|
+
} catch (error87) {
|
|
54093
|
+
try {
|
|
54094
|
+
await fallbackHandler(req, res);
|
|
54095
|
+
} catch (fallbackError) {
|
|
54096
|
+
error(`Fallback also failed for ${endpoint}:`, fallbackError);
|
|
54097
|
+
if (!res.headersSent) {
|
|
54098
|
+
res.status(500).json({
|
|
54099
|
+
error: "Both VSCode and filesystem operations failed",
|
|
54100
|
+
message: fallbackError instanceof Error ? fallbackError.message : String(fallbackError)
|
|
54101
|
+
});
|
|
54130
54102
|
}
|
|
54131
54103
|
}
|
|
54132
|
-
if (originalContent !== newContent) {
|
|
54133
|
-
await fs6.writeFile(filePath, newContent, "utf8");
|
|
54134
|
-
} else {
|
|
54135
|
-
log(`=> NO CHANGES to file ${filePath}`);
|
|
54136
|
-
}
|
|
54137
|
-
} catch (e) {
|
|
54138
|
-
throw new Error(`Failed to modify file ${filePath}: ${e}`);
|
|
54139
54104
|
}
|
|
54140
54105
|
}
|
|
54141
|
-
async function deleteFile(filePath) {
|
|
54142
|
-
await fs6.unlink(filePath);
|
|
54143
|
-
}
|
|
54144
54106
|
|
|
54145
54107
|
// src/proxy/rules.ts
|
|
54146
|
-
var
|
|
54147
|
-
var
|
|
54108
|
+
var fs6 = __toESM(require("fs/promises"), 1);
|
|
54109
|
+
var path5 = __toESM(require("path"), 1);
|
|
54148
54110
|
|
|
54149
54111
|
// node_modules/glob/node_modules/minimatch/dist/esm/index.js
|
|
54150
54112
|
var import_brace_expansion = __toESM(require_brace_expansion(), 1);
|
|
@@ -54817,11 +54779,11 @@ var qmarksTestNoExtDot = ([$0]) => {
|
|
|
54817
54779
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
54818
54780
|
};
|
|
54819
54781
|
var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
54820
|
-
var
|
|
54782
|
+
var path4 = {
|
|
54821
54783
|
win32: { sep: "\\" },
|
|
54822
54784
|
posix: { sep: "/" }
|
|
54823
54785
|
};
|
|
54824
|
-
var sep = defaultPlatform === "win32" ?
|
|
54786
|
+
var sep = defaultPlatform === "win32" ? path4.win32.sep : path4.posix.sep;
|
|
54825
54787
|
minimatch.sep = sep;
|
|
54826
54788
|
var GLOBSTAR = Symbol("globstar **");
|
|
54827
54789
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -58870,8 +58832,8 @@ var PathScurryBase = class {
|
|
|
58870
58832
|
*
|
|
58871
58833
|
* @internal
|
|
58872
58834
|
*/
|
|
58873
|
-
constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs:
|
|
58874
|
-
this.#fs = fsFromOption(
|
|
58835
|
+
constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs: fs14 = defaultFS } = {}) {
|
|
58836
|
+
this.#fs = fsFromOption(fs14);
|
|
58875
58837
|
if (cwd instanceof URL || cwd.startsWith("file://")) {
|
|
58876
58838
|
cwd = (0, import_node_url.fileURLToPath)(cwd);
|
|
58877
58839
|
}
|
|
@@ -59429,8 +59391,8 @@ var PathScurryWin32 = class extends PathScurryBase {
|
|
|
59429
59391
|
/**
|
|
59430
59392
|
* @internal
|
|
59431
59393
|
*/
|
|
59432
|
-
newRoot(
|
|
59433
|
-
return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs:
|
|
59394
|
+
newRoot(fs14) {
|
|
59395
|
+
return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs14 });
|
|
59434
59396
|
}
|
|
59435
59397
|
/**
|
|
59436
59398
|
* Return true if the provided path string is an absolute path
|
|
@@ -59458,8 +59420,8 @@ var PathScurryPosix = class extends PathScurryBase {
|
|
|
59458
59420
|
/**
|
|
59459
59421
|
* @internal
|
|
59460
59422
|
*/
|
|
59461
|
-
newRoot(
|
|
59462
|
-
return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs:
|
|
59423
|
+
newRoot(fs14) {
|
|
59424
|
+
return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs14 });
|
|
59463
59425
|
}
|
|
59464
59426
|
/**
|
|
59465
59427
|
* Return true if the provided path string is an absolute path
|
|
@@ -60553,8 +60515,8 @@ async function gatherRulesFiles(workspaceRoot2) {
|
|
|
60553
60515
|
}
|
|
60554
60516
|
const readPromises = Array.from(uniqueFiles).map(async (filePath) => {
|
|
60555
60517
|
try {
|
|
60556
|
-
const content = await
|
|
60557
|
-
const relativePath =
|
|
60518
|
+
const content = await fs6.readFile(filePath, "utf8");
|
|
60519
|
+
const relativePath = path5.relative(workspaceRoot2, filePath);
|
|
60558
60520
|
return { path: relativePath, content };
|
|
60559
60521
|
} catch (error87) {
|
|
60560
60522
|
error(`Error reading file ${filePath}:`, error87);
|
|
@@ -60578,8 +60540,8 @@ async function loadGitignorePatterns(workspaceRoot2) {
|
|
|
60578
60540
|
"**/build/**"
|
|
60579
60541
|
];
|
|
60580
60542
|
try {
|
|
60581
|
-
const gitignorePath =
|
|
60582
|
-
const gitignoreContent = await
|
|
60543
|
+
const gitignorePath = path5.join(workspaceRoot2, ".gitignore");
|
|
60544
|
+
const gitignoreContent = await fs6.readFile(gitignorePath, "utf8");
|
|
60583
60545
|
const gitignorePatterns = gitignoreContent.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((pattern) => {
|
|
60584
60546
|
if (pattern.endsWith("/")) {
|
|
60585
60547
|
return `**/${pattern}**`;
|
|
@@ -60596,8 +60558,8 @@ async function loadGitignorePatterns(workspaceRoot2) {
|
|
|
60596
60558
|
// src/proxy/git.ts
|
|
60597
60559
|
var import_util2 = require("util");
|
|
60598
60560
|
var proc4 = __toESM(require("child_process"), 1);
|
|
60599
|
-
var
|
|
60600
|
-
var
|
|
60561
|
+
var fs7 = __toESM(require("fs/promises"), 1);
|
|
60562
|
+
var path6 = __toESM(require("path"), 1);
|
|
60601
60563
|
|
|
60602
60564
|
// node_modules/async-mutex/index.mjs
|
|
60603
60565
|
var E_TIMEOUT = new Error("timeout while waiting for mutex to become available");
|
|
@@ -60839,18 +60801,18 @@ var Repo = class {
|
|
|
60839
60801
|
return this.cwd;
|
|
60840
60802
|
}
|
|
60841
60803
|
async waitForGitLock(maxWaitMs = 5e3) {
|
|
60842
|
-
const lockFile =
|
|
60804
|
+
const lockFile = path6.join(this.cwd, ".git", "index.lock");
|
|
60843
60805
|
const startTime = Date.now();
|
|
60844
60806
|
while (Date.now() - startTime < maxWaitMs) {
|
|
60845
60807
|
try {
|
|
60846
|
-
await
|
|
60808
|
+
await fs7.access(lockFile);
|
|
60847
60809
|
await new Promise((resolve5) => setTimeout(resolve5, 50));
|
|
60848
60810
|
} catch {
|
|
60849
60811
|
return;
|
|
60850
60812
|
}
|
|
60851
60813
|
}
|
|
60852
60814
|
try {
|
|
60853
|
-
await
|
|
60815
|
+
await fs7.unlink(lockFile);
|
|
60854
60816
|
} catch {
|
|
60855
60817
|
}
|
|
60856
60818
|
throw new Error(`Git lock timeout after ${maxWaitMs}ms`);
|
|
@@ -61152,9 +61114,7 @@ var Repo = class {
|
|
|
61152
61114
|
const isRejectedDueToRemoteChanges = errorMessage.includes("[rejected]") || errorMessage.includes("non-fast-forward") || errorMessage.includes("Updates were rejected");
|
|
61153
61115
|
if (isRejectedDueToRemoteChanges) {
|
|
61154
61116
|
try {
|
|
61155
|
-
await this.execCommand(
|
|
61156
|
-
`git pull --rebase origin ${currentBranch}`
|
|
61157
|
-
);
|
|
61117
|
+
await this.execCommand(`git pull --rebase origin ${currentBranch}`);
|
|
61158
61118
|
} catch (pullError) {
|
|
61159
61119
|
const status = await this.execCommand("git status");
|
|
61160
61120
|
if (status.includes("rebase in progress") || status.includes("Unmerged") || status.includes("both modified")) {
|
|
@@ -61253,7 +61213,7 @@ var SSEManager = class {
|
|
|
61253
61213
|
var sseManager = new SSEManager();
|
|
61254
61214
|
|
|
61255
61215
|
// src/proxy/version.ts
|
|
61256
|
-
var VERSION = "0.3.
|
|
61216
|
+
var VERSION = "0.3.76";
|
|
61257
61217
|
|
|
61258
61218
|
// node_modules/uuid/dist/esm/stringify.js
|
|
61259
61219
|
var byteToHex = [];
|
|
@@ -61407,8 +61367,8 @@ function sanitizeShellArg(arg) {
|
|
|
61407
61367
|
}
|
|
61408
61368
|
|
|
61409
61369
|
// src/agent/goose.ts
|
|
61410
|
-
var
|
|
61411
|
-
var
|
|
61370
|
+
var fs8 = __toESM(require("fs"), 1);
|
|
61371
|
+
var path7 = __toESM(require("path"), 1);
|
|
61412
61372
|
var os = __toESM(require("os"), 1);
|
|
61413
61373
|
|
|
61414
61374
|
// src/agent/system_suffix.ts
|
|
@@ -121597,10 +121557,17 @@ function goose_env(apiKey) {
|
|
|
121597
121557
|
GOOSE_CLI_MIN_PRIORITY: "1.0",
|
|
121598
121558
|
CONFIGURE: "false",
|
|
121599
121559
|
ANTHROPIC_API_KEY: apiKey,
|
|
121600
|
-
PATH: `${
|
|
121560
|
+
PATH: `${path7.join(os.homedir(), ".local", "bin")}:${process.env.PATH}`
|
|
121601
121561
|
};
|
|
121602
121562
|
}
|
|
121603
|
-
async function runAgent({
|
|
121563
|
+
async function runAgent({
|
|
121564
|
+
prompt,
|
|
121565
|
+
apiKey,
|
|
121566
|
+
cwd,
|
|
121567
|
+
system_prompt,
|
|
121568
|
+
session,
|
|
121569
|
+
summarize
|
|
121570
|
+
}) {
|
|
121604
121571
|
createGooseConfig();
|
|
121605
121572
|
const env = goose_env(apiKey);
|
|
121606
121573
|
console.log("RUN goose with env", env);
|
|
@@ -121630,8 +121597,8 @@ async function runAgent({ prompt, apiKey, cwd, system_prompt, session, summarize
|
|
|
121630
121597
|
return output;
|
|
121631
121598
|
}
|
|
121632
121599
|
function createGooseConfig() {
|
|
121633
|
-
const configDir =
|
|
121634
|
-
const configPath =
|
|
121600
|
+
const configDir = path7.join(os.homedir(), ".config", "goose");
|
|
121601
|
+
const configPath = path7.join(configDir, "config.yaml");
|
|
121635
121602
|
const configContent = `extensions:
|
|
121636
121603
|
extension_name:
|
|
121637
121604
|
bundled: true
|
|
@@ -121642,10 +121609,10 @@ function createGooseConfig() {
|
|
|
121642
121609
|
type: "builtin"
|
|
121643
121610
|
`;
|
|
121644
121611
|
try {
|
|
121645
|
-
if (!
|
|
121646
|
-
|
|
121612
|
+
if (!fs8.existsSync(configDir)) {
|
|
121613
|
+
fs8.mkdirSync(configDir, { recursive: true });
|
|
121647
121614
|
}
|
|
121648
|
-
|
|
121615
|
+
fs8.writeFileSync(configPath, configContent, "utf-8");
|
|
121649
121616
|
console.log(`Created goose config at ${configPath}`);
|
|
121650
121617
|
} catch (error87) {
|
|
121651
121618
|
console.warn("Error creating goose config:", error87);
|
|
@@ -121677,7 +121644,11 @@ async function runGooseWeb(apiKey, cwd, port) {
|
|
|
121677
121644
|
|
|
121678
121645
|
// src/agent/claude_code.ts
|
|
121679
121646
|
var import_child_process3 = require("child_process");
|
|
121680
|
-
async function runAgent2({
|
|
121647
|
+
async function runAgent2({
|
|
121648
|
+
prompt,
|
|
121649
|
+
apiKey,
|
|
121650
|
+
cwd
|
|
121651
|
+
}) {
|
|
121681
121652
|
if (!apiKey) {
|
|
121682
121653
|
throw new Error("Missing ANTHROPIC_API_KEY");
|
|
121683
121654
|
}
|
|
@@ -121814,7 +121785,7 @@ function chooseAgent(agentName) {
|
|
|
121814
121785
|
}
|
|
121815
121786
|
|
|
121816
121787
|
// src/agent/validate.ts
|
|
121817
|
-
var
|
|
121788
|
+
var fs9 = __toESM(require("fs/promises"), 1);
|
|
121818
121789
|
var SYSTEM3 = `You are a frontend validation expert. Your job is to analyze logs and a screenshot to determine if a frontend application is running correctly.
|
|
121819
121790
|
|
|
121820
121791
|
You will be provided with:
|
|
@@ -121845,7 +121816,7 @@ Please analyze:
|
|
|
121845
121816
|
Return your analysis in the XML format specified in the system prompt.`;
|
|
121846
121817
|
};
|
|
121847
121818
|
async function runValidation(screenshotPath, logs, apiKey) {
|
|
121848
|
-
const imageBuffer = await
|
|
121819
|
+
const imageBuffer = await fs9.readFile(screenshotPath);
|
|
121849
121820
|
const base64Image = imageBuffer.toString("base64");
|
|
121850
121821
|
const systemMessage = {
|
|
121851
121822
|
role: "system",
|
|
@@ -132945,7 +132916,9 @@ async function streamGooseToSSE(sessionId, prompt, apiKey, res, options) {
|
|
|
132945
132916
|
id: completed.id,
|
|
132946
132917
|
text: completed.text,
|
|
132947
132918
|
timestamp: Date.now()
|
|
132948
|
-
}).catch(
|
|
132919
|
+
}).catch(
|
|
132920
|
+
(error87) => error("Error posting to webhook", error87)
|
|
132921
|
+
);
|
|
132949
132922
|
textAccumulators.delete(p.id);
|
|
132950
132923
|
}
|
|
132951
132924
|
break;
|
|
@@ -132957,7 +132930,9 @@ async function streamGooseToSSE(sessionId, prompt, apiKey, res, options) {
|
|
|
132957
132930
|
toolName: p.toolName,
|
|
132958
132931
|
input: p.input,
|
|
132959
132932
|
timestamp: Date.now()
|
|
132960
|
-
}).catch(
|
|
132933
|
+
}).catch(
|
|
132934
|
+
(error87) => error("Error posting to webhook", error87)
|
|
132935
|
+
);
|
|
132961
132936
|
break;
|
|
132962
132937
|
case "tool-result":
|
|
132963
132938
|
postToWebhook(options.webhookUrl, {
|
|
@@ -132967,7 +132942,9 @@ async function streamGooseToSSE(sessionId, prompt, apiKey, res, options) {
|
|
|
132967
132942
|
toolName: p.toolName,
|
|
132968
132943
|
output: truncateOutput(p.output),
|
|
132969
132944
|
timestamp: Date.now()
|
|
132970
|
-
}).catch(
|
|
132945
|
+
}).catch(
|
|
132946
|
+
(error87) => error("Error posting to webhook", error87)
|
|
132947
|
+
);
|
|
132971
132948
|
break;
|
|
132972
132949
|
}
|
|
132973
132950
|
}
|
|
@@ -133074,9 +133051,57 @@ function cleanup() {
|
|
|
133074
133051
|
}
|
|
133075
133052
|
setInterval(cleanup, 6e4);
|
|
133076
133053
|
|
|
133077
|
-
// src/
|
|
133078
|
-
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
133054
|
+
// src/agent/async_handler.ts
|
|
133079
133055
|
var import_path2 = __toESM(require("path"), 1);
|
|
133056
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
133057
|
+
var createAsyncAgentHandler = (getParams, transformResult) => {
|
|
133058
|
+
return async (req, res) => {
|
|
133059
|
+
const request_id = startReq();
|
|
133060
|
+
try {
|
|
133061
|
+
const { repoName, apiKey, agent_name, session, summarize } = req.body;
|
|
133062
|
+
const params = getParams(req);
|
|
133063
|
+
const workspaceRoot2 = await workspaceRoot();
|
|
133064
|
+
let repoPath = workspaceRoot2;
|
|
133065
|
+
if (repoName) {
|
|
133066
|
+
const candidate = import_path2.default.join(workspaceRoot2, repoName);
|
|
133067
|
+
try {
|
|
133068
|
+
const stat4 = await import_promises2.default.stat(candidate);
|
|
133069
|
+
if (stat4.isDirectory()) {
|
|
133070
|
+
repoPath = candidate;
|
|
133071
|
+
}
|
|
133072
|
+
} catch {
|
|
133073
|
+
}
|
|
133074
|
+
}
|
|
133075
|
+
const agentFn = chooseAgent(agent_name || "goose");
|
|
133076
|
+
agentFn({
|
|
133077
|
+
prompt: params.prompt,
|
|
133078
|
+
apiKey: apiKey || process.env.ANTHROPIC_API_KEY || "",
|
|
133079
|
+
cwd: repoPath,
|
|
133080
|
+
system_prompt: params.system,
|
|
133081
|
+
session,
|
|
133082
|
+
summarize: summarize ? true : false
|
|
133083
|
+
}).then((result) => {
|
|
133084
|
+
const finalResult = transformResult ? transformResult(result) : result;
|
|
133085
|
+
finishReq(request_id, {
|
|
133086
|
+
success: true,
|
|
133087
|
+
result: finalResult
|
|
133088
|
+
});
|
|
133089
|
+
}).catch((error87) => {
|
|
133090
|
+
failReq(request_id, error87);
|
|
133091
|
+
});
|
|
133092
|
+
res.json({ request_id, status: "pending" });
|
|
133093
|
+
} catch (e) {
|
|
133094
|
+
error("Error running agent:", e);
|
|
133095
|
+
res.status(500).json({
|
|
133096
|
+
error: "Internal server error",
|
|
133097
|
+
message: e instanceof Error ? e.message : String(e)
|
|
133098
|
+
});
|
|
133099
|
+
}
|
|
133100
|
+
};
|
|
133101
|
+
};
|
|
133102
|
+
|
|
133103
|
+
// src/proxy/server.ts
|
|
133104
|
+
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
133080
133105
|
|
|
133081
133106
|
// src/proxy/gitleaks.ts
|
|
133082
133107
|
var import_child_process4 = require("child_process");
|
|
@@ -133106,424 +133131,6 @@ function gitleaksProtect() {
|
|
|
133106
133131
|
}
|
|
133107
133132
|
}
|
|
133108
133133
|
|
|
133109
|
-
// src/proxy/gh.ts
|
|
133110
|
-
var import_child_process5 = require("child_process");
|
|
133111
|
-
var import_util18 = require("util");
|
|
133112
|
-
var execAsync = (0, import_util18.promisify)(import_child_process5.exec);
|
|
133113
|
-
function sanitizeGitHubText(text3) {
|
|
133114
|
-
return text3.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
133115
|
-
}
|
|
133116
|
-
var GitHubCLI = class {
|
|
133117
|
-
token;
|
|
133118
|
-
branch;
|
|
133119
|
-
owner;
|
|
133120
|
-
repo;
|
|
133121
|
-
cwd;
|
|
133122
|
-
constructor(token, branch, cwd) {
|
|
133123
|
-
this.token = token;
|
|
133124
|
-
this.branch = branch;
|
|
133125
|
-
this.cwd = cwd;
|
|
133126
|
-
}
|
|
133127
|
-
/**
|
|
133128
|
-
* Initialize and get repo info from git
|
|
133129
|
-
*/
|
|
133130
|
-
async init() {
|
|
133131
|
-
try {
|
|
133132
|
-
const { stdout } = await this.execGH("repo view --json owner,name");
|
|
133133
|
-
const data = JSON.parse(stdout);
|
|
133134
|
-
this.owner = data.owner.login;
|
|
133135
|
-
this.repo = data.name;
|
|
133136
|
-
} catch (error87) {
|
|
133137
|
-
throw new Error(`Failed to initialize: ${error87}`);
|
|
133138
|
-
}
|
|
133139
|
-
}
|
|
133140
|
-
/**
|
|
133141
|
-
* Execute gh CLI command with authentication
|
|
133142
|
-
*/
|
|
133143
|
-
async execGH(command) {
|
|
133144
|
-
const fullCommand = `gh ${command}`;
|
|
133145
|
-
try {
|
|
133146
|
-
return await execAsync(fullCommand, {
|
|
133147
|
-
cwd: this.cwd,
|
|
133148
|
-
env: {
|
|
133149
|
-
...process.env,
|
|
133150
|
-
GH_TOKEN: this.token,
|
|
133151
|
-
GITHUB_TOKEN: this.token
|
|
133152
|
-
}
|
|
133153
|
-
});
|
|
133154
|
-
} catch (error87) {
|
|
133155
|
-
throw new Error(
|
|
133156
|
-
`gh command failed: ${error87.message}
|
|
133157
|
-
Stderr: ${error87.stderr}`
|
|
133158
|
-
);
|
|
133159
|
-
}
|
|
133160
|
-
}
|
|
133161
|
-
/**
|
|
133162
|
-
* Sync version of execGH for simple checks
|
|
133163
|
-
*/
|
|
133164
|
-
execGHSync(command) {
|
|
133165
|
-
const fullCommand = `gh ${command}`;
|
|
133166
|
-
try {
|
|
133167
|
-
return (0, import_child_process5.execSync)(fullCommand, {
|
|
133168
|
-
cwd: this.cwd,
|
|
133169
|
-
encoding: "utf-8",
|
|
133170
|
-
env: {
|
|
133171
|
-
...process.env,
|
|
133172
|
-
GH_TOKEN: this.token,
|
|
133173
|
-
GITHUB_TOKEN: this.token
|
|
133174
|
-
}
|
|
133175
|
-
});
|
|
133176
|
-
} catch (error87) {
|
|
133177
|
-
throw new Error(
|
|
133178
|
-
`gh command failed: ${error87.message}
|
|
133179
|
-
Stderr: ${error87.stderr}`
|
|
133180
|
-
);
|
|
133181
|
-
}
|
|
133182
|
-
}
|
|
133183
|
-
/**
|
|
133184
|
-
* Check if the branch exists
|
|
133185
|
-
*/
|
|
133186
|
-
async branchExists() {
|
|
133187
|
-
try {
|
|
133188
|
-
const { stdout } = await execAsync(
|
|
133189
|
-
"git rev-parse --verify --quiet " + this.branch
|
|
133190
|
-
);
|
|
133191
|
-
return true;
|
|
133192
|
-
} catch {
|
|
133193
|
-
return false;
|
|
133194
|
-
}
|
|
133195
|
-
}
|
|
133196
|
-
/**
|
|
133197
|
-
* Get current branch name
|
|
133198
|
-
*/
|
|
133199
|
-
async getCurrentBranch() {
|
|
133200
|
-
const { stdout } = await execAsync("git rev-parse --abbrev-ref HEAD");
|
|
133201
|
-
return stdout.trim();
|
|
133202
|
-
}
|
|
133203
|
-
/**
|
|
133204
|
-
* Checkout the branch
|
|
133205
|
-
*/
|
|
133206
|
-
async checkoutBranch() {
|
|
133207
|
-
try {
|
|
133208
|
-
await execAsync(`git checkout ${this.branch}`);
|
|
133209
|
-
} catch (error87) {
|
|
133210
|
-
throw new Error(`Failed to checkout branch ${this.branch}: ${error87}`);
|
|
133211
|
-
}
|
|
133212
|
-
}
|
|
133213
|
-
/**
|
|
133214
|
-
* Create a new PR from the branch
|
|
133215
|
-
*/
|
|
133216
|
-
async createPR(options) {
|
|
133217
|
-
const args = [
|
|
133218
|
-
"pr",
|
|
133219
|
-
"create",
|
|
133220
|
-
"--head",
|
|
133221
|
-
this.branch,
|
|
133222
|
-
"--title",
|
|
133223
|
-
`"${sanitizeGitHubText(options.title)}"`
|
|
133224
|
-
];
|
|
133225
|
-
if (options.body) {
|
|
133226
|
-
args.push("--body", `"${sanitizeGitHubText(options.body)}"`);
|
|
133227
|
-
}
|
|
133228
|
-
if (options.base) {
|
|
133229
|
-
args.push("--base", options.base);
|
|
133230
|
-
}
|
|
133231
|
-
if (options.draft) {
|
|
133232
|
-
args.push("--draft");
|
|
133233
|
-
}
|
|
133234
|
-
if (options.assignees?.length) {
|
|
133235
|
-
args.push("--assignee", options.assignees.join(","));
|
|
133236
|
-
}
|
|
133237
|
-
if (options.reviewers?.length) {
|
|
133238
|
-
args.push("--reviewer", options.reviewers.join(","));
|
|
133239
|
-
}
|
|
133240
|
-
if (options.labels?.length) {
|
|
133241
|
-
args.push("--label", options.labels.join(","));
|
|
133242
|
-
}
|
|
133243
|
-
try {
|
|
133244
|
-
const { stdout } = await this.execGH(args.join(" "));
|
|
133245
|
-
const prUrl = stdout.trim();
|
|
133246
|
-
const prNumber = parseInt(prUrl.split("/").pop() || "0");
|
|
133247
|
-
return await this.getPR(prNumber);
|
|
133248
|
-
} catch (error87) {
|
|
133249
|
-
throw new Error(`Failed to create PR: ${error87}`);
|
|
133250
|
-
}
|
|
133251
|
-
}
|
|
133252
|
-
/**
|
|
133253
|
-
* Find PR by branch name
|
|
133254
|
-
*/
|
|
133255
|
-
async findPRByBranch() {
|
|
133256
|
-
try {
|
|
133257
|
-
const { stdout } = await this.execGH(
|
|
133258
|
-
`pr list --head ${this.branch} --json number,title,body,state,url,headRefName,baseRefName,mergeable,mergeStateStatus,isDraft,author,createdAt,updatedAt --limit 1`
|
|
133259
|
-
);
|
|
133260
|
-
const prs = JSON.parse(stdout);
|
|
133261
|
-
if (prs.length === 0) {
|
|
133262
|
-
return null;
|
|
133263
|
-
}
|
|
133264
|
-
const pr = prs[0];
|
|
133265
|
-
return {
|
|
133266
|
-
number: pr.number,
|
|
133267
|
-
title: pr.title,
|
|
133268
|
-
body: pr.body,
|
|
133269
|
-
state: pr.state,
|
|
133270
|
-
url: pr.url,
|
|
133271
|
-
headRefName: pr.headRefName,
|
|
133272
|
-
baseRefName: pr.baseRefName,
|
|
133273
|
-
mergeable: pr.mergeable,
|
|
133274
|
-
mergeStateStatus: pr.mergeStateStatus,
|
|
133275
|
-
isDraft: pr.isDraft,
|
|
133276
|
-
author: pr.author.login,
|
|
133277
|
-
createdAt: pr.createdAt,
|
|
133278
|
-
updatedAt: pr.updatedAt
|
|
133279
|
-
};
|
|
133280
|
-
} catch (error87) {
|
|
133281
|
-
throw new Error(`Failed to find PR: ${error87}`);
|
|
133282
|
-
}
|
|
133283
|
-
}
|
|
133284
|
-
/**
|
|
133285
|
-
* Get PR details by number
|
|
133286
|
-
*/
|
|
133287
|
-
async getPR(prNumber) {
|
|
133288
|
-
try {
|
|
133289
|
-
const { stdout } = await this.execGH(
|
|
133290
|
-
`pr view ${prNumber} --json number,title,body,state,url,headRefName,baseRefName,mergeable,mergeStateStatus,isDraft,author,createdAt,updatedAt`
|
|
133291
|
-
);
|
|
133292
|
-
const pr = JSON.parse(stdout);
|
|
133293
|
-
return {
|
|
133294
|
-
number: pr.number,
|
|
133295
|
-
title: pr.title,
|
|
133296
|
-
body: pr.body,
|
|
133297
|
-
state: pr.state,
|
|
133298
|
-
url: pr.url,
|
|
133299
|
-
headRefName: pr.headRefName,
|
|
133300
|
-
baseRefName: pr.baseRefName,
|
|
133301
|
-
mergeable: pr.mergeable,
|
|
133302
|
-
mergeStateStatus: pr.mergeStateStatus,
|
|
133303
|
-
isDraft: pr.isDraft,
|
|
133304
|
-
author: pr.author.login,
|
|
133305
|
-
createdAt: pr.createdAt,
|
|
133306
|
-
updatedAt: pr.updatedAt
|
|
133307
|
-
};
|
|
133308
|
-
} catch (error87) {
|
|
133309
|
-
throw new Error(`Failed to get PR #${prNumber}: ${error87}`);
|
|
133310
|
-
}
|
|
133311
|
-
}
|
|
133312
|
-
/**
|
|
133313
|
-
* Check if PR is auto-mergeable
|
|
133314
|
-
*/
|
|
133315
|
-
async isPRMergeable(prNumber) {
|
|
133316
|
-
const pr = await this.getPR(prNumber);
|
|
133317
|
-
const isMergeable = pr.mergeable === "MERGEABLE" && pr.mergeStateStatus === "CLEAN" && pr.state === "OPEN" && !pr.isDraft;
|
|
133318
|
-
let reason;
|
|
133319
|
-
if (!isMergeable) {
|
|
133320
|
-
if (pr.isDraft) {
|
|
133321
|
-
reason = "PR is in draft state";
|
|
133322
|
-
} else if (pr.mergeable === "CONFLICTING") {
|
|
133323
|
-
reason = "PR has merge conflicts";
|
|
133324
|
-
} else if (pr.mergeStateStatus === "BLOCKED") {
|
|
133325
|
-
reason = "PR is blocked by required checks";
|
|
133326
|
-
} else if (pr.mergeStateStatus === "BEHIND") {
|
|
133327
|
-
reason = "Branch is behind base branch";
|
|
133328
|
-
} else if (pr.mergeStateStatus === "UNSTABLE") {
|
|
133329
|
-
reason = "Some checks are failing";
|
|
133330
|
-
} else if (pr.mergeable === "UNKNOWN") {
|
|
133331
|
-
reason = "Mergeability is still being calculated";
|
|
133332
|
-
} else {
|
|
133333
|
-
reason = `Status: ${pr.mergeStateStatus}`;
|
|
133334
|
-
}
|
|
133335
|
-
}
|
|
133336
|
-
return { mergeable: isMergeable, reason };
|
|
133337
|
-
}
|
|
133338
|
-
/**
|
|
133339
|
-
* Merge a PR
|
|
133340
|
-
*/
|
|
133341
|
-
async mergePR(prNumber, options) {
|
|
133342
|
-
const {
|
|
133343
|
-
method = "squash",
|
|
133344
|
-
deleteBranch = true,
|
|
133345
|
-
auto = false,
|
|
133346
|
-
commitTitle,
|
|
133347
|
-
commitBody
|
|
133348
|
-
} = options || {};
|
|
133349
|
-
const args = ["pr", "merge", prNumber.toString()];
|
|
133350
|
-
if (auto) {
|
|
133351
|
-
args.push("--auto");
|
|
133352
|
-
}
|
|
133353
|
-
switch (method) {
|
|
133354
|
-
case "squash":
|
|
133355
|
-
args.push("--squash");
|
|
133356
|
-
break;
|
|
133357
|
-
case "rebase":
|
|
133358
|
-
args.push("--rebase");
|
|
133359
|
-
break;
|
|
133360
|
-
case "merge":
|
|
133361
|
-
args.push("--merge");
|
|
133362
|
-
break;
|
|
133363
|
-
}
|
|
133364
|
-
if (deleteBranch) {
|
|
133365
|
-
args.push("--delete-branch");
|
|
133366
|
-
}
|
|
133367
|
-
if (commitTitle) {
|
|
133368
|
-
args.push("--subject", `"${sanitizeGitHubText(commitTitle)}"`);
|
|
133369
|
-
}
|
|
133370
|
-
if (commitBody) {
|
|
133371
|
-
args.push("--body", `"${sanitizeGitHubText(commitBody)}"`);
|
|
133372
|
-
}
|
|
133373
|
-
try {
|
|
133374
|
-
const { stdout } = await this.execGH(args.join(" "));
|
|
133375
|
-
log(stdout);
|
|
133376
|
-
} catch (error87) {
|
|
133377
|
-
throw new Error(`Failed to merge PR #${prNumber}: ${error87}`);
|
|
133378
|
-
}
|
|
133379
|
-
}
|
|
133380
|
-
/**
|
|
133381
|
-
* Check if PR is mergeable and merge if ready
|
|
133382
|
-
*/
|
|
133383
|
-
async checkAndMerge(prNumber, options) {
|
|
133384
|
-
const { mergeable, reason } = await this.isPRMergeable(prNumber);
|
|
133385
|
-
if (!mergeable) {
|
|
133386
|
-
log(`PR #${prNumber} is not mergeable: ${reason}`);
|
|
133387
|
-
return false;
|
|
133388
|
-
}
|
|
133389
|
-
await this.mergePR(prNumber, options);
|
|
133390
|
-
return true;
|
|
133391
|
-
}
|
|
133392
|
-
/**
|
|
133393
|
-
* Wait for PR to be mergeable and then merge
|
|
133394
|
-
*/
|
|
133395
|
-
async waitAndMerge(prNumber, options) {
|
|
133396
|
-
const {
|
|
133397
|
-
maxWaitTime = 3e5,
|
|
133398
|
-
// 5 minutes
|
|
133399
|
-
checkInterval = 1e4,
|
|
133400
|
-
// 10 seconds
|
|
133401
|
-
...mergeOptions
|
|
133402
|
-
} = options || {};
|
|
133403
|
-
const startTime = Date.now();
|
|
133404
|
-
while (Date.now() - startTime < maxWaitTime) {
|
|
133405
|
-
const { mergeable, reason } = await this.isPRMergeable(prNumber);
|
|
133406
|
-
if (mergeable) {
|
|
133407
|
-
await this.mergePR(prNumber, mergeOptions);
|
|
133408
|
-
return true;
|
|
133409
|
-
}
|
|
133410
|
-
if (reason?.includes("conflict")) {
|
|
133411
|
-
throw new Error(`PR #${prNumber} has merge conflicts`);
|
|
133412
|
-
}
|
|
133413
|
-
log(`Waiting for PR #${prNumber} to be mergeable... (${reason})`);
|
|
133414
|
-
await new Promise((resolve5) => setTimeout(resolve5, checkInterval));
|
|
133415
|
-
}
|
|
133416
|
-
throw new Error(
|
|
133417
|
-
`Timeout: PR #${prNumber} did not become mergeable within ${maxWaitTime}ms`
|
|
133418
|
-
);
|
|
133419
|
-
}
|
|
133420
|
-
/**
|
|
133421
|
-
* Add comment to PR
|
|
133422
|
-
*/
|
|
133423
|
-
async addComment(prNumber, comment) {
|
|
133424
|
-
try {
|
|
133425
|
-
await this.execGH(
|
|
133426
|
-
`pr comment ${prNumber} --body "${sanitizeGitHubText(comment)}"`
|
|
133427
|
-
);
|
|
133428
|
-
} catch (error87) {
|
|
133429
|
-
throw new Error(`Failed to add comment to PR #${prNumber}: ${error87}`);
|
|
133430
|
-
}
|
|
133431
|
-
}
|
|
133432
|
-
/**
|
|
133433
|
-
* Update PR title and/or body
|
|
133434
|
-
*/
|
|
133435
|
-
async updatePR(prNumber, updates) {
|
|
133436
|
-
const args = ["pr", "edit", prNumber.toString()];
|
|
133437
|
-
if (updates.title) {
|
|
133438
|
-
args.push("--title", `"${sanitizeGitHubText(updates.title)}"`);
|
|
133439
|
-
}
|
|
133440
|
-
if (updates.body) {
|
|
133441
|
-
args.push("--body", `"${sanitizeGitHubText(updates.body)}"`);
|
|
133442
|
-
}
|
|
133443
|
-
try {
|
|
133444
|
-
await this.execGH(args.join(" "));
|
|
133445
|
-
} catch (error87) {
|
|
133446
|
-
throw new Error(`Failed to update PR #${prNumber}: ${error87}`);
|
|
133447
|
-
}
|
|
133448
|
-
}
|
|
133449
|
-
/**
|
|
133450
|
-
* Close PR
|
|
133451
|
-
*/
|
|
133452
|
-
async closePR(prNumber, comment) {
|
|
133453
|
-
try {
|
|
133454
|
-
if (comment) {
|
|
133455
|
-
await this.addComment(prNumber, comment);
|
|
133456
|
-
}
|
|
133457
|
-
await this.execGH(`pr close ${prNumber}`);
|
|
133458
|
-
} catch (error87) {
|
|
133459
|
-
throw new Error(`Failed to close PR #${prNumber}: ${error87}`);
|
|
133460
|
-
}
|
|
133461
|
-
}
|
|
133462
|
-
/**
|
|
133463
|
-
* Reopen PR
|
|
133464
|
-
*/
|
|
133465
|
-
async reopenPR(prNumber) {
|
|
133466
|
-
try {
|
|
133467
|
-
await this.execGH(`pr reopen ${prNumber}`);
|
|
133468
|
-
} catch (error87) {
|
|
133469
|
-
throw new Error(`Failed to reopen PR #${prNumber}: ${error87}`);
|
|
133470
|
-
}
|
|
133471
|
-
}
|
|
133472
|
-
/**
|
|
133473
|
-
* Get PR checks status
|
|
133474
|
-
*/
|
|
133475
|
-
async getPRChecks(prNumber) {
|
|
133476
|
-
try {
|
|
133477
|
-
const { stdout } = await this.execGH(
|
|
133478
|
-
`pr checks ${prNumber} --json name,state`
|
|
133479
|
-
);
|
|
133480
|
-
return JSON.parse(stdout);
|
|
133481
|
-
} catch (error87) {
|
|
133482
|
-
throw new Error(`Failed to get PR checks for #${prNumber}: ${error87}`);
|
|
133483
|
-
}
|
|
133484
|
-
}
|
|
133485
|
-
/**
|
|
133486
|
-
* Wait for CI checks to appear on a PR
|
|
133487
|
-
*/
|
|
133488
|
-
async waitForChecksToExist(prNumber, timeout = 3e4, interval = 2e3) {
|
|
133489
|
-
const start = Date.now();
|
|
133490
|
-
while (Date.now() - start < timeout) {
|
|
133491
|
-
const checks = await this.getPRChecks(prNumber);
|
|
133492
|
-
if (checks.length > 0) {
|
|
133493
|
-
log(`=> Found ${checks.length} CI check(s) for PR #${prNumber}`);
|
|
133494
|
-
return true;
|
|
133495
|
-
}
|
|
133496
|
-
await new Promise((r) => setTimeout(r, interval));
|
|
133497
|
-
}
|
|
133498
|
-
log(`=> No CI checks found for PR #${prNumber} within ${timeout}ms`);
|
|
133499
|
-
return false;
|
|
133500
|
-
}
|
|
133501
|
-
/**
|
|
133502
|
-
* Get repo info
|
|
133503
|
-
*/
|
|
133504
|
-
getRepoInfo() {
|
|
133505
|
-
return { owner: this.owner, repo: this.repo };
|
|
133506
|
-
}
|
|
133507
|
-
/**
|
|
133508
|
-
* Get branch name
|
|
133509
|
-
*/
|
|
133510
|
-
getBranch() {
|
|
133511
|
-
return this.branch;
|
|
133512
|
-
}
|
|
133513
|
-
async addLabelToPR(prNumber, label) {
|
|
133514
|
-
try {
|
|
133515
|
-
await this.execGH(`pr edit ${prNumber} --add-label ${label}`);
|
|
133516
|
-
} catch (error87) {
|
|
133517
|
-
if (error87 instanceof Error && error87.message.includes("not found")) {
|
|
133518
|
-
await this.execGH(`label create ${label}`);
|
|
133519
|
-
await this.execGH(`pr edit ${prNumber} --add-label "${label}"`);
|
|
133520
|
-
} else {
|
|
133521
|
-
throw error87;
|
|
133522
|
-
}
|
|
133523
|
-
}
|
|
133524
|
-
}
|
|
133525
|
-
};
|
|
133526
|
-
|
|
133527
133134
|
// src/proxy/playwright.ts
|
|
133528
133135
|
var fs11 = __toESM(require("fs/promises"), 1);
|
|
133529
133136
|
var path9 = __toESM(require("path"), 1);
|
|
@@ -133966,9 +133573,7 @@ async function cleanupConfig(configState) {
|
|
|
133966
133573
|
try {
|
|
133967
133574
|
if (configState.wasCreated) {
|
|
133968
133575
|
await fs11.unlink(configState.configPath);
|
|
133969
|
-
log(
|
|
133970
|
-
`Cleaned up created config file: ${configState.configPath}`
|
|
133971
|
-
);
|
|
133576
|
+
log(`Cleaned up created config file: ${configState.configPath}`);
|
|
133972
133577
|
} else if (configState.wasModified && configState.originalContent) {
|
|
133973
133578
|
await fs11.writeFile(configState.configPath, configState.originalContent);
|
|
133974
133579
|
log(`Restored original config: ${configState.configPath}`);
|
|
@@ -134064,15 +133669,829 @@ async function takeScreenshot(port) {
|
|
|
134064
133669
|
}
|
|
134065
133670
|
}
|
|
134066
133671
|
|
|
134067
|
-
// src/proxy/
|
|
134068
|
-
var
|
|
134069
|
-
var
|
|
134070
|
-
var
|
|
134071
|
-
|
|
134072
|
-
|
|
134073
|
-
|
|
134074
|
-
|
|
133672
|
+
// src/proxy/gh.ts
|
|
133673
|
+
var import_child_process5 = require("child_process");
|
|
133674
|
+
var import_util18 = require("util");
|
|
133675
|
+
var execAsync = (0, import_util18.promisify)(import_child_process5.exec);
|
|
133676
|
+
function sanitizeGitHubText(text3) {
|
|
133677
|
+
return text3.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
133678
|
+
}
|
|
133679
|
+
var GitHubCLI = class {
|
|
133680
|
+
token;
|
|
133681
|
+
branch;
|
|
133682
|
+
owner;
|
|
133683
|
+
repo;
|
|
133684
|
+
cwd;
|
|
133685
|
+
constructor(token, branch, cwd) {
|
|
133686
|
+
this.token = token;
|
|
133687
|
+
this.branch = branch;
|
|
133688
|
+
this.cwd = cwd;
|
|
133689
|
+
}
|
|
133690
|
+
/**
|
|
133691
|
+
* Initialize and get repo info from git
|
|
133692
|
+
*/
|
|
133693
|
+
async init() {
|
|
133694
|
+
try {
|
|
133695
|
+
const { stdout } = await this.execGH("repo view --json owner,name");
|
|
133696
|
+
const data = JSON.parse(stdout);
|
|
133697
|
+
this.owner = data.owner.login;
|
|
133698
|
+
this.repo = data.name;
|
|
133699
|
+
} catch (error87) {
|
|
133700
|
+
throw new Error(`Failed to initialize: ${error87}`);
|
|
133701
|
+
}
|
|
133702
|
+
}
|
|
133703
|
+
/**
|
|
133704
|
+
* Execute gh CLI command with authentication
|
|
133705
|
+
*/
|
|
133706
|
+
async execGH(command) {
|
|
133707
|
+
const fullCommand = `gh ${command}`;
|
|
133708
|
+
try {
|
|
133709
|
+
return await execAsync(fullCommand, {
|
|
133710
|
+
cwd: this.cwd,
|
|
133711
|
+
env: {
|
|
133712
|
+
...process.env,
|
|
133713
|
+
GH_TOKEN: this.token,
|
|
133714
|
+
GITHUB_TOKEN: this.token
|
|
133715
|
+
}
|
|
133716
|
+
});
|
|
133717
|
+
} catch (error87) {
|
|
133718
|
+
throw new Error(
|
|
133719
|
+
`gh command failed: ${error87.message}
|
|
133720
|
+
Stderr: ${error87.stderr}`
|
|
133721
|
+
);
|
|
133722
|
+
}
|
|
133723
|
+
}
|
|
133724
|
+
/**
|
|
133725
|
+
* Sync version of execGH for simple checks
|
|
133726
|
+
*/
|
|
133727
|
+
execGHSync(command) {
|
|
133728
|
+
const fullCommand = `gh ${command}`;
|
|
133729
|
+
try {
|
|
133730
|
+
return (0, import_child_process5.execSync)(fullCommand, {
|
|
133731
|
+
cwd: this.cwd,
|
|
133732
|
+
encoding: "utf-8",
|
|
133733
|
+
env: {
|
|
133734
|
+
...process.env,
|
|
133735
|
+
GH_TOKEN: this.token,
|
|
133736
|
+
GITHUB_TOKEN: this.token
|
|
133737
|
+
}
|
|
133738
|
+
});
|
|
133739
|
+
} catch (error87) {
|
|
133740
|
+
throw new Error(
|
|
133741
|
+
`gh command failed: ${error87.message}
|
|
133742
|
+
Stderr: ${error87.stderr}`
|
|
133743
|
+
);
|
|
133744
|
+
}
|
|
133745
|
+
}
|
|
133746
|
+
/**
|
|
133747
|
+
* Check if the branch exists
|
|
133748
|
+
*/
|
|
133749
|
+
async branchExists() {
|
|
133750
|
+
try {
|
|
133751
|
+
const { stdout } = await execAsync(
|
|
133752
|
+
"git rev-parse --verify --quiet " + this.branch
|
|
133753
|
+
);
|
|
133754
|
+
return true;
|
|
133755
|
+
} catch {
|
|
133756
|
+
return false;
|
|
133757
|
+
}
|
|
133758
|
+
}
|
|
133759
|
+
/**
|
|
133760
|
+
* Get current branch name
|
|
133761
|
+
*/
|
|
133762
|
+
async getCurrentBranch() {
|
|
133763
|
+
const { stdout } = await execAsync("git rev-parse --abbrev-ref HEAD");
|
|
133764
|
+
return stdout.trim();
|
|
133765
|
+
}
|
|
133766
|
+
/**
|
|
133767
|
+
* Checkout the branch
|
|
133768
|
+
*/
|
|
133769
|
+
async checkoutBranch() {
|
|
133770
|
+
try {
|
|
133771
|
+
await execAsync(`git checkout ${this.branch}`);
|
|
133772
|
+
} catch (error87) {
|
|
133773
|
+
throw new Error(`Failed to checkout branch ${this.branch}: ${error87}`);
|
|
133774
|
+
}
|
|
133775
|
+
}
|
|
133776
|
+
/**
|
|
133777
|
+
* Create a new PR from the branch
|
|
133778
|
+
*/
|
|
133779
|
+
async createPR(options) {
|
|
133780
|
+
const args = [
|
|
133781
|
+
"pr",
|
|
133782
|
+
"create",
|
|
133783
|
+
"--head",
|
|
133784
|
+
this.branch,
|
|
133785
|
+
"--title",
|
|
133786
|
+
`"${sanitizeGitHubText(options.title)}"`
|
|
133787
|
+
];
|
|
133788
|
+
if (options.body) {
|
|
133789
|
+
args.push("--body", `"${sanitizeGitHubText(options.body)}"`);
|
|
133790
|
+
}
|
|
133791
|
+
if (options.base) {
|
|
133792
|
+
args.push("--base", options.base);
|
|
133793
|
+
}
|
|
133794
|
+
if (options.draft) {
|
|
133795
|
+
args.push("--draft");
|
|
133796
|
+
}
|
|
133797
|
+
if (options.assignees?.length) {
|
|
133798
|
+
args.push("--assignee", options.assignees.join(","));
|
|
133799
|
+
}
|
|
133800
|
+
if (options.reviewers?.length) {
|
|
133801
|
+
args.push("--reviewer", options.reviewers.join(","));
|
|
133802
|
+
}
|
|
133803
|
+
if (options.labels?.length) {
|
|
133804
|
+
args.push("--label", options.labels.join(","));
|
|
133805
|
+
}
|
|
133806
|
+
try {
|
|
133807
|
+
const { stdout } = await this.execGH(args.join(" "));
|
|
133808
|
+
const prUrl = stdout.trim();
|
|
133809
|
+
const prNumber = parseInt(prUrl.split("/").pop() || "0");
|
|
133810
|
+
return await this.getPR(prNumber);
|
|
133811
|
+
} catch (error87) {
|
|
133812
|
+
throw new Error(`Failed to create PR: ${error87}`);
|
|
133813
|
+
}
|
|
133814
|
+
}
|
|
133815
|
+
/**
|
|
133816
|
+
* Find PR by branch name
|
|
133817
|
+
*/
|
|
133818
|
+
async findPRByBranch() {
|
|
133819
|
+
try {
|
|
133820
|
+
const { stdout } = await this.execGH(
|
|
133821
|
+
`pr list --head ${this.branch} --json number,title,body,state,url,headRefName,baseRefName,mergeable,mergeStateStatus,isDraft,author,createdAt,updatedAt --limit 1`
|
|
133822
|
+
);
|
|
133823
|
+
const prs = JSON.parse(stdout);
|
|
133824
|
+
if (prs.length === 0) {
|
|
133825
|
+
return null;
|
|
133826
|
+
}
|
|
133827
|
+
const pr = prs[0];
|
|
133828
|
+
return {
|
|
133829
|
+
number: pr.number,
|
|
133830
|
+
title: pr.title,
|
|
133831
|
+
body: pr.body,
|
|
133832
|
+
state: pr.state,
|
|
133833
|
+
url: pr.url,
|
|
133834
|
+
headRefName: pr.headRefName,
|
|
133835
|
+
baseRefName: pr.baseRefName,
|
|
133836
|
+
mergeable: pr.mergeable,
|
|
133837
|
+
mergeStateStatus: pr.mergeStateStatus,
|
|
133838
|
+
isDraft: pr.isDraft,
|
|
133839
|
+
author: pr.author.login,
|
|
133840
|
+
createdAt: pr.createdAt,
|
|
133841
|
+
updatedAt: pr.updatedAt
|
|
133842
|
+
};
|
|
133843
|
+
} catch (error87) {
|
|
133844
|
+
throw new Error(`Failed to find PR: ${error87}`);
|
|
133845
|
+
}
|
|
133846
|
+
}
|
|
133847
|
+
/**
|
|
133848
|
+
* Get PR details by number
|
|
133849
|
+
*/
|
|
133850
|
+
async getPR(prNumber) {
|
|
133851
|
+
try {
|
|
133852
|
+
const { stdout } = await this.execGH(
|
|
133853
|
+
`pr view ${prNumber} --json number,title,body,state,url,headRefName,baseRefName,mergeable,mergeStateStatus,isDraft,author,createdAt,updatedAt`
|
|
133854
|
+
);
|
|
133855
|
+
const pr = JSON.parse(stdout);
|
|
133856
|
+
return {
|
|
133857
|
+
number: pr.number,
|
|
133858
|
+
title: pr.title,
|
|
133859
|
+
body: pr.body,
|
|
133860
|
+
state: pr.state,
|
|
133861
|
+
url: pr.url,
|
|
133862
|
+
headRefName: pr.headRefName,
|
|
133863
|
+
baseRefName: pr.baseRefName,
|
|
133864
|
+
mergeable: pr.mergeable,
|
|
133865
|
+
mergeStateStatus: pr.mergeStateStatus,
|
|
133866
|
+
isDraft: pr.isDraft,
|
|
133867
|
+
author: pr.author.login,
|
|
133868
|
+
createdAt: pr.createdAt,
|
|
133869
|
+
updatedAt: pr.updatedAt
|
|
133870
|
+
};
|
|
133871
|
+
} catch (error87) {
|
|
133872
|
+
throw new Error(`Failed to get PR #${prNumber}: ${error87}`);
|
|
133873
|
+
}
|
|
133874
|
+
}
|
|
133875
|
+
/**
|
|
133876
|
+
* Check if PR is auto-mergeable
|
|
133877
|
+
*/
|
|
133878
|
+
async isPRMergeable(prNumber) {
|
|
133879
|
+
const pr = await this.getPR(prNumber);
|
|
133880
|
+
const isMergeable = pr.mergeable === "MERGEABLE" && pr.mergeStateStatus === "CLEAN" && pr.state === "OPEN" && !pr.isDraft;
|
|
133881
|
+
let reason;
|
|
133882
|
+
if (!isMergeable) {
|
|
133883
|
+
if (pr.isDraft) {
|
|
133884
|
+
reason = "PR is in draft state";
|
|
133885
|
+
} else if (pr.mergeable === "CONFLICTING") {
|
|
133886
|
+
reason = "PR has merge conflicts";
|
|
133887
|
+
} else if (pr.mergeStateStatus === "BLOCKED") {
|
|
133888
|
+
reason = "PR is blocked by required checks";
|
|
133889
|
+
} else if (pr.mergeStateStatus === "BEHIND") {
|
|
133890
|
+
reason = "Branch is behind base branch";
|
|
133891
|
+
} else if (pr.mergeStateStatus === "UNSTABLE") {
|
|
133892
|
+
reason = "Some checks are failing";
|
|
133893
|
+
} else if (pr.mergeable === "UNKNOWN") {
|
|
133894
|
+
reason = "Mergeability is still being calculated";
|
|
133895
|
+
} else {
|
|
133896
|
+
reason = `Status: ${pr.mergeStateStatus}`;
|
|
133897
|
+
}
|
|
133898
|
+
}
|
|
133899
|
+
return { mergeable: isMergeable, reason };
|
|
133900
|
+
}
|
|
133901
|
+
/**
|
|
133902
|
+
* Merge a PR
|
|
133903
|
+
*/
|
|
133904
|
+
async mergePR(prNumber, options) {
|
|
133905
|
+
const {
|
|
133906
|
+
method = "squash",
|
|
133907
|
+
deleteBranch = true,
|
|
133908
|
+
auto = false,
|
|
133909
|
+
commitTitle,
|
|
133910
|
+
commitBody
|
|
133911
|
+
} = options || {};
|
|
133912
|
+
const args = ["pr", "merge", prNumber.toString()];
|
|
133913
|
+
if (auto) {
|
|
133914
|
+
args.push("--auto");
|
|
133915
|
+
}
|
|
133916
|
+
switch (method) {
|
|
133917
|
+
case "squash":
|
|
133918
|
+
args.push("--squash");
|
|
133919
|
+
break;
|
|
133920
|
+
case "rebase":
|
|
133921
|
+
args.push("--rebase");
|
|
133922
|
+
break;
|
|
133923
|
+
case "merge":
|
|
133924
|
+
args.push("--merge");
|
|
133925
|
+
break;
|
|
133926
|
+
}
|
|
133927
|
+
if (deleteBranch) {
|
|
133928
|
+
args.push("--delete-branch");
|
|
133929
|
+
}
|
|
133930
|
+
if (commitTitle) {
|
|
133931
|
+
args.push("--subject", `"${sanitizeGitHubText(commitTitle)}"`);
|
|
133932
|
+
}
|
|
133933
|
+
if (commitBody) {
|
|
133934
|
+
args.push("--body", `"${sanitizeGitHubText(commitBody)}"`);
|
|
133935
|
+
}
|
|
133936
|
+
try {
|
|
133937
|
+
const { stdout } = await this.execGH(args.join(" "));
|
|
133938
|
+
log(stdout);
|
|
133939
|
+
} catch (error87) {
|
|
133940
|
+
throw new Error(`Failed to merge PR #${prNumber}: ${error87}`);
|
|
133941
|
+
}
|
|
133942
|
+
}
|
|
133943
|
+
/**
|
|
133944
|
+
* Check if PR is mergeable and merge if ready
|
|
133945
|
+
*/
|
|
133946
|
+
async checkAndMerge(prNumber, options) {
|
|
133947
|
+
const { mergeable, reason } = await this.isPRMergeable(prNumber);
|
|
133948
|
+
if (!mergeable) {
|
|
133949
|
+
log(`PR #${prNumber} is not mergeable: ${reason}`);
|
|
133950
|
+
return false;
|
|
133951
|
+
}
|
|
133952
|
+
await this.mergePR(prNumber, options);
|
|
133953
|
+
return true;
|
|
133954
|
+
}
|
|
133955
|
+
/**
|
|
133956
|
+
* Wait for PR to be mergeable and then merge
|
|
133957
|
+
*/
|
|
133958
|
+
async waitAndMerge(prNumber, options) {
|
|
133959
|
+
const {
|
|
133960
|
+
maxWaitTime = 3e5,
|
|
133961
|
+
// 5 minutes
|
|
133962
|
+
checkInterval = 1e4,
|
|
133963
|
+
// 10 seconds
|
|
133964
|
+
...mergeOptions
|
|
133965
|
+
} = options || {};
|
|
133966
|
+
const startTime = Date.now();
|
|
133967
|
+
while (Date.now() - startTime < maxWaitTime) {
|
|
133968
|
+
const { mergeable, reason } = await this.isPRMergeable(prNumber);
|
|
133969
|
+
if (mergeable) {
|
|
133970
|
+
await this.mergePR(prNumber, mergeOptions);
|
|
133971
|
+
return true;
|
|
133972
|
+
}
|
|
133973
|
+
if (reason?.includes("conflict")) {
|
|
133974
|
+
throw new Error(`PR #${prNumber} has merge conflicts`);
|
|
133975
|
+
}
|
|
133976
|
+
log(`Waiting for PR #${prNumber} to be mergeable... (${reason})`);
|
|
133977
|
+
await new Promise((resolve5) => setTimeout(resolve5, checkInterval));
|
|
133978
|
+
}
|
|
133979
|
+
throw new Error(
|
|
133980
|
+
`Timeout: PR #${prNumber} did not become mergeable within ${maxWaitTime}ms`
|
|
133981
|
+
);
|
|
133982
|
+
}
|
|
133983
|
+
/**
|
|
133984
|
+
* Add comment to PR
|
|
133985
|
+
*/
|
|
133986
|
+
async addComment(prNumber, comment) {
|
|
133987
|
+
try {
|
|
133988
|
+
await this.execGH(
|
|
133989
|
+
`pr comment ${prNumber} --body "${sanitizeGitHubText(comment)}"`
|
|
133990
|
+
);
|
|
133991
|
+
} catch (error87) {
|
|
133992
|
+
throw new Error(`Failed to add comment to PR #${prNumber}: ${error87}`);
|
|
133993
|
+
}
|
|
133994
|
+
}
|
|
133995
|
+
/**
|
|
133996
|
+
* Update PR title and/or body
|
|
133997
|
+
*/
|
|
133998
|
+
async updatePR(prNumber, updates) {
|
|
133999
|
+
const args = ["pr", "edit", prNumber.toString()];
|
|
134000
|
+
if (updates.title) {
|
|
134001
|
+
args.push("--title", `"${sanitizeGitHubText(updates.title)}"`);
|
|
134002
|
+
}
|
|
134003
|
+
if (updates.body) {
|
|
134004
|
+
args.push("--body", `"${sanitizeGitHubText(updates.body)}"`);
|
|
134005
|
+
}
|
|
134006
|
+
try {
|
|
134007
|
+
await this.execGH(args.join(" "));
|
|
134008
|
+
} catch (error87) {
|
|
134009
|
+
throw new Error(`Failed to update PR #${prNumber}: ${error87}`);
|
|
134010
|
+
}
|
|
134011
|
+
}
|
|
134012
|
+
/**
|
|
134013
|
+
* Close PR
|
|
134014
|
+
*/
|
|
134015
|
+
async closePR(prNumber, comment) {
|
|
134016
|
+
try {
|
|
134017
|
+
if (comment) {
|
|
134018
|
+
await this.addComment(prNumber, comment);
|
|
134019
|
+
}
|
|
134020
|
+
await this.execGH(`pr close ${prNumber}`);
|
|
134021
|
+
} catch (error87) {
|
|
134022
|
+
throw new Error(`Failed to close PR #${prNumber}: ${error87}`);
|
|
134023
|
+
}
|
|
134024
|
+
}
|
|
134025
|
+
/**
|
|
134026
|
+
* Reopen PR
|
|
134027
|
+
*/
|
|
134028
|
+
async reopenPR(prNumber) {
|
|
134029
|
+
try {
|
|
134030
|
+
await this.execGH(`pr reopen ${prNumber}`);
|
|
134031
|
+
} catch (error87) {
|
|
134032
|
+
throw new Error(`Failed to reopen PR #${prNumber}: ${error87}`);
|
|
134033
|
+
}
|
|
134034
|
+
}
|
|
134035
|
+
/**
|
|
134036
|
+
* Get PR checks status
|
|
134037
|
+
*/
|
|
134038
|
+
async getPRChecks(prNumber) {
|
|
134039
|
+
try {
|
|
134040
|
+
const { stdout } = await this.execGH(
|
|
134041
|
+
`pr checks ${prNumber} --json name,state`
|
|
134042
|
+
);
|
|
134043
|
+
return JSON.parse(stdout);
|
|
134044
|
+
} catch (error87) {
|
|
134045
|
+
throw new Error(`Failed to get PR checks for #${prNumber}: ${error87}`);
|
|
134046
|
+
}
|
|
134047
|
+
}
|
|
134048
|
+
/**
|
|
134049
|
+
* Wait for CI checks to appear on a PR
|
|
134050
|
+
*/
|
|
134051
|
+
async waitForChecksToExist(prNumber, timeout = 3e4, interval = 2e3) {
|
|
134052
|
+
const start = Date.now();
|
|
134053
|
+
while (Date.now() - start < timeout) {
|
|
134054
|
+
const checks = await this.getPRChecks(prNumber);
|
|
134055
|
+
if (checks.length > 0) {
|
|
134056
|
+
log(`=> Found ${checks.length} CI check(s) for PR #${prNumber}`);
|
|
134057
|
+
return true;
|
|
134058
|
+
}
|
|
134059
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
134060
|
+
}
|
|
134061
|
+
log(`=> No CI checks found for PR #${prNumber} within ${timeout}ms`);
|
|
134062
|
+
return false;
|
|
134063
|
+
}
|
|
134064
|
+
/**
|
|
134065
|
+
* Get repo info
|
|
134066
|
+
*/
|
|
134067
|
+
getRepoInfo() {
|
|
134068
|
+
return { owner: this.owner, repo: this.repo };
|
|
134069
|
+
}
|
|
134070
|
+
/**
|
|
134071
|
+
* Get branch name
|
|
134072
|
+
*/
|
|
134073
|
+
getBranch() {
|
|
134074
|
+
return this.branch;
|
|
134075
|
+
}
|
|
134076
|
+
async addLabelToPR(prNumber, label) {
|
|
134077
|
+
try {
|
|
134078
|
+
await this.execGH(`pr edit ${prNumber} --add-label ${label}`);
|
|
134079
|
+
} catch (error87) {
|
|
134080
|
+
if (error87 instanceof Error && error87.message.includes("not found")) {
|
|
134081
|
+
await this.execGH(`label create ${label}`);
|
|
134082
|
+
await this.execGH(`pr edit ${prNumber} --add-label "${label}"`);
|
|
134083
|
+
} else {
|
|
134084
|
+
throw error87;
|
|
134085
|
+
}
|
|
134086
|
+
}
|
|
134087
|
+
}
|
|
134088
|
+
};
|
|
134089
|
+
|
|
134090
|
+
// src/proxy/git_actions.ts
|
|
134091
|
+
async function handleBranchDiff(req, res) {
|
|
134092
|
+
try {
|
|
134093
|
+
const results = [];
|
|
134094
|
+
const repos = await getReposMaybe();
|
|
134095
|
+
const baseBranch = req.query.base || "main";
|
|
134096
|
+
for (const r of repos) {
|
|
134097
|
+
const repo = await NewRepo(r);
|
|
134098
|
+
const repoName = getRepoNameFromUrl(r);
|
|
134099
|
+
const currentBranch = (await repo.printCurrentBranch()).trim();
|
|
134100
|
+
if (currentBranch === baseBranch) {
|
|
134101
|
+
continue;
|
|
134102
|
+
}
|
|
134103
|
+
let mergeBase;
|
|
134104
|
+
try {
|
|
134105
|
+
mergeBase = (await repo.execCommand(`git merge-base ${baseBranch} HEAD`)).trim();
|
|
134106
|
+
} catch {
|
|
134107
|
+
try {
|
|
134108
|
+
mergeBase = (await repo.execCommand(`git merge-base origin/${baseBranch} HEAD`)).trim();
|
|
134109
|
+
} catch {
|
|
134110
|
+
warn(
|
|
134111
|
+
`Could not find merge base for ${repoName}, skipping...`
|
|
134112
|
+
);
|
|
134113
|
+
continue;
|
|
134114
|
+
}
|
|
134115
|
+
}
|
|
134116
|
+
const diffOutput = await repo.execCommand(
|
|
134117
|
+
`git diff --name-status ${mergeBase}`
|
|
134118
|
+
);
|
|
134119
|
+
const lines = diffOutput.trim().split("\n").filter((line) => line);
|
|
134120
|
+
for (const line of lines) {
|
|
134121
|
+
const parts = line.split(" ");
|
|
134122
|
+
if (parts.length < 2) {
|
|
134123
|
+
continue;
|
|
134124
|
+
}
|
|
134125
|
+
const status = parts[0];
|
|
134126
|
+
const filePath = parts[1];
|
|
134127
|
+
let action;
|
|
134128
|
+
if (status.startsWith("A")) {
|
|
134129
|
+
action = "create";
|
|
134130
|
+
} else if (status.startsWith("D")) {
|
|
134131
|
+
action = "delete";
|
|
134132
|
+
} else {
|
|
134133
|
+
action = "modify";
|
|
134134
|
+
}
|
|
134135
|
+
let content = "";
|
|
134136
|
+
try {
|
|
134137
|
+
content = await repo.execCommand(
|
|
134138
|
+
`git diff ${mergeBase} -- "${filePath}"`
|
|
134139
|
+
);
|
|
134140
|
+
} catch (error87) {
|
|
134141
|
+
warn(`Error getting branch diff for ${filePath}:`, error87);
|
|
134142
|
+
}
|
|
134143
|
+
results.push({
|
|
134144
|
+
file: `${repoName}/${filePath}`,
|
|
134145
|
+
action,
|
|
134146
|
+
content,
|
|
134147
|
+
repoName,
|
|
134148
|
+
errors: []
|
|
134149
|
+
});
|
|
134150
|
+
}
|
|
134151
|
+
}
|
|
134152
|
+
res.status(200).json(results);
|
|
134153
|
+
} catch (error87) {
|
|
134154
|
+
error("Error fetching branch diff:", error87);
|
|
134155
|
+
fail(res, error87);
|
|
134156
|
+
}
|
|
134157
|
+
}
|
|
134158
|
+
async function handleDiff(_req, res) {
|
|
134159
|
+
try {
|
|
134160
|
+
const results = [];
|
|
134161
|
+
const repos = await getReposMaybe();
|
|
134162
|
+
for (const r of repos) {
|
|
134163
|
+
const repo = await NewRepo(r);
|
|
134164
|
+
const repoName = getRepoNameFromUrl(r);
|
|
134165
|
+
const diffOutput = await repo.execCommand("git diff --name-status HEAD");
|
|
134166
|
+
const untrackedOutput = await repo.execCommand(
|
|
134167
|
+
"git ls-files --others --exclude-standard"
|
|
134168
|
+
);
|
|
134169
|
+
const lines = diffOutput.trim().split("\n").filter((line) => line);
|
|
134170
|
+
for (const line of lines) {
|
|
134171
|
+
const parts = line.split(" ");
|
|
134172
|
+
if (parts.length < 2) {
|
|
134173
|
+
continue;
|
|
134174
|
+
}
|
|
134175
|
+
const status = parts[0];
|
|
134176
|
+
const filePath = parts[1];
|
|
134177
|
+
let action;
|
|
134178
|
+
if (status.startsWith("A")) {
|
|
134179
|
+
action = "create";
|
|
134180
|
+
} else if (status.startsWith("D")) {
|
|
134181
|
+
action = "delete";
|
|
134182
|
+
} else {
|
|
134183
|
+
action = "modify";
|
|
134184
|
+
}
|
|
134185
|
+
let content = "";
|
|
134186
|
+
try {
|
|
134187
|
+
if (action === "delete") {
|
|
134188
|
+
content = await repo.execCommand(`git diff HEAD -- "${filePath}"`);
|
|
134189
|
+
} else {
|
|
134190
|
+
log("Getting diff for", filePath);
|
|
134191
|
+
content = await repo.execCommand(`git diff HEAD -- "${filePath}"`);
|
|
134192
|
+
}
|
|
134193
|
+
} catch (error87) {
|
|
134194
|
+
warn(`Error getting diff for ${filePath}:`, error87);
|
|
134195
|
+
}
|
|
134196
|
+
results.push({
|
|
134197
|
+
file: `${repoName}/${filePath}`,
|
|
134198
|
+
action,
|
|
134199
|
+
content,
|
|
134200
|
+
repoName,
|
|
134201
|
+
errors: []
|
|
134202
|
+
});
|
|
134203
|
+
}
|
|
134204
|
+
const untrackedLines = untrackedOutput.trim().split("\n").filter((line) => line);
|
|
134205
|
+
for (const filePath of untrackedLines) {
|
|
134206
|
+
let content = "";
|
|
134207
|
+
try {
|
|
134208
|
+
const fileContent = await repo.execCommand(`cat "${filePath}"`);
|
|
134209
|
+
const lines2 = fileContent.split("\n");
|
|
134210
|
+
content = `--- /dev/null
|
|
134211
|
+
+++ b/${filePath}
|
|
134212
|
+
@@ -0,0 +1,${lines2.length} @@
|
|
134213
|
+
${lines2.map((line) => "+" + line).join("\n")}`;
|
|
134214
|
+
} catch (error87) {
|
|
134215
|
+
warn(`Error reading untracked file ${filePath}:`, error87);
|
|
134216
|
+
}
|
|
134217
|
+
results.push({
|
|
134218
|
+
file: `${repoName}/${filePath}`,
|
|
134219
|
+
action: "create",
|
|
134220
|
+
content,
|
|
134221
|
+
repoName,
|
|
134222
|
+
errors: []
|
|
134223
|
+
});
|
|
134224
|
+
}
|
|
134225
|
+
}
|
|
134226
|
+
res.status(200).json(results);
|
|
134227
|
+
} catch (error87) {
|
|
134228
|
+
error("Error fetching git diff:", error87);
|
|
134229
|
+
fail(res, error87);
|
|
134230
|
+
}
|
|
134231
|
+
}
|
|
134232
|
+
async function handleCommit(req, res) {
|
|
134233
|
+
try {
|
|
134234
|
+
const code = req.body;
|
|
134235
|
+
const repos = getReposWithChangedFiles(code);
|
|
134236
|
+
log(`=> COMMIT for repo ${repos.map((r) => r.url).join(", ")}`);
|
|
134237
|
+
if (repos.length === 0) {
|
|
134238
|
+
warn("No repos with changed files found");
|
|
134239
|
+
res.json({ success: false, message: "No changed repos" });
|
|
134240
|
+
return;
|
|
134241
|
+
}
|
|
134242
|
+
for (const repodata of repos) {
|
|
134243
|
+
const repo = await NewRepo(repodata.url);
|
|
134244
|
+
log(`=> COMMIT in ${repo.printcwd()}`);
|
|
134245
|
+
const isOnMain = await repo.isOnMainBranch();
|
|
134246
|
+
const isOnBaseBranch = await repo.isOnBranch(repodata.base_branch);
|
|
134247
|
+
if (isOnMain || isOnBaseBranch) {
|
|
134248
|
+
log(`=> CHECKOUT to new branch: ${repodata.branch_name}`);
|
|
134249
|
+
await repo.checkoutNew(repodata.branch_name);
|
|
134250
|
+
}
|
|
134251
|
+
await repo.addAll();
|
|
134252
|
+
log(
|
|
134253
|
+
`=> COMMIT to ${repodata.url} new branch: ${repodata.branch_name}`
|
|
134254
|
+
);
|
|
134255
|
+
await repo.commitWithCredentials(
|
|
134256
|
+
repodata.commit_name,
|
|
134257
|
+
code.git_credentials
|
|
134258
|
+
);
|
|
134259
|
+
}
|
|
134260
|
+
res.json({ success: true });
|
|
134261
|
+
} catch (e) {
|
|
134262
|
+
error(e);
|
|
134263
|
+
fail(res, e);
|
|
134264
|
+
}
|
|
134075
134265
|
}
|
|
134266
|
+
async function handlePush(req, res) {
|
|
134267
|
+
try {
|
|
134268
|
+
const code = req.body;
|
|
134269
|
+
const repos = getReposWithChangedFiles(code);
|
|
134270
|
+
const createPR = req.query.pr === "true";
|
|
134271
|
+
const shouldCommit = req.query.commit === "true";
|
|
134272
|
+
const autoMerge = req.query.automerge === "true";
|
|
134273
|
+
const label = req.query.label;
|
|
134274
|
+
const stayOnCurrentBranch = req.query.stayOnCurrentBranch === "true";
|
|
134275
|
+
const commits = [];
|
|
134276
|
+
const prs = {};
|
|
134277
|
+
const prErrors = {};
|
|
134278
|
+
const branches = {};
|
|
134279
|
+
log(`=> Commit requested: ${shouldCommit}`);
|
|
134280
|
+
log(`=> PR creation requested: ${createPR}`);
|
|
134281
|
+
log(`=> Auto-merge requested: ${autoMerge}`);
|
|
134282
|
+
log(
|
|
134283
|
+
`=> Git credentials available: ${!!code.git_credentials?.auth_data?.token}`
|
|
134284
|
+
);
|
|
134285
|
+
for (const r of repos) {
|
|
134286
|
+
const repo = await NewRepo(r.url);
|
|
134287
|
+
const repoName = getRepoNameFromUrl(r.url);
|
|
134288
|
+
let currentBranch = (await repo.printCurrentBranch()).trim();
|
|
134289
|
+
const requestedBranch = stayOnCurrentBranch ? currentBranch : await repo.generateUniqueBranchName(r.branch_name);
|
|
134290
|
+
log(`=> Current branch: ${currentBranch}`);
|
|
134291
|
+
log(`=> Requested branch: ${requestedBranch}`);
|
|
134292
|
+
const protectedBranches = ["main", "master"];
|
|
134293
|
+
const isOnProtectedBranch = protectedBranches.includes(currentBranch);
|
|
134294
|
+
const shouldSwitchBranch = (!stayOnCurrentBranch || isOnProtectedBranch) && currentBranch !== requestedBranch;
|
|
134295
|
+
if (shouldSwitchBranch) {
|
|
134296
|
+
const branchExists = await repo.branchExists(requestedBranch);
|
|
134297
|
+
if (branchExists) {
|
|
134298
|
+
log(`=> Switching to existing branch: ${requestedBranch}`);
|
|
134299
|
+
await repo.checkout(requestedBranch);
|
|
134300
|
+
} else {
|
|
134301
|
+
log(
|
|
134302
|
+
`=> Creating new branch from current position: ${requestedBranch}`
|
|
134303
|
+
);
|
|
134304
|
+
await repo.checkoutNew(requestedBranch);
|
|
134305
|
+
}
|
|
134306
|
+
} else {
|
|
134307
|
+
log(`=> Already on correct branch: ${requestedBranch}`);
|
|
134308
|
+
}
|
|
134309
|
+
if (shouldCommit) {
|
|
134310
|
+
log(`=> COMMIT to ${r.url}`);
|
|
134311
|
+
log(`=> COMMIT in ${repo.printcwd()}`);
|
|
134312
|
+
const status = await repo.execCommand("git status --porcelain");
|
|
134313
|
+
if (status.trim()) {
|
|
134314
|
+
log(`=> Found uncommitted changes, committing...`);
|
|
134315
|
+
await repo.addAll();
|
|
134316
|
+
currentBranch = (await repo.printCurrentBranch()).trim();
|
|
134317
|
+
log(`=> COMMIT to ${r.url} on branch: ${currentBranch}`);
|
|
134318
|
+
await repo.commitWithCredentials(
|
|
134319
|
+
r.commit_name || `Changes from ${currentBranch}`,
|
|
134320
|
+
code.git_credentials
|
|
134321
|
+
);
|
|
134322
|
+
log(`=> Commit successful`);
|
|
134323
|
+
} else {
|
|
134324
|
+
log(
|
|
134325
|
+
`=> No uncommitted changes, skipping commit (may have been committed by agent)`
|
|
134326
|
+
);
|
|
134327
|
+
}
|
|
134328
|
+
}
|
|
134329
|
+
log(`=> PUSH to ${r.url}`);
|
|
134330
|
+
await repo.pushCurrentBranchWithCredentials(code.git_credentials);
|
|
134331
|
+
log(`=> Push successful`);
|
|
134332
|
+
const link = await repo.getLatestCommitLink();
|
|
134333
|
+
commits.push(link);
|
|
134334
|
+
currentBranch = (await repo.printCurrentBranch()).trim();
|
|
134335
|
+
branches[repoName] = currentBranch;
|
|
134336
|
+
log(`=> Branch for ${repoName}: ${currentBranch}`);
|
|
134337
|
+
if (createPR) {
|
|
134338
|
+
if (!code.git_credentials?.auth_data?.token) {
|
|
134339
|
+
error(`=> No GitHub token available for PR creation`);
|
|
134340
|
+
prErrors[repoName] = "GitHub token required for PR creation";
|
|
134341
|
+
continue;
|
|
134342
|
+
}
|
|
134343
|
+
try {
|
|
134344
|
+
log(`=> Creating PR for ${r.url}`);
|
|
134345
|
+
log(`=> Branch: ${r.branch_name}`);
|
|
134346
|
+
log(`=> Base: ${r.base_branch || "main"}`);
|
|
134347
|
+
const repoLocation = repo.printcwd();
|
|
134348
|
+
log(`=> Repo location: ${repoLocation}`);
|
|
134349
|
+
const currentBranchRaw = await repo.execCommand(
|
|
134350
|
+
"git rev-parse --abbrev-ref HEAD"
|
|
134351
|
+
);
|
|
134352
|
+
const currentBranch2 = currentBranchRaw.trim();
|
|
134353
|
+
log(`=> Current branch: ${currentBranch2}`);
|
|
134354
|
+
const baseBranch = r.base_branch || "main";
|
|
134355
|
+
if (currentBranch2 === baseBranch) {
|
|
134356
|
+
log(
|
|
134357
|
+
`=> Cannot create PR: currently on base branch (${baseBranch})`
|
|
134358
|
+
);
|
|
134359
|
+
prErrors[repoName] = `Cannot create PR - currently on base branch (${baseBranch}). Commit changes to a feature branch first.`;
|
|
134360
|
+
continue;
|
|
134361
|
+
}
|
|
134362
|
+
try {
|
|
134363
|
+
const diff = await repo.execCommand(
|
|
134364
|
+
`git log origin/${baseBranch}..HEAD --oneline`
|
|
134365
|
+
);
|
|
134366
|
+
if (!diff.trim()) {
|
|
134367
|
+
log(
|
|
134368
|
+
`=> No commits between ${baseBranch} and ${currentBranch2}`
|
|
134369
|
+
);
|
|
134370
|
+
prErrors[repoName] = `No commits to create PR - ${currentBranch2} is up to date with ${baseBranch}`;
|
|
134371
|
+
continue;
|
|
134372
|
+
}
|
|
134373
|
+
log(`=> Commits to include in PR:
|
|
134374
|
+
${diff.trim()}`);
|
|
134375
|
+
} catch (diffError) {
|
|
134376
|
+
warn(`=> Could not check commit diff: ${diffError}`);
|
|
134377
|
+
}
|
|
134378
|
+
const gh = new GitHubCLI(
|
|
134379
|
+
code.git_credentials.auth_data.token,
|
|
134380
|
+
currentBranch2,
|
|
134381
|
+
repoLocation
|
|
134382
|
+
);
|
|
134383
|
+
await gh.init();
|
|
134384
|
+
const existingPR = await gh.findPRByBranch();
|
|
134385
|
+
let prNumber;
|
|
134386
|
+
if (existingPR) {
|
|
134387
|
+
log(`=> PR already exists: ${existingPR.url}`);
|
|
134388
|
+
prs[repoName] = existingPR.url;
|
|
134389
|
+
prNumber = existingPR.number;
|
|
134390
|
+
} else {
|
|
134391
|
+
const commitMsg = r.commit_name || `Changes from ${currentBranch2}`;
|
|
134392
|
+
const { title, body } = processCommitMessage(commitMsg);
|
|
134393
|
+
const pr = await gh.createPR({
|
|
134394
|
+
title,
|
|
134395
|
+
body,
|
|
134396
|
+
base: baseBranch,
|
|
134397
|
+
draft: false
|
|
134398
|
+
});
|
|
134399
|
+
log(`=> PR created: ${pr.url}`);
|
|
134400
|
+
prs[repoName] = pr.url;
|
|
134401
|
+
prNumber = pr.number;
|
|
134402
|
+
if (label) {
|
|
134403
|
+
try {
|
|
134404
|
+
await gh.addLabelToPR(pr.number, label);
|
|
134405
|
+
} catch (e) {
|
|
134406
|
+
error(`=> PR failed to add label: ${e}`);
|
|
134407
|
+
}
|
|
134408
|
+
}
|
|
134409
|
+
}
|
|
134410
|
+
if (autoMerge) {
|
|
134411
|
+
try {
|
|
134412
|
+
log(
|
|
134413
|
+
`=> Waiting for CI checks to appear for PR #${prNumber}...`
|
|
134414
|
+
);
|
|
134415
|
+
const hasChecks = await gh.waitForChecksToExist(prNumber, 3e4);
|
|
134416
|
+
if (hasChecks) {
|
|
134417
|
+
log(`=> Enabling auto-merge for PR #${prNumber}...`);
|
|
134418
|
+
await gh.mergePR(prNumber, {
|
|
134419
|
+
auto: true,
|
|
134420
|
+
method: "squash",
|
|
134421
|
+
deleteBranch: true
|
|
134422
|
+
});
|
|
134423
|
+
log(`=> Auto-merge enabled for PR #${prNumber}`);
|
|
134424
|
+
} else {
|
|
134425
|
+
log(
|
|
134426
|
+
`=> No CI checks found, skipping auto-merge for PR #${prNumber}`
|
|
134427
|
+
);
|
|
134428
|
+
}
|
|
134429
|
+
} catch (autoMergeError) {
|
|
134430
|
+
error(
|
|
134431
|
+
`=> Failed to auto-merge PR #${prNumber}:`,
|
|
134432
|
+
autoMergeError
|
|
134433
|
+
);
|
|
134434
|
+
}
|
|
134435
|
+
}
|
|
134436
|
+
} catch (prError) {
|
|
134437
|
+
error(`=> Failed to create PR for ${r.url}:`, prError);
|
|
134438
|
+
prErrors[repoName] = prError instanceof Error ? prError.message : String(prError);
|
|
134439
|
+
}
|
|
134440
|
+
}
|
|
134441
|
+
}
|
|
134442
|
+
const response = { success: true, commits, branches };
|
|
134443
|
+
if (createPR) {
|
|
134444
|
+
response.prs = prs;
|
|
134445
|
+
if (Object.keys(prErrors).length > 0) {
|
|
134446
|
+
response.prErrors = prErrors;
|
|
134447
|
+
}
|
|
134448
|
+
}
|
|
134449
|
+
res.json(response);
|
|
134450
|
+
} catch (e) {
|
|
134451
|
+
fail(res, e);
|
|
134452
|
+
}
|
|
134453
|
+
}
|
|
134454
|
+
async function handleReset(req, res) {
|
|
134455
|
+
try {
|
|
134456
|
+
const code = req.body;
|
|
134457
|
+
for (const r of code.repos) {
|
|
134458
|
+
const repo = await NewRepo(r.url);
|
|
134459
|
+
await repo.checkoutMainBranchOrBaseBranch(r.base_branch);
|
|
134460
|
+
await repo.fetchLatestWithCredentials(code.git_credentials);
|
|
134461
|
+
await repo.resetHardOriginWithCredentials(r.base_branch);
|
|
134462
|
+
await repo.clean();
|
|
134463
|
+
}
|
|
134464
|
+
res.json({ success: true });
|
|
134465
|
+
} catch (e) {
|
|
134466
|
+
fail(res, e);
|
|
134467
|
+
}
|
|
134468
|
+
}
|
|
134469
|
+
async function handlePr(req, res) {
|
|
134470
|
+
try {
|
|
134471
|
+
const code = req.body;
|
|
134472
|
+
const repos = getReposWithChangedFiles(code);
|
|
134473
|
+
const results = {};
|
|
134474
|
+
for (const r of repos) {
|
|
134475
|
+
log(`=> NEW PR in ${r}`);
|
|
134476
|
+
const repo = await NewRepo(r.url);
|
|
134477
|
+
const result = await repo.newPr();
|
|
134478
|
+
const repoName = getRepoNameFromUrl(r.url);
|
|
134479
|
+
if (result) {
|
|
134480
|
+
results[repoName] = result;
|
|
134481
|
+
} else {
|
|
134482
|
+
results[repoName] = "No changes to commit";
|
|
134483
|
+
}
|
|
134484
|
+
}
|
|
134485
|
+
res.json({ success: true, results });
|
|
134486
|
+
} catch (e) {
|
|
134487
|
+
error(e);
|
|
134488
|
+
fail(res, e);
|
|
134489
|
+
}
|
|
134490
|
+
}
|
|
134491
|
+
|
|
134492
|
+
// src/proxy/code_actions.ts
|
|
134493
|
+
var fs12 = __toESM(require("fs/promises"), 1);
|
|
134494
|
+
var path10 = __toESM(require("path"), 1);
|
|
134076
134495
|
async function processCode(code) {
|
|
134077
134496
|
const results = [];
|
|
134078
134497
|
const workspaceHasOneRepo = await hasOnlyOneRepo();
|
|
@@ -134126,6 +134545,100 @@ async function processCode(code) {
|
|
|
134126
134545
|
}
|
|
134127
134546
|
return results;
|
|
134128
134547
|
}
|
|
134548
|
+
async function applyFileAction(file3, filePath) {
|
|
134549
|
+
switch (file3.action) {
|
|
134550
|
+
case "create":
|
|
134551
|
+
await createFile(filePath, file3.changes[0].content);
|
|
134552
|
+
break;
|
|
134553
|
+
case "rewrite":
|
|
134554
|
+
await rewriteFile(filePath, file3.changes[0].content);
|
|
134555
|
+
break;
|
|
134556
|
+
case "modify":
|
|
134557
|
+
await modifyFile(filePath, file3.changes);
|
|
134558
|
+
break;
|
|
134559
|
+
case "delete":
|
|
134560
|
+
await deleteFile(filePath);
|
|
134561
|
+
break;
|
|
134562
|
+
default:
|
|
134563
|
+
throw new Error(`Unknown action: ${file3.action}`);
|
|
134564
|
+
}
|
|
134565
|
+
}
|
|
134566
|
+
async function createFile(filePath, content) {
|
|
134567
|
+
try {
|
|
134568
|
+
await fs12.stat(filePath);
|
|
134569
|
+
throw new Error(`File ${filePath} already exists`);
|
|
134570
|
+
} catch (e) {
|
|
134571
|
+
if (e.code !== "ENOENT") {
|
|
134572
|
+
throw e;
|
|
134573
|
+
}
|
|
134574
|
+
}
|
|
134575
|
+
const dirPath = path10.dirname(filePath);
|
|
134576
|
+
try {
|
|
134577
|
+
await fs12.mkdir(dirPath, { recursive: true });
|
|
134578
|
+
} catch (e) {
|
|
134579
|
+
}
|
|
134580
|
+
await fs12.writeFile(filePath, content, "utf8");
|
|
134581
|
+
}
|
|
134582
|
+
async function rewriteFile(filePath, content) {
|
|
134583
|
+
try {
|
|
134584
|
+
await fs12.stat(filePath);
|
|
134585
|
+
await fs12.writeFile(filePath, content, "utf8");
|
|
134586
|
+
} catch (e) {
|
|
134587
|
+
if (e.code === "ENOENT") {
|
|
134588
|
+
await createFile(filePath, content);
|
|
134589
|
+
} else {
|
|
134590
|
+
throw e;
|
|
134591
|
+
}
|
|
134592
|
+
}
|
|
134593
|
+
}
|
|
134594
|
+
async function modifyFile(filePath, changes) {
|
|
134595
|
+
try {
|
|
134596
|
+
const originalContent = await fs12.readFile(filePath, "utf8");
|
|
134597
|
+
let newContent = originalContent.replace(/\r\n/g, "\n");
|
|
134598
|
+
const finalChange = changes[changes.length - 1];
|
|
134599
|
+
if (finalChange && finalChange.search) {
|
|
134600
|
+
const normalizedSearch = finalChange.search.replace(/\r\n/g, "\n");
|
|
134601
|
+
const searchIndex = newContent.lastIndexOf(normalizedSearch);
|
|
134602
|
+
if (searchIndex !== -1) {
|
|
134603
|
+
log("Found changes to apply!");
|
|
134604
|
+
} else {
|
|
134605
|
+
const emsg = `Search pattern not found in ${filePath} ${finalChange.search}`;
|
|
134606
|
+
error(emsg);
|
|
134607
|
+
throw new Error(emsg);
|
|
134608
|
+
}
|
|
134609
|
+
}
|
|
134610
|
+
for (const change of changes) {
|
|
134611
|
+
if (!change.search) {
|
|
134612
|
+
continue;
|
|
134613
|
+
}
|
|
134614
|
+
const normalizedSearch = change.search.replace(/\r\n/g, "\n");
|
|
134615
|
+
const searchIndex = newContent.indexOf(normalizedSearch);
|
|
134616
|
+
if (searchIndex !== -1) {
|
|
134617
|
+
newContent = newContent.substring(0, searchIndex) + change.content + newContent.substring(searchIndex + normalizedSearch.length);
|
|
134618
|
+
}
|
|
134619
|
+
}
|
|
134620
|
+
if (originalContent !== newContent) {
|
|
134621
|
+
await fs12.writeFile(filePath, newContent, "utf8");
|
|
134622
|
+
} else {
|
|
134623
|
+
log(`=> NO CHANGES to file ${filePath}`);
|
|
134624
|
+
}
|
|
134625
|
+
} catch (e) {
|
|
134626
|
+
throw new Error(`Failed to modify file ${filePath}: ${e}`);
|
|
134627
|
+
}
|
|
134628
|
+
}
|
|
134629
|
+
async function deleteFile(filePath) {
|
|
134630
|
+
await fs12.unlink(filePath);
|
|
134631
|
+
}
|
|
134632
|
+
|
|
134633
|
+
// src/proxy/server.ts
|
|
134634
|
+
var PORT = parseInt(process.env.STAKLINK_PORT || "15552") || 15552;
|
|
134635
|
+
var VSCODE_EXTENSION_URL = `http://localhost:${PORT + 1}`;
|
|
134636
|
+
var GOOSE_WEB_PORT = PORT - 1;
|
|
134637
|
+
var lastHitTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
134638
|
+
function updateLastHit(_req, _res, next) {
|
|
134639
|
+
lastHitTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
134640
|
+
next();
|
|
134641
|
+
}
|
|
134129
134642
|
async function startProxyServer() {
|
|
134130
134643
|
function requireAuth(req, res, next) {
|
|
134131
134644
|
const password = process.env.CODE_SERVER_PASSWORD;
|
|
@@ -134220,41 +134733,65 @@ async function startProxyServer() {
|
|
|
134220
134733
|
app.use(updateLastHit);
|
|
134221
134734
|
app.post("/code", async (req, res) => {
|
|
134222
134735
|
log("===> POST code");
|
|
134223
|
-
await handleRequest(
|
|
134224
|
-
|
|
134225
|
-
|
|
134226
|
-
|
|
134227
|
-
|
|
134228
|
-
|
|
134229
|
-
|
|
134230
|
-
|
|
134231
|
-
|
|
134736
|
+
await handleRequest(
|
|
134737
|
+
req,
|
|
134738
|
+
res,
|
|
134739
|
+
"code",
|
|
134740
|
+
async (req2, res2) => {
|
|
134741
|
+
let restartAfter = req2.query.restart === "false" ? false : true;
|
|
134742
|
+
const code = req2.body;
|
|
134743
|
+
const result = await processCode(code);
|
|
134744
|
+
if (restartAfter) {
|
|
134745
|
+
await restartConfiguredProcesses();
|
|
134746
|
+
}
|
|
134747
|
+
res2.json({ success: true, result });
|
|
134748
|
+
},
|
|
134749
|
+
VSCODE_EXTENSION_URL
|
|
134750
|
+
);
|
|
134232
134751
|
});
|
|
134233
134752
|
app.post("/actions", async (req, res) => {
|
|
134234
134753
|
log("===> POST actions");
|
|
134235
|
-
await handleRequest(
|
|
134236
|
-
|
|
134237
|
-
|
|
134238
|
-
|
|
134239
|
-
|
|
134240
|
-
|
|
134241
|
-
|
|
134242
|
-
|
|
134243
|
-
|
|
134754
|
+
await handleRequest(
|
|
134755
|
+
req,
|
|
134756
|
+
res,
|
|
134757
|
+
"actions",
|
|
134758
|
+
async (req2, res2) => {
|
|
134759
|
+
let restartAfter = req2.query.restart === "false" ? false : true;
|
|
134760
|
+
const code = req2.body;
|
|
134761
|
+
const result = await processCode(code);
|
|
134762
|
+
if (restartAfter) {
|
|
134763
|
+
await restartConfiguredProcesses();
|
|
134764
|
+
}
|
|
134765
|
+
res2.json({ success: true, result });
|
|
134766
|
+
},
|
|
134767
|
+
VSCODE_EXTENSION_URL
|
|
134768
|
+
);
|
|
134244
134769
|
});
|
|
134245
134770
|
app.get("/rules", async (req, res) => {
|
|
134246
134771
|
log("===> GET rules");
|
|
134247
|
-
await handleRequest(
|
|
134248
|
-
|
|
134249
|
-
|
|
134250
|
-
|
|
134251
|
-
|
|
134772
|
+
await handleRequest(
|
|
134773
|
+
req,
|
|
134774
|
+
res,
|
|
134775
|
+
"rules",
|
|
134776
|
+
async (req2, res2) => {
|
|
134777
|
+
const root = await workspaceRoot();
|
|
134778
|
+
const rules = await gatherRulesFiles(root);
|
|
134779
|
+
res2.json(rules);
|
|
134780
|
+
},
|
|
134781
|
+
VSCODE_EXTENSION_URL
|
|
134782
|
+
);
|
|
134252
134783
|
});
|
|
134253
134784
|
app.get("/errors", async (req, res) => {
|
|
134254
134785
|
log("===> GET errors");
|
|
134255
|
-
await handleRequest(
|
|
134256
|
-
|
|
134257
|
-
|
|
134786
|
+
await handleRequest(
|
|
134787
|
+
req,
|
|
134788
|
+
res,
|
|
134789
|
+
"errors",
|
|
134790
|
+
async (req2, res2) => {
|
|
134791
|
+
res2.json({});
|
|
134792
|
+
},
|
|
134793
|
+
VSCODE_EXTENSION_URL
|
|
134794
|
+
);
|
|
134258
134795
|
});
|
|
134259
134796
|
app.get("/logs", async (req, res) => {
|
|
134260
134797
|
log("===> GET logs");
|
|
@@ -134303,350 +134840,27 @@ async function startProxyServer() {
|
|
|
134303
134840
|
});
|
|
134304
134841
|
app.get("/diff", async (req, res) => {
|
|
134305
134842
|
log("===> GET diff");
|
|
134306
|
-
|
|
134307
|
-
|
|
134308
|
-
|
|
134309
|
-
|
|
134310
|
-
|
|
134311
|
-
const repoName = getRepoNameFromUrl(r);
|
|
134312
|
-
const diffOutput = await repo.execCommand(
|
|
134313
|
-
"git diff --name-status HEAD"
|
|
134314
|
-
);
|
|
134315
|
-
const untrackedOutput = await repo.execCommand(
|
|
134316
|
-
"git ls-files --others --exclude-standard"
|
|
134317
|
-
);
|
|
134318
|
-
const lines = diffOutput.trim().split("\n").filter((line) => line);
|
|
134319
|
-
for (const line of lines) {
|
|
134320
|
-
const parts = line.split(" ");
|
|
134321
|
-
if (parts.length < 2) {
|
|
134322
|
-
continue;
|
|
134323
|
-
}
|
|
134324
|
-
const status = parts[0];
|
|
134325
|
-
const filePath = parts[1];
|
|
134326
|
-
let action;
|
|
134327
|
-
if (status.startsWith("A")) {
|
|
134328
|
-
action = "create";
|
|
134329
|
-
} else if (status.startsWith("D")) {
|
|
134330
|
-
action = "delete";
|
|
134331
|
-
} else {
|
|
134332
|
-
action = "modify";
|
|
134333
|
-
}
|
|
134334
|
-
let content = "";
|
|
134335
|
-
try {
|
|
134336
|
-
if (action === "delete") {
|
|
134337
|
-
content = await repo.execCommand(
|
|
134338
|
-
`git diff HEAD -- "${filePath}"`
|
|
134339
|
-
);
|
|
134340
|
-
} else {
|
|
134341
|
-
log("Getting diff for", filePath);
|
|
134342
|
-
content = await repo.execCommand(
|
|
134343
|
-
`git diff HEAD -- "${filePath}"`
|
|
134344
|
-
);
|
|
134345
|
-
}
|
|
134346
|
-
} catch (error87) {
|
|
134347
|
-
warn(`Error getting diff for ${filePath}:`, error87);
|
|
134348
|
-
}
|
|
134349
|
-
results.push({
|
|
134350
|
-
file: `${repoName}/${filePath}`,
|
|
134351
|
-
action,
|
|
134352
|
-
content,
|
|
134353
|
-
repoName,
|
|
134354
|
-
errors: []
|
|
134355
|
-
});
|
|
134356
|
-
}
|
|
134357
|
-
const untrackedLines = untrackedOutput.trim().split("\n").filter((line) => line);
|
|
134358
|
-
for (const filePath of untrackedLines) {
|
|
134359
|
-
let content = "";
|
|
134360
|
-
try {
|
|
134361
|
-
const fileContent = await repo.execCommand(`cat "${filePath}"`);
|
|
134362
|
-
const lines2 = fileContent.split("\n");
|
|
134363
|
-
content = `--- /dev/null
|
|
134364
|
-
+++ b/${filePath}
|
|
134365
|
-
@@ -0,0 +1,${lines2.length} @@
|
|
134366
|
-
${lines2.map((line) => "+" + line).join("\n")}`;
|
|
134367
|
-
} catch (error87) {
|
|
134368
|
-
warn(`Error reading untracked file ${filePath}:`, error87);
|
|
134369
|
-
}
|
|
134370
|
-
results.push({
|
|
134371
|
-
file: `${repoName}/${filePath}`,
|
|
134372
|
-
action: "create",
|
|
134373
|
-
content,
|
|
134374
|
-
repoName,
|
|
134375
|
-
errors: []
|
|
134376
|
-
});
|
|
134377
|
-
}
|
|
134378
|
-
}
|
|
134379
|
-
res.status(200).json(results);
|
|
134380
|
-
} catch (error87) {
|
|
134381
|
-
error("Error fetching git diff:", error87);
|
|
134382
|
-
fail(res, error87);
|
|
134383
|
-
}
|
|
134843
|
+
await handleDiff(req, res);
|
|
134844
|
+
});
|
|
134845
|
+
app.get("/branch-diff", async (req, res) => {
|
|
134846
|
+
log("===> GET branch-diff");
|
|
134847
|
+
await handleBranchDiff(req, res);
|
|
134384
134848
|
});
|
|
134385
134849
|
app.post("/commit", async (req, res) => {
|
|
134386
134850
|
log(`===> POST /commit`);
|
|
134387
|
-
|
|
134388
|
-
const code = req.body;
|
|
134389
|
-
const repos = getReposWithChangedFiles(code);
|
|
134390
|
-
log(`=> COMMIT for repo ${repos.map((r) => r.url).join(", ")}`);
|
|
134391
|
-
if (repos.length === 0) {
|
|
134392
|
-
warn("No repos with changed files found");
|
|
134393
|
-
res.json({ success: false, message: "No changed repos" });
|
|
134394
|
-
return;
|
|
134395
|
-
}
|
|
134396
|
-
for (const repodata of repos) {
|
|
134397
|
-
const repo = await NewRepo(repodata.url);
|
|
134398
|
-
log(`=> COMMIT in ${repo.printcwd()}`);
|
|
134399
|
-
const isOnMain = await repo.isOnMainBranch();
|
|
134400
|
-
const isOnBaseBranch = await repo.isOnBranch(repodata.base_branch);
|
|
134401
|
-
if (isOnMain || isOnBaseBranch) {
|
|
134402
|
-
log(`=> CHECKOUT to new branch: ${repodata.branch_name}`);
|
|
134403
|
-
await repo.checkoutNew(repodata.branch_name);
|
|
134404
|
-
}
|
|
134405
|
-
await repo.addAll();
|
|
134406
|
-
log(
|
|
134407
|
-
`=> COMMIT to ${repodata.url} new branch: ${repodata.branch_name}`
|
|
134408
|
-
);
|
|
134409
|
-
await repo.commitWithCredentials(
|
|
134410
|
-
repodata.commit_name,
|
|
134411
|
-
code.git_credentials
|
|
134412
|
-
);
|
|
134413
|
-
}
|
|
134414
|
-
res.json({ success: true });
|
|
134415
|
-
} catch (e) {
|
|
134416
|
-
error(e);
|
|
134417
|
-
fail(res, e);
|
|
134418
|
-
}
|
|
134851
|
+
await handleCommit(req, res);
|
|
134419
134852
|
});
|
|
134420
134853
|
app.post("/push", async (req, res) => {
|
|
134421
|
-
|
|
134422
|
-
|
|
134423
|
-
const code = req.body;
|
|
134424
|
-
const repos = getReposWithChangedFiles(code);
|
|
134425
|
-
const createPR = req.query.pr === "true";
|
|
134426
|
-
const shouldCommit = req.query.commit === "true";
|
|
134427
|
-
const autoMerge = req.query.automerge === "true";
|
|
134428
|
-
const label = req.query.label;
|
|
134429
|
-
const stayOnCurrentBranch = req.query.stayOnCurrentBranch === "true";
|
|
134430
|
-
const commits = [];
|
|
134431
|
-
const prs = {};
|
|
134432
|
-
const prErrors = {};
|
|
134433
|
-
const branches = {};
|
|
134434
|
-
log(`=> Commit requested: ${shouldCommit}`);
|
|
134435
|
-
log(`=> PR creation requested: ${createPR}`);
|
|
134436
|
-
log(`=> Auto-merge requested: ${autoMerge}`);
|
|
134437
|
-
log(
|
|
134438
|
-
`=> Git credentials available: ${!!code.git_credentials?.auth_data?.token}`
|
|
134439
|
-
);
|
|
134440
|
-
for (const r of repos) {
|
|
134441
|
-
const repo = await NewRepo(r.url);
|
|
134442
|
-
const repoName = getRepoNameFromUrl(r.url);
|
|
134443
|
-
let currentBranch = (await repo.printCurrentBranch()).trim();
|
|
134444
|
-
const requestedBranch = stayOnCurrentBranch ? currentBranch : await repo.generateUniqueBranchName(r.branch_name);
|
|
134445
|
-
log(`=> Current branch: ${currentBranch}`);
|
|
134446
|
-
log(`=> Requested branch: ${requestedBranch}`);
|
|
134447
|
-
const protectedBranches = ["main", "master"];
|
|
134448
|
-
const isOnProtectedBranch = protectedBranches.includes(currentBranch);
|
|
134449
|
-
const shouldSwitchBranch = (!stayOnCurrentBranch || isOnProtectedBranch) && currentBranch !== requestedBranch;
|
|
134450
|
-
if (shouldSwitchBranch) {
|
|
134451
|
-
const branchExists = await repo.branchExists(requestedBranch);
|
|
134452
|
-
if (branchExists) {
|
|
134453
|
-
log(`=> Switching to existing branch: ${requestedBranch}`);
|
|
134454
|
-
await repo.checkout(requestedBranch);
|
|
134455
|
-
} else {
|
|
134456
|
-
log(
|
|
134457
|
-
`=> Creating new branch from current position: ${requestedBranch}`
|
|
134458
|
-
);
|
|
134459
|
-
await repo.checkoutNew(requestedBranch);
|
|
134460
|
-
}
|
|
134461
|
-
} else {
|
|
134462
|
-
log(`=> Already on correct branch: ${requestedBranch}`);
|
|
134463
|
-
}
|
|
134464
|
-
if (shouldCommit) {
|
|
134465
|
-
log(`=> COMMIT to ${r.url}`);
|
|
134466
|
-
log(`=> COMMIT in ${repo.printcwd()}`);
|
|
134467
|
-
const status = await repo.execCommand("git status --porcelain");
|
|
134468
|
-
if (status.trim()) {
|
|
134469
|
-
log(`=> Found uncommitted changes, committing...`);
|
|
134470
|
-
await repo.addAll();
|
|
134471
|
-
currentBranch = (await repo.printCurrentBranch()).trim();
|
|
134472
|
-
log(`=> COMMIT to ${r.url} on branch: ${currentBranch}`);
|
|
134473
|
-
await repo.commitWithCredentials(
|
|
134474
|
-
r.commit_name || `Changes from ${currentBranch}`,
|
|
134475
|
-
code.git_credentials
|
|
134476
|
-
);
|
|
134477
|
-
log(`=> Commit successful`);
|
|
134478
|
-
} else {
|
|
134479
|
-
log(
|
|
134480
|
-
`=> No uncommitted changes, skipping commit (may have been committed by agent)`
|
|
134481
|
-
);
|
|
134482
|
-
}
|
|
134483
|
-
}
|
|
134484
|
-
log(`=> PUSH to ${r.url}`);
|
|
134485
|
-
await repo.pushCurrentBranchWithCredentials(code.git_credentials);
|
|
134486
|
-
log(`=> Push successful`);
|
|
134487
|
-
const link = await repo.getLatestCommitLink();
|
|
134488
|
-
commits.push(link);
|
|
134489
|
-
currentBranch = (await repo.printCurrentBranch()).trim();
|
|
134490
|
-
branches[repoName] = currentBranch;
|
|
134491
|
-
log(`=> Branch for ${repoName}: ${currentBranch}`);
|
|
134492
|
-
if (createPR) {
|
|
134493
|
-
if (!code.git_credentials?.auth_data?.token) {
|
|
134494
|
-
error(`=> No GitHub token available for PR creation`);
|
|
134495
|
-
prErrors[repoName] = "GitHub token required for PR creation";
|
|
134496
|
-
continue;
|
|
134497
|
-
}
|
|
134498
|
-
try {
|
|
134499
|
-
log(`=> Creating PR for ${r.url}`);
|
|
134500
|
-
log(`=> Branch: ${r.branch_name}`);
|
|
134501
|
-
log(`=> Base: ${r.base_branch || "main"}`);
|
|
134502
|
-
const repoLocation = repo.printcwd();
|
|
134503
|
-
log(`=> Repo location: ${repoLocation}`);
|
|
134504
|
-
const currentBranchRaw = await repo.execCommand(
|
|
134505
|
-
"git rev-parse --abbrev-ref HEAD"
|
|
134506
|
-
);
|
|
134507
|
-
const currentBranch2 = currentBranchRaw.trim();
|
|
134508
|
-
log(`=> Current branch: ${currentBranch2}`);
|
|
134509
|
-
const baseBranch = r.base_branch || "main";
|
|
134510
|
-
if (currentBranch2 === baseBranch) {
|
|
134511
|
-
log(
|
|
134512
|
-
`=> Cannot create PR: currently on base branch (${baseBranch})`
|
|
134513
|
-
);
|
|
134514
|
-
prErrors[repoName] = `Cannot create PR - currently on base branch (${baseBranch}). Commit changes to a feature branch first.`;
|
|
134515
|
-
continue;
|
|
134516
|
-
}
|
|
134517
|
-
try {
|
|
134518
|
-
const diff = await repo.execCommand(
|
|
134519
|
-
`git log origin/${baseBranch}..HEAD --oneline`
|
|
134520
|
-
);
|
|
134521
|
-
if (!diff.trim()) {
|
|
134522
|
-
log(
|
|
134523
|
-
`=> No commits between ${baseBranch} and ${currentBranch2}`
|
|
134524
|
-
);
|
|
134525
|
-
prErrors[repoName] = `No commits to create PR - ${currentBranch2} is up to date with ${baseBranch}`;
|
|
134526
|
-
continue;
|
|
134527
|
-
}
|
|
134528
|
-
log(`=> Commits to include in PR:
|
|
134529
|
-
${diff.trim()}`);
|
|
134530
|
-
} catch (diffError) {
|
|
134531
|
-
warn(`=> Could not check commit diff: ${diffError}`);
|
|
134532
|
-
}
|
|
134533
|
-
const gh = new GitHubCLI(
|
|
134534
|
-
code.git_credentials.auth_data.token,
|
|
134535
|
-
currentBranch2,
|
|
134536
|
-
repoLocation
|
|
134537
|
-
);
|
|
134538
|
-
await gh.init();
|
|
134539
|
-
const existingPR = await gh.findPRByBranch();
|
|
134540
|
-
let prNumber;
|
|
134541
|
-
if (existingPR) {
|
|
134542
|
-
log(`=> PR already exists: ${existingPR.url}`);
|
|
134543
|
-
prs[repoName] = existingPR.url;
|
|
134544
|
-
prNumber = existingPR.number;
|
|
134545
|
-
} else {
|
|
134546
|
-
const commitMsg = r.commit_name || `Changes from ${currentBranch2}`;
|
|
134547
|
-
const { title, body } = processCommitMessage(commitMsg);
|
|
134548
|
-
const pr = await gh.createPR({
|
|
134549
|
-
title,
|
|
134550
|
-
body,
|
|
134551
|
-
base: baseBranch,
|
|
134552
|
-
draft: false
|
|
134553
|
-
});
|
|
134554
|
-
log(`=> PR created: ${pr.url}`);
|
|
134555
|
-
prs[repoName] = pr.url;
|
|
134556
|
-
prNumber = pr.number;
|
|
134557
|
-
if (label) {
|
|
134558
|
-
try {
|
|
134559
|
-
await gh.addLabelToPR(pr.number, label);
|
|
134560
|
-
} catch (e) {
|
|
134561
|
-
error(`=> PR failed to add label: ${e}`);
|
|
134562
|
-
}
|
|
134563
|
-
}
|
|
134564
|
-
}
|
|
134565
|
-
if (autoMerge) {
|
|
134566
|
-
try {
|
|
134567
|
-
log(
|
|
134568
|
-
`=> Waiting for CI checks to appear for PR #${prNumber}...`
|
|
134569
|
-
);
|
|
134570
|
-
const hasChecks = await gh.waitForChecksToExist(
|
|
134571
|
-
prNumber,
|
|
134572
|
-
3e4
|
|
134573
|
-
);
|
|
134574
|
-
if (hasChecks) {
|
|
134575
|
-
log(`=> Enabling auto-merge for PR #${prNumber}...`);
|
|
134576
|
-
await gh.mergePR(prNumber, {
|
|
134577
|
-
auto: true,
|
|
134578
|
-
method: "squash",
|
|
134579
|
-
deleteBranch: true
|
|
134580
|
-
});
|
|
134581
|
-
log(`=> Auto-merge enabled for PR #${prNumber}`);
|
|
134582
|
-
} else {
|
|
134583
|
-
log(
|
|
134584
|
-
`=> No CI checks found, skipping auto-merge for PR #${prNumber}`
|
|
134585
|
-
);
|
|
134586
|
-
}
|
|
134587
|
-
} catch (autoMergeError) {
|
|
134588
|
-
error(
|
|
134589
|
-
`=> Failed to auto-merge PR #${prNumber}:`,
|
|
134590
|
-
autoMergeError
|
|
134591
|
-
);
|
|
134592
|
-
}
|
|
134593
|
-
}
|
|
134594
|
-
} catch (prError) {
|
|
134595
|
-
error(`=> Failed to create PR for ${r.url}:`, prError);
|
|
134596
|
-
prErrors[repoName] = prError instanceof Error ? prError.message : String(prError);
|
|
134597
|
-
}
|
|
134598
|
-
}
|
|
134599
|
-
}
|
|
134600
|
-
const response = { success: true, commits, branches };
|
|
134601
|
-
if (createPR) {
|
|
134602
|
-
response.prs = prs;
|
|
134603
|
-
if (Object.keys(prErrors).length > 0) {
|
|
134604
|
-
response.prErrors = prErrors;
|
|
134605
|
-
}
|
|
134606
|
-
}
|
|
134607
|
-
res.json(response);
|
|
134608
|
-
} catch (e) {
|
|
134609
|
-
fail(res, e);
|
|
134610
|
-
}
|
|
134854
|
+
log(`===> POST /push`);
|
|
134855
|
+
await handlePush(req, res);
|
|
134611
134856
|
});
|
|
134612
134857
|
app.post("/reset", async (req, res) => {
|
|
134613
134858
|
log(`===> POST /reset`);
|
|
134614
|
-
|
|
134615
|
-
const code = req.body;
|
|
134616
|
-
for (const r of code.repos) {
|
|
134617
|
-
const repo = await NewRepo(r.url);
|
|
134618
|
-
await repo.checkoutMainBranchOrBaseBranch(r.base_branch);
|
|
134619
|
-
await repo.fetchLatestWithCredentials(code.git_credentials);
|
|
134620
|
-
await repo.resetHardOriginWithCredentials(r.base_branch);
|
|
134621
|
-
await repo.clean();
|
|
134622
|
-
}
|
|
134623
|
-
res.json({ success: true });
|
|
134624
|
-
} catch (e) {
|
|
134625
|
-
fail(res, e);
|
|
134626
|
-
}
|
|
134859
|
+
await handleReset(req, res);
|
|
134627
134860
|
});
|
|
134628
134861
|
app.post("/pr", async (req, res) => {
|
|
134629
134862
|
log(`===> POST /pr`);
|
|
134630
|
-
|
|
134631
|
-
const code = req.body;
|
|
134632
|
-
const repos = getReposWithChangedFiles(code);
|
|
134633
|
-
const results = {};
|
|
134634
|
-
for (const r of repos) {
|
|
134635
|
-
log(`=> NEW PR in ${r}`);
|
|
134636
|
-
const repo = await NewRepo(r.url);
|
|
134637
|
-
const result = await repo.newPr();
|
|
134638
|
-
const repoName = getRepoNameFromUrl(r.url);
|
|
134639
|
-
if (result) {
|
|
134640
|
-
results[repoName] = result;
|
|
134641
|
-
} else {
|
|
134642
|
-
results[repoName] = "No changes to commit";
|
|
134643
|
-
}
|
|
134644
|
-
}
|
|
134645
|
-
res.json({ success: true, results });
|
|
134646
|
-
} catch (e) {
|
|
134647
|
-
error(e);
|
|
134648
|
-
fail(res, e);
|
|
134649
|
-
}
|
|
134863
|
+
await handlePr(req, res);
|
|
134650
134864
|
});
|
|
134651
134865
|
app.get("/events", (req, res) => {
|
|
134652
134866
|
log("===> GET /events");
|
|
@@ -134961,6 +135175,19 @@ ${diff.trim()}`);
|
|
|
134961
135175
|
res.setHeader("Connection", "keep-alive");
|
|
134962
135176
|
res.setHeader("X-Accel-Buffering", "no");
|
|
134963
135177
|
res.flushHeaders();
|
|
135178
|
+
req.socket.setTimeout(0);
|
|
135179
|
+
const keepAliveInterval = setInterval(() => {
|
|
135180
|
+
if (!res.writableEnded) {
|
|
135181
|
+
res.write(`:keepalive
|
|
135182
|
+
|
|
135183
|
+
`);
|
|
135184
|
+
} else {
|
|
135185
|
+
clearInterval(keepAliveInterval);
|
|
135186
|
+
}
|
|
135187
|
+
}, 15e3);
|
|
135188
|
+
req.on("close", () => {
|
|
135189
|
+
clearInterval(keepAliveInterval);
|
|
135190
|
+
});
|
|
134964
135191
|
await streamGooseToSSE(
|
|
134965
135192
|
sessionId,
|
|
134966
135193
|
prompt,
|
|
@@ -134968,6 +135195,7 @@ ${diff.trim()}`);
|
|
|
134968
135195
|
res,
|
|
134969
135196
|
{ system, webhookUrl: session.webhookUrl }
|
|
134970
135197
|
);
|
|
135198
|
+
clearInterval(keepAliveInterval);
|
|
134971
135199
|
});
|
|
134972
135200
|
app.use(requireAuth);
|
|
134973
135201
|
app.post("/session", async (req, res) => {
|
|
@@ -134986,48 +135214,6 @@ ${diff.trim()}`);
|
|
|
134986
135214
|
streamUrl: `${req.protocol}://${req.get("host")}/stream/${sessionId}`
|
|
134987
135215
|
});
|
|
134988
135216
|
});
|
|
134989
|
-
const createAsyncAgentHandler = (getParams, transformResult) => {
|
|
134990
|
-
return async (req, res) => {
|
|
134991
|
-
const request_id = startReq();
|
|
134992
|
-
try {
|
|
134993
|
-
const { repoName, apiKey, agent_name, session, summarize } = req.body;
|
|
134994
|
-
const params = getParams(req);
|
|
134995
|
-
const workspaceRoot2 = await workspaceRoot();
|
|
134996
|
-
let repoPath = workspaceRoot2;
|
|
134997
|
-
if (repoName) {
|
|
134998
|
-
const candidate = import_path2.default.join(workspaceRoot2, repoName);
|
|
134999
|
-
try {
|
|
135000
|
-
const stat4 = await import_promises2.default.stat(candidate);
|
|
135001
|
-
if (stat4.isDirectory()) {
|
|
135002
|
-
repoPath = candidate;
|
|
135003
|
-
}
|
|
135004
|
-
} catch {
|
|
135005
|
-
}
|
|
135006
|
-
}
|
|
135007
|
-
const agentFn = chooseAgent(agent_name || "goose");
|
|
135008
|
-
agentFn({
|
|
135009
|
-
prompt: params.prompt,
|
|
135010
|
-
apiKey: apiKey || process.env.ANTHROPIC_API_KEY || "",
|
|
135011
|
-
cwd: repoPath,
|
|
135012
|
-
system_prompt: params.system,
|
|
135013
|
-
session,
|
|
135014
|
-
summarize: summarize ? true : false
|
|
135015
|
-
}).then((result) => {
|
|
135016
|
-
const finalResult = transformResult ? transformResult(result) : result;
|
|
135017
|
-
finishReq(request_id, {
|
|
135018
|
-
success: true,
|
|
135019
|
-
result: finalResult
|
|
135020
|
-
});
|
|
135021
|
-
}).catch((error87) => {
|
|
135022
|
-
failReq(request_id, error87);
|
|
135023
|
-
});
|
|
135024
|
-
res.json({ request_id, status: "pending" });
|
|
135025
|
-
} catch (e) {
|
|
135026
|
-
error("Error running claude scripts:", e);
|
|
135027
|
-
fail(res, e);
|
|
135028
|
-
}
|
|
135029
|
-
};
|
|
135030
|
-
};
|
|
135031
135217
|
app.post("/validate_session", async (req, res) => {
|
|
135032
135218
|
log("===> POST /validate_session");
|
|
135033
135219
|
const { session } = req.body;
|
|
@@ -135131,7 +135317,7 @@ ${logs}
|
|
|
135131
135317
|
);
|
|
135132
135318
|
const validation = parseValidationResponse(result);
|
|
135133
135319
|
try {
|
|
135134
|
-
await
|
|
135320
|
+
await import_promises3.default.unlink(screenshotPath);
|
|
135135
135321
|
} catch (error87) {
|
|
135136
135322
|
warn("Failed to cleanup screenshot:", error87);
|
|
135137
135323
|
}
|
|
@@ -135194,56 +135380,6 @@ ${logs}
|
|
|
135194
135380
|
server.keepAliveTimeout = 6e5;
|
|
135195
135381
|
server.headersTimeout = 61e4;
|
|
135196
135382
|
}
|
|
135197
|
-
async function handleRequest(req, res, endpoint, fallbackHandler) {
|
|
135198
|
-
try {
|
|
135199
|
-
let url3 = `${VSCODE_EXTENSION_URL}/${endpoint}`;
|
|
135200
|
-
if (req.method === "GET" && Object.keys(req.query).length > 0) {
|
|
135201
|
-
const params = new URLSearchParams(req.query);
|
|
135202
|
-
url3 += `?${params.toString()}`;
|
|
135203
|
-
}
|
|
135204
|
-
const forwardHeaders = {
|
|
135205
|
-
"Content-Type": "application/json"
|
|
135206
|
-
};
|
|
135207
|
-
if (req.headers.authorization) {
|
|
135208
|
-
forwardHeaders.authorization = req.headers.authorization;
|
|
135209
|
-
}
|
|
135210
|
-
const controller = new AbortController();
|
|
135211
|
-
const timeoutId = setTimeout(() => controller.abort(), 2e4);
|
|
135212
|
-
const response = await fetch(url3, {
|
|
135213
|
-
method: req.method,
|
|
135214
|
-
headers: forwardHeaders,
|
|
135215
|
-
body: req.method !== "GET" ? JSON.stringify(req.body) : void 0,
|
|
135216
|
-
signal: controller.signal
|
|
135217
|
-
});
|
|
135218
|
-
clearTimeout(timeoutId);
|
|
135219
|
-
if (!response.ok) {
|
|
135220
|
-
throw new Error(
|
|
135221
|
-
`VSCode extension server responded with status ${response.status}`
|
|
135222
|
-
);
|
|
135223
|
-
}
|
|
135224
|
-
const data = await response.json();
|
|
135225
|
-
res.status(response.status).json(data);
|
|
135226
|
-
} catch (error87) {
|
|
135227
|
-
try {
|
|
135228
|
-
await fallbackHandler(req, res);
|
|
135229
|
-
} catch (fallbackError) {
|
|
135230
|
-
error(`Fallback also failed for ${endpoint}:`, fallbackError);
|
|
135231
|
-
if (!res.headersSent) {
|
|
135232
|
-
res.status(500).json({
|
|
135233
|
-
error: "Both VSCode and filesystem operations failed",
|
|
135234
|
-
message: fallbackError instanceof Error ? fallbackError.message : String(fallbackError)
|
|
135235
|
-
});
|
|
135236
|
-
}
|
|
135237
|
-
}
|
|
135238
|
-
}
|
|
135239
|
-
}
|
|
135240
|
-
function fail(res, e) {
|
|
135241
|
-
error(e);
|
|
135242
|
-
res.status(200).json({
|
|
135243
|
-
error: "Internal server error",
|
|
135244
|
-
message: e instanceof Error ? e.message : String(e)
|
|
135245
|
-
});
|
|
135246
|
-
}
|
|
135247
135383
|
|
|
135248
135384
|
// src/proxy/main.ts
|
|
135249
135385
|
console.log("Starting staklink proxy server...");
|