Commit Graph
57 Commits
Author SHA1 Message Date
Adrià Arrufat 88d133a676 cli: suggest the closest flag or command on a typo
An unknown --flag now logs the nearest accepted spelling within two
edits as did_you_mean, painted green next to the red typo in the pretty
log format; logfmt stays plain. A bare first argument within two edits
of a command name is rejected with the same hint instead of being
fetched as a url.

The Levenshtein helper moves from SlashCommand into string.zig so the
agent REPL and the CLI share it, with the table widened to fit the
longest flag name.
2026-09-08 16:50:46 +02:00
Muki Kiboigo 212c806be5 add basic CorsGate scaffold 2026-09-04 06:59:37 -07:00
Karl Seguin e6241c1918 serve: Improve cdp/bidi server
Significant rework of the CDP/BiDi server. There are two main changes:

1 - poll replaced with EPoll/Kqueue (1)
2 - make http serving a first class citizen

The change from poll -> epoll/kqueue isn't performance driven, it's just about
tighter code. Both epoll and kqueue let you associate arbitrary data with a
socket, so we don't need to keep arrays in sync in order to associate a socket
with a CDP by index. They both provide some event/notification mechanism, which
is cleaner than the pipe required by poll.

The poll -> epoll/kqueue change could almost have been mechanical. Making HTTP
a first class citizen is the more significant of the two changes

In `main`, a new connection always spawns a thread and, until does its own
little read loop until the connection is upgraded. This is not efficient, it
uses up a connection slot, and it's inconsistent with the final WebSocket
connection which _is_ polled off the main loop. Using up a slot means that
keepalive isn't possible, else HTTP connections would quickly use up all
available slots/threads.

This commit parses and serves HTTP requests on the main thread (safe
because none of the processing is blocking). The approach is better streamlined
for HTTP requests which never upgrade (/metrics, WebDriver) without causing
any performance overhead for those that do. It simplifies some things (e.g. an
"http" socket or a "websocket" socket is monitored and read in a similar manner
(on the main loop)). It makes other things more complicated; the flow is no
longer accept -> spawn -> upgrade -> websocket loop. It's loop -> accept -> loop
-> process -> (http | ws).

This is built ontop of the BiDi branch because (a) WebDriver is what needs
better  HTTP support and (b) some of the more mechanical changes already exist
in that branch (e.g. src/cdp/, src/server.zig -> src/server/*)

(1) kqueue landing in 2 commits from now on this branch.
2026-09-03 06:47:49 +08:00
Karl Seguin 9c83bf3ef9 log: allow 'note' to be silence, log CLI tips
We currently have 1 note: it prints the server's listening address:port. Note
is a special un-ignorable level. This keeps the "note" level, but logs it under
a new scope: "note", so that it _can_ be silenced with a `--log-filter note`.

Add a new note, on startup, that displays tips. Currently, only displays when
--obey-robots is not enabled:

NOTE  note : config tips . . . . . . . . . . . . . . . . . . . [+0ms]
      robots = use '--obey-robots' to use a sites robots.txt
      meta = use '--log-filter note' to silence this message
2026-09-01 12:35:56 +08:00
Karl Seguin 7c51dfff05 bidi: add skeleton for bidi
1. Abstract "Driver". Non-CDP things that referenced *CDP now reference a Driver
2. Move the NodeRegistry out of CDP. This created an artificial link between
   agent / mcp and CDP
3. Add BiDi driver with enough to navigate to a page
2026-08-31 22:44:59 +08:00
Karl Seguin 861ffb164b enable --log-filter in release
Log on the first iframe/worker that refuses to load due to iframe/worker loading
being disabled.
2026-08-27 17:25:42 +08:00
Karl Seguin db258bfec4 breaking: disable worker and iframe loading by default
By default, iframes and workers no longer loaded. Use `--load-resources iframe`
and `--load-resources worker` to restore the previous behavior. The disabling
makes resource loading more consistent.

To further make things more consistent, Config seems the following changes:

1. remove  `--timeout` from `serve` which does nothing but has printed a
   deprecation warning for a long time

2. added .deprecated field to CLI config flags which now logs the specified
   deprecated warning when used

3. `--log-filter-scopes` is deprecated in favor of `--log-scopes`

4. `--disable_subframes` is deprecated. Iframe loading is disabled by default,
    use `--load-resources iframe` to enable iframe loading

5. `--disable_workers` is deprecated. Worker loading is disabled by default,
    use `--load-resources worker` to enable worker loading

6. `--enable_external_stylesheets`  is deprecated. Stylesheets remain disabled
    by default. Use `--load-resources stylesheet` to enable loading external
    stylesheets

CLI log parameters now alter the logger behavior on parse. This helps minimze
the window where default log settings are in-play. It also means things like
this work:

```
./lightpanda --log-level fatal --disable_subframes --log-level warn
```

More seriously, there's now an optional `beforeParse` fired once the mode is
known. This is used by mcp to set the default log level to logfmt. Previously
this was done much later and could easily result in a mix of pretty and logfmt
logs.
2026-08-27 17:25:42 +08:00
Karl Seguin 6a83881634 chore: add lp.IS_DEBUG and lp.IS_TEST
Change all users to builtin.mode and built.is_test
2026-08-01 09:15:18 +08:00
Karl Seguin c726b022e8 tests: improve test output and test log handling
1 - TEST_VERBOSE is now off by default
2 - There's a afterEach callback that is automatically run after each tests, it:
     a - clears the log filters
     b - resets the test arena
3 - LogFilter replace with
     a - testing.silenceLog(&.{...scopes...}); to silence all logs for the given
         scopes.
     b - testing.expectLog(&.{...scopes}); to set log expectations, 1 per log.
         The goal here isn't so much to expect logs (though, you can do that),
         but rather to silence an expected # of logs, without silencing more.
2026-07-31 07:30:25 +08:00
Karl Seguin dcb98853b9 chore: make our use of clock/timestamp more consistent
Zig 0.16 changed clocks/timestamps. Our migration to Zig 0.16 took the path of
least resistance. For example, we kept 'monotonic' and 'real' even though Zig
0.16 renamed them to 'boot' and 'clock'

This commit tries to standardize all timestamp usage to: lp.datetime.timestamp
and lp.datetime.milliTimestamp using the new zig 0.16 names ('boot' and 'clock')
2026-07-27 09:28:48 +08:00
Karl Seguin 8e42d63c1c zig: Zig 0.16
Built against https://github.com/lightpanda-io/zig-v8-fork/tree/zig-0.16 but
it doesn't require a new v8 build.

Built against https://github.com/lightpanda-io/boringssl-zig/tree/zig-0.16
since the current fork we point to isn't updated.

A global std.Io instance, lp.io. Way easier this way and requires 0 changes to
our libcurl integration / event loop.

Network code uses a new layer that does what Zig 0.15's posix package used to
do. Again, quicker migration that way. But, as long as we have the global IO,
and given the half-baked nature of networking in std.Io 0.16, this just makes
sense. Things can be migrated as needed.

The std.time.* -> std.Io.Timestamp/Clock/Duration resulted in _a lot_ of
changes. ArrayList = .{} -> ArrayList -> .empty also resulted in a lot of
changes, but that's obviously superficial. As is the trimLeft/trimRight ->
trimStart/trimEnd rename.

Locking adopt the `Uncancelable` variants, e.g. mutex.lockUncancelable() to
preserve the error-free signature (and, because cancellation would be something
we'd have to put more thought into).

std.json.ObjectMap is now unmanaged, so the allocator had to be passed along.
However, there's still a deprecated managed variant of MemoryPool, so I switched
to it (we can do a small follow up PR to move to the unmanaged after).

I tried use_llvm = false, but it locks my computer, consuming RAM until MacOS
gives me a popup I've never seen before, begging me to start killing processes.

Agent and the networking stuff saw the most significant changes.
2026-07-22 13:26:03 +08:00
Pierre Tachoire 37a7f034c8 use writerStreaming in log 2026-07-02 11:23:45 +02:00
Karl Seguin ec1d6fcb27 minor: don't log .note level logs during unit tests 2026-06-26 08:34:24 +08:00
Pierre Tachoire 828444675f feat(log): support +/-/all syntax in --log-filter-scopes
`--log-filter-scopes` previously took a comma-separated list of scopes to
suppress. Extend it with explicit include/exclude directives applied
left-to-right:

  -X     filter out scope X
  X      alias for -X (backward compatible)
  +X     filter in scope X
  all    target every scope

This makes "filter everything except one scope" expressible, e.g.
`-all,+cdp` suppresses all logs except `cdp`. Bare scope names keep their
old meaning, so existing invocations like `http,unknown_prop` are
unaffected.

Internally, the suppressed-scope list is replaced by a resolved per-scope
`scope_enabled` boolean array built via `log.resolveFilterScopes()`, so the
hot-path `enabled()` check is a single array index. Filtering remains
Debug-only, as before. `testing.LogFilter` is adapted to the new
representation; all existing call sites are unchanged.

Adds a unit test for resolveFilterScopes (default, backward-compat,
-all,+cdp, ordering) and updates the help text.
2026-06-18 14:42:07 +02:00
Karl Seguin 655523c12a ops: Add "note" log level for CDP "server running" message
`note` is the highest log level (e.g. cannot be turned off), and thus will
always be displayed. The purpose is to make sure that people who use --port 0
can see the listening port WITHOUT giving up log-level filtering (e.g. having
to use --log-level info JUST to see the listening port).

I'm personally a fan of using .note only when (port == 0), but we can tweak it
later if people complain about it showing up despite using a warn/error/fatal
log level.
2026-06-17 11:15:34 +08:00
Adrià Arrufat 314e3c58ea Merge branch 'main' into agent
# Conflicts:
#	src/mcp/Server.zig
2026-06-11 09:00:44 +02:00
Karl Seguin 93346ef5a6 mem: reduce generic use in log
Experiment to see if this shrinks the build by any significant amount. Some
investigation did surface this as some of the code we control directly which
did stand out.
2026-06-11 10:21:57 +08:00
Karl Seguin 96becbd296 log, mem: Make scope and msg runtime values to shrink generated code size 2026-06-11 08:41:57 +08:00
Adrià Arrufat 4bc5baf7ee agent: improve command diagnostics and fix various bugs
- Add diagnostics to command parsing to report invalid field values.
- Fix UTF-8 truncation in spinner to avoid splitting codepoints.
- Fix swapped arguments in log error formatting.
- Distinguish "null" values from missing elements in verifier.
- Handle MCP tool cancellation and timeouts with specific error codes.
2026-05-25 17:51:46 +02:00
Adrià Arrufat c30ccae5fb terminal: improve log routing and navigation error reporting
- Route logs through a terminal sink to avoid trampling the spinner.
- Track and format the last navigation error in browser tools.
2026-05-22 19:58:02 +02:00
Karl Seguin 2275416505 Page -> Frame
This is to pave the way for introducing a new "Page" container, which will take
over the page lifecycle currently burdening Session. The ultimate goal of that
is to allow the Session to have multiple pages (mostly for better transitions
between pages), which is hard to do now since the Session has so much state.

This rename was aggressive, e.g. currentPage() -> currentFrame() so that, when
the new Page container is added, you won't see "currentPage()" and wonder:

  "Does 'currentPage' mean the new Page container, or the Frame (which
  used to be called Page)".
2026-04-22 08:42:18 +08:00
Karl Seguin 6d0003ad2b Sqlite
This adds an app.storage which is a union around configurable storage engine
(currently, only sqlite).

It is _not_ being used anywhere right now. The goal is to get feedback on
the implementation and then move cache to it.

This doesn't expose a generic query API. The goal is that the storage will
expose high level methods, e.g. `cacheGet(req: CacheGetRequest)` and every
storage engine will translate the `storage.CacheGetRequest` as needed.

A thin wrapper around the Sqlite C api is included, e.g. exec(SQL, .{args}) a
`rows` and `row` fetcher. A connection pool is included. By default, an
in-memory DB is currently created. And a `migrations` table with an id of `1`
is created/inserted. I don't imagine needing fancy migratations.
2026-04-20 17:13:06 +08:00
Karl Seguin 14dcb7895a Give websockets their own connection pool, improve websocket message logging 2026-04-04 07:00:24 +08:00
Muki Kiboigo d65a4b09f3 better logging for cache 2026-04-03 07:23:30 -07:00
Karl Seguin ea422075c7 Remove unused imports
And some smaller cleanups.
2026-03-27 12:45:26 +08:00
Adrià Arrufat a27339b954 mcp: add Model Context Protocol server support
Adds a new `mcp` run mode to start an MCP server over stdio.
Implements tools for navigation and JS evaluation, along with
resources for HTML and Markdown page content.
2026-02-22 22:32:14 +09:00
Nikolay Govorov 6553bb8147 Remove los interceptors feature 2026-02-16 15:48:18 +00:00
Karl Seguin 8e14dacc32 Improve ergonomics of try catch (and Function's tryCall)
It now returns a Caught struct which contains all information. The Caught struct
can be logged directly, providing more consistent logs for caught errors.
2026-01-14 17:34:02 +08:00
Pierre Tachoire 76f30dc985 zig fmt 2025-12-29 11:40:32 +01:00
Karl Seguin dc33c4d5fd improve console.log output when using logfmt 2025-12-29 12:58:20 +08:00
Karl Seguin e336c67857 various small api fixes/tweaks 2025-11-24 20:12:43 +08:00
Karl Seguin 1164da5e7a copyright notices 2025-11-14 10:52:43 +08:00
Karl Seguin 5ec5647395 Merge ScriptManager/Module loading changes
Get tests passing.
2025-11-13 17:21:08 +08:00
Karl Seguin cdd31353c5 get fetch campire working 2025-10-28 11:24:29 +08:00
Karl Seguin b047cb6dc1 remove libdom 2025-10-27 22:14:59 +08:00
Karl Seguin fe9a10c617 Emit Log.addEntry
Currently, this hooks a single log.Interceptor into the logging framework, but
changing it to take a list shouldn't be too hard. Biggest issue is who will own
it, as we'd need an allocator to maintain a list / lookup (which log doesn't
currently have).

Uses logFmt format, and, for now, always filters out debug messages and a few
particularly verbose scopes.
2025-10-03 17:29:01 +08:00
Karl Seguin 024f7ad9ef Merge pull request #1056 from lightpanda-io/DOM_NO_ERR
Convert more DOM_NO_ERR cases to assertions
2025-09-18 19:06:32 +08:00
Muki Kiboigo f22ee54bd8 use fetch logging scope, clean some comments 2025-09-17 08:46:35 -07:00
Karl Seguin 58acb2b821 Convert more DOM_NO_ERR cases to assertions
There is some risk to this change. The first is that I made a mistake. The
other is that one of the APIs that doesn't currently return an error changes
in the future.
2025-09-17 13:37:48 +08:00
Karl Seguin c40704d2f3 Prototype new test runner
Follows up on https://github.com/lightpanda-io/browser/pull/994 and replaces
the jsRunner with a new page.navigation-based test runner.

Currently only implemented for the Window tests, looking for feedback and
converting every existing test will take time - so for a while, newRunner (to be
renamed) will sit side-by-side with jsRunner.

In addition to the benefits outlined in 994, largely around code simplicity and
putting more of the actual code under tests, I think our WebAPI tests
particularly benefit from:
1 - No need to recompile when modifying the html tests
2 - Much better assertions, e.g. you can assert that something is actually an
    array, not just a string representation of an array
3 - Ability to test some edge cases (e.g. dynamic script loading)

I've put some effort into testing.js to make sure that, if the encapsulating
zig test passes, it's because it actually passed, not because it didn't run.

For the time being, console tests are removed. I think it's more useful to have
access to the console within tests, than it is to test the console (which is
just a wrapper around log, which is both tested and heavily used).
2025-09-02 07:38:02 +08:00
Karl Seguin ada9ddd5b8 Improve clocks
There's a flaky performance test that I wanted to fix (1). This led to a couple
changes.

1 - Add timestamp() and milliTimestamp() to datetime.zig. Reduce some code
    duplication and use better clock_ids where available

2 - Change Performance API to use milliTimestamp and store a u64 instead of a
    f64. While the spec says a float, Firefox deals with u64 and implicit
    conversion is always available. Makes our APIs simpler.

(1) - https://github.com/lightpanda-io/browser/actions/runs/17313296490/job/49151366798#step:4:131
2025-08-30 13:45:12 +08:00
Karl Seguin 1443f38e5f Zig 0.15.1
Depends on https://github.com/lightpanda-io/zig-v8-fork/pull/89
2025-08-29 10:42:06 +08:00
Karl Seguin 5a3d5f5512 improve elapsed display for larger numbers 2025-08-13 18:17:59 +08:00
Karl Seguin c7484c69c0 Increase max concurrent request to 10
Improve wait analysis dump.

De-prioritize secondary schedules.

Don't log warning for application/json scripts

Change pretty log timer to display time from start.
2025-08-11 21:37:02 +08:00
Pierre Tachoire f09caec09a use internal logger instead of std.log 2025-08-08 16:21:23 +02:00
Pierre Tachoire 13c623755c js: remove existing unknown property debug
Because it will be displayed only if the property is non-native.
So if your property is set in pureJS, you will still have the log...
2025-07-07 16:31:54 -07:00
Pierre Tachoire 941dace7f9 enable conditionnal loading for polyfill 2025-07-07 16:31:53 -07:00
Karl Seguin c28d87d59c Improve build and test speed
Test speed has been improved only slightly by tweaking a 2-second running tests.

Build has been improved by:
1 - moving logFunctionCallError out of js.Caller and to a standalone function
2 - removing some non-generic code from the generic portions of the logger

Caller.getter and Caller.setter have been removed in favor or calling
Caller.method. This wasn't previously possible - prior to our v8 upgrade, they
had different signatures.

Also removed a largely unused parser/str.zig file.
2025-06-16 19:50:13 +08:00
Karl Seguin 6e80b03faa Improve script logging
1 - Add a custom console.lp function to make our debug logs stand out from
    script logs.

2 - In some cases, significantly improve how JavaScript values are serialized
    in debug logs and in console.log.
2025-06-02 21:38:57 +08:00
Karl Seguin c3f3eea7fb Improve logging
1 - Make log_level a runtime option (not a build-time)
2 - Make log_format a runtime option
3 - In Debug mode, allow for log scope filtering

Improve the general usability of scopes. Previously, the scope was more or less
based on the file that the log was in. Now they are more logically grouped.
Consider the case where you want to silence HTTP request information, previously
you'd have to filter out the `page`, `xhr` and `http_client` scopes, but that
would also elimiate other page, xhr and http_client logs. Now, you can just
filter out the `http` scope.
2025-06-02 21:38:56 +08:00