@bunny-agent/runner-cli 0.9.28 → 0.9.29-beta.5
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/build-image.d.ts.map +1 -1
- package/dist/build-image.js +2 -0
- package/dist/build-image.js.map +1 -1
- package/dist/bundle.mjs +342 -24
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-image.d.ts","sourceRoot":"","sources":["../src/build-image.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,IAAI,EAAE,OAAO,CAAC;CACf;
|
|
1
|
+
{"version":3,"file":"build-image.d.ts","sourceRoot":"","sources":["../src/build-image.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,IAAI,EAAE,OAAO,CAAC;CACf;AA6ED,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2EvE"}
|
package/dist/build-image.js
CHANGED
|
@@ -13,10 +13,12 @@ function getShippedDockerfile() {
|
|
|
13
13
|
// Look for Dockerfile in several locations:
|
|
14
14
|
// 1. Package root (apps/runner-cli/Dockerfile) — shipped with npm package
|
|
15
15
|
// 2. docker/bunny-agent-claude/Dockerfile — monorepo development
|
|
16
|
+
// 3. CWD-relative docker/bunny-agent-claude/Dockerfile — global install invoked from repo root
|
|
16
17
|
const packageRoot = getPackageRoot();
|
|
17
18
|
const candidates = [
|
|
18
19
|
join(packageRoot, "Dockerfile"),
|
|
19
20
|
resolve(packageRoot, "..", "..", "docker", "bunny-agent-claude", "Dockerfile"),
|
|
21
|
+
resolve(process.cwd(), "docker", "bunny-agent-claude", "Dockerfile"),
|
|
20
22
|
];
|
|
21
23
|
for (const p of candidates) {
|
|
22
24
|
if (existsSync(p))
|
package/dist/build-image.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-image.js","sourceRoot":"","sources":["../src/build-image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EACL,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAiBzC,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,SAAS,cAAc;IACrB,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,OAAO,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,oBAAoB;IAC3B,4CAA4C;IAC5C,0EAA0E;IAC1E,iEAAiE;IACjE,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;QAC/B,OAAO,CACL,WAAW,EACX,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,oBAAoB,EACpB,YAAY,CACb;
|
|
1
|
+
{"version":3,"file":"build-image.js","sourceRoot":"","sources":["../src/build-image.ts"],"names":[],"mappings":"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"}
|
package/dist/bundle.mjs
CHANGED
|
@@ -33,7 +33,8 @@ function getShippedDockerfile() {
|
|
|
33
33
|
"docker",
|
|
34
34
|
"bunny-agent-claude",
|
|
35
35
|
"Dockerfile"
|
|
36
|
-
)
|
|
36
|
+
),
|
|
37
|
+
resolve(process.cwd(), "docker", "bunny-agent-claude", "Dockerfile")
|
|
37
38
|
];
|
|
38
39
|
for (const p of candidates) {
|
|
39
40
|
if (existsSync(p)) return p;
|
|
@@ -1406,18 +1407,139 @@ var generateImageSchema = {
|
|
|
1406
1407
|
required: ["prompt"],
|
|
1407
1408
|
additionalProperties: false
|
|
1408
1409
|
};
|
|
1409
|
-
async function resolveB64(item) {
|
|
1410
|
+
async function resolveB64(item, apiKey) {
|
|
1410
1411
|
if (item.b64_json)
|
|
1411
1412
|
return item.b64_json;
|
|
1412
|
-
if (item.
|
|
1413
|
-
|
|
1413
|
+
if (item.b64Json)
|
|
1414
|
+
return item.b64Json;
|
|
1415
|
+
if (item.image_base64)
|
|
1416
|
+
return item.image_base64;
|
|
1417
|
+
if (item.imageBase64)
|
|
1418
|
+
return item.imageBase64;
|
|
1419
|
+
if (item.base64)
|
|
1420
|
+
return item.base64;
|
|
1421
|
+
if (typeof item.image === "string")
|
|
1422
|
+
return item.image;
|
|
1423
|
+
if (item.image?.b64_json)
|
|
1424
|
+
return item.image.b64_json;
|
|
1425
|
+
if (item.image?.base64)
|
|
1426
|
+
return item.image.base64;
|
|
1427
|
+
const url = item.url ?? item.image_url ?? item.imageUrl ?? item.image?.url;
|
|
1428
|
+
if (url) {
|
|
1429
|
+
const headers = {};
|
|
1430
|
+
if (apiKey) {
|
|
1431
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
1432
|
+
}
|
|
1433
|
+
const res = await fetch(url, { headers });
|
|
1414
1434
|
if (res.ok)
|
|
1415
1435
|
return Buffer.from(await res.arrayBuffer()).toString("base64");
|
|
1416
1436
|
}
|
|
1417
1437
|
return void 0;
|
|
1418
1438
|
}
|
|
1419
|
-
|
|
1420
|
-
const
|
|
1439
|
+
function pickImageItem(response) {
|
|
1440
|
+
const tryFromObject = (value) => {
|
|
1441
|
+
if (!value || typeof value !== "object")
|
|
1442
|
+
return void 0;
|
|
1443
|
+
const obj = value;
|
|
1444
|
+
return {
|
|
1445
|
+
b64_json: obj.b64_json ?? obj.b64Json,
|
|
1446
|
+
b64Json: obj.b64Json,
|
|
1447
|
+
url: obj.url ?? obj.imageUrl,
|
|
1448
|
+
image_base64: obj.image_base64 ?? obj.imageBase64,
|
|
1449
|
+
imageBase64: obj.imageBase64,
|
|
1450
|
+
image_url: obj.image_url ?? obj.imageUrl,
|
|
1451
|
+
imageUrl: obj.imageUrl,
|
|
1452
|
+
base64: obj.base64,
|
|
1453
|
+
image: obj.image
|
|
1454
|
+
};
|
|
1455
|
+
};
|
|
1456
|
+
const asItem = (value) => {
|
|
1457
|
+
if (value == null)
|
|
1458
|
+
return void 0;
|
|
1459
|
+
if (typeof value === "string") {
|
|
1460
|
+
return { base64: value };
|
|
1461
|
+
}
|
|
1462
|
+
if (typeof value === "object") {
|
|
1463
|
+
const normalized = tryFromObject(value);
|
|
1464
|
+
if (normalized)
|
|
1465
|
+
return normalized;
|
|
1466
|
+
}
|
|
1467
|
+
return void 0;
|
|
1468
|
+
};
|
|
1469
|
+
const fromDataArray = Array.isArray(response.data) ? asItem(response.data[0]) : void 0;
|
|
1470
|
+
if (fromDataArray)
|
|
1471
|
+
return fromDataArray;
|
|
1472
|
+
const fromDataValue = asItem(response.data);
|
|
1473
|
+
if (fromDataValue)
|
|
1474
|
+
return fromDataValue;
|
|
1475
|
+
const responseRecord = response;
|
|
1476
|
+
const imagesValue = responseRecord.images;
|
|
1477
|
+
const outputValue = responseRecord.output;
|
|
1478
|
+
const fromImagesArray = Array.isArray(imagesValue) ? asItem(imagesValue[0]) : void 0;
|
|
1479
|
+
if (fromImagesArray)
|
|
1480
|
+
return fromImagesArray;
|
|
1481
|
+
const fromImagesValue = asItem(imagesValue);
|
|
1482
|
+
if (fromImagesValue)
|
|
1483
|
+
return fromImagesValue;
|
|
1484
|
+
const fromOutputArray = Array.isArray(outputValue) ? asItem(outputValue[0]) : void 0;
|
|
1485
|
+
if (fromOutputArray)
|
|
1486
|
+
return fromOutputArray;
|
|
1487
|
+
const fromOutputValue = asItem(outputValue);
|
|
1488
|
+
if (fromOutputValue)
|
|
1489
|
+
return fromOutputValue;
|
|
1490
|
+
const fromTopLevel = asItem(response);
|
|
1491
|
+
if (fromTopLevel)
|
|
1492
|
+
return fromTopLevel;
|
|
1493
|
+
const queue = [response];
|
|
1494
|
+
while (queue.length > 0) {
|
|
1495
|
+
const current = queue.shift();
|
|
1496
|
+
if (current == null)
|
|
1497
|
+
continue;
|
|
1498
|
+
if (typeof current === "string") {
|
|
1499
|
+
if (/^[A-Za-z0-9+/=]{32,}$/.test(current))
|
|
1500
|
+
return { base64: current };
|
|
1501
|
+
continue;
|
|
1502
|
+
}
|
|
1503
|
+
if (typeof current !== "object")
|
|
1504
|
+
continue;
|
|
1505
|
+
const normalized = tryFromObject(current);
|
|
1506
|
+
if (normalized) {
|
|
1507
|
+
const hasUsefulField = Boolean(normalized.b64_json ?? normalized.b64Json ?? normalized.image_base64 ?? normalized.imageBase64 ?? normalized.base64 ?? normalized.url ?? normalized.image_url ?? normalized.imageUrl ?? (typeof normalized.image === "string" ? normalized.image : normalized.image?.b64_json ?? normalized.image?.base64 ?? normalized.image?.url));
|
|
1508
|
+
if (hasUsefulField)
|
|
1509
|
+
return normalized;
|
|
1510
|
+
}
|
|
1511
|
+
if (Array.isArray(current)) {
|
|
1512
|
+
queue.push(...current);
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
for (const value of Object.values(current)) {
|
|
1516
|
+
queue.push(value);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
return {};
|
|
1520
|
+
}
|
|
1521
|
+
function detectImageMime(filePath) {
|
|
1522
|
+
const ext = extname(filePath).toLowerCase();
|
|
1523
|
+
if (ext === ".jpg" || ext === ".jpeg")
|
|
1524
|
+
return "image/jpeg";
|
|
1525
|
+
if (ext === ".webp")
|
|
1526
|
+
return "image/webp";
|
|
1527
|
+
if (ext === ".gif")
|
|
1528
|
+
return "image/gif";
|
|
1529
|
+
return "image/png";
|
|
1530
|
+
}
|
|
1531
|
+
function buildPolicySafeEditPrompt(prompt) {
|
|
1532
|
+
const riskyPattern = /\b(watermark|watermarks|logo|logos|copyright|brand mark|remove branding)\b/i;
|
|
1533
|
+
if (!riskyPattern.test(prompt)) {
|
|
1534
|
+
return { prompt, rewritten: false };
|
|
1535
|
+
}
|
|
1536
|
+
return {
|
|
1537
|
+
prompt: "Clean up distracting overlay text or marks naturally while preserving the original scene, style, and layout. Keep the result seamless and high quality.",
|
|
1538
|
+
rewritten: true
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
async function saveImageItem(item, filePath, apiKey) {
|
|
1542
|
+
const b64 = await resolveB64(item, apiKey);
|
|
1421
1543
|
if (!b64)
|
|
1422
1544
|
return void 0;
|
|
1423
1545
|
mkdirSync2(dirname3(filePath), { recursive: true });
|
|
@@ -1437,7 +1559,7 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
1437
1559
|
],
|
|
1438
1560
|
// biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
|
|
1439
1561
|
parameters: generateImageSchema,
|
|
1440
|
-
async execute(_toolCallId, params,
|
|
1562
|
+
async execute(_toolCallId, params, signal, _onUpdate) {
|
|
1441
1563
|
const p = params;
|
|
1442
1564
|
const prompt = p.prompt;
|
|
1443
1565
|
const size = p.size ?? "1024x1024";
|
|
@@ -1458,20 +1580,23 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
1458
1580
|
prompt,
|
|
1459
1581
|
n: 1,
|
|
1460
1582
|
size,
|
|
1461
|
-
quality
|
|
1462
|
-
|
|
1583
|
+
quality,
|
|
1584
|
+
response_format: "b64_json",
|
|
1585
|
+
output_format: "png"
|
|
1586
|
+
}),
|
|
1587
|
+
signal
|
|
1463
1588
|
});
|
|
1464
1589
|
if (!res.ok) {
|
|
1465
1590
|
throw new Error(`Image generation failed (${res.status}): ${await res.text()}`);
|
|
1466
1591
|
}
|
|
1467
1592
|
const json = await res.json();
|
|
1468
|
-
const item = json
|
|
1469
|
-
const savedPath = await saveImageItem(item, filePath);
|
|
1593
|
+
const item = pickImageItem(json);
|
|
1594
|
+
const savedPath = await saveImageItem(item, filePath, apiKey);
|
|
1470
1595
|
return {
|
|
1471
1596
|
content: [
|
|
1472
1597
|
{
|
|
1473
1598
|
type: "text",
|
|
1474
|
-
text: savedPath ??
|
|
1599
|
+
text: savedPath ?? `Image generated but could not be saved: no image payload returned; image_model: ${imageModelId}`
|
|
1475
1600
|
}
|
|
1476
1601
|
],
|
|
1477
1602
|
details: {
|
|
@@ -1491,6 +1616,193 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
1491
1616
|
}
|
|
1492
1617
|
};
|
|
1493
1618
|
}
|
|
1619
|
+
var editImageSchema = {
|
|
1620
|
+
type: "object",
|
|
1621
|
+
properties: {
|
|
1622
|
+
image: {
|
|
1623
|
+
type: "string",
|
|
1624
|
+
description: "Path to the source image file to edit (relative to working directory or absolute)."
|
|
1625
|
+
},
|
|
1626
|
+
prompt: {
|
|
1627
|
+
type: "string",
|
|
1628
|
+
description: "Text description of the desired final image. Describe the full result, not just the change."
|
|
1629
|
+
},
|
|
1630
|
+
mask: {
|
|
1631
|
+
type: "string",
|
|
1632
|
+
description: "Optional path to a mask image (PNG with transparent areas indicating where to edit). If omitted, the model decides what to change based on the prompt."
|
|
1633
|
+
},
|
|
1634
|
+
filename: {
|
|
1635
|
+
type: "string",
|
|
1636
|
+
description: "Output filename with extension, e.g. 'edited_cat.png'. Defaults to a timestamp-based name."
|
|
1637
|
+
},
|
|
1638
|
+
size: {
|
|
1639
|
+
type: "string",
|
|
1640
|
+
enum: ["1024x1024", "1024x1536", "1536x1024", "auto"],
|
|
1641
|
+
description: "Output image dimensions. Optional; omit or set auto to let model decide."
|
|
1642
|
+
},
|
|
1643
|
+
quality: {
|
|
1644
|
+
type: "string",
|
|
1645
|
+
enum: ["low", "medium", "high", "auto"],
|
|
1646
|
+
description: "Image quality. Optional; omit or set auto to let model decide."
|
|
1647
|
+
}
|
|
1648
|
+
},
|
|
1649
|
+
required: ["image", "prompt"],
|
|
1650
|
+
additionalProperties: false
|
|
1651
|
+
};
|
|
1652
|
+
function buildMultipartBody(fields, files) {
|
|
1653
|
+
const boundary = `----SandagentBoundary${Date.now()}${Math.random().toString(36).slice(2)}`;
|
|
1654
|
+
const parts = [];
|
|
1655
|
+
for (const { name, value } of fields) {
|
|
1656
|
+
parts.push(Buffer.from(`--${boundary}\r
|
|
1657
|
+
Content-Disposition: form-data; name="${name}"\r
|
|
1658
|
+
\r
|
|
1659
|
+
${value}\r
|
|
1660
|
+
`));
|
|
1661
|
+
}
|
|
1662
|
+
for (const { name, filename, buffer, mime } of files) {
|
|
1663
|
+
parts.push(Buffer.from(`--${boundary}\r
|
|
1664
|
+
Content-Disposition: form-data; name="${name}"; filename="${filename}"\r
|
|
1665
|
+
Content-Type: ${mime}\r
|
|
1666
|
+
\r
|
|
1667
|
+
`));
|
|
1668
|
+
parts.push(buffer);
|
|
1669
|
+
parts.push(Buffer.from("\r\n"));
|
|
1670
|
+
}
|
|
1671
|
+
parts.push(Buffer.from(`--${boundary}--\r
|
|
1672
|
+
`));
|
|
1673
|
+
return {
|
|
1674
|
+
body: Buffer.concat(parts),
|
|
1675
|
+
contentType: `multipart/form-data; boundary=${boundary}`
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
1679
|
+
return {
|
|
1680
|
+
name: "edit_image",
|
|
1681
|
+
label: "edit image",
|
|
1682
|
+
description: "Edit an existing image based on a text prompt. Optionally use a mask to control which areas to modify. Saves the result to disk and returns the file path.",
|
|
1683
|
+
promptSnippet: "edit_image(image, prompt, mask?, filename?, size?, quality?) - edit an existing image",
|
|
1684
|
+
promptGuidelines: [
|
|
1685
|
+
"Use edit_image when the user wants to modify, retouch, or transform an existing image.",
|
|
1686
|
+
"The prompt should describe the full desired final image, not just the change.",
|
|
1687
|
+
"Provide the source image path. Use a mask image (PNG with transparent areas) to control where edits happen.",
|
|
1688
|
+
"Without a mask, the model decides what to change based on the prompt."
|
|
1689
|
+
],
|
|
1690
|
+
// biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
|
|
1691
|
+
parameters: editImageSchema,
|
|
1692
|
+
async execute(_toolCallId, params, signal, _onUpdate) {
|
|
1693
|
+
const { readFileSync: readFileSync4, existsSync: existsSync8 } = await import("node:fs");
|
|
1694
|
+
const { resolve: resolve4, basename: basename2 } = await import("node:path");
|
|
1695
|
+
const p = params;
|
|
1696
|
+
const imagePath = p.image;
|
|
1697
|
+
const prompt = p.prompt;
|
|
1698
|
+
const maskPath = p.mask;
|
|
1699
|
+
const size = p.size;
|
|
1700
|
+
const quality = p.quality;
|
|
1701
|
+
const rawFilename = p.filename;
|
|
1702
|
+
const safePrompt = buildPolicySafeEditPrompt(prompt);
|
|
1703
|
+
const resolvedImage = resolve4(cwd, imagePath);
|
|
1704
|
+
if (!existsSync8(resolvedImage)) {
|
|
1705
|
+
return {
|
|
1706
|
+
content: [
|
|
1707
|
+
{
|
|
1708
|
+
type: "text",
|
|
1709
|
+
text: `Image edit error: source image not found at ${resolvedImage}`
|
|
1710
|
+
}
|
|
1711
|
+
],
|
|
1712
|
+
details: void 0
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
const filename = rawFilename ? extname(rawFilename) ? rawFilename : `${rawFilename}.png` : `edited_${Date.now()}.png`;
|
|
1716
|
+
const filePath = join6(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
|
|
1717
|
+
try {
|
|
1718
|
+
const imageBuffer = readFileSync4(resolvedImage);
|
|
1719
|
+
const fields = [
|
|
1720
|
+
{ name: "model", value: imageModelId },
|
|
1721
|
+
{ name: "prompt", value: safePrompt.prompt },
|
|
1722
|
+
{ name: "n", value: "1" },
|
|
1723
|
+
{ name: "response_format", value: "b64_json" },
|
|
1724
|
+
{ name: "output_format", value: "png" }
|
|
1725
|
+
];
|
|
1726
|
+
if (size && size !== "auto") {
|
|
1727
|
+
fields.push({ name: "size", value: size });
|
|
1728
|
+
}
|
|
1729
|
+
if (quality && quality !== "auto") {
|
|
1730
|
+
fields.push({ name: "quality", value: quality });
|
|
1731
|
+
}
|
|
1732
|
+
const files = [
|
|
1733
|
+
{
|
|
1734
|
+
name: "image",
|
|
1735
|
+
filename: basename2(resolvedImage),
|
|
1736
|
+
buffer: imageBuffer,
|
|
1737
|
+
mime: detectImageMime(resolvedImage)
|
|
1738
|
+
}
|
|
1739
|
+
];
|
|
1740
|
+
if (maskPath) {
|
|
1741
|
+
const resolvedMask = resolve4(cwd, maskPath);
|
|
1742
|
+
if (existsSync8(resolvedMask)) {
|
|
1743
|
+
files.push({
|
|
1744
|
+
name: "mask",
|
|
1745
|
+
filename: basename2(resolvedMask),
|
|
1746
|
+
buffer: readFileSync4(resolvedMask),
|
|
1747
|
+
mime: detectImageMime(resolvedMask)
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
const { body: multipartBody, contentType } = buildMultipartBody(fields, files);
|
|
1752
|
+
const url = `${baseUrl.replace(/\/$/, "")}/v1/images/edits`;
|
|
1753
|
+
const sendRequest = async (body, type) => {
|
|
1754
|
+
const res = await fetch(url, {
|
|
1755
|
+
method: "POST",
|
|
1756
|
+
headers: {
|
|
1757
|
+
"Content-Type": type,
|
|
1758
|
+
Authorization: `Bearer ${apiKey}`
|
|
1759
|
+
},
|
|
1760
|
+
body,
|
|
1761
|
+
signal
|
|
1762
|
+
});
|
|
1763
|
+
if (!res.ok) {
|
|
1764
|
+
throw new Error(`Image edit failed (${res.status}): ${await res.text()}`);
|
|
1765
|
+
}
|
|
1766
|
+
return await res.json();
|
|
1767
|
+
};
|
|
1768
|
+
let json = await sendRequest(multipartBody, contentType);
|
|
1769
|
+
const item = pickImageItem(json);
|
|
1770
|
+
let savedPath = await saveImageItem(item, filePath, apiKey);
|
|
1771
|
+
const firstResponseHasEmptyDataArray = Array.isArray(json.data) && json.data.length === 0;
|
|
1772
|
+
if (!savedPath && safePrompt.rewritten && firstResponseHasEmptyDataArray) {
|
|
1773
|
+
const retryFields = fields.map((f) => f.name === "prompt" ? {
|
|
1774
|
+
name: "prompt",
|
|
1775
|
+
value: "Remove only distracting overlay text artifacts naturally and keep all original content unchanged."
|
|
1776
|
+
} : f);
|
|
1777
|
+
const retryMultipart = buildMultipartBody(retryFields, files);
|
|
1778
|
+
json = await sendRequest(retryMultipart.body, retryMultipart.contentType);
|
|
1779
|
+
const retryItem = pickImageItem(json);
|
|
1780
|
+
savedPath = await saveImageItem(retryItem, filePath, apiKey);
|
|
1781
|
+
}
|
|
1782
|
+
return {
|
|
1783
|
+
content: [
|
|
1784
|
+
{
|
|
1785
|
+
type: "text",
|
|
1786
|
+
text: savedPath ?? `Image edited but could not be saved: no image payload returned; image_model: ${imageModelId}`
|
|
1787
|
+
}
|
|
1788
|
+
],
|
|
1789
|
+
details: {
|
|
1790
|
+
filePath: savedPath,
|
|
1791
|
+
response: json
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
} catch (e) {
|
|
1795
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1796
|
+
return {
|
|
1797
|
+
content: [
|
|
1798
|
+
{ type: "text", text: `Image edit error: ${msg}` }
|
|
1799
|
+
],
|
|
1800
|
+
details: void 0
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1494
1806
|
|
|
1495
1807
|
// ../../packages/runner-pi/dist/tool-overrides.js
|
|
1496
1808
|
import { createBashTool, createReadTool } from "@mariozechner/pi-coding-agent";
|
|
@@ -1500,7 +1812,7 @@ var braveProvider = {
|
|
|
1500
1812
|
id: "brave",
|
|
1501
1813
|
label: "Brave Search",
|
|
1502
1814
|
envKeys: ["BRAVE_API_KEY"],
|
|
1503
|
-
async search({ apiKey, query, count, country, freshness }) {
|
|
1815
|
+
async search({ apiKey, query, count, country, freshness, signal }) {
|
|
1504
1816
|
const params = new URLSearchParams({
|
|
1505
1817
|
q: query,
|
|
1506
1818
|
count: String(Math.min(count, 20))
|
|
@@ -1514,7 +1826,8 @@ var braveProvider = {
|
|
|
1514
1826
|
Accept: "application/json",
|
|
1515
1827
|
"Accept-Encoding": "gzip",
|
|
1516
1828
|
"X-Subscription-Token": apiKey
|
|
1517
|
-
}
|
|
1829
|
+
},
|
|
1830
|
+
signal
|
|
1518
1831
|
});
|
|
1519
1832
|
if (!res.ok) {
|
|
1520
1833
|
const body = await res.text().catch(() => "");
|
|
@@ -1542,7 +1855,7 @@ var tavilyProvider = {
|
|
|
1542
1855
|
id: "tavily",
|
|
1543
1856
|
label: "Tavily",
|
|
1544
1857
|
envKeys: ["TAVILY_API_KEY"],
|
|
1545
|
-
async search({ apiKey, query, count }) {
|
|
1858
|
+
async search({ apiKey, query, count, signal }) {
|
|
1546
1859
|
const res = await fetch("https://api.tavily.com/search", {
|
|
1547
1860
|
method: "POST",
|
|
1548
1861
|
headers: { "Content-Type": "application/json" },
|
|
@@ -1551,7 +1864,8 @@ var tavilyProvider = {
|
|
|
1551
1864
|
query,
|
|
1552
1865
|
max_results: Math.min(count, 10),
|
|
1553
1866
|
include_answer: false
|
|
1554
|
-
})
|
|
1867
|
+
}),
|
|
1868
|
+
signal
|
|
1555
1869
|
});
|
|
1556
1870
|
if (!res.ok) {
|
|
1557
1871
|
const body = await res.text().catch(() => "");
|
|
@@ -1604,9 +1918,12 @@ var BROWSER_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/53
|
|
|
1604
1918
|
function htmlToText(html) {
|
|
1605
1919
|
return html.replace(/<(script|style|noscript)[^>]*>[\s\S]*?<\/\1>/gi, "").replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|h[1-6]|li|tr)>/gi, "\n").replace(/<(p|div|h[1-6]|li|tr)[^>]*>/gi, "\n").replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ").replace(/[ \t]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
|
|
1606
1920
|
}
|
|
1607
|
-
async function fetchPageContent(url) {
|
|
1921
|
+
async function fetchPageContent(url, externalSignal) {
|
|
1608
1922
|
const controller = new AbortController();
|
|
1609
1923
|
const timeout = setTimeout(() => controller.abort(), 15e3);
|
|
1924
|
+
externalSignal?.addEventListener("abort", () => controller.abort(), {
|
|
1925
|
+
once: true
|
|
1926
|
+
});
|
|
1610
1927
|
try {
|
|
1611
1928
|
const res = await fetch(url, {
|
|
1612
1929
|
headers: {
|
|
@@ -1703,7 +2020,7 @@ function buildWebSearchTool(env) {
|
|
|
1703
2020
|
],
|
|
1704
2021
|
// biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
|
|
1705
2022
|
parameters: webSearchSchema,
|
|
1706
|
-
async execute(_toolCallId, params,
|
|
2023
|
+
async execute(_toolCallId, params, signal, _onUpdate) {
|
|
1707
2024
|
const p = params;
|
|
1708
2025
|
const query = p.query;
|
|
1709
2026
|
const count = p.count ?? 5;
|
|
@@ -1718,11 +2035,12 @@ function buildWebSearchTool(env) {
|
|
|
1718
2035
|
query,
|
|
1719
2036
|
count,
|
|
1720
2037
|
country,
|
|
1721
|
-
freshness
|
|
2038
|
+
freshness,
|
|
2039
|
+
signal
|
|
1722
2040
|
});
|
|
1723
2041
|
if (shouldFetchContent) {
|
|
1724
2042
|
for (const r of results) {
|
|
1725
|
-
r.content = await fetchPageContent(r.link);
|
|
2043
|
+
r.content = await fetchPageContent(r.link, signal);
|
|
1726
2044
|
}
|
|
1727
2045
|
}
|
|
1728
2046
|
return {
|
|
@@ -1768,11 +2086,11 @@ function buildWebFetchTool() {
|
|
|
1768
2086
|
],
|
|
1769
2087
|
// biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
|
|
1770
2088
|
parameters: webFetchSchema,
|
|
1771
|
-
async execute(_toolCallId, params,
|
|
2089
|
+
async execute(_toolCallId, params, signal, _onUpdate) {
|
|
1772
2090
|
const p = params;
|
|
1773
2091
|
const url = p.url;
|
|
1774
2092
|
try {
|
|
1775
|
-
const content = await fetchPageContent(url);
|
|
2093
|
+
const content = await fetchPageContent(url, signal);
|
|
1776
2094
|
return {
|
|
1777
2095
|
content: [{ type: "text", text: content }],
|
|
1778
2096
|
details: void 0
|
|
@@ -2065,7 +2383,7 @@ function createPiRunner(options = {}) {
|
|
|
2065
2383
|
const customTools = options.env && Object.keys(options.env).length > 0 ? buildSecretAwareTools(cwd, options.env) : [];
|
|
2066
2384
|
if (imageModelName) {
|
|
2067
2385
|
const apiKey = await modelRegistry.authStorage.getApiKey(provider) ?? "";
|
|
2068
|
-
customTools.push(buildImageGenerateTool(cwd, imageModelName, model.baseUrl, apiKey));
|
|
2386
|
+
customTools.push(buildImageGenerateTool(cwd, imageModelName, model.baseUrl, apiKey), buildImageEditTool(cwd, imageModelName, model.baseUrl, apiKey));
|
|
2069
2387
|
}
|
|
2070
2388
|
const { session } = await createAgentSession({
|
|
2071
2389
|
cwd,
|
|
@@ -2236,7 +2554,7 @@ function createPiRunner(options = {}) {
|
|
|
2236
2554
|
if (options.env && Object.keys(options.env).length > 0) {
|
|
2237
2555
|
output = redactSecrets(output, options.env);
|
|
2238
2556
|
}
|
|
2239
|
-
if (event.toolName === "generate_image" && event.result !== null && typeof event.result === "object") {
|
|
2557
|
+
if ((event.toolName === "generate_image" || event.toolName === "edit_image") && event.result !== null && typeof event.result === "object") {
|
|
2240
2558
|
const details = event.result.details;
|
|
2241
2559
|
const u = details?.response?.usage;
|
|
2242
2560
|
if (u) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bunny-agent/runner-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.29-beta.5",
|
|
4
4
|
"description": "BunnyAgent Runner CLI - Like gemini-cli or claude-code, runs in your local terminal with AI SDK UI streaming",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"typescript": "^5.3.0",
|
|
55
55
|
"vitest": "^1.6.1",
|
|
56
56
|
"@bunny-agent/runner-harness": "0.1.1-beta.0",
|
|
57
|
-
"@bunny-agent/runner-claude": "0.6.2",
|
|
58
57
|
"@bunny-agent/runner-codex": "0.6.2",
|
|
58
|
+
"@bunny-agent/runner-claude": "0.6.2",
|
|
59
59
|
"@bunny-agent/runner-gemini": "0.6.2",
|
|
60
60
|
"@bunny-agent/runner-opencode": "0.6.2",
|
|
61
61
|
"@bunny-agent/runner-pi": "0.6.4-beta.0"
|