Summary
PulsarSession.open/normalize/load already support local files, but only through an undocumented internal pseudo-URL: http://localfile.internal?path=<base64url(path)>, produced by URLUtils.pathToLocalURL(Path). There is no user-facing API overload or CLI flag, the docs never mention it, and several edges are fragile.
Current behavior (verified)
| Input |
Result |
http://localfile.internal?path=<base64> (from URLUtils.pathToLocalURL) |
Works end-to-end: session.load(url, "-refresh"), open, and loadDocument load the file through PulsarWebDriver.navigate -> openLocalFile -> Path.toUri() -> Page.navigate(file://...) |
file:///abs/path.html |
normalize accepts it, but isStandard(file://) is false (OkHttp-based) and the driver does not use the localfile branch; it only works "by accident" for simple HTML |
C:\x\a.html, /tmp/a.html, ./a.html |
Not supported: AbstractPulsarContext.normalize treats non-:// strings as base64 or falls back to the default search engine; load then throws on the NIL URL |
-ignoreQuery + localfile pseudo-URL |
Breaks: normalize(..., ignoreUrlQuery=true) strips ?path=..., then localURLToPath throws Missing query parameter 'path' |
Pain points
- No user-facing entry point:
PulsarSession has no Path/File overload, and the CLI open <url> passes the URL through verbatim, so users must hand-construct the base64 pseudo-URL to load a local file.
- Undocumented: no mention in
skills/browser4-cli/SKILL.md, CLI help, or docs/; only integration tests reference pathToLocalURL.
- Fragile edges: the
-ignoreQuery conflict, half-supported file://, and raw paths silently becoming search-engine queries.
Proposed scope
- API: add
PulsarSession.openFile(Path) / loadFile(Path, args) overloads that auto-convert via pathToLocalURL.
- CLI:
open/navigate auto-detect an existing local path (or add --file <path>) and build the pseudo-URL.
- Normalization: preserve the
path query parameter for localfile.internal when ignoreUrlQuery is set (or move the encoded path into the URL path segment).
- Optionally: recognize
file:// in the driver's local-file branch so it behaves identically to the pseudo-URL.
- Docs: document supported forms and pitfalls in
skills/browser4-cli/SKILL.md, CLI help, and docs/.
- Tests: unit tests for
normalize with pseudo-URLs and -ignoreQuery; e2e coverage for the CLI entry.
- Security: if exposed to agentic/MCP calls, consider a workspace whitelist; note that
ActionValidator currently allows only http/https and would block file:// once wired.
References
AbstractPulsarContext.normalize (base64/search fallback): browser4-core/browser4-skeleton/src/main/kotlin/ai/platon/pulsar/skeleton/context/support/AbstractPulsarContext.kt
CombinedUrlNormalizer: browser4-core/browser4-skeleton/src/main/kotlin/ai/platon/pulsar/skeleton/common/urls/CombinedUrlNormalizer.kt
- Driver local-file branch:
pulsar-browser dependency (PulsarWebDriver.navigateInvaded -> openLocalFile); BrowserEmulatorImplBase.kt special-cases localfile.internal
- Existing test:
browser4-tests/pulsar-it-tests/src/test/kotlin/ai/platon/pulsar/basic/session/PulsarSessionTests.kt (testLoadLocalFile)
- CLI
open: cli/browser4-cli/src/commands.rs
Summary
PulsarSession.open/normalize/loadalready support local files, but only through an undocumented internal pseudo-URL:http://localfile.internal?path=<base64url(path)>, produced byURLUtils.pathToLocalURL(Path). There is no user-facing API overload or CLI flag, the docs never mention it, and several edges are fragile.Current behavior (verified)
http://localfile.internal?path=<base64>(fromURLUtils.pathToLocalURL)session.load(url, "-refresh"),open, andloadDocumentload the file throughPulsarWebDriver.navigate->openLocalFile->Path.toUri()->Page.navigate(file://...)file:///abs/path.htmlnormalizeaccepts it, butisStandard(file://)is false (OkHttp-based) and the driver does not use the localfile branch; it only works "by accident" for simple HTMLC:\x\a.html,/tmp/a.html,./a.htmlAbstractPulsarContext.normalizetreats non-://strings as base64 or falls back to the default search engine;loadthen throws on the NIL URL-ignoreQuery+ localfile pseudo-URLnormalize(..., ignoreUrlQuery=true)strips?path=..., thenlocalURLToPaththrowsMissing query parameter 'path'Pain points
PulsarSessionhas noPath/Fileoverload, and the CLIopen <url>passes the URL through verbatim, so users must hand-construct the base64 pseudo-URL to load a local file.skills/browser4-cli/SKILL.md, CLI help, ordocs/; only integration tests referencepathToLocalURL.-ignoreQueryconflict, half-supportedfile://, and raw paths silently becoming search-engine queries.Proposed scope
PulsarSession.openFile(Path)/loadFile(Path, args)overloads that auto-convert viapathToLocalURL.open/navigateauto-detect an existing local path (or add--file <path>) and build the pseudo-URL.pathquery parameter forlocalfile.internalwhenignoreUrlQueryis set (or move the encoded path into the URL path segment).file://in the driver's local-file branch so it behaves identically to the pseudo-URL.skills/browser4-cli/SKILL.md, CLI help, anddocs/.normalizewith pseudo-URLs and-ignoreQuery; e2e coverage for the CLI entry.ActionValidatorcurrently allows only http/https and would blockfile://once wired.References
AbstractPulsarContext.normalize(base64/search fallback):browser4-core/browser4-skeleton/src/main/kotlin/ai/platon/pulsar/skeleton/context/support/AbstractPulsarContext.ktCombinedUrlNormalizer:browser4-core/browser4-skeleton/src/main/kotlin/ai/platon/pulsar/skeleton/common/urls/CombinedUrlNormalizer.ktpulsar-browserdependency (PulsarWebDriver.navigateInvaded->openLocalFile);BrowserEmulatorImplBase.ktspecial-caseslocalfile.internalbrowser4-tests/pulsar-it-tests/src/test/kotlin/ai/platon/pulsar/basic/session/PulsarSessionTests.kt(testLoadLocalFile)open:cli/browser4-cli/src/commands.rs