@arnilo/prism-tools 0.0.0-stage → 0.13.1
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/CHANGELOG.md +418 -0
- package/LICENSE +21 -0
- package/README.md +44 -2
- package/browser/README.md +98 -0
- package/browser/skills/browser-automation/SKILL.md +14 -0
- package/browser/src/actions/cdp.ts +131 -0
- package/browser/src/actions/dialog.ts +25 -0
- package/browser/src/actions/download_release.ts +22 -0
- package/browser/src/actions/index.ts +37 -0
- package/browser/src/actions/locator.ts +70 -0
- package/browser/src/actions/navigate.ts +23 -0
- package/browser/src/actions/screenshot.ts +27 -0
- package/browser/src/actions/scroll.ts +24 -0
- package/browser/src/actions/select_page.ts +21 -0
- package/browser/src/actions/types.ts +114 -0
- package/browser/src/actions/upload.ts +44 -0
- package/browser/src/actions/wait.ts +45 -0
- package/browser/src/browser.ts +197 -0
- package/browser/src/cdp.ts +178 -0
- package/browser/src/checkpoint.ts +189 -0
- package/browser/src/config.ts +72 -0
- package/browser/src/downloads.ts +253 -0
- package/browser/src/errors.ts +31 -0
- package/browser/src/evaluate.ts +101 -0
- package/browser/src/index.ts +129 -0
- package/browser/src/limits.ts +271 -0
- package/browser/src/manager.ts +730 -0
- package/browser/src/network.ts +263 -0
- package/browser/src/observe.ts +178 -0
- package/browser/src/policy.ts +133 -0
- package/browser/src/screenshot.ts +107 -0
- package/browser/src/shared-sandbox.ts +53 -0
- package/browser/src/snapshot.ts +99 -0
- package/browser/src/targets.ts +165 -0
- package/browser/src/tools.ts +480 -0
- package/browser/src/types.ts +316 -0
- package/browser/src/uploads.ts +174 -0
- package/coding/README.md +337 -0
- package/coding/src/agent/acp-operations.ts +146 -0
- package/coding/src/agent/artifacts.ts +38 -0
- package/coding/src/agent/ask-user-decision.ts +623 -0
- package/coding/src/agent/atomic-write.ts +23 -0
- package/coding/src/agent/bounded-file.ts +21 -0
- package/coding/src/agent/checks.ts +301 -0
- package/coding/src/agent/coding-checkpoint.ts +828 -0
- package/coding/src/agent/delete.ts +237 -0
- package/coding/src/agent/diagnostics-tool.ts +163 -0
- package/coding/src/agent/diagnostics.ts +262 -0
- package/coding/src/agent/edit-diff.ts +382 -0
- package/coding/src/agent/edit.ts +322 -0
- package/coding/src/agent/effects.ts +102 -0
- package/coding/src/agent/env.ts +28 -0
- package/coding/src/agent/execution-policy.ts +89 -0
- package/coding/src/agent/file-mutation-queue.ts +72 -0
- package/coding/src/agent/forge/github.ts +603 -0
- package/coding/src/agent/forge/index.ts +16 -0
- package/coding/src/agent/forge/types.ts +185 -0
- package/coding/src/agent/git-aware-repository.ts +305 -0
- package/coding/src/agent/git-exec.ts +326 -0
- package/coding/src/agent/git-status.ts +177 -0
- package/coding/src/agent/git-tools.ts +360 -0
- package/coding/src/agent/git.ts +1074 -0
- package/coding/src/agent/glob-match.ts +146 -0
- package/coding/src/agent/glob.ts +182 -0
- package/coding/src/agent/goal-verify.ts +385 -0
- package/coding/src/agent/grep-worker.ts +74 -0
- package/coding/src/agent/grep.ts +459 -0
- package/coding/src/agent/identity.ts +48 -0
- package/coding/src/agent/index.ts +676 -0
- package/coding/src/agent/language/client.ts +429 -0
- package/coding/src/agent/language/framing.ts +190 -0
- package/coding/src/agent/language/index.ts +23 -0
- package/coding/src/agent/language/intelligence.ts +682 -0
- package/coding/src/agent/language/types.ts +187 -0
- package/coding/src/agent/lifecycle-events.ts +121 -0
- package/coding/src/agent/lifecycle.ts +166 -0
- package/coding/src/agent/limits.ts +214 -0
- package/coding/src/agent/move.ts +178 -0
- package/coding/src/agent/mutation-path.ts +52 -0
- package/coding/src/agent/output-accumulator.ts +304 -0
- package/coding/src/agent/path-utils.ts +120 -0
- package/coding/src/agent/process/index.ts +58 -0
- package/coding/src/agent/process/recovery.ts +508 -0
- package/coding/src/agent/process/sessions-host.ts +222 -0
- package/coding/src/agent/process/sessions-monitor.ts +141 -0
- package/coding/src/agent/process/sessions-recovery.ts +241 -0
- package/coding/src/agent/process/sessions-spawn.ts +613 -0
- package/coding/src/agent/process/sessions-teardown.ts +281 -0
- package/coding/src/agent/process/sessions.ts +30 -0
- package/coding/src/agent/process/types.ts +358 -0
- package/coding/src/agent/read-path-set.ts +126 -0
- package/coding/src/agent/read.ts +713 -0
- package/coding/src/agent/repository/glob.ts +147 -0
- package/coding/src/agent/repository/indexed-search.ts +480 -0
- package/coding/src/agent/repository/list.ts +131 -0
- package/coding/src/agent/repository/operations.ts +22 -0
- package/coding/src/agent/repository/path.ts +98 -0
- package/coding/src/agent/repository/search.ts +277 -0
- package/coding/src/agent/repository/types.ts +243 -0
- package/coding/src/agent/repository/walk.ts +126 -0
- package/coding/src/agent/repository.ts +12 -0
- package/coding/src/agent/shell-background.ts +264 -0
- package/coding/src/agent/shell-output.ts +75 -0
- package/coding/src/agent/shell.ts +526 -0
- package/coding/src/agent/todo.ts +399 -0
- package/coding/src/agent/truncate.ts +251 -0
- package/coding/src/agent/write.ts +170 -0
- package/coding/src/ask-user-host.ts +48 -0
- package/coding/src/config.ts +278 -0
- package/coding/src/hard-deny.ts +97 -0
- package/coding/src/index.ts +318 -0
- package/coding/src/principal-guard.ts +84 -0
- package/coding/src/sandbox.ts +92 -0
- package/coding/src/security/approval.ts +144 -0
- package/coding/src/security/command-rules.ts +57 -0
- package/coding/src/security/docker-cli.ts +222 -0
- package/coding/src/security/docker-sandbox.ts +1205 -0
- package/coding/src/security/e2b-sandbox.ts +853 -0
- package/coding/src/security/egress/dns-pin.ts +88 -0
- package/coding/src/security/egress/index.ts +32 -0
- package/coding/src/security/egress/limits.ts +57 -0
- package/coding/src/security/egress/policy.ts +137 -0
- package/coding/src/security/egress/proxy.ts +599 -0
- package/coding/src/security/egress/types.ts +30 -0
- package/coding/src/security/index.ts +199 -0
- package/coding/src/security/native-sandbox.ts +639 -0
- package/coding/src/security/path-containment.ts +55 -0
- package/coding/src/security/sandbox-coding-operations.ts +392 -0
- package/coding/src/security/sandbox-fs-operations.ts +644 -0
- package/coding/src/security/sandbox-limits.ts +147 -0
- package/coding/src/security/sandbox-tar.ts +308 -0
- package/coding/src/security/sandbox.ts +179 -0
- package/coding/src/security/semaphore.ts +55 -0
- package/coding/src/targets.ts +110 -0
- package/coding/src/workspace/diff.ts +78 -0
- package/coding/src/workspace/errors.ts +29 -0
- package/coding/src/workspace/index.ts +5 -0
- package/coding/src/workspace/merge.ts +151 -0
- package/coding/src/workspace/routing.ts +76 -0
- package/coding/src/workspace/service.ts +584 -0
- package/computer-use/README.md +105 -0
- package/computer-use/skills/computer-use-linux/SKILL.md +17 -0
- package/computer-use/src/classify.ts +53 -0
- package/computer-use/src/command.ts +29 -0
- package/computer-use/src/config.ts +69 -0
- package/computer-use/src/create.ts +170 -0
- package/computer-use/src/devices.ts +247 -0
- package/computer-use/src/env.ts +28 -0
- package/computer-use/src/index.ts +157 -0
- package/computer-use/src/skill.ts +21 -0
- package/openapi/README.md +97 -0
- package/openapi/src/compile.ts +386 -0
- package/openapi/src/config.ts +73 -0
- package/openapi/src/errors.ts +16 -0
- package/openapi/src/execute.ts +241 -0
- package/openapi/src/index.ts +124 -0
- package/openapi/src/limits.ts +63 -0
- package/openapi/src/specs.ts +86 -0
- package/openapi/src/tools.ts +60 -0
- package/openapi/src/types.ts +61 -0
- package/package.json +171 -4
- package/research/README.md +72 -0
- package/research/skills/research-report/SKILL.md +12 -0
- package/research/skills/research-scoping/SKILL.md +10 -0
- package/research/skills/research-synthesis/SKILL.md +10 -0
- package/research/skills/source-triage/SKILL.md +10 -0
- package/research/src/check.ts +71 -0
- package/research/src/config.ts +41 -0
- package/research/src/index.ts +147 -0
- package/research/src/ledger.ts +157 -0
- package/research/src/urls.ts +21 -0
- package/web/README.md +287 -0
- package/web/skills/web-research/SKILL.md +17 -0
- package/web/src/brave.ts +66 -0
- package/web/src/cache.ts +151 -0
- package/web/src/config.ts +210 -0
- package/web/src/controls.ts +128 -0
- package/web/src/domains.ts +114 -0
- package/web/src/exa.ts +60 -0
- package/web/src/firecrawl.ts +160 -0
- package/web/src/index.ts +387 -0
- package/web/src/kagi.ts +73 -0
- package/web/src/limits.ts +55 -0
- package/web/src/native/convert.ts +84 -0
- package/web/src/native/fetch.ts +226 -0
- package/web/src/normalize.ts +131 -0
- package/web/src/obscura/cdp.ts +201 -0
- package/web/src/obscura/classify.ts +35 -0
- package/web/src/obscura/cli.ts +254 -0
- package/web/src/obscura/errors.ts +8 -0
- package/web/src/obscura/index.ts +59 -0
- package/web/src/obscura/limits.ts +92 -0
- package/web/src/obscura/mcp.ts +104 -0
- package/web/src/obscura/playwright.ts +133 -0
- package/web/src/obscura/process.ts +235 -0
- package/web/src/obscura/search-profile.ts +73 -0
- package/web/src/obscura/types.ts +48 -0
- package/web/src/obscura/web.ts +419 -0
- package/web/src/prompt-fetch.ts +88 -0
- package/web/src/render.ts +75 -0
- package/web/src/searxng.ts +78 -0
- package/web/src/slice.ts +139 -0
- package/web/src/tavily.ts +77 -0
- package/web/src/tools.ts +182 -0
- package/web/src/transport.ts +310 -0
- package/web/src/types.ts +199 -0
- package/work/README.md +77 -0
- package/work/THIRD_PARTY_NOTICES +24 -0
- package/work/docling/ocr.py +150 -0
- package/work/sandbox/Dockerfile +36 -0
- package/work/sandbox/soffice.sh +12 -0
- package/work/src/command.ts +29 -0
- package/work/src/config.ts +138 -0
- package/work/src/connectors/cli.ts +343 -0
- package/work/src/connectors/drafts.ts +647 -0
- package/work/src/connectors/errors.ts +8 -0
- package/work/src/connectors/file-bytes.ts +149 -0
- package/work/src/connectors/google-workspace-http.ts +399 -0
- package/work/src/connectors/google-workspace.ts +465 -0
- package/work/src/connectors/gws-tools.ts +378 -0
- package/work/src/connectors/http.ts +123 -0
- package/work/src/connectors/idempotency.ts +235 -0
- package/work/src/connectors/identity.ts +34 -0
- package/work/src/connectors/index.ts +93 -0
- package/work/src/connectors/limits.ts +50 -0
- package/work/src/connectors/m365-tools.ts +308 -0
- package/work/src/connectors/microsoft365-http.ts +428 -0
- package/work/src/connectors/microsoft365.ts +402 -0
- package/work/src/connectors/normalize.ts +120 -0
- package/work/src/connectors/tool-helpers.ts +398 -0
- package/work/src/connectors/tools.ts +42 -0
- package/work/src/connectors/types.ts +399 -0
- package/work/src/diagrams/canonicalize.ts +166 -0
- package/work/src/diagrams/caps.ts +52 -0
- package/work/src/diagrams/embed.ts +340 -0
- package/work/src/diagrams/errors.ts +76 -0
- package/work/src/diagrams/index.ts +81 -0
- package/work/src/diagrams/messages.ts +150 -0
- package/work/src/diagrams/origin.ts +52 -0
- package/work/src/diagrams/telemetry.ts +34 -0
- package/work/src/diagrams/xml.ts +267 -0
- package/work/src/document-extraction/index.ts +563 -0
- package/work/src/document-reader/ambient.d.ts +2 -0
- package/work/src/document-reader/errors.ts +7 -0
- package/work/src/document-reader/index.ts +181 -0
- package/work/src/document-reader/mistral-ocr.ts +318 -0
- package/work/src/document-reader/parsers.ts +171 -0
- package/work/src/documents/caps.ts +135 -0
- package/work/src/documents/diff.ts +269 -0
- package/work/src/documents/errors.ts +44 -0
- package/work/src/documents/fidelity.ts +98 -0
- package/work/src/documents/generate.ts +96 -0
- package/work/src/documents/hash.ts +8 -0
- package/work/src/documents/index.ts +14 -0
- package/work/src/documents/model-schema.ts +639 -0
- package/work/src/documents/parse.ts +252 -0
- package/work/src/documents/patch-history.ts +81 -0
- package/work/src/documents/patch.ts +476 -0
- package/work/src/documents/preview-html.ts +245 -0
- package/work/src/documents/preview.ts +227 -0
- package/work/src/documents/telemetry.ts +36 -0
- package/work/src/documents/translate/docx.ts +292 -0
- package/work/src/documents/translate/pptx.ts +304 -0
- package/work/src/documents/translate/xlsx.ts +137 -0
- package/work/src/documents/types.ts +188 -0
- package/work/src/index.ts +311 -0
- package/work/src/runtime/worker-pool-entry.ts +102 -0
- package/work/src/runtime/worker-pool.ts +267 -0
- package/work/src/sandbox/composition.ts +212 -0
- package/work/src/sandbox/image.ts +6 -0
- package/work/src/sandbox/index.ts +14 -0
- package/work/src/sheets/caps.ts +68 -0
- package/work/src/sheets/csv.ts +351 -0
- package/work/src/sheets/decimal.ts +388 -0
- package/work/src/sheets/errors.ts +52 -0
- package/work/src/sheets/index.ts +62 -0
- package/work/src/sheets/inference.ts +378 -0
- package/work/src/sheets/telemetry.ts +34 -0
- package/work/src/sheets/types.ts +162 -0
- package/work/src/sheets/xlsx.ts +388 -0
- package/work/src/skills/index.ts +8 -0
- package/work/src/skills/load.ts +43 -0
- package/work/src/tools/filesystem.ts +21 -0
- package/work/src/tools/index.ts +1 -0
- package/work/src/tools/local-filesystem.ts +33 -0
- package/work/src/tools/office.ts +311 -0
- package/work/vendor/hermes-agent/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/SKILL.md +196 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_comments.py +289 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_common.py +94 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_create.py +177 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_edit.py +250 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_read.py +149 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_revisions.py +147 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_template.py +70 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_validate.py +156 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/SKILL.md +125 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/_raster.py +76 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_marker.py +88 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_pymupdf.py +101 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_create.py +130 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_fill_form.py +97 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_form_layout.py +168 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_make_form.py +145 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_merge.py +50 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_meta.py +115 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_page_image.py +99 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_read.py +153 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_secure.py +71 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_split.py +84 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_stamp.py +143 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_watermark.py +51 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/SKILL.md +220 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_create.py +214 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_edit.py +436 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_from_template.py +88 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_read.py +131 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_render.py +93 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/SKILL.md +196 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/csv_to_xlsx.py +104 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_create.py +259 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_edit.py +263 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_read.py +160 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_recalc.py +111 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_restructure.py +337 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_to_csv.py +69 -0
- package/work/vendor-lock.json +11 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded download quarantine: stream → hash/MIME/name metadata → host release approval.
|
|
3
|
+
*/
|
|
4
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
5
|
+
import { createWriteStream } from "node:fs";
|
|
6
|
+
import { mkdir, open, rm } from "node:fs/promises";
|
|
7
|
+
import { basename, isAbsolute, join, resolve } from "node:path";
|
|
8
|
+
import { Readable } from "node:stream";
|
|
9
|
+
import { pipeline } from "node:stream/promises";
|
|
10
|
+
import { BrowserError } from "./errors.js";
|
|
11
|
+
import type { ResolvedBrowserLimits } from "./limits.js";
|
|
12
|
+
import type { PlaywrightDownload } from "./types.js";
|
|
13
|
+
|
|
14
|
+
export interface BrowserDownloadOptions {
|
|
15
|
+
/** Absolute quarantine directory (typically sandbox `/downloads` on host or bind). */
|
|
16
|
+
readonly quarantine: string;
|
|
17
|
+
/** Per-file byte cap override (still clamped by resolved limits). */
|
|
18
|
+
readonly maxBytes?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Host callback required before a quarantined download may leave quarantine.
|
|
21
|
+
* Returning false keeps the file quarantined (and eligible for cleanup).
|
|
22
|
+
*/
|
|
23
|
+
readonly approveRelease?: (meta: DownloadMetadata) => boolean | Promise<boolean>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface DownloadMetadata {
|
|
27
|
+
readonly downloadId: string;
|
|
28
|
+
readonly suggestedName: string;
|
|
29
|
+
readonly safeName: string;
|
|
30
|
+
readonly quarantinePath: string;
|
|
31
|
+
readonly url: string;
|
|
32
|
+
readonly bytes: number;
|
|
33
|
+
readonly sha256: string;
|
|
34
|
+
readonly mimeType?: string;
|
|
35
|
+
readonly released: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface DownloadBudget {
|
|
39
|
+
count: number;
|
|
40
|
+
aggregateBytes: number;
|
|
41
|
+
readonly items: Map<string, DownloadMetadata & { released: boolean }>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function createDownloadBudget(): DownloadBudget {
|
|
45
|
+
return { count: 0, aggregateBytes: 0, items: new Map() };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function sanitizeDownloadName(name: string): string {
|
|
49
|
+
const base = basename(name || "download.bin").replace(/[\u0000-\u001f\u007f]/g, "");
|
|
50
|
+
const cleaned = base.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^\.+/, "");
|
|
51
|
+
const trimmed = cleaned.slice(0, 128) || "download.bin";
|
|
52
|
+
if (trimmed === "." || trimmed === ".." || trimmed.includes("/") || trimmed.includes("\\")) {
|
|
53
|
+
return "download.bin";
|
|
54
|
+
}
|
|
55
|
+
return trimmed;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function sniffMime(buf: Buffer): string | undefined {
|
|
59
|
+
if (buf.length >= 8 && buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) {
|
|
60
|
+
return "image/png";
|
|
61
|
+
}
|
|
62
|
+
if (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) return "image/jpeg";
|
|
63
|
+
if (buf.length >= 4 && buf.toString("ascii", 0, 4) === "%PDF") return "application/pdf";
|
|
64
|
+
if (buf.length >= 2 && buf[0] === 0x50 && buf[1] === 0x4b) return "application/zip";
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function* boundedChunks(
|
|
69
|
+
source: AsyncIterable<unknown>,
|
|
70
|
+
caps: {
|
|
71
|
+
perFileCap: number;
|
|
72
|
+
aggregateCap: number;
|
|
73
|
+
aggregateUsed: number;
|
|
74
|
+
signal?: AbortSignal;
|
|
75
|
+
onHead: (buf: Buffer) => void;
|
|
76
|
+
onBytes: (n: number) => void;
|
|
77
|
+
hash: { update(buf: Buffer): void };
|
|
78
|
+
},
|
|
79
|
+
): AsyncGenerator<Buffer> {
|
|
80
|
+
let bytes = 0;
|
|
81
|
+
let sawHead = false;
|
|
82
|
+
for await (const chunk of source) {
|
|
83
|
+
if (caps.signal?.aborted) {
|
|
84
|
+
throw new BrowserError("ERR_PRISM_BROWSER", "download aborted");
|
|
85
|
+
}
|
|
86
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : chunk instanceof Uint8Array ? Buffer.from(chunk) : Buffer.from(String(chunk));
|
|
87
|
+
bytes += buf.byteLength;
|
|
88
|
+
if (bytes > caps.perFileCap) {
|
|
89
|
+
throw new BrowserError("ERR_PRISM_BROWSER_LIMIT", `download exceeds maxDownloadBytes ${caps.perFileCap}`);
|
|
90
|
+
}
|
|
91
|
+
if (caps.aggregateUsed + bytes > caps.aggregateCap) {
|
|
92
|
+
throw new BrowserError("ERR_PRISM_BROWSER_LIMIT", `download exceeds maxDownloadAggregateBytes ${caps.aggregateCap}`);
|
|
93
|
+
}
|
|
94
|
+
if (!sawHead) {
|
|
95
|
+
caps.onHead(buf.subarray(0, Math.min(buf.byteLength, 16)));
|
|
96
|
+
sawHead = true;
|
|
97
|
+
}
|
|
98
|
+
caps.hash.update(buf);
|
|
99
|
+
caps.onBytes(bytes);
|
|
100
|
+
yield buf;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function quarantineDownload(
|
|
105
|
+
download: PlaywrightDownload,
|
|
106
|
+
options: BrowserDownloadOptions,
|
|
107
|
+
limits: ResolvedBrowserLimits,
|
|
108
|
+
budget: DownloadBudget,
|
|
109
|
+
signal?: AbortSignal,
|
|
110
|
+
): Promise<DownloadMetadata> {
|
|
111
|
+
if (!isAbsolute(options.quarantine)) {
|
|
112
|
+
throw new BrowserError("ERR_PRISM_BROWSER_INPUT", "downloads.quarantine must be absolute");
|
|
113
|
+
}
|
|
114
|
+
if (budget.count >= limits.maxDownloads) {
|
|
115
|
+
try {
|
|
116
|
+
await download.cancel?.();
|
|
117
|
+
} catch {
|
|
118
|
+
/* ignore */
|
|
119
|
+
}
|
|
120
|
+
throw new BrowserError("ERR_PRISM_BROWSER_LIMIT", `maxDownloads ${limits.maxDownloads} exceeded`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const perFileCap = Math.min(options.maxBytes ?? limits.maxDownloadBytes, limits.maxDownloadBytes);
|
|
124
|
+
await mkdir(options.quarantine, { recursive: true, mode: 0o700 });
|
|
125
|
+
|
|
126
|
+
const downloadId = `dl_${randomBytes(8).toString("hex")}`;
|
|
127
|
+
const suggested = sanitizeDownloadName(download.suggestedFilename?.() ?? "download.bin");
|
|
128
|
+
const safeName = `${downloadId}_${suggested}`;
|
|
129
|
+
const quarantineRoot = resolve(options.quarantine);
|
|
130
|
+
const quarantinePath = resolve(join(quarantineRoot, safeName));
|
|
131
|
+
if (!quarantinePath.startsWith(`${quarantineRoot}/`) && quarantinePath !== quarantineRoot) {
|
|
132
|
+
throw new BrowserError("ERR_PRISM_BROWSER_ARTIFACT", "quarantine path escaped root");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const hash = createHash("sha256");
|
|
136
|
+
let bytes = 0;
|
|
137
|
+
let head: Buffer | undefined;
|
|
138
|
+
const url = (() => {
|
|
139
|
+
try {
|
|
140
|
+
return download.url().slice(0, 2_048);
|
|
141
|
+
} catch {
|
|
142
|
+
return "";
|
|
143
|
+
}
|
|
144
|
+
})();
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const stream = typeof download.createReadStream === "function" ? await download.createReadStream() : null;
|
|
148
|
+
if (stream) {
|
|
149
|
+
await pipeline(
|
|
150
|
+
Readable.from(
|
|
151
|
+
boundedChunks(stream as AsyncIterable<unknown>, {
|
|
152
|
+
perFileCap,
|
|
153
|
+
aggregateCap: limits.maxDownloadAggregateBytes,
|
|
154
|
+
aggregateUsed: budget.aggregateBytes,
|
|
155
|
+
signal,
|
|
156
|
+
onHead: (buf) => {
|
|
157
|
+
head = buf;
|
|
158
|
+
},
|
|
159
|
+
onBytes: (n) => {
|
|
160
|
+
bytes = n;
|
|
161
|
+
},
|
|
162
|
+
hash,
|
|
163
|
+
}),
|
|
164
|
+
),
|
|
165
|
+
createWriteStream(quarantinePath, { mode: 0o600, flags: "wx" }),
|
|
166
|
+
);
|
|
167
|
+
} else if (typeof download.saveAs === "function") {
|
|
168
|
+
await download.saveAs(quarantinePath);
|
|
169
|
+
const handle = await open(quarantinePath, "r");
|
|
170
|
+
try {
|
|
171
|
+
const stat = await handle.stat();
|
|
172
|
+
bytes = stat.size;
|
|
173
|
+
if (bytes > perFileCap || budget.aggregateBytes + bytes > limits.maxDownloadAggregateBytes) {
|
|
174
|
+
await rm(quarantinePath, { force: true });
|
|
175
|
+
throw new BrowserError("ERR_PRISM_BROWSER_LIMIT", "download exceeds byte budget");
|
|
176
|
+
}
|
|
177
|
+
const buf = Buffer.alloc(64 * 1024);
|
|
178
|
+
let remaining = bytes;
|
|
179
|
+
while (remaining > 0) {
|
|
180
|
+
const { bytesRead } = await handle.read(buf, 0, Math.min(buf.length, remaining), null);
|
|
181
|
+
if (bytesRead <= 0) break;
|
|
182
|
+
if (!head) head = buf.subarray(0, Math.min(bytesRead, 16));
|
|
183
|
+
hash.update(buf.subarray(0, bytesRead));
|
|
184
|
+
remaining -= bytesRead;
|
|
185
|
+
}
|
|
186
|
+
} finally {
|
|
187
|
+
await handle.close();
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
throw new BrowserError("ERR_PRISM_BROWSER", "Download stream API unavailable");
|
|
191
|
+
}
|
|
192
|
+
} catch (error) {
|
|
193
|
+
await rm(quarantinePath, { force: true }).catch(() => undefined);
|
|
194
|
+
try {
|
|
195
|
+
await download.cancel?.();
|
|
196
|
+
} catch {
|
|
197
|
+
/* ignore */
|
|
198
|
+
}
|
|
199
|
+
if (error instanceof BrowserError) throw error;
|
|
200
|
+
throw new BrowserError(
|
|
201
|
+
"ERR_PRISM_BROWSER_ARTIFACT",
|
|
202
|
+
`download quarantine failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const meta: DownloadMetadata = {
|
|
207
|
+
downloadId,
|
|
208
|
+
suggestedName: suggested,
|
|
209
|
+
safeName,
|
|
210
|
+
quarantinePath,
|
|
211
|
+
url,
|
|
212
|
+
bytes,
|
|
213
|
+
sha256: hash.digest("hex"),
|
|
214
|
+
mimeType: head ? sniffMime(head) : undefined,
|
|
215
|
+
released: false,
|
|
216
|
+
};
|
|
217
|
+
budget.count += 1;
|
|
218
|
+
budget.aggregateBytes += bytes;
|
|
219
|
+
budget.items.set(downloadId, { ...meta, released: false });
|
|
220
|
+
return meta;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export async function releaseDownload(
|
|
224
|
+
downloadId: string,
|
|
225
|
+
options: BrowserDownloadOptions,
|
|
226
|
+
budget: DownloadBudget,
|
|
227
|
+
): Promise<DownloadMetadata> {
|
|
228
|
+
const item = budget.items.get(downloadId);
|
|
229
|
+
if (!item) {
|
|
230
|
+
throw new BrowserError("ERR_PRISM_BROWSER_STATE", `Unknown downloadId ${downloadId}`);
|
|
231
|
+
}
|
|
232
|
+
if (item.released) return item;
|
|
233
|
+
if (!options.approveRelease) {
|
|
234
|
+
throw new BrowserError("ERR_PRISM_BROWSER_ARTIFACT", "download release requires host approveRelease callback");
|
|
235
|
+
}
|
|
236
|
+
const ok = await options.approveRelease(item);
|
|
237
|
+
if (!ok) {
|
|
238
|
+
throw new BrowserError("ERR_PRISM_BROWSER_ARTIFACT", "host denied download release");
|
|
239
|
+
}
|
|
240
|
+
const released = { ...item, released: true };
|
|
241
|
+
budget.items.set(downloadId, released);
|
|
242
|
+
return released;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export async function cleanupDownloads(budget: DownloadBudget): Promise<void> {
|
|
246
|
+
for (const item of budget.items.values()) {
|
|
247
|
+
if (item.released) continue;
|
|
248
|
+
await rm(item.quarantinePath, { force: true }).catch(() => undefined);
|
|
249
|
+
}
|
|
250
|
+
budget.items.clear();
|
|
251
|
+
budget.count = 0;
|
|
252
|
+
budget.aggregateBytes = 0;
|
|
253
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Stable browser-package errors. Messages stay bounded and omit storage/secrets. */
|
|
2
|
+
|
|
3
|
+
export type BrowserErrorCode =
|
|
4
|
+
| "ERR_PRISM_BROWSER"
|
|
5
|
+
| "ERR_PRISM_BROWSER_LIMIT"
|
|
6
|
+
| "ERR_PRISM_BROWSER_STATE"
|
|
7
|
+
| "ERR_PRISM_BROWSER_TARGET"
|
|
8
|
+
| "ERR_PRISM_BROWSER_CLOSED"
|
|
9
|
+
| "ERR_PRISM_BROWSER_INPUT"
|
|
10
|
+
| "ERR_PRISM_BROWSER_NETWORK"
|
|
11
|
+
| "ERR_PRISM_BROWSER_ARTIFACT"
|
|
12
|
+
| "ERR_PRISM_BROWSER_CDP_UNAVAILABLE"
|
|
13
|
+
| "ERR_PRISM_BROWSER_CDP";
|
|
14
|
+
|
|
15
|
+
export class BrowserError extends Error {
|
|
16
|
+
readonly code: BrowserErrorCode;
|
|
17
|
+
|
|
18
|
+
constructor(code: BrowserErrorCode, message: string) {
|
|
19
|
+
super(truncateMessage(message));
|
|
20
|
+
this.name = "BrowserError";
|
|
21
|
+
this.code = code;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MAX_ERROR_BYTES = 1_024;
|
|
26
|
+
|
|
27
|
+
function truncateMessage(message: string): string {
|
|
28
|
+
const buf = Buffer.from(message, "utf8");
|
|
29
|
+
if (buf.byteLength <= MAX_ERROR_BYTES) return message;
|
|
30
|
+
return `${buf.subarray(0, MAX_ERROR_BYTES).toString("utf8")}…`;
|
|
31
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded page-context evaluation via CDP Runtime.evaluate (0.1.4, plan 016 Task 4).
|
|
3
|
+
* Expression bytes and result bytes are capped; result is JSON-serializable by
|
|
4
|
+
* construction (returnByValue) or surfaced as a bounded exception description.
|
|
5
|
+
*/
|
|
6
|
+
import { cdpRuntimeEvaluate } from "./cdp.js";
|
|
7
|
+
import { BrowserError } from "./errors.js";
|
|
8
|
+
import type { PlaywrightCdpSession } from "./types.js";
|
|
9
|
+
|
|
10
|
+
export interface EvaluateOutcome {
|
|
11
|
+
/** JSON-serializable evaluated value (bounded by maxEvaluateResultBytes). */
|
|
12
|
+
readonly value?: unknown;
|
|
13
|
+
/** Bounded exception description when evaluation threw or was unserializable. */
|
|
14
|
+
readonly exception?: string;
|
|
15
|
+
/** The serialized result was truncated to the byte cap. */
|
|
16
|
+
readonly truncated?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface EvaluateBounds {
|
|
20
|
+
readonly maxActionInputBytes: number;
|
|
21
|
+
readonly actionTimeoutMs: number;
|
|
22
|
+
readonly maxEvaluateResultBytes: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MAX_EXCEPTION_DESCRIPTION_BYTES = 2_048;
|
|
26
|
+
|
|
27
|
+
export async function evaluateInPage(
|
|
28
|
+
session: PlaywrightCdpSession,
|
|
29
|
+
input: { expression: string; awaitPromise?: boolean; timeoutMs?: number },
|
|
30
|
+
bounds: EvaluateBounds,
|
|
31
|
+
): Promise<EvaluateOutcome> {
|
|
32
|
+
if (typeof input.expression !== "string" || !input.expression) {
|
|
33
|
+
throw new BrowserError("ERR_PRISM_BROWSER_INPUT", "evaluate requires a non-empty expression");
|
|
34
|
+
}
|
|
35
|
+
if (Buffer.byteLength(input.expression, "utf8") > bounds.maxActionInputBytes) {
|
|
36
|
+
throw new BrowserError("ERR_PRISM_BROWSER_LIMIT", `expression exceeds maxActionInputBytes ${bounds.maxActionInputBytes}`);
|
|
37
|
+
}
|
|
38
|
+
const timeout = clampTimeout(input.timeoutMs, bounds.actionTimeoutMs);
|
|
39
|
+
|
|
40
|
+
const response = await cdpRuntimeEvaluate(session, {
|
|
41
|
+
expression: input.expression,
|
|
42
|
+
awaitPromise: input.awaitPromise === true,
|
|
43
|
+
returnByValue: true,
|
|
44
|
+
userGesture: true,
|
|
45
|
+
timeout,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (response.exceptionDetails) {
|
|
49
|
+
return { exception: describeException(response.exceptionDetails) };
|
|
50
|
+
}
|
|
51
|
+
const raw = response.result?.value;
|
|
52
|
+
if (raw === undefined) return { value: undefined };
|
|
53
|
+
const { json, truncated } = boundedJson(raw, bounds.maxEvaluateResultBytes);
|
|
54
|
+
return { value: json, ...(truncated ? { truncated: true } : {}) };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function describeException(details: {
|
|
58
|
+
readonly text?: string;
|
|
59
|
+
readonly url?: string;
|
|
60
|
+
readonly lineNumber?: number;
|
|
61
|
+
readonly columnNumber?: number;
|
|
62
|
+
readonly exception?: { readonly description?: string };
|
|
63
|
+
}): string {
|
|
64
|
+
const description =
|
|
65
|
+
details.exception?.description && details.exception.description.length > 0
|
|
66
|
+
? details.exception.description
|
|
67
|
+
: (details.text ?? "unknown exception");
|
|
68
|
+
const bounded = truncateUtf8(description, MAX_EXCEPTION_DESCRIPTION_BYTES);
|
|
69
|
+
const where = details.url && details.lineNumber !== undefined ? ` at ${details.url}:${details.lineNumber + 1}` : "";
|
|
70
|
+
return `${bounded}${where}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** JSON-serialize with a byte cap; truncation marks the result, never throws. */
|
|
74
|
+
export function boundedJson(value: unknown, maxBytes: number): { json: unknown; truncated: boolean } {
|
|
75
|
+
let text: string;
|
|
76
|
+
try {
|
|
77
|
+
text = JSON.stringify(value);
|
|
78
|
+
} catch {
|
|
79
|
+
return { json: String(value), truncated: false };
|
|
80
|
+
}
|
|
81
|
+
if (text === undefined) return { json: undefined, truncated: false };
|
|
82
|
+
if (Buffer.byteLength(text, "utf8") <= maxBytes) {
|
|
83
|
+
// Round-trip back to a real value; the parse is safe (stringify just succeeded).
|
|
84
|
+
return { json: JSON.parse(text) as unknown, truncated: false };
|
|
85
|
+
}
|
|
86
|
+
return { json: truncateUtf8(text, maxBytes), truncated: true };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function truncateUtf8(text: string, maxBytes: number): string {
|
|
90
|
+
const buf = Buffer.from(text, "utf8");
|
|
91
|
+
if (buf.byteLength <= maxBytes) return text;
|
|
92
|
+
return `${buf.subarray(0, maxBytes).toString("utf8")}…`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function clampTimeout(value: number | undefined, hard: number): number {
|
|
96
|
+
if (value === undefined) return hard;
|
|
97
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > hard) {
|
|
98
|
+
throw new BrowserError("ERR_PRISM_BROWSER_LIMIT", `timeoutMs must be 1..${hard}`);
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@arnilo/prism-tools/browser` — the first-party browser automation tools as a package.
|
|
3
|
+
*
|
|
4
|
+
* The default export registers the six exclusive model-facing tools from `createBrowserTools`
|
|
5
|
+
* (`browser_open`, `browser_snapshot`, `browser_act`, `browser_close`, `browser_evaluate`,
|
|
6
|
+
* `browser_observe`), each declaring its `effects` for the host approval hook. `playwright-core`
|
|
7
|
+
* is imported dynamically in `setup` and used to launch the run-owned browser; when it is missing
|
|
8
|
+
* (or the launch fails) the package registers nothing and writes a `warn` status note. The
|
|
9
|
+
* manager and the launched browser are closed in `onDispose`.
|
|
10
|
+
*
|
|
11
|
+
* Egress is fail-closed: without a `containedProxy` attestation every external request is denied,
|
|
12
|
+
* and `allowOrigins` narrows navigation to an explicit origin allowlist. Blocked schemes, loopback,
|
|
13
|
+
* private, link-local, and ULA destinations are denied inside the manager, so they hold under any
|
|
14
|
+
* approval mode.
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* import toolsBrowser from "@arnilo/prism-tools/browser";
|
|
18
|
+
* import { createAgent } from "@arnilo/prism";
|
|
19
|
+
*
|
|
20
|
+
* const agent = await createAgent({ model, packages: [toolsBrowser({ headless: true, allowOrigins: ["https://example.com"] })] });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
import { definePackage, type PackageInstance, type ToolDefinition } from "@arnilo/prism";
|
|
25
|
+
import manifest from "../../package.json" with { type: "json" };
|
|
26
|
+
|
|
27
|
+
import { type ToolsBrowserConfig, toolsBrowserConfigSchema } from "./config.js";
|
|
28
|
+
import { createBrowserManager } from "./manager.js";
|
|
29
|
+
import type { BrowserNetworkPolicy } from "./network.js";
|
|
30
|
+
import { createBrowserTools } from "./tools.js";
|
|
31
|
+
import type { PlaywrightBrowser } from "./types.js";
|
|
32
|
+
|
|
33
|
+
// The moved 0.12 surface keeps its names, now behind one entry point.
|
|
34
|
+
export * from "./browser.js";
|
|
35
|
+
export type { ToolsBrowserConfig, ToolsBrowserProxyConfig } from "./config.js";
|
|
36
|
+
export { toolsBrowserConfigSchema } from "./config.js";
|
|
37
|
+
|
|
38
|
+
/** The subset of `playwright-core` the package uses; kept structural so the peer stays optional. */
|
|
39
|
+
interface PlaywrightChromium {
|
|
40
|
+
launch(options: { readonly headless?: boolean; readonly executablePath?: string; readonly channel?: string }): Promise<PlaywrightBrowser>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function errorMessage(error: unknown): string {
|
|
44
|
+
return error instanceof Error ? error.message : String(error);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Normalize `allowOrigins` entries to origins and deny every other origin. */
|
|
48
|
+
function originPolicy(allowOrigins: readonly string[]): BrowserNetworkPolicy["validateUrl"] {
|
|
49
|
+
const allowed = new Set(
|
|
50
|
+
allowOrigins.map((origin) => {
|
|
51
|
+
try {
|
|
52
|
+
return new URL(origin).origin;
|
|
53
|
+
} catch {
|
|
54
|
+
return origin;
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
return (url: URL): boolean => allowed.has(url.origin);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function networkPolicyOf(config: ToolsBrowserConfig): BrowserNetworkPolicy {
|
|
62
|
+
const policy: BrowserNetworkPolicy = {
|
|
63
|
+
...(config.containedProxy !== undefined ? { containedProxyAttestation: config.containedProxy } : {}),
|
|
64
|
+
...(config.allowLoopback === true ? { allowLoopback: true } : {}),
|
|
65
|
+
...(config.allowPrivateHosts === true ? { allowPrivateHosts: true } : {}),
|
|
66
|
+
};
|
|
67
|
+
if (config.allowOrigins !== undefined && config.allowOrigins.length > 0) {
|
|
68
|
+
return { ...policy, validateUrl: originPolicy(config.allowOrigins) };
|
|
69
|
+
}
|
|
70
|
+
return policy;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const definition = definePackage<ToolsBrowserConfig>({
|
|
74
|
+
name: "@arnilo/prism-tools/browser",
|
|
75
|
+
version: manifest.version,
|
|
76
|
+
description: "Six exclusive browser tools over Playwright with declared effects, fail-closed egress policy, and artifact quarantine.",
|
|
77
|
+
requires: { api: 1 },
|
|
78
|
+
config: toolsBrowserConfigSchema,
|
|
79
|
+
setup: async (prism) => {
|
|
80
|
+
// Registered first: `requiresTools` hides the skill when playwright-core is missing or the launch fails.
|
|
81
|
+
prism.skills.addDir(fileURLToPath(new URL("../skills", import.meta.url)));
|
|
82
|
+
let chromium: PlaywrightChromium;
|
|
83
|
+
try {
|
|
84
|
+
({ chromium } = (await import("playwright-core")) as unknown as { chromium: PlaywrightChromium });
|
|
85
|
+
} catch (error) {
|
|
86
|
+
prism.status.note("warn", `browser: playwright-core is not installed (${errorMessage(error)})`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const launchOptions: { headless: boolean; executablePath?: string; channel?: string } = {
|
|
91
|
+
headless: prism.config.headless ?? true,
|
|
92
|
+
};
|
|
93
|
+
if (prism.config.executablePath !== undefined) launchOptions.executablePath = prism.config.executablePath;
|
|
94
|
+
if (prism.config.channel !== undefined) launchOptions.channel = prism.config.channel;
|
|
95
|
+
|
|
96
|
+
let browser: PlaywrightBrowser;
|
|
97
|
+
try {
|
|
98
|
+
browser = await chromium.launch(launchOptions);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
prism.status.note("warn", `browser: launch failed (${errorMessage(error)})`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const uploadRoots = prism.config.uploadRoots;
|
|
105
|
+
const quarantine = prism.config.quarantine;
|
|
106
|
+
const manager = createBrowserManager({
|
|
107
|
+
browser,
|
|
108
|
+
networkPolicy: networkPolicyOf(prism.config),
|
|
109
|
+
cdp: { mode: prism.config.cdp ?? "auto" },
|
|
110
|
+
...(uploadRoots !== undefined && uploadRoots.length > 0 ? { uploads: { roots: uploadRoots } } : {}),
|
|
111
|
+
...(quarantine !== undefined ? { downloads: { quarantine } } : {}),
|
|
112
|
+
});
|
|
113
|
+
const tools: readonly ToolDefinition[] = createBrowserTools({ manager });
|
|
114
|
+
for (const tool of tools) prism.tools.register(tool);
|
|
115
|
+
|
|
116
|
+
prism.onDispose(async () => {
|
|
117
|
+
await manager.close();
|
|
118
|
+
await browser.close?.();
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/** The package definition. Pass config through `packageConfig` or use {@link toolsBrowser}. */
|
|
124
|
+
export default definition;
|
|
125
|
+
|
|
126
|
+
/** The package with config baked in, for `packages: [toolsBrowser({ headless })]`. */
|
|
127
|
+
export function toolsBrowser(config: ToolsBrowserConfig = {}): PackageInstance {
|
|
128
|
+
return definition(config);
|
|
129
|
+
}
|