openfox 2.0.76 → 2.0.78
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/{chat-handler-3BZ4PDL2.js → chat-handler-5ZHPYU24.js} +21 -20
- package/dist/{chunk-MF3UW36X.js → chunk-37WF2Z3T.js} +84 -17
- package/dist/{chunk-OBQPXUXI.js → chunk-5CQRB7AD.js} +2 -2
- package/dist/chunk-64TKWWNF.js +7 -0
- package/dist/{chunk-LXFLRM7M.js → chunk-6AE3JPDJ.js} +2 -2
- package/dist/chunk-AANVMTYU.js +327 -0
- package/dist/{chunk-YMFCFMMB.js → chunk-AFJLY43Z.js} +8 -3
- package/dist/{chunk-OCVRIPQP.js → chunk-CJDYSUU5.js} +3 -3
- package/dist/{chunk-OMWVB2XC.js → chunk-DI4SIPFT.js} +46 -27
- package/dist/{chunk-7772ENZN.js → chunk-EHVDAVYG.js} +4 -3
- package/dist/{chunk-DH3SSBZN.js → chunk-EYIWUONA.js} +2 -2
- package/dist/{chunk-NGJ5XJ7D.js → chunk-M7EFXRDS.js} +57 -290
- package/dist/{chunk-4QHDQP2N.js → chunk-MTUYDBSL.js} +2 -2
- package/dist/{chunk-HHUIXHHX.js → chunk-NUR2B4RU.js} +7 -7
- package/dist/{chunk-46RORSKU.js → chunk-NZIYELSQ.js} +9 -7
- package/dist/{chunk-7QXRQ5ZL.js → chunk-RGUUZD5B.js} +5 -1
- package/dist/{chunk-EZK7CGJ7.js → chunk-RMXPVQP4.js} +315 -45
- package/dist/{chunk-D6CI2B7B.js → chunk-SGBXBP3S.js} +253 -110
- package/dist/{chunk-O3NUNU4H.js → chunk-SJTG56CH.js} +5 -4
- package/dist/{chunk-DJASQ2ZB.js → chunk-TSVFRWSG.js} +24 -5
- package/dist/{chunk-XTK6E2QR.js → chunk-UIU7OGHP.js} +26 -10
- package/dist/{chunk-J7VCI5EX.js → chunk-UMX7MMNW.js} +652 -248
- package/dist/{chunk-HLHGNHMV.js → chunk-VGWJFXPR.js} +23 -11
- package/dist/{chunk-KVRSAC4Z.js → chunk-VPFUXGMM.js} +6 -1
- package/dist/{chunk-W4ZEG3LX.js → chunk-WL2WEJAK.js} +2 -2
- package/dist/{chunk-HAQV6AJP.js → chunk-WUSWNLTV.js} +3 -3
- package/dist/{chunk-5GOOSZX7.js → chunk-X6BXZFO7.js} +1 -1
- package/dist/{chunk-WGAAQASA.js → chunk-YFVSSMA3.js} +3 -2
- package/dist/cli/dev.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/{client-TDPQVBQ7.js → client-V5DZRO2D.js} +3 -3
- package/dist/{client-pure-NMLDKRDN.js → client-pure-5YE5WFJP.js} +2 -2
- package/dist/compactor-G7B3V24F.js +24 -0
- package/dist/{config-EUMVEFHU.js → config-MD74ETY5.js} +2 -2
- package/dist/{events-FLWRLM6V.js → events-KNOZX7YG.js} +7 -4
- package/dist/{folding-ONH3D4OK.js → folding-OPP2BVUB.js} +2 -2
- package/dist/{inspect-proxy-JHZV34QR.js → inspect-proxy-DRYIIH4O.js} +4 -4
- package/dist/{manager-OPMCSYN2.js → manager-ATWZUL3O.js} +5 -5
- package/dist/{orchestrator-5IMCWX5P.js → orchestrator-L6LX5BV7.js} +20 -19
- package/dist/package.json +6 -1
- package/dist/{path-security-ZROWNL22.js → path-security-HUXQHEQ5.js} +10 -7
- package/dist/{platform-QGXO3CNR.js → platform-VBNNMTR5.js} +4 -4
- package/dist/{processor-3R4GS6VB.js → processor-UQX5KL56.js} +21 -20
- package/dist/{project-creator-C22J43BC.js → project-creator-NNUITNUI.js} +3 -3
- package/dist/{projects-WSVINK2C.js → projects-ZGJGICGT.js} +3 -3
- package/dist/{protocol-Sr6Jr4ZS.d.ts → protocol-BXxJmsOW.d.ts} +2 -1
- package/dist/{protocol-EWEVW65B.js → protocol-Q7X4NEME.js} +3 -3
- package/dist/provider/index.d.ts +4 -4
- package/dist/{provider-G2YC2IC4.js → provider-PI3RDDE4.js} +6 -6
- package/dist/{provider-manager-SQIELYLK.js → provider-manager-PD3BXUHY.js} +4 -4
- package/dist/{serve-MXKMKZYY.js → serve-QWC3U5MB.js} +26 -25
- package/dist/server/index.d.ts +27 -4
- package/dist/server/index.js +25 -24
- package/dist/server-3XUD2NB7.js +40 -0
- package/dist/sessions-SFG42M4W.js +42 -0
- package/dist/{settings-7XDMSPIC.js → settings-6M55N4Q2.js} +5 -3
- package/dist/shared/index.d.ts +3 -3
- package/dist/shared/index.js +1 -1
- package/dist/{tools-JRCUWPJO.js → tools-HGKUD327.js} +26 -23
- package/dist/{types-C4cG9CtC.d.ts → types-C308SU4B.d.ts} +3 -0
- package/dist/{types-DSZhFA8O.d.ts → types-o07LJcLI.d.ts} +1 -1
- package/dist/{update-OXDQ75M7.js → update-NWZALB5M.js} +2 -2
- package/dist/{update-server-4MTM7M5P.js → update-server-V6GJ43WH.js} +2 -2
- package/dist/web/assets/index-7_xKqeJc.js +305 -0
- package/dist/web/assets/index-DRkTjPo3.css +32 -0
- package/dist/web/index.html +4 -4
- package/dist/web/manifest.webmanifest +1 -1
- package/dist/web/registerSW.js +1 -1
- package/dist/web/sw.js +1 -1
- package/dist/{workspace-6QTTUMMW.js → workspace-J74ZMSOJ.js} +14 -2
- package/package.json +6 -1
- package/dist/chunk-KQEVAO2Q.js +0 -7
- package/dist/compactor-BB3YYVRP.js +0 -23
- package/dist/server-SPYFMFQE.js +0 -39
- package/dist/web/assets/index-BNVBxKnB.css +0 -32
- package/dist/web/assets/index-ghDkIdlf.js +0 -305
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getEventStore
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-M7EFXRDS.js";
|
|
4
4
|
import {
|
|
5
5
|
createChatPathConfirmationMessage
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SJTG56CH.js";
|
|
7
7
|
|
|
8
8
|
// src/server/tools/path-security.ts
|
|
9
9
|
import { realpath } from "fs/promises";
|
|
@@ -416,10 +416,8 @@ function providePathConfirmation(callId, approved, alwaysAllow) {
|
|
|
416
416
|
});
|
|
417
417
|
} catch {
|
|
418
418
|
}
|
|
419
|
-
if (approved) {
|
|
420
|
-
if (
|
|
421
|
-
addAllowedPaths(pending.sessionId, pending.paths);
|
|
422
|
-
} else {
|
|
419
|
+
if (approved && alwaysAllow) {
|
|
420
|
+
if (pending.reason !== "dangerous_command" && pending.reason !== "git_no_verify") {
|
|
423
421
|
addAllowedPaths(pending.sessionId, pending.paths);
|
|
424
422
|
}
|
|
425
423
|
}
|
|
@@ -451,6 +449,9 @@ function cancelPathConfirmationsForSession(sessionId, reason) {
|
|
|
451
449
|
function hasPendingPathConfirmation(callId) {
|
|
452
450
|
return pendingConfirmations.has(callId);
|
|
453
451
|
}
|
|
452
|
+
function getConfirmationSessionId(callId) {
|
|
453
|
+
return pendingConfirmations.get(callId)?.sessionId;
|
|
454
|
+
}
|
|
454
455
|
function getPendingConfirmationsBySession() {
|
|
455
456
|
const bySession = {};
|
|
456
457
|
for (const [_callId, pending] of pendingConfirmations.entries()) {
|
|
@@ -490,6 +491,7 @@ export {
|
|
|
490
491
|
cancelPathConfirmation,
|
|
491
492
|
cancelPathConfirmationsForSession,
|
|
492
493
|
hasPendingPathConfirmation,
|
|
494
|
+
getConfirmationSessionId,
|
|
493
495
|
getPendingConfirmationsBySession
|
|
494
496
|
};
|
|
495
|
-
//# sourceMappingURL=chunk-
|
|
497
|
+
//# sourceMappingURL=chunk-NZIYELSQ.js.map
|
|
@@ -181,6 +181,10 @@ function runMigrations(db2) {
|
|
|
181
181
|
logger.info("Migrating sessions table: adding workspace column");
|
|
182
182
|
db2.exec(`ALTER TABLE sessions ADD COLUMN workspace TEXT`);
|
|
183
183
|
}
|
|
184
|
+
if (!columnNames.includes("branch")) {
|
|
185
|
+
logger.info("Migrating sessions table: adding branch column");
|
|
186
|
+
db2.exec(`ALTER TABLE sessions ADD COLUMN branch TEXT`);
|
|
187
|
+
}
|
|
184
188
|
logger.info("Database migrations completed");
|
|
185
189
|
}
|
|
186
190
|
|
|
@@ -188,4 +192,4 @@ export {
|
|
|
188
192
|
initDatabase,
|
|
189
193
|
getDatabase
|
|
190
194
|
};
|
|
191
|
-
//# sourceMappingURL=chunk-
|
|
195
|
+
//# sourceMappingURL=chunk-RGUUZD5B.js.map
|
|
@@ -19,6 +19,7 @@ var OUTPUT_LIMITS = {
|
|
|
19
19
|
maxImageBytes: 2097152,
|
|
20
20
|
// 2MB for images
|
|
21
21
|
maxPdfPages: 50,
|
|
22
|
+
maxPdfImages: 20,
|
|
22
23
|
maxFileBytes: 20971520
|
|
23
24
|
// 20MB general file safety limit
|
|
24
25
|
},
|
|
@@ -38,7 +39,8 @@ var OUTPUT_LIMITS = {
|
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
// src/server/tools/pdf-utils.ts
|
|
41
|
-
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
42
|
+
import { getDocument, OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
43
|
+
import { PNG } from "pngjs";
|
|
42
44
|
var PDF_HEADER = Buffer.from("%PDF");
|
|
43
45
|
function isPdfBuffer(buffer) {
|
|
44
46
|
return buffer.length > 4 && buffer.subarray(0, 4).equals(PDF_HEADER);
|
|
@@ -63,47 +65,278 @@ function processPdfContent(text, maxBytes) {
|
|
|
63
65
|
const isScanned = output.replace(/\[Page \d+\/\d+\]\n/g, "").trim().length === 0;
|
|
64
66
|
return { output, truncated, isScanned };
|
|
65
67
|
}
|
|
66
|
-
|
|
68
|
+
var IMAGE_KIND_GRAYSCALE_1BPP = 1;
|
|
69
|
+
var IMAGE_KIND_RGB_24BPP = 2;
|
|
70
|
+
var IMAGE_KIND_RGBA_32BPP = 3;
|
|
71
|
+
function getRgbaPixel(data, width, x, y, kind) {
|
|
72
|
+
if (kind === IMAGE_KIND_RGBA_32BPP) {
|
|
73
|
+
const i = (y * width + x) * 4;
|
|
74
|
+
return [data[i] ?? 0, data[i + 1] ?? 0, data[i + 2] ?? 0, data[i + 3] ?? 255];
|
|
75
|
+
}
|
|
76
|
+
if (kind === IMAGE_KIND_RGB_24BPP) {
|
|
77
|
+
const i = (y * width + x) * 3;
|
|
78
|
+
return [data[i] ?? 0, data[i + 1] ?? 0, data[i + 2] ?? 0, 255];
|
|
79
|
+
}
|
|
80
|
+
if (kind === IMAGE_KIND_GRAYSCALE_1BPP) {
|
|
81
|
+
const bytesPerRow = Math.ceil(width / 8);
|
|
82
|
+
const byteIdx = y * bytesPerRow + Math.floor(x / 8);
|
|
83
|
+
const bitIdx = 7 - x % 8;
|
|
84
|
+
const val = (data[byteIdx] ?? 0) >> bitIdx & 1 ? 255 : 0;
|
|
85
|
+
return [val, val, val, 255];
|
|
86
|
+
}
|
|
87
|
+
return [0, 0, 0, 255];
|
|
88
|
+
}
|
|
89
|
+
function toRgbaBuffer(data, width, height, kind) {
|
|
90
|
+
const rgba = new Uint8Array(width * height * 4);
|
|
91
|
+
for (let y = 0; y < height; y++) {
|
|
92
|
+
for (let x = 0; x < width; x++) {
|
|
93
|
+
const [r, g, b, a] = getRgbaPixel(data, width, x, y, kind);
|
|
94
|
+
const i = (y * width + x) * 4;
|
|
95
|
+
rgba[i] = r;
|
|
96
|
+
rgba[i + 1] = g;
|
|
97
|
+
rgba[i + 2] = b;
|
|
98
|
+
rgba[i + 3] = a;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return rgba;
|
|
102
|
+
}
|
|
103
|
+
function extractBitmapToRawImageData(bitmap) {
|
|
104
|
+
const canvas = new globalThis.OffscreenCanvas(bitmap.width, bitmap.height);
|
|
105
|
+
const ctx = canvas.getContext("2d");
|
|
106
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
107
|
+
const imageData = ctx.getImageData(0, 0, bitmap.width, bitmap.height);
|
|
108
|
+
return {
|
|
109
|
+
width: bitmap.width,
|
|
110
|
+
height: bitmap.height,
|
|
111
|
+
data: new Uint8Array(imageData.data.buffer),
|
|
112
|
+
kind: IMAGE_KIND_RGBA_32BPP
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function encodeImageToDataUrl(imgData, maxDimension = 1024) {
|
|
116
|
+
try {
|
|
117
|
+
let { width, height } = imgData;
|
|
118
|
+
let { data, kind } = imgData;
|
|
119
|
+
if (imgData.bitmap && !data) {
|
|
120
|
+
const converted = extractBitmapToRawImageData(imgData.bitmap);
|
|
121
|
+
data = converted.data;
|
|
122
|
+
kind = converted.kind;
|
|
123
|
+
}
|
|
124
|
+
if (!data) return null;
|
|
125
|
+
if (width * height * 4 > 20971520) return null;
|
|
126
|
+
if (maxDimension > 0 && (width > maxDimension || height > maxDimension)) {
|
|
127
|
+
const scale = maxDimension / Math.max(width, height);
|
|
128
|
+
const newWidth = Math.round(width * scale);
|
|
129
|
+
const newHeight = Math.round(height * scale);
|
|
130
|
+
const scaled = new Uint8Array(newWidth * newHeight * 4);
|
|
131
|
+
for (let y = 0; y < newHeight; y++) {
|
|
132
|
+
for (let x = 0; x < newWidth; x++) {
|
|
133
|
+
const srcX = Math.floor(x / scale);
|
|
134
|
+
const srcY = Math.floor(y / scale);
|
|
135
|
+
const dstI = (y * newWidth + x) * 4;
|
|
136
|
+
const [r, g, b, a] = getRgbaPixel(data, width, srcX, srcY, kind);
|
|
137
|
+
scaled[dstI] = r;
|
|
138
|
+
scaled[dstI + 1] = g;
|
|
139
|
+
scaled[dstI + 2] = b;
|
|
140
|
+
scaled[dstI + 3] = a;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
width = newWidth;
|
|
144
|
+
height = newHeight;
|
|
145
|
+
const png2 = new PNG({ width, height });
|
|
146
|
+
Buffer.from(scaled.buffer, scaled.byteOffset, scaled.byteLength).copy(png2.data);
|
|
147
|
+
const pngBuffer2 = PNG.sync.write(png2);
|
|
148
|
+
return `data:image/png;base64,${pngBuffer2.toString("base64")}`;
|
|
149
|
+
}
|
|
150
|
+
let rgba;
|
|
151
|
+
if (kind === IMAGE_KIND_RGBA_32BPP) {
|
|
152
|
+
rgba = Uint8Array.from(
|
|
153
|
+
data instanceof Uint8ClampedArray ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : data
|
|
154
|
+
);
|
|
155
|
+
} else {
|
|
156
|
+
rgba = toRgbaBuffer(data, width, height, kind);
|
|
157
|
+
}
|
|
158
|
+
const png = new PNG({ width, height });
|
|
159
|
+
Buffer.from(rgba.buffer, rgba.byteOffset, rgba.byteLength).copy(png.data);
|
|
160
|
+
const pngBuffer = PNG.sync.write(png);
|
|
161
|
+
return `data:image/png;base64,${pngBuffer.toString("base64")}`;
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function extractPageBlocks(page, pageIndex, pageCount, imageCounter, maxImages) {
|
|
167
|
+
const [textContent, opList] = await Promise.all([page.getTextContent(), page.getOperatorList()]);
|
|
168
|
+
const textItems = textContent.items.filter(
|
|
169
|
+
(item) => "str" in item
|
|
170
|
+
);
|
|
171
|
+
const textStr = textItems.map((t) => t.str).join(" ").trim();
|
|
172
|
+
const imageOpIndices = [];
|
|
173
|
+
for (let i = 0; i < opList.fnArray.length; i++) {
|
|
174
|
+
const op = opList.fnArray[i];
|
|
175
|
+
if (op === OPS.paintImageXObject || op === OPS.paintInlineImageXObject || op === OPS.paintImageMaskXObject || op === OPS.paintImageMaskXObjectGroup || op === OPS.paintImageXObjectRepeat || op === OPS.paintImageMaskXObjectRepeat || op === OPS.paintInlineImageXObjectGroup || op === OPS.paintSolidColorImageMask) {
|
|
176
|
+
imageOpIndices.push(i);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const blocks = [];
|
|
180
|
+
if (textStr) {
|
|
181
|
+
blocks.push({ type: "text", content: `[Page ${pageIndex}/${pageCount}]
|
|
182
|
+
${textStr}` });
|
|
183
|
+
}
|
|
184
|
+
for (let imageOpIdx = 0; imageOpIdx < imageOpIndices.length; imageOpIdx++) {
|
|
185
|
+
if (imageCounter.count >= maxImages) {
|
|
186
|
+
imageCounter.limitReached = true;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
const opIdx = imageOpIndices[imageOpIdx];
|
|
190
|
+
if (opIdx === void 0) break;
|
|
191
|
+
const imgBlock = await extractImageBlock(page, opList, opIdx);
|
|
192
|
+
if (imgBlock) {
|
|
193
|
+
blocks.push(imgBlock);
|
|
194
|
+
imageCounter.count++;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return blocks;
|
|
198
|
+
}
|
|
199
|
+
function isValidPixelData(data) {
|
|
200
|
+
return data instanceof Uint8Array || data instanceof Uint8ClampedArray;
|
|
201
|
+
}
|
|
202
|
+
var IMAGE_RESOLVE_TIMEOUT_MS = 5e3;
|
|
203
|
+
async function getObjectWithFallback(objs, objId) {
|
|
204
|
+
try {
|
|
205
|
+
return objs.get(objId);
|
|
206
|
+
} catch {
|
|
207
|
+
}
|
|
208
|
+
return new Promise((resolve, reject) => {
|
|
209
|
+
const timeoutId = setTimeout(() => reject(new Error("timeout")), IMAGE_RESOLVE_TIMEOUT_MS);
|
|
210
|
+
try {
|
|
211
|
+
;
|
|
212
|
+
objs.get(objId, (data) => {
|
|
213
|
+
clearTimeout(timeoutId);
|
|
214
|
+
resolve(data);
|
|
215
|
+
});
|
|
216
|
+
} catch (e) {
|
|
217
|
+
clearTimeout(timeoutId);
|
|
218
|
+
reject(e);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async function extractImageBlock(page, opList, opIdx) {
|
|
223
|
+
const args = opList.argsArray[opIdx];
|
|
224
|
+
const objId = Array.isArray(args) ? args[0] : void 0;
|
|
225
|
+
let imgData = null;
|
|
226
|
+
if (objId && typeof objId === "string") {
|
|
227
|
+
try {
|
|
228
|
+
const store = objId.startsWith("g_") ? page.commonObjs : page.objs;
|
|
229
|
+
const raw = await getObjectWithFallback(store, objId);
|
|
230
|
+
if (raw && typeof raw === "object" && "width" in raw && "height" in raw) {
|
|
231
|
+
const r = raw;
|
|
232
|
+
if (isValidPixelData(r["data"])) {
|
|
233
|
+
imgData = raw;
|
|
234
|
+
} else if (r["bitmap"] && !r["data"]) {
|
|
235
|
+
imgData = raw;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} catch {
|
|
239
|
+
}
|
|
240
|
+
} else if (Array.isArray(args) && args[0] && typeof args[0] === "object" && "width" in args[0]) {
|
|
241
|
+
const inline = args[0];
|
|
242
|
+
if (isValidPixelData(inline["data"])) {
|
|
243
|
+
imgData = args[0];
|
|
244
|
+
} else if (inline["bitmap"] && !inline["data"]) {
|
|
245
|
+
imgData = args[0];
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (imgData) {
|
|
249
|
+
const dataUrl = encodeImageToDataUrl(imgData);
|
|
250
|
+
if (dataUrl) {
|
|
251
|
+
return { type: "image", dataUrl };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
async function extractPdfContent(buffer) {
|
|
67
257
|
const doc = await getDocument({ data: Uint8Array.from(buffer) }).promise;
|
|
68
258
|
const pageCount = doc.numPages;
|
|
69
|
-
const
|
|
70
|
-
const info = rawMeta.info;
|
|
71
|
-
const title = info?.["Title"] || null;
|
|
72
|
-
const author = info?.["Author"] || null;
|
|
73
|
-
const pages = [];
|
|
259
|
+
const allBlocks = [];
|
|
74
260
|
const limitedPageCount = Math.min(pageCount, OUTPUT_LIMITS.read_file.maxPdfPages);
|
|
261
|
+
const maxImages = OUTPUT_LIMITS.read_file.maxPdfImages;
|
|
262
|
+
const imageCounter = { count: 0, limitReached: false };
|
|
263
|
+
let title = null;
|
|
264
|
+
let author = null;
|
|
265
|
+
try {
|
|
266
|
+
const rawMeta = await doc.getMetadata();
|
|
267
|
+
const info = rawMeta.info;
|
|
268
|
+
title = info?.["Title"] || null;
|
|
269
|
+
author = info?.["Author"] || null;
|
|
270
|
+
} catch {
|
|
271
|
+
}
|
|
75
272
|
try {
|
|
76
273
|
for (let i = 1; i <= limitedPageCount; i++) {
|
|
77
274
|
const page = await doc.getPage(i);
|
|
78
275
|
try {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
pages.push(`[Page ${i}/${pageCount}]
|
|
82
|
-
${pageText}`);
|
|
276
|
+
const blocks = await extractPageBlocks(page, i, pageCount, imageCounter, maxImages);
|
|
277
|
+
allBlocks.push(...blocks);
|
|
83
278
|
} finally {
|
|
84
279
|
page.cleanup();
|
|
85
280
|
}
|
|
86
281
|
}
|
|
282
|
+
} catch {
|
|
87
283
|
} finally {
|
|
88
284
|
doc.cleanup();
|
|
89
285
|
}
|
|
90
|
-
let text = pages.join("\n\n");
|
|
91
286
|
if (pageCount > limitedPageCount) {
|
|
92
|
-
|
|
287
|
+
allBlocks.push({
|
|
288
|
+
type: "text",
|
|
289
|
+
content: `[PDF has ${pageCount} pages, showing first ${limitedPageCount}. Use a shell command to process more.]`
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (imageCounter.limitReached) {
|
|
293
|
+
allBlocks.push({
|
|
294
|
+
type: "text",
|
|
295
|
+
content: `[Image limit reached: showing first ${maxImages} images out of more in this document.]`
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
blocks: allBlocks,
|
|
300
|
+
pageCount,
|
|
301
|
+
title,
|
|
302
|
+
author,
|
|
303
|
+
imageCount: imageCounter.count,
|
|
304
|
+
imageLimitReached: imageCounter.limitReached
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
async function extractPdfText(buffer) {
|
|
308
|
+
const result = await extractPdfContent(buffer);
|
|
309
|
+
const text = result.blocks.filter((b) => b.type === "text").map((b) => b.content ?? "").join("\n\n");
|
|
310
|
+
return { text, pageCount: result.pageCount, title: result.title, author: result.author };
|
|
311
|
+
}
|
|
93
312
|
|
|
94
|
-
|
|
313
|
+
// src/server/utils/cache.ts
|
|
314
|
+
import { createHash } from "crypto";
|
|
315
|
+
function contentHash(data) {
|
|
316
|
+
return createHash("sha256").update(data).digest("hex").slice(0, 16);
|
|
317
|
+
}
|
|
318
|
+
function cacheSet(cache, key, value) {
|
|
319
|
+
const max = 50;
|
|
320
|
+
if (cache.size >= max) {
|
|
321
|
+
const first = cache.keys().next().value;
|
|
322
|
+
if (first !== void 0) cache.delete(first);
|
|
95
323
|
}
|
|
96
|
-
|
|
324
|
+
cache.set(key, value);
|
|
97
325
|
}
|
|
98
326
|
|
|
99
|
-
// src/server/
|
|
100
|
-
function
|
|
327
|
+
// src/server/utils/data-url.ts
|
|
328
|
+
function decodeDataUrl(data) {
|
|
101
329
|
const match = data.match(/^data:.*?;base64,(.+)$/);
|
|
102
|
-
if (match?.[1])
|
|
103
|
-
|
|
330
|
+
if (!match?.[1]) return null;
|
|
331
|
+
try {
|
|
332
|
+
return Buffer.from(match[1], "base64");
|
|
333
|
+
} catch {
|
|
334
|
+
return null;
|
|
104
335
|
}
|
|
105
|
-
return data;
|
|
106
336
|
}
|
|
337
|
+
|
|
338
|
+
// src/server/llm/resolve-attachments.ts
|
|
339
|
+
var pdfBlockCache = /* @__PURE__ */ new Map();
|
|
107
340
|
async function extractPdfFromDataUrl(data, filename) {
|
|
108
341
|
const base64Match = data.match(/^data:.*?;base64,(.+)$/);
|
|
109
342
|
if (base64Match?.[1]) {
|
|
@@ -118,14 +351,50 @@ ${result.text}`;
|
|
|
118
351
|
}
|
|
119
352
|
return `[PDF: ${filename}] (could not extract text)`;
|
|
120
353
|
}
|
|
354
|
+
async function extractPdfBlocksFromDataUrl(data, filename) {
|
|
355
|
+
const cacheKey = contentHash(data);
|
|
356
|
+
const cached = pdfBlockCache.get(cacheKey);
|
|
357
|
+
if (cached) {
|
|
358
|
+
const result = [{ type: "text", text: `[PDF: ${filename}]` }];
|
|
359
|
+
result.push(...cached);
|
|
360
|
+
return result;
|
|
361
|
+
}
|
|
362
|
+
const base64Match = data.match(/^data:.*?;base64,(.+)$/);
|
|
363
|
+
if (!base64Match?.[1]) {
|
|
364
|
+
cacheSet(pdfBlockCache, cacheKey, []);
|
|
365
|
+
return [{ type: "text", text: `[PDF: ${filename}] (could not extract content)` }];
|
|
366
|
+
}
|
|
367
|
+
try {
|
|
368
|
+
const buffer = Buffer.from(base64Match[1], "base64");
|
|
369
|
+
const result = await extractPdfContent(buffer);
|
|
370
|
+
const body = [];
|
|
371
|
+
for (const block of result.blocks) {
|
|
372
|
+
if (block.type === "text" && block.content) {
|
|
373
|
+
body.push({ type: "text", text: block.content });
|
|
374
|
+
} else if (block.type === "image" && block.dataUrl) {
|
|
375
|
+
body.push({ type: "image_url", image_url: { url: block.dataUrl } });
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
cacheSet(pdfBlockCache, cacheKey, body);
|
|
379
|
+
const parts = [{ type: "text", text: `[PDF: ${filename}]` }];
|
|
380
|
+
parts.push(...body);
|
|
381
|
+
return parts;
|
|
382
|
+
} catch {
|
|
383
|
+
cacheSet(pdfBlockCache, cacheKey, []);
|
|
384
|
+
return [{ type: "text", text: `[PDF: ${filename}] (could not extract content)` }];
|
|
385
|
+
}
|
|
386
|
+
}
|
|
121
387
|
async function resolveAttachmentToText(attachment, supportsVision) {
|
|
122
388
|
const mimeType = attachment.mimeType;
|
|
123
389
|
if (TEXT_MIME_EXACT.includes(mimeType) || TEXT_MIME_PREFIXES.some((p) => mimeType.startsWith(p))) {
|
|
124
|
-
const text =
|
|
390
|
+
const text = decodeDataUrl(attachment.data)?.toString("utf8") ?? attachment.data;
|
|
125
391
|
return `[File: ${attachment.filename || "file"}]
|
|
126
392
|
${text}`;
|
|
127
393
|
}
|
|
128
394
|
if (mimeType === "application/pdf") {
|
|
395
|
+
if (attachment.pdfContent) {
|
|
396
|
+
return attachment.pdfContent;
|
|
397
|
+
}
|
|
129
398
|
return extractPdfFromDataUrl(attachment.data, attachment.filename || "document.pdf");
|
|
130
399
|
}
|
|
131
400
|
if (supportsVision) {
|
|
@@ -136,6 +405,7 @@ ${text}`;
|
|
|
136
405
|
}
|
|
137
406
|
return `[Image: ${attachment.filename || "image"}] (vision not supported, cannot describe)`;
|
|
138
407
|
}
|
|
408
|
+
var isVisionAttachment = (attachment) => attachment.mimeType.startsWith("image/") || attachment.mimeType === "application/pdf";
|
|
139
409
|
async function resolveAttachmentsInMessages(messages, supportsVision) {
|
|
140
410
|
const result = [];
|
|
141
411
|
for (const msg of messages) {
|
|
@@ -143,7 +413,7 @@ async function resolveAttachmentsInMessages(messages, supportsVision) {
|
|
|
143
413
|
result.push(msg);
|
|
144
414
|
continue;
|
|
145
415
|
}
|
|
146
|
-
if (supportsVision && msg.attachments.every(
|
|
416
|
+
if (supportsVision && msg.attachments.every(isVisionAttachment)) {
|
|
147
417
|
result.push(msg);
|
|
148
418
|
continue;
|
|
149
419
|
}
|
|
@@ -151,7 +421,7 @@ async function resolveAttachmentsInMessages(messages, supportsVision) {
|
|
|
151
421
|
if (msg.content?.trim()) parts.push(msg.content);
|
|
152
422
|
const remainingImageAttachments = [];
|
|
153
423
|
for (const attachment of msg.attachments) {
|
|
154
|
-
if (supportsVision && attachment
|
|
424
|
+
if (supportsVision && isVisionAttachment(attachment)) {
|
|
155
425
|
remainingImageAttachments.push(attachment);
|
|
156
426
|
continue;
|
|
157
427
|
}
|
|
@@ -197,8 +467,8 @@ async function buildAttachmentContent(msgContent, attachments, modelSupportsVisi
|
|
|
197
467
|
content.push({ type: "text", text: msgContent });
|
|
198
468
|
}
|
|
199
469
|
for (const attachment of attachments) {
|
|
200
|
-
const
|
|
201
|
-
content.push(
|
|
470
|
+
const parts = await convertAttachment(attachment, modelSupportsVision);
|
|
471
|
+
content.push(...parts);
|
|
202
472
|
}
|
|
203
473
|
return content;
|
|
204
474
|
}
|
|
@@ -222,7 +492,7 @@ function getThinking(msg, override) {
|
|
|
222
492
|
function buildAssistantMessage(msg, thinkingField) {
|
|
223
493
|
const result = {
|
|
224
494
|
role: "assistant",
|
|
225
|
-
content: msg.content || null
|
|
495
|
+
content: msg.toolCalls?.length ? msg.content || "" : msg.content || null
|
|
226
496
|
};
|
|
227
497
|
if (msg.toolCalls?.length) {
|
|
228
498
|
result["tool_calls"] = convertToolCalls(msg.toolCalls);
|
|
@@ -235,32 +505,28 @@ function buildAssistantMessage(msg, thinkingField) {
|
|
|
235
505
|
async function convertAttachment(attachment, modelSupportsVision) {
|
|
236
506
|
const mimeType = attachment.mimeType;
|
|
237
507
|
if (TEXT_MIME_EXACT.includes(mimeType) || TEXT_MIME_PREFIXES.some((p) => mimeType.startsWith(p))) {
|
|
238
|
-
return {
|
|
239
|
-
|
|
240
|
-
text: `[File: ${attachment.filename || "file"}]
|
|
241
|
-
${attachment.data}`
|
|
242
|
-
};
|
|
508
|
+
return [{ type: "text", text: `[File: ${attachment.filename || "file"}]
|
|
509
|
+
${attachment.data}` }];
|
|
243
510
|
}
|
|
244
511
|
if (mimeType === "application/pdf") {
|
|
512
|
+
if (modelSupportsVision) {
|
|
513
|
+
return extractPdfBlocksFromDataUrl(attachment.data, attachment.filename || "document.pdf");
|
|
514
|
+
}
|
|
515
|
+
if (attachment.pdfContent) {
|
|
516
|
+
return [{ type: "text", text: attachment.pdfContent }];
|
|
517
|
+
}
|
|
245
518
|
const text = await extractPdfFromDataUrl(attachment.data, attachment.filename || "document.pdf");
|
|
246
|
-
return { type: "text", text };
|
|
519
|
+
return [{ type: "text", text }];
|
|
247
520
|
}
|
|
248
521
|
if (modelSupportsVision) {
|
|
249
|
-
return {
|
|
250
|
-
type: "image_url",
|
|
251
|
-
image_url: { url: attachment.data }
|
|
252
|
-
};
|
|
522
|
+
return [{ type: "image_url", image_url: { url: attachment.data } }];
|
|
253
523
|
}
|
|
254
524
|
if (attachment.description) {
|
|
255
|
-
return
|
|
256
|
-
type: "text",
|
|
257
|
-
|
|
258
|
-
};
|
|
525
|
+
return [
|
|
526
|
+
{ type: "text", text: `[Image: ${attachment.filename || "image"} - description: ${attachment.description}]` }
|
|
527
|
+
];
|
|
259
528
|
}
|
|
260
|
-
return {
|
|
261
|
-
type: "text",
|
|
262
|
-
text: `[Image: ${attachment.filename || "image"}] (vision not supported, cannot describe)`
|
|
263
|
-
};
|
|
529
|
+
return [{ type: "text", text: `[Image: ${attachment.filename || "image"}] (vision not supported, cannot describe)` }];
|
|
264
530
|
}
|
|
265
531
|
async function convertMessages(messages, modelSupportsVision, thinkingField) {
|
|
266
532
|
const filtered = messages.filter((msg) => {
|
|
@@ -398,7 +664,11 @@ export {
|
|
|
398
664
|
isPdfBuffer,
|
|
399
665
|
formatPdfErrorMessage,
|
|
400
666
|
processPdfContent,
|
|
667
|
+
extractPdfContent,
|
|
401
668
|
extractPdfText,
|
|
669
|
+
contentHash,
|
|
670
|
+
cacheSet,
|
|
671
|
+
decodeDataUrl,
|
|
402
672
|
resolveAttachmentsInMessages,
|
|
403
673
|
parseToolArguments,
|
|
404
674
|
buildModelParams,
|
|
@@ -409,4 +679,4 @@ export {
|
|
|
409
679
|
buildStreamingCreateParams,
|
|
410
680
|
mapFinishReason
|
|
411
681
|
};
|
|
412
|
-
//# sourceMappingURL=chunk-
|
|
682
|
+
//# sourceMappingURL=chunk-RMXPVQP4.js.map
|