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.
@@ -1289,8 +1289,8 @@ var require_node = __commonJS({
1289
1289
  }
1290
1290
  break;
1291
1291
  case "FILE":
1292
- var fs13 = require("fs");
1293
- stream3 = new fs13.SyncWriteStream(fd2, { autoClose: false });
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 fs13 = require("fs");
17512
- stream3 = new fs13.SyncWriteStream(fd2, { autoClose: false });
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 fs13 = require("fs");
18231
- stream3 = new fs13.SyncWriteStream(fd2, { autoClose: false });
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 fs13 = require("fs");
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 fs13.statSync(path12);
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 fs13 = require("fs");
19795
- stream3 = new fs13.SyncWriteStream(fd2, { autoClose: false });
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 fs13 = require("fs");
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 = fs13.readFileSync(file3, "ascii"), lines = content.split(/[\r\n]+/);
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 fs13 = require("fs");
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
- fs13.stat(path12, function onstat(err, stat4) {
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
- fs13.stat(p, function(err2, stat4) {
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
- fs13.stat(p, function(err2, stat4) {
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 = fs13.createReadStream(path12, options);
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 fs13 = __toESM2(require("fs"));
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 (!fs13.existsSync(tokenPath)) {
24166
+ if (!fs14.existsSync(tokenPath)) {
24167
24167
  return null;
24168
24168
  }
24169
- const token = fs13.readFileSync(tokenPath, "utf8");
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 (!fs13.existsSync(prjPath)) {
24211
+ if (!fs14.existsSync(prjPath)) {
24212
24212
  throw new import_token_error.VercelOidcTokenError("project.json not found");
24213
24213
  }
24214
- const prj = JSON.parse(fs13.readFileSync(prjPath, "utf8"));
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
- fs13.mkdirSync(path11.dirname(tokenPath), { mode: 504, recursive: true });
24232
- fs13.writeFileSync(tokenPath, tokenJson);
24233
- fs13.chmodSync(tokenPath, 432);
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 (!fs13.existsSync(tokenPath)) {
24246
+ if (!fs14.existsSync(tokenPath)) {
24247
24247
  return null;
24248
24248
  }
24249
- const token = JSON.parse(fs13.readFileSync(tokenPath, "utf8"));
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 fs13 = __toESM2(require("fs"));
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 (!fs13.existsSync(authPath)) {
26296
+ if (!fs14.existsSync(authPath)) {
26297
26297
  return null;
26298
26298
  }
26299
- const content = fs13.readFileSync(authPath, "utf8");
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 (!fs13.existsSync(authDir)) {
26312
- fs13.mkdirSync(authDir, { mode: 504, recursive: true });
26311
+ if (!fs14.existsSync(authDir)) {
26312
+ fs14.mkdirSync(authDir, { mode: 504, recursive: true });
26313
26313
  }
26314
- fs13.writeFileSync(authPath, JSON.stringify(config4, null, 2), { mode: 384 });
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 fs13 = __toESM2(require("fs"));
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 (!fs13.existsSync(prjPath)) {
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(fs13.readFileSync(prjPath, "utf8"));
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
- fs13.mkdirSync(path11.dirname(tokenPath), { mode: 504, recursive: true });
26569
- fs13.writeFileSync(tokenPath, tokenJson);
26570
- fs13.chmodSync(tokenPath, 432);
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 (!fs13.existsSync(tokenPath)) {
26581
+ if (!fs14.existsSync(tokenPath)) {
26582
26582
  return null;
26583
26583
  }
26584
- const token = JSON.parse(fs13.readFileSync(tokenPath, "utf8"));
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/utils.ts
53946
- async function findRepoLocation(repoFullName) {
53947
- try {
53948
- log(`Finding location for repo: ${repoFullName}`);
53949
- if (repoFullName.startsWith("/") && await dirExists(repoFullName)) {
53950
- return repoFullName;
53951
- }
53952
- const repoName = repoFullName.split("/").pop() || "";
53953
- const repodir1 = `/workspaces/${repoName}`;
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
- try {
53964
- const repodirs = (await fs5.readdir("/workspaces")).filter(
53965
- (dir) => !dir.startsWith(".")
53966
- );
53967
- if (repodirs.length === 0) {
53968
- warn("No workspace folders found");
53969
- return null;
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 repodir3 = `/workspaces/${repodirs[0]}`;
53972
- if (await dirExists(repodir3)) {
53973
- return repodir3;
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
- return null;
53976
- } catch (readdirError) {
53977
- return null;
53921
+ if (changes.length > 0) {
53922
+ files2.push({ path: path11, action, changes });
53923
+ }
53924
+ remainingXml = remainingXml.slice(fileEnd);
53978
53925
  }
53979
- } catch (error87) {
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
- async function hasOnlyOneRepo() {
53985
- try {
53986
- const basePath = process.cwd().startsWith("/workspaces") ? "/workspaces" : process.cwd();
53987
- const dirs = (await fs5.readdir(basePath, { withFileTypes: true })).filter((dirent) => dirent.isDirectory() && !dirent.name.startsWith(".")).map((dirent) => dirent.name);
53988
- return dirs.length === 1;
53989
- } catch (error87) {
53990
- error("Error checking workspace directories:", error87);
53991
- return false;
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
- function getRepoNameFromUrl(url3) {
53995
- const matches = url3.match(/github\.com\/([^\/]+\/[^\/\.]+)(\.git)?$/);
53996
- if (matches && matches[1]) {
53997
- return matches[1];
53998
- }
53999
- const repoName = url3.split("/").pop()?.replace(".git", "") || "";
54000
- return repoName;
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
- // src/proxy/actions.ts
54058
- var fs6 = __toESM(require("fs/promises"), 1);
54059
- var path4 = __toESM(require("path"), 1);
54060
- async function applyFileAction(file3, filePath) {
54061
- switch (file3.action) {
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 createFile(filePath, content) {
54063
+ async function handleRequest(req, res, endpoint, fallbackHandler, vscodeExtensionUrl) {
54079
54064
  try {
54080
- await fs6.stat(filePath);
54081
- throw new Error(`File ${filePath} already exists`);
54082
- } catch (e) {
54083
- if (e.code !== "ENOENT") {
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
- const dirPath = path4.dirname(filePath);
54088
- try {
54089
- await fs6.mkdir(dirPath, { recursive: true });
54090
- } catch (e) {
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
- async function modifyFile(filePath, changes) {
54107
- try {
54108
- const originalContent = await fs6.readFile(filePath, "utf8");
54109
- let newContent = originalContent.replace(/\r\n/g, "\n");
54110
- const finalChange = changes[changes.length - 1];
54111
- if (finalChange && finalChange.search) {
54112
- const normalizedSearch = finalChange.search.replace(/\r\n/g, "\n");
54113
- const searchIndex = newContent.lastIndexOf(normalizedSearch);
54114
- if (searchIndex !== -1) {
54115
- log("Found changes to apply!");
54116
- } else {
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
- for (const change of changes) {
54123
- if (!change.search) {
54124
- continue;
54125
- }
54126
- const normalizedSearch = change.search.replace(/\r\n/g, "\n");
54127
- const searchIndex = newContent.indexOf(normalizedSearch);
54128
- if (searchIndex !== -1) {
54129
- newContent = newContent.substring(0, searchIndex) + change.content + newContent.substring(searchIndex + normalizedSearch.length);
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 fs7 = __toESM(require("fs/promises"), 1);
54147
- var path6 = __toESM(require("path"), 1);
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 path5 = {
54782
+ var path4 = {
54821
54783
  win32: { sep: "\\" },
54822
54784
  posix: { sep: "/" }
54823
54785
  };
54824
- var sep = defaultPlatform === "win32" ? path5.win32.sep : path5.posix.sep;
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: fs13 = defaultFS } = {}) {
58874
- this.#fs = fsFromOption(fs13);
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(fs13) {
59433
- return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs13 });
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(fs13) {
59462
- return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs13 });
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 fs7.readFile(filePath, "utf8");
60557
- const relativePath = path6.relative(workspaceRoot2, filePath);
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 = path6.join(workspaceRoot2, ".gitignore");
60582
- const gitignoreContent = await fs7.readFile(gitignorePath, "utf8");
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 fs8 = __toESM(require("fs/promises"), 1);
60600
- var path7 = __toESM(require("path"), 1);
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 = path7.join(this.cwd, ".git", "index.lock");
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 fs8.access(lockFile);
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 fs8.unlink(lockFile);
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.74";
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 fs9 = __toESM(require("fs"), 1);
61411
- var path8 = __toESM(require("path"), 1);
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: `${path8.join(os.homedir(), ".local", "bin")}:${process.env.PATH}`
121560
+ PATH: `${path7.join(os.homedir(), ".local", "bin")}:${process.env.PATH}`
121601
121561
  };
121602
121562
  }
121603
- async function runAgent({ prompt, apiKey, cwd, system_prompt, session, summarize }) {
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 = path8.join(os.homedir(), ".config", "goose");
121634
- const configPath = path8.join(configDir, "config.yaml");
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 (!fs9.existsSync(configDir)) {
121646
- fs9.mkdirSync(configDir, { recursive: true });
121612
+ if (!fs8.existsSync(configDir)) {
121613
+ fs8.mkdirSync(configDir, { recursive: true });
121647
121614
  }
121648
- fs9.writeFileSync(configPath, configContent, "utf-8");
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({ prompt, apiKey, cwd }) {
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 fs10 = __toESM(require("fs/promises"), 1);
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 fs10.readFile(screenshotPath);
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((error87) => error("Error posting to webhook", error87));
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((error87) => error("Error posting to webhook", error87));
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((error87) => error("Error posting to webhook", error87));
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/proxy/server.ts
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/server.ts
134068
- var PORT = parseInt(process.env.STAKLINK_PORT || "15552") || 15552;
134069
- var VSCODE_EXTENSION_URL = `http://localhost:${PORT + 1}`;
134070
- var GOOSE_WEB_PORT = PORT - 1;
134071
- var lastHitTimestamp = (/* @__PURE__ */ new Date()).toISOString();
134072
- function updateLastHit(_req, _res, next) {
134073
- lastHitTimestamp = (/* @__PURE__ */ new Date()).toISOString();
134074
- next();
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(req, res, "code", async (req2, res2) => {
134224
- let restartAfter = req2.query.restart === "false" ? false : true;
134225
- const code = req2.body;
134226
- const result = await processCode(code);
134227
- if (restartAfter) {
134228
- await restartConfiguredProcesses();
134229
- }
134230
- res2.json({ success: true, result });
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(req, res, "actions", async (req2, res2) => {
134236
- let restartAfter = req2.query.restart === "false" ? false : true;
134237
- const code = req2.body;
134238
- const result = await processCode(code);
134239
- if (restartAfter) {
134240
- await restartConfiguredProcesses();
134241
- }
134242
- res2.json({ success: true, result });
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(req, res, "rules", async (req2, res2) => {
134248
- const root = await workspaceRoot();
134249
- const rules = await gatherRulesFiles(root);
134250
- res2.json(rules);
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(req, res, "errors", async (req2, res2) => {
134256
- res2.json({});
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
- try {
134307
- const results = [];
134308
- const repos = await getReposMaybe();
134309
- for (const r of repos) {
134310
- const repo = await NewRepo(r);
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
- try {
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
- try {
134422
- log(`===> POST /push`);
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
- try {
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
- try {
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 import_promises2.default.unlink(screenshotPath);
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...");