pullfrog 0.0.200 → 0.0.202
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/opencode.d.ts +1 -0
- package/dist/agents/shared.d.ts +65 -1
- package/dist/cli.mjs +2093 -888
- package/dist/external.d.ts +1 -1
- package/dist/index.js +2078 -878
- package/dist/internal.js +10 -8
- package/dist/lifecycle.d.ts +1 -1
- package/dist/mcp/checkout.d.ts +16 -2
- package/dist/mcp/comment.d.ts +1 -0
- package/dist/mcp/geminiSanitizer.d.ts +17 -0
- package/dist/mcp/git.d.ts +8 -2
- package/dist/mcp/review.d.ts +104 -0
- package/dist/mcp/server.d.ts +12 -0
- package/dist/mcp/shared.d.ts +1 -1
- package/dist/modes.d.ts +1 -1
- package/dist/utils/activity.d.ts +4 -0
- package/dist/utils/agent.d.ts +3 -1
- package/dist/utils/diffCoverage.d.ts +62 -0
- package/dist/utils/lifecycle.d.ts +14 -2
- package/dist/utils/log.d.ts +13 -2
- package/dist/utils/patchWorkflowRunFields.d.ts +27 -4
- package/dist/utils/runContext.d.ts +1 -0
- package/dist/utils/secrets.d.ts +9 -2
- package/dist/utils/setup.d.ts +13 -0
- package/dist/utils/subprocess.d.ts +7 -0
- package/dist/utils/time.d.ts +1 -0
- package/dist/utils/todoTracking.d.ts +3 -1
- package/package.json +3 -2
- package/dist/agents/opentoad.d.ts +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -4293,8 +4293,8 @@ var require_util2 = __commonJS({
|
|
|
4293
4293
|
function createDeferredPromise() {
|
|
4294
4294
|
let res;
|
|
4295
4295
|
let rej;
|
|
4296
|
-
const promise2 = new Promise((
|
|
4297
|
-
res =
|
|
4296
|
+
const promise2 = new Promise((resolve3, reject) => {
|
|
4297
|
+
res = resolve3;
|
|
4298
4298
|
rej = reject;
|
|
4299
4299
|
});
|
|
4300
4300
|
return { promise: promise2, resolve: res, reject: rej };
|
|
@@ -5798,8 +5798,8 @@ Content-Type: ${value2.type || "application/octet-stream"}\r
|
|
|
5798
5798
|
});
|
|
5799
5799
|
}
|
|
5800
5800
|
});
|
|
5801
|
-
const busboyResolve = new Promise((
|
|
5802
|
-
busboy.on("finish",
|
|
5801
|
+
const busboyResolve = new Promise((resolve3, reject) => {
|
|
5802
|
+
busboy.on("finish", resolve3);
|
|
5803
5803
|
busboy.on("error", (err) => reject(new TypeError(err)));
|
|
5804
5804
|
});
|
|
5805
5805
|
if (this.body !== null) for await (const chunk of consumeBody(this[kState].body)) busboy.write(chunk);
|
|
@@ -6333,9 +6333,9 @@ var require_dispatcher_base = __commonJS({
|
|
|
6333
6333
|
}
|
|
6334
6334
|
close(callback) {
|
|
6335
6335
|
if (callback === void 0) {
|
|
6336
|
-
return new Promise((
|
|
6336
|
+
return new Promise((resolve3, reject) => {
|
|
6337
6337
|
this.close((err, data) => {
|
|
6338
|
-
return err ? reject(err) :
|
|
6338
|
+
return err ? reject(err) : resolve3(data);
|
|
6339
6339
|
});
|
|
6340
6340
|
});
|
|
6341
6341
|
}
|
|
@@ -6373,12 +6373,12 @@ var require_dispatcher_base = __commonJS({
|
|
|
6373
6373
|
err = null;
|
|
6374
6374
|
}
|
|
6375
6375
|
if (callback === void 0) {
|
|
6376
|
-
return new Promise((
|
|
6376
|
+
return new Promise((resolve3, reject) => {
|
|
6377
6377
|
this.destroy(err, (err2, data) => {
|
|
6378
6378
|
return err2 ? (
|
|
6379
6379
|
/* istanbul ignore next: should never error */
|
|
6380
6380
|
reject(err2)
|
|
6381
|
-
) :
|
|
6381
|
+
) : resolve3(data);
|
|
6382
6382
|
});
|
|
6383
6383
|
});
|
|
6384
6384
|
}
|
|
@@ -7438,16 +7438,16 @@ var require_client = __commonJS({
|
|
|
7438
7438
|
return this[kNeedDrain] < 2;
|
|
7439
7439
|
}
|
|
7440
7440
|
async [kClose]() {
|
|
7441
|
-
return new Promise((
|
|
7441
|
+
return new Promise((resolve3) => {
|
|
7442
7442
|
if (!this[kSize]) {
|
|
7443
|
-
|
|
7443
|
+
resolve3(null);
|
|
7444
7444
|
} else {
|
|
7445
|
-
this[kClosedResolve] =
|
|
7445
|
+
this[kClosedResolve] = resolve3;
|
|
7446
7446
|
}
|
|
7447
7447
|
});
|
|
7448
7448
|
}
|
|
7449
7449
|
async [kDestroy](err) {
|
|
7450
|
-
return new Promise((
|
|
7450
|
+
return new Promise((resolve3) => {
|
|
7451
7451
|
const requests = this[kQueue].splice(this[kPendingIdx]);
|
|
7452
7452
|
for (let i = 0; i < requests.length; i++) {
|
|
7453
7453
|
const request2 = requests[i];
|
|
@@ -7458,7 +7458,7 @@ var require_client = __commonJS({
|
|
|
7458
7458
|
this[kClosedResolve]();
|
|
7459
7459
|
this[kClosedResolve] = null;
|
|
7460
7460
|
}
|
|
7461
|
-
|
|
7461
|
+
resolve3();
|
|
7462
7462
|
};
|
|
7463
7463
|
if (this[kHTTP2Session] != null) {
|
|
7464
7464
|
util2.destroy(this[kHTTP2Session], err);
|
|
@@ -8038,7 +8038,7 @@ var require_client = __commonJS({
|
|
|
8038
8038
|
});
|
|
8039
8039
|
}
|
|
8040
8040
|
try {
|
|
8041
|
-
const socket = await new Promise((
|
|
8041
|
+
const socket = await new Promise((resolve3, reject) => {
|
|
8042
8042
|
client[kConnector]({
|
|
8043
8043
|
host,
|
|
8044
8044
|
hostname: hostname4,
|
|
@@ -8050,7 +8050,7 @@ var require_client = __commonJS({
|
|
|
8050
8050
|
if (err) {
|
|
8051
8051
|
reject(err);
|
|
8052
8052
|
} else {
|
|
8053
|
-
|
|
8053
|
+
resolve3(socket2);
|
|
8054
8054
|
}
|
|
8055
8055
|
});
|
|
8056
8056
|
});
|
|
@@ -8674,12 +8674,12 @@ upgrade: ${upgrade}\r
|
|
|
8674
8674
|
cb();
|
|
8675
8675
|
}
|
|
8676
8676
|
}
|
|
8677
|
-
const waitForDrain = () => new Promise((
|
|
8677
|
+
const waitForDrain = () => new Promise((resolve3, reject) => {
|
|
8678
8678
|
assert3(callback === null);
|
|
8679
8679
|
if (socket[kError]) {
|
|
8680
8680
|
reject(socket[kError]);
|
|
8681
8681
|
} else {
|
|
8682
|
-
callback =
|
|
8682
|
+
callback = resolve3;
|
|
8683
8683
|
}
|
|
8684
8684
|
});
|
|
8685
8685
|
if (client[kHTTPConnVersion] === "h2") {
|
|
@@ -9024,8 +9024,8 @@ var require_pool_base = __commonJS({
|
|
|
9024
9024
|
if (this[kQueue].isEmpty()) {
|
|
9025
9025
|
return Promise.all(this[kClients].map((c2) => c2.close()));
|
|
9026
9026
|
} else {
|
|
9027
|
-
return new Promise((
|
|
9028
|
-
this[kClosedResolve] =
|
|
9027
|
+
return new Promise((resolve3) => {
|
|
9028
|
+
this[kClosedResolve] = resolve3;
|
|
9029
9029
|
});
|
|
9030
9030
|
}
|
|
9031
9031
|
}
|
|
@@ -9603,7 +9603,7 @@ var require_readable = __commonJS({
|
|
|
9603
9603
|
if (this.closed) {
|
|
9604
9604
|
return Promise.resolve(null);
|
|
9605
9605
|
}
|
|
9606
|
-
return new Promise((
|
|
9606
|
+
return new Promise((resolve3, reject) => {
|
|
9607
9607
|
const signalListenerCleanup = signal ? util2.addAbortListener(signal, () => {
|
|
9608
9608
|
this.destroy();
|
|
9609
9609
|
}) : noop4;
|
|
@@ -9612,7 +9612,7 @@ var require_readable = __commonJS({
|
|
|
9612
9612
|
if (signal && signal.aborted) {
|
|
9613
9613
|
reject(signal.reason || Object.assign(new Error("The operation was aborted"), { name: "AbortError" }));
|
|
9614
9614
|
} else {
|
|
9615
|
-
|
|
9615
|
+
resolve3(null);
|
|
9616
9616
|
}
|
|
9617
9617
|
}).on("error", noop4).on("data", function(chunk) {
|
|
9618
9618
|
limit -= chunk.length;
|
|
@@ -9634,11 +9634,11 @@ var require_readable = __commonJS({
|
|
|
9634
9634
|
throw new TypeError("unusable");
|
|
9635
9635
|
}
|
|
9636
9636
|
assert3(!stream[kConsume]);
|
|
9637
|
-
return new Promise((
|
|
9637
|
+
return new Promise((resolve3, reject) => {
|
|
9638
9638
|
stream[kConsume] = {
|
|
9639
9639
|
type: type2,
|
|
9640
9640
|
stream,
|
|
9641
|
-
resolve:
|
|
9641
|
+
resolve: resolve3,
|
|
9642
9642
|
reject,
|
|
9643
9643
|
length: 0,
|
|
9644
9644
|
body: []
|
|
@@ -9673,12 +9673,12 @@ var require_readable = __commonJS({
|
|
|
9673
9673
|
}
|
|
9674
9674
|
}
|
|
9675
9675
|
function consumeEnd(consume2) {
|
|
9676
|
-
const { type: type2, body, resolve:
|
|
9676
|
+
const { type: type2, body, resolve: resolve3, stream, length } = consume2;
|
|
9677
9677
|
try {
|
|
9678
9678
|
if (type2 === "text") {
|
|
9679
|
-
|
|
9679
|
+
resolve3(toUSVString(Buffer.concat(body)));
|
|
9680
9680
|
} else if (type2 === "json") {
|
|
9681
|
-
|
|
9681
|
+
resolve3(JSON.parse(Buffer.concat(body)));
|
|
9682
9682
|
} else if (type2 === "arrayBuffer") {
|
|
9683
9683
|
const dst = new Uint8Array(length);
|
|
9684
9684
|
let pos = 0;
|
|
@@ -9686,12 +9686,12 @@ var require_readable = __commonJS({
|
|
|
9686
9686
|
dst.set(buf, pos);
|
|
9687
9687
|
pos += buf.byteLength;
|
|
9688
9688
|
}
|
|
9689
|
-
|
|
9689
|
+
resolve3(dst.buffer);
|
|
9690
9690
|
} else if (type2 === "blob") {
|
|
9691
9691
|
if (!Blob2) {
|
|
9692
9692
|
Blob2 = __require("buffer").Blob;
|
|
9693
9693
|
}
|
|
9694
|
-
|
|
9694
|
+
resolve3(new Blob2(body, { type: stream[kContentType] }));
|
|
9695
9695
|
}
|
|
9696
9696
|
consumeFinish(consume2);
|
|
9697
9697
|
} catch (err) {
|
|
@@ -9946,9 +9946,9 @@ var require_api_request = __commonJS({
|
|
|
9946
9946
|
};
|
|
9947
9947
|
function request2(opts, callback) {
|
|
9948
9948
|
if (callback === void 0) {
|
|
9949
|
-
return new Promise((
|
|
9949
|
+
return new Promise((resolve3, reject) => {
|
|
9950
9950
|
request2.call(this, opts, (err, data) => {
|
|
9951
|
-
return err ? reject(err) :
|
|
9951
|
+
return err ? reject(err) : resolve3(data);
|
|
9952
9952
|
});
|
|
9953
9953
|
});
|
|
9954
9954
|
}
|
|
@@ -10121,9 +10121,9 @@ var require_api_stream = __commonJS({
|
|
|
10121
10121
|
};
|
|
10122
10122
|
function stream(opts, factory, callback) {
|
|
10123
10123
|
if (callback === void 0) {
|
|
10124
|
-
return new Promise((
|
|
10124
|
+
return new Promise((resolve3, reject) => {
|
|
10125
10125
|
stream.call(this, opts, factory, (err, data) => {
|
|
10126
|
-
return err ? reject(err) :
|
|
10126
|
+
return err ? reject(err) : resolve3(data);
|
|
10127
10127
|
});
|
|
10128
10128
|
});
|
|
10129
10129
|
}
|
|
@@ -10404,9 +10404,9 @@ var require_api_upgrade = __commonJS({
|
|
|
10404
10404
|
};
|
|
10405
10405
|
function upgrade(opts, callback) {
|
|
10406
10406
|
if (callback === void 0) {
|
|
10407
|
-
return new Promise((
|
|
10407
|
+
return new Promise((resolve3, reject) => {
|
|
10408
10408
|
upgrade.call(this, opts, (err, data) => {
|
|
10409
|
-
return err ? reject(err) :
|
|
10409
|
+
return err ? reject(err) : resolve3(data);
|
|
10410
10410
|
});
|
|
10411
10411
|
});
|
|
10412
10412
|
}
|
|
@@ -10495,9 +10495,9 @@ var require_api_connect = __commonJS({
|
|
|
10495
10495
|
};
|
|
10496
10496
|
function connect(opts, callback) {
|
|
10497
10497
|
if (callback === void 0) {
|
|
10498
|
-
return new Promise((
|
|
10498
|
+
return new Promise((resolve3, reject) => {
|
|
10499
10499
|
connect.call(this, opts, (err, data) => {
|
|
10500
|
-
return err ? reject(err) :
|
|
10500
|
+
return err ? reject(err) : resolve3(data);
|
|
10501
10501
|
});
|
|
10502
10502
|
});
|
|
10503
10503
|
}
|
|
@@ -14119,7 +14119,7 @@ var require_fetch = __commonJS({
|
|
|
14119
14119
|
async function dispatch({ body }) {
|
|
14120
14120
|
const url4 = requestCurrentURL(request2);
|
|
14121
14121
|
const agent2 = fetchParams.controller.dispatcher;
|
|
14122
|
-
return new Promise((
|
|
14122
|
+
return new Promise((resolve3, reject) => agent2.dispatch(
|
|
14123
14123
|
{
|
|
14124
14124
|
path: url4.pathname + url4.search,
|
|
14125
14125
|
origin: url4.origin,
|
|
@@ -14195,7 +14195,7 @@ var require_fetch = __commonJS({
|
|
|
14195
14195
|
}
|
|
14196
14196
|
}
|
|
14197
14197
|
}
|
|
14198
|
-
|
|
14198
|
+
resolve3({
|
|
14199
14199
|
status,
|
|
14200
14200
|
statusText,
|
|
14201
14201
|
headersList: headers[kHeadersList],
|
|
@@ -14238,7 +14238,7 @@ var require_fetch = __commonJS({
|
|
|
14238
14238
|
const val = headersList[n + 1].toString("latin1");
|
|
14239
14239
|
headers[kHeadersList].append(key, val);
|
|
14240
14240
|
}
|
|
14241
|
-
|
|
14241
|
+
resolve3({
|
|
14242
14242
|
status,
|
|
14243
14243
|
statusText: STATUS_CODES[status],
|
|
14244
14244
|
headersList: headers[kHeadersList],
|
|
@@ -17592,11 +17592,11 @@ var require_lib = __commonJS({
|
|
|
17592
17592
|
};
|
|
17593
17593
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
|
|
17594
17594
|
function adopt(value2) {
|
|
17595
|
-
return value2 instanceof P3 ? value2 : new P3(function(
|
|
17596
|
-
|
|
17595
|
+
return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
|
|
17596
|
+
resolve3(value2);
|
|
17597
17597
|
});
|
|
17598
17598
|
}
|
|
17599
|
-
return new (P3 || (P3 = Promise))(function(
|
|
17599
|
+
return new (P3 || (P3 = Promise))(function(resolve3, reject) {
|
|
17600
17600
|
function fulfilled(value2) {
|
|
17601
17601
|
try {
|
|
17602
17602
|
step(generator.next(value2));
|
|
@@ -17612,7 +17612,7 @@ var require_lib = __commonJS({
|
|
|
17612
17612
|
}
|
|
17613
17613
|
}
|
|
17614
17614
|
function step(result) {
|
|
17615
|
-
result.done ?
|
|
17615
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
17616
17616
|
}
|
|
17617
17617
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
17618
17618
|
});
|
|
@@ -17698,26 +17698,26 @@ var require_lib = __commonJS({
|
|
|
17698
17698
|
}
|
|
17699
17699
|
readBody() {
|
|
17700
17700
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17701
|
-
return new Promise((
|
|
17701
|
+
return new Promise((resolve3) => __awaiter(this, void 0, void 0, function* () {
|
|
17702
17702
|
let output = Buffer.alloc(0);
|
|
17703
17703
|
this.message.on("data", (chunk) => {
|
|
17704
17704
|
output = Buffer.concat([output, chunk]);
|
|
17705
17705
|
});
|
|
17706
17706
|
this.message.on("end", () => {
|
|
17707
|
-
|
|
17707
|
+
resolve3(output.toString());
|
|
17708
17708
|
});
|
|
17709
17709
|
}));
|
|
17710
17710
|
});
|
|
17711
17711
|
}
|
|
17712
17712
|
readBodyBuffer() {
|
|
17713
17713
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17714
|
-
return new Promise((
|
|
17714
|
+
return new Promise((resolve3) => __awaiter(this, void 0, void 0, function* () {
|
|
17715
17715
|
const chunks = [];
|
|
17716
17716
|
this.message.on("data", (chunk) => {
|
|
17717
17717
|
chunks.push(chunk);
|
|
17718
17718
|
});
|
|
17719
17719
|
this.message.on("end", () => {
|
|
17720
|
-
|
|
17720
|
+
resolve3(Buffer.concat(chunks));
|
|
17721
17721
|
});
|
|
17722
17722
|
}));
|
|
17723
17723
|
});
|
|
@@ -17926,14 +17926,14 @@ var require_lib = __commonJS({
|
|
|
17926
17926
|
*/
|
|
17927
17927
|
requestRaw(info3, data) {
|
|
17928
17928
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17929
|
-
return new Promise((
|
|
17929
|
+
return new Promise((resolve3, reject) => {
|
|
17930
17930
|
function callbackForResult(err, res) {
|
|
17931
17931
|
if (err) {
|
|
17932
17932
|
reject(err);
|
|
17933
17933
|
} else if (!res) {
|
|
17934
17934
|
reject(new Error("Unknown error"));
|
|
17935
17935
|
} else {
|
|
17936
|
-
|
|
17936
|
+
resolve3(res);
|
|
17937
17937
|
}
|
|
17938
17938
|
}
|
|
17939
17939
|
this.requestRawWithCallback(info3, data, callbackForResult);
|
|
@@ -18115,12 +18115,12 @@ var require_lib = __commonJS({
|
|
|
18115
18115
|
return __awaiter(this, void 0, void 0, function* () {
|
|
18116
18116
|
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
|
|
18117
18117
|
const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
|
|
18118
|
-
return new Promise((
|
|
18118
|
+
return new Promise((resolve3) => setTimeout(() => resolve3(), ms));
|
|
18119
18119
|
});
|
|
18120
18120
|
}
|
|
18121
18121
|
_processResponse(res, options) {
|
|
18122
18122
|
return __awaiter(this, void 0, void 0, function* () {
|
|
18123
|
-
return new Promise((
|
|
18123
|
+
return new Promise((resolve3, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
18124
18124
|
const statusCode = res.message.statusCode || 0;
|
|
18125
18125
|
const response = {
|
|
18126
18126
|
statusCode,
|
|
@@ -18128,7 +18128,7 @@ var require_lib = __commonJS({
|
|
|
18128
18128
|
headers: {}
|
|
18129
18129
|
};
|
|
18130
18130
|
if (statusCode === HttpCodes.NotFound) {
|
|
18131
|
-
|
|
18131
|
+
resolve3(response);
|
|
18132
18132
|
}
|
|
18133
18133
|
function dateTimeDeserializer(key, value2) {
|
|
18134
18134
|
if (typeof value2 === "string") {
|
|
@@ -18167,7 +18167,7 @@ var require_lib = __commonJS({
|
|
|
18167
18167
|
err.result = response.result;
|
|
18168
18168
|
reject(err);
|
|
18169
18169
|
} else {
|
|
18170
|
-
|
|
18170
|
+
resolve3(response);
|
|
18171
18171
|
}
|
|
18172
18172
|
}));
|
|
18173
18173
|
});
|
|
@@ -18184,11 +18184,11 @@ var require_auth = __commonJS({
|
|
|
18184
18184
|
"use strict";
|
|
18185
18185
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
|
|
18186
18186
|
function adopt(value2) {
|
|
18187
|
-
return value2 instanceof P3 ? value2 : new P3(function(
|
|
18188
|
-
|
|
18187
|
+
return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
|
|
18188
|
+
resolve3(value2);
|
|
18189
18189
|
});
|
|
18190
18190
|
}
|
|
18191
|
-
return new (P3 || (P3 = Promise))(function(
|
|
18191
|
+
return new (P3 || (P3 = Promise))(function(resolve3, reject) {
|
|
18192
18192
|
function fulfilled(value2) {
|
|
18193
18193
|
try {
|
|
18194
18194
|
step(generator.next(value2));
|
|
@@ -18204,7 +18204,7 @@ var require_auth = __commonJS({
|
|
|
18204
18204
|
}
|
|
18205
18205
|
}
|
|
18206
18206
|
function step(result) {
|
|
18207
|
-
result.done ?
|
|
18207
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18208
18208
|
}
|
|
18209
18209
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18210
18210
|
});
|
|
@@ -18288,11 +18288,11 @@ var require_oidc_utils = __commonJS({
|
|
|
18288
18288
|
"use strict";
|
|
18289
18289
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
|
|
18290
18290
|
function adopt(value2) {
|
|
18291
|
-
return value2 instanceof P3 ? value2 : new P3(function(
|
|
18292
|
-
|
|
18291
|
+
return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
|
|
18292
|
+
resolve3(value2);
|
|
18293
18293
|
});
|
|
18294
18294
|
}
|
|
18295
|
-
return new (P3 || (P3 = Promise))(function(
|
|
18295
|
+
return new (P3 || (P3 = Promise))(function(resolve3, reject) {
|
|
18296
18296
|
function fulfilled(value2) {
|
|
18297
18297
|
try {
|
|
18298
18298
|
step(generator.next(value2));
|
|
@@ -18308,7 +18308,7 @@ var require_oidc_utils = __commonJS({
|
|
|
18308
18308
|
}
|
|
18309
18309
|
}
|
|
18310
18310
|
function step(result) {
|
|
18311
|
-
result.done ?
|
|
18311
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18312
18312
|
}
|
|
18313
18313
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18314
18314
|
});
|
|
@@ -18386,11 +18386,11 @@ var require_summary = __commonJS({
|
|
|
18386
18386
|
"use strict";
|
|
18387
18387
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
|
|
18388
18388
|
function adopt(value2) {
|
|
18389
|
-
return value2 instanceof P3 ? value2 : new P3(function(
|
|
18390
|
-
|
|
18389
|
+
return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
|
|
18390
|
+
resolve3(value2);
|
|
18391
18391
|
});
|
|
18392
18392
|
}
|
|
18393
|
-
return new (P3 || (P3 = Promise))(function(
|
|
18393
|
+
return new (P3 || (P3 = Promise))(function(resolve3, reject) {
|
|
18394
18394
|
function fulfilled(value2) {
|
|
18395
18395
|
try {
|
|
18396
18396
|
step(generator.next(value2));
|
|
@@ -18406,7 +18406,7 @@ var require_summary = __commonJS({
|
|
|
18406
18406
|
}
|
|
18407
18407
|
}
|
|
18408
18408
|
function step(result) {
|
|
18409
|
-
result.done ?
|
|
18409
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18410
18410
|
}
|
|
18411
18411
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18412
18412
|
});
|
|
@@ -18752,11 +18752,11 @@ var require_io_util = __commonJS({
|
|
|
18752
18752
|
};
|
|
18753
18753
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
|
|
18754
18754
|
function adopt(value2) {
|
|
18755
|
-
return value2 instanceof P3 ? value2 : new P3(function(
|
|
18756
|
-
|
|
18755
|
+
return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
|
|
18756
|
+
resolve3(value2);
|
|
18757
18757
|
});
|
|
18758
18758
|
}
|
|
18759
|
-
return new (P3 || (P3 = Promise))(function(
|
|
18759
|
+
return new (P3 || (P3 = Promise))(function(resolve3, reject) {
|
|
18760
18760
|
function fulfilled(value2) {
|
|
18761
18761
|
try {
|
|
18762
18762
|
step(generator.next(value2));
|
|
@@ -18772,7 +18772,7 @@ var require_io_util = __commonJS({
|
|
|
18772
18772
|
}
|
|
18773
18773
|
}
|
|
18774
18774
|
function step(result) {
|
|
18775
|
-
result.done ?
|
|
18775
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18776
18776
|
}
|
|
18777
18777
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18778
18778
|
});
|
|
@@ -18925,11 +18925,11 @@ var require_io = __commonJS({
|
|
|
18925
18925
|
};
|
|
18926
18926
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
|
|
18927
18927
|
function adopt(value2) {
|
|
18928
|
-
return value2 instanceof P3 ? value2 : new P3(function(
|
|
18929
|
-
|
|
18928
|
+
return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
|
|
18929
|
+
resolve3(value2);
|
|
18930
18930
|
});
|
|
18931
18931
|
}
|
|
18932
|
-
return new (P3 || (P3 = Promise))(function(
|
|
18932
|
+
return new (P3 || (P3 = Promise))(function(resolve3, reject) {
|
|
18933
18933
|
function fulfilled(value2) {
|
|
18934
18934
|
try {
|
|
18935
18935
|
step(generator.next(value2));
|
|
@@ -18945,7 +18945,7 @@ var require_io = __commonJS({
|
|
|
18945
18945
|
}
|
|
18946
18946
|
}
|
|
18947
18947
|
function step(result) {
|
|
18948
|
-
result.done ?
|
|
18948
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18949
18949
|
}
|
|
18950
18950
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18951
18951
|
});
|
|
@@ -19173,11 +19173,11 @@ var require_toolrunner = __commonJS({
|
|
|
19173
19173
|
};
|
|
19174
19174
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
|
|
19175
19175
|
function adopt(value2) {
|
|
19176
|
-
return value2 instanceof P3 ? value2 : new P3(function(
|
|
19177
|
-
|
|
19176
|
+
return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
|
|
19177
|
+
resolve3(value2);
|
|
19178
19178
|
});
|
|
19179
19179
|
}
|
|
19180
|
-
return new (P3 || (P3 = Promise))(function(
|
|
19180
|
+
return new (P3 || (P3 = Promise))(function(resolve3, reject) {
|
|
19181
19181
|
function fulfilled(value2) {
|
|
19182
19182
|
try {
|
|
19183
19183
|
step(generator.next(value2));
|
|
@@ -19193,7 +19193,7 @@ var require_toolrunner = __commonJS({
|
|
|
19193
19193
|
}
|
|
19194
19194
|
}
|
|
19195
19195
|
function step(result) {
|
|
19196
|
-
result.done ?
|
|
19196
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
19197
19197
|
}
|
|
19198
19198
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19199
19199
|
});
|
|
@@ -19421,7 +19421,7 @@ var require_toolrunner = __commonJS({
|
|
|
19421
19421
|
this.toolPath = path3.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);
|
|
19422
19422
|
}
|
|
19423
19423
|
this.toolPath = yield io.which(this.toolPath, true);
|
|
19424
|
-
return new Promise((
|
|
19424
|
+
return new Promise((resolve3, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
19425
19425
|
this._debug(`exec tool: ${this.toolPath}`);
|
|
19426
19426
|
this._debug("arguments:");
|
|
19427
19427
|
for (const arg4 of this.args) {
|
|
@@ -19504,7 +19504,7 @@ var require_toolrunner = __commonJS({
|
|
|
19504
19504
|
if (error49) {
|
|
19505
19505
|
reject(error49);
|
|
19506
19506
|
} else {
|
|
19507
|
-
|
|
19507
|
+
resolve3(exitCode);
|
|
19508
19508
|
}
|
|
19509
19509
|
});
|
|
19510
19510
|
if (this.options.input) {
|
|
@@ -19657,11 +19657,11 @@ var require_exec = __commonJS({
|
|
|
19657
19657
|
};
|
|
19658
19658
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
|
|
19659
19659
|
function adopt(value2) {
|
|
19660
|
-
return value2 instanceof P3 ? value2 : new P3(function(
|
|
19661
|
-
|
|
19660
|
+
return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
|
|
19661
|
+
resolve3(value2);
|
|
19662
19662
|
});
|
|
19663
19663
|
}
|
|
19664
|
-
return new (P3 || (P3 = Promise))(function(
|
|
19664
|
+
return new (P3 || (P3 = Promise))(function(resolve3, reject) {
|
|
19665
19665
|
function fulfilled(value2) {
|
|
19666
19666
|
try {
|
|
19667
19667
|
step(generator.next(value2));
|
|
@@ -19677,7 +19677,7 @@ var require_exec = __commonJS({
|
|
|
19677
19677
|
}
|
|
19678
19678
|
}
|
|
19679
19679
|
function step(result) {
|
|
19680
|
-
result.done ?
|
|
19680
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
19681
19681
|
}
|
|
19682
19682
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19683
19683
|
});
|
|
@@ -19768,11 +19768,11 @@ var require_platform = __commonJS({
|
|
|
19768
19768
|
};
|
|
19769
19769
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
|
|
19770
19770
|
function adopt(value2) {
|
|
19771
|
-
return value2 instanceof P3 ? value2 : new P3(function(
|
|
19772
|
-
|
|
19771
|
+
return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
|
|
19772
|
+
resolve3(value2);
|
|
19773
19773
|
});
|
|
19774
19774
|
}
|
|
19775
|
-
return new (P3 || (P3 = Promise))(function(
|
|
19775
|
+
return new (P3 || (P3 = Promise))(function(resolve3, reject) {
|
|
19776
19776
|
function fulfilled(value2) {
|
|
19777
19777
|
try {
|
|
19778
19778
|
step(generator.next(value2));
|
|
@@ -19788,7 +19788,7 @@ var require_platform = __commonJS({
|
|
|
19788
19788
|
}
|
|
19789
19789
|
}
|
|
19790
19790
|
function step(result) {
|
|
19791
|
-
result.done ?
|
|
19791
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
19792
19792
|
}
|
|
19793
19793
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19794
19794
|
});
|
|
@@ -19887,11 +19887,11 @@ var require_core = __commonJS({
|
|
|
19887
19887
|
};
|
|
19888
19888
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P3, generator) {
|
|
19889
19889
|
function adopt(value2) {
|
|
19890
|
-
return value2 instanceof P3 ? value2 : new P3(function(
|
|
19891
|
-
|
|
19890
|
+
return value2 instanceof P3 ? value2 : new P3(function(resolve3) {
|
|
19891
|
+
resolve3(value2);
|
|
19892
19892
|
});
|
|
19893
19893
|
}
|
|
19894
|
-
return new (P3 || (P3 = Promise))(function(
|
|
19894
|
+
return new (P3 || (P3 = Promise))(function(resolve3, reject) {
|
|
19895
19895
|
function fulfilled(value2) {
|
|
19896
19896
|
try {
|
|
19897
19897
|
step(generator.next(value2));
|
|
@@ -19907,7 +19907,7 @@ var require_core = __commonJS({
|
|
|
19907
19907
|
}
|
|
19908
19908
|
}
|
|
19909
19909
|
function step(result) {
|
|
19910
|
-
result.done ?
|
|
19910
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
19911
19911
|
}
|
|
19912
19912
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19913
19913
|
});
|
|
@@ -45598,8 +45598,8 @@ var require_resolve = __commonJS({
|
|
|
45598
45598
|
}
|
|
45599
45599
|
return count;
|
|
45600
45600
|
}
|
|
45601
|
-
function getFullPath(resolver, id = "",
|
|
45602
|
-
if (
|
|
45601
|
+
function getFullPath(resolver, id = "", normalize3) {
|
|
45602
|
+
if (normalize3 !== false)
|
|
45603
45603
|
id = normalizeId(id);
|
|
45604
45604
|
const p2 = resolver.parse(id);
|
|
45605
45605
|
return _getFullPath(resolver, p2);
|
|
@@ -46347,7 +46347,7 @@ var require_compile = __commonJS({
|
|
|
46347
46347
|
const schOrFunc = root.refs[ref];
|
|
46348
46348
|
if (schOrFunc)
|
|
46349
46349
|
return schOrFunc;
|
|
46350
|
-
let _sch =
|
|
46350
|
+
let _sch = resolve3.call(this, root, ref);
|
|
46351
46351
|
if (_sch === void 0) {
|
|
46352
46352
|
const schema2 = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
|
|
46353
46353
|
const { schemaId } = this.opts;
|
|
@@ -46374,7 +46374,7 @@ var require_compile = __commonJS({
|
|
|
46374
46374
|
function sameSchemaEnv(s1, s2) {
|
|
46375
46375
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
46376
46376
|
}
|
|
46377
|
-
function
|
|
46377
|
+
function resolve3(root, ref) {
|
|
46378
46378
|
let sch;
|
|
46379
46379
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
46380
46380
|
ref = sch;
|
|
@@ -46939,7 +46939,7 @@ var require_fast_uri = __commonJS({
|
|
|
46939
46939
|
"use strict";
|
|
46940
46940
|
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils4();
|
|
46941
46941
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
46942
|
-
function
|
|
46942
|
+
function normalize3(uri, options) {
|
|
46943
46943
|
if (typeof uri === "string") {
|
|
46944
46944
|
uri = /** @type {T} */
|
|
46945
46945
|
serialize(parse5(uri, options), options);
|
|
@@ -46949,7 +46949,7 @@ var require_fast_uri = __commonJS({
|
|
|
46949
46949
|
}
|
|
46950
46950
|
return uri;
|
|
46951
46951
|
}
|
|
46952
|
-
function
|
|
46952
|
+
function resolve3(baseURI, relativeURI, options) {
|
|
46953
46953
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
46954
46954
|
const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
46955
46955
|
schemelessOptions.skipEscape = true;
|
|
@@ -47175,8 +47175,8 @@ var require_fast_uri = __commonJS({
|
|
|
47175
47175
|
}
|
|
47176
47176
|
var fastUri = {
|
|
47177
47177
|
SCHEMES,
|
|
47178
|
-
normalize:
|
|
47179
|
-
resolve:
|
|
47178
|
+
normalize: normalize3,
|
|
47179
|
+
resolve: resolve3,
|
|
47180
47180
|
resolveComponent,
|
|
47181
47181
|
equal,
|
|
47182
47182
|
serialize,
|
|
@@ -52752,9 +52752,9 @@ var require_dispatcher_base2 = __commonJS({
|
|
|
52752
52752
|
}
|
|
52753
52753
|
close(callback) {
|
|
52754
52754
|
if (callback === void 0) {
|
|
52755
|
-
return new Promise((
|
|
52755
|
+
return new Promise((resolve3, reject) => {
|
|
52756
52756
|
this.close((err, data) => {
|
|
52757
|
-
return err ? reject(err) :
|
|
52757
|
+
return err ? reject(err) : resolve3(data);
|
|
52758
52758
|
});
|
|
52759
52759
|
});
|
|
52760
52760
|
}
|
|
@@ -52792,9 +52792,9 @@ var require_dispatcher_base2 = __commonJS({
|
|
|
52792
52792
|
err = null;
|
|
52793
52793
|
}
|
|
52794
52794
|
if (callback === void 0) {
|
|
52795
|
-
return new Promise((
|
|
52795
|
+
return new Promise((resolve3, reject) => {
|
|
52796
52796
|
this.destroy(err, (err2, data) => {
|
|
52797
|
-
return err2 ? reject(err2) :
|
|
52797
|
+
return err2 ? reject(err2) : resolve3(data);
|
|
52798
52798
|
});
|
|
52799
52799
|
});
|
|
52800
52800
|
}
|
|
@@ -56270,8 +56270,8 @@ var require_promise = __commonJS({
|
|
|
56270
56270
|
function createDeferredPromise() {
|
|
56271
56271
|
let res;
|
|
56272
56272
|
let rej;
|
|
56273
|
-
const promise2 = new Promise((
|
|
56274
|
-
res =
|
|
56273
|
+
const promise2 = new Promise((resolve3, reject) => {
|
|
56274
|
+
res = resolve3;
|
|
56275
56275
|
rej = reject;
|
|
56276
56276
|
});
|
|
56277
56277
|
return { promise: promise2, resolve: res, reject: rej };
|
|
@@ -57569,12 +57569,12 @@ upgrade: ${upgrade}\r
|
|
|
57569
57569
|
cb();
|
|
57570
57570
|
}
|
|
57571
57571
|
}
|
|
57572
|
-
const waitForDrain = () => new Promise((
|
|
57572
|
+
const waitForDrain = () => new Promise((resolve3, reject) => {
|
|
57573
57573
|
assert3(callback === null);
|
|
57574
57574
|
if (socket[kError]) {
|
|
57575
57575
|
reject(socket[kError]);
|
|
57576
57576
|
} else {
|
|
57577
|
-
callback =
|
|
57577
|
+
callback = resolve3;
|
|
57578
57578
|
}
|
|
57579
57579
|
});
|
|
57580
57580
|
socket.on("close", onDrain).on("drain", onDrain);
|
|
@@ -58415,12 +58415,12 @@ var require_client_h2 = __commonJS({
|
|
|
58415
58415
|
cb();
|
|
58416
58416
|
}
|
|
58417
58417
|
}
|
|
58418
|
-
const waitForDrain = () => new Promise((
|
|
58418
|
+
const waitForDrain = () => new Promise((resolve3, reject) => {
|
|
58419
58419
|
assert3(callback === null);
|
|
58420
58420
|
if (socket[kError]) {
|
|
58421
58421
|
reject(socket[kError]);
|
|
58422
58422
|
} else {
|
|
58423
|
-
callback =
|
|
58423
|
+
callback = resolve3;
|
|
58424
58424
|
}
|
|
58425
58425
|
});
|
|
58426
58426
|
h2stream.on("close", onDrain).on("drain", onDrain);
|
|
@@ -58728,16 +58728,16 @@ var require_client2 = __commonJS({
|
|
|
58728
58728
|
return this[kNeedDrain] < 2;
|
|
58729
58729
|
}
|
|
58730
58730
|
[kClose]() {
|
|
58731
|
-
return new Promise((
|
|
58731
|
+
return new Promise((resolve3) => {
|
|
58732
58732
|
if (this[kSize]) {
|
|
58733
|
-
this[kClosedResolve] =
|
|
58733
|
+
this[kClosedResolve] = resolve3;
|
|
58734
58734
|
} else {
|
|
58735
|
-
|
|
58735
|
+
resolve3(null);
|
|
58736
58736
|
}
|
|
58737
58737
|
});
|
|
58738
58738
|
}
|
|
58739
58739
|
[kDestroy](err) {
|
|
58740
|
-
return new Promise((
|
|
58740
|
+
return new Promise((resolve3) => {
|
|
58741
58741
|
const requests = this[kQueue].splice(this[kPendingIdx]);
|
|
58742
58742
|
for (let i = 0; i < requests.length; i++) {
|
|
58743
58743
|
const request2 = requests[i];
|
|
@@ -58748,7 +58748,7 @@ var require_client2 = __commonJS({
|
|
|
58748
58748
|
this[kClosedResolve]();
|
|
58749
58749
|
this[kClosedResolve] = null;
|
|
58750
58750
|
}
|
|
58751
|
-
|
|
58751
|
+
resolve3(null);
|
|
58752
58752
|
};
|
|
58753
58753
|
if (this[kHTTPContext]) {
|
|
58754
58754
|
this[kHTTPContext].destroy(err, callback);
|
|
@@ -59145,8 +59145,8 @@ var require_pool_base2 = __commonJS({
|
|
|
59145
59145
|
}
|
|
59146
59146
|
return Promise.all(closeAll);
|
|
59147
59147
|
} else {
|
|
59148
|
-
return new Promise((
|
|
59149
|
-
this[kClosedResolve] =
|
|
59148
|
+
return new Promise((resolve3) => {
|
|
59149
|
+
this[kClosedResolve] = resolve3;
|
|
59150
59150
|
});
|
|
59151
59151
|
}
|
|
59152
59152
|
}
|
|
@@ -60675,7 +60675,7 @@ var require_readable2 = __commonJS({
|
|
|
60675
60675
|
if (this._readableState.closeEmitted) {
|
|
60676
60676
|
return Promise.resolve(null);
|
|
60677
60677
|
}
|
|
60678
|
-
return new Promise((
|
|
60678
|
+
return new Promise((resolve3, reject) => {
|
|
60679
60679
|
if (this[kContentLength] && this[kContentLength] > limit || this[kBytesRead] > limit) {
|
|
60680
60680
|
this.destroy(new AbortError2());
|
|
60681
60681
|
}
|
|
@@ -60689,11 +60689,11 @@ var require_readable2 = __commonJS({
|
|
|
60689
60689
|
if (signal.aborted) {
|
|
60690
60690
|
reject(signal.reason ?? new AbortError2());
|
|
60691
60691
|
} else {
|
|
60692
|
-
|
|
60692
|
+
resolve3(null);
|
|
60693
60693
|
}
|
|
60694
60694
|
});
|
|
60695
60695
|
} else {
|
|
60696
|
-
this.on("close",
|
|
60696
|
+
this.on("close", resolve3);
|
|
60697
60697
|
}
|
|
60698
60698
|
this.on("error", noop4).on("data", () => {
|
|
60699
60699
|
if (this[kBytesRead] > limit) {
|
|
@@ -60721,7 +60721,7 @@ var require_readable2 = __commonJS({
|
|
|
60721
60721
|
}
|
|
60722
60722
|
function consume(stream, type2) {
|
|
60723
60723
|
assert3(!stream[kConsume]);
|
|
60724
|
-
return new Promise((
|
|
60724
|
+
return new Promise((resolve3, reject) => {
|
|
60725
60725
|
if (isUnusable(stream)) {
|
|
60726
60726
|
const rState = stream._readableState;
|
|
60727
60727
|
if (rState.destroyed && rState.closeEmitted === false) {
|
|
@@ -60736,7 +60736,7 @@ var require_readable2 = __commonJS({
|
|
|
60736
60736
|
stream[kConsume] = {
|
|
60737
60737
|
type: type2,
|
|
60738
60738
|
stream,
|
|
60739
|
-
resolve:
|
|
60739
|
+
resolve: resolve3,
|
|
60740
60740
|
reject,
|
|
60741
60741
|
length: 0,
|
|
60742
60742
|
body: []
|
|
@@ -60810,18 +60810,18 @@ var require_readable2 = __commonJS({
|
|
|
60810
60810
|
return buffer;
|
|
60811
60811
|
}
|
|
60812
60812
|
function consumeEnd(consume2, encoding) {
|
|
60813
|
-
const { type: type2, body, resolve:
|
|
60813
|
+
const { type: type2, body, resolve: resolve3, stream, length } = consume2;
|
|
60814
60814
|
try {
|
|
60815
60815
|
if (type2 === "text") {
|
|
60816
|
-
|
|
60816
|
+
resolve3(chunksDecode(body, length, encoding));
|
|
60817
60817
|
} else if (type2 === "json") {
|
|
60818
|
-
|
|
60818
|
+
resolve3(JSON.parse(chunksDecode(body, length, encoding)));
|
|
60819
60819
|
} else if (type2 === "arrayBuffer") {
|
|
60820
|
-
|
|
60820
|
+
resolve3(chunksConcat(body, length).buffer);
|
|
60821
60821
|
} else if (type2 === "blob") {
|
|
60822
|
-
|
|
60822
|
+
resolve3(new Blob(body, { type: stream[kContentType] }));
|
|
60823
60823
|
} else if (type2 === "bytes") {
|
|
60824
|
-
|
|
60824
|
+
resolve3(chunksConcat(body, length));
|
|
60825
60825
|
}
|
|
60826
60826
|
consumeFinish(consume2);
|
|
60827
60827
|
} catch (err) {
|
|
@@ -61011,9 +61011,9 @@ var require_api_request2 = __commonJS({
|
|
|
61011
61011
|
};
|
|
61012
61012
|
function request2(opts, callback) {
|
|
61013
61013
|
if (callback === void 0) {
|
|
61014
|
-
return new Promise((
|
|
61014
|
+
return new Promise((resolve3, reject) => {
|
|
61015
61015
|
request2.call(this, opts, (err, data) => {
|
|
61016
|
-
return err ? reject(err) :
|
|
61016
|
+
return err ? reject(err) : resolve3(data);
|
|
61017
61017
|
});
|
|
61018
61018
|
});
|
|
61019
61019
|
}
|
|
@@ -61225,9 +61225,9 @@ var require_api_stream2 = __commonJS({
|
|
|
61225
61225
|
};
|
|
61226
61226
|
function stream(opts, factory, callback) {
|
|
61227
61227
|
if (callback === void 0) {
|
|
61228
|
-
return new Promise((
|
|
61228
|
+
return new Promise((resolve3, reject) => {
|
|
61229
61229
|
stream.call(this, opts, factory, (err, data) => {
|
|
61230
|
-
return err ? reject(err) :
|
|
61230
|
+
return err ? reject(err) : resolve3(data);
|
|
61231
61231
|
});
|
|
61232
61232
|
});
|
|
61233
61233
|
}
|
|
@@ -61515,9 +61515,9 @@ var require_api_upgrade2 = __commonJS({
|
|
|
61515
61515
|
};
|
|
61516
61516
|
function upgrade(opts, callback) {
|
|
61517
61517
|
if (callback === void 0) {
|
|
61518
|
-
return new Promise((
|
|
61518
|
+
return new Promise((resolve3, reject) => {
|
|
61519
61519
|
upgrade.call(this, opts, (err, data) => {
|
|
61520
|
-
return err ? reject(err) :
|
|
61520
|
+
return err ? reject(err) : resolve3(data);
|
|
61521
61521
|
});
|
|
61522
61522
|
});
|
|
61523
61523
|
}
|
|
@@ -61610,9 +61610,9 @@ var require_api_connect2 = __commonJS({
|
|
|
61610
61610
|
};
|
|
61611
61611
|
function connect(opts, callback) {
|
|
61612
61612
|
if (callback === void 0) {
|
|
61613
|
-
return new Promise((
|
|
61613
|
+
return new Promise((resolve3, reject) => {
|
|
61614
61614
|
connect.call(this, opts, (err, data) => {
|
|
61615
|
-
return err ? reject(err) :
|
|
61615
|
+
return err ? reject(err) : resolve3(data);
|
|
61616
61616
|
});
|
|
61617
61617
|
});
|
|
61618
61618
|
}
|
|
@@ -62880,7 +62880,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
62880
62880
|
"node_modules/.pnpm/undici@7.22.0/node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
|
|
62881
62881
|
"use strict";
|
|
62882
62882
|
var { writeFile: writeFile2, readFile, mkdir } = __require("node:fs/promises");
|
|
62883
|
-
var { dirname: dirname4, resolve:
|
|
62883
|
+
var { dirname: dirname4, resolve: resolve3 } = __require("node:path");
|
|
62884
62884
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("node:timers");
|
|
62885
62885
|
var { InvalidArgumentError, UndiciError } = require_errors4();
|
|
62886
62886
|
var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -63081,7 +63081,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
63081
63081
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
63082
63082
|
}
|
|
63083
63083
|
try {
|
|
63084
|
-
const data = await readFile(
|
|
63084
|
+
const data = await readFile(resolve3(path3), "utf8");
|
|
63085
63085
|
const parsed2 = JSON.parse(data);
|
|
63086
63086
|
if (Array.isArray(parsed2)) {
|
|
63087
63087
|
this.#snapshots.clear();
|
|
@@ -63110,7 +63110,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
63110
63110
|
if (!path3) {
|
|
63111
63111
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
63112
63112
|
}
|
|
63113
|
-
const resolvedPath =
|
|
63113
|
+
const resolvedPath = resolve3(path3);
|
|
63114
63114
|
await mkdir(dirname4(resolvedPath), { recursive: true });
|
|
63115
63115
|
const data = Array.from(this.#snapshots.entries()).map(([hash2, snapshot2]) => ({
|
|
63116
63116
|
hash: hash2,
|
|
@@ -69687,7 +69687,7 @@ var require_fetch2 = __commonJS({
|
|
|
69687
69687
|
function dispatch({ body }) {
|
|
69688
69688
|
const url4 = requestCurrentURL(request2);
|
|
69689
69689
|
const agent2 = fetchParams.controller.dispatcher;
|
|
69690
|
-
return new Promise((
|
|
69690
|
+
return new Promise((resolve3, reject) => agent2.dispatch(
|
|
69691
69691
|
{
|
|
69692
69692
|
path: url4.pathname + url4.search,
|
|
69693
69693
|
origin: url4.origin,
|
|
@@ -69767,7 +69767,7 @@ var require_fetch2 = __commonJS({
|
|
|
69767
69767
|
}
|
|
69768
69768
|
}
|
|
69769
69769
|
const onError = this.onError.bind(this);
|
|
69770
|
-
|
|
69770
|
+
resolve3({
|
|
69771
69771
|
status,
|
|
69772
69772
|
statusText,
|
|
69773
69773
|
headersList,
|
|
@@ -69820,7 +69820,7 @@ var require_fetch2 = __commonJS({
|
|
|
69820
69820
|
headersList.append(headerName, String(value2), true);
|
|
69821
69821
|
}
|
|
69822
69822
|
}
|
|
69823
|
-
|
|
69823
|
+
resolve3({
|
|
69824
69824
|
status,
|
|
69825
69825
|
statusText: STATUS_CODES[status],
|
|
69826
69826
|
headersList,
|
|
@@ -69836,7 +69836,7 @@ var require_fetch2 = __commonJS({
|
|
|
69836
69836
|
for (let i = 0; i < rawHeaders.length; i += 2) {
|
|
69837
69837
|
headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString("latin1"), true);
|
|
69838
69838
|
}
|
|
69839
|
-
|
|
69839
|
+
resolve3({
|
|
69840
69840
|
status,
|
|
69841
69841
|
statusText: STATUS_CODES[status],
|
|
69842
69842
|
headersList,
|
|
@@ -97692,14 +97692,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
97692
97692
|
} else if (node2.nodeType === 1) {
|
|
97693
97693
|
replacement = replacementForNode.call(self2, node2);
|
|
97694
97694
|
}
|
|
97695
|
-
return
|
|
97695
|
+
return join15(output, replacement);
|
|
97696
97696
|
}, "");
|
|
97697
97697
|
}
|
|
97698
97698
|
function postProcess(output) {
|
|
97699
97699
|
var self2 = this;
|
|
97700
97700
|
this.rules.forEach(function(rule) {
|
|
97701
97701
|
if (typeof rule.append === "function") {
|
|
97702
|
-
output =
|
|
97702
|
+
output = join15(output, rule.append(self2.options));
|
|
97703
97703
|
}
|
|
97704
97704
|
});
|
|
97705
97705
|
return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -97711,7 +97711,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
97711
97711
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
97712
97712
|
return whitespace.leading + rule.replacement(content, node2, this.options) + whitespace.trailing;
|
|
97713
97713
|
}
|
|
97714
|
-
function
|
|
97714
|
+
function join15(output, replacement) {
|
|
97715
97715
|
var s1 = trimTrailingNewlines(output);
|
|
97716
97716
|
var s2 = trimLeadingNewlines(replacement);
|
|
97717
97717
|
var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
|
|
@@ -98178,8 +98178,8 @@ var require_light = __commonJS({
|
|
|
98178
98178
|
return this.Promise.resolve();
|
|
98179
98179
|
}
|
|
98180
98180
|
yieldLoop(t2 = 0) {
|
|
98181
|
-
return new this.Promise(function(
|
|
98182
|
-
return setTimeout(
|
|
98181
|
+
return new this.Promise(function(resolve3, reject) {
|
|
98182
|
+
return setTimeout(resolve3, t2);
|
|
98183
98183
|
});
|
|
98184
98184
|
}
|
|
98185
98185
|
computePenalty() {
|
|
@@ -98390,15 +98390,15 @@ var require_light = __commonJS({
|
|
|
98390
98390
|
return this._queue.length === 0;
|
|
98391
98391
|
}
|
|
98392
98392
|
async _tryToRun() {
|
|
98393
|
-
var args2, cb, error49, reject,
|
|
98393
|
+
var args2, cb, error49, reject, resolve3, returned, task;
|
|
98394
98394
|
if (this._running < 1 && this._queue.length > 0) {
|
|
98395
98395
|
this._running++;
|
|
98396
|
-
({ task, args: args2, resolve:
|
|
98396
|
+
({ task, args: args2, resolve: resolve3, reject } = this._queue.shift());
|
|
98397
98397
|
cb = await (async function() {
|
|
98398
98398
|
try {
|
|
98399
98399
|
returned = await task(...args2);
|
|
98400
98400
|
return function() {
|
|
98401
|
-
return
|
|
98401
|
+
return resolve3(returned);
|
|
98402
98402
|
};
|
|
98403
98403
|
} catch (error1) {
|
|
98404
98404
|
error49 = error1;
|
|
@@ -98413,13 +98413,13 @@ var require_light = __commonJS({
|
|
|
98413
98413
|
}
|
|
98414
98414
|
}
|
|
98415
98415
|
schedule(task, ...args2) {
|
|
98416
|
-
var promise2, reject,
|
|
98417
|
-
|
|
98416
|
+
var promise2, reject, resolve3;
|
|
98417
|
+
resolve3 = reject = null;
|
|
98418
98418
|
promise2 = new this.Promise(function(_resolve, _reject) {
|
|
98419
|
-
|
|
98419
|
+
resolve3 = _resolve;
|
|
98420
98420
|
return reject = _reject;
|
|
98421
98421
|
});
|
|
98422
|
-
this._queue.push({ task, args: args2, resolve:
|
|
98422
|
+
this._queue.push({ task, args: args2, resolve: resolve3, reject });
|
|
98423
98423
|
this._tryToRun();
|
|
98424
98424
|
return promise2;
|
|
98425
98425
|
}
|
|
@@ -98820,14 +98820,14 @@ var require_light = __commonJS({
|
|
|
98820
98820
|
counts = this._states.counts;
|
|
98821
98821
|
return counts[0] + counts[1] + counts[2] + counts[3] === at2;
|
|
98822
98822
|
};
|
|
98823
|
-
return new this.Promise((
|
|
98823
|
+
return new this.Promise((resolve3, reject) => {
|
|
98824
98824
|
if (finished()) {
|
|
98825
|
-
return
|
|
98825
|
+
return resolve3();
|
|
98826
98826
|
} else {
|
|
98827
98827
|
return this.on("done", () => {
|
|
98828
98828
|
if (finished()) {
|
|
98829
98829
|
this.removeAllListeners("done");
|
|
98830
|
-
return
|
|
98830
|
+
return resolve3();
|
|
98831
98831
|
}
|
|
98832
98832
|
});
|
|
98833
98833
|
}
|
|
@@ -98920,9 +98920,9 @@ var require_light = __commonJS({
|
|
|
98920
98920
|
options = parser$5.load(options, this.jobDefaults);
|
|
98921
98921
|
}
|
|
98922
98922
|
task = (...args3) => {
|
|
98923
|
-
return new this.Promise(function(
|
|
98923
|
+
return new this.Promise(function(resolve3, reject) {
|
|
98924
98924
|
return fn2(...args3, function(...args4) {
|
|
98925
|
-
return (args4[0] != null ? reject :
|
|
98925
|
+
return (args4[0] != null ? reject : resolve3)(args4);
|
|
98926
98926
|
});
|
|
98927
98927
|
});
|
|
98928
98928
|
};
|
|
@@ -99208,6 +99208,8 @@ import { dirname as dirname3 } from "node:path";
|
|
|
99208
99208
|
|
|
99209
99209
|
// main.ts
|
|
99210
99210
|
var core6 = __toESM(require_core(), 1);
|
|
99211
|
+
import { existsSync as existsSync6, readdirSync } from "node:fs";
|
|
99212
|
+
import { join as join14 } from "node:path";
|
|
99211
99213
|
|
|
99212
99214
|
// node_modules/.pnpm/@ark+util@0.56.0/node_modules/@ark/util/out/arrays.js
|
|
99213
99215
|
var liftArray = (data) => Array.isArray(data) ? data : [data];
|
|
@@ -105526,10 +105528,10 @@ var BaseScope = class {
|
|
|
105526
105528
|
});
|
|
105527
105529
|
};
|
|
105528
105530
|
lazyResolutions = [];
|
|
105529
|
-
lazilyResolve(
|
|
105531
|
+
lazilyResolve(resolve3, syntheticAlias) {
|
|
105530
105532
|
const node2 = this.node("alias", {
|
|
105531
105533
|
reference: syntheticAlias ?? "synthetic",
|
|
105532
|
-
resolve:
|
|
105534
|
+
resolve: resolve3
|
|
105533
105535
|
}, { prereduced: true });
|
|
105534
105536
|
if (!this.resolved)
|
|
105535
105537
|
this.lazyResolutions.push(node2);
|
|
@@ -107679,6 +107681,81 @@ var core = __toESM(require_core(), 1);
|
|
|
107679
107681
|
var import_table = __toESM(require_src(), 1);
|
|
107680
107682
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
107681
107683
|
|
|
107684
|
+
// agents/shared.ts
|
|
107685
|
+
import { execFileSync } from "node:child_process";
|
|
107686
|
+
var MAX_STDERR_LINES = 20;
|
|
107687
|
+
var MAX_COMMIT_RETRIES = 3;
|
|
107688
|
+
function getGitStatus() {
|
|
107689
|
+
try {
|
|
107690
|
+
return execFileSync("git", ["status", "--porcelain"], {
|
|
107691
|
+
encoding: "utf-8",
|
|
107692
|
+
timeout: 1e4
|
|
107693
|
+
}).trim();
|
|
107694
|
+
} catch {
|
|
107695
|
+
return "";
|
|
107696
|
+
}
|
|
107697
|
+
}
|
|
107698
|
+
function buildCommitPrompt(_agentId, status) {
|
|
107699
|
+
return [
|
|
107700
|
+
`UNCOMMITTED CHANGES \u2014 the working tree is dirty. push all changes to a pull request (new or existing). \`git status\` must be clean before you finish.`,
|
|
107701
|
+
"",
|
|
107702
|
+
"```",
|
|
107703
|
+
status,
|
|
107704
|
+
"```"
|
|
107705
|
+
].join("\n");
|
|
107706
|
+
}
|
|
107707
|
+
var agent = (input) => {
|
|
107708
|
+
return {
|
|
107709
|
+
...input,
|
|
107710
|
+
run: async (ctx) => {
|
|
107711
|
+
log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`);
|
|
107712
|
+
return input.run(ctx);
|
|
107713
|
+
}
|
|
107714
|
+
};
|
|
107715
|
+
};
|
|
107716
|
+
function formatCostUsd(costUsd) {
|
|
107717
|
+
return costUsd.toFixed(4);
|
|
107718
|
+
}
|
|
107719
|
+
function mergeAgentUsage(a, b) {
|
|
107720
|
+
if (!a && !b) return void 0;
|
|
107721
|
+
if (!a) return { ...b };
|
|
107722
|
+
if (!b) return { ...a };
|
|
107723
|
+
const cacheRead = (a.cacheReadTokens ?? 0) + (b.cacheReadTokens ?? 0);
|
|
107724
|
+
const cacheWrite = (a.cacheWriteTokens ?? 0) + (b.cacheWriteTokens ?? 0);
|
|
107725
|
+
const cost = (a.costUsd ?? 0) + (b.costUsd ?? 0);
|
|
107726
|
+
return {
|
|
107727
|
+
agent: a.agent,
|
|
107728
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
107729
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
107730
|
+
cacheReadTokens: cacheRead > 0 ? cacheRead : void 0,
|
|
107731
|
+
cacheWriteTokens: cacheWrite > 0 ? cacheWrite : void 0,
|
|
107732
|
+
costUsd: cost > 0 ? cost : void 0
|
|
107733
|
+
};
|
|
107734
|
+
}
|
|
107735
|
+
function logTokenTable(t2) {
|
|
107736
|
+
const total = t2.input + t2.cacheRead + t2.cacheWrite + t2.output;
|
|
107737
|
+
const costUsd = typeof t2.costUsd === "number" && t2.costUsd > 0 ? t2.costUsd : void 0;
|
|
107738
|
+
const headerRow = [
|
|
107739
|
+
{ data: "Input", header: true },
|
|
107740
|
+
{ data: "Cache Read", header: true },
|
|
107741
|
+
{ data: "Cache Write", header: true },
|
|
107742
|
+
{ data: "Output", header: true },
|
|
107743
|
+
{ data: "Total", header: true }
|
|
107744
|
+
];
|
|
107745
|
+
const dataRow = [
|
|
107746
|
+
String(t2.input),
|
|
107747
|
+
String(t2.cacheRead),
|
|
107748
|
+
String(t2.cacheWrite),
|
|
107749
|
+
String(t2.output),
|
|
107750
|
+
String(total)
|
|
107751
|
+
];
|
|
107752
|
+
if (costUsd !== void 0) {
|
|
107753
|
+
headerRow.push({ data: "Cost ($)", header: true });
|
|
107754
|
+
dataRow.push(formatCostUsd(costUsd));
|
|
107755
|
+
}
|
|
107756
|
+
log.table([headerRow, dataRow]);
|
|
107757
|
+
}
|
|
107758
|
+
|
|
107682
107759
|
// utils/globals.ts
|
|
107683
107760
|
import { existsSync } from "node:fs";
|
|
107684
107761
|
var isCloudflareSandbox = !!process.env.CLOUDFLARE_APPLICATION_ID && !!process.env.SANDBOX_VERSION;
|
|
@@ -107875,20 +107952,26 @@ function formatJsonValue(value2) {
|
|
|
107875
107952
|
}
|
|
107876
107953
|
function formatUsageSummary(entries) {
|
|
107877
107954
|
if (entries.length === 0) return "";
|
|
107878
|
-
const header = "| Agent | Input |
|
|
107879
|
-
const separatorRow = "| --- | ---: | ---: | ---: | ---: |";
|
|
107955
|
+
const header = "| Agent | Input | Cache Read | Cache Write | Output | Total | Cost ($) |";
|
|
107956
|
+
const separatorRow = "| --- | ---: | ---: | ---: | ---: | ---: | ---: |";
|
|
107880
107957
|
const fmt = (n) => n.toLocaleString("en-US");
|
|
107958
|
+
const nonCachedInput = (e) => Math.max(0, e.inputTokens - (e.cacheReadTokens ?? 0) - (e.cacheWriteTokens ?? 0));
|
|
107959
|
+
const totalFor = (e) => nonCachedInput(e) + (e.cacheReadTokens ?? 0) + (e.cacheWriteTokens ?? 0) + e.outputTokens;
|
|
107960
|
+
const costCell = (e) => typeof e.costUsd === "number" && e.costUsd > 0 ? formatCostUsd(e.costUsd) : "\u2014";
|
|
107881
107961
|
const rows = entries.map(
|
|
107882
|
-
(e) => `| ${e.agent} | ${fmt(e
|
|
107962
|
+
(e) => `| ${e.agent} | ${fmt(nonCachedInput(e))} | ${fmt(e.cacheReadTokens ?? 0)} | ${fmt(e.cacheWriteTokens ?? 0)} | ${fmt(e.outputTokens)} | ${fmt(totalFor(e))} | ${costCell(e)} |`
|
|
107883
107963
|
);
|
|
107884
107964
|
const totalsRows = [];
|
|
107885
107965
|
if (entries.length > 1) {
|
|
107886
|
-
const totalInput = entries.reduce((sum, e) => sum + e
|
|
107966
|
+
const totalInput = entries.reduce((sum, e) => sum + nonCachedInput(e), 0);
|
|
107887
107967
|
const totalOutput = entries.reduce((sum, e) => sum + e.outputTokens, 0);
|
|
107888
107968
|
const totalCacheRead = entries.reduce((sum, e) => sum + (e.cacheReadTokens ?? 0), 0);
|
|
107889
107969
|
const totalCacheWrite = entries.reduce((sum, e) => sum + (e.cacheWriteTokens ?? 0), 0);
|
|
107970
|
+
const grandTotal = totalInput + totalCacheRead + totalCacheWrite + totalOutput;
|
|
107971
|
+
const totalCostUsd = entries.reduce((sum, e) => sum + (e.costUsd ?? 0), 0);
|
|
107972
|
+
const totalCostCell = totalCostUsd > 0 ? `**${formatCostUsd(totalCostUsd)}**` : "\u2014";
|
|
107890
107973
|
totalsRows.push(
|
|
107891
|
-
`| **Total** | **${fmt(totalInput)}** | **${fmt(
|
|
107974
|
+
`| **Total** | **${fmt(totalInput)}** | **${fmt(totalCacheRead)}** | **${fmt(totalCacheWrite)}** | **${fmt(totalOutput)}** | **${fmt(grandTotal)}** | ${totalCostCell} |`
|
|
107892
107975
|
);
|
|
107893
107976
|
}
|
|
107894
107977
|
return [
|
|
@@ -107931,7 +108014,7 @@ var providers = {
|
|
|
107931
108014
|
models: {
|
|
107932
108015
|
"claude-opus": {
|
|
107933
108016
|
displayName: "Claude Opus",
|
|
107934
|
-
resolve: "anthropic/claude-opus-4-
|
|
108017
|
+
resolve: "anthropic/claude-opus-4-7",
|
|
107935
108018
|
openRouterResolve: "openrouter/anthropic/claude-opus-4.6",
|
|
107936
108019
|
preferred: true
|
|
107937
108020
|
},
|
|
@@ -107959,7 +108042,7 @@ var providers = {
|
|
|
107959
108042
|
},
|
|
107960
108043
|
"gpt-codex-mini": {
|
|
107961
108044
|
displayName: "GPT Codex Mini",
|
|
107962
|
-
resolve: "openai/codex-mini
|
|
108045
|
+
resolve: "openai/gpt-5.1-codex-mini",
|
|
107963
108046
|
openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini"
|
|
107964
108047
|
},
|
|
107965
108048
|
o3: {
|
|
@@ -108049,7 +108132,7 @@ var providers = {
|
|
|
108049
108132
|
},
|
|
108050
108133
|
"claude-opus": {
|
|
108051
108134
|
displayName: "Claude Opus",
|
|
108052
|
-
resolve: "opencode/claude-opus-4-
|
|
108135
|
+
resolve: "opencode/claude-opus-4-7",
|
|
108053
108136
|
openRouterResolve: "openrouter/anthropic/claude-opus-4.6"
|
|
108054
108137
|
},
|
|
108055
108138
|
"claude-sonnet": {
|
|
@@ -108327,22 +108410,35 @@ async function retry(fn2, options = {}) {
|
|
|
108327
108410
|
}
|
|
108328
108411
|
|
|
108329
108412
|
// utils/patchWorkflowRunFields.ts
|
|
108330
|
-
var
|
|
108413
|
+
var STRING_KEYS = [
|
|
108331
108414
|
"prNodeId",
|
|
108332
108415
|
"issueNodeId",
|
|
108333
108416
|
"reviewNodeId",
|
|
108334
108417
|
"planCommentNodeId",
|
|
108335
108418
|
"summaryCommentNodeId"
|
|
108336
108419
|
];
|
|
108420
|
+
var NUMBER_KEYS = [
|
|
108421
|
+
"inputTokens",
|
|
108422
|
+
"outputTokens",
|
|
108423
|
+
"cacheReadTokens",
|
|
108424
|
+
"cacheWriteTokens",
|
|
108425
|
+
"costUsd"
|
|
108426
|
+
];
|
|
108337
108427
|
async function patchWorkflowRunFields(ctx, fields) {
|
|
108338
108428
|
if (ctx.runId === void 0 || !ctx.apiToken) return;
|
|
108339
108429
|
const body = {};
|
|
108340
|
-
for (const key of
|
|
108430
|
+
for (const key of STRING_KEYS) {
|
|
108341
108431
|
const value2 = fields[key];
|
|
108342
108432
|
if (typeof value2 === "string" && value2.length > 0) {
|
|
108343
108433
|
body[key] = value2;
|
|
108344
108434
|
}
|
|
108345
108435
|
}
|
|
108436
|
+
for (const key of NUMBER_KEYS) {
|
|
108437
|
+
const value2 = fields[key];
|
|
108438
|
+
if (typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0) {
|
|
108439
|
+
body[key] = value2;
|
|
108440
|
+
}
|
|
108441
|
+
}
|
|
108346
108442
|
if (Object.keys(body).length === 0) return;
|
|
108347
108443
|
try {
|
|
108348
108444
|
await retry(
|
|
@@ -108369,6 +108465,38 @@ async function patchWorkflowRunFields(ctx, fields) {
|
|
|
108369
108465
|
log.warning(`patchWorkflowRunFields exhausted retries: ${error49}`);
|
|
108370
108466
|
}
|
|
108371
108467
|
}
|
|
108468
|
+
var INT4_MAX = 2147483647;
|
|
108469
|
+
function clampInt(value2, field) {
|
|
108470
|
+
if (value2 > INT4_MAX) {
|
|
108471
|
+
log.warning(
|
|
108472
|
+
`aggregateUsage: ${field}=${value2} exceeds INT4_MAX (${INT4_MAX}) \u2014 clamping so the rest of the usage row still persists.`
|
|
108473
|
+
);
|
|
108474
|
+
return INT4_MAX;
|
|
108475
|
+
}
|
|
108476
|
+
return value2;
|
|
108477
|
+
}
|
|
108478
|
+
function aggregateUsage(entries) {
|
|
108479
|
+
if (entries.length === 0) return {};
|
|
108480
|
+
const sum = entries.reduce(
|
|
108481
|
+
(acc, e) => ({
|
|
108482
|
+
inputTokens: acc.inputTokens + e.inputTokens,
|
|
108483
|
+
outputTokens: acc.outputTokens + e.outputTokens,
|
|
108484
|
+
cacheReadTokens: acc.cacheReadTokens + (e.cacheReadTokens ?? 0),
|
|
108485
|
+
cacheWriteTokens: acc.cacheWriteTokens + (e.cacheWriteTokens ?? 0),
|
|
108486
|
+
costUsd: acc.costUsd + (e.costUsd ?? 0)
|
|
108487
|
+
}),
|
|
108488
|
+
{ inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costUsd: 0 }
|
|
108489
|
+
);
|
|
108490
|
+
const out = {};
|
|
108491
|
+
if (sum.inputTokens > 0) out.inputTokens = clampInt(sum.inputTokens, "inputTokens");
|
|
108492
|
+
if (sum.outputTokens > 0) out.outputTokens = clampInt(sum.outputTokens, "outputTokens");
|
|
108493
|
+
if (sum.cacheReadTokens > 0)
|
|
108494
|
+
out.cacheReadTokens = clampInt(sum.cacheReadTokens, "cacheReadTokens");
|
|
108495
|
+
if (sum.cacheWriteTokens > 0)
|
|
108496
|
+
out.cacheWriteTokens = clampInt(sum.cacheWriteTokens, "cacheWriteTokens");
|
|
108497
|
+
if (sum.costUsd > 0) out.costUsd = sum.costUsd;
|
|
108498
|
+
return out;
|
|
108499
|
+
}
|
|
108372
108500
|
|
|
108373
108501
|
// node_modules/.pnpm/@toon-format+toon@1.4.0/node_modules/@toon-format/toon/dist/index.mjs
|
|
108374
108502
|
var LIST_ITEM_MARKER = "-";
|
|
@@ -108725,6 +108853,126 @@ function resolveOptions(options) {
|
|
|
108725
108853
|
};
|
|
108726
108854
|
}
|
|
108727
108855
|
|
|
108856
|
+
// mcp/geminiSanitizer.ts
|
|
108857
|
+
function parseStringEnumBranch(item) {
|
|
108858
|
+
if (!item || typeof item !== "object") return null;
|
|
108859
|
+
const record3 = item;
|
|
108860
|
+
if (Array.isArray(record3.enum)) {
|
|
108861
|
+
const strings = record3.enum.filter((v) => typeof v === "string");
|
|
108862
|
+
return strings.length === record3.enum.length && strings.length > 0 ? { values: strings } : null;
|
|
108863
|
+
}
|
|
108864
|
+
if (typeof record3.const === "string") {
|
|
108865
|
+
return { values: [record3.const] };
|
|
108866
|
+
}
|
|
108867
|
+
return null;
|
|
108868
|
+
}
|
|
108869
|
+
function collapseStringUnion(branches) {
|
|
108870
|
+
const values = [];
|
|
108871
|
+
for (const item of branches) {
|
|
108872
|
+
const parsed2 = parseStringEnumBranch(item);
|
|
108873
|
+
if (!parsed2) return null;
|
|
108874
|
+
values.push(...parsed2.values);
|
|
108875
|
+
}
|
|
108876
|
+
if (values.length === 0) return null;
|
|
108877
|
+
return { type: "string", enum: [...new Set(values)] };
|
|
108878
|
+
}
|
|
108879
|
+
function sanitizeForGemini(schema2) {
|
|
108880
|
+
if (!schema2 || typeof schema2 !== "object") return schema2;
|
|
108881
|
+
if (Array.isArray(schema2)) return schema2.map(sanitizeForGemini);
|
|
108882
|
+
const source = schema2;
|
|
108883
|
+
if (Array.isArray(source.enum) && typeof source.type !== "string") {
|
|
108884
|
+
const allStrings = source.enum.every((v) => typeof v === "string");
|
|
108885
|
+
if (allStrings) {
|
|
108886
|
+
const result = { type: "string", enum: source.enum };
|
|
108887
|
+
if (typeof source.description === "string") result.description = source.description;
|
|
108888
|
+
return result;
|
|
108889
|
+
}
|
|
108890
|
+
}
|
|
108891
|
+
for (const unionKey of ["anyOf", "oneOf"]) {
|
|
108892
|
+
const branches = source[unionKey];
|
|
108893
|
+
if (Array.isArray(branches) && branches.length > 0) {
|
|
108894
|
+
const collapsed = collapseStringUnion(branches);
|
|
108895
|
+
if (collapsed) {
|
|
108896
|
+
const result = { ...collapsed };
|
|
108897
|
+
if (typeof source.description === "string") result.description = source.description;
|
|
108898
|
+
return result;
|
|
108899
|
+
}
|
|
108900
|
+
}
|
|
108901
|
+
}
|
|
108902
|
+
if (Array.isArray(source.anyOf) || Array.isArray(source.oneOf)) {
|
|
108903
|
+
const result = {};
|
|
108904
|
+
if (Array.isArray(source.anyOf)) result.anyOf = source.anyOf.map(sanitizeForGemini);
|
|
108905
|
+
if (Array.isArray(source.oneOf)) result.oneOf = source.oneOf.map(sanitizeForGemini);
|
|
108906
|
+
return result;
|
|
108907
|
+
}
|
|
108908
|
+
const sanitized = {};
|
|
108909
|
+
for (const [key, value2] of Object.entries(source)) {
|
|
108910
|
+
if (key === "$schema") continue;
|
|
108911
|
+
if (key === "$defs") {
|
|
108912
|
+
sanitized.definitions = sanitizeForGemini(value2);
|
|
108913
|
+
continue;
|
|
108914
|
+
}
|
|
108915
|
+
sanitized[key] = sanitizeForGemini(value2);
|
|
108916
|
+
}
|
|
108917
|
+
return sanitized;
|
|
108918
|
+
}
|
|
108919
|
+
function wrapJsonSchemaProducer(producer) {
|
|
108920
|
+
return new Proxy(producer, {
|
|
108921
|
+
get(target, prop, receiver) {
|
|
108922
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
108923
|
+
if ((prop === "input" || prop === "output") && typeof value2 === "function") {
|
|
108924
|
+
const fn2 = value2;
|
|
108925
|
+
return (...args2) => sanitizeForGemini(fn2.apply(target, args2));
|
|
108926
|
+
}
|
|
108927
|
+
return value2;
|
|
108928
|
+
}
|
|
108929
|
+
});
|
|
108930
|
+
}
|
|
108931
|
+
function wrapStandard(standard) {
|
|
108932
|
+
return new Proxy(standard, {
|
|
108933
|
+
get(target, prop, receiver) {
|
|
108934
|
+
if (prop === "jsonSchema") {
|
|
108935
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
108936
|
+
if (value2 && typeof value2 === "object") {
|
|
108937
|
+
return wrapJsonSchemaProducer(value2);
|
|
108938
|
+
}
|
|
108939
|
+
return value2;
|
|
108940
|
+
}
|
|
108941
|
+
return Reflect.get(target, prop, receiver);
|
|
108942
|
+
}
|
|
108943
|
+
});
|
|
108944
|
+
}
|
|
108945
|
+
function wrapSchemaForGemini(schema2) {
|
|
108946
|
+
return new Proxy(schema2, {
|
|
108947
|
+
get(target, prop, receiver) {
|
|
108948
|
+
if (prop === "~standard") {
|
|
108949
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
108950
|
+
if (value2 && typeof value2 === "object") {
|
|
108951
|
+
return wrapStandard(value2);
|
|
108952
|
+
}
|
|
108953
|
+
return value2;
|
|
108954
|
+
}
|
|
108955
|
+
if (prop === "toJsonSchema") {
|
|
108956
|
+
const method = Reflect.get(target, prop, receiver);
|
|
108957
|
+
if (typeof method === "function") {
|
|
108958
|
+
return () => sanitizeForGemini(method.call(target));
|
|
108959
|
+
}
|
|
108960
|
+
return method;
|
|
108961
|
+
}
|
|
108962
|
+
return Reflect.get(target, prop, receiver);
|
|
108963
|
+
}
|
|
108964
|
+
});
|
|
108965
|
+
}
|
|
108966
|
+
function sanitizeToolForGemini(tool2) {
|
|
108967
|
+
if (!tool2.parameters) return tool2;
|
|
108968
|
+
return { ...tool2, parameters: wrapSchemaForGemini(tool2.parameters) };
|
|
108969
|
+
}
|
|
108970
|
+
function isGeminiRouted(ctx) {
|
|
108971
|
+
const effective = ctx.payload.proxyModel ?? ctx.resolvedModel ?? ctx.payload.model;
|
|
108972
|
+
if (!effective) return false;
|
|
108973
|
+
return effective.toLowerCase().includes("gemini");
|
|
108974
|
+
}
|
|
108975
|
+
|
|
108728
108976
|
// mcp/shared.ts
|
|
108729
108977
|
var tool = (toolDef) => toolDef;
|
|
108730
108978
|
var handleToolSuccess = (data) => {
|
|
@@ -108760,15 +109008,21 @@ var execute = (fn2, toolName) => {
|
|
|
108760
109008
|
};
|
|
108761
109009
|
return _fn;
|
|
108762
109010
|
};
|
|
108763
|
-
var addTools = (
|
|
109011
|
+
var addTools = (ctx, server, tools) => {
|
|
109012
|
+
const shouldSanitize = isGeminiRouted(ctx);
|
|
108764
109013
|
for (const tool2 of tools) {
|
|
108765
|
-
server.addTool(tool2);
|
|
109014
|
+
server.addTool(shouldSanitize ? sanitizeToolForGemini(tool2) : tool2);
|
|
108766
109015
|
}
|
|
108767
109016
|
return server;
|
|
108768
109017
|
};
|
|
108769
109018
|
|
|
108770
109019
|
// mcp/comment.ts
|
|
108771
109020
|
var LEAPING_INTO_ACTION_PREFIX = "Leaping into action";
|
|
109021
|
+
function isLeapingIntoActionCommentBody(body) {
|
|
109022
|
+
const content = stripExistingFooter(body).trimStart();
|
|
109023
|
+
const firstLine = content.split(/\r?\n/, 1)[0]?.trimEnd() ?? "";
|
|
109024
|
+
return new RegExp(`(^|\\s)${LEAPING_INTO_ACTION_PREFIX}(\\.\\.\\.)?$`).test(firstLine);
|
|
109025
|
+
}
|
|
108772
109026
|
function buildCommentFooter(ctx, customParts) {
|
|
108773
109027
|
const runId = ctx.runId;
|
|
108774
109028
|
return buildPullfrogFooter({
|
|
@@ -109012,8 +109266,9 @@ function ReportProgressTool(ctx) {
|
|
|
109012
109266
|
if (!params.target_plan_comment && ctx.toolState.todoTracker) {
|
|
109013
109267
|
ctx.toolState.todoTracker.cancel();
|
|
109014
109268
|
await ctx.toolState.todoTracker.settled();
|
|
109015
|
-
ctx.toolState.todoTracker.
|
|
109016
|
-
|
|
109269
|
+
const collapsible = ctx.toolState.todoTracker.renderCollapsible({
|
|
109270
|
+
completeInProgress: true
|
|
109271
|
+
});
|
|
109017
109272
|
if (collapsible) {
|
|
109018
109273
|
body = `${body}
|
|
109019
109274
|
|
|
@@ -109400,8 +109655,27 @@ import { performance as performance3 } from "node:perf_hooks";
|
|
|
109400
109655
|
|
|
109401
109656
|
// utils/activity.ts
|
|
109402
109657
|
import { performance as performance2 } from "node:perf_hooks";
|
|
109658
|
+
function isMonitorDebugEnabled() {
|
|
109659
|
+
return process.env.ACTIONS_STEP_DEBUG === "true" || process.env.RUNNER_DEBUG === "1" || process.env.LOG_LEVEL === "debug";
|
|
109660
|
+
}
|
|
109403
109661
|
var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
|
|
109404
109662
|
var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
|
|
109663
|
+
var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
|
|
109664
|
+
var ACTIVITY_NOISE_PATTERNS = [
|
|
109665
|
+
new RegExp(`${DEBUG_TS_PREFIX}\\[mcp-proxy\\]`),
|
|
109666
|
+
new RegExp(`${DEBUG_TS_PREFIX}\xBB provider error detected`),
|
|
109667
|
+
new RegExp(`${DEBUG_TS_PREFIX}\\[DEBUG\\]\\s+(?:spawn|process) activity `),
|
|
109668
|
+
/^::debug::(?:spawn|process) activity /
|
|
109669
|
+
];
|
|
109670
|
+
function isActivityNoise(chunk) {
|
|
109671
|
+
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
109672
|
+
if (!text.trim()) return true;
|
|
109673
|
+
return text.split("\n").every((line) => {
|
|
109674
|
+
const trimmed = line.trim();
|
|
109675
|
+
if (!trimmed) return true;
|
|
109676
|
+
return ACTIVITY_NOISE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
109677
|
+
});
|
|
109678
|
+
}
|
|
109405
109679
|
var _lastActivity = performance2.now();
|
|
109406
109680
|
function markActivity() {
|
|
109407
109681
|
_lastActivity = performance2.now();
|
|
@@ -109411,7 +109685,9 @@ function getIdleMs() {
|
|
|
109411
109685
|
}
|
|
109412
109686
|
function wrapWrite(original, onActivity) {
|
|
109413
109687
|
const wrapped = (chunk, encodingOrCb, cb) => {
|
|
109414
|
-
|
|
109688
|
+
if (!isActivityNoise(chunk)) {
|
|
109689
|
+
onActivity();
|
|
109690
|
+
}
|
|
109415
109691
|
if (typeof encodingOrCb === "function") {
|
|
109416
109692
|
return original(chunk, encodingOrCb);
|
|
109417
109693
|
}
|
|
@@ -109425,10 +109701,15 @@ function startProcessOutputMonitor(ctx) {
|
|
|
109425
109701
|
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
109426
109702
|
process.stdout.write = wrapWrite(originalStdoutWrite, markActivity);
|
|
109427
109703
|
process.stderr.write = wrapWrite(originalStderrWrite, markActivity);
|
|
109428
|
-
|
|
109704
|
+
const debugBypass = (msg) => {
|
|
109705
|
+
if (!isMonitorDebugEnabled()) return;
|
|
109706
|
+
originalStdoutWrite(`[${(/* @__PURE__ */ new Date()).toISOString()}] [DEBUG] ${msg}
|
|
109707
|
+
`);
|
|
109708
|
+
};
|
|
109709
|
+
debugBypass(`process activity monitor started: timeout=${ctx.timeoutMs}ms`);
|
|
109429
109710
|
const intervalId = setInterval(() => {
|
|
109430
109711
|
const idleMs = getIdleMs();
|
|
109431
|
-
|
|
109712
|
+
debugBypass(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
|
|
109432
109713
|
if (timedOut || idleMs <= ctx.timeoutMs) return;
|
|
109433
109714
|
timedOut = true;
|
|
109434
109715
|
ctx.onTimeout(idleMs);
|
|
@@ -109456,12 +109737,26 @@ function createProcessOutputActivityTimeout(ctx) {
|
|
|
109456
109737
|
if (monitor) {
|
|
109457
109738
|
monitor.stop();
|
|
109458
109739
|
}
|
|
109459
|
-
|
|
109740
|
+
const reject = rejectFn;
|
|
109741
|
+
rejectFn = null;
|
|
109742
|
+
reject(new Error(`activity timeout: no output for ${idleSec}s`));
|
|
109460
109743
|
}
|
|
109461
109744
|
});
|
|
109462
109745
|
return {
|
|
109463
109746
|
promise: promise2,
|
|
109464
|
-
stop
|
|
109747
|
+
// stop() also disarms forceReject so a late safety-net fire can't reject
|
|
109748
|
+
// the promise after the run has already succeeded.
|
|
109749
|
+
stop: () => {
|
|
109750
|
+
monitor?.stop();
|
|
109751
|
+
rejectFn = null;
|
|
109752
|
+
},
|
|
109753
|
+
forceReject: (reason) => {
|
|
109754
|
+
if (!rejectFn) return;
|
|
109755
|
+
monitor?.stop();
|
|
109756
|
+
const reject = rejectFn;
|
|
109757
|
+
rejectFn = null;
|
|
109758
|
+
reject(new Error(reason));
|
|
109759
|
+
}
|
|
109465
109760
|
};
|
|
109466
109761
|
}
|
|
109467
109762
|
|
|
@@ -109491,6 +109786,16 @@ function exitWithSignal(signal) {
|
|
|
109491
109786
|
}
|
|
109492
109787
|
|
|
109493
109788
|
// utils/subprocess.ts
|
|
109789
|
+
var SPAWN_TIMEOUT_CODE = "E_SPAWN_TIMEOUT";
|
|
109790
|
+
var SPAWN_ACTIVITY_TIMEOUT_CODE = "E_SPAWN_ACTIVITY_TIMEOUT";
|
|
109791
|
+
var SpawnTimeoutError = class extends Error {
|
|
109792
|
+
code;
|
|
109793
|
+
constructor(message, code) {
|
|
109794
|
+
super(message);
|
|
109795
|
+
this.name = "SpawnTimeoutError";
|
|
109796
|
+
this.code = code;
|
|
109797
|
+
}
|
|
109798
|
+
};
|
|
109494
109799
|
var activeChildren = /* @__PURE__ */ new Map();
|
|
109495
109800
|
var externalSignalHandler = null;
|
|
109496
109801
|
function trackChild(options) {
|
|
@@ -109536,7 +109841,7 @@ async function spawn(options) {
|
|
|
109536
109841
|
const startTime = performance3.now();
|
|
109537
109842
|
let stdoutBuffer = "";
|
|
109538
109843
|
let stderrBuffer = "";
|
|
109539
|
-
return new Promise((
|
|
109844
|
+
return new Promise((resolve3, reject) => {
|
|
109540
109845
|
const child = nodeSpawn(options.cmd, options.args, {
|
|
109541
109846
|
env: options.env || {
|
|
109542
109847
|
PATH: process.env.PATH || "",
|
|
@@ -109547,15 +109852,17 @@ async function spawn(options) {
|
|
|
109547
109852
|
});
|
|
109548
109853
|
trackChild({ child });
|
|
109549
109854
|
let timeoutId;
|
|
109855
|
+
let sigkillEscalatorId;
|
|
109550
109856
|
let activityCheckIntervalId;
|
|
109551
109857
|
let isTimedOut = false;
|
|
109552
109858
|
let isActivityTimedOut = false;
|
|
109553
109859
|
let lastActivityTime = performance3.now();
|
|
109860
|
+
let killedAtIdleMs;
|
|
109554
109861
|
if (options.timeout) {
|
|
109555
109862
|
timeoutId = setTimeout(() => {
|
|
109556
109863
|
isTimedOut = true;
|
|
109557
109864
|
child.kill("SIGTERM");
|
|
109558
|
-
setTimeout(() => {
|
|
109865
|
+
sigkillEscalatorId = setTimeout(() => {
|
|
109559
109866
|
if (!child.killed) {
|
|
109560
109867
|
child.kill("SIGKILL");
|
|
109561
109868
|
}
|
|
@@ -109573,12 +109880,20 @@ async function spawn(options) {
|
|
|
109573
109880
|
);
|
|
109574
109881
|
if (idleMs > activityTimeoutMs) {
|
|
109575
109882
|
isActivityTimedOut = true;
|
|
109883
|
+
killedAtIdleMs = idleMs;
|
|
109576
109884
|
const idleSec = Math.round(idleMs / 1e3);
|
|
109577
109885
|
log.info(
|
|
109578
109886
|
`no output for ${idleSec}s from pid=${child.pid} (${options.cmd}), killing process`
|
|
109579
109887
|
);
|
|
109580
109888
|
child.kill("SIGKILL");
|
|
109581
109889
|
clearInterval(activityCheckIntervalId);
|
|
109890
|
+
try {
|
|
109891
|
+
options.onActivityTimeout?.();
|
|
109892
|
+
} catch (err) {
|
|
109893
|
+
log.debug(
|
|
109894
|
+
`spawn onActivityTimeout handler threw: ${err instanceof Error ? err.message : String(err)}`
|
|
109895
|
+
);
|
|
109896
|
+
}
|
|
109582
109897
|
}
|
|
109583
109898
|
}, DEFAULT_ACTIVITY_CHECK_INTERVAL_MS);
|
|
109584
109899
|
}
|
|
@@ -109600,24 +109915,41 @@ async function spawn(options) {
|
|
|
109600
109915
|
options.onStderr?.(chunk);
|
|
109601
109916
|
});
|
|
109602
109917
|
}
|
|
109603
|
-
child.on("close", (exitCode) => {
|
|
109918
|
+
child.on("close", (exitCode, signal) => {
|
|
109604
109919
|
const durationMs = performance3.now() - startTime;
|
|
109605
109920
|
untrackChild(child);
|
|
109606
109921
|
if (timeoutId) clearTimeout(timeoutId);
|
|
109922
|
+
if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
|
|
109607
109923
|
if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
|
|
109608
109924
|
if (isTimedOut) {
|
|
109609
|
-
reject(
|
|
109925
|
+
reject(
|
|
109926
|
+
new SpawnTimeoutError(`process timed out after ${options.timeout}ms`, SPAWN_TIMEOUT_CODE)
|
|
109927
|
+
);
|
|
109610
109928
|
return;
|
|
109611
109929
|
}
|
|
109612
109930
|
if (isActivityTimedOut) {
|
|
109613
|
-
const
|
|
109614
|
-
|
|
109931
|
+
const idleMs = killedAtIdleMs ?? performance3.now() - lastActivityTime;
|
|
109932
|
+
const idleSec = Math.round(idleMs / 1e3);
|
|
109933
|
+
reject(
|
|
109934
|
+
new SpawnTimeoutError(
|
|
109935
|
+
`activity timeout: no output for ${idleSec}s`,
|
|
109936
|
+
SPAWN_ACTIVITY_TIMEOUT_CODE
|
|
109937
|
+
)
|
|
109938
|
+
);
|
|
109615
109939
|
return;
|
|
109616
109940
|
}
|
|
109617
|
-
|
|
109941
|
+
let resolvedExitCode = exitCode ?? 0;
|
|
109942
|
+
let resolvedStderr = stderrBuffer;
|
|
109943
|
+
if (exitCode === null && signal) {
|
|
109944
|
+
const killMsg = `[spawn] ${options.cmd}: killed by signal ${signal}`;
|
|
109945
|
+
resolvedStderr = resolvedStderr ? `${resolvedStderr}
|
|
109946
|
+
${killMsg}` : killMsg;
|
|
109947
|
+
resolvedExitCode = 1;
|
|
109948
|
+
}
|
|
109949
|
+
resolve3({
|
|
109618
109950
|
stdout: stdoutBuffer,
|
|
109619
|
-
stderr:
|
|
109620
|
-
exitCode:
|
|
109951
|
+
stderr: resolvedStderr,
|
|
109952
|
+
exitCode: resolvedExitCode,
|
|
109621
109953
|
durationMs
|
|
109622
109954
|
});
|
|
109623
109955
|
});
|
|
@@ -109625,9 +109957,13 @@ async function spawn(options) {
|
|
|
109625
109957
|
const durationMs = performance3.now() - startTime;
|
|
109626
109958
|
untrackChild(child);
|
|
109627
109959
|
if (timeoutId) clearTimeout(timeoutId);
|
|
109960
|
+
if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
|
|
109628
109961
|
if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
|
|
109629
|
-
|
|
109630
|
-
|
|
109962
|
+
const errMsg = `[spawn] ${options.cmd}: ${error49.message}`;
|
|
109963
|
+
console.error(errMsg);
|
|
109964
|
+
stderrBuffer = stderrBuffer ? `${stderrBuffer}
|
|
109965
|
+
${errMsg}` : errMsg;
|
|
109966
|
+
resolve3({
|
|
109631
109967
|
stdout: stdoutBuffer,
|
|
109632
109968
|
stderr: stderrBuffer,
|
|
109633
109969
|
exitCode: 1,
|
|
@@ -114394,7 +114730,7 @@ var Protocol = class {
|
|
|
114394
114730
|
return;
|
|
114395
114731
|
}
|
|
114396
114732
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
114397
|
-
await new Promise((
|
|
114733
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
|
|
114398
114734
|
options?.signal?.throwIfAborted();
|
|
114399
114735
|
}
|
|
114400
114736
|
} catch (error49) {
|
|
@@ -114411,7 +114747,7 @@ var Protocol = class {
|
|
|
114411
114747
|
*/
|
|
114412
114748
|
request(request2, resultSchema, options) {
|
|
114413
114749
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
114414
|
-
return new Promise((
|
|
114750
|
+
return new Promise((resolve3, reject) => {
|
|
114415
114751
|
const earlyReject = (error49) => {
|
|
114416
114752
|
reject(error49);
|
|
114417
114753
|
};
|
|
@@ -114489,7 +114825,7 @@ var Protocol = class {
|
|
|
114489
114825
|
if (!parseResult.success) {
|
|
114490
114826
|
reject(parseResult.error);
|
|
114491
114827
|
} else {
|
|
114492
|
-
|
|
114828
|
+
resolve3(parseResult.data);
|
|
114493
114829
|
}
|
|
114494
114830
|
} catch (error49) {
|
|
114495
114831
|
reject(error49);
|
|
@@ -114750,12 +115086,12 @@ var Protocol = class {
|
|
|
114750
115086
|
}
|
|
114751
115087
|
} catch {
|
|
114752
115088
|
}
|
|
114753
|
-
return new Promise((
|
|
115089
|
+
return new Promise((resolve3, reject) => {
|
|
114754
115090
|
if (signal.aborted) {
|
|
114755
115091
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
114756
115092
|
return;
|
|
114757
115093
|
}
|
|
114758
|
-
const timeoutId = setTimeout(
|
|
115094
|
+
const timeoutId = setTimeout(resolve3, interval);
|
|
114759
115095
|
signal.addEventListener("abort", () => {
|
|
114760
115096
|
clearTimeout(timeoutId);
|
|
114761
115097
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -115625,12 +115961,12 @@ var StdioServerTransport = class {
|
|
|
115625
115961
|
this.onclose?.();
|
|
115626
115962
|
}
|
|
115627
115963
|
send(message) {
|
|
115628
|
-
return new Promise((
|
|
115964
|
+
return new Promise((resolve3) => {
|
|
115629
115965
|
const json4 = serializeMessage(message);
|
|
115630
115966
|
if (this._stdout.write(json4)) {
|
|
115631
|
-
|
|
115967
|
+
resolve3();
|
|
115632
115968
|
} else {
|
|
115633
|
-
this._stdout.once("drain",
|
|
115969
|
+
this._stdout.once("drain", resolve3);
|
|
115634
115970
|
}
|
|
115635
115971
|
});
|
|
115636
115972
|
}
|
|
@@ -136658,12 +136994,12 @@ var require_schemes2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
136658
136994
|
var require_fast_uri2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
136659
136995
|
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils5();
|
|
136660
136996
|
const { SCHEMES, getSchemeHandler } = require_schemes2();
|
|
136661
|
-
function
|
|
136997
|
+
function normalize3(uri$2, options) {
|
|
136662
136998
|
if (typeof uri$2 === "string") uri$2 = serialize(parse5(uri$2, options), options);
|
|
136663
136999
|
else if (typeof uri$2 === "object") uri$2 = parse5(serialize(uri$2, options), options);
|
|
136664
137000
|
return uri$2;
|
|
136665
137001
|
}
|
|
136666
|
-
function
|
|
137002
|
+
function resolve3(baseURI, relativeURI, options) {
|
|
136667
137003
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
136668
137004
|
const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
136669
137005
|
schemelessOptions.skipEscape = true;
|
|
@@ -136836,8 +137172,8 @@ var require_fast_uri2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
136836
137172
|
}
|
|
136837
137173
|
const fastUri = {
|
|
136838
137174
|
SCHEMES,
|
|
136839
|
-
normalize:
|
|
136840
|
-
resolve:
|
|
137175
|
+
normalize: normalize3,
|
|
137176
|
+
resolve: resolve3,
|
|
136841
137177
|
resolveComponent,
|
|
136842
137178
|
equal: equal$1,
|
|
136843
137179
|
serialize,
|
|
@@ -140425,7 +140761,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
|
|
|
140425
140761
|
new Error(`Connection is in ${this.#connectionState} state`)
|
|
140426
140762
|
);
|
|
140427
140763
|
}
|
|
140428
|
-
return new Promise((
|
|
140764
|
+
return new Promise((resolve3, reject) => {
|
|
140429
140765
|
const timeout = setTimeout(() => {
|
|
140430
140766
|
reject(
|
|
140431
140767
|
new Error(
|
|
@@ -140435,7 +140771,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
|
|
|
140435
140771
|
}, 5e3);
|
|
140436
140772
|
this.once("ready", () => {
|
|
140437
140773
|
clearTimeout(timeout);
|
|
140438
|
-
|
|
140774
|
+
resolve3();
|
|
140439
140775
|
});
|
|
140440
140776
|
this.once("error", (event) => {
|
|
140441
140777
|
clearTimeout(timeout);
|
|
@@ -140882,7 +141218,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
|
|
|
140882
141218
|
}
|
|
140883
141219
|
});
|
|
140884
141220
|
if (this.#needsEventLoopFlush) {
|
|
140885
|
-
await new Promise((
|
|
141221
|
+
await new Promise((resolve3) => setImmediate(resolve3));
|
|
140886
141222
|
}
|
|
140887
141223
|
} catch (progressError) {
|
|
140888
141224
|
this.#logger.warn(
|
|
@@ -140940,7 +141276,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
|
|
|
140940
141276
|
}
|
|
140941
141277
|
});
|
|
140942
141278
|
if (this.#needsEventLoopFlush) {
|
|
140943
|
-
await new Promise((
|
|
141279
|
+
await new Promise((resolve3) => setImmediate(resolve3));
|
|
140944
141280
|
}
|
|
140945
141281
|
} catch (streamError) {
|
|
140946
141282
|
this.#logger.warn(
|
|
@@ -141945,7 +142281,7 @@ function formatMcpToolRef(agentId, toolName) {
|
|
|
141945
142281
|
switch (agentId) {
|
|
141946
142282
|
case "claude":
|
|
141947
142283
|
return `mcp__${pullfrogMcpName}__${toolName}`;
|
|
141948
|
-
case "
|
|
142284
|
+
case "opencode":
|
|
141949
142285
|
return `${pullfrogMcpName}_${toolName}`;
|
|
141950
142286
|
default:
|
|
141951
142287
|
return agentId;
|
|
@@ -141953,7 +142289,7 @@ function formatMcpToolRef(agentId, toolName) {
|
|
|
141953
142289
|
}
|
|
141954
142290
|
|
|
141955
142291
|
// utils/browser.ts
|
|
141956
|
-
import { execFileSync, spawnSync } from "node:child_process";
|
|
142292
|
+
import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
|
|
141957
142293
|
import { existsSync as existsSync4 } from "node:fs";
|
|
141958
142294
|
import { dirname } from "node:path";
|
|
141959
142295
|
|
|
@@ -141968,12 +142304,77 @@ var SENSITIVE_PATTERNS = [
|
|
|
141968
142304
|
function isSensitiveEnvName(key) {
|
|
141969
142305
|
return SENSITIVE_PATTERNS.some((p2) => p2.test(key));
|
|
141970
142306
|
}
|
|
142307
|
+
var SAFE_ENV_PREFIXES = ["GITHUB_", "RUNNER_", "JAVA_HOME_", "GOROOT_"];
|
|
142308
|
+
var SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
|
|
142309
|
+
// system
|
|
142310
|
+
"CI",
|
|
142311
|
+
"HOME",
|
|
142312
|
+
"LANG",
|
|
142313
|
+
"LOGNAME",
|
|
142314
|
+
"PATH",
|
|
142315
|
+
"SHELL",
|
|
142316
|
+
"SHLVL",
|
|
142317
|
+
"TERM",
|
|
142318
|
+
"TMPDIR",
|
|
142319
|
+
"TZ",
|
|
142320
|
+
"USER",
|
|
142321
|
+
"XDG_CONFIG_HOME",
|
|
142322
|
+
"XDG_RUNTIME_DIR",
|
|
142323
|
+
"DEBIAN_FRONTEND",
|
|
142324
|
+
// runner image toolchain
|
|
142325
|
+
"ACCEPT_EULA",
|
|
142326
|
+
"AGENT_TOOLSDIRECTORY",
|
|
142327
|
+
"ANDROID_HOME",
|
|
142328
|
+
"ANDROID_NDK",
|
|
142329
|
+
"ANDROID_NDK_HOME",
|
|
142330
|
+
"ANDROID_NDK_LATEST_HOME",
|
|
142331
|
+
"ANDROID_NDK_ROOT",
|
|
142332
|
+
"ANDROID_SDK_ROOT",
|
|
142333
|
+
"ANT_HOME",
|
|
142334
|
+
"AZURE_EXTENSION_DIR",
|
|
142335
|
+
"BOOTSTRAP_HASKELL_NONINTERACTIVE",
|
|
142336
|
+
"CHROME_BIN",
|
|
142337
|
+
"CHROMEWEBDRIVER",
|
|
142338
|
+
"CONDA",
|
|
142339
|
+
"DOTNET_MULTILEVEL_LOOKUP",
|
|
142340
|
+
"DOTNET_NOLOGO",
|
|
142341
|
+
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
|
|
142342
|
+
"EDGEWEBDRIVER",
|
|
142343
|
+
"GECKOWEBDRIVER",
|
|
142344
|
+
"GHCUP_INSTALL_BASE_PREFIX",
|
|
142345
|
+
"GRADLE_HOME",
|
|
142346
|
+
"JAVA_HOME",
|
|
142347
|
+
"HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS",
|
|
142348
|
+
"HOMEBREW_NO_AUTO_UPDATE",
|
|
142349
|
+
"ImageOS",
|
|
142350
|
+
"ImageVersion",
|
|
142351
|
+
"NVM_DIR",
|
|
142352
|
+
"PIPX_BIN_DIR",
|
|
142353
|
+
"PIPX_HOME",
|
|
142354
|
+
"PSModulePath",
|
|
142355
|
+
"SELENIUM_JAR_PATH",
|
|
142356
|
+
"SGX_AESM_ADDR",
|
|
142357
|
+
"SWIFT_PATH",
|
|
142358
|
+
"VCPKG_INSTALLATION_ROOT"
|
|
142359
|
+
]);
|
|
142360
|
+
var _userAllowlist = null;
|
|
142361
|
+
function setEnvAllowlist(raw2) {
|
|
142362
|
+
const names = raw2.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
142363
|
+
_userAllowlist = new Set(names);
|
|
142364
|
+
}
|
|
142365
|
+
function isSafeEnvVar(key) {
|
|
142366
|
+
if (SAFE_ENV_NAMES.has(key)) return true;
|
|
142367
|
+
return SAFE_ENV_PREFIXES.some((p2) => key.startsWith(p2));
|
|
142368
|
+
}
|
|
141971
142369
|
function filterEnv() {
|
|
141972
142370
|
const filtered = {};
|
|
141973
142371
|
for (const [key, value2] of Object.entries(process.env)) {
|
|
141974
142372
|
if (value2 === void 0) continue;
|
|
141975
|
-
|
|
141976
|
-
|
|
142373
|
+
const userAllowed = _userAllowlist?.has(key) ?? false;
|
|
142374
|
+
if (isSensitiveEnvName(key) && !userAllowed) continue;
|
|
142375
|
+
if (isSafeEnvVar(key) || userAllowed) {
|
|
142376
|
+
filtered[key] = value2;
|
|
142377
|
+
}
|
|
141977
142378
|
}
|
|
141978
142379
|
return filtered;
|
|
141979
142380
|
}
|
|
@@ -141993,7 +142394,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
141993
142394
|
// package.json
|
|
141994
142395
|
var package_default = {
|
|
141995
142396
|
name: "pullfrog",
|
|
141996
|
-
version: "0.0.
|
|
142397
|
+
version: "0.0.202",
|
|
141997
142398
|
type: "module",
|
|
141998
142399
|
bin: {
|
|
141999
142400
|
pullfrog: "dist/cli.mjs",
|
|
@@ -142005,6 +142406,7 @@ var package_default = {
|
|
|
142005
142406
|
],
|
|
142006
142407
|
scripts: {
|
|
142007
142408
|
test: "vitest",
|
|
142409
|
+
"test:catalog": "vitest run --config vitest.main.config.ts",
|
|
142008
142410
|
typecheck: "tsc --noEmit",
|
|
142009
142411
|
build: "node esbuild.config.js && tsc -p tsconfig.exports.json",
|
|
142010
142412
|
"check:entrypoints": "node scripts/check-entrypoint-imports.ts",
|
|
@@ -142017,7 +142419,7 @@ var package_default = {
|
|
|
142017
142419
|
},
|
|
142018
142420
|
devDependencies: {
|
|
142019
142421
|
"@actions/core": "^1.11.1",
|
|
142020
|
-
"@anthropic-ai/claude-code": "2.1.
|
|
142422
|
+
"@anthropic-ai/claude-code": "2.1.112",
|
|
142021
142423
|
"@ark/fs": "0.56.0",
|
|
142022
142424
|
"@ark/util": "0.56.0",
|
|
142023
142425
|
"@clack/prompts": "^1.2.0",
|
|
@@ -142141,7 +142543,7 @@ function ensureBrowserDaemon(toolState) {
|
|
|
142141
142543
|
log.info("agent-browser installed");
|
|
142142
142544
|
let binDir;
|
|
142143
142545
|
try {
|
|
142144
|
-
const binPath =
|
|
142546
|
+
const binPath = execFileSync2("which", ["agent-browser"], { encoding: "utf-8" }).trim();
|
|
142145
142547
|
binDir = dirname(binPath);
|
|
142146
142548
|
log.info(`agent-browser binary: ${binPath}`);
|
|
142147
142549
|
} catch {
|
|
@@ -142188,9 +142590,271 @@ function closeBrowserDaemon(toolState) {
|
|
|
142188
142590
|
}
|
|
142189
142591
|
|
|
142190
142592
|
// mcp/checkout.ts
|
|
142593
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
142191
142594
|
import { writeFileSync } from "node:fs";
|
|
142192
142595
|
import { join as join3 } from "node:path";
|
|
142193
142596
|
|
|
142597
|
+
// utils/diffCoverage.ts
|
|
142598
|
+
import { isAbsolute, normalize as normalize2, resolve } from "node:path";
|
|
142599
|
+
function countLines(params) {
|
|
142600
|
+
const content = params.content;
|
|
142601
|
+
if (content.length === 0) return 0;
|
|
142602
|
+
return content.split("\n").length;
|
|
142603
|
+
}
|
|
142604
|
+
function parseDiffTocEntries(params) {
|
|
142605
|
+
const lines = params.toc.split("\n");
|
|
142606
|
+
const entries = [];
|
|
142607
|
+
for (const line of lines) {
|
|
142608
|
+
const match3 = line.match(/^- (.+) (?:→|->) lines (\d+)-(\d+)(?: · diff-[0-9a-f]+)?$/);
|
|
142609
|
+
if (!match3) continue;
|
|
142610
|
+
const startLine = Number.parseInt(match3[2], 10);
|
|
142611
|
+
const endLine = Number.parseInt(match3[3], 10);
|
|
142612
|
+
if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) continue;
|
|
142613
|
+
entries.push({ filename: match3[1], startLine, endLine });
|
|
142614
|
+
}
|
|
142615
|
+
return entries;
|
|
142616
|
+
}
|
|
142617
|
+
function createDiffCoverageState(params) {
|
|
142618
|
+
return {
|
|
142619
|
+
diffPath: params.diffPath,
|
|
142620
|
+
totalLines: params.totalLines,
|
|
142621
|
+
tocEntries: parseDiffTocEntries({ toc: params.toc }),
|
|
142622
|
+
coveredRanges: [],
|
|
142623
|
+
coveragePreflightRan: false
|
|
142624
|
+
};
|
|
142625
|
+
}
|
|
142626
|
+
function recordDiffReadFromToolUse(params) {
|
|
142627
|
+
const state = params.state;
|
|
142628
|
+
if (!state) return false;
|
|
142629
|
+
if (!isReadTool(params.toolName)) return false;
|
|
142630
|
+
const readTarget = extractReadTarget({ input: params.input });
|
|
142631
|
+
if (!readTarget) return false;
|
|
142632
|
+
const normalizedReadPath = normalizePath({ path: readTarget.path, cwd: params.cwd });
|
|
142633
|
+
const normalizedDiffPath = normalize2(state.diffPath);
|
|
142634
|
+
if (normalizedReadPath !== normalizedDiffPath) return false;
|
|
142635
|
+
const range2 = resolveReadRange({
|
|
142636
|
+
totalLines: state.totalLines,
|
|
142637
|
+
offset: readTarget.offset,
|
|
142638
|
+
limit: readTarget.limit,
|
|
142639
|
+
startLine: readTarget.startLine,
|
|
142640
|
+
endLine: readTarget.endLine,
|
|
142641
|
+
offsetBase: resolveOffsetBase({ toolName: params.toolName })
|
|
142642
|
+
});
|
|
142643
|
+
if (!range2) return false;
|
|
142644
|
+
state.coveredRanges = mergeRanges({ ranges: state.coveredRanges, nextRange: range2 });
|
|
142645
|
+
return true;
|
|
142646
|
+
}
|
|
142647
|
+
function getDiffCoverageBreakdown(params) {
|
|
142648
|
+
const state = params.state;
|
|
142649
|
+
const coveredRanges = mergeRangesList({ ranges: state.coveredRanges });
|
|
142650
|
+
const unreadRanges = invertRanges({ totalLines: state.totalLines, coveredRanges });
|
|
142651
|
+
const coveredLines = countLinesInRanges({ ranges: coveredRanges });
|
|
142652
|
+
const unreadLines = Math.max(0, state.totalLines - coveredLines);
|
|
142653
|
+
const coveragePercent = state.totalLines ? Number((coveredLines / state.totalLines * 100).toFixed(2)) : 100;
|
|
142654
|
+
const files = [];
|
|
142655
|
+
for (const entry of state.tocEntries) {
|
|
142656
|
+
const fileRange = { startLine: entry.startLine, endLine: entry.endLine };
|
|
142657
|
+
const coveredInFile = intersectRangesWithRange({ ranges: coveredRanges, target: fileRange });
|
|
142658
|
+
const unreadInFile = intersectRangesWithRange({ ranges: unreadRanges, target: fileRange });
|
|
142659
|
+
const totalFileLines = Math.max(0, entry.endLine - entry.startLine + 1);
|
|
142660
|
+
const fileCoveredLines = countLinesInRanges({ ranges: coveredInFile });
|
|
142661
|
+
files.push({
|
|
142662
|
+
filename: entry.filename,
|
|
142663
|
+
startLine: entry.startLine,
|
|
142664
|
+
endLine: entry.endLine,
|
|
142665
|
+
totalLines: totalFileLines,
|
|
142666
|
+
coveredLines: fileCoveredLines,
|
|
142667
|
+
coveredRanges: coveredInFile,
|
|
142668
|
+
unreadRanges: unreadInFile
|
|
142669
|
+
});
|
|
142670
|
+
}
|
|
142671
|
+
return {
|
|
142672
|
+
totalLines: state.totalLines,
|
|
142673
|
+
coveredLines,
|
|
142674
|
+
unreadLines,
|
|
142675
|
+
coveragePercent,
|
|
142676
|
+
coveredRanges,
|
|
142677
|
+
unreadRanges,
|
|
142678
|
+
files
|
|
142679
|
+
};
|
|
142680
|
+
}
|
|
142681
|
+
function renderDiffCoverageBreakdown(params) {
|
|
142682
|
+
const breakdown = params.breakdown;
|
|
142683
|
+
const lines = [];
|
|
142684
|
+
lines.push(`diff coverage report for \`${params.diffPath}\``);
|
|
142685
|
+
lines.push(
|
|
142686
|
+
`overall: ${breakdown.coveredLines}/${breakdown.totalLines} lines read (${breakdown.coveragePercent}%), unread: ${breakdown.unreadLines}`
|
|
142687
|
+
);
|
|
142688
|
+
lines.push(`covered ranges: ${formatRanges({ ranges: breakdown.coveredRanges })}`);
|
|
142689
|
+
lines.push(`unread ranges: ${formatRanges({ ranges: breakdown.unreadRanges })}`);
|
|
142690
|
+
lines.push("");
|
|
142691
|
+
lines.push("per-file TOC coverage:");
|
|
142692
|
+
for (const file2 of breakdown.files) {
|
|
142693
|
+
const filePercent = file2.totalLines ? Number((file2.coveredLines / file2.totalLines * 100).toFixed(2)) : 100;
|
|
142694
|
+
lines.push(
|
|
142695
|
+
`- ${file2.filename} (toc lines ${file2.startLine}-${file2.endLine}): ${file2.coveredLines}/${file2.totalLines} lines read (${filePercent}%)`
|
|
142696
|
+
);
|
|
142697
|
+
lines.push(` read: ${formatRanges({ ranges: file2.coveredRanges })}`);
|
|
142698
|
+
lines.push(` unread: ${formatRanges({ ranges: file2.unreadRanges })}`);
|
|
142699
|
+
}
|
|
142700
|
+
return lines.join("\n");
|
|
142701
|
+
}
|
|
142702
|
+
function resolveOffsetBase(params) {
|
|
142703
|
+
const lower2 = params.toolName.toLowerCase();
|
|
142704
|
+
if (lower2 === "readfile" || lower2.endsWith(".readfile")) {
|
|
142705
|
+
return "one";
|
|
142706
|
+
}
|
|
142707
|
+
return "zero";
|
|
142708
|
+
}
|
|
142709
|
+
function isReadTool(toolName) {
|
|
142710
|
+
const lower2 = toolName.toLowerCase();
|
|
142711
|
+
if (lower2 === "read" || lower2 === "readfile") return true;
|
|
142712
|
+
if (lower2.endsWith(".read") || lower2.endsWith(".readfile")) return true;
|
|
142713
|
+
return false;
|
|
142714
|
+
}
|
|
142715
|
+
function extractReadTarget(params) {
|
|
142716
|
+
const inputRecord = asRecord(params.input);
|
|
142717
|
+
if (!inputRecord) return null;
|
|
142718
|
+
const direct = extractReadTargetFromRecord({ record: inputRecord });
|
|
142719
|
+
if (direct) return direct;
|
|
142720
|
+
const nestedCandidates = [inputRecord.args, inputRecord.params, inputRecord.input];
|
|
142721
|
+
for (const candidate of nestedCandidates) {
|
|
142722
|
+
const nestedRecord = asRecord(candidate);
|
|
142723
|
+
if (!nestedRecord) continue;
|
|
142724
|
+
const nested = extractReadTargetFromRecord({ record: nestedRecord });
|
|
142725
|
+
if (nested) return nested;
|
|
142726
|
+
}
|
|
142727
|
+
return null;
|
|
142728
|
+
}
|
|
142729
|
+
function extractReadTargetFromRecord(params) {
|
|
142730
|
+
const record3 = params.record;
|
|
142731
|
+
const pathValue = readString({ value: record3.path }) ?? readString({ value: record3.file_path }) ?? readString({ value: record3.filePath }) ?? readString({ value: record3.filepath }) ?? readString({ value: record3.file }) ?? readString({ value: record3.target_file });
|
|
142732
|
+
if (!pathValue) return null;
|
|
142733
|
+
const offset = readNumber({ value: record3.offset });
|
|
142734
|
+
const limit = readNumber({ value: record3.limit });
|
|
142735
|
+
const startLine = readNumber({ value: record3.start_line }) ?? readNumber({ value: record3.startLine }) ?? readNumber({ value: record3.line_start });
|
|
142736
|
+
const endLine = readNumber({ value: record3.end_line }) ?? readNumber({ value: record3.endLine }) ?? readNumber({ value: record3.line_end });
|
|
142737
|
+
return { path: pathValue, offset, limit, startLine, endLine };
|
|
142738
|
+
}
|
|
142739
|
+
function resolveReadRange(params) {
|
|
142740
|
+
const totalLines = params.totalLines;
|
|
142741
|
+
if (totalLines <= 0) return null;
|
|
142742
|
+
if (params.startLine !== void 0 || params.endLine !== void 0) {
|
|
142743
|
+
const rawStart = params.startLine ?? 1;
|
|
142744
|
+
const rawEnd = params.endLine ?? totalLines;
|
|
142745
|
+
const startLine2 = clampLine({ value: rawStart, totalLines });
|
|
142746
|
+
const endLine2 = clampLine({ value: rawEnd, totalLines });
|
|
142747
|
+
if (endLine2 < startLine2) return null;
|
|
142748
|
+
return { startLine: startLine2, endLine: endLine2 };
|
|
142749
|
+
}
|
|
142750
|
+
let startLine = 1;
|
|
142751
|
+
if (params.offset !== void 0) {
|
|
142752
|
+
if (params.offset >= 0) {
|
|
142753
|
+
const normalizedOffset = params.offsetBase === "zero" ? params.offset + 1 : params.offset === 0 ? 1 : params.offset;
|
|
142754
|
+
startLine = clampLine({ value: normalizedOffset, totalLines });
|
|
142755
|
+
} else {
|
|
142756
|
+
startLine = clampLine({ value: totalLines + params.offset + 1, totalLines });
|
|
142757
|
+
}
|
|
142758
|
+
}
|
|
142759
|
+
let endLine = totalLines;
|
|
142760
|
+
if (params.limit !== void 0) {
|
|
142761
|
+
if (params.limit <= 0) return null;
|
|
142762
|
+
endLine = clampLine({ value: startLine + params.limit - 1, totalLines });
|
|
142763
|
+
}
|
|
142764
|
+
if (endLine < startLine) return null;
|
|
142765
|
+
return { startLine, endLine };
|
|
142766
|
+
}
|
|
142767
|
+
function normalizePath(params) {
|
|
142768
|
+
if (isAbsolute(params.path)) return normalize2(params.path);
|
|
142769
|
+
return normalize2(resolve(params.cwd, params.path));
|
|
142770
|
+
}
|
|
142771
|
+
function mergeRanges(params) {
|
|
142772
|
+
return mergeRangesList({ ranges: [...params.ranges, params.nextRange] });
|
|
142773
|
+
}
|
|
142774
|
+
function mergeRangesList(params) {
|
|
142775
|
+
if (params.ranges.length === 0) return [];
|
|
142776
|
+
const sorted = [...params.ranges].sort((a, b) => a.startLine - b.startLine);
|
|
142777
|
+
const merged = [];
|
|
142778
|
+
for (const range2 of sorted) {
|
|
142779
|
+
const last = merged[merged.length - 1];
|
|
142780
|
+
if (!last) {
|
|
142781
|
+
merged.push({ startLine: range2.startLine, endLine: range2.endLine });
|
|
142782
|
+
continue;
|
|
142783
|
+
}
|
|
142784
|
+
if (range2.startLine <= last.endLine + 1) {
|
|
142785
|
+
if (range2.endLine > last.endLine) {
|
|
142786
|
+
last.endLine = range2.endLine;
|
|
142787
|
+
}
|
|
142788
|
+
continue;
|
|
142789
|
+
}
|
|
142790
|
+
merged.push({ startLine: range2.startLine, endLine: range2.endLine });
|
|
142791
|
+
}
|
|
142792
|
+
return merged;
|
|
142793
|
+
}
|
|
142794
|
+
function invertRanges(params) {
|
|
142795
|
+
if (params.totalLines <= 0) return [];
|
|
142796
|
+
if (params.coveredRanges.length === 0) {
|
|
142797
|
+
return [{ startLine: 1, endLine: params.totalLines }];
|
|
142798
|
+
}
|
|
142799
|
+
const unread = [];
|
|
142800
|
+
let cursor = 1;
|
|
142801
|
+
for (const range2 of params.coveredRanges) {
|
|
142802
|
+
if (cursor < range2.startLine) {
|
|
142803
|
+
unread.push({ startLine: cursor, endLine: range2.startLine - 1 });
|
|
142804
|
+
}
|
|
142805
|
+
cursor = Math.max(cursor, range2.endLine + 1);
|
|
142806
|
+
}
|
|
142807
|
+
if (cursor <= params.totalLines) {
|
|
142808
|
+
unread.push({ startLine: cursor, endLine: params.totalLines });
|
|
142809
|
+
}
|
|
142810
|
+
return unread;
|
|
142811
|
+
}
|
|
142812
|
+
function intersectRangesWithRange(params) {
|
|
142813
|
+
const intersections = [];
|
|
142814
|
+
for (const range2 of params.ranges) {
|
|
142815
|
+
if (range2.endLine < params.target.startLine) continue;
|
|
142816
|
+
if (range2.startLine > params.target.endLine) continue;
|
|
142817
|
+
const startLine = Math.max(range2.startLine, params.target.startLine);
|
|
142818
|
+
const endLine = Math.min(range2.endLine, params.target.endLine);
|
|
142819
|
+
if (endLine >= startLine) {
|
|
142820
|
+
intersections.push({ startLine, endLine });
|
|
142821
|
+
}
|
|
142822
|
+
}
|
|
142823
|
+
return intersections;
|
|
142824
|
+
}
|
|
142825
|
+
function countLinesInRanges(params) {
|
|
142826
|
+
let total = 0;
|
|
142827
|
+
for (const range2 of params.ranges) {
|
|
142828
|
+
total += range2.endLine - range2.startLine + 1;
|
|
142829
|
+
}
|
|
142830
|
+
return total;
|
|
142831
|
+
}
|
|
142832
|
+
function formatRanges(params) {
|
|
142833
|
+
if (params.ranges.length === 0) return "none";
|
|
142834
|
+
return params.ranges.map((range2) => `${range2.startLine}-${range2.endLine}`).join(", ");
|
|
142835
|
+
}
|
|
142836
|
+
function clampLine(params) {
|
|
142837
|
+
if (params.value < 1) return 1;
|
|
142838
|
+
if (params.value > params.totalLines) return params.totalLines;
|
|
142839
|
+
return params.value;
|
|
142840
|
+
}
|
|
142841
|
+
function asRecord(value2) {
|
|
142842
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
|
|
142843
|
+
return Object.fromEntries(Object.entries(value2));
|
|
142844
|
+
}
|
|
142845
|
+
function readString(params) {
|
|
142846
|
+
if (typeof params.value === "string") return params.value;
|
|
142847
|
+
return void 0;
|
|
142848
|
+
}
|
|
142849
|
+
function readNumber(params) {
|
|
142850
|
+
if (typeof params.value === "number" && Number.isFinite(params.value)) return params.value;
|
|
142851
|
+
if (typeof params.value === "string") {
|
|
142852
|
+
const parsed2 = Number.parseInt(params.value, 10);
|
|
142853
|
+
if (Number.isFinite(parsed2)) return parsed2;
|
|
142854
|
+
}
|
|
142855
|
+
return void 0;
|
|
142856
|
+
}
|
|
142857
|
+
|
|
142194
142858
|
// utils/gitAuth.ts
|
|
142195
142859
|
import { execSync } from "node:child_process";
|
|
142196
142860
|
import { createHash } from "node:crypto";
|
|
@@ -142204,7 +142868,7 @@ function resolveGit() {
|
|
|
142204
142868
|
const resolvedPath = realpathSync(whichPath);
|
|
142205
142869
|
const sha256 = hashFile(resolvedPath);
|
|
142206
142870
|
gitBinary = { path: resolvedPath, sha256 };
|
|
142207
|
-
log.
|
|
142871
|
+
log.debug(`\xBB git binary: ${resolvedPath} (sha256: ${sha256.slice(0, 12)}...)`);
|
|
142208
142872
|
}
|
|
142209
142873
|
function verifyGitBinary() {
|
|
142210
142874
|
if (!gitBinary) {
|
|
@@ -142283,29 +142947,43 @@ async function $git(subcommand, args2, options) {
|
|
|
142283
142947
|
}
|
|
142284
142948
|
|
|
142285
142949
|
// lifecycle.ts
|
|
142286
|
-
var LIFECYCLE_HOOK_TIMEOUT_MS =
|
|
142950
|
+
var LIFECYCLE_HOOK_TIMEOUT_MS = 6e5;
|
|
142287
142951
|
|
|
142288
142952
|
// utils/lifecycle.ts
|
|
142289
142953
|
async function executeLifecycleHook(params) {
|
|
142290
|
-
if (!params.script) return;
|
|
142954
|
+
if (!params.script) return {};
|
|
142291
142955
|
log.info(`\xBB executing ${params.event} lifecycle hook...`);
|
|
142292
|
-
|
|
142293
|
-
|
|
142294
|
-
|
|
142295
|
-
|
|
142296
|
-
|
|
142297
|
-
|
|
142298
|
-
|
|
142299
|
-
|
|
142300
|
-
|
|
142301
|
-
|
|
142302
|
-
|
|
142303
|
-
|
|
142304
|
-
|
|
142305
|
-
${output}
|
|
142306
|
-
|
|
142956
|
+
try {
|
|
142957
|
+
const result = await spawn({
|
|
142958
|
+
cmd: "bash",
|
|
142959
|
+
args: ["-c", params.script],
|
|
142960
|
+
env: process.env,
|
|
142961
|
+
timeout: LIFECYCLE_HOOK_TIMEOUT_MS,
|
|
142962
|
+
activityTimeout: 0,
|
|
142963
|
+
onStdout: (chunk) => process.stdout.write(chunk),
|
|
142964
|
+
onStderr: (chunk) => process.stderr.write(chunk)
|
|
142965
|
+
});
|
|
142966
|
+
if (result.exitCode !== 0) {
|
|
142967
|
+
const output = (result.stderr || result.stdout).trim();
|
|
142968
|
+
return {
|
|
142969
|
+
warning: `lifecycle hook '${params.event}' failed with exit code ${result.exitCode}. output: ${output || "(empty)"}. retry the operation if the failure looks flaky (network blips, transient rate limits). do NOT retry if the script is broken (missing commands, syntax errors) or the error is persistent.`
|
|
142970
|
+
};
|
|
142971
|
+
}
|
|
142972
|
+
log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
|
|
142973
|
+
return {};
|
|
142974
|
+
} catch (err) {
|
|
142975
|
+
const isTimeout = err instanceof SpawnTimeoutError && (err.code === SPAWN_TIMEOUT_CODE || err.code === SPAWN_ACTIVITY_TIMEOUT_CODE);
|
|
142976
|
+
if (isTimeout) {
|
|
142977
|
+
const minutes = Math.round(LIFECYCLE_HOOK_TIMEOUT_MS / 6e4);
|
|
142978
|
+
return {
|
|
142979
|
+
warning: `lifecycle hook '${params.event}' timed out after ${minutes}min. do NOT retry \u2014 the script is likely hung or doing too much work. ask the repo owner to simplify the hook (e.g. move long-running work out of the hook, add caching, or split it).`
|
|
142980
|
+
};
|
|
142981
|
+
}
|
|
142982
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
142983
|
+
return {
|
|
142984
|
+
warning: `lifecycle hook '${params.event}' failed to spawn: ${msg}. this is likely a transient failure \u2014 retry the operation.`
|
|
142985
|
+
};
|
|
142307
142986
|
}
|
|
142308
|
-
log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
|
|
142309
142987
|
}
|
|
142310
142988
|
|
|
142311
142989
|
// utils/shell.ts
|
|
@@ -142455,6 +143133,787 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
|
|
|
142455
143133
|
return hasChanges ? out : null;
|
|
142456
143134
|
}
|
|
142457
143135
|
|
|
143136
|
+
// mcp/git.ts
|
|
143137
|
+
function getPushDestination(branch, storedDest) {
|
|
143138
|
+
if (storedDest && storedDest.localBranch === branch) {
|
|
143139
|
+
log.debug(`using stored push destination: ${storedDest.remoteName}/${storedDest.remoteBranch}`);
|
|
143140
|
+
const url4 = $("git", ["remote", "get-url", "--push", storedDest.remoteName], {
|
|
143141
|
+
log: false
|
|
143142
|
+
}).trim();
|
|
143143
|
+
return { remoteName: storedDest.remoteName, remoteBranch: storedDest.remoteBranch, url: url4 };
|
|
143144
|
+
}
|
|
143145
|
+
try {
|
|
143146
|
+
const pushRemote = $("git", ["config", `branch.${branch}.pushRemote`], { log: false }).trim();
|
|
143147
|
+
const merge4 = $("git", ["config", `branch.${branch}.merge`], { log: false }).trim();
|
|
143148
|
+
const remoteBranch = merge4.replace(/^refs\/heads\//, "");
|
|
143149
|
+
const url4 = $("git", ["remote", "get-url", "--push", pushRemote], { log: false }).trim();
|
|
143150
|
+
return { remoteName: pushRemote, remoteBranch, url: url4 };
|
|
143151
|
+
} catch {
|
|
143152
|
+
log.debug(`no push config for ${branch}, falling back to origin/${branch}`);
|
|
143153
|
+
const url4 = $("git", ["remote", "get-url", "--push", "origin"], { log: false }).trim();
|
|
143154
|
+
return { remoteName: "origin", remoteBranch: branch, url: url4 };
|
|
143155
|
+
}
|
|
143156
|
+
}
|
|
143157
|
+
function normalizeUrl(url4) {
|
|
143158
|
+
return url4.replace(/\.git$/, "").toLowerCase();
|
|
143159
|
+
}
|
|
143160
|
+
function rejectIfLeadingDash(value2, kind) {
|
|
143161
|
+
if (value2.startsWith("-")) {
|
|
143162
|
+
throw new Error(`Blocked: ${kind} '${value2}' starts with '-' \u2014 git could parse it as a flag.`);
|
|
143163
|
+
}
|
|
143164
|
+
}
|
|
143165
|
+
var SYMBOLIC_REFS = /* @__PURE__ */ new Set(["HEAD", "FETCH_HEAD", "ORIG_HEAD", "MERGE_HEAD"]);
|
|
143166
|
+
function rejectSpecialRef(value2, kind) {
|
|
143167
|
+
rejectIfLeadingDash(value2, kind);
|
|
143168
|
+
if (value2.startsWith("refs/")) {
|
|
143169
|
+
throw new Error(
|
|
143170
|
+
`Blocked: ${kind} '${value2}' is a fully-qualified ref path. Use a bare branch name (e.g. 'feature/foo' or 'main'), not a 'refs/heads/...' form.`
|
|
143171
|
+
);
|
|
143172
|
+
}
|
|
143173
|
+
if (SYMBOLIC_REFS.has(value2)) {
|
|
143174
|
+
throw new Error(
|
|
143175
|
+
`Blocked: ${kind} '${value2}' is a git symbolic ref, not a branch name. Pass the resolved branch name (e.g. 'main'), or omit branchName to push the current branch.`
|
|
143176
|
+
);
|
|
143177
|
+
}
|
|
143178
|
+
const BAD = /[:+^~?*[\\\s]/;
|
|
143179
|
+
const badMatch = value2.match(BAD);
|
|
143180
|
+
if (badMatch) {
|
|
143181
|
+
throw new Error(
|
|
143182
|
+
`Blocked: ${kind} '${value2}' contains '${badMatch[0]}', which git interprets as refspec/revision syntax, not as part of a branch name.`
|
|
143183
|
+
);
|
|
143184
|
+
}
|
|
143185
|
+
}
|
|
143186
|
+
function validateTagName(tag) {
|
|
143187
|
+
rejectIfLeadingDash(tag, "tag");
|
|
143188
|
+
if (!/^[A-Za-z0-9._/-]+$/.test(tag)) {
|
|
143189
|
+
throw new Error(
|
|
143190
|
+
`Blocked: tag '${tag}' contains characters that could be parsed as a refspec or flag. Tags must match [A-Za-z0-9._/-]+.`
|
|
143191
|
+
);
|
|
143192
|
+
}
|
|
143193
|
+
}
|
|
143194
|
+
function validatePushDestination(ctx, branch) {
|
|
143195
|
+
const pushUrl = ctx.toolState.pushUrl;
|
|
143196
|
+
if (!pushUrl) throw new Error("pushUrl not set - setupGit must run before push_branch");
|
|
143197
|
+
const dest = getPushDestination(branch, ctx.toolState.pushDest);
|
|
143198
|
+
if (normalizeUrl(dest.url) !== normalizeUrl(pushUrl)) {
|
|
143199
|
+
throw new Error(
|
|
143200
|
+
`Push blocked: destination does not match expected repository.
|
|
143201
|
+
Expected: ${pushUrl}
|
|
143202
|
+
Actual: ${dest.url}
|
|
143203
|
+
Git configuration may have been tampered with.`
|
|
143204
|
+
);
|
|
143205
|
+
}
|
|
143206
|
+
return dest;
|
|
143207
|
+
}
|
|
143208
|
+
var PushBranch = type({
|
|
143209
|
+
branchName: type.string.describe("The branch name to push (defaults to current branch)").optional(),
|
|
143210
|
+
force: type.boolean.describe("Force push (use with caution)").default(false)
|
|
143211
|
+
});
|
|
143212
|
+
function PushBranchTool(ctx) {
|
|
143213
|
+
const defaultBranch = ctx.repo.data.default_branch || "main";
|
|
143214
|
+
const pushPermission = ctx.payload.push;
|
|
143215
|
+
return tool({
|
|
143216
|
+
name: "push_branch",
|
|
143217
|
+
description: "Push the current branch to the remote repository. Omit branchName to push the current branch (recommended). If specifying branchName, use the LOCAL branch name (e.g., 'pr-1'), not the remote branch name. The correct remote and remote branch are determined automatically from branch config set by checkout_pr. Requires a clean working tree. Runs the repository prepush hook (if configured) before the network push \u2014 hook failure means tests/lint or similar in that script failed, not necessarily a Pullfrog timeout. Never force push unless explicitly requested. Pushes to the default branch are blocked in restricted mode.",
|
|
143218
|
+
parameters: PushBranch,
|
|
143219
|
+
execute: execute(async ({ branchName, force }) => {
|
|
143220
|
+
if (pushPermission === "disabled") {
|
|
143221
|
+
throw new Error("Push is disabled. This repository is configured for read-only access.");
|
|
143222
|
+
}
|
|
143223
|
+
const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
|
143224
|
+
rejectSpecialRef(branch, "branch");
|
|
143225
|
+
const status = $("git", ["status", "--porcelain"], { log: false });
|
|
143226
|
+
if (status) {
|
|
143227
|
+
throw new Error(
|
|
143228
|
+
`push blocked: working tree is not clean (tracked changes and/or untracked files). commit, discard, or remove stray artifacts before pushing.
|
|
143229
|
+
|
|
143230
|
+
git status:
|
|
143231
|
+
${status}`
|
|
143232
|
+
);
|
|
143233
|
+
}
|
|
143234
|
+
const pushDest = validatePushDestination(ctx, branch);
|
|
143235
|
+
if (pushPermission === "restricted" && pushDest.remoteBranch === defaultBranch) {
|
|
143236
|
+
throw new Error(
|
|
143237
|
+
`Push blocked: cannot push directly to default branch '${pushDest.remoteBranch}'. Create a feature branch and open a PR instead.`
|
|
143238
|
+
);
|
|
143239
|
+
}
|
|
143240
|
+
const refspec = branch === pushDest.remoteBranch ? branch : `${branch}:${pushDest.remoteBranch}`;
|
|
143241
|
+
const pushArgs = force ? ["--force", "-u", pushDest.remoteName, refspec] : ["-u", pushDest.remoteName, refspec];
|
|
143242
|
+
const prepushHook = await executeLifecycleHook({
|
|
143243
|
+
event: "prepush",
|
|
143244
|
+
script: ctx.prepushScript
|
|
143245
|
+
});
|
|
143246
|
+
if (prepushHook.warning) {
|
|
143247
|
+
throw new Error(prepushHook.warning);
|
|
143248
|
+
}
|
|
143249
|
+
const postHookStatus = $("git", ["status", "--porcelain"], { log: false });
|
|
143250
|
+
if (postHookStatus) {
|
|
143251
|
+
throw new Error(
|
|
143252
|
+
`push blocked: the prepush hook modified the working tree. those changes are not included in the push. commit or discard them (or change the hook to not mutate tracked files) before retrying.
|
|
143253
|
+
|
|
143254
|
+
git status:
|
|
143255
|
+
${postHookStatus}`
|
|
143256
|
+
);
|
|
143257
|
+
}
|
|
143258
|
+
log.debug(`pushing ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`);
|
|
143259
|
+
if (force) {
|
|
143260
|
+
log.warning(`force pushing - this will overwrite remote history`);
|
|
143261
|
+
}
|
|
143262
|
+
try {
|
|
143263
|
+
await $git("push", pushArgs, {
|
|
143264
|
+
token: ctx.gitToken
|
|
143265
|
+
});
|
|
143266
|
+
} catch (err) {
|
|
143267
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
143268
|
+
if (msg.includes("fetch first") || msg.includes("non-fast-forward")) {
|
|
143269
|
+
const integrateStep = ctx.payload.shell === "disabled" ? `2. use the git tool to merge the remote branch into yours: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] })` : `2. use the git tool to rebase or merge your changes on top: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] }) (or 'rebase')`;
|
|
143270
|
+
throw new Error(
|
|
143271
|
+
`push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally.
|
|
143272
|
+
|
|
143273
|
+
to resolve this:
|
|
143274
|
+
1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
|
|
143275
|
+
${integrateStep}
|
|
143276
|
+
3. resolve any merge conflicts if needed
|
|
143277
|
+
4. retry push_branch`
|
|
143278
|
+
);
|
|
143279
|
+
}
|
|
143280
|
+
throw err;
|
|
143281
|
+
}
|
|
143282
|
+
return {
|
|
143283
|
+
success: true,
|
|
143284
|
+
branch,
|
|
143285
|
+
remoteBranch: pushDest.remoteBranch,
|
|
143286
|
+
remote: pushDest.remoteName,
|
|
143287
|
+
force,
|
|
143288
|
+
message: `successfully pushed ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`
|
|
143289
|
+
};
|
|
143290
|
+
})
|
|
143291
|
+
});
|
|
143292
|
+
}
|
|
143293
|
+
var AUTH_REQUIRED_REDIRECT = {
|
|
143294
|
+
push: "use the push_branch tool instead \u2014 it handles authentication and permission checks.",
|
|
143295
|
+
fetch: "use the git_fetch tool instead \u2014 it handles authentication.",
|
|
143296
|
+
pull: "use git_fetch to fetch the remote ref, then call this git tool with command 'merge' locally.",
|
|
143297
|
+
clone: "the repository is already cloned. use checkout_pr for PR branches."
|
|
143298
|
+
};
|
|
143299
|
+
var NOSHELL_BLOCKED_SUBCOMMANDS = {
|
|
143300
|
+
config: "Blocked: git config can set up filter drivers or hooks that execute arbitrary code.",
|
|
143301
|
+
submodule: "Blocked: git submodule can reference malicious repositories and execute code on update.",
|
|
143302
|
+
"update-index": "Blocked: git update-index can modify index entries in ways that bypass file protections.",
|
|
143303
|
+
"filter-branch": "Blocked: git filter-branch executes arbitrary code on repository history.",
|
|
143304
|
+
replace: "Blocked: git replace can redirect object lookups.",
|
|
143305
|
+
// subcommands that accept --exec or similar flags for arbitrary code execution
|
|
143306
|
+
rebase: "Blocked: git rebase --exec can execute arbitrary shell commands. Use 'merge' instead to integrate remote changes.",
|
|
143307
|
+
bisect: "Blocked: git bisect run can execute arbitrary shell commands. Bisect by hand (bisect start/good/bad/reset) is not available through this tool either \u2014 ask the user to run the bisect if needed.",
|
|
143308
|
+
// difftool/mergetool exist to shell out to external diff/merge programs.
|
|
143309
|
+
// both accept `--extcmd` / `-x` (difftool) or configured tool commands
|
|
143310
|
+
// (mergetool) that run arbitrary code. NOSHELL_BLOCKED_ARGS catches the
|
|
143311
|
+
// long `--extcmd` form, but not the `-x` short form — and globally blocking
|
|
143312
|
+
// `-x` would false-positive on `git cherry-pick -x`. block the subcommands
|
|
143313
|
+
// wholesale instead; neither has a meaningful use in an automated agent
|
|
143314
|
+
// workflow (agents use `git diff` / `git show` for diffs and resolve
|
|
143315
|
+
// conflicts via file edits, not a TUI merge tool).
|
|
143316
|
+
difftool: "Blocked: git difftool runs an external diff program via --extcmd/-x or configured tool and can execute arbitrary shell commands. Use 'diff' (or 'show' for single commits) to inspect changes \u2014 those output directly and don't invoke an external tool.",
|
|
143317
|
+
mergetool: "Blocked: git mergetool runs an external merge program configured via mergetool.<name>.cmd and can execute arbitrary shell commands. Resolve conflicts by editing the files directly (conflict markers are written into the working tree) and then commit."
|
|
143318
|
+
};
|
|
143319
|
+
var NOSHELL_BLOCKED_ARGS = ["--exec", "--extcmd", "--upload-pack", "--receive-pack"];
|
|
143320
|
+
var COLLAPSE_THRESHOLD = 200;
|
|
143321
|
+
var subcommandPattern = regex("^[a-z][a-z0-9-]*$");
|
|
143322
|
+
var Git = type({
|
|
143323
|
+
command: type(subcommandPattern).describe("Git command (e.g., 'status', 'log', 'diff')"),
|
|
143324
|
+
args: type.string.array().describe("Additional arguments for the git command").optional()
|
|
143325
|
+
});
|
|
143326
|
+
function GitTool(ctx) {
|
|
143327
|
+
return tool({
|
|
143328
|
+
name: "git",
|
|
143329
|
+
description: "Run git commands. For push/fetch, use the dedicated MCP tools (push_branch, git_fetch). git pull is not available \u2014 use git_fetch then this tool with command 'merge'.",
|
|
143330
|
+
parameters: Git,
|
|
143331
|
+
execute: execute(async (params) => {
|
|
143332
|
+
const command = params.command;
|
|
143333
|
+
const args2 = params.args ?? [];
|
|
143334
|
+
const redirect = AUTH_REQUIRED_REDIRECT[command];
|
|
143335
|
+
if (redirect) {
|
|
143336
|
+
throw new Error(`git ${command} is not available through this tool \u2014 ${redirect}`);
|
|
143337
|
+
}
|
|
143338
|
+
if (ctx.payload.shell === "disabled") {
|
|
143339
|
+
const blocked = NOSHELL_BLOCKED_SUBCOMMANDS[command];
|
|
143340
|
+
if (blocked) {
|
|
143341
|
+
throw new Error(blocked);
|
|
143342
|
+
}
|
|
143343
|
+
for (const arg4 of args2) {
|
|
143344
|
+
const isBlocked = NOSHELL_BLOCKED_ARGS.some(
|
|
143345
|
+
(flag) => arg4 === flag || arg4.startsWith(flag + "=")
|
|
143346
|
+
);
|
|
143347
|
+
if (isBlocked) {
|
|
143348
|
+
throw new Error(
|
|
143349
|
+
`Blocked: '${arg4}' flag can execute arbitrary code and is not allowed.`
|
|
143350
|
+
);
|
|
143351
|
+
}
|
|
143352
|
+
}
|
|
143353
|
+
}
|
|
143354
|
+
const output = $("git", [command, ...args2], { log: false });
|
|
143355
|
+
const lineCount = output.split("\n").length;
|
|
143356
|
+
if (lineCount > COLLAPSE_THRESHOLD) {
|
|
143357
|
+
log.group(`git ${command} output (${lineCount} lines)`, () => {
|
|
143358
|
+
log.info(output);
|
|
143359
|
+
});
|
|
143360
|
+
} else if (output) {
|
|
143361
|
+
log.info(output);
|
|
143362
|
+
}
|
|
143363
|
+
return { success: true, output };
|
|
143364
|
+
})
|
|
143365
|
+
});
|
|
143366
|
+
}
|
|
143367
|
+
var GitFetch = type({
|
|
143368
|
+
ref: type.string.describe("Ref to fetch: branch name, tag, or 'pull/N/head' for PRs"),
|
|
143369
|
+
depth: type.number.describe("Fetch depth (for shallow clones)").optional()
|
|
143370
|
+
});
|
|
143371
|
+
function GitFetchTool(ctx) {
|
|
143372
|
+
return tool({
|
|
143373
|
+
name: "git_fetch",
|
|
143374
|
+
description: "Fetch refs from remote repository. Use this instead of git fetch directly.",
|
|
143375
|
+
parameters: GitFetch,
|
|
143376
|
+
execute: execute(async (params) => {
|
|
143377
|
+
rejectIfLeadingDash(params.ref, "ref");
|
|
143378
|
+
const fetchArgs = ["--no-tags", "origin", params.ref];
|
|
143379
|
+
if (params.depth !== void 0) {
|
|
143380
|
+
fetchArgs.push(`--depth=${params.depth}`);
|
|
143381
|
+
}
|
|
143382
|
+
await $git("fetch", fetchArgs, {
|
|
143383
|
+
token: ctx.gitToken
|
|
143384
|
+
});
|
|
143385
|
+
return { success: true, ref: params.ref };
|
|
143386
|
+
})
|
|
143387
|
+
});
|
|
143388
|
+
}
|
|
143389
|
+
var DeleteBranch = type({
|
|
143390
|
+
branchName: type.string.describe("Remote branch to delete")
|
|
143391
|
+
});
|
|
143392
|
+
function DeleteBranchTool(ctx) {
|
|
143393
|
+
const pushPermission = ctx.payload.push;
|
|
143394
|
+
const defaultBranch = ctx.repo.data.default_branch || "main";
|
|
143395
|
+
return tool({
|
|
143396
|
+
name: "delete_branch",
|
|
143397
|
+
description: "Delete a remote branch. Requires push: enabled permission. Deletion of the repository's default branch is always blocked regardless of permission mode.",
|
|
143398
|
+
parameters: DeleteBranch,
|
|
143399
|
+
execute: execute(async (params) => {
|
|
143400
|
+
if (pushPermission !== "enabled") {
|
|
143401
|
+
throw new Error(
|
|
143402
|
+
"Branch deletion requires push: enabled permission. Current mode only allows pushing to non-protected branches."
|
|
143403
|
+
);
|
|
143404
|
+
}
|
|
143405
|
+
rejectSpecialRef(params.branchName, "branchName");
|
|
143406
|
+
if (params.branchName === defaultBranch) {
|
|
143407
|
+
throw new Error(
|
|
143408
|
+
`Blocked: cannot delete the default branch '${defaultBranch}'. If you really need to delete or rename it, do it manually via the repository settings.`
|
|
143409
|
+
);
|
|
143410
|
+
}
|
|
143411
|
+
await $git("push", ["origin", "--delete", `refs/heads/${params.branchName}`], {
|
|
143412
|
+
token: ctx.gitToken
|
|
143413
|
+
});
|
|
143414
|
+
return { success: true, deleted: params.branchName };
|
|
143415
|
+
})
|
|
143416
|
+
});
|
|
143417
|
+
}
|
|
143418
|
+
var PushTags = type({
|
|
143419
|
+
tag: type.string.describe("Tag name to push"),
|
|
143420
|
+
force: type.boolean.describe("Force push the tag").default(false)
|
|
143421
|
+
});
|
|
143422
|
+
function PushTagsTool(ctx) {
|
|
143423
|
+
const pushPermission = ctx.payload.push;
|
|
143424
|
+
return tool({
|
|
143425
|
+
name: "push_tags",
|
|
143426
|
+
description: "Push a tag to remote. Requires push: enabled permission.",
|
|
143427
|
+
parameters: PushTags,
|
|
143428
|
+
execute: execute(async (params) => {
|
|
143429
|
+
if (pushPermission !== "enabled") {
|
|
143430
|
+
throw new Error(
|
|
143431
|
+
"Tag pushing requires push: enabled permission. Current mode only allows pushing branches."
|
|
143432
|
+
);
|
|
143433
|
+
}
|
|
143434
|
+
validateTagName(params.tag);
|
|
143435
|
+
const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
|
|
143436
|
+
await $git("push", pushArgs, {
|
|
143437
|
+
token: ctx.gitToken
|
|
143438
|
+
});
|
|
143439
|
+
return { success: true, tag: params.tag };
|
|
143440
|
+
})
|
|
143441
|
+
});
|
|
143442
|
+
}
|
|
143443
|
+
|
|
143444
|
+
// mcp/review.ts
|
|
143445
|
+
function getHttpStatus(err) {
|
|
143446
|
+
if (typeof err !== "object" || err === null) return void 0;
|
|
143447
|
+
const status = err.status;
|
|
143448
|
+
return typeof status === "number" ? status : void 0;
|
|
143449
|
+
}
|
|
143450
|
+
function commentableLinesForFile(patch) {
|
|
143451
|
+
const right = /* @__PURE__ */ new Set();
|
|
143452
|
+
const left = /* @__PURE__ */ new Set();
|
|
143453
|
+
if (!patch) return { RIGHT: right, LEFT: left };
|
|
143454
|
+
let oldLine = 0;
|
|
143455
|
+
let newLine = 0;
|
|
143456
|
+
for (const line of patch.split("\n")) {
|
|
143457
|
+
const hunk = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
143458
|
+
if (hunk) {
|
|
143459
|
+
oldLine = parseInt(hunk[1], 10);
|
|
143460
|
+
newLine = parseInt(hunk[2], 10);
|
|
143461
|
+
continue;
|
|
143462
|
+
}
|
|
143463
|
+
const changeType = line[0];
|
|
143464
|
+
if (changeType === "+") {
|
|
143465
|
+
right.add(newLine);
|
|
143466
|
+
newLine++;
|
|
143467
|
+
} else if (changeType === "-") {
|
|
143468
|
+
left.add(oldLine);
|
|
143469
|
+
oldLine++;
|
|
143470
|
+
} else if (changeType === " ") {
|
|
143471
|
+
right.add(newLine);
|
|
143472
|
+
left.add(oldLine);
|
|
143473
|
+
newLine++;
|
|
143474
|
+
oldLine++;
|
|
143475
|
+
}
|
|
143476
|
+
}
|
|
143477
|
+
return { RIGHT: right, LEFT: left };
|
|
143478
|
+
}
|
|
143479
|
+
async function buildCommentableMap(ctx, pullNumber) {
|
|
143480
|
+
const cached4 = ctx.toolState.commentableLinesByFile;
|
|
143481
|
+
const cachedFor = ctx.toolState.commentableLinesPullNumber;
|
|
143482
|
+
const cachedSha = ctx.toolState.commentableLinesCheckoutSha;
|
|
143483
|
+
const currentSha = ctx.toolState.checkoutSha;
|
|
143484
|
+
if (cached4 && cachedFor === pullNumber && cachedSha && cachedSha === currentSha) return cached4;
|
|
143485
|
+
const files = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listFiles, {
|
|
143486
|
+
owner: ctx.repo.owner,
|
|
143487
|
+
repo: ctx.repo.name,
|
|
143488
|
+
pull_number: pullNumber,
|
|
143489
|
+
per_page: 100
|
|
143490
|
+
});
|
|
143491
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
143492
|
+
for (const file2 of files) {
|
|
143493
|
+
map2.set(file2.filename, commentableLinesForFile(file2.patch));
|
|
143494
|
+
}
|
|
143495
|
+
return map2;
|
|
143496
|
+
}
|
|
143497
|
+
function validateInlineComments(comments, map2) {
|
|
143498
|
+
const valid = [];
|
|
143499
|
+
const dropped = [];
|
|
143500
|
+
for (const c2 of comments) {
|
|
143501
|
+
const side = c2.side === "LEFT" ? "LEFT" : "RIGHT";
|
|
143502
|
+
const line = c2.line ?? 0;
|
|
143503
|
+
const startLine = c2.start_line ?? line;
|
|
143504
|
+
const lines = map2.get(c2.path);
|
|
143505
|
+
const record3 = (reason) => {
|
|
143506
|
+
const entry = { path: c2.path, line, side, reason };
|
|
143507
|
+
if (c2.start_line != null) entry.startLine = c2.start_line;
|
|
143508
|
+
dropped.push(entry);
|
|
143509
|
+
};
|
|
143510
|
+
if (!lines) {
|
|
143511
|
+
record3(`file not in PR diff`);
|
|
143512
|
+
continue;
|
|
143513
|
+
}
|
|
143514
|
+
if (lines.LEFT.size === 0 && lines.RIGHT.size === 0) {
|
|
143515
|
+
record3(`file has no textual diff (binary, pure rename, or mode change)`);
|
|
143516
|
+
continue;
|
|
143517
|
+
}
|
|
143518
|
+
const anchors = lines[side];
|
|
143519
|
+
if (!anchors.has(line)) {
|
|
143520
|
+
record3(`line ${line} (${side}) is not inside a diff hunk`);
|
|
143521
|
+
continue;
|
|
143522
|
+
}
|
|
143523
|
+
if (c2.start_line != null && c2.start_line > line) {
|
|
143524
|
+
record3(
|
|
143525
|
+
`start_line ${c2.start_line} is after line ${line} \u2014 ranges must satisfy start_line <= line`
|
|
143526
|
+
);
|
|
143527
|
+
continue;
|
|
143528
|
+
}
|
|
143529
|
+
if (startLine !== line && !anchors.has(startLine)) {
|
|
143530
|
+
record3(`start_line ${startLine} (${side}) is not inside a diff hunk`);
|
|
143531
|
+
continue;
|
|
143532
|
+
}
|
|
143533
|
+
valid.push(c2);
|
|
143534
|
+
}
|
|
143535
|
+
return { valid, dropped };
|
|
143536
|
+
}
|
|
143537
|
+
var MAX_DROPPED_COMMENT_LINES = 50;
|
|
143538
|
+
function reviewSkipDecision(params) {
|
|
143539
|
+
if (params.body || params.hasComments) return null;
|
|
143540
|
+
if (!params.approved) {
|
|
143541
|
+
return {
|
|
143542
|
+
kind: "no-issues",
|
|
143543
|
+
reason: "no issues found \u2014 nothing to post"
|
|
143544
|
+
};
|
|
143545
|
+
}
|
|
143546
|
+
if (!params.prApproveEnabled) {
|
|
143547
|
+
return {
|
|
143548
|
+
kind: "empty-downgraded-approve",
|
|
143549
|
+
reason: "approve requested but prApproveEnabled is disabled; no feedback body or comments to post as a COMMENT review instead"
|
|
143550
|
+
};
|
|
143551
|
+
}
|
|
143552
|
+
return null;
|
|
143553
|
+
}
|
|
143554
|
+
function formatDroppedCommentsNote(dropped) {
|
|
143555
|
+
const renderEntry = (d3) => {
|
|
143556
|
+
const range2 = d3.startLine != null && d3.startLine !== d3.line ? `${d3.startLine}-${d3.line}` : `${d3.line}`;
|
|
143557
|
+
return `- \`${d3.path}:${range2}\` (${d3.side}) \u2014 ${d3.reason}`;
|
|
143558
|
+
};
|
|
143559
|
+
const shown = dropped.slice(0, MAX_DROPPED_COMMENT_LINES).map(renderEntry);
|
|
143560
|
+
const remainder = dropped.length - shown.length;
|
|
143561
|
+
if (remainder > 0) shown.push(`- \u2026and ${remainder} more dropped comment(s) not shown`);
|
|
143562
|
+
return `
|
|
143563
|
+
|
|
143564
|
+
---
|
|
143565
|
+
|
|
143566
|
+
**Note:** ${dropped.length} inline comment(s) dropped because they did not anchor to lines inside the PR diff:
|
|
143567
|
+
` + shown.join("\n");
|
|
143568
|
+
}
|
|
143569
|
+
var CreatePullRequestReview = type({
|
|
143570
|
+
pull_number: type.number.describe("The pull request number to review"),
|
|
143571
|
+
body: type.string.describe(
|
|
143572
|
+
"1-2 sentence high-level summary with urgency level, critical callouts, and feedback about code outside the diff. Specific feedback on diff lines goes in 'comments' array."
|
|
143573
|
+
).optional(),
|
|
143574
|
+
approved: type.boolean.describe(
|
|
143575
|
+
"Set to true to submit as an approval. ONLY when the review contains no actionable feedback \u2014 neither inline comments nor actionable content in the body. Defaults to false (comment-only review). Rejections are not supported."
|
|
143576
|
+
).optional(),
|
|
143577
|
+
commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
|
|
143578
|
+
comments: type({
|
|
143579
|
+
path: type.string.describe(
|
|
143580
|
+
"The file path to comment on (relative to repo root). Must be a file that appears in the PR diff."
|
|
143581
|
+
),
|
|
143582
|
+
line: type.number.describe(
|
|
143583
|
+
"Line number to comment on. For multi-line ranges, this is the end line. Use NEW column from diff format."
|
|
143584
|
+
),
|
|
143585
|
+
side: type.enumerated("LEFT", "RIGHT").describe(
|
|
143586
|
+
"Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT."
|
|
143587
|
+
).optional(),
|
|
143588
|
+
body: type.string.describe("Explanatory comment text (optional if suggestion is provided)").optional(),
|
|
143589
|
+
suggestion: type.string.describe(
|
|
143590
|
+
"Full replacement code for the line range [start_line, line]. MUST preserve the exact indentation of the original code."
|
|
143591
|
+
).optional(),
|
|
143592
|
+
start_line: type.number.describe(
|
|
143593
|
+
"Start line for multi-line comment ranges. Omit for single-line comments. The range [start_line, line] defines which lines a suggestion replaces."
|
|
143594
|
+
).optional()
|
|
143595
|
+
}).array().describe(
|
|
143596
|
+
"Inline comments on lines within diff hunks. Feedback about code outside the diff goes in 'body' instead."
|
|
143597
|
+
).optional()
|
|
143598
|
+
});
|
|
143599
|
+
function CreatePullRequestReviewTool(ctx) {
|
|
143600
|
+
return tool({
|
|
143601
|
+
name: "create_pull_request_review",
|
|
143602
|
+
description: `Submit a review for an existing pull request. Each call creates a permanent, visible review on the PR \u2014 NEVER submit test or diagnostic reviews. Reviews with no body AND no comments are silently skipped (nothing to post). IMPORTANT: 95%+ of feedback should be in 'comments' array with file paths and line numbers. Only use 'body' for a 1-2 sentence summary with urgency and critical callouts. Use 'suggestion' to propose replacement code - MUST preserve exact indentation of original code. The first submission may error once with a one-time diff-coverage nudge listing unread TOC regions \u2014 retry with the same arguments and the pre-flight will not block again. Example replacing lines 42-44 (3 lines) with 5 lines: { path: 'src/api.ts', start_line: 42, line: 44, suggestion: ' const result = await fetch(url);\\n if (!result.ok) {\\n log.error(result.status);\\n throw new Error("request failed");\\n }' } CONSTRAINT: Inline comments can ONLY target files and lines that appear in the PR diff. Comments anchored outside a diff hunk are dropped automatically (with a note appended to the review body) \u2014 the rest of the review still posts.`,
|
|
143603
|
+
parameters: CreatePullRequestReview,
|
|
143604
|
+
execute: execute(async ({ pull_number, body, approved, commit_id, comments = [] }) => {
|
|
143605
|
+
if (body) body = fixDoubleEscapedString(body);
|
|
143606
|
+
ctx.toolState.issueNumber = pull_number;
|
|
143607
|
+
const skip = reviewSkipDecision({
|
|
143608
|
+
approved: approved ?? false,
|
|
143609
|
+
body,
|
|
143610
|
+
hasComments: comments.length > 0,
|
|
143611
|
+
prApproveEnabled: ctx.prApproveEnabled
|
|
143612
|
+
});
|
|
143613
|
+
if (skip) {
|
|
143614
|
+
log.info(`skipping review submission: ${skip.reason}`);
|
|
143615
|
+
return { success: true, skipped: true, reason: skip.reason };
|
|
143616
|
+
}
|
|
143617
|
+
let event = approved ? "APPROVE" : "COMMENT";
|
|
143618
|
+
if (event === "APPROVE" && !ctx.prApproveEnabled) {
|
|
143619
|
+
log.info("prApproveEnabled is disabled \u2014 downgrading APPROVE to COMMENT");
|
|
143620
|
+
event = "COMMENT";
|
|
143621
|
+
}
|
|
143622
|
+
const params = {
|
|
143623
|
+
owner: ctx.repo.owner,
|
|
143624
|
+
repo: ctx.repo.name,
|
|
143625
|
+
pull_number,
|
|
143626
|
+
event
|
|
143627
|
+
};
|
|
143628
|
+
let latestHeadSha;
|
|
143629
|
+
if (commit_id) {
|
|
143630
|
+
params.commit_id = commit_id;
|
|
143631
|
+
} else {
|
|
143632
|
+
const pr = await ctx.octokit.rest.pulls.get({
|
|
143633
|
+
owner: ctx.repo.owner,
|
|
143634
|
+
repo: ctx.repo.name,
|
|
143635
|
+
pull_number
|
|
143636
|
+
});
|
|
143637
|
+
latestHeadSha = pr.data.head.sha;
|
|
143638
|
+
params.commit_id = ctx.toolState.checkoutSha ?? latestHeadSha;
|
|
143639
|
+
if (ctx.toolState.checkoutSha && latestHeadSha !== ctx.toolState.checkoutSha) {
|
|
143640
|
+
log.info(
|
|
143641
|
+
`anchoring review to checkout ${ctx.toolState.checkoutSha.slice(0, 7)} (HEAD is now ${latestHeadSha.slice(0, 7)})`
|
|
143642
|
+
);
|
|
143643
|
+
}
|
|
143644
|
+
}
|
|
143645
|
+
runDiffCoveragePreflight({ ctx });
|
|
143646
|
+
const reviewComments = comments.map((comment) => {
|
|
143647
|
+
let commentBody = fixDoubleEscapedString(comment.body || "");
|
|
143648
|
+
if (comment.suggestion !== void 0) {
|
|
143649
|
+
const suggestionBlock = "```suggestion\n" + comment.suggestion + "\n```";
|
|
143650
|
+
commentBody = commentBody ? commentBody + "\n\n" + suggestionBlock : suggestionBlock;
|
|
143651
|
+
}
|
|
143652
|
+
const side = comment.side || "RIGHT";
|
|
143653
|
+
const reviewComment = {
|
|
143654
|
+
path: comment.path,
|
|
143655
|
+
line: comment.line,
|
|
143656
|
+
body: commentBody,
|
|
143657
|
+
side
|
|
143658
|
+
};
|
|
143659
|
+
if (comment.start_line != null && comment.start_line !== comment.line) {
|
|
143660
|
+
reviewComment.start_line = comment.start_line;
|
|
143661
|
+
reviewComment.start_side = side;
|
|
143662
|
+
}
|
|
143663
|
+
return reviewComment;
|
|
143664
|
+
});
|
|
143665
|
+
let droppedComments = [];
|
|
143666
|
+
if (reviewComments.length > 0) {
|
|
143667
|
+
const commentableMap = await buildCommentableMap(ctx, pull_number);
|
|
143668
|
+
const validation = validateInlineComments(reviewComments, commentableMap);
|
|
143669
|
+
droppedComments = validation.dropped;
|
|
143670
|
+
if (droppedComments.length > 0) {
|
|
143671
|
+
log.info(
|
|
143672
|
+
`dropping ${droppedComments.length}/${reviewComments.length} inline comment(s) that do not anchor to PR diff lines`
|
|
143673
|
+
);
|
|
143674
|
+
}
|
|
143675
|
+
params.comments = validation.valid;
|
|
143676
|
+
}
|
|
143677
|
+
if (droppedComments.length > 0) {
|
|
143678
|
+
const note = formatDroppedCommentsNote(droppedComments);
|
|
143679
|
+
body = body ? body + note : note.replace(/^\n\n/, "");
|
|
143680
|
+
}
|
|
143681
|
+
if (!approved && !body && !params.comments?.length) {
|
|
143682
|
+
log.info("review has no body and all inline comments were dropped \u2014 skipping submission");
|
|
143683
|
+
return {
|
|
143684
|
+
success: true,
|
|
143685
|
+
skipped: true,
|
|
143686
|
+
reason: "all inline comments were invalid \u2014 nothing to post",
|
|
143687
|
+
droppedComments
|
|
143688
|
+
};
|
|
143689
|
+
}
|
|
143690
|
+
let result;
|
|
143691
|
+
try {
|
|
143692
|
+
result = body ? await createAndSubmitWithFooter(ctx, params, {
|
|
143693
|
+
body,
|
|
143694
|
+
approved: approved ?? false,
|
|
143695
|
+
hasComments: (params.comments?.length ?? 0) > 0
|
|
143696
|
+
}) : await createReviewWithStrandedRecovery(ctx, params);
|
|
143697
|
+
} catch (err) {
|
|
143698
|
+
if (getHttpStatus(err) !== 422 || !params.comments?.length) throw err;
|
|
143699
|
+
const details = params.comments.map((c2) => {
|
|
143700
|
+
const line = c2.line ?? 0;
|
|
143701
|
+
const startLine = c2.start_line ?? line;
|
|
143702
|
+
const range2 = startLine !== line ? `${startLine}-${line}` : `${line}`;
|
|
143703
|
+
return `${c2.path}:${range2} (${c2.side ?? "RIGHT"})`;
|
|
143704
|
+
});
|
|
143705
|
+
const rawMsg = err instanceof Error ? err.message : String(err);
|
|
143706
|
+
const checkoutRef = formatMcpToolRef(ctx.agentId, "checkout_pr");
|
|
143707
|
+
throw new Error(
|
|
143708
|
+
`GitHub rejected the review with 422 even after pre-validation. Likely causes (check "GitHub said" below to narrow down): (1) new commits pushed after pre-validation \u2014 call \`${checkoutRef}\` again to refresh the diff snapshot, then resubmit; (2) the review body exceeded GitHub's ~65KB limit \u2014 shorten it and retry; (3) a \`suggestion\` block is malformed (missing backticks, extra backticks, or wrong indentation) \u2014 inspect the affected comments below. If none apply, move the failing comments into the review body as text so the rest still posts. Affected comments: ${details.join(", ")}. GitHub said: ${rawMsg}`,
|
|
143709
|
+
{ cause: err }
|
|
143710
|
+
);
|
|
143711
|
+
}
|
|
143712
|
+
log.debug(`createReview response: ${JSON.stringify(result.data)}`);
|
|
143713
|
+
if (!result.data.id) {
|
|
143714
|
+
throw new Error(`createReview returned invalid data: ${JSON.stringify(result.data)}`);
|
|
143715
|
+
}
|
|
143716
|
+
const reviewId = result.data.id;
|
|
143717
|
+
const reviewNodeId = result.data.node_id;
|
|
143718
|
+
const actuallyReviewedSha = ctx.toolState.checkoutSha ?? params.commit_id;
|
|
143719
|
+
ctx.toolState.review = {
|
|
143720
|
+
id: reviewId,
|
|
143721
|
+
nodeId: reviewNodeId,
|
|
143722
|
+
reviewedSha: actuallyReviewedSha
|
|
143723
|
+
};
|
|
143724
|
+
if (ctx.toolState.checkoutSha && latestHeadSha && latestHeadSha !== ctx.toolState.checkoutSha) {
|
|
143725
|
+
const fromSha = ctx.toolState.checkoutSha;
|
|
143726
|
+
const toSha = latestHeadSha;
|
|
143727
|
+
ctx.toolState.beforeSha = fromSha;
|
|
143728
|
+
ctx.toolState.checkoutSha = toSha;
|
|
143729
|
+
log.info(
|
|
143730
|
+
`new commits detected during review: ${fromSha.slice(0, 7)}..${toSha.slice(0, 7)}`
|
|
143731
|
+
);
|
|
143732
|
+
return {
|
|
143733
|
+
success: true,
|
|
143734
|
+
reviewId,
|
|
143735
|
+
html_url: result.data.html_url,
|
|
143736
|
+
state: result.data.state,
|
|
143737
|
+
user: result.data.user?.login,
|
|
143738
|
+
submitted_at: result.data.submitted_at,
|
|
143739
|
+
droppedComments: droppedComments.length > 0 ? droppedComments : void 0,
|
|
143740
|
+
newCommits: {
|
|
143741
|
+
from: fromSha,
|
|
143742
|
+
to: toSha,
|
|
143743
|
+
instructions: `new commits were pushed while you were reviewing. call \`${formatMcpToolRef(ctx.agentId, "checkout_pr")}\` again to fetch the latest version \u2014 it will compute the incremental diff automatically. submit another review covering only the new changes. do not repeat feedback from your previous review.`
|
|
143744
|
+
}
|
|
143745
|
+
};
|
|
143746
|
+
}
|
|
143747
|
+
return {
|
|
143748
|
+
success: true,
|
|
143749
|
+
reviewId,
|
|
143750
|
+
html_url: result.data.html_url,
|
|
143751
|
+
state: result.data.state,
|
|
143752
|
+
user: result.data.user?.login,
|
|
143753
|
+
submitted_at: result.data.submitted_at,
|
|
143754
|
+
droppedComments: droppedComments.length > 0 ? droppedComments : void 0
|
|
143755
|
+
};
|
|
143756
|
+
})
|
|
143757
|
+
});
|
|
143758
|
+
}
|
|
143759
|
+
function runDiffCoveragePreflight(params) {
|
|
143760
|
+
const coverageState = params.ctx.toolState.diffCoverage;
|
|
143761
|
+
if (!coverageState) {
|
|
143762
|
+
log.debug("diff coverage pre-flight skipped: no diffCoverage state present in toolState");
|
|
143763
|
+
return;
|
|
143764
|
+
}
|
|
143765
|
+
if (coverageState.coveragePreflightRan) {
|
|
143766
|
+
log.debug("diff coverage pre-flight skipped: already ran in this session");
|
|
143767
|
+
return;
|
|
143768
|
+
}
|
|
143769
|
+
coverageState.coveragePreflightRan = true;
|
|
143770
|
+
log.debug(
|
|
143771
|
+
`diff coverage pre-flight start: diffPath=${coverageState.diffPath}, totalLines=${coverageState.totalLines}, tocEntries=${coverageState.tocEntries.length}, coveredRanges=${coverageState.coveredRanges.length}`
|
|
143772
|
+
);
|
|
143773
|
+
const breakdown = getDiffCoverageBreakdown({ state: coverageState });
|
|
143774
|
+
const unread = [];
|
|
143775
|
+
let unreadLines = 0;
|
|
143776
|
+
for (const file2 of breakdown.files) {
|
|
143777
|
+
if (file2.unreadRanges.length === 0) continue;
|
|
143778
|
+
const rangesText = file2.unreadRanges.map((range2) => `${range2.startLine}-${range2.endLine}`).join(", ");
|
|
143779
|
+
const fileUnreadLines = countLinesInRanges({ ranges: file2.unreadRanges });
|
|
143780
|
+
unread.push({ path: file2.filename, ranges: rangesText, unreadLines: fileUnreadLines });
|
|
143781
|
+
unreadLines += fileUnreadLines;
|
|
143782
|
+
}
|
|
143783
|
+
coverageState.lastBreakdown = renderDiffCoverageBreakdown({
|
|
143784
|
+
diffPath: coverageState.diffPath,
|
|
143785
|
+
breakdown
|
|
143786
|
+
});
|
|
143787
|
+
log.debug(
|
|
143788
|
+
`diff coverage pre-flight breakdown: coveredLines=${breakdown.coveredLines}, unreadLines=${unreadLines}`
|
|
143789
|
+
);
|
|
143790
|
+
if (unreadLines === 0) {
|
|
143791
|
+
log.debug("diff coverage pre-flight passed: no unread regions");
|
|
143792
|
+
return;
|
|
143793
|
+
}
|
|
143794
|
+
log.info(
|
|
143795
|
+
`diff coverage pre-flight nudge: unread lines=${unreadLines}, unread files=${unread.length}`
|
|
143796
|
+
);
|
|
143797
|
+
const unreadText = unread.map((entry) => `- ${entry.path} (${entry.unreadLines} lines, ${entry.ranges})`).join("\n");
|
|
143798
|
+
throw new Error(
|
|
143799
|
+
`diff coverage pre-flight: some TOC regions were not read before review submission. this is a one-time nudge \u2014 optionally read the ranges below from ${coverageState.diffPath}, then call create_pull_request_review again with the same arguments. this pre-flight will not block again in this review session.
|
|
143800
|
+
|
|
143801
|
+
unread TOC regions:
|
|
143802
|
+
${unreadText}
|
|
143803
|
+
|
|
143804
|
+
${coverageState.lastBreakdown}`
|
|
143805
|
+
);
|
|
143806
|
+
}
|
|
143807
|
+
async function clearStrandedPendingReview(ctx, params) {
|
|
143808
|
+
const originalErr = params.originalErr;
|
|
143809
|
+
const msg = originalErr instanceof Error ? originalErr.message.toLowerCase() : "";
|
|
143810
|
+
if (getHttpStatus(originalErr) !== 422 || !msg.includes("pending review")) throw originalErr;
|
|
143811
|
+
const reviews = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listReviews, {
|
|
143812
|
+
owner: params.owner,
|
|
143813
|
+
repo: params.repo,
|
|
143814
|
+
pull_number: params.pull_number,
|
|
143815
|
+
per_page: 100
|
|
143816
|
+
}).catch((listErr) => {
|
|
143817
|
+
log.info(
|
|
143818
|
+
`\xBB listReviews failed during pending-review cleanup, surfacing original 422: ${listErr instanceof Error ? listErr.message : String(listErr)}`
|
|
143819
|
+
);
|
|
143820
|
+
throw originalErr;
|
|
143821
|
+
});
|
|
143822
|
+
const leftover = reviews.find((r) => r.state === "PENDING");
|
|
143823
|
+
if (!leftover?.id) throw originalErr;
|
|
143824
|
+
log.info(
|
|
143825
|
+
`\xBB clearing leftover pending review ${leftover.id} (likely stranded by a killed prior run)`
|
|
143826
|
+
);
|
|
143827
|
+
try {
|
|
143828
|
+
await ctx.octokit.rest.pulls.deletePendingReview({
|
|
143829
|
+
owner: params.owner,
|
|
143830
|
+
repo: params.repo,
|
|
143831
|
+
pull_number: params.pull_number,
|
|
143832
|
+
review_id: leftover.id
|
|
143833
|
+
});
|
|
143834
|
+
} catch (cleanupErr) {
|
|
143835
|
+
const cleanupStatus = getHttpStatus(cleanupErr);
|
|
143836
|
+
if (cleanupStatus !== 404 && cleanupStatus !== 422) throw cleanupErr;
|
|
143837
|
+
log.debug(`\xBB delete of leftover pending ${leftover.id} no-op (status ${cleanupStatus})`);
|
|
143838
|
+
}
|
|
143839
|
+
}
|
|
143840
|
+
async function createReviewWithStrandedRecovery(ctx, params) {
|
|
143841
|
+
try {
|
|
143842
|
+
return await ctx.octokit.rest.pulls.createReview(params);
|
|
143843
|
+
} catch (err) {
|
|
143844
|
+
await clearStrandedPendingReview(ctx, {
|
|
143845
|
+
owner: params.owner,
|
|
143846
|
+
repo: params.repo,
|
|
143847
|
+
pull_number: params.pull_number,
|
|
143848
|
+
originalErr: err
|
|
143849
|
+
});
|
|
143850
|
+
return await ctx.octokit.rest.pulls.createReview(params);
|
|
143851
|
+
}
|
|
143852
|
+
}
|
|
143853
|
+
async function createAndSubmitWithFooter(ctx, params, opts) {
|
|
143854
|
+
const { event: _2, ...pendingParams } = params;
|
|
143855
|
+
let pending;
|
|
143856
|
+
try {
|
|
143857
|
+
pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
|
|
143858
|
+
} catch (err) {
|
|
143859
|
+
await clearStrandedPendingReview(ctx, {
|
|
143860
|
+
owner: params.owner,
|
|
143861
|
+
repo: params.repo,
|
|
143862
|
+
pull_number: params.pull_number,
|
|
143863
|
+
originalErr: err
|
|
143864
|
+
});
|
|
143865
|
+
pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
|
|
143866
|
+
}
|
|
143867
|
+
if (!pending.data.id) {
|
|
143868
|
+
throw new Error(`createReview returned invalid data: ${JSON.stringify(pending.data)}`);
|
|
143869
|
+
}
|
|
143870
|
+
try {
|
|
143871
|
+
const customParts = [];
|
|
143872
|
+
if (!opts.approved) {
|
|
143873
|
+
const apiUrl = getApiUrl();
|
|
143874
|
+
if (opts.hasComments) {
|
|
143875
|
+
const fixAllUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
|
|
143876
|
+
const fixApprovedUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix-approved&review_id=${pending.data.id}`;
|
|
143877
|
+
customParts.push(`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`);
|
|
143878
|
+
} else {
|
|
143879
|
+
const fixUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
|
|
143880
|
+
customParts.push(`[Fix it \u2794](${fixUrl})`);
|
|
143881
|
+
}
|
|
143882
|
+
}
|
|
143883
|
+
const footer = buildPullfrogFooter({
|
|
143884
|
+
workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
|
|
143885
|
+
customParts,
|
|
143886
|
+
model: ctx.toolState.model
|
|
143887
|
+
});
|
|
143888
|
+
return await ctx.octokit.rest.pulls.submitReview({
|
|
143889
|
+
owner: params.owner,
|
|
143890
|
+
repo: params.repo,
|
|
143891
|
+
pull_number: params.pull_number,
|
|
143892
|
+
review_id: pending.data.id,
|
|
143893
|
+
event: params.event,
|
|
143894
|
+
body: opts.body + footer
|
|
143895
|
+
});
|
|
143896
|
+
} catch (err) {
|
|
143897
|
+
try {
|
|
143898
|
+
await ctx.octokit.rest.pulls.deletePendingReview({
|
|
143899
|
+
owner: params.owner,
|
|
143900
|
+
repo: params.repo,
|
|
143901
|
+
pull_number: params.pull_number,
|
|
143902
|
+
review_id: pending.data.id
|
|
143903
|
+
});
|
|
143904
|
+
log.debug(`\xBB deleted leftover pending review ${pending.data.id} after failure`);
|
|
143905
|
+
} catch (cleanupErr) {
|
|
143906
|
+
log.debug(
|
|
143907
|
+
`\xBB failed to delete pending review ${pending.data.id}: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`
|
|
143908
|
+
);
|
|
143909
|
+
}
|
|
143910
|
+
throw err;
|
|
143911
|
+
}
|
|
143912
|
+
}
|
|
143913
|
+
async function reportReviewNodeId(ctx, params) {
|
|
143914
|
+
await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
|
|
143915
|
+
}
|
|
143916
|
+
|
|
142458
143917
|
// mcp/checkout.ts
|
|
142459
143918
|
function formatFilesWithLineNumbers(files) {
|
|
142460
143919
|
const output = [];
|
|
@@ -142521,7 +143980,10 @@ function formatFilesWithLineNumbers(files) {
|
|
|
142521
143980
|
}
|
|
142522
143981
|
const tocLines = [`## Files (${files.length})`];
|
|
142523
143982
|
for (const entry of tocEntries) {
|
|
142524
|
-
|
|
143983
|
+
const anchor = createHash2("sha256").update(entry.filename).digest("hex");
|
|
143984
|
+
tocLines.push(
|
|
143985
|
+
`- ${entry.filename} \u2192 lines ${entry.startLine}-${entry.endLine} \xB7 diff-${anchor}`
|
|
143986
|
+
);
|
|
142525
143987
|
}
|
|
142526
143988
|
tocLines.push("");
|
|
142527
143989
|
tocLines.push("---");
|
|
@@ -142543,7 +144005,7 @@ async function fetchAndFormatPrDiff(ctx, pullNumber) {
|
|
|
142543
144005
|
pull_number: pullNumber,
|
|
142544
144006
|
per_page: 100
|
|
142545
144007
|
});
|
|
142546
|
-
return formatFilesWithLineNumbers(files);
|
|
144008
|
+
return { ...formatFilesWithLineNumbers(files), files };
|
|
142547
144009
|
}
|
|
142548
144010
|
async function createTempBranch(params) {
|
|
142549
144011
|
const response = await params.octokit.rest.git.createRef({
|
|
@@ -142610,6 +144072,8 @@ async function ensureBeforeShaReachable(params) {
|
|
|
142610
144072
|
async function checkoutPrBranch(pr, params) {
|
|
142611
144073
|
const { octokit, owner, name, gitToken, toolState, beforeSha } = params;
|
|
142612
144074
|
log.info(`\xBB checking out PR #${pr.number}...`);
|
|
144075
|
+
rejectIfLeadingDash(pr.baseRef, "PR base ref");
|
|
144076
|
+
rejectIfLeadingDash(pr.headRef, "PR head ref");
|
|
142613
144077
|
const isFork = pr.headRepoFullName !== pr.baseRepoFullName;
|
|
142614
144078
|
const localBranch = `pr-${pr.number}`;
|
|
142615
144079
|
const isShallow = $("git", ["rev-parse", "--is-shallow-repository"], { log: false }).trim() === "true";
|
|
@@ -142620,7 +144084,7 @@ async function checkoutPrBranch(pr, params) {
|
|
|
142620
144084
|
if (!alreadyOnBranch) {
|
|
142621
144085
|
$("git", ["checkout", "-B", pr.baseRef, `origin/${pr.baseRef}`], { log: false });
|
|
142622
144086
|
log.debug(`\xBB fetching PR #${pr.number} (${localBranch})...`);
|
|
142623
|
-
await $git("fetch", ["--no-tags", "origin",
|
|
144087
|
+
await $git("fetch", ["--no-tags", "origin", `+pull/${pr.number}/head:${localBranch}`], {
|
|
142624
144088
|
token: gitToken
|
|
142625
144089
|
});
|
|
142626
144090
|
$("git", ["checkout", localBranch], { log: false });
|
|
@@ -142703,10 +144167,11 @@ async function checkoutPrBranch(pr, params) {
|
|
|
142703
144167
|
remoteBranch: pr.headRef,
|
|
142704
144168
|
localBranch
|
|
142705
144169
|
};
|
|
142706
|
-
await executeLifecycleHook({
|
|
144170
|
+
const postCheckoutHook = await executeLifecycleHook({
|
|
142707
144171
|
event: "post-checkout",
|
|
142708
144172
|
script: params.postCheckoutScript
|
|
142709
144173
|
});
|
|
144174
|
+
return { hookWarning: postCheckoutHook.warning };
|
|
142710
144175
|
}
|
|
142711
144176
|
function CheckoutPrTool(ctx) {
|
|
142712
144177
|
return tool({
|
|
@@ -142732,7 +144197,7 @@ function CheckoutPrTool(ctx) {
|
|
|
142732
144197
|
baseRepoFullName: prResponse.data.base.repo.full_name,
|
|
142733
144198
|
maintainerCanModify: prResponse.data.maintainer_can_modify
|
|
142734
144199
|
};
|
|
142735
|
-
await checkoutPrBranch(pr, {
|
|
144200
|
+
const checkoutResult = await checkoutPrBranch(pr, {
|
|
142736
144201
|
octokit: ctx.octokit,
|
|
142737
144202
|
owner: ctx.repo.owner,
|
|
142738
144203
|
name: ctx.repo.name,
|
|
@@ -142775,11 +144240,49 @@ ${diffPreview}`);
|
|
|
142775
144240
|
const diffPath = join3(tempDir, `pr-${pull_number}-${headShort}.diff`);
|
|
142776
144241
|
writeFileSync(diffPath, formatResult.content);
|
|
142777
144242
|
log.debug(`wrote diff to ${diffPath} (${formatResult.content.length} bytes)`);
|
|
144243
|
+
ctx.toolState.diffCoverage = createDiffCoverageState({
|
|
144244
|
+
diffPath,
|
|
144245
|
+
totalLines: countLines({ content: formatResult.content }),
|
|
144246
|
+
toc: formatResult.toc
|
|
144247
|
+
});
|
|
144248
|
+
log.debug(
|
|
144249
|
+
`\xBB diff coverage initialized: diffPath=${diffPath}, totalLines=${ctx.toolState.diffCoverage.totalLines}, tocEntries=${ctx.toolState.diffCoverage.tocEntries.length}`
|
|
144250
|
+
);
|
|
144251
|
+
const cached4 = /* @__PURE__ */ new Map();
|
|
144252
|
+
for (const file2 of formatResult.files) {
|
|
144253
|
+
cached4.set(file2.filename, commentableLinesForFile(file2.patch));
|
|
144254
|
+
}
|
|
144255
|
+
ctx.toolState.commentableLinesByFile = cached4;
|
|
144256
|
+
ctx.toolState.commentableLinesPullNumber = pull_number;
|
|
144257
|
+
ctx.toolState.commentableLinesCheckoutSha = ctx.toolState.checkoutSha;
|
|
142778
144258
|
const incrementalInstructions = incrementalDiffPath ? ` IMPORTANT: incrementalDiffPath contains ONLY the changes since the last reviewed version (computed via range-diff). you MUST read incrementalDiffPath FIRST to understand what changed, then use diffPath for full PR context. do NOT skip the incremental diff.` : "";
|
|
144259
|
+
const COMMIT_LOG_MAX = 200;
|
|
144260
|
+
const baseRange = `origin/${pr.baseRef}..HEAD`;
|
|
144261
|
+
let commitCount = 0;
|
|
144262
|
+
let commitLog = "";
|
|
144263
|
+
let commitLogUnavailable = false;
|
|
144264
|
+
try {
|
|
144265
|
+
commitCount = parseInt(
|
|
144266
|
+
$("git", ["rev-list", "--count", baseRange], { log: false }).trim() || "0",
|
|
144267
|
+
10
|
|
144268
|
+
);
|
|
144269
|
+
commitLog = $("git", ["log", "--oneline", `--max-count=${COMMIT_LOG_MAX}`, baseRange], {
|
|
144270
|
+
log: false
|
|
144271
|
+
});
|
|
144272
|
+
} catch (err) {
|
|
144273
|
+
commitLogUnavailable = true;
|
|
144274
|
+
log.debug(
|
|
144275
|
+
`\xBB unable to compute commit metadata for ${baseRange}: ${err instanceof Error ? err.message : String(err)}`
|
|
144276
|
+
);
|
|
144277
|
+
}
|
|
144278
|
+
const commitLogTruncated = commitCount > COMMIT_LOG_MAX;
|
|
144279
|
+
const hookWarningInstructions = checkoutResult.hookWarning ? ` HOOK WARNING: the post-checkout lifecycle hook reported a non-fatal failure (see hookWarning). decide whether to retry based on the guidance in that field before proceeding.` : "";
|
|
144280
|
+
const commitLogInstructions = commitLogUnavailable ? ` NOTE: commit metadata is partial (base ref unreachable, likely a shallow fetch). commitCount/commitLog may be 0/empty or incomplete; treat them as "unknown" rather than "no commits", and use \`git log\` directly if you need the full history.` : commitLogTruncated ? ` NOTE: commitLog was capped at ${COMMIT_LOG_MAX} entries out of ${commitCount} commits; use \`git log\` directly if you need the full history.` : "";
|
|
142779
144281
|
return {
|
|
142780
144282
|
success: true,
|
|
142781
144283
|
number: prResponse.data.number,
|
|
142782
144284
|
title: prResponse.data.title,
|
|
144285
|
+
body: prResponse.data.body,
|
|
142783
144286
|
base: pr.baseRef,
|
|
142784
144287
|
localBranch: `pr-${pull_number}`,
|
|
142785
144288
|
remoteBranch: `refs/heads/${pr.headRef}`,
|
|
@@ -142790,7 +144293,12 @@ ${diffPreview}`);
|
|
|
142790
144293
|
diffPath,
|
|
142791
144294
|
incrementalDiffPath,
|
|
142792
144295
|
toc: formatResult.toc,
|
|
142793
|
-
|
|
144296
|
+
commitCount,
|
|
144297
|
+
commitLog,
|
|
144298
|
+
commitLogTruncated,
|
|
144299
|
+
commitLogUnavailable,
|
|
144300
|
+
hookWarning: checkoutResult.hookWarning,
|
|
144301
|
+
instructions: `the diff file at diffPath contains a table of contents (TOC) at the top listing every changed file with its line range. use the TOC line ranges as your checklist and read specific files from the diff instead of reading the entire file. for example, if the TOC says "src/foo.ts \u2192 lines 5-42", read lines 5-42 from diffPath to see that file's changes. review files selectively based on relevance rather than reading everything sequentially. to inspect the PR's changed files, use diffPath \u2014 do NOT run \`git diff <base>..<head>\` to re-derive what's already in diffPath. the formatted diff with line numbers is authoritative. \`git log\` and \`git diff --stat\` are fine for commit-range overview, and \`git diff\` / \`git diff --cached\` are fine for inspecting *your own* uncommitted changes \u2014 but PR review content MUST come from diffPath. before your review is submitted, a one-time coverage pre-flight may error listing unread TOC regions. retry the same create_pull_request_review call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session. the local branch is 'localBranch' (pr-{number}), not the remote branch name. when pushing, omit branchName to use the current branch. do not use remoteBranch as a local branch name.` + incrementalInstructions + hookWarningInstructions + commitLogInstructions
|
|
142794
144302
|
};
|
|
142795
144303
|
})
|
|
142796
144304
|
});
|
|
@@ -143018,244 +144526,6 @@ function CommitInfoTool(ctx) {
|
|
|
143018
144526
|
});
|
|
143019
144527
|
}
|
|
143020
144528
|
|
|
143021
|
-
// mcp/git.ts
|
|
143022
|
-
function getPushDestination(branch, storedDest) {
|
|
143023
|
-
if (storedDest && storedDest.localBranch === branch) {
|
|
143024
|
-
log.debug(`using stored push destination: ${storedDest.remoteName}/${storedDest.remoteBranch}`);
|
|
143025
|
-
const url4 = $("git", ["remote", "get-url", "--push", storedDest.remoteName], {
|
|
143026
|
-
log: false
|
|
143027
|
-
}).trim();
|
|
143028
|
-
return { remoteName: storedDest.remoteName, remoteBranch: storedDest.remoteBranch, url: url4 };
|
|
143029
|
-
}
|
|
143030
|
-
try {
|
|
143031
|
-
const pushRemote = $("git", ["config", `branch.${branch}.pushRemote`], { log: false }).trim();
|
|
143032
|
-
const merge4 = $("git", ["config", `branch.${branch}.merge`], { log: false }).trim();
|
|
143033
|
-
const remoteBranch = merge4.replace(/^refs\/heads\//, "");
|
|
143034
|
-
const url4 = $("git", ["remote", "get-url", "--push", pushRemote], { log: false }).trim();
|
|
143035
|
-
return { remoteName: pushRemote, remoteBranch, url: url4 };
|
|
143036
|
-
} catch {
|
|
143037
|
-
log.debug(`no push config for ${branch}, falling back to origin/${branch}`);
|
|
143038
|
-
const url4 = $("git", ["remote", "get-url", "--push", "origin"], { log: false }).trim();
|
|
143039
|
-
return { remoteName: "origin", remoteBranch: branch, url: url4 };
|
|
143040
|
-
}
|
|
143041
|
-
}
|
|
143042
|
-
function normalizeUrl(url4) {
|
|
143043
|
-
return url4.replace(/\.git$/, "").toLowerCase();
|
|
143044
|
-
}
|
|
143045
|
-
function validatePushDestination(ctx, branch) {
|
|
143046
|
-
const pushUrl = ctx.toolState.pushUrl;
|
|
143047
|
-
if (!pushUrl) throw new Error("pushUrl not set - setupGit must run before push_branch");
|
|
143048
|
-
const dest = getPushDestination(branch, ctx.toolState.pushDest);
|
|
143049
|
-
if (normalizeUrl(dest.url) !== normalizeUrl(pushUrl)) {
|
|
143050
|
-
throw new Error(
|
|
143051
|
-
`Push blocked: destination does not match expected repository.
|
|
143052
|
-
Expected: ${pushUrl}
|
|
143053
|
-
Actual: ${dest.url}
|
|
143054
|
-
Git configuration may have been tampered with.`
|
|
143055
|
-
);
|
|
143056
|
-
}
|
|
143057
|
-
return dest;
|
|
143058
|
-
}
|
|
143059
|
-
var PushBranch = type({
|
|
143060
|
-
branchName: type.string.describe("The branch name to push (defaults to current branch)").optional(),
|
|
143061
|
-
force: type.boolean.describe("Force push (use with caution)").default(false)
|
|
143062
|
-
});
|
|
143063
|
-
function PushBranchTool(ctx) {
|
|
143064
|
-
const defaultBranch = ctx.repo.data.default_branch || "main";
|
|
143065
|
-
const pushPermission = ctx.payload.push;
|
|
143066
|
-
return tool({
|
|
143067
|
-
name: "push_branch",
|
|
143068
|
-
description: "Push the current branch to the remote repository. Omit branchName to push the current branch (recommended). If specifying branchName, use the LOCAL branch name (e.g., 'pr-1'), not the remote branch name. The correct remote and remote branch are determined automatically from branch config set by checkout_pr. Requires a clean working tree. Runs the repository prepush hook (if configured) before the network push \u2014 hook failure means tests/lint or similar in that script failed, not necessarily a Pullfrog timeout. Never force push unless explicitly requested. Pushes to the default branch are blocked in restricted mode.",
|
|
143069
|
-
parameters: PushBranch,
|
|
143070
|
-
execute: execute(async ({ branchName, force }) => {
|
|
143071
|
-
if (pushPermission === "disabled") {
|
|
143072
|
-
throw new Error("Push is disabled. This repository is configured for read-only access.");
|
|
143073
|
-
}
|
|
143074
|
-
const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
|
143075
|
-
const status = $("git", ["status", "--porcelain"], { log: false });
|
|
143076
|
-
if (status) {
|
|
143077
|
-
throw new Error(
|
|
143078
|
-
`push blocked: working tree is not clean (tracked changes and/or untracked files). commit, discard, or remove stray artifacts before pushing.
|
|
143079
|
-
|
|
143080
|
-
git status:
|
|
143081
|
-
${status}`
|
|
143082
|
-
);
|
|
143083
|
-
}
|
|
143084
|
-
const pushDest = validatePushDestination(ctx, branch);
|
|
143085
|
-
if (pushPermission === "restricted" && pushDest.remoteBranch === defaultBranch) {
|
|
143086
|
-
throw new Error(
|
|
143087
|
-
`Push blocked: cannot push directly to default branch '${pushDest.remoteBranch}'. Create a feature branch and open a PR instead.`
|
|
143088
|
-
);
|
|
143089
|
-
}
|
|
143090
|
-
const refspec = branch === pushDest.remoteBranch ? branch : `${branch}:${pushDest.remoteBranch}`;
|
|
143091
|
-
const pushArgs = force ? ["--force", "-u", pushDest.remoteName, refspec] : ["-u", pushDest.remoteName, refspec];
|
|
143092
|
-
await executeLifecycleHook({ event: "prepush", script: ctx.prepushScript });
|
|
143093
|
-
log.debug(`pushing ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`);
|
|
143094
|
-
if (force) {
|
|
143095
|
-
log.warning(`force pushing - this will overwrite remote history`);
|
|
143096
|
-
}
|
|
143097
|
-
try {
|
|
143098
|
-
await $git("push", pushArgs, {
|
|
143099
|
-
token: ctx.gitToken
|
|
143100
|
-
});
|
|
143101
|
-
} catch (err) {
|
|
143102
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
143103
|
-
if (msg.includes("fetch first") || msg.includes("non-fast-forward")) {
|
|
143104
|
-
throw new Error(
|
|
143105
|
-
`push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally.
|
|
143106
|
-
|
|
143107
|
-
to resolve this:
|
|
143108
|
-
1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
|
|
143109
|
-
2. use the git tool to rebase your changes: git({ subcommand: "rebase", args: ["origin/${pushDest.remoteBranch}"] })
|
|
143110
|
-
3. resolve any merge conflicts if needed
|
|
143111
|
-
4. retry push_branch`
|
|
143112
|
-
);
|
|
143113
|
-
}
|
|
143114
|
-
throw err;
|
|
143115
|
-
}
|
|
143116
|
-
return {
|
|
143117
|
-
success: true,
|
|
143118
|
-
branch,
|
|
143119
|
-
remoteBranch: pushDest.remoteBranch,
|
|
143120
|
-
remote: pushDest.remoteName,
|
|
143121
|
-
force,
|
|
143122
|
-
message: `successfully pushed ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`
|
|
143123
|
-
};
|
|
143124
|
-
})
|
|
143125
|
-
});
|
|
143126
|
-
}
|
|
143127
|
-
var AUTH_REQUIRED_REDIRECT = {
|
|
143128
|
-
push: "use the push_branch tool instead \u2014 it handles authentication and permission checks.",
|
|
143129
|
-
fetch: "use the git_fetch tool instead \u2014 it handles authentication.",
|
|
143130
|
-
pull: "use git_fetch to fetch the remote ref, then use this git tool with subcommand 'merge' or 'rebase' locally.",
|
|
143131
|
-
clone: "the repository is already cloned. use checkout_pr for PR branches."
|
|
143132
|
-
};
|
|
143133
|
-
var NOSHELL_BLOCKED_SUBCOMMANDS = {
|
|
143134
|
-
config: "Blocked: git config can set up filter drivers or hooks that execute arbitrary code.",
|
|
143135
|
-
submodule: "Blocked: git submodule can reference malicious repositories and execute code on update.",
|
|
143136
|
-
"update-index": "Blocked: git update-index can modify index entries in ways that bypass file protections.",
|
|
143137
|
-
"filter-branch": "Blocked: git filter-branch executes arbitrary code on repository history.",
|
|
143138
|
-
replace: "Blocked: git replace can redirect object lookups.",
|
|
143139
|
-
// subcommands that accept --exec or similar flags for arbitrary code execution
|
|
143140
|
-
rebase: "Blocked: git rebase --exec can execute arbitrary shell commands.",
|
|
143141
|
-
bisect: "Blocked: git bisect run can execute arbitrary shell commands."
|
|
143142
|
-
};
|
|
143143
|
-
var NOSHELL_BLOCKED_ARGS = ["--exec", "--extcmd", "--upload-pack", "--receive-pack"];
|
|
143144
|
-
var COLLAPSE_THRESHOLD = 200;
|
|
143145
|
-
var subcommandPattern = regex("^[a-z][a-z0-9-]*$");
|
|
143146
|
-
var Git = type({
|
|
143147
|
-
subcommand: type(subcommandPattern).describe("Git subcommand (e.g., 'status', 'log', 'diff')"),
|
|
143148
|
-
args: type.string.array().describe("Additional arguments for the git command").optional()
|
|
143149
|
-
});
|
|
143150
|
-
function GitTool(ctx) {
|
|
143151
|
-
return tool({
|
|
143152
|
-
name: "git",
|
|
143153
|
-
description: "Run git commands. For push/fetch/pull, use the dedicated MCP tools instead (push_branch, git_fetch).",
|
|
143154
|
-
parameters: Git,
|
|
143155
|
-
execute: execute(async (params) => {
|
|
143156
|
-
const subcommand = params.subcommand;
|
|
143157
|
-
const args2 = params.args ?? [];
|
|
143158
|
-
const redirect = AUTH_REQUIRED_REDIRECT[subcommand];
|
|
143159
|
-
if (redirect) {
|
|
143160
|
-
throw new Error(`git ${subcommand} is not available through this tool \u2014 ${redirect}`);
|
|
143161
|
-
}
|
|
143162
|
-
if (ctx.payload.shell === "disabled") {
|
|
143163
|
-
const blocked = NOSHELL_BLOCKED_SUBCOMMANDS[subcommand];
|
|
143164
|
-
if (blocked) {
|
|
143165
|
-
throw new Error(blocked);
|
|
143166
|
-
}
|
|
143167
|
-
for (const arg4 of args2) {
|
|
143168
|
-
const isBlocked = NOSHELL_BLOCKED_ARGS.some(
|
|
143169
|
-
(flag) => arg4 === flag || arg4.startsWith(flag + "=")
|
|
143170
|
-
);
|
|
143171
|
-
if (isBlocked) {
|
|
143172
|
-
throw new Error(
|
|
143173
|
-
`Blocked: '${arg4}' flag can execute arbitrary code and is not allowed.`
|
|
143174
|
-
);
|
|
143175
|
-
}
|
|
143176
|
-
}
|
|
143177
|
-
}
|
|
143178
|
-
const output = $("git", [subcommand, ...args2], { log: false });
|
|
143179
|
-
const lineCount = output.split("\n").length;
|
|
143180
|
-
if (lineCount > COLLAPSE_THRESHOLD) {
|
|
143181
|
-
log.group(`git ${subcommand} output (${lineCount} lines)`, () => {
|
|
143182
|
-
log.info(output);
|
|
143183
|
-
});
|
|
143184
|
-
} else if (output) {
|
|
143185
|
-
log.info(output);
|
|
143186
|
-
}
|
|
143187
|
-
return { success: true, output };
|
|
143188
|
-
})
|
|
143189
|
-
});
|
|
143190
|
-
}
|
|
143191
|
-
var GitFetch = type({
|
|
143192
|
-
ref: type.string.describe("Ref to fetch: branch name, tag, or 'pull/N/head' for PRs"),
|
|
143193
|
-
depth: type.number.describe("Fetch depth (for shallow clones)").optional()
|
|
143194
|
-
});
|
|
143195
|
-
function GitFetchTool(ctx) {
|
|
143196
|
-
return tool({
|
|
143197
|
-
name: "git_fetch",
|
|
143198
|
-
description: "Fetch refs from remote repository. Use this instead of git fetch directly.",
|
|
143199
|
-
parameters: GitFetch,
|
|
143200
|
-
execute: execute(async (params) => {
|
|
143201
|
-
const fetchArgs = ["--no-tags", "origin", params.ref];
|
|
143202
|
-
if (params.depth !== void 0) {
|
|
143203
|
-
fetchArgs.push(`--depth=${params.depth}`);
|
|
143204
|
-
}
|
|
143205
|
-
await $git("fetch", fetchArgs, {
|
|
143206
|
-
token: ctx.gitToken
|
|
143207
|
-
});
|
|
143208
|
-
return { success: true, ref: params.ref };
|
|
143209
|
-
})
|
|
143210
|
-
});
|
|
143211
|
-
}
|
|
143212
|
-
var DeleteBranch = type({
|
|
143213
|
-
branchName: type.string.describe("Remote branch to delete")
|
|
143214
|
-
});
|
|
143215
|
-
function DeleteBranchTool(ctx) {
|
|
143216
|
-
const pushPermission = ctx.payload.push;
|
|
143217
|
-
return tool({
|
|
143218
|
-
name: "delete_branch",
|
|
143219
|
-
description: "Delete a remote branch. Requires push: enabled permission.",
|
|
143220
|
-
parameters: DeleteBranch,
|
|
143221
|
-
execute: execute(async (params) => {
|
|
143222
|
-
if (pushPermission !== "enabled") {
|
|
143223
|
-
throw new Error(
|
|
143224
|
-
"Branch deletion requires push: enabled permission. Current mode only allows pushing to non-protected branches."
|
|
143225
|
-
);
|
|
143226
|
-
}
|
|
143227
|
-
await $git("push", ["origin", "--delete", params.branchName], {
|
|
143228
|
-
token: ctx.gitToken
|
|
143229
|
-
});
|
|
143230
|
-
return { success: true, deleted: params.branchName };
|
|
143231
|
-
})
|
|
143232
|
-
});
|
|
143233
|
-
}
|
|
143234
|
-
var PushTags = type({
|
|
143235
|
-
tag: type.string.describe("Tag name to push"),
|
|
143236
|
-
force: type.boolean.describe("Force push the tag").default(false)
|
|
143237
|
-
});
|
|
143238
|
-
function PushTagsTool(ctx) {
|
|
143239
|
-
const pushPermission = ctx.payload.push;
|
|
143240
|
-
return tool({
|
|
143241
|
-
name: "push_tags",
|
|
143242
|
-
description: "Push a tag to remote. Requires push: enabled permission.",
|
|
143243
|
-
parameters: PushTags,
|
|
143244
|
-
execute: execute(async (params) => {
|
|
143245
|
-
if (pushPermission !== "enabled") {
|
|
143246
|
-
throw new Error(
|
|
143247
|
-
"Tag pushing requires push: enabled permission. Current mode only allows pushing branches."
|
|
143248
|
-
);
|
|
143249
|
-
}
|
|
143250
|
-
const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
|
|
143251
|
-
await $git("push", pushArgs, {
|
|
143252
|
-
token: ctx.gitToken
|
|
143253
|
-
});
|
|
143254
|
-
return { success: true, tag: params.tag };
|
|
143255
|
-
})
|
|
143256
|
-
});
|
|
143257
|
-
}
|
|
143258
|
-
|
|
143259
144529
|
// mcp/issue.ts
|
|
143260
144530
|
var Issue = type({
|
|
143261
144531
|
title: type.string.describe("the title of the issue"),
|
|
@@ -143718,221 +144988,6 @@ function PullRequestInfoTool(ctx) {
|
|
|
143718
144988
|
});
|
|
143719
144989
|
}
|
|
143720
144990
|
|
|
143721
|
-
// mcp/review.ts
|
|
143722
|
-
function getHttpStatus(err) {
|
|
143723
|
-
if (typeof err !== "object" || err === null) return void 0;
|
|
143724
|
-
const status = err.status;
|
|
143725
|
-
return typeof status === "number" ? status : void 0;
|
|
143726
|
-
}
|
|
143727
|
-
var CreatePullRequestReview = type({
|
|
143728
|
-
pull_number: type.number.describe("The pull request number to review"),
|
|
143729
|
-
body: type.string.describe(
|
|
143730
|
-
"1-2 sentence high-level summary with urgency level, critical callouts, and feedback about code outside the diff. Specific feedback on diff lines goes in 'comments' array."
|
|
143731
|
-
).optional(),
|
|
143732
|
-
approved: type.boolean.describe(
|
|
143733
|
-
"Set to true to submit as an approval. ONLY when the review contains no actionable feedback \u2014 neither inline comments nor actionable content in the body. Defaults to false (comment-only review). Rejections are not supported."
|
|
143734
|
-
).optional(),
|
|
143735
|
-
commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
|
|
143736
|
-
comments: type({
|
|
143737
|
-
path: type.string.describe(
|
|
143738
|
-
"The file path to comment on (relative to repo root). Must be a file that appears in the PR diff."
|
|
143739
|
-
),
|
|
143740
|
-
line: type.number.describe(
|
|
143741
|
-
"Line number to comment on. For multi-line ranges, this is the end line. Use NEW column from diff format."
|
|
143742
|
-
),
|
|
143743
|
-
side: type.enumerated("LEFT", "RIGHT").describe(
|
|
143744
|
-
"Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT."
|
|
143745
|
-
).optional(),
|
|
143746
|
-
body: type.string.describe("Explanatory comment text (optional if suggestion is provided)").optional(),
|
|
143747
|
-
suggestion: type.string.describe(
|
|
143748
|
-
"Full replacement code for the line range [start_line, line]. MUST preserve the exact indentation of the original code."
|
|
143749
|
-
).optional(),
|
|
143750
|
-
start_line: type.number.describe(
|
|
143751
|
-
"Start line for multi-line comment ranges. Omit for single-line comments. The range [start_line, line] defines which lines a suggestion replaces."
|
|
143752
|
-
).optional()
|
|
143753
|
-
}).array().describe(
|
|
143754
|
-
"Inline comments on lines within diff hunks. Feedback about code outside the diff goes in 'body' instead."
|
|
143755
|
-
).optional()
|
|
143756
|
-
});
|
|
143757
|
-
function CreatePullRequestReviewTool(ctx) {
|
|
143758
|
-
return tool({
|
|
143759
|
-
name: "create_pull_request_review",
|
|
143760
|
-
description: `Submit a review for an existing pull request. Each call creates a permanent, visible review on the PR \u2014 NEVER submit test or diagnostic reviews. Reviews with no body AND no comments are silently skipped (nothing to post). IMPORTANT: 95%+ of feedback should be in 'comments' array with file paths and line numbers. Only use 'body' for a 1-2 sentence summary with urgency and critical callouts. Use 'suggestion' to propose replacement code - MUST preserve exact indentation of original code. Example replacing lines 42-44 (3 lines) with 5 lines: { path: 'src/api.ts', start_line: 42, line: 44, suggestion: ' const result = await fetch(url);\\n if (!result.ok) {\\n log.error(result.status);\\n throw new Error("request failed");\\n }' } CONSTRAINT: Inline comments can ONLY target files and lines that appear in the PR diff. If GitHub rejects comments due to incorrect line numbers, re-read the diff and retry.`,
|
|
143761
|
-
parameters: CreatePullRequestReview,
|
|
143762
|
-
execute: execute(async ({ pull_number, body, approved, commit_id, comments = [] }) => {
|
|
143763
|
-
if (body) body = fixDoubleEscapedString(body);
|
|
143764
|
-
if (body && ctx.toolState.selectedMode === "Review" && ctx.toolState.todoTracker) {
|
|
143765
|
-
ctx.toolState.todoTracker.cancel();
|
|
143766
|
-
await ctx.toolState.todoTracker.settled();
|
|
143767
|
-
ctx.toolState.todoTracker.completeInProgress();
|
|
143768
|
-
const collapsible = ctx.toolState.todoTracker.renderCollapsible();
|
|
143769
|
-
if (collapsible) {
|
|
143770
|
-
body = `${body}
|
|
143771
|
-
|
|
143772
|
-
${collapsible}`;
|
|
143773
|
-
}
|
|
143774
|
-
}
|
|
143775
|
-
ctx.toolState.issueNumber = pull_number;
|
|
143776
|
-
if (!approved && !body && comments.length === 0) {
|
|
143777
|
-
log.info(
|
|
143778
|
-
"review has no body and no inline comments \u2014 skipping submission (no issues found)"
|
|
143779
|
-
);
|
|
143780
|
-
return {
|
|
143781
|
-
success: true,
|
|
143782
|
-
skipped: true,
|
|
143783
|
-
reason: "no issues found \u2014 nothing to post"
|
|
143784
|
-
};
|
|
143785
|
-
}
|
|
143786
|
-
let event = approved ? "APPROVE" : "COMMENT";
|
|
143787
|
-
if (event === "APPROVE" && !ctx.prApproveEnabled) {
|
|
143788
|
-
log.info("prApproveEnabled is disabled \u2014 downgrading APPROVE to COMMENT");
|
|
143789
|
-
event = "COMMENT";
|
|
143790
|
-
}
|
|
143791
|
-
const params = {
|
|
143792
|
-
owner: ctx.repo.owner,
|
|
143793
|
-
repo: ctx.repo.name,
|
|
143794
|
-
pull_number,
|
|
143795
|
-
event
|
|
143796
|
-
};
|
|
143797
|
-
let latestHeadSha;
|
|
143798
|
-
if (commit_id) {
|
|
143799
|
-
params.commit_id = commit_id;
|
|
143800
|
-
} else {
|
|
143801
|
-
const pr = await ctx.octokit.rest.pulls.get({
|
|
143802
|
-
owner: ctx.repo.owner,
|
|
143803
|
-
repo: ctx.repo.name,
|
|
143804
|
-
pull_number
|
|
143805
|
-
});
|
|
143806
|
-
latestHeadSha = pr.data.head.sha;
|
|
143807
|
-
params.commit_id = ctx.toolState.checkoutSha ?? latestHeadSha;
|
|
143808
|
-
if (ctx.toolState.checkoutSha && latestHeadSha !== ctx.toolState.checkoutSha) {
|
|
143809
|
-
log.info(
|
|
143810
|
-
`anchoring review to checkout ${ctx.toolState.checkoutSha.slice(0, 7)} (HEAD is now ${latestHeadSha.slice(0, 7)})`
|
|
143811
|
-
);
|
|
143812
|
-
}
|
|
143813
|
-
}
|
|
143814
|
-
const reviewComments = comments.map((comment) => {
|
|
143815
|
-
let commentBody = fixDoubleEscapedString(comment.body || "");
|
|
143816
|
-
if (comment.suggestion !== void 0) {
|
|
143817
|
-
const suggestionBlock = "```suggestion\n" + comment.suggestion + "\n```";
|
|
143818
|
-
commentBody = commentBody ? commentBody + "\n\n" + suggestionBlock : suggestionBlock;
|
|
143819
|
-
}
|
|
143820
|
-
const side = comment.side || "RIGHT";
|
|
143821
|
-
const reviewComment = {
|
|
143822
|
-
path: comment.path,
|
|
143823
|
-
line: comment.line,
|
|
143824
|
-
body: commentBody,
|
|
143825
|
-
side
|
|
143826
|
-
};
|
|
143827
|
-
if (comment.start_line != null && comment.start_line !== comment.line) {
|
|
143828
|
-
reviewComment.start_line = comment.start_line;
|
|
143829
|
-
reviewComment.start_side = side;
|
|
143830
|
-
}
|
|
143831
|
-
return reviewComment;
|
|
143832
|
-
});
|
|
143833
|
-
if (reviewComments.length > 0) {
|
|
143834
|
-
params.comments = reviewComments;
|
|
143835
|
-
}
|
|
143836
|
-
let result;
|
|
143837
|
-
try {
|
|
143838
|
-
result = body ? await createAndSubmitWithFooter(ctx, params, {
|
|
143839
|
-
body,
|
|
143840
|
-
approved: approved ?? false,
|
|
143841
|
-
hasComments: reviewComments.length > 0
|
|
143842
|
-
}) : await ctx.octokit.rest.pulls.createReview(params);
|
|
143843
|
-
} catch (err) {
|
|
143844
|
-
if (getHttpStatus(err) !== 422 || !params.comments?.length) throw err;
|
|
143845
|
-
const details = params.comments.map((c2) => {
|
|
143846
|
-
const line = c2.line ?? 0;
|
|
143847
|
-
const startLine = c2.start_line ?? line;
|
|
143848
|
-
const range2 = startLine !== line ? `${startLine}-${line}` : `${line}`;
|
|
143849
|
-
return `${c2.path}:${range2} (${c2.side ?? "RIGHT"})`;
|
|
143850
|
-
});
|
|
143851
|
-
throw new Error(
|
|
143852
|
-
`GitHub rejected inline comment(s) with "Line could not be resolved". This usually means the diff changed since you last read it (new commits pushed). Re-read the diff to get current line numbers, or move failing comments to the review body. Affected: ${details.join(", ")}`
|
|
143853
|
-
);
|
|
143854
|
-
}
|
|
143855
|
-
log.debug(`createReview response: ${JSON.stringify(result.data)}`);
|
|
143856
|
-
if (!result.data.id) {
|
|
143857
|
-
throw new Error(`createReview returned invalid data: ${JSON.stringify(result.data)}`);
|
|
143858
|
-
}
|
|
143859
|
-
const reviewId = result.data.id;
|
|
143860
|
-
const reviewNodeId = result.data.node_id;
|
|
143861
|
-
const actuallyReviewedSha = ctx.toolState.checkoutSha ?? params.commit_id;
|
|
143862
|
-
ctx.toolState.review = {
|
|
143863
|
-
id: reviewId,
|
|
143864
|
-
nodeId: reviewNodeId,
|
|
143865
|
-
reviewedSha: actuallyReviewedSha
|
|
143866
|
-
};
|
|
143867
|
-
if (ctx.toolState.checkoutSha && latestHeadSha && latestHeadSha !== ctx.toolState.checkoutSha) {
|
|
143868
|
-
const fromSha = ctx.toolState.checkoutSha;
|
|
143869
|
-
const toSha = latestHeadSha;
|
|
143870
|
-
ctx.toolState.beforeSha = fromSha;
|
|
143871
|
-
ctx.toolState.checkoutSha = toSha;
|
|
143872
|
-
log.info(
|
|
143873
|
-
`new commits detected during review: ${fromSha.slice(0, 7)}..${toSha.slice(0, 7)}`
|
|
143874
|
-
);
|
|
143875
|
-
return {
|
|
143876
|
-
success: true,
|
|
143877
|
-
reviewId,
|
|
143878
|
-
html_url: result.data.html_url,
|
|
143879
|
-
state: result.data.state,
|
|
143880
|
-
user: result.data.user?.login,
|
|
143881
|
-
submitted_at: result.data.submitted_at,
|
|
143882
|
-
newCommits: {
|
|
143883
|
-
from: fromSha,
|
|
143884
|
-
to: toSha,
|
|
143885
|
-
instructions: `new commits were pushed while you were reviewing. call \`${formatMcpToolRef(ctx.agentId, "checkout_pr")}\` again to fetch the latest version \u2014 it will compute the incremental diff automatically. submit another review covering only the new changes. do not repeat feedback from your previous review.`
|
|
143886
|
-
}
|
|
143887
|
-
};
|
|
143888
|
-
}
|
|
143889
|
-
return {
|
|
143890
|
-
success: true,
|
|
143891
|
-
reviewId,
|
|
143892
|
-
html_url: result.data.html_url,
|
|
143893
|
-
state: result.data.state,
|
|
143894
|
-
user: result.data.user?.login,
|
|
143895
|
-
submitted_at: result.data.submitted_at
|
|
143896
|
-
};
|
|
143897
|
-
})
|
|
143898
|
-
});
|
|
143899
|
-
}
|
|
143900
|
-
async function createAndSubmitWithFooter(ctx, params, opts) {
|
|
143901
|
-
const { event: _2, ...pendingParams } = params;
|
|
143902
|
-
const pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
|
|
143903
|
-
if (!pending.data.id) {
|
|
143904
|
-
throw new Error(`createReview returned invalid data: ${JSON.stringify(pending.data)}`);
|
|
143905
|
-
}
|
|
143906
|
-
const customParts = [];
|
|
143907
|
-
if (!opts.approved) {
|
|
143908
|
-
const apiUrl = getApiUrl();
|
|
143909
|
-
if (opts.hasComments) {
|
|
143910
|
-
const fixAllUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
|
|
143911
|
-
const fixApprovedUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix-approved&review_id=${pending.data.id}`;
|
|
143912
|
-
customParts.push(`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`);
|
|
143913
|
-
} else {
|
|
143914
|
-
const fixUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
|
|
143915
|
-
customParts.push(`[Fix it \u2794](${fixUrl})`);
|
|
143916
|
-
}
|
|
143917
|
-
}
|
|
143918
|
-
const footer = buildPullfrogFooter({
|
|
143919
|
-
workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
|
|
143920
|
-
customParts,
|
|
143921
|
-
model: ctx.toolState.model
|
|
143922
|
-
});
|
|
143923
|
-
return ctx.octokit.rest.pulls.submitReview({
|
|
143924
|
-
owner: params.owner,
|
|
143925
|
-
repo: params.repo,
|
|
143926
|
-
pull_number: params.pull_number,
|
|
143927
|
-
review_id: pending.data.id,
|
|
143928
|
-
event: params.event,
|
|
143929
|
-
body: opts.body + footer
|
|
143930
|
-
});
|
|
143931
|
-
}
|
|
143932
|
-
async function reportReviewNodeId(ctx, params) {
|
|
143933
|
-
await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
|
|
143934
|
-
}
|
|
143935
|
-
|
|
143936
144991
|
// mcp/reviewComments.ts
|
|
143937
144992
|
import { writeFileSync as writeFileSync4 } from "node:fs";
|
|
143938
144993
|
import { join as join6 } from "node:path";
|
|
@@ -143980,7 +145035,7 @@ query ($owner: String!, $name: String!, $prNumber: Int!) {
|
|
|
143980
145035
|
}
|
|
143981
145036
|
}
|
|
143982
145037
|
`;
|
|
143983
|
-
function
|
|
145038
|
+
function countLines2(str) {
|
|
143984
145039
|
let count = 1;
|
|
143985
145040
|
let index = -1;
|
|
143986
145041
|
while ((index = str.indexOf("\n", index + 1)) !== -1) {
|
|
@@ -144122,13 +145177,13 @@ function formatReviewThreads(threadBlocks, header) {
|
|
|
144122
145177
|
const reviewBodyLines = [];
|
|
144123
145178
|
if (header.reviewBody) {
|
|
144124
145179
|
reviewBodyLines.push("## Review Body", "", header.reviewBody, "");
|
|
144125
|
-
currentLine += reviewBodyLines.reduce((sum, line) => sum +
|
|
145180
|
+
currentLine += reviewBodyLines.reduce((sum, line) => sum + countLines2(line), 0);
|
|
144126
145181
|
}
|
|
144127
145182
|
const tocEntries = [];
|
|
144128
145183
|
const threadLines = [];
|
|
144129
145184
|
for (const block of threadBlocks) {
|
|
144130
145185
|
const startLine = currentLine;
|
|
144131
|
-
const actualLineCount = block.content.reduce((sum, line) => sum +
|
|
145186
|
+
const actualLineCount = block.content.reduce((sum, line) => sum + countLines2(line), 0);
|
|
144132
145187
|
const endLine = currentLine + actualLineCount - 1;
|
|
144133
145188
|
tocEntries.push(`- ${block.path}:${block.lineRange} \u2192 lines ${startLine}-${endLine}`);
|
|
144134
145189
|
threadLines.push(...block.content);
|
|
@@ -144451,7 +145506,7 @@ GitHub's markdown parser requires a blank line between ALL block-level elements.
|
|
|
144451
145506
|
Rules:
|
|
144452
145507
|
- \`##\` titles and key-change bullet lead-ins are plain-language summaries; backtick only actual code tokens (files, types, functions) where they appear in the title
|
|
144453
145508
|
- ALL variable names, identifiers, and file names in body text must be in backticks
|
|
144454
|
-
- ALL file references MUST link to the PR Files Changed view.
|
|
145509
|
+
- ALL file references MUST link to the PR Files Changed view. Use the \`diff-<hex>\` anchor precomputed next to each filename in the \`checkout_pr\` TOC \u2014 do NOT run \`sha256sum\` or any other shell command to compute anchors. NEVER fabricate hex strings. If a file is not in the TOC, omit the \`#diff-\` anchor rather than guessing.
|
|
144455
145510
|
- Add <br/> before each ## heading for visual spacing. Do NOT use horizontal rules (---)
|
|
144456
145511
|
- Do NOT include raw diff stats like '+123 / -45' or line counts
|
|
144457
145512
|
- Do NOT include code blocks or repeat diff contents
|
|
@@ -144526,7 +145581,7 @@ ${learningsStep(t2, 6)}`
|
|
|
144526
145581
|
description: "Review code, PRs, or implementations; provide feedback or suggestions; identify issues; or check code quality, style, and correctness",
|
|
144527
145582
|
prompt: `### Checklist
|
|
144528
145583
|
|
|
144529
|
-
1. Checkout the PR via \`${t2("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`.
|
|
145584
|
+
1. Checkout the PR via \`${t2("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`. read the diff TOC first and treat its file line ranges as your coverage checklist.
|
|
144530
145585
|
|
|
144531
145586
|
2. For each area of change:
|
|
144532
145587
|
- read the diff and trace data flow, check boundaries, and verify assumptions
|
|
@@ -144543,6 +145598,7 @@ ${learningsStep(t2, 6)}`
|
|
|
144543
145598
|
4. Submit \u2014 ALWAYS submit exactly one review via \`${t2("create_pull_request_review")}\`.
|
|
144544
145599
|
Do NOT call \`report_progress\` \u2014 the review is the final record and the progress
|
|
144545
145600
|
comment will be cleaned up automatically.
|
|
145601
|
+
note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session.
|
|
144546
145602
|
|
|
144547
145603
|
- **critical issues** (blocks merge \u2014 bugs, security, data loss):
|
|
144548
145604
|
\`approved: false\`. Body begins with a GitHub alert blockquote, e.g.:
|
|
@@ -144560,7 +145616,7 @@ ${learningsStep(t2, 6)}`
|
|
|
144560
145616
|
description: "Re-review a PR after new commits are pushed; focus on new changes since the last review",
|
|
144561
145617
|
prompt: `### Checklist
|
|
144562
145618
|
|
|
144563
|
-
1. Checkout the PR via \`${t2("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), and \`incrementalDiffPath\` (changes since last reviewed version, if available).
|
|
145619
|
+
1. Checkout the PR via \`${t2("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), and \`incrementalDiffPath\` (changes since last reviewed version, if available). read the diff TOC first and use its line ranges as your coverage checklist.
|
|
144564
145620
|
|
|
144565
145621
|
2. If \`incrementalDiffPath\` is present, read it to see what changed since the last review. This is a range-diff that isolates the net changes, filtering out base branch noise. If not present, fall back to reviewing the full PR diff.
|
|
144566
145622
|
|
|
@@ -144584,6 +145640,7 @@ ${learningsStep(t2, 6)}`
|
|
|
144584
145640
|
- in some cases you may receive a complete diff for the whole pull request instead of an incremental one. when this happens, you will need to determine what changes have happened since Pullfrog's most recent review.
|
|
144585
145641
|
|
|
144586
145642
|
7. Submit \u2014 Do NOT call \`report_progress\` or \`create_issue_comment\` \u2014 the review is the final record and the progress comment will be cleaned up automatically. the review body always includes the reviewed changes from step 6a. append \`Prior review feedback:\\n\` with the checklist from step 6b only if any prior comments were addressed. Follow these rules:
|
|
145643
|
+
- note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session.
|
|
144587
145644
|
- IF NO NEW ISSUES, NON-SUBSTANTIVE CHANGES ONLY (trivial formatting, import reordering, comment tweaks): do NOT submit a review. Do NOT call \`report_progress\`. Exit \u2014 the progress comment will be cleaned up automatically.
|
|
144588
145645
|
- ELSE IF NEW CRITICAL ISSUES (blocks merge): call \`${t2("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with a GitHub alert blockquote (e.g. \`> [!CAUTION]\\n> This PR introduces ...\`), then the reviewed changes summary and prior feedback (if any).
|
|
144589
145646
|
- ELSE IF NEW RECOMMENDED CHANGES (non-critical): call \`${t2("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with \`> [!IMPORTANT]\\n> ...\` alert, then the reviewed changes summary and prior feedback (if any).
|
|
@@ -144688,7 +145745,7 @@ ${PR_SUMMARY_FORMAT}`
|
|
|
144688
145745
|
}
|
|
144689
145746
|
];
|
|
144690
145747
|
}
|
|
144691
|
-
var modes = computeModes("
|
|
145748
|
+
var modes = computeModes("opencode");
|
|
144692
145749
|
|
|
144693
145750
|
// mcp/selectMode.ts
|
|
144694
145751
|
var SelectModeParams = type({
|
|
@@ -144891,13 +145948,19 @@ function detectSandboxMethod() {
|
|
|
144891
145948
|
} catch {
|
|
144892
145949
|
}
|
|
144893
145950
|
detectedSandboxMethod = "none";
|
|
144894
|
-
log.info("PID namespace isolation not available
|
|
145951
|
+
log.info("PID namespace isolation not available");
|
|
144895
145952
|
return "none";
|
|
144896
145953
|
}
|
|
144897
145954
|
var PROC_CLEANUP = "umount /proc 2>/dev/null; umount /proc 2>/dev/null; mount -t proc proc /proc 2>/dev/null;";
|
|
144898
145955
|
function spawnShell(params) {
|
|
144899
145956
|
const spawnOpts = { env: params.env, cwd: params.cwd, stdio: params.stdio, detached: true };
|
|
144900
145957
|
const sandboxMethod = detectSandboxMethod();
|
|
145958
|
+
const ci = process.env.CI === "true";
|
|
145959
|
+
if (ci && sandboxMethod === "none") {
|
|
145960
|
+
throw new Error(
|
|
145961
|
+
"pid namespace isolation is required in CI but unavailable (both unshare and sudo unshare failed)"
|
|
145962
|
+
);
|
|
145963
|
+
}
|
|
144901
145964
|
if (sandboxMethod === "unshare") {
|
|
144902
145965
|
return spawn2(
|
|
144903
145966
|
"unshare",
|
|
@@ -145045,11 +146108,11 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
|
|
|
145045
146108
|
await killProcessGroup(proc);
|
|
145046
146109
|
}
|
|
145047
146110
|
}, timeout);
|
|
145048
|
-
const exitCode = await new Promise((
|
|
146111
|
+
const exitCode = await new Promise((resolve3) => {
|
|
145049
146112
|
const done = (code) => {
|
|
145050
146113
|
exited = true;
|
|
145051
146114
|
clearTimeout(timeoutId);
|
|
145052
|
-
|
|
146115
|
+
resolve3(code);
|
|
145053
146116
|
};
|
|
145054
146117
|
proc.on("exit", done);
|
|
145055
146118
|
proc.on("error", () => done(null));
|
|
@@ -145188,12 +146251,12 @@ function readEnvPort() {
|
|
|
145188
146251
|
return parsed2;
|
|
145189
146252
|
}
|
|
145190
146253
|
function isPortAvailable(port) {
|
|
145191
|
-
return new Promise((
|
|
146254
|
+
return new Promise((resolve3) => {
|
|
145192
146255
|
const server = createServer();
|
|
145193
146256
|
server.unref();
|
|
145194
|
-
server.once("error", () =>
|
|
146257
|
+
server.once("error", () => resolve3(false));
|
|
145195
146258
|
server.once("listening", () => {
|
|
145196
|
-
server.close(() =>
|
|
146259
|
+
server.close(() => resolve3(true));
|
|
145197
146260
|
});
|
|
145198
146261
|
server.listen(port, mcpHost);
|
|
145199
146262
|
});
|
|
@@ -145333,9 +146396,12 @@ async function killBackgroundProcesses(toolState) {
|
|
|
145333
146396
|
async function startMcpHttpServer(ctx, options) {
|
|
145334
146397
|
const tools = buildOrchestratorTools(ctx, options?.outputSchema);
|
|
145335
146398
|
const startResult = await selectMcpPort(ctx, tools);
|
|
146399
|
+
let disposed = false;
|
|
145336
146400
|
return {
|
|
145337
146401
|
url: startResult.url,
|
|
145338
146402
|
[Symbol.asyncDispose]: async () => {
|
|
146403
|
+
if (disposed) return;
|
|
146404
|
+
disposed = true;
|
|
145339
146405
|
closeBrowserDaemon(ctx.toolState);
|
|
145340
146406
|
await killBackgroundProcesses(ctx.toolState);
|
|
145341
146407
|
await startResult.server.stop();
|
|
@@ -145530,39 +146596,6 @@ var ThinkingTimer = class {
|
|
|
145530
146596
|
}
|
|
145531
146597
|
};
|
|
145532
146598
|
|
|
145533
|
-
// agents/shared.ts
|
|
145534
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
145535
|
-
var MAX_STDERR_LINES = 20;
|
|
145536
|
-
var MAX_COMMIT_RETRIES = 3;
|
|
145537
|
-
function getGitStatus() {
|
|
145538
|
-
try {
|
|
145539
|
-
return execFileSync2("git", ["status", "--porcelain"], {
|
|
145540
|
-
encoding: "utf-8",
|
|
145541
|
-
timeout: 1e4
|
|
145542
|
-
}).trim();
|
|
145543
|
-
} catch {
|
|
145544
|
-
return "";
|
|
145545
|
-
}
|
|
145546
|
-
}
|
|
145547
|
-
function buildCommitPrompt(_agentId, status) {
|
|
145548
|
-
return [
|
|
145549
|
-
`UNCOMMITTED CHANGES \u2014 the working tree is dirty. push all changes to a pull request (new or existing). \`git status\` must be clean before you finish.`,
|
|
145550
|
-
"",
|
|
145551
|
-
"```",
|
|
145552
|
-
status,
|
|
145553
|
-
"```"
|
|
145554
|
-
].join("\n");
|
|
145555
|
-
}
|
|
145556
|
-
var agent = (input) => {
|
|
145557
|
-
return {
|
|
145558
|
-
...input,
|
|
145559
|
-
run: async (ctx) => {
|
|
145560
|
-
log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`);
|
|
145561
|
-
return input.run(ctx);
|
|
145562
|
-
}
|
|
145563
|
-
};
|
|
145564
|
-
};
|
|
145565
|
-
|
|
145566
146599
|
// agents/claude.ts
|
|
145567
146600
|
async function installClaudeCli() {
|
|
145568
146601
|
return await installFromNpmTarball({
|
|
@@ -145601,6 +146634,7 @@ async function runClaude(params) {
|
|
|
145601
146634
|
let finalOutput = "";
|
|
145602
146635
|
let sessionId;
|
|
145603
146636
|
let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
146637
|
+
let accumulatedCostUsd = 0;
|
|
145604
146638
|
let tokensLogged = false;
|
|
145605
146639
|
function buildUsage() {
|
|
145606
146640
|
const totalInput = accumulatedTokens.input + accumulatedTokens.cacheRead + accumulatedTokens.cacheWrite;
|
|
@@ -145609,7 +146643,8 @@ async function runClaude(params) {
|
|
|
145609
146643
|
inputTokens: totalInput,
|
|
145610
146644
|
outputTokens: accumulatedTokens.output,
|
|
145611
146645
|
cacheReadTokens: accumulatedTokens.cacheRead || void 0,
|
|
145612
|
-
cacheWriteTokens: accumulatedTokens.cacheWrite || void 0
|
|
146646
|
+
cacheWriteTokens: accumulatedTokens.cacheWrite || void 0,
|
|
146647
|
+
costUsd: accumulatedCostUsd > 0 ? accumulatedCostUsd : void 0
|
|
145613
146648
|
} : void 0;
|
|
145614
146649
|
}
|
|
145615
146650
|
const handlers2 = {
|
|
@@ -145626,6 +146661,12 @@ async function runClaude(params) {
|
|
|
145626
146661
|
finalOutput = message;
|
|
145627
146662
|
} else if (block.type === "tool_use") {
|
|
145628
146663
|
const toolName = block.name || "unknown";
|
|
146664
|
+
if (params.onToolUse) {
|
|
146665
|
+
params.onToolUse({
|
|
146666
|
+
toolName,
|
|
146667
|
+
input: block.input
|
|
146668
|
+
});
|
|
146669
|
+
}
|
|
145629
146670
|
thinkingTimer.markToolCall();
|
|
145630
146671
|
log.toolCall({ toolName, input: block.input || {} });
|
|
145631
146672
|
if (toolName.includes("report_progress") && params.todoTracker) {
|
|
@@ -145641,6 +146682,8 @@ async function runClaude(params) {
|
|
|
145641
146682
|
if (msgUsage) {
|
|
145642
146683
|
accumulatedTokens.input += msgUsage.input_tokens || 0;
|
|
145643
146684
|
accumulatedTokens.output += msgUsage.output_tokens || 0;
|
|
146685
|
+
accumulatedTokens.cacheRead += msgUsage.cache_read_input_tokens || 0;
|
|
146686
|
+
accumulatedTokens.cacheWrite += msgUsage.cache_creation_input_tokens || 0;
|
|
145644
146687
|
}
|
|
145645
146688
|
},
|
|
145646
146689
|
user: (event) => {
|
|
@@ -145671,19 +146714,18 @@ async function runClaude(params) {
|
|
|
145671
146714
|
const cacheRead = usage?.cache_read_input_tokens || 0;
|
|
145672
146715
|
const cacheWrite = usage?.cache_creation_input_tokens || 0;
|
|
145673
146716
|
const outputTokens = usage?.output_tokens || 0;
|
|
145674
|
-
const
|
|
146717
|
+
const costUsd = typeof event.total_cost_usd === "number" && Number.isFinite(event.total_cost_usd) ? event.total_cost_usd : 0;
|
|
145675
146718
|
accumulatedTokens = { input: inputTokens, output: outputTokens, cacheRead, cacheWrite };
|
|
146719
|
+
accumulatedCostUsd = costUsd;
|
|
145676
146720
|
log.info(`\xBB ${params.label} result: subtype=${subtype}, turns=${numTurns}`);
|
|
145677
146721
|
if (!tokensLogged) {
|
|
145678
|
-
|
|
145679
|
-
|
|
145680
|
-
|
|
145681
|
-
|
|
145682
|
-
|
|
145683
|
-
|
|
145684
|
-
|
|
145685
|
-
[String(totalInput), String(cacheRead), String(cacheWrite), String(outputTokens)]
|
|
145686
|
-
]);
|
|
146722
|
+
logTokenTable({
|
|
146723
|
+
input: inputTokens,
|
|
146724
|
+
cacheRead,
|
|
146725
|
+
cacheWrite,
|
|
146726
|
+
output: outputTokens,
|
|
146727
|
+
costUsd
|
|
146728
|
+
});
|
|
145687
146729
|
tokensLogged = true;
|
|
145688
146730
|
}
|
|
145689
146731
|
} else if (subtype === "error_max_turns") {
|
|
@@ -145718,6 +146760,7 @@ async function runClaude(params) {
|
|
|
145718
146760
|
cwd: params.cwd,
|
|
145719
146761
|
env: params.env,
|
|
145720
146762
|
activityTimeout: 3e5,
|
|
146763
|
+
onActivityTimeout: params.onActivityTimeout,
|
|
145721
146764
|
stdio: ["ignore", "pipe", "pipe"],
|
|
145722
146765
|
onStdout: async (chunk) => {
|
|
145723
146766
|
const text = chunk.toString();
|
|
@@ -145729,25 +146772,33 @@ async function runClaude(params) {
|
|
|
145729
146772
|
for (const line of lines) {
|
|
145730
146773
|
const trimmed = line.trim();
|
|
145731
146774
|
if (!trimmed) continue;
|
|
146775
|
+
let event;
|
|
145732
146776
|
try {
|
|
145733
|
-
|
|
145734
|
-
eventCount++;
|
|
145735
|
-
log.debug(JSON.stringify(event, null, 2));
|
|
145736
|
-
const timeSinceLastActivity = getIdleMs();
|
|
145737
|
-
if (timeSinceLastActivity > 1e4) {
|
|
145738
|
-
log.info(
|
|
145739
|
-
`\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s (${params.label} may be processing internally) (${eventCount} events processed so far)`
|
|
145740
|
-
);
|
|
145741
|
-
}
|
|
145742
|
-
markActivity();
|
|
145743
|
-
const handler2 = handlers2[event.type];
|
|
145744
|
-
if (handler2) {
|
|
145745
|
-
handler2(event);
|
|
145746
|
-
} else {
|
|
145747
|
-
log.debug(`\xBB ${params.label} event (unhandled): type=${event.type}`);
|
|
145748
|
-
}
|
|
146777
|
+
event = JSON.parse(trimmed);
|
|
145749
146778
|
} catch {
|
|
145750
146779
|
log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`);
|
|
146780
|
+
continue;
|
|
146781
|
+
}
|
|
146782
|
+
eventCount++;
|
|
146783
|
+
log.debug(JSON.stringify(event, null, 2));
|
|
146784
|
+
const timeSinceLastActivity = getIdleMs();
|
|
146785
|
+
if (timeSinceLastActivity > 1e4) {
|
|
146786
|
+
log.info(
|
|
146787
|
+
`\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s (${params.label} may be processing internally) (${eventCount} events processed so far)`
|
|
146788
|
+
);
|
|
146789
|
+
}
|
|
146790
|
+
markActivity();
|
|
146791
|
+
const handler2 = handlers2[event.type];
|
|
146792
|
+
if (!handler2) {
|
|
146793
|
+
log.debug(`\xBB ${params.label} event (unhandled): type=${event.type}`);
|
|
146794
|
+
continue;
|
|
146795
|
+
}
|
|
146796
|
+
try {
|
|
146797
|
+
handler2(event);
|
|
146798
|
+
} catch (err) {
|
|
146799
|
+
log.info(
|
|
146800
|
+
`\xBB ${params.label} handler for type=${event.type} threw: ${err instanceof Error ? err.message : String(err)}`
|
|
146801
|
+
);
|
|
145751
146802
|
}
|
|
145752
146803
|
}
|
|
145753
146804
|
},
|
|
@@ -145781,16 +146832,9 @@ async function runClaude(params) {
|
|
|
145781
146832
|
if (stderrContext) log.info(`\xBB last stderr output:
|
|
145782
146833
|
${stderrContext}`);
|
|
145783
146834
|
}
|
|
145784
|
-
if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0)) {
|
|
145785
|
-
|
|
145786
|
-
|
|
145787
|
-
[
|
|
145788
|
-
{ data: "Input Tokens", header: true },
|
|
145789
|
-
{ data: "Output Tokens", header: true },
|
|
145790
|
-
{ data: "Total Tokens", header: true }
|
|
145791
|
-
],
|
|
145792
|
-
[String(accumulatedTokens.input), String(accumulatedTokens.output), String(totalTokens)]
|
|
145793
|
-
]);
|
|
146835
|
+
if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
|
|
146836
|
+
logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
|
|
146837
|
+
tokensLogged = true;
|
|
145794
146838
|
}
|
|
145795
146839
|
const usage = buildUsage();
|
|
145796
146840
|
if (result.exitCode !== 0) {
|
|
@@ -145823,7 +146867,7 @@ ${stderrContext}`);
|
|
|
145823
146867
|
params.todoTracker?.cancel();
|
|
145824
146868
|
const duration4 = performance6.now() - startTime;
|
|
145825
146869
|
const errorMessage = error49 instanceof Error ? error49.message : String(error49);
|
|
145826
|
-
const isActivityTimeout =
|
|
146870
|
+
const isActivityTimeout = error49 instanceof SpawnTimeoutError && error49.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
|
|
145827
146871
|
const stderrContext = recentStderr.slice(-10).join("\n");
|
|
145828
146872
|
const diagnosis = lastProviderError ? `likely cause: ${lastProviderError}` : eventCount === 0 ? "Claude produced 0 stdout events - check if the API is reachable" : `${eventCount} events were processed before the hang`;
|
|
145829
146873
|
log.info(
|
|
@@ -145914,8 +146958,7 @@ var claude = agent({
|
|
|
145914
146958
|
"--effort",
|
|
145915
146959
|
effort,
|
|
145916
146960
|
"--disallowedTools",
|
|
145917
|
-
"Bash"
|
|
145918
|
-
"Agent(Bash)"
|
|
146961
|
+
"Bash,Agent(Bash)"
|
|
145919
146962
|
];
|
|
145920
146963
|
if (model) {
|
|
145921
146964
|
baseArgs.push("--model", model);
|
|
@@ -145928,11 +146971,19 @@ var claude = agent({
|
|
|
145928
146971
|
log.info(`\xBB effort: ${effort}`);
|
|
145929
146972
|
log.debug(`\xBB starting Pullfrog (Claude Code): node ${baseArgs.join(" ")}`);
|
|
145930
146973
|
log.debug(`\xBB working directory: ${repoDir}`);
|
|
145931
|
-
const runParams = {
|
|
146974
|
+
const runParams = {
|
|
146975
|
+
label: "Pullfrog",
|
|
146976
|
+
cwd: repoDir,
|
|
146977
|
+
env: env2,
|
|
146978
|
+
todoTracker: ctx.todoTracker,
|
|
146979
|
+
onActivityTimeout: ctx.onActivityTimeout,
|
|
146980
|
+
onToolUse: ctx.onToolUse
|
|
146981
|
+
};
|
|
145932
146982
|
let result = await runClaude({
|
|
145933
146983
|
...runParams,
|
|
145934
146984
|
args: [...baseArgs, "-p", ctx.instructions.full]
|
|
145935
146985
|
});
|
|
146986
|
+
let aggregatedUsage = result.usage;
|
|
145936
146987
|
for (let attempt = 0; attempt < MAX_COMMIT_RETRIES; attempt++) {
|
|
145937
146988
|
if (!result.success || !result.sessionId) break;
|
|
145938
146989
|
const status = getGitStatus();
|
|
@@ -145949,12 +147000,13 @@ ${status}`);
|
|
|
145949
147000
|
result.sessionId
|
|
145950
147001
|
]
|
|
145951
147002
|
});
|
|
147003
|
+
aggregatedUsage = mergeAgentUsage(aggregatedUsage, result.usage);
|
|
145952
147004
|
}
|
|
145953
|
-
return result;
|
|
147005
|
+
return { ...result, usage: aggregatedUsage };
|
|
145954
147006
|
}
|
|
145955
147007
|
});
|
|
145956
147008
|
|
|
145957
|
-
// agents/
|
|
147009
|
+
// agents/opencode.ts
|
|
145958
147010
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
145959
147011
|
import { mkdirSync as mkdirSync4 } from "node:fs";
|
|
145960
147012
|
import { join as join10 } from "node:path";
|
|
@@ -146032,6 +147084,7 @@ async function runOpenCode(params) {
|
|
|
146032
147084
|
const thinkingTimer = new ThinkingTimer();
|
|
146033
147085
|
let finalOutput = "";
|
|
146034
147086
|
let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
147087
|
+
let accumulatedCostUsd = 0;
|
|
146035
147088
|
let tokensLogged = false;
|
|
146036
147089
|
const toolCallTimings = /* @__PURE__ */ new Map();
|
|
146037
147090
|
let currentStepId = null;
|
|
@@ -146044,7 +147097,8 @@ async function runOpenCode(params) {
|
|
|
146044
147097
|
inputTokens: totalInput,
|
|
146045
147098
|
outputTokens: accumulatedTokens.output,
|
|
146046
147099
|
cacheReadTokens: accumulatedTokens.cacheRead || void 0,
|
|
146047
|
-
cacheWriteTokens: accumulatedTokens.cacheWrite || void 0
|
|
147100
|
+
cacheWriteTokens: accumulatedTokens.cacheWrite || void 0,
|
|
147101
|
+
costUsd: accumulatedCostUsd > 0 ? accumulatedCostUsd : void 0
|
|
146048
147102
|
} : void 0;
|
|
146049
147103
|
}
|
|
146050
147104
|
const handlers2 = {
|
|
@@ -146055,6 +147109,7 @@ async function runOpenCode(params) {
|
|
|
146055
147109
|
log.debug(`\xBB ${params.label} init event (full): ${JSON.stringify(event)}`);
|
|
146056
147110
|
finalOutput = "";
|
|
146057
147111
|
accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
147112
|
+
accumulatedCostUsd = 0;
|
|
146058
147113
|
tokensLogged = false;
|
|
146059
147114
|
},
|
|
146060
147115
|
message: (event) => {
|
|
@@ -146099,6 +147154,9 @@ async function runOpenCode(params) {
|
|
|
146099
147154
|
accumulatedTokens.cacheRead += eventTokens.cache?.read || 0;
|
|
146100
147155
|
accumulatedTokens.cacheWrite += eventTokens.cache?.write || 0;
|
|
146101
147156
|
}
|
|
147157
|
+
if (typeof event.part?.cost === "number" && Number.isFinite(event.part.cost)) {
|
|
147158
|
+
accumulatedCostUsd += event.part.cost;
|
|
147159
|
+
}
|
|
146102
147160
|
if (currentStepId === stepId) {
|
|
146103
147161
|
currentStepId = null;
|
|
146104
147162
|
currentStepType = null;
|
|
@@ -146116,6 +147174,12 @@ async function runOpenCode(params) {
|
|
|
146116
147174
|
if (stepHistory.length > 0) {
|
|
146117
147175
|
stepHistory[stepHistory.length - 1].toolCalls.push(toolName);
|
|
146118
147176
|
}
|
|
147177
|
+
if (params.onToolUse) {
|
|
147178
|
+
params.onToolUse({
|
|
147179
|
+
toolName,
|
|
147180
|
+
input: event.part?.state?.input
|
|
147181
|
+
});
|
|
147182
|
+
}
|
|
146119
147183
|
thinkingTimer.markToolCall();
|
|
146120
147184
|
log.toolCall({ toolName, input: event.part?.state?.input || {} });
|
|
146121
147185
|
if (event.part?.state?.status === "completed" && event.part.state.output) {
|
|
@@ -146171,19 +147235,9 @@ async function runOpenCode(params) {
|
|
|
146171
147235
|
if (event.status === "error") {
|
|
146172
147236
|
log.info(`\xBB ${params.label} failed: ${JSON.stringify(event)}`);
|
|
146173
147237
|
} else {
|
|
146174
|
-
const inputTokens = event.stats?.input_tokens || accumulatedTokens.input || 0;
|
|
146175
|
-
const outputTokens = event.stats?.output_tokens || accumulatedTokens.output || 0;
|
|
146176
|
-
const totalTokens = event.stats?.total_tokens || inputTokens + outputTokens;
|
|
146177
147238
|
log.info(`\xBB run complete: tool_calls=${toolCalls}, duration=${duration4}ms`);
|
|
146178
|
-
if ((
|
|
146179
|
-
|
|
146180
|
-
[
|
|
146181
|
-
{ data: "Input Tokens", header: true },
|
|
146182
|
-
{ data: "Output Tokens", header: true },
|
|
146183
|
-
{ data: "Total Tokens", header: true }
|
|
146184
|
-
],
|
|
146185
|
-
[String(inputTokens), String(outputTokens), String(totalTokens)]
|
|
146186
|
-
]);
|
|
147239
|
+
if ((accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0) && !tokensLogged) {
|
|
147240
|
+
logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
|
|
146187
147241
|
tokensLogged = true;
|
|
146188
147242
|
}
|
|
146189
147243
|
}
|
|
@@ -146200,6 +147254,7 @@ async function runOpenCode(params) {
|
|
|
146200
147254
|
cwd: params.cwd,
|
|
146201
147255
|
env: params.env,
|
|
146202
147256
|
activityTimeout: 3e5,
|
|
147257
|
+
onActivityTimeout: params.onActivityTimeout,
|
|
146203
147258
|
stdio: ["ignore", "pipe", "pipe"],
|
|
146204
147259
|
onStdout: async (chunk) => {
|
|
146205
147260
|
const text = chunk.toString();
|
|
@@ -146211,29 +147266,37 @@ async function runOpenCode(params) {
|
|
|
146211
147266
|
for (const line of lines) {
|
|
146212
147267
|
const trimmed = line.trim();
|
|
146213
147268
|
if (!trimmed) continue;
|
|
147269
|
+
let event;
|
|
146214
147270
|
try {
|
|
146215
|
-
|
|
146216
|
-
eventCount++;
|
|
146217
|
-
log.debug(JSON.stringify(event, null, 2));
|
|
146218
|
-
const timeSinceLastActivity = getIdleMs();
|
|
146219
|
-
if (timeSinceLastActivity > 1e4) {
|
|
146220
|
-
const activeToolCalls = toolCallTimings.size;
|
|
146221
|
-
const toolCallInfo = activeToolCalls > 0 ? ` (waiting for ${activeToolCalls} tool call${activeToolCalls > 1 ? "s" : ""})` : ` (${params.label} may be processing internally - LLM calls, planning, etc.)`;
|
|
146222
|
-
log.info(
|
|
146223
|
-
`\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s${toolCallInfo} (${eventCount} events processed so far)`
|
|
146224
|
-
);
|
|
146225
|
-
}
|
|
146226
|
-
markActivity();
|
|
146227
|
-
const handler2 = handlers2[event.type];
|
|
146228
|
-
if (handler2) {
|
|
146229
|
-
await handler2(event);
|
|
146230
|
-
} else {
|
|
146231
|
-
log.info(
|
|
146232
|
-
`\xBB ${params.label} event (unhandled): type=${event.type}, data=${JSON.stringify(event).substring(0, 500)}`
|
|
146233
|
-
);
|
|
146234
|
-
}
|
|
147271
|
+
event = JSON.parse(trimmed);
|
|
146235
147272
|
} catch {
|
|
146236
147273
|
log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`);
|
|
147274
|
+
continue;
|
|
147275
|
+
}
|
|
147276
|
+
eventCount++;
|
|
147277
|
+
log.debug(JSON.stringify(event, null, 2));
|
|
147278
|
+
const timeSinceLastActivity = getIdleMs();
|
|
147279
|
+
if (timeSinceLastActivity > 1e4) {
|
|
147280
|
+
const activeToolCalls = toolCallTimings.size;
|
|
147281
|
+
const toolCallInfo = activeToolCalls > 0 ? ` (waiting for ${activeToolCalls} tool call${activeToolCalls > 1 ? "s" : ""})` : ` (${params.label} may be processing internally - LLM calls, planning, etc.)`;
|
|
147282
|
+
log.info(
|
|
147283
|
+
`\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s${toolCallInfo} (${eventCount} events processed so far)`
|
|
147284
|
+
);
|
|
147285
|
+
}
|
|
147286
|
+
markActivity();
|
|
147287
|
+
const handler2 = handlers2[event.type];
|
|
147288
|
+
if (!handler2) {
|
|
147289
|
+
log.info(
|
|
147290
|
+
`\xBB ${params.label} event (unhandled): type=${event.type}, data=${JSON.stringify(event).substring(0, 500)}`
|
|
147291
|
+
);
|
|
147292
|
+
continue;
|
|
147293
|
+
}
|
|
147294
|
+
try {
|
|
147295
|
+
await handler2(event);
|
|
147296
|
+
} catch (err) {
|
|
147297
|
+
log.info(
|
|
147298
|
+
`\xBB ${params.label} handler for type=${event.type} threw: ${err instanceof Error ? err.message : String(err)}`
|
|
147299
|
+
);
|
|
146237
147300
|
}
|
|
146238
147301
|
}
|
|
146239
147302
|
},
|
|
@@ -146267,16 +147330,9 @@ async function runOpenCode(params) {
|
|
|
146267
147330
|
if (stderrContext) log.info(`\xBB last stderr output:
|
|
146268
147331
|
${stderrContext}`);
|
|
146269
147332
|
}
|
|
146270
|
-
if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0)) {
|
|
146271
|
-
|
|
146272
|
-
|
|
146273
|
-
[
|
|
146274
|
-
{ data: "Input Tokens", header: true },
|
|
146275
|
-
{ data: "Output Tokens", header: true },
|
|
146276
|
-
{ data: "Total Tokens", header: true }
|
|
146277
|
-
],
|
|
146278
|
-
[String(accumulatedTokens.input), String(accumulatedTokens.output), String(totalTokens)]
|
|
146279
|
-
]);
|
|
147333
|
+
if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
|
|
147334
|
+
logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
|
|
147335
|
+
tokensLogged = true;
|
|
146280
147336
|
}
|
|
146281
147337
|
const usage = buildUsage();
|
|
146282
147338
|
if (result.exitCode !== 0) {
|
|
@@ -146302,7 +147358,7 @@ ${stderrContext}`);
|
|
|
146302
147358
|
params.todoTracker?.cancel();
|
|
146303
147359
|
const duration4 = performance7.now() - startTime;
|
|
146304
147360
|
const errorMessage = error49 instanceof Error ? error49.message : String(error49);
|
|
146305
|
-
const isActivityTimeout =
|
|
147361
|
+
const isActivityTimeout = error49 instanceof SpawnTimeoutError && error49.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
|
|
146306
147362
|
const stderrContext = recentStderr.slice(-10).join("\n");
|
|
146307
147363
|
const diagnosis = lastProviderError ? `likely cause: ${lastProviderError}` : eventCount === 0 ? "OpenCode produced 0 stdout events - check if the model provider is reachable" : `${eventCount} events were processed before the hang`;
|
|
146308
147364
|
log.info(
|
|
@@ -146322,8 +147378,8 @@ ${stderrContext}`
|
|
|
146322
147378
|
};
|
|
146323
147379
|
}
|
|
146324
147380
|
}
|
|
146325
|
-
var
|
|
146326
|
-
name: "
|
|
147381
|
+
var opencode = agent({
|
|
147382
|
+
name: "opencode",
|
|
146327
147383
|
install: installOpencodeCli,
|
|
146328
147384
|
run: async (ctx) => {
|
|
146329
147385
|
const cliPath = await installOpencodeCli();
|
|
@@ -146359,12 +147415,15 @@ var opentoad = agent({
|
|
|
146359
147415
|
cliPath,
|
|
146360
147416
|
cwd: repoDir,
|
|
146361
147417
|
env: env2,
|
|
146362
|
-
todoTracker: ctx.todoTracker
|
|
147418
|
+
todoTracker: ctx.todoTracker,
|
|
147419
|
+
onActivityTimeout: ctx.onActivityTimeout,
|
|
147420
|
+
onToolUse: ctx.onToolUse
|
|
146363
147421
|
};
|
|
146364
147422
|
let result = await runOpenCode({
|
|
146365
147423
|
...runParams,
|
|
146366
147424
|
args: [...baseArgs, ctx.instructions.full]
|
|
146367
147425
|
});
|
|
147426
|
+
let aggregatedUsage = result.usage;
|
|
146368
147427
|
for (let attempt = 0; attempt < MAX_COMMIT_RETRIES; attempt++) {
|
|
146369
147428
|
if (!result.success) break;
|
|
146370
147429
|
const status = getGitStatus();
|
|
@@ -146373,15 +147432,16 @@ var opentoad = agent({
|
|
|
146373
147432
|
${status}`);
|
|
146374
147433
|
result = await runOpenCode({
|
|
146375
147434
|
...runParams,
|
|
146376
|
-
args: [...baseArgs, "--continue", buildCommitPrompt("
|
|
147435
|
+
args: [...baseArgs, "--continue", buildCommitPrompt("opencode", status)]
|
|
146377
147436
|
});
|
|
147437
|
+
aggregatedUsage = mergeAgentUsage(aggregatedUsage, result.usage);
|
|
146378
147438
|
}
|
|
146379
|
-
return result;
|
|
147439
|
+
return { ...result, usage: aggregatedUsage };
|
|
146380
147440
|
}
|
|
146381
147441
|
});
|
|
146382
147442
|
|
|
146383
147443
|
// agents/index.ts
|
|
146384
|
-
var agents = { claude,
|
|
147444
|
+
var agents = { claude, opencode };
|
|
146385
147445
|
|
|
146386
147446
|
// utils/agent.ts
|
|
146387
147447
|
function hasEnvVar(name) {
|
|
@@ -146394,13 +147454,11 @@ function hasClaudeCodeAuth() {
|
|
|
146394
147454
|
function resolveModel(ctx) {
|
|
146395
147455
|
const envModel = process.env.PULLFROG_MODEL?.trim();
|
|
146396
147456
|
if (envModel) {
|
|
146397
|
-
|
|
146398
|
-
return envModel;
|
|
147457
|
+
return resolveCliModel(envModel) ?? envModel;
|
|
146399
147458
|
}
|
|
146400
147459
|
if (ctx.slug) {
|
|
146401
147460
|
const resolved = resolveCliModel(ctx.slug);
|
|
146402
147461
|
if (resolved) {
|
|
146403
|
-
log.info(`\xBB model: ${resolved} (resolved from ${ctx.slug})`);
|
|
146404
147462
|
return resolved;
|
|
146405
147463
|
}
|
|
146406
147464
|
log.warning(`\xBB unknown model slug "${ctx.slug}" \u2014 agent will auto-select`);
|
|
@@ -146411,7 +147469,6 @@ function resolveAgent(ctx) {
|
|
|
146411
147469
|
const envAgent = process.env.PULLFROG_AGENT?.trim();
|
|
146412
147470
|
if (envAgent) {
|
|
146413
147471
|
if (envAgent in agents) {
|
|
146414
|
-
log.info(`\xBB agent: ${envAgent} (override via PULLFROG_AGENT)`);
|
|
146415
147472
|
return agents[envAgent];
|
|
146416
147473
|
}
|
|
146417
147474
|
log.warning(`\xBB unknown PULLFROG_AGENT="${envAgent}" \u2014 falling through to auto-select`);
|
|
@@ -146420,13 +147477,12 @@ function resolveAgent(ctx) {
|
|
|
146420
147477
|
try {
|
|
146421
147478
|
const provider2 = getModelProvider(ctx.model);
|
|
146422
147479
|
if (provider2 === "anthropic" && hasClaudeCodeAuth()) {
|
|
146423
|
-
log.info(`\xBB agent: claude (auto-selected for ${ctx.model})`);
|
|
146424
147480
|
return agents.claude;
|
|
146425
147481
|
}
|
|
146426
147482
|
} catch {
|
|
146427
147483
|
}
|
|
146428
147484
|
}
|
|
146429
|
-
return agents.
|
|
147485
|
+
return agents.opencode;
|
|
146430
147486
|
}
|
|
146431
147487
|
|
|
146432
147488
|
// utils/apiKeys.ts
|
|
@@ -150640,9 +151696,9 @@ async function startGitAuthServer(tmpdir3) {
|
|
|
150640
151696
|
res.writeHead(409, { "Content-Type": "text/plain" });
|
|
150641
151697
|
res.end("compromised");
|
|
150642
151698
|
});
|
|
150643
|
-
await new Promise((
|
|
151699
|
+
await new Promise((resolve3, reject) => {
|
|
150644
151700
|
server.on("error", reject);
|
|
150645
|
-
server.listen(0, "127.0.0.1", () =>
|
|
151701
|
+
server.listen(0, "127.0.0.1", () => resolve3());
|
|
150646
151702
|
});
|
|
150647
151703
|
const rawAddr = server.address();
|
|
150648
151704
|
if (!rawAddr || typeof rawAddr === "string") {
|
|
@@ -150686,7 +151742,7 @@ async function startGitAuthServer(tmpdir3) {
|
|
|
150686
151742
|
clearTimeout(entry.timeout);
|
|
150687
151743
|
}
|
|
150688
151744
|
codes.clear();
|
|
150689
|
-
await new Promise((
|
|
151745
|
+
await new Promise((resolve3) => server.close(() => resolve3()));
|
|
150690
151746
|
log.debug("git auth server closed");
|
|
150691
151747
|
}
|
|
150692
151748
|
return {
|
|
@@ -150873,7 +151929,7 @@ MCP servers provide tools you can call. Inspect your available MCP servers at st
|
|
|
150873
151929
|
|
|
150874
151930
|
### Git
|
|
150875
151931
|
|
|
150876
|
-
Use \`${t2("git")}\` for local git commands (status, log,
|
|
151932
|
+
Use \`${t2("git")}\` for local git commands (status, log, add, commit, checkout, branch, merge, etc.). When reviewing a PR, do NOT re-derive the PR diff via \`git diff <base>..<head>\` \u2014 the diffPath returned by \`${t2("checkout_pr")}\` is authoritative. \`git log\` and \`git diff --stat\` are fine for commit-range overview; \`git diff\` / \`git diff --cached\` are fine for inspecting your *own* uncommitted changes. For operations requiring remote authentication, use the dedicated MCP tools:
|
|
150877
151933
|
- \`${t2("push_branch")}\` - push current or specified branch
|
|
150878
151934
|
- \`${t2("git_fetch")}\` - fetch refs from remote
|
|
150879
151935
|
- \`${t2("checkout_pr")}\` - checkout a PR branch (fetches and configures push for forks)
|
|
@@ -150914,6 +151970,8 @@ Never use \`sleep\` to wait for commands to complete. Commands run synchronously
|
|
|
150914
151970
|
|
|
150915
151971
|
When posting comments via ${pullfrogMcpName}, write as a professional team member would. Your final comments should be polished and actionable \u2014 do not include intermediate reasoning like "I'll now look at the code" or "Let me respond to the question."
|
|
150916
151972
|
|
|
151973
|
+
When embedding images (e.g. uploaded screenshots) in comments or PR bodies, always use markdown image syntax: \`\`. Never paste a naked URL \u2014 it will not render as an image.
|
|
151974
|
+
|
|
150917
151975
|
### Progress reporting
|
|
150918
151976
|
|
|
150919
151977
|
**Task list**: at the start of every run, create an internal task list based on the steps in your current mode. Update it as you complete each step. The system automatically renders this list to the progress comment \u2014 you do not need to call \`report_progress\` for this.
|
|
@@ -151049,7 +152107,7 @@ function normalizeEnv() {
|
|
|
151049
152107
|
|
|
151050
152108
|
// utils/payload.ts
|
|
151051
152109
|
var core4 = __toESM(require_core(), 1);
|
|
151052
|
-
import { isAbsolute, resolve } from "node:path";
|
|
152110
|
+
import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
151053
152111
|
|
|
151054
152112
|
// utils/versioning.ts
|
|
151055
152113
|
var import_semver2 = __toESM(require_semver2(), 1);
|
|
@@ -151103,8 +152161,8 @@ function isPayloadEvent(value2) {
|
|
|
151103
152161
|
function resolveCwd(cwd) {
|
|
151104
152162
|
const workspace = process.env.GITHUB_WORKSPACE;
|
|
151105
152163
|
if (!cwd) return workspace;
|
|
151106
|
-
if (
|
|
151107
|
-
return workspace ?
|
|
152164
|
+
if (isAbsolute2(cwd)) return cwd;
|
|
152165
|
+
return workspace ? resolve2(workspace, cwd) : cwd;
|
|
151108
152166
|
}
|
|
151109
152167
|
function resolvePromptInput() {
|
|
151110
152168
|
const prompt = core4.getInput("prompt", { required: true });
|
|
@@ -151283,7 +152341,8 @@ var defaultSettings = {
|
|
|
151283
152341
|
shell: "restricted",
|
|
151284
152342
|
prApproveEnabled: false,
|
|
151285
152343
|
modeInstructions: {},
|
|
151286
|
-
learnings: null
|
|
152344
|
+
learnings: null,
|
|
152345
|
+
envAllowlist: null
|
|
151287
152346
|
};
|
|
151288
152347
|
var defaultRunContext = {
|
|
151289
152348
|
settings: defaultSettings,
|
|
@@ -151363,7 +152422,7 @@ async function resolveRunContextData(params) {
|
|
|
151363
152422
|
}
|
|
151364
152423
|
|
|
151365
152424
|
// utils/setup.ts
|
|
151366
|
-
import { execSync as execSync3 } from "node:child_process";
|
|
152425
|
+
import { execFileSync as execFileSync5, execSync as execSync3 } from "node:child_process";
|
|
151367
152426
|
import { mkdtempSync } from "node:fs";
|
|
151368
152427
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
151369
152428
|
import { join as join13 } from "node:path";
|
|
@@ -151373,6 +152432,51 @@ function createTempDirectory() {
|
|
|
151373
152432
|
log.info(`\xBB created temp dir at ${sharedTempDir}`);
|
|
151374
152433
|
return sharedTempDir;
|
|
151375
152434
|
}
|
|
152435
|
+
function envScopedToRepo() {
|
|
152436
|
+
const scoped = { ...process.env };
|
|
152437
|
+
for (const key of Object.keys(scoped)) {
|
|
152438
|
+
if (key.startsWith("GIT_")) delete scoped[key];
|
|
152439
|
+
}
|
|
152440
|
+
return scoped;
|
|
152441
|
+
}
|
|
152442
|
+
function removeIncludeIfEntries(repoDir) {
|
|
152443
|
+
const env2 = envScopedToRepo();
|
|
152444
|
+
let configOutput;
|
|
152445
|
+
try {
|
|
152446
|
+
configOutput = execSync3("git config --local --get-regexp -z ^includeif\\.", {
|
|
152447
|
+
cwd: repoDir,
|
|
152448
|
+
encoding: "utf-8",
|
|
152449
|
+
stdio: "pipe",
|
|
152450
|
+
env: env2
|
|
152451
|
+
});
|
|
152452
|
+
} catch {
|
|
152453
|
+
log.debug("\xBB no includeIf credential entries to remove");
|
|
152454
|
+
return;
|
|
152455
|
+
}
|
|
152456
|
+
const seen = /* @__PURE__ */ new Set();
|
|
152457
|
+
for (const entry of configOutput.split("\0")) {
|
|
152458
|
+
if (!entry) continue;
|
|
152459
|
+
const nl = entry.indexOf("\n");
|
|
152460
|
+
const key = nl === -1 ? entry : entry.slice(0, nl);
|
|
152461
|
+
if (!key || seen.has(key)) continue;
|
|
152462
|
+
seen.add(key);
|
|
152463
|
+
try {
|
|
152464
|
+
execFileSync5("git", ["config", "--local", "--unset-all", key], {
|
|
152465
|
+
cwd: repoDir,
|
|
152466
|
+
stdio: "pipe",
|
|
152467
|
+
env: env2
|
|
152468
|
+
});
|
|
152469
|
+
} catch (error49) {
|
|
152470
|
+
log.debug(
|
|
152471
|
+
`\xBB failed to unset ${key}: ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
152472
|
+
);
|
|
152473
|
+
}
|
|
152474
|
+
}
|
|
152475
|
+
if (seen.size > 0)
|
|
152476
|
+
log.info(
|
|
152477
|
+
`\xBB removed ${seen.size} includeIf credential ${seen.size === 1 ? "entry" : "entries"}`
|
|
152478
|
+
);
|
|
152479
|
+
}
|
|
151376
152480
|
async function setupGit(params) {
|
|
151377
152481
|
const repoDir = process.cwd();
|
|
151378
152482
|
log.info("\xBB setting up git configuration...");
|
|
@@ -151419,24 +152523,7 @@ async function setupGit(params) {
|
|
|
151419
152523
|
} catch {
|
|
151420
152524
|
log.debug("\xBB no existing authentication headers to remove");
|
|
151421
152525
|
}
|
|
151422
|
-
|
|
151423
|
-
const configOutput = execSync3("git config --local --get-regexp ^includeif\\.", {
|
|
151424
|
-
cwd: repoDir,
|
|
151425
|
-
encoding: "utf-8",
|
|
151426
|
-
stdio: "pipe"
|
|
151427
|
-
});
|
|
151428
|
-
for (const line of configOutput.trim().split("\n")) {
|
|
151429
|
-
const key = line.split(" ")[0];
|
|
151430
|
-
if (!key) continue;
|
|
151431
|
-
execSync3(`git config --local --unset "${key}"`, {
|
|
151432
|
-
cwd: repoDir,
|
|
151433
|
-
stdio: "pipe"
|
|
151434
|
-
});
|
|
151435
|
-
}
|
|
151436
|
-
log.info("\xBB removed includeIf credential entries");
|
|
151437
|
-
} catch {
|
|
151438
|
-
log.debug("\xBB no includeIf credential entries to remove");
|
|
151439
|
-
}
|
|
152526
|
+
removeIncludeIfEntries(repoDir);
|
|
151440
152527
|
const originUrl = `https://github.com/${params.owner}/${params.name}.git`;
|
|
151441
152528
|
$("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir });
|
|
151442
152529
|
params.toolState.pushUrl = originUrl;
|
|
@@ -151455,6 +152542,13 @@ function parseTimeString(input) {
|
|
|
151455
152542
|
const seconds = parseInt(match3[3] || "0", 10);
|
|
151456
152543
|
return (hours * 3600 + minutes * 60 + seconds) * 1e3;
|
|
151457
152544
|
}
|
|
152545
|
+
var TIMEOUT_MAX_MS = 2147483647;
|
|
152546
|
+
function resolveTimeoutMs(input) {
|
|
152547
|
+
if (!input) return null;
|
|
152548
|
+
const parsed2 = parseTimeString(input);
|
|
152549
|
+
if (parsed2 === null || parsed2 <= 0 || parsed2 > TIMEOUT_MAX_MS) return null;
|
|
152550
|
+
return parsed2;
|
|
152551
|
+
}
|
|
151458
152552
|
|
|
151459
152553
|
// utils/todoTracking.ts
|
|
151460
152554
|
function isValidTodoStatus(value2) {
|
|
@@ -151557,9 +152651,12 @@ function createTodoTracker(onUpdate) {
|
|
|
151557
152651
|
if (item.status === "in_progress") item.status = "completed";
|
|
151558
152652
|
}
|
|
151559
152653
|
},
|
|
151560
|
-
renderCollapsible() {
|
|
152654
|
+
renderCollapsible(options) {
|
|
151561
152655
|
if (state.size === 0) return "";
|
|
151562
|
-
const
|
|
152656
|
+
const shouldCompleteInProgress = options?.completeInProgress === true;
|
|
152657
|
+
const todos = Array.from(state.values()).map(
|
|
152658
|
+
(item) => shouldCompleteInProgress && item.status === "in_progress" ? { ...item, status: "completed" } : item
|
|
152659
|
+
);
|
|
151563
152660
|
const completed = todos.filter((t2) => t2.status === "completed").length;
|
|
151564
152661
|
const markdown = renderTodoMarkdown(todos);
|
|
151565
152662
|
return `<details>
|
|
@@ -151619,6 +152716,32 @@ function resolveOutputSchema() {
|
|
|
151619
152716
|
log.info("\xBB structured output schema provided \u2014 output will be required");
|
|
151620
152717
|
return parsed2;
|
|
151621
152718
|
}
|
|
152719
|
+
function resolveTimeoutForLog(timeout) {
|
|
152720
|
+
if (!timeout) return "1h (default)";
|
|
152721
|
+
if (timeout === TIMEOUT_DISABLED) return "none (disabled)";
|
|
152722
|
+
return timeout;
|
|
152723
|
+
}
|
|
152724
|
+
function resolveModelForLog(ctx) {
|
|
152725
|
+
const envModel = process.env.PULLFROG_MODEL?.trim();
|
|
152726
|
+
if (envModel) return `${envModel} (override via PULLFROG_MODEL)`;
|
|
152727
|
+
if (ctx.payload.proxyModel) return `${ctx.payload.proxyModel} (proxy)`;
|
|
152728
|
+
if (ctx.resolvedModel && ctx.payload.model && ctx.payload.model !== ctx.resolvedModel) {
|
|
152729
|
+
return `${ctx.resolvedModel} (resolved from ${ctx.payload.model})`;
|
|
152730
|
+
}
|
|
152731
|
+
if (ctx.resolvedModel) return ctx.resolvedModel;
|
|
152732
|
+
if (ctx.payload.model) return `${ctx.payload.model} (unresolved)`;
|
|
152733
|
+
return "auto";
|
|
152734
|
+
}
|
|
152735
|
+
function resolveAgentForLog(ctx) {
|
|
152736
|
+
const envAgent = process.env.PULLFROG_AGENT?.trim();
|
|
152737
|
+
if (envAgent && envAgent === ctx.agentName) {
|
|
152738
|
+
return `${ctx.agentName} (override via PULLFROG_AGENT)`;
|
|
152739
|
+
}
|
|
152740
|
+
if (ctx.agentName === "claude" && ctx.resolvedModel) {
|
|
152741
|
+
return `${ctx.agentName} (auto-selected for ${ctx.resolvedModel})`;
|
|
152742
|
+
}
|
|
152743
|
+
return ctx.agentName;
|
|
152744
|
+
}
|
|
151622
152745
|
async function mintProxyKey(ctx) {
|
|
151623
152746
|
try {
|
|
151624
152747
|
process.env.ACTIONS_ID_TOKEN_REQUEST_URL = ctx.oidcCredentials.requestUrl;
|
|
@@ -151678,6 +152801,7 @@ async function main() {
|
|
|
151678
152801
|
}
|
|
151679
152802
|
const timer = new Timer();
|
|
151680
152803
|
let activityTimeout = null;
|
|
152804
|
+
let safetyNetTimer;
|
|
151681
152805
|
const resolvedPromptInput = resolvePromptInput();
|
|
151682
152806
|
const toolState = initToolState({
|
|
151683
152807
|
progressCommentId: typeof resolvedPromptInput !== "string" ? resolvedPromptInput.progressCommentId : void 0
|
|
@@ -151697,6 +152821,9 @@ async function main() {
|
|
|
151697
152821
|
const count = Object.keys(runContext.dbSecrets).length;
|
|
151698
152822
|
if (count > 0) log.info(`\xBB ${count} db secret(s) loaded`);
|
|
151699
152823
|
}
|
|
152824
|
+
if (runContext.repoSettings.envAllowlist) {
|
|
152825
|
+
setEnvAllowlist(runContext.repoSettings.envAllowlist);
|
|
152826
|
+
}
|
|
151700
152827
|
const payload = resolvePayload(resolvedPromptInput, runContext.repoSettings);
|
|
151701
152828
|
toolState.model = payload.model;
|
|
151702
152829
|
if (payload.event.trigger === "pull_request_synchronize") {
|
|
@@ -151761,10 +152888,13 @@ async function main() {
|
|
|
151761
152888
|
postCheckoutScript: runContext.repoSettings.postCheckoutScript
|
|
151762
152889
|
});
|
|
151763
152890
|
timer.checkpoint("git");
|
|
151764
|
-
await executeLifecycleHook({
|
|
152891
|
+
const setupHook = await executeLifecycleHook({
|
|
151765
152892
|
event: "setup",
|
|
151766
152893
|
script: runContext.repoSettings.setupScript
|
|
151767
152894
|
});
|
|
152895
|
+
if (setupHook.warning) {
|
|
152896
|
+
throw new Error(setupHook.warning);
|
|
152897
|
+
}
|
|
151768
152898
|
timer.checkpoint("lifecycleHooks::setup");
|
|
151769
152899
|
const agentId = agent2.name;
|
|
151770
152900
|
const modes2 = [...computeModes(agentId), ...runContext.repoSettings.modes];
|
|
@@ -151786,17 +152916,22 @@ async function main() {
|
|
|
151786
152916
|
runId: runInfo.runId,
|
|
151787
152917
|
jobId: runInfo.jobId,
|
|
151788
152918
|
mcpServerUrl: "",
|
|
151789
|
-
tmpdir: tmpdir3
|
|
152919
|
+
tmpdir: tmpdir3,
|
|
152920
|
+
resolvedModel
|
|
151790
152921
|
};
|
|
151791
152922
|
const mcpHttpServer = __using(_stack, await startMcpHttpServer(toolContext, { outputSchema }), true);
|
|
151792
152923
|
toolContext.mcpServerUrl = mcpHttpServer.url;
|
|
151793
152924
|
log.info(`\xBB MCP server started at ${mcpHttpServer.url}`);
|
|
151794
152925
|
timer.checkpoint("mcpServer");
|
|
151795
152926
|
startInstallation(toolContext);
|
|
151796
|
-
|
|
151797
|
-
|
|
152927
|
+
const modelForLog = resolveModelForLog({ payload, resolvedModel });
|
|
152928
|
+
const agentForLog = resolveAgentForLog({ agentName: agent2.name, resolvedModel });
|
|
152929
|
+
const timeoutForLog = resolveTimeoutForLog(payload.timeout);
|
|
152930
|
+
log.info(`\xBB model: ${modelForLog}`);
|
|
152931
|
+
log.info(`\xBB agent: ${agentForLog}`);
|
|
151798
152932
|
log.info(`\xBB push: ${payload.push}`);
|
|
151799
152933
|
log.info(`\xBB shell: ${payload.shell}`);
|
|
152934
|
+
log.info(`\xBB timeout: ${timeoutForLog}`);
|
|
151800
152935
|
const instructions = resolveInstructions({
|
|
151801
152936
|
payload,
|
|
151802
152937
|
repo: runContext.repo,
|
|
@@ -151818,6 +152953,18 @@ ${instructions.user}` : null,
|
|
|
151818
152953
|
log.group("View full prompt", () => {
|
|
151819
152954
|
log.info(instructions.full);
|
|
151820
152955
|
});
|
|
152956
|
+
if (agentId === "opencode") {
|
|
152957
|
+
const pluginDir = join14(process.cwd(), ".opencode", "plugin");
|
|
152958
|
+
const hasPlugins = existsSync6(pluginDir) && readdirSync(pluginDir).some((f) => /\.[jt]sx?$/.test(f));
|
|
152959
|
+
if (hasPlugins && toolState.dependencyInstallation?.promise) {
|
|
152960
|
+
log.info(
|
|
152961
|
+
"\xBB .opencode/plugin/ detected \u2014 awaiting dependency installation before agent start"
|
|
152962
|
+
);
|
|
152963
|
+
await toolState.dependencyInstallation.promise.catch(() => {
|
|
152964
|
+
});
|
|
152965
|
+
timer.checkpoint("awaitDepsForPlugins");
|
|
152966
|
+
}
|
|
152967
|
+
}
|
|
151821
152968
|
activityTimeout = createProcessOutputActivityTimeout({
|
|
151822
152969
|
timeoutMs: DEFAULT_ACTIVITY_TIMEOUT_MS,
|
|
151823
152970
|
checkIntervalMs: DEFAULT_ACTIVITY_CHECK_INTERVAL_MS
|
|
@@ -151833,24 +152980,62 @@ ${instructions.user}` : null,
|
|
|
151833
152980
|
}
|
|
151834
152981
|
});
|
|
151835
152982
|
toolState.todoTracker = todoTracker;
|
|
152983
|
+
let innerTimeoutFired = false;
|
|
152984
|
+
const onInnerActivityTimeout = () => {
|
|
152985
|
+
if (innerTimeoutFired) return;
|
|
152986
|
+
innerTimeoutFired = true;
|
|
152987
|
+
log.info(
|
|
152988
|
+
"\xBB inner activity timeout fired \u2014 stopping MCP server and starting 5min safety-net timer"
|
|
152989
|
+
);
|
|
152990
|
+
mcpHttpServer[Symbol.asyncDispose]().catch((err) => {
|
|
152991
|
+
log.debug(
|
|
152992
|
+
`mcp server stop after inner kill failed: ${err instanceof Error ? err.message : String(err)}`
|
|
152993
|
+
);
|
|
152994
|
+
});
|
|
152995
|
+
safetyNetTimer = setTimeout(
|
|
152996
|
+
() => {
|
|
152997
|
+
activityTimeout?.forceReject(
|
|
152998
|
+
"agent still pending 5min after inner activity kill \u2014 forcing exit"
|
|
152999
|
+
);
|
|
153000
|
+
},
|
|
153001
|
+
5 * 60 * 1e3
|
|
153002
|
+
);
|
|
153003
|
+
safetyNetTimer.unref?.();
|
|
153004
|
+
};
|
|
151836
153005
|
const agentPromise = agent2.run({
|
|
151837
153006
|
payload,
|
|
151838
153007
|
resolvedModel,
|
|
151839
153008
|
mcpServerUrl: mcpHttpServer.url,
|
|
151840
153009
|
tmpdir: tmpdir3,
|
|
151841
153010
|
instructions,
|
|
151842
|
-
todoTracker
|
|
153011
|
+
todoTracker,
|
|
153012
|
+
onActivityTimeout: onInnerActivityTimeout,
|
|
153013
|
+
onToolUse: (event) => {
|
|
153014
|
+
const wasTracked = recordDiffReadFromToolUse({
|
|
153015
|
+
state: toolState.diffCoverage,
|
|
153016
|
+
toolName: event.toolName,
|
|
153017
|
+
input: event.input,
|
|
153018
|
+
cwd: process.cwd()
|
|
153019
|
+
});
|
|
153020
|
+
if (!wasTracked) return;
|
|
153021
|
+
const trackedRanges = toolState.diffCoverage?.coveredRanges ?? [];
|
|
153022
|
+
log.debug(
|
|
153023
|
+
`\xBB diff coverage tracked from tool ${event.toolName} (${trackedRanges.length} merged range${trackedRanges.length === 1 ? "" : "s"})`
|
|
153024
|
+
);
|
|
153025
|
+
}
|
|
153026
|
+
});
|
|
153027
|
+
agentPromise.catch(() => {
|
|
151843
153028
|
});
|
|
151844
153029
|
let result;
|
|
151845
153030
|
if (payload.timeout === TIMEOUT_DISABLED) {
|
|
151846
153031
|
result = await Promise.race([agentPromise, activityTimeout.promise]);
|
|
151847
153032
|
} else {
|
|
151848
|
-
const
|
|
151849
|
-
if (payload.timeout &&
|
|
151850
|
-
log.warning(`invalid timeout
|
|
153033
|
+
const usable = resolveTimeoutMs(payload.timeout);
|
|
153034
|
+
if (payload.timeout && usable === null) {
|
|
153035
|
+
log.warning(`invalid timeout "${payload.timeout}" (use --notimeout to disable), using 1h`);
|
|
151851
153036
|
}
|
|
151852
|
-
const timeoutMs =
|
|
151853
|
-
const actualTimeout =
|
|
153037
|
+
const timeoutMs = usable ?? 36e5;
|
|
153038
|
+
const actualTimeout = usable !== null ? payload.timeout : "1h";
|
|
151854
153039
|
let timeoutId;
|
|
151855
153040
|
const timeoutPromise = new Promise((_4, reject) => {
|
|
151856
153041
|
timeoutId = setTimeout(() => {
|
|
@@ -151912,7 +153097,14 @@ ${instructions.user}` : null,
|
|
|
151912
153097
|
killTrackedChildren();
|
|
151913
153098
|
log.error(errorMessage);
|
|
151914
153099
|
try {
|
|
151915
|
-
|
|
153100
|
+
const errorSummary = `### \u274C Pullfrog failed
|
|
153101
|
+
|
|
153102
|
+
\`\`\`
|
|
153103
|
+
${errorMessage}
|
|
153104
|
+
\`\`\``;
|
|
153105
|
+
const usageSummary = formatUsageSummary(toolState.usageEntries);
|
|
153106
|
+
const parts = [errorSummary, toolState.lastProgressBody, usageSummary].filter(Boolean);
|
|
153107
|
+
await writeSummary(parts.join("\n\n"));
|
|
151916
153108
|
} catch {
|
|
151917
153109
|
}
|
|
151918
153110
|
try {
|
|
@@ -151930,8 +153122,21 @@ ${instructions.user}` : null,
|
|
|
151930
153122
|
};
|
|
151931
153123
|
} finally {
|
|
151932
153124
|
activityTimeout?.stop();
|
|
153125
|
+
if (safetyNetTimer) clearTimeout(safetyNetTimer);
|
|
151933
153126
|
if (usageSummaryPath) {
|
|
151934
|
-
|
|
153127
|
+
try {
|
|
153128
|
+
await writeGitHubUsageSummaryToFile(usageSummaryPath);
|
|
153129
|
+
} catch (err) {
|
|
153130
|
+
log.debug(
|
|
153131
|
+
`failed to write usage summary to ${usageSummaryPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
153132
|
+
);
|
|
153133
|
+
}
|
|
153134
|
+
}
|
|
153135
|
+
if (toolContext) {
|
|
153136
|
+
const patch = aggregateUsage(toolState.usageEntries);
|
|
153137
|
+
if (Object.keys(patch).length > 0) {
|
|
153138
|
+
await patchWorkflowRunFields(toolContext, patch);
|
|
153139
|
+
}
|
|
151935
153140
|
}
|
|
151936
153141
|
}
|
|
151937
153142
|
} catch (_3) {
|
|
@@ -151985,7 +153190,7 @@ async function validateStuckProgressComment(ctx) {
|
|
|
151985
153190
|
comment_id: commentId
|
|
151986
153191
|
});
|
|
151987
153192
|
const body = commentResult.data.body ?? "";
|
|
151988
|
-
if (body
|
|
153193
|
+
if (isLeapingIntoActionCommentBody(body)) {
|
|
151989
153194
|
log.info(`[post] comment ${commentId} is stuck on "Leaping into action"`);
|
|
151990
153195
|
return commentId;
|
|
151991
153196
|
}
|
|
@@ -152195,7 +153400,7 @@ async function run(args2) {
|
|
|
152195
153400
|
}
|
|
152196
153401
|
|
|
152197
153402
|
// commands/init.ts
|
|
152198
|
-
import { execFileSync as
|
|
153403
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
152199
153404
|
|
|
152200
153405
|
// node_modules/.pnpm/@clack+core@1.2.0/node_modules/@clack/core/dist/index.mjs
|
|
152201
153406
|
import { styleText as y } from "node:util";
|
|
@@ -153187,7 +154392,7 @@ function handleCancel(value2) {
|
|
|
153187
154392
|
function getGhToken() {
|
|
153188
154393
|
let token;
|
|
153189
154394
|
try {
|
|
153190
|
-
token =
|
|
154395
|
+
token = execFileSync6("gh", ["auth", "token"], { encoding: "utf-8" }).trim();
|
|
153191
154396
|
} catch {
|
|
153192
154397
|
bail(
|
|
153193
154398
|
`gh cli not found or not authenticated.
|
|
@@ -153230,7 +154435,7 @@ async function ghApi(path3, token) {
|
|
|
153230
154435
|
function parseGitRemote() {
|
|
153231
154436
|
let url4;
|
|
153232
154437
|
try {
|
|
153233
|
-
url4 =
|
|
154438
|
+
url4 = execFileSync6("git", ["remote", "get-url", "origin"], { encoding: "utf-8" }).trim();
|
|
153234
154439
|
} catch {
|
|
153235
154440
|
bail("not a git repository or no 'origin' remote found.");
|
|
153236
154441
|
}
|
|
@@ -153241,10 +154446,10 @@ function parseGitRemote() {
|
|
|
153241
154446
|
function openBrowser(url4) {
|
|
153242
154447
|
try {
|
|
153243
154448
|
const platform = process.platform;
|
|
153244
|
-
if (platform === "darwin")
|
|
154449
|
+
if (platform === "darwin") execFileSync6("open", [url4], { stdio: "ignore" });
|
|
153245
154450
|
else if (platform === "win32")
|
|
153246
|
-
|
|
153247
|
-
else
|
|
154451
|
+
execFileSync6("cmd", ["/c", "start", "", url4], { stdio: "ignore" });
|
|
154452
|
+
else execFileSync6("xdg-open", [url4], { stdio: "ignore" });
|
|
153248
154453
|
} catch {
|
|
153249
154454
|
}
|
|
153250
154455
|
}
|
|
@@ -153471,7 +154676,7 @@ function setGhSecret(ctx) {
|
|
|
153471
154676
|
let orgFailed = false;
|
|
153472
154677
|
if (ctx.org) {
|
|
153473
154678
|
try {
|
|
153474
|
-
|
|
154679
|
+
execFileSync6("gh", ["secret", "set", ctx.name, "--org", ctx.org, "--visibility", "all"], {
|
|
153475
154680
|
input: ctx.value,
|
|
153476
154681
|
stdio: ["pipe", "ignore", "pipe"],
|
|
153477
154682
|
encoding: "utf-8"
|
|
@@ -153482,7 +154687,7 @@ function setGhSecret(ctx) {
|
|
|
153482
154687
|
}
|
|
153483
154688
|
}
|
|
153484
154689
|
try {
|
|
153485
|
-
|
|
154690
|
+
execFileSync6("gh", ["secret", "set", ctx.name, "--repo", ctx.repoSlug], {
|
|
153486
154691
|
input: ctx.value,
|
|
153487
154692
|
stdio: ["pipe", "ignore", "pipe"],
|
|
153488
154693
|
encoding: "utf-8"
|
|
@@ -153852,7 +155057,7 @@ async function run2() {
|
|
|
153852
155057
|
}
|
|
153853
155058
|
|
|
153854
155059
|
// cli.ts
|
|
153855
|
-
var VERSION10 = "0.0.
|
|
155060
|
+
var VERSION10 = "0.0.202";
|
|
153856
155061
|
var bin = basename2(process.argv[1] || "");
|
|
153857
155062
|
var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
|
|
153858
155063
|
var rawArgs = process.argv.slice(2);
|