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/index.js
CHANGED
|
@@ -28027,7 +28027,7 @@ var require_cross_spawn = __commonJS({
|
|
|
28027
28027
|
enoent.hookChildProcess(spawned, parsed2);
|
|
28028
28028
|
return spawned;
|
|
28029
28029
|
}
|
|
28030
|
-
function
|
|
28030
|
+
function spawnSync8(command, args2, options) {
|
|
28031
28031
|
const parsed2 = parse6(command, args2, options);
|
|
28032
28032
|
const result = cp.spawnSync(parsed2.command, parsed2.args, parsed2.options);
|
|
28033
28033
|
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed2);
|
|
@@ -28035,7 +28035,7 @@ var require_cross_spawn = __commonJS({
|
|
|
28035
28035
|
}
|
|
28036
28036
|
module.exports = spawn4;
|
|
28037
28037
|
module.exports.spawn = spawn4;
|
|
28038
|
-
module.exports.sync =
|
|
28038
|
+
module.exports.sync = spawnSync8;
|
|
28039
28039
|
module.exports._parse = parse6;
|
|
28040
28040
|
module.exports._enoent = enoent;
|
|
28041
28041
|
}
|
|
@@ -109795,7 +109795,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
109795
109795
|
// package.json
|
|
109796
109796
|
var package_default = {
|
|
109797
109797
|
name: "pullfrog",
|
|
109798
|
-
version: "0.1.
|
|
109798
|
+
version: "0.1.75",
|
|
109799
109799
|
cliContractVersion: "1",
|
|
109800
109800
|
type: "module",
|
|
109801
109801
|
bin: {
|
|
@@ -176983,8 +176983,8 @@ function closeBrowserDaemon(toolState) {
|
|
|
176983
176983
|
|
|
176984
176984
|
// mcp/checkout.ts
|
|
176985
176985
|
import { createHash as createHash2 } from "node:crypto";
|
|
176986
|
-
import { statSync, unlinkSync as unlinkSync3, writeFileSync as
|
|
176987
|
-
import { join as
|
|
176986
|
+
import { statSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync10 } from "node:fs";
|
|
176987
|
+
import { join as join15 } from "node:path";
|
|
176988
176988
|
|
|
176989
176989
|
// node_modules/.pnpm/@ark+schema@0.56.0/node_modules/@ark/schema/out/shared/errors.js
|
|
176990
176990
|
var ArkError = class _ArkError extends CastableBase {
|
|
@@ -185256,6 +185256,960 @@ async function $gitFetchWithDeepen(args2, options, label) {
|
|
|
185256
185256
|
}
|
|
185257
185257
|
}
|
|
185258
185258
|
|
|
185259
|
+
// utils/lifecycle.ts
|
|
185260
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
185261
|
+
|
|
185262
|
+
// mcp/shell.ts
|
|
185263
|
+
import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
|
|
185264
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
185265
|
+
import { closeSync, openSync, writeFileSync as writeFileSync8 } from "node:fs";
|
|
185266
|
+
import { userInfo as userInfo2 } from "node:os";
|
|
185267
|
+
import { join as join10 } from "node:path";
|
|
185268
|
+
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
185269
|
+
|
|
185270
|
+
// node_modules/.pnpm/@toon-format+toon@1.4.0/node_modules/@toon-format/toon/dist/index.mjs
|
|
185271
|
+
var LIST_ITEM_MARKER = "-";
|
|
185272
|
+
var LIST_ITEM_PREFIX = "- ";
|
|
185273
|
+
var COMMA = ",";
|
|
185274
|
+
var PIPE = "|";
|
|
185275
|
+
var DOT = ".";
|
|
185276
|
+
var NULL_LITERAL = "null";
|
|
185277
|
+
var TRUE_LITERAL = "true";
|
|
185278
|
+
var FALSE_LITERAL = "false";
|
|
185279
|
+
var BACKSLASH = "\\";
|
|
185280
|
+
var DOUBLE_QUOTE = '"';
|
|
185281
|
+
var TAB = " ";
|
|
185282
|
+
var DELIMITERS = {
|
|
185283
|
+
comma: COMMA,
|
|
185284
|
+
tab: TAB,
|
|
185285
|
+
pipe: PIPE
|
|
185286
|
+
};
|
|
185287
|
+
var DEFAULT_DELIMITER = DELIMITERS.comma;
|
|
185288
|
+
function escapeString(value2) {
|
|
185289
|
+
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`);
|
|
185290
|
+
}
|
|
185291
|
+
function isBooleanOrNullLiteral(token) {
|
|
185292
|
+
return token === TRUE_LITERAL || token === FALSE_LITERAL || token === NULL_LITERAL;
|
|
185293
|
+
}
|
|
185294
|
+
function normalizeValue(value2) {
|
|
185295
|
+
if (value2 === null) return null;
|
|
185296
|
+
if (typeof value2 === "string" || typeof value2 === "boolean") return value2;
|
|
185297
|
+
if (typeof value2 === "number") {
|
|
185298
|
+
if (Object.is(value2, -0)) return 0;
|
|
185299
|
+
if (!Number.isFinite(value2)) return null;
|
|
185300
|
+
return value2;
|
|
185301
|
+
}
|
|
185302
|
+
if (typeof value2 === "bigint") {
|
|
185303
|
+
if (value2 >= Number.MIN_SAFE_INTEGER && value2 <= Number.MAX_SAFE_INTEGER) return Number(value2);
|
|
185304
|
+
return value2.toString();
|
|
185305
|
+
}
|
|
185306
|
+
if (value2 instanceof Date) return value2.toISOString();
|
|
185307
|
+
if (Array.isArray(value2)) return value2.map(normalizeValue);
|
|
185308
|
+
if (value2 instanceof Set) return Array.from(value2).map(normalizeValue);
|
|
185309
|
+
if (value2 instanceof Map) return Object.fromEntries(Array.from(value2, ([k2, v]) => [String(k2), normalizeValue(v)]));
|
|
185310
|
+
if (isPlainObject6(value2)) {
|
|
185311
|
+
const normalized = {};
|
|
185312
|
+
for (const key in value2) if (Object.prototype.hasOwnProperty.call(value2, key)) normalized[key] = normalizeValue(value2[key]);
|
|
185313
|
+
return normalized;
|
|
185314
|
+
}
|
|
185315
|
+
return null;
|
|
185316
|
+
}
|
|
185317
|
+
function isJsonPrimitive(value2) {
|
|
185318
|
+
return value2 === null || typeof value2 === "string" || typeof value2 === "number" || typeof value2 === "boolean";
|
|
185319
|
+
}
|
|
185320
|
+
function isJsonArray(value2) {
|
|
185321
|
+
return Array.isArray(value2);
|
|
185322
|
+
}
|
|
185323
|
+
function isJsonObject4(value2) {
|
|
185324
|
+
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
185325
|
+
}
|
|
185326
|
+
function isEmptyObject2(value2) {
|
|
185327
|
+
return Object.keys(value2).length === 0;
|
|
185328
|
+
}
|
|
185329
|
+
function isPlainObject6(value2) {
|
|
185330
|
+
if (value2 === null || typeof value2 !== "object") return false;
|
|
185331
|
+
const prototype = Object.getPrototypeOf(value2);
|
|
185332
|
+
return prototype === null || prototype === Object.prototype;
|
|
185333
|
+
}
|
|
185334
|
+
function isArrayOfPrimitives(value2) {
|
|
185335
|
+
return value2.length === 0 || value2.every((item) => isJsonPrimitive(item));
|
|
185336
|
+
}
|
|
185337
|
+
function isArrayOfArrays(value2) {
|
|
185338
|
+
return value2.length === 0 || value2.every((item) => isJsonArray(item));
|
|
185339
|
+
}
|
|
185340
|
+
function isArrayOfObjects(value2) {
|
|
185341
|
+
return value2.length === 0 || value2.every((item) => isJsonObject4(item));
|
|
185342
|
+
}
|
|
185343
|
+
function isValidUnquotedKey(key) {
|
|
185344
|
+
return /^[A-Z_][\w.]*$/i.test(key);
|
|
185345
|
+
}
|
|
185346
|
+
function isIdentifierSegment(key) {
|
|
185347
|
+
return /^[A-Z_]\w*$/i.test(key);
|
|
185348
|
+
}
|
|
185349
|
+
function isSafeUnquoted(value2, delimiter2 = DEFAULT_DELIMITER) {
|
|
185350
|
+
if (!value2) return false;
|
|
185351
|
+
if (value2 !== value2.trim()) return false;
|
|
185352
|
+
if (isBooleanOrNullLiteral(value2) || isNumericLike(value2)) return false;
|
|
185353
|
+
if (value2.includes(":")) return false;
|
|
185354
|
+
if (value2.includes('"') || value2.includes("\\")) return false;
|
|
185355
|
+
if (/[[\]{}]/.test(value2)) return false;
|
|
185356
|
+
if (/[\n\r\t]/.test(value2)) return false;
|
|
185357
|
+
if (value2.includes(delimiter2)) return false;
|
|
185358
|
+
if (value2.startsWith(LIST_ITEM_MARKER)) return false;
|
|
185359
|
+
return true;
|
|
185360
|
+
}
|
|
185361
|
+
function isNumericLike(value2) {
|
|
185362
|
+
return /^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(value2) || /^0\d+$/.test(value2);
|
|
185363
|
+
}
|
|
185364
|
+
var QUOTED_KEY_MARKER = Symbol("quotedKey");
|
|
185365
|
+
function tryFoldKeyChain(key, value2, siblings, options, rootLiteralKeys, pathPrefix, flattenDepth) {
|
|
185366
|
+
if (options.keyFolding !== "safe") return;
|
|
185367
|
+
if (!isJsonObject4(value2)) return;
|
|
185368
|
+
const { segments, tail, leafValue } = collectSingleKeyChain(key, value2, flattenDepth ?? options.flattenDepth);
|
|
185369
|
+
if (segments.length < 2) return;
|
|
185370
|
+
if (!segments.every((seg) => isIdentifierSegment(seg))) return;
|
|
185371
|
+
const foldedKey = buildFoldedKey(segments);
|
|
185372
|
+
const absolutePath = pathPrefix ? `${pathPrefix}${DOT}${foldedKey}` : foldedKey;
|
|
185373
|
+
if (siblings.includes(foldedKey)) return;
|
|
185374
|
+
if (rootLiteralKeys && rootLiteralKeys.has(absolutePath)) return;
|
|
185375
|
+
return {
|
|
185376
|
+
foldedKey,
|
|
185377
|
+
remainder: tail,
|
|
185378
|
+
leafValue,
|
|
185379
|
+
segmentCount: segments.length
|
|
185380
|
+
};
|
|
185381
|
+
}
|
|
185382
|
+
function collectSingleKeyChain(startKey, startValue, maxDepth) {
|
|
185383
|
+
const segments = [startKey];
|
|
185384
|
+
let currentValue = startValue;
|
|
185385
|
+
while (segments.length < maxDepth) {
|
|
185386
|
+
if (!isJsonObject4(currentValue)) break;
|
|
185387
|
+
const keys = Object.keys(currentValue);
|
|
185388
|
+
if (keys.length !== 1) break;
|
|
185389
|
+
const nextKey = keys[0];
|
|
185390
|
+
const nextValue = currentValue[nextKey];
|
|
185391
|
+
segments.push(nextKey);
|
|
185392
|
+
currentValue = nextValue;
|
|
185393
|
+
}
|
|
185394
|
+
if (!isJsonObject4(currentValue) || isEmptyObject2(currentValue)) return {
|
|
185395
|
+
segments,
|
|
185396
|
+
tail: void 0,
|
|
185397
|
+
leafValue: currentValue
|
|
185398
|
+
};
|
|
185399
|
+
return {
|
|
185400
|
+
segments,
|
|
185401
|
+
tail: currentValue,
|
|
185402
|
+
leafValue: currentValue
|
|
185403
|
+
};
|
|
185404
|
+
}
|
|
185405
|
+
function buildFoldedKey(segments) {
|
|
185406
|
+
return segments.join(DOT);
|
|
185407
|
+
}
|
|
185408
|
+
function encodePrimitive(value2, delimiter2) {
|
|
185409
|
+
if (value2 === null) return NULL_LITERAL;
|
|
185410
|
+
if (typeof value2 === "boolean") return String(value2);
|
|
185411
|
+
if (typeof value2 === "number") return String(value2);
|
|
185412
|
+
return encodeStringLiteral(value2, delimiter2);
|
|
185413
|
+
}
|
|
185414
|
+
function encodeStringLiteral(value2, delimiter2 = DEFAULT_DELIMITER) {
|
|
185415
|
+
if (isSafeUnquoted(value2, delimiter2)) return value2;
|
|
185416
|
+
return `${DOUBLE_QUOTE}${escapeString(value2)}${DOUBLE_QUOTE}`;
|
|
185417
|
+
}
|
|
185418
|
+
function encodeKey(key) {
|
|
185419
|
+
if (isValidUnquotedKey(key)) return key;
|
|
185420
|
+
return `${DOUBLE_QUOTE}${escapeString(key)}${DOUBLE_QUOTE}`;
|
|
185421
|
+
}
|
|
185422
|
+
function encodeAndJoinPrimitives(values, delimiter2 = DEFAULT_DELIMITER) {
|
|
185423
|
+
return values.map((v) => encodePrimitive(v, delimiter2)).join(delimiter2);
|
|
185424
|
+
}
|
|
185425
|
+
function formatHeader(length, options) {
|
|
185426
|
+
const key = options?.key;
|
|
185427
|
+
const fields = options?.fields;
|
|
185428
|
+
const delimiter2 = options?.delimiter ?? COMMA;
|
|
185429
|
+
let header = "";
|
|
185430
|
+
if (key) header += encodeKey(key);
|
|
185431
|
+
header += `[${length}${delimiter2 !== DEFAULT_DELIMITER ? delimiter2 : ""}]`;
|
|
185432
|
+
if (fields) {
|
|
185433
|
+
const quotedFields = fields.map((f) => encodeKey(f));
|
|
185434
|
+
header += `{${quotedFields.join(delimiter2)}}`;
|
|
185435
|
+
}
|
|
185436
|
+
header += ":";
|
|
185437
|
+
return header;
|
|
185438
|
+
}
|
|
185439
|
+
function* encodeJsonValue(value2, options, depth) {
|
|
185440
|
+
if (isJsonPrimitive(value2)) {
|
|
185441
|
+
const encodedPrimitive = encodePrimitive(value2, options.delimiter);
|
|
185442
|
+
if (encodedPrimitive !== "") yield encodedPrimitive;
|
|
185443
|
+
return;
|
|
185444
|
+
}
|
|
185445
|
+
if (isJsonArray(value2)) yield* encodeArrayLines(void 0, value2, depth, options);
|
|
185446
|
+
else if (isJsonObject4(value2)) yield* encodeObjectLines(value2, depth, options);
|
|
185447
|
+
}
|
|
185448
|
+
function* encodeObjectLines(value2, depth, options, rootLiteralKeys, pathPrefix, remainingDepth) {
|
|
185449
|
+
const keys = Object.keys(value2);
|
|
185450
|
+
if (depth === 0 && !rootLiteralKeys) rootLiteralKeys = new Set(keys.filter((k2) => k2.includes(".")));
|
|
185451
|
+
const effectiveFlattenDepth = remainingDepth ?? options.flattenDepth;
|
|
185452
|
+
for (const [key, val] of Object.entries(value2)) yield* encodeKeyValuePairLines(key, val, depth, options, keys, rootLiteralKeys, pathPrefix, effectiveFlattenDepth);
|
|
185453
|
+
}
|
|
185454
|
+
function* encodeKeyValuePairLines(key, value2, depth, options, siblings, rootLiteralKeys, pathPrefix, flattenDepth) {
|
|
185455
|
+
const currentPath = pathPrefix ? `${pathPrefix}${DOT}${key}` : key;
|
|
185456
|
+
const effectiveFlattenDepth = flattenDepth ?? options.flattenDepth;
|
|
185457
|
+
if (options.keyFolding === "safe" && siblings) {
|
|
185458
|
+
const foldResult = tryFoldKeyChain(key, value2, siblings, options, rootLiteralKeys, pathPrefix, effectiveFlattenDepth);
|
|
185459
|
+
if (foldResult) {
|
|
185460
|
+
const { foldedKey, remainder, leafValue, segmentCount } = foldResult;
|
|
185461
|
+
const encodedFoldedKey = encodeKey(foldedKey);
|
|
185462
|
+
if (remainder === void 0) {
|
|
185463
|
+
if (isJsonPrimitive(leafValue)) {
|
|
185464
|
+
yield indentedLine(depth, `${encodedFoldedKey}: ${encodePrimitive(leafValue, options.delimiter)}`, options.indent);
|
|
185465
|
+
return;
|
|
185466
|
+
} else if (isJsonArray(leafValue)) {
|
|
185467
|
+
yield* encodeArrayLines(foldedKey, leafValue, depth, options);
|
|
185468
|
+
return;
|
|
185469
|
+
} else if (isJsonObject4(leafValue) && isEmptyObject2(leafValue)) {
|
|
185470
|
+
yield indentedLine(depth, `${encodedFoldedKey}:`, options.indent);
|
|
185471
|
+
return;
|
|
185472
|
+
}
|
|
185473
|
+
}
|
|
185474
|
+
if (isJsonObject4(remainder)) {
|
|
185475
|
+
yield indentedLine(depth, `${encodedFoldedKey}:`, options.indent);
|
|
185476
|
+
const remainingDepth = effectiveFlattenDepth - segmentCount;
|
|
185477
|
+
const foldedPath = pathPrefix ? `${pathPrefix}${DOT}${foldedKey}` : foldedKey;
|
|
185478
|
+
yield* encodeObjectLines(remainder, depth + 1, options, rootLiteralKeys, foldedPath, remainingDepth);
|
|
185479
|
+
return;
|
|
185480
|
+
}
|
|
185481
|
+
}
|
|
185482
|
+
}
|
|
185483
|
+
const encodedKey = encodeKey(key);
|
|
185484
|
+
if (isJsonPrimitive(value2)) yield indentedLine(depth, `${encodedKey}: ${encodePrimitive(value2, options.delimiter)}`, options.indent);
|
|
185485
|
+
else if (isJsonArray(value2)) yield* encodeArrayLines(key, value2, depth, options);
|
|
185486
|
+
else if (isJsonObject4(value2)) {
|
|
185487
|
+
yield indentedLine(depth, `${encodedKey}:`, options.indent);
|
|
185488
|
+
if (!isEmptyObject2(value2)) yield* encodeObjectLines(value2, depth + 1, options, rootLiteralKeys, currentPath, effectiveFlattenDepth);
|
|
185489
|
+
}
|
|
185490
|
+
}
|
|
185491
|
+
function* encodeArrayLines(key, value2, depth, options) {
|
|
185492
|
+
if (value2.length === 0) {
|
|
185493
|
+
yield indentedLine(depth, formatHeader(0, {
|
|
185494
|
+
key,
|
|
185495
|
+
delimiter: options.delimiter
|
|
185496
|
+
}), options.indent);
|
|
185497
|
+
return;
|
|
185498
|
+
}
|
|
185499
|
+
if (isArrayOfPrimitives(value2)) {
|
|
185500
|
+
yield indentedLine(depth, encodeInlineArrayLine(value2, options.delimiter, key), options.indent);
|
|
185501
|
+
return;
|
|
185502
|
+
}
|
|
185503
|
+
if (isArrayOfArrays(value2)) {
|
|
185504
|
+
if (value2.every((arr) => isArrayOfPrimitives(arr))) {
|
|
185505
|
+
yield* encodeArrayOfArraysAsListItemsLines(key, value2, depth, options);
|
|
185506
|
+
return;
|
|
185507
|
+
}
|
|
185508
|
+
}
|
|
185509
|
+
if (isArrayOfObjects(value2)) {
|
|
185510
|
+
const header = extractTabularHeader(value2);
|
|
185511
|
+
if (header) yield* encodeArrayOfObjectsAsTabularLines(key, value2, header, depth, options);
|
|
185512
|
+
else yield* encodeMixedArrayAsListItemsLines(key, value2, depth, options);
|
|
185513
|
+
return;
|
|
185514
|
+
}
|
|
185515
|
+
yield* encodeMixedArrayAsListItemsLines(key, value2, depth, options);
|
|
185516
|
+
}
|
|
185517
|
+
function* encodeArrayOfArraysAsListItemsLines(prefix, values, depth, options) {
|
|
185518
|
+
yield indentedLine(depth, formatHeader(values.length, {
|
|
185519
|
+
key: prefix,
|
|
185520
|
+
delimiter: options.delimiter
|
|
185521
|
+
}), options.indent);
|
|
185522
|
+
for (const arr of values) if (isArrayOfPrimitives(arr)) {
|
|
185523
|
+
const arrayLine = encodeInlineArrayLine(arr, options.delimiter);
|
|
185524
|
+
yield indentedListItem(depth + 1, arrayLine, options.indent);
|
|
185525
|
+
}
|
|
185526
|
+
}
|
|
185527
|
+
function encodeInlineArrayLine(values, delimiter2, prefix) {
|
|
185528
|
+
const header = formatHeader(values.length, {
|
|
185529
|
+
key: prefix,
|
|
185530
|
+
delimiter: delimiter2
|
|
185531
|
+
});
|
|
185532
|
+
const joinedValue = encodeAndJoinPrimitives(values, delimiter2);
|
|
185533
|
+
if (values.length === 0) return header;
|
|
185534
|
+
return `${header} ${joinedValue}`;
|
|
185535
|
+
}
|
|
185536
|
+
function* encodeArrayOfObjectsAsTabularLines(prefix, rows, header, depth, options) {
|
|
185537
|
+
yield indentedLine(depth, formatHeader(rows.length, {
|
|
185538
|
+
key: prefix,
|
|
185539
|
+
fields: header,
|
|
185540
|
+
delimiter: options.delimiter
|
|
185541
|
+
}), options.indent);
|
|
185542
|
+
yield* writeTabularRowsLines(rows, header, depth + 1, options);
|
|
185543
|
+
}
|
|
185544
|
+
function extractTabularHeader(rows) {
|
|
185545
|
+
if (rows.length === 0) return;
|
|
185546
|
+
const firstRow = rows[0];
|
|
185547
|
+
const firstKeys = Object.keys(firstRow);
|
|
185548
|
+
if (firstKeys.length === 0) return;
|
|
185549
|
+
if (isTabularArray(rows, firstKeys)) return firstKeys;
|
|
185550
|
+
}
|
|
185551
|
+
function isTabularArray(rows, header) {
|
|
185552
|
+
for (const row of rows) {
|
|
185553
|
+
if (Object.keys(row).length !== header.length) return false;
|
|
185554
|
+
for (const key of header) {
|
|
185555
|
+
if (!(key in row)) return false;
|
|
185556
|
+
if (!isJsonPrimitive(row[key])) return false;
|
|
185557
|
+
}
|
|
185558
|
+
}
|
|
185559
|
+
return true;
|
|
185560
|
+
}
|
|
185561
|
+
function* writeTabularRowsLines(rows, header, depth, options) {
|
|
185562
|
+
for (const row of rows) yield indentedLine(depth, encodeAndJoinPrimitives(header.map((key) => row[key]), options.delimiter), options.indent);
|
|
185563
|
+
}
|
|
185564
|
+
function* encodeMixedArrayAsListItemsLines(prefix, items, depth, options) {
|
|
185565
|
+
yield indentedLine(depth, formatHeader(items.length, {
|
|
185566
|
+
key: prefix,
|
|
185567
|
+
delimiter: options.delimiter
|
|
185568
|
+
}), options.indent);
|
|
185569
|
+
for (const item of items) yield* encodeListItemValueLines(item, depth + 1, options);
|
|
185570
|
+
}
|
|
185571
|
+
function* encodeObjectAsListItemLines(obj, depth, options) {
|
|
185572
|
+
if (isEmptyObject2(obj)) {
|
|
185573
|
+
yield indentedLine(depth, LIST_ITEM_MARKER, options.indent);
|
|
185574
|
+
return;
|
|
185575
|
+
}
|
|
185576
|
+
const entries = Object.entries(obj);
|
|
185577
|
+
if (entries.length === 1) {
|
|
185578
|
+
const [key, value2] = entries[0];
|
|
185579
|
+
if (isJsonArray(value2) && isArrayOfObjects(value2)) {
|
|
185580
|
+
const header = extractTabularHeader(value2);
|
|
185581
|
+
if (header) {
|
|
185582
|
+
yield indentedListItem(depth, formatHeader(value2.length, {
|
|
185583
|
+
key,
|
|
185584
|
+
fields: header,
|
|
185585
|
+
delimiter: options.delimiter
|
|
185586
|
+
}), options.indent);
|
|
185587
|
+
yield* writeTabularRowsLines(value2, header, depth + 1, options);
|
|
185588
|
+
return;
|
|
185589
|
+
}
|
|
185590
|
+
}
|
|
185591
|
+
}
|
|
185592
|
+
yield indentedLine(depth, LIST_ITEM_MARKER, options.indent);
|
|
185593
|
+
yield* encodeObjectLines(obj, depth + 1, options);
|
|
185594
|
+
}
|
|
185595
|
+
function* encodeListItemValueLines(value2, depth, options) {
|
|
185596
|
+
if (isJsonPrimitive(value2)) yield indentedListItem(depth, encodePrimitive(value2, options.delimiter), options.indent);
|
|
185597
|
+
else if (isJsonArray(value2)) if (isArrayOfPrimitives(value2)) yield indentedListItem(depth, encodeInlineArrayLine(value2, options.delimiter), options.indent);
|
|
185598
|
+
else {
|
|
185599
|
+
yield indentedListItem(depth, formatHeader(value2.length, { delimiter: options.delimiter }), options.indent);
|
|
185600
|
+
for (const item of value2) yield* encodeListItemValueLines(item, depth + 1, options);
|
|
185601
|
+
}
|
|
185602
|
+
else if (isJsonObject4(value2)) yield* encodeObjectAsListItemLines(value2, depth, options);
|
|
185603
|
+
}
|
|
185604
|
+
function indentedLine(depth, content, indentSize) {
|
|
185605
|
+
return " ".repeat(indentSize * depth) + content;
|
|
185606
|
+
}
|
|
185607
|
+
function indentedListItem(depth, content, indentSize) {
|
|
185608
|
+
return indentedLine(depth, LIST_ITEM_PREFIX + content, indentSize);
|
|
185609
|
+
}
|
|
185610
|
+
function encode4(input2, options) {
|
|
185611
|
+
return Array.from(encodeLines(input2, options)).join("\n");
|
|
185612
|
+
}
|
|
185613
|
+
function encodeLines(input2, options) {
|
|
185614
|
+
return encodeJsonValue(normalizeValue(input2), resolveOptions(options), 0);
|
|
185615
|
+
}
|
|
185616
|
+
function resolveOptions(options) {
|
|
185617
|
+
return {
|
|
185618
|
+
indent: options?.indent ?? 2,
|
|
185619
|
+
delimiter: options?.delimiter ?? DEFAULT_DELIMITER,
|
|
185620
|
+
keyFolding: options?.keyFolding ?? "off",
|
|
185621
|
+
flattenDepth: options?.flattenDepth ?? Number.POSITIVE_INFINITY
|
|
185622
|
+
};
|
|
185623
|
+
}
|
|
185624
|
+
|
|
185625
|
+
// mcp/geminiSanitizer.ts
|
|
185626
|
+
function parseStringEnumBranch(item) {
|
|
185627
|
+
if (!item || typeof item !== "object") return null;
|
|
185628
|
+
const record3 = item;
|
|
185629
|
+
if (Array.isArray(record3.enum)) {
|
|
185630
|
+
const strings = record3.enum.filter((v) => typeof v === "string");
|
|
185631
|
+
return strings.length === record3.enum.length && strings.length > 0 ? { values: strings } : null;
|
|
185632
|
+
}
|
|
185633
|
+
if (typeof record3.const === "string") {
|
|
185634
|
+
return { values: [record3.const] };
|
|
185635
|
+
}
|
|
185636
|
+
return null;
|
|
185637
|
+
}
|
|
185638
|
+
function collapseStringUnion(branches) {
|
|
185639
|
+
const values = [];
|
|
185640
|
+
for (const item of branches) {
|
|
185641
|
+
const parsed2 = parseStringEnumBranch(item);
|
|
185642
|
+
if (!parsed2) return null;
|
|
185643
|
+
values.push(...parsed2.values);
|
|
185644
|
+
}
|
|
185645
|
+
if (values.length === 0) return null;
|
|
185646
|
+
return { type: "string", enum: [...new Set(values)] };
|
|
185647
|
+
}
|
|
185648
|
+
function sanitizeForGemini(schema2) {
|
|
185649
|
+
if (!schema2 || typeof schema2 !== "object") return schema2;
|
|
185650
|
+
if (Array.isArray(schema2)) return schema2.map(sanitizeForGemini);
|
|
185651
|
+
const source = schema2;
|
|
185652
|
+
if (Array.isArray(source.enum) && typeof source.type !== "string") {
|
|
185653
|
+
const allStrings = source.enum.every((v) => typeof v === "string");
|
|
185654
|
+
if (allStrings) {
|
|
185655
|
+
const result = { type: "string", enum: source.enum };
|
|
185656
|
+
if (typeof source.description === "string") result.description = source.description;
|
|
185657
|
+
return result;
|
|
185658
|
+
}
|
|
185659
|
+
}
|
|
185660
|
+
for (const unionKey of ["anyOf", "oneOf"]) {
|
|
185661
|
+
const branches = source[unionKey];
|
|
185662
|
+
if (Array.isArray(branches) && branches.length > 0) {
|
|
185663
|
+
const collapsed = collapseStringUnion(branches);
|
|
185664
|
+
if (collapsed) {
|
|
185665
|
+
const result = { ...collapsed };
|
|
185666
|
+
if (typeof source.description === "string") result.description = source.description;
|
|
185667
|
+
return result;
|
|
185668
|
+
}
|
|
185669
|
+
}
|
|
185670
|
+
}
|
|
185671
|
+
if (Array.isArray(source.anyOf) || Array.isArray(source.oneOf)) {
|
|
185672
|
+
const result = {};
|
|
185673
|
+
if (Array.isArray(source.anyOf)) result.anyOf = source.anyOf.map(sanitizeForGemini);
|
|
185674
|
+
if (Array.isArray(source.oneOf)) result.oneOf = source.oneOf.map(sanitizeForGemini);
|
|
185675
|
+
return result;
|
|
185676
|
+
}
|
|
185677
|
+
const sanitized = {};
|
|
185678
|
+
for (const [key, value2] of Object.entries(source)) {
|
|
185679
|
+
if (key === "$schema") continue;
|
|
185680
|
+
if (key === "$defs") {
|
|
185681
|
+
sanitized.definitions = sanitizeForGemini(value2);
|
|
185682
|
+
continue;
|
|
185683
|
+
}
|
|
185684
|
+
sanitized[key] = sanitizeForGemini(value2);
|
|
185685
|
+
}
|
|
185686
|
+
return sanitized;
|
|
185687
|
+
}
|
|
185688
|
+
function wrapJsonSchemaProducer(producer) {
|
|
185689
|
+
return new Proxy(producer, {
|
|
185690
|
+
get(target, prop, receiver) {
|
|
185691
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
185692
|
+
if ((prop === "input" || prop === "output") && typeof value2 === "function") {
|
|
185693
|
+
const fn2 = value2;
|
|
185694
|
+
return (...args2) => sanitizeForGemini(fn2.apply(target, args2));
|
|
185695
|
+
}
|
|
185696
|
+
return value2;
|
|
185697
|
+
}
|
|
185698
|
+
});
|
|
185699
|
+
}
|
|
185700
|
+
function wrapStandard(standard) {
|
|
185701
|
+
return new Proxy(standard, {
|
|
185702
|
+
get(target, prop, receiver) {
|
|
185703
|
+
if (prop === "jsonSchema") {
|
|
185704
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
185705
|
+
if (value2 && typeof value2 === "object") {
|
|
185706
|
+
return wrapJsonSchemaProducer(value2);
|
|
185707
|
+
}
|
|
185708
|
+
return value2;
|
|
185709
|
+
}
|
|
185710
|
+
return Reflect.get(target, prop, receiver);
|
|
185711
|
+
}
|
|
185712
|
+
});
|
|
185713
|
+
}
|
|
185714
|
+
function wrapSchemaForGemini(schema2) {
|
|
185715
|
+
return new Proxy(schema2, {
|
|
185716
|
+
get(target, prop, receiver) {
|
|
185717
|
+
if (prop === "~standard") {
|
|
185718
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
185719
|
+
if (value2 && typeof value2 === "object") {
|
|
185720
|
+
return wrapStandard(value2);
|
|
185721
|
+
}
|
|
185722
|
+
return value2;
|
|
185723
|
+
}
|
|
185724
|
+
if (prop === "toJsonSchema") {
|
|
185725
|
+
const method = Reflect.get(target, prop, receiver);
|
|
185726
|
+
if (typeof method === "function") {
|
|
185727
|
+
return () => sanitizeForGemini(method.call(target));
|
|
185728
|
+
}
|
|
185729
|
+
return method;
|
|
185730
|
+
}
|
|
185731
|
+
return Reflect.get(target, prop, receiver);
|
|
185732
|
+
}
|
|
185733
|
+
});
|
|
185734
|
+
}
|
|
185735
|
+
function sanitizeToolForGemini(tool2) {
|
|
185736
|
+
if (!tool2.parameters) return tool2;
|
|
185737
|
+
return { ...tool2, parameters: wrapSchemaForGemini(tool2.parameters) };
|
|
185738
|
+
}
|
|
185739
|
+
function isGeminiRouted(ctx) {
|
|
185740
|
+
const effective = ctx.payload.proxyModel ?? ctx.resolvedModel ?? ctx.payload.model;
|
|
185741
|
+
if (!effective) return true;
|
|
185742
|
+
const normalized = effective.toLowerCase();
|
|
185743
|
+
if (normalized.includes("gemini")) return true;
|
|
185744
|
+
if (!normalized.includes("/")) return true;
|
|
185745
|
+
return false;
|
|
185746
|
+
}
|
|
185747
|
+
|
|
185748
|
+
// mcp/shared.ts
|
|
185749
|
+
function getHttpStatus(err) {
|
|
185750
|
+
if (typeof err !== "object" || err === null || !("status" in err)) return void 0;
|
|
185751
|
+
return typeof err.status === "number" ? err.status : void 0;
|
|
185752
|
+
}
|
|
185753
|
+
var tool = (toolDef) => toolDef;
|
|
185754
|
+
var handleToolSuccess = (data) => {
|
|
185755
|
+
const text = typeof data === "string" ? data : encode4(data);
|
|
185756
|
+
return {
|
|
185757
|
+
content: [{ type: "text", text }]
|
|
185758
|
+
};
|
|
185759
|
+
};
|
|
185760
|
+
var handleToolError = (error62) => {
|
|
185761
|
+
const errorMessage = error62 instanceof Error ? error62.message : String(error62);
|
|
185762
|
+
return {
|
|
185763
|
+
content: [
|
|
185764
|
+
{
|
|
185765
|
+
type: "text",
|
|
185766
|
+
text: `Error: ${errorMessage}`
|
|
185767
|
+
}
|
|
185768
|
+
],
|
|
185769
|
+
isError: true
|
|
185770
|
+
};
|
|
185771
|
+
};
|
|
185772
|
+
var execute = (fn2, toolName) => {
|
|
185773
|
+
const _fn = async (params) => {
|
|
185774
|
+
try {
|
|
185775
|
+
const result = await fn2(params);
|
|
185776
|
+
return handleToolSuccess(result);
|
|
185777
|
+
} catch (error62) {
|
|
185778
|
+
const errorMessage = error62 instanceof Error ? error62.message : String(error62);
|
|
185779
|
+
const prefix = toolName ? `[${toolName}]` : "tool";
|
|
185780
|
+
log.info(`${prefix} error: ${errorMessage}`);
|
|
185781
|
+
log.debug(`${prefix} params: ${formatJsonValue(params)}`);
|
|
185782
|
+
return handleToolError(error62);
|
|
185783
|
+
}
|
|
185784
|
+
};
|
|
185785
|
+
return _fn;
|
|
185786
|
+
};
|
|
185787
|
+
var addTools = (ctx, server, tools) => {
|
|
185788
|
+
const shouldSanitize = isGeminiRouted(ctx);
|
|
185789
|
+
for (const tool2 of tools) {
|
|
185790
|
+
server.addTool(shouldSanitize ? sanitizeToolForGemini(tool2) : tool2);
|
|
185791
|
+
}
|
|
185792
|
+
return server;
|
|
185793
|
+
};
|
|
185794
|
+
|
|
185795
|
+
// mcp/shell.ts
|
|
185796
|
+
var ShellParams = type({
|
|
185797
|
+
command: "string",
|
|
185798
|
+
// advisory and unread — no code path consumes it. kept in the schema only
|
|
185799
|
+
// because it nudges the model to state intent before running a command;
|
|
185800
|
+
// REQUIRING it cost a full rejected turn every time one was omitted. #1140.
|
|
185801
|
+
"description?": "string",
|
|
185802
|
+
"timeout?": type.number.describe(
|
|
185803
|
+
"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."
|
|
185804
|
+
),
|
|
185805
|
+
"working_directory?": "string",
|
|
185806
|
+
"background?": "boolean"
|
|
185807
|
+
});
|
|
185808
|
+
var detectedSandboxMethod;
|
|
185809
|
+
var usernsProbeError;
|
|
185810
|
+
function getSandboxMethod() {
|
|
185811
|
+
return detectSandboxMethod();
|
|
185812
|
+
}
|
|
185813
|
+
function detectSandboxMethod() {
|
|
185814
|
+
if (detectedSandboxMethod !== void 0) {
|
|
185815
|
+
return detectedSandboxMethod;
|
|
185816
|
+
}
|
|
185817
|
+
if (process.env.CI !== "true") {
|
|
185818
|
+
detectedSandboxMethod = "none";
|
|
185819
|
+
log.debug("sandbox disabled (CI !== true)");
|
|
185820
|
+
return "none";
|
|
185821
|
+
}
|
|
185822
|
+
try {
|
|
185823
|
+
const result = spawnSync6("unshare", ["--pid", "--fork", "--mount-proc", "true"], {
|
|
185824
|
+
timeout: 5e3,
|
|
185825
|
+
stdio: "ignore"
|
|
185826
|
+
});
|
|
185827
|
+
if (result.status === 0) {
|
|
185828
|
+
detectedSandboxMethod = "unshare";
|
|
185829
|
+
log.debug("PID namespace isolation enabled (unprivileged unshare)");
|
|
185830
|
+
return "unshare";
|
|
185831
|
+
}
|
|
185832
|
+
} catch {
|
|
185833
|
+
}
|
|
185834
|
+
try {
|
|
185835
|
+
const result = spawnSync6("sudo", ["unshare", "--pid", "--fork", "--mount-proc", "true"], {
|
|
185836
|
+
timeout: 5e3,
|
|
185837
|
+
stdio: "ignore"
|
|
185838
|
+
});
|
|
185839
|
+
if (result.status === 0) {
|
|
185840
|
+
detectedSandboxMethod = "sudo-unshare";
|
|
185841
|
+
log.debug("PID namespace isolation enabled (sudo unshare)");
|
|
185842
|
+
return "sudo-unshare";
|
|
185843
|
+
}
|
|
185844
|
+
} catch {
|
|
185845
|
+
}
|
|
185846
|
+
try {
|
|
185847
|
+
const result = spawnSync6(
|
|
185848
|
+
"unshare",
|
|
185849
|
+
[
|
|
185850
|
+
"--user",
|
|
185851
|
+
"--map-root-user",
|
|
185852
|
+
"--pid",
|
|
185853
|
+
"--fork",
|
|
185854
|
+
"--mount",
|
|
185855
|
+
"setpriv",
|
|
185856
|
+
"--no-new-privs",
|
|
185857
|
+
"--bounding-set",
|
|
185858
|
+
"-all",
|
|
185859
|
+
"--inh-caps",
|
|
185860
|
+
"-all",
|
|
185861
|
+
"--ambient-caps",
|
|
185862
|
+
"-all",
|
|
185863
|
+
"true"
|
|
185864
|
+
],
|
|
185865
|
+
{ timeout: 5e3, stdio: ["ignore", "ignore", "pipe"] }
|
|
185866
|
+
);
|
|
185867
|
+
if (result.status === 0) {
|
|
185868
|
+
detectedSandboxMethod = "userns-unshare";
|
|
185869
|
+
log.debug("PID namespace isolation enabled (unprivileged userns unshare)");
|
|
185870
|
+
return "userns-unshare";
|
|
185871
|
+
}
|
|
185872
|
+
usernsProbeError = result.stderr?.toString().trim() || result.error?.message;
|
|
185873
|
+
} catch {
|
|
185874
|
+
}
|
|
185875
|
+
detectedSandboxMethod = "none";
|
|
185876
|
+
log.info("PID namespace isolation not available");
|
|
185877
|
+
return "none";
|
|
185878
|
+
}
|
|
185879
|
+
var PROC_CLEANUP = "umount /proc 2>/dev/null; umount /proc 2>/dev/null; mount -t proc proc /proc 2>/dev/null;";
|
|
185880
|
+
var SOCKET_CLEANUP = [
|
|
185881
|
+
"/var/run/docker.sock",
|
|
185882
|
+
"/run/docker.sock",
|
|
185883
|
+
"/var/run/podman/podman.sock",
|
|
185884
|
+
"/run/podman/podman.sock",
|
|
185885
|
+
"/run/containerd/containerd.sock",
|
|
185886
|
+
"/var/run/crio/crio.sock"
|
|
185887
|
+
].map((path4) => `mount --bind /dev/null ${path4} 2>/dev/null;`).join(" ");
|
|
185888
|
+
function buildFsMounts(repoDir) {
|
|
185889
|
+
const escaped = repoDir.replace(/'/g, "'\\''");
|
|
185890
|
+
return [
|
|
185891
|
+
`mkdir -p /var/lib/pullfrog 2>/dev/null;`,
|
|
185892
|
+
`mount -t tmpfs tmpfs /var/lib/pullfrog 2>/dev/null;`,
|
|
185893
|
+
`[ -n "$RUNNER_TEMP" ] && [ -d "$RUNNER_TEMP/_runner_file_commands" ] && mount -t tmpfs tmpfs "$RUNNER_TEMP/_runner_file_commands" 2>/dev/null;`,
|
|
185894
|
+
`[ -e '${escaped}/.git' ] && mount --bind '${escaped}/.git' '${escaped}/.git' 2>/dev/null && mount -o remount,bind,ro '${escaped}/.git' 2>/dev/null;`,
|
|
185895
|
+
// the corepack shim dir sits FIRST on PATH (utils/packageManager.ts) and
|
|
185896
|
+
// PATH survives filterEnv, so a writable pm-bin lets sandboxed code plant
|
|
185897
|
+
// a `git`/`pnpm` that a later UNSANDBOXED spawn resolves with the parent
|
|
185898
|
+
// env. read-only closes that for every sandboxed command at once.
|
|
185899
|
+
//
|
|
185900
|
+
// the path is interpolated HERE rather than read as `$PULLFROG_TEMP_DIR`
|
|
185901
|
+
// inside the sandbox: that name is not on the `filterEnv` safe list, so in
|
|
185902
|
+
// restricted mode it expands to empty and the mount silently no-ops in the
|
|
185903
|
+
// one tier it exists to protect. `$RUNNER_TEMP` above is safe only because
|
|
185904
|
+
// `RUNNER_` is an allowed prefix.
|
|
185905
|
+
shimMount()
|
|
185906
|
+
].filter(Boolean).join(" ");
|
|
185907
|
+
}
|
|
185908
|
+
function shimMount() {
|
|
185909
|
+
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
185910
|
+
if (!tempDir) return "";
|
|
185911
|
+
const dir = join10(tempDir, "pm-bin").replace(/'/g, "'\\''");
|
|
185912
|
+
return `[ -d '${dir}' ] && mount --bind '${dir}' '${dir}' 2>/dev/null && mount -o remount,bind,ro '${dir}' 2>/dev/null;`;
|
|
185913
|
+
}
|
|
185914
|
+
var _repoRoot;
|
|
185915
|
+
function resolveRepoRoot() {
|
|
185916
|
+
if (_repoRoot) return _repoRoot;
|
|
185917
|
+
const fromEnv = process.env.GITHUB_WORKSPACE;
|
|
185918
|
+
if (fromEnv) {
|
|
185919
|
+
_repoRoot = fromEnv;
|
|
185920
|
+
return _repoRoot;
|
|
185921
|
+
}
|
|
185922
|
+
try {
|
|
185923
|
+
_repoRoot = spawnSync6("git", ["rev-parse", "--show-toplevel"], {
|
|
185924
|
+
cwd: process.cwd(),
|
|
185925
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
185926
|
+
encoding: "utf-8"
|
|
185927
|
+
}).stdout?.trim();
|
|
185928
|
+
} catch {
|
|
185929
|
+
}
|
|
185930
|
+
if (!_repoRoot) _repoRoot = process.cwd();
|
|
185931
|
+
return _repoRoot;
|
|
185932
|
+
}
|
|
185933
|
+
function spawnShell(params) {
|
|
185934
|
+
const spawnOpts = { env: params.env, cwd: params.cwd, stdio: params.stdio, detached: true };
|
|
185935
|
+
const sandboxMethod = detectSandboxMethod();
|
|
185936
|
+
const ci = process.env.CI === "true";
|
|
185937
|
+
if (ci && sandboxMethod === "none") {
|
|
185938
|
+
throw new Error(
|
|
185939
|
+
"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"
|
|
185940
|
+
);
|
|
185941
|
+
}
|
|
185942
|
+
const repoRoot = resolveRepoRoot();
|
|
185943
|
+
const fsMounts = buildFsMounts(repoRoot);
|
|
185944
|
+
if (sandboxMethod === "unshare") {
|
|
185945
|
+
return spawn3(
|
|
185946
|
+
"unshare",
|
|
185947
|
+
[
|
|
185948
|
+
"--pid",
|
|
185949
|
+
"--fork",
|
|
185950
|
+
"--mount-proc",
|
|
185951
|
+
"bash",
|
|
185952
|
+
"-c",
|
|
185953
|
+
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} ${params.command}`
|
|
185954
|
+
],
|
|
185955
|
+
spawnOpts
|
|
185956
|
+
);
|
|
185957
|
+
}
|
|
185958
|
+
if (sandboxMethod === "userns-unshare") {
|
|
185959
|
+
const escaped = params.command.replace(/'/g, "'\\''");
|
|
185960
|
+
return spawn3(
|
|
185961
|
+
"unshare",
|
|
185962
|
+
[
|
|
185963
|
+
"--user",
|
|
185964
|
+
"--map-root-user",
|
|
185965
|
+
"--pid",
|
|
185966
|
+
"--fork",
|
|
185967
|
+
"--mount",
|
|
185968
|
+
"bash",
|
|
185969
|
+
"-c",
|
|
185970
|
+
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec setpriv --no-new-privs --bounding-set -all --inh-caps -all --ambient-caps -all bash -c '${escaped}'`
|
|
185971
|
+
],
|
|
185972
|
+
spawnOpts
|
|
185973
|
+
);
|
|
185974
|
+
}
|
|
185975
|
+
if (sandboxMethod === "sudo-unshare") {
|
|
185976
|
+
const envArgs = [];
|
|
185977
|
+
for (const [k2, v] of Object.entries(params.env)) {
|
|
185978
|
+
if (v !== void 0) {
|
|
185979
|
+
envArgs.push(`${k2}=${v}`);
|
|
185980
|
+
}
|
|
185981
|
+
}
|
|
185982
|
+
const username = userInfo2().username;
|
|
185983
|
+
const pathRestore = 'export PATH="${SANDBOX_PATH:-$PATH}"; ';
|
|
185984
|
+
const escaped = (pathRestore + params.command).replace(/'/g, "'\\''");
|
|
185985
|
+
envArgs.push(`SANDBOX_PATH=${params.env.PATH ?? ""}`);
|
|
185986
|
+
return spawn3(
|
|
185987
|
+
"sudo",
|
|
185988
|
+
[
|
|
185989
|
+
"env",
|
|
185990
|
+
...envArgs,
|
|
185991
|
+
"unshare",
|
|
185992
|
+
"--pid",
|
|
185993
|
+
"--fork",
|
|
185994
|
+
"--mount-proc",
|
|
185995
|
+
"bash",
|
|
185996
|
+
"-c",
|
|
185997
|
+
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec su -p -s /bin/bash ${username} -c '${escaped}'`
|
|
185998
|
+
],
|
|
185999
|
+
{ ...spawnOpts, env: {} }
|
|
186000
|
+
);
|
|
186001
|
+
}
|
|
186002
|
+
return spawn3("bash", ["-c", params.command], spawnOpts);
|
|
186003
|
+
}
|
|
186004
|
+
async function killProcessGroup(proc) {
|
|
186005
|
+
if (!proc.pid) return;
|
|
186006
|
+
try {
|
|
186007
|
+
process.kill(-proc.pid, "SIGTERM");
|
|
186008
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
186009
|
+
process.kill(-proc.pid, "SIGKILL");
|
|
186010
|
+
} catch {
|
|
186011
|
+
try {
|
|
186012
|
+
proc.kill("SIGKILL");
|
|
186013
|
+
} catch {
|
|
186014
|
+
}
|
|
186015
|
+
}
|
|
186016
|
+
}
|
|
186017
|
+
function getTempDir() {
|
|
186018
|
+
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
186019
|
+
if (!tempDir) {
|
|
186020
|
+
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
186021
|
+
}
|
|
186022
|
+
return tempDir;
|
|
186023
|
+
}
|
|
186024
|
+
var MAX_OUTPUT_CHARS = 5e3;
|
|
186025
|
+
async function runSandboxed(params) {
|
|
186026
|
+
const proc = spawnShell({
|
|
186027
|
+
command: params.command,
|
|
186028
|
+
env: params.env,
|
|
186029
|
+
cwd: params.cwd,
|
|
186030
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
186031
|
+
});
|
|
186032
|
+
const stdout = new TailBuffer(DEFAULT_MAX_RETAINED_BYTES);
|
|
186033
|
+
const stderr = new TailBuffer(DEFAULT_MAX_RETAINED_BYTES);
|
|
186034
|
+
let timedOut = false, exited = false, spawnError = "";
|
|
186035
|
+
proc.stdout?.on("data", (chunk) => {
|
|
186036
|
+
const text = chunk.toString();
|
|
186037
|
+
stdout.append(text);
|
|
186038
|
+
params.onStdout?.(text);
|
|
186039
|
+
});
|
|
186040
|
+
proc.stderr?.on("data", (chunk) => {
|
|
186041
|
+
const text = chunk.toString();
|
|
186042
|
+
stderr.append(text);
|
|
186043
|
+
params.onStderr?.(text);
|
|
186044
|
+
});
|
|
186045
|
+
const timeoutId = setTimeout(async () => {
|
|
186046
|
+
if (!exited) {
|
|
186047
|
+
timedOut = true;
|
|
186048
|
+
await killProcessGroup(proc);
|
|
186049
|
+
}
|
|
186050
|
+
}, params.timeout);
|
|
186051
|
+
const exitCode = await new Promise((resolve3) => {
|
|
186052
|
+
const done = (code) => {
|
|
186053
|
+
exited = true;
|
|
186054
|
+
clearTimeout(timeoutId);
|
|
186055
|
+
resolve3(code);
|
|
186056
|
+
};
|
|
186057
|
+
proc.on("exit", done);
|
|
186058
|
+
proc.on("error", (err) => {
|
|
186059
|
+
spawnError = err.message;
|
|
186060
|
+
done(null);
|
|
186061
|
+
});
|
|
186062
|
+
});
|
|
186063
|
+
proc.stdout?.destroy();
|
|
186064
|
+
proc.stderr?.destroy();
|
|
186065
|
+
const outText = stdout.toString();
|
|
186066
|
+
const errText = stderr.toString();
|
|
186067
|
+
let output2 = errText ? outText ? `${outText}
|
|
186068
|
+
${errText}` : errText : outText;
|
|
186069
|
+
if (spawnError) output2 = output2 ? `${output2}
|
|
186070
|
+
${spawnError}` : spawnError;
|
|
186071
|
+
if (timedOut)
|
|
186072
|
+
output2 = output2 ? `${output2}
|
|
186073
|
+
[timed out after ${params.timeout}ms]` : `[timed out after ${params.timeout}ms]`;
|
|
186074
|
+
return {
|
|
186075
|
+
output: output2.trim(),
|
|
186076
|
+
exitCode: exitCode ?? (timedOut ? 124 : -1),
|
|
186077
|
+
timedOut
|
|
186078
|
+
};
|
|
186079
|
+
}
|
|
186080
|
+
function capOutput(output2) {
|
|
186081
|
+
if (output2.length <= MAX_OUTPUT_CHARS) return output2;
|
|
186082
|
+
const fullPath = join10(getTempDir(), `shell-${randomUUID3().slice(0, 8)}.log`);
|
|
186083
|
+
writeFileSync8(fullPath, output2);
|
|
186084
|
+
const elided = output2.length - MAX_OUTPUT_CHARS;
|
|
186085
|
+
return `... [${elided} chars truncated; output saved to ${fullPath}] ...
|
|
186086
|
+
${output2.slice(-MAX_OUTPUT_CHARS)}`;
|
|
186087
|
+
}
|
|
186088
|
+
function isGitCommand(command) {
|
|
186089
|
+
const trimmed = command.trim();
|
|
186090
|
+
if (trimmed === "git" || trimmed.startsWith("git ")) return true;
|
|
186091
|
+
if (trimmed.startsWith("sudo git")) return true;
|
|
186092
|
+
return /[;&|]\s*(?:sudo\s+)?git(?:\s|$)/.test(trimmed);
|
|
186093
|
+
}
|
|
186094
|
+
function ShellTool(ctx) {
|
|
186095
|
+
return tool({
|
|
186096
|
+
name: "shell",
|
|
186097
|
+
timeoutMs: 12e4,
|
|
186098
|
+
description: `Execute shell commands securely. Environment is filtered to remove API keys and secrets.
|
|
186099
|
+
|
|
186100
|
+
Example: \`shell({ command: "pnpm test", description: "run the test suite" })\`.
|
|
186101
|
+
|
|
186102
|
+
Use this tool to:
|
|
186103
|
+
- Run shell commands (ls, cat, grep, find, etc.)
|
|
186104
|
+
- Execute build tools (npm, pnpm, cargo, make, etc.)
|
|
186105
|
+
- Run tests and linters
|
|
186106
|
+
|
|
186107
|
+
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.
|
|
186108
|
+
|
|
186109
|
+
Do NOT use this tool for git commands \u2014 use the dedicated git tools instead.`,
|
|
186110
|
+
parameters: ShellParams,
|
|
186111
|
+
execute: execute(async (params) => {
|
|
186112
|
+
if (isGitCommand(params.command)) {
|
|
186113
|
+
throw new Error(
|
|
186114
|
+
'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'
|
|
186115
|
+
);
|
|
186116
|
+
}
|
|
186117
|
+
const timeout = Math.min(params.timeout ?? 3e4, 12e4);
|
|
186118
|
+
const cwd = params.working_directory ?? process.cwd();
|
|
186119
|
+
const env2 = resolveEnv(ctx.payload.shell === "enabled" ? "inherit" : "restricted");
|
|
186120
|
+
if (params.command.includes("agent-browser")) {
|
|
186121
|
+
const daemonError = ensureBrowserDaemon(ctx.toolState);
|
|
186122
|
+
if (daemonError) {
|
|
186123
|
+
return {
|
|
186124
|
+
output: `browser daemon unavailable: ${daemonError}`,
|
|
186125
|
+
exit_code: 1,
|
|
186126
|
+
timed_out: false
|
|
186127
|
+
};
|
|
186128
|
+
}
|
|
186129
|
+
const binDir = ctx.toolState.browserDaemon?.binDir;
|
|
186130
|
+
if (binDir) {
|
|
186131
|
+
env2.PATH = `${binDir}:${env2.PATH ?? ""}`;
|
|
186132
|
+
}
|
|
186133
|
+
}
|
|
186134
|
+
if (params.background) {
|
|
186135
|
+
const tempDir = getTempDir();
|
|
186136
|
+
const handle = `bg-${randomUUID3().slice(0, 8)}`;
|
|
186137
|
+
const outputPath = join10(tempDir, `${handle}.log`);
|
|
186138
|
+
const pidPath = join10(tempDir, `${handle}.pid`);
|
|
186139
|
+
const logFd = openSync(outputPath, "a");
|
|
186140
|
+
let proc;
|
|
186141
|
+
try {
|
|
186142
|
+
proc = spawnShell({
|
|
186143
|
+
command: params.command,
|
|
186144
|
+
env: env2,
|
|
186145
|
+
cwd,
|
|
186146
|
+
stdio: ["ignore", logFd, logFd]
|
|
186147
|
+
});
|
|
186148
|
+
} finally {
|
|
186149
|
+
closeSync(logFd);
|
|
186150
|
+
}
|
|
186151
|
+
if (!proc.pid) {
|
|
186152
|
+
throw new Error("failed to start background process");
|
|
186153
|
+
}
|
|
186154
|
+
proc.unref();
|
|
186155
|
+
writeFileSync8(pidPath, `${proc.pid}
|
|
186156
|
+
`);
|
|
186157
|
+
ctx.toolState.backgroundProcesses.set(handle, { pid: proc.pid, outputPath, pidPath });
|
|
186158
|
+
return {
|
|
186159
|
+
handle,
|
|
186160
|
+
outputPath,
|
|
186161
|
+
pidPath,
|
|
186162
|
+
message: `started background process ${handle} (pid ${proc.pid})`
|
|
186163
|
+
};
|
|
186164
|
+
}
|
|
186165
|
+
const result = await runSandboxed({ command: params.command, env: env2, cwd, timeout });
|
|
186166
|
+
if (result.exitCode !== 0) {
|
|
186167
|
+
log.info(`shell command failed with exit code ${result.exitCode}: ${params.command}`);
|
|
186168
|
+
if (result.output) log.info(`output: ${result.output}`);
|
|
186169
|
+
}
|
|
186170
|
+
return {
|
|
186171
|
+
output: capOutput(result.output),
|
|
186172
|
+
exit_code: result.exitCode,
|
|
186173
|
+
timed_out: result.timedOut
|
|
186174
|
+
};
|
|
186175
|
+
})
|
|
186176
|
+
});
|
|
186177
|
+
}
|
|
186178
|
+
var KillBackgroundParams = type({
|
|
186179
|
+
handle: type.string.describe("The handle of the background process to kill (e.g., bg-a1b2c3d4)")
|
|
186180
|
+
});
|
|
186181
|
+
function KillBackgroundTool(ctx) {
|
|
186182
|
+
return tool({
|
|
186183
|
+
name: "kill_background",
|
|
186184
|
+
mutates: true,
|
|
186185
|
+
description: `Kill a background process by its handle. Use this to stop dev servers or other long-running processes started with shell({ background: true }).`,
|
|
186186
|
+
parameters: KillBackgroundParams,
|
|
186187
|
+
execute: execute(async (params) => {
|
|
186188
|
+
const proc = ctx.toolState.backgroundProcesses.get(params.handle);
|
|
186189
|
+
if (!proc) {
|
|
186190
|
+
return {
|
|
186191
|
+
success: false,
|
|
186192
|
+
message: `no background process with handle ${params.handle}`
|
|
186193
|
+
};
|
|
186194
|
+
}
|
|
186195
|
+
try {
|
|
186196
|
+
process.kill(-proc.pid, "SIGTERM");
|
|
186197
|
+
} catch {
|
|
186198
|
+
}
|
|
186199
|
+
await sleep2(200);
|
|
186200
|
+
try {
|
|
186201
|
+
process.kill(-proc.pid, "SIGKILL");
|
|
186202
|
+
} catch {
|
|
186203
|
+
}
|
|
186204
|
+
ctx.toolState.backgroundProcesses.delete(params.handle);
|
|
186205
|
+
return {
|
|
186206
|
+
success: true,
|
|
186207
|
+
message: `killed background process ${params.handle} (pid ${proc.pid})`
|
|
186208
|
+
};
|
|
186209
|
+
})
|
|
186210
|
+
});
|
|
186211
|
+
}
|
|
186212
|
+
|
|
185259
186213
|
// utils/lifecycle.ts
|
|
185260
186214
|
function describeSetupFailure(failure2) {
|
|
185261
186215
|
if (!failure2) return "";
|
|
@@ -185277,44 +186231,47 @@ ${failure2.output || "(empty)"}`;
|
|
|
185277
186231
|
async function executeLifecycleHook(params) {
|
|
185278
186232
|
if (!params.script) return {};
|
|
185279
186233
|
log.info(`\xBB executing ${params.event} lifecycle hook...`);
|
|
186234
|
+
resolveTrustedGit();
|
|
185280
186235
|
const preHookTrackedCount = params.normalizeWorkingTreeAfter ? (await runGitLines(["diff", "--name-only", "HEAD"])).length : 0;
|
|
185281
186236
|
let result;
|
|
185282
186237
|
try {
|
|
186238
|
+
let sandboxed;
|
|
185283
186239
|
try {
|
|
185284
|
-
|
|
185285
|
-
|
|
185286
|
-
|
|
185287
|
-
|
|
186240
|
+
sandboxed = await runSandboxed({
|
|
186241
|
+
command: params.script,
|
|
186242
|
+
env: resolveEnv(params.shell === "enabled" ? "inherit" : "restricted"),
|
|
186243
|
+
cwd: process.cwd(),
|
|
185288
186244
|
timeout: LIFECYCLE_HOOK_TIMEOUT_MS,
|
|
185289
|
-
activityTimeout: 0,
|
|
185290
186245
|
onStdout: (chunk) => process.stdout.write(chunk),
|
|
185291
186246
|
onStderr: (chunk) => process.stderr.write(chunk)
|
|
185292
186247
|
});
|
|
185293
|
-
if (spawnResult.exitCode !== 0) {
|
|
185294
|
-
const output2 = (spawnResult.stderr || spawnResult.stdout).trim();
|
|
185295
|
-
result = {
|
|
185296
|
-
failure: { kind: "exit", output: output2, exitCode: spawnResult.exitCode },
|
|
185297
|
-
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.`
|
|
185298
|
-
};
|
|
185299
|
-
} else {
|
|
185300
|
-
log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
|
|
185301
|
-
result = {};
|
|
185302
|
-
}
|
|
185303
186248
|
} catch (err) {
|
|
185304
|
-
const
|
|
185305
|
-
|
|
185306
|
-
|
|
185307
|
-
|
|
185308
|
-
|
|
185309
|
-
|
|
185310
|
-
|
|
185311
|
-
|
|
185312
|
-
|
|
185313
|
-
|
|
185314
|
-
|
|
185315
|
-
|
|
185316
|
-
|
|
185317
|
-
|
|
186249
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
186250
|
+
return {
|
|
186251
|
+
failure: { kind: "spawn", spawnError: msg },
|
|
186252
|
+
warning: `lifecycle hook '${params.event}' could not be sandboxed, so it did not run: ${msg}`
|
|
186253
|
+
};
|
|
186254
|
+
}
|
|
186255
|
+
if (sandboxed.timedOut) {
|
|
186256
|
+
const minutes = Math.round(LIFECYCLE_HOOK_TIMEOUT_MS / 6e4);
|
|
186257
|
+
result = {
|
|
186258
|
+
failure: { kind: "timeout" },
|
|
186259
|
+
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).`
|
|
186260
|
+
};
|
|
186261
|
+
} else if (sandboxed.exitCode === -1) {
|
|
186262
|
+
result = {
|
|
186263
|
+
failure: { kind: "spawn", spawnError: sandboxed.output },
|
|
186264
|
+
warning: `lifecycle hook '${params.event}' failed to spawn: ${sandboxed.output}. this is likely a transient failure \u2014 retry the operation.`
|
|
186265
|
+
};
|
|
186266
|
+
} else if (sandboxed.exitCode !== 0) {
|
|
186267
|
+
const output2 = capOutput(sandboxed.output);
|
|
186268
|
+
result = {
|
|
186269
|
+
failure: { kind: "exit", output: output2, exitCode: sandboxed.exitCode },
|
|
186270
|
+
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.`
|
|
186271
|
+
};
|
|
186272
|
+
} else {
|
|
186273
|
+
log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
|
|
186274
|
+
result = {};
|
|
185318
186275
|
}
|
|
185319
186276
|
} finally {
|
|
185320
186277
|
if (params.normalizeWorkingTreeAfter) {
|
|
@@ -185335,8 +186292,20 @@ async function normalizeWorkingTreeAfterHook(params) {
|
|
|
185335
186292
|
await runGit(["restore", "--staged", "--worktree", "."]);
|
|
185336
186293
|
log.info(`\xBB discarded ${trackedCount} tracked changes from ${params.event} hook`);
|
|
185337
186294
|
}
|
|
186295
|
+
var trustedGit;
|
|
186296
|
+
function resolveTrustedGit() {
|
|
186297
|
+
if (trustedGit) return trustedGit;
|
|
186298
|
+
const resolved = spawnSync7("sh", ["-c", "command -v git"], { encoding: "utf-8" });
|
|
186299
|
+
trustedGit = resolved.stdout?.trim() || "git";
|
|
186300
|
+
return trustedGit;
|
|
186301
|
+
}
|
|
185338
186302
|
async function runGit(args2) {
|
|
185339
|
-
const result = await spawn({
|
|
186303
|
+
const result = await spawn({
|
|
186304
|
+
cmd: resolveTrustedGit(),
|
|
186305
|
+
args: args2,
|
|
186306
|
+
env: filterEnvForUntrustedCode(),
|
|
186307
|
+
activityTimeout: 0
|
|
186308
|
+
});
|
|
185340
186309
|
if (result.exitCode !== 0) {
|
|
185341
186310
|
throw new Error(
|
|
185342
186311
|
`git ${args2.join(" ")} failed (exit ${result.exitCode}): ${result.stderr.trim() || "(no stderr)"}`
|
|
@@ -185456,7 +186425,7 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
|
|
|
185456
186425
|
|
|
185457
186426
|
// yes/index.ts
|
|
185458
186427
|
import { performance as performance8 } from "node:perf_hooks";
|
|
185459
|
-
import { setTimeout as
|
|
186428
|
+
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
185460
186429
|
|
|
185461
186430
|
// node_modules/.pnpm/lru-cache@11.5.2/node_modules/lru-cache/dist/esm/node/index.min.js
|
|
185462
186431
|
import { tracingChannel as G, channel as P } from "node:diagnostics_channel";
|
|
@@ -186250,7 +187219,7 @@ function _op(fn2, options) {
|
|
|
186250
187219
|
);
|
|
186251
187220
|
log2.info({ key, error: error62, attempt });
|
|
186252
187221
|
}
|
|
186253
|
-
await
|
|
187222
|
+
await sleep3(delay2);
|
|
186254
187223
|
}
|
|
186255
187224
|
}
|
|
186256
187225
|
}
|
|
@@ -186318,14 +187287,14 @@ function _op(fn2, options) {
|
|
|
186318
187287
|
}
|
|
186319
187288
|
|
|
186320
187289
|
// mcp/git.ts
|
|
186321
|
-
import { randomUUID as
|
|
186322
|
-
import { writeFileSync as
|
|
186323
|
-
import { join as
|
|
187290
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
187291
|
+
import { writeFileSync as writeFileSync9 } from "node:fs";
|
|
187292
|
+
import { join as join13 } from "node:path";
|
|
186324
187293
|
|
|
186325
187294
|
// utils/apiCommit.ts
|
|
186326
187295
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
186327
187296
|
import { lstat, readlink } from "node:fs/promises";
|
|
186328
|
-
import { join as
|
|
187297
|
+
import { join as join11 } from "node:path";
|
|
186329
187298
|
var GITHUB_API = "https://api.github.com";
|
|
186330
187299
|
var MAX_BLOB_BYTES = 30 * 1024 * 1024;
|
|
186331
187300
|
var BLOB_UPLOAD_CONCURRENCY = 8;
|
|
@@ -186390,7 +187359,7 @@ async function assertApiCommittable(files) {
|
|
|
186390
187359
|
const root = getRepoRoot();
|
|
186391
187360
|
const attrs = $(
|
|
186392
187361
|
"git",
|
|
186393
|
-
["check-attr", "filter", "-z", "--", ...present.map((p) =>
|
|
187362
|
+
["check-attr", "filter", "-z", "--", ...present.map((p) => join11(root, p))],
|
|
186394
187363
|
{ log: false }
|
|
186395
187364
|
);
|
|
186396
187365
|
const parts = attrs.split("\0");
|
|
@@ -186402,7 +187371,7 @@ async function assertApiCommittable(files) {
|
|
|
186402
187371
|
}
|
|
186403
187372
|
}
|
|
186404
187373
|
for (const path4 of present) {
|
|
186405
|
-
const stat = await lstat(
|
|
187374
|
+
const stat = await lstat(join11(root, path4));
|
|
186406
187375
|
if (stat.isDirectory()) {
|
|
186407
187376
|
throw new Error(
|
|
186408
187377
|
`'${path4}' is a directory (nested repository or submodule?) \u2014 signed commits only support files and symlinks.`
|
|
@@ -186411,7 +187380,7 @@ async function assertApiCommittable(files) {
|
|
|
186411
187380
|
}
|
|
186412
187381
|
}
|
|
186413
187382
|
async function createBlobEntry(params) {
|
|
186414
|
-
const absPath =
|
|
187383
|
+
const absPath = join11(params.repoRoot, params.path);
|
|
186415
187384
|
const stat = await lstat(absPath);
|
|
186416
187385
|
if (stat.size > MAX_BLOB_BYTES) {
|
|
186417
187386
|
throw new Error(
|
|
@@ -186591,7 +187560,7 @@ this usually means your local branch has commits that were never pushed \u2014 s
|
|
|
186591
187560
|
var core5 = __toESM(require_core(), 1);
|
|
186592
187561
|
import { createSign } from "node:crypto";
|
|
186593
187562
|
import { rename, writeFile } from "node:fs/promises";
|
|
186594
|
-
import { dirname as dirname3, join as
|
|
187563
|
+
import { dirname as dirname3, join as join12 } from "node:path";
|
|
186595
187564
|
|
|
186596
187565
|
// node_modules/.pnpm/@octokit+plugin-throttling@11.0.5_@octokit+core@7.0.8/node_modules/@octokit/plugin-throttling/dist-bundle/index.js
|
|
186597
187566
|
var import_light = __toESM(require_light(), 1);
|
|
@@ -186963,7 +187932,7 @@ function lowercaseKeys(object5) {
|
|
|
186963
187932
|
return newObj;
|
|
186964
187933
|
}, {});
|
|
186965
187934
|
}
|
|
186966
|
-
function
|
|
187935
|
+
function isPlainObject7(value2) {
|
|
186967
187936
|
if (typeof value2 !== "object" || value2 === null) return false;
|
|
186968
187937
|
if (Object.prototype.toString.call(value2) !== "[object Object]") return false;
|
|
186969
187938
|
const proto = Object.getPrototypeOf(value2);
|
|
@@ -186974,7 +187943,7 @@ function isPlainObject6(value2) {
|
|
|
186974
187943
|
function mergeDeep(defaults, options) {
|
|
186975
187944
|
const result = Object.assign({}, defaults);
|
|
186976
187945
|
Object.keys(options).forEach((key) => {
|
|
186977
|
-
if (
|
|
187946
|
+
if (isPlainObject7(options[key])) {
|
|
186978
187947
|
if (!(key in defaults)) Object.assign(result, { [key]: options[key] });
|
|
186979
187948
|
else result[key] = mergeDeep(defaults[key], options[key]);
|
|
186980
187949
|
} else {
|
|
@@ -187261,7 +188230,7 @@ var NullObject = /* @__PURE__ */ (() => {
|
|
|
187261
188230
|
return C;
|
|
187262
188231
|
})();
|
|
187263
188232
|
function parse5(header, options) {
|
|
187264
|
-
const stopChar = options?.comma === true ?
|
|
188233
|
+
const stopChar = options?.comma === true ? COMMA2 : 65536;
|
|
187265
188234
|
const len = header.length;
|
|
187266
188235
|
let index = skipOWS(header, options?.start ?? 0, len);
|
|
187267
188236
|
const valueStart = index;
|
|
@@ -187279,7 +188248,7 @@ var SEMI = 59;
|
|
|
187279
188248
|
var EQ = 61;
|
|
187280
188249
|
var DQUOTE = 34;
|
|
187281
188250
|
var BSLASH = 92;
|
|
187282
|
-
var
|
|
188251
|
+
var COMMA2 = 44;
|
|
187283
188252
|
function parseParameters(header, type2, index, len, stopChar) {
|
|
187284
188253
|
const parameters = new NullObject();
|
|
187285
188254
|
parameter: while (index < len) {
|
|
@@ -187738,7 +188707,7 @@ var defaults_default = {
|
|
|
187738
188707
|
"user-agent": `octokit-request.js/${VERSION3} ${getUserAgent2()}`
|
|
187739
188708
|
}
|
|
187740
188709
|
};
|
|
187741
|
-
function
|
|
188710
|
+
function isPlainObject8(value2) {
|
|
187742
188711
|
if (typeof value2 !== "object" || value2 === null) return false;
|
|
187743
188712
|
if (Object.prototype.toString.call(value2) !== "[object Object]") return false;
|
|
187744
188713
|
const proto = Object.getPrototypeOf(value2);
|
|
@@ -187756,7 +188725,7 @@ async function fetchWrapper(requestOptions) {
|
|
|
187756
188725
|
}
|
|
187757
188726
|
const log3 = requestOptions.request?.log || console;
|
|
187758
188727
|
const parseSuccessResponseBody = requestOptions.request?.parseSuccessResponseBody !== false;
|
|
187759
|
-
const body =
|
|
188728
|
+
const body = isPlainObject8(requestOptions.body) || Array.isArray(requestOptions.body) ? JSONStringify(requestOptions.body) : requestOptions.body;
|
|
187760
188729
|
const requestHeaders = Object.fromEntries(
|
|
187761
188730
|
Object.entries(requestOptions.headers).map(([name, value2]) => [
|
|
187762
188731
|
name,
|
|
@@ -191061,7 +192030,7 @@ function getGitHubUsageSummary() {
|
|
|
191061
192030
|
}
|
|
191062
192031
|
async function writeGitHubUsageSummaryToFile(path4) {
|
|
191063
192032
|
const summary2 = getGitHubUsageSummary();
|
|
191064
|
-
const tmpPath =
|
|
192033
|
+
const tmpPath = join12(dirname3(path4), `.usage-summary-${process.pid}.tmp`);
|
|
191065
192034
|
await writeFile(tmpPath, JSON.stringify(summary2));
|
|
191066
192035
|
await rename(tmpPath, path4);
|
|
191067
192036
|
}
|
|
@@ -191168,531 +192137,6 @@ function resolveRepoCtx(ctx, repo) {
|
|
|
191168
192137
|
};
|
|
191169
192138
|
}
|
|
191170
192139
|
|
|
191171
|
-
// node_modules/.pnpm/@toon-format+toon@1.4.0/node_modules/@toon-format/toon/dist/index.mjs
|
|
191172
|
-
var LIST_ITEM_MARKER = "-";
|
|
191173
|
-
var LIST_ITEM_PREFIX = "- ";
|
|
191174
|
-
var COMMA2 = ",";
|
|
191175
|
-
var PIPE = "|";
|
|
191176
|
-
var DOT = ".";
|
|
191177
|
-
var NULL_LITERAL = "null";
|
|
191178
|
-
var TRUE_LITERAL = "true";
|
|
191179
|
-
var FALSE_LITERAL = "false";
|
|
191180
|
-
var BACKSLASH = "\\";
|
|
191181
|
-
var DOUBLE_QUOTE = '"';
|
|
191182
|
-
var TAB = " ";
|
|
191183
|
-
var DELIMITERS = {
|
|
191184
|
-
comma: COMMA2,
|
|
191185
|
-
tab: TAB,
|
|
191186
|
-
pipe: PIPE
|
|
191187
|
-
};
|
|
191188
|
-
var DEFAULT_DELIMITER = DELIMITERS.comma;
|
|
191189
|
-
function escapeString(value2) {
|
|
191190
|
-
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`);
|
|
191191
|
-
}
|
|
191192
|
-
function isBooleanOrNullLiteral(token) {
|
|
191193
|
-
return token === TRUE_LITERAL || token === FALSE_LITERAL || token === NULL_LITERAL;
|
|
191194
|
-
}
|
|
191195
|
-
function normalizeValue(value2) {
|
|
191196
|
-
if (value2 === null) return null;
|
|
191197
|
-
if (typeof value2 === "string" || typeof value2 === "boolean") return value2;
|
|
191198
|
-
if (typeof value2 === "number") {
|
|
191199
|
-
if (Object.is(value2, -0)) return 0;
|
|
191200
|
-
if (!Number.isFinite(value2)) return null;
|
|
191201
|
-
return value2;
|
|
191202
|
-
}
|
|
191203
|
-
if (typeof value2 === "bigint") {
|
|
191204
|
-
if (value2 >= Number.MIN_SAFE_INTEGER && value2 <= Number.MAX_SAFE_INTEGER) return Number(value2);
|
|
191205
|
-
return value2.toString();
|
|
191206
|
-
}
|
|
191207
|
-
if (value2 instanceof Date) return value2.toISOString();
|
|
191208
|
-
if (Array.isArray(value2)) return value2.map(normalizeValue);
|
|
191209
|
-
if (value2 instanceof Set) return Array.from(value2).map(normalizeValue);
|
|
191210
|
-
if (value2 instanceof Map) return Object.fromEntries(Array.from(value2, ([k2, v]) => [String(k2), normalizeValue(v)]));
|
|
191211
|
-
if (isPlainObject8(value2)) {
|
|
191212
|
-
const normalized = {};
|
|
191213
|
-
for (const key in value2) if (Object.prototype.hasOwnProperty.call(value2, key)) normalized[key] = normalizeValue(value2[key]);
|
|
191214
|
-
return normalized;
|
|
191215
|
-
}
|
|
191216
|
-
return null;
|
|
191217
|
-
}
|
|
191218
|
-
function isJsonPrimitive(value2) {
|
|
191219
|
-
return value2 === null || typeof value2 === "string" || typeof value2 === "number" || typeof value2 === "boolean";
|
|
191220
|
-
}
|
|
191221
|
-
function isJsonArray(value2) {
|
|
191222
|
-
return Array.isArray(value2);
|
|
191223
|
-
}
|
|
191224
|
-
function isJsonObject4(value2) {
|
|
191225
|
-
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
191226
|
-
}
|
|
191227
|
-
function isEmptyObject2(value2) {
|
|
191228
|
-
return Object.keys(value2).length === 0;
|
|
191229
|
-
}
|
|
191230
|
-
function isPlainObject8(value2) {
|
|
191231
|
-
if (value2 === null || typeof value2 !== "object") return false;
|
|
191232
|
-
const prototype = Object.getPrototypeOf(value2);
|
|
191233
|
-
return prototype === null || prototype === Object.prototype;
|
|
191234
|
-
}
|
|
191235
|
-
function isArrayOfPrimitives(value2) {
|
|
191236
|
-
return value2.length === 0 || value2.every((item) => isJsonPrimitive(item));
|
|
191237
|
-
}
|
|
191238
|
-
function isArrayOfArrays(value2) {
|
|
191239
|
-
return value2.length === 0 || value2.every((item) => isJsonArray(item));
|
|
191240
|
-
}
|
|
191241
|
-
function isArrayOfObjects(value2) {
|
|
191242
|
-
return value2.length === 0 || value2.every((item) => isJsonObject4(item));
|
|
191243
|
-
}
|
|
191244
|
-
function isValidUnquotedKey(key) {
|
|
191245
|
-
return /^[A-Z_][\w.]*$/i.test(key);
|
|
191246
|
-
}
|
|
191247
|
-
function isIdentifierSegment(key) {
|
|
191248
|
-
return /^[A-Z_]\w*$/i.test(key);
|
|
191249
|
-
}
|
|
191250
|
-
function isSafeUnquoted(value2, delimiter2 = DEFAULT_DELIMITER) {
|
|
191251
|
-
if (!value2) return false;
|
|
191252
|
-
if (value2 !== value2.trim()) return false;
|
|
191253
|
-
if (isBooleanOrNullLiteral(value2) || isNumericLike(value2)) return false;
|
|
191254
|
-
if (value2.includes(":")) return false;
|
|
191255
|
-
if (value2.includes('"') || value2.includes("\\")) return false;
|
|
191256
|
-
if (/[[\]{}]/.test(value2)) return false;
|
|
191257
|
-
if (/[\n\r\t]/.test(value2)) return false;
|
|
191258
|
-
if (value2.includes(delimiter2)) return false;
|
|
191259
|
-
if (value2.startsWith(LIST_ITEM_MARKER)) return false;
|
|
191260
|
-
return true;
|
|
191261
|
-
}
|
|
191262
|
-
function isNumericLike(value2) {
|
|
191263
|
-
return /^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(value2) || /^0\d+$/.test(value2);
|
|
191264
|
-
}
|
|
191265
|
-
var QUOTED_KEY_MARKER = Symbol("quotedKey");
|
|
191266
|
-
function tryFoldKeyChain(key, value2, siblings, options, rootLiteralKeys, pathPrefix, flattenDepth) {
|
|
191267
|
-
if (options.keyFolding !== "safe") return;
|
|
191268
|
-
if (!isJsonObject4(value2)) return;
|
|
191269
|
-
const { segments, tail, leafValue } = collectSingleKeyChain(key, value2, flattenDepth ?? options.flattenDepth);
|
|
191270
|
-
if (segments.length < 2) return;
|
|
191271
|
-
if (!segments.every((seg) => isIdentifierSegment(seg))) return;
|
|
191272
|
-
const foldedKey = buildFoldedKey(segments);
|
|
191273
|
-
const absolutePath = pathPrefix ? `${pathPrefix}${DOT}${foldedKey}` : foldedKey;
|
|
191274
|
-
if (siblings.includes(foldedKey)) return;
|
|
191275
|
-
if (rootLiteralKeys && rootLiteralKeys.has(absolutePath)) return;
|
|
191276
|
-
return {
|
|
191277
|
-
foldedKey,
|
|
191278
|
-
remainder: tail,
|
|
191279
|
-
leafValue,
|
|
191280
|
-
segmentCount: segments.length
|
|
191281
|
-
};
|
|
191282
|
-
}
|
|
191283
|
-
function collectSingleKeyChain(startKey, startValue, maxDepth) {
|
|
191284
|
-
const segments = [startKey];
|
|
191285
|
-
let currentValue = startValue;
|
|
191286
|
-
while (segments.length < maxDepth) {
|
|
191287
|
-
if (!isJsonObject4(currentValue)) break;
|
|
191288
|
-
const keys = Object.keys(currentValue);
|
|
191289
|
-
if (keys.length !== 1) break;
|
|
191290
|
-
const nextKey = keys[0];
|
|
191291
|
-
const nextValue = currentValue[nextKey];
|
|
191292
|
-
segments.push(nextKey);
|
|
191293
|
-
currentValue = nextValue;
|
|
191294
|
-
}
|
|
191295
|
-
if (!isJsonObject4(currentValue) || isEmptyObject2(currentValue)) return {
|
|
191296
|
-
segments,
|
|
191297
|
-
tail: void 0,
|
|
191298
|
-
leafValue: currentValue
|
|
191299
|
-
};
|
|
191300
|
-
return {
|
|
191301
|
-
segments,
|
|
191302
|
-
tail: currentValue,
|
|
191303
|
-
leafValue: currentValue
|
|
191304
|
-
};
|
|
191305
|
-
}
|
|
191306
|
-
function buildFoldedKey(segments) {
|
|
191307
|
-
return segments.join(DOT);
|
|
191308
|
-
}
|
|
191309
|
-
function encodePrimitive(value2, delimiter2) {
|
|
191310
|
-
if (value2 === null) return NULL_LITERAL;
|
|
191311
|
-
if (typeof value2 === "boolean") return String(value2);
|
|
191312
|
-
if (typeof value2 === "number") return String(value2);
|
|
191313
|
-
return encodeStringLiteral(value2, delimiter2);
|
|
191314
|
-
}
|
|
191315
|
-
function encodeStringLiteral(value2, delimiter2 = DEFAULT_DELIMITER) {
|
|
191316
|
-
if (isSafeUnquoted(value2, delimiter2)) return value2;
|
|
191317
|
-
return `${DOUBLE_QUOTE}${escapeString(value2)}${DOUBLE_QUOTE}`;
|
|
191318
|
-
}
|
|
191319
|
-
function encodeKey(key) {
|
|
191320
|
-
if (isValidUnquotedKey(key)) return key;
|
|
191321
|
-
return `${DOUBLE_QUOTE}${escapeString(key)}${DOUBLE_QUOTE}`;
|
|
191322
|
-
}
|
|
191323
|
-
function encodeAndJoinPrimitives(values, delimiter2 = DEFAULT_DELIMITER) {
|
|
191324
|
-
return values.map((v) => encodePrimitive(v, delimiter2)).join(delimiter2);
|
|
191325
|
-
}
|
|
191326
|
-
function formatHeader(length, options) {
|
|
191327
|
-
const key = options?.key;
|
|
191328
|
-
const fields = options?.fields;
|
|
191329
|
-
const delimiter2 = options?.delimiter ?? COMMA2;
|
|
191330
|
-
let header = "";
|
|
191331
|
-
if (key) header += encodeKey(key);
|
|
191332
|
-
header += `[${length}${delimiter2 !== DEFAULT_DELIMITER ? delimiter2 : ""}]`;
|
|
191333
|
-
if (fields) {
|
|
191334
|
-
const quotedFields = fields.map((f) => encodeKey(f));
|
|
191335
|
-
header += `{${quotedFields.join(delimiter2)}}`;
|
|
191336
|
-
}
|
|
191337
|
-
header += ":";
|
|
191338
|
-
return header;
|
|
191339
|
-
}
|
|
191340
|
-
function* encodeJsonValue(value2, options, depth) {
|
|
191341
|
-
if (isJsonPrimitive(value2)) {
|
|
191342
|
-
const encodedPrimitive = encodePrimitive(value2, options.delimiter);
|
|
191343
|
-
if (encodedPrimitive !== "") yield encodedPrimitive;
|
|
191344
|
-
return;
|
|
191345
|
-
}
|
|
191346
|
-
if (isJsonArray(value2)) yield* encodeArrayLines(void 0, value2, depth, options);
|
|
191347
|
-
else if (isJsonObject4(value2)) yield* encodeObjectLines(value2, depth, options);
|
|
191348
|
-
}
|
|
191349
|
-
function* encodeObjectLines(value2, depth, options, rootLiteralKeys, pathPrefix, remainingDepth) {
|
|
191350
|
-
const keys = Object.keys(value2);
|
|
191351
|
-
if (depth === 0 && !rootLiteralKeys) rootLiteralKeys = new Set(keys.filter((k2) => k2.includes(".")));
|
|
191352
|
-
const effectiveFlattenDepth = remainingDepth ?? options.flattenDepth;
|
|
191353
|
-
for (const [key, val] of Object.entries(value2)) yield* encodeKeyValuePairLines(key, val, depth, options, keys, rootLiteralKeys, pathPrefix, effectiveFlattenDepth);
|
|
191354
|
-
}
|
|
191355
|
-
function* encodeKeyValuePairLines(key, value2, depth, options, siblings, rootLiteralKeys, pathPrefix, flattenDepth) {
|
|
191356
|
-
const currentPath = pathPrefix ? `${pathPrefix}${DOT}${key}` : key;
|
|
191357
|
-
const effectiveFlattenDepth = flattenDepth ?? options.flattenDepth;
|
|
191358
|
-
if (options.keyFolding === "safe" && siblings) {
|
|
191359
|
-
const foldResult = tryFoldKeyChain(key, value2, siblings, options, rootLiteralKeys, pathPrefix, effectiveFlattenDepth);
|
|
191360
|
-
if (foldResult) {
|
|
191361
|
-
const { foldedKey, remainder, leafValue, segmentCount } = foldResult;
|
|
191362
|
-
const encodedFoldedKey = encodeKey(foldedKey);
|
|
191363
|
-
if (remainder === void 0) {
|
|
191364
|
-
if (isJsonPrimitive(leafValue)) {
|
|
191365
|
-
yield indentedLine(depth, `${encodedFoldedKey}: ${encodePrimitive(leafValue, options.delimiter)}`, options.indent);
|
|
191366
|
-
return;
|
|
191367
|
-
} else if (isJsonArray(leafValue)) {
|
|
191368
|
-
yield* encodeArrayLines(foldedKey, leafValue, depth, options);
|
|
191369
|
-
return;
|
|
191370
|
-
} else if (isJsonObject4(leafValue) && isEmptyObject2(leafValue)) {
|
|
191371
|
-
yield indentedLine(depth, `${encodedFoldedKey}:`, options.indent);
|
|
191372
|
-
return;
|
|
191373
|
-
}
|
|
191374
|
-
}
|
|
191375
|
-
if (isJsonObject4(remainder)) {
|
|
191376
|
-
yield indentedLine(depth, `${encodedFoldedKey}:`, options.indent);
|
|
191377
|
-
const remainingDepth = effectiveFlattenDepth - segmentCount;
|
|
191378
|
-
const foldedPath = pathPrefix ? `${pathPrefix}${DOT}${foldedKey}` : foldedKey;
|
|
191379
|
-
yield* encodeObjectLines(remainder, depth + 1, options, rootLiteralKeys, foldedPath, remainingDepth);
|
|
191380
|
-
return;
|
|
191381
|
-
}
|
|
191382
|
-
}
|
|
191383
|
-
}
|
|
191384
|
-
const encodedKey = encodeKey(key);
|
|
191385
|
-
if (isJsonPrimitive(value2)) yield indentedLine(depth, `${encodedKey}: ${encodePrimitive(value2, options.delimiter)}`, options.indent);
|
|
191386
|
-
else if (isJsonArray(value2)) yield* encodeArrayLines(key, value2, depth, options);
|
|
191387
|
-
else if (isJsonObject4(value2)) {
|
|
191388
|
-
yield indentedLine(depth, `${encodedKey}:`, options.indent);
|
|
191389
|
-
if (!isEmptyObject2(value2)) yield* encodeObjectLines(value2, depth + 1, options, rootLiteralKeys, currentPath, effectiveFlattenDepth);
|
|
191390
|
-
}
|
|
191391
|
-
}
|
|
191392
|
-
function* encodeArrayLines(key, value2, depth, options) {
|
|
191393
|
-
if (value2.length === 0) {
|
|
191394
|
-
yield indentedLine(depth, formatHeader(0, {
|
|
191395
|
-
key,
|
|
191396
|
-
delimiter: options.delimiter
|
|
191397
|
-
}), options.indent);
|
|
191398
|
-
return;
|
|
191399
|
-
}
|
|
191400
|
-
if (isArrayOfPrimitives(value2)) {
|
|
191401
|
-
yield indentedLine(depth, encodeInlineArrayLine(value2, options.delimiter, key), options.indent);
|
|
191402
|
-
return;
|
|
191403
|
-
}
|
|
191404
|
-
if (isArrayOfArrays(value2)) {
|
|
191405
|
-
if (value2.every((arr) => isArrayOfPrimitives(arr))) {
|
|
191406
|
-
yield* encodeArrayOfArraysAsListItemsLines(key, value2, depth, options);
|
|
191407
|
-
return;
|
|
191408
|
-
}
|
|
191409
|
-
}
|
|
191410
|
-
if (isArrayOfObjects(value2)) {
|
|
191411
|
-
const header = extractTabularHeader(value2);
|
|
191412
|
-
if (header) yield* encodeArrayOfObjectsAsTabularLines(key, value2, header, depth, options);
|
|
191413
|
-
else yield* encodeMixedArrayAsListItemsLines(key, value2, depth, options);
|
|
191414
|
-
return;
|
|
191415
|
-
}
|
|
191416
|
-
yield* encodeMixedArrayAsListItemsLines(key, value2, depth, options);
|
|
191417
|
-
}
|
|
191418
|
-
function* encodeArrayOfArraysAsListItemsLines(prefix, values, depth, options) {
|
|
191419
|
-
yield indentedLine(depth, formatHeader(values.length, {
|
|
191420
|
-
key: prefix,
|
|
191421
|
-
delimiter: options.delimiter
|
|
191422
|
-
}), options.indent);
|
|
191423
|
-
for (const arr of values) if (isArrayOfPrimitives(arr)) {
|
|
191424
|
-
const arrayLine = encodeInlineArrayLine(arr, options.delimiter);
|
|
191425
|
-
yield indentedListItem(depth + 1, arrayLine, options.indent);
|
|
191426
|
-
}
|
|
191427
|
-
}
|
|
191428
|
-
function encodeInlineArrayLine(values, delimiter2, prefix) {
|
|
191429
|
-
const header = formatHeader(values.length, {
|
|
191430
|
-
key: prefix,
|
|
191431
|
-
delimiter: delimiter2
|
|
191432
|
-
});
|
|
191433
|
-
const joinedValue = encodeAndJoinPrimitives(values, delimiter2);
|
|
191434
|
-
if (values.length === 0) return header;
|
|
191435
|
-
return `${header} ${joinedValue}`;
|
|
191436
|
-
}
|
|
191437
|
-
function* encodeArrayOfObjectsAsTabularLines(prefix, rows, header, depth, options) {
|
|
191438
|
-
yield indentedLine(depth, formatHeader(rows.length, {
|
|
191439
|
-
key: prefix,
|
|
191440
|
-
fields: header,
|
|
191441
|
-
delimiter: options.delimiter
|
|
191442
|
-
}), options.indent);
|
|
191443
|
-
yield* writeTabularRowsLines(rows, header, depth + 1, options);
|
|
191444
|
-
}
|
|
191445
|
-
function extractTabularHeader(rows) {
|
|
191446
|
-
if (rows.length === 0) return;
|
|
191447
|
-
const firstRow = rows[0];
|
|
191448
|
-
const firstKeys = Object.keys(firstRow);
|
|
191449
|
-
if (firstKeys.length === 0) return;
|
|
191450
|
-
if (isTabularArray(rows, firstKeys)) return firstKeys;
|
|
191451
|
-
}
|
|
191452
|
-
function isTabularArray(rows, header) {
|
|
191453
|
-
for (const row of rows) {
|
|
191454
|
-
if (Object.keys(row).length !== header.length) return false;
|
|
191455
|
-
for (const key of header) {
|
|
191456
|
-
if (!(key in row)) return false;
|
|
191457
|
-
if (!isJsonPrimitive(row[key])) return false;
|
|
191458
|
-
}
|
|
191459
|
-
}
|
|
191460
|
-
return true;
|
|
191461
|
-
}
|
|
191462
|
-
function* writeTabularRowsLines(rows, header, depth, options) {
|
|
191463
|
-
for (const row of rows) yield indentedLine(depth, encodeAndJoinPrimitives(header.map((key) => row[key]), options.delimiter), options.indent);
|
|
191464
|
-
}
|
|
191465
|
-
function* encodeMixedArrayAsListItemsLines(prefix, items, depth, options) {
|
|
191466
|
-
yield indentedLine(depth, formatHeader(items.length, {
|
|
191467
|
-
key: prefix,
|
|
191468
|
-
delimiter: options.delimiter
|
|
191469
|
-
}), options.indent);
|
|
191470
|
-
for (const item of items) yield* encodeListItemValueLines(item, depth + 1, options);
|
|
191471
|
-
}
|
|
191472
|
-
function* encodeObjectAsListItemLines(obj, depth, options) {
|
|
191473
|
-
if (isEmptyObject2(obj)) {
|
|
191474
|
-
yield indentedLine(depth, LIST_ITEM_MARKER, options.indent);
|
|
191475
|
-
return;
|
|
191476
|
-
}
|
|
191477
|
-
const entries = Object.entries(obj);
|
|
191478
|
-
if (entries.length === 1) {
|
|
191479
|
-
const [key, value2] = entries[0];
|
|
191480
|
-
if (isJsonArray(value2) && isArrayOfObjects(value2)) {
|
|
191481
|
-
const header = extractTabularHeader(value2);
|
|
191482
|
-
if (header) {
|
|
191483
|
-
yield indentedListItem(depth, formatHeader(value2.length, {
|
|
191484
|
-
key,
|
|
191485
|
-
fields: header,
|
|
191486
|
-
delimiter: options.delimiter
|
|
191487
|
-
}), options.indent);
|
|
191488
|
-
yield* writeTabularRowsLines(value2, header, depth + 1, options);
|
|
191489
|
-
return;
|
|
191490
|
-
}
|
|
191491
|
-
}
|
|
191492
|
-
}
|
|
191493
|
-
yield indentedLine(depth, LIST_ITEM_MARKER, options.indent);
|
|
191494
|
-
yield* encodeObjectLines(obj, depth + 1, options);
|
|
191495
|
-
}
|
|
191496
|
-
function* encodeListItemValueLines(value2, depth, options) {
|
|
191497
|
-
if (isJsonPrimitive(value2)) yield indentedListItem(depth, encodePrimitive(value2, options.delimiter), options.indent);
|
|
191498
|
-
else if (isJsonArray(value2)) if (isArrayOfPrimitives(value2)) yield indentedListItem(depth, encodeInlineArrayLine(value2, options.delimiter), options.indent);
|
|
191499
|
-
else {
|
|
191500
|
-
yield indentedListItem(depth, formatHeader(value2.length, { delimiter: options.delimiter }), options.indent);
|
|
191501
|
-
for (const item of value2) yield* encodeListItemValueLines(item, depth + 1, options);
|
|
191502
|
-
}
|
|
191503
|
-
else if (isJsonObject4(value2)) yield* encodeObjectAsListItemLines(value2, depth, options);
|
|
191504
|
-
}
|
|
191505
|
-
function indentedLine(depth, content, indentSize) {
|
|
191506
|
-
return " ".repeat(indentSize * depth) + content;
|
|
191507
|
-
}
|
|
191508
|
-
function indentedListItem(depth, content, indentSize) {
|
|
191509
|
-
return indentedLine(depth, LIST_ITEM_PREFIX + content, indentSize);
|
|
191510
|
-
}
|
|
191511
|
-
function encode4(input2, options) {
|
|
191512
|
-
return Array.from(encodeLines(input2, options)).join("\n");
|
|
191513
|
-
}
|
|
191514
|
-
function encodeLines(input2, options) {
|
|
191515
|
-
return encodeJsonValue(normalizeValue(input2), resolveOptions(options), 0);
|
|
191516
|
-
}
|
|
191517
|
-
function resolveOptions(options) {
|
|
191518
|
-
return {
|
|
191519
|
-
indent: options?.indent ?? 2,
|
|
191520
|
-
delimiter: options?.delimiter ?? DEFAULT_DELIMITER,
|
|
191521
|
-
keyFolding: options?.keyFolding ?? "off",
|
|
191522
|
-
flattenDepth: options?.flattenDepth ?? Number.POSITIVE_INFINITY
|
|
191523
|
-
};
|
|
191524
|
-
}
|
|
191525
|
-
|
|
191526
|
-
// mcp/geminiSanitizer.ts
|
|
191527
|
-
function parseStringEnumBranch(item) {
|
|
191528
|
-
if (!item || typeof item !== "object") return null;
|
|
191529
|
-
const record3 = item;
|
|
191530
|
-
if (Array.isArray(record3.enum)) {
|
|
191531
|
-
const strings = record3.enum.filter((v) => typeof v === "string");
|
|
191532
|
-
return strings.length === record3.enum.length && strings.length > 0 ? { values: strings } : null;
|
|
191533
|
-
}
|
|
191534
|
-
if (typeof record3.const === "string") {
|
|
191535
|
-
return { values: [record3.const] };
|
|
191536
|
-
}
|
|
191537
|
-
return null;
|
|
191538
|
-
}
|
|
191539
|
-
function collapseStringUnion(branches) {
|
|
191540
|
-
const values = [];
|
|
191541
|
-
for (const item of branches) {
|
|
191542
|
-
const parsed2 = parseStringEnumBranch(item);
|
|
191543
|
-
if (!parsed2) return null;
|
|
191544
|
-
values.push(...parsed2.values);
|
|
191545
|
-
}
|
|
191546
|
-
if (values.length === 0) return null;
|
|
191547
|
-
return { type: "string", enum: [...new Set(values)] };
|
|
191548
|
-
}
|
|
191549
|
-
function sanitizeForGemini(schema2) {
|
|
191550
|
-
if (!schema2 || typeof schema2 !== "object") return schema2;
|
|
191551
|
-
if (Array.isArray(schema2)) return schema2.map(sanitizeForGemini);
|
|
191552
|
-
const source = schema2;
|
|
191553
|
-
if (Array.isArray(source.enum) && typeof source.type !== "string") {
|
|
191554
|
-
const allStrings = source.enum.every((v) => typeof v === "string");
|
|
191555
|
-
if (allStrings) {
|
|
191556
|
-
const result = { type: "string", enum: source.enum };
|
|
191557
|
-
if (typeof source.description === "string") result.description = source.description;
|
|
191558
|
-
return result;
|
|
191559
|
-
}
|
|
191560
|
-
}
|
|
191561
|
-
for (const unionKey of ["anyOf", "oneOf"]) {
|
|
191562
|
-
const branches = source[unionKey];
|
|
191563
|
-
if (Array.isArray(branches) && branches.length > 0) {
|
|
191564
|
-
const collapsed = collapseStringUnion(branches);
|
|
191565
|
-
if (collapsed) {
|
|
191566
|
-
const result = { ...collapsed };
|
|
191567
|
-
if (typeof source.description === "string") result.description = source.description;
|
|
191568
|
-
return result;
|
|
191569
|
-
}
|
|
191570
|
-
}
|
|
191571
|
-
}
|
|
191572
|
-
if (Array.isArray(source.anyOf) || Array.isArray(source.oneOf)) {
|
|
191573
|
-
const result = {};
|
|
191574
|
-
if (Array.isArray(source.anyOf)) result.anyOf = source.anyOf.map(sanitizeForGemini);
|
|
191575
|
-
if (Array.isArray(source.oneOf)) result.oneOf = source.oneOf.map(sanitizeForGemini);
|
|
191576
|
-
return result;
|
|
191577
|
-
}
|
|
191578
|
-
const sanitized = {};
|
|
191579
|
-
for (const [key, value2] of Object.entries(source)) {
|
|
191580
|
-
if (key === "$schema") continue;
|
|
191581
|
-
if (key === "$defs") {
|
|
191582
|
-
sanitized.definitions = sanitizeForGemini(value2);
|
|
191583
|
-
continue;
|
|
191584
|
-
}
|
|
191585
|
-
sanitized[key] = sanitizeForGemini(value2);
|
|
191586
|
-
}
|
|
191587
|
-
return sanitized;
|
|
191588
|
-
}
|
|
191589
|
-
function wrapJsonSchemaProducer(producer) {
|
|
191590
|
-
return new Proxy(producer, {
|
|
191591
|
-
get(target, prop, receiver) {
|
|
191592
|
-
const value2 = Reflect.get(target, prop, receiver);
|
|
191593
|
-
if ((prop === "input" || prop === "output") && typeof value2 === "function") {
|
|
191594
|
-
const fn2 = value2;
|
|
191595
|
-
return (...args2) => sanitizeForGemini(fn2.apply(target, args2));
|
|
191596
|
-
}
|
|
191597
|
-
return value2;
|
|
191598
|
-
}
|
|
191599
|
-
});
|
|
191600
|
-
}
|
|
191601
|
-
function wrapStandard(standard) {
|
|
191602
|
-
return new Proxy(standard, {
|
|
191603
|
-
get(target, prop, receiver) {
|
|
191604
|
-
if (prop === "jsonSchema") {
|
|
191605
|
-
const value2 = Reflect.get(target, prop, receiver);
|
|
191606
|
-
if (value2 && typeof value2 === "object") {
|
|
191607
|
-
return wrapJsonSchemaProducer(value2);
|
|
191608
|
-
}
|
|
191609
|
-
return value2;
|
|
191610
|
-
}
|
|
191611
|
-
return Reflect.get(target, prop, receiver);
|
|
191612
|
-
}
|
|
191613
|
-
});
|
|
191614
|
-
}
|
|
191615
|
-
function wrapSchemaForGemini(schema2) {
|
|
191616
|
-
return new Proxy(schema2, {
|
|
191617
|
-
get(target, prop, receiver) {
|
|
191618
|
-
if (prop === "~standard") {
|
|
191619
|
-
const value2 = Reflect.get(target, prop, receiver);
|
|
191620
|
-
if (value2 && typeof value2 === "object") {
|
|
191621
|
-
return wrapStandard(value2);
|
|
191622
|
-
}
|
|
191623
|
-
return value2;
|
|
191624
|
-
}
|
|
191625
|
-
if (prop === "toJsonSchema") {
|
|
191626
|
-
const method = Reflect.get(target, prop, receiver);
|
|
191627
|
-
if (typeof method === "function") {
|
|
191628
|
-
return () => sanitizeForGemini(method.call(target));
|
|
191629
|
-
}
|
|
191630
|
-
return method;
|
|
191631
|
-
}
|
|
191632
|
-
return Reflect.get(target, prop, receiver);
|
|
191633
|
-
}
|
|
191634
|
-
});
|
|
191635
|
-
}
|
|
191636
|
-
function sanitizeToolForGemini(tool2) {
|
|
191637
|
-
if (!tool2.parameters) return tool2;
|
|
191638
|
-
return { ...tool2, parameters: wrapSchemaForGemini(tool2.parameters) };
|
|
191639
|
-
}
|
|
191640
|
-
function isGeminiRouted(ctx) {
|
|
191641
|
-
const effective = ctx.payload.proxyModel ?? ctx.resolvedModel ?? ctx.payload.model;
|
|
191642
|
-
if (!effective) return true;
|
|
191643
|
-
const normalized = effective.toLowerCase();
|
|
191644
|
-
if (normalized.includes("gemini")) return true;
|
|
191645
|
-
if (!normalized.includes("/")) return true;
|
|
191646
|
-
return false;
|
|
191647
|
-
}
|
|
191648
|
-
|
|
191649
|
-
// mcp/shared.ts
|
|
191650
|
-
function getHttpStatus(err) {
|
|
191651
|
-
if (typeof err !== "object" || err === null || !("status" in err)) return void 0;
|
|
191652
|
-
return typeof err.status === "number" ? err.status : void 0;
|
|
191653
|
-
}
|
|
191654
|
-
var tool = (toolDef) => toolDef;
|
|
191655
|
-
var handleToolSuccess = (data) => {
|
|
191656
|
-
const text = typeof data === "string" ? data : encode4(data);
|
|
191657
|
-
return {
|
|
191658
|
-
content: [{ type: "text", text }]
|
|
191659
|
-
};
|
|
191660
|
-
};
|
|
191661
|
-
var handleToolError = (error62) => {
|
|
191662
|
-
const errorMessage = error62 instanceof Error ? error62.message : String(error62);
|
|
191663
|
-
return {
|
|
191664
|
-
content: [
|
|
191665
|
-
{
|
|
191666
|
-
type: "text",
|
|
191667
|
-
text: `Error: ${errorMessage}`
|
|
191668
|
-
}
|
|
191669
|
-
],
|
|
191670
|
-
isError: true
|
|
191671
|
-
};
|
|
191672
|
-
};
|
|
191673
|
-
var execute = (fn2, toolName) => {
|
|
191674
|
-
const _fn = async (params) => {
|
|
191675
|
-
try {
|
|
191676
|
-
const result = await fn2(params);
|
|
191677
|
-
return handleToolSuccess(result);
|
|
191678
|
-
} catch (error62) {
|
|
191679
|
-
const errorMessage = error62 instanceof Error ? error62.message : String(error62);
|
|
191680
|
-
const prefix = toolName ? `[${toolName}]` : "tool";
|
|
191681
|
-
log.info(`${prefix} error: ${errorMessage}`);
|
|
191682
|
-
log.debug(`${prefix} params: ${formatJsonValue(params)}`);
|
|
191683
|
-
return handleToolError(error62);
|
|
191684
|
-
}
|
|
191685
|
-
};
|
|
191686
|
-
return _fn;
|
|
191687
|
-
};
|
|
191688
|
-
var addTools = (ctx, server, tools) => {
|
|
191689
|
-
const shouldSanitize = isGeminiRouted(ctx);
|
|
191690
|
-
for (const tool2 of tools) {
|
|
191691
|
-
server.addTool(shouldSanitize ? sanitizeToolForGemini(tool2) : tool2);
|
|
191692
|
-
}
|
|
191693
|
-
return server;
|
|
191694
|
-
};
|
|
191695
|
-
|
|
191696
192140
|
// mcp/git.ts
|
|
191697
192141
|
function getPushDestination(branch, storedDest, cwd) {
|
|
191698
192142
|
if (storedDest && storedDest.localBranch === branch) {
|
|
@@ -191818,7 +192262,8 @@ async function runPrepushHook(ctx, retryTool) {
|
|
|
191818
192262
|
if (!ctx.prepushScript) return false;
|
|
191819
192263
|
const prepushHook = await executeLifecycleHook({
|
|
191820
192264
|
event: "prepush",
|
|
191821
|
-
script: ctx.prepushScript
|
|
192265
|
+
script: ctx.prepushScript,
|
|
192266
|
+
shell: ctx.payload.shell
|
|
191822
192267
|
});
|
|
191823
192268
|
if (prepushHook.failure) {
|
|
191824
192269
|
ctx.toolState.prepushFailureCount += 1;
|
|
@@ -192213,8 +192658,8 @@ function countAhead(head, base, cwd) {
|
|
|
192213
192658
|
function spillGitOutput(params) {
|
|
192214
192659
|
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
192215
192660
|
if (!tempDir) throw new Error("PULLFROG_TEMP_DIR not set");
|
|
192216
|
-
const outputPath =
|
|
192217
|
-
|
|
192661
|
+
const outputPath = join13(tempDir, `git-${params.command}-${randomUUID4().slice(0, 8)}.txt`);
|
|
192662
|
+
writeFileSync9(outputPath, params.output);
|
|
192218
192663
|
const previewByLines = params.output.split("\n").slice(0, OVERFLOW_PREVIEW_LINES).join("\n");
|
|
192219
192664
|
const preview = previewByLines.length <= OVERFLOW_PREVIEW_MAX_CHARS ? previewByLines : `${previewByLines.slice(0, OVERFLOW_PREVIEW_MAX_CHARS)}\u2026`;
|
|
192220
192665
|
log.info(
|
|
@@ -193740,9 +194185,9 @@ async function reportReviewNodeId(ctx, params) {
|
|
|
193740
194185
|
import { execFileSync as execFileSync6, execSync as execSync2 } from "node:child_process";
|
|
193741
194186
|
import { mkdtempSync, readdirSync, realpathSync as realpathSync2, unlinkSync as unlinkSync2 } from "node:fs";
|
|
193742
194187
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
193743
|
-
import { join as
|
|
194188
|
+
import { join as join14 } from "node:path";
|
|
193744
194189
|
function createTempDirectory() {
|
|
193745
|
-
const sharedTempDir = mkdtempSync(
|
|
194190
|
+
const sharedTempDir = mkdtempSync(join14(tmpdir2(), "pullfrog-"));
|
|
193746
194191
|
process.env.PULLFROG_TEMP_DIR = sharedTempDir;
|
|
193747
194192
|
log.info(`\xBB created temp dir at ${sharedTempDir}`);
|
|
193748
194193
|
return sharedTempDir;
|
|
@@ -193787,13 +194232,13 @@ function wipeRunnerLeakSurface() {
|
|
|
193787
194232
|
return [];
|
|
193788
194233
|
}
|
|
193789
194234
|
};
|
|
193790
|
-
const fileCommandsDir =
|
|
194235
|
+
const fileCommandsDir = join14(runnerTemp, "_runner_file_commands");
|
|
193791
194236
|
for (const entry of listDir(fileCommandsDir)) {
|
|
193792
|
-
tryUnlink(
|
|
194237
|
+
tryUnlink(join14(fileCommandsDir, entry));
|
|
193793
194238
|
}
|
|
193794
194239
|
for (const entry of listDir(runnerTemp)) {
|
|
193795
194240
|
if (entry.endsWith(".sh") || /^git-credentials-.*\.config$/.test(entry)) {
|
|
193796
|
-
tryUnlink(
|
|
194241
|
+
tryUnlink(join14(runnerTemp, entry));
|
|
193797
194242
|
}
|
|
193798
194243
|
}
|
|
193799
194244
|
if (wiped.length > 0) {
|
|
@@ -194268,6 +194713,7 @@ async function checkoutPrBranch(pr, params) {
|
|
|
194268
194713
|
const postCheckoutHook = await executeLifecycleHook({
|
|
194269
194714
|
event: "post-checkout",
|
|
194270
194715
|
script: params.postCheckoutScript,
|
|
194716
|
+
shell: params.shell,
|
|
194271
194717
|
normalizeWorkingTreeAfter: true
|
|
194272
194718
|
});
|
|
194273
194719
|
return { hookWarning: postCheckoutHook.warning };
|
|
@@ -194349,11 +194795,11 @@ function CheckoutPrTool(ctx) {
|
|
|
194349
194795
|
headSha: checkoutSha
|
|
194350
194796
|
});
|
|
194351
194797
|
if (incremental.status === "ok") {
|
|
194352
|
-
incrementalDiffPath =
|
|
194798
|
+
incrementalDiffPath = join15(
|
|
194353
194799
|
tempDir,
|
|
194354
194800
|
`pr-${pull_number}-${beforeShort}-${headShort}-incremental.diff`
|
|
194355
194801
|
);
|
|
194356
|
-
|
|
194802
|
+
writeFileSync10(incrementalDiffPath, incremental.diff);
|
|
194357
194803
|
log.info(
|
|
194358
194804
|
`\xBB incremental diff computed (${incremental.diff.length} bytes) \u2192 ${incrementalDiffPath}`
|
|
194359
194805
|
);
|
|
@@ -194366,8 +194812,8 @@ function CheckoutPrTool(ctx) {
|
|
|
194366
194812
|
const diffPreview = formatResult.content.split("\n").slice(0, 100).join("\n");
|
|
194367
194813
|
log.debug(`formatted diff preview (first 100 lines):
|
|
194368
194814
|
${diffPreview}`);
|
|
194369
|
-
const diffPath =
|
|
194370
|
-
|
|
194815
|
+
const diffPath = join15(tempDir, `pr-${pull_number}-${headShort}.diff`);
|
|
194816
|
+
writeFileSync10(diffPath, formatResult.content);
|
|
194371
194817
|
log.debug(`wrote diff to ${diffPath} (${formatResult.content.length} bytes)`);
|
|
194372
194818
|
primary.diffCoverage = createDiffCoverageState({
|
|
194373
194819
|
diffPath,
|
|
@@ -194524,8 +194970,8 @@ ${dirty}`
|
|
|
194524
194970
|
}
|
|
194525
194971
|
|
|
194526
194972
|
// mcp/checkSuite.ts
|
|
194527
|
-
import { mkdirSync as mkdirSync8, writeFileSync as
|
|
194528
|
-
import { join as
|
|
194973
|
+
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync11 } from "node:fs";
|
|
194974
|
+
import { join as join16 } from "node:path";
|
|
194529
194975
|
var GetCheckSuiteLogs = type({
|
|
194530
194976
|
check_suite_id: type.number.describe("the id from check_suite.id")
|
|
194531
194977
|
});
|
|
@@ -194621,7 +195067,7 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
194621
195067
|
if (!tempDir) {
|
|
194622
195068
|
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
194623
195069
|
}
|
|
194624
|
-
const logsDir =
|
|
195070
|
+
const logsDir = join16(tempDir, "ci-logs");
|
|
194625
195071
|
mkdirSync8(logsDir, { recursive: true });
|
|
194626
195072
|
const jobResults = [];
|
|
194627
195073
|
for (const run of failedRuns) {
|
|
@@ -194648,8 +195094,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
194648
195094
|
);
|
|
194649
195095
|
}
|
|
194650
195096
|
const logsText = await logsResult.text();
|
|
194651
|
-
const logPath =
|
|
194652
|
-
|
|
195097
|
+
const logPath = join16(logsDir, `job-${job.id}.log`);
|
|
195098
|
+
writeFileSync11(logPath, logsText);
|
|
194653
195099
|
const analysis = analyzeLog(logsText, 80);
|
|
194654
195100
|
const failedSteps = job.steps?.filter((s) => s.conclusion === "failure").map((s) => `Step ${s.number}: ${s.name}`) ?? [];
|
|
194655
195101
|
jobResults.push({
|
|
@@ -194697,8 +195143,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
194697
195143
|
}
|
|
194698
195144
|
|
|
194699
195145
|
// mcp/commitInfo.ts
|
|
194700
|
-
import { writeFileSync as
|
|
194701
|
-
import { join as
|
|
195146
|
+
import { writeFileSync as writeFileSync12 } from "node:fs";
|
|
195147
|
+
import { join as join17 } from "node:path";
|
|
194702
195148
|
var CommitInfo = type({
|
|
194703
195149
|
sha: type.string.describe("the commit SHA (full or abbreviated) to fetch")
|
|
194704
195150
|
});
|
|
@@ -194722,8 +195168,8 @@ function CommitInfoTool(ctx) {
|
|
|
194722
195168
|
"PULLFROG_TEMP_DIR not set - get_commit_info must run in pullfrog action context"
|
|
194723
195169
|
);
|
|
194724
195170
|
}
|
|
194725
|
-
const diffFile =
|
|
194726
|
-
|
|
195171
|
+
const diffFile = join17(tempDir, `commit-${sha.slice(0, 7)}.diff`);
|
|
195172
|
+
writeFileSync12(diffFile, formatResult.content);
|
|
194727
195173
|
log.debug(`wrote commit diff to ${diffFile} (${formatResult.content.length} bytes)`);
|
|
194728
195174
|
return {
|
|
194729
195175
|
sha: data.sha,
|
|
@@ -194860,13 +195306,13 @@ import { performance as performance9 } from "node:perf_hooks";
|
|
|
194860
195306
|
|
|
194861
195307
|
// prep/installNodeDependencies.ts
|
|
194862
195308
|
import { existsSync as existsSync8 } from "node:fs";
|
|
194863
|
-
import { join as
|
|
195309
|
+
import { join as join19 } from "node:path";
|
|
194864
195310
|
|
|
194865
195311
|
// utils/packageManager.ts
|
|
194866
195312
|
var import_semver2 = __toESM(require_semver2(), 1);
|
|
194867
195313
|
import { existsSync as existsSync7 } from "node:fs";
|
|
194868
195314
|
import { mkdir, readFile as readFile2 } from "node:fs/promises";
|
|
194869
|
-
import { delimiter, join as
|
|
195315
|
+
import { delimiter, join as join18 } from "node:path";
|
|
194870
195316
|
var SUPPORTED_NAMES = ["npm", "pnpm", "yarn", "bun"];
|
|
194871
195317
|
var COREPACK_MANAGED = ["pnpm", "yarn"];
|
|
194872
195318
|
function isSupported(name) {
|
|
@@ -194904,7 +195350,7 @@ function parseDevEnginesField(field) {
|
|
|
194904
195350
|
};
|
|
194905
195351
|
}
|
|
194906
195352
|
async function resolvePackageManagerSpec(cwd) {
|
|
194907
|
-
const pkgPath =
|
|
195353
|
+
const pkgPath = join18(cwd, "package.json");
|
|
194908
195354
|
if (!existsSync7(pkgPath)) return null;
|
|
194909
195355
|
let pkg;
|
|
194910
195356
|
try {
|
|
@@ -194963,7 +195409,7 @@ async function currentVersion(name) {
|
|
|
194963
195409
|
return result.stdout.trim();
|
|
194964
195410
|
}
|
|
194965
195411
|
function packageManagerBinDir(tmpdir3) {
|
|
194966
|
-
return
|
|
195412
|
+
return join18(tmpdir3, "pm-bin");
|
|
194967
195413
|
}
|
|
194968
195414
|
async function ensurePackageManager(params) {
|
|
194969
195415
|
const spec = params.spec;
|
|
@@ -195026,7 +195472,7 @@ async function installFallback(name, installSpec) {
|
|
|
195026
195472
|
});
|
|
195027
195473
|
if (result.exitCode !== 0) return result.stderr || `failed to install ${name}`;
|
|
195028
195474
|
if (name === "deno") {
|
|
195029
|
-
const denoPath =
|
|
195475
|
+
const denoPath = join18(process.env.HOME || "", ".deno", "bin");
|
|
195030
195476
|
process.env.PATH = `${denoPath}:${process.env.PATH}`;
|
|
195031
195477
|
}
|
|
195032
195478
|
log.info(`\xBB installed ${name}`);
|
|
@@ -195059,7 +195505,7 @@ async function isCommandAvailable2(command) {
|
|
|
195059
195505
|
var installNodeDependencies = {
|
|
195060
195506
|
name: "installNodeDependencies",
|
|
195061
195507
|
shouldRun: () => {
|
|
195062
|
-
const packageJsonPath =
|
|
195508
|
+
const packageJsonPath = join19(process.cwd(), "package.json");
|
|
195063
195509
|
return existsSync8(packageJsonPath);
|
|
195064
195510
|
},
|
|
195065
195511
|
run: async (options) => {
|
|
@@ -195155,12 +195601,12 @@ ${errorMessage}`]
|
|
|
195155
195601
|
|
|
195156
195602
|
// prep/installPythonDependencies.ts
|
|
195157
195603
|
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "node:fs";
|
|
195158
|
-
import { join as
|
|
195604
|
+
import { join as join20 } from "node:path";
|
|
195159
195605
|
function declaresBuildSystem(path4) {
|
|
195160
195606
|
return /^\s*\[\s*build-system\s*\]/m.test(readFileSync5(path4, "utf8"));
|
|
195161
195607
|
}
|
|
195162
195608
|
function configApplies(config3, cwd) {
|
|
195163
|
-
const path4 =
|
|
195609
|
+
const path4 = join20(cwd, config3.file);
|
|
195164
195610
|
return existsSync9(path4) && (config3.applies?.(path4) ?? true);
|
|
195165
195611
|
}
|
|
195166
195612
|
var PYTHON_CONFIGS = [
|
|
@@ -195484,407 +195930,6 @@ function AwaitDependencyInstallationTool(ctx) {
|
|
|
195484
195930
|
// mcp/gh.ts
|
|
195485
195931
|
import { mkdirSync as mkdirSync9 } from "node:fs";
|
|
195486
195932
|
import { join as join21 } from "node:path";
|
|
195487
|
-
|
|
195488
|
-
// mcp/shell.ts
|
|
195489
|
-
import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
|
|
195490
|
-
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
195491
|
-
import { closeSync, openSync, writeFileSync as writeFileSync12 } from "node:fs";
|
|
195492
|
-
import { userInfo as userInfo2 } from "node:os";
|
|
195493
|
-
import { join as join20 } from "node:path";
|
|
195494
|
-
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
195495
|
-
var ShellParams = type({
|
|
195496
|
-
command: "string",
|
|
195497
|
-
// advisory and unread — no code path consumes it. kept in the schema only
|
|
195498
|
-
// because it nudges the model to state intent before running a command;
|
|
195499
|
-
// REQUIRING it cost a full rejected turn every time one was omitted. #1140.
|
|
195500
|
-
"description?": "string",
|
|
195501
|
-
"timeout?": type.number.describe(
|
|
195502
|
-
"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."
|
|
195503
|
-
),
|
|
195504
|
-
"working_directory?": "string",
|
|
195505
|
-
"background?": "boolean"
|
|
195506
|
-
});
|
|
195507
|
-
var detectedSandboxMethod;
|
|
195508
|
-
var usernsProbeError;
|
|
195509
|
-
function getSandboxMethod() {
|
|
195510
|
-
return detectSandboxMethod();
|
|
195511
|
-
}
|
|
195512
|
-
function detectSandboxMethod() {
|
|
195513
|
-
if (detectedSandboxMethod !== void 0) {
|
|
195514
|
-
return detectedSandboxMethod;
|
|
195515
|
-
}
|
|
195516
|
-
if (process.env.CI !== "true") {
|
|
195517
|
-
detectedSandboxMethod = "none";
|
|
195518
|
-
log.debug("sandbox disabled (CI !== true)");
|
|
195519
|
-
return "none";
|
|
195520
|
-
}
|
|
195521
|
-
try {
|
|
195522
|
-
const result = spawnSync6("unshare", ["--pid", "--fork", "--mount-proc", "true"], {
|
|
195523
|
-
timeout: 5e3,
|
|
195524
|
-
stdio: "ignore"
|
|
195525
|
-
});
|
|
195526
|
-
if (result.status === 0) {
|
|
195527
|
-
detectedSandboxMethod = "unshare";
|
|
195528
|
-
log.debug("PID namespace isolation enabled (unprivileged unshare)");
|
|
195529
|
-
return "unshare";
|
|
195530
|
-
}
|
|
195531
|
-
} catch {
|
|
195532
|
-
}
|
|
195533
|
-
try {
|
|
195534
|
-
const result = spawnSync6("sudo", ["unshare", "--pid", "--fork", "--mount-proc", "true"], {
|
|
195535
|
-
timeout: 5e3,
|
|
195536
|
-
stdio: "ignore"
|
|
195537
|
-
});
|
|
195538
|
-
if (result.status === 0) {
|
|
195539
|
-
detectedSandboxMethod = "sudo-unshare";
|
|
195540
|
-
log.debug("PID namespace isolation enabled (sudo unshare)");
|
|
195541
|
-
return "sudo-unshare";
|
|
195542
|
-
}
|
|
195543
|
-
} catch {
|
|
195544
|
-
}
|
|
195545
|
-
try {
|
|
195546
|
-
const result = spawnSync6(
|
|
195547
|
-
"unshare",
|
|
195548
|
-
[
|
|
195549
|
-
"--user",
|
|
195550
|
-
"--map-root-user",
|
|
195551
|
-
"--pid",
|
|
195552
|
-
"--fork",
|
|
195553
|
-
"--mount",
|
|
195554
|
-
"setpriv",
|
|
195555
|
-
"--no-new-privs",
|
|
195556
|
-
"--bounding-set",
|
|
195557
|
-
"-all",
|
|
195558
|
-
"--inh-caps",
|
|
195559
|
-
"-all",
|
|
195560
|
-
"--ambient-caps",
|
|
195561
|
-
"-all",
|
|
195562
|
-
"true"
|
|
195563
|
-
],
|
|
195564
|
-
{ timeout: 5e3, stdio: ["ignore", "ignore", "pipe"] }
|
|
195565
|
-
);
|
|
195566
|
-
if (result.status === 0) {
|
|
195567
|
-
detectedSandboxMethod = "userns-unshare";
|
|
195568
|
-
log.debug("PID namespace isolation enabled (unprivileged userns unshare)");
|
|
195569
|
-
return "userns-unshare";
|
|
195570
|
-
}
|
|
195571
|
-
usernsProbeError = result.stderr?.toString().trim() || result.error?.message;
|
|
195572
|
-
} catch {
|
|
195573
|
-
}
|
|
195574
|
-
detectedSandboxMethod = "none";
|
|
195575
|
-
log.info("PID namespace isolation not available");
|
|
195576
|
-
return "none";
|
|
195577
|
-
}
|
|
195578
|
-
var PROC_CLEANUP = "umount /proc 2>/dev/null; umount /proc 2>/dev/null; mount -t proc proc /proc 2>/dev/null;";
|
|
195579
|
-
var SOCKET_CLEANUP = [
|
|
195580
|
-
"/var/run/docker.sock",
|
|
195581
|
-
"/run/docker.sock",
|
|
195582
|
-
"/var/run/podman/podman.sock",
|
|
195583
|
-
"/run/podman/podman.sock",
|
|
195584
|
-
"/run/containerd/containerd.sock",
|
|
195585
|
-
"/var/run/crio/crio.sock"
|
|
195586
|
-
].map((path4) => `mount --bind /dev/null ${path4} 2>/dev/null;`).join(" ");
|
|
195587
|
-
function buildFsMounts(repoDir) {
|
|
195588
|
-
const escaped = repoDir.replace(/'/g, "'\\''");
|
|
195589
|
-
return [
|
|
195590
|
-
`mkdir -p /var/lib/pullfrog 2>/dev/null;`,
|
|
195591
|
-
`mount -t tmpfs tmpfs /var/lib/pullfrog 2>/dev/null;`,
|
|
195592
|
-
`[ -n "$RUNNER_TEMP" ] && [ -d "$RUNNER_TEMP/_runner_file_commands" ] && mount -t tmpfs tmpfs "$RUNNER_TEMP/_runner_file_commands" 2>/dev/null;`,
|
|
195593
|
-
`[ -e '${escaped}/.git' ] && mount --bind '${escaped}/.git' '${escaped}/.git' 2>/dev/null && mount -o remount,bind,ro '${escaped}/.git' 2>/dev/null;`
|
|
195594
|
-
].join(" ");
|
|
195595
|
-
}
|
|
195596
|
-
var _repoRoot;
|
|
195597
|
-
function resolveRepoRoot() {
|
|
195598
|
-
if (_repoRoot) return _repoRoot;
|
|
195599
|
-
const fromEnv = process.env.GITHUB_WORKSPACE;
|
|
195600
|
-
if (fromEnv) {
|
|
195601
|
-
_repoRoot = fromEnv;
|
|
195602
|
-
return _repoRoot;
|
|
195603
|
-
}
|
|
195604
|
-
try {
|
|
195605
|
-
_repoRoot = spawnSync6("git", ["rev-parse", "--show-toplevel"], {
|
|
195606
|
-
cwd: process.cwd(),
|
|
195607
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
195608
|
-
encoding: "utf-8"
|
|
195609
|
-
}).stdout?.trim();
|
|
195610
|
-
} catch {
|
|
195611
|
-
}
|
|
195612
|
-
if (!_repoRoot) _repoRoot = process.cwd();
|
|
195613
|
-
return _repoRoot;
|
|
195614
|
-
}
|
|
195615
|
-
function spawnShell(params) {
|
|
195616
|
-
const spawnOpts = { env: params.env, cwd: params.cwd, stdio: params.stdio, detached: true };
|
|
195617
|
-
const sandboxMethod = detectSandboxMethod();
|
|
195618
|
-
const ci = process.env.CI === "true";
|
|
195619
|
-
if (ci && sandboxMethod === "none") {
|
|
195620
|
-
throw new Error(
|
|
195621
|
-
"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"
|
|
195622
|
-
);
|
|
195623
|
-
}
|
|
195624
|
-
const repoRoot = resolveRepoRoot();
|
|
195625
|
-
const fsMounts = buildFsMounts(repoRoot);
|
|
195626
|
-
if (sandboxMethod === "unshare") {
|
|
195627
|
-
return spawn3(
|
|
195628
|
-
"unshare",
|
|
195629
|
-
[
|
|
195630
|
-
"--pid",
|
|
195631
|
-
"--fork",
|
|
195632
|
-
"--mount-proc",
|
|
195633
|
-
"bash",
|
|
195634
|
-
"-c",
|
|
195635
|
-
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} ${params.command}`
|
|
195636
|
-
],
|
|
195637
|
-
spawnOpts
|
|
195638
|
-
);
|
|
195639
|
-
}
|
|
195640
|
-
if (sandboxMethod === "userns-unshare") {
|
|
195641
|
-
const escaped = params.command.replace(/'/g, "'\\''");
|
|
195642
|
-
return spawn3(
|
|
195643
|
-
"unshare",
|
|
195644
|
-
[
|
|
195645
|
-
"--user",
|
|
195646
|
-
"--map-root-user",
|
|
195647
|
-
"--pid",
|
|
195648
|
-
"--fork",
|
|
195649
|
-
"--mount",
|
|
195650
|
-
"bash",
|
|
195651
|
-
"-c",
|
|
195652
|
-
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec setpriv --no-new-privs --bounding-set -all --inh-caps -all --ambient-caps -all bash -c '${escaped}'`
|
|
195653
|
-
],
|
|
195654
|
-
spawnOpts
|
|
195655
|
-
);
|
|
195656
|
-
}
|
|
195657
|
-
if (sandboxMethod === "sudo-unshare") {
|
|
195658
|
-
const envArgs = [];
|
|
195659
|
-
for (const [k2, v] of Object.entries(params.env)) {
|
|
195660
|
-
if (v !== void 0) {
|
|
195661
|
-
envArgs.push(`${k2}=${v}`);
|
|
195662
|
-
}
|
|
195663
|
-
}
|
|
195664
|
-
const username = userInfo2().username;
|
|
195665
|
-
const pathRestore = 'export PATH="${SANDBOX_PATH:-$PATH}"; ';
|
|
195666
|
-
const escaped = (pathRestore + params.command).replace(/'/g, "'\\''");
|
|
195667
|
-
envArgs.push(`SANDBOX_PATH=${params.env.PATH ?? ""}`);
|
|
195668
|
-
return spawn3(
|
|
195669
|
-
"sudo",
|
|
195670
|
-
[
|
|
195671
|
-
"env",
|
|
195672
|
-
...envArgs,
|
|
195673
|
-
"unshare",
|
|
195674
|
-
"--pid",
|
|
195675
|
-
"--fork",
|
|
195676
|
-
"--mount-proc",
|
|
195677
|
-
"bash",
|
|
195678
|
-
"-c",
|
|
195679
|
-
`${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec su -p -s /bin/bash ${username} -c '${escaped}'`
|
|
195680
|
-
],
|
|
195681
|
-
{ ...spawnOpts, env: {} }
|
|
195682
|
-
);
|
|
195683
|
-
}
|
|
195684
|
-
return spawn3("bash", ["-c", params.command], spawnOpts);
|
|
195685
|
-
}
|
|
195686
|
-
async function killProcessGroup(proc) {
|
|
195687
|
-
if (!proc.pid) return;
|
|
195688
|
-
try {
|
|
195689
|
-
process.kill(-proc.pid, "SIGTERM");
|
|
195690
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
195691
|
-
process.kill(-proc.pid, "SIGKILL");
|
|
195692
|
-
} catch {
|
|
195693
|
-
try {
|
|
195694
|
-
proc.kill("SIGKILL");
|
|
195695
|
-
} catch {
|
|
195696
|
-
}
|
|
195697
|
-
}
|
|
195698
|
-
}
|
|
195699
|
-
function getTempDir() {
|
|
195700
|
-
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
195701
|
-
if (!tempDir) {
|
|
195702
|
-
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
195703
|
-
}
|
|
195704
|
-
return tempDir;
|
|
195705
|
-
}
|
|
195706
|
-
var MAX_OUTPUT_CHARS = 5e3;
|
|
195707
|
-
async function runSandboxed(params) {
|
|
195708
|
-
const proc = spawnShell({
|
|
195709
|
-
command: params.command,
|
|
195710
|
-
env: params.env,
|
|
195711
|
-
cwd: params.cwd,
|
|
195712
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
195713
|
-
});
|
|
195714
|
-
let stdout = "", stderr = "", timedOut = false, exited = false, spawnError = "";
|
|
195715
|
-
proc.stdout?.on("data", (chunk) => {
|
|
195716
|
-
stdout += chunk.toString();
|
|
195717
|
-
});
|
|
195718
|
-
proc.stderr?.on("data", (chunk) => {
|
|
195719
|
-
stderr += chunk.toString();
|
|
195720
|
-
});
|
|
195721
|
-
const timeoutId = setTimeout(async () => {
|
|
195722
|
-
if (!exited) {
|
|
195723
|
-
timedOut = true;
|
|
195724
|
-
await killProcessGroup(proc);
|
|
195725
|
-
}
|
|
195726
|
-
}, params.timeout);
|
|
195727
|
-
const exitCode = await new Promise((resolve3) => {
|
|
195728
|
-
const done = (code) => {
|
|
195729
|
-
exited = true;
|
|
195730
|
-
clearTimeout(timeoutId);
|
|
195731
|
-
resolve3(code);
|
|
195732
|
-
};
|
|
195733
|
-
proc.on("exit", done);
|
|
195734
|
-
proc.on("error", (err) => {
|
|
195735
|
-
spawnError = err.message;
|
|
195736
|
-
done(null);
|
|
195737
|
-
});
|
|
195738
|
-
});
|
|
195739
|
-
proc.stdout?.destroy();
|
|
195740
|
-
proc.stderr?.destroy();
|
|
195741
|
-
let output2 = stderr ? stdout ? `${stdout}
|
|
195742
|
-
${stderr}` : stderr : stdout;
|
|
195743
|
-
if (spawnError) output2 = output2 ? `${output2}
|
|
195744
|
-
${spawnError}` : spawnError;
|
|
195745
|
-
if (timedOut)
|
|
195746
|
-
output2 = output2 ? `${output2}
|
|
195747
|
-
[timed out after ${params.timeout}ms]` : `[timed out after ${params.timeout}ms]`;
|
|
195748
|
-
return {
|
|
195749
|
-
output: output2.trim(),
|
|
195750
|
-
exitCode: exitCode ?? (timedOut ? 124 : -1),
|
|
195751
|
-
timedOut
|
|
195752
|
-
};
|
|
195753
|
-
}
|
|
195754
|
-
function capOutput(output2) {
|
|
195755
|
-
if (output2.length <= MAX_OUTPUT_CHARS) return output2;
|
|
195756
|
-
const fullPath = join20(getTempDir(), `shell-${randomUUID4().slice(0, 8)}.log`);
|
|
195757
|
-
writeFileSync12(fullPath, output2);
|
|
195758
|
-
const elided = output2.length - MAX_OUTPUT_CHARS;
|
|
195759
|
-
return `... [${elided} chars truncated; full output saved to ${fullPath}] ...
|
|
195760
|
-
${output2.slice(-MAX_OUTPUT_CHARS)}`;
|
|
195761
|
-
}
|
|
195762
|
-
function isGitCommand(command) {
|
|
195763
|
-
const trimmed = command.trim();
|
|
195764
|
-
if (trimmed === "git" || trimmed.startsWith("git ")) return true;
|
|
195765
|
-
if (trimmed.startsWith("sudo git")) return true;
|
|
195766
|
-
return /[;&|]\s*(?:sudo\s+)?git(?:\s|$)/.test(trimmed);
|
|
195767
|
-
}
|
|
195768
|
-
function ShellTool(ctx) {
|
|
195769
|
-
return tool({
|
|
195770
|
-
name: "shell",
|
|
195771
|
-
timeoutMs: 12e4,
|
|
195772
|
-
description: `Execute shell commands securely. Environment is filtered to remove API keys and secrets.
|
|
195773
|
-
|
|
195774
|
-
Example: \`shell({ command: "pnpm test", description: "run the test suite" })\`.
|
|
195775
|
-
|
|
195776
|
-
Use this tool to:
|
|
195777
|
-
- Run shell commands (ls, cat, grep, find, etc.)
|
|
195778
|
-
- Execute build tools (npm, pnpm, cargo, make, etc.)
|
|
195779
|
-
- Run tests and linters
|
|
195780
|
-
|
|
195781
|
-
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.
|
|
195782
|
-
|
|
195783
|
-
Do NOT use this tool for git commands \u2014 use the dedicated git tools instead.`,
|
|
195784
|
-
parameters: ShellParams,
|
|
195785
|
-
execute: execute(async (params) => {
|
|
195786
|
-
if (isGitCommand(params.command)) {
|
|
195787
|
-
throw new Error(
|
|
195788
|
-
'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'
|
|
195789
|
-
);
|
|
195790
|
-
}
|
|
195791
|
-
const timeout = Math.min(params.timeout ?? 3e4, 12e4);
|
|
195792
|
-
const cwd = params.working_directory ?? process.cwd();
|
|
195793
|
-
const env2 = resolveEnv(ctx.payload.shell === "enabled" ? "inherit" : "restricted");
|
|
195794
|
-
if (params.command.includes("agent-browser")) {
|
|
195795
|
-
const daemonError = ensureBrowserDaemon(ctx.toolState);
|
|
195796
|
-
if (daemonError) {
|
|
195797
|
-
return {
|
|
195798
|
-
output: `browser daemon unavailable: ${daemonError}`,
|
|
195799
|
-
exit_code: 1,
|
|
195800
|
-
timed_out: false
|
|
195801
|
-
};
|
|
195802
|
-
}
|
|
195803
|
-
const binDir = ctx.toolState.browserDaemon?.binDir;
|
|
195804
|
-
if (binDir) {
|
|
195805
|
-
env2.PATH = `${binDir}:${env2.PATH ?? ""}`;
|
|
195806
|
-
}
|
|
195807
|
-
}
|
|
195808
|
-
if (params.background) {
|
|
195809
|
-
const tempDir = getTempDir();
|
|
195810
|
-
const handle = `bg-${randomUUID4().slice(0, 8)}`;
|
|
195811
|
-
const outputPath = join20(tempDir, `${handle}.log`);
|
|
195812
|
-
const pidPath = join20(tempDir, `${handle}.pid`);
|
|
195813
|
-
const logFd = openSync(outputPath, "a");
|
|
195814
|
-
let proc;
|
|
195815
|
-
try {
|
|
195816
|
-
proc = spawnShell({
|
|
195817
|
-
command: params.command,
|
|
195818
|
-
env: env2,
|
|
195819
|
-
cwd,
|
|
195820
|
-
stdio: ["ignore", logFd, logFd]
|
|
195821
|
-
});
|
|
195822
|
-
} finally {
|
|
195823
|
-
closeSync(logFd);
|
|
195824
|
-
}
|
|
195825
|
-
if (!proc.pid) {
|
|
195826
|
-
throw new Error("failed to start background process");
|
|
195827
|
-
}
|
|
195828
|
-
proc.unref();
|
|
195829
|
-
writeFileSync12(pidPath, `${proc.pid}
|
|
195830
|
-
`);
|
|
195831
|
-
ctx.toolState.backgroundProcesses.set(handle, { pid: proc.pid, outputPath, pidPath });
|
|
195832
|
-
return {
|
|
195833
|
-
handle,
|
|
195834
|
-
outputPath,
|
|
195835
|
-
pidPath,
|
|
195836
|
-
message: `started background process ${handle} (pid ${proc.pid})`
|
|
195837
|
-
};
|
|
195838
|
-
}
|
|
195839
|
-
const result = await runSandboxed({ command: params.command, env: env2, cwd, timeout });
|
|
195840
|
-
if (result.exitCode !== 0) {
|
|
195841
|
-
log.info(`shell command failed with exit code ${result.exitCode}: ${params.command}`);
|
|
195842
|
-
if (result.output) log.info(`output: ${result.output}`);
|
|
195843
|
-
}
|
|
195844
|
-
return {
|
|
195845
|
-
output: capOutput(result.output),
|
|
195846
|
-
exit_code: result.exitCode,
|
|
195847
|
-
timed_out: result.timedOut
|
|
195848
|
-
};
|
|
195849
|
-
})
|
|
195850
|
-
});
|
|
195851
|
-
}
|
|
195852
|
-
var KillBackgroundParams = type({
|
|
195853
|
-
handle: type.string.describe("The handle of the background process to kill (e.g., bg-a1b2c3d4)")
|
|
195854
|
-
});
|
|
195855
|
-
function KillBackgroundTool(ctx) {
|
|
195856
|
-
return tool({
|
|
195857
|
-
name: "kill_background",
|
|
195858
|
-
mutates: true,
|
|
195859
|
-
description: `Kill a background process by its handle. Use this to stop dev servers or other long-running processes started with shell({ background: true }).`,
|
|
195860
|
-
parameters: KillBackgroundParams,
|
|
195861
|
-
execute: execute(async (params) => {
|
|
195862
|
-
const proc = ctx.toolState.backgroundProcesses.get(params.handle);
|
|
195863
|
-
if (!proc) {
|
|
195864
|
-
return {
|
|
195865
|
-
success: false,
|
|
195866
|
-
message: `no background process with handle ${params.handle}`
|
|
195867
|
-
};
|
|
195868
|
-
}
|
|
195869
|
-
try {
|
|
195870
|
-
process.kill(-proc.pid, "SIGTERM");
|
|
195871
|
-
} catch {
|
|
195872
|
-
}
|
|
195873
|
-
await sleep3(200);
|
|
195874
|
-
try {
|
|
195875
|
-
process.kill(-proc.pid, "SIGKILL");
|
|
195876
|
-
} catch {
|
|
195877
|
-
}
|
|
195878
|
-
ctx.toolState.backgroundProcesses.delete(params.handle);
|
|
195879
|
-
return {
|
|
195880
|
-
success: true,
|
|
195881
|
-
message: `killed background process ${params.handle} (pid ${proc.pid})`
|
|
195882
|
-
};
|
|
195883
|
-
})
|
|
195884
|
-
});
|
|
195885
|
-
}
|
|
195886
|
-
|
|
195887
|
-
// mcp/gh.ts
|
|
195888
195933
|
var GH_TIMEOUT_MS = 12e4;
|
|
195889
195934
|
var GhParams = type({
|
|
195890
195935
|
args: type.string.array().describe(
|
|
@@ -201683,6 +201728,7 @@ async function main() {
|
|
|
201683
201728
|
const setupHook = await executeLifecycleHook({
|
|
201684
201729
|
event: "setup",
|
|
201685
201730
|
script: runContext.repoSettings.setupScript,
|
|
201731
|
+
shell: payload.shell,
|
|
201686
201732
|
normalizeWorkingTreeAfter: true
|
|
201687
201733
|
});
|
|
201688
201734
|
if (setupHook.warning) {
|