@phi-code-admin/browser 1.0.3 → 1.0.4
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/index.d.ts +0 -6
- package/dist/index.js +65 -13
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -108,12 +108,6 @@ export declare function screenshot(options: {
|
|
|
108
108
|
tabId: string;
|
|
109
109
|
userId?: string;
|
|
110
110
|
fullPage?: boolean;
|
|
111
|
-
clip?: {
|
|
112
|
-
x: number;
|
|
113
|
-
y: number;
|
|
114
|
-
width: number;
|
|
115
|
-
height: number;
|
|
116
|
-
};
|
|
117
111
|
}): Promise<ScreenshotResult>;
|
|
118
112
|
/**
|
|
119
113
|
* High-level search macro: opens a new tab, navigates to a search engine
|
package/dist/index.js
CHANGED
|
@@ -222,6 +222,27 @@ async function request(pathname, options = {}) {
|
|
|
222
222
|
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
223
223
|
signal: controller.signal,
|
|
224
224
|
});
|
|
225
|
+
if (options.responseType === "binary") {
|
|
226
|
+
if (!res.ok) {
|
|
227
|
+
// Even on error, the body is JSON — fall through to error parsing.
|
|
228
|
+
const text = await res.text();
|
|
229
|
+
let parsed = undefined;
|
|
230
|
+
if (text) {
|
|
231
|
+
try {
|
|
232
|
+
parsed = JSON.parse(text);
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
parsed = text;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const message = typeof parsed === "object" && parsed && "error" in parsed
|
|
239
|
+
? String(parsed.error)
|
|
240
|
+
: `HTTP ${res.status}`;
|
|
241
|
+
throw new Error(`${method} ${pathname} → ${message}`);
|
|
242
|
+
}
|
|
243
|
+
const buffer = new Uint8Array(await res.arrayBuffer());
|
|
244
|
+
return buffer;
|
|
245
|
+
}
|
|
225
246
|
const text = await res.text();
|
|
226
247
|
let parsed = undefined;
|
|
227
248
|
if (text) {
|
|
@@ -332,29 +353,60 @@ export async function extract(options) {
|
|
|
332
353
|
sessionKey: options.sessionKey,
|
|
333
354
|
});
|
|
334
355
|
}
|
|
335
|
-
|
|
356
|
+
// The camofox-browser POST /tabs/:tabId/extract endpoint is a
|
|
357
|
+
// *deterministic* extractor that requires a structured `schema` of
|
|
358
|
+
// refs from a prior snapshot — it's not a Readability extractor. Phi
|
|
359
|
+
// callers expect a plain `{title, content, textContent}` blob, so we
|
|
360
|
+
// achieve that via /evaluate, running a small Readability-style script
|
|
361
|
+
// inside the page. This keeps the public API stable regardless of how
|
|
362
|
+
// the camofox-browser server evolves.
|
|
363
|
+
const mode = options.mode ?? "readability";
|
|
364
|
+
const expression = `(() => {
|
|
365
|
+
const limit = 50000;
|
|
366
|
+
const title = document.title || "";
|
|
367
|
+
const url = window.location.href || "";
|
|
368
|
+
if (${JSON.stringify(mode)} === "html") {
|
|
369
|
+
return { title, url, content: document.documentElement.outerHTML.slice(0, limit) };
|
|
370
|
+
}
|
|
371
|
+
if (${JSON.stringify(mode)} === "text") {
|
|
372
|
+
return { title, url, textContent: (document.body && document.body.innerText || "").slice(0, limit) };
|
|
373
|
+
}
|
|
374
|
+
// readability-light: strip nav/footer/header/aside, keep <main>/<article>/body.
|
|
375
|
+
const clone = document.cloneNode(true);
|
|
376
|
+
clone.querySelectorAll("script,style,noscript,iframe,nav,footer,header,aside,svg,form").forEach((el) => el.remove());
|
|
377
|
+
const root = clone.querySelector("main") || clone.querySelector("article") || clone.body || clone;
|
|
378
|
+
const text = (root.innerText || root.textContent || "").replace(/\\n{3,}/g, "\\n\\n").trim();
|
|
379
|
+
const excerpt = text.slice(0, 240);
|
|
380
|
+
return {
|
|
381
|
+
title,
|
|
382
|
+
url,
|
|
383
|
+
content: root.innerHTML ? root.innerHTML.slice(0, limit) : undefined,
|
|
384
|
+
textContent: text.slice(0, limit),
|
|
385
|
+
excerpt,
|
|
386
|
+
length: text.length,
|
|
387
|
+
};
|
|
388
|
+
})()`;
|
|
389
|
+
const evalRes = await request(`/tabs/${encodeURIComponent(tabId)}/evaluate`, {
|
|
336
390
|
method: "POST",
|
|
337
|
-
body: {
|
|
338
|
-
userId: options.userId ?? DEFAULT_USER_ID,
|
|
339
|
-
sessionKey: options.sessionKey ?? DEFAULT_SESSION_KEY,
|
|
340
|
-
mode: options.mode ?? "readability",
|
|
341
|
-
},
|
|
391
|
+
body: { userId: options.userId ?? DEFAULT_USER_ID, expression },
|
|
342
392
|
});
|
|
343
|
-
return
|
|
393
|
+
return evalRes.result ?? {};
|
|
344
394
|
}
|
|
345
395
|
/** Capture a screenshot of the given tab as a base64-encoded PNG. */
|
|
346
396
|
export async function screenshot(options) {
|
|
347
397
|
const query = new URLSearchParams();
|
|
348
398
|
query.set("userId", options.userId ?? DEFAULT_USER_ID);
|
|
399
|
+
// The server expects `fullPage=true` (string match), not `=1`.
|
|
349
400
|
if (options.fullPage)
|
|
350
|
-
query.set("fullPage", "
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
401
|
+
query.set("fullPage", "true");
|
|
402
|
+
// The camofox-browser screenshot endpoint streams a raw `image/png`
|
|
403
|
+
// body, not a JSON envelope. Pull it as a Uint8Array and base64-encode
|
|
404
|
+
// here so the result is JSON-safe for the tool result channel.
|
|
405
|
+
const bytes = await request(`/tabs/${encodeURIComponent(options.tabId)}/screenshot?${query.toString()}`, { responseType: "binary" });
|
|
354
406
|
return {
|
|
355
407
|
tabId: options.tabId,
|
|
356
|
-
mimeType:
|
|
357
|
-
bytesBase64:
|
|
408
|
+
mimeType: "image/png",
|
|
409
|
+
bytesBase64: Buffer.from(bytes).toString("base64"),
|
|
358
410
|
};
|
|
359
411
|
}
|
|
360
412
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phi-code-admin/browser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Phi-code browser automation API: lazy-start the bundled Camoufox + camofox-browser server and expose 10 high-level tools (navigate, extract, screenshot, click, type, scroll, snapshot, search, close_tab, list_tabs) as plain ES module functions. Zero external dependencies at runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|