pullfrog 0.1.73 → 0.1.75
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/cli.mjs +1055 -1009
- package/dist/index.js +1053 -1007
- package/dist/internal.js +1 -1
- package/dist/mcp/shell.d.ts +5 -0
- package/dist/utils/lifecycle.d.ts +13 -0
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -28300,7 +28300,7 @@ var require_cross_spawn = __commonJS({
|
|
|
28300
28300
|
enoent.hookChildProcess(spawned, parsed2);
|
|
28301
28301
|
return spawned;
|
|
28302
28302
|
}
|
|
28303
|
-
function
|
|
28303
|
+
function spawnSync9(command, args2, options) {
|
|
28304
28304
|
const parsed2 = parse8(command, args2, options);
|
|
28305
28305
|
const result = cp.spawnSync(parsed2.command, parsed2.args, parsed2.options);
|
|
28306
28306
|
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed2);
|
|
@@ -28308,7 +28308,7 @@ var require_cross_spawn = __commonJS({
|
|
|
28308
28308
|
}
|
|
28309
28309
|
module.exports = spawn6;
|
|
28310
28310
|
module.exports.spawn = spawn6;
|
|
28311
|
-
module.exports.sync =
|
|
28311
|
+
module.exports.sync = spawnSync9;
|
|
28312
28312
|
module.exports._parse = parse8;
|
|
28313
28313
|
module.exports._enoent = enoent;
|
|
28314
28314
|
}
|
|
@@ -123000,7 +123000,7 @@ var import_picocolors = __toESM(require_picocolors(), 1);
|
|
|
123000
123000
|
// package.json
|
|
123001
123001
|
var package_default = {
|
|
123002
123002
|
name: "pullfrog",
|
|
123003
|
-
version: "0.1.
|
|
123003
|
+
version: "0.1.75",
|
|
123004
123004
|
cliContractVersion: "1",
|
|
123005
123005
|
type: "module",
|
|
123006
123006
|
bin: {
|
|
@@ -194109,8 +194109,8 @@ function closeBrowserDaemon(toolState) {
|
|
|
194109
194109
|
|
|
194110
194110
|
// mcp/checkout.ts
|
|
194111
194111
|
import { createHash as createHash2 } from "node:crypto";
|
|
194112
|
-
import { statSync, unlinkSync as unlinkSync3, writeFileSync as
|
|
194113
|
-
import { join as
|
|
194112
|
+
import { statSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync11 } from "node:fs";
|
|
194113
|
+
import { join as join16 } from "node:path";
|
|
194114
194114
|
|
|
194115
194115
|
// node_modules/.pnpm/@ark+schema@0.56.0/node_modules/@ark/schema/out/shared/errors.js
|
|
194116
194116
|
var ArkError = class _ArkError extends CastableBase {
|
|
@@ -202382,6 +202382,960 @@ async function $gitFetchWithDeepen(args2, options, label) {
|
|
|
202382
202382
|
}
|
|
202383
202383
|
}
|
|
202384
202384
|
|
|
202385
|
+
// utils/lifecycle.ts
|
|
202386
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
202387
|
+
|
|
202388
|
+
// mcp/shell.ts
|
|
202389
|
+
import { spawn as spawn5, spawnSync as spawnSync7 } from "node:child_process";
|
|
202390
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
202391
|
+
import { closeSync, openSync, writeFileSync as writeFileSync9 } from "node:fs";
|
|
202392
|
+
import { userInfo as userInfo2 } from "node:os";
|
|
202393
|
+
import { join as join11 } from "node:path";
|
|
202394
|
+
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
202395
|
+
|
|
202396
|
+
// node_modules/.pnpm/@toon-format+toon@1.4.0/node_modules/@toon-format/toon/dist/index.mjs
|
|
202397
|
+
var LIST_ITEM_MARKER = "-";
|
|
202398
|
+
var LIST_ITEM_PREFIX = "- ";
|
|
202399
|
+
var COMMA = ",";
|
|
202400
|
+
var PIPE = "|";
|
|
202401
|
+
var DOT = ".";
|
|
202402
|
+
var NULL_LITERAL = "null";
|
|
202403
|
+
var TRUE_LITERAL = "true";
|
|
202404
|
+
var FALSE_LITERAL = "false";
|
|
202405
|
+
var BACKSLASH = "\\";
|
|
202406
|
+
var DOUBLE_QUOTE = '"';
|
|
202407
|
+
var TAB = " ";
|
|
202408
|
+
var DELIMITERS = {
|
|
202409
|
+
comma: COMMA,
|
|
202410
|
+
tab: TAB,
|
|
202411
|
+
pipe: PIPE
|
|
202412
|
+
};
|
|
202413
|
+
var DEFAULT_DELIMITER = DELIMITERS.comma;
|
|
202414
|
+
function escapeString(value2) {
|
|
202415
|
+
return value2.replace(/\\/g, `${BACKSLASH}${BACKSLASH}`).replace(/"/g, `${BACKSLASH}${DOUBLE_QUOTE}`).replace(/\n/g, `${BACKSLASH}n`).replace(/\r/g, `${BACKSLASH}r`).replace(/\t/g, `${BACKSLASH}t`);
|
|
202416
|
+
}
|
|
202417
|
+
function isBooleanOrNullLiteral(token) {
|
|
202418
|
+
return token === TRUE_LITERAL || token === FALSE_LITERAL || token === NULL_LITERAL;
|
|
202419
|
+
}
|
|
202420
|
+
function normalizeValue(value2) {
|
|
202421
|
+
if (value2 === null) return null;
|
|
202422
|
+
if (typeof value2 === "string" || typeof value2 === "boolean") return value2;
|
|
202423
|
+
if (typeof value2 === "number") {
|
|
202424
|
+
if (Object.is(value2, -0)) return 0;
|
|
202425
|
+
if (!Number.isFinite(value2)) return null;
|
|
202426
|
+
return value2;
|
|
202427
|
+
}
|
|
202428
|
+
if (typeof value2 === "bigint") {
|
|
202429
|
+
if (value2 >= Number.MIN_SAFE_INTEGER && value2 <= Number.MAX_SAFE_INTEGER) return Number(value2);
|
|
202430
|
+
return value2.toString();
|
|
202431
|
+
}
|
|
202432
|
+
if (value2 instanceof Date) return value2.toISOString();
|
|
202433
|
+
if (Array.isArray(value2)) return value2.map(normalizeValue);
|
|
202434
|
+
if (value2 instanceof Set) return Array.from(value2).map(normalizeValue);
|
|
202435
|
+
if (value2 instanceof Map) return Object.fromEntries(Array.from(value2, ([k2, v]) => [String(k2), normalizeValue(v)]));
|
|
202436
|
+
if (isPlainObject7(value2)) {
|
|
202437
|
+
const normalized = {};
|
|
202438
|
+
for (const key in value2) if (Object.prototype.hasOwnProperty.call(value2, key)) normalized[key] = normalizeValue(value2[key]);
|
|
202439
|
+
return normalized;
|
|
202440
|
+
}
|
|
202441
|
+
return null;
|
|
202442
|
+
}
|
|
202443
|
+
function isJsonPrimitive(value2) {
|
|
202444
|
+
return value2 === null || typeof value2 === "string" || typeof value2 === "number" || typeof value2 === "boolean";
|
|
202445
|
+
}
|
|
202446
|
+
function isJsonArray(value2) {
|
|
202447
|
+
return Array.isArray(value2);
|
|
202448
|
+
}
|
|
202449
|
+
function isJsonObject4(value2) {
|
|
202450
|
+
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
202451
|
+
}
|
|
202452
|
+
function isEmptyObject2(value2) {
|
|
202453
|
+
return Object.keys(value2).length === 0;
|
|
202454
|
+
}
|
|
202455
|
+
function isPlainObject7(value2) {
|
|
202456
|
+
if (value2 === null || typeof value2 !== "object") return false;
|
|
202457
|
+
const prototype = Object.getPrototypeOf(value2);
|
|
202458
|
+
return prototype === null || prototype === Object.prototype;
|
|
202459
|
+
}
|
|
202460
|
+
function isArrayOfPrimitives(value2) {
|
|
202461
|
+
return value2.length === 0 || value2.every((item) => isJsonPrimitive(item));
|
|
202462
|
+
}
|
|
202463
|
+
function isArrayOfArrays(value2) {
|
|
202464
|
+
return value2.length === 0 || value2.every((item) => isJsonArray(item));
|
|
202465
|
+
}
|
|
202466
|
+
function isArrayOfObjects(value2) {
|
|
202467
|
+
return value2.length === 0 || value2.every((item) => isJsonObject4(item));
|
|
202468
|
+
}
|
|
202469
|
+
function isValidUnquotedKey(key) {
|
|
202470
|
+
return /^[A-Z_][\w.]*$/i.test(key);
|
|
202471
|
+
}
|
|
202472
|
+
function isIdentifierSegment(key) {
|
|
202473
|
+
return /^[A-Z_]\w*$/i.test(key);
|
|
202474
|
+
}
|
|
202475
|
+
function isSafeUnquoted(value2, delimiter3 = DEFAULT_DELIMITER) {
|
|
202476
|
+
if (!value2) return false;
|
|
202477
|
+
if (value2 !== value2.trim()) return false;
|
|
202478
|
+
if (isBooleanOrNullLiteral(value2) || isNumericLike(value2)) return false;
|
|
202479
|
+
if (value2.includes(":")) return false;
|
|
202480
|
+
if (value2.includes('"') || value2.includes("\\")) return false;
|
|
202481
|
+
if (/[[\]{}]/.test(value2)) return false;
|
|
202482
|
+
if (/[\n\r\t]/.test(value2)) return false;
|
|
202483
|
+
if (value2.includes(delimiter3)) return false;
|
|
202484
|
+
if (value2.startsWith(LIST_ITEM_MARKER)) return false;
|
|
202485
|
+
return true;
|
|
202486
|
+
}
|
|
202487
|
+
function isNumericLike(value2) {
|
|
202488
|
+
return /^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(value2) || /^0\d+$/.test(value2);
|
|
202489
|
+
}
|
|
202490
|
+
var QUOTED_KEY_MARKER = Symbol("quotedKey");
|
|
202491
|
+
function tryFoldKeyChain(key, value2, siblings, options, rootLiteralKeys, pathPrefix, flattenDepth) {
|
|
202492
|
+
if (options.keyFolding !== "safe") return;
|
|
202493
|
+
if (!isJsonObject4(value2)) return;
|
|
202494
|
+
const { segments, tail, leafValue } = collectSingleKeyChain(key, value2, flattenDepth ?? options.flattenDepth);
|
|
202495
|
+
if (segments.length < 2) return;
|
|
202496
|
+
if (!segments.every((seg) => isIdentifierSegment(seg))) return;
|
|
202497
|
+
const foldedKey = buildFoldedKey(segments);
|
|
202498
|
+
const absolutePath = pathPrefix ? `${pathPrefix}${DOT}${foldedKey}` : foldedKey;
|
|
202499
|
+
if (siblings.includes(foldedKey)) return;
|
|
202500
|
+
if (rootLiteralKeys && rootLiteralKeys.has(absolutePath)) return;
|
|
202501
|
+
return {
|
|
202502
|
+
foldedKey,
|
|
202503
|
+
remainder: tail,
|
|
202504
|
+
leafValue,
|
|
202505
|
+
segmentCount: segments.length
|
|
202506
|
+
};
|
|
202507
|
+
}
|
|
202508
|
+
function collectSingleKeyChain(startKey, startValue, maxDepth) {
|
|
202509
|
+
const segments = [startKey];
|
|
202510
|
+
let currentValue = startValue;
|
|
202511
|
+
while (segments.length < maxDepth) {
|
|
202512
|
+
if (!isJsonObject4(currentValue)) break;
|
|
202513
|
+
const keys = Object.keys(currentValue);
|
|
202514
|
+
if (keys.length !== 1) break;
|
|
202515
|
+
const nextKey = keys[0];
|
|
202516
|
+
const nextValue = currentValue[nextKey];
|
|
202517
|
+
segments.push(nextKey);
|
|
202518
|
+
currentValue = nextValue;
|
|
202519
|
+
}
|
|
202520
|
+
if (!isJsonObject4(currentValue) || isEmptyObject2(currentValue)) return {
|
|
202521
|
+
segments,
|
|
202522
|
+
tail: void 0,
|
|
202523
|
+
leafValue: currentValue
|
|
202524
|
+
};
|
|
202525
|
+
return {
|
|
202526
|
+
segments,
|
|
202527
|
+
tail: currentValue,
|
|
202528
|
+
leafValue: currentValue
|
|
202529
|
+
};
|
|
202530
|
+
}
|
|
202531
|
+
function buildFoldedKey(segments) {
|
|
202532
|
+
return segments.join(DOT);
|
|
202533
|
+
}
|
|
202534
|
+
function encodePrimitive(value2, delimiter3) {
|
|
202535
|
+
if (value2 === null) return NULL_LITERAL;
|
|
202536
|
+
if (typeof value2 === "boolean") return String(value2);
|
|
202537
|
+
if (typeof value2 === "number") return String(value2);
|
|
202538
|
+
return encodeStringLiteral(value2, delimiter3);
|
|
202539
|
+
}
|
|
202540
|
+
function encodeStringLiteral(value2, delimiter3 = DEFAULT_DELIMITER) {
|
|
202541
|
+
if (isSafeUnquoted(value2, delimiter3)) return value2;
|
|
202542
|
+
return `${DOUBLE_QUOTE}${escapeString(value2)}${DOUBLE_QUOTE}`;
|
|
202543
|
+
}
|
|
202544
|
+
function encodeKey(key) {
|
|
202545
|
+
if (isValidUnquotedKey(key)) return key;
|
|
202546
|
+
return `${DOUBLE_QUOTE}${escapeString(key)}${DOUBLE_QUOTE}`;
|
|
202547
|
+
}
|
|
202548
|
+
function encodeAndJoinPrimitives(values, delimiter3 = DEFAULT_DELIMITER) {
|
|
202549
|
+
return values.map((v) => encodePrimitive(v, delimiter3)).join(delimiter3);
|
|
202550
|
+
}
|
|
202551
|
+
function formatHeader(length, options) {
|
|
202552
|
+
const key = options?.key;
|
|
202553
|
+
const fields = options?.fields;
|
|
202554
|
+
const delimiter3 = options?.delimiter ?? COMMA;
|
|
202555
|
+
let header = "";
|
|
202556
|
+
if (key) header += encodeKey(key);
|
|
202557
|
+
header += `[${length}${delimiter3 !== DEFAULT_DELIMITER ? delimiter3 : ""}]`;
|
|
202558
|
+
if (fields) {
|
|
202559
|
+
const quotedFields = fields.map((f) => encodeKey(f));
|
|
202560
|
+
header += `{${quotedFields.join(delimiter3)}}`;
|
|
202561
|
+
}
|
|
202562
|
+
header += ":";
|
|
202563
|
+
return header;
|
|
202564
|
+
}
|
|
202565
|
+
function* encodeJsonValue(value2, options, depth) {
|
|
202566
|
+
if (isJsonPrimitive(value2)) {
|
|
202567
|
+
const encodedPrimitive = encodePrimitive(value2, options.delimiter);
|
|
202568
|
+
if (encodedPrimitive !== "") yield encodedPrimitive;
|
|
202569
|
+
return;
|
|
202570
|
+
}
|
|
202571
|
+
if (isJsonArray(value2)) yield* encodeArrayLines(void 0, value2, depth, options);
|
|
202572
|
+
else if (isJsonObject4(value2)) yield* encodeObjectLines(value2, depth, options);
|
|
202573
|
+
}
|
|
202574
|
+
function* encodeObjectLines(value2, depth, options, rootLiteralKeys, pathPrefix, remainingDepth) {
|
|
202575
|
+
const keys = Object.keys(value2);
|
|
202576
|
+
if (depth === 0 && !rootLiteralKeys) rootLiteralKeys = new Set(keys.filter((k2) => k2.includes(".")));
|
|
202577
|
+
const effectiveFlattenDepth = remainingDepth ?? options.flattenDepth;
|
|
202578
|
+
for (const [key, val] of Object.entries(value2)) yield* encodeKeyValuePairLines(key, val, depth, options, keys, rootLiteralKeys, pathPrefix, effectiveFlattenDepth);
|
|
202579
|
+
}
|
|
202580
|
+
function* encodeKeyValuePairLines(key, value2, depth, options, siblings, rootLiteralKeys, pathPrefix, flattenDepth) {
|
|
202581
|
+
const currentPath = pathPrefix ? `${pathPrefix}${DOT}${key}` : key;
|
|
202582
|
+
const effectiveFlattenDepth = flattenDepth ?? options.flattenDepth;
|
|
202583
|
+
if (options.keyFolding === "safe" && siblings) {
|
|
202584
|
+
const foldResult = tryFoldKeyChain(key, value2, siblings, options, rootLiteralKeys, pathPrefix, effectiveFlattenDepth);
|
|
202585
|
+
if (foldResult) {
|
|
202586
|
+
const { foldedKey, remainder, leafValue, segmentCount } = foldResult;
|
|
202587
|
+
const encodedFoldedKey = encodeKey(foldedKey);
|
|
202588
|
+
if (remainder === void 0) {
|
|
202589
|
+
if (isJsonPrimitive(leafValue)) {
|
|
202590
|
+
yield indentedLine(depth, `${encodedFoldedKey}: ${encodePrimitive(leafValue, options.delimiter)}`, options.indent);
|
|
202591
|
+
return;
|
|
202592
|
+
} else if (isJsonArray(leafValue)) {
|
|
202593
|
+
yield* encodeArrayLines(foldedKey, leafValue, depth, options);
|
|
202594
|
+
return;
|
|
202595
|
+
} else if (isJsonObject4(leafValue) && isEmptyObject2(leafValue)) {
|
|
202596
|
+
yield indentedLine(depth, `${encodedFoldedKey}:`, options.indent);
|
|
202597
|
+
return;
|
|
202598
|
+
}
|
|
202599
|
+
}
|
|
202600
|
+
if (isJsonObject4(remainder)) {
|
|
202601
|
+
yield indentedLine(depth, `${encodedFoldedKey}:`, options.indent);
|
|
202602
|
+
const remainingDepth = effectiveFlattenDepth - segmentCount;
|
|
202603
|
+
const foldedPath = pathPrefix ? `${pathPrefix}${DOT}${foldedKey}` : foldedKey;
|
|
202604
|
+
yield* encodeObjectLines(remainder, depth + 1, options, rootLiteralKeys, foldedPath, remainingDepth);
|
|
202605
|
+
return;
|
|
202606
|
+
}
|
|
202607
|
+
}
|
|
202608
|
+
}
|
|
202609
|
+
const encodedKey = encodeKey(key);
|
|
202610
|
+
if (isJsonPrimitive(value2)) yield indentedLine(depth, `${encodedKey}: ${encodePrimitive(value2, options.delimiter)}`, options.indent);
|
|
202611
|
+
else if (isJsonArray(value2)) yield* encodeArrayLines(key, value2, depth, options);
|
|
202612
|
+
else if (isJsonObject4(value2)) {
|
|
202613
|
+
yield indentedLine(depth, `${encodedKey}:`, options.indent);
|
|
202614
|
+
if (!isEmptyObject2(value2)) yield* encodeObjectLines(value2, depth + 1, options, rootLiteralKeys, currentPath, effectiveFlattenDepth);
|
|
202615
|
+
}
|
|
202616
|
+
}
|
|
202617
|
+
function* encodeArrayLines(key, value2, depth, options) {
|
|
202618
|
+
if (value2.length === 0) {
|
|
202619
|
+
yield indentedLine(depth, formatHeader(0, {
|
|
202620
|
+
key,
|
|
202621
|
+
delimiter: options.delimiter
|
|
202622
|
+
}), options.indent);
|
|
202623
|
+
return;
|
|
202624
|
+
}
|
|
202625
|
+
if (isArrayOfPrimitives(value2)) {
|
|
202626
|
+
yield indentedLine(depth, encodeInlineArrayLine(value2, options.delimiter, key), options.indent);
|
|
202627
|
+
return;
|
|
202628
|
+
}
|
|
202629
|
+
if (isArrayOfArrays(value2)) {
|
|
202630
|
+
if (value2.every((arr) => isArrayOfPrimitives(arr))) {
|
|
202631
|
+
yield* encodeArrayOfArraysAsListItemsLines(key, value2, depth, options);
|
|
202632
|
+
return;
|
|
202633
|
+
}
|
|
202634
|
+
}
|
|
202635
|
+
if (isArrayOfObjects(value2)) {
|
|
202636
|
+
const header = extractTabularHeader(value2);
|
|
202637
|
+
if (header) yield* encodeArrayOfObjectsAsTabularLines(key, value2, header, depth, options);
|
|
202638
|
+
else yield* encodeMixedArrayAsListItemsLines(key, value2, depth, options);
|
|
202639
|
+
return;
|
|
202640
|
+
}
|
|
202641
|
+
yield* encodeMixedArrayAsListItemsLines(key, value2, depth, options);
|
|
202642
|
+
}
|
|
202643
|
+
function* encodeArrayOfArraysAsListItemsLines(prefix, values, depth, options) {
|
|
202644
|
+
yield indentedLine(depth, formatHeader(values.length, {
|
|
202645
|
+
key: prefix,
|
|
202646
|
+
delimiter: options.delimiter
|
|
202647
|
+
}), options.indent);
|
|
202648
|
+
for (const arr of values) if (isArrayOfPrimitives(arr)) {
|
|
202649
|
+
const arrayLine = encodeInlineArrayLine(arr, options.delimiter);
|
|
202650
|
+
yield indentedListItem(depth + 1, arrayLine, options.indent);
|
|
202651
|
+
}
|
|
202652
|
+
}
|
|
202653
|
+
function encodeInlineArrayLine(values, delimiter3, prefix) {
|
|
202654
|
+
const header = formatHeader(values.length, {
|
|
202655
|
+
key: prefix,
|
|
202656
|
+
delimiter: delimiter3
|
|
202657
|
+
});
|
|
202658
|
+
const joinedValue = encodeAndJoinPrimitives(values, delimiter3);
|
|
202659
|
+
if (values.length === 0) return header;
|
|
202660
|
+
return `${header} ${joinedValue}`;
|
|
202661
|
+
}
|
|
202662
|
+
function* encodeArrayOfObjectsAsTabularLines(prefix, rows, header, depth, options) {
|
|
202663
|
+
yield indentedLine(depth, formatHeader(rows.length, {
|
|
202664
|
+
key: prefix,
|
|
202665
|
+
fields: header,
|
|
202666
|
+
delimiter: options.delimiter
|
|
202667
|
+
}), options.indent);
|
|
202668
|
+
yield* writeTabularRowsLines(rows, header, depth + 1, options);
|
|
202669
|
+
}
|
|
202670
|
+
function extractTabularHeader(rows) {
|
|
202671
|
+
if (rows.length === 0) return;
|
|
202672
|
+
const firstRow = rows[0];
|
|
202673
|
+
const firstKeys = Object.keys(firstRow);
|
|
202674
|
+
if (firstKeys.length === 0) return;
|
|
202675
|
+
if (isTabularArray(rows, firstKeys)) return firstKeys;
|
|
202676
|
+
}
|
|
202677
|
+
function isTabularArray(rows, header) {
|
|
202678
|
+
for (const row of rows) {
|
|
202679
|
+
if (Object.keys(row).length !== header.length) return false;
|
|
202680
|
+
for (const key of header) {
|
|
202681
|
+
if (!(key in row)) return false;
|
|
202682
|
+
if (!isJsonPrimitive(row[key])) return false;
|
|
202683
|
+
}
|
|
202684
|
+
}
|
|
202685
|
+
return true;
|
|
202686
|
+
}
|
|
202687
|
+
function* writeTabularRowsLines(rows, header, depth, options) {
|
|
202688
|
+
for (const row of rows) yield indentedLine(depth, encodeAndJoinPrimitives(header.map((key) => row[key]), options.delimiter), options.indent);
|
|
202689
|
+
}
|
|
202690
|
+
function* encodeMixedArrayAsListItemsLines(prefix, items, depth, options) {
|
|
202691
|
+
yield indentedLine(depth, formatHeader(items.length, {
|
|
202692
|
+
key: prefix,
|
|
202693
|
+
delimiter: options.delimiter
|
|
202694
|
+
}), options.indent);
|
|
202695
|
+
for (const item of items) yield* encodeListItemValueLines(item, depth + 1, options);
|
|
202696
|
+
}
|
|
202697
|
+
function* encodeObjectAsListItemLines(obj, depth, options) {
|
|
202698
|
+
if (isEmptyObject2(obj)) {
|
|
202699
|
+
yield indentedLine(depth, LIST_ITEM_MARKER, options.indent);
|
|
202700
|
+
return;
|
|
202701
|
+
}
|
|
202702
|
+
const entries = Object.entries(obj);
|
|
202703
|
+
if (entries.length === 1) {
|
|
202704
|
+
const [key, value2] = entries[0];
|
|
202705
|
+
if (isJsonArray(value2) && isArrayOfObjects(value2)) {
|
|
202706
|
+
const header = extractTabularHeader(value2);
|
|
202707
|
+
if (header) {
|
|
202708
|
+
yield indentedListItem(depth, formatHeader(value2.length, {
|
|
202709
|
+
key,
|
|
202710
|
+
fields: header,
|
|
202711
|
+
delimiter: options.delimiter
|
|
202712
|
+
}), options.indent);
|
|
202713
|
+
yield* writeTabularRowsLines(value2, header, depth + 1, options);
|
|
202714
|
+
return;
|
|
202715
|
+
}
|
|
202716
|
+
}
|
|
202717
|
+
}
|
|
202718
|
+
yield indentedLine(depth, LIST_ITEM_MARKER, options.indent);
|
|
202719
|
+
yield* encodeObjectLines(obj, depth + 1, options);
|
|
202720
|
+
}
|
|
202721
|
+
function* encodeListItemValueLines(value2, depth, options) {
|
|
202722
|
+
if (isJsonPrimitive(value2)) yield indentedListItem(depth, encodePrimitive(value2, options.delimiter), options.indent);
|
|
202723
|
+
else if (isJsonArray(value2)) if (isArrayOfPrimitives(value2)) yield indentedListItem(depth, encodeInlineArrayLine(value2, options.delimiter), options.indent);
|
|
202724
|
+
else {
|
|
202725
|
+
yield indentedListItem(depth, formatHeader(value2.length, { delimiter: options.delimiter }), options.indent);
|
|
202726
|
+
for (const item of value2) yield* encodeListItemValueLines(item, depth + 1, options);
|
|
202727
|
+
}
|
|
202728
|
+
else if (isJsonObject4(value2)) yield* encodeObjectAsListItemLines(value2, depth, options);
|
|
202729
|
+
}
|
|
202730
|
+
function indentedLine(depth, content, indentSize) {
|
|
202731
|
+
return " ".repeat(indentSize * depth) + content;
|
|
202732
|
+
}
|
|
202733
|
+
function indentedListItem(depth, content, indentSize) {
|
|
202734
|
+
return indentedLine(depth, LIST_ITEM_PREFIX + content, indentSize);
|
|
202735
|
+
}
|
|
202736
|
+
function encode6(input2, options) {
|
|
202737
|
+
return Array.from(encodeLines(input2, options)).join("\n");
|
|
202738
|
+
}
|
|
202739
|
+
function encodeLines(input2, options) {
|
|
202740
|
+
return encodeJsonValue(normalizeValue(input2), resolveOptions(options), 0);
|
|
202741
|
+
}
|
|
202742
|
+
function resolveOptions(options) {
|
|
202743
|
+
return {
|
|
202744
|
+
indent: options?.indent ?? 2,
|
|
202745
|
+
delimiter: options?.delimiter ?? DEFAULT_DELIMITER,
|
|
202746
|
+
keyFolding: options?.keyFolding ?? "off",
|
|
202747
|
+
flattenDepth: options?.flattenDepth ?? Number.POSITIVE_INFINITY
|
|
202748
|
+
};
|
|
202749
|
+
}
|
|
202750
|
+
|
|
202751
|
+
// mcp/geminiSanitizer.ts
|
|
202752
|
+
function parseStringEnumBranch(item) {
|
|
202753
|
+
if (!item || typeof item !== "object") return null;
|
|
202754
|
+
const record4 = item;
|
|
202755
|
+
if (Array.isArray(record4.enum)) {
|
|
202756
|
+
const strings = record4.enum.filter((v) => typeof v === "string");
|
|
202757
|
+
return strings.length === record4.enum.length && strings.length > 0 ? { values: strings } : null;
|
|
202758
|
+
}
|
|
202759
|
+
if (typeof record4.const === "string") {
|
|
202760
|
+
return { values: [record4.const] };
|
|
202761
|
+
}
|
|
202762
|
+
return null;
|
|
202763
|
+
}
|
|
202764
|
+
function collapseStringUnion(branches) {
|
|
202765
|
+
const values = [];
|
|
202766
|
+
for (const item of branches) {
|
|
202767
|
+
const parsed2 = parseStringEnumBranch(item);
|
|
202768
|
+
if (!parsed2) return null;
|
|
202769
|
+
values.push(...parsed2.values);
|
|
202770
|
+
}
|
|
202771
|
+
if (values.length === 0) return null;
|
|
202772
|
+
return { type: "string", enum: [...new Set(values)] };
|
|
202773
|
+
}
|
|
202774
|
+
function sanitizeForGemini(schema2) {
|
|
202775
|
+
if (!schema2 || typeof schema2 !== "object") return schema2;
|
|
202776
|
+
if (Array.isArray(schema2)) return schema2.map(sanitizeForGemini);
|
|
202777
|
+
const source = schema2;
|
|
202778
|
+
if (Array.isArray(source.enum) && typeof source.type !== "string") {
|
|
202779
|
+
const allStrings = source.enum.every((v) => typeof v === "string");
|
|
202780
|
+
if (allStrings) {
|
|
202781
|
+
const result = { type: "string", enum: source.enum };
|
|
202782
|
+
if (typeof source.description === "string") result.description = source.description;
|
|
202783
|
+
return result;
|
|
202784
|
+
}
|
|
202785
|
+
}
|
|
202786
|
+
for (const unionKey of ["anyOf", "oneOf"]) {
|
|
202787
|
+
const branches = source[unionKey];
|
|
202788
|
+
if (Array.isArray(branches) && branches.length > 0) {
|
|
202789
|
+
const collapsed = collapseStringUnion(branches);
|
|
202790
|
+
if (collapsed) {
|
|
202791
|
+
const result = { ...collapsed };
|
|
202792
|
+
if (typeof source.description === "string") result.description = source.description;
|
|
202793
|
+
return result;
|
|
202794
|
+
}
|
|
202795
|
+
}
|
|
202796
|
+
}
|
|
202797
|
+
if (Array.isArray(source.anyOf) || Array.isArray(source.oneOf)) {
|
|
202798
|
+
const result = {};
|
|
202799
|
+
if (Array.isArray(source.anyOf)) result.anyOf = source.anyOf.map(sanitizeForGemini);
|
|
202800
|
+
if (Array.isArray(source.oneOf)) result.oneOf = source.oneOf.map(sanitizeForGemini);
|
|
202801
|
+
return result;
|
|
202802
|
+
}
|
|
202803
|
+
const sanitized = {};
|
|
202804
|
+
for (const [key, value2] of Object.entries(source)) {
|
|
202805
|
+
if (key === "$schema") continue;
|
|
202806
|
+
if (key === "$defs") {
|
|
202807
|
+
sanitized.definitions = sanitizeForGemini(value2);
|
|
202808
|
+
continue;
|
|
202809
|
+
}
|
|
202810
|
+
sanitized[key] = sanitizeForGemini(value2);
|
|
202811
|
+
}
|
|
202812
|
+
return sanitized;
|
|
202813
|
+
}
|
|
202814
|
+
function wrapJsonSchemaProducer(producer) {
|
|
202815
|
+
return new Proxy(producer, {
|
|
202816
|
+
get(target, prop, receiver) {
|
|
202817
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
202818
|
+
if ((prop === "input" || prop === "output") && typeof value2 === "function") {
|
|
202819
|
+
const fn2 = value2;
|
|
202820
|
+
return (...args2) => sanitizeForGemini(fn2.apply(target, args2));
|
|
202821
|
+
}
|
|
202822
|
+
return value2;
|
|
202823
|
+
}
|
|
202824
|
+
});
|
|
202825
|
+
}
|
|
202826
|
+
function wrapStandard(standard) {
|
|
202827
|
+
return new Proxy(standard, {
|
|
202828
|
+
get(target, prop, receiver) {
|
|
202829
|
+
if (prop === "jsonSchema") {
|
|
202830
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
202831
|
+
if (value2 && typeof value2 === "object") {
|
|
202832
|
+
return wrapJsonSchemaProducer(value2);
|
|
202833
|
+
}
|
|
202834
|
+
return value2;
|
|
202835
|
+
}
|
|
202836
|
+
return Reflect.get(target, prop, receiver);
|
|
202837
|
+
}
|
|
202838
|
+
});
|
|
202839
|
+
}
|
|
202840
|
+
function wrapSchemaForGemini(schema2) {
|
|
202841
|
+
return new Proxy(schema2, {
|
|
202842
|
+
get(target, prop, receiver) {
|
|
202843
|
+
if (prop === "~standard") {
|
|
202844
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
202845
|
+
if (value2 && typeof value2 === "object") {
|
|
202846
|
+
return wrapStandard(value2);
|
|
202847
|
+
}
|
|
202848
|
+
return value2;
|
|
202849
|
+
}
|
|
202850
|
+
if (prop === "toJsonSchema") {
|
|
202851
|
+
const method = Reflect.get(target, prop, receiver);
|
|
202852
|
+
if (typeof method === "function") {
|
|
202853
|
+
return () => sanitizeForGemini(method.call(target));
|
|
202854
|
+
}
|
|
202855
|
+
return method;
|
|
202856
|
+
}
|
|
202857
|
+
return Reflect.get(target, prop, receiver);
|
|
202858
|
+
}
|
|
202859
|
+
});
|
|
202860
|
+
}
|
|
202861
|
+
function sanitizeToolForGemini(tool2) {
|
|
202862
|
+
if (!tool2.parameters) return tool2;
|
|
202863
|
+
return { ...tool2, parameters: wrapSchemaForGemini(tool2.parameters) };
|
|
202864
|
+
}
|
|
202865
|
+
function isGeminiRouted(ctx) {
|
|
202866
|
+
const effective = ctx.payload.proxyModel ?? ctx.resolvedModel ?? ctx.payload.model;
|
|
202867
|
+
if (!effective) return true;
|
|
202868
|
+
const normalized = effective.toLowerCase();
|
|
202869
|
+
if (normalized.includes("gemini")) return true;
|
|
202870
|
+
if (!normalized.includes("/")) return true;
|
|
202871
|
+
return false;
|
|
202872
|
+
}
|
|
202873
|
+
|
|
202874
|
+
// mcp/shared.ts
|
|
202875
|
+
function getHttpStatus(err) {
|
|
202876
|
+
if (typeof err !== "object" || err === null || !("status" in err)) return void 0;
|
|
202877
|
+
return typeof err.status === "number" ? err.status : void 0;
|
|
202878
|
+
}
|
|
202879
|
+
var tool = (toolDef) => toolDef;
|
|
202880
|
+
var handleToolSuccess = (data) => {
|
|
202881
|
+
const text = typeof data === "string" ? data : encode6(data);
|
|
202882
|
+
return {
|
|
202883
|
+
content: [{ type: "text", text }]
|
|
202884
|
+
};
|
|
202885
|
+
};
|
|
202886
|
+
var handleToolError = (error109) => {
|
|
202887
|
+
const errorMessage = error109 instanceof Error ? error109.message : String(error109);
|
|
202888
|
+
return {
|
|
202889
|
+
content: [
|
|
202890
|
+
{
|
|
202891
|
+
type: "text",
|
|
202892
|
+
text: `Error: ${errorMessage}`
|
|
202893
|
+
}
|
|
202894
|
+
],
|
|
202895
|
+
isError: true
|
|
202896
|
+
};
|
|
202897
|
+
};
|
|
202898
|
+
var execute = (fn2, toolName) => {
|
|
202899
|
+
const _fn = async (params) => {
|
|
202900
|
+
try {
|
|
202901
|
+
const result = await fn2(params);
|
|
202902
|
+
return handleToolSuccess(result);
|
|
202903
|
+
} catch (error109) {
|
|
202904
|
+
const errorMessage = error109 instanceof Error ? error109.message : String(error109);
|
|
202905
|
+
const prefix = toolName ? `[${toolName}]` : "tool";
|
|
202906
|
+
log2.info(`${prefix} error: ${errorMessage}`);
|
|
202907
|
+
log2.debug(`${prefix} params: ${formatJsonValue(params)}`);
|
|
202908
|
+
return handleToolError(error109);
|
|
202909
|
+
}
|
|
202910
|
+
};
|
|
202911
|
+
return _fn;
|
|
202912
|
+
};
|
|
202913
|
+
var addTools = (ctx, server, tools) => {
|
|
202914
|
+
const shouldSanitize = isGeminiRouted(ctx);
|
|
202915
|
+
for (const tool2 of tools) {
|
|
202916
|
+
server.addTool(shouldSanitize ? sanitizeToolForGemini(tool2) : tool2);
|
|
202917
|
+
}
|
|
202918
|
+
return server;
|
|
202919
|
+
};
|
|
202920
|
+
|
|
202921
|
+
// mcp/shell.ts
|
|
202922
|
+
var ShellParams = type({
|
|
202923
|
+
command: "string",
|
|
202924
|
+
// advisory and unread — no code path consumes it. kept in the schema only
|
|
202925
|
+
// because it nudges the model to state intent before running a command;
|
|
202926
|
+
// REQUIRING it cost a full rejected turn every time one was omitted. #1140.
|
|
202927
|
+
"description?": "string",
|
|
202928
|
+
"timeout?": type.number.describe(
|
|
202929
|
+
"Timeout in MILLISECONDS (not seconds). Default 30000 (30s), max 120000 (2m). e.g. timeout: 180000 for 3 minutes; timeout: 180 means 180ms and will kill the process almost immediately."
|
|
202930
|
+
),
|
|
202931
|
+
"working_directory?": "string",
|
|
202932
|
+
"background?": "boolean"
|
|
202933
|
+
});
|
|
202934
|
+
var detectedSandboxMethod;
|
|
202935
|
+
var usernsProbeError;
|
|
202936
|
+
function getSandboxMethod() {
|
|
202937
|
+
return detectSandboxMethod();
|
|
202938
|
+
}
|
|
202939
|
+
function detectSandboxMethod() {
|
|
202940
|
+
if (detectedSandboxMethod !== void 0) {
|
|
202941
|
+
return detectedSandboxMethod;
|
|
202942
|
+
}
|
|
202943
|
+
if (process.env.CI !== "true") {
|
|
202944
|
+
detectedSandboxMethod = "none";
|
|
202945
|
+
log2.debug("sandbox disabled (CI !== true)");
|
|
202946
|
+
return "none";
|
|
202947
|
+
}
|
|
202948
|
+
try {
|
|
202949
|
+
const result = spawnSync7("unshare", ["--pid", "--fork", "--mount-proc", "true"], {
|
|
202950
|
+
timeout: 5e3,
|
|
202951
|
+
stdio: "ignore"
|
|
202952
|
+
});
|
|
202953
|
+
if (result.status === 0) {
|
|
202954
|
+
detectedSandboxMethod = "unshare";
|
|
202955
|
+
log2.debug("PID namespace isolation enabled (unprivileged unshare)");
|
|
202956
|
+
return "unshare";
|
|
202957
|
+
}
|
|
202958
|
+
} catch {
|
|
202959
|
+
}
|
|
202960
|
+
try {
|
|
202961
|
+
const result = spawnSync7("sudo", ["unshare", "--pid", "--fork", "--mount-proc", "true"], {
|
|
202962
|
+
timeout: 5e3,
|
|
202963
|
+
stdio: "ignore"
|
|
202964
|
+
});
|
|
202965
|
+
if (result.status === 0) {
|
|
202966
|
+
detectedSandboxMethod = "sudo-unshare";
|
|
202967
|
+
log2.debug("PID namespace isolation enabled (sudo unshare)");
|
|
202968
|
+
return "sudo-unshare";
|
|
202969
|
+
}
|
|
202970
|
+
} catch {
|
|
202971
|
+
}
|
|
202972
|
+
try {
|
|
202973
|
+
const result = spawnSync7(
|
|
202974
|
+
"unshare",
|
|
202975
|
+
[
|
|
202976
|
+
"--user",
|
|
202977
|
+
"--map-root-user",
|
|
202978
|
+
"--pid",
|
|
202979
|
+
"--fork",
|
|
202980
|
+
"--mount",
|
|
202981
|
+
"setpriv",
|
|
202982
|
+
"--no-new-privs",
|
|
202983
|
+
"--bounding-set",
|
|
202984
|
+
"-all",
|
|
202985
|
+
"--inh-caps",
|
|
202986
|
+
"-all",
|
|
202987
|
+
"--ambient-caps",
|
|
202988
|
+
"-all",
|
|
202989
|
+
"true"
|
|
202990
|
+
],
|
|
202991
|
+
{ timeout: 5e3, stdio: ["ignore", "ignore", "pipe"] }
|
|
202992
|
+
);
|
|
202993
|
+
if (result.status === 0) {
|
|
202994
|
+
detectedSandboxMethod = "userns-unshare";
|
|
202995
|
+
log2.debug("PID namespace isolation enabled (unprivileged userns unshare)");
|
|
202996
|
+
return "userns-unshare";
|
|
202997
|
+
}
|
|
202998
|
+
usernsProbeError = result.stderr?.toString().trim() || result.error?.message;
|
|
202999
|
+
} catch {
|
|
203000
|
+
}
|
|
203001
|
+
detectedSandboxMethod = "none";
|
|
203002
|
+
log2.info("PID namespace isolation not available");
|
|
203003
|
+
return "none";
|
|
203004
|
+
}
|
|
203005
|
+
var PROC_CLEANUP = "umount /proc 2>/dev/null; umount /proc 2>/dev/null; mount -t proc proc /proc 2>/dev/null;";
|
|
203006
|
+
var SOCKET_CLEANUP = [
|
|
203007
|
+
"/var/run/docker.sock",
|
|
203008
|
+
"/run/docker.sock",
|
|
203009
|
+
"/var/run/podman/podman.sock",
|
|
203010
|
+
"/run/podman/podman.sock",
|
|
203011
|
+
"/run/containerd/containerd.sock",
|
|
203012
|
+
"/var/run/crio/crio.sock"
|
|
203013
|
+
].map((path4) => `mount --bind /dev/null ${path4} 2>/dev/null;`).join(" ");
|
|
203014
|
+
function buildFsMounts(repoDir) {
|
|
203015
|
+
const escaped = repoDir.replace(/'/g, "'\\''");
|
|
203016
|
+
return [
|
|
203017
|
+
`mkdir -p /var/lib/pullfrog 2>/dev/null;`,
|
|
203018
|
+
`mount -t tmpfs tmpfs /var/lib/pullfrog 2>/dev/null;`,
|
|
203019
|
+
`[ -n "$RUNNER_TEMP" ] && [ -d "$RUNNER_TEMP/_runner_file_commands" ] && mount -t tmpfs tmpfs "$RUNNER_TEMP/_runner_file_commands" 2>/dev/null;`,
|
|
203020
|
+
`[ -e '${escaped}/.git' ] && mount --bind '${escaped}/.git' '${escaped}/.git' 2>/dev/null && mount -o remount,bind,ro '${escaped}/.git' 2>/dev/null;`,
|
|
203021
|
+
// the corepack shim dir sits FIRST on PATH (utils/packageManager.ts) and
|
|
203022
|
+
// PATH survives filterEnv, so a writable pm-bin lets sandboxed code plant
|
|
203023
|
+
// a `git`/`pnpm` that a later UNSANDBOXED spawn resolves with the parent
|
|
203024
|
+
// env. read-only closes that for every sandboxed command at once.
|
|
203025
|
+
//
|
|
203026
|
+
// the path is interpolated HERE rather than read as `$PULLFROG_TEMP_DIR`
|
|
203027
|
+
// inside the sandbox: that name is not on the `filterEnv` safe list, so in
|
|
203028
|
+
// restricted mode it expands to empty and the mount silently no-ops in the
|
|
203029
|
+
// one tier it exists to protect. `$RUNNER_TEMP` above is safe only because
|
|
203030
|
+
// `RUNNER_` is an allowed prefix.
|
|
203031
|
+
shimMount()
|
|
203032
|
+
].filter(Boolean).join(" ");
|
|
203033
|
+
}
|
|
203034
|
+
function shimMount() {
|
|
203035
|
+
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
203036
|
+
if (!tempDir) return "";
|
|
203037
|
+
const dir = join11(tempDir, "pm-bin").replace(/'/g, "'\\''");
|
|
203038
|
+
return `[ -d '${dir}' ] && mount --bind '${dir}' '${dir}' 2>/dev/null && mount -o remount,bind,ro '${dir}' 2>/dev/null;`;
|
|
203039
|
+
}
|
|
203040
|
+
var _repoRoot;
|
|
203041
|
+
function resolveRepoRoot() {
|
|
203042
|
+
if (_repoRoot) return _repoRoot;
|
|
203043
|
+
const fromEnv = process.env.GITHUB_WORKSPACE;
|
|
203044
|
+
if (fromEnv) {
|
|
203045
|
+
_repoRoot = fromEnv;
|
|
203046
|
+
return _repoRoot;
|
|
203047
|
+
}
|
|
203048
|
+
try {
|
|
203049
|
+
_repoRoot = spawnSync7("git", ["rev-parse", "--show-toplevel"], {
|
|
203050
|
+
cwd: process.cwd(),
|
|
203051
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
203052
|
+
encoding: "utf-8"
|
|
203053
|
+
}).stdout?.trim();
|
|
203054
|
+
} catch {
|
|
203055
|
+
}
|
|
203056
|
+
if (!_repoRoot) _repoRoot = process.cwd();
|
|
203057
|
+
return _repoRoot;
|
|
203058
|
+
}
|
|
203059
|
+
function spawnShell(params) {
|
|
203060
|
+
const spawnOpts = { env: params.env, cwd: params.cwd, stdio: params.stdio, detached: true };
|
|
203061
|
+
const sandboxMethod = detectSandboxMethod();
|
|
203062
|
+
const ci = process.env.CI === "true";
|
|
203063
|
+
if (ci && sandboxMethod === "none") {
|
|
203064
|
+
throw new Error(
|
|
203065
|
+
"PID-namespace isolation is required in CI but unavailable: unprivileged unshare, sudo unshare, and userns-nested unshare all failed. " + (usernsProbeError ? `userns probe said: ${usernsProbeError}. ` : "") + "on a containerized / Kubernetes self-hosted runner the cause is usually one of: the pod's seccomp profile blocks user-namespace creation (Pod Security Standards baseline/restricted); the pod runs as root without CAP_SETFCAP, which the kernel requires to map uid 0; or the node kernel disallows unprivileged user namespaces (Ubuntu >= 23.10, Bottlerocket, Talos). elsewhere, check that `unshare` and `setpriv` (util-linux >= 2.31, which is what added --ambient-caps) are on PATH. see https://docs.pullfrog.com/security#self-hosted-runners"
|
|
203066
|
+
);
|
|
203067
|
+
}
|
|
203068
|
+
const repoRoot = resolveRepoRoot();
|
|
203069
|
+
const fsMounts = buildFsMounts(repoRoot);
|
|
203070
|
+
if (sandboxMethod === "unshare") {
|
|
203071
|
+
return spawn5(
|
|
203072
|
+
"unshare",
|
|
203073
|
+
[
|
|
203074
|
+
"--pid",
|
|
203075
|
+
"--fork",
|
|
203076
|
+
"--mount-proc",
|
|
203077
|
+
"bash",
|
|
203078
|
+
"-c",
|
|
203079
|
+
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} ${params.command}`
|
|
203080
|
+
],
|
|
203081
|
+
spawnOpts
|
|
203082
|
+
);
|
|
203083
|
+
}
|
|
203084
|
+
if (sandboxMethod === "userns-unshare") {
|
|
203085
|
+
const escaped = params.command.replace(/'/g, "'\\''");
|
|
203086
|
+
return spawn5(
|
|
203087
|
+
"unshare",
|
|
203088
|
+
[
|
|
203089
|
+
"--user",
|
|
203090
|
+
"--map-root-user",
|
|
203091
|
+
"--pid",
|
|
203092
|
+
"--fork",
|
|
203093
|
+
"--mount",
|
|
203094
|
+
"bash",
|
|
203095
|
+
"-c",
|
|
203096
|
+
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec setpriv --no-new-privs --bounding-set -all --inh-caps -all --ambient-caps -all bash -c '${escaped}'`
|
|
203097
|
+
],
|
|
203098
|
+
spawnOpts
|
|
203099
|
+
);
|
|
203100
|
+
}
|
|
203101
|
+
if (sandboxMethod === "sudo-unshare") {
|
|
203102
|
+
const envArgs = [];
|
|
203103
|
+
for (const [k2, v] of Object.entries(params.env)) {
|
|
203104
|
+
if (v !== void 0) {
|
|
203105
|
+
envArgs.push(`${k2}=${v}`);
|
|
203106
|
+
}
|
|
203107
|
+
}
|
|
203108
|
+
const username = userInfo2().username;
|
|
203109
|
+
const pathRestore = 'export PATH="${SANDBOX_PATH:-$PATH}"; ';
|
|
203110
|
+
const escaped = (pathRestore + params.command).replace(/'/g, "'\\''");
|
|
203111
|
+
envArgs.push(`SANDBOX_PATH=${params.env.PATH ?? ""}`);
|
|
203112
|
+
return spawn5(
|
|
203113
|
+
"sudo",
|
|
203114
|
+
[
|
|
203115
|
+
"env",
|
|
203116
|
+
...envArgs,
|
|
203117
|
+
"unshare",
|
|
203118
|
+
"--pid",
|
|
203119
|
+
"--fork",
|
|
203120
|
+
"--mount-proc",
|
|
203121
|
+
"bash",
|
|
203122
|
+
"-c",
|
|
203123
|
+
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec su -p -s /bin/bash ${username} -c '${escaped}'`
|
|
203124
|
+
],
|
|
203125
|
+
{ ...spawnOpts, env: {} }
|
|
203126
|
+
);
|
|
203127
|
+
}
|
|
203128
|
+
return spawn5("bash", ["-c", params.command], spawnOpts);
|
|
203129
|
+
}
|
|
203130
|
+
async function killProcessGroup(proc) {
|
|
203131
|
+
if (!proc.pid) return;
|
|
203132
|
+
try {
|
|
203133
|
+
process.kill(-proc.pid, "SIGTERM");
|
|
203134
|
+
await new Promise((r2) => setTimeout(r2, 200));
|
|
203135
|
+
process.kill(-proc.pid, "SIGKILL");
|
|
203136
|
+
} catch {
|
|
203137
|
+
try {
|
|
203138
|
+
proc.kill("SIGKILL");
|
|
203139
|
+
} catch {
|
|
203140
|
+
}
|
|
203141
|
+
}
|
|
203142
|
+
}
|
|
203143
|
+
function getTempDir() {
|
|
203144
|
+
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
203145
|
+
if (!tempDir) {
|
|
203146
|
+
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
203147
|
+
}
|
|
203148
|
+
return tempDir;
|
|
203149
|
+
}
|
|
203150
|
+
var MAX_OUTPUT_CHARS = 5e3;
|
|
203151
|
+
async function runSandboxed(params) {
|
|
203152
|
+
const proc = spawnShell({
|
|
203153
|
+
command: params.command,
|
|
203154
|
+
env: params.env,
|
|
203155
|
+
cwd: params.cwd,
|
|
203156
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
203157
|
+
});
|
|
203158
|
+
const stdout2 = new TailBuffer(DEFAULT_MAX_RETAINED_BYTES);
|
|
203159
|
+
const stderr = new TailBuffer(DEFAULT_MAX_RETAINED_BYTES);
|
|
203160
|
+
let timedOut = false, exited = false, spawnError = "";
|
|
203161
|
+
proc.stdout?.on("data", (chunk) => {
|
|
203162
|
+
const text = chunk.toString();
|
|
203163
|
+
stdout2.append(text);
|
|
203164
|
+
params.onStdout?.(text);
|
|
203165
|
+
});
|
|
203166
|
+
proc.stderr?.on("data", (chunk) => {
|
|
203167
|
+
const text = chunk.toString();
|
|
203168
|
+
stderr.append(text);
|
|
203169
|
+
params.onStderr?.(text);
|
|
203170
|
+
});
|
|
203171
|
+
const timeoutId = setTimeout(async () => {
|
|
203172
|
+
if (!exited) {
|
|
203173
|
+
timedOut = true;
|
|
203174
|
+
await killProcessGroup(proc);
|
|
203175
|
+
}
|
|
203176
|
+
}, params.timeout);
|
|
203177
|
+
const exitCode = await new Promise((resolve3) => {
|
|
203178
|
+
const done = (code) => {
|
|
203179
|
+
exited = true;
|
|
203180
|
+
clearTimeout(timeoutId);
|
|
203181
|
+
resolve3(code);
|
|
203182
|
+
};
|
|
203183
|
+
proc.on("exit", done);
|
|
203184
|
+
proc.on("error", (err) => {
|
|
203185
|
+
spawnError = err.message;
|
|
203186
|
+
done(null);
|
|
203187
|
+
});
|
|
203188
|
+
});
|
|
203189
|
+
proc.stdout?.destroy();
|
|
203190
|
+
proc.stderr?.destroy();
|
|
203191
|
+
const outText = stdout2.toString();
|
|
203192
|
+
const errText = stderr.toString();
|
|
203193
|
+
let output2 = errText ? outText ? `${outText}
|
|
203194
|
+
${errText}` : errText : outText;
|
|
203195
|
+
if (spawnError) output2 = output2 ? `${output2}
|
|
203196
|
+
${spawnError}` : spawnError;
|
|
203197
|
+
if (timedOut)
|
|
203198
|
+
output2 = output2 ? `${output2}
|
|
203199
|
+
[timed out after ${params.timeout}ms]` : `[timed out after ${params.timeout}ms]`;
|
|
203200
|
+
return {
|
|
203201
|
+
output: output2.trim(),
|
|
203202
|
+
exitCode: exitCode ?? (timedOut ? 124 : -1),
|
|
203203
|
+
timedOut
|
|
203204
|
+
};
|
|
203205
|
+
}
|
|
203206
|
+
function capOutput(output2) {
|
|
203207
|
+
if (output2.length <= MAX_OUTPUT_CHARS) return output2;
|
|
203208
|
+
const fullPath = join11(getTempDir(), `shell-${randomUUID3().slice(0, 8)}.log`);
|
|
203209
|
+
writeFileSync9(fullPath, output2);
|
|
203210
|
+
const elided = output2.length - MAX_OUTPUT_CHARS;
|
|
203211
|
+
return `... [${elided} chars truncated; output saved to ${fullPath}] ...
|
|
203212
|
+
${output2.slice(-MAX_OUTPUT_CHARS)}`;
|
|
203213
|
+
}
|
|
203214
|
+
function isGitCommand(command) {
|
|
203215
|
+
const trimmed = command.trim();
|
|
203216
|
+
if (trimmed === "git" || trimmed.startsWith("git ")) return true;
|
|
203217
|
+
if (trimmed.startsWith("sudo git")) return true;
|
|
203218
|
+
return /[;&|]\s*(?:sudo\s+)?git(?:\s|$)/.test(trimmed);
|
|
203219
|
+
}
|
|
203220
|
+
function ShellTool(ctx) {
|
|
203221
|
+
return tool({
|
|
203222
|
+
name: "shell",
|
|
203223
|
+
timeoutMs: 12e4,
|
|
203224
|
+
description: `Execute shell commands securely. Environment is filtered to remove API keys and secrets.
|
|
203225
|
+
|
|
203226
|
+
Example: \`shell({ command: "pnpm test", description: "run the test suite" })\`.
|
|
203227
|
+
|
|
203228
|
+
Use this tool to:
|
|
203229
|
+
- Run shell commands (ls, cat, grep, find, etc.)
|
|
203230
|
+
- Execute build tools (npm, pnpm, cargo, make, etc.)
|
|
203231
|
+
- Run tests and linters
|
|
203232
|
+
|
|
203233
|
+
Output is capped at ${MAX_OUTPUT_CHARS} chars: if exceeded, only the tail is returned and the captured output is saved to a tempfile (path included in the response). Re-read the tempfile with cat/tail/grep when you need more.
|
|
203234
|
+
|
|
203235
|
+
Do NOT use this tool for git commands \u2014 use the dedicated git tools instead.`,
|
|
203236
|
+
parameters: ShellParams,
|
|
203237
|
+
execute: execute(async (params) => {
|
|
203238
|
+
if (isGitCommand(params.command)) {
|
|
203239
|
+
throw new Error(
|
|
203240
|
+
'git commands are not allowed in the shell tool. use the dedicated git tools instead:\n- git: local operations, e.g. git({ command: "log", args: ["--oneline"] }) \u2014 `command` is the subcommand ONLY\n- push_branch: push to remote (handles authentication)\n- git_fetch: fetch from remote (handles authentication)\n- checkout_pr: check out PR branches'
|
|
203241
|
+
);
|
|
203242
|
+
}
|
|
203243
|
+
const timeout = Math.min(params.timeout ?? 3e4, 12e4);
|
|
203244
|
+
const cwd = params.working_directory ?? process.cwd();
|
|
203245
|
+
const env2 = resolveEnv(ctx.payload.shell === "enabled" ? "inherit" : "restricted");
|
|
203246
|
+
if (params.command.includes("agent-browser")) {
|
|
203247
|
+
const daemonError = ensureBrowserDaemon(ctx.toolState);
|
|
203248
|
+
if (daemonError) {
|
|
203249
|
+
return {
|
|
203250
|
+
output: `browser daemon unavailable: ${daemonError}`,
|
|
203251
|
+
exit_code: 1,
|
|
203252
|
+
timed_out: false
|
|
203253
|
+
};
|
|
203254
|
+
}
|
|
203255
|
+
const binDir = ctx.toolState.browserDaemon?.binDir;
|
|
203256
|
+
if (binDir) {
|
|
203257
|
+
env2.PATH = `${binDir}:${env2.PATH ?? ""}`;
|
|
203258
|
+
}
|
|
203259
|
+
}
|
|
203260
|
+
if (params.background) {
|
|
203261
|
+
const tempDir = getTempDir();
|
|
203262
|
+
const handle = `bg-${randomUUID3().slice(0, 8)}`;
|
|
203263
|
+
const outputPath = join11(tempDir, `${handle}.log`);
|
|
203264
|
+
const pidPath = join11(tempDir, `${handle}.pid`);
|
|
203265
|
+
const logFd = openSync(outputPath, "a");
|
|
203266
|
+
let proc;
|
|
203267
|
+
try {
|
|
203268
|
+
proc = spawnShell({
|
|
203269
|
+
command: params.command,
|
|
203270
|
+
env: env2,
|
|
203271
|
+
cwd,
|
|
203272
|
+
stdio: ["ignore", logFd, logFd]
|
|
203273
|
+
});
|
|
203274
|
+
} finally {
|
|
203275
|
+
closeSync(logFd);
|
|
203276
|
+
}
|
|
203277
|
+
if (!proc.pid) {
|
|
203278
|
+
throw new Error("failed to start background process");
|
|
203279
|
+
}
|
|
203280
|
+
proc.unref();
|
|
203281
|
+
writeFileSync9(pidPath, `${proc.pid}
|
|
203282
|
+
`);
|
|
203283
|
+
ctx.toolState.backgroundProcesses.set(handle, { pid: proc.pid, outputPath, pidPath });
|
|
203284
|
+
return {
|
|
203285
|
+
handle,
|
|
203286
|
+
outputPath,
|
|
203287
|
+
pidPath,
|
|
203288
|
+
message: `started background process ${handle} (pid ${proc.pid})`
|
|
203289
|
+
};
|
|
203290
|
+
}
|
|
203291
|
+
const result = await runSandboxed({ command: params.command, env: env2, cwd, timeout });
|
|
203292
|
+
if (result.exitCode !== 0) {
|
|
203293
|
+
log2.info(`shell command failed with exit code ${result.exitCode}: ${params.command}`);
|
|
203294
|
+
if (result.output) log2.info(`output: ${result.output}`);
|
|
203295
|
+
}
|
|
203296
|
+
return {
|
|
203297
|
+
output: capOutput(result.output),
|
|
203298
|
+
exit_code: result.exitCode,
|
|
203299
|
+
timed_out: result.timedOut
|
|
203300
|
+
};
|
|
203301
|
+
})
|
|
203302
|
+
});
|
|
203303
|
+
}
|
|
203304
|
+
var KillBackgroundParams = type({
|
|
203305
|
+
handle: type.string.describe("The handle of the background process to kill (e.g., bg-a1b2c3d4)")
|
|
203306
|
+
});
|
|
203307
|
+
function KillBackgroundTool(ctx) {
|
|
203308
|
+
return tool({
|
|
203309
|
+
name: "kill_background",
|
|
203310
|
+
mutates: true,
|
|
203311
|
+
description: `Kill a background process by its handle. Use this to stop dev servers or other long-running processes started with shell({ background: true }).`,
|
|
203312
|
+
parameters: KillBackgroundParams,
|
|
203313
|
+
execute: execute(async (params) => {
|
|
203314
|
+
const proc = ctx.toolState.backgroundProcesses.get(params.handle);
|
|
203315
|
+
if (!proc) {
|
|
203316
|
+
return {
|
|
203317
|
+
success: false,
|
|
203318
|
+
message: `no background process with handle ${params.handle}`
|
|
203319
|
+
};
|
|
203320
|
+
}
|
|
203321
|
+
try {
|
|
203322
|
+
process.kill(-proc.pid, "SIGTERM");
|
|
203323
|
+
} catch {
|
|
203324
|
+
}
|
|
203325
|
+
await sleep2(200);
|
|
203326
|
+
try {
|
|
203327
|
+
process.kill(-proc.pid, "SIGKILL");
|
|
203328
|
+
} catch {
|
|
203329
|
+
}
|
|
203330
|
+
ctx.toolState.backgroundProcesses.delete(params.handle);
|
|
203331
|
+
return {
|
|
203332
|
+
success: true,
|
|
203333
|
+
message: `killed background process ${params.handle} (pid ${proc.pid})`
|
|
203334
|
+
};
|
|
203335
|
+
})
|
|
203336
|
+
});
|
|
203337
|
+
}
|
|
203338
|
+
|
|
202385
203339
|
// utils/lifecycle.ts
|
|
202386
203340
|
function describeSetupFailure(failure2) {
|
|
202387
203341
|
if (!failure2) return "";
|
|
@@ -202403,44 +203357,47 @@ ${failure2.output || "(empty)"}`;
|
|
|
202403
203357
|
async function executeLifecycleHook(params) {
|
|
202404
203358
|
if (!params.script) return {};
|
|
202405
203359
|
log2.info(`\xBB executing ${params.event} lifecycle hook...`);
|
|
203360
|
+
resolveTrustedGit();
|
|
202406
203361
|
const preHookTrackedCount = params.normalizeWorkingTreeAfter ? (await runGitLines(["diff", "--name-only", "HEAD"])).length : 0;
|
|
202407
203362
|
let result;
|
|
202408
203363
|
try {
|
|
203364
|
+
let sandboxed;
|
|
202409
203365
|
try {
|
|
202410
|
-
|
|
202411
|
-
|
|
202412
|
-
|
|
202413
|
-
|
|
203366
|
+
sandboxed = await runSandboxed({
|
|
203367
|
+
command: params.script,
|
|
203368
|
+
env: resolveEnv(params.shell === "enabled" ? "inherit" : "restricted"),
|
|
203369
|
+
cwd: process.cwd(),
|
|
202414
203370
|
timeout: LIFECYCLE_HOOK_TIMEOUT_MS,
|
|
202415
|
-
activityTimeout: 0,
|
|
202416
203371
|
onStdout: (chunk) => process.stdout.write(chunk),
|
|
202417
203372
|
onStderr: (chunk) => process.stderr.write(chunk)
|
|
202418
203373
|
});
|
|
202419
|
-
if (spawnResult.exitCode !== 0) {
|
|
202420
|
-
const output2 = (spawnResult.stderr || spawnResult.stdout).trim();
|
|
202421
|
-
result = {
|
|
202422
|
-
failure: { kind: "exit", output: output2, exitCode: spawnResult.exitCode },
|
|
202423
|
-
warning: `lifecycle hook '${params.event}' failed with exit code ${spawnResult.exitCode}. output: ${output2 || "(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.`
|
|
202424
|
-
};
|
|
202425
|
-
} else {
|
|
202426
|
-
log2.info(`\xBB ${params.event} lifecycle hook completed successfully`);
|
|
202427
|
-
result = {};
|
|
202428
|
-
}
|
|
202429
203374
|
} catch (err) {
|
|
202430
|
-
const
|
|
202431
|
-
|
|
202432
|
-
|
|
202433
|
-
|
|
202434
|
-
|
|
202435
|
-
|
|
202436
|
-
|
|
202437
|
-
|
|
202438
|
-
|
|
202439
|
-
|
|
202440
|
-
|
|
202441
|
-
|
|
202442
|
-
|
|
202443
|
-
|
|
203375
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
203376
|
+
return {
|
|
203377
|
+
failure: { kind: "spawn", spawnError: msg },
|
|
203378
|
+
warning: `lifecycle hook '${params.event}' could not be sandboxed, so it did not run: ${msg}`
|
|
203379
|
+
};
|
|
203380
|
+
}
|
|
203381
|
+
if (sandboxed.timedOut) {
|
|
203382
|
+
const minutes = Math.round(LIFECYCLE_HOOK_TIMEOUT_MS / 6e4);
|
|
203383
|
+
result = {
|
|
203384
|
+
failure: { kind: "timeout" },
|
|
203385
|
+
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).`
|
|
203386
|
+
};
|
|
203387
|
+
} else if (sandboxed.exitCode === -1) {
|
|
203388
|
+
result = {
|
|
203389
|
+
failure: { kind: "spawn", spawnError: sandboxed.output },
|
|
203390
|
+
warning: `lifecycle hook '${params.event}' failed to spawn: ${sandboxed.output}. this is likely a transient failure \u2014 retry the operation.`
|
|
203391
|
+
};
|
|
203392
|
+
} else if (sandboxed.exitCode !== 0) {
|
|
203393
|
+
const output2 = capOutput(sandboxed.output);
|
|
203394
|
+
result = {
|
|
203395
|
+
failure: { kind: "exit", output: output2, exitCode: sandboxed.exitCode },
|
|
203396
|
+
warning: `lifecycle hook '${params.event}' failed with exit code ${sandboxed.exitCode}. output: ${output2 || "(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. if it failed on a missing environment variable, the repo's env allowlist has to name it \u2014 hooks run with the same filtered env as the shell tool.`
|
|
203397
|
+
};
|
|
203398
|
+
} else {
|
|
203399
|
+
log2.info(`\xBB ${params.event} lifecycle hook completed successfully`);
|
|
203400
|
+
result = {};
|
|
202444
203401
|
}
|
|
202445
203402
|
} finally {
|
|
202446
203403
|
if (params.normalizeWorkingTreeAfter) {
|
|
@@ -202461,8 +203418,20 @@ async function normalizeWorkingTreeAfterHook(params) {
|
|
|
202461
203418
|
await runGit(["restore", "--staged", "--worktree", "."]);
|
|
202462
203419
|
log2.info(`\xBB discarded ${trackedCount} tracked changes from ${params.event} hook`);
|
|
202463
203420
|
}
|
|
203421
|
+
var trustedGit;
|
|
203422
|
+
function resolveTrustedGit() {
|
|
203423
|
+
if (trustedGit) return trustedGit;
|
|
203424
|
+
const resolved = spawnSync8("sh", ["-c", "command -v git"], { encoding: "utf-8" });
|
|
203425
|
+
trustedGit = resolved.stdout?.trim() || "git";
|
|
203426
|
+
return trustedGit;
|
|
203427
|
+
}
|
|
202464
203428
|
async function runGit(args2) {
|
|
202465
|
-
const result = await spawn3({
|
|
203429
|
+
const result = await spawn3({
|
|
203430
|
+
cmd: resolveTrustedGit(),
|
|
203431
|
+
args: args2,
|
|
203432
|
+
env: filterEnvForUntrustedCode(),
|
|
203433
|
+
activityTimeout: 0
|
|
203434
|
+
});
|
|
202466
203435
|
if (result.exitCode !== 0) {
|
|
202467
203436
|
throw new Error(
|
|
202468
203437
|
`git ${args2.join(" ")} failed (exit ${result.exitCode}): ${result.stderr.trim() || "(no stderr)"}`
|
|
@@ -202582,7 +203551,7 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
|
|
|
202582
203551
|
|
|
202583
203552
|
// yes/index.ts
|
|
202584
203553
|
import { performance as performance8 } from "node:perf_hooks";
|
|
202585
|
-
import { setTimeout as
|
|
203554
|
+
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
202586
203555
|
|
|
202587
203556
|
// node_modules/.pnpm/lru-cache@11.5.2/node_modules/lru-cache/dist/esm/node/index.min.js
|
|
202588
203557
|
import { tracingChannel as G, channel as P } from "node:diagnostics_channel";
|
|
@@ -203376,7 +204345,7 @@ function _op(fn2, options) {
|
|
|
203376
204345
|
);
|
|
203377
204346
|
log3.info({ key, error: error109, attempt });
|
|
203378
204347
|
}
|
|
203379
|
-
await
|
|
204348
|
+
await sleep3(delay2);
|
|
203380
204349
|
}
|
|
203381
204350
|
}
|
|
203382
204351
|
}
|
|
@@ -203444,14 +204413,14 @@ function _op(fn2, options) {
|
|
|
203444
204413
|
}
|
|
203445
204414
|
|
|
203446
204415
|
// mcp/git.ts
|
|
203447
|
-
import { randomUUID as
|
|
203448
|
-
import { writeFileSync as
|
|
203449
|
-
import { join as
|
|
204416
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
204417
|
+
import { writeFileSync as writeFileSync10 } from "node:fs";
|
|
204418
|
+
import { join as join14 } from "node:path";
|
|
203450
204419
|
|
|
203451
204420
|
// utils/apiCommit.ts
|
|
203452
204421
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
203453
204422
|
import { lstat, readlink } from "node:fs/promises";
|
|
203454
|
-
import { join as
|
|
204423
|
+
import { join as join12 } from "node:path";
|
|
203455
204424
|
var GITHUB_API = "https://api.github.com";
|
|
203456
204425
|
var MAX_BLOB_BYTES = 30 * 1024 * 1024;
|
|
203457
204426
|
var BLOB_UPLOAD_CONCURRENCY = 8;
|
|
@@ -203516,7 +204485,7 @@ async function assertApiCommittable(files) {
|
|
|
203516
204485
|
const root = getRepoRoot();
|
|
203517
204486
|
const attrs = $(
|
|
203518
204487
|
"git",
|
|
203519
|
-
["check-attr", "filter", "-z", "--", ...present.map((p) =>
|
|
204488
|
+
["check-attr", "filter", "-z", "--", ...present.map((p) => join12(root, p))],
|
|
203520
204489
|
{ log: false }
|
|
203521
204490
|
);
|
|
203522
204491
|
const parts = attrs.split("\0");
|
|
@@ -203528,7 +204497,7 @@ async function assertApiCommittable(files) {
|
|
|
203528
204497
|
}
|
|
203529
204498
|
}
|
|
203530
204499
|
for (const path4 of present) {
|
|
203531
|
-
const stat = await lstat(
|
|
204500
|
+
const stat = await lstat(join12(root, path4));
|
|
203532
204501
|
if (stat.isDirectory()) {
|
|
203533
204502
|
throw new Error(
|
|
203534
204503
|
`'${path4}' is a directory (nested repository or submodule?) \u2014 signed commits only support files and symlinks.`
|
|
@@ -203537,7 +204506,7 @@ async function assertApiCommittable(files) {
|
|
|
203537
204506
|
}
|
|
203538
204507
|
}
|
|
203539
204508
|
async function createBlobEntry(params) {
|
|
203540
|
-
const absPath =
|
|
204509
|
+
const absPath = join12(params.repoRoot, params.path);
|
|
203541
204510
|
const stat = await lstat(absPath);
|
|
203542
204511
|
if (stat.size > MAX_BLOB_BYTES) {
|
|
203543
204512
|
throw new Error(
|
|
@@ -203717,7 +204686,7 @@ this usually means your local branch has commits that were never pushed \u2014 s
|
|
|
203717
204686
|
var core5 = __toESM(require_core(), 1);
|
|
203718
204687
|
import { createSign } from "node:crypto";
|
|
203719
204688
|
import { rename, writeFile } from "node:fs/promises";
|
|
203720
|
-
import { dirname as dirname3, join as
|
|
204689
|
+
import { dirname as dirname3, join as join13 } from "node:path";
|
|
203721
204690
|
|
|
203722
204691
|
// node_modules/.pnpm/@octokit+plugin-throttling@11.0.5_@octokit+core@7.0.8/node_modules/@octokit/plugin-throttling/dist-bundle/index.js
|
|
203723
204692
|
var import_light = __toESM(require_light(), 1);
|
|
@@ -204089,7 +205058,7 @@ function lowercaseKeys(object6) {
|
|
|
204089
205058
|
return newObj;
|
|
204090
205059
|
}, {});
|
|
204091
205060
|
}
|
|
204092
|
-
function
|
|
205061
|
+
function isPlainObject8(value2) {
|
|
204093
205062
|
if (typeof value2 !== "object" || value2 === null) return false;
|
|
204094
205063
|
if (Object.prototype.toString.call(value2) !== "[object Object]") return false;
|
|
204095
205064
|
const proto = Object.getPrototypeOf(value2);
|
|
@@ -204100,7 +205069,7 @@ function isPlainObject7(value2) {
|
|
|
204100
205069
|
function mergeDeep(defaults2, options) {
|
|
204101
205070
|
const result = Object.assign({}, defaults2);
|
|
204102
205071
|
Object.keys(options).forEach((key) => {
|
|
204103
|
-
if (
|
|
205072
|
+
if (isPlainObject8(options[key])) {
|
|
204104
205073
|
if (!(key in defaults2)) Object.assign(result, { [key]: options[key] });
|
|
204105
205074
|
else result[key] = mergeDeep(defaults2[key], options[key]);
|
|
204106
205075
|
} else {
|
|
@@ -204387,7 +205356,7 @@ var NullObject = /* @__PURE__ */ (() => {
|
|
|
204387
205356
|
return C;
|
|
204388
205357
|
})();
|
|
204389
205358
|
function parse7(header, options) {
|
|
204390
|
-
const stopChar = options?.comma === true ?
|
|
205359
|
+
const stopChar = options?.comma === true ? COMMA2 : 65536;
|
|
204391
205360
|
const len = header.length;
|
|
204392
205361
|
let index = skipOWS(header, options?.start ?? 0, len);
|
|
204393
205362
|
const valueStart = index;
|
|
@@ -204405,7 +205374,7 @@ var SEMI = 59;
|
|
|
204405
205374
|
var EQ = 61;
|
|
204406
205375
|
var DQUOTE = 34;
|
|
204407
205376
|
var BSLASH = 92;
|
|
204408
|
-
var
|
|
205377
|
+
var COMMA2 = 44;
|
|
204409
205378
|
function parseParameters(header, type2, index, len, stopChar) {
|
|
204410
205379
|
const parameters = new NullObject();
|
|
204411
205380
|
parameter: while (index < len) {
|
|
@@ -204864,7 +205833,7 @@ var defaults_default = {
|
|
|
204864
205833
|
"user-agent": `octokit-request.js/${VERSION3} ${getUserAgent2()}`
|
|
204865
205834
|
}
|
|
204866
205835
|
};
|
|
204867
|
-
function
|
|
205836
|
+
function isPlainObject9(value2) {
|
|
204868
205837
|
if (typeof value2 !== "object" || value2 === null) return false;
|
|
204869
205838
|
if (Object.prototype.toString.call(value2) !== "[object Object]") return false;
|
|
204870
205839
|
const proto = Object.getPrototypeOf(value2);
|
|
@@ -204882,7 +205851,7 @@ async function fetchWrapper(requestOptions) {
|
|
|
204882
205851
|
}
|
|
204883
205852
|
const log4 = requestOptions.request?.log || console;
|
|
204884
205853
|
const parseSuccessResponseBody = requestOptions.request?.parseSuccessResponseBody !== false;
|
|
204885
|
-
const body =
|
|
205854
|
+
const body = isPlainObject9(requestOptions.body) || Array.isArray(requestOptions.body) ? JSONStringify(requestOptions.body) : requestOptions.body;
|
|
204886
205855
|
const requestHeaders = Object.fromEntries(
|
|
204887
205856
|
Object.entries(requestOptions.headers).map(([name, value2]) => [
|
|
204888
205857
|
name,
|
|
@@ -208187,7 +209156,7 @@ function getGitHubUsageSummary() {
|
|
|
208187
209156
|
}
|
|
208188
209157
|
async function writeGitHubUsageSummaryToFile(path4) {
|
|
208189
209158
|
const summary2 = getGitHubUsageSummary();
|
|
208190
|
-
const tmpPath =
|
|
209159
|
+
const tmpPath = join13(dirname3(path4), `.usage-summary-${process.pid}.tmp`);
|
|
208191
209160
|
await writeFile(tmpPath, JSON.stringify(summary2));
|
|
208192
209161
|
await rename(tmpPath, path4);
|
|
208193
209162
|
}
|
|
@@ -208294,531 +209263,6 @@ function resolveRepoCtx(ctx, repo) {
|
|
|
208294
209263
|
};
|
|
208295
209264
|
}
|
|
208296
209265
|
|
|
208297
|
-
// node_modules/.pnpm/@toon-format+toon@1.4.0/node_modules/@toon-format/toon/dist/index.mjs
|
|
208298
|
-
var LIST_ITEM_MARKER = "-";
|
|
208299
|
-
var LIST_ITEM_PREFIX = "- ";
|
|
208300
|
-
var COMMA2 = ",";
|
|
208301
|
-
var PIPE = "|";
|
|
208302
|
-
var DOT = ".";
|
|
208303
|
-
var NULL_LITERAL = "null";
|
|
208304
|
-
var TRUE_LITERAL = "true";
|
|
208305
|
-
var FALSE_LITERAL = "false";
|
|
208306
|
-
var BACKSLASH = "\\";
|
|
208307
|
-
var DOUBLE_QUOTE = '"';
|
|
208308
|
-
var TAB = " ";
|
|
208309
|
-
var DELIMITERS = {
|
|
208310
|
-
comma: COMMA2,
|
|
208311
|
-
tab: TAB,
|
|
208312
|
-
pipe: PIPE
|
|
208313
|
-
};
|
|
208314
|
-
var DEFAULT_DELIMITER = DELIMITERS.comma;
|
|
208315
|
-
function escapeString(value2) {
|
|
208316
|
-
return value2.replace(/\\/g, `${BACKSLASH}${BACKSLASH}`).replace(/"/g, `${BACKSLASH}${DOUBLE_QUOTE}`).replace(/\n/g, `${BACKSLASH}n`).replace(/\r/g, `${BACKSLASH}r`).replace(/\t/g, `${BACKSLASH}t`);
|
|
208317
|
-
}
|
|
208318
|
-
function isBooleanOrNullLiteral(token) {
|
|
208319
|
-
return token === TRUE_LITERAL || token === FALSE_LITERAL || token === NULL_LITERAL;
|
|
208320
|
-
}
|
|
208321
|
-
function normalizeValue(value2) {
|
|
208322
|
-
if (value2 === null) return null;
|
|
208323
|
-
if (typeof value2 === "string" || typeof value2 === "boolean") return value2;
|
|
208324
|
-
if (typeof value2 === "number") {
|
|
208325
|
-
if (Object.is(value2, -0)) return 0;
|
|
208326
|
-
if (!Number.isFinite(value2)) return null;
|
|
208327
|
-
return value2;
|
|
208328
|
-
}
|
|
208329
|
-
if (typeof value2 === "bigint") {
|
|
208330
|
-
if (value2 >= Number.MIN_SAFE_INTEGER && value2 <= Number.MAX_SAFE_INTEGER) return Number(value2);
|
|
208331
|
-
return value2.toString();
|
|
208332
|
-
}
|
|
208333
|
-
if (value2 instanceof Date) return value2.toISOString();
|
|
208334
|
-
if (Array.isArray(value2)) return value2.map(normalizeValue);
|
|
208335
|
-
if (value2 instanceof Set) return Array.from(value2).map(normalizeValue);
|
|
208336
|
-
if (value2 instanceof Map) return Object.fromEntries(Array.from(value2, ([k2, v]) => [String(k2), normalizeValue(v)]));
|
|
208337
|
-
if (isPlainObject9(value2)) {
|
|
208338
|
-
const normalized = {};
|
|
208339
|
-
for (const key in value2) if (Object.prototype.hasOwnProperty.call(value2, key)) normalized[key] = normalizeValue(value2[key]);
|
|
208340
|
-
return normalized;
|
|
208341
|
-
}
|
|
208342
|
-
return null;
|
|
208343
|
-
}
|
|
208344
|
-
function isJsonPrimitive(value2) {
|
|
208345
|
-
return value2 === null || typeof value2 === "string" || typeof value2 === "number" || typeof value2 === "boolean";
|
|
208346
|
-
}
|
|
208347
|
-
function isJsonArray(value2) {
|
|
208348
|
-
return Array.isArray(value2);
|
|
208349
|
-
}
|
|
208350
|
-
function isJsonObject4(value2) {
|
|
208351
|
-
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
208352
|
-
}
|
|
208353
|
-
function isEmptyObject2(value2) {
|
|
208354
|
-
return Object.keys(value2).length === 0;
|
|
208355
|
-
}
|
|
208356
|
-
function isPlainObject9(value2) {
|
|
208357
|
-
if (value2 === null || typeof value2 !== "object") return false;
|
|
208358
|
-
const prototype = Object.getPrototypeOf(value2);
|
|
208359
|
-
return prototype === null || prototype === Object.prototype;
|
|
208360
|
-
}
|
|
208361
|
-
function isArrayOfPrimitives(value2) {
|
|
208362
|
-
return value2.length === 0 || value2.every((item) => isJsonPrimitive(item));
|
|
208363
|
-
}
|
|
208364
|
-
function isArrayOfArrays(value2) {
|
|
208365
|
-
return value2.length === 0 || value2.every((item) => isJsonArray(item));
|
|
208366
|
-
}
|
|
208367
|
-
function isArrayOfObjects(value2) {
|
|
208368
|
-
return value2.length === 0 || value2.every((item) => isJsonObject4(item));
|
|
208369
|
-
}
|
|
208370
|
-
function isValidUnquotedKey(key) {
|
|
208371
|
-
return /^[A-Z_][\w.]*$/i.test(key);
|
|
208372
|
-
}
|
|
208373
|
-
function isIdentifierSegment(key) {
|
|
208374
|
-
return /^[A-Z_]\w*$/i.test(key);
|
|
208375
|
-
}
|
|
208376
|
-
function isSafeUnquoted(value2, delimiter3 = DEFAULT_DELIMITER) {
|
|
208377
|
-
if (!value2) return false;
|
|
208378
|
-
if (value2 !== value2.trim()) return false;
|
|
208379
|
-
if (isBooleanOrNullLiteral(value2) || isNumericLike(value2)) return false;
|
|
208380
|
-
if (value2.includes(":")) return false;
|
|
208381
|
-
if (value2.includes('"') || value2.includes("\\")) return false;
|
|
208382
|
-
if (/[[\]{}]/.test(value2)) return false;
|
|
208383
|
-
if (/[\n\r\t]/.test(value2)) return false;
|
|
208384
|
-
if (value2.includes(delimiter3)) return false;
|
|
208385
|
-
if (value2.startsWith(LIST_ITEM_MARKER)) return false;
|
|
208386
|
-
return true;
|
|
208387
|
-
}
|
|
208388
|
-
function isNumericLike(value2) {
|
|
208389
|
-
return /^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(value2) || /^0\d+$/.test(value2);
|
|
208390
|
-
}
|
|
208391
|
-
var QUOTED_KEY_MARKER = Symbol("quotedKey");
|
|
208392
|
-
function tryFoldKeyChain(key, value2, siblings, options, rootLiteralKeys, pathPrefix, flattenDepth) {
|
|
208393
|
-
if (options.keyFolding !== "safe") return;
|
|
208394
|
-
if (!isJsonObject4(value2)) return;
|
|
208395
|
-
const { segments, tail, leafValue } = collectSingleKeyChain(key, value2, flattenDepth ?? options.flattenDepth);
|
|
208396
|
-
if (segments.length < 2) return;
|
|
208397
|
-
if (!segments.every((seg) => isIdentifierSegment(seg))) return;
|
|
208398
|
-
const foldedKey = buildFoldedKey(segments);
|
|
208399
|
-
const absolutePath = pathPrefix ? `${pathPrefix}${DOT}${foldedKey}` : foldedKey;
|
|
208400
|
-
if (siblings.includes(foldedKey)) return;
|
|
208401
|
-
if (rootLiteralKeys && rootLiteralKeys.has(absolutePath)) return;
|
|
208402
|
-
return {
|
|
208403
|
-
foldedKey,
|
|
208404
|
-
remainder: tail,
|
|
208405
|
-
leafValue,
|
|
208406
|
-
segmentCount: segments.length
|
|
208407
|
-
};
|
|
208408
|
-
}
|
|
208409
|
-
function collectSingleKeyChain(startKey, startValue, maxDepth) {
|
|
208410
|
-
const segments = [startKey];
|
|
208411
|
-
let currentValue = startValue;
|
|
208412
|
-
while (segments.length < maxDepth) {
|
|
208413
|
-
if (!isJsonObject4(currentValue)) break;
|
|
208414
|
-
const keys = Object.keys(currentValue);
|
|
208415
|
-
if (keys.length !== 1) break;
|
|
208416
|
-
const nextKey = keys[0];
|
|
208417
|
-
const nextValue = currentValue[nextKey];
|
|
208418
|
-
segments.push(nextKey);
|
|
208419
|
-
currentValue = nextValue;
|
|
208420
|
-
}
|
|
208421
|
-
if (!isJsonObject4(currentValue) || isEmptyObject2(currentValue)) return {
|
|
208422
|
-
segments,
|
|
208423
|
-
tail: void 0,
|
|
208424
|
-
leafValue: currentValue
|
|
208425
|
-
};
|
|
208426
|
-
return {
|
|
208427
|
-
segments,
|
|
208428
|
-
tail: currentValue,
|
|
208429
|
-
leafValue: currentValue
|
|
208430
|
-
};
|
|
208431
|
-
}
|
|
208432
|
-
function buildFoldedKey(segments) {
|
|
208433
|
-
return segments.join(DOT);
|
|
208434
|
-
}
|
|
208435
|
-
function encodePrimitive(value2, delimiter3) {
|
|
208436
|
-
if (value2 === null) return NULL_LITERAL;
|
|
208437
|
-
if (typeof value2 === "boolean") return String(value2);
|
|
208438
|
-
if (typeof value2 === "number") return String(value2);
|
|
208439
|
-
return encodeStringLiteral(value2, delimiter3);
|
|
208440
|
-
}
|
|
208441
|
-
function encodeStringLiteral(value2, delimiter3 = DEFAULT_DELIMITER) {
|
|
208442
|
-
if (isSafeUnquoted(value2, delimiter3)) return value2;
|
|
208443
|
-
return `${DOUBLE_QUOTE}${escapeString(value2)}${DOUBLE_QUOTE}`;
|
|
208444
|
-
}
|
|
208445
|
-
function encodeKey(key) {
|
|
208446
|
-
if (isValidUnquotedKey(key)) return key;
|
|
208447
|
-
return `${DOUBLE_QUOTE}${escapeString(key)}${DOUBLE_QUOTE}`;
|
|
208448
|
-
}
|
|
208449
|
-
function encodeAndJoinPrimitives(values, delimiter3 = DEFAULT_DELIMITER) {
|
|
208450
|
-
return values.map((v) => encodePrimitive(v, delimiter3)).join(delimiter3);
|
|
208451
|
-
}
|
|
208452
|
-
function formatHeader(length, options) {
|
|
208453
|
-
const key = options?.key;
|
|
208454
|
-
const fields = options?.fields;
|
|
208455
|
-
const delimiter3 = options?.delimiter ?? COMMA2;
|
|
208456
|
-
let header = "";
|
|
208457
|
-
if (key) header += encodeKey(key);
|
|
208458
|
-
header += `[${length}${delimiter3 !== DEFAULT_DELIMITER ? delimiter3 : ""}]`;
|
|
208459
|
-
if (fields) {
|
|
208460
|
-
const quotedFields = fields.map((f) => encodeKey(f));
|
|
208461
|
-
header += `{${quotedFields.join(delimiter3)}}`;
|
|
208462
|
-
}
|
|
208463
|
-
header += ":";
|
|
208464
|
-
return header;
|
|
208465
|
-
}
|
|
208466
|
-
function* encodeJsonValue(value2, options, depth) {
|
|
208467
|
-
if (isJsonPrimitive(value2)) {
|
|
208468
|
-
const encodedPrimitive = encodePrimitive(value2, options.delimiter);
|
|
208469
|
-
if (encodedPrimitive !== "") yield encodedPrimitive;
|
|
208470
|
-
return;
|
|
208471
|
-
}
|
|
208472
|
-
if (isJsonArray(value2)) yield* encodeArrayLines(void 0, value2, depth, options);
|
|
208473
|
-
else if (isJsonObject4(value2)) yield* encodeObjectLines(value2, depth, options);
|
|
208474
|
-
}
|
|
208475
|
-
function* encodeObjectLines(value2, depth, options, rootLiteralKeys, pathPrefix, remainingDepth) {
|
|
208476
|
-
const keys = Object.keys(value2);
|
|
208477
|
-
if (depth === 0 && !rootLiteralKeys) rootLiteralKeys = new Set(keys.filter((k2) => k2.includes(".")));
|
|
208478
|
-
const effectiveFlattenDepth = remainingDepth ?? options.flattenDepth;
|
|
208479
|
-
for (const [key, val] of Object.entries(value2)) yield* encodeKeyValuePairLines(key, val, depth, options, keys, rootLiteralKeys, pathPrefix, effectiveFlattenDepth);
|
|
208480
|
-
}
|
|
208481
|
-
function* encodeKeyValuePairLines(key, value2, depth, options, siblings, rootLiteralKeys, pathPrefix, flattenDepth) {
|
|
208482
|
-
const currentPath = pathPrefix ? `${pathPrefix}${DOT}${key}` : key;
|
|
208483
|
-
const effectiveFlattenDepth = flattenDepth ?? options.flattenDepth;
|
|
208484
|
-
if (options.keyFolding === "safe" && siblings) {
|
|
208485
|
-
const foldResult = tryFoldKeyChain(key, value2, siblings, options, rootLiteralKeys, pathPrefix, effectiveFlattenDepth);
|
|
208486
|
-
if (foldResult) {
|
|
208487
|
-
const { foldedKey, remainder, leafValue, segmentCount } = foldResult;
|
|
208488
|
-
const encodedFoldedKey = encodeKey(foldedKey);
|
|
208489
|
-
if (remainder === void 0) {
|
|
208490
|
-
if (isJsonPrimitive(leafValue)) {
|
|
208491
|
-
yield indentedLine(depth, `${encodedFoldedKey}: ${encodePrimitive(leafValue, options.delimiter)}`, options.indent);
|
|
208492
|
-
return;
|
|
208493
|
-
} else if (isJsonArray(leafValue)) {
|
|
208494
|
-
yield* encodeArrayLines(foldedKey, leafValue, depth, options);
|
|
208495
|
-
return;
|
|
208496
|
-
} else if (isJsonObject4(leafValue) && isEmptyObject2(leafValue)) {
|
|
208497
|
-
yield indentedLine(depth, `${encodedFoldedKey}:`, options.indent);
|
|
208498
|
-
return;
|
|
208499
|
-
}
|
|
208500
|
-
}
|
|
208501
|
-
if (isJsonObject4(remainder)) {
|
|
208502
|
-
yield indentedLine(depth, `${encodedFoldedKey}:`, options.indent);
|
|
208503
|
-
const remainingDepth = effectiveFlattenDepth - segmentCount;
|
|
208504
|
-
const foldedPath = pathPrefix ? `${pathPrefix}${DOT}${foldedKey}` : foldedKey;
|
|
208505
|
-
yield* encodeObjectLines(remainder, depth + 1, options, rootLiteralKeys, foldedPath, remainingDepth);
|
|
208506
|
-
return;
|
|
208507
|
-
}
|
|
208508
|
-
}
|
|
208509
|
-
}
|
|
208510
|
-
const encodedKey = encodeKey(key);
|
|
208511
|
-
if (isJsonPrimitive(value2)) yield indentedLine(depth, `${encodedKey}: ${encodePrimitive(value2, options.delimiter)}`, options.indent);
|
|
208512
|
-
else if (isJsonArray(value2)) yield* encodeArrayLines(key, value2, depth, options);
|
|
208513
|
-
else if (isJsonObject4(value2)) {
|
|
208514
|
-
yield indentedLine(depth, `${encodedKey}:`, options.indent);
|
|
208515
|
-
if (!isEmptyObject2(value2)) yield* encodeObjectLines(value2, depth + 1, options, rootLiteralKeys, currentPath, effectiveFlattenDepth);
|
|
208516
|
-
}
|
|
208517
|
-
}
|
|
208518
|
-
function* encodeArrayLines(key, value2, depth, options) {
|
|
208519
|
-
if (value2.length === 0) {
|
|
208520
|
-
yield indentedLine(depth, formatHeader(0, {
|
|
208521
|
-
key,
|
|
208522
|
-
delimiter: options.delimiter
|
|
208523
|
-
}), options.indent);
|
|
208524
|
-
return;
|
|
208525
|
-
}
|
|
208526
|
-
if (isArrayOfPrimitives(value2)) {
|
|
208527
|
-
yield indentedLine(depth, encodeInlineArrayLine(value2, options.delimiter, key), options.indent);
|
|
208528
|
-
return;
|
|
208529
|
-
}
|
|
208530
|
-
if (isArrayOfArrays(value2)) {
|
|
208531
|
-
if (value2.every((arr) => isArrayOfPrimitives(arr))) {
|
|
208532
|
-
yield* encodeArrayOfArraysAsListItemsLines(key, value2, depth, options);
|
|
208533
|
-
return;
|
|
208534
|
-
}
|
|
208535
|
-
}
|
|
208536
|
-
if (isArrayOfObjects(value2)) {
|
|
208537
|
-
const header = extractTabularHeader(value2);
|
|
208538
|
-
if (header) yield* encodeArrayOfObjectsAsTabularLines(key, value2, header, depth, options);
|
|
208539
|
-
else yield* encodeMixedArrayAsListItemsLines(key, value2, depth, options);
|
|
208540
|
-
return;
|
|
208541
|
-
}
|
|
208542
|
-
yield* encodeMixedArrayAsListItemsLines(key, value2, depth, options);
|
|
208543
|
-
}
|
|
208544
|
-
function* encodeArrayOfArraysAsListItemsLines(prefix, values, depth, options) {
|
|
208545
|
-
yield indentedLine(depth, formatHeader(values.length, {
|
|
208546
|
-
key: prefix,
|
|
208547
|
-
delimiter: options.delimiter
|
|
208548
|
-
}), options.indent);
|
|
208549
|
-
for (const arr of values) if (isArrayOfPrimitives(arr)) {
|
|
208550
|
-
const arrayLine = encodeInlineArrayLine(arr, options.delimiter);
|
|
208551
|
-
yield indentedListItem(depth + 1, arrayLine, options.indent);
|
|
208552
|
-
}
|
|
208553
|
-
}
|
|
208554
|
-
function encodeInlineArrayLine(values, delimiter3, prefix) {
|
|
208555
|
-
const header = formatHeader(values.length, {
|
|
208556
|
-
key: prefix,
|
|
208557
|
-
delimiter: delimiter3
|
|
208558
|
-
});
|
|
208559
|
-
const joinedValue = encodeAndJoinPrimitives(values, delimiter3);
|
|
208560
|
-
if (values.length === 0) return header;
|
|
208561
|
-
return `${header} ${joinedValue}`;
|
|
208562
|
-
}
|
|
208563
|
-
function* encodeArrayOfObjectsAsTabularLines(prefix, rows, header, depth, options) {
|
|
208564
|
-
yield indentedLine(depth, formatHeader(rows.length, {
|
|
208565
|
-
key: prefix,
|
|
208566
|
-
fields: header,
|
|
208567
|
-
delimiter: options.delimiter
|
|
208568
|
-
}), options.indent);
|
|
208569
|
-
yield* writeTabularRowsLines(rows, header, depth + 1, options);
|
|
208570
|
-
}
|
|
208571
|
-
function extractTabularHeader(rows) {
|
|
208572
|
-
if (rows.length === 0) return;
|
|
208573
|
-
const firstRow = rows[0];
|
|
208574
|
-
const firstKeys = Object.keys(firstRow);
|
|
208575
|
-
if (firstKeys.length === 0) return;
|
|
208576
|
-
if (isTabularArray(rows, firstKeys)) return firstKeys;
|
|
208577
|
-
}
|
|
208578
|
-
function isTabularArray(rows, header) {
|
|
208579
|
-
for (const row of rows) {
|
|
208580
|
-
if (Object.keys(row).length !== header.length) return false;
|
|
208581
|
-
for (const key of header) {
|
|
208582
|
-
if (!(key in row)) return false;
|
|
208583
|
-
if (!isJsonPrimitive(row[key])) return false;
|
|
208584
|
-
}
|
|
208585
|
-
}
|
|
208586
|
-
return true;
|
|
208587
|
-
}
|
|
208588
|
-
function* writeTabularRowsLines(rows, header, depth, options) {
|
|
208589
|
-
for (const row of rows) yield indentedLine(depth, encodeAndJoinPrimitives(header.map((key) => row[key]), options.delimiter), options.indent);
|
|
208590
|
-
}
|
|
208591
|
-
function* encodeMixedArrayAsListItemsLines(prefix, items, depth, options) {
|
|
208592
|
-
yield indentedLine(depth, formatHeader(items.length, {
|
|
208593
|
-
key: prefix,
|
|
208594
|
-
delimiter: options.delimiter
|
|
208595
|
-
}), options.indent);
|
|
208596
|
-
for (const item of items) yield* encodeListItemValueLines(item, depth + 1, options);
|
|
208597
|
-
}
|
|
208598
|
-
function* encodeObjectAsListItemLines(obj, depth, options) {
|
|
208599
|
-
if (isEmptyObject2(obj)) {
|
|
208600
|
-
yield indentedLine(depth, LIST_ITEM_MARKER, options.indent);
|
|
208601
|
-
return;
|
|
208602
|
-
}
|
|
208603
|
-
const entries = Object.entries(obj);
|
|
208604
|
-
if (entries.length === 1) {
|
|
208605
|
-
const [key, value2] = entries[0];
|
|
208606
|
-
if (isJsonArray(value2) && isArrayOfObjects(value2)) {
|
|
208607
|
-
const header = extractTabularHeader(value2);
|
|
208608
|
-
if (header) {
|
|
208609
|
-
yield indentedListItem(depth, formatHeader(value2.length, {
|
|
208610
|
-
key,
|
|
208611
|
-
fields: header,
|
|
208612
|
-
delimiter: options.delimiter
|
|
208613
|
-
}), options.indent);
|
|
208614
|
-
yield* writeTabularRowsLines(value2, header, depth + 1, options);
|
|
208615
|
-
return;
|
|
208616
|
-
}
|
|
208617
|
-
}
|
|
208618
|
-
}
|
|
208619
|
-
yield indentedLine(depth, LIST_ITEM_MARKER, options.indent);
|
|
208620
|
-
yield* encodeObjectLines(obj, depth + 1, options);
|
|
208621
|
-
}
|
|
208622
|
-
function* encodeListItemValueLines(value2, depth, options) {
|
|
208623
|
-
if (isJsonPrimitive(value2)) yield indentedListItem(depth, encodePrimitive(value2, options.delimiter), options.indent);
|
|
208624
|
-
else if (isJsonArray(value2)) if (isArrayOfPrimitives(value2)) yield indentedListItem(depth, encodeInlineArrayLine(value2, options.delimiter), options.indent);
|
|
208625
|
-
else {
|
|
208626
|
-
yield indentedListItem(depth, formatHeader(value2.length, { delimiter: options.delimiter }), options.indent);
|
|
208627
|
-
for (const item of value2) yield* encodeListItemValueLines(item, depth + 1, options);
|
|
208628
|
-
}
|
|
208629
|
-
else if (isJsonObject4(value2)) yield* encodeObjectAsListItemLines(value2, depth, options);
|
|
208630
|
-
}
|
|
208631
|
-
function indentedLine(depth, content, indentSize) {
|
|
208632
|
-
return " ".repeat(indentSize * depth) + content;
|
|
208633
|
-
}
|
|
208634
|
-
function indentedListItem(depth, content, indentSize) {
|
|
208635
|
-
return indentedLine(depth, LIST_ITEM_PREFIX + content, indentSize);
|
|
208636
|
-
}
|
|
208637
|
-
function encode6(input2, options) {
|
|
208638
|
-
return Array.from(encodeLines(input2, options)).join("\n");
|
|
208639
|
-
}
|
|
208640
|
-
function encodeLines(input2, options) {
|
|
208641
|
-
return encodeJsonValue(normalizeValue(input2), resolveOptions(options), 0);
|
|
208642
|
-
}
|
|
208643
|
-
function resolveOptions(options) {
|
|
208644
|
-
return {
|
|
208645
|
-
indent: options?.indent ?? 2,
|
|
208646
|
-
delimiter: options?.delimiter ?? DEFAULT_DELIMITER,
|
|
208647
|
-
keyFolding: options?.keyFolding ?? "off",
|
|
208648
|
-
flattenDepth: options?.flattenDepth ?? Number.POSITIVE_INFINITY
|
|
208649
|
-
};
|
|
208650
|
-
}
|
|
208651
|
-
|
|
208652
|
-
// mcp/geminiSanitizer.ts
|
|
208653
|
-
function parseStringEnumBranch(item) {
|
|
208654
|
-
if (!item || typeof item !== "object") return null;
|
|
208655
|
-
const record4 = item;
|
|
208656
|
-
if (Array.isArray(record4.enum)) {
|
|
208657
|
-
const strings = record4.enum.filter((v) => typeof v === "string");
|
|
208658
|
-
return strings.length === record4.enum.length && strings.length > 0 ? { values: strings } : null;
|
|
208659
|
-
}
|
|
208660
|
-
if (typeof record4.const === "string") {
|
|
208661
|
-
return { values: [record4.const] };
|
|
208662
|
-
}
|
|
208663
|
-
return null;
|
|
208664
|
-
}
|
|
208665
|
-
function collapseStringUnion(branches) {
|
|
208666
|
-
const values = [];
|
|
208667
|
-
for (const item of branches) {
|
|
208668
|
-
const parsed2 = parseStringEnumBranch(item);
|
|
208669
|
-
if (!parsed2) return null;
|
|
208670
|
-
values.push(...parsed2.values);
|
|
208671
|
-
}
|
|
208672
|
-
if (values.length === 0) return null;
|
|
208673
|
-
return { type: "string", enum: [...new Set(values)] };
|
|
208674
|
-
}
|
|
208675
|
-
function sanitizeForGemini(schema2) {
|
|
208676
|
-
if (!schema2 || typeof schema2 !== "object") return schema2;
|
|
208677
|
-
if (Array.isArray(schema2)) return schema2.map(sanitizeForGemini);
|
|
208678
|
-
const source = schema2;
|
|
208679
|
-
if (Array.isArray(source.enum) && typeof source.type !== "string") {
|
|
208680
|
-
const allStrings = source.enum.every((v) => typeof v === "string");
|
|
208681
|
-
if (allStrings) {
|
|
208682
|
-
const result = { type: "string", enum: source.enum };
|
|
208683
|
-
if (typeof source.description === "string") result.description = source.description;
|
|
208684
|
-
return result;
|
|
208685
|
-
}
|
|
208686
|
-
}
|
|
208687
|
-
for (const unionKey of ["anyOf", "oneOf"]) {
|
|
208688
|
-
const branches = source[unionKey];
|
|
208689
|
-
if (Array.isArray(branches) && branches.length > 0) {
|
|
208690
|
-
const collapsed = collapseStringUnion(branches);
|
|
208691
|
-
if (collapsed) {
|
|
208692
|
-
const result = { ...collapsed };
|
|
208693
|
-
if (typeof source.description === "string") result.description = source.description;
|
|
208694
|
-
return result;
|
|
208695
|
-
}
|
|
208696
|
-
}
|
|
208697
|
-
}
|
|
208698
|
-
if (Array.isArray(source.anyOf) || Array.isArray(source.oneOf)) {
|
|
208699
|
-
const result = {};
|
|
208700
|
-
if (Array.isArray(source.anyOf)) result.anyOf = source.anyOf.map(sanitizeForGemini);
|
|
208701
|
-
if (Array.isArray(source.oneOf)) result.oneOf = source.oneOf.map(sanitizeForGemini);
|
|
208702
|
-
return result;
|
|
208703
|
-
}
|
|
208704
|
-
const sanitized = {};
|
|
208705
|
-
for (const [key, value2] of Object.entries(source)) {
|
|
208706
|
-
if (key === "$schema") continue;
|
|
208707
|
-
if (key === "$defs") {
|
|
208708
|
-
sanitized.definitions = sanitizeForGemini(value2);
|
|
208709
|
-
continue;
|
|
208710
|
-
}
|
|
208711
|
-
sanitized[key] = sanitizeForGemini(value2);
|
|
208712
|
-
}
|
|
208713
|
-
return sanitized;
|
|
208714
|
-
}
|
|
208715
|
-
function wrapJsonSchemaProducer(producer) {
|
|
208716
|
-
return new Proxy(producer, {
|
|
208717
|
-
get(target, prop, receiver) {
|
|
208718
|
-
const value2 = Reflect.get(target, prop, receiver);
|
|
208719
|
-
if ((prop === "input" || prop === "output") && typeof value2 === "function") {
|
|
208720
|
-
const fn2 = value2;
|
|
208721
|
-
return (...args2) => sanitizeForGemini(fn2.apply(target, args2));
|
|
208722
|
-
}
|
|
208723
|
-
return value2;
|
|
208724
|
-
}
|
|
208725
|
-
});
|
|
208726
|
-
}
|
|
208727
|
-
function wrapStandard(standard) {
|
|
208728
|
-
return new Proxy(standard, {
|
|
208729
|
-
get(target, prop, receiver) {
|
|
208730
|
-
if (prop === "jsonSchema") {
|
|
208731
|
-
const value2 = Reflect.get(target, prop, receiver);
|
|
208732
|
-
if (value2 && typeof value2 === "object") {
|
|
208733
|
-
return wrapJsonSchemaProducer(value2);
|
|
208734
|
-
}
|
|
208735
|
-
return value2;
|
|
208736
|
-
}
|
|
208737
|
-
return Reflect.get(target, prop, receiver);
|
|
208738
|
-
}
|
|
208739
|
-
});
|
|
208740
|
-
}
|
|
208741
|
-
function wrapSchemaForGemini(schema2) {
|
|
208742
|
-
return new Proxy(schema2, {
|
|
208743
|
-
get(target, prop, receiver) {
|
|
208744
|
-
if (prop === "~standard") {
|
|
208745
|
-
const value2 = Reflect.get(target, prop, receiver);
|
|
208746
|
-
if (value2 && typeof value2 === "object") {
|
|
208747
|
-
return wrapStandard(value2);
|
|
208748
|
-
}
|
|
208749
|
-
return value2;
|
|
208750
|
-
}
|
|
208751
|
-
if (prop === "toJsonSchema") {
|
|
208752
|
-
const method = Reflect.get(target, prop, receiver);
|
|
208753
|
-
if (typeof method === "function") {
|
|
208754
|
-
return () => sanitizeForGemini(method.call(target));
|
|
208755
|
-
}
|
|
208756
|
-
return method;
|
|
208757
|
-
}
|
|
208758
|
-
return Reflect.get(target, prop, receiver);
|
|
208759
|
-
}
|
|
208760
|
-
});
|
|
208761
|
-
}
|
|
208762
|
-
function sanitizeToolForGemini(tool2) {
|
|
208763
|
-
if (!tool2.parameters) return tool2;
|
|
208764
|
-
return { ...tool2, parameters: wrapSchemaForGemini(tool2.parameters) };
|
|
208765
|
-
}
|
|
208766
|
-
function isGeminiRouted(ctx) {
|
|
208767
|
-
const effective = ctx.payload.proxyModel ?? ctx.resolvedModel ?? ctx.payload.model;
|
|
208768
|
-
if (!effective) return true;
|
|
208769
|
-
const normalized = effective.toLowerCase();
|
|
208770
|
-
if (normalized.includes("gemini")) return true;
|
|
208771
|
-
if (!normalized.includes("/")) return true;
|
|
208772
|
-
return false;
|
|
208773
|
-
}
|
|
208774
|
-
|
|
208775
|
-
// mcp/shared.ts
|
|
208776
|
-
function getHttpStatus(err) {
|
|
208777
|
-
if (typeof err !== "object" || err === null || !("status" in err)) return void 0;
|
|
208778
|
-
return typeof err.status === "number" ? err.status : void 0;
|
|
208779
|
-
}
|
|
208780
|
-
var tool = (toolDef) => toolDef;
|
|
208781
|
-
var handleToolSuccess = (data) => {
|
|
208782
|
-
const text = typeof data === "string" ? data : encode6(data);
|
|
208783
|
-
return {
|
|
208784
|
-
content: [{ type: "text", text }]
|
|
208785
|
-
};
|
|
208786
|
-
};
|
|
208787
|
-
var handleToolError = (error109) => {
|
|
208788
|
-
const errorMessage = error109 instanceof Error ? error109.message : String(error109);
|
|
208789
|
-
return {
|
|
208790
|
-
content: [
|
|
208791
|
-
{
|
|
208792
|
-
type: "text",
|
|
208793
|
-
text: `Error: ${errorMessage}`
|
|
208794
|
-
}
|
|
208795
|
-
],
|
|
208796
|
-
isError: true
|
|
208797
|
-
};
|
|
208798
|
-
};
|
|
208799
|
-
var execute = (fn2, toolName) => {
|
|
208800
|
-
const _fn = async (params) => {
|
|
208801
|
-
try {
|
|
208802
|
-
const result = await fn2(params);
|
|
208803
|
-
return handleToolSuccess(result);
|
|
208804
|
-
} catch (error109) {
|
|
208805
|
-
const errorMessage = error109 instanceof Error ? error109.message : String(error109);
|
|
208806
|
-
const prefix = toolName ? `[${toolName}]` : "tool";
|
|
208807
|
-
log2.info(`${prefix} error: ${errorMessage}`);
|
|
208808
|
-
log2.debug(`${prefix} params: ${formatJsonValue(params)}`);
|
|
208809
|
-
return handleToolError(error109);
|
|
208810
|
-
}
|
|
208811
|
-
};
|
|
208812
|
-
return _fn;
|
|
208813
|
-
};
|
|
208814
|
-
var addTools = (ctx, server, tools) => {
|
|
208815
|
-
const shouldSanitize = isGeminiRouted(ctx);
|
|
208816
|
-
for (const tool2 of tools) {
|
|
208817
|
-
server.addTool(shouldSanitize ? sanitizeToolForGemini(tool2) : tool2);
|
|
208818
|
-
}
|
|
208819
|
-
return server;
|
|
208820
|
-
};
|
|
208821
|
-
|
|
208822
209266
|
// mcp/git.ts
|
|
208823
209267
|
function getPushDestination(branch, storedDest, cwd) {
|
|
208824
209268
|
if (storedDest && storedDest.localBranch === branch) {
|
|
@@ -208944,7 +209388,8 @@ async function runPrepushHook(ctx, retryTool) {
|
|
|
208944
209388
|
if (!ctx.prepushScript) return false;
|
|
208945
209389
|
const prepushHook = await executeLifecycleHook({
|
|
208946
209390
|
event: "prepush",
|
|
208947
|
-
script: ctx.prepushScript
|
|
209391
|
+
script: ctx.prepushScript,
|
|
209392
|
+
shell: ctx.payload.shell
|
|
208948
209393
|
});
|
|
208949
209394
|
if (prepushHook.failure) {
|
|
208950
209395
|
ctx.toolState.prepushFailureCount += 1;
|
|
@@ -209339,8 +209784,8 @@ function countAhead(head, base, cwd) {
|
|
|
209339
209784
|
function spillGitOutput(params) {
|
|
209340
209785
|
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
209341
209786
|
if (!tempDir) throw new Error("PULLFROG_TEMP_DIR not set");
|
|
209342
|
-
const outputPath =
|
|
209343
|
-
|
|
209787
|
+
const outputPath = join14(tempDir, `git-${params.command}-${randomUUID4().slice(0, 8)}.txt`);
|
|
209788
|
+
writeFileSync10(outputPath, params.output);
|
|
209344
209789
|
const previewByLines = params.output.split("\n").slice(0, OVERFLOW_PREVIEW_LINES).join("\n");
|
|
209345
209790
|
const preview = previewByLines.length <= OVERFLOW_PREVIEW_MAX_CHARS ? previewByLines : `${previewByLines.slice(0, OVERFLOW_PREVIEW_MAX_CHARS)}\u2026`;
|
|
209346
209791
|
log2.info(
|
|
@@ -210866,9 +211311,9 @@ async function reportReviewNodeId(ctx, params) {
|
|
|
210866
211311
|
import { execFileSync as execFileSync7, execSync as execSync2 } from "node:child_process";
|
|
210867
211312
|
import { mkdtempSync as mkdtempSync2, readdirSync, realpathSync as realpathSync2, unlinkSync as unlinkSync2 } from "node:fs";
|
|
210868
211313
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
210869
|
-
import { join as
|
|
211314
|
+
import { join as join15 } from "node:path";
|
|
210870
211315
|
function createTempDirectory() {
|
|
210871
|
-
const sharedTempDir = mkdtempSync2(
|
|
211316
|
+
const sharedTempDir = mkdtempSync2(join15(tmpdir3(), "pullfrog-"));
|
|
210872
211317
|
process.env.PULLFROG_TEMP_DIR = sharedTempDir;
|
|
210873
211318
|
log2.info(`\xBB created temp dir at ${sharedTempDir}`);
|
|
210874
211319
|
return sharedTempDir;
|
|
@@ -210913,13 +211358,13 @@ function wipeRunnerLeakSurface() {
|
|
|
210913
211358
|
return [];
|
|
210914
211359
|
}
|
|
210915
211360
|
};
|
|
210916
|
-
const fileCommandsDir =
|
|
211361
|
+
const fileCommandsDir = join15(runnerTemp, "_runner_file_commands");
|
|
210917
211362
|
for (const entry of listDir(fileCommandsDir)) {
|
|
210918
|
-
tryUnlink(
|
|
211363
|
+
tryUnlink(join15(fileCommandsDir, entry));
|
|
210919
211364
|
}
|
|
210920
211365
|
for (const entry of listDir(runnerTemp)) {
|
|
210921
211366
|
if (entry.endsWith(".sh") || /^git-credentials-.*\.config$/.test(entry)) {
|
|
210922
|
-
tryUnlink(
|
|
211367
|
+
tryUnlink(join15(runnerTemp, entry));
|
|
210923
211368
|
}
|
|
210924
211369
|
}
|
|
210925
211370
|
if (wiped.length > 0) {
|
|
@@ -211394,6 +211839,7 @@ async function checkoutPrBranch(pr, params) {
|
|
|
211394
211839
|
const postCheckoutHook = await executeLifecycleHook({
|
|
211395
211840
|
event: "post-checkout",
|
|
211396
211841
|
script: params.postCheckoutScript,
|
|
211842
|
+
shell: params.shell,
|
|
211397
211843
|
normalizeWorkingTreeAfter: true
|
|
211398
211844
|
});
|
|
211399
211845
|
return { hookWarning: postCheckoutHook.warning };
|
|
@@ -211475,11 +211921,11 @@ function CheckoutPrTool(ctx) {
|
|
|
211475
211921
|
headSha: checkoutSha
|
|
211476
211922
|
});
|
|
211477
211923
|
if (incremental.status === "ok") {
|
|
211478
|
-
incrementalDiffPath =
|
|
211924
|
+
incrementalDiffPath = join16(
|
|
211479
211925
|
tempDir,
|
|
211480
211926
|
`pr-${pull_number}-${beforeShort}-${headShort}-incremental.diff`
|
|
211481
211927
|
);
|
|
211482
|
-
|
|
211928
|
+
writeFileSync11(incrementalDiffPath, incremental.diff);
|
|
211483
211929
|
log2.info(
|
|
211484
211930
|
`\xBB incremental diff computed (${incremental.diff.length} bytes) \u2192 ${incrementalDiffPath}`
|
|
211485
211931
|
);
|
|
@@ -211492,8 +211938,8 @@ function CheckoutPrTool(ctx) {
|
|
|
211492
211938
|
const diffPreview = formatResult.content.split("\n").slice(0, 100).join("\n");
|
|
211493
211939
|
log2.debug(`formatted diff preview (first 100 lines):
|
|
211494
211940
|
${diffPreview}`);
|
|
211495
|
-
const diffPath =
|
|
211496
|
-
|
|
211941
|
+
const diffPath = join16(tempDir, `pr-${pull_number}-${headShort}.diff`);
|
|
211942
|
+
writeFileSync11(diffPath, formatResult.content);
|
|
211497
211943
|
log2.debug(`wrote diff to ${diffPath} (${formatResult.content.length} bytes)`);
|
|
211498
211944
|
primary.diffCoverage = createDiffCoverageState({
|
|
211499
211945
|
diffPath,
|
|
@@ -211650,8 +212096,8 @@ ${dirty}`
|
|
|
211650
212096
|
}
|
|
211651
212097
|
|
|
211652
212098
|
// mcp/checkSuite.ts
|
|
211653
|
-
import { mkdirSync as mkdirSync8, writeFileSync as
|
|
211654
|
-
import { join as
|
|
212099
|
+
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync12 } from "node:fs";
|
|
212100
|
+
import { join as join17 } from "node:path";
|
|
211655
212101
|
var GetCheckSuiteLogs = type({
|
|
211656
212102
|
check_suite_id: type.number.describe("the id from check_suite.id")
|
|
211657
212103
|
});
|
|
@@ -211747,7 +212193,7 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
211747
212193
|
if (!tempDir) {
|
|
211748
212194
|
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
211749
212195
|
}
|
|
211750
|
-
const logsDir =
|
|
212196
|
+
const logsDir = join17(tempDir, "ci-logs");
|
|
211751
212197
|
mkdirSync8(logsDir, { recursive: true });
|
|
211752
212198
|
const jobResults = [];
|
|
211753
212199
|
for (const run4 of failedRuns) {
|
|
@@ -211774,8 +212220,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
211774
212220
|
);
|
|
211775
212221
|
}
|
|
211776
212222
|
const logsText = await logsResult.text();
|
|
211777
|
-
const logPath =
|
|
211778
|
-
|
|
212223
|
+
const logPath = join17(logsDir, `job-${job.id}.log`);
|
|
212224
|
+
writeFileSync12(logPath, logsText);
|
|
211779
212225
|
const analysis = analyzeLog(logsText, 80);
|
|
211780
212226
|
const failedSteps = job.steps?.filter((s) => s.conclusion === "failure").map((s) => `Step ${s.number}: ${s.name}`) ?? [];
|
|
211781
212227
|
jobResults.push({
|
|
@@ -211823,8 +212269,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
211823
212269
|
}
|
|
211824
212270
|
|
|
211825
212271
|
// mcp/commitInfo.ts
|
|
211826
|
-
import { writeFileSync as
|
|
211827
|
-
import { join as
|
|
212272
|
+
import { writeFileSync as writeFileSync13 } from "node:fs";
|
|
212273
|
+
import { join as join18 } from "node:path";
|
|
211828
212274
|
var CommitInfo = type({
|
|
211829
212275
|
sha: type.string.describe("the commit SHA (full or abbreviated) to fetch")
|
|
211830
212276
|
});
|
|
@@ -211848,8 +212294,8 @@ function CommitInfoTool(ctx) {
|
|
|
211848
212294
|
"PULLFROG_TEMP_DIR not set - get_commit_info must run in pullfrog action context"
|
|
211849
212295
|
);
|
|
211850
212296
|
}
|
|
211851
|
-
const diffFile =
|
|
211852
|
-
|
|
212297
|
+
const diffFile = join18(tempDir, `commit-${sha.slice(0, 7)}.diff`);
|
|
212298
|
+
writeFileSync13(diffFile, formatResult.content);
|
|
211853
212299
|
log2.debug(`wrote commit diff to ${diffFile} (${formatResult.content.length} bytes)`);
|
|
211854
212300
|
return {
|
|
211855
212301
|
sha: data.sha,
|
|
@@ -211986,13 +212432,13 @@ import { performance as performance9 } from "node:perf_hooks";
|
|
|
211986
212432
|
|
|
211987
212433
|
// prep/installNodeDependencies.ts
|
|
211988
212434
|
import { existsSync as existsSync10 } from "node:fs";
|
|
211989
|
-
import { join as
|
|
212435
|
+
import { join as join20 } from "node:path";
|
|
211990
212436
|
|
|
211991
212437
|
// utils/packageManager.ts
|
|
211992
212438
|
var import_semver2 = __toESM(require_semver2(), 1);
|
|
211993
212439
|
import { existsSync as existsSync9 } from "node:fs";
|
|
211994
212440
|
import { mkdir, readFile as readFile3 } from "node:fs/promises";
|
|
211995
|
-
import { delimiter as delimiter2, join as
|
|
212441
|
+
import { delimiter as delimiter2, join as join19 } from "node:path";
|
|
211996
212442
|
var SUPPORTED_NAMES = ["npm", "pnpm", "yarn", "bun"];
|
|
211997
212443
|
var COREPACK_MANAGED = ["pnpm", "yarn"];
|
|
211998
212444
|
function isSupported(name) {
|
|
@@ -212030,7 +212476,7 @@ function parseDevEnginesField(field) {
|
|
|
212030
212476
|
};
|
|
212031
212477
|
}
|
|
212032
212478
|
async function resolvePackageManagerSpec(cwd) {
|
|
212033
|
-
const pkgPath =
|
|
212479
|
+
const pkgPath = join19(cwd, "package.json");
|
|
212034
212480
|
if (!existsSync9(pkgPath)) return null;
|
|
212035
212481
|
let pkg;
|
|
212036
212482
|
try {
|
|
@@ -212089,7 +212535,7 @@ async function currentVersion(name) {
|
|
|
212089
212535
|
return result.stdout.trim();
|
|
212090
212536
|
}
|
|
212091
212537
|
function packageManagerBinDir(tmpdir4) {
|
|
212092
|
-
return
|
|
212538
|
+
return join19(tmpdir4, "pm-bin");
|
|
212093
212539
|
}
|
|
212094
212540
|
async function ensurePackageManager(params) {
|
|
212095
212541
|
const spec = params.spec;
|
|
@@ -212152,7 +212598,7 @@ async function installFallback(name, installSpec) {
|
|
|
212152
212598
|
});
|
|
212153
212599
|
if (result.exitCode !== 0) return result.stderr || `failed to install ${name}`;
|
|
212154
212600
|
if (name === "deno") {
|
|
212155
|
-
const denoPath =
|
|
212601
|
+
const denoPath = join19(process.env.HOME || "", ".deno", "bin");
|
|
212156
212602
|
process.env.PATH = `${denoPath}:${process.env.PATH}`;
|
|
212157
212603
|
}
|
|
212158
212604
|
log2.info(`\xBB installed ${name}`);
|
|
@@ -212185,7 +212631,7 @@ async function isCommandAvailable2(command) {
|
|
|
212185
212631
|
var installNodeDependencies = {
|
|
212186
212632
|
name: "installNodeDependencies",
|
|
212187
212633
|
shouldRun: () => {
|
|
212188
|
-
const packageJsonPath =
|
|
212634
|
+
const packageJsonPath = join20(process.cwd(), "package.json");
|
|
212189
212635
|
return existsSync10(packageJsonPath);
|
|
212190
212636
|
},
|
|
212191
212637
|
run: async (options) => {
|
|
@@ -212281,12 +212727,12 @@ ${errorMessage}`]
|
|
|
212281
212727
|
|
|
212282
212728
|
// prep/installPythonDependencies.ts
|
|
212283
212729
|
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
|
|
212284
|
-
import { join as
|
|
212730
|
+
import { join as join21 } from "node:path";
|
|
212285
212731
|
function declaresBuildSystem(path4) {
|
|
212286
212732
|
return /^\s*\[\s*build-system\s*\]/m.test(readFileSync7(path4, "utf8"));
|
|
212287
212733
|
}
|
|
212288
212734
|
function configApplies(config4, cwd) {
|
|
212289
|
-
const path4 =
|
|
212735
|
+
const path4 = join21(cwd, config4.file);
|
|
212290
212736
|
return existsSync11(path4) && (config4.applies?.(path4) ?? true);
|
|
212291
212737
|
}
|
|
212292
212738
|
var PYTHON_CONFIGS = [
|
|
@@ -212610,407 +213056,6 @@ function AwaitDependencyInstallationTool(ctx) {
|
|
|
212610
213056
|
// mcp/gh.ts
|
|
212611
213057
|
import { mkdirSync as mkdirSync9 } from "node:fs";
|
|
212612
213058
|
import { join as join22 } from "node:path";
|
|
212613
|
-
|
|
212614
|
-
// mcp/shell.ts
|
|
212615
|
-
import { spawn as spawn5, spawnSync as spawnSync7 } from "node:child_process";
|
|
212616
|
-
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
212617
|
-
import { closeSync, openSync, writeFileSync as writeFileSync13 } from "node:fs";
|
|
212618
|
-
import { userInfo as userInfo2 } from "node:os";
|
|
212619
|
-
import { join as join21 } from "node:path";
|
|
212620
|
-
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
212621
|
-
var ShellParams = type({
|
|
212622
|
-
command: "string",
|
|
212623
|
-
// advisory and unread — no code path consumes it. kept in the schema only
|
|
212624
|
-
// because it nudges the model to state intent before running a command;
|
|
212625
|
-
// REQUIRING it cost a full rejected turn every time one was omitted. #1140.
|
|
212626
|
-
"description?": "string",
|
|
212627
|
-
"timeout?": type.number.describe(
|
|
212628
|
-
"Timeout in MILLISECONDS (not seconds). Default 30000 (30s), max 120000 (2m). e.g. timeout: 180000 for 3 minutes; timeout: 180 means 180ms and will kill the process almost immediately."
|
|
212629
|
-
),
|
|
212630
|
-
"working_directory?": "string",
|
|
212631
|
-
"background?": "boolean"
|
|
212632
|
-
});
|
|
212633
|
-
var detectedSandboxMethod;
|
|
212634
|
-
var usernsProbeError;
|
|
212635
|
-
function getSandboxMethod() {
|
|
212636
|
-
return detectSandboxMethod();
|
|
212637
|
-
}
|
|
212638
|
-
function detectSandboxMethod() {
|
|
212639
|
-
if (detectedSandboxMethod !== void 0) {
|
|
212640
|
-
return detectedSandboxMethod;
|
|
212641
|
-
}
|
|
212642
|
-
if (process.env.CI !== "true") {
|
|
212643
|
-
detectedSandboxMethod = "none";
|
|
212644
|
-
log2.debug("sandbox disabled (CI !== true)");
|
|
212645
|
-
return "none";
|
|
212646
|
-
}
|
|
212647
|
-
try {
|
|
212648
|
-
const result = spawnSync7("unshare", ["--pid", "--fork", "--mount-proc", "true"], {
|
|
212649
|
-
timeout: 5e3,
|
|
212650
|
-
stdio: "ignore"
|
|
212651
|
-
});
|
|
212652
|
-
if (result.status === 0) {
|
|
212653
|
-
detectedSandboxMethod = "unshare";
|
|
212654
|
-
log2.debug("PID namespace isolation enabled (unprivileged unshare)");
|
|
212655
|
-
return "unshare";
|
|
212656
|
-
}
|
|
212657
|
-
} catch {
|
|
212658
|
-
}
|
|
212659
|
-
try {
|
|
212660
|
-
const result = spawnSync7("sudo", ["unshare", "--pid", "--fork", "--mount-proc", "true"], {
|
|
212661
|
-
timeout: 5e3,
|
|
212662
|
-
stdio: "ignore"
|
|
212663
|
-
});
|
|
212664
|
-
if (result.status === 0) {
|
|
212665
|
-
detectedSandboxMethod = "sudo-unshare";
|
|
212666
|
-
log2.debug("PID namespace isolation enabled (sudo unshare)");
|
|
212667
|
-
return "sudo-unshare";
|
|
212668
|
-
}
|
|
212669
|
-
} catch {
|
|
212670
|
-
}
|
|
212671
|
-
try {
|
|
212672
|
-
const result = spawnSync7(
|
|
212673
|
-
"unshare",
|
|
212674
|
-
[
|
|
212675
|
-
"--user",
|
|
212676
|
-
"--map-root-user",
|
|
212677
|
-
"--pid",
|
|
212678
|
-
"--fork",
|
|
212679
|
-
"--mount",
|
|
212680
|
-
"setpriv",
|
|
212681
|
-
"--no-new-privs",
|
|
212682
|
-
"--bounding-set",
|
|
212683
|
-
"-all",
|
|
212684
|
-
"--inh-caps",
|
|
212685
|
-
"-all",
|
|
212686
|
-
"--ambient-caps",
|
|
212687
|
-
"-all",
|
|
212688
|
-
"true"
|
|
212689
|
-
],
|
|
212690
|
-
{ timeout: 5e3, stdio: ["ignore", "ignore", "pipe"] }
|
|
212691
|
-
);
|
|
212692
|
-
if (result.status === 0) {
|
|
212693
|
-
detectedSandboxMethod = "userns-unshare";
|
|
212694
|
-
log2.debug("PID namespace isolation enabled (unprivileged userns unshare)");
|
|
212695
|
-
return "userns-unshare";
|
|
212696
|
-
}
|
|
212697
|
-
usernsProbeError = result.stderr?.toString().trim() || result.error?.message;
|
|
212698
|
-
} catch {
|
|
212699
|
-
}
|
|
212700
|
-
detectedSandboxMethod = "none";
|
|
212701
|
-
log2.info("PID namespace isolation not available");
|
|
212702
|
-
return "none";
|
|
212703
|
-
}
|
|
212704
|
-
var PROC_CLEANUP = "umount /proc 2>/dev/null; umount /proc 2>/dev/null; mount -t proc proc /proc 2>/dev/null;";
|
|
212705
|
-
var SOCKET_CLEANUP = [
|
|
212706
|
-
"/var/run/docker.sock",
|
|
212707
|
-
"/run/docker.sock",
|
|
212708
|
-
"/var/run/podman/podman.sock",
|
|
212709
|
-
"/run/podman/podman.sock",
|
|
212710
|
-
"/run/containerd/containerd.sock",
|
|
212711
|
-
"/var/run/crio/crio.sock"
|
|
212712
|
-
].map((path4) => `mount --bind /dev/null ${path4} 2>/dev/null;`).join(" ");
|
|
212713
|
-
function buildFsMounts(repoDir) {
|
|
212714
|
-
const escaped = repoDir.replace(/'/g, "'\\''");
|
|
212715
|
-
return [
|
|
212716
|
-
`mkdir -p /var/lib/pullfrog 2>/dev/null;`,
|
|
212717
|
-
`mount -t tmpfs tmpfs /var/lib/pullfrog 2>/dev/null;`,
|
|
212718
|
-
`[ -n "$RUNNER_TEMP" ] && [ -d "$RUNNER_TEMP/_runner_file_commands" ] && mount -t tmpfs tmpfs "$RUNNER_TEMP/_runner_file_commands" 2>/dev/null;`,
|
|
212719
|
-
`[ -e '${escaped}/.git' ] && mount --bind '${escaped}/.git' '${escaped}/.git' 2>/dev/null && mount -o remount,bind,ro '${escaped}/.git' 2>/dev/null;`
|
|
212720
|
-
].join(" ");
|
|
212721
|
-
}
|
|
212722
|
-
var _repoRoot;
|
|
212723
|
-
function resolveRepoRoot() {
|
|
212724
|
-
if (_repoRoot) return _repoRoot;
|
|
212725
|
-
const fromEnv = process.env.GITHUB_WORKSPACE;
|
|
212726
|
-
if (fromEnv) {
|
|
212727
|
-
_repoRoot = fromEnv;
|
|
212728
|
-
return _repoRoot;
|
|
212729
|
-
}
|
|
212730
|
-
try {
|
|
212731
|
-
_repoRoot = spawnSync7("git", ["rev-parse", "--show-toplevel"], {
|
|
212732
|
-
cwd: process.cwd(),
|
|
212733
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
212734
|
-
encoding: "utf-8"
|
|
212735
|
-
}).stdout?.trim();
|
|
212736
|
-
} catch {
|
|
212737
|
-
}
|
|
212738
|
-
if (!_repoRoot) _repoRoot = process.cwd();
|
|
212739
|
-
return _repoRoot;
|
|
212740
|
-
}
|
|
212741
|
-
function spawnShell(params) {
|
|
212742
|
-
const spawnOpts = { env: params.env, cwd: params.cwd, stdio: params.stdio, detached: true };
|
|
212743
|
-
const sandboxMethod = detectSandboxMethod();
|
|
212744
|
-
const ci = process.env.CI === "true";
|
|
212745
|
-
if (ci && sandboxMethod === "none") {
|
|
212746
|
-
throw new Error(
|
|
212747
|
-
"PID-namespace isolation is required in CI but unavailable: unprivileged unshare, sudo unshare, and userns-nested unshare all failed. " + (usernsProbeError ? `userns probe said: ${usernsProbeError}. ` : "") + "on a containerized / Kubernetes self-hosted runner the cause is usually one of: the pod's seccomp profile blocks user-namespace creation (Pod Security Standards baseline/restricted); the pod runs as root without CAP_SETFCAP, which the kernel requires to map uid 0; or the node kernel disallows unprivileged user namespaces (Ubuntu >= 23.10, Bottlerocket, Talos). elsewhere, check that `unshare` and `setpriv` (util-linux >= 2.31, which is what added --ambient-caps) are on PATH. see https://docs.pullfrog.com/security#self-hosted-runners"
|
|
212748
|
-
);
|
|
212749
|
-
}
|
|
212750
|
-
const repoRoot = resolveRepoRoot();
|
|
212751
|
-
const fsMounts = buildFsMounts(repoRoot);
|
|
212752
|
-
if (sandboxMethod === "unshare") {
|
|
212753
|
-
return spawn5(
|
|
212754
|
-
"unshare",
|
|
212755
|
-
[
|
|
212756
|
-
"--pid",
|
|
212757
|
-
"--fork",
|
|
212758
|
-
"--mount-proc",
|
|
212759
|
-
"bash",
|
|
212760
|
-
"-c",
|
|
212761
|
-
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} ${params.command}`
|
|
212762
|
-
],
|
|
212763
|
-
spawnOpts
|
|
212764
|
-
);
|
|
212765
|
-
}
|
|
212766
|
-
if (sandboxMethod === "userns-unshare") {
|
|
212767
|
-
const escaped = params.command.replace(/'/g, "'\\''");
|
|
212768
|
-
return spawn5(
|
|
212769
|
-
"unshare",
|
|
212770
|
-
[
|
|
212771
|
-
"--user",
|
|
212772
|
-
"--map-root-user",
|
|
212773
|
-
"--pid",
|
|
212774
|
-
"--fork",
|
|
212775
|
-
"--mount",
|
|
212776
|
-
"bash",
|
|
212777
|
-
"-c",
|
|
212778
|
-
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec setpriv --no-new-privs --bounding-set -all --inh-caps -all --ambient-caps -all bash -c '${escaped}'`
|
|
212779
|
-
],
|
|
212780
|
-
spawnOpts
|
|
212781
|
-
);
|
|
212782
|
-
}
|
|
212783
|
-
if (sandboxMethod === "sudo-unshare") {
|
|
212784
|
-
const envArgs = [];
|
|
212785
|
-
for (const [k2, v] of Object.entries(params.env)) {
|
|
212786
|
-
if (v !== void 0) {
|
|
212787
|
-
envArgs.push(`${k2}=${v}`);
|
|
212788
|
-
}
|
|
212789
|
-
}
|
|
212790
|
-
const username = userInfo2().username;
|
|
212791
|
-
const pathRestore = 'export PATH="${SANDBOX_PATH:-$PATH}"; ';
|
|
212792
|
-
const escaped = (pathRestore + params.command).replace(/'/g, "'\\''");
|
|
212793
|
-
envArgs.push(`SANDBOX_PATH=${params.env.PATH ?? ""}`);
|
|
212794
|
-
return spawn5(
|
|
212795
|
-
"sudo",
|
|
212796
|
-
[
|
|
212797
|
-
"env",
|
|
212798
|
-
...envArgs,
|
|
212799
|
-
"unshare",
|
|
212800
|
-
"--pid",
|
|
212801
|
-
"--fork",
|
|
212802
|
-
"--mount-proc",
|
|
212803
|
-
"bash",
|
|
212804
|
-
"-c",
|
|
212805
|
-
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec su -p -s /bin/bash ${username} -c '${escaped}'`
|
|
212806
|
-
],
|
|
212807
|
-
{ ...spawnOpts, env: {} }
|
|
212808
|
-
);
|
|
212809
|
-
}
|
|
212810
|
-
return spawn5("bash", ["-c", params.command], spawnOpts);
|
|
212811
|
-
}
|
|
212812
|
-
async function killProcessGroup(proc) {
|
|
212813
|
-
if (!proc.pid) return;
|
|
212814
|
-
try {
|
|
212815
|
-
process.kill(-proc.pid, "SIGTERM");
|
|
212816
|
-
await new Promise((r2) => setTimeout(r2, 200));
|
|
212817
|
-
process.kill(-proc.pid, "SIGKILL");
|
|
212818
|
-
} catch {
|
|
212819
|
-
try {
|
|
212820
|
-
proc.kill("SIGKILL");
|
|
212821
|
-
} catch {
|
|
212822
|
-
}
|
|
212823
|
-
}
|
|
212824
|
-
}
|
|
212825
|
-
function getTempDir() {
|
|
212826
|
-
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
212827
|
-
if (!tempDir) {
|
|
212828
|
-
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
212829
|
-
}
|
|
212830
|
-
return tempDir;
|
|
212831
|
-
}
|
|
212832
|
-
var MAX_OUTPUT_CHARS = 5e3;
|
|
212833
|
-
async function runSandboxed(params) {
|
|
212834
|
-
const proc = spawnShell({
|
|
212835
|
-
command: params.command,
|
|
212836
|
-
env: params.env,
|
|
212837
|
-
cwd: params.cwd,
|
|
212838
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
212839
|
-
});
|
|
212840
|
-
let stdout2 = "", stderr = "", timedOut = false, exited = false, spawnError = "";
|
|
212841
|
-
proc.stdout?.on("data", (chunk) => {
|
|
212842
|
-
stdout2 += chunk.toString();
|
|
212843
|
-
});
|
|
212844
|
-
proc.stderr?.on("data", (chunk) => {
|
|
212845
|
-
stderr += chunk.toString();
|
|
212846
|
-
});
|
|
212847
|
-
const timeoutId = setTimeout(async () => {
|
|
212848
|
-
if (!exited) {
|
|
212849
|
-
timedOut = true;
|
|
212850
|
-
await killProcessGroup(proc);
|
|
212851
|
-
}
|
|
212852
|
-
}, params.timeout);
|
|
212853
|
-
const exitCode = await new Promise((resolve3) => {
|
|
212854
|
-
const done = (code) => {
|
|
212855
|
-
exited = true;
|
|
212856
|
-
clearTimeout(timeoutId);
|
|
212857
|
-
resolve3(code);
|
|
212858
|
-
};
|
|
212859
|
-
proc.on("exit", done);
|
|
212860
|
-
proc.on("error", (err) => {
|
|
212861
|
-
spawnError = err.message;
|
|
212862
|
-
done(null);
|
|
212863
|
-
});
|
|
212864
|
-
});
|
|
212865
|
-
proc.stdout?.destroy();
|
|
212866
|
-
proc.stderr?.destroy();
|
|
212867
|
-
let output2 = stderr ? stdout2 ? `${stdout2}
|
|
212868
|
-
${stderr}` : stderr : stdout2;
|
|
212869
|
-
if (spawnError) output2 = output2 ? `${output2}
|
|
212870
|
-
${spawnError}` : spawnError;
|
|
212871
|
-
if (timedOut)
|
|
212872
|
-
output2 = output2 ? `${output2}
|
|
212873
|
-
[timed out after ${params.timeout}ms]` : `[timed out after ${params.timeout}ms]`;
|
|
212874
|
-
return {
|
|
212875
|
-
output: output2.trim(),
|
|
212876
|
-
exitCode: exitCode ?? (timedOut ? 124 : -1),
|
|
212877
|
-
timedOut
|
|
212878
|
-
};
|
|
212879
|
-
}
|
|
212880
|
-
function capOutput(output2) {
|
|
212881
|
-
if (output2.length <= MAX_OUTPUT_CHARS) return output2;
|
|
212882
|
-
const fullPath = join21(getTempDir(), `shell-${randomUUID4().slice(0, 8)}.log`);
|
|
212883
|
-
writeFileSync13(fullPath, output2);
|
|
212884
|
-
const elided = output2.length - MAX_OUTPUT_CHARS;
|
|
212885
|
-
return `... [${elided} chars truncated; full output saved to ${fullPath}] ...
|
|
212886
|
-
${output2.slice(-MAX_OUTPUT_CHARS)}`;
|
|
212887
|
-
}
|
|
212888
|
-
function isGitCommand(command) {
|
|
212889
|
-
const trimmed = command.trim();
|
|
212890
|
-
if (trimmed === "git" || trimmed.startsWith("git ")) return true;
|
|
212891
|
-
if (trimmed.startsWith("sudo git")) return true;
|
|
212892
|
-
return /[;&|]\s*(?:sudo\s+)?git(?:\s|$)/.test(trimmed);
|
|
212893
|
-
}
|
|
212894
|
-
function ShellTool(ctx) {
|
|
212895
|
-
return tool({
|
|
212896
|
-
name: "shell",
|
|
212897
|
-
timeoutMs: 12e4,
|
|
212898
|
-
description: `Execute shell commands securely. Environment is filtered to remove API keys and secrets.
|
|
212899
|
-
|
|
212900
|
-
Example: \`shell({ command: "pnpm test", description: "run the test suite" })\`.
|
|
212901
|
-
|
|
212902
|
-
Use this tool to:
|
|
212903
|
-
- Run shell commands (ls, cat, grep, find, etc.)
|
|
212904
|
-
- Execute build tools (npm, pnpm, cargo, make, etc.)
|
|
212905
|
-
- Run tests and linters
|
|
212906
|
-
|
|
212907
|
-
Output is capped at ${MAX_OUTPUT_CHARS} chars: if exceeded, only the tail is returned and the full body is saved to a tempfile (path included in the response). Re-read the tempfile with cat/tail/grep when you need more.
|
|
212908
|
-
|
|
212909
|
-
Do NOT use this tool for git commands \u2014 use the dedicated git tools instead.`,
|
|
212910
|
-
parameters: ShellParams,
|
|
212911
|
-
execute: execute(async (params) => {
|
|
212912
|
-
if (isGitCommand(params.command)) {
|
|
212913
|
-
throw new Error(
|
|
212914
|
-
'git commands are not allowed in the shell tool. use the dedicated git tools instead:\n- git: local operations, e.g. git({ command: "log", args: ["--oneline"] }) \u2014 `command` is the subcommand ONLY\n- push_branch: push to remote (handles authentication)\n- git_fetch: fetch from remote (handles authentication)\n- checkout_pr: check out PR branches'
|
|
212915
|
-
);
|
|
212916
|
-
}
|
|
212917
|
-
const timeout = Math.min(params.timeout ?? 3e4, 12e4);
|
|
212918
|
-
const cwd = params.working_directory ?? process.cwd();
|
|
212919
|
-
const env2 = resolveEnv(ctx.payload.shell === "enabled" ? "inherit" : "restricted");
|
|
212920
|
-
if (params.command.includes("agent-browser")) {
|
|
212921
|
-
const daemonError = ensureBrowserDaemon(ctx.toolState);
|
|
212922
|
-
if (daemonError) {
|
|
212923
|
-
return {
|
|
212924
|
-
output: `browser daemon unavailable: ${daemonError}`,
|
|
212925
|
-
exit_code: 1,
|
|
212926
|
-
timed_out: false
|
|
212927
|
-
};
|
|
212928
|
-
}
|
|
212929
|
-
const binDir = ctx.toolState.browserDaemon?.binDir;
|
|
212930
|
-
if (binDir) {
|
|
212931
|
-
env2.PATH = `${binDir}:${env2.PATH ?? ""}`;
|
|
212932
|
-
}
|
|
212933
|
-
}
|
|
212934
|
-
if (params.background) {
|
|
212935
|
-
const tempDir = getTempDir();
|
|
212936
|
-
const handle = `bg-${randomUUID4().slice(0, 8)}`;
|
|
212937
|
-
const outputPath = join21(tempDir, `${handle}.log`);
|
|
212938
|
-
const pidPath = join21(tempDir, `${handle}.pid`);
|
|
212939
|
-
const logFd = openSync(outputPath, "a");
|
|
212940
|
-
let proc;
|
|
212941
|
-
try {
|
|
212942
|
-
proc = spawnShell({
|
|
212943
|
-
command: params.command,
|
|
212944
|
-
env: env2,
|
|
212945
|
-
cwd,
|
|
212946
|
-
stdio: ["ignore", logFd, logFd]
|
|
212947
|
-
});
|
|
212948
|
-
} finally {
|
|
212949
|
-
closeSync(logFd);
|
|
212950
|
-
}
|
|
212951
|
-
if (!proc.pid) {
|
|
212952
|
-
throw new Error("failed to start background process");
|
|
212953
|
-
}
|
|
212954
|
-
proc.unref();
|
|
212955
|
-
writeFileSync13(pidPath, `${proc.pid}
|
|
212956
|
-
`);
|
|
212957
|
-
ctx.toolState.backgroundProcesses.set(handle, { pid: proc.pid, outputPath, pidPath });
|
|
212958
|
-
return {
|
|
212959
|
-
handle,
|
|
212960
|
-
outputPath,
|
|
212961
|
-
pidPath,
|
|
212962
|
-
message: `started background process ${handle} (pid ${proc.pid})`
|
|
212963
|
-
};
|
|
212964
|
-
}
|
|
212965
|
-
const result = await runSandboxed({ command: params.command, env: env2, cwd, timeout });
|
|
212966
|
-
if (result.exitCode !== 0) {
|
|
212967
|
-
log2.info(`shell command failed with exit code ${result.exitCode}: ${params.command}`);
|
|
212968
|
-
if (result.output) log2.info(`output: ${result.output}`);
|
|
212969
|
-
}
|
|
212970
|
-
return {
|
|
212971
|
-
output: capOutput(result.output),
|
|
212972
|
-
exit_code: result.exitCode,
|
|
212973
|
-
timed_out: result.timedOut
|
|
212974
|
-
};
|
|
212975
|
-
})
|
|
212976
|
-
});
|
|
212977
|
-
}
|
|
212978
|
-
var KillBackgroundParams = type({
|
|
212979
|
-
handle: type.string.describe("The handle of the background process to kill (e.g., bg-a1b2c3d4)")
|
|
212980
|
-
});
|
|
212981
|
-
function KillBackgroundTool(ctx) {
|
|
212982
|
-
return tool({
|
|
212983
|
-
name: "kill_background",
|
|
212984
|
-
mutates: true,
|
|
212985
|
-
description: `Kill a background process by its handle. Use this to stop dev servers or other long-running processes started with shell({ background: true }).`,
|
|
212986
|
-
parameters: KillBackgroundParams,
|
|
212987
|
-
execute: execute(async (params) => {
|
|
212988
|
-
const proc = ctx.toolState.backgroundProcesses.get(params.handle);
|
|
212989
|
-
if (!proc) {
|
|
212990
|
-
return {
|
|
212991
|
-
success: false,
|
|
212992
|
-
message: `no background process with handle ${params.handle}`
|
|
212993
|
-
};
|
|
212994
|
-
}
|
|
212995
|
-
try {
|
|
212996
|
-
process.kill(-proc.pid, "SIGTERM");
|
|
212997
|
-
} catch {
|
|
212998
|
-
}
|
|
212999
|
-
await sleep3(200);
|
|
213000
|
-
try {
|
|
213001
|
-
process.kill(-proc.pid, "SIGKILL");
|
|
213002
|
-
} catch {
|
|
213003
|
-
}
|
|
213004
|
-
ctx.toolState.backgroundProcesses.delete(params.handle);
|
|
213005
|
-
return {
|
|
213006
|
-
success: true,
|
|
213007
|
-
message: `killed background process ${params.handle} (pid ${proc.pid})`
|
|
213008
|
-
};
|
|
213009
|
-
})
|
|
213010
|
-
});
|
|
213011
|
-
}
|
|
213012
|
-
|
|
213013
|
-
// mcp/gh.ts
|
|
213014
213059
|
var GH_TIMEOUT_MS = 12e4;
|
|
213015
213060
|
var GhParams = type({
|
|
213016
213061
|
args: type.string.array().describe(
|
|
@@ -218809,6 +218854,7 @@ async function main() {
|
|
|
218809
218854
|
const setupHook = await executeLifecycleHook({
|
|
218810
218855
|
event: "setup",
|
|
218811
218856
|
script: runContext.repoSettings.setupScript,
|
|
218857
|
+
shell: payload.shell,
|
|
218812
218858
|
normalizeWorkingTreeAfter: true
|
|
218813
218859
|
});
|
|
218814
218860
|
if (setupHook.warning) {
|
|
@@ -219684,7 +219730,7 @@ function resolveTarget2(repo) {
|
|
|
219684
219730
|
return { owner: match3[1], repo: match3[2] };
|
|
219685
219731
|
}
|
|
219686
219732
|
function cliVersion() {
|
|
219687
|
-
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.
|
|
219733
|
+
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.75");
|
|
219688
219734
|
if (!parts) return "0.0.0";
|
|
219689
219735
|
return `${Number(parts[1])}.${Number(parts[2])}.${Number(parts[3])}`;
|
|
219690
219736
|
}
|
|
@@ -219851,7 +219897,7 @@ async function runCli8(input2) {
|
|
|
219851
219897
|
}
|
|
219852
219898
|
|
|
219853
219899
|
// cli.ts
|
|
219854
|
-
var VERSION10 = "0.1.
|
|
219900
|
+
var VERSION10 = "0.1.75";
|
|
219855
219901
|
var bin = basename2(process.argv[1] || "");
|
|
219856
219902
|
var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
|
|
219857
219903
|
var rawArgs = process.argv.slice(2);
|