From c1bb0d0cb7306c361b1d765d419c6875821a1697 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sat, 5 Sep 2026 06:17:08 +0000 Subject: [PATCH 1/6] chore: re-pin agent-bundle preview d30d9acb6; hauler dashboard uses spawnServeApp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pkg.pr.new preview of agent-bundle main d30d9acb6bd106762bb975ac58d6bf437c176091 (#588; includes #582 `agent-bundle/serve-app-command` + AB4837, #575 Rslib 1.0 / Rsbuild 2.2, #565 serve-app binds the host's own opening call, #585 AB4770–AB4772). src/cli/dashboard.ts: `hauler dashboard` calls `spawnServeApp` from `agent-bundle/serve-app-command` (agent-bundle#558) instead of its own child-process plumbing. The helper now owns resolving the installed framework CLI (was `frameworkCliPath`), spawning `agent-bundle serve-app` (was `serve`), parsing the ready line (was the `servedUrl` regex), relaying the child's stdout to stderr, and turning the request signal into SIGTERM; its typed `ServeAppCommandError` becomes the command's failure message. What stays is `locateProject` — where this install keeps its artifact — since the helper takes `root`/`artifact` as inputs. Flags, the `hauler_status` opening call, foreground-until-Ctrl-C, and the checkout-only scope are unchanged; the routed bin still never imports the compiler (AB4837), and the built bins inline the helper (no bare specifier, AB6005 clean including the package build's dist). Verified: validate, build (no new diagnostics), typecheck, diagnostics, tests/dashboard.test.ts + tests/version-consistency.test.ts (104) and tests/route-unit/routes.test.ts (7). Smoke: `hauler dashboard --no-open --port 0` printed `MCP App hauler/dashboard at http://127.0.0.1:40577/ (tool hauler_status; Ctrl-C stops the server)`; curl of that URL returned HTTP 200 text/html (`hauler/dashboard`); SIGINT ended the whole tree (`Aborted.`). --- .changeset/serve-app-command.md | 5 + README.md | 15 +- package.json | 4 +- pnpm-lock.yaml | 1004 +++---------------------------- src/cli/dashboard.ts | 142 ++--- 5 files changed, 146 insertions(+), 1024 deletions(-) create mode 100644 .changeset/serve-app-command.md diff --git a/.changeset/serve-app-command.md b/.changeset/serve-app-command.md new file mode 100644 index 00000000..f9db5e6f --- /dev/null +++ b/.changeset/serve-app-command.md @@ -0,0 +1,5 @@ +--- +'cargo-hauler': patch +--- + +`hauler dashboard` serves the App through `spawnServeApp` from `agent-bundle/serve-app-command` (agent-bundle #558/#582) instead of its own child-process plumbing. The framework helper now resolves the installed `agent-bundle` CLI, spawns `agent-bundle serve-app`, waits for its ready line, relays the child's output to stderr, and turns Ctrl-C into the server's SIGTERM; its typed failures (`framework-not-installed`, `artifact-missing`, `exited-before-ready`, …) become the command's message, and the routed bin still never imports the compiler (agent-bundle `AB4837`). The flags (`--target`, `--port`, `--no-open`), the opening `hauler_status` call, the foreground-until-Ctrl-C behaviour, and the checkout-only scope are unchanged. Rebuilt against the `agent-bundle` / `@agent-bundle/runtime` preview of main commit `d30d9acb6`, which also holds the package build's `dist/` to `AB6005` (#588), compiles on Rslib 1.0 / Rsbuild 2.2 (#575), binds the host's own opening call in `serve-app` (#565), and reports MCP App view compile errors as `AB4770`–`AB4772` (#585). (#TBD) diff --git a/README.md b/README.md index 000f38e1..486250e6 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ The CLI is `hauler` on PATH from `npm i -g cargo-hauler`. Never run | `hauler request [--session ID] [--host HOST] [--cwd DIR] [--after TICKET …] -- ` | Submit a background request and return its ticket, with where it landed in its lane (`queued behind cc-3281 (~13m)`, `waiting for cc-3281`, or `attached to cc-3281`). `--after` works as for `exec`. | | `hauler daemon ` | Manage the daemon lifecycle. `restart` is the manual replacement: it sends the graceful stop, waits up to 5 s for the old pid to exit, then starts a daemon from this install and prints both (`restarted: pid 741314 (0.6.0) → pid 742001 (0.6.1)`); a daemon that has not exited by then is reported, not killed, and nothing is started (exit `1`). Tickets in flight are not handed over: the old daemon settles them itself as it shuts down — `killed`, error `daemon shutdown` — and callers resubmit (only rows a daemon that died without shutting down never marked are stamped `orphaned by daemon restart` by the next daemon's first ledger pass). After upgrading the package, the next `hauler exec`, `hauler request`, hook rewrite, or `hauler daemon start` replaces a daemon from the previous install automatically the same way; when the old daemon has not exited within the grace, that call fails with `` cargo-hauler daemon pid N (X.Y.Z) is still running 5s after the shutdown request; not restarted — retry once it has exited, or stop it with `hauler daemon stop` `` instead of starting a second daemon. | | `hauler install-shim [--dir DIR] [--real-cargo PATH] [--force]` | Install the optional PATH shim. | -| `hauler dashboard [--target claude\|codex\|cursor\|portable] [--port N] [--no-open]` | Open the dashboard in a plain browser tab: serve the MCP App standalone against the plugin's own `hauler` server on `127.0.0.1` (through `agent-bundle serve-app`), call `hauler_status` once so it opens populated, and stay in the foreground until Ctrl-C. A checkout command: it needs the built `artifact/` beside the CLI and `agent-bundle` under `node_modules` (`pnpm install && pnpm build`); the npm package ships no runtime dependencies and an installed host pack has no artifact, so both report what is missing. In an MCP host, call `hauler_status` instead. | +| `hauler dashboard [--target claude\|codex\|cursor\|portable] [--port N] [--no-open]` | Open the dashboard in a plain browser tab: serve the MCP App standalone against the plugin's own `hauler` server on `127.0.0.1` (`spawnServeApp` from `agent-bundle/serve-app-command`, which runs `agent-bundle serve-app` as a child process and prints its URL), call `hauler_status` once so it opens populated, and stay in the foreground until Ctrl-C. A checkout command: it needs the built `artifact/` beside the CLI and `agent-bundle` under `node_modules` (`pnpm install && pnpm build`); the npm package ships no runtime dependencies and an installed host pack has no artifact, so both report what is missing. In an MCP host, call `hauler_status` instead. | The `hauler` MCP server projects the same operations as `hauler_status`, `hauler_log`, `hauler_last`, `hauler_await`, `hauler_result`, `hauler_kill`, @@ -872,14 +872,17 @@ pnpm run check # the gate To see the dashboard outside an MCP host, run `node dist/bin/hauler.js dashboard` after a build: it serves the `ui://cargo-hauler/dashboard.html` App -standalone against the generated `hauler` server (`serveApp` from -`agent-bundle/api`, the Workbench's own host stack), so the data is the -daemon's own. `pnpm run dev` and the Workbench's MCP page preview the same App -with live rebuilds. The repository ships no preview harness of its own. +standalone against the generated `hauler` server (`spawnServeApp` from +`agent-bundle/serve-app-command` runs `agent-bundle serve-app`, the +Workbench's own host stack, as a child process — the routed bin stays +self-contained and never imports the compiler, agent-bundle `AB4837`), so the +data is the daemon's own. `pnpm run dev` and the Workbench's MCP page preview +the same App with live rebuilds. The repository ships no preview harness of +its own. agent-bundle does not yet have an npm release; this repository pins the [pkg.pr.new](https://pkg.pr.new) preview of main commit -[`cd0b4a62c`](https://github.com/ScriptedAlchemy/agent-bundle/commit/cd0b4a62cd92b3fbced6742b3cead32d9639c3e4) +[`d30d9acb6`](https://github.com/ScriptedAlchemy/agent-bundle/commit/d30d9acb6bd106762bb975ac58d6bf437c176091) for both `agent-bundle` and `@agent-bundle/runtime`. `inspect` reports the `agent` component kind as unavailable on every host (agent-bundle G5 deferral); this plugin defines no agents. diff --git a/package.json b/package.json index af12972f..f3636acf 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "release:version": "changeset version" }, "devDependencies": { - "@agent-bundle/runtime": "https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4", + "@agent-bundle/runtime": "https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@d30d9acb6bd106762bb975ac58d6bf437c176091", "@changesets/cli": "3.0.1", "@effect/atom-react": "4.0.0-rc.112", "@effect/platform-node": "4.0.0-rc.112", @@ -69,7 +69,7 @@ "@types/react": "^19.2.18", "@types/react-dom": "^19.2.5", "@typescript/native-preview": "^7.0.0-dev.20260707.2", - "agent-bundle": "https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4", + "agent-bundle": "https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@d30d9acb6bd106762bb975ac58d6bf437c176091", "bashjsast": "github:woolkingx/bashjsast#131f4b677d1906774fc49cfceab0ccb7b370b2e6", "effect": "4.0.0-rc.112", "effect-rstest": "https://pkg.pr.new/ScriptedAlchemy/effect-rstest@e5f8d5f", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72ce6ff2..b0d7f7f1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: devDependencies: '@agent-bundle/runtime': - specifier: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4 - version: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@d30d9acb6bd106762bb975ac58d6bf437c176091 + version: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@d30d9acb6bd106762bb975ac58d6bf437c176091(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@changesets/cli': specifier: 3.0.1 version: 3.0.1 @@ -45,8 +45,8 @@ importers: specifier: ^7.0.0-dev.20260707.2 version: 7.0.0-dev.20260707.2 agent-bundle: - specifier: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4 - version: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4(@agent-bundle/runtime@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@rspack/core@2.2.2(@swc/helpers@0.5.23))(@rstest/core@0.11.11)(@typescript/native-preview@7.0.0-dev.20260707.2)(hono@4.13.5)(react@19.2.8)(typescript@7.0.2) + specifier: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@d30d9acb6bd106762bb975ac58d6bf437c176091 + version: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@d30d9acb6bd106762bb975ac58d6bf437c176091(@agent-bundle/runtime@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@d30d9acb6bd106762bb975ac58d6bf437c176091(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@rspack/core@2.2.2(@swc/helpers@0.5.23))(@rstest/core@0.11.11)(hono@4.13.5)(react@19.2.8)(typescript@7.0.2) bashjsast: specifier: github:woolkingx/bashjsast#131f4b677d1906774fc49cfceab0ccb7b370b2e6 version: https://codeload.github.com/woolkingx/bashjsast/tar.gz/131f4b677d1906774fc49cfceab0ccb7b370b2e6 @@ -77,75 +77,78 @@ importers: packages: - '@agent-bundle/runtime@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4': - resolution: {integrity: sha512-8XoyWyhdk4VK82gTNtHAxCdGfJ2zg0p+RZyoc+IweMOPNO5TbkRDJUXbRh7xCRNwIimgFjwFxR3qeVaRm7cyGA==, tarball: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4} - version: 0.0.0-preview-cd0b4a6 + '@agent-bundle/runtime@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@d30d9acb6bd106762bb975ac58d6bf437c176091': + resolution: {integrity: sha512-TfPPyck4SAqiODISGghjLfjoRBuLzC84nCj3h3DJPIDe4aCei7sj5GCTxUPBqaZmc2mlQUdzYWtoVK7PMtJL5Q==, tarball: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@d30d9acb6bd106762bb975ac58d6bf437c176091} + version: 0.0.0-preview-d30d9ac engines: {node: '>=22.19.0'} peerDependencies: '@rspack/core': ^2.2.0-0 react: 19.2.8 react-dom: 19.2.8 + peerDependenciesMeta: + '@rspack/core': + optional: true - '@ast-grep/napi-darwin-arm64@0.37.0': - resolution: {integrity: sha512-QAiIiaAbLvMEg/yBbyKn+p1gX2/FuaC0SMf7D7capm/oG4xGMzdeaQIcSosF4TCxxV+hIH4Bz9e4/u7w6Bnk3Q==} + '@ast-grep/napi-darwin-arm64@0.45.2': + resolution: {integrity: sha512-29+sfXTJ7xxqgTuWAHA89gDAQIiJyAZ1ZiHoupjKIqyn0mhXrktO1WOghlDzyYs++hITEPOeXftxy48Y0tDGDQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@ast-grep/napi-darwin-x64@0.37.0': - resolution: {integrity: sha512-zvcvdgekd4ySV3zUbUp8HF5nk5zqwiMXTuVzTUdl/w08O7JjM6XPOIVT+d2o/MqwM9rsXdzdergY5oY2RdhSPA==} + '@ast-grep/napi-darwin-x64@0.45.2': + resolution: {integrity: sha512-PUjh7Zf4dKNzYLOYhX5wU6O4BgRPdJnD9zkS1n+/5SeK0U1L5YL486RjNgFt9Xyff0PGtP/wDeoHMF5PhKEwsw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@ast-grep/napi-linux-arm64-gnu@0.37.0': - resolution: {integrity: sha512-L7Sj0lXy8X+BqSMgr1LB8cCoWk0rericdeu+dC8/c8zpsav5Oo2IQKY1PmiZ7H8IHoFBbURLf8iklY9wsD+cyA==} + '@ast-grep/napi-linux-arm64-gnu@0.45.2': + resolution: {integrity: sha512-bnGKLLHWO13pjPChOFq7Ifqj3HJdOAxMQAGDCUC2JSJhO2YL8/xBZrpN0cHIWNuoUNSAXjCtr5AsCnPz/3jlFg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@ast-grep/napi-linux-arm64-musl@0.37.0': - resolution: {integrity: sha512-LF9sAvYy6es/OdyJDO3RwkX3I82Vkfsng1sqUBcoWC1jVb1wX5YVzHtpQox9JrEhGl+bNp7FYxB4Qba9OdA5GA==} + '@ast-grep/napi-linux-arm64-musl@0.45.2': + resolution: {integrity: sha512-FXVb4/V55THjhKxi5zWVrAO8JQj+/DiYP7JLsqenkm5V3UNuC8p2opGOyQpUvNpvcqJiZA9Hl+Onj/yfwSVr/A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@ast-grep/napi-linux-x64-gnu@0.37.0': - resolution: {integrity: sha512-TViz5/klqre6aSmJzswEIjApnGjJzstG/SE8VDWsrftMBMYt2PTu3MeluZVwzSqDao8doT/P+6U11dU05UOgxw==} + '@ast-grep/napi-linux-x64-gnu@0.45.2': + resolution: {integrity: sha512-mtEIPLV9MW2PBswFcp3ydM4T5Ub73s89SRd0QMT2+DfA9XBLCLhnC+r/Yt4eRgnf8FyNx49bFTPxXvFKscr9hw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@ast-grep/napi-linux-x64-musl@0.37.0': - resolution: {integrity: sha512-/BcCH33S9E3ovOAEoxYngUNXgb+JLg991sdyiNP2bSoYd30a9RHrG7CYwW6fMgua3ijQ474eV6cq9yZO1bCpXg==} + '@ast-grep/napi-linux-x64-musl@0.45.2': + resolution: {integrity: sha512-WLLHxDmuXkb/e3mz/xDCrB2dZPzv1ntsepuRTtpaIGczxnQTgFSzX/CXPD0e1MfsU/QL1AiXxxni8w+NGy/ZpQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@ast-grep/napi-win32-arm64-msvc@0.37.0': - resolution: {integrity: sha512-TjQA4cFoIEW2bgjLkaL9yqT4XWuuLa5MCNd0VCDhGRDMNQ9+rhwi9eLOWRaap3xzT7g+nlbcEHL3AkVCD2+b3A==} + '@ast-grep/napi-win32-arm64-msvc@0.45.2': + resolution: {integrity: sha512-04iOFaMzD2nf8CKw3nURWN42900wqPtUTQaMo1I2KRk3qbgtHSJjJoVJGAWUXkCq4LhPg3Xlk1s+ssWf4rNm2A==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@ast-grep/napi-win32-ia32-msvc@0.37.0': - resolution: {integrity: sha512-uNmVka8fJCdYsyOlF9aZqQMLTatEYBynjChVTzUfFMDfmZ0bihs/YTqJVbkSm8TZM7CUX82apvn50z/dX5iWRA==} + '@ast-grep/napi-win32-ia32-msvc@0.45.2': + resolution: {integrity: sha512-tE3y0RQcajocKwpYLbgHKd4lzGGAx1wGMA0bGPsWdoFK8VSob6ZDuYdSlSdnaRs8jAc4F3axToNRKn15p+0/Dg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@ast-grep/napi-win32-x64-msvc@0.37.0': - resolution: {integrity: sha512-vCiFOT3hSCQuHHfZ933GAwnPzmL0G04JxQEsBRfqONywyT8bSdDc/ECpAfr3S9VcS4JZ9/F6tkePKW/Om2Dq2g==} + '@ast-grep/napi-win32-x64-msvc@0.45.2': + resolution: {integrity: sha512-o8Z5Z42TAJe0fPnb11rrjtte58wlw/MuR4KpVMHl8IhJLBBR7gHR0E8wfMz88TxzsQz0U2KYev0k0a2fV4i0sQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@ast-grep/napi@0.37.0': - resolution: {integrity: sha512-Hb4o6h1Pf6yRUAX07DR4JVY7dmQw+RVQMW5/m55GoiAT/VRoKCWBtIUPPOnqDVhbx1Cjfil9b6EDrgJsUAujEQ==} + '@ast-grep/napi@0.45.2': + resolution: {integrity: sha512-hBA5c7JV6OM7zFWoLjUNZhsDpxEIEIeew5S3z1giwK5tPQqobuFfM2OhS45bN3E0+yQtgCX/o8/AEHKbGD3ykg==} engines: {node: '>= 10'} '@changesets/apply-release-plan@8.0.0': @@ -291,12 +294,6 @@ packages: peerDependencies: hono: ^4 - '@hono/node-server@2.1.1': - resolution: {integrity: sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==} - engines: {node: '>=20'} - peerDependencies: - hono: ^4 - '@manypkg/find-root@3.1.0': resolution: {integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==} engines: {node: '>=20.0.0'} @@ -327,16 +324,6 @@ packages: hono: optional: true - '@modelcontextprotocol/sdk@1.30.0': - resolution: {integrity: sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==} - engines: {node: '>=18'} - peerDependencies: - '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@cfworker/json-schema': - optional: true - '@modelcontextprotocol/server@2.0.0': resolution: {integrity: sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==} engines: {node: '>=20'} @@ -429,18 +416,8 @@ packages: peerDependencies: '@redis/client': ^6.2.1 - '@rsbuild/core@2.1.13': - resolution: {integrity: sha512-Z+6MzmjOio4+bFZQ24k+7ge/oNCOdXIunAssrswTNE8AIf6mcyXpJZevRXRiOEMZasRPA8VNyh+9JngQLg729Q==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - core-js: '>= 3.0.0' - peerDependenciesMeta: - core-js: - optional: true - - '@rsbuild/core@2.2.1': - resolution: {integrity: sha512-JcGtG4bo7PBihj6fBL6gaxaJihqLf7nGWW/t4zEmXpMJkV6XNdr1jAo9B0xI4mLAOmtFeva8cUbn9SE2ZEmAIw==} + '@rsbuild/core@2.2.2': + resolution: {integrity: sha512-T82jUaeMUFhcBqpmKvRiNAcWMp6abxkvMEwiZi4x+gf7NAbyiF5jrbLnQLm1y4909eANsTSHenp99HlRPIByLw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -449,8 +426,8 @@ packages: core-js: optional: true - '@rsbuild/core@2.2.2': - resolution: {integrity: sha512-T82jUaeMUFhcBqpmKvRiNAcWMp6abxkvMEwiZi4x+gf7NAbyiF5jrbLnQLm1y4909eANsTSHenp99HlRPIByLw==} + '@rsbuild/core@2.2.3': + resolution: {integrity: sha512-oJrYtYDhb7AMwY8HIda2hgMuuxHkYPYar4svdS5uTq0fXY0M1wLfllkTQz0d729pNJ4S//6GUM1WX5LsW2mFLw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -467,13 +444,13 @@ packages: '@rsbuild/core': optional: true - '@rslib/core@0.23.2': - resolution: {integrity: sha512-KxSp+/y0BJ1O4oKwxX4ydBY7/ZVeJCUpWAmQniCIct8mTxIQFPGO/ibKbKq2IxZYqRuRpM8g+Dtyq0VbEQf9HQ==} + '@rslib/core@1.0.0': + resolution: {integrity: sha512-eZNXCWU1v5oti4+DKCunc76DkNnYqMgRtks9UyZRRFlmaK4pKAxhejeCUS+XkbMurGIAa+aDyVynQD7DXEgK1w==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@microsoft/api-extractor': ^7 - typescript: ^5 || ^6 || ^7.0.1-0 + typescript: ^5 || ^6 || ^7 peerDependenciesMeta: '@microsoft/api-extractor': optional: true @@ -533,178 +510,86 @@ packages: cpu: [x64] os: [win32] - '@rspack/binding-darwin-arm64@2.1.10': - resolution: {integrity: sha512-DZlcTpbIb2mjeS1aSG4k01UH33Zj7T+k8ZylPK6HmsKs4JvK4wgpWFC78WVv3p/Aj3MZS6DwtDLwpZ2Ihj/fpg==} - cpu: [arm64] - os: [darwin] - '@rspack/binding-darwin-arm64@2.2.2': resolution: {integrity: sha512-/le/AvV4HSinXTPs2Lqpujxt189Z5T10Ggt96QzckLRPvIchzqoavVDDjltzC7XcaZ87XCH/X06jNKgzkcBBNA==} cpu: [arm64] os: [darwin] - '@rspack/binding-darwin-x64@2.1.10': - resolution: {integrity: sha512-my/0h2LwxCRT6cg3oDDC2e0ZOxQLVajAdIcv0fqnQk5JRNvVuL89PuTutitnSqie1A0/JSL8OQz5XHwmoS3kow==} - cpu: [x64] - os: [darwin] - '@rspack/binding-darwin-x64@2.2.2': resolution: {integrity: sha512-uFIcUPUXiPxM6ljenLafp5TemT8eLZm1riRn8fJYmpqNCK+aCcTaud18XHZpI5SjzzcY+xUHfVShgvNzKXuf2g==} cpu: [x64] os: [darwin] - '@rspack/binding-linux-arm64-gnu@2.1.10': - resolution: {integrity: sha512-laevn9g+E5PAUEGqiKe6Ju5KApsuQYp+bPI17XS3Lkl8eqL5pS/BmHYU7QMlst4GzV8+wlruVTMh//+st6Vqzg==} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@rspack/binding-linux-arm64-gnu@2.2.2': resolution: {integrity: sha512-Pjby4pDSMNJQK2VBzpgCj6lb+DGuenS1fEDb6xi5/apbJb9v5WE+e43Mz7i+XqgXS8e846pjaONV3KM5WKB1LQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@rspack/binding-linux-arm64-musl@2.1.10': - resolution: {integrity: sha512-V71+Qz5G72+ROZXrJn5zxOszdG1AEbO8pcC/itXXtf4yRR6a3bVHKNKGhipBNxb8eI6cnD/01FH1h3ZG655jLw==} - cpu: [arm64] - os: [linux] - libc: [musl] - '@rspack/binding-linux-arm64-musl@2.2.2': resolution: {integrity: sha512-0u9O7tTVT2z+F6o/eEY6f6+My8Jn9U56QiBwkfy90ZaoAfZyQSSTxqaK/zA7W43oFxQefeCpiPas27VUzrSakw==} cpu: [arm64] os: [linux] libc: [musl] - '@rspack/binding-linux-ppc64-gnu@2.1.10': - resolution: {integrity: sha512-U7HlNzHcDtZ+LYOtOJmtx67kHEybZzUUAaP7aEXjGYO5WTCgh/176sW2UYP0rmZLrgUNFUuzn+B98RLaClNaVg==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@rspack/binding-linux-ppc64-gnu@2.2.2': resolution: {integrity: sha512-N3lVnhq5qOvpmP5n386JzR1GcE8HzAqq4/r440Z6yle9m7PhVFJ6oXVWxgaDTYV0mtv9YLJmaG+YrjYfUa1vuA==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rspack/binding-linux-riscv64-gnu@2.1.10': - resolution: {integrity: sha512-GMGTJpy9/ecE+5F5IfxZH4bXv0Wx/b2TiehTlCbTksbL+pKpLHYy0rwGdjWDKbmBkhxMMqPiC7PDnn9LbdnnLA==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@rspack/binding-linux-riscv64-gnu@2.2.2': resolution: {integrity: sha512-ReClZyp32/rJkUDV/oGDU0X6BCFyEkTR6r4stFwm/qOOaG6mUMRzZe4gur8Yh979p4Hiz9EdkmfEHY02GBXcaQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rspack/binding-linux-riscv64-musl@2.1.10': - resolution: {integrity: sha512-rkurnAWc04vIbzG1QCrPBWSJadZvaOt1mazFH3EdiJO8VUiu0I1T9zdiwuDOPrd50lOKIZlcTXbd5aaAkWEnvQ==} - cpu: [riscv64] - os: [linux] - libc: [musl] - '@rspack/binding-linux-riscv64-musl@2.2.2': resolution: {integrity: sha512-mqsorvTNerr3r8zI35NFTPYATPDlhepdiUhZjKT6ylqpHlP7vLU9fp4aEMK/CuTv2f+IVsa0+iZqtMxHs2tSjw==} cpu: [riscv64] os: [linux] libc: [musl] - '@rspack/binding-linux-s390x-gnu@2.1.10': - resolution: {integrity: sha512-X+DyxkriZEAF/wihI7ERDv+CAS0mbMv36aEuQ+vXzTlvS6cSmpou/r29AHbvIF3NlG1UeAbDVlOs9QrMBZjpUQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@rspack/binding-linux-s390x-gnu@2.2.2': resolution: {integrity: sha512-ql2Jub8QYWSugBppmj2u0SjcIj6fOQuAaLCYQOqU7zbvz/uuWTfoqmdWKExwrxeCU9Tzt7emVM0ETuVEpoca+A==} cpu: [s390x] os: [linux] libc: [glibc] - '@rspack/binding-linux-x64-gnu@2.1.10': - resolution: {integrity: sha512-Fat09V6jUuyo9qG7Wyj9cQ31VDfLmokXyBtGqKxY5OvSWHereB7QUub5btbPXHwbp6Iq4aAQyUbbLTzvR1YaBw==} - cpu: [x64] - os: [linux] - libc: [glibc] - '@rspack/binding-linux-x64-gnu@2.2.2': resolution: {integrity: sha512-MNYKYEHrtIVEno2q5rgpou/JVffRwn109xPK3kxct95EojHsniNa8jwy6eEHeOazP4EN60Si7NElE3aQ6JssHw==} cpu: [x64] os: [linux] libc: [glibc] - '@rspack/binding-linux-x64-musl@2.1.10': - resolution: {integrity: sha512-lhHOnIJ4ClpIlA1f1L8aoxEZivYLjnjq5A6jKKz7BKsm+cHK8kqqEm6lO5KqA5xQT0Lonq1o28bmKHEj6JHInw==} - cpu: [x64] - os: [linux] - libc: [musl] - '@rspack/binding-linux-x64-musl@2.2.2': resolution: {integrity: sha512-y9/9CmE8lrECaF17GAhacibTL+SvlEPEotQnUuBFC/WFtXh8hU2E7a7bAkpYGSLH6kM0nrJZHO3hTvM1wdWOJw==} cpu: [x64] os: [linux] libc: [musl] - '@rspack/binding-wasm32-wasi@2.1.10': - resolution: {integrity: sha512-KY5YbWbuvYcoaLXnV+vzZOvGRCeb6jt4EpVpKdph1h1IJjwX/ju15EQ+GOe3iecZEdf0OttQcNVcwBkLkFT9ag==} - cpu: [wasm32] - '@rspack/binding-wasm32-wasi@2.2.2': resolution: {integrity: sha512-VbDIjjeFwZvMSKAOGY5IbU6lLzt6AHHHncTdMMkZ94Xk7O2BOHe9BXDV32Ln29TIW2C8m1fdxfPZWDiecVghUQ==} cpu: [wasm32] - '@rspack/binding-win32-arm64-msvc@2.1.10': - resolution: {integrity: sha512-z4GWzMLofaDGpAt9Z+MlN88LlUBDm+zM6R2GdOOPM6/4g/h3/+47OP7casmSL3AwTGYBEJqogwt08sRSosB6Cg==} - cpu: [arm64] - os: [win32] - '@rspack/binding-win32-arm64-msvc@2.2.2': resolution: {integrity: sha512-rfcNg0W3ZPZvXma1gTyEt9/Z8FxASIaQr+sMWTSaTPPaeU3xY1+0hYcrD0kUFNs3/5L4u63myI4R8qRXiuW3pA==} cpu: [arm64] os: [win32] - '@rspack/binding-win32-ia32-msvc@2.1.10': - resolution: {integrity: sha512-7qcWdsZ+GuGtzKjqgy7wTN7Dso/ezIY8yhx1r2yIbcczdmXj4FhaEampMDp/25HwtKwIGBBoh6HHSt3JWxpTUg==} - cpu: [ia32] - os: [win32] - '@rspack/binding-win32-ia32-msvc@2.2.2': resolution: {integrity: sha512-TFPvr9RZw9oHIhooDhXHzWjKcHpGPTxkznSeM2poIWU0CdEuua2rVUfsrriTF1Dmx+9kMly61DQmyOHCbb+b2g==} cpu: [ia32] os: [win32] - '@rspack/binding-win32-x64-msvc@2.1.10': - resolution: {integrity: sha512-pgp23pLrzfhGnKycxzr7ifP17lAbWZEfnx1bX8gXtYrnpJ66DRNyTKSzxB6sa/HBWjS1L8PX5TjMZ44WfPydqQ==} - cpu: [x64] - os: [win32] - '@rspack/binding-win32-x64-msvc@2.2.2': resolution: {integrity: sha512-GvEGyL594dtWN9SoVnKWh0exrM8WLInaUjwcuA2JKbCi1ak/9iHxip/U1dY3DuVqBYBhmvYq96JPbl91xnzFjg==} cpu: [x64] os: [win32] - '@rspack/binding@2.1.10': - resolution: {integrity: sha512-vnu/UP5HnrND15lO9+VeG6eUrbTyycHNQNQ3XEiRiFojuoiGZkIZC3Hbzr8qQH44C6vScPODEPvvIVvLcO2LpQ==} - '@rspack/binding@2.2.2': resolution: {integrity: sha512-gWjKDQfVQJSBh/I+y9WTlyERsiShSJ7eI6Yl0SJs/6gjx8t4ixuwWCsaEFFjwSL4nSn6ML5hNQ7UFY3gj72BWA==} - '@rspack/core@2.1.10': - resolution: {integrity: sha512-YSS2/Xxz8uiG/KXDkqOoA3dTetNo/vysk7bAexQOrU8iuq7JuzDTTAwLKvWZnwmvME8M8m5wcM4YvfIwYmidHA==} - engines: {node: ^20.19.0 || >=22.12.0} - peerDependencies: - '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0 - '@swc/helpers': ^0.5.23 - peerDependenciesMeta: - '@module-federation/runtime-tools': - optional: true - '@swc/helpers': - optional: true - '@rspack/core@2.2.2': resolution: {integrity: sha512-/yztfDZR5syIPBrUpzBpL+6fhhl0IHBPcXlNr4tOMBULbocFIz7Z4/cqvf1ix0DBbKpIGx99v6N1IDbk2gi8hw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -946,22 +831,18 @@ packages: cpu: [x64] os: [win32] - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - acorn@8.18.0: resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} engines: {node: '>=0.4.0'} hasBin: true - agent-bundle@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4: - resolution: {integrity: sha512-0Xe+YUTxWapV9Sf+98c3PhYmSdAG9FOMLy/5XZoh+dMc5tYfEUzaBhtfYxbPIDItQE8/d/2Vr1PCCpaPYr0iRg==, tarball: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4} - version: 0.0.0-preview-cd0b4a6 + agent-bundle@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@d30d9acb6bd106762bb975ac58d6bf437c176091: + resolution: {integrity: sha512-9Qosvkw5o/zy5gSvP1zSe6gJHml/XaQKd9V+onJLn3eYdPKK2BOLNxOUEqgaGfNoEfJETnMQpn1emdM3IrrBVg==, tarball: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@d30d9acb6bd106762bb975ac58d6bf437c176091} + version: 0.0.0-preview-d30d9ac engines: {node: '>=22.19.0'} hasBin: true peerDependencies: - '@agent-bundle/runtime': 0.0.0-preview-cd0b4a6 + '@agent-bundle/runtime': 0.0.0-preview-d30d9ac '@rstest/core': ^0.11.10 react: 19.2.8 peerDependenciesMeta: @@ -991,10 +872,6 @@ packages: resolution: {gitHosted: true, integrity: sha512-FlWFuz0lwF0kpWZ2XPfV6jKC9SL7Gf654JvGmFsXWLni105Tv2wMEw9rLhXcB1rm/AvPYgCJeH/JZVc/9WblpA==, tarball: https://codeload.github.com/woolkingx/bashjsast/tar.gz/131f4b677d1906774fc49cfceab0ccb7b370b2e6} version: 0.2.0 - body-parser@2.3.0: - resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} - engines: {node: '>=18'} - braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -1003,22 +880,10 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - cac@7.0.0: resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} engines: {node: '>=20.19.0'} - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - chokidar@5.0.0: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} @@ -1031,30 +896,6 @@ packages: resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} engines: {node: '>=22.12.0'} - content-disposition@1.1.0: - resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} - engines: {node: '>=18'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - content-type@2.1.0: - resolution: {integrity: sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==} - engines: {node: '>=18'} - - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - - cors@2.8.6: - resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} - engines: {node: '>= 0.10'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1062,15 +903,6 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - default-browser-id@5.0.1: resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} engines: {node: '>=18'} @@ -1083,21 +915,10 @@ packages: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - effect-rstest@https://pkg.pr.new/ScriptedAlchemy/effect-rstest@e5f8d5f: resolution: {integrity: sha512-vAoaIALD1PEKZDHdX8RdFKzJQtV5FTTzmGt9+xSbjbCJyPX/yKXvL9kJlR9QjSXRxGwFcxy+v+QxEK2ja7JKpA==, tarball: https://pkg.pr.new/ScriptedAlchemy/effect-rstest@e5f8d5f} version: 0.1.0 @@ -1108,32 +929,9 @@ packages: effect@4.0.0-rc.112: resolution: {integrity: sha512-wXxwuh1Ywnv4cPRM3Wfa0vDwuOHnZ1TsTgHJkG9XgzND6inhBH9n1vBxhg3iIXOia/OrpmvVmd3lrD4vq6bF3A==} - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} - es-object-atoms@1.1.2: - resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} - engines: {node: '>= 0.4'} - - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - eventsource-parser@3.1.1: resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} engines: {node: '>=18.0.0'} @@ -1142,16 +940,6 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} - express-rate-limit@8.7.0: - resolution: {integrity: sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==} - engines: {node: '>= 16'} - peerDependencies: - express: '>= 4.11' - - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} - fast-check@4.9.0: resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==} engines: {node: '>=12.17.0'} @@ -1191,53 +979,18 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} - flare-redact@1.6.1: resolution: {integrity: sha512-13Htu6VPk2ttxcoVOP909pXao6orrIBAMS/qWdhQn3/BS5AxSB5fpyqaPZnRGJhfBAM2M9T+RE662hxEpv6nGQ==} engines: {node: '>=20'} hasBin: true - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - hasown@2.0.4: - resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} - engines: {node: '>= 0.4'} - hono@4.13.5: resolution: {integrity: sha512-O6+/eCYRkzzzy0rPWwKLiGBR1nFuUPZynnwjxN1MBA62NNqbT0wQEzQyK2gSO5yDIDB336sXQleAhOHrzlYyKw==} engines: {node: '>=16.9.0'} @@ -1246,18 +999,10 @@ packages: resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} engines: {node: ^20.17.0 || >=22.9.0} - http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} - engines: {node: '>= 0.8'} - human-id@4.2.1: resolution: {integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==} hasBin: true - iconv-lite@0.7.3: - resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} - engines: {node: '>=0.10.0'} - ignore@7.0.7: resolution: {integrity: sha512-dML0wP6oak21rsNYCJpJB6O1BJIEwNpGrTw0URPfAk4hm0e3pRfCtzkfB6olBcXcVlU2rouCyz7lCyRB0OMVCA==} engines: {node: '>= 4'} @@ -1265,17 +1010,6 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ip-address@10.7.0: - resolution: {integrity: sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==} - engines: {node: '>= 12'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1302,9 +1036,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - is-wsl@3.1.1: resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} @@ -1325,9 +1056,6 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} - jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} @@ -1338,18 +1066,6 @@ packages: resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - media-typer@1.1.1: - resolution: {integrity: sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==} - engines: {node: '>= 0.8'} - - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1358,22 +1074,11 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@3.0.2: - resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} - engines: {node: '>=18'} - mime@4.1.0: resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} engines: {node: '>=16'} hasBin: true - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msgpackr-extract@3.0.4: resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==} hasBin: true @@ -1381,10 +1086,6 @@ packages: msgpackr@2.1.0: resolution: {integrity: sha512-p/pBCVO63CsvvpkomUnNNag6+n38rULuDA6HHe70o2gtC8ODI52foF/4ko2qQcp6OiErJXTmrZeXmsGGHsIQNQ==} - negotiator@1.1.0: - resolution: {integrity: sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==} - engines: {node: '>=18'} - node-gyp-build-optional-packages@5.2.2: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true @@ -1393,21 +1094,6 @@ packages: resolution: {integrity: sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==} engines: {node: ^20.17.0 || >=22.9.0} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - open@11.0.2: resolution: {integrity: sha512-RWqF+pBSkqecEvCKOn8QYhaNdRMJDZRIrlS/7rTDdLHaPcfXGCZ/h8zb413NfvdeAV0MR7T1yJcA34/q+CSm1Q==} engines: {node: '>=20'} @@ -1415,17 +1101,10 @@ packages: package-manager-detector@1.8.0: resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-to-regexp@8.4.2: - resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1456,28 +1135,12 @@ packages: proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - pure-rand@8.4.2: resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==} - qs@6.16.0: - resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} - engines: {node: '>=0.6'} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - range-parser@1.3.0: - resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} - engines: {node: '>= 0.6'} - - raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} - react-dom@19.2.8: resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} peerDependencies: @@ -1519,29 +1182,22 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - - rsbuild-plugin-dts@0.23.2: - resolution: {integrity: sha512-Ve8WOSPyTBjxH40O/6fChJtflL7yqcUtt5zbF0eHDM4hIt35jqLocRlSdCxFJdQm0blTjl6+hkmPa/vNI06ArA==} + rsbuild-plugin-dts@1.0.0: + resolution: {integrity: sha512-FVoTsiTfSc0LPeSjrVhpvFOBRig0YXJq3Hp+bVxzh9/bzFvODzLRWOEux8q1Phe1oVCIIVSc2+Nb7kDcxIhosw==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: '@microsoft/api-extractor': ^7 - '@rsbuild/core': ^1.0.0 || ^2.0.0-0 - '@typescript/native-preview': ^7.0.0-0 - typescript: ^5 || ^6 || ^7.0.1-0 + '@rsbuild/core': ^2.0.0 + typescript: ^5 || ^6 || ^7 peerDependenciesMeta: '@microsoft/api-extractor': optional: true - '@typescript/native-preview': - optional: true typescript: optional: true - rsc-markdown-stream@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4: - resolution: {integrity: sha512-a6AbvP8lHduILq3HZpOEkUrfMU9wlInmoOenRHr7FbMSDkMgKmKFC9eDCU9UiGieq7l8AzsYX4+QNMHYNUCsdg==, tarball: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4} - version: 0.0.0-preview-cd0b4a6 + rsc-markdown-stream@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@d30d9acb6bd106762bb975ac58d6bf437c176091: + resolution: {integrity: sha512-Klh0xMj4UdOpxWMr/e2DcujKgiqg6siqzZ6h0U1Ee1lCTnW+yeMYLsL4Zazr4cdMiCsTklaQjo7JxzVDOK8z7Q==, tarball: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@d30d9acb6bd106762bb975ac58d6bf437c176091} + version: 0.0.0-preview-d30d9ac engines: {node: '>=22.19.0'} peerDependencies: react: ^19.0.0 @@ -1553,9 +1209,6 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -1564,17 +1217,6 @@ packages: engines: {node: '>=10'} hasBin: true - send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} - engines: {node: '>= 18'} - - serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} - engines: {node: '>= 18'} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1587,32 +1229,12 @@ packages: resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} engines: {node: '>= 0.4'} - side-channel-list@1.0.1: - resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.1: - resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} - engines: {node: '>= 0.4'} - signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} - tinyexec@1.3.0: resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} engines: {node: '>=18'} @@ -1625,17 +1247,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - type-is@2.1.0: - resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} - engines: {node: '>= 18'} - typescript@7.0.2: resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} engines: {node: '>=16.20.0'} @@ -1648,26 +1262,15 @@ packages: resolution: {integrity: sha512-YQ3WlbqjYMmNpdvDH64jAgLjxuAR9+649calDWhbshYaeQGO2bR4nI94ORJmwI3J9YhoKQnpyGOK+0zlWS5N5Q==} engines: {node: '>=22.19.0'} - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - validate-npm-package-name@7.0.2: resolution: {integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==} engines: {node: ^20.17.0 || >=22.9.0} - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.21.3: resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} engines: {node: '>=10.0.0'} @@ -1689,70 +1292,62 @@ packages: engines: {node: '>= 14.6'} hasBin: true - zod-to-json-schema@3.25.2: - resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} - peerDependencies: - zod: ^3.25.28 || ^4 - zod@4.5.4: resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} snapshots: - '@agent-bundle/runtime@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@agent-bundle/runtime@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@d30d9acb6bd106762bb975ac58d6bf437c176091(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@modelcontextprotocol/sdk': 1.30.0(zod@4.5.4) '@modelcontextprotocol/server': 2.0.0 - '@rspack/core': 2.2.2(@swc/helpers@0.5.23) effect: 4.0.0-rc.112 flare-redact: 1.6.1 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) react-server-dom-rspack: 0.1.0(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - rsc-markdown-stream: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4(react@19.2.8) + rsc-markdown-stream: https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@d30d9acb6bd106762bb975ac58d6bf437c176091(react@19.2.8) zod: 4.5.4 - transitivePeerDependencies: - - '@cfworker/json-schema' - - supports-color + optionalDependencies: + '@rspack/core': 2.2.2(@swc/helpers@0.5.23) - '@ast-grep/napi-darwin-arm64@0.37.0': + '@ast-grep/napi-darwin-arm64@0.45.2': optional: true - '@ast-grep/napi-darwin-x64@0.37.0': + '@ast-grep/napi-darwin-x64@0.45.2': optional: true - '@ast-grep/napi-linux-arm64-gnu@0.37.0': + '@ast-grep/napi-linux-arm64-gnu@0.45.2': optional: true - '@ast-grep/napi-linux-arm64-musl@0.37.0': + '@ast-grep/napi-linux-arm64-musl@0.45.2': optional: true - '@ast-grep/napi-linux-x64-gnu@0.37.0': + '@ast-grep/napi-linux-x64-gnu@0.45.2': optional: true - '@ast-grep/napi-linux-x64-musl@0.37.0': + '@ast-grep/napi-linux-x64-musl@0.45.2': optional: true - '@ast-grep/napi-win32-arm64-msvc@0.37.0': + '@ast-grep/napi-win32-arm64-msvc@0.45.2': optional: true - '@ast-grep/napi-win32-ia32-msvc@0.37.0': + '@ast-grep/napi-win32-ia32-msvc@0.45.2': optional: true - '@ast-grep/napi-win32-x64-msvc@0.37.0': + '@ast-grep/napi-win32-x64-msvc@0.45.2': optional: true - '@ast-grep/napi@0.37.0': + '@ast-grep/napi@0.45.2': optionalDependencies: - '@ast-grep/napi-darwin-arm64': 0.37.0 - '@ast-grep/napi-darwin-x64': 0.37.0 - '@ast-grep/napi-linux-arm64-gnu': 0.37.0 - '@ast-grep/napi-linux-arm64-musl': 0.37.0 - '@ast-grep/napi-linux-x64-gnu': 0.37.0 - '@ast-grep/napi-linux-x64-musl': 0.37.0 - '@ast-grep/napi-win32-arm64-msvc': 0.37.0 - '@ast-grep/napi-win32-ia32-msvc': 0.37.0 - '@ast-grep/napi-win32-x64-msvc': 0.37.0 + '@ast-grep/napi-darwin-arm64': 0.45.2 + '@ast-grep/napi-darwin-x64': 0.45.2 + '@ast-grep/napi-linux-arm64-gnu': 0.45.2 + '@ast-grep/napi-linux-arm64-musl': 0.45.2 + '@ast-grep/napi-linux-x64-gnu': 0.45.2 + '@ast-grep/napi-linux-x64-musl': 0.45.2 + '@ast-grep/napi-win32-arm64-msvc': 0.45.2 + '@ast-grep/napi-win32-ia32-msvc': 0.45.2 + '@ast-grep/napi-win32-x64-msvc': 0.45.2 '@changesets/apply-release-plan@8.0.0': dependencies: @@ -1947,10 +1542,6 @@ snapshots: dependencies: hono: 4.13.5 - '@hono/node-server@2.1.1(hono@4.13.5)': - dependencies: - hono: 4.13.5 - '@manypkg/find-root@3.1.0': dependencies: '@manypkg/tools': 2.1.2 @@ -1987,28 +1578,6 @@ snapshots: optionalDependencies: hono: 4.13.5 - '@modelcontextprotocol/sdk@1.30.0(zod@4.5.4)': - dependencies: - '@hono/node-server': 2.1.1(hono@4.13.5) - ajv: 8.20.0 - ajv-formats: 3.0.1(ajv@8.20.0) - content-type: 1.0.5 - cors: 2.8.6 - cross-spawn: 7.0.6 - eventsource: 3.0.7 - eventsource-parser: 3.1.1 - express: 5.2.1 - express-rate-limit: 8.7.0(express@5.2.1) - hono: 4.13.5 - jose: 6.2.10 - json-schema-typed: 8.0.2 - pkce-challenge: 5.0.1 - raw-body: 3.0.2 - zod: 4.5.4 - zod-to-json-schema: 3.25.2(zod@4.5.4) - transitivePeerDependencies: - - supports-color - '@modelcontextprotocol/server@2.0.0': dependencies: '@modelcontextprotocol/core': 2.0.0 @@ -2073,45 +1642,37 @@ snapshots: dependencies: '@redis/client': 6.2.1 - '@rsbuild/core@2.1.13': - dependencies: - '@rspack/core': 2.1.10(@swc/helpers@0.5.23) - '@swc/helpers': 0.5.23 - transitivePeerDependencies: - - '@module-federation/runtime-tools' - - '@rsbuild/core@2.2.1': + '@rsbuild/core@2.2.2': dependencies: '@rspack/core': 2.2.2(@swc/helpers@0.5.23) '@swc/helpers': 0.5.23 transitivePeerDependencies: - '@module-federation/runtime-tools' - '@rsbuild/core@2.2.2': + '@rsbuild/core@2.2.3': dependencies: '@rspack/core': 2.2.2(@swc/helpers@0.5.23) '@swc/helpers': 0.5.23 transitivePeerDependencies: - '@module-federation/runtime-tools' - '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.2.1)(@rspack/core@2.2.2(@swc/helpers@0.5.23))': + '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.2.3)(@rspack/core@2.2.2(@swc/helpers@0.5.23))': dependencies: '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-refresh@0.18.0) react-refresh: 0.18.0 optionalDependencies: - '@rsbuild/core': 2.2.1 + '@rsbuild/core': 2.2.3 transitivePeerDependencies: - '@rspack/core' - '@rslib/core@0.23.2(@typescript/native-preview@7.0.0-dev.20260707.2)(typescript@7.0.2)': + '@rslib/core@1.0.0(typescript@7.0.2)': dependencies: - '@rsbuild/core': 2.1.13 - rsbuild-plugin-dts: 0.23.2(@rsbuild/core@2.1.13)(@typescript/native-preview@7.0.0-dev.20260707.2)(typescript@7.0.2) + '@rsbuild/core': 2.2.2 + rsbuild-plugin-dts: 1.0.0(@rsbuild/core@2.2.2)(typescript@7.0.2) optionalDependencies: typescript: 7.0.2 transitivePeerDependencies: - '@module-federation/runtime-tools' - - '@typescript/native-preview' - core-js '@rslint/core@0.8.2(jiti@2.7.0)': @@ -2152,73 +1713,36 @@ snapshots: '@rslint/native-win32-x64-msvc@0.8.2': optional: true - '@rspack/binding-darwin-arm64@2.1.10': - optional: true - '@rspack/binding-darwin-arm64@2.2.2': optional: true - '@rspack/binding-darwin-x64@2.1.10': - optional: true - '@rspack/binding-darwin-x64@2.2.2': optional: true - '@rspack/binding-linux-arm64-gnu@2.1.10': - optional: true - '@rspack/binding-linux-arm64-gnu@2.2.2': optional: true - '@rspack/binding-linux-arm64-musl@2.1.10': - optional: true - '@rspack/binding-linux-arm64-musl@2.2.2': optional: true - '@rspack/binding-linux-ppc64-gnu@2.1.10': - optional: true - '@rspack/binding-linux-ppc64-gnu@2.2.2': optional: true - '@rspack/binding-linux-riscv64-gnu@2.1.10': - optional: true - '@rspack/binding-linux-riscv64-gnu@2.2.2': optional: true - '@rspack/binding-linux-riscv64-musl@2.1.10': - optional: true - '@rspack/binding-linux-riscv64-musl@2.2.2': optional: true - '@rspack/binding-linux-s390x-gnu@2.1.10': - optional: true - '@rspack/binding-linux-s390x-gnu@2.2.2': optional: true - '@rspack/binding-linux-x64-gnu@2.1.10': - optional: true - '@rspack/binding-linux-x64-gnu@2.2.2': optional: true - '@rspack/binding-linux-x64-musl@2.1.10': - optional: true - '@rspack/binding-linux-x64-musl@2.2.2': optional: true - '@rspack/binding-wasm32-wasi@2.1.10': - dependencies: - '@emnapi/core': 1.11.3 - '@emnapi/runtime': 1.11.3 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) - optional: true - '@rspack/binding-wasm32-wasi@2.2.2': dependencies: '@emnapi/core': 1.11.3 @@ -2226,41 +1750,15 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) optional: true - '@rspack/binding-win32-arm64-msvc@2.1.10': - optional: true - '@rspack/binding-win32-arm64-msvc@2.2.2': optional: true - '@rspack/binding-win32-ia32-msvc@2.1.10': - optional: true - '@rspack/binding-win32-ia32-msvc@2.2.2': optional: true - '@rspack/binding-win32-x64-msvc@2.1.10': - optional: true - '@rspack/binding-win32-x64-msvc@2.2.2': optional: true - '@rspack/binding@2.1.10': - optionalDependencies: - '@rspack/binding-darwin-arm64': 2.1.10 - '@rspack/binding-darwin-x64': 2.1.10 - '@rspack/binding-linux-arm64-gnu': 2.1.10 - '@rspack/binding-linux-arm64-musl': 2.1.10 - '@rspack/binding-linux-ppc64-gnu': 2.1.10 - '@rspack/binding-linux-riscv64-gnu': 2.1.10 - '@rspack/binding-linux-riscv64-musl': 2.1.10 - '@rspack/binding-linux-s390x-gnu': 2.1.10 - '@rspack/binding-linux-x64-gnu': 2.1.10 - '@rspack/binding-linux-x64-musl': 2.1.10 - '@rspack/binding-wasm32-wasi': 2.1.10 - '@rspack/binding-win32-arm64-msvc': 2.1.10 - '@rspack/binding-win32-ia32-msvc': 2.1.10 - '@rspack/binding-win32-x64-msvc': 2.1.10 - '@rspack/binding@2.2.2': optionalDependencies: '@rspack/binding-darwin-arm64': 2.2.2 @@ -2278,12 +1776,6 @@ snapshots: '@rspack/binding-win32-ia32-msvc': 2.2.2 '@rspack/binding-win32-x64-msvc': 2.2.2 - '@rspack/core@2.1.10(@swc/helpers@0.5.23)': - dependencies: - '@rspack/binding': 2.1.10 - optionalDependencies: - '@swc/helpers': 0.5.23 - '@rspack/core@2.2.2(@swc/helpers@0.5.23)': dependencies: '@rspack/binding': 2.2.2 @@ -2437,22 +1929,17 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true - accepts@2.0.0: - dependencies: - mime-types: 3.0.2 - negotiator: 1.1.0 - acorn@8.18.0: {} - agent-bundle@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4(@agent-bundle/runtime@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@rspack/core@2.2.2(@swc/helpers@0.5.23))(@rstest/core@0.11.11)(@typescript/native-preview@7.0.0-dev.20260707.2)(hono@4.13.5)(react@19.2.8)(typescript@7.0.2): + agent-bundle@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@d30d9acb6bd106762bb975ac58d6bf437c176091(@agent-bundle/runtime@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@d30d9acb6bd106762bb975ac58d6bf437c176091(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@rspack/core@2.2.2(@swc/helpers@0.5.23))(@rstest/core@0.11.11)(hono@4.13.5)(react@19.2.8)(typescript@7.0.2): dependencies: '@effect/platform-node-shared': 4.0.0-rc.112(effect@4.0.0-rc.112) '@modelcontextprotocol/client': 2.0.0 '@modelcontextprotocol/node': 2.0.0(@modelcontextprotocol/server@2.0.0)(hono@4.13.5) '@modelcontextprotocol/server': 2.0.0 - '@rsbuild/core': 2.2.1 - '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.2.1)(@rspack/core@2.2.2(@swc/helpers@0.5.23)) - '@rslib/core': 0.23.2(@typescript/native-preview@7.0.0-dev.20260707.2)(typescript@7.0.2) + '@rsbuild/core': 2.2.3 + '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.2.3)(@rspack/core@2.2.2(@swc/helpers@0.5.23)) + '@rslib/core': 1.0.0(typescript@7.0.2) '@rslint/core': 0.8.2(jiti@2.7.0) '@rstackjs/load-config': 0.1.2(jiti@2.7.0) acorn: 8.18.0 @@ -2470,14 +1957,13 @@ snapshots: ws: 8.21.3 yaml: 2.9.0 optionalDependencies: - '@agent-bundle/runtime': https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@agent-bundle/runtime': https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@d30d9acb6bd106762bb975ac58d6bf437c176091(@rspack/core@2.2.2(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@rstest/core': 0.11.11 react: 19.2.8 transitivePeerDependencies: - '@microsoft/api-extractor' - '@module-federation/runtime-tools' - '@rspack/core' - - '@typescript/native-preview' - bufferutil - core-js - hono @@ -2499,20 +1985,6 @@ snapshots: bashjsast@https://codeload.github.com/woolkingx/bashjsast/tar.gz/131f4b677d1906774fc49cfceab0ccb7b370b2e6: {} - body-parser@2.3.0: - dependencies: - bytes: 3.1.2 - content-type: 2.1.0 - debug: 4.4.3 - http-errors: 2.0.1 - iconv-lite: 0.7.3 - on-finished: 2.4.1 - qs: 6.16.0 - raw-body: 3.0.2 - type-is: 2.1.0 - transitivePeerDependencies: - - supports-color - braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -2521,20 +1993,8 @@ snapshots: dependencies: run-applescript: 7.1.0 - bytes@3.1.2: {} - cac@7.0.0: {} - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - chokidar@5.0.0: dependencies: readdirp: 5.1.1 @@ -2543,21 +2003,6 @@ snapshots: commander@15.0.0: {} - content-disposition@1.1.0: {} - - content-type@1.0.5: {} - - content-type@2.1.0: {} - - cookie-signature@1.2.2: {} - - cookie@0.7.2: {} - - cors@2.8.6: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -2566,10 +2011,6 @@ snapshots: csstype@3.2.3: {} - debug@4.4.3: - dependencies: - ms: 2.1.3 - default-browser-id@5.0.1: {} default-browser@5.5.1: @@ -2579,19 +2020,9 @@ snapshots: define-lazy-prop@3.0.0: {} - depd@2.0.0: {} - detect-libc@2.1.2: optional: true - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - ee-first@1.1.1: {} - effect-rstest@https://pkg.pr.new/ScriptedAlchemy/effect-rstest@e5f8d5f(@rstest/core@0.11.11)(effect@4.0.0-rc.112): dependencies: '@rstest/core': 0.11.11 @@ -2602,69 +2033,14 @@ snapshots: fast-check: 4.9.0 msgpackr: 2.1.0 - encodeurl@2.0.0: {} - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - es-module-lexer@2.3.2: {} - es-object-atoms@1.1.2: - dependencies: - es-errors: 1.3.0 - - escape-html@1.0.3: {} - - etag@1.8.1: {} - eventsource-parser@3.1.1: {} eventsource@3.0.7: dependencies: eventsource-parser: 3.1.1 - express-rate-limit@8.7.0(express@5.2.1): - dependencies: - debug: 4.4.3 - express: 5.2.1 - ip-address: 10.7.0 - transitivePeerDependencies: - - supports-color - - express@5.2.1: - dependencies: - accepts: 2.0.0 - body-parser: 2.3.0 - content-disposition: 1.1.0 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.1 - fresh: 2.0.0 - http-errors: 2.0.1 - merge-descriptors: 2.0.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.16.0 - range-parser: 1.3.0 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 - statuses: 2.0.2 - type-is: 2.1.0 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - fast-check@4.9.0: dependencies: pure-rand: 8.4.2 @@ -2703,87 +2079,26 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@2.1.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - flare-redact@1.6.1: {} - forwarded@0.2.0: {} - - fresh@2.0.0: {} - - function-bind@1.1.2: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.2 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.4 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.2 - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - gopd@1.2.0: {} - graceful-fs@4.2.11: {} - has-symbols@1.1.0: {} - - hasown@2.0.4: - dependencies: - function-bind: 1.1.2 - hono@4.13.5: {} hosted-git-info@9.0.3: dependencies: lru-cache: 11.5.2 - http-errors@2.0.1: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.2 - toidentifier: 1.0.1 - human-id@4.2.1: {} - iconv-lite@0.7.3: - dependencies: - safer-buffer: 2.1.2 - ignore@7.0.7: {} import-meta-resolve@4.2.0: {} - inherits@2.0.4: {} - - ip-address@10.7.0: {} - - ipaddr.js@1.9.1: {} - is-docker@3.0.0: {} is-extglob@2.1.1: {} @@ -2800,8 +2115,6 @@ snapshots: is-number@7.0.0: {} - is-promise@4.0.0: {} - is-wsl@3.1.1: dependencies: is-inside-container: 1.0.0 @@ -2816,8 +2129,6 @@ snapshots: json-schema-traverse@1.0.0: {} - json-schema-typed@8.0.2: {} - jsonc-parser@3.3.1: {} launch-editor@2.14.1: @@ -2827,12 +2138,6 @@ snapshots: lru-cache@11.5.2: {} - math-intrinsics@1.1.0: {} - - media-typer@1.1.1: {} - - merge-descriptors@2.0.0: {} - merge2@1.4.1: {} micromatch@4.0.8: @@ -2840,16 +2145,8 @@ snapshots: braces: 3.0.3 picomatch: 2.3.2 - mime-db@1.54.0: {} - - mime-types@3.0.2: - dependencies: - mime-db: 1.54.0 - mime@4.1.0: {} - ms@2.1.3: {} - msgpackr-extract@3.0.4: dependencies: node-gyp-build-optional-packages: 5.2.2 @@ -2866,10 +2163,6 @@ snapshots: optionalDependencies: msgpackr-extract: 3.0.4 - negotiator@1.1.0: - dependencies: - content-type: 2.1.0 - node-gyp-build-optional-packages@5.2.2: dependencies: detect-libc: 2.1.2 @@ -2882,18 +2175,6 @@ snapshots: semver: 7.8.5 validate-npm-package-name: 7.0.2 - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - open@11.0.2: dependencies: default-browser: 5.5.1 @@ -2905,12 +2186,8 @@ snapshots: package-manager-detector@1.8.0: {} - parseurl@1.3.3: {} - path-key@3.1.1: {} - path-to-regexp@8.4.2: {} - picocolors@1.1.1: {} picomatch@2.3.2: {} @@ -2931,29 +2208,10 @@ snapshots: retry: 0.12.0 signal-exit: 3.0.7 - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - pure-rand@8.4.2: {} - qs@6.16.0: - dependencies: - es-define-property: 1.0.1 - side-channel: 1.1.1 - queue-microtask@1.2.3: {} - range-parser@1.3.0: {} - - raw-body@3.0.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.7.3 - unpipe: 1.0.0 - react-dom@19.2.8(react@19.2.8): dependencies: react: 19.2.8 @@ -2988,25 +2246,14 @@ snapshots: reusify@1.1.0: {} - router@2.2.0: + rsbuild-plugin-dts@1.0.0(@rsbuild/core@2.2.2)(typescript@7.0.2): dependencies: - debug: 4.4.3 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.4.2 - transitivePeerDependencies: - - supports-color - - rsbuild-plugin-dts@0.23.2(@rsbuild/core@2.1.13)(@typescript/native-preview@7.0.0-dev.20260707.2)(typescript@7.0.2): - dependencies: - '@ast-grep/napi': 0.37.0 - '@rsbuild/core': 2.1.13 + '@ast-grep/napi': 0.45.2 + '@rsbuild/core': 2.2.2 optionalDependencies: - '@typescript/native-preview': 7.0.0-dev.20260707.2 typescript: 7.0.2 - rsc-markdown-stream@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@cd0b4a62cd92b3fbced6742b3cead32d9639c3e4(react@19.2.8): + rsc-markdown-stream@https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@d30d9acb6bd106762bb975ac58d6bf437c176091(react@19.2.8): dependencies: react: 19.2.8 @@ -3016,39 +2263,10 @@ snapshots: dependencies: queue-microtask: 1.2.3 - safer-buffer@2.1.2: {} - scheduler@0.27.0: {} semver@7.8.5: {} - send@1.2.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.1 - mime-types: 3.0.2 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.3.0 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - serve-static@2.2.1: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.1 - transitivePeerDependencies: - - supports-color - - setprototypeof@1.2.0: {} - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -3057,40 +2275,10 @@ snapshots: shell-quote@1.10.0: {} - side-channel-list@1.0.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.1 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - signal-exit@3.0.7: {} sisteransi@1.0.5: {} - statuses@2.0.2: {} - tinyexec@1.3.0: {} tinyglobby@0.2.17: @@ -3102,16 +2290,8 @@ snapshots: dependencies: is-number: 7.0.0 - toidentifier@1.0.1: {} - tslib@2.8.1: {} - type-is@2.1.0: - dependencies: - content-type: 2.1.0 - media-typer: 1.1.1 - mime-types: 3.0.2 - typescript@7.0.2: optionalDependencies: '@typescript/typescript-aix-ppc64': 7.0.2 @@ -3139,18 +2319,12 @@ snapshots: undici@8.10.1: {} - unpipe@1.0.0: {} - validate-npm-package-name@7.0.2: {} - vary@1.1.2: {} - which@2.0.2: dependencies: isexe: 2.0.0 - wrappy@1.0.2: {} - ws@8.21.3: {} wsl-utils@1.0.0: @@ -3160,8 +2334,4 @@ snapshots: yaml@2.9.0: {} - zod-to-json-schema@3.25.2(zod@4.5.4): - dependencies: - zod: 4.5.4 - zod@4.5.4: {} diff --git a/src/cli/dashboard.ts b/src/cli/dashboard.ts index 0d944054..0e9b947a 100644 --- a/src/cli/dashboard.ts +++ b/src/cli/dashboard.ts @@ -1,9 +1,9 @@ -import { spawn } from 'node:child_process'; -import { existsSync, readFileSync } from 'node:fs'; -import { dirname, join, resolve } from 'node:path'; +import { existsSync } from 'node:fs'; +import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import type { CliRouteConfig, CliRouteProps } from 'agent-bundle'; +import { ServeAppCommandError, spawnServeApp, type ServeAppExit, type SpawnedServeApp } from 'agent-bundle/serve-app-command'; import { z } from 'zod'; /** @@ -16,12 +16,16 @@ import { z } from 'zod'; * the command stays in the foreground until Ctrl-C or the server exits. A * plain command, not a rendered one: an open browser tab has no render budget. * - * The framework is spawned, never imported: a route that imported - * `agent-bundle/api` would either inline the framework into every host pack's - * bin or leave a bare import the artifact validator rejects (`AB6005`). The - * packs stay self-contained, and `agent-bundle` is resolved from the project - * at run time — the checkout has it under `node_modules`; the npm package - * (no runtime dependencies, #82) and an installed host pack do not, and the + * The framework is spawned, never imported: a route that value-imported + * `agent-bundle/api` would inline the compiler into every host pack's bin + * (`AB4837`). `spawnServeApp` from `agent-bundle/serve-app-command` + * (agent-bundle#558) is the sanctioned shape — dependency-free, so it bundles + * into the bin — and owns resolving the installed framework CLI, spawning it, + * relaying its stdout to stderr so this command keeps stdout for its result, + * waiting for the ready line, and turning the request signal into the child's + * SIGTERM. What it cannot know is where this install keeps its artifact: the + * checkout has it under `node_modules` and `artifact/`; the npm package (no + * runtime dependencies, #82) and an installed host pack do not, and the * command says so. */ export const config = { @@ -46,7 +50,7 @@ export const resultSchema = z exitCode: z.number().int().min(0).max(255), message: z.string(), operation: z.literal('dashboard'), - /** The served host page, once `agent-bundle serve-app` printed it. */ + /** The served host page, once `agent-bundle serve-app` printed its ready line. */ url: z.string().nullable(), }) .strict(); @@ -71,110 +75,50 @@ const locateProject = (): { readonly artifact: string; readonly root: string } | return undefined; }; -/** - * The framework's own CLI as the project resolves it: the `agent-bundle` - * package under the nearest `node_modules` at or above the root, read from its - * manifest's `bin`. Looked up by path, not `require.resolve`: the package's - * `exports` declare no `require` condition, and an `import()` of it here - * would drag the framework into every bin that carries this route. - */ -const frameworkCliPath = (root: string): string | undefined => { - for (let directory = root; ; directory = dirname(directory)) { - const packageDir = join(directory, 'node_modules', 'agent-bundle'); - const manifestPath = join(packageDir, 'package.json'); - if (existsSync(manifestPath)) { - const manifest: unknown = JSON.parse(readFileSync(manifestPath, 'utf8')); - const bin = typeof manifest === 'object' && manifest !== null && 'bin' in manifest ? manifest.bin : undefined; - const relative = - typeof bin === 'string' ? bin : typeof bin === 'object' && bin !== null ? Reflect.get(bin, 'agent-bundle') : undefined; - return typeof relative === 'string' ? resolve(packageDir, relative) : undefined; - } - if (directory === dirname(directory)) { - return undefined; - } - } -}; - -const failure = (message: string): DashboardResult => ({ exitCode: 1, message, operation: 'dashboard', url: null }); - const inHost = 'In an MCP host, call hauler_status instead — the dashboard App is attached to its result.'; -/** `agent-bundle serve-app` prints `MCP App at (…)` once the host listens. */ -const servedUrl = (line: string): string | undefined => /\bat (https?:\/\/\S+)/u.exec(line)?.[1]; +const failure = (exitCode: number, message: string): DashboardResult => ({ exitCode, message, operation: 'dashboard', url: null }); -/** - * Runs the framework CLI in the foreground. The routed CLI owns stdout for the - * result document, so the child's stdout — the URL line — is relayed to - * stderr, the operator's channel; its stderr is inherited. The request signal - * (Ctrl-C reaching the routed CLI) becomes the child's SIGTERM. - */ -const serve = ( - argv: readonly string[], - signal: AbortSignal, -): Promise<{ readonly exitCode: number; readonly url: string | null }> => - new Promise((resolvePromise, reject) => { - const child = spawn(process.execPath, argv, { stdio: ['ignore', 'pipe', 'inherit'] }); - let url: string | null = null; - let buffered = ''; - child.stdout.on('data', (chunk: Buffer) => { - const text = chunk.toString('utf8'); - process.stderr.write(text); - buffered += text; - for (const line of buffered.split('\n').slice(0, -1)) { - url ??= servedUrl(line) ?? null; - } - buffered = buffered.slice(buffered.lastIndexOf('\n') + 1); - }); - const stop = (): void => { - child.kill('SIGTERM'); - }; - signal.addEventListener('abort', stop, { once: true }); - child.once('error', reject); - child.once('exit', (code, exitSignal) => { - signal.removeEventListener('abort', stop); - resolvePromise({ exitCode: code ?? (exitSignal === null ? 1 : 128), url }); - }); - }); +/** The child's exit as this command's: its code, or 1 for an exit with neither code nor signal, or 128 for a signal. */ +const exitCodeOf = ({ code, signal }: ServeAppExit): number => code ?? (signal === null ? 1 : 128); export default async function Dashboard({ input, signal }: CliRouteProps): Promise { const project = locateProject(); if (project === undefined) { return failure( + 1, `hauler dashboard runs from the plugin checkout, where the built artifact sits beside the CLI; an installed host pack has none. ${inHost}`, ); } - const cli = frameworkCliPath(project.root); - if (cli === undefined) { - return failure( - `hauler dashboard needs agent-bundle resolvable from ${project.root} (\`pnpm install\` in the checkout; the npm package ships no runtime dependencies). ${inHost}`, - ); + let served: SpawnedServeApp; + try { + served = await spawnServeApp({ + app: 'hauler/dashboard', + root: project.root, + artifact: project.artifact, + target: input.target ?? 'portable', + tool: 'hauler_status', + autoApprove: ['call-tool'], + open: input.noOpen !== true, + ...(input.port === undefined ? {} : { port: input.port }), + // Ctrl-C reaching the routed CLI stops the server. + signal, + }); + } catch (error) { + if (error instanceof ServeAppCommandError) { + // framework-not-installed, artifact-missing, exited-before-ready (with + // the child's exit), spawn-failed, aborted, stop-failed. + return failure(error.exit === undefined ? 1 : exitCodeOf(error.exit), `${error.message} ${inHost}`); + } + throw error; } - const served = await serve( - [ - cli, - 'serve-app', - 'hauler/dashboard', - '--root', - project.root, - '--artifact', - project.artifact, - '--target', - input.target ?? 'portable', - '--tool', - 'hauler_status', - '--allow', - 'call-tool', - input.noOpen === true ? '--no-open' : '--open', - ...(input.port === undefined ? [] : ['--port', String(input.port)]), - ], - signal, - ); + const exit = await served.closed; return { - exitCode: served.exitCode, + exitCode: exitCodeOf(exit), message: - served.exitCode === 0 + exit.code === 0 ? 'dashboard closed' - : `agent-bundle serve-app exited with ${String(served.exitCode)}; see its diagnostics above`, + : `agent-bundle serve-app exited with ${exit.signal ?? String(exit.code)}; see its diagnostics above`, operation: 'dashboard', url: served.url, }; From 9f9ae21bc68771c192bf42ab16b197b5e31a176a Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sat, 5 Sep 2026 06:19:49 +0000 Subject: [PATCH 2/6] changeset: name PR #122 --- .changeset/serve-app-command.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/serve-app-command.md b/.changeset/serve-app-command.md index f9db5e6f..6fe560cd 100644 --- a/.changeset/serve-app-command.md +++ b/.changeset/serve-app-command.md @@ -2,4 +2,4 @@ 'cargo-hauler': patch --- -`hauler dashboard` serves the App through `spawnServeApp` from `agent-bundle/serve-app-command` (agent-bundle #558/#582) instead of its own child-process plumbing. The framework helper now resolves the installed `agent-bundle` CLI, spawns `agent-bundle serve-app`, waits for its ready line, relays the child's output to stderr, and turns Ctrl-C into the server's SIGTERM; its typed failures (`framework-not-installed`, `artifact-missing`, `exited-before-ready`, …) become the command's message, and the routed bin still never imports the compiler (agent-bundle `AB4837`). The flags (`--target`, `--port`, `--no-open`), the opening `hauler_status` call, the foreground-until-Ctrl-C behaviour, and the checkout-only scope are unchanged. Rebuilt against the `agent-bundle` / `@agent-bundle/runtime` preview of main commit `d30d9acb6`, which also holds the package build's `dist/` to `AB6005` (#588), compiles on Rslib 1.0 / Rsbuild 2.2 (#575), binds the host's own opening call in `serve-app` (#565), and reports MCP App view compile errors as `AB4770`–`AB4772` (#585). (#TBD) +`hauler dashboard` serves the App through `spawnServeApp` from `agent-bundle/serve-app-command` (agent-bundle #558/#582) instead of its own child-process plumbing. The framework helper now resolves the installed `agent-bundle` CLI, spawns `agent-bundle serve-app`, waits for its ready line, relays the child's output to stderr, and turns Ctrl-C into the server's SIGTERM; its typed failures (`framework-not-installed`, `artifact-missing`, `exited-before-ready`, …) become the command's message, and the routed bin still never imports the compiler (agent-bundle `AB4837`). The flags (`--target`, `--port`, `--no-open`), the opening `hauler_status` call, the foreground-until-Ctrl-C behaviour, and the checkout-only scope are unchanged. Rebuilt against the `agent-bundle` / `@agent-bundle/runtime` preview of main commit `d30d9acb6`, which also holds the package build's `dist/` to `AB6005` (#588), compiles on Rslib 1.0 / Rsbuild 2.2 (#575), binds the host's own opening call in `serve-app` (#565), and reports MCP App view compile errors as `AB4770`–`AB4772` (#585). (#122) From 5cc0f3bf8e4064fbc00c8d081820eb9e15614e79 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sat, 5 Sep 2026 06:20:06 +0000 Subject: [PATCH 3/6] changeset: user-facing summary --- .changeset/serve-app-command.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/serve-app-command.md b/.changeset/serve-app-command.md index 6fe560cd..97a3c041 100644 --- a/.changeset/serve-app-command.md +++ b/.changeset/serve-app-command.md @@ -2,4 +2,4 @@ 'cargo-hauler': patch --- -`hauler dashboard` serves the App through `spawnServeApp` from `agent-bundle/serve-app-command` (agent-bundle #558/#582) instead of its own child-process plumbing. The framework helper now resolves the installed `agent-bundle` CLI, spawns `agent-bundle serve-app`, waits for its ready line, relays the child's output to stderr, and turns Ctrl-C into the server's SIGTERM; its typed failures (`framework-not-installed`, `artifact-missing`, `exited-before-ready`, …) become the command's message, and the routed bin still never imports the compiler (agent-bundle `AB4837`). The flags (`--target`, `--port`, `--no-open`), the opening `hauler_status` call, the foreground-until-Ctrl-C behaviour, and the checkout-only scope are unchanged. Rebuilt against the `agent-bundle` / `@agent-bundle/runtime` preview of main commit `d30d9acb6`, which also holds the package build's `dist/` to `AB6005` (#588), compiles on Rslib 1.0 / Rsbuild 2.2 (#575), binds the host's own opening call in `serve-app` (#565), and reports MCP App view compile errors as `AB4770`–`AB4772` (#585). (#122) +Serve `hauler dashboard` through `spawnServeApp` from `agent-bundle/serve-app-command` instead of hand-rolled child-process plumbing, and re-pin the `agent-bundle` / `@agent-bundle/runtime` preview to main `d30d9acb6` (agent-bundle #582 `serve-app-command` + `AB4837`, #588 package `dist/` held to `AB6005`). Flags (`--target`, `--port`, `--no-open`), the opening `hauler_status` call, and the checkout-only scope are unchanged; the helper's typed failures (`framework-not-installed`, `artifact-missing`, `exited-before-ready`) become the command's message. (#122) From b0651deb63a4948562ddd66ec31adb8d13ed320f Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sat, 5 Sep 2026 06:39:54 +0000 Subject: [PATCH 4/6] fix(dashboard): surface a post-ready stop failure instead of hanging; test the serve-app adapter with an injected spawn Reviewer findings on #122. After the ready line the helper reports a refused SIGTERM only through close(), so an abort whose stop failed left closed pending for ever; serveDashboard now closes on abort itself and turns that stop-failed into the result (with the pid to kill by hand). The route's logic moved to src/lib/dashboard-serve.ts, which the route imports, so the option mapping, every ServeAppCommandError kind, and the abort paths have unit tests. --- src/cli/dashboard.ts | 65 ++--------- src/lib/dashboard-serve.ts | 128 ++++++++++++++++++++++ tests/dashboard-serve.test.ts | 198 ++++++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+), 55 deletions(-) create mode 100644 src/lib/dashboard-serve.ts create mode 100644 tests/dashboard-serve.test.ts diff --git a/src/cli/dashboard.ts b/src/cli/dashboard.ts index 0e9b947a..8c7cc508 100644 --- a/src/cli/dashboard.ts +++ b/src/cli/dashboard.ts @@ -3,9 +3,10 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import type { CliRouteConfig, CliRouteProps } from 'agent-bundle'; -import { ServeAppCommandError, spawnServeApp, type ServeAppExit, type SpawnedServeApp } from 'agent-bundle/serve-app-command'; import { z } from 'zod'; +import { serveDashboard, type DashboardProject } from '../lib/dashboard-serve.js'; + /** * `hauler dashboard`: the MCP App in a plain browser tab, outside any MCP * host. It runs the framework's own `agent-bundle serve-app` (agent-bundle#514) @@ -23,10 +24,11 @@ import { z } from 'zod'; * into the bin — and owns resolving the installed framework CLI, spawning it, * relaying its stdout to stderr so this command keeps stdout for its result, * waiting for the ready line, and turning the request signal into the child's - * SIGTERM. What it cannot know is where this install keeps its artifact: the - * checkout has it under `node_modules` and `artifact/`; the npm package (no - * runtime dependencies, #82) and an installed host pack do not, and the - * command says so. + * SIGTERM; `serveDashboard` (`src/lib/dashboard-serve.ts`) maps its outcomes + * onto this command's result. What neither can know is where this install + * keeps its artifact: the checkout has it under `node_modules` and + * `artifact/`; the npm package (no runtime dependencies, #82) and an installed + * host pack do not, and the command says so. */ export const config = { description: @@ -55,8 +57,6 @@ export const resultSchema = z }) .strict(); -type DashboardResult = z.infer; - /** * Where this CLI lives decides what it can serve: `dist/bin/cargo-hauler.js` * sits two levels under the project root (a checkout or the npm package), @@ -64,7 +64,7 @@ type DashboardResult = z.infer; * cargo-hauler.mjs` sits two levels under that artifact root. An installed * host pack is neither and has no artifact to serve from. */ -const locateProject = (): { readonly artifact: string; readonly root: string } | undefined => { +const locateProject = (): DashboardProject | undefined => { const here = fileURLToPath(new URL('../../', import.meta.url)); if (existsSync(join(here, 'agent-bundle.manifest.json'))) { return { artifact: here, root: dirname(here) }; @@ -75,51 +75,6 @@ const locateProject = (): { readonly artifact: string; readonly root: string } | return undefined; }; -const inHost = 'In an MCP host, call hauler_status instead — the dashboard App is attached to its result.'; - -const failure = (exitCode: number, message: string): DashboardResult => ({ exitCode, message, operation: 'dashboard', url: null }); - -/** The child's exit as this command's: its code, or 1 for an exit with neither code nor signal, or 128 for a signal. */ -const exitCodeOf = ({ code, signal }: ServeAppExit): number => code ?? (signal === null ? 1 : 128); - -export default async function Dashboard({ input, signal }: CliRouteProps): Promise { - const project = locateProject(); - if (project === undefined) { - return failure( - 1, - `hauler dashboard runs from the plugin checkout, where the built artifact sits beside the CLI; an installed host pack has none. ${inHost}`, - ); - } - let served: SpawnedServeApp; - try { - served = await spawnServeApp({ - app: 'hauler/dashboard', - root: project.root, - artifact: project.artifact, - target: input.target ?? 'portable', - tool: 'hauler_status', - autoApprove: ['call-tool'], - open: input.noOpen !== true, - ...(input.port === undefined ? {} : { port: input.port }), - // Ctrl-C reaching the routed CLI stops the server. - signal, - }); - } catch (error) { - if (error instanceof ServeAppCommandError) { - // framework-not-installed, artifact-missing, exited-before-ready (with - // the child's exit), spawn-failed, aborted, stop-failed. - return failure(error.exit === undefined ? 1 : exitCodeOf(error.exit), `${error.message} ${inHost}`); - } - throw error; - } - const exit = await served.closed; - return { - exitCode: exitCodeOf(exit), - message: - exit.code === 0 - ? 'dashboard closed' - : `agent-bundle serve-app exited with ${exit.signal ?? String(exit.code)}; see its diagnostics above`, - operation: 'dashboard', - url: served.url, - }; +export default async function Dashboard({ input, signal }: CliRouteProps): Promise> { + return await serveDashboard({ input, project: locateProject(), signal }); } diff --git a/src/lib/dashboard-serve.ts b/src/lib/dashboard-serve.ts new file mode 100644 index 00000000..f8f12dfc --- /dev/null +++ b/src/lib/dashboard-serve.ts @@ -0,0 +1,128 @@ +import { + ServeAppCommandError, + spawnServeApp, + type ServeAppExit, + type SpawnedServeApp, + type SpawnServeAppOptions, +} from 'agent-bundle/serve-app-command'; + +/** Where this install keeps its built host packs; `undefined` when it has none (npm package, installed host pack). */ +export interface DashboardProject { + readonly artifact: string; + readonly root: string; +} + +export interface DashboardServeInput { + readonly noOpen?: boolean | undefined; + readonly port?: number | undefined; + readonly target?: 'claude' | 'codex' | 'cursor' | 'portable' | undefined; +} + +export interface DashboardServeResult { + readonly exitCode: number; + readonly message: string; + readonly operation: 'dashboard'; + /** The served host page, once `agent-bundle serve-app` printed its ready line. */ + readonly url: string | null; +} + +export interface DashboardServeOptions { + readonly input: DashboardServeInput; + readonly project: DashboardProject | undefined; + readonly signal: AbortSignal; + /** Injectable only to make the framework process deterministic in tests. */ + readonly spawn?: (options: SpawnServeAppOptions) => Promise; +} + +const inHost = 'In an MCP host, call hauler_status instead — the dashboard App is attached to its result.'; + +const failure = (exitCode: number, message: string): DashboardServeResult => ({ + exitCode, + message, + operation: 'dashboard', + url: null, +}); + +/** The child's exit as this command's: its code, or 1 for an exit with neither code nor signal, or 128 for a signal. */ +const exitCodeOf = ({ code, signal }: ServeAppExit): number => code ?? (signal === null ? 1 : 128); + +/** + * After the ready line the App is the caller's: the helper's own abort + * handler sends SIGTERM but reports a refused signal only through `close()`, + * so an abort whose stop failed would otherwise leave `closed` pending for + * ever. This settles with the exit, or rejects with that `stop-failed`. + */ +const exitOrStopFailure = (served: SpawnedServeApp, signal: AbortSignal): Promise => + new Promise((settle, reject) => { + const onAbort = (): void => { + served.close().then(settle, reject); + }; + void served.closed.then((exit) => { + signal.removeEventListener('abort', onAbort); + settle(exit); + }); + if (signal.aborted) { + onAbort(); + } else { + signal.addEventListener('abort', onAbort, { once: true }); + } + }); + +/** + * Serves `hauler/dashboard` through `agent-bundle serve-app` against the + * install's built artifact and stays until the server exits or `signal` + * aborts; every `ServeAppCommandError` becomes the command's result. + */ +export const serveDashboard = async ({ + input, + project, + signal, + spawn = spawnServeApp, +}: DashboardServeOptions): Promise => { + if (project === undefined) { + return failure( + 1, + `hauler dashboard runs from the plugin checkout, where the built artifact sits beside the CLI; an installed host pack has none. ${inHost}`, + ); + } + let served: SpawnedServeApp; + try { + served = await spawn({ + app: 'hauler/dashboard', + root: project.root, + artifact: project.artifact, + target: input.target ?? 'portable', + tool: 'hauler_status', + autoApprove: ['call-tool'], + open: input.noOpen !== true, + ...(input.port === undefined ? {} : { port: input.port }), + // Ctrl-C reaching the routed CLI stops the server. + signal, + }); + } catch (error) { + if (error instanceof ServeAppCommandError) { + // framework-not-installed, artifact-missing, exited-before-ready (with + // the child's exit), spawn-failed, aborted, stop-failed. + return failure(error.exit === undefined ? 1 : exitCodeOf(error.exit), `${error.message} ${inHost}`); + } + throw error; + } + let exit: ServeAppExit; + try { + exit = await exitOrStopFailure(served, signal); + } catch (error) { + if (error instanceof ServeAppCommandError) { + return { ...failure(1, `${error.message} Stop it by hand (kill ${String(served.pid)}).`), url: served.url }; + } + throw error; + } + return { + exitCode: exitCodeOf(exit), + message: + exit.code === 0 + ? 'dashboard closed' + : `agent-bundle serve-app exited with ${exit.signal ?? String(exit.code)}; see its diagnostics above`, + operation: 'dashboard', + url: served.url, + }; +}; diff --git a/tests/dashboard-serve.test.ts b/tests/dashboard-serve.test.ts new file mode 100644 index 00000000..ada2cf95 --- /dev/null +++ b/tests/dashboard-serve.test.ts @@ -0,0 +1,198 @@ +import { describe, expect, it } from 'effect-rstest'; +import { + ServeAppCommandError, + type ServeAppExit, + type SpawnedServeApp, + type SpawnServeAppOptions, +} from 'agent-bundle/serve-app-command'; + +import { serveDashboard, type DashboardServeOptions } from '../src/lib/dashboard-serve.js'; + +const project = { artifact: '/checkout/artifact', root: '/checkout' }; + +interface FakeServer { + readonly served: SpawnedServeApp; + readonly exit: (exit: ServeAppExit) => void; + readonly closeCalls: () => number; +} + +const fakeServer = (options: { readonly closeRejects?: ServeAppCommandError } = {}): FakeServer => { + let settle: (exit: ServeAppExit) => void = () => undefined; + const closed = new Promise((resolveClosed) => { + settle = resolveClosed; + }); + let closeCalls = 0; + const served: SpawnedServeApp = { + app: 'hauler/dashboard', + tool: 'hauler_status', + url: 'http://127.0.0.1:40577/', + server: 'hauler', + port: 40_577, + pid: 4242, + closed, + close: () => { + closeCalls += 1; + if (options.closeRejects !== undefined) { + return Promise.reject(options.closeRejects); + } + settle({ code: null, signal: 'SIGTERM' }); + return closed; + }, + }; + return { served, exit: settle, closeCalls: () => closeCalls }; +}; + +const serve = ( + overrides: Partial & Pick, +): ReturnType => + serveDashboard({ + input: {}, + project, + signal: new AbortController().signal, + ...overrides, + }); + +describe('serveDashboard', () => { + it('refuses without a located artifact and points at hauler_status in a host', async () => { + let spawned = false; + const result = await serve({ + project: undefined, + spawn: () => { + spawned = true; + return Promise.reject(new Error('unreachable')); + }, + }); + expect(spawned).toBe(false); + expect(result.exitCode).toBe(1); + expect(result.url).toBeNull(); + expect(result.message).toContain('plugin checkout'); + expect(result.message).toContain('call hauler_status instead'); + }); + + it('passes the App, artifact, opening tool, approval, and flags through to spawnServeApp', async () => { + let seen: SpawnServeAppOptions | undefined; + const controller = new AbortController(); + const server = fakeServer(); + const pending = serve({ + input: { noOpen: true, port: 4321, target: 'cursor' }, + signal: controller.signal, + spawn: (options) => { + seen = options; + return Promise.resolve(server.served); + }, + }); + server.exit({ code: 0, signal: null }); + const result = await pending; + expect(seen).toEqual({ + app: 'hauler/dashboard', + root: '/checkout', + artifact: '/checkout/artifact', + target: 'cursor', + tool: 'hauler_status', + autoApprove: ['call-tool'], + open: false, + port: 4321, + signal: controller.signal, + }); + expect(result).toEqual({ + exitCode: 0, + message: 'dashboard closed', + operation: 'dashboard', + url: 'http://127.0.0.1:40577/', + }); + }); + + it('defaults to the portable pack, an ephemeral port, and opening the browser', async () => { + let seen: SpawnServeAppOptions | undefined; + const server = fakeServer(); + const pending = serve({ + spawn: (options) => { + seen = options; + return Promise.resolve(server.served); + }, + }); + server.exit({ code: 0, signal: null }); + await pending; + expect(seen?.target).toBe('portable'); + expect(seen?.open).toBe(true); + expect(seen).not.toHaveProperty('port'); + }); + + it.each([ + ['framework-not-installed', undefined, 1], + ['artifact-missing', undefined, 1], + ['spawn-failed', undefined, 1], + ['aborted', undefined, 1], + ['exited-before-ready', { code: 3, signal: null }, 3], + ['exited-before-ready', { code: null, signal: 'SIGKILL' }, 128], + ] as const)('maps a %s failure before the ready line onto the result (exit %s)', async (code, exit, exitCode) => { + const error = new ServeAppCommandError(code, `${code} happened.`, exit === undefined ? {} : { exit }); + const result = await serve({ spawn: () => Promise.reject(error) }); + expect(result).toEqual({ + exitCode, + message: expect.stringContaining(`${code} happened. In an MCP host, call hauler_status instead`), + operation: 'dashboard', + url: null, + }); + }); + + it('rethrows an unexpected spawn error instead of masking it as a result', async () => { + await expect(serve({ spawn: () => Promise.reject(new TypeError('bad option')) })).rejects.toThrow('bad option'); + }); + + it('reports a server that exited on its own with its code or signal', async () => { + const byCode = fakeServer(); + const codePending = serve({ spawn: () => Promise.resolve(byCode.served) }); + byCode.exit({ code: 2, signal: null }); + expect(await codePending).toMatchObject({ exitCode: 2, message: expect.stringContaining('exited with 2') }); + + const bySignal = fakeServer(); + const signalPending = serve({ spawn: () => Promise.resolve(bySignal.served) }); + bySignal.exit({ code: null, signal: 'SIGTERM' }); + expect(await signalPending).toMatchObject({ + exitCode: 128, + message: expect.stringContaining('exited with SIGTERM'), + url: 'http://127.0.0.1:40577/', + }); + }); + + it('closes the served App when the request aborts and returns its exit', async () => { + const controller = new AbortController(); + const server = fakeServer(); + const pending = serve({ signal: controller.signal, spawn: () => Promise.resolve(server.served) }); + controller.abort(); + const result = await pending; + expect(server.closeCalls()).toBe(1); + expect(result.exitCode).toBe(128); + }); + + it('does not hang when the abort cannot stop the server: the stop failure is the result', async () => { + const controller = new AbortController(); + const stopFailed = new ServeAppCommandError( + 'stop-failed', + 'agent-bundle serve-app (pid 4242) could not be signalled to stop and is still running.', + ); + const server = fakeServer({ closeRejects: stopFailed }); + const pending = serve({ signal: controller.signal, spawn: () => Promise.resolve(server.served) }); + controller.abort(); + const result = await pending; + expect(server.closeCalls()).toBe(1); + expect(result).toEqual({ + exitCode: 1, + message: expect.stringContaining('could not be signalled to stop and is still running. Stop it by hand (kill 4242).'), + operation: 'dashboard', + url: 'http://127.0.0.1:40577/', + }); + }); + + it('settles on the abort even when the signal was already aborted after the ready line', async () => { + const controller = new AbortController(); + const server = fakeServer(); + controller.abort(); + // The helper itself rejects a pre-aborted spawn; here the fake served it, + // so the caller's own abort handling must still close it. + const result = await serve({ signal: controller.signal, spawn: () => Promise.resolve(server.served) }); + expect(server.closeCalls()).toBe(1); + expect(result.exitCode).toBe(128); + }); +}); From 761fa9a81639074253a7ff9dcea577593657ba65 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sat, 5 Sep 2026 06:50:51 +0000 Subject: [PATCH 5/6] =?UTF-8?q?fix(dashboard):=20one=20SIGTERM=20owner=20?= =?UTF-8?q?=E2=80=94=20the=20helper=20before=20the=20ready=20line,=20close?= =?UTF-8?q?()=20after=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second-pass finding on #122: passing the request signal to the helper and also calling close() on abort sent the server two SIGTERMs, which can bypass a one-shot graceful handler. The helper now gets a private signal that follows the request only until readiness; after it a single close() stops the server and surfaces stop-failed. The test fake honours options.signal like the real helper and asserts exactly one stop per scenario. Changeset lists every surfaced failure kind. --- .changeset/serve-app-command.md | 2 +- src/lib/dashboard-serve.ts | 46 +++++-- tests/dashboard-serve.test.ts | 208 ++++++++++++++++++++------------ 3 files changed, 174 insertions(+), 82 deletions(-) diff --git a/.changeset/serve-app-command.md b/.changeset/serve-app-command.md index 97a3c041..413eebb0 100644 --- a/.changeset/serve-app-command.md +++ b/.changeset/serve-app-command.md @@ -2,4 +2,4 @@ 'cargo-hauler': patch --- -Serve `hauler dashboard` through `spawnServeApp` from `agent-bundle/serve-app-command` instead of hand-rolled child-process plumbing, and re-pin the `agent-bundle` / `@agent-bundle/runtime` preview to main `d30d9acb6` (agent-bundle #582 `serve-app-command` + `AB4837`, #588 package `dist/` held to `AB6005`). Flags (`--target`, `--port`, `--no-open`), the opening `hauler_status` call, and the checkout-only scope are unchanged; the helper's typed failures (`framework-not-installed`, `artifact-missing`, `exited-before-ready`) become the command's message. (#122) +Serve `hauler dashboard` through `spawnServeApp` from `agent-bundle/serve-app-command` instead of hand-rolled child-process plumbing, and re-pin the `agent-bundle` / `@agent-bundle/runtime` preview to main `d30d9acb6` (agent-bundle #582 `serve-app-command` + `AB4837`, #588 package `dist/` held to `AB6005`). Flags (`--target`, `--port`, `--no-open`), the opening `hauler_status` call, and the checkout-only scope are unchanged; the helper's typed failures (`framework-not-installed`, `artifact-missing`, `spawn-failed`, `exited-before-ready` with the child's exit code, `aborted`, `stop-failed`) become the command's message, and a Ctrl-C whose stop the server refuses now ends the command with that failure instead of hanging. (#122) diff --git a/src/lib/dashboard-serve.ts b/src/lib/dashboard-serve.ts index f8f12dfc..9f9b9347 100644 --- a/src/lib/dashboard-serve.ts +++ b/src/lib/dashboard-serve.ts @@ -47,10 +47,12 @@ const failure = (exitCode: number, message: string): DashboardServeResult => ({ const exitCodeOf = ({ code, signal }: ServeAppExit): number => code ?? (signal === null ? 1 : 128); /** - * After the ready line the App is the caller's: the helper's own abort - * handler sends SIGTERM but reports a refused signal only through `close()`, - * so an abort whose stop failed would otherwise leave `closed` pending for - * ever. This settles with the exit, or rejects with that `stop-failed`. + * After the ready line the App is the caller's. Exactly one party sends the + * server its SIGTERM — a second one can bypass a one-shot graceful handler in + * the child — and only `close()` reports a refused signal, so the request + * `signal` is not handed to the helper past readiness (see `serveDashboard`); + * here an abort means one `close()`, whose exit settles this or whose + * `stop-failed` rejects it instead of leaving `closed` pending for ever. */ const exitOrStopFailure = (served: SpawnedServeApp, signal: AbortSignal): Promise => new Promise((settle, reject) => { @@ -68,6 +70,30 @@ const exitOrStopFailure = (served: SpawnedServeApp, signal: AbortSignal): Promis } }); +/** + * A signal for the helper that follows the request `signal` only until the + * ready line: before it the helper owns the child and an abort is its + * `aborted` / `stop-failed` rejection; after it `exitOrStopFailure` owns the + * one stop. Returns the signal and the hand-over that stops following. + */ +const untilReady = (signal: AbortSignal): { readonly signal: AbortSignal; readonly handOver: () => void } => { + const controller = new AbortController(); + const follow = (): void => { + controller.abort(); + }; + if (signal.aborted) { + follow(); + } else { + signal.addEventListener('abort', follow, { once: true }); + } + return { + signal: controller.signal, + handOver: () => { + signal.removeEventListener('abort', follow); + }, + }; +}; + /** * Serves `hauler/dashboard` through `agent-bundle serve-app` against the * install's built artifact and stays until the server exits or `signal` @@ -85,6 +111,7 @@ export const serveDashboard = async ({ `hauler dashboard runs from the plugin checkout, where the built artifact sits beside the CLI; an installed host pack has none. ${inHost}`, ); } + const helper = untilReady(signal); let served: SpawnedServeApp; try { served = await spawn({ @@ -96,10 +123,11 @@ export const serveDashboard = async ({ autoApprove: ['call-tool'], open: input.noOpen !== true, ...(input.port === undefined ? {} : { port: input.port }), - // Ctrl-C reaching the routed CLI stops the server. - signal, + // Ctrl-C reaching the routed CLI before the ready line stops the server. + signal: helper.signal, }); } catch (error) { + helper.handOver(); if (error instanceof ServeAppCommandError) { // framework-not-installed, artifact-missing, exited-before-ready (with // the child's exit), spawn-failed, aborted, stop-failed. @@ -107,9 +135,13 @@ export const serveDashboard = async ({ } throw error; } + helper.handOver(); let exit: ServeAppExit; try { - exit = await exitOrStopFailure(served, signal); + // An abort that landed between the ready line and this hand-over already + // had the helper send the stop; a second `close()` would be a second + // SIGTERM, so that exit is simply awaited. + exit = helper.signal.aborted ? await served.closed : await exitOrStopFailure(served, signal); } catch (error) { if (error instanceof ServeAppCommandError) { return { ...failure(1, `${error.message} Stop it by hand (kill ${String(served.pid)}).`), url: served.url }; diff --git a/tests/dashboard-serve.test.ts b/tests/dashboard-serve.test.ts index ada2cf95..1275d496 100644 --- a/tests/dashboard-serve.test.ts +++ b/tests/dashboard-serve.test.ts @@ -10,18 +10,48 @@ import { serveDashboard, type DashboardServeOptions } from '../src/lib/dashboard const project = { artifact: '/checkout/artifact', root: '/checkout' }; -interface FakeServer { - readonly served: SpawnedServeApp; +interface FakeHelper { + /** Stands in for `spawnServeApp`; records the options it was given. */ + readonly spawn: (options: SpawnServeAppOptions) => Promise; + readonly options: () => SpawnServeAppOptions | undefined; + /** How many SIGTERMs the fake child received — from the helper's own abort listener or from `close()`. */ + readonly stops: () => number; + /** The child exits on its own. */ readonly exit: (exit: ServeAppExit) => void; - readonly closeCalls: () => number; + /** Print the ready line: `spawn` resolves. */ + readonly ready: () => void; } -const fakeServer = (options: { readonly closeRejects?: ServeAppCommandError } = {}): FakeServer => { - let settle: (exit: ServeAppExit) => void = () => undefined; +/** + * Mirrors the real helper's contract: an abort of `options.signal` sends the + * child a SIGTERM — rejecting `spawn` with `aborted` before the ready line, + * settling `closed` after it — and `close()` sends one too, resolving with + * the exit or rejecting with `stop-failed` when the child refuses it. + */ +const fakeHelper = (behaviour: { readonly refuseStop?: boolean; readonly readyAtOnce?: boolean } = {}): FakeHelper => { + let seen: SpawnServeAppOptions | undefined; + let stops = 0; + let settledExit: ServeAppExit | undefined; + let settleClosed: (exit: ServeAppExit) => void = () => undefined; const closed = new Promise((resolveClosed) => { - settle = resolveClosed; + settleClosed = resolveClosed; }); - let closeCalls = 0; + let announceReady: () => void = () => undefined; + let rejectSpawn: (error: unknown) => void = () => undefined; + const finish = (exit: ServeAppExit): void => { + if (settledExit !== undefined) return; + settledExit = exit; + settleClosed(exit); + }; + const stopFailure = (): ServeAppCommandError => + new ServeAppCommandError('stop-failed', 'agent-bundle serve-app (pid 4242) could not be signalled to stop and is still running.'); + const stop = (): ServeAppCommandError | undefined => { + if (settledExit !== undefined) return undefined; + stops += 1; + if (behaviour.refuseStop === true) return stopFailure(); + finish({ code: null, signal: 'SIGTERM' }); + return undefined; + }; const served: SpawnedServeApp = { app: 'hauler/dashboard', tool: 'hauler_status', @@ -31,20 +61,46 @@ const fakeServer = (options: { readonly closeRejects?: ServeAppCommandError } = pid: 4242, closed, close: () => { - closeCalls += 1; - if (options.closeRejects !== undefined) { - return Promise.reject(options.closeRejects); + const refused = stop(); + return refused === undefined ? closed : Promise.reject(refused); + }, + }; + return { + spawn: (options) => { + seen = options; + if (options.signal?.aborted === true) { + // The real helper checks this before spawning anything. + return Promise.reject(new ServeAppCommandError('aborted', 'Serving hauler/dashboard was aborted before agent-bundle serve-app started.')); } - settle({ code: null, signal: 'SIGTERM' }); - return closed; + return new Promise((settle, reject) => { + let ready = false; + rejectSpawn = reject; + announceReady = () => { + ready = true; + settle(served); + }; + options.signal?.addEventListener( + 'abort', + () => { + const refused = stop(); + if (!ready) reject(refused ?? new ServeAppCommandError('aborted', 'Serving hauler/dashboard was aborted before agent-bundle serve-app was ready.')); + }, + { once: true }, + ); + if (behaviour.readyAtOnce !== false) announceReady(); + }); + }, + options: () => seen, + stops: () => stops, + exit: finish, + ready: () => { + announceReady(); + rejectSpawn = () => undefined; }, }; - return { served, exit: settle, closeCalls: () => closeCalls }; }; -const serve = ( - overrides: Partial & Pick, -): ReturnType => +const serve = (overrides: Partial & Pick): ReturnType => serveDashboard({ input: {}, project, @@ -52,17 +108,13 @@ const serve = ( ...overrides, }); +const settled = (): Promise => new Promise((resolveTick) => setTimeout(resolveTick, 0)); + describe('serveDashboard', () => { it('refuses without a located artifact and points at hauler_status in a host', async () => { - let spawned = false; - const result = await serve({ - project: undefined, - spawn: () => { - spawned = true; - return Promise.reject(new Error('unreachable')); - }, - }); - expect(spawned).toBe(false); + const helper = fakeHelper(); + const result = await serve({ project: undefined, spawn: helper.spawn }); + expect(helper.options()).toBeUndefined(); expect(result.exitCode).toBe(1); expect(result.url).toBeNull(); expect(result.message).toContain('plugin checkout'); @@ -70,20 +122,11 @@ describe('serveDashboard', () => { }); it('passes the App, artifact, opening tool, approval, and flags through to spawnServeApp', async () => { - let seen: SpawnServeAppOptions | undefined; - const controller = new AbortController(); - const server = fakeServer(); - const pending = serve({ - input: { noOpen: true, port: 4321, target: 'cursor' }, - signal: controller.signal, - spawn: (options) => { - seen = options; - return Promise.resolve(server.served); - }, - }); - server.exit({ code: 0, signal: null }); + const helper = fakeHelper(); + const pending = serve({ input: { noOpen: true, port: 4321, target: 'cursor' }, spawn: helper.spawn }); + helper.exit({ code: 0, signal: null }); const result = await pending; - expect(seen).toEqual({ + expect(helper.options()).toEqual({ app: 'hauler/dashboard', root: '/checkout', artifact: '/checkout/artifact', @@ -92,7 +135,7 @@ describe('serveDashboard', () => { autoApprove: ['call-tool'], open: false, port: 4321, - signal: controller.signal, + signal: expect.any(AbortSignal), }); expect(result).toEqual({ exitCode: 0, @@ -103,19 +146,13 @@ describe('serveDashboard', () => { }); it('defaults to the portable pack, an ephemeral port, and opening the browser', async () => { - let seen: SpawnServeAppOptions | undefined; - const server = fakeServer(); - const pending = serve({ - spawn: (options) => { - seen = options; - return Promise.resolve(server.served); - }, - }); - server.exit({ code: 0, signal: null }); + const helper = fakeHelper(); + const pending = serve({ spawn: helper.spawn }); + helper.exit({ code: 0, signal: null }); await pending; - expect(seen?.target).toBe('portable'); - expect(seen?.open).toBe(true); - expect(seen).not.toHaveProperty('port'); + expect(helper.options()?.target).toBe('portable'); + expect(helper.options()?.open).toBe(true); + expect(helper.options()).not.toHaveProperty('port'); }); it.each([ @@ -123,6 +160,7 @@ describe('serveDashboard', () => { ['artifact-missing', undefined, 1], ['spawn-failed', undefined, 1], ['aborted', undefined, 1], + ['stop-failed', undefined, 1], ['exited-before-ready', { code: 3, signal: null }, 3], ['exited-before-ready', { code: null, signal: 'SIGKILL' }, 128], ] as const)('maps a %s failure before the ready line onto the result (exit %s)', async (code, exit, exitCode) => { @@ -141,42 +179,52 @@ describe('serveDashboard', () => { }); it('reports a server that exited on its own with its code or signal', async () => { - const byCode = fakeServer(); - const codePending = serve({ spawn: () => Promise.resolve(byCode.served) }); + const byCode = fakeHelper(); + const codePending = serve({ spawn: byCode.spawn }); byCode.exit({ code: 2, signal: null }); expect(await codePending).toMatchObject({ exitCode: 2, message: expect.stringContaining('exited with 2') }); - const bySignal = fakeServer(); - const signalPending = serve({ spawn: () => Promise.resolve(bySignal.served) }); + const bySignal = fakeHelper(); + const signalPending = serve({ spawn: bySignal.spawn }); bySignal.exit({ code: null, signal: 'SIGTERM' }); expect(await signalPending).toMatchObject({ exitCode: 128, message: expect.stringContaining('exited with SIGTERM'), url: 'http://127.0.0.1:40577/', }); + expect(byCode.stops() + bySignal.stops()).toBe(0); }); - it('closes the served App when the request aborts and returns its exit', async () => { + it('an abort before the ready line reaches the helper, which stops the child once', async () => { const controller = new AbortController(); - const server = fakeServer(); - const pending = serve({ signal: controller.signal, spawn: () => Promise.resolve(server.served) }); + const helper = fakeHelper({ readyAtOnce: false }); + const pending = serve({ signal: controller.signal, spawn: helper.spawn }); + await settled(); controller.abort(); const result = await pending; - expect(server.closeCalls()).toBe(1); - expect(result.exitCode).toBe(128); + expect(helper.stops()).toBe(1); + expect(result).toMatchObject({ exitCode: 1, url: null, message: expect.stringContaining('aborted before agent-bundle serve-app was ready') }); + }); + + it('an abort after the ready line stops the server exactly once and returns its exit', async () => { + const controller = new AbortController(); + const helper = fakeHelper(); + const pending = serve({ signal: controller.signal, spawn: helper.spawn }); + await settled(); + controller.abort(); + const result = await pending; + expect(helper.stops()).toBe(1); + expect(result).toMatchObject({ exitCode: 128, message: expect.stringContaining('exited with SIGTERM'), url: 'http://127.0.0.1:40577/' }); }); it('does not hang when the abort cannot stop the server: the stop failure is the result', async () => { const controller = new AbortController(); - const stopFailed = new ServeAppCommandError( - 'stop-failed', - 'agent-bundle serve-app (pid 4242) could not be signalled to stop and is still running.', - ); - const server = fakeServer({ closeRejects: stopFailed }); - const pending = serve({ signal: controller.signal, spawn: () => Promise.resolve(server.served) }); + const helper = fakeHelper({ refuseStop: true }); + const pending = serve({ signal: controller.signal, spawn: helper.spawn }); + await settled(); controller.abort(); const result = await pending; - expect(server.closeCalls()).toBe(1); + expect(helper.stops()).toBe(1); expect(result).toEqual({ exitCode: 1, message: expect.stringContaining('could not be signalled to stop and is still running. Stop it by hand (kill 4242).'), @@ -185,14 +233,26 @@ describe('serveDashboard', () => { }); }); - it('settles on the abort even when the signal was already aborted after the ready line', async () => { + it('a signal aborted before the spawn is handed to the helper, which rejects it', async () => { const controller = new AbortController(); - const server = fakeServer(); controller.abort(); - // The helper itself rejects a pre-aborted spawn; here the fake served it, - // so the caller's own abort handling must still close it. - const result = await serve({ signal: controller.signal, spawn: () => Promise.resolve(server.served) }); - expect(server.closeCalls()).toBe(1); - expect(result.exitCode).toBe(128); + const helper = fakeHelper({ readyAtOnce: false }); + const result = await serve({ signal: controller.signal, spawn: helper.spawn }); + expect(helper.options()?.signal?.aborted).toBe(true); + expect(helper.stops()).toBe(0); + expect(result).toMatchObject({ exitCode: 1, url: null, message: expect.stringContaining('aborted before agent-bundle serve-app started') }); + }); + + it('a server that exits on its own after an abort was handled is not stopped again', async () => { + const controller = new AbortController(); + const helper = fakeHelper(); + const pending = serve({ signal: controller.signal, spawn: helper.spawn }); + await settled(); + helper.exit({ code: 0, signal: null }); + await settled(); + controller.abort(); + const result = await pending; + expect(helper.stops()).toBe(0); + expect(result.exitCode).toBe(0); }); }); From 2190c2cf23503545ec491ec45c3e4cb92d56888c Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sat, 5 Sep 2026 06:57:57 +0000 Subject: [PATCH 6/6] test(dashboard): cover the pre-ready abort races (ready line while dying; refused stop) and document why no post-ready window exists --- src/lib/dashboard-serve.ts | 10 ++++++--- tests/dashboard-serve.test.ts | 38 +++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/lib/dashboard-serve.ts b/src/lib/dashboard-serve.ts index 9f9b9347..060d72ae 100644 --- a/src/lib/dashboard-serve.ts +++ b/src/lib/dashboard-serve.ts @@ -138,9 +138,13 @@ export const serveDashboard = async ({ helper.handOver(); let exit: ServeAppExit; try { - // An abort that landed between the ready line and this hand-over already - // had the helper send the stop; a second `close()` would be a second - // SIGTERM, so that exit is simply awaited. + // `helper.signal` aborted and yet the App was served: the abort came + // before the ready line, the helper's SIGTERM was accepted (a refusal + // rejects `spawn` with `stop-failed`), and the child printed its ready + // line while dying. Its exit is on the way; a `close()` would be a second + // SIGTERM. An abort cannot land between the ready line and this point: + // the ready line settles `spawn` from a stdout event and this continuation + // runs in the microtasks after it, before any signal handler or timer. exit = helper.signal.aborted ? await served.closed : await exitOrStopFailure(served, signal); } catch (error) { if (error instanceof ServeAppCommandError) { diff --git a/tests/dashboard-serve.test.ts b/tests/dashboard-serve.test.ts index 1275d496..d8f994b6 100644 --- a/tests/dashboard-serve.test.ts +++ b/tests/dashboard-serve.test.ts @@ -28,7 +28,14 @@ interface FakeHelper { * settling `closed` after it — and `close()` sends one too, resolving with * the exit or rejecting with `stop-failed` when the child refuses it. */ -const fakeHelper = (behaviour: { readonly refuseStop?: boolean; readonly readyAtOnce?: boolean } = {}): FakeHelper => { +const fakeHelper = ( + behaviour: { + readonly refuseStop?: boolean; + readonly readyAtOnce?: boolean; + /** The child prints its ready line while dying from the pre-ready stop: `spawn` resolves although the signal aborted. */ + readonly readyWhileDying?: boolean; + } = {}, +): FakeHelper => { let seen: SpawnServeAppOptions | undefined; let stops = 0; let settledExit: ServeAppExit | undefined; @@ -83,7 +90,12 @@ const fakeHelper = (behaviour: { readonly refuseStop?: boolean; readonly readyAt 'abort', () => { const refused = stop(); - if (!ready) reject(refused ?? new ServeAppCommandError('aborted', 'Serving hauler/dashboard was aborted before agent-bundle serve-app was ready.')); + if (ready) return; + if (refused === undefined && behaviour.readyWhileDying === true) { + announceReady(); + return; + } + reject(refused ?? new ServeAppCommandError('aborted', 'Serving hauler/dashboard was aborted before agent-bundle serve-app was ready.')); }, { once: true }, ); @@ -233,6 +245,28 @@ describe('serveDashboard', () => { }); }); + it('a pre-ready abort whose stop was accepted, with the ready line racing in, is not stopped again', async () => { + const controller = new AbortController(); + const helper = fakeHelper({ readyAtOnce: false, readyWhileDying: true }); + const pending = serve({ signal: controller.signal, spawn: helper.spawn }); + await settled(); + controller.abort(); + const result = await pending; + expect(helper.stops()).toBe(1); + expect(result).toMatchObject({ exitCode: 128, message: expect.stringContaining('exited with SIGTERM'), url: 'http://127.0.0.1:40577/' }); + }); + + it('a pre-ready abort whose stop was refused is the stop-failed result, not a hang', async () => { + const controller = new AbortController(); + const helper = fakeHelper({ readyAtOnce: false, refuseStop: true }); + const pending = serve({ signal: controller.signal, spawn: helper.spawn }); + await settled(); + controller.abort(); + const result = await pending; + expect(helper.stops()).toBe(1); + expect(result).toMatchObject({ exitCode: 1, url: null, message: expect.stringContaining('could not be signalled to stop') }); + }); + it('a signal aborted before the spawn is handed to the helper, which rejects it', async () => { const controller = new AbortController(); controller.abort();