@poe-platform/safe-bash 0.1.758 → 0.1.760
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/image-ast/index.browser.js +41 -16
- package/dist/image-ast/index.js +41 -16
- package/dist/pdf-ast/ast.d.ts +8 -3
- package/dist/pdf-ast/index.js +45 -19
- package/dist/safe-bash/chunks/{chunk-WNWIBBJZ.js → chunk-CRQDMBQA.js} +130 -9
- package/dist/safe-bash/command-chunks/{chunk-L2C5TIII.js → chunk-MC4XXPQJ.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-TCAGGM44.js → chunk-S3SCPBHC.js} +41 -17
- package/dist/safe-bash/command-chunks/{chunk-XYCWDGOG.js → chunk-UCI6ZQFB.js} +1 -1
- package/dist/safe-bash/commands/python/index.browser.js +1 -1
- package/dist/safe-bash/commands/python/index.d.ts +1 -1
- package/dist/safe-bash/commands/python/llm-capability.d.ts +7 -3
- package/dist/safe-bash/commands/python/llm-capability.js +149 -10
- package/dist/safe-bash/commands/python/shell-capability.js +10 -2
- package/dist/safe-bash/core.browser.js +1 -1
- package/dist/safe-bash-command-ffmpeg/index.js +3513 -3500
- package/dist/safe-bash-command-gh/index.js +128 -27
- package/dist/safe-bash-command-imagemagick/index.js +2 -2
- package/dist/safe-bash-command-pandoc/command.js +3 -3
- package/dist/safe-bash-command-pandoc/index.js +3 -3
- package/dist/safe-bash-command-pdfimages/index.js +41 -16
- package/dist/safe-bash-command-pdfinfo/index.js +45 -19
- package/dist/safe-bash-command-pdftk/index.js +41 -16
- package/dist/safe-bash-command-pdftoppm/index.js +45 -19
- package/dist/safe-bash-command-pdftotext/index.js +41 -16
- package/dist/safe-bash-command-qpdf/index.js +41 -16
- package/dist/safe-bash-command-sips/index.js +2 -2
- package/dist/safe-bash-command-soffice/index.js +1 -1
- package/dist/safe-bash-command-ssconvert/index.js +63 -38
- package/dist/safe-bash-command-wkhtmltopdf/index.js +42 -16
- package/dist/safe-bash-io-engine/internal.d.ts +2 -2
- package/dist/safe-bash-mcp/index.d.ts +1 -1
- package/package.json +2 -2
- package/third-party/pdf-ast/THIRD_PARTY_NOTICES.md +22 -0
|
@@ -1626,9 +1626,14 @@ function createPythonShellCapability(context, options = {}) {
|
|
|
1626
1626
|
const stdout = [];
|
|
1627
1627
|
const stderr = [];
|
|
1628
1628
|
let captured = 0;
|
|
1629
|
+
let outputFailure;
|
|
1629
1630
|
const sink = (target, type) => ({ async write(chunk) {
|
|
1630
1631
|
childSignal.throwIfAborted();
|
|
1631
|
-
if (
|
|
1632
|
+
if (outputFailure) throw outputFailure;
|
|
1633
|
+
if (chunk.length > outputLimit - captured) {
|
|
1634
|
+
outputFailure = new RangeError("Python shell output limit exceeded");
|
|
1635
|
+
throw outputFailure;
|
|
1636
|
+
}
|
|
1632
1637
|
captured += chunk.length;
|
|
1633
1638
|
if (publish) {
|
|
1634
1639
|
for (let offset = 0; offset < chunk.length; offset += chunkBytes) {
|
|
@@ -1652,6 +1657,7 @@ function createPythonShellCapability(context, options = {}) {
|
|
|
1652
1657
|
externalInvocation: true
|
|
1653
1658
|
});
|
|
1654
1659
|
childSignal.throwIfAborted();
|
|
1660
|
+
if (outputFailure) throw outputFailure;
|
|
1655
1661
|
return { stdout, stderr, exitCode: result.exitCode };
|
|
1656
1662
|
} catch (error) {
|
|
1657
1663
|
if (!publish && !signal.aborted && childSignal.aborted && childSignal.reason?.name === "TimeoutError") {
|
|
@@ -1732,9 +1738,90 @@ function record(value) {
|
|
|
1732
1738
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("Expected a structured Python LLM request");
|
|
1733
1739
|
return value;
|
|
1734
1740
|
}
|
|
1741
|
+
function jsonBytes(value, limit) {
|
|
1742
|
+
if (limit === Infinity) return 0;
|
|
1743
|
+
let bytes = 0;
|
|
1744
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
1745
|
+
const add2 = (size) => {
|
|
1746
|
+
if (size > limit - bytes) throw new RangeError("Python LLM serialized response limit exceeded");
|
|
1747
|
+
bytes += size;
|
|
1748
|
+
};
|
|
1749
|
+
const string = (text2) => {
|
|
1750
|
+
add2(2);
|
|
1751
|
+
if (text2.length > limit - bytes) throw new RangeError("Python LLM serialized response limit exceeded");
|
|
1752
|
+
for (let i = 0; i < text2.length; i++) {
|
|
1753
|
+
const point = text2.codePointAt(i);
|
|
1754
|
+
if (point > 65535) {
|
|
1755
|
+
add2(4);
|
|
1756
|
+
i++;
|
|
1757
|
+
} else if (point === 34 || point === 92) add2(2);
|
|
1758
|
+
else if (point < 32) add2([8, 9, 10, 12, 13].includes(point) ? 2 : 6);
|
|
1759
|
+
else if (point >= 55296 && point <= 57343) add2(6);
|
|
1760
|
+
else add2(point < 128 ? 1 : point < 2048 ? 2 : 3);
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1763
|
+
const visit = (item) => {
|
|
1764
|
+
if (typeof item === "string") {
|
|
1765
|
+
string(item);
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
if (item === null || typeof item === "boolean" || typeof item === "number" && Number.isFinite(item)) {
|
|
1769
|
+
add2(JSON.stringify(item).length);
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
if (!item || typeof item !== "object" || ancestors.has(item)) throw new TypeError("Python LLM response must contain acyclic data");
|
|
1773
|
+
ancestors.add(item);
|
|
1774
|
+
add2(2);
|
|
1775
|
+
let entries = 0;
|
|
1776
|
+
if (Array.isArray(item)) {
|
|
1777
|
+
if (item.length > limit - bytes) throw new RangeError("Python LLM serialized response limit exceeded");
|
|
1778
|
+
for (let i = 0; i < item.length; i++) {
|
|
1779
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, String(i));
|
|
1780
|
+
if (!descriptor || !("value" in descriptor)) throw new TypeError("Python LLM response must contain data");
|
|
1781
|
+
if (entries++) add2(1);
|
|
1782
|
+
visit(descriptor.value);
|
|
1783
|
+
}
|
|
1784
|
+
} else {
|
|
1785
|
+
for (const key in item) {
|
|
1786
|
+
if (!Object.hasOwn(item, key)) continue;
|
|
1787
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, key);
|
|
1788
|
+
if (!("value" in descriptor)) throw new TypeError("Python LLM response must contain data");
|
|
1789
|
+
if (entries++) add2(1);
|
|
1790
|
+
string(key);
|
|
1791
|
+
add2(1);
|
|
1792
|
+
visit(descriptor.value);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
ancestors.delete(item);
|
|
1796
|
+
};
|
|
1797
|
+
visit(value);
|
|
1798
|
+
return bytes;
|
|
1799
|
+
}
|
|
1800
|
+
function* textFragments(text2, limit) {
|
|
1801
|
+
let start = 0, bytes = 0;
|
|
1802
|
+
for (let i = 0; i < text2.length; ) {
|
|
1803
|
+
const point = text2.codePointAt(i);
|
|
1804
|
+
const width = point < 128 ? 1 : point < 2048 ? 2 : point < 65536 ? 3 : 4;
|
|
1805
|
+
if (width > limit) throw new RangeError("Python LLM stream chunk limit cannot fit a Unicode scalar");
|
|
1806
|
+
if (width > limit - bytes) {
|
|
1807
|
+
yield text2.slice(start, i);
|
|
1808
|
+
start = i;
|
|
1809
|
+
bytes = 0;
|
|
1810
|
+
}
|
|
1811
|
+
bytes += width;
|
|
1812
|
+
i += point > 65535 ? 2 : 1;
|
|
1813
|
+
}
|
|
1814
|
+
if (start < text2.length || !text2.length) yield text2.slice(start);
|
|
1815
|
+
}
|
|
1735
1816
|
function createPythonLlmCapability(context, service, options = {}) {
|
|
1736
1817
|
const chunkBytes = options.maxStreamChunkBytes ?? 16384;
|
|
1737
1818
|
if (!Number.isSafeInteger(chunkBytes) || chunkBytes < 1) throw new RangeError("Invalid Python LLM stream chunk limit");
|
|
1819
|
+
const bufferedLimit = options.maxBufferedResponseBytes ?? Infinity;
|
|
1820
|
+
const eventLimit = options.maxBufferedEvents ?? bufferedLimit;
|
|
1821
|
+
const metadataLimit = options.maxMetadataBytes ?? bufferedLimit;
|
|
1822
|
+
for (const limit of [bufferedLimit, eventLimit, metadataLimit]) {
|
|
1823
|
+
if (limit !== Infinity && (!Number.isSafeInteger(limit) || limit < 1)) throw new RangeError("Invalid Python LLM host limit");
|
|
1824
|
+
}
|
|
1738
1825
|
const prepare = async (value, signal) => {
|
|
1739
1826
|
const payload = record(value);
|
|
1740
1827
|
for (const key of ["template", "conversation"]) {
|
|
@@ -1791,7 +1878,15 @@ function createPythonLlmCapability(context, service, options = {}) {
|
|
|
1791
1878
|
request.signal.throwIfAborted();
|
|
1792
1879
|
yield { type: "bytes", data: Array.from(event.data.subarray(offset, offset + chunkBytes)) };
|
|
1793
1880
|
}
|
|
1794
|
-
} else
|
|
1881
|
+
} else if (event.type === "text") {
|
|
1882
|
+
for (const text2 of textFragments(event.text, chunkBytes)) {
|
|
1883
|
+
request.signal.throwIfAborted();
|
|
1884
|
+
yield { type: "text", text: text2 };
|
|
1885
|
+
}
|
|
1886
|
+
} else {
|
|
1887
|
+
jsonBytes(event.response, metadataLimit);
|
|
1888
|
+
yield event;
|
|
1889
|
+
}
|
|
1795
1890
|
}
|
|
1796
1891
|
};
|
|
1797
1892
|
return {
|
|
@@ -1814,15 +1909,41 @@ function createPythonLlmCapability(context, service, options = {}) {
|
|
|
1814
1909
|
});
|
|
1815
1910
|
}
|
|
1816
1911
|
if (operation.operation !== "complete") throw new TypeError("Unsupported Python LLM operation");
|
|
1817
|
-
const
|
|
1912
|
+
const request = await prepare(payload, signal);
|
|
1913
|
+
const bounded = { ...request, maxOutputBytes: Math.min(request.maxOutputBytes ?? Infinity, bufferedLimit) };
|
|
1914
|
+
let text2 = "", textBytes = 0, dataBytes = 0, received = 0;
|
|
1915
|
+
const data = [];
|
|
1818
1916
|
let response = {};
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
if (
|
|
1822
|
-
|
|
1823
|
-
|
|
1917
|
+
let envelopeBytes = jsonBytes({ text: "", data: [] }, bufferedLimit);
|
|
1918
|
+
const admit = (size) => {
|
|
1919
|
+
if (size > bufferedLimit - envelopeBytes - textBytes - dataBytes) throw new RangeError("Python LLM buffered response limit exceeded");
|
|
1920
|
+
};
|
|
1921
|
+
for await (const event of service.stream(bounded)) {
|
|
1922
|
+
bounded.signal.throwIfAborted();
|
|
1923
|
+
if (++received > eventLimit) throw new RangeError("Python LLM buffered event limit exceeded");
|
|
1924
|
+
if (event.type === "text") {
|
|
1925
|
+
const size = bufferedLimit === Infinity || !event.text ? 0 : jsonBytes(event.text, bufferedLimit - envelopeBytes - textBytes - dataBytes + 2) - 2;
|
|
1926
|
+
admit(size);
|
|
1927
|
+
textBytes += size;
|
|
1928
|
+
if (event.text) text2 += event.text;
|
|
1929
|
+
} else if (event.type === "bytes") {
|
|
1930
|
+
let size = 0;
|
|
1931
|
+
if (bufferedLimit !== Infinity) for (let i = 0; i < event.data.length; i++) {
|
|
1932
|
+
size += String(event.data[i]).length + (data.length || i ? 1 : 0);
|
|
1933
|
+
admit(size);
|
|
1934
|
+
}
|
|
1935
|
+
dataBytes += size;
|
|
1936
|
+
for (const byte of event.data) data.push(byte);
|
|
1937
|
+
} else {
|
|
1938
|
+
jsonBytes(event.response, metadataLimit);
|
|
1939
|
+
const details = record(event.response);
|
|
1940
|
+
const size = jsonBytes({ ...details, text: "", data: [] }, bufferedLimit);
|
|
1941
|
+
if (size > bufferedLimit - textBytes - dataBytes) throw new RangeError("Python LLM buffered response limit exceeded");
|
|
1942
|
+
envelopeBytes = size;
|
|
1943
|
+
response = { ...details };
|
|
1944
|
+
}
|
|
1824
1945
|
}
|
|
1825
|
-
return { ...response, text: text2
|
|
1946
|
+
return { ...response, text: text2, data };
|
|
1826
1947
|
},
|
|
1827
1948
|
stream: events
|
|
1828
1949
|
};
|
|
@@ -48,11 +48,11 @@ import {
|
|
|
48
48
|
trimImage,
|
|
49
49
|
unflattenImage,
|
|
50
50
|
unpremultiplyRgbaImage
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-UCI6ZQFB.js";
|
|
52
52
|
import {
|
|
53
53
|
PdfDocument,
|
|
54
54
|
encodePng
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-S3SCPBHC.js";
|
|
56
56
|
import {
|
|
57
57
|
createLuaFilterCapability
|
|
58
58
|
} from "./chunk-Y2KHC6TZ.js";
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// packages/pdf-ast/src/cos/filters.ts
|
|
6
6
|
init_buffer();
|
|
7
|
-
import { deflate, Inflate } from "pako";
|
|
7
|
+
import { deflate, Inflate, Z_BUF_ERROR } from "pako";
|
|
8
8
|
|
|
9
9
|
// packages/pdf-ast/src/vendor/pdfjs-fonts.mjs
|
|
10
10
|
init_buffer();
|
|
@@ -19365,22 +19365,31 @@ function decodeFlate(bytes, parms, maxDecodedBytes = DEFAULT_MAX_DECODED_BYTES)
|
|
|
19365
19365
|
inflated = new BoundedFlateStream(bytes, maxDecodedBytes).getBytes();
|
|
19366
19366
|
} catch (error) {
|
|
19367
19367
|
if (error instanceof PdfError && error.code === "E_LIMIT") throw error;
|
|
19368
|
+
const wrapped = bytes[0] === 31 && bytes[1] === 139 || bytes.length >= 2 && (bytes[0] & 15) === 8 && bytes[0] >>> 4 <= 7 && (bytes[0] << 8 | bytes[1]) % 31 === 0;
|
|
19368
19369
|
const decoder = new Inflate({
|
|
19369
|
-
raw: !
|
|
19370
|
+
raw: !wrapped,
|
|
19370
19371
|
chunkSize: Math.max(1, Math.min(64 * 1024, maxDecodedBytes + 1))
|
|
19371
19372
|
});
|
|
19373
|
+
const chunks = [];
|
|
19372
19374
|
let length = 0;
|
|
19373
19375
|
decoder.onData = (chunk) => {
|
|
19374
19376
|
length += chunk.length;
|
|
19375
19377
|
if (length > maxDecodedBytes) {
|
|
19376
19378
|
throw new PdfError("E_LIMIT", "FlateDecode output exceeds maximum decoded byte budget");
|
|
19377
19379
|
}
|
|
19378
|
-
|
|
19380
|
+
chunks.push(chunk);
|
|
19379
19381
|
};
|
|
19380
|
-
|
|
19382
|
+
const complete = decoder.push(bytes, true);
|
|
19383
|
+
const truncated = wrapped && decoder.err === Z_BUF_ERROR && length > 0;
|
|
19384
|
+
if ((!complete || decoder.err || !decoder.ended) && !truncated) {
|
|
19381
19385
|
throw new PdfError("E_CAPABILITY", "Invalid FlateDecode compressed stream");
|
|
19382
19386
|
}
|
|
19383
|
-
inflated =
|
|
19387
|
+
inflated = new Uint8Array(length);
|
|
19388
|
+
let offset = 0;
|
|
19389
|
+
for (const chunk of chunks) {
|
|
19390
|
+
inflated.set(chunk, offset);
|
|
19391
|
+
offset += chunk.length;
|
|
19392
|
+
}
|
|
19384
19393
|
}
|
|
19385
19394
|
return applyPredictor(inflated, parms);
|
|
19386
19395
|
}
|
|
@@ -30960,7 +30969,7 @@ function resolvePageFonts(doc, resourcesDict) {
|
|
|
30960
30969
|
const glyphNames = buildFontEncodingGlyphNamesMap(encNode);
|
|
30961
30970
|
const widths = /* @__PURE__ */ new Map();
|
|
30962
30971
|
let defaultWidth = 556;
|
|
30963
|
-
const isTwoByteCid = subtype === "Type0"
|
|
30972
|
+
const isTwoByteCid = subtype === "Type0";
|
|
30964
30973
|
let fontMatrix;
|
|
30965
30974
|
let charProcs;
|
|
30966
30975
|
let fontResources;
|
|
@@ -32413,7 +32422,7 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
32413
32422
|
const [x, y] = transformPoint(st.ctm, seg.x, seg.y);
|
|
32414
32423
|
transformedSegments.push({ kind: "cubic", x1, y1, x2, y2, x, y });
|
|
32415
32424
|
} else if (seg.kind === "rect") {
|
|
32416
|
-
if (hasRotOrShear) {
|
|
32425
|
+
if (hasRotOrShear || seg.width * st.ctm[0] < 0 || seg.height * st.ctm[3] < 0) {
|
|
32417
32426
|
const [p0x, p0y] = transformPoint(st.ctm, seg.x, seg.y);
|
|
32418
32427
|
const [p1x, p1y] = transformPoint(st.ctm, seg.x + seg.width, seg.y);
|
|
32419
32428
|
const [p2x, p2y] = transformPoint(st.ctm, seg.x + seg.width, seg.y + seg.height);
|
|
@@ -32446,7 +32455,12 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
32446
32455
|
transformedSegments.push({ kind: "close" });
|
|
32447
32456
|
}
|
|
32448
32457
|
}
|
|
32449
|
-
|
|
32458
|
+
const applyClip = () => {
|
|
32459
|
+
if (!node.clip) return;
|
|
32460
|
+
st.clipPaths = [...st.clipPaths ?? [], {
|
|
32461
|
+
segments: transformedSegments,
|
|
32462
|
+
fillRule: node.clip === "W*" ? "evenodd" : "nonzero"
|
|
32463
|
+
}];
|
|
32450
32464
|
let cMinX = Infinity, cMinY = Infinity, cMaxX = -Infinity, cMaxY = -Infinity;
|
|
32451
32465
|
for (const s of transformedSegments) {
|
|
32452
32466
|
if (s.kind === "move" || s.kind === "line") {
|
|
@@ -32474,8 +32488,11 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
32474
32488
|
Math.min(st.clipRect[3], cMaxY)
|
|
32475
32489
|
] : [cMinX, cMinY, cMaxX, cMaxY];
|
|
32476
32490
|
}
|
|
32491
|
+
};
|
|
32492
|
+
if (node.paint === "n") {
|
|
32493
|
+
applyClip();
|
|
32494
|
+
break;
|
|
32477
32495
|
}
|
|
32478
|
-
if (node.paint === "n") break;
|
|
32479
32496
|
const isFill = ["f", "F", "f*", "B", "B*", "b", "b*"].includes(node.paint);
|
|
32480
32497
|
const isStroke = ["S", "s", "B", "B*", "b", "b*"].includes(node.paint);
|
|
32481
32498
|
let evaluatedFillPattern = false;
|
|
@@ -32608,7 +32625,10 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
32608
32625
|
}
|
|
32609
32626
|
}
|
|
32610
32627
|
}
|
|
32611
|
-
if (evaluatedFillPattern && !isStroke)
|
|
32628
|
+
if (evaluatedFillPattern && !isStroke) {
|
|
32629
|
+
applyClip();
|
|
32630
|
+
break;
|
|
32631
|
+
}
|
|
32612
32632
|
const fillRule = node.paint.includes("*") ? "evenodd" : "nonzero";
|
|
32613
32633
|
const ctmScale = Math.max(1e-6, Math.hypot(st.ctm[0], st.ctm[1]));
|
|
32614
32634
|
const scaledStrokeWidth = st.strokeWidth === 0 ? 0 : st.strokeWidth * ctmScale;
|
|
@@ -32628,6 +32648,7 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
32628
32648
|
...st.dashPhase !== void 0 ? { dashPhase: st.dashPhase } : {},
|
|
32629
32649
|
...st.clipRect ? { clipRect: [...st.clipRect] } : {}
|
|
32630
32650
|
} });
|
|
32651
|
+
applyClip();
|
|
32631
32652
|
break;
|
|
32632
32653
|
}
|
|
32633
32654
|
case "xobject": {
|
|
@@ -34736,8 +34757,8 @@ function segmentsToScreenEdges(segments, pageHeight, scale, closeSubpaths = fals
|
|
|
34736
34757
|
if (closeSubpaths && hasOpenSubpath && (curX !== startX || curY !== startY)) {
|
|
34737
34758
|
edges.push({ x0: curX, y0: curY, x1: startX, y1: startY });
|
|
34738
34759
|
}
|
|
34739
|
-
const [rx0, ry0] = toScreen(seg.x, seg.y
|
|
34740
|
-
const [rx1, ry1] = toScreen(seg.x + seg.width, seg.y);
|
|
34760
|
+
const [rx0, ry0] = toScreen(seg.x, seg.y);
|
|
34761
|
+
const [rx1, ry1] = toScreen(seg.x + seg.width, seg.y + seg.height);
|
|
34741
34762
|
edges.push(
|
|
34742
34763
|
{ x0: rx0, y0: ry0, x1: rx1, y1: ry0 },
|
|
34743
34764
|
{ x0: rx1, y0: ry0, x1: rx1, y1: ry1 },
|
|
@@ -34880,21 +34901,24 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
34880
34901
|
}
|
|
34881
34902
|
const aaVec = options.antialiasVector !== false;
|
|
34882
34903
|
const aaTxt = options.antialiasText !== false;
|
|
34883
|
-
|
|
34904
|
+
let cachedClips;
|
|
34905
|
+
let cachedClipMask;
|
|
34884
34906
|
const imageClipMasks = /* @__PURE__ */ new Map();
|
|
34885
34907
|
for (const original of paintOperations(displayList)) {
|
|
34886
34908
|
if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
|
|
34887
34909
|
const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
|
|
34888
34910
|
const clips = original.value.clipPaths;
|
|
34889
|
-
let clipMask = clips
|
|
34911
|
+
let clipMask = clips && clips === cachedClips ? cachedClipMask : void 0;
|
|
34890
34912
|
if (clips && !clipMask) {
|
|
34891
34913
|
clipMask = new Uint8Array(width * height * 4).fill(255);
|
|
34892
|
-
for (const
|
|
34914
|
+
for (const clip of clips) {
|
|
34915
|
+
const { segments, fillRule } = "segments" in clip ? clip : { segments: clip, fillRule: "nonzero" };
|
|
34893
34916
|
const layer = new Uint8Array(width * height * 4);
|
|
34894
|
-
fillEdgesScanline4x4(layer, width, height, segmentsToScreenEdges(segments, displayList.height, scale, true), { r: 1, g: 1, b: 1 });
|
|
34917
|
+
fillEdgesScanline4x4(layer, width, height, segmentsToScreenEdges(segments, displayList.height, scale, true), { r: 1, g: 1, b: 1 }, 1, fillRule);
|
|
34895
34918
|
for (let i = 3; i < layer.length; i += 4) clipMask[i] = Math.round(clipMask[i] * layer[i] / 255);
|
|
34896
34919
|
}
|
|
34897
|
-
|
|
34920
|
+
cachedClips = clips;
|
|
34921
|
+
cachedClipMask = clipMask;
|
|
34898
34922
|
}
|
|
34899
34923
|
const imageClips = original.value.clipImages;
|
|
34900
34924
|
if (imageClips) {
|
|
@@ -4,7 +4,7 @@ import { type PythonDiagnosticObserver } from './diagnostics.js';
|
|
|
4
4
|
import { createPythonHostBridge, type PythonHostCapability, type PythonHostBridgeOptions } from './host-capabilities.js';
|
|
5
5
|
export * from './host-capabilities.js';
|
|
6
6
|
export { createPythonShellCapability } from './shell-capability.js';
|
|
7
|
-
export { createPythonLlmCapability } from './llm-capability.js';
|
|
7
|
+
export { createPythonLlmCapability, type PythonLlmCapabilityOptions } from './llm-capability.js';
|
|
8
8
|
/** A dedicated interpreter worker. The service event loop must never block. */
|
|
9
9
|
export interface PythonWorkerEndpoint {
|
|
10
10
|
postMessage(value: unknown): void;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { CommandContext } from '../../contracts/index.js';
|
|
2
2
|
import type { LlmService } from '../llm/service.js';
|
|
3
3
|
import type { PythonHostCapability } from './host-capabilities.js';
|
|
4
|
-
|
|
5
|
-
export declare function createPythonLlmCapability(context: Pick<CommandContext, 'fs' | 'cwd' | 'inputBudget'>, service: LlmService, options?: {
|
|
4
|
+
export interface PythonLlmCapabilityOptions {
|
|
6
5
|
readonly maxStreamChunkBytes?: number;
|
|
7
|
-
|
|
6
|
+
readonly maxBufferedResponseBytes?: number;
|
|
7
|
+
readonly maxBufferedEvents?: number;
|
|
8
|
+
readonly maxMetadataBytes?: number;
|
|
9
|
+
}
|
|
10
|
+
/** Reuses the invocation's authorized service; Python receives only model data. */
|
|
11
|
+
export declare function createPythonLlmCapability(context: Pick<CommandContext, 'fs' | 'cwd' | 'inputBudget'>, service: LlmService, options?: PythonLlmCapabilityOptions): PythonHostCapability;
|
|
8
12
|
//# sourceMappingURL=llm-capability.d.ts.map
|
|
@@ -5,11 +5,112 @@ function record(value) {
|
|
|
5
5
|
throw new TypeError('Expected a structured Python LLM request');
|
|
6
6
|
return value;
|
|
7
7
|
}
|
|
8
|
+
/** Count the wire representation before retaining/serializing host metadata. */
|
|
9
|
+
function jsonBytes(value, limit) {
|
|
10
|
+
if (limit === Infinity)
|
|
11
|
+
return 0;
|
|
12
|
+
let bytes = 0;
|
|
13
|
+
const ancestors = new Set();
|
|
14
|
+
const add = (size) => {
|
|
15
|
+
if (size > limit - bytes)
|
|
16
|
+
throw new RangeError('Python LLM serialized response limit exceeded');
|
|
17
|
+
bytes += size;
|
|
18
|
+
};
|
|
19
|
+
const string = (text) => {
|
|
20
|
+
add(2);
|
|
21
|
+
if (text.length > limit - bytes)
|
|
22
|
+
throw new RangeError('Python LLM serialized response limit exceeded');
|
|
23
|
+
for (let i = 0; i < text.length; i++) {
|
|
24
|
+
const point = text.codePointAt(i);
|
|
25
|
+
if (point > 65535) {
|
|
26
|
+
add(4);
|
|
27
|
+
i++;
|
|
28
|
+
}
|
|
29
|
+
else if (point === 34 || point === 92)
|
|
30
|
+
add(2);
|
|
31
|
+
else if (point < 32)
|
|
32
|
+
add([8, 9, 10, 12, 13].includes(point) ? 2 : 6);
|
|
33
|
+
else if (point >= 0xd800 && point <= 0xdfff)
|
|
34
|
+
add(6);
|
|
35
|
+
else
|
|
36
|
+
add(point < 128 ? 1 : point < 2048 ? 2 : 3);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const visit = (item) => {
|
|
40
|
+
if (typeof item === 'string') {
|
|
41
|
+
string(item);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (item === null || typeof item === 'boolean' || typeof item === 'number' && Number.isFinite(item)) {
|
|
45
|
+
add(JSON.stringify(item).length);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!item || typeof item !== 'object' || ancestors.has(item))
|
|
49
|
+
throw new TypeError('Python LLM response must contain acyclic data');
|
|
50
|
+
ancestors.add(item);
|
|
51
|
+
add(2);
|
|
52
|
+
let entries = 0;
|
|
53
|
+
if (Array.isArray(item)) {
|
|
54
|
+
if (item.length > limit - bytes)
|
|
55
|
+
throw new RangeError('Python LLM serialized response limit exceeded');
|
|
56
|
+
for (let i = 0; i < item.length; i++) {
|
|
57
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, String(i));
|
|
58
|
+
if (!descriptor || !('value' in descriptor))
|
|
59
|
+
throw new TypeError('Python LLM response must contain data');
|
|
60
|
+
if (entries++)
|
|
61
|
+
add(1);
|
|
62
|
+
visit(descriptor.value);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
for (const key in item) {
|
|
67
|
+
if (!Object.hasOwn(item, key))
|
|
68
|
+
continue;
|
|
69
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, key);
|
|
70
|
+
if (!('value' in descriptor))
|
|
71
|
+
throw new TypeError('Python LLM response must contain data');
|
|
72
|
+
if (entries++)
|
|
73
|
+
add(1);
|
|
74
|
+
string(key);
|
|
75
|
+
add(1);
|
|
76
|
+
visit(descriptor.value);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
ancestors.delete(item);
|
|
80
|
+
};
|
|
81
|
+
visit(value);
|
|
82
|
+
return bytes;
|
|
83
|
+
}
|
|
84
|
+
function* textFragments(text, limit) {
|
|
85
|
+
let start = 0, bytes = 0;
|
|
86
|
+
for (let i = 0; i < text.length;) {
|
|
87
|
+
const point = text.codePointAt(i);
|
|
88
|
+
const width = point < 128 ? 1 : point < 2048 ? 2 : point < 65536 ? 3 : 4;
|
|
89
|
+
if (width > limit)
|
|
90
|
+
throw new RangeError('Python LLM stream chunk limit cannot fit a Unicode scalar');
|
|
91
|
+
if (width > limit - bytes) {
|
|
92
|
+
yield text.slice(start, i);
|
|
93
|
+
start = i;
|
|
94
|
+
bytes = 0;
|
|
95
|
+
}
|
|
96
|
+
bytes += width;
|
|
97
|
+
i += point > 65535 ? 2 : 1;
|
|
98
|
+
}
|
|
99
|
+
if (start < text.length || !text.length)
|
|
100
|
+
yield text.slice(start);
|
|
101
|
+
}
|
|
8
102
|
/** Reuses the invocation's authorized service; Python receives only model data. */
|
|
9
103
|
export function createPythonLlmCapability(context, service, options = {}) {
|
|
10
104
|
const chunkBytes = options.maxStreamChunkBytes ?? 16384;
|
|
11
105
|
if (!Number.isSafeInteger(chunkBytes) || chunkBytes < 1)
|
|
12
106
|
throw new RangeError('Invalid Python LLM stream chunk limit');
|
|
107
|
+
const bufferedLimit = options.maxBufferedResponseBytes ?? Infinity;
|
|
108
|
+
const eventLimit = options.maxBufferedEvents ?? bufferedLimit;
|
|
109
|
+
const metadataLimit = options.maxMetadataBytes ?? bufferedLimit;
|
|
110
|
+
for (const limit of [bufferedLimit, eventLimit, metadataLimit]) {
|
|
111
|
+
if (limit !== Infinity && (!Number.isSafeInteger(limit) || limit < 1))
|
|
112
|
+
throw new RangeError('Invalid Python LLM host limit');
|
|
113
|
+
}
|
|
13
114
|
const prepare = async (value, signal) => {
|
|
14
115
|
const payload = record(value);
|
|
15
116
|
for (const key of ['template', 'conversation']) {
|
|
@@ -73,8 +174,16 @@ export function createPythonLlmCapability(context, service, options = {}) {
|
|
|
73
174
|
yield { type: 'bytes', data: Array.from(event.data.subarray(offset, offset + chunkBytes)) };
|
|
74
175
|
}
|
|
75
176
|
}
|
|
76
|
-
else
|
|
177
|
+
else if (event.type === 'text') {
|
|
178
|
+
for (const text of textFragments(event.text, chunkBytes)) {
|
|
179
|
+
request.signal.throwIfAborted();
|
|
180
|
+
yield { type: 'text', text };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
jsonBytes(event.response, metadataLimit);
|
|
77
185
|
yield event;
|
|
186
|
+
}
|
|
78
187
|
}
|
|
79
188
|
};
|
|
80
189
|
return {
|
|
@@ -93,19 +202,49 @@ export function createPythonLlmCapability(context, service, options = {}) {
|
|
|
93
202
|
}
|
|
94
203
|
if (operation.operation !== 'complete')
|
|
95
204
|
throw new TypeError('Unsupported Python LLM operation');
|
|
96
|
-
const
|
|
205
|
+
const request = await prepare(payload, signal);
|
|
206
|
+
const bounded = { ...request, maxOutputBytes: Math.min(request.maxOutputBytes ?? Infinity, bufferedLimit) };
|
|
207
|
+
let text = '', textBytes = 0, dataBytes = 0, received = 0;
|
|
208
|
+
const data = [];
|
|
97
209
|
let response = {};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
210
|
+
let envelopeBytes = jsonBytes({ text: '', data: [] }, bufferedLimit);
|
|
211
|
+
const admit = (size) => {
|
|
212
|
+
if (size > bufferedLimit - envelopeBytes - textBytes - dataBytes)
|
|
213
|
+
throw new RangeError('Python LLM buffered response limit exceeded');
|
|
214
|
+
};
|
|
215
|
+
for await (const event of service.stream(bounded)) {
|
|
216
|
+
bounded.signal.throwIfAborted();
|
|
217
|
+
if (++received > eventLimit)
|
|
218
|
+
throw new RangeError('Python LLM buffered event limit exceeded');
|
|
219
|
+
if (event.type === 'text') {
|
|
220
|
+
const size = bufferedLimit === Infinity || !event.text ? 0 : jsonBytes(event.text, bufferedLimit - envelopeBytes - textBytes - dataBytes + 2) - 2;
|
|
221
|
+
admit(size);
|
|
222
|
+
textBytes += size;
|
|
223
|
+
if (event.text)
|
|
224
|
+
text += event.text;
|
|
225
|
+
}
|
|
226
|
+
else if (event.type === 'bytes') {
|
|
227
|
+
let size = 0;
|
|
228
|
+
if (bufferedLimit !== Infinity)
|
|
229
|
+
for (let i = 0; i < event.data.length; i++) {
|
|
230
|
+
size += String(event.data[i]).length + (data.length || i ? 1 : 0);
|
|
231
|
+
admit(size);
|
|
232
|
+
}
|
|
233
|
+
dataBytes += size;
|
|
103
234
|
for (const byte of event.data)
|
|
104
235
|
data.push(byte);
|
|
105
|
-
|
|
106
|
-
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
jsonBytes(event.response, metadataLimit);
|
|
239
|
+
const details = record(event.response);
|
|
240
|
+
const size = jsonBytes({ ...details, text: '', data: [] }, bufferedLimit);
|
|
241
|
+
if (size > bufferedLimit - textBytes - dataBytes)
|
|
242
|
+
throw new RangeError('Python LLM buffered response limit exceeded');
|
|
243
|
+
envelopeBytes = size;
|
|
244
|
+
response = { ...details };
|
|
245
|
+
}
|
|
107
246
|
}
|
|
108
|
-
return { ...response, text
|
|
247
|
+
return { ...response, text, data };
|
|
109
248
|
},
|
|
110
249
|
stream: events,
|
|
111
250
|
};
|
|
@@ -73,10 +73,15 @@ export function createPythonShellCapability(context, options = {}) {
|
|
|
73
73
|
const stdout = [];
|
|
74
74
|
const stderr = [];
|
|
75
75
|
let captured = 0;
|
|
76
|
+
let outputFailure;
|
|
76
77
|
const sink = (target, type) => ({ async write(chunk) {
|
|
77
78
|
childSignal.throwIfAborted();
|
|
78
|
-
if (
|
|
79
|
-
throw
|
|
79
|
+
if (outputFailure)
|
|
80
|
+
throw outputFailure;
|
|
81
|
+
if (chunk.length > outputLimit - captured) {
|
|
82
|
+
outputFailure = new RangeError('Python shell output limit exceeded');
|
|
83
|
+
throw outputFailure;
|
|
84
|
+
}
|
|
80
85
|
captured += chunk.length;
|
|
81
86
|
if (publish) {
|
|
82
87
|
for (let offset = 0; offset < chunk.length; offset += chunkBytes) {
|
|
@@ -97,6 +102,9 @@ export function createPythonShellCapability(context, options = {}) {
|
|
|
97
102
|
stdin: childInput, stdout: sink(stdout, 'stdout'), stderr: sink(stderr, 'stderr'), externalInvocation: true,
|
|
98
103
|
});
|
|
99
104
|
childSignal.throwIfAborted();
|
|
105
|
+
// A command may catch sink rejection and return a status; the host limit still failed.
|
|
106
|
+
if (outputFailure)
|
|
107
|
+
throw outputFailure;
|
|
100
108
|
return { stdout, stderr, exitCode: result.exitCode };
|
|
101
109
|
}
|
|
102
110
|
catch (error) {
|